Generalize browser automation hosting beyond the desktop app (#1878)
* feat(browser): generalize browser automation hosts Rename the browser automation client capability to a host-neutral payload, advertise supported commands, and route tab-scoped commands by browserId affinity so future browser hosts can register without another protocol shape change. * fix(browser): harden browser host registration Reject unusable browser host capabilities, clean up pending requests when a host registration is replaced, and defer list_tabs affinity updates until every host in the aggregation succeeds. * fix(browser): preserve host tab affinity across reconnects Keep stranded tab ownership tied to the disconnected host so the same host can reclaim open tabs after reconnect, and tolerate future command names in browser_host capability payloads while retaining only known commands.
This commit is contained in:
@@ -2,6 +2,7 @@ import { afterEach, expect, expectTypeOf, test, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
import { DaemonClient, type DaemonTransport, type Logger } from "./daemon-client";
|
||||
import { CLIENT_CAPS } from "@getpaseo/protocol/client-capabilities";
|
||||
import { BROWSER_AUTOMATION_COMMAND_NAMES } from "@getpaseo/protocol/browser-automation/rpc-schemas";
|
||||
import {
|
||||
decodeFileTransferFrame,
|
||||
encodeFileTransferFrame,
|
||||
@@ -164,7 +165,7 @@ test("does not infer browser automation capabilities from Electron runtime", asy
|
||||
capabilities: z.record(z.unknown()),
|
||||
})
|
||||
.parse(JSON.parse(assertStr(mock.sent[0])));
|
||||
expect(hello.capabilities[CLIENT_CAPS.desktopBrowserAutomation]).toBeUndefined();
|
||||
expect(hello.capabilities[CLIENT_CAPS.browserHost]).toBeUndefined();
|
||||
});
|
||||
|
||||
test("advertises consumer-provided browser automation capabilities", async () => {
|
||||
@@ -174,7 +175,12 @@ test("advertises consumer-provided browser automation capabilities", async () =>
|
||||
clientId: "browser_capability_unit_test",
|
||||
transportFactory: () => mock.transport,
|
||||
reconnect: { enabled: false },
|
||||
capabilities: { [CLIENT_CAPS.desktopBrowserAutomation]: true },
|
||||
capabilities: {
|
||||
[CLIENT_CAPS.browserHost]: {
|
||||
supportedCommands: [...BROWSER_AUTOMATION_COMMAND_NAMES],
|
||||
hostKind: "desktop app",
|
||||
},
|
||||
},
|
||||
});
|
||||
clients.push(client);
|
||||
|
||||
@@ -188,7 +194,10 @@ test("advertises consumer-provided browser automation capabilities", async () =>
|
||||
capabilities: z.record(z.unknown()),
|
||||
})
|
||||
.parse(JSON.parse(assertStr(mock.sent[0])));
|
||||
expect(hello.capabilities[CLIENT_CAPS.desktopBrowserAutomation]).toBe(true);
|
||||
expect(hello.capabilities[CLIENT_CAPS.browserHost]).toEqual({
|
||||
supportedCommands: [...BROWSER_AUTOMATION_COMMAND_NAMES],
|
||||
hostKind: "desktop app",
|
||||
});
|
||||
});
|
||||
|
||||
const noopLogger: Logger = {
|
||||
@@ -506,7 +515,12 @@ test("advertises client capabilities in hello", async () => {
|
||||
logger,
|
||||
reconnect: { enabled: false },
|
||||
transportFactory: () => mock.transport,
|
||||
capabilities: { desktop_browser_automation: true },
|
||||
capabilities: {
|
||||
browser_host: {
|
||||
supportedCommands: ["list_tabs"],
|
||||
hostKind: "desktop app",
|
||||
},
|
||||
},
|
||||
});
|
||||
clients.push(client);
|
||||
|
||||
@@ -524,7 +538,10 @@ test("advertises client capabilities in hello", async () => {
|
||||
custom_mode_icons: true,
|
||||
reasoning_merge_enum: true,
|
||||
terminal_reflowable_snapshot: true,
|
||||
desktop_browser_automation: true,
|
||||
browser_host: {
|
||||
supportedCommands: ["list_tabs"],
|
||||
hostKind: "desktop app",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -252,7 +252,7 @@ export interface DaemonClientConfig {
|
||||
};
|
||||
runtimeMetricsIntervalMs?: number;
|
||||
runtimeMetricsWindowMs?: number;
|
||||
capabilities?: Partial<Record<ClientCapability, boolean>>;
|
||||
capabilities?: Partial<Record<ClientCapability, unknown>>;
|
||||
}
|
||||
|
||||
export interface SendMessageOptions {
|
||||
|
||||
Reference in New Issue
Block a user