diff options
| author | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-09-09 22:54:22 -0400 |
|---|---|---|
| committer | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-09-09 23:14:15 -0400 |
| commit | 1ec29fc90b84517322bf5994245368f0e59eff61 (patch) | |
| tree | 5a8ba52d06a1c96d93540cfe1b34f699cd447c68 /extensions/chrome-profile-bridge/index.ts | |
| parent | 017ff4b9a639f0b8b213e58a3f30613fc38edcc8 (diff) | |
Fix session cleanup and release 0.15.47
Diffstat (limited to 'extensions/chrome-profile-bridge/index.ts')
| -rw-r--r-- | extensions/chrome-profile-bridge/index.ts | 39 |
1 files changed, 23 insertions, 16 deletions
diff --git a/extensions/chrome-profile-bridge/index.ts b/extensions/chrome-profile-bridge/index.ts index e3e5a04..fdfa39c 100644 --- a/extensions/chrome-profile-bridge/index.ts +++ b/extensions/chrome-profile-bridge/index.ts @@ -759,13 +759,25 @@ export default function (pi: ExtensionAPI): void { }, { triggerTurn: false }); }; - // Close THIS session's dedicated automation window/tab. Fire-and-forget and best-effort: it - // must never block /quit, /reload, revoke, or session end, and the service-worker side only - // ever closes targets this session created itself (never user tabs/windows, never another - // session's target). Errors (bridge down, target already closed) are intentionally swallowed. - const cleanupAutomationTargetBestEffort = (): void => { + // Bound the entire request, including shared-owner forwarding/takeover. Revoke can launch + // this in the background; shutdown must wait before tearing down the bridge. + const cleanupAutomationTargetBestEffort = async (timeoutMs = 2_000): Promise<void> => { const sessionKey = sessionKeyFor(sessionCtx); - void bridge.send("automation.cleanup", sessionKey !== undefined ? { sessionKey } : {}, 3_000).catch(() => undefined); + if (sessionKey === undefined) return; // Never clean up an unscoped/default session. + const controller = new AbortController(); + let timer: ReturnType<typeof setTimeout> | undefined; + try { + await Promise.race([ + bridge.send("automation.cleanup", { sessionKey }, timeoutMs, controller.signal).catch(() => undefined), + new Promise<void>((resolveCleanup) => { + timer = setTimeout(() => { controller.abort(); resolveCleanup(); }, timeoutMs); + }), + ]); + } catch { + // Shutdown/revoke remains best-effort when Chrome or its bridge is unavailable. + } finally { + clearTimeout(timer); + } }; const lockChromeControl = (logAction?: "revoked" | "expired"): void => { @@ -778,7 +790,7 @@ export default function (pi: ExtensionAPI): void { chromeAuthorizedUntil = undefined; persistAuth(); // Revoking control ends pi-chrome's automation for this session; tidy up the target we own. - cleanupAutomationTargetBestEffort(); + void cleanupAutomationTargetBestEffort(); }; const authSummary = (): string => { @@ -925,17 +937,12 @@ export default function (pi: ExtensionAPI): void { updateChromeStatus(ctx); }); - pi.on("session_shutdown", (event) => { + pi.on("session_shutdown", async (event) => { clearAuthExpiryTimer(); clearCountdownInterval(); - // Tidy up this session's dedicated automation window on real session end, but NOT on - // "reload": /reload tears down and re-evaluates this module while the *same* session - // (same sessionKey) continues, so we keep the window so it is reused, not churned. The - // call is fire-and-forget and runs before bridge.stop() so it never blocks shutdown. - // (Owner-session quit may not deliver in time since stop() closes the bridge server; - // that only ever leaves a clearly pi-chrome window for the user to close — never a user - // tab — and /chrome revoke remains the reliable, bridge-alive cleanup path.) - if (event?.reason !== "reload") cleanupAutomationTargetBestEffort(); + // /reload continues the same session. On exit, give Chrome a bounded opportunity to + // close this session's created tabs and ungroup adopted tabs before stopping the broker. + if (event?.reason !== "reload") await cleanupAutomationTargetBestEffort(); bridge.stop(); if (globalState[PI_CHROME_GLOBAL_KEY]?.token === instanceToken) { delete globalState[PI_CHROME_GLOBAL_KEY]; |
