diff options
Diffstat (limited to 'src/index.ts')
| -rw-r--r-- | src/index.ts | 83 |
1 files changed, 81 insertions, 2 deletions
diff --git a/src/index.ts b/src/index.ts index 2b95697..152c94d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,9 @@ import { restoreDownloadBehavior } from "./domains/files"; import { createDaemonTransport } from "./daemon/transport"; import { setDebugClicks } from "./util/debug"; import { asBoolean, asString } from "./util/guards"; -import { describeCdpTarget, resolveCdpTarget } from "./cdp/endpoint"; +import { describeCdpTarget, isValidTargetValue, localAliases, resolveCdpTarget } from "./cdp/endpoint"; +import { clearTarget, readTarget, writeTarget } from "./cdp/target-store"; +import { ensureDaemon, stopDaemon } from "./daemon/spawn"; import { queryLiveWsUrl } from "./cdp/discovery"; export default function browserHarnessExtension(pi: ExtensionAPI): void { @@ -47,7 +49,7 @@ export default function browserHarnessExtension(pi: ExtensionAPI): void { // first browser_* tool runs. A plain "Disconnected" reads as a fault // when it is really just "not needed yet", so say which it is, and show // what we would connect to. - const target = resolveCdpTarget(); + const target = resolveCdpTarget(process.env, await readTarget()); const reachable = target.kind === "remote" ? (await queryLiveWsUrl(target.port, 2_000, target.host)) !== null : null; @@ -91,6 +93,83 @@ export default function browserHarnessExtension(pi: ExtensionAPI): void { }, }); + // FORK: switch which browser the harness drives, without leaving pi. + // + // The target is resolved per daemon process and the daemon holds the browser + // connection, so a switch has to replace that process — writing the store + // alone would leave tool calls going to the old browser while /browser-status + // reported the new one. + pi.registerCommand("browser-target", { + description: "Show or change which browser to drive (local, default, or host:port)", + handler: async (args, ctx) => { + const wanted = (typeof args === "string" ? args : "").trim(); + + if (wanted === "") { + const current = resolveCdpTarget(process.env, await readTarget()); + ctx.ui.notify( + `Browser target: ${describeCdpTarget(current)}\n` + + `Change with: /browser-target local | default | host:port`, + "info", + ); + return; + } + + const clearing = wanted.toLowerCase() === "default"; + if (!clearing && !isValidTargetValue(wanted)) { + ctx.ui.notify( + `Not a usable target: "${wanted}"\n` + + `Expected "host:port", "default", or one of ${localAliases().join(", ")}.`, + "error", + ); + return; + } + + const saved = clearing ? await clearTarget() : await writeTarget(wanted); + if (!saved.success) { + ctx.ui.notify(saved.error, "error"); + return; + } + + const resolved = resolveCdpTarget(process.env, await readTarget()); + + // Warn rather than silently disobey: the environment deliberately wins, + // so a stored choice made now would not take effect this session. + const envOverride = (process.env["BU_CDP_HTTP"] ?? "").trim(); + if (envOverride !== "") { + ctx.ui.notify( + `Saved, but BU_CDP_HTTP=${envOverride} is set in this environment and takes precedence.\n` + + `Still using: ${describeCdpTarget(resolved)}`, + "warning", + ); + return; + } + + ctx.ui.notify(`Switching to ${describeCdpTarget(resolved)}...`, "info"); + + if (client) await client.stop(); + if (!(await stopDaemon())) { + ctx.ui.notify("Saved, but the browser daemon did not stop — run /browser-setup to pick it up.", "warning"); + return; + } + if (!(await ensureDaemon())) { + ctx.ui.notify("Saved, but the browser daemon did not come back. Run /browser-setup.", "error"); + return; + } + + if (!client) { + ctx.ui.notify(`Browser target set to ${describeCdpTarget(resolved)} ✓ — run /browser-setup to connect.`, "info"); + return; + } + const started = await client.start(); + ctx.ui.notify( + started.success + ? `Browser target set to ${describeCdpTarget(resolved)} ✓` + : `Target saved (${describeCdpTarget(resolved)}) but connecting failed: ${started.error.message}`, + started.success ? "info" : "error", + ); + }, + }); + pi.registerCommand("browser-reload-daemon", { description: "Restart the browser client", handler: async (_args, ctx) => { |
