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.ts76
1 files changed, 56 insertions, 20 deletions
diff --git a/src/cdp/endpoint.ts b/src/cdp/endpoint.ts
index 1be5322..96cf2ea 100644
--- a/src/cdp/endpoint.ts
+++ b/src/cdp/endpoint.ts
@@ -15,31 +15,53 @@ export const DEFAULT_REMOTE_CDP = "10.88.0.25:9223";
/** Values of BU_CDP_HTTP that mean "ignore the default, use this machine". */
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.
+ */
+export type CdpTargetSource = "env" | "stored" | "default";
+
export type CdpTarget =
| {
readonly kind: "remote";
readonly host: string;
readonly port: number;
- /** "env" was asked for explicitly; "default" is this fork's built-in. */
- readonly source: "env" | "default";
+ readonly source: CdpTargetSource;
}
- | { readonly kind: "local"; readonly source: "env" }
- | { readonly kind: "invalid"; readonly raw: string; readonly reason: string };
+ | { readonly kind: "local"; readonly source: Exclude<CdpTargetSource, "default"> }
+ | { readonly kind: "invalid"; readonly raw: string; readonly reason: string; readonly source: CdpTargetSource };
/**
- * Precedence:
- * BU_CDP_HTTP="host:port" -> that remote, and a failure to reach it is fatal
- * BU_CDP_HTTP=local|off|… -> upstream behaviour, this machine's own browser
- * unset -> DEFAULT_REMOTE_CDP, falling back to local when
- * it isn't reachable
+ * Precedence: BU_CDP_HTTP, then the /browser-target choice, then the built-in
+ * default.
+ *
+ * "host:port" -> that remote, and a failure to reach it is fatal
+ * local|off|… -> upstream behaviour, this machine's own browser
+ * neither set -> DEFAULT_REMOTE_CDP, falling back to local when unreachable
+ *
+ * The environment wins over the stored choice so that a one-off
+ * `BU_CDP_HTTP=… pi` is never silently overridden by a setting made days ago.
+ *
+ * The fallback asymmetry is deliberate. An explicit endpoint — from either
+ * source — is an instruction, so silently driving a different browser instead
+ * would be wrong: it would automate the operator's own signed-in Chrome rather
+ * than the shared one. The built-in default is only a preference, so a laptop
+ * away from the wire network still gets the local browser the extension was
+ * originally written for.
*
- * The asymmetry is deliberate. An explicit endpoint is an instruction, so
- * silently driving a different browser instead would be wrong. The built-in
- * default is only a preference, so a laptop away from the wire network still
- * gets the local browser the extension was originally written for.
+ * `stored` is passed in rather than read here so this stays pure and
+ * synchronous: /browser-status can describe the target without I/O, and the
+ * daemon can resolve identically in its own process.
*/
-export const resolveCdpTarget = (env: NodeJS.ProcessEnv = process.env): CdpTarget => {
- const raw = env["BU_CDP_HTTP"]?.trim();
+export const resolveCdpTarget = (
+ env: NodeJS.ProcessEnv = process.env,
+ stored: string | null = null,
+): CdpTarget => {
+ const fromEnv = env["BU_CDP_HTTP"]?.trim();
+ const fromStore = stored?.trim();
+
+ const raw = fromEnv !== undefined && fromEnv !== "" ? fromEnv : fromStore;
+ const source: CdpTargetSource = fromEnv !== undefined && fromEnv !== "" ? "env" : "stored";
if (raw === undefined || raw === "") {
const parsed = parseHostPort(DEFAULT_REMOTE_CDP);
@@ -48,17 +70,28 @@ export const resolveCdpTarget = (env: NodeJS.ProcessEnv = process.env): CdpTarge
return { kind: "remote", host: parsed.host, port: parsed.port, source: "default" };
}
- if (LOCAL_ALIASES.has(raw.toLowerCase())) return { kind: "local", source: "env" };
+ if (LOCAL_ALIASES.has(raw.toLowerCase())) return { kind: "local", source };
const parsed = parseHostPort(raw);
if (!parsed) {
return {
kind: "invalid",
raw,
+ source,
reason: `expected "host:port" or one of ${[...LOCAL_ALIASES].join(", ")}`,
};
}
- return { kind: "remote", host: parsed.host, port: parsed.port, source: "env" };
+ return { kind: "remote", host: parsed.host, port: parsed.port, source };
+};
+
+/** The vocabulary /browser-target accepts, for help text and validation. */
+export const localAliases = (): ReadonlyArray<string> => [...LOCAL_ALIASES];
+
+/** True when `value` is something resolveCdpTarget can make sense of. */
+export const isValidTargetValue = (value: string): boolean => {
+ const v = value.trim();
+ if (v === "") return false;
+ return LOCAL_ALIASES.has(v.toLowerCase()) || parseHostPort(v) !== null;
};
const parseHostPort = (value: string): { host: string; port: number } | null => {
@@ -71,14 +104,17 @@ const parseHostPort = (value: string): { host: string; port: number } | null =>
return { host, port };
};
+const sourceLabel = (source: CdpTargetSource): string =>
+ 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 => {
switch (target.kind) {
case "remote":
- return `${target.host}:${target.port}` + (target.source === "default" ? " (built-in default)" : " (BU_CDP_HTTP)");
+ return `${target.host}:${target.port} (${sourceLabel(target.source)})`;
case "local":
- return "this machine's own browser (BU_CDP_HTTP=local)";
+ return `this machine's own browser (${sourceLabel(target.source)})`;
case "invalid":
- return `invalid BU_CDP_HTTP "${target.raw}" — ${target.reason}`;
+ return `invalid target "${target.raw}" from ${sourceLabel(target.source)} — ${target.reason}`;
}
};