aboutsummaryrefslogtreecommitdiff
path: root/src/cdp/endpoint.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/cdp/endpoint.ts')
-rw-r--r--src/cdp/endpoint.ts60
1 files changed, 53 insertions, 7 deletions
diff --git a/src/cdp/endpoint.ts b/src/cdp/endpoint.ts
index 96cf2ea..9cb21c6 100644
--- a/src/cdp/endpoint.ts
+++ b/src/cdp/endpoint.ts
@@ -16,10 +16,11 @@ export const DEFAULT_REMOTE_CDP = "10.88.0.25:9223";
const LOCAL_ALIASES = new Set(["local", "localhost", "off", "none", "0", "no"]);
/**
- * Where a target came from. "env" is BU_CDP_HTTP, "stored" is /browser-target,
- * "default" is this fork's built-in. Only "default" is allowed to fall back.
+ * Where a target came from. "ws" is upstream's BU_CDP_WS, "env" is BU_CDP_HTTP,
+ * "stored" is /browser-target, "default" is this fork's built-in. Only
+ * "default" is allowed to fall back.
*/
-export type CdpTargetSource = "env" | "stored" | "default";
+export type CdpTargetSource = "ws" | "env" | "stored" | "default";
export type CdpTarget =
| {
@@ -28,12 +29,17 @@ export type CdpTarget =
readonly port: number;
readonly source: CdpTargetSource;
}
- | { readonly kind: "local"; readonly source: Exclude<CdpTargetSource, "default"> }
+ | { readonly kind: "local"; readonly source: Exclude<CdpTargetSource, "default" | "ws"> }
| { readonly kind: "invalid"; readonly raw: string; readonly reason: string; readonly source: CdpTargetSource };
/**
- * Precedence: BU_CDP_HTTP, then the /browser-target choice, then the built-in
- * default.
+ * Precedence: BU_CDP_WS, then BU_CDP_HTTP, then the /browser-target choice,
+ * then the built-in default.
+ *
+ * BU_CDP_WS -> upstream's pinned WebSocket URL; discoverEndpoint() returns
+ * it before any of the rest is consulted, so it is reported
+ * here only so status, /browser-target and the prompt agree
+ * with what the daemon actually dials
*
* "host:port" -> that remote, and a failure to reach it is fatal
* local|off|… -> upstream behaviour, this machine's own browser
@@ -57,6 +63,16 @@ export const resolveCdpTarget = (
env: NodeJS.ProcessEnv = process.env,
stored: string | null = null,
): CdpTarget => {
+ // FORK: mirror upstream's endpointFromEnv() in discovery.ts, which wins.
+ const pinnedWs = env["BU_CDP_WS"]?.trim();
+ if (pinnedWs) {
+ const parsed = parseWsUrl(pinnedWs);
+ if (!parsed) {
+ return { kind: "invalid", raw: redactWsUrl(pinnedWs), source: "ws", reason: "expected a ws:// or wss:// URL" };
+ }
+ return { kind: "remote", host: parsed.host, port: parsed.port, source: "ws" };
+ }
+
const fromEnv = env["BU_CDP_HTTP"]?.trim();
const fromStore = stored?.trim();
@@ -104,8 +120,38 @@ const parseHostPort = (value: string): { host: string; port: number } | null =>
return { host, port };
};
+/**
+ * host:port of a BU_CDP_WS URL. Only that much is ever shown: the path carries
+ * the browser's id, and a proxied endpoint may carry a token or credentials.
+ */
+const parseWsUrl = (value: string): { host: string; port: number } | null => {
+ let url: URL;
+ try {
+ url = new URL(value);
+ } catch {
+ return null;
+ }
+ if (url.protocol !== "ws:" && url.protocol !== "wss:") return null;
+ const host = url.hostname.replace(/^\[(.*)\]$/, "$1");
+ if (!host) return null;
+ const port = url.port === "" ? (url.protocol === "wss:" ? 443 : 80) : Number(url.port);
+ return { host, port };
+};
+
+/** A BU_CDP_WS value safe to print: host:port when parseable, otherwise a placeholder. */
+export const redactWsUrl = (value: string): string => {
+ const parsed = parseWsUrl(value.trim());
+ return parsed ? `${parsed.host}:${parsed.port}` : "<unparseable URL>";
+};
+
const sourceLabel = (source: CdpTargetSource): string =>
- source === "env" ? "BU_CDP_HTTP" : source === "stored" ? "/browser-target" : "built-in default";
+ source === "ws"
+ ? "BU_CDP_WS, pinned WebSocket"
+ : source === "env"
+ ? "BU_CDP_HTTP"
+ : source === "stored"
+ ? "/browser-target"
+ : "built-in default";
/** One-line description for /browser-status and error messages. */
export const describeCdpTarget = (target: CdpTarget): string => {