fix: handle extension relay session reuse
This commit is contained in:
@@ -48,6 +48,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- Agents: treat plugin-only tool allowlists as opt-ins; keep core tools enabled. (#1467)
|
- Agents: treat plugin-only tool allowlists as opt-ins; keep core tools enabled. (#1467)
|
||||||
- Exec approvals: persist allowlist entry ids to keep macOS allowlist rows stable. (#1521) Thanks @ngutman.
|
- Exec approvals: persist allowlist entry ids to keep macOS allowlist rows stable. (#1521) Thanks @ngutman.
|
||||||
- MS Teams (plugin): remove `.default` suffix from Graph scopes to avoid double-appending. (#1507) Thanks @Evizero.
|
- MS Teams (plugin): remove `.default` suffix from Graph scopes to avoid double-appending. (#1507) Thanks @Evizero.
|
||||||
|
- Browser: keep extension relay tabs controllable when the extension reuses a session id after switching tabs. (#1160)
|
||||||
|
|
||||||
## 2026.1.22
|
## 2026.1.22
|
||||||
|
|
||||||
|
|||||||
@@ -247,4 +247,71 @@ describe("chrome extension relay server", () => {
|
|||||||
cdp.close();
|
cdp.close();
|
||||||
ext.close();
|
ext.close();
|
||||||
}, 15_000);
|
}, 15_000);
|
||||||
|
|
||||||
|
it("rebroadcasts attach when a session id is reused for a new target", async () => {
|
||||||
|
const port = await getFreePort();
|
||||||
|
cdpUrl = `http://127.0.0.1:${port}`;
|
||||||
|
await ensureChromeExtensionRelayServer({ cdpUrl });
|
||||||
|
|
||||||
|
const ext = new WebSocket(`ws://127.0.0.1:${port}/extension`);
|
||||||
|
await waitForOpen(ext);
|
||||||
|
|
||||||
|
const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`);
|
||||||
|
await waitForOpen(cdp);
|
||||||
|
const q = createMessageQueue(cdp);
|
||||||
|
|
||||||
|
ext.send(
|
||||||
|
JSON.stringify({
|
||||||
|
method: "forwardCDPEvent",
|
||||||
|
params: {
|
||||||
|
method: "Target.attachedToTarget",
|
||||||
|
params: {
|
||||||
|
sessionId: "shared-session",
|
||||||
|
targetInfo: {
|
||||||
|
targetId: "t1",
|
||||||
|
type: "page",
|
||||||
|
title: "First",
|
||||||
|
url: "https://example.com",
|
||||||
|
},
|
||||||
|
waitingForDebugger: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const first = JSON.parse(await q.next()) as { method?: string; params?: unknown };
|
||||||
|
expect(first.method).toBe("Target.attachedToTarget");
|
||||||
|
expect(JSON.stringify(first.params ?? {})).toContain("t1");
|
||||||
|
|
||||||
|
ext.send(
|
||||||
|
JSON.stringify({
|
||||||
|
method: "forwardCDPEvent",
|
||||||
|
params: {
|
||||||
|
method: "Target.attachedToTarget",
|
||||||
|
params: {
|
||||||
|
sessionId: "shared-session",
|
||||||
|
targetInfo: {
|
||||||
|
targetId: "t2",
|
||||||
|
type: "page",
|
||||||
|
title: "Second",
|
||||||
|
url: "https://example.org",
|
||||||
|
},
|
||||||
|
waitingForDebugger: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const received: Array<{ method?: string; params?: unknown }> = [];
|
||||||
|
received.push(JSON.parse(await q.next()) as never);
|
||||||
|
received.push(JSON.parse(await q.next()) as never);
|
||||||
|
|
||||||
|
const detached = received.find((m) => m.method === "Target.detachedFromTarget");
|
||||||
|
const attached = received.find((m) => m.method === "Target.attachedToTarget");
|
||||||
|
expect(JSON.stringify(detached?.params ?? {})).toContain("t1");
|
||||||
|
expect(JSON.stringify(attached?.params ?? {})).toContain("t2");
|
||||||
|
|
||||||
|
cdp.close();
|
||||||
|
ext.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -477,13 +477,23 @@ export async function ensureChromeExtensionRelayServer(opts: {
|
|||||||
const targetType = attached?.targetInfo?.type ?? "page";
|
const targetType = attached?.targetInfo?.type ?? "page";
|
||||||
if (targetType !== "page") return;
|
if (targetType !== "page") return;
|
||||||
if (attached?.sessionId && attached?.targetInfo?.targetId) {
|
if (attached?.sessionId && attached?.targetInfo?.targetId) {
|
||||||
const already = connectedTargets.has(attached.sessionId);
|
const prev = connectedTargets.get(attached.sessionId);
|
||||||
|
const nextTargetId = attached.targetInfo.targetId;
|
||||||
|
const prevTargetId = prev?.targetId;
|
||||||
|
const changedTarget = Boolean(prev && prevTargetId && prevTargetId !== nextTargetId);
|
||||||
connectedTargets.set(attached.sessionId, {
|
connectedTargets.set(attached.sessionId, {
|
||||||
sessionId: attached.sessionId,
|
sessionId: attached.sessionId,
|
||||||
targetId: attached.targetInfo.targetId,
|
targetId: nextTargetId,
|
||||||
targetInfo: attached.targetInfo,
|
targetInfo: attached.targetInfo,
|
||||||
});
|
});
|
||||||
if (!already) {
|
if (changedTarget && prevTargetId) {
|
||||||
|
broadcastToCdpClients({
|
||||||
|
method: "Target.detachedFromTarget",
|
||||||
|
params: { sessionId: attached.sessionId, targetId: prevTargetId },
|
||||||
|
sessionId: attached.sessionId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!prev || changedTarget) {
|
||||||
broadcastToCdpClients({ method, params, sessionId });
|
broadcastToCdpClients({ method, params, sessionId });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user