fix(desktop): isolate remote runtime auth and embeds
Fix remote Desktop runtime bootstrapping across context-panel session chats, additional windows, and host switches.\n\n- Bootstrap embedded session-chat frames through a same-origin parent handshake that supplies the active endpoint, bearer token, runtime headers, local origin, and a credential-free relay descriptor.\n- Keep relay pairing grants out of iframe state and explicitly rebind the SDK after embedded bootstrap or relay restoration.\n- Preserve each additional and Mini Chat window's own init script instead of overwriting it when the main window's host configuration changes.\n- Replace direct iframe global calls with same-origin postMessage synchronization for theme, chat settings, and visibility.\n\nHarden Desktop host authentication and probing.\n\n- Bind password, passkey, session-status, and token-persistence completions to the runtime identity that started them, so a late result cannot alter a newly selected host.\n- Cancel active passkey operations and reset transient auth UI state on endpoint changes.\n- Verify stored client authentication via /auth/session for direct and relay host probes, distinguishing reachable hosts from hosts that require re-authentication.\n- Bound every relay probe request with an aborting timeout so a stalled auth request cannot hang refresh or host switching.\n\nAdd regression coverage for the embedded bootstrap handshake, credential-free relay descriptor exposure, runtime configuration, stale password completion after an A-to-B switch, and SDK errors that carry a zero response status.\n\nAlso preserve SDK response status on session-message loader errors so callers can distinguish transport and server failures.
This commit is contained in:
@@ -140,6 +140,7 @@ describe("SessionMessageLoader", () => {
|
||||
|
||||
await loader.ensure(target, { force: true })
|
||||
expect(loader.getSnapshot(target).status).toBe("error")
|
||||
expect((loader.getSnapshot(target).error as Error & { status?: number }).status).toBe(400)
|
||||
expect(store.getState().message[target.sessionID]?.[0]?.id).toBe("cached")
|
||||
|
||||
fail = false
|
||||
@@ -149,6 +150,20 @@ describe("SessionMessageLoader", () => {
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("propagates a zero response status on SDK errors", async () => {
|
||||
const { childStores, loader } = createLoader(async () => ({
|
||||
error: { message: "network rejected" },
|
||||
response: { status: 0 },
|
||||
}))
|
||||
const target = { directory: "/repo", sessionID: "session-a" }
|
||||
|
||||
await loader.ensure(target, { force: true })
|
||||
|
||||
expect((loader.getSnapshot(target).error as Error & { status?: number }).status).toBe(0)
|
||||
loader.dispose()
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("prevents an evicted in-flight request from repopulating the store", async () => {
|
||||
const pending = deferred<ReturnType<typeof response>>()
|
||||
const { childStores, loader } = createLoader(async () => pending.promise)
|
||||
|
||||
@@ -98,7 +98,10 @@ const assertSdkSuccess = (result: {
|
||||
}, operation: string): void => {
|
||||
if (!result.error) return
|
||||
const status = result.response?.status
|
||||
throw new Error(`${operation} failed${status ? ` (${status})` : ""}: ${formatSdkError(result.error)}`)
|
||||
const message = `${operation} failed${status ? ` (${status})` : ""}: ${formatSdkError(result.error)}`
|
||||
const error = new Error(message) as Error & { status?: number }
|
||||
if (status !== undefined) error.status = status
|
||||
throw error
|
||||
}
|
||||
|
||||
const sortParts = (parts: Part[]): Part[] => parts
|
||||
|
||||
Reference in New Issue
Block a user