fix(bug): avoid overwriting stored tokens with an empty UI value

GatewayClient.ts:944 → token: "" || undefined = undefined → omitted from patch
mergeGatewayConnectionState: patch.token === undefined → patchedToken = undefined → nextToken = undefined || current?.token ?? "" = "abc123" ✓

scenarionn- user explicitly clears token (empty string patch):

mergeGatewayConnectionState: patchedToken = "" → nextToken = "" || current?.token ?? "" = falls back to existing stored token

Authored By: GSKNNFT
This commit is contained in:
gsknnft
2026-04-19 12:39:48 -04:00
parent da4f519745
commit e215cae553
3 changed files with 80 additions and 3 deletions
+1 -1
View File
@@ -943,7 +943,7 @@ export const useGatewayConnection = (
gateway: {
lastKnownGood: {
url: gatewayUrl.trim(),
token,
token: token || undefined,
adapterType: nextDetectedAdapterType,
},
},
+2 -2
View File
@@ -917,8 +917,8 @@ const mergeGatewayConnectionState = (
const nextUrl =
patch.url === undefined ? current?.url ?? "" : normalizeGatewayUrl(patch.url);
if (!nextUrl) return null;
const nextToken =
patch.token === undefined ? current?.token ?? "" : coerceString(patch.token);
const patchedToken = patch.token === undefined ? undefined : coerceString(patch.token);
const nextToken = patchedToken || (current?.token ?? "");
const nextAdapterType =
patch.adapterType === undefined
? current?.adapterType ?? "openclaw"
+77
View File
@@ -438,4 +438,81 @@ describe("studio settings normalization", () => {
token: "",
});
});
it("merging lastKnownGood with an empty-string token does not overwrite a stored token", () => {
const current = normalizeStudioSettings({
gateway: {
url: "ws://localhost:18789",
token: "stored-token",
lastKnownGood: {
url: "ws://localhost:18789",
token: "stored-token",
adapterType: "openclaw",
},
},
});
const merged = mergeStudioSettings(current, {
gateway: {
lastKnownGood: {
url: "ws://localhost:18789",
token: "",
adapterType: "openclaw",
},
},
});
expect(merged.gateway?.lastKnownGood?.token).toBe("stored-token");
});
it("merging lastKnownGood with a real token overwrites the stored token", () => {
const current = normalizeStudioSettings({
gateway: {
url: "ws://localhost:18789",
token: "old-token",
lastKnownGood: {
url: "ws://localhost:18789",
token: "old-token",
adapterType: "openclaw",
},
},
});
const merged = mergeStudioSettings(current, {
gateway: {
lastKnownGood: {
url: "ws://localhost:18789",
token: "new-token",
adapterType: "openclaw",
},
},
});
expect(merged.gateway?.lastKnownGood?.token).toBe("new-token");
});
it("merging lastKnownGood with undefined token leaves the stored token unchanged", () => {
const current = normalizeStudioSettings({
gateway: {
url: "ws://localhost:18789",
token: "stored-token",
lastKnownGood: {
url: "ws://localhost:18789",
token: "stored-token",
adapterType: "openclaw",
},
},
});
const merged = mergeStudioSettings(current, {
gateway: {
lastKnownGood: {
url: "ws://localhost:18789",
adapterType: "openclaw",
},
},
});
expect(merged.gateway?.lastKnownGood?.token).toBe("stored-token");
});
});