From a6ec1d0915106b127affbd6b5fcc9efec48ef712 Mon Sep 17 00:00:00 2001 From: amankumarsingh77 Date: Tue, 28 Jul 2026 20:29:29 +0530 Subject: refactor(profile): strip comment noise Removes module banners and comments restating the code. Keeps one-line notes for the ProcessSingleton user-data-dir rule, the sentinel-URL and async-spawn-error quirks, the replicated getAgentDir, pi's tilde-expansion mirroring, the Windows wmic removal, the macOS/Linux process-identification quirks, the picker's undefined-means-cancel contract, and the deliberately unpersisted browserContextId. Co-Authored-By: Claude Opus 5 (1M context) --- src/profile/launch.ts | 44 ++++---------------------------------------- 1 file changed, 4 insertions(+), 40 deletions(-) (limited to 'src/profile/launch.ts') diff --git a/src/profile/launch.ts b/src/profile/launch.ts index 0088b6d..a54906d 100644 --- a/src/profile/launch.ts +++ b/src/profile/launch.ts @@ -1,19 +1,5 @@ -/** - * Opening a browser window for a chosen profile. - * - * CDP cannot do this: `Target.createTarget` rejects another profile's - * browserContextId ("Failed to find browser context with id"), and a bare - * createTarget lands in `defaultBrowserContextId`, which follows window focus. - * The only way in is the browser's own command line — Chromium's - * ProcessSingleton hands a second launch's argv to the running browser, which - * "does what it would have done", i.e. opens a window in `--profile-directory`. - * - * The window is then identified by a unique `file://` sentinel page. That - * matters: a bare `about:blank#token` argument is discarded by Chrome (it opens - * chrome://newtab instead), so the token has to ride on a real URL. Polling for - * the sentinel URL — rather than diffing the target list — also keeps - * identification correct when the user opens tabs at the same moment. - */ +// CDP cannot open a window in another profile, so this goes through the command line: ProcessSingleton hands the argv to the running browser. +// The sentinel must ride on a real `file://` URL — Chrome discards a bare `about:blank#token` argument and opens chrome://newtab instead. import { spawn, type ChildProcess } from "node:child_process"; import { randomUUID } from "node:crypto"; @@ -24,23 +10,13 @@ import { pathToFileURL } from "node:url"; import { type Result, err, ok } from "../util/result"; export type ProfileWindowRequest = { - /** Browser binary to invoke. */ readonly exePath: string; - /** Profile subdirectory, e.g. "Profile 1". */ readonly profileDir: string; - /** - * Only set when the RUNNING browser was launched with an explicit - * --user-data-dir. ProcessSingleton keys on this path, so passing one that - * differs by case or normalisation would start a second browser instead of - * delegating to the running one. - */ readonly explicitUserDataDir?: string; }; export type ProfileWindowHandle = { - /** file:// URL that will appear as the new window's page target. */ readonly sentinelUrl: string; - /** Remove the sentinel file. Safe to call more than once. */ readonly cleanup: () => Promise; readonly kill: () => void; }; @@ -49,10 +25,6 @@ const SENTINEL_HTML = (token: string): string => `${token}` + `pi browser harness — opening this profile…`; -/** - * Ask the running browser to open a window in `profileDir`, marked with a - * unique sentinel page the caller can then find over CDP. - */ export const openProfileWindow = async ( req: ProfileWindowRequest, ): Promise> => { @@ -71,26 +43,18 @@ export const openProfileWindow = async ( let child: ChildProcess; try { - // No shell: arguments are passed as an array, so profile names and paths - // containing spaces survive intact on every platform. child = spawn(req.exePath, args, { detached: true, stdio: "ignore" }); } catch (e) { await rm(file, { force: true }).catch(() => {}); return err(`could not launch ${req.exePath}: ${e instanceof Error ? e.message : String(e)}`); } - // spawn() reports a missing or non-executable binary ASYNCHRONOUSLY, as an - // 'error' event — it does not throw. Ignoring that event made an ENOENT - // indistinguishable from a browser that simply never opened the window, so - // the caller's 15s sentinel poll reported a timeout for what was really a bad - // executable path. Node guarantees exactly one of 'spawn' or 'error', so - // waiting for it costs nothing and turns the failure into a named cause. + // spawn() reports a bad binary asynchronously as an 'error' event rather than throwing; unhandled, an ENOENT looked like the 15s sentinel timeout. const spawnFailure = await new Promise((resolve) => { child.once("spawn", () => resolve(undefined)); child.once("error", (e: Error) => resolve(e.message)); }); - // A later 'error' event with no listener would throw; keep one attached for - // the lifetime of the detached launcher. + // A later 'error' event with no listener would throw, so keep one attached for the detached launcher's lifetime. child.on("error", () => {}); child.unref(); -- cgit v1.2.3