github-router 0.3.305 → 0.3.308
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{attribution-settings-Chqw6VeK.js → attribution-settings-C_ZLSCQ4.js} +60 -61
- package/dist/attribution-settings-C_ZLSCQ4.js.map +1 -0
- package/dist/{auth-CpjOjoUG.js → auth-O_UG1ABO.js} +3 -3
- package/dist/{auth-CpjOjoUG.js.map → auth-O_UG1ABO.js.map} +1 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/{check-usage-BsN3S4Fu.js → check-usage-dicJOWML.js} +4 -4
- package/dist/{check-usage-BsN3S4Fu.js.map → check-usage-dicJOWML.js.map} +1 -1
- package/dist/{claude-C3LWG_3H.js → claude-CyiXTFxe.js} +33 -30
- package/dist/claude-CyiXTFxe.js.map +1 -0
- package/dist/{codex-DdhBGec4.js → codex-BV4Tvt0q.js} +5 -5
- package/dist/{codex-DdhBGec4.js.map → codex-BV4Tvt0q.js.map} +1 -1
- package/dist/{debug-BXRPyeqO.js → debug-2PgImRnr.js} +2 -2
- package/dist/{debug-BXRPyeqO.js.map → debug-2PgImRnr.js.map} +1 -1
- package/dist/engine-Cv4SNuzn.js +2 -0
- package/dist/{fast-profile-contract-OuZQaGif.js → fast-profile-contract-BWCJy6iL.js} +6 -4
- package/dist/fast-profile-contract-BWCJy6iL.js.map +1 -0
- package/dist/{gate-discovery-C6jB3bBG.js → gate-discovery-CLV0ft3G.js} +5 -5
- package/dist/{gate-discovery-C6jB3bBG.js.map → gate-discovery-CLV0ft3G.js.map} +1 -1
- package/dist/{get-copilot-usage-ByjQqhdi.js → get-copilot-usage-Cjy8TiFf.js} +2 -2
- package/dist/{get-copilot-usage-ByjQqhdi.js.map → get-copilot-usage-Cjy8TiFf.js.map} +1 -1
- package/dist/hooks.mjs +28 -12
- package/dist/hooks.sha256 +1 -1
- package/dist/{internal-artifact-open-DRSxShAW.js → internal-artifact-open-CxeIfsW_.js} +2 -2
- package/dist/{internal-artifact-open-DRSxShAW.js.map → internal-artifact-open-CxeIfsW_.js.map} +1 -1
- package/dist/{internal-fast-dispatch-guard-ByzwIsxA.js → internal-fast-dispatch-guard-CrsZI0pO.js} +1 -1
- package/dist/{internal-fast-dispatch-guard-BaUB-F8o.js → internal-fast-dispatch-guard-kzSKB0yV.js} +3 -3
- package/dist/{internal-fast-dispatch-guard-BaUB-F8o.js.map → internal-fast-dispatch-guard-kzSKB0yV.js.map} +1 -1
- package/dist/{internal-first-mate-guard-Bcb5JwiO.js → internal-first-mate-guard-BuweUgjb.js} +3 -3
- package/dist/{internal-first-mate-guard-Bcb5JwiO.js.map → internal-first-mate-guard-BuweUgjb.js.map} +1 -1
- package/dist/{internal-first-mate-guard-Brh_uois.js → internal-first-mate-guard-Nf1H0mCK.js} +1 -1
- package/dist/{internal-max-dispatch-guard-Sb6ACr_0.js → internal-max-dispatch-guard-DF-f8VwE.js} +1 -1
- package/dist/{internal-max-dispatch-guard-CXRB-4a3.js → internal-max-dispatch-guard-lS4usOV5.js} +12 -5
- package/dist/internal-max-dispatch-guard-lS4usOV5.js.map +1 -0
- package/dist/{internal-plan-review-DVY-iV2x.js → internal-plan-review-DLduDz41.js} +3 -3
- package/dist/{internal-plan-review-DVY-iV2x.js.map → internal-plan-review-DLduDz41.js.map} +1 -1
- package/dist/{internal-prompt-submit-BTqRn4FW.js → internal-prompt-submit-DG94f8E-.js} +4 -4
- package/dist/{internal-prompt-submit-BTqRn4FW.js.map → internal-prompt-submit-DG94f8E-.js.map} +1 -1
- package/dist/{internal-session-bind-YfH656lt.js → internal-session-bind-D6Hpl-84.js} +2 -2
- package/dist/{internal-session-bind-YfH656lt.js.map → internal-session-bind-D6Hpl-84.js.map} +1 -1
- package/dist/{internal-stop-hook-EPAu3tkg.js → internal-stop-hook-B8Z1k6xC.js} +5 -5
- package/dist/{internal-stop-hook-EPAu3tkg.js.map → internal-stop-hook-B8Z1k6xC.js.map} +1 -1
- package/dist/{internal-stop-review-B8MmEyQ1.js → internal-stop-review-DlKzZgFm.js} +2 -2
- package/dist/{internal-stop-review-B8MmEyQ1.js.map → internal-stop-review-DlKzZgFm.js.map} +1 -1
- package/dist/{internal-worker-guard-Dr6W83TC.js → internal-worker-guard-D7sfByyo.js} +2 -2
- package/dist/{internal-worker-guard-Dr6W83TC.js.map → internal-worker-guard-D7sfByyo.js.map} +1 -1
- package/dist/{internal-workspace-header-Cep0WSIb.js → internal-workspace-header-CU5OjLCB.js} +2 -2
- package/dist/{internal-workspace-header-Cep0WSIb.js.map → internal-workspace-header-CU5OjLCB.js.map} +1 -1
- package/dist/lifecycle-B0USSJvd.js +2 -0
- package/dist/{lifecycle-DgsJ_8Am.js → lifecycle-BCnarXJV.js} +2 -2
- package/dist/{lifecycle-DgsJ_8Am.js.map → lifecycle-BCnarXJV.js.map} +1 -1
- package/dist/lifecycle-CQFjaQc0.js +2 -0
- package/dist/{lifecycle-CmPJ6e_6.js → lifecycle-CtRA3Y7p.js} +2 -2
- package/dist/{lifecycle-CmPJ6e_6.js.map → lifecycle-CtRA3Y7p.js.map} +1 -1
- package/dist/main.js +19 -19
- package/dist/max-profile-contract-v2Q6eN5w.js +355 -0
- package/dist/max-profile-contract-v2Q6eN5w.js.map +1 -0
- package/dist/{mcp-workspace-header-NzFD0Wgd.js → mcp-workspace-header-B60WSR1Y.js} +2 -2
- package/dist/{mcp-workspace-header-NzFD0Wgd.js.map → mcp-workspace-header-B60WSR1Y.js.map} +1 -1
- package/dist/{models-1KwEUgzd.js → models-COuMOfM6.js} +3 -3
- package/dist/{models-1KwEUgzd.js.map → models-COuMOfM6.js.map} +1 -1
- package/dist/{orchestration-CYyp9w-t.js → orchestration-DjLA-lxB.js} +2 -2
- package/dist/{orchestration-CYyp9w-t.js.map → orchestration-DjLA-lxB.js.map} +1 -1
- package/dist/{paths-IcZW8gZx.js → paths-De1Qh9Q9.js} +235 -4
- package/dist/paths-De1Qh9Q9.js.map +1 -0
- package/dist/paths-DyX6SEnd.js +2 -0
- package/dist/{peer-mcp-personas-C9fDtu69.js → peer-mcp-personas-DAd076hn.js} +112 -352
- package/dist/peer-mcp-personas-DAd076hn.js.map +1 -0
- package/dist/{plan-review-hook-szSNt6uU.js → plan-review-hook-vL6BYmA8.js} +3 -3
- package/dist/{plan-review-hook-szSNt6uU.js.map → plan-review-hook-vL6BYmA8.js.map} +1 -1
- package/dist/{prompt-submit-hook-ZUxo_jEl.js → prompt-submit-hook-DfqufWC1.js} +3 -3
- package/dist/{prompt-submit-hook-ZUxo_jEl.js.map → prompt-submit-hook-DfqufWC1.js.map} +1 -1
- package/dist/{provision-Bn4FOzR4.js → provision-Xcr-fJii.js} +4 -4
- package/dist/{provision-Bn4FOzR4.js.map → provision-Xcr-fJii.js.map} +1 -1
- package/dist/{self-invocation-BJWbA8ka.js → self-invocation-B84JCHps.js} +2 -2
- package/dist/{self-invocation-BJWbA8ka.js.map → self-invocation-B84JCHps.js.map} +1 -1
- package/dist/{serve-7q23O8hl.js → serve-8tqMuzVi.js} +12 -12
- package/dist/{serve-7q23O8hl.js.map → serve-8tqMuzVi.js.map} +1 -1
- package/dist/{server-setup-BP_JM0qb.js → server-setup-C7jSESEI.js} +301 -11
- package/dist/server-setup-C7jSESEI.js.map +1 -0
- package/dist/{start-wa7_DyyG.js → start-CaTinp_s.js} +3 -3
- package/dist/{start-wa7_DyyG.js.map → start-CaTinp_s.js.map} +1 -1
- package/dist/{stop-gate-hook-CJOk28zd.js → stop-gate-hook-BEU8qQrC.js} +3 -3
- package/dist/{stop-gate-hook-CJOk28zd.js.map → stop-gate-hook-BEU8qQrC.js.map} +1 -1
- package/dist/{stop-gate-policy-zhzRIj6G.js → stop-gate-policy-qzqXfRxE.js} +2 -2
- package/dist/{stop-gate-policy-zhzRIj6G.js.map → stop-gate-policy-qzqXfRxE.js.map} +1 -1
- package/dist/{token-CbuFyx-B.js → token-CCg-Ov5q.js} +2 -2
- package/dist/{token-CbuFyx-B.js.map → token-CCg-Ov5q.js.map} +1 -1
- package/dist/{worker-dispatch-DgP5yT26.js → worker-dispatch-CH-NN5FW.js} +2 -2
- package/dist/{worker-dispatch-DgP5yT26.js.map → worker-dispatch-CH-NN5FW.js.map} +1 -1
- package/package.json +1 -1
- package/dist/attribution-settings-Chqw6VeK.js.map +0 -1
- package/dist/claude-C3LWG_3H.js.map +0 -1
- package/dist/engine-C7DkYq2g.js +0 -2
- package/dist/fast-profile-contract-OuZQaGif.js.map +0 -1
- package/dist/internal-max-dispatch-guard-CXRB-4a3.js.map +0 -1
- package/dist/lifecycle-4TZrJuAa.js +0 -2
- package/dist/lifecycle-O91Vzzb-.js +0 -2
- package/dist/max-profile-contract-CiD5wyET.js +0 -276
- package/dist/max-profile-contract-CiD5wyET.js.map +0 -1
- package/dist/paths-DDAiPVto.js +0 -2
- package/dist/paths-IcZW8gZx.js.map +0 -1
- package/dist/peer-mcp-personas-C9fDtu69.js.map +0 -1
- package/dist/server-setup-BP_JM0qb.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve-7q23O8hl.js","names":["fs","fsSync","fs"],"sources":["../src/lib/serve/cloudcli.ts","../src/lib/serve/devtunnel.ts","../src/lib/serve/enhancements.ts","../src/lib/serve/provider-facade.ts","../src/lib/serve/reverse-proxy.ts","../src/serve.ts"],"sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\"\nimport { randomBytes } from \"node:crypto\"\nimport fs from \"node:fs/promises\"\nimport fsSync from \"node:fs\"\nimport http from \"node:http\"\nimport path from \"node:path\"\n\nimport consola from \"consola\"\n\nimport { runCommandCapture, resolveExecutable } from \"../exec\"\nimport { PATHS } from \"../paths\"\nimport { buildEnv } from \"../worker-agent/bash\"\n\nexport const CLOUDCLI_PACKAGE = \"@cloudcli-ai/cloudcli\"\n/** Pinned, tested known-good version. Never `@latest` (see docs/serve-control-plane.md). */\nexport const CLOUDCLI_PINNED_VERSION = \"1.36.1\"\n\n/**\n * Build the env handed to the CloudCLI child. Starts from `buildEnv()` — the\n * vetted secret-stripping ALLOWLIST used for worker bash (drops GITHUB_TOKEN,\n * GH_ROUTER_*, ANTHROPIC_AUTH_TOKEN, OPENAI_API_KEY, COPILOT_TOKEN by omission)\n * — then layers the non-secret Anthropic/Claude vars. The spawned claude still\n * authenticates because that rides the synthetic `.credentials.json` FILE in\n * CLAUDE_CONFIG_DIR, not an env token. Load-bearing: CloudCLI exposes a browser\n * terminal that can read its own env, so no secret may live there.\n */\nexport function composeCloudCliChildEnv(\n anthropicVars: Record<string, string>,\n): NodeJS.ProcessEnv {\n return { ...buildEnv(), ...anthropicVars }\n}\n\nconst SERVER_ENTRY_REL = path.join(\n \"dist-server\",\n \"server\",\n \"index.js\",\n)\n\nexport interface ResolvedCloudCli {\n /** Absolute path to the CloudCLI server entry (dist-server/server/index.js). */\n serverEntry: string\n version: string | null\n}\n\n/** Where the package's server entry lives under a given install root. */\nfunction serverEntryUnderRoot(root: string): string {\n return path.join(root, \"node_modules\", CLOUDCLI_PACKAGE, SERVER_ENTRY_REL)\n}\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await fs.access(p)\n return true\n } catch {\n return false\n }\n}\n\nasync function readVersion(pkgRoot: string): Promise<string | null> {\n try {\n const raw = await fs.readFile(\n path.join(pkgRoot, \"node_modules\", CLOUDCLI_PACKAGE, \"package.json\"),\n \"utf8\",\n )\n return (JSON.parse(raw) as { version?: string }).version ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Resolve the CloudCLI server entry, in order:\n * 1. an explicit `--cloudcli-path` (a package dir OR an install root),\n * 2. an existing pinned install in the router-owned dir,\n * 3. (unless `noInstall`) install the pinned version into the router-owned dir.\n * Never installs globally, never `@latest`.\n */\nexport async function resolveCloudCli(opts: {\n cliPath?: string\n noInstall: boolean\n version?: string\n}): Promise<ResolvedCloudCli> {\n const version = opts.version ?? CLOUDCLI_PINNED_VERSION\n\n // 1. explicit path — accept either a package dir or an install root.\n if (opts.cliPath) {\n const direct = path.join(opts.cliPath, SERVER_ENTRY_REL)\n const underRoot = serverEntryUnderRoot(opts.cliPath)\n if (await exists(direct)) return { serverEntry: direct, version: null }\n if (await exists(underRoot))\n return { serverEntry: underRoot, version: await readVersion(opts.cliPath) }\n throw new Error(\n `--cloudcli-path \"${opts.cliPath}\" does not contain a CloudCLI install (looked for ${SERVER_ENTRY_REL}).`,\n )\n }\n\n // 2. existing router-owned install.\n const root = PATHS.CLOUDCLI_HOME\n const entry = serverEntryUnderRoot(root)\n if (await exists(entry)) {\n return { serverEntry: entry, version: await readVersion(root) }\n }\n\n // 3. install the pinned version locally into the router-owned dir.\n if (opts.noInstall) {\n throw new Error(\n `CloudCLI is not installed and --no-install was given. Install it with:\\n npm i --prefix \"${root}\" ${CLOUDCLI_PACKAGE}@${version}`,\n )\n }\n await installCloudCli(root, version)\n if (!(await exists(entry))) {\n throw new Error(\n `CloudCLI install completed but the server entry is missing (${entry}).`,\n )\n }\n return { serverEntry: entry, version: await readVersion(root) }\n}\n\nasync function installCloudCli(root: string, version: string): Promise<void> {\n const npm = resolveExecutable(\"npm\")\n if (!npm) {\n throw new Error(\n \"npm not found on PATH — cannot install CloudCLI. Install Node.js/npm, or pass --cloudcli-path to an existing install.\",\n )\n }\n await fs.mkdir(root, { recursive: true, mode: 0o700 })\n // A local package.json so `npm i` installs into <root>/node_modules\n // instead of walking up to a parent project.\n const pkgJson = path.join(root, \"package.json\")\n if (!(await exists(pkgJson))) {\n await fs.writeFile(\n pkgJson,\n JSON.stringify({ name: \"gh-router-cloudcli-host\", private: true }, null, 2),\n )\n }\n consola.info(\n `Installing ${CLOUDCLI_PACKAGE}@${version} (one-time, ~a minute; builds native deps)…`,\n )\n const { code, stderr } = await runCommandCapture(\n [\n npm,\n \"install\",\n `${CLOUDCLI_PACKAGE}@${version}`,\n \"--prefix\",\n root,\n \"--no-audit\",\n \"--no-fund\",\n \"--loglevel=error\",\n ],\n { timeoutMs: 10 * 60_000 },\n )\n if (code !== 0) {\n throw new Error(\n `CloudCLI install failed (npm exit ${code}). This often means no network, a corporate proxy, or missing Windows build tools for native deps.\\nInstall manually then re-run with --cloudcli-path:\\n npm i --prefix \"${root}\" ${CLOUDCLI_PACKAGE}@${version}\\n${stderr.slice(0, 400)}`,\n )\n }\n}\n\nexport interface CloudCliProcess {\n child: ChildProcess\n port: number\n databasePath: string\n}\n\n/** Spawn CloudCLI on a loopback port with the given (already-filtered) env. */\nexport function spawnCloudCli(opts: {\n serverEntry: string\n port: number\n env: NodeJS.ProcessEnv\n}): CloudCliProcess {\n const databasePath = path.join(PATHS.CLOUDCLI_HOME, \"auth.db\")\n const node = resolveExecutable(\"node\") ?? process.execPath\n // Diagnostic opt-in: route CloudCLI's stdout+stderr to a log file so its\n // internal errors (SDK/DB/session-create) are inspectable. Default stays\n // \"ignore\" (CloudCLI is noisy and its logs aren't user-facing).\n const logPath = process.env.GH_ROUTER_CLOUDCLI_LOG\n let stdio: \"ignore\" | [\"ignore\", number, number] = \"ignore\"\n if (logPath) {\n try {\n const fd = fsSync.openSync(logPath, \"a\")\n stdio = [\"ignore\", fd, fd]\n } catch {\n /* unwritable log path — fall back to ignore */\n }\n }\n const child = spawn(node, [opts.serverEntry], {\n env: {\n ...opts.env,\n SERVER_PORT: String(opts.port),\n HOST: \"127.0.0.1\", // loopback only — never 0.0.0.0\n DATABASE_PATH: databasePath, // router-owned, isolated from a user's standalone CloudCLI\n // Do NOT set VITE_IS_PLATFORM: it disarms server auth and still shows a\n // login wall in the OSS bundle. Keep JWT auth on; auto-login is done by\n // the reverse proxy injecting the token.\n VITE_IS_PLATFORM: \"\",\n },\n stdio,\n windowsHide: true,\n })\n return { child, port: opts.port, databasePath }\n}\n\n// ---- health + JWT --------------------------------------------------------\n\nfunction req(\n port: number,\n method: \"GET\" | \"POST\",\n urlPath: string,\n body?: unknown,\n): Promise<{ status: number; body: string }> {\n return new Promise((resolve, reject) => {\n const data = body === undefined ? undefined : JSON.stringify(body)\n const r = http.request(\n {\n host: \"127.0.0.1\",\n port,\n path: urlPath,\n method,\n headers: data\n ? { \"content-type\": \"application/json\", \"content-length\": Buffer.byteLength(data) }\n : {},\n },\n (res) => {\n let b = \"\"\n res.on(\"data\", (d) => (b += d))\n res.on(\"end\", () => resolve({ status: res.statusCode ?? 0, body: b }))\n },\n )\n r.on(\"error\", reject)\n if (data) r.write(data)\n r.end()\n })\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\n/** Poll /api/auth/status until the server answers. Returns `needsSetup`. */\nexport async function waitForCloudCliReady(\n port: number,\n timeoutMs = 60_000,\n): Promise<{ needsSetup: boolean }> {\n const deadline = Date.now() + timeoutMs\n while (Date.now() < deadline) {\n try {\n const r = await req(port, \"GET\", \"/api/auth/status\")\n if (r.status === 200) {\n const parsed = JSON.parse(r.body) as { needsSetup?: boolean }\n return { needsSetup: parsed.needsSetup === true }\n }\n } catch {\n // not up yet\n }\n await sleep(1000)\n }\n throw new Error(`CloudCLI did not become ready within ${timeoutMs}ms`)\n}\n\nconst secretFile = () => path.join(PATHS.CLOUDCLI_HOME, \".serve-secret.json\")\n\nasync function loadPassword(username: string): Promise<string | null> {\n try {\n const raw = await fs.readFile(secretFile(), \"utf8\")\n const parsed = JSON.parse(raw) as { username?: string; password?: string }\n if (parsed.username === username && parsed.password) return parsed.password\n } catch {\n // no file yet\n }\n return null\n}\n\nasync function persistPassword(username: string, password: string): Promise<void> {\n // mode 0o600 + a 0o700 parent dir. On Windows the mode bits are ignored, but\n // the dir lives under the per-user home, which is user-ACL'd by default; the\n // password is a low-value local login for a loopback-only, Origin-gated app.\n await fs.mkdir(PATHS.CLOUDCLI_HOME, { recursive: true, mode: 0o700 })\n await fs.writeFile(\n secretFile(),\n JSON.stringify({ username, password }),\n { mode: 0o600 },\n )\n}\n\n/**\n * Ensure a CloudCLI user exists and return a valid JWT. On a fresh (isolated)\n * DB we register (persisting the password only AFTER success); on an existing\n * DB we log in with the persisted password.\n */\nexport async function mintJwt(\n port: number,\n username: string,\n needsSetup: boolean,\n): Promise<string> {\n const existing = await loadPassword(username)\n if (needsSetup) {\n const password = existing ?? randomBytes(24).toString(\"base64url\")\n const r = await req(port, \"POST\", \"/api/auth/register\", { username, password })\n if (r.status === 200) {\n const token = (JSON.parse(r.body) as { token?: string }).token\n if (token) {\n await persistPassword(username, password)\n return token\n }\n }\n // Fall through to login (e.g. a race created the user first).\n }\n const password = existing\n if (!password) {\n throw new Error(\n `CloudCLI already has a user but no stored password for \"${username}\". Delete ${path.join(PATHS.CLOUDCLI_HOME, \"auth.db\")} and retry.`,\n )\n }\n const r = await req(port, \"POST\", \"/api/auth/login\", { username, password })\n if (r.status === 200) {\n const token = (JSON.parse(r.body) as { token?: string }).token\n if (token) return token\n }\n throw new Error(\n `Could not obtain a CloudCLI session token (status ${r.status}). If a stale isolated DB exists, delete ${path.join(PATHS.CLOUDCLI_HOME, \"auth.db\")} and retry.`,\n )\n}\n","import { spawn } from \"node:child_process\"\nimport { createHash } from \"node:crypto\"\nimport os from \"node:os\"\n\nimport { killChildProcessTree, resolveExecutable, runCommandCapture } from \"../exec\"\n\n// Label stamped on every tunnel `serve --tunnel` creates, so our tunnels are\n// identifiable and sweepable (`devtunnel list -l github-router-serve`). Before\n// this, serve minted an anonymous, unlabeled tunnel per launch that was never\n// deleted — they accumulated server-side against the hard\n// `TunnelsPerUserPerCluster` (10) cap until new tunnels were denied.\nexport const SERVE_TUNNEL_LABEL = \"github-router-serve\"\n\n// A per-machine label so reuse/sweep scopes to THIS host and never touches a\n// tunnel another machine (sharing the same dev-tunnels account) owns. Hashed so\n// an arbitrary hostname maps to a label-charset-safe token.\nexport function serveTunnelMachineLabel(hostname: string = os.hostname()): string {\n return `ghr-machine-${createHash(\"sha256\").update(hostname).digest(\"hex\").slice(0, 12)}`\n}\n\n/**\n * Stable, deterministic tunnel ID for THIS machine, so `serve --tunnel` REUSES\n * one tunnel across launches → a stable, bookmarkable public URL. Derived from a\n * hash of the hostname (valid tunnel-id charset: lowercase alnum + hyphens,\n * bounded length). NOTE: the hosting URL's subdomain is a devtunnels-assigned\n * token, NOT this id — the id only anchors find/reuse; the URL is stable because\n * the same tunnel is re-hosted, not because the id appears in it.\n */\nexport function serveTunnelId(hostname: string = os.hostname()): string {\n return `ghr-serve-${createHash(\"sha256\").update(hostname).digest(\"hex\").slice(0, 12)}`\n}\n\n/**\n * Reconcile a reused tunnel's ports to exactly `[desired]`: which existing ports\n * to delete and whether the desired port must be created. Pure (unit-tested).\n * This is why reuse works across `--port` changes — `devtunnel host <id>\n * -p <newport>` fails (\"Batch update of ports is not supported\"), but per-port\n * `port delete` + `port create` does not.\n */\nexport function portsToReconcile(\n currentPorts: number[],\n desired: number,\n): { toDelete: number[]; toCreate: number | null } {\n return {\n toDelete: currentPorts.filter((p) => p !== desired),\n toCreate: currentPorts.includes(desired) ? null : desired,\n }\n}\n\nexport interface TunnelInfo {\n tunnelId: string\n labels?: string[]\n hostConnections?: number\n}\n\n/**\n * The IDs of our own idle tunnels for this machine to sweep — EXCLUDING the\n * stable reused tunnel (`exceptId`), which we keep and re-host. Pure\n * (unit-tested). A tunnel with a live host connection (a concurrent serve\n * instance) is never swept. Cleans up pre-migration random-named tunnels + any\n * crash-orphaned duplicates while preserving the one stable tunnel.\n */\nexport function serveTunnelIdsToSweep(\n tunnels: TunnelInfo[],\n machineLabel: string,\n exceptId?: string,\n): string[] {\n return tunnels\n .filter((t) => (t.labels ?? []).includes(machineLabel) && !t.hostConnections)\n .map((t) => t.tunnelId)\n .filter((id) => !exceptId || id.split(\".\")[0] !== exceptId)\n}\n\n/** List the tunnels serve owns (label-filtered). Best-effort: [] on any error. */\nasync function listServeTunnels(cli: string): Promise<TunnelInfo[]> {\n try {\n const { stdout, code } = await runCommandCapture(\n [cli, \"list\", \"-l\", SERVE_TUNNEL_LABEL, \"-j\"],\n { timeoutMs: 15_000 },\n )\n if (code !== 0) return []\n const parsed = JSON.parse(stdout) as { tunnels?: TunnelInfo[] }\n return Array.isArray(parsed?.tunnels) ? parsed.tunnels : []\n } catch {\n return []\n }\n}\n\n/** Best-effort delete of a single tunnel by full id (e.g. `fancy-fog-x.inc1`). */\nasync function deleteTunnel(cli: string, tunnelId: string): Promise<void> {\n try {\n await runCommandCapture([cli, \"delete\", tunnelId, \"-f\"], { timeoutMs: 15_000 })\n } catch {\n /* best-effort — a stale delete failing must not block hosting */\n }\n}\n\n/** Current port numbers configured on a tunnel. Best-effort: [] on any error. */\nasync function getTunnelPorts(cli: string, tunnelId: string): Promise<number[]> {\n try {\n const { stdout, code } = await runCommandCapture(\n [cli, \"port\", \"list\", tunnelId, \"-j\"],\n { timeoutMs: 15_000 },\n )\n if (code !== 0) return []\n const parsed = JSON.parse(stdout) as { ports?: Array<{ portNumber?: number }> }\n return (parsed?.ports ?? [])\n .map((p) => p.portNumber)\n .filter((n): n is number => typeof n === \"number\")\n } catch {\n return []\n }\n}\n\n/**\n * Ensure the stable per-machine tunnel exists (create it labeled if absent).\n * Returns true when the stable tunnel is present + owned by us (safe to reuse),\n * false to fall back to the anonymous-labeled host path. `existing` is the\n * pre-fetched label-scoped list (so we don't re-list).\n */\nasync function ensureStableTunnel(\n cli: string,\n stableId: string,\n machineLabel: string,\n existing: TunnelInfo[],\n): Promise<boolean> {\n const present = existing.some((t) => t.tunnelId.split(\".\")[0] === stableId)\n if (present) return true\n try {\n // `create` errors (\"Conflict\") if the id is already taken — but we only get\n // here when our label-scoped list did NOT contain it, so a conflict means a\n // DIFFERENT owner holds the id: fall back rather than reuse a foreign tunnel.\n const { code } = await runCommandCapture(\n [cli, \"create\", stableId, \"-l\", SERVE_TUNNEL_LABEL, \"-l\", machineLabel,\n \"-d\", \"github-router-serve-control-plane\"],\n { timeoutMs: 20_000 },\n )\n return code === 0\n } catch {\n return false\n }\n}\n\n/** Reconcile the tunnel's ports to exactly `[port]` (delete stale, create missing). */\nasync function reconcileTunnelPorts(cli: string, tunnelId: string, port: number): Promise<void> {\n const { toDelete, toCreate } = portsToReconcile(await getTunnelPorts(cli, tunnelId), port)\n for (const p of toDelete) {\n await runCommandCapture([cli, \"port\", \"delete\", tunnelId, \"-p\", String(p)], { timeoutMs: 15_000 }).catch(() => {})\n }\n if (toCreate != null) {\n await runCommandCapture([cli, \"port\", \"create\", tunnelId, \"-p\", String(toCreate)], { timeoutMs: 15_000 }).catch(() => {})\n }\n}\n\n\n// Matches the browser URL devtunnel prints, e.g.\n// Hosting port 5454 at https://l3rs99qw-5454.usw2.devtunnels.ms/\nconst DEVTUNNEL_URL_RE =\n /https:\\/\\/[a-z0-9][a-z0-9-]*(?:\\.[a-z0-9-]+)*\\.devtunnels\\.ms\\/?/i\n\nexport type DevtunnelFailureKind =\n | \"not-installed\"\n | \"not-logged-in\"\n | \"timeout\"\n | \"exited\"\n | \"spawn\"\n\nexport class DevtunnelError extends Error {\n readonly kind: DevtunnelFailureKind\n constructor(kind: DevtunnelFailureKind, message: string) {\n super(message)\n this.name = \"DevtunnelError\"\n this.kind = kind\n }\n}\n\nexport interface DevtunnelHandle {\n url: string\n stop: () => void\n}\n\n/** Absolute path to the `devtunnel` CLI, or null if not on PATH. */\nexport function resolveDevtunnelCli(): string | null {\n return resolveExecutable(\"devtunnel\")\n}\n\n/** Best-effort login check via `devtunnel user show`. */\nexport async function isDevtunnelLoggedIn(cli: string): Promise<boolean> {\n try {\n const { stdout, stderr, code } = await runCommandCapture(\n [cli, \"user\", \"show\"],\n { timeoutMs: 15_000 },\n )\n const out = `${stdout}\\n${stderr}`\n if (/not logged in/i.test(out)) return false\n if (/logged in as/i.test(out)) return true\n return code === 0\n } catch {\n return false\n }\n}\n\n/** Extract the dev-tunnel browser URL from CLI output. Exported for tests. */\nexport function parseDevtunnelUrl(text: string): string | null {\n const m = text.match(DEVTUNNEL_URL_RE)\n return m ? m[0].replace(/\\/+$/, \"\") : null\n}\n\n/**\n * Spawn `devtunnel <args>` and resolve once it prints its public URL. Shared by\n * the stable-reuse and anonymous-labeled host paths. Rejects (DevtunnelError) on\n * timeout, early exit, an auth error in the output, or a spawn failure.\n */\nfunction hostTunnelProcess(cli: string, args: string[], timeoutMs: number): Promise<DevtunnelHandle> {\n const child = spawn(cli, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"], windowsHide: true })\n return new Promise<DevtunnelHandle>((resolve, reject) => {\n let settled = false\n const stop = () => {\n // Tree-kill: the devtunnel process spawns a helper and a plain\n // child.kill() leaves the tunnel up (verified). taskkill /T /F on\n // Windows, process-group SIGTERM on POSIX.\n try {\n killChildProcessTree(child, { detachedGroup: process.platform !== \"win32\" })\n } catch {\n try {\n child.kill()\n } catch {\n /* already gone */\n }\n }\n }\n const finish = (fn: () => void) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n fn()\n }\n const timer = setTimeout(\n () =>\n finish(() => {\n stop()\n reject(new DevtunnelError(\"timeout\", \"dev tunnel did not report a URL in time\"))\n }),\n timeoutMs,\n )\n const onData = (buf: Buffer) => {\n const text = String(buf)\n const url = parseDevtunnelUrl(text)\n if (url) {\n finish(() => resolve({ url, stop }))\n return\n }\n if (/not logged in|login (required|expired)|unauthorized/i.test(text)) {\n finish(() => {\n stop()\n reject(new DevtunnelError(\"not-logged-in\", \"dev tunnel authentication failed; run `devtunnel user login`\"))\n })\n }\n }\n child.stdout?.on(\"data\", onData)\n child.stderr?.on(\"data\", onData)\n child.once(\"error\", (err) => finish(() => reject(new DevtunnelError(\"spawn\", err.message))))\n child.once(\"exit\", (code) =>\n finish(() =>\n reject(new DevtunnelError(\"exited\", `devtunnel exited (code ${code}) before printing a URL`)),\n ),\n )\n })\n}\n\n/**\n * Host an AUTHENTICATED (never anonymous) dev tunnel forwarding `port`, resolving\n * once its public URL is printed. NEVER `--allow-anonymous` (owner-only access).\n *\n * REUSES a stable per-machine tunnel (`serveTunnelId`) across launches → a\n * STABLE, bookmarkable public URL. Each launch: ensure the stable tunnel exists\n * (create it labeled if absent), reconcile its ports to exactly `[port]` via\n * per-port delete/create (`host <id> -p <newport>` can't change ports — it 400s\n * \"Batch update of ports is not supported\"), sweep our OTHER idle labeled tunnels\n * (pre-migration random-named ones / crash orphans), then `host <id>`. The URL's\n * subdomain is a devtunnels-assigned token (not the id), but it's stable because\n * the SAME tunnel is re-hosted every launch.\n *\n * If any stable-path step fails (id already taken by another owner, CLI hiccup),\n * falls back to hosting a fresh anonymous-labeled tunnel (the prior behavior) so\n * `--tunnel` always works — just without a stable URL that launch.\n */\nexport async function startDevtunnel(\n port: number,\n opts: { timeoutMs?: number } = {},\n): Promise<DevtunnelHandle> {\n const cli = resolveDevtunnelCli()\n if (!cli) {\n throw new DevtunnelError(\n \"not-installed\",\n \"The `devtunnel` CLI is not installed. Install it (winget install Microsoft.devtunnel / brew install --cask devtunnel) then run `devtunnel user login`.\",\n )\n }\n if (!(await isDevtunnelLoggedIn(cli))) {\n throw new DevtunnelError(\n \"not-logged-in\",\n \"Not signed in to dev tunnels. Run `devtunnel user login` (or `devtunnel user login -g` for GitHub) first.\",\n )\n }\n\n const timeoutMs = opts.timeoutMs ?? 40_000\n const machineLabel = serveTunnelMachineLabel()\n const stableId = serveTunnelId()\n\n // Stable-reuse path (best-effort; any failure falls through to anonymous host).\n try {\n const owned = await listServeTunnels(cli)\n if (await ensureStableTunnel(cli, stableId, machineLabel, owned)) {\n await reconcileTunnelPorts(cli, stableId, port)\n // Sweep our OTHER idle labeled tunnels but KEEP the stable one.\n for (const id of serveTunnelIdsToSweep(owned, machineLabel, stableId)) {\n await deleteTunnel(cli, id)\n }\n return await hostTunnelProcess(cli, [\"host\", stableId], timeoutMs)\n }\n } catch {\n /* fall through to the anonymous-labeled host path */\n }\n\n // Fallback: sweep our idle labeled tunnels, then host a fresh anonymous one.\n // Labels are SEPARATE `-l` flags — the service rejects a label containing a\n // space (`must match '[\\w-=]{1,50}'`), so they can't be joined into one arg.\n try {\n for (const id of serveTunnelIdsToSweep(await listServeTunnels(cli), machineLabel)) {\n await deleteTunnel(cli, id)\n }\n } catch {\n /* best-effort sweep — never blocks hosting */\n }\n return await hostTunnelProcess(\n cli,\n [\"host\", \"-p\", String(port), \"-l\", SERVE_TUNNEL_LABEL, \"-l\", machineLabel, \"-d\", \"github-router-serve-control-plane\"],\n timeoutMs,\n )\n}\n","import path from \"node:path\"\n\nimport consola from \"consola\"\n\nimport { injectAttributionSuppressionIntoSettingsFile } from \"../attribution-settings\"\nimport {\n BUILTIN_SUBAGENT_DEFINITIONS,\n injectPeerMcpIntoMirror,\n resolveGroupKeysFromMirror,\n workersKeyOf,\n writePeerMcpRuntimeFiles,\n} from \"../codex-mcp-config\"\nimport {\n appendPeerAwarenessToMirroredClaudeMd,\n appendToolbeltAwarenessToMirroredClaudeMd,\n buildOperatingDefaultsDirective,\n type NativeAgentAvailability,\n prependOperatingDefaultsToMirroredClaudeMd,\n prependStyleDirectiveToMirroredClaudeMd,\n} from \"../claude-md-injection\"\nimport { type SelfInvocation } from \"../hook-launcher/self-invocation\"\nimport { INJECTED_SKILLS, writeInjectedSkill } from \"../injected-skills\"\nimport { injectModelPickerSettingsFile } from \"../model-picker-settings\"\nimport {\n configureServeDefaultPermissionMode,\n injectAllowRules,\n planModeAllowRules,\n sanitizeServeSettingsEnv,\n} from \"../mcp-permissions-settings\"\nimport {\n agentToolsEnabled,\n browseAgentEnabled,\n browserCompoundToolsEnabled,\n browserToolsEnabled,\n fleetToolsEnabled,\n geminiAvailable,\n nativeSubagentModel,\n brainstormModel,\n reviewerModel,\n reviewerFastModel,\n resolveGeminiReviewModel,\n scoutModel,\n scribeModel,\n implementerFastModel,\n generalPurposeFastModel,\n standInToolEnabled,\n workerToolsEnabled,\n} from \"../mcp-capabilities\"\nimport { buildPlanReviewHookCommand, planReviewEnabled } from \"../orchestration/plan-review-hook\"\nimport { buildPromptSubmitHookCommand } from \"../orchestration/prompt-submit-hook\"\nimport { injectStopHookIntoSettingsFile } from \"../orchestration/stop-gate-hook\"\nimport { PATHS } from \"../paths\"\nimport {\n buildPeerAwarenessSnippet,\n enumerateInjectedMcpToolNames,\n type McpGroup,\n} from \"../peer-mcp-personas\"\nimport { state } from \"../state\"\nimport { availableToolCommands, buildToolbeltAwareness, toolbeltEnabled } from \"../toolbelt\"\nimport {\n activeDispatchModes,\n buildWorkerGuardHookCommand,\n guardToolMatcher,\n} from \"../worker-dispatch\"\n\nexport interface ServeEnhancementsHandle {\n cleanup: () => Promise<void>\n nonce?: string\n /** Effective mirror-level modelPicker rows, including a preserved user picker.\n * Used to derive the launch-global compaction minimum before CloudCLI starts. */\n pickerModels?: string[]\n /** Exact `mcp__<key>__<tool>` names of every injected MCP tool — seeded into\n * the reverse-proxy's `localStorage['claude-settings'].allowedTools` so\n * CloudCLI's `canUseTool` auto-approves them in PLAN mode. */\n mcpToolNames?: string[]\n}\n\nexport interface ServeEnhancementOpts {\n /** Stable invocation baked into every persisted hook/helper command. */\n selfInvocation: SelfInvocation\n /** Route Codex personas through a local `codex mcp-server` (stdio) when the backend resolved to \"cli\". */\n codexCli?: boolean\n /** True when the control plane is reachable beyond loopback (a --tunnel or --public-url is active). Gates the highest-blast-radius capabilities OFF by default: the server-side browser MCP (a session-hijack / SSRF / cloud-metadata primitive when reachable remotely) and first-mate (mints a repo+workflow GitHub write token). */\n tunnelExposed?: boolean\n /** Operator opt-in to expose the browser MCP over the tunnel despite the risk. */\n browseOverTunnel?: boolean\n /** Operator opt-in to expose first-mate over the tunnel despite the risk. */\n agentsOverTunnel?: boolean\n /** Operator opt-in to expose the fleet MCP over the tunnel despite the risk\n * (fleet drives remote coding sessions with the operator's stored fleet\n * credentials — an equal-or-higher blast radius than the browser MCP). */\n fleetOverTunnel?: boolean\n}\n\nconst NOOP: ServeEnhancementsHandle = { cleanup: async () => {} }\n\nexport function resolveServeNativeAgentOptions() {\n const models = {\n nativeSubagentModel: nativeSubagentModel(),\n reviewerModel: reviewerModel(),\n reviewerFastModel: reviewerFastModel(),\n brainstormModel: brainstormModel(),\n scoutModel: scoutModel(),\n scribeModel: scribeModel(),\n implementerFastModel: implementerFastModel(),\n generalPurposeFastModel: generalPurposeFastModel(),\n }\n const nativeAvailability: NativeAgentAvailability = {\n scoutAvailable: models.scoutModel != null,\n implementerFastAvailable: models.implementerFastModel != null,\n reviewerFastAvailable: models.reviewerFastModel != null,\n generalPurposeFastAvailable: models.generalPurposeFastModel != null,\n }\n return { models, nativeAvailability }\n}\n\n/**\n * Wire the github-router enhancement layer into the router-owned\n * CLAUDE_CONFIG_DIR mirror so a CloudCLI-spawned Claude session gets the same\n * tools `github-router claude` provides:\n * - the scoped MCP servers (peers / search / orchestrate, plus workers /\n * decide / browser / fleet / first-mate when their gate passes) — the\n * SDK-spawned claude reads them from the mirror's `.claude.json`,\n * - the peer-critic / worker / implementer subagents (`.md` files written into\n * the mirror's `agents/`),\n * - the gh-* skills (research / orchestrate / floor-keeper / worker).\n *\n * Best-effort: on any failure Claude still works, just without the extras.\n * Must run AFTER `ensureClaudeConfigMirror()` and BEFORE CloudCLI is spawned.\n * Tab-specific bits (ai-or-die hooks, artifact review) are intentionally left\n * out — they don't apply to the serve control plane.\n */\nexport async function provisionServeEnhancements(\n serverUrl: string,\n opts: ServeEnhancementOpts,\n): Promise<ServeEnhancementsHandle> {\n try {\n const tunnelExposed = opts.tunnelExposed === true\n const browseAllowed = browserToolsEnabled() && (!tunnelExposed || opts.browseOverTunnel === true)\n const firstMateAllowed = agentToolsEnabled() && (!tunnelExposed || opts.agentsOverTunnel === true)\n const fleetAllowed = fleetToolsEnabled() && (!tunnelExposed || opts.fleetOverTunnel === true)\n\n const enabledGroups: McpGroup[] = [\"peers\", \"search\", \"orchestrate\"]\n if (workerToolsEnabled()) enabledGroups.push(\"workers\")\n if (standInToolEnabled()) enabledGroups.push(\"decide\")\n if (browseAllowed) enabledGroups.push(\"browser\")\n if (fleetAllowed) enabledGroups.push(\"fleet\")\n if (firstMateAllowed) enabledGroups.push(\"first-mate\")\n\n const gem = geminiAvailable()\n const { keys: groupKeys } = await resolveGroupKeysFromMirror(enabledGroups)\n // Resolved once and reused by the `.md` generation, the awareness snippet,\n // and the operating-defaults directive, so the three surfaces cannot\n // disagree about which conditionally-emitted natives exist.\n const { models: nativeModels, nativeAvailability } =\n resolveServeNativeAgentOptions()\n\n const runtime = await writePeerMcpRuntimeFiles(serverUrl, {\n codexCli: opts.codexCli === true,\n selfInvocation: opts.selfInvocation,\n geminiAvailable: gem,\n geminiModel: resolveGeminiReviewModel(),\n groupKeys,\n workerToolsAvailable: workerToolsEnabled(),\n browseAvailable: browseAllowed && browseAgentEnabled(),\n ...nativeModels,\n // Serve-only: register Claude Code's built-in Explore/Plan/general-purpose\n // subagents (the Agent SDK doesn't) so the model's habitual Agent() calls\n // resolve. Never passed by `github-router claude` (would shadow the CLI's\n // native built-ins).\n builtinSubagents: BUILTIN_SUBAGENT_DEFINITIONS,\n })\n // The proxy's /mcp handler authorizes tool calls against this per-launch\n // nonce (the same value baked into the mirrored mcpServers Authorization).\n state.peerMcpNonce = runtime.nonce\n\n const injected = await injectPeerMcpIntoMirror(serverUrl, {\n codexCli: opts.codexCli === true,\n selfInvocation: opts.selfInvocation,\n geminiAvailable: gem,\n groupKeys,\n nonce: runtime.nonce,\n })\n\n const peerSnippet = buildPeerAwarenessSnippet({\n codexCli: opts.codexCli === true,\n geminiAvailable: gem,\n geminiModel: resolveGeminiReviewModel(),\n workerToolsAvailable: workerToolsEnabled(),\n standInAvailable: standInToolEnabled(),\n browseAvailable: browseAllowed,\n compoundBrowseAvailable: browseAllowed && browserCompoundToolsEnabled(),\n powerBrowseAvailable: browseAllowed && state.powerBrowseEnabled,\n agentToolsAvailable: firstMateAllowed,\n ...nativeAvailability,\n groupKeys,\n })\n await appendPeerAwarenessToMirroredClaudeMd(peerSnippet).catch((err) =>\n consola.warn(`Peer-awareness CLAUDE.md append failed: ${String(err)}`),\n )\n await prependStyleDirectiveToMirroredClaudeMd().catch((err) =>\n consola.warn(`Style-directive CLAUDE.md prepend failed: ${String(err)}`),\n )\n await prependOperatingDefaultsToMirroredClaudeMd(\n buildOperatingDefaultsDirective(nativeAvailability),\n ).catch((err) =>\n consola.warn(`Operating-defaults CLAUDE.md prepend failed: ${String(err)}`),\n )\n if (toolbeltEnabled()) {\n const toolbeltLine = buildToolbeltAwareness(availableToolCommands())\n if (toolbeltLine) {\n await appendToolbeltAwarenessToMirroredClaudeMd(toolbeltLine).catch((err) =>\n consola.warn(`Toolbelt CLAUDE.md append failed: ${String(err)}`),\n )\n }\n }\n\n // gh-* skills, minus the operator/tab-specific ones (first-mate, artifact).\n let skillsWritten = 0\n for (const s of INJECTED_SKILLS) {\n if (s.name.startsWith(\"gh-first-mate\") || s.name === \"gh-artifact-review\") {\n continue\n }\n const r = await writeInjectedSkill(s.name, s.md).catch(() => ({\n written: false,\n }))\n if (r.written) skillsWritten++\n }\n\n const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n\n // CloudCLI reads the same per-launch mirror as the CLI launcher. Add the\n // Standard profile's live-catalog-gated rows through the supported picker\n // setting before CloudCLI starts. Preserve an existing user-curated picker;\n // explicit model selection remains available if this best-effort write fails.\n const picker = await injectModelPickerSettingsFile(settingsPath, \"standard\").catch((err) => {\n consola.warn(`Could not inject serve model-picker settings: ${String(err)}`)\n return undefined\n })\n if (picker?.reason === \"user-set\") {\n consola.info(\n \"Serve: preserving the user-configured modelPicker; router-curated rows were not merged.\",\n )\n }\n\n // Suppress AI attribution in the mirror's settings.json (no-op if the user\n // already configured it).\n await injectAttributionSuppressionIntoSettingsFile(settingsPath).catch(() => {})\n\n // Strip serve-inappropriate CLAUDE_CODE_* env keys the user may have set in\n // their real settings.json — notably CLAUDE_CODE_COORDINATOR_MODE, which\n // CloudCLI applies via settingSources and which strips the single serve chat\n // agent down to delegation-only tools (a direct Glob/Read/Bash then fails\n // \"not enabled in this context\"). UNCONDITIONAL: a working toolset is needed\n // even when GH_ROUTER_SERVE_NO_AUTO_APPROVE=1 keeps permission prompts on.\n const strip = await sanitizeServeSettingsEnv(settingsPath).catch((err) => {\n consola.warn(`Could not sanitize serve settings env: ${String(err)}`)\n return { removed: [] as string[] }\n })\n if (strip.removed.length) {\n consola.info(`Serve: removed ${strip.removed.join(\", \")} from the mirror settings (single-agent surface).`)\n }\n\n // Serve's permission model: \"seamless routine + decisions to the user\".\n // Set the mirror's default permission mode to \"default\" (NON-bypass) so\n // CloudCLI's canUseTool callback stays live — routine tools pre-approved via\n // the seeded allow-list run with no prompt, while AskUserQuestion/ExitPlanMode\n // (never allow-listed) reach the human as real widgets. Bypass would skip\n // canUseTool and auto-answer those two. Opt out with GH_ROUTER_SERVE_NO_AUTO_APPROVE=1\n // (then CloudCLI prompts on every not-yet-approved tool).\n if (process.env.GH_ROUTER_SERVE_NO_AUTO_APPROVE !== \"1\") {\n await configureServeDefaultPermissionMode(settingsPath).catch((err) =>\n consola.warn(`Could not set serve permission defaults: ${String(err)}`),\n )\n }\n\n // Auto-approve github-router's injected MCP servers PLUS the native read-only\n // research tools (WebSearch/WebFetch) via allow rules (MCP on the resolved\n // keys). Redundant under the bypass default above, but load-bearing the moment\n // a serve user switches the composer to PLAN mode: plan mode re-gates tools,\n // and this is exactly the research surface wanted while planning. Runs AFTER\n // the bypass config so the entries survive its allow-list clear. Uses the\n // authoritative injected-server list (`injected.serversAdded` — includes the\n // `codex-cli` stdio server), falling back to the resolved keys + codex-cli on\n // collision.\n const injectedServerKeys = injected.ok\n ? injected.serversAdded\n : [\n ...Object.values(groupKeys).filter((k): k is string => Boolean(k)),\n ...(opts.codexCli === true ? [\"codex-cli\"] : []),\n ]\n await injectAllowRules(settingsPath, planModeAllowRules(injectedServerKeys)).catch((err) =>\n consola.warn(`Could not auto-approve injected tools: ${String(err)}`),\n )\n\n if (workerToolsEnabled()) {\n const promptCmd = buildPromptSubmitHookCommand(opts.selfInvocation)\n await injectStopHookIntoSettingsFile(settingsPath, promptCmd, \"UserPromptSubmit\", 45).catch(\n (err) => consola.warn(`Could not register the UserPromptSubmit hook: ${String(err)}`),\n )\n\n if (!injected.ok) {\n consola.warn(\n \"Workers non-blocking guard NOT registered: subagent MCP injection fell back to parent-only, so worker-* dispatchers cannot reach the workers server. Raw worker tools remain usable on the main thread.\",\n )\n } else if (process.env.GH_ROUTER_DISABLE_WORKER_GUARD !== \"1\") {\n const workersKey = workersKeyOf(groupKeys)\n const modes = activeDispatchModes({ browse: browseAllowed && browseAgentEnabled() })\n const cmd = buildWorkerGuardHookCommand(\n opts.selfInvocation,\n workersKey,\n modes,\n )\n const matcher = guardToolMatcher(workersKey, modes)\n await injectStopHookIntoSettingsFile(settingsPath, cmd, \"PreToolUse\", 10, matcher).catch(\n (err) => consola.warn(`Could not register the workers PreToolUse guard hook: ${String(err)}`),\n )\n } else {\n consola.info(\n \"Workers non-blocking guard disabled via GH_ROUTER_DISABLE_WORKER_GUARD=1; raw worker tools remain callable on the main thread.\",\n )\n }\n }\n\n if (planReviewEnabled()) {\n try {\n const command = buildPlanReviewHookCommand(opts.selfInvocation)\n // Advisory PostToolUse(ExitPlanMode) hook: non-blocking; findings surface on the next prompt.\n // Verified (scripts/verify-plan-mode-cloudcli.mjs): ExitPlanMode runs as a real tool under\n // CloudCLI's SDK path, and settingSources=['project','user','local'] loads this mirror\n // settings.json hook, so PostToolUse(ExitPlanMode) fires on approval+execution.\n await injectStopHookIntoSettingsFile(settingsPath, command, \"PostToolUse\", undefined, \"ExitPlanMode\")\n } catch (err) {\n consola.warn(`Could not register the advisory plan-review hook: ${String(err)}`)\n }\n }\n\n const servers = injected.ok\n ? injected.serversAdded.join(\", \")\n : \"parent-only (user mcpServers collision)\"\n consola.info(\n `github-router tools wired into Claude sessions: MCP [${servers}], ${runtime.personas.length} subagents, ${skillsWritten} skills.`,\n )\n return {\n cleanup: runtime.cleanup,\n nonce: runtime.nonce,\n pickerModels: picker?.models,\n mcpToolNames: enumerateInjectedMcpToolNames(groupKeys, { codexCli: opts.codexCli === true }),\n }\n } catch (err) {\n consola.warn(\n `Could not wire the github-router MCP/skills layer: ${\n err instanceof Error ? err.message : String(err)\n }. Claude still works, without the extra tools.`,\n )\n return NOOP\n }\n}\n","import fs from \"node:fs/promises\"\nimport path from \"node:path\"\n\nimport type { Model, ModelsResponse } from \"~/services/copilot/get-models\"\n\nexport interface ProviderFacadeContext {\n getModels: () => ModelsResponse | undefined\n defaultModel: string\n claudeConfigDir: string\n}\n\nexport type ProviderFacadeKind = \"models\" | \"mcp\" | \"skills\" | \"commands\" | \"auth\"\n\ntype JsonObject = Record<string, unknown>\n\nfunction isObject(value: unknown): value is JsonObject {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\nfunction envelopedData(value: unknown): JsonObject | null {\n if (!isObject(value) || !isObject(value.data)) return null\n return value.data\n}\n\nfunction cloneObject<T extends JsonObject>(value: T): JsonObject {\n return { ...value }\n}\n\n/**\n * Requests serve answers DIRECTLY (never forwarded to CloudCLI) with a canned\n * JSON body. `POST /api/system/update` is CloudCLI's \"update available\" button,\n * which runs a GLOBAL `npm install -g @cloudcli-ai/cloudcli@latest` in the user's\n * home dir (`server/index.js`) — mutating their global npm and never applying to\n * serve's pinned, router-owned install. Blocking it prevents that surprise\n * mutation and tells the user how updates actually work. HTTP 200 + `success:\n * false` so the UI neither runs the update nor throws an error toast. Returns\n * null for anything not blocked.\n */\nexport function facadeBlockedRequest(\n method: string,\n pathname: string,\n): { status: number; json: JsonObject } | null {\n if (method.toUpperCase() === \"POST\" && pathname === \"/api/system/update\") {\n return {\n status: 200,\n json: {\n success: false,\n managed: true,\n message:\n \"CloudCLI is managed by github-router (a pinned, router-owned install). \"\n + \"To update, run `npm i -g github-router@latest`, then restart `github-router serve`.\",\n },\n }\n }\n return null\n}\n\nexport function facadeInterceptKind(\n method: string,\n pathname: string,\n): ProviderFacadeKind | null {\n const normalizedMethod = method.toUpperCase()\n if (normalizedMethod === \"POST\" && pathname === \"/api/commands/list\") {\n return \"commands\"\n }\n if (normalizedMethod !== \"GET\") return null\n\n switch (pathname) {\n case \"/api/providers/claude/models\":\n return \"models\"\n case \"/api/providers/claude/mcp/servers\":\n return \"mcp\"\n case \"/api/providers/claude/skills\":\n return \"skills\"\n case \"/api/providers/claude/auth/status\":\n return \"auth\"\n default:\n return null\n }\n}\n\nexport async function rewriteProviderResponse(\n kind: ProviderFacadeKind,\n upstreamJson: unknown,\n ctx: ProviderFacadeContext,\n query: URLSearchParams,\n): Promise<unknown | null> {\n try {\n switch (kind) {\n case \"models\":\n return rewriteModels(upstreamJson, ctx)\n case \"mcp\":\n return rewriteMcp(upstreamJson, ctx, query)\n case \"skills\":\n return rewriteSkills(upstreamJson, ctx)\n case \"commands\":\n return rewriteCommands(upstreamJson, ctx)\n case \"auth\":\n return rewriteAuth(upstreamJson)\n }\n } catch {\n return null\n }\n}\n\nfunction rewriteModels(upstreamJson: unknown, ctx: ProviderFacadeContext): unknown | null {\n if (!isObject(upstreamJson)) return null\n const data = envelopedData(upstreamJson)\n if (!data || !isObject(data.models) || data.cache === undefined) return null\n\n const catalog = ctx.getModels()\n const models = catalog?.data\n if (!Array.isArray(models) || models.length === 0) return null\n\n const options = models\n .filter(isPickerModel)\n .map((m) => {\n const option: { value: string; label: string; description: string; effort?: JsonObject } = {\n value: m.id,\n label: m.name || m.id,\n description: `${m.vendor} · ${m.capabilities.family}`,\n }\n // Emit the effort selector the CloudCLI UI + `resolveClaudeEffort` need:\n // `effort.values` is `[{value}]` and the server maps it back with\n // `.map(v => v.value)` to validate a client-picked effort before passing\n // it to the binary. Derived from the Copilot catalog's per-model\n // `reasoning_effort` allowlist; omitted for non-reasoning models (no\n // selector shown). Default to `high` when supported (matches the shim's\n // default), else the model's top tier.\n const efforts = m.capabilities.supports?.reasoning_effort\n if (Array.isArray(efforts) && efforts.length > 0) {\n option.effort = {\n default: efforts.includes(\"high\") ? \"high\" : efforts[efforts.length - 1],\n values: efforts.map((e) => ({ value: e })),\n }\n }\n return option\n })\n if (options.length === 0) return null\n\n const values = new Set(options.map((o) => o.value))\n const upstreamDefault = typeof data.models.DEFAULT === \"string\" ? data.models.DEFAULT : undefined\n const DEFAULT = values.has(ctx.defaultModel)\n ? ctx.defaultModel\n : (options[0]?.value ?? upstreamDefault)\n\n const nextData = cloneObject(data)\n nextData.models = { OPTIONS: options, DEFAULT }\n return { ...upstreamJson, data: nextData }\n}\n\nfunction isPickerModel(model: unknown): model is Model {\n return isObject(model)\n && typeof model.id === \"string\"\n && model.model_picker_enabled !== false\n && typeof model.vendor === \"string\"\n && (model.name === undefined || typeof model.name === \"string\")\n && isObject(model.capabilities)\n && typeof model.capabilities.family === \"string\"\n}\n\nasync function rewriteMcp(\n upstreamJson: unknown,\n ctx: ProviderFacadeContext,\n query: URLSearchParams,\n): Promise<unknown | null> {\n if (!isObject(upstreamJson) || query.get(\"scope\") !== \"user\") return null\n const data = envelopedData(upstreamJson)\n if (!data || !Array.isArray(data.servers)) return null\n\n const mirror = await readJson(path.join(ctx.claudeConfigDir, \".claude.json\"))\n if (!isObject(mirror) || !isObject(mirror.mcpServers)) return null\n\n const existing = new Set(\n data.servers\n .filter(isObject)\n .map((s) => s.name)\n .filter((name): name is string => typeof name === \"string\"),\n )\n const additions: JsonObject[] = []\n for (const [name, entry] of Object.entries(mirror.mcpServers)) {\n if (existing.has(name) || !isObject(entry)) continue\n const transport = entry.type === \"http\" ? \"http\" : \"stdio\"\n const server: JsonObject = {\n provider: \"claude\",\n name,\n scope: \"user\",\n transport,\n }\n if (typeof entry.url === \"string\") server.url = entry.url\n additions.push(server)\n existing.add(name)\n }\n if (additions.length === 0) return null\n\n const nextData = cloneObject(data)\n nextData.servers = [...data.servers, ...additions]\n return { ...upstreamJson, data: nextData }\n}\n\nasync function rewriteSkills(\n upstreamJson: unknown,\n ctx: ProviderFacadeContext,\n): Promise<unknown | null> {\n if (!isObject(upstreamJson)) return null\n const data = envelopedData(upstreamJson)\n if (!data || !Array.isArray(data.skills)) return null\n\n const skills = await readSkillDirs(path.join(ctx.claudeConfigDir, \"skills\"))\n if (skills.length === 0) return null\n\n const existing = new Set(\n data.skills\n .filter(isObject)\n .map((s) => s.command)\n .filter((command): command is string => typeof command === \"string\"),\n )\n const additions = skills\n .filter((skill) => !existing.has(skill.name))\n .map((skill) => ({\n provider: \"claude\",\n name: skill.name,\n description: skill.description,\n command: skill.name,\n scope: \"user\",\n // sourcePath intentionally omitted — the UI dispatches on `command`/`name`,\n // and an absolute mirror path would leak the home dir / username to the browser.\n }))\n if (additions.length === 0) return null\n\n const nextData = cloneObject(data)\n nextData.skills = [...data.skills, ...additions]\n return { ...upstreamJson, data: nextData }\n}\n\nasync function rewriteCommands(\n upstreamJson: unknown,\n ctx: ProviderFacadeContext,\n): Promise<unknown | null> {\n if (!isObject(upstreamJson) || !Array.isArray(upstreamJson.custom)) return null\n\n const dirs = [\n path.join(ctx.claudeConfigDir, \"commands\"),\n path.join(ctx.claudeConfigDir, \".claude\", \"commands\"),\n ]\n const commandFiles = (await Promise.all(dirs.map(readCommandFiles))).flat()\n if (commandFiles.length === 0) return null\n\n const existing = new Set(\n upstreamJson.custom\n .filter(isObject)\n .map((c) => c.name)\n .filter((name): name is string => typeof name === \"string\"),\n )\n const additions = commandFiles\n .filter((command) => !existing.has(command.name))\n .map((command) => ({\n name: command.name,\n // Relative path only — an absolute mirror path would leak the home dir /\n // username to the browser. The UI dispatches on `name`.\n path: command.relativePath,\n relativePath: command.relativePath,\n description: command.description,\n namespace: \"user\",\n metadata: { type: \"custom\" },\n }))\n if (additions.length === 0) return null\n\n const custom = [...upstreamJson.custom, ...additions]\n return {\n ...upstreamJson,\n custom,\n count: countCommands(upstreamJson.builtIn) + custom.length,\n }\n}\n\nfunction rewriteAuth(upstreamJson: unknown): unknown | null {\n if (!isObject(upstreamJson)) return null\n const data = envelopedData(upstreamJson)\n if (!data) return null\n\n return {\n ...upstreamJson,\n data: {\n ...data,\n authenticated: true,\n installed: true,\n },\n }\n}\n\nasync function readJson(filePath: string): Promise<unknown | null> {\n try {\n return JSON.parse(await fs.readFile(filePath, \"utf8\"))\n } catch {\n return null\n }\n}\n\nasync function readSkillDirs(skillsDir: string): Promise<Array<{ name: string; description: string; sourcePath: string }>> {\n try {\n const entries = await fs.readdir(skillsDir, { withFileTypes: true })\n const skills = await Promise.all(\n entries\n .filter((entry) => entry.isDirectory() && entry.name.startsWith(\"gh-\"))\n .map(async (entry) => {\n const sourcePath = path.join(skillsDir, entry.name, \"SKILL.md\")\n let description = \"\"\n try {\n const md = await fs.readFile(sourcePath, \"utf8\")\n description = parseFrontmatterField(md, \"description\") ?? \"\"\n } catch {\n // Keep the skill visible even if only the directory is readable.\n }\n const name = entry.name\n return { name, description, sourcePath }\n }),\n )\n return skills\n } catch {\n return []\n }\n}\n\nasync function readCommandFiles(commandsDir: string): Promise<Array<{ name: string; relativePath: string; filePath: string; description: string }>> {\n try {\n const stat = await fs.stat(commandsDir)\n if (!stat.isDirectory()) return []\n } catch {\n return []\n }\n return readCommandFilesRecursive(commandsDir, commandsDir)\n}\n\nasync function readCommandFilesRecursive(\n root: string,\n dir: string,\n): Promise<Array<{ name: string; relativePath: string; filePath: string; description: string }>> {\n let entries\n try {\n entries = await fs.readdir(dir, { withFileTypes: true })\n } catch {\n return []\n }\n\n const out: Array<{ name: string; relativePath: string; filePath: string; description: string }> = []\n for (const entry of entries) {\n const filePath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n out.push(...await readCommandFilesRecursive(root, filePath))\n continue\n }\n if (!entry.isFile() || !entry.name.endsWith(\".md\")) continue\n const relativePath = normalizeRelative(path.relative(root, filePath))\n const name = relativePath.replace(/\\.md$/i, \"\")\n let description = \"\"\n try {\n const md = await fs.readFile(filePath, \"utf8\")\n description = parseFrontmatterField(md, \"description\") ?? firstMarkdownLine(md)\n } catch {\n // A listed file that disappears mid-read should not break the UI.\n }\n out.push({ name, relativePath, filePath, description })\n }\n return out\n}\n\nfunction parseFrontmatterField(markdown: string, field: string): string | null {\n if (!markdown.startsWith(\"---\")) return null\n const end = markdown.indexOf(\"\\n---\", 3)\n if (end < 0) return null\n const frontmatter = markdown.slice(3, end).split(/\\r?\\n/)\n for (const line of frontmatter) {\n const match = line.match(/^([A-Za-z0-9_-]+):\\s*(.*)$/)\n if (!match || match[1] !== field) continue\n return match[2].replace(/^['\"]|['\"]$/g, \"\").trim()\n }\n return null\n}\n\nfunction firstMarkdownLine(markdown: string): string {\n const body = markdown.startsWith(\"---\")\n ? markdown.slice(markdown.indexOf(\"\\n---\", 3) + 4)\n : markdown\n for (const line of body.split(/\\r?\\n/)) {\n const trimmed = line.trim()\n if (trimmed) return trimmed.replace(/^#+\\s*/, \"\")\n }\n return \"\"\n}\n\nfunction normalizeRelative(value: string): string {\n return value.split(path.sep).join(\"/\")\n}\n\nfunction countCommands(builtIn: unknown): number {\n return Array.isArray(builtIn) ? builtIn.length : 0\n}\n","import http from \"node:http\"\nimport type { AddressInfo, Socket } from \"node:net\"\n\nimport { getPackageVersion } from \"../version\"\n\n/**\n * Loopback-only identity endpoint served directly by the reverse proxy (never\n * forwarded to CloudCLI). A second `github-router serve` probes this on the\n * intended port to detect an already-running instance (single-instance guard),\n * so it can attach/refuse instead of starting a duplicate CloudCLI. Carries no\n * secret — just a service marker + version + origin.\n */\nexport const SERVE_IDENTITY_PATH = \"/__github-router-serve__\"\nexport const SERVE_IDENTITY_SERVICE = \"github-router-serve\"\n\n/**\n * Standalone HTTP + WebSocket reverse proxy that fronts a locally-running\n * CloudCLI instance under github-router's own origin. It is deliberately NOT\n * routed through the srvx/Hono app — a separate lightweight proxy avoids\n * entangling CloudCLI's raw WebSocket upgrades (`/ws` chat, `/shell` node-pty\n * terminal) with the model-API server.\n *\n * Security responsibilities CloudCLI's OSS server does not handle itself:\n * 1. Injects `localStorage['auth-token']` into the served `index.html` so the\n * SPA boots already-authenticated (true zero-login).\n * 2. Rejects foreign `Host` (DNS-rebinding defense — an `Origin`-only check\n * still lets a rebound `attacker.com → 127.0.0.1` navigation fetch the\n * token-injected HTML) AND foreign `Origin` on requests + WS upgrades\n * (CloudCLI performs NO Origin check on `/ws` or `/shell`).\n */\nexport interface ReverseProxyOptions {\n targetHost: string\n targetPort: number\n bindHost: string\n bindPort: number\n /** JWT injected into the SPA so it boots authenticated. */\n authToken: string\n /**\n * Seed CloudCLI's `claude-settings` localStorage with `skipPermissions:false`\n * and an `allowedTools` allow-list (routine built-ins + injected `mcp__*`) so\n * routine tools auto-run with no prompt while `AskUserQuestion`/`ExitPlanMode`\n * still reach the user (serve default; disabled by GH_ROUTER_SERVE_NO_AUTO_APPROVE=1).\n */\n seedToolSettings?: boolean\n /** Exact tool names to seed into the `claude-settings` `allowedTools`\n * auto-approve list (only used when `seedToolSettings`) — the routine\n * built-ins plus every exact `mcp__<key>__<tool>` name. MUST NOT include\n * `AskUserQuestion`/`ExitPlanMode`, or they would auto-resolve instead of\n * reaching the user. */\n seedAllowedTools?: string[]\n providerFacade?: {\n kindFor: (method: string, pathname: string) => string | null\n rewrite: (\n kind: string,\n upstreamJson: unknown,\n query: URLSearchParams,\n ) => Promise<unknown | null>\n /**\n * Requests answered DIRECTLY with a canned JSON body, never forwarded to\n * CloudCLI (e.g. `POST /api/system/update`, which would run a global npm\n * install). Returns null to forward normally.\n */\n blockedRequest?: (\n method: string,\n pathname: string,\n ) => { status: number; json: unknown } | null\n }\n /**\n * Extra exact `host:port` values to accept beyond loopback (e.g. a specific\n * dev-tunnel host `abc-5454.usw2.devtunnels.ms`). Used for remote access.\n */\n extraAllowedHosts?: string[]\n /** Extra exact origins to accept (e.g. `https://abc-5454.usw2.devtunnels.ms`). */\n extraAllowedOrigins?: string[]\n /**\n * Accept ANY `*.devtunnels.ms` host + `https://*.devtunnels.ms` origin. A\n * convenience for Microsoft dev tunnels when the exact URL isn't pinned. Only\n * the user's own authenticated tunnel actually forwards to this loopback port,\n * so this does not weaken the same-user loopback trust boundary.\n */\n allowDevtunnelHosts?: boolean\n}\n\nexport interface ReverseProxyHandle {\n url: string\n port: number\n close: () => Promise<void>\n}\n\nconst HEAD_CLOSE = \"</head>\"\n\n/** CloudCLI's localStorage key for per-tool chat permission settings. */\nconst CLAUDE_SETTINGS_KEY = \"claude-settings\"\n\n/**\n * The two interaction tools CloudCLI routes to the human (its\n * `TOOLS_REQUIRING_INTERACTION`). They must NEVER appear in the seeded\n * `allowedTools` — an exact-name match there would auto-approve them and defeat\n * the question widget / plan card. Stripped defensively at the seed chokepoint\n * so no upstream list (mcp names, a future caller) can reintroduce them.\n */\nconst NEVER_AUTO_APPROVE = new Set([\"AskUserQuestion\", \"ExitPlanMode\"])\n\n/**\n * Embed the token as a JS string literal. JSON.stringify escapes quotes and\n * backslashes; we additionally neutralize `<` so the value can never break out\n * of the surrounding <script> element regardless of contents.\n *\n * When `seedToolSettings` is set, also seed CloudCLI's `claude-settings`\n * localStorage with `{allowedTools:<routine+mcp>, disallowedTools:[], skipPermissions:false}`.\n * This is serve's \"seamless routine + decisions to the user\" model: `canUseTool`\n * stays LIVE (skipPermissions:false, NOT bypass), routine tools in `allowedTools`\n * auto-run with no prompt, and `AskUserQuestion`/`ExitPlanMode` — deliberately\n * NOT in the list and hard-cased by CloudCLI into `TOOLS_REQUIRING_INTERACTION` —\n * always reach the human as a real question widget / plan Approve-Reject card.\n * `skipPermissions:true` (bypass) would skip `canUseTool` entirely and silently\n * auto-answer those two, which is exactly what we are fixing.\n */\nfunction buildInjection(\n token: string,\n seedToolSettings = false,\n seedAllowedTools: string[] = [],\n): string {\n const safe = JSON.stringify(token).replace(/</g, \"\\\\u003c\")\n let body = `localStorage.setItem('auth-token',${safe});`\n if (seedToolSettings) {\n const settings = JSON.stringify({\n // CloudCLI's per-tool AUTO-APPROVE list — feeds the Agent-SDK `canUseTool`\n // fast-path (EXACT-name match, no `mcp__<server>` wildcard) AND\n // `sdkOptions.allowedTools`. We seed the routine built-ins + every exact\n // `mcp__<key>__<tool>` name so ordinary work never prompts. The two\n // interaction tools are filtered out unconditionally (NEVER_AUTO_APPROVE)\n // so they always reach the user, no matter what the caller passed.\n allowedTools: seedAllowedTools.filter((t) => !NEVER_AUTO_APPROVE.has(t)),\n disallowedTools: [],\n // NON-bypass: keep canUseTool live so AskUserQuestion/ExitPlanMode reach\n // the user. Bypass would auto-resolve them on a generated answer.\n skipPermissions: false,\n })\n const safeSettings = JSON.stringify(settings).replace(/</g, \"\\\\u003c\")\n body += `localStorage.setItem('${CLAUDE_SETTINGS_KEY}',${safeSettings});`\n }\n return `<script>try{${body}}catch(e){}</script>`\n}\n\nfunction headerLines(headers: http.IncomingHttpHeaders): string {\n return Object.entries(headers)\n .map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(\", \") : v}`)\n .join(\"\\r\\n\")\n}\n\nexport async function startReverseProxy(\n opts: ReverseProxyOptions,\n): Promise<ReverseProxyHandle> {\n const { targetHost, targetPort, bindHost, bindPort, authToken } = opts\n const ownOrigin = `http://${bindHost}:${bindPort}`\n const injection = buildInjection(authToken, opts.seedToolSettings === true, opts.seedAllowedTools ?? [])\n\n const allowedHosts = new Set([\n `127.0.0.1:${bindPort}`,\n `localhost:${bindPort}`,\n `[::1]:${bindPort}`,\n ...(opts.extraAllowedHosts ?? []).map((h) => h.toLowerCase()),\n ])\n // Loopback-only subset — the single-instance identity probe always comes from\n // 127.0.0.1, so the identity endpoint is answered only for a loopback Host\n // (never a dev-tunnel host), keeping the version off the remote surface.\n const loopbackHosts = new Set([\n `127.0.0.1:${bindPort}`,\n `localhost:${bindPort}`,\n `[::1]:${bindPort}`,\n ])\n const allowedOrigins = new Set([\n `http://127.0.0.1:${bindPort}`,\n `http://localhost:${bindPort}`,\n `http://[::1]:${bindPort}`,\n ...(opts.extraAllowedOrigins ?? []).map((o) => o.toLowerCase()),\n ])\n // `(^|\\.)devtunnels.ms$` matches `x.devtunnels.ms` but not `evildevtunnels.ms`\n // / `evil-devtunnels.ms` (the char before must be start-of-string or a dot).\n const DEVTUNNEL_RE = /(^|\\.)devtunnels\\.ms$/i\n const isDevtunnelHost = (host: string): boolean =>\n !!opts.allowDevtunnelHosts && DEVTUNNEL_RE.test(host.replace(/:\\d+$/, \"\"))\n const isDevtunnelOrigin = (origin: string): boolean => {\n if (!opts.allowDevtunnelHosts) return false\n try {\n const u = new URL(origin)\n return u.protocol === \"https:\" && DEVTUNNEL_RE.test(u.hostname)\n } catch {\n return false\n }\n }\n // Host defends against DNS-rebinding; Origin defends against cross-origin\n // browser access. A missing Origin (plain navigation / non-browser) is only\n // allowed once the Host has already been confirmed allowed.\n const hostAllowed = (host?: string): boolean =>\n !!host\n && (allowedHosts.has(host.toLowerCase()) || isDevtunnelHost(host.toLowerCase()))\n const originAllowed = (origin?: string): boolean =>\n !origin\n || allowedOrigins.has(origin.toLowerCase())\n || isDevtunnelOrigin(origin)\n\n // Track hijacked upgrade sockets so close() can destroy them — http.Server\n // .close() otherwise waits for them forever (shutdown hang).\n const liveSockets = new Set<Socket>()\n\n const server = http.createServer((clientReq, clientRes) => {\n clientReq.on(\"error\", () => clientRes.destroy())\n clientRes.on(\"error\", () => clientReq.destroy())\n\n if (!hostAllowed(clientReq.headers.host) || !originAllowed(clientReq.headers.origin)) {\n clientRes.writeHead(403, { \"content-type\": \"text/plain\" })\n clientRes.end(\"Forbidden\")\n return\n }\n\n // Single-instance identity probe — answered directly, never proxied to\n // CloudCLI. Loopback Host only (the probe is always local); carries no\n // secret and no attacker-usable URL.\n if ((clientReq.url ?? \"\").split(\"?\")[0] === SERVE_IDENTITY_PATH) {\n if (!loopbackHosts.has(String(clientReq.headers.host ?? \"\").toLowerCase())) {\n clientRes.writeHead(404, { \"content-type\": \"text/plain\" })\n clientRes.end(\"Not found\")\n return\n }\n clientRes.writeHead(200, { \"content-type\": \"application/json\" })\n clientRes.end(\n JSON.stringify({\n service: SERVE_IDENTITY_SERVICE,\n version: getPackageVersion(),\n }),\n )\n return\n }\n\n const requestUrl = new URL(clientReq.url ?? \"/\", ownOrigin)\n\n // Requests serve answers directly (never forwarded) — e.g. CloudCLI's\n // system-update button, which would run a global `npm i -g` mutation.\n const blocked = opts.providerFacade?.blockedRequest?.(\n clientReq.method ?? \"GET\",\n requestUrl.pathname,\n )\n if (blocked) {\n clientReq.resume() // drain the request body so the socket can be reused\n clientRes.writeHead(blocked.status, { \"content-type\": \"application/json\" })\n clientRes.end(JSON.stringify(blocked.json))\n return\n }\n\n const facadeKind = opts.providerFacade?.kindFor(\n clientReq.method ?? \"GET\",\n requestUrl.pathname,\n ) ?? null\n\n // Drop accept-encoding so HTML comes back uncompressed and stays\n // injectable; façade JSON rewrites also need original bytes, not gzip/br.\n const headers = { ...clientReq.headers }\n delete headers[\"accept-encoding\"]\n\n const proxyReq = http.request(\n {\n host: targetHost,\n port: targetPort,\n path: clientReq.url,\n method: clientReq.method,\n headers,\n },\n (proxyRes) => {\n proxyRes.on(\"error\", () => clientRes.destroy())\n const contentType = String(proxyRes.headers[\"content-type\"] ?? \"\")\n if (facadeKind) {\n const chunks: Buffer[] = []\n proxyRes.on(\"data\", (d) => chunks.push(Buffer.from(d)))\n proxyRes.on(\"end\", () => {\n const original = Buffer.concat(chunks)\n const sendOriginal = () => {\n // We buffered the whole body, so send it as a fixed buffer and let\n // Node re-frame — any upstream chunked/encoded framing is now wrong.\n const outHeaders = { ...proxyRes.headers }\n delete outHeaders[\"content-length\"]\n delete outHeaders[\"content-encoding\"]\n delete outHeaders[\"transfer-encoding\"]\n clientRes.writeHead(proxyRes.statusCode ?? 502, outHeaders)\n clientRes.end(original)\n }\n if (proxyRes.statusCode !== 200) {\n sendOriginal()\n return\n }\n void (async () => {\n try {\n const parsed = JSON.parse(original.toString(\"utf8\"))\n const rewritten = await opts.providerFacade?.rewrite(\n facadeKind,\n parsed,\n requestUrl.searchParams,\n )\n if (rewritten == null) {\n sendOriginal()\n return\n }\n const outHeaders = { ...proxyRes.headers }\n // We send a fresh JSON body, so any upstream length/encoding\n // framing is now wrong — drop it and let Node re-frame.\n delete outHeaders[\"content-length\"]\n delete outHeaders[\"content-encoding\"]\n delete outHeaders[\"transfer-encoding\"]\n outHeaders[\"content-type\"] = \"application/json\"\n clientRes.writeHead(200, outHeaders)\n clientRes.end(JSON.stringify(rewritten))\n } catch {\n sendOriginal()\n }\n })()\n })\n } else if (contentType.includes(\"text/html\")) {\n const chunks: Buffer[] = []\n proxyRes.on(\"data\", (d) => chunks.push(Buffer.from(d)))\n proxyRes.on(\"end\", () => {\n let html = Buffer.concat(chunks).toString(\"utf8\")\n html = html.includes(HEAD_CLOSE)\n ? html.replace(HEAD_CLOSE, injection + HEAD_CLOSE)\n : injection + html\n const outHeaders = { ...proxyRes.headers }\n // We send a fresh fixed buffer, so any length/encoding framing the\n // upstream set is now wrong — drop it and let Node re-frame.\n delete outHeaders[\"content-length\"]\n delete outHeaders[\"content-encoding\"]\n delete outHeaders[\"transfer-encoding\"]\n clientRes.writeHead(proxyRes.statusCode ?? 502, outHeaders)\n clientRes.end(html)\n })\n } else {\n clientRes.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers)\n proxyRes.pipe(clientRes)\n }\n },\n )\n proxyReq.on(\"error\", (err) => {\n if (!clientRes.headersSent)\n clientRes.writeHead(502, { \"content-type\": \"text/plain\" })\n clientRes.end(`Bad gateway: ${err.message}`)\n })\n clientReq.pipe(proxyReq)\n })\n\n server.on(\"upgrade\", (clientReq, clientSock: Socket, clientHead) => {\n // Attach error handling + track the socket IMMEDIATELY, before any async\n // window (a client reset during the upstream handshake must not crash us).\n clientSock.on(\"error\", () => clientSock.destroy())\n liveSockets.add(clientSock)\n clientSock.once(\"close\", () => liveSockets.delete(clientSock))\n\n if (!hostAllowed(clientReq.headers.host) || !originAllowed(clientReq.headers.origin)) {\n clientSock.destroy()\n return\n }\n\n const proxyReq = http.request({\n host: targetHost,\n port: targetPort,\n path: clientReq.url,\n method: clientReq.method,\n headers: clientReq.headers,\n })\n proxyReq.on(\"upgrade\", (proxyRes, proxySock, proxyHead) => {\n clientSock.write(\n `HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage}\\r\\n${headerLines(\n proxyRes.headers,\n )}\\r\\n\\r\\n`,\n )\n if (clientHead && clientHead.length) proxySock.write(clientHead)\n if (proxyHead && proxyHead.length) proxySock.unshift(proxyHead)\n proxySock.pipe(clientSock)\n clientSock.pipe(proxySock)\n const cleanup = () => {\n proxySock.destroy()\n clientSock.destroy()\n }\n // Tear down on BOTH error and normal close, on either side (else the\n // paired socket leaks on every page refresh / terminal close).\n clientSock.on(\"error\", cleanup)\n proxySock.on(\"error\", cleanup)\n clientSock.on(\"close\", cleanup)\n proxySock.on(\"close\", cleanup)\n })\n // CloudCLI answered the upgrade with an ordinary response (e.g. 401/404 for\n // a missing/invalid JWT). Relay it and close so the browser WS doesn't hang.\n proxyReq.on(\"response\", (proxyRes) => {\n try {\n clientSock.write(\n `HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage}\\r\\n${headerLines(\n proxyRes.headers,\n )}\\r\\n\\r\\n`,\n )\n } catch {\n /* socket already gone */\n }\n proxyRes.on(\"data\", (d) => {\n clientSock.write(d as Buffer)\n })\n proxyRes.on(\"end\", () => clientSock.end())\n proxyRes.on(\"error\", () => clientSock.destroy())\n })\n proxyReq.on(\"error\", () => clientSock.destroy())\n proxyReq.end()\n })\n\n await new Promise<void>((resolve, reject) => {\n const onError = (err: Error) => reject(err)\n server.once(\"error\", onError)\n server.listen(bindPort, bindHost, () => {\n server.removeListener(\"error\", onError)\n resolve()\n })\n })\n\n // Defense-in-depth: refuse to run if we somehow bound a non-loopback address.\n const addr = server.address() as AddressInfo | null\n if (addr && addr.address !== \"127.0.0.1\" && addr.address !== \"::1\") {\n await new Promise<void>((r) => server.close(() => r()))\n throw new Error(\n `reverse proxy refused non-loopback bind (got ${addr.address})`,\n )\n }\n\n return {\n url: ownOrigin,\n port: bindPort,\n close: () =>\n new Promise<void>((resolve) => {\n for (const s of liveSockets) s.destroy()\n liveSockets.clear()\n const t = setTimeout(resolve, 1500) // never let shutdown hang\n server.close(() => {\n clearTimeout(t)\n resolve()\n })\n }),\n }\n}\n\n/** Exported for unit tests. */\nexport const __test = { buildInjection, HEAD_CLOSE }\n","import { spawn } from \"node:child_process\"\nimport http from \"node:http\"\nimport type { AddressInfo } from \"node:net\"\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { provisionBrowserAssets } from \"./lib/browser-mcp/provision\"\nimport { provisionAndIndexColbert } from \"./lib/colbert\"\nimport { resolveCodexCliBackend } from \"./lib/codex-mcp-config\"\nimport { killChildProcessTree } from \"./lib/exec\"\nimport { startKeepAwake, stopKeepAwake } from \"./lib/keep-awake\"\nimport {\n hookLauncherDegradedWarning,\n resolveSelfInvocation,\n} from \"./lib/hook-launcher/self-invocation\"\nimport { getCodexVersion } from \"./lib/launch\"\nimport { resolveLaunchProfile } from \"./lib/launch-profile\"\nimport { browserToolsEnabled } from \"./lib/mcp-capabilities\"\nimport { SEAMLESS_BUILTIN_TOOLS } from \"./lib/mcp-permissions-settings\"\nimport { ensureClaudeConfigMirror, PATHS, removeOwnClaudeConfigMirror } from \"./lib/paths\"\nimport {\n DEFAULT_CLAUDE_MODEL_FALLBACKS,\n pickClaudeDefault,\n resolveLeadSlugArg,\n} from \"./lib/port\"\nimport {\n getClaudeCodeEnvVars,\n parseSharedArgs,\n setupAndServe,\n sharedServerArgs,\n} from \"./lib/server-setup\"\nimport {\n composeCloudCliChildEnv,\n mintJwt,\n resolveCloudCli,\n spawnCloudCli,\n waitForCloudCliReady,\n} from \"./lib/serve/cloudcli\"\nimport { DevtunnelError, startDevtunnel } from \"./lib/serve/devtunnel\"\nimport { provisionServeEnhancements } from \"./lib/serve/enhancements\"\nimport { facadeBlockedRequest, facadeInterceptKind, rewriteProviderResponse } from \"./lib/serve/provider-facade\"\nimport {\n SERVE_IDENTITY_PATH,\n SERVE_IDENTITY_SERVICE,\n startReverseProxy,\n} from \"./lib/serve/reverse-proxy\"\nimport { runSelfUpdate } from \"./lib/self-update\"\nimport { state } from \"./lib/state\"\nimport { provisionToolbelt } from \"./lib/toolbelt/provision\"\nimport { resolveModel } from \"./lib/utils\"\nimport { getGitHubUser } from \"./services/github/get-user\"\n\nexport function assertStandardServeModel(model: string | undefined): void {\n const requestedProfile = resolveLaunchProfile(model)\n if (requestedProfile === \"standard\") return\n throw new Error(\n `The ${requestedProfile} launch profile is available only through `\n + `github-router claude -m ${requestedProfile}; github-router serve uses `\n + \"the Standard roster, ACL, and model picker. Pass an explicit model id \"\n + \"to serve instead of the profile alias.\",\n )\n}\n\nasync function getFreePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const s = http.createServer()\n s.once(\"error\", reject)\n s.listen(0, \"127.0.0.1\", () => {\n const port = (s.address() as AddressInfo).port\n s.close(() => resolve(port))\n })\n })\n}\n\n/** Default user-facing control-plane port when `--port` is not given. */\nconst DEFAULT_SERVE_PORT = 5454\n\nfunction isPortFree(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const s = http.createServer()\n s.once(\"error\", () => resolve(false))\n s.listen(port, \"127.0.0.1\", () => s.close(() => resolve(true)))\n })\n}\n\n/**\n * Probe a loopback port for an already-running `github-router serve` (its\n * identity endpoint). Returns true when the port hosts OUR serve, else false\n * (free, or a foreign process). `serve` is designed to run ONCE per machine\n * (a single machine-wide control plane for all repos), so a second launch\n * attaches to the running one instead of spawning a duplicate CloudCLI +\n * contending on the shared auth DB. We intentionally do NOT trust any URL the\n * probed service reports — a local squatter could forge the identity marker, so\n * the caller only ever opens a locally-constructed loopback origin.\n */\nfunction probeExistingServe(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const req = http.get(\n {\n host: \"127.0.0.1\",\n port,\n path: SERVE_IDENTITY_PATH,\n headers: { host: `127.0.0.1:${port}` },\n timeout: 1500,\n },\n (res) => {\n if (res.statusCode !== 200) {\n res.resume()\n resolve(false)\n return\n }\n const chunks: Buffer[] = []\n res.on(\"data\", (d) => chunks.push(Buffer.from(d)))\n res.on(\"end\", () => {\n try {\n const body = JSON.parse(Buffer.concat(chunks).toString(\"utf8\")) as {\n service?: string\n }\n resolve(body.service === SERVE_IDENTITY_SERVICE)\n } catch {\n resolve(false)\n }\n })\n res.on(\"error\", () => resolve(false))\n },\n )\n req.on(\"timeout\", () => req.destroy())\n req.on(\"error\", () => resolve(false))\n })\n}\n\n/**\n * Resolve the user-facing port: honor an explicit `--port`; otherwise use 5454\n * when free, else fall back to a random free port. (Single-instance detection —\n * an already-running github-router serve on the intended port — is handled by\n * the earlier `probeExistingServe` attach/refuse in `run`.)\n */\nasync function resolveServePort(requested?: number): Promise<number> {\n if (requested != null) return requested\n if (await isPortFree(DEFAULT_SERVE_PORT)) return DEFAULT_SERVE_PORT\n const fallback = await getFreePort()\n consola.info(`Port ${DEFAULT_SERVE_PORT} is in use; using ${fallback} instead.`)\n return fallback\n}\n\nfunction openBrowser(url: string): void {\n try {\n if (process.platform === \"win32\") {\n spawn(\"cmd\", [\"/c\", \"start\", \"\", url], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n }).unref()\n } else if (process.platform === \"darwin\") {\n spawn(\"open\", [url], { detached: true, stdio: \"ignore\" }).unref()\n } else {\n spawn(\"xdg-open\", [url], { detached: true, stdio: \"ignore\" }).unref()\n }\n } catch {\n // best-effort; the URL is printed too\n }\n}\n\nexport const serve = defineCommand({\n meta: {\n name: \"serve\",\n description:\n \"Serve a browser control plane (CloudCLI) with Claude routed through the proxy\",\n },\n args: {\n ...sharedServerArgs,\n model: {\n alias: \"m\",\n type: \"string\",\n description: \"Override the default Claude model\",\n },\n \"cloudcli-path\": {\n type: \"string\",\n description: \"Use an existing CloudCLI install instead of the managed one\",\n },\n \"cloudcli-version\": {\n type: \"string\",\n description: \"Override the pinned CloudCLI version to install\",\n },\n \"codex-cli\": {\n type: \"boolean\",\n default: false,\n description: \"Route Codex personas through a local `codex mcp-server` (requires codex 0.129+; falls back to HTTP if missing)\",\n },\n \"browse-over-tunnel\": {\n type: \"boolean\",\n default: false,\n description: \"Expose the server-side browser MCP even when tunnel-exposed (session-hijack / SSRF risk — only with fully-trusted tunnel access)\",\n },\n \"agents-over-tunnel\": {\n type: \"boolean\",\n default: false,\n description: \"Expose first-mate (mints a repo+workflow GitHub write token) even when tunnel-exposed (only with fully-trusted tunnel access)\",\n },\n \"fleet-over-tunnel\": {\n type: \"boolean\",\n default: false,\n description: \"Expose the fleet MCP (drives remote coding sessions with your fleet credentials) even when tunnel-exposed (only with fully-trusted tunnel access)\",\n },\n \"no-install\": {\n type: \"boolean\",\n default: false,\n description: \"Do not auto-install CloudCLI; require an existing install\",\n },\n \"no-open\": {\n type: \"boolean\",\n default: false,\n description: \"Do not open the browser automatically\",\n },\n \"public-url\": {\n type: \"string\",\n description:\n \"Public URL(s) this is reachable at (e.g. a dev-tunnel https URL), comma-separated — allowlists their host+origin for remote access\",\n },\n tunnel: {\n type: \"boolean\",\n default: false,\n description:\n \"Auto-create + host an AUTHENTICATED (never anonymous) Microsoft dev tunnel for the serve port and print its public URL, so you can reach the control plane from anywhere. Requires the `devtunnel` CLI + `devtunnel user login`.\",\n },\n },\n async run({ args }) {\n state.serveMode = true\n\n if (process.versions.bun) {\n consola.warn(\n \"`serve` should run under Node.js. Bun's node:http upgrade sockets cannot relay the CloudCLI terminal/chat WebSockets; run the installed `github-router` binary (Node).\",\n )\n }\n\n const parsed = parseSharedArgs(args as unknown as Record<string, unknown>)\n try {\n assertStandardServeModel(args.model as string | undefined)\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n\n // Single-instance guard: `serve` is a machine-wide control plane meant to run\n // ONCE per machine (work on any repo through its file explorer / sessions). If\n // one is already running on the intended port, attach to it (open + exit)\n // instead of spawning a second CloudCLI that would contend on the shared auth\n // DB. Only triggers for OUR serve; a foreign process on the port falls through\n // to the random-port fallback in resolveServePort.\n const intendedPort = parsed.port ?? DEFAULT_SERVE_PORT\n if (await probeExistingServe(intendedPort)) {\n // Construct the URL locally — never trust a URL the probed service reports\n // (a local squatter could forge the identity marker into an open-browser).\n const existingUrl = `http://127.0.0.1:${intendedPort}`\n consola.box(\n `github-router control plane already running\\n\\n${existingUrl}\\n\\nOpening it — stop that instance first if you want a fresh one.`,\n )\n if (args[\"no-open\"] !== true) openBrowser(existingUrl)\n return\n }\n\n // 1. github-router proxy — internal (random port). The USER-facing port is\n // the reverse proxy below, so force this one random regardless of --port.\n const { server, serverUrl } = await setupAndServe({\n ...parsed,\n port: undefined,\n silent: true,\n })\n\n // 2. provision the router-owned CLAUDE_CONFIG_DIR (synthetic creds +\n // onboarding-skip) so CloudCLI's SDK-spawned claude authenticates\n // through the proxy. Hard-fail on error (mirrors src/claude.ts).\n try {\n await ensureClaudeConfigMirror()\n } catch (err) {\n consola.error(\n `Failed to provision CLAUDE_CONFIG_DIR mirror: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n process.exit(1)\n }\n\n // Best-effort self-update (detached, applies next launch). Runs after the\n // server is listening so the bounded probe can't delay it.\n void runSelfUpdate({ selfUpdate: args[\"self-update\"] !== false })\n\n // Best-effort ColBERT semantic-search provision. ON by default; never blocks\n // launch. `skipCwdIndex`: serve is machine-wide, so the launch cwd is usually\n // NOT a repo the user works on — per-workspace on-demand indexing (kicked by\n // the first search for a given repo) covers real queries instead.\n void provisionAndIndexColbert({ skipCwdIndex: true })\n\n // Best-effort LLM toolbelt materialization. The mirror awareness line is\n // written by provisionServeEnhancements below, matching `github-router claude`.\n void provisionToolbelt().catch((err) =>\n consola.debug(\"Toolbelt provisioning failed:\", err),\n )\n\n // Best-effort: keep the machine awake while the proxy/CloudCLI session runs.\n // Released via stopKeepAwake() in the shutdown chain below and by the module's\n // own self-registered signal/exit reaper.\n startKeepAwake()\n\n // Best-effort: materialize the browser extension + bridge into the stable\n // app-dir when browser tools are enabled.\n if (browserToolsEnabled()) {\n void provisionBrowserAssets().catch((err) =>\n consola.debug(\"Browser extension provisioning failed:\", err),\n )\n }\n\n // Same lead-slug resolution the `claude` subcommand uses, via the shared\n // helper rather than a second copy of the branching. The copy that used to\n // live here had drifted twice: it never learned the `-m fast` alias, and\n // its fallback walk assigned the bare constant, silently dropping the\n // `[1m]` accounting on a family that is 1M-capable. Re-deriving the\n // fallback through `pickClaudeDefault` is what `src/claude.ts` already\n // does, for that exact reason.\n let chosenSlug = resolveLeadSlugArg(args.model as string | undefined)\n if (!(args.model as string | undefined)?.trim() && state.models) {\n const inCache = (slug: string): boolean =>\n state.models?.data.some((m) => m.id === resolveModel(slug)) ?? false\n if (!inCache(chosenSlug)) {\n for (const fb of DEFAULT_CLAUDE_MODEL_FALLBACKS) {\n if (inCache(fb)) {\n const fallbackSlug = pickClaudeDefault(\n fb.replace(/^claude-opus-/, \"\"),\n )\n consola.info(\n `Default model \"${chosenSlug}\" not in your Copilot model list; falling back to \"${fallbackSlug}\".`,\n )\n chosenSlug = fallbackSlug\n break\n }\n }\n }\n }\n\n const requestedCli = args[\"codex-cli\"] === true\n const backend = resolveCodexCliBackend({\n requested: requestedCli,\n codexInfo: requestedCli ? getCodexVersion() : null,\n })\n const tunnelExposed =\n args.tunnel === true\n || String(args[\"public-url\"] ?? \"\")\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean).length > 0\n if (tunnelExposed && browserToolsEnabled() && args[\"browse-over-tunnel\"] !== true) {\n consola.info(\n \"Browser MCP is disabled over the tunnel (session-hijack/SSRF risk). Pass --browse-over-tunnel to enable it for fully-trusted access.\",\n )\n }\n if (tunnelExposed && state.agentsEnabled && args[\"agents-over-tunnel\"] !== true) {\n consola.info(\n \"first-mate is disabled over the tunnel (it mints a GitHub write token). Pass --agents-over-tunnel to enable it for fully-trusted access.\",\n )\n }\n if (tunnelExposed && state.fleetEnabled && args[\"fleet-over-tunnel\"] !== true) {\n consola.info(\n \"Fleet MCP is disabled over the tunnel (it drives remote coding sessions with your fleet credentials). Pass --fleet-over-tunnel to enable it for fully-trusted access.\",\n )\n }\n\n // 2b. wire the github-router enhancement layer (MCP servers + peer/worker\n // subagents + gh-* skills) into the mirror so CloudCLI-spawned Claude\n // sessions get the same tools `github-router claude` provides. Must run\n // after the mirror exists and before CloudCLI spawns claude. Resolve the\n // self-invocation first so no persisted command races launcher publication.\n const selfInvocation = await resolveSelfInvocation()\n {\n const warning = hookLauncherDegradedWarning(selfInvocation)\n if (warning) process.stderr.write(`${warning}\\n`)\n }\n const enhancements = await provisionServeEnhancements(serverUrl, {\n selfInvocation,\n codexCli: backend === \"cli\",\n tunnelExposed,\n browseOverTunnel: args[\"browse-over-tunnel\"] === true,\n agentsOverTunnel: args[\"agents-over-tunnel\"] === true,\n fleetOverTunnel: args[\"fleet-over-tunnel\"] === true,\n })\n\n // 3. filtered child env: the vetted secret-stripping allowlist (buildEnv,\n // drops GITHUB_TOKEN/GH_ROUTER_*/ANTHROPIC_AUTH_TOKEN/OPENAI_API_KEY/\n // COPILOT_TOKEN) plus the non-secret ANTHROPIC_BASE_URL/CLAUDE_CONFIG_DIR\n // /model vars. Auth rides the synthetic .credentials.json FILE, not env.\n const anthropicVars = getClaudeCodeEnvVars(\n serverUrl,\n chosenSlug,\n \"standard\",\n enhancements.pickerModels,\n )\n // CLAUDE_CODE_FORK_SUBAGENT silently no-ops under CloudCLI's headless\n // `claude --print` path (the binary's Z8() precondition disables forking\n // without an interactive session), so setting it here is dead weight that\n // would mislead the model into assuming forked subagents inherit full\n // context. Drop it for serve. (getClaudeCodeEnvVars sets it for the\n // interactive `claude` path, where forking works.)\n delete anthropicVars.CLAUDE_CODE_FORK_SUBAGENT\n if (enhancements.nonce) {\n anthropicVars.GH_ROUTER_HOOK_MCP_URL = serverUrl\n anthropicVars.GH_ROUTER_HOOK_NONCE = enhancements.nonce\n }\n const childEnv: NodeJS.ProcessEnv = composeCloudCliChildEnv(anthropicVars)\n // Placeholder only: CloudCLI's Claude provider checks this env var to mark\n // Claude as connected. Claude still authenticates through the proxy mirror.\n childEnv.ANTHROPIC_API_KEY = \"sk-ant-github-router-proxy-placeholder\"\n // CloudCLI's cloned server defines CLAUDE_FALLBACK_MODELS as an in-code object,\n // and the searched server tree has no process.env.CLAUDE_FALLBACK_MODELS reader.\n // Do not set an env var here unless CloudCLI adds a documented string format.\n\n // 4. resolve/install + spawn CloudCLI on a loopback port.\n let cloudcli\n try {\n cloudcli = await resolveCloudCli({\n cliPath: args[\"cloudcli-path\"] as string | undefined,\n noInstall: args[\"no-install\"] === true,\n version: args[\"cloudcli-version\"] as string | undefined,\n })\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n\n const ccPort = await getFreePort()\n const cc = spawnCloudCli({\n serverEntry: cloudcli.serverEntry,\n port: ccPort,\n env: childEnv,\n })\n\n // 5. shutdown wiring (we don't use launchChild — its kind union is closed).\n let shuttingDown = false\n let reverseClose: (() => Promise<void>) | null = null\n let tunnelStop: (() => void) | null = null\n const shutdown = async (): Promise<void> => {\n if (shuttingDown) return\n shuttingDown = true\n if (tunnelStop) {\n try {\n tunnelStop()\n } catch {\n /* best-effort */\n }\n }\n try {\n killChildProcessTree(cc.child, {\n detachedGroup: process.platform !== \"win32\",\n })\n } catch {\n /* best-effort */\n }\n if (reverseClose) await reverseClose().catch(() => {})\n try {\n ;(server as unknown as { close?: () => void }).close?.()\n } catch {\n /* best-effort */\n }\n await stopKeepAwake().catch(() => {})\n await enhancements.cleanup().catch(() => {})\n await removeOwnClaudeConfigMirror().catch(() => {})\n }\n // `shutdown()` must never strand the process. `.then(exit)` alone skips the\n // exit on a REJECTION, so a teardown failure leaves a process that was\n // explicitly trying to die running forever — the worst possible outcome on\n // a shutdown path. `.finally` guarantees the exit either way, and the code\n // is preserved because an operator's script reads it.\n const exitAfterShutdown = (code: number) => {\n void shutdown()\n .catch((err: unknown) => {\n consola.error(\"Shutdown failed; exiting anyway:\", err)\n })\n .finally(() => process.exit(code))\n }\n process.once(\"SIGINT\", () => exitAfterShutdown(0))\n process.once(\"SIGTERM\", () => exitAfterShutdown(0))\n cc.child.once(\"exit\", (code) => {\n if (!shuttingDown) {\n consola.error(`CloudCLI exited unexpectedly (code ${code}).`)\n exitAfterShutdown(1)\n }\n })\n cc.child.once(\"error\", (err) => {\n // spawn failure (ENOENT/EACCES) emits 'error', not 'exit' — an unhandled\n // 'error' crashes the process and bypasses shutdown, orphaning the mirror.\n if (!shuttingDown) {\n consola.error(`CloudCLI failed to start: ${err.message}`)\n exitAfterShutdown(1)\n }\n })\n\n // 6. wait for CloudCLI, seed the user from the GitHub identity, mint a JWT.\n let ready\n try {\n ready = await waitForCloudCliReady(ccPort)\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err))\n await shutdown()\n process.exit(1)\n }\n\n let username = \"github-router\"\n try {\n username = (await getGitHubUser()).login\n } catch {\n // fall back to a generic control-plane user; not fatal\n }\n\n let token: string\n try {\n token = await mintJwt(ccPort, username, ready.needsSetup)\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err))\n await shutdown()\n process.exit(1)\n }\n\n // 7. resolve remote-access allowlist (dev tunnels) + the user-facing port.\n const publicUrls = String(args[\"public-url\"] ?? \"\")\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n const extraAllowedHosts: string[] = []\n const extraAllowedOrigins: string[] = []\n for (const u of publicUrls) {\n try {\n const url = new URL(u)\n extraAllowedHosts.push(url.host)\n extraAllowedOrigins.push(url.origin)\n } catch {\n consola.warn(`Ignoring invalid --public-url \"${u}\"`)\n }\n }\n const tunnelMode = args.tunnel === true\n if (extraAllowedHosts.length || tunnelMode) {\n consola.warn(\n \"Remote access enabled: anyone who reaches this through the tunnel AND passes its authentication gets FULL control-plane access — including a shell on this machine. The tunnel is authenticated (never anonymous); only grant access to people you fully trust.\",\n )\n }\n\n const servePort = await resolveServePort(parsed.port)\n const providerFacade = {\n kindFor: facadeInterceptKind,\n blockedRequest: facadeBlockedRequest,\n rewrite: (kind: string, json: unknown, query: URLSearchParams) =>\n rewriteProviderResponse(\n kind as Parameters<typeof rewriteProviderResponse>[0],\n json,\n {\n getModels: () => state.models,\n // Resolve to the Copilot catalog slug so it matches the picker\n // OPTIONS values (chosenSlug may be a dashed/`[1m]`-bracketed Anthropic\n // slug; the OPTIONS values are catalog ids). Otherwise DEFAULT misses\n // and falls back to the first option.\n defaultModel: resolveModel(chosenSlug),\n claudeConfigDir: PATHS.CLAUDE_CONFIG_DIR,\n },\n query,\n ),\n }\n let reverse\n try {\n reverse = await startReverseProxy({\n targetHost: \"127.0.0.1\",\n targetPort: ccPort,\n bindHost: \"127.0.0.1\",\n bindPort: servePort,\n authToken: token,\n seedToolSettings: process.env.GH_ROUTER_SERVE_NO_AUTO_APPROVE !== \"1\",\n // Routine built-ins + every injected mcp tool name → auto-approve; the\n // two interaction tools (AskUserQuestion/ExitPlanMode) are excluded so\n // they reach the user (SEAMLESS_BUILTIN_TOOLS never lists them).\n seedAllowedTools: [...SEAMLESS_BUILTIN_TOOLS, ...(enhancements.mcpToolNames ?? [])],\n extraAllowedHosts,\n extraAllowedOrigins,\n allowDevtunnelHosts: tunnelMode,\n providerFacade,\n })\n } catch (err) {\n consola.error(\n `Failed to bind the control-plane port ${servePort}: ${\n err instanceof Error ? err.message : String(err)\n }. Is it already in use? Pass --port <n>.`,\n )\n await shutdown()\n process.exit(1)\n }\n reverseClose = reverse.close\n\n // 8. optionally auto-create + host an AUTHENTICATED dev tunnel for remote\n // access. Non-fatal: on any failure keep serving locally (and the\n // *.devtunnels.ms allowlist stays, so a manual `devtunnel host` works).\n let tunnelUrl: string | null = null\n if (tunnelMode) {\n consola.info(\"Creating an authenticated dev tunnel…\")\n try {\n const tunnel = await startDevtunnel(servePort)\n tunnelUrl = tunnel.url\n tunnelStop = tunnel.stop\n } catch (err) {\n const msg = err instanceof DevtunnelError ? err.message : String(err)\n consola.warn(\n `Could not auto-host a dev tunnel: ${msg}\\nServing locally only. To host manually once fixed: devtunnel host -p ${servePort}`,\n )\n }\n }\n\n // 9. ready.\n const publicLines = [\n ...publicUrls.map((u) => `remote: ${u}`),\n ...(tunnelUrl ? [`remote (tunnel): ${tunnelUrl} (stable — bookmark it; same URL every launch)`] : []),\n ]\n const publicBlock = publicLines.length ? `\\n${publicLines.join(\"\\n\")}` : \"\"\n consola.box(\n `🖥️ github-router control plane\\n\\nlocal: ${reverse.url}${publicBlock}\\n\\nsigned in as ${username} · model ${chosenSlug}\\nCtrl+C to stop`,\n )\n if (args[\"no-open\"] !== true) openBrowser(reverse.url)\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"serve-8tqMuzVi.js","names":["fs","fsSync","fs"],"sources":["../src/lib/serve/cloudcli.ts","../src/lib/serve/devtunnel.ts","../src/lib/serve/enhancements.ts","../src/lib/serve/provider-facade.ts","../src/lib/serve/reverse-proxy.ts","../src/serve.ts"],"sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\"\nimport { randomBytes } from \"node:crypto\"\nimport fs from \"node:fs/promises\"\nimport fsSync from \"node:fs\"\nimport http from \"node:http\"\nimport path from \"node:path\"\n\nimport consola from \"consola\"\n\nimport { runCommandCapture, resolveExecutable } from \"../exec\"\nimport { PATHS } from \"../paths\"\nimport { buildEnv } from \"../worker-agent/bash\"\n\nexport const CLOUDCLI_PACKAGE = \"@cloudcli-ai/cloudcli\"\n/** Pinned, tested known-good version. Never `@latest` (see docs/serve-control-plane.md). */\nexport const CLOUDCLI_PINNED_VERSION = \"1.36.1\"\n\n/**\n * Build the env handed to the CloudCLI child. Starts from `buildEnv()` — the\n * vetted secret-stripping ALLOWLIST used for worker bash (drops GITHUB_TOKEN,\n * GH_ROUTER_*, ANTHROPIC_AUTH_TOKEN, OPENAI_API_KEY, COPILOT_TOKEN by omission)\n * — then layers the non-secret Anthropic/Claude vars. The spawned claude still\n * authenticates because that rides the synthetic `.credentials.json` FILE in\n * CLAUDE_CONFIG_DIR, not an env token. Load-bearing: CloudCLI exposes a browser\n * terminal that can read its own env, so no secret may live there.\n */\nexport function composeCloudCliChildEnv(\n anthropicVars: Record<string, string>,\n): NodeJS.ProcessEnv {\n return { ...buildEnv(), ...anthropicVars }\n}\n\nconst SERVER_ENTRY_REL = path.join(\n \"dist-server\",\n \"server\",\n \"index.js\",\n)\n\nexport interface ResolvedCloudCli {\n /** Absolute path to the CloudCLI server entry (dist-server/server/index.js). */\n serverEntry: string\n version: string | null\n}\n\n/** Where the package's server entry lives under a given install root. */\nfunction serverEntryUnderRoot(root: string): string {\n return path.join(root, \"node_modules\", CLOUDCLI_PACKAGE, SERVER_ENTRY_REL)\n}\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await fs.access(p)\n return true\n } catch {\n return false\n }\n}\n\nasync function readVersion(pkgRoot: string): Promise<string | null> {\n try {\n const raw = await fs.readFile(\n path.join(pkgRoot, \"node_modules\", CLOUDCLI_PACKAGE, \"package.json\"),\n \"utf8\",\n )\n return (JSON.parse(raw) as { version?: string }).version ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Resolve the CloudCLI server entry, in order:\n * 1. an explicit `--cloudcli-path` (a package dir OR an install root),\n * 2. an existing pinned install in the router-owned dir,\n * 3. (unless `noInstall`) install the pinned version into the router-owned dir.\n * Never installs globally, never `@latest`.\n */\nexport async function resolveCloudCli(opts: {\n cliPath?: string\n noInstall: boolean\n version?: string\n}): Promise<ResolvedCloudCli> {\n const version = opts.version ?? CLOUDCLI_PINNED_VERSION\n\n // 1. explicit path — accept either a package dir or an install root.\n if (opts.cliPath) {\n const direct = path.join(opts.cliPath, SERVER_ENTRY_REL)\n const underRoot = serverEntryUnderRoot(opts.cliPath)\n if (await exists(direct)) return { serverEntry: direct, version: null }\n if (await exists(underRoot))\n return { serverEntry: underRoot, version: await readVersion(opts.cliPath) }\n throw new Error(\n `--cloudcli-path \"${opts.cliPath}\" does not contain a CloudCLI install (looked for ${SERVER_ENTRY_REL}).`,\n )\n }\n\n // 2. existing router-owned install.\n const root = PATHS.CLOUDCLI_HOME\n const entry = serverEntryUnderRoot(root)\n if (await exists(entry)) {\n return { serverEntry: entry, version: await readVersion(root) }\n }\n\n // 3. install the pinned version locally into the router-owned dir.\n if (opts.noInstall) {\n throw new Error(\n `CloudCLI is not installed and --no-install was given. Install it with:\\n npm i --prefix \"${root}\" ${CLOUDCLI_PACKAGE}@${version}`,\n )\n }\n await installCloudCli(root, version)\n if (!(await exists(entry))) {\n throw new Error(\n `CloudCLI install completed but the server entry is missing (${entry}).`,\n )\n }\n return { serverEntry: entry, version: await readVersion(root) }\n}\n\nasync function installCloudCli(root: string, version: string): Promise<void> {\n const npm = resolveExecutable(\"npm\")\n if (!npm) {\n throw new Error(\n \"npm not found on PATH — cannot install CloudCLI. Install Node.js/npm, or pass --cloudcli-path to an existing install.\",\n )\n }\n await fs.mkdir(root, { recursive: true, mode: 0o700 })\n // A local package.json so `npm i` installs into <root>/node_modules\n // instead of walking up to a parent project.\n const pkgJson = path.join(root, \"package.json\")\n if (!(await exists(pkgJson))) {\n await fs.writeFile(\n pkgJson,\n JSON.stringify({ name: \"gh-router-cloudcli-host\", private: true }, null, 2),\n )\n }\n consola.info(\n `Installing ${CLOUDCLI_PACKAGE}@${version} (one-time, ~a minute; builds native deps)…`,\n )\n const { code, stderr } = await runCommandCapture(\n [\n npm,\n \"install\",\n `${CLOUDCLI_PACKAGE}@${version}`,\n \"--prefix\",\n root,\n \"--no-audit\",\n \"--no-fund\",\n \"--loglevel=error\",\n ],\n { timeoutMs: 10 * 60_000 },\n )\n if (code !== 0) {\n throw new Error(\n `CloudCLI install failed (npm exit ${code}). This often means no network, a corporate proxy, or missing Windows build tools for native deps.\\nInstall manually then re-run with --cloudcli-path:\\n npm i --prefix \"${root}\" ${CLOUDCLI_PACKAGE}@${version}\\n${stderr.slice(0, 400)}`,\n )\n }\n}\n\nexport interface CloudCliProcess {\n child: ChildProcess\n port: number\n databasePath: string\n}\n\n/** Spawn CloudCLI on a loopback port with the given (already-filtered) env. */\nexport function spawnCloudCli(opts: {\n serverEntry: string\n port: number\n env: NodeJS.ProcessEnv\n}): CloudCliProcess {\n const databasePath = path.join(PATHS.CLOUDCLI_HOME, \"auth.db\")\n const node = resolveExecutable(\"node\") ?? process.execPath\n // Diagnostic opt-in: route CloudCLI's stdout+stderr to a log file so its\n // internal errors (SDK/DB/session-create) are inspectable. Default stays\n // \"ignore\" (CloudCLI is noisy and its logs aren't user-facing).\n const logPath = process.env.GH_ROUTER_CLOUDCLI_LOG\n let stdio: \"ignore\" | [\"ignore\", number, number] = \"ignore\"\n if (logPath) {\n try {\n const fd = fsSync.openSync(logPath, \"a\")\n stdio = [\"ignore\", fd, fd]\n } catch {\n /* unwritable log path — fall back to ignore */\n }\n }\n const child = spawn(node, [opts.serverEntry], {\n env: {\n ...opts.env,\n SERVER_PORT: String(opts.port),\n HOST: \"127.0.0.1\", // loopback only — never 0.0.0.0\n DATABASE_PATH: databasePath, // router-owned, isolated from a user's standalone CloudCLI\n // Do NOT set VITE_IS_PLATFORM: it disarms server auth and still shows a\n // login wall in the OSS bundle. Keep JWT auth on; auto-login is done by\n // the reverse proxy injecting the token.\n VITE_IS_PLATFORM: \"\",\n },\n stdio,\n windowsHide: true,\n })\n return { child, port: opts.port, databasePath }\n}\n\n// ---- health + JWT --------------------------------------------------------\n\nfunction req(\n port: number,\n method: \"GET\" | \"POST\",\n urlPath: string,\n body?: unknown,\n): Promise<{ status: number; body: string }> {\n return new Promise((resolve, reject) => {\n const data = body === undefined ? undefined : JSON.stringify(body)\n const r = http.request(\n {\n host: \"127.0.0.1\",\n port,\n path: urlPath,\n method,\n headers: data\n ? { \"content-type\": \"application/json\", \"content-length\": Buffer.byteLength(data) }\n : {},\n },\n (res) => {\n let b = \"\"\n res.on(\"data\", (d) => (b += d))\n res.on(\"end\", () => resolve({ status: res.statusCode ?? 0, body: b }))\n },\n )\n r.on(\"error\", reject)\n if (data) r.write(data)\n r.end()\n })\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\n/** Poll /api/auth/status until the server answers. Returns `needsSetup`. */\nexport async function waitForCloudCliReady(\n port: number,\n timeoutMs = 60_000,\n): Promise<{ needsSetup: boolean }> {\n const deadline = Date.now() + timeoutMs\n while (Date.now() < deadline) {\n try {\n const r = await req(port, \"GET\", \"/api/auth/status\")\n if (r.status === 200) {\n const parsed = JSON.parse(r.body) as { needsSetup?: boolean }\n return { needsSetup: parsed.needsSetup === true }\n }\n } catch {\n // not up yet\n }\n await sleep(1000)\n }\n throw new Error(`CloudCLI did not become ready within ${timeoutMs}ms`)\n}\n\nconst secretFile = () => path.join(PATHS.CLOUDCLI_HOME, \".serve-secret.json\")\n\nasync function loadPassword(username: string): Promise<string | null> {\n try {\n const raw = await fs.readFile(secretFile(), \"utf8\")\n const parsed = JSON.parse(raw) as { username?: string; password?: string }\n if (parsed.username === username && parsed.password) return parsed.password\n } catch {\n // no file yet\n }\n return null\n}\n\nasync function persistPassword(username: string, password: string): Promise<void> {\n // mode 0o600 + a 0o700 parent dir. On Windows the mode bits are ignored, but\n // the dir lives under the per-user home, which is user-ACL'd by default; the\n // password is a low-value local login for a loopback-only, Origin-gated app.\n await fs.mkdir(PATHS.CLOUDCLI_HOME, { recursive: true, mode: 0o700 })\n await fs.writeFile(\n secretFile(),\n JSON.stringify({ username, password }),\n { mode: 0o600 },\n )\n}\n\n/**\n * Ensure a CloudCLI user exists and return a valid JWT. On a fresh (isolated)\n * DB we register (persisting the password only AFTER success); on an existing\n * DB we log in with the persisted password.\n */\nexport async function mintJwt(\n port: number,\n username: string,\n needsSetup: boolean,\n): Promise<string> {\n const existing = await loadPassword(username)\n if (needsSetup) {\n const password = existing ?? randomBytes(24).toString(\"base64url\")\n const r = await req(port, \"POST\", \"/api/auth/register\", { username, password })\n if (r.status === 200) {\n const token = (JSON.parse(r.body) as { token?: string }).token\n if (token) {\n await persistPassword(username, password)\n return token\n }\n }\n // Fall through to login (e.g. a race created the user first).\n }\n const password = existing\n if (!password) {\n throw new Error(\n `CloudCLI already has a user but no stored password for \"${username}\". Delete ${path.join(PATHS.CLOUDCLI_HOME, \"auth.db\")} and retry.`,\n )\n }\n const r = await req(port, \"POST\", \"/api/auth/login\", { username, password })\n if (r.status === 200) {\n const token = (JSON.parse(r.body) as { token?: string }).token\n if (token) return token\n }\n throw new Error(\n `Could not obtain a CloudCLI session token (status ${r.status}). If a stale isolated DB exists, delete ${path.join(PATHS.CLOUDCLI_HOME, \"auth.db\")} and retry.`,\n )\n}\n","import { spawn } from \"node:child_process\"\nimport { createHash } from \"node:crypto\"\nimport os from \"node:os\"\n\nimport { killChildProcessTree, resolveExecutable, runCommandCapture } from \"../exec\"\n\n// Label stamped on every tunnel `serve --tunnel` creates, so our tunnels are\n// identifiable and sweepable (`devtunnel list -l github-router-serve`). Before\n// this, serve minted an anonymous, unlabeled tunnel per launch that was never\n// deleted — they accumulated server-side against the hard\n// `TunnelsPerUserPerCluster` (10) cap until new tunnels were denied.\nexport const SERVE_TUNNEL_LABEL = \"github-router-serve\"\n\n// A per-machine label so reuse/sweep scopes to THIS host and never touches a\n// tunnel another machine (sharing the same dev-tunnels account) owns. Hashed so\n// an arbitrary hostname maps to a label-charset-safe token.\nexport function serveTunnelMachineLabel(hostname: string = os.hostname()): string {\n return `ghr-machine-${createHash(\"sha256\").update(hostname).digest(\"hex\").slice(0, 12)}`\n}\n\n/**\n * Stable, deterministic tunnel ID for THIS machine, so `serve --tunnel` REUSES\n * one tunnel across launches → a stable, bookmarkable public URL. Derived from a\n * hash of the hostname (valid tunnel-id charset: lowercase alnum + hyphens,\n * bounded length). NOTE: the hosting URL's subdomain is a devtunnels-assigned\n * token, NOT this id — the id only anchors find/reuse; the URL is stable because\n * the same tunnel is re-hosted, not because the id appears in it.\n */\nexport function serveTunnelId(hostname: string = os.hostname()): string {\n return `ghr-serve-${createHash(\"sha256\").update(hostname).digest(\"hex\").slice(0, 12)}`\n}\n\n/**\n * Reconcile a reused tunnel's ports to exactly `[desired]`: which existing ports\n * to delete and whether the desired port must be created. Pure (unit-tested).\n * This is why reuse works across `--port` changes — `devtunnel host <id>\n * -p <newport>` fails (\"Batch update of ports is not supported\"), but per-port\n * `port delete` + `port create` does not.\n */\nexport function portsToReconcile(\n currentPorts: number[],\n desired: number,\n): { toDelete: number[]; toCreate: number | null } {\n return {\n toDelete: currentPorts.filter((p) => p !== desired),\n toCreate: currentPorts.includes(desired) ? null : desired,\n }\n}\n\nexport interface TunnelInfo {\n tunnelId: string\n labels?: string[]\n hostConnections?: number\n}\n\n/**\n * The IDs of our own idle tunnels for this machine to sweep — EXCLUDING the\n * stable reused tunnel (`exceptId`), which we keep and re-host. Pure\n * (unit-tested). A tunnel with a live host connection (a concurrent serve\n * instance) is never swept. Cleans up pre-migration random-named tunnels + any\n * crash-orphaned duplicates while preserving the one stable tunnel.\n */\nexport function serveTunnelIdsToSweep(\n tunnels: TunnelInfo[],\n machineLabel: string,\n exceptId?: string,\n): string[] {\n return tunnels\n .filter((t) => (t.labels ?? []).includes(machineLabel) && !t.hostConnections)\n .map((t) => t.tunnelId)\n .filter((id) => !exceptId || id.split(\".\")[0] !== exceptId)\n}\n\n/** List the tunnels serve owns (label-filtered). Best-effort: [] on any error. */\nasync function listServeTunnels(cli: string): Promise<TunnelInfo[]> {\n try {\n const { stdout, code } = await runCommandCapture(\n [cli, \"list\", \"-l\", SERVE_TUNNEL_LABEL, \"-j\"],\n { timeoutMs: 15_000 },\n )\n if (code !== 0) return []\n const parsed = JSON.parse(stdout) as { tunnels?: TunnelInfo[] }\n return Array.isArray(parsed?.tunnels) ? parsed.tunnels : []\n } catch {\n return []\n }\n}\n\n/** Best-effort delete of a single tunnel by full id (e.g. `fancy-fog-x.inc1`). */\nasync function deleteTunnel(cli: string, tunnelId: string): Promise<void> {\n try {\n await runCommandCapture([cli, \"delete\", tunnelId, \"-f\"], { timeoutMs: 15_000 })\n } catch {\n /* best-effort — a stale delete failing must not block hosting */\n }\n}\n\n/** Current port numbers configured on a tunnel. Best-effort: [] on any error. */\nasync function getTunnelPorts(cli: string, tunnelId: string): Promise<number[]> {\n try {\n const { stdout, code } = await runCommandCapture(\n [cli, \"port\", \"list\", tunnelId, \"-j\"],\n { timeoutMs: 15_000 },\n )\n if (code !== 0) return []\n const parsed = JSON.parse(stdout) as { ports?: Array<{ portNumber?: number }> }\n return (parsed?.ports ?? [])\n .map((p) => p.portNumber)\n .filter((n): n is number => typeof n === \"number\")\n } catch {\n return []\n }\n}\n\n/**\n * Ensure the stable per-machine tunnel exists (create it labeled if absent).\n * Returns true when the stable tunnel is present + owned by us (safe to reuse),\n * false to fall back to the anonymous-labeled host path. `existing` is the\n * pre-fetched label-scoped list (so we don't re-list).\n */\nasync function ensureStableTunnel(\n cli: string,\n stableId: string,\n machineLabel: string,\n existing: TunnelInfo[],\n): Promise<boolean> {\n const present = existing.some((t) => t.tunnelId.split(\".\")[0] === stableId)\n if (present) return true\n try {\n // `create` errors (\"Conflict\") if the id is already taken — but we only get\n // here when our label-scoped list did NOT contain it, so a conflict means a\n // DIFFERENT owner holds the id: fall back rather than reuse a foreign tunnel.\n const { code } = await runCommandCapture(\n [cli, \"create\", stableId, \"-l\", SERVE_TUNNEL_LABEL, \"-l\", machineLabel,\n \"-d\", \"github-router-serve-control-plane\"],\n { timeoutMs: 20_000 },\n )\n return code === 0\n } catch {\n return false\n }\n}\n\n/** Reconcile the tunnel's ports to exactly `[port]` (delete stale, create missing). */\nasync function reconcileTunnelPorts(cli: string, tunnelId: string, port: number): Promise<void> {\n const { toDelete, toCreate } = portsToReconcile(await getTunnelPorts(cli, tunnelId), port)\n for (const p of toDelete) {\n await runCommandCapture([cli, \"port\", \"delete\", tunnelId, \"-p\", String(p)], { timeoutMs: 15_000 }).catch(() => {})\n }\n if (toCreate != null) {\n await runCommandCapture([cli, \"port\", \"create\", tunnelId, \"-p\", String(toCreate)], { timeoutMs: 15_000 }).catch(() => {})\n }\n}\n\n\n// Matches the browser URL devtunnel prints, e.g.\n// Hosting port 5454 at https://l3rs99qw-5454.usw2.devtunnels.ms/\nconst DEVTUNNEL_URL_RE =\n /https:\\/\\/[a-z0-9][a-z0-9-]*(?:\\.[a-z0-9-]+)*\\.devtunnels\\.ms\\/?/i\n\nexport type DevtunnelFailureKind =\n | \"not-installed\"\n | \"not-logged-in\"\n | \"timeout\"\n | \"exited\"\n | \"spawn\"\n\nexport class DevtunnelError extends Error {\n readonly kind: DevtunnelFailureKind\n constructor(kind: DevtunnelFailureKind, message: string) {\n super(message)\n this.name = \"DevtunnelError\"\n this.kind = kind\n }\n}\n\nexport interface DevtunnelHandle {\n url: string\n stop: () => void\n}\n\n/** Absolute path to the `devtunnel` CLI, or null if not on PATH. */\nexport function resolveDevtunnelCli(): string | null {\n return resolveExecutable(\"devtunnel\")\n}\n\n/** Best-effort login check via `devtunnel user show`. */\nexport async function isDevtunnelLoggedIn(cli: string): Promise<boolean> {\n try {\n const { stdout, stderr, code } = await runCommandCapture(\n [cli, \"user\", \"show\"],\n { timeoutMs: 15_000 },\n )\n const out = `${stdout}\\n${stderr}`\n if (/not logged in/i.test(out)) return false\n if (/logged in as/i.test(out)) return true\n return code === 0\n } catch {\n return false\n }\n}\n\n/** Extract the dev-tunnel browser URL from CLI output. Exported for tests. */\nexport function parseDevtunnelUrl(text: string): string | null {\n const m = text.match(DEVTUNNEL_URL_RE)\n return m ? m[0].replace(/\\/+$/, \"\") : null\n}\n\n/**\n * Spawn `devtunnel <args>` and resolve once it prints its public URL. Shared by\n * the stable-reuse and anonymous-labeled host paths. Rejects (DevtunnelError) on\n * timeout, early exit, an auth error in the output, or a spawn failure.\n */\nfunction hostTunnelProcess(cli: string, args: string[], timeoutMs: number): Promise<DevtunnelHandle> {\n const child = spawn(cli, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"], windowsHide: true })\n return new Promise<DevtunnelHandle>((resolve, reject) => {\n let settled = false\n const stop = () => {\n // Tree-kill: the devtunnel process spawns a helper and a plain\n // child.kill() leaves the tunnel up (verified). taskkill /T /F on\n // Windows, process-group SIGTERM on POSIX.\n try {\n killChildProcessTree(child, { detachedGroup: process.platform !== \"win32\" })\n } catch {\n try {\n child.kill()\n } catch {\n /* already gone */\n }\n }\n }\n const finish = (fn: () => void) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n fn()\n }\n const timer = setTimeout(\n () =>\n finish(() => {\n stop()\n reject(new DevtunnelError(\"timeout\", \"dev tunnel did not report a URL in time\"))\n }),\n timeoutMs,\n )\n const onData = (buf: Buffer) => {\n const text = String(buf)\n const url = parseDevtunnelUrl(text)\n if (url) {\n finish(() => resolve({ url, stop }))\n return\n }\n if (/not logged in|login (required|expired)|unauthorized/i.test(text)) {\n finish(() => {\n stop()\n reject(new DevtunnelError(\"not-logged-in\", \"dev tunnel authentication failed; run `devtunnel user login`\"))\n })\n }\n }\n child.stdout?.on(\"data\", onData)\n child.stderr?.on(\"data\", onData)\n child.once(\"error\", (err) => finish(() => reject(new DevtunnelError(\"spawn\", err.message))))\n child.once(\"exit\", (code) =>\n finish(() =>\n reject(new DevtunnelError(\"exited\", `devtunnel exited (code ${code}) before printing a URL`)),\n ),\n )\n })\n}\n\n/**\n * Host an AUTHENTICATED (never anonymous) dev tunnel forwarding `port`, resolving\n * once its public URL is printed. NEVER `--allow-anonymous` (owner-only access).\n *\n * REUSES a stable per-machine tunnel (`serveTunnelId`) across launches → a\n * STABLE, bookmarkable public URL. Each launch: ensure the stable tunnel exists\n * (create it labeled if absent), reconcile its ports to exactly `[port]` via\n * per-port delete/create (`host <id> -p <newport>` can't change ports — it 400s\n * \"Batch update of ports is not supported\"), sweep our OTHER idle labeled tunnels\n * (pre-migration random-named ones / crash orphans), then `host <id>`. The URL's\n * subdomain is a devtunnels-assigned token (not the id), but it's stable because\n * the SAME tunnel is re-hosted every launch.\n *\n * If any stable-path step fails (id already taken by another owner, CLI hiccup),\n * falls back to hosting a fresh anonymous-labeled tunnel (the prior behavior) so\n * `--tunnel` always works — just without a stable URL that launch.\n */\nexport async function startDevtunnel(\n port: number,\n opts: { timeoutMs?: number } = {},\n): Promise<DevtunnelHandle> {\n const cli = resolveDevtunnelCli()\n if (!cli) {\n throw new DevtunnelError(\n \"not-installed\",\n \"The `devtunnel` CLI is not installed. Install it (winget install Microsoft.devtunnel / brew install --cask devtunnel) then run `devtunnel user login`.\",\n )\n }\n if (!(await isDevtunnelLoggedIn(cli))) {\n throw new DevtunnelError(\n \"not-logged-in\",\n \"Not signed in to dev tunnels. Run `devtunnel user login` (or `devtunnel user login -g` for GitHub) first.\",\n )\n }\n\n const timeoutMs = opts.timeoutMs ?? 40_000\n const machineLabel = serveTunnelMachineLabel()\n const stableId = serveTunnelId()\n\n // Stable-reuse path (best-effort; any failure falls through to anonymous host).\n try {\n const owned = await listServeTunnels(cli)\n if (await ensureStableTunnel(cli, stableId, machineLabel, owned)) {\n await reconcileTunnelPorts(cli, stableId, port)\n // Sweep our OTHER idle labeled tunnels but KEEP the stable one.\n for (const id of serveTunnelIdsToSweep(owned, machineLabel, stableId)) {\n await deleteTunnel(cli, id)\n }\n return await hostTunnelProcess(cli, [\"host\", stableId], timeoutMs)\n }\n } catch {\n /* fall through to the anonymous-labeled host path */\n }\n\n // Fallback: sweep our idle labeled tunnels, then host a fresh anonymous one.\n // Labels are SEPARATE `-l` flags — the service rejects a label containing a\n // space (`must match '[\\w-=]{1,50}'`), so they can't be joined into one arg.\n try {\n for (const id of serveTunnelIdsToSweep(await listServeTunnels(cli), machineLabel)) {\n await deleteTunnel(cli, id)\n }\n } catch {\n /* best-effort sweep — never blocks hosting */\n }\n return await hostTunnelProcess(\n cli,\n [\"host\", \"-p\", String(port), \"-l\", SERVE_TUNNEL_LABEL, \"-l\", machineLabel, \"-d\", \"github-router-serve-control-plane\"],\n timeoutMs,\n )\n}\n","import path from \"node:path\"\n\nimport consola from \"consola\"\n\nimport { injectAttributionSuppressionIntoSettingsFile } from \"../attribution-settings\"\nimport {\n BUILTIN_SUBAGENT_DEFINITIONS,\n injectPeerMcpIntoMirror,\n resolveGroupKeysFromMirror,\n workersKeyOf,\n writePeerMcpRuntimeFiles,\n} from \"../codex-mcp-config\"\nimport {\n appendPeerAwarenessToMirroredClaudeMd,\n appendToolbeltAwarenessToMirroredClaudeMd,\n buildOperatingDefaultsDirective,\n type NativeAgentAvailability,\n prependOperatingDefaultsToMirroredClaudeMd,\n prependStyleDirectiveToMirroredClaudeMd,\n} from \"../claude-md-injection\"\nimport { type SelfInvocation } from \"../hook-launcher/self-invocation\"\nimport { INJECTED_SKILLS, writeInjectedSkill } from \"../injected-skills\"\nimport { injectModelPickerSettingsFile } from \"../model-picker-settings\"\nimport {\n configureServeDefaultPermissionMode,\n injectAllowRules,\n planModeAllowRules,\n sanitizeServeSettingsEnv,\n} from \"../mcp-permissions-settings\"\nimport {\n agentToolsEnabled,\n browseAgentEnabled,\n browserCompoundToolsEnabled,\n browserToolsEnabled,\n fleetToolsEnabled,\n geminiAvailable,\n nativeSubagentModel,\n brainstormModel,\n reviewerModel,\n reviewerFastModel,\n resolveGeminiReviewModel,\n scoutModel,\n scribeModel,\n implementerFastModel,\n generalPurposeFastModel,\n standInToolEnabled,\n workerToolsEnabled,\n} from \"../mcp-capabilities\"\nimport { buildPlanReviewHookCommand, planReviewEnabled } from \"../orchestration/plan-review-hook\"\nimport { buildPromptSubmitHookCommand } from \"../orchestration/prompt-submit-hook\"\nimport { injectStopHookIntoSettingsFile } from \"../orchestration/stop-gate-hook\"\nimport { PATHS } from \"../paths\"\nimport {\n buildPeerAwarenessSnippet,\n enumerateInjectedMcpToolNames,\n type McpGroup,\n} from \"../peer-mcp-personas\"\nimport { state } from \"../state\"\nimport { availableToolCommands, buildToolbeltAwareness, toolbeltEnabled } from \"../toolbelt\"\nimport {\n activeDispatchModes,\n buildWorkerGuardHookCommand,\n guardToolMatcher,\n} from \"../worker-dispatch\"\n\nexport interface ServeEnhancementsHandle {\n cleanup: () => Promise<void>\n nonce?: string\n /** Effective mirror-level modelPicker rows, including a preserved user picker.\n * Used to derive the launch-global compaction minimum before CloudCLI starts. */\n pickerModels?: string[]\n /** Exact `mcp__<key>__<tool>` names of every injected MCP tool — seeded into\n * the reverse-proxy's `localStorage['claude-settings'].allowedTools` so\n * CloudCLI's `canUseTool` auto-approves them in PLAN mode. */\n mcpToolNames?: string[]\n}\n\nexport interface ServeEnhancementOpts {\n /** Stable invocation baked into every persisted hook/helper command. */\n selfInvocation: SelfInvocation\n /** Route Codex personas through a local `codex mcp-server` (stdio) when the backend resolved to \"cli\". */\n codexCli?: boolean\n /** True when the control plane is reachable beyond loopback (a --tunnel or --public-url is active). Gates the highest-blast-radius capabilities OFF by default: the server-side browser MCP (a session-hijack / SSRF / cloud-metadata primitive when reachable remotely) and first-mate (mints a repo+workflow GitHub write token). */\n tunnelExposed?: boolean\n /** Operator opt-in to expose the browser MCP over the tunnel despite the risk. */\n browseOverTunnel?: boolean\n /** Operator opt-in to expose first-mate over the tunnel despite the risk. */\n agentsOverTunnel?: boolean\n /** Operator opt-in to expose the fleet MCP over the tunnel despite the risk\n * (fleet drives remote coding sessions with the operator's stored fleet\n * credentials — an equal-or-higher blast radius than the browser MCP). */\n fleetOverTunnel?: boolean\n}\n\nconst NOOP: ServeEnhancementsHandle = { cleanup: async () => {} }\n\nexport function resolveServeNativeAgentOptions() {\n const models = {\n nativeSubagentModel: nativeSubagentModel(),\n reviewerModel: reviewerModel(),\n reviewerFastModel: reviewerFastModel(),\n brainstormModel: brainstormModel(),\n scoutModel: scoutModel(),\n scribeModel: scribeModel(),\n implementerFastModel: implementerFastModel(),\n generalPurposeFastModel: generalPurposeFastModel(),\n }\n const nativeAvailability: NativeAgentAvailability = {\n scoutAvailable: models.scoutModel != null,\n implementerFastAvailable: models.implementerFastModel != null,\n reviewerFastAvailable: models.reviewerFastModel != null,\n generalPurposeFastAvailable: models.generalPurposeFastModel != null,\n }\n return { models, nativeAvailability }\n}\n\n/**\n * Wire the github-router enhancement layer into the router-owned\n * CLAUDE_CONFIG_DIR mirror so a CloudCLI-spawned Claude session gets the same\n * tools `github-router claude` provides:\n * - the scoped MCP servers (peers / search / orchestrate, plus workers /\n * decide / browser / fleet / first-mate when their gate passes) — the\n * SDK-spawned claude reads them from the mirror's `.claude.json`,\n * - the peer-critic / worker / implementer subagents (`.md` files written into\n * the mirror's `agents/`),\n * - the gh-* skills (research / orchestrate / floor-keeper / worker).\n *\n * Best-effort: on any failure Claude still works, just without the extras.\n * Must run AFTER `ensureClaudeConfigMirror()` and BEFORE CloudCLI is spawned.\n * Tab-specific bits (ai-or-die hooks, artifact review) are intentionally left\n * out — they don't apply to the serve control plane.\n */\nexport async function provisionServeEnhancements(\n serverUrl: string,\n opts: ServeEnhancementOpts,\n): Promise<ServeEnhancementsHandle> {\n try {\n const tunnelExposed = opts.tunnelExposed === true\n const browseAllowed = browserToolsEnabled() && (!tunnelExposed || opts.browseOverTunnel === true)\n const firstMateAllowed = agentToolsEnabled() && (!tunnelExposed || opts.agentsOverTunnel === true)\n const fleetAllowed = fleetToolsEnabled() && (!tunnelExposed || opts.fleetOverTunnel === true)\n\n const enabledGroups: McpGroup[] = [\"peers\", \"search\", \"orchestrate\"]\n if (workerToolsEnabled()) enabledGroups.push(\"workers\")\n if (standInToolEnabled()) enabledGroups.push(\"decide\")\n if (browseAllowed) enabledGroups.push(\"browser\")\n if (fleetAllowed) enabledGroups.push(\"fleet\")\n if (firstMateAllowed) enabledGroups.push(\"first-mate\")\n\n const gem = geminiAvailable()\n const { keys: groupKeys } = await resolveGroupKeysFromMirror(enabledGroups)\n // Resolved once and reused by the `.md` generation, the awareness snippet,\n // and the operating-defaults directive, so the three surfaces cannot\n // disagree about which conditionally-emitted natives exist.\n const { models: nativeModels, nativeAvailability } =\n resolveServeNativeAgentOptions()\n\n const runtime = await writePeerMcpRuntimeFiles(serverUrl, {\n codexCli: opts.codexCli === true,\n selfInvocation: opts.selfInvocation,\n geminiAvailable: gem,\n geminiModel: resolveGeminiReviewModel(),\n groupKeys,\n workerToolsAvailable: workerToolsEnabled(),\n browseAvailable: browseAllowed && browseAgentEnabled(),\n ...nativeModels,\n // Serve-only: register Claude Code's built-in Explore/Plan/general-purpose\n // subagents (the Agent SDK doesn't) so the model's habitual Agent() calls\n // resolve. Never passed by `github-router claude` (would shadow the CLI's\n // native built-ins).\n builtinSubagents: BUILTIN_SUBAGENT_DEFINITIONS,\n })\n // The proxy's /mcp handler authorizes tool calls against this per-launch\n // nonce (the same value baked into the mirrored mcpServers Authorization).\n state.peerMcpNonce = runtime.nonce\n\n const injected = await injectPeerMcpIntoMirror(serverUrl, {\n codexCli: opts.codexCli === true,\n selfInvocation: opts.selfInvocation,\n geminiAvailable: gem,\n groupKeys,\n nonce: runtime.nonce,\n })\n\n const peerSnippet = buildPeerAwarenessSnippet({\n codexCli: opts.codexCli === true,\n geminiAvailable: gem,\n geminiModel: resolveGeminiReviewModel(),\n workerToolsAvailable: workerToolsEnabled(),\n standInAvailable: standInToolEnabled(),\n browseAvailable: browseAllowed,\n compoundBrowseAvailable: browseAllowed && browserCompoundToolsEnabled(),\n powerBrowseAvailable: browseAllowed && state.powerBrowseEnabled,\n agentToolsAvailable: firstMateAllowed,\n ...nativeAvailability,\n groupKeys,\n })\n await appendPeerAwarenessToMirroredClaudeMd(peerSnippet).catch((err) =>\n consola.warn(`Peer-awareness CLAUDE.md append failed: ${String(err)}`),\n )\n await prependStyleDirectiveToMirroredClaudeMd().catch((err) =>\n consola.warn(`Style-directive CLAUDE.md prepend failed: ${String(err)}`),\n )\n await prependOperatingDefaultsToMirroredClaudeMd(\n buildOperatingDefaultsDirective(nativeAvailability),\n ).catch((err) =>\n consola.warn(`Operating-defaults CLAUDE.md prepend failed: ${String(err)}`),\n )\n if (toolbeltEnabled()) {\n const toolbeltLine = buildToolbeltAwareness(availableToolCommands())\n if (toolbeltLine) {\n await appendToolbeltAwarenessToMirroredClaudeMd(toolbeltLine).catch((err) =>\n consola.warn(`Toolbelt CLAUDE.md append failed: ${String(err)}`),\n )\n }\n }\n\n // gh-* skills, minus the operator/tab-specific ones (first-mate, artifact).\n let skillsWritten = 0\n for (const s of INJECTED_SKILLS) {\n if (s.name.startsWith(\"gh-first-mate\") || s.name === \"gh-artifact-review\") {\n continue\n }\n const r = await writeInjectedSkill(s.name, s.md).catch(() => ({\n written: false,\n }))\n if (r.written) skillsWritten++\n }\n\n const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n\n // CloudCLI reads the same per-launch mirror as the CLI launcher. Add the\n // Standard profile's live-catalog-gated rows through the supported picker\n // setting before CloudCLI starts. Preserve an existing user-curated picker;\n // explicit model selection remains available if this best-effort write fails.\n const picker = await injectModelPickerSettingsFile(settingsPath, \"standard\").catch((err) => {\n consola.warn(`Could not inject serve model-picker settings: ${String(err)}`)\n return undefined\n })\n if (picker?.reason === \"user-set\") {\n consola.info(\n \"Serve: preserving the user-configured modelPicker; router-curated rows were not merged.\",\n )\n }\n\n // Suppress AI attribution in the mirror's settings.json (no-op if the user\n // already configured it).\n await injectAttributionSuppressionIntoSettingsFile(settingsPath).catch(() => {})\n\n // Strip serve-inappropriate CLAUDE_CODE_* env keys the user may have set in\n // their real settings.json — notably CLAUDE_CODE_COORDINATOR_MODE, which\n // CloudCLI applies via settingSources and which strips the single serve chat\n // agent down to delegation-only tools (a direct Glob/Read/Bash then fails\n // \"not enabled in this context\"). UNCONDITIONAL: a working toolset is needed\n // even when GH_ROUTER_SERVE_NO_AUTO_APPROVE=1 keeps permission prompts on.\n const strip = await sanitizeServeSettingsEnv(settingsPath).catch((err) => {\n consola.warn(`Could not sanitize serve settings env: ${String(err)}`)\n return { removed: [] as string[] }\n })\n if (strip.removed.length) {\n consola.info(`Serve: removed ${strip.removed.join(\", \")} from the mirror settings (single-agent surface).`)\n }\n\n // Serve's permission model: \"seamless routine + decisions to the user\".\n // Set the mirror's default permission mode to \"default\" (NON-bypass) so\n // CloudCLI's canUseTool callback stays live — routine tools pre-approved via\n // the seeded allow-list run with no prompt, while AskUserQuestion/ExitPlanMode\n // (never allow-listed) reach the human as real widgets. Bypass would skip\n // canUseTool and auto-answer those two. Opt out with GH_ROUTER_SERVE_NO_AUTO_APPROVE=1\n // (then CloudCLI prompts on every not-yet-approved tool).\n if (process.env.GH_ROUTER_SERVE_NO_AUTO_APPROVE !== \"1\") {\n await configureServeDefaultPermissionMode(settingsPath).catch((err) =>\n consola.warn(`Could not set serve permission defaults: ${String(err)}`),\n )\n }\n\n // Auto-approve github-router's injected MCP servers PLUS the native read-only\n // research tools (WebSearch/WebFetch) via allow rules (MCP on the resolved\n // keys). Redundant under the bypass default above, but load-bearing the moment\n // a serve user switches the composer to PLAN mode: plan mode re-gates tools,\n // and this is exactly the research surface wanted while planning. Runs AFTER\n // the bypass config so the entries survive its allow-list clear. Uses the\n // authoritative injected-server list (`injected.serversAdded` — includes the\n // `codex-cli` stdio server), falling back to the resolved keys + codex-cli on\n // collision.\n const injectedServerKeys = injected.ok\n ? injected.serversAdded\n : [\n ...Object.values(groupKeys).filter((k): k is string => Boolean(k)),\n ...(opts.codexCli === true ? [\"codex-cli\"] : []),\n ]\n await injectAllowRules(settingsPath, planModeAllowRules(injectedServerKeys)).catch((err) =>\n consola.warn(`Could not auto-approve injected tools: ${String(err)}`),\n )\n\n if (workerToolsEnabled()) {\n const promptCmd = buildPromptSubmitHookCommand(opts.selfInvocation)\n await injectStopHookIntoSettingsFile(settingsPath, promptCmd, \"UserPromptSubmit\", 45).catch(\n (err) => consola.warn(`Could not register the UserPromptSubmit hook: ${String(err)}`),\n )\n\n if (!injected.ok) {\n consola.warn(\n \"Workers non-blocking guard NOT registered: subagent MCP injection fell back to parent-only, so worker-* dispatchers cannot reach the workers server. Raw worker tools remain usable on the main thread.\",\n )\n } else if (process.env.GH_ROUTER_DISABLE_WORKER_GUARD !== \"1\") {\n const workersKey = workersKeyOf(groupKeys)\n const modes = activeDispatchModes({ browse: browseAllowed && browseAgentEnabled() })\n const cmd = buildWorkerGuardHookCommand(\n opts.selfInvocation,\n workersKey,\n modes,\n )\n const matcher = guardToolMatcher(workersKey, modes)\n await injectStopHookIntoSettingsFile(settingsPath, cmd, \"PreToolUse\", 10, matcher).catch(\n (err) => consola.warn(`Could not register the workers PreToolUse guard hook: ${String(err)}`),\n )\n } else {\n consola.info(\n \"Workers non-blocking guard disabled via GH_ROUTER_DISABLE_WORKER_GUARD=1; raw worker tools remain callable on the main thread.\",\n )\n }\n }\n\n if (planReviewEnabled()) {\n try {\n const command = buildPlanReviewHookCommand(opts.selfInvocation)\n // Advisory PostToolUse(ExitPlanMode) hook: non-blocking; findings surface on the next prompt.\n // Verified (scripts/verify-plan-mode-cloudcli.mjs): ExitPlanMode runs as a real tool under\n // CloudCLI's SDK path, and settingSources=['project','user','local'] loads this mirror\n // settings.json hook, so PostToolUse(ExitPlanMode) fires on approval+execution.\n await injectStopHookIntoSettingsFile(settingsPath, command, \"PostToolUse\", undefined, \"ExitPlanMode\")\n } catch (err) {\n consola.warn(`Could not register the advisory plan-review hook: ${String(err)}`)\n }\n }\n\n const servers = injected.ok\n ? injected.serversAdded.join(\", \")\n : \"parent-only (user mcpServers collision)\"\n consola.info(\n `github-router tools wired into Claude sessions: MCP [${servers}], ${runtime.personas.length} subagents, ${skillsWritten} skills.`,\n )\n return {\n cleanup: runtime.cleanup,\n nonce: runtime.nonce,\n pickerModels: picker?.models,\n mcpToolNames: enumerateInjectedMcpToolNames(groupKeys, { codexCli: opts.codexCli === true }),\n }\n } catch (err) {\n consola.warn(\n `Could not wire the github-router MCP/skills layer: ${\n err instanceof Error ? err.message : String(err)\n }. Claude still works, without the extra tools.`,\n )\n return NOOP\n }\n}\n","import fs from \"node:fs/promises\"\nimport path from \"node:path\"\n\nimport type { Model, ModelsResponse } from \"~/services/copilot/get-models\"\n\nexport interface ProviderFacadeContext {\n getModels: () => ModelsResponse | undefined\n defaultModel: string\n claudeConfigDir: string\n}\n\nexport type ProviderFacadeKind = \"models\" | \"mcp\" | \"skills\" | \"commands\" | \"auth\"\n\ntype JsonObject = Record<string, unknown>\n\nfunction isObject(value: unknown): value is JsonObject {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\nfunction envelopedData(value: unknown): JsonObject | null {\n if (!isObject(value) || !isObject(value.data)) return null\n return value.data\n}\n\nfunction cloneObject<T extends JsonObject>(value: T): JsonObject {\n return { ...value }\n}\n\n/**\n * Requests serve answers DIRECTLY (never forwarded to CloudCLI) with a canned\n * JSON body. `POST /api/system/update` is CloudCLI's \"update available\" button,\n * which runs a GLOBAL `npm install -g @cloudcli-ai/cloudcli@latest` in the user's\n * home dir (`server/index.js`) — mutating their global npm and never applying to\n * serve's pinned, router-owned install. Blocking it prevents that surprise\n * mutation and tells the user how updates actually work. HTTP 200 + `success:\n * false` so the UI neither runs the update nor throws an error toast. Returns\n * null for anything not blocked.\n */\nexport function facadeBlockedRequest(\n method: string,\n pathname: string,\n): { status: number; json: JsonObject } | null {\n if (method.toUpperCase() === \"POST\" && pathname === \"/api/system/update\") {\n return {\n status: 200,\n json: {\n success: false,\n managed: true,\n message:\n \"CloudCLI is managed by github-router (a pinned, router-owned install). \"\n + \"To update, run `npm i -g github-router@latest`, then restart `github-router serve`.\",\n },\n }\n }\n return null\n}\n\nexport function facadeInterceptKind(\n method: string,\n pathname: string,\n): ProviderFacadeKind | null {\n const normalizedMethod = method.toUpperCase()\n if (normalizedMethod === \"POST\" && pathname === \"/api/commands/list\") {\n return \"commands\"\n }\n if (normalizedMethod !== \"GET\") return null\n\n switch (pathname) {\n case \"/api/providers/claude/models\":\n return \"models\"\n case \"/api/providers/claude/mcp/servers\":\n return \"mcp\"\n case \"/api/providers/claude/skills\":\n return \"skills\"\n case \"/api/providers/claude/auth/status\":\n return \"auth\"\n default:\n return null\n }\n}\n\nexport async function rewriteProviderResponse(\n kind: ProviderFacadeKind,\n upstreamJson: unknown,\n ctx: ProviderFacadeContext,\n query: URLSearchParams,\n): Promise<unknown | null> {\n try {\n switch (kind) {\n case \"models\":\n return rewriteModels(upstreamJson, ctx)\n case \"mcp\":\n return rewriteMcp(upstreamJson, ctx, query)\n case \"skills\":\n return rewriteSkills(upstreamJson, ctx)\n case \"commands\":\n return rewriteCommands(upstreamJson, ctx)\n case \"auth\":\n return rewriteAuth(upstreamJson)\n }\n } catch {\n return null\n }\n}\n\nfunction rewriteModels(upstreamJson: unknown, ctx: ProviderFacadeContext): unknown | null {\n if (!isObject(upstreamJson)) return null\n const data = envelopedData(upstreamJson)\n if (!data || !isObject(data.models) || data.cache === undefined) return null\n\n const catalog = ctx.getModels()\n const models = catalog?.data\n if (!Array.isArray(models) || models.length === 0) return null\n\n const options = models\n .filter(isPickerModel)\n .map((m) => {\n const option: { value: string; label: string; description: string; effort?: JsonObject } = {\n value: m.id,\n label: m.name || m.id,\n description: `${m.vendor} · ${m.capabilities.family}`,\n }\n // Emit the effort selector the CloudCLI UI + `resolveClaudeEffort` need:\n // `effort.values` is `[{value}]` and the server maps it back with\n // `.map(v => v.value)` to validate a client-picked effort before passing\n // it to the binary. Derived from the Copilot catalog's per-model\n // `reasoning_effort` allowlist; omitted for non-reasoning models (no\n // selector shown). Default to `high` when supported (matches the shim's\n // default), else the model's top tier.\n const efforts = m.capabilities.supports?.reasoning_effort\n if (Array.isArray(efforts) && efforts.length > 0) {\n option.effort = {\n default: efforts.includes(\"high\") ? \"high\" : efforts[efforts.length - 1],\n values: efforts.map((e) => ({ value: e })),\n }\n }\n return option\n })\n if (options.length === 0) return null\n\n const values = new Set(options.map((o) => o.value))\n const upstreamDefault = typeof data.models.DEFAULT === \"string\" ? data.models.DEFAULT : undefined\n const DEFAULT = values.has(ctx.defaultModel)\n ? ctx.defaultModel\n : (options[0]?.value ?? upstreamDefault)\n\n const nextData = cloneObject(data)\n nextData.models = { OPTIONS: options, DEFAULT }\n return { ...upstreamJson, data: nextData }\n}\n\nfunction isPickerModel(model: unknown): model is Model {\n return isObject(model)\n && typeof model.id === \"string\"\n && model.model_picker_enabled !== false\n && typeof model.vendor === \"string\"\n && (model.name === undefined || typeof model.name === \"string\")\n && isObject(model.capabilities)\n && typeof model.capabilities.family === \"string\"\n}\n\nasync function rewriteMcp(\n upstreamJson: unknown,\n ctx: ProviderFacadeContext,\n query: URLSearchParams,\n): Promise<unknown | null> {\n if (!isObject(upstreamJson) || query.get(\"scope\") !== \"user\") return null\n const data = envelopedData(upstreamJson)\n if (!data || !Array.isArray(data.servers)) return null\n\n const mirror = await readJson(path.join(ctx.claudeConfigDir, \".claude.json\"))\n if (!isObject(mirror) || !isObject(mirror.mcpServers)) return null\n\n const existing = new Set(\n data.servers\n .filter(isObject)\n .map((s) => s.name)\n .filter((name): name is string => typeof name === \"string\"),\n )\n const additions: JsonObject[] = []\n for (const [name, entry] of Object.entries(mirror.mcpServers)) {\n if (existing.has(name) || !isObject(entry)) continue\n const transport = entry.type === \"http\" ? \"http\" : \"stdio\"\n const server: JsonObject = {\n provider: \"claude\",\n name,\n scope: \"user\",\n transport,\n }\n if (typeof entry.url === \"string\") server.url = entry.url\n additions.push(server)\n existing.add(name)\n }\n if (additions.length === 0) return null\n\n const nextData = cloneObject(data)\n nextData.servers = [...data.servers, ...additions]\n return { ...upstreamJson, data: nextData }\n}\n\nasync function rewriteSkills(\n upstreamJson: unknown,\n ctx: ProviderFacadeContext,\n): Promise<unknown | null> {\n if (!isObject(upstreamJson)) return null\n const data = envelopedData(upstreamJson)\n if (!data || !Array.isArray(data.skills)) return null\n\n const skills = await readSkillDirs(path.join(ctx.claudeConfigDir, \"skills\"))\n if (skills.length === 0) return null\n\n const existing = new Set(\n data.skills\n .filter(isObject)\n .map((s) => s.command)\n .filter((command): command is string => typeof command === \"string\"),\n )\n const additions = skills\n .filter((skill) => !existing.has(skill.name))\n .map((skill) => ({\n provider: \"claude\",\n name: skill.name,\n description: skill.description,\n command: skill.name,\n scope: \"user\",\n // sourcePath intentionally omitted — the UI dispatches on `command`/`name`,\n // and an absolute mirror path would leak the home dir / username to the browser.\n }))\n if (additions.length === 0) return null\n\n const nextData = cloneObject(data)\n nextData.skills = [...data.skills, ...additions]\n return { ...upstreamJson, data: nextData }\n}\n\nasync function rewriteCommands(\n upstreamJson: unknown,\n ctx: ProviderFacadeContext,\n): Promise<unknown | null> {\n if (!isObject(upstreamJson) || !Array.isArray(upstreamJson.custom)) return null\n\n const dirs = [\n path.join(ctx.claudeConfigDir, \"commands\"),\n path.join(ctx.claudeConfigDir, \".claude\", \"commands\"),\n ]\n const commandFiles = (await Promise.all(dirs.map(readCommandFiles))).flat()\n if (commandFiles.length === 0) return null\n\n const existing = new Set(\n upstreamJson.custom\n .filter(isObject)\n .map((c) => c.name)\n .filter((name): name is string => typeof name === \"string\"),\n )\n const additions = commandFiles\n .filter((command) => !existing.has(command.name))\n .map((command) => ({\n name: command.name,\n // Relative path only — an absolute mirror path would leak the home dir /\n // username to the browser. The UI dispatches on `name`.\n path: command.relativePath,\n relativePath: command.relativePath,\n description: command.description,\n namespace: \"user\",\n metadata: { type: \"custom\" },\n }))\n if (additions.length === 0) return null\n\n const custom = [...upstreamJson.custom, ...additions]\n return {\n ...upstreamJson,\n custom,\n count: countCommands(upstreamJson.builtIn) + custom.length,\n }\n}\n\nfunction rewriteAuth(upstreamJson: unknown): unknown | null {\n if (!isObject(upstreamJson)) return null\n const data = envelopedData(upstreamJson)\n if (!data) return null\n\n return {\n ...upstreamJson,\n data: {\n ...data,\n authenticated: true,\n installed: true,\n },\n }\n}\n\nasync function readJson(filePath: string): Promise<unknown | null> {\n try {\n return JSON.parse(await fs.readFile(filePath, \"utf8\"))\n } catch {\n return null\n }\n}\n\nasync function readSkillDirs(skillsDir: string): Promise<Array<{ name: string; description: string; sourcePath: string }>> {\n try {\n const entries = await fs.readdir(skillsDir, { withFileTypes: true })\n const skills = await Promise.all(\n entries\n .filter((entry) => entry.isDirectory() && entry.name.startsWith(\"gh-\"))\n .map(async (entry) => {\n const sourcePath = path.join(skillsDir, entry.name, \"SKILL.md\")\n let description = \"\"\n try {\n const md = await fs.readFile(sourcePath, \"utf8\")\n description = parseFrontmatterField(md, \"description\") ?? \"\"\n } catch {\n // Keep the skill visible even if only the directory is readable.\n }\n const name = entry.name\n return { name, description, sourcePath }\n }),\n )\n return skills\n } catch {\n return []\n }\n}\n\nasync function readCommandFiles(commandsDir: string): Promise<Array<{ name: string; relativePath: string; filePath: string; description: string }>> {\n try {\n const stat = await fs.stat(commandsDir)\n if (!stat.isDirectory()) return []\n } catch {\n return []\n }\n return readCommandFilesRecursive(commandsDir, commandsDir)\n}\n\nasync function readCommandFilesRecursive(\n root: string,\n dir: string,\n): Promise<Array<{ name: string; relativePath: string; filePath: string; description: string }>> {\n let entries\n try {\n entries = await fs.readdir(dir, { withFileTypes: true })\n } catch {\n return []\n }\n\n const out: Array<{ name: string; relativePath: string; filePath: string; description: string }> = []\n for (const entry of entries) {\n const filePath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n out.push(...await readCommandFilesRecursive(root, filePath))\n continue\n }\n if (!entry.isFile() || !entry.name.endsWith(\".md\")) continue\n const relativePath = normalizeRelative(path.relative(root, filePath))\n const name = relativePath.replace(/\\.md$/i, \"\")\n let description = \"\"\n try {\n const md = await fs.readFile(filePath, \"utf8\")\n description = parseFrontmatterField(md, \"description\") ?? firstMarkdownLine(md)\n } catch {\n // A listed file that disappears mid-read should not break the UI.\n }\n out.push({ name, relativePath, filePath, description })\n }\n return out\n}\n\nfunction parseFrontmatterField(markdown: string, field: string): string | null {\n if (!markdown.startsWith(\"---\")) return null\n const end = markdown.indexOf(\"\\n---\", 3)\n if (end < 0) return null\n const frontmatter = markdown.slice(3, end).split(/\\r?\\n/)\n for (const line of frontmatter) {\n const match = line.match(/^([A-Za-z0-9_-]+):\\s*(.*)$/)\n if (!match || match[1] !== field) continue\n return match[2].replace(/^['\"]|['\"]$/g, \"\").trim()\n }\n return null\n}\n\nfunction firstMarkdownLine(markdown: string): string {\n const body = markdown.startsWith(\"---\")\n ? markdown.slice(markdown.indexOf(\"\\n---\", 3) + 4)\n : markdown\n for (const line of body.split(/\\r?\\n/)) {\n const trimmed = line.trim()\n if (trimmed) return trimmed.replace(/^#+\\s*/, \"\")\n }\n return \"\"\n}\n\nfunction normalizeRelative(value: string): string {\n return value.split(path.sep).join(\"/\")\n}\n\nfunction countCommands(builtIn: unknown): number {\n return Array.isArray(builtIn) ? builtIn.length : 0\n}\n","import http from \"node:http\"\nimport type { AddressInfo, Socket } from \"node:net\"\n\nimport { getPackageVersion } from \"../version\"\n\n/**\n * Loopback-only identity endpoint served directly by the reverse proxy (never\n * forwarded to CloudCLI). A second `github-router serve` probes this on the\n * intended port to detect an already-running instance (single-instance guard),\n * so it can attach/refuse instead of starting a duplicate CloudCLI. Carries no\n * secret — just a service marker + version + origin.\n */\nexport const SERVE_IDENTITY_PATH = \"/__github-router-serve__\"\nexport const SERVE_IDENTITY_SERVICE = \"github-router-serve\"\n\n/**\n * Standalone HTTP + WebSocket reverse proxy that fronts a locally-running\n * CloudCLI instance under github-router's own origin. It is deliberately NOT\n * routed through the srvx/Hono app — a separate lightweight proxy avoids\n * entangling CloudCLI's raw WebSocket upgrades (`/ws` chat, `/shell` node-pty\n * terminal) with the model-API server.\n *\n * Security responsibilities CloudCLI's OSS server does not handle itself:\n * 1. Injects `localStorage['auth-token']` into the served `index.html` so the\n * SPA boots already-authenticated (true zero-login).\n * 2. Rejects foreign `Host` (DNS-rebinding defense — an `Origin`-only check\n * still lets a rebound `attacker.com → 127.0.0.1` navigation fetch the\n * token-injected HTML) AND foreign `Origin` on requests + WS upgrades\n * (CloudCLI performs NO Origin check on `/ws` or `/shell`).\n */\nexport interface ReverseProxyOptions {\n targetHost: string\n targetPort: number\n bindHost: string\n bindPort: number\n /** JWT injected into the SPA so it boots authenticated. */\n authToken: string\n /**\n * Seed CloudCLI's `claude-settings` localStorage with `skipPermissions:false`\n * and an `allowedTools` allow-list (routine built-ins + injected `mcp__*`) so\n * routine tools auto-run with no prompt while `AskUserQuestion`/`ExitPlanMode`\n * still reach the user (serve default; disabled by GH_ROUTER_SERVE_NO_AUTO_APPROVE=1).\n */\n seedToolSettings?: boolean\n /** Exact tool names to seed into the `claude-settings` `allowedTools`\n * auto-approve list (only used when `seedToolSettings`) — the routine\n * built-ins plus every exact `mcp__<key>__<tool>` name. MUST NOT include\n * `AskUserQuestion`/`ExitPlanMode`, or they would auto-resolve instead of\n * reaching the user. */\n seedAllowedTools?: string[]\n providerFacade?: {\n kindFor: (method: string, pathname: string) => string | null\n rewrite: (\n kind: string,\n upstreamJson: unknown,\n query: URLSearchParams,\n ) => Promise<unknown | null>\n /**\n * Requests answered DIRECTLY with a canned JSON body, never forwarded to\n * CloudCLI (e.g. `POST /api/system/update`, which would run a global npm\n * install). Returns null to forward normally.\n */\n blockedRequest?: (\n method: string,\n pathname: string,\n ) => { status: number; json: unknown } | null\n }\n /**\n * Extra exact `host:port` values to accept beyond loopback (e.g. a specific\n * dev-tunnel host `abc-5454.usw2.devtunnels.ms`). Used for remote access.\n */\n extraAllowedHosts?: string[]\n /** Extra exact origins to accept (e.g. `https://abc-5454.usw2.devtunnels.ms`). */\n extraAllowedOrigins?: string[]\n /**\n * Accept ANY `*.devtunnels.ms` host + `https://*.devtunnels.ms` origin. A\n * convenience for Microsoft dev tunnels when the exact URL isn't pinned. Only\n * the user's own authenticated tunnel actually forwards to this loopback port,\n * so this does not weaken the same-user loopback trust boundary.\n */\n allowDevtunnelHosts?: boolean\n}\n\nexport interface ReverseProxyHandle {\n url: string\n port: number\n close: () => Promise<void>\n}\n\nconst HEAD_CLOSE = \"</head>\"\n\n/** CloudCLI's localStorage key for per-tool chat permission settings. */\nconst CLAUDE_SETTINGS_KEY = \"claude-settings\"\n\n/**\n * The two interaction tools CloudCLI routes to the human (its\n * `TOOLS_REQUIRING_INTERACTION`). They must NEVER appear in the seeded\n * `allowedTools` — an exact-name match there would auto-approve them and defeat\n * the question widget / plan card. Stripped defensively at the seed chokepoint\n * so no upstream list (mcp names, a future caller) can reintroduce them.\n */\nconst NEVER_AUTO_APPROVE = new Set([\"AskUserQuestion\", \"ExitPlanMode\"])\n\n/**\n * Embed the token as a JS string literal. JSON.stringify escapes quotes and\n * backslashes; we additionally neutralize `<` so the value can never break out\n * of the surrounding <script> element regardless of contents.\n *\n * When `seedToolSettings` is set, also seed CloudCLI's `claude-settings`\n * localStorage with `{allowedTools:<routine+mcp>, disallowedTools:[], skipPermissions:false}`.\n * This is serve's \"seamless routine + decisions to the user\" model: `canUseTool`\n * stays LIVE (skipPermissions:false, NOT bypass), routine tools in `allowedTools`\n * auto-run with no prompt, and `AskUserQuestion`/`ExitPlanMode` — deliberately\n * NOT in the list and hard-cased by CloudCLI into `TOOLS_REQUIRING_INTERACTION` —\n * always reach the human as a real question widget / plan Approve-Reject card.\n * `skipPermissions:true` (bypass) would skip `canUseTool` entirely and silently\n * auto-answer those two, which is exactly what we are fixing.\n */\nfunction buildInjection(\n token: string,\n seedToolSettings = false,\n seedAllowedTools: string[] = [],\n): string {\n const safe = JSON.stringify(token).replace(/</g, \"\\\\u003c\")\n let body = `localStorage.setItem('auth-token',${safe});`\n if (seedToolSettings) {\n const settings = JSON.stringify({\n // CloudCLI's per-tool AUTO-APPROVE list — feeds the Agent-SDK `canUseTool`\n // fast-path (EXACT-name match, no `mcp__<server>` wildcard) AND\n // `sdkOptions.allowedTools`. We seed the routine built-ins + every exact\n // `mcp__<key>__<tool>` name so ordinary work never prompts. The two\n // interaction tools are filtered out unconditionally (NEVER_AUTO_APPROVE)\n // so they always reach the user, no matter what the caller passed.\n allowedTools: seedAllowedTools.filter((t) => !NEVER_AUTO_APPROVE.has(t)),\n disallowedTools: [],\n // NON-bypass: keep canUseTool live so AskUserQuestion/ExitPlanMode reach\n // the user. Bypass would auto-resolve them on a generated answer.\n skipPermissions: false,\n })\n const safeSettings = JSON.stringify(settings).replace(/</g, \"\\\\u003c\")\n body += `localStorage.setItem('${CLAUDE_SETTINGS_KEY}',${safeSettings});`\n }\n return `<script>try{${body}}catch(e){}</script>`\n}\n\nfunction headerLines(headers: http.IncomingHttpHeaders): string {\n return Object.entries(headers)\n .map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(\", \") : v}`)\n .join(\"\\r\\n\")\n}\n\nexport async function startReverseProxy(\n opts: ReverseProxyOptions,\n): Promise<ReverseProxyHandle> {\n const { targetHost, targetPort, bindHost, bindPort, authToken } = opts\n const ownOrigin = `http://${bindHost}:${bindPort}`\n const injection = buildInjection(authToken, opts.seedToolSettings === true, opts.seedAllowedTools ?? [])\n\n const allowedHosts = new Set([\n `127.0.0.1:${bindPort}`,\n `localhost:${bindPort}`,\n `[::1]:${bindPort}`,\n ...(opts.extraAllowedHosts ?? []).map((h) => h.toLowerCase()),\n ])\n // Loopback-only subset — the single-instance identity probe always comes from\n // 127.0.0.1, so the identity endpoint is answered only for a loopback Host\n // (never a dev-tunnel host), keeping the version off the remote surface.\n const loopbackHosts = new Set([\n `127.0.0.1:${bindPort}`,\n `localhost:${bindPort}`,\n `[::1]:${bindPort}`,\n ])\n const allowedOrigins = new Set([\n `http://127.0.0.1:${bindPort}`,\n `http://localhost:${bindPort}`,\n `http://[::1]:${bindPort}`,\n ...(opts.extraAllowedOrigins ?? []).map((o) => o.toLowerCase()),\n ])\n // `(^|\\.)devtunnels.ms$` matches `x.devtunnels.ms` but not `evildevtunnels.ms`\n // / `evil-devtunnels.ms` (the char before must be start-of-string or a dot).\n const DEVTUNNEL_RE = /(^|\\.)devtunnels\\.ms$/i\n const isDevtunnelHost = (host: string): boolean =>\n !!opts.allowDevtunnelHosts && DEVTUNNEL_RE.test(host.replace(/:\\d+$/, \"\"))\n const isDevtunnelOrigin = (origin: string): boolean => {\n if (!opts.allowDevtunnelHosts) return false\n try {\n const u = new URL(origin)\n return u.protocol === \"https:\" && DEVTUNNEL_RE.test(u.hostname)\n } catch {\n return false\n }\n }\n // Host defends against DNS-rebinding; Origin defends against cross-origin\n // browser access. A missing Origin (plain navigation / non-browser) is only\n // allowed once the Host has already been confirmed allowed.\n const hostAllowed = (host?: string): boolean =>\n !!host\n && (allowedHosts.has(host.toLowerCase()) || isDevtunnelHost(host.toLowerCase()))\n const originAllowed = (origin?: string): boolean =>\n !origin\n || allowedOrigins.has(origin.toLowerCase())\n || isDevtunnelOrigin(origin)\n\n // Track hijacked upgrade sockets so close() can destroy them — http.Server\n // .close() otherwise waits for them forever (shutdown hang).\n const liveSockets = new Set<Socket>()\n\n const server = http.createServer((clientReq, clientRes) => {\n clientReq.on(\"error\", () => clientRes.destroy())\n clientRes.on(\"error\", () => clientReq.destroy())\n\n if (!hostAllowed(clientReq.headers.host) || !originAllowed(clientReq.headers.origin)) {\n clientRes.writeHead(403, { \"content-type\": \"text/plain\" })\n clientRes.end(\"Forbidden\")\n return\n }\n\n // Single-instance identity probe — answered directly, never proxied to\n // CloudCLI. Loopback Host only (the probe is always local); carries no\n // secret and no attacker-usable URL.\n if ((clientReq.url ?? \"\").split(\"?\")[0] === SERVE_IDENTITY_PATH) {\n if (!loopbackHosts.has(String(clientReq.headers.host ?? \"\").toLowerCase())) {\n clientRes.writeHead(404, { \"content-type\": \"text/plain\" })\n clientRes.end(\"Not found\")\n return\n }\n clientRes.writeHead(200, { \"content-type\": \"application/json\" })\n clientRes.end(\n JSON.stringify({\n service: SERVE_IDENTITY_SERVICE,\n version: getPackageVersion(),\n }),\n )\n return\n }\n\n const requestUrl = new URL(clientReq.url ?? \"/\", ownOrigin)\n\n // Requests serve answers directly (never forwarded) — e.g. CloudCLI's\n // system-update button, which would run a global `npm i -g` mutation.\n const blocked = opts.providerFacade?.blockedRequest?.(\n clientReq.method ?? \"GET\",\n requestUrl.pathname,\n )\n if (blocked) {\n clientReq.resume() // drain the request body so the socket can be reused\n clientRes.writeHead(blocked.status, { \"content-type\": \"application/json\" })\n clientRes.end(JSON.stringify(blocked.json))\n return\n }\n\n const facadeKind = opts.providerFacade?.kindFor(\n clientReq.method ?? \"GET\",\n requestUrl.pathname,\n ) ?? null\n\n // Drop accept-encoding so HTML comes back uncompressed and stays\n // injectable; façade JSON rewrites also need original bytes, not gzip/br.\n const headers = { ...clientReq.headers }\n delete headers[\"accept-encoding\"]\n\n const proxyReq = http.request(\n {\n host: targetHost,\n port: targetPort,\n path: clientReq.url,\n method: clientReq.method,\n headers,\n },\n (proxyRes) => {\n proxyRes.on(\"error\", () => clientRes.destroy())\n const contentType = String(proxyRes.headers[\"content-type\"] ?? \"\")\n if (facadeKind) {\n const chunks: Buffer[] = []\n proxyRes.on(\"data\", (d) => chunks.push(Buffer.from(d)))\n proxyRes.on(\"end\", () => {\n const original = Buffer.concat(chunks)\n const sendOriginal = () => {\n // We buffered the whole body, so send it as a fixed buffer and let\n // Node re-frame — any upstream chunked/encoded framing is now wrong.\n const outHeaders = { ...proxyRes.headers }\n delete outHeaders[\"content-length\"]\n delete outHeaders[\"content-encoding\"]\n delete outHeaders[\"transfer-encoding\"]\n clientRes.writeHead(proxyRes.statusCode ?? 502, outHeaders)\n clientRes.end(original)\n }\n if (proxyRes.statusCode !== 200) {\n sendOriginal()\n return\n }\n void (async () => {\n try {\n const parsed = JSON.parse(original.toString(\"utf8\"))\n const rewritten = await opts.providerFacade?.rewrite(\n facadeKind,\n parsed,\n requestUrl.searchParams,\n )\n if (rewritten == null) {\n sendOriginal()\n return\n }\n const outHeaders = { ...proxyRes.headers }\n // We send a fresh JSON body, so any upstream length/encoding\n // framing is now wrong — drop it and let Node re-frame.\n delete outHeaders[\"content-length\"]\n delete outHeaders[\"content-encoding\"]\n delete outHeaders[\"transfer-encoding\"]\n outHeaders[\"content-type\"] = \"application/json\"\n clientRes.writeHead(200, outHeaders)\n clientRes.end(JSON.stringify(rewritten))\n } catch {\n sendOriginal()\n }\n })()\n })\n } else if (contentType.includes(\"text/html\")) {\n const chunks: Buffer[] = []\n proxyRes.on(\"data\", (d) => chunks.push(Buffer.from(d)))\n proxyRes.on(\"end\", () => {\n let html = Buffer.concat(chunks).toString(\"utf8\")\n html = html.includes(HEAD_CLOSE)\n ? html.replace(HEAD_CLOSE, injection + HEAD_CLOSE)\n : injection + html\n const outHeaders = { ...proxyRes.headers }\n // We send a fresh fixed buffer, so any length/encoding framing the\n // upstream set is now wrong — drop it and let Node re-frame.\n delete outHeaders[\"content-length\"]\n delete outHeaders[\"content-encoding\"]\n delete outHeaders[\"transfer-encoding\"]\n clientRes.writeHead(proxyRes.statusCode ?? 502, outHeaders)\n clientRes.end(html)\n })\n } else {\n clientRes.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers)\n proxyRes.pipe(clientRes)\n }\n },\n )\n proxyReq.on(\"error\", (err) => {\n if (!clientRes.headersSent)\n clientRes.writeHead(502, { \"content-type\": \"text/plain\" })\n clientRes.end(`Bad gateway: ${err.message}`)\n })\n clientReq.pipe(proxyReq)\n })\n\n server.on(\"upgrade\", (clientReq, clientSock: Socket, clientHead) => {\n // Attach error handling + track the socket IMMEDIATELY, before any async\n // window (a client reset during the upstream handshake must not crash us).\n clientSock.on(\"error\", () => clientSock.destroy())\n liveSockets.add(clientSock)\n clientSock.once(\"close\", () => liveSockets.delete(clientSock))\n\n if (!hostAllowed(clientReq.headers.host) || !originAllowed(clientReq.headers.origin)) {\n clientSock.destroy()\n return\n }\n\n const proxyReq = http.request({\n host: targetHost,\n port: targetPort,\n path: clientReq.url,\n method: clientReq.method,\n headers: clientReq.headers,\n })\n proxyReq.on(\"upgrade\", (proxyRes, proxySock, proxyHead) => {\n clientSock.write(\n `HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage}\\r\\n${headerLines(\n proxyRes.headers,\n )}\\r\\n\\r\\n`,\n )\n if (clientHead && clientHead.length) proxySock.write(clientHead)\n if (proxyHead && proxyHead.length) proxySock.unshift(proxyHead)\n proxySock.pipe(clientSock)\n clientSock.pipe(proxySock)\n const cleanup = () => {\n proxySock.destroy()\n clientSock.destroy()\n }\n // Tear down on BOTH error and normal close, on either side (else the\n // paired socket leaks on every page refresh / terminal close).\n clientSock.on(\"error\", cleanup)\n proxySock.on(\"error\", cleanup)\n clientSock.on(\"close\", cleanup)\n proxySock.on(\"close\", cleanup)\n })\n // CloudCLI answered the upgrade with an ordinary response (e.g. 401/404 for\n // a missing/invalid JWT). Relay it and close so the browser WS doesn't hang.\n proxyReq.on(\"response\", (proxyRes) => {\n try {\n clientSock.write(\n `HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage}\\r\\n${headerLines(\n proxyRes.headers,\n )}\\r\\n\\r\\n`,\n )\n } catch {\n /* socket already gone */\n }\n proxyRes.on(\"data\", (d) => {\n clientSock.write(d as Buffer)\n })\n proxyRes.on(\"end\", () => clientSock.end())\n proxyRes.on(\"error\", () => clientSock.destroy())\n })\n proxyReq.on(\"error\", () => clientSock.destroy())\n proxyReq.end()\n })\n\n await new Promise<void>((resolve, reject) => {\n const onError = (err: Error) => reject(err)\n server.once(\"error\", onError)\n server.listen(bindPort, bindHost, () => {\n server.removeListener(\"error\", onError)\n resolve()\n })\n })\n\n // Defense-in-depth: refuse to run if we somehow bound a non-loopback address.\n const addr = server.address() as AddressInfo | null\n if (addr && addr.address !== \"127.0.0.1\" && addr.address !== \"::1\") {\n await new Promise<void>((r) => server.close(() => r()))\n throw new Error(\n `reverse proxy refused non-loopback bind (got ${addr.address})`,\n )\n }\n\n return {\n url: ownOrigin,\n port: bindPort,\n close: () =>\n new Promise<void>((resolve) => {\n for (const s of liveSockets) s.destroy()\n liveSockets.clear()\n const t = setTimeout(resolve, 1500) // never let shutdown hang\n server.close(() => {\n clearTimeout(t)\n resolve()\n })\n }),\n }\n}\n\n/** Exported for unit tests. */\nexport const __test = { buildInjection, HEAD_CLOSE }\n","import { spawn } from \"node:child_process\"\nimport http from \"node:http\"\nimport type { AddressInfo } from \"node:net\"\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { provisionBrowserAssets } from \"./lib/browser-mcp/provision\"\nimport { provisionAndIndexColbert } from \"./lib/colbert\"\nimport { resolveCodexCliBackend } from \"./lib/codex-mcp-config\"\nimport { killChildProcessTree } from \"./lib/exec\"\nimport { startKeepAwake, stopKeepAwake } from \"./lib/keep-awake\"\nimport {\n hookLauncherDegradedWarning,\n resolveSelfInvocation,\n} from \"./lib/hook-launcher/self-invocation\"\nimport { getCodexVersion } from \"./lib/launch\"\nimport { resolveLaunchProfile } from \"./lib/launch-profile\"\nimport { browserToolsEnabled } from \"./lib/mcp-capabilities\"\nimport { SEAMLESS_BUILTIN_TOOLS } from \"./lib/mcp-permissions-settings\"\nimport { ensureClaudeConfigMirror, PATHS, removeOwnClaudeConfigMirror } from \"./lib/paths\"\nimport {\n DEFAULT_CLAUDE_MODEL_FALLBACKS,\n pickClaudeDefault,\n resolveLeadSlugArg,\n} from \"./lib/port\"\nimport {\n getClaudeCodeEnvVars,\n parseSharedArgs,\n setupAndServe,\n sharedServerArgs,\n} from \"./lib/server-setup\"\nimport {\n composeCloudCliChildEnv,\n mintJwt,\n resolveCloudCli,\n spawnCloudCli,\n waitForCloudCliReady,\n} from \"./lib/serve/cloudcli\"\nimport { DevtunnelError, startDevtunnel } from \"./lib/serve/devtunnel\"\nimport { provisionServeEnhancements } from \"./lib/serve/enhancements\"\nimport { facadeBlockedRequest, facadeInterceptKind, rewriteProviderResponse } from \"./lib/serve/provider-facade\"\nimport {\n SERVE_IDENTITY_PATH,\n SERVE_IDENTITY_SERVICE,\n startReverseProxy,\n} from \"./lib/serve/reverse-proxy\"\nimport { runSelfUpdate } from \"./lib/self-update\"\nimport { state } from \"./lib/state\"\nimport { provisionToolbelt } from \"./lib/toolbelt/provision\"\nimport { resolveModel } from \"./lib/utils\"\nimport { getGitHubUser } from \"./services/github/get-user\"\n\nexport function assertStandardServeModel(model: string | undefined): void {\n const requestedProfile = resolveLaunchProfile(model)\n if (requestedProfile === \"standard\") return\n throw new Error(\n `The ${requestedProfile} launch profile is available only through `\n + `github-router claude -m ${requestedProfile}; github-router serve uses `\n + \"the Standard roster, ACL, and model picker. Pass an explicit model id \"\n + \"to serve instead of the profile alias.\",\n )\n}\n\nasync function getFreePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const s = http.createServer()\n s.once(\"error\", reject)\n s.listen(0, \"127.0.0.1\", () => {\n const port = (s.address() as AddressInfo).port\n s.close(() => resolve(port))\n })\n })\n}\n\n/** Default user-facing control-plane port when `--port` is not given. */\nconst DEFAULT_SERVE_PORT = 5454\n\nfunction isPortFree(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const s = http.createServer()\n s.once(\"error\", () => resolve(false))\n s.listen(port, \"127.0.0.1\", () => s.close(() => resolve(true)))\n })\n}\n\n/**\n * Probe a loopback port for an already-running `github-router serve` (its\n * identity endpoint). Returns true when the port hosts OUR serve, else false\n * (free, or a foreign process). `serve` is designed to run ONCE per machine\n * (a single machine-wide control plane for all repos), so a second launch\n * attaches to the running one instead of spawning a duplicate CloudCLI +\n * contending on the shared auth DB. We intentionally do NOT trust any URL the\n * probed service reports — a local squatter could forge the identity marker, so\n * the caller only ever opens a locally-constructed loopback origin.\n */\nfunction probeExistingServe(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const req = http.get(\n {\n host: \"127.0.0.1\",\n port,\n path: SERVE_IDENTITY_PATH,\n headers: { host: `127.0.0.1:${port}` },\n timeout: 1500,\n },\n (res) => {\n if (res.statusCode !== 200) {\n res.resume()\n resolve(false)\n return\n }\n const chunks: Buffer[] = []\n res.on(\"data\", (d) => chunks.push(Buffer.from(d)))\n res.on(\"end\", () => {\n try {\n const body = JSON.parse(Buffer.concat(chunks).toString(\"utf8\")) as {\n service?: string\n }\n resolve(body.service === SERVE_IDENTITY_SERVICE)\n } catch {\n resolve(false)\n }\n })\n res.on(\"error\", () => resolve(false))\n },\n )\n req.on(\"timeout\", () => req.destroy())\n req.on(\"error\", () => resolve(false))\n })\n}\n\n/**\n * Resolve the user-facing port: honor an explicit `--port`; otherwise use 5454\n * when free, else fall back to a random free port. (Single-instance detection —\n * an already-running github-router serve on the intended port — is handled by\n * the earlier `probeExistingServe` attach/refuse in `run`.)\n */\nasync function resolveServePort(requested?: number): Promise<number> {\n if (requested != null) return requested\n if (await isPortFree(DEFAULT_SERVE_PORT)) return DEFAULT_SERVE_PORT\n const fallback = await getFreePort()\n consola.info(`Port ${DEFAULT_SERVE_PORT} is in use; using ${fallback} instead.`)\n return fallback\n}\n\nfunction openBrowser(url: string): void {\n try {\n if (process.platform === \"win32\") {\n spawn(\"cmd\", [\"/c\", \"start\", \"\", url], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n }).unref()\n } else if (process.platform === \"darwin\") {\n spawn(\"open\", [url], { detached: true, stdio: \"ignore\" }).unref()\n } else {\n spawn(\"xdg-open\", [url], { detached: true, stdio: \"ignore\" }).unref()\n }\n } catch {\n // best-effort; the URL is printed too\n }\n}\n\nexport const serve = defineCommand({\n meta: {\n name: \"serve\",\n description:\n \"Serve a browser control plane (CloudCLI) with Claude routed through the proxy\",\n },\n args: {\n ...sharedServerArgs,\n model: {\n alias: \"m\",\n type: \"string\",\n description: \"Override the default Claude model\",\n },\n \"cloudcli-path\": {\n type: \"string\",\n description: \"Use an existing CloudCLI install instead of the managed one\",\n },\n \"cloudcli-version\": {\n type: \"string\",\n description: \"Override the pinned CloudCLI version to install\",\n },\n \"codex-cli\": {\n type: \"boolean\",\n default: false,\n description: \"Route Codex personas through a local `codex mcp-server` (requires codex 0.129+; falls back to HTTP if missing)\",\n },\n \"browse-over-tunnel\": {\n type: \"boolean\",\n default: false,\n description: \"Expose the server-side browser MCP even when tunnel-exposed (session-hijack / SSRF risk — only with fully-trusted tunnel access)\",\n },\n \"agents-over-tunnel\": {\n type: \"boolean\",\n default: false,\n description: \"Expose first-mate (mints a repo+workflow GitHub write token) even when tunnel-exposed (only with fully-trusted tunnel access)\",\n },\n \"fleet-over-tunnel\": {\n type: \"boolean\",\n default: false,\n description: \"Expose the fleet MCP (drives remote coding sessions with your fleet credentials) even when tunnel-exposed (only with fully-trusted tunnel access)\",\n },\n \"no-install\": {\n type: \"boolean\",\n default: false,\n description: \"Do not auto-install CloudCLI; require an existing install\",\n },\n \"no-open\": {\n type: \"boolean\",\n default: false,\n description: \"Do not open the browser automatically\",\n },\n \"public-url\": {\n type: \"string\",\n description:\n \"Public URL(s) this is reachable at (e.g. a dev-tunnel https URL), comma-separated — allowlists their host+origin for remote access\",\n },\n tunnel: {\n type: \"boolean\",\n default: false,\n description:\n \"Auto-create + host an AUTHENTICATED (never anonymous) Microsoft dev tunnel for the serve port and print its public URL, so you can reach the control plane from anywhere. Requires the `devtunnel` CLI + `devtunnel user login`.\",\n },\n },\n async run({ args }) {\n state.serveMode = true\n\n if (process.versions.bun) {\n consola.warn(\n \"`serve` should run under Node.js. Bun's node:http upgrade sockets cannot relay the CloudCLI terminal/chat WebSockets; run the installed `github-router` binary (Node).\",\n )\n }\n\n const parsed = parseSharedArgs(args as unknown as Record<string, unknown>)\n try {\n assertStandardServeModel(args.model as string | undefined)\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n\n // Single-instance guard: `serve` is a machine-wide control plane meant to run\n // ONCE per machine (work on any repo through its file explorer / sessions). If\n // one is already running on the intended port, attach to it (open + exit)\n // instead of spawning a second CloudCLI that would contend on the shared auth\n // DB. Only triggers for OUR serve; a foreign process on the port falls through\n // to the random-port fallback in resolveServePort.\n const intendedPort = parsed.port ?? DEFAULT_SERVE_PORT\n if (await probeExistingServe(intendedPort)) {\n // Construct the URL locally — never trust a URL the probed service reports\n // (a local squatter could forge the identity marker into an open-browser).\n const existingUrl = `http://127.0.0.1:${intendedPort}`\n consola.box(\n `github-router control plane already running\\n\\n${existingUrl}\\n\\nOpening it — stop that instance first if you want a fresh one.`,\n )\n if (args[\"no-open\"] !== true) openBrowser(existingUrl)\n return\n }\n\n // 1. github-router proxy — internal (random port). The USER-facing port is\n // the reverse proxy below, so force this one random regardless of --port.\n const { server, serverUrl } = await setupAndServe({\n ...parsed,\n port: undefined,\n silent: true,\n })\n\n // 2. provision the router-owned CLAUDE_CONFIG_DIR (synthetic creds +\n // onboarding-skip) so CloudCLI's SDK-spawned claude authenticates\n // through the proxy. Hard-fail on error (mirrors src/claude.ts).\n try {\n await ensureClaudeConfigMirror()\n } catch (err) {\n consola.error(\n `Failed to provision CLAUDE_CONFIG_DIR mirror: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n process.exit(1)\n }\n\n // Best-effort self-update (detached, applies next launch). Runs after the\n // server is listening so the bounded probe can't delay it.\n void runSelfUpdate({ selfUpdate: args[\"self-update\"] !== false })\n\n // Best-effort ColBERT semantic-search provision. ON by default; never blocks\n // launch. `skipCwdIndex`: serve is machine-wide, so the launch cwd is usually\n // NOT a repo the user works on — per-workspace on-demand indexing (kicked by\n // the first search for a given repo) covers real queries instead.\n void provisionAndIndexColbert({ skipCwdIndex: true })\n\n // Best-effort LLM toolbelt materialization. The mirror awareness line is\n // written by provisionServeEnhancements below, matching `github-router claude`.\n void provisionToolbelt().catch((err) =>\n consola.debug(\"Toolbelt provisioning failed:\", err),\n )\n\n // Best-effort: keep the machine awake while the proxy/CloudCLI session runs.\n // Released via stopKeepAwake() in the shutdown chain below and by the module's\n // own self-registered signal/exit reaper.\n startKeepAwake()\n\n // Best-effort: materialize the browser extension + bridge into the stable\n // app-dir when browser tools are enabled.\n if (browserToolsEnabled()) {\n void provisionBrowserAssets().catch((err) =>\n consola.debug(\"Browser extension provisioning failed:\", err),\n )\n }\n\n // Same lead-slug resolution the `claude` subcommand uses, via the shared\n // helper rather than a second copy of the branching. The copy that used to\n // live here had drifted twice: it never learned the `-m fast` alias, and\n // its fallback walk assigned the bare constant, silently dropping the\n // `[1m]` accounting on a family that is 1M-capable. Re-deriving the\n // fallback through `pickClaudeDefault` is what `src/claude.ts` already\n // does, for that exact reason.\n let chosenSlug = resolveLeadSlugArg(args.model as string | undefined)\n if (!(args.model as string | undefined)?.trim() && state.models) {\n const inCache = (slug: string): boolean =>\n state.models?.data.some((m) => m.id === resolveModel(slug)) ?? false\n if (!inCache(chosenSlug)) {\n for (const fb of DEFAULT_CLAUDE_MODEL_FALLBACKS) {\n if (inCache(fb)) {\n const fallbackSlug = pickClaudeDefault(\n fb.replace(/^claude-opus-/, \"\"),\n )\n consola.info(\n `Default model \"${chosenSlug}\" not in your Copilot model list; falling back to \"${fallbackSlug}\".`,\n )\n chosenSlug = fallbackSlug\n break\n }\n }\n }\n }\n\n const requestedCli = args[\"codex-cli\"] === true\n const backend = resolveCodexCliBackend({\n requested: requestedCli,\n codexInfo: requestedCli ? getCodexVersion() : null,\n })\n const tunnelExposed =\n args.tunnel === true\n || String(args[\"public-url\"] ?? \"\")\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean).length > 0\n if (tunnelExposed && browserToolsEnabled() && args[\"browse-over-tunnel\"] !== true) {\n consola.info(\n \"Browser MCP is disabled over the tunnel (session-hijack/SSRF risk). Pass --browse-over-tunnel to enable it for fully-trusted access.\",\n )\n }\n if (tunnelExposed && state.agentsEnabled && args[\"agents-over-tunnel\"] !== true) {\n consola.info(\n \"first-mate is disabled over the tunnel (it mints a GitHub write token). Pass --agents-over-tunnel to enable it for fully-trusted access.\",\n )\n }\n if (tunnelExposed && state.fleetEnabled && args[\"fleet-over-tunnel\"] !== true) {\n consola.info(\n \"Fleet MCP is disabled over the tunnel (it drives remote coding sessions with your fleet credentials). Pass --fleet-over-tunnel to enable it for fully-trusted access.\",\n )\n }\n\n // 2b. wire the github-router enhancement layer (MCP servers + peer/worker\n // subagents + gh-* skills) into the mirror so CloudCLI-spawned Claude\n // sessions get the same tools `github-router claude` provides. Must run\n // after the mirror exists and before CloudCLI spawns claude. Resolve the\n // self-invocation first so no persisted command races launcher publication.\n const selfInvocation = await resolveSelfInvocation()\n {\n const warning = hookLauncherDegradedWarning(selfInvocation)\n if (warning) process.stderr.write(`${warning}\\n`)\n }\n const enhancements = await provisionServeEnhancements(serverUrl, {\n selfInvocation,\n codexCli: backend === \"cli\",\n tunnelExposed,\n browseOverTunnel: args[\"browse-over-tunnel\"] === true,\n agentsOverTunnel: args[\"agents-over-tunnel\"] === true,\n fleetOverTunnel: args[\"fleet-over-tunnel\"] === true,\n })\n\n // 3. filtered child env: the vetted secret-stripping allowlist (buildEnv,\n // drops GITHUB_TOKEN/GH_ROUTER_*/ANTHROPIC_AUTH_TOKEN/OPENAI_API_KEY/\n // COPILOT_TOKEN) plus the non-secret ANTHROPIC_BASE_URL/CLAUDE_CONFIG_DIR\n // /model vars. Auth rides the synthetic .credentials.json FILE, not env.\n const anthropicVars = getClaudeCodeEnvVars(\n serverUrl,\n chosenSlug,\n \"standard\",\n enhancements.pickerModels,\n )\n // CLAUDE_CODE_FORK_SUBAGENT silently no-ops under CloudCLI's headless\n // `claude --print` path (the binary's Z8() precondition disables forking\n // without an interactive session), so setting it here is dead weight that\n // would mislead the model into assuming forked subagents inherit full\n // context. Drop it for serve. (getClaudeCodeEnvVars sets it for the\n // interactive `claude` path, where forking works.)\n delete anthropicVars.CLAUDE_CODE_FORK_SUBAGENT\n if (enhancements.nonce) {\n anthropicVars.GH_ROUTER_HOOK_MCP_URL = serverUrl\n anthropicVars.GH_ROUTER_HOOK_NONCE = enhancements.nonce\n }\n const childEnv: NodeJS.ProcessEnv = composeCloudCliChildEnv(anthropicVars)\n // Placeholder only: CloudCLI's Claude provider checks this env var to mark\n // Claude as connected. Claude still authenticates through the proxy mirror.\n childEnv.ANTHROPIC_API_KEY = \"sk-ant-github-router-proxy-placeholder\"\n // CloudCLI's cloned server defines CLAUDE_FALLBACK_MODELS as an in-code object,\n // and the searched server tree has no process.env.CLAUDE_FALLBACK_MODELS reader.\n // Do not set an env var here unless CloudCLI adds a documented string format.\n\n // 4. resolve/install + spawn CloudCLI on a loopback port.\n let cloudcli\n try {\n cloudcli = await resolveCloudCli({\n cliPath: args[\"cloudcli-path\"] as string | undefined,\n noInstall: args[\"no-install\"] === true,\n version: args[\"cloudcli-version\"] as string | undefined,\n })\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n\n const ccPort = await getFreePort()\n const cc = spawnCloudCli({\n serverEntry: cloudcli.serverEntry,\n port: ccPort,\n env: childEnv,\n })\n\n // 5. shutdown wiring (we don't use launchChild — its kind union is closed).\n let shuttingDown = false\n let reverseClose: (() => Promise<void>) | null = null\n let tunnelStop: (() => void) | null = null\n const shutdown = async (): Promise<void> => {\n if (shuttingDown) return\n shuttingDown = true\n if (tunnelStop) {\n try {\n tunnelStop()\n } catch {\n /* best-effort */\n }\n }\n try {\n killChildProcessTree(cc.child, {\n detachedGroup: process.platform !== \"win32\",\n })\n } catch {\n /* best-effort */\n }\n if (reverseClose) await reverseClose().catch(() => {})\n try {\n ;(server as unknown as { close?: () => void }).close?.()\n } catch {\n /* best-effort */\n }\n await stopKeepAwake().catch(() => {})\n await enhancements.cleanup().catch(() => {})\n await removeOwnClaudeConfigMirror().catch(() => {})\n }\n // `shutdown()` must never strand the process. `.then(exit)` alone skips the\n // exit on a REJECTION, so a teardown failure leaves a process that was\n // explicitly trying to die running forever — the worst possible outcome on\n // a shutdown path. `.finally` guarantees the exit either way, and the code\n // is preserved because an operator's script reads it.\n const exitAfterShutdown = (code: number) => {\n void shutdown()\n .catch((err: unknown) => {\n consola.error(\"Shutdown failed; exiting anyway:\", err)\n })\n .finally(() => process.exit(code))\n }\n process.once(\"SIGINT\", () => exitAfterShutdown(0))\n process.once(\"SIGTERM\", () => exitAfterShutdown(0))\n cc.child.once(\"exit\", (code) => {\n if (!shuttingDown) {\n consola.error(`CloudCLI exited unexpectedly (code ${code}).`)\n exitAfterShutdown(1)\n }\n })\n cc.child.once(\"error\", (err) => {\n // spawn failure (ENOENT/EACCES) emits 'error', not 'exit' — an unhandled\n // 'error' crashes the process and bypasses shutdown, orphaning the mirror.\n if (!shuttingDown) {\n consola.error(`CloudCLI failed to start: ${err.message}`)\n exitAfterShutdown(1)\n }\n })\n\n // 6. wait for CloudCLI, seed the user from the GitHub identity, mint a JWT.\n let ready\n try {\n ready = await waitForCloudCliReady(ccPort)\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err))\n await shutdown()\n process.exit(1)\n }\n\n let username = \"github-router\"\n try {\n username = (await getGitHubUser()).login\n } catch {\n // fall back to a generic control-plane user; not fatal\n }\n\n let token: string\n try {\n token = await mintJwt(ccPort, username, ready.needsSetup)\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err))\n await shutdown()\n process.exit(1)\n }\n\n // 7. resolve remote-access allowlist (dev tunnels) + the user-facing port.\n const publicUrls = String(args[\"public-url\"] ?? \"\")\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n const extraAllowedHosts: string[] = []\n const extraAllowedOrigins: string[] = []\n for (const u of publicUrls) {\n try {\n const url = new URL(u)\n extraAllowedHosts.push(url.host)\n extraAllowedOrigins.push(url.origin)\n } catch {\n consola.warn(`Ignoring invalid --public-url \"${u}\"`)\n }\n }\n const tunnelMode = args.tunnel === true\n if (extraAllowedHosts.length || tunnelMode) {\n consola.warn(\n \"Remote access enabled: anyone who reaches this through the tunnel AND passes its authentication gets FULL control-plane access — including a shell on this machine. The tunnel is authenticated (never anonymous); only grant access to people you fully trust.\",\n )\n }\n\n const servePort = await resolveServePort(parsed.port)\n const providerFacade = {\n kindFor: facadeInterceptKind,\n blockedRequest: facadeBlockedRequest,\n rewrite: (kind: string, json: unknown, query: URLSearchParams) =>\n rewriteProviderResponse(\n kind as Parameters<typeof rewriteProviderResponse>[0],\n json,\n {\n getModels: () => state.models,\n // Resolve to the Copilot catalog slug so it matches the picker\n // OPTIONS values (chosenSlug may be a dashed/`[1m]`-bracketed Anthropic\n // slug; the OPTIONS values are catalog ids). Otherwise DEFAULT misses\n // and falls back to the first option.\n defaultModel: resolveModel(chosenSlug),\n claudeConfigDir: PATHS.CLAUDE_CONFIG_DIR,\n },\n query,\n ),\n }\n let reverse\n try {\n reverse = await startReverseProxy({\n targetHost: \"127.0.0.1\",\n targetPort: ccPort,\n bindHost: \"127.0.0.1\",\n bindPort: servePort,\n authToken: token,\n seedToolSettings: process.env.GH_ROUTER_SERVE_NO_AUTO_APPROVE !== \"1\",\n // Routine built-ins + every injected mcp tool name → auto-approve; the\n // two interaction tools (AskUserQuestion/ExitPlanMode) are excluded so\n // they reach the user (SEAMLESS_BUILTIN_TOOLS never lists them).\n seedAllowedTools: [...SEAMLESS_BUILTIN_TOOLS, ...(enhancements.mcpToolNames ?? [])],\n extraAllowedHosts,\n extraAllowedOrigins,\n allowDevtunnelHosts: tunnelMode,\n providerFacade,\n })\n } catch (err) {\n consola.error(\n `Failed to bind the control-plane port ${servePort}: ${\n err instanceof Error ? err.message : String(err)\n }. Is it already in use? Pass --port <n>.`,\n )\n await shutdown()\n process.exit(1)\n }\n reverseClose = reverse.close\n\n // 8. optionally auto-create + host an AUTHENTICATED dev tunnel for remote\n // access. Non-fatal: on any failure keep serving locally (and the\n // *.devtunnels.ms allowlist stays, so a manual `devtunnel host` works).\n let tunnelUrl: string | null = null\n if (tunnelMode) {\n consola.info(\"Creating an authenticated dev tunnel…\")\n try {\n const tunnel = await startDevtunnel(servePort)\n tunnelUrl = tunnel.url\n tunnelStop = tunnel.stop\n } catch (err) {\n const msg = err instanceof DevtunnelError ? err.message : String(err)\n consola.warn(\n `Could not auto-host a dev tunnel: ${msg}\\nServing locally only. To host manually once fixed: devtunnel host -p ${servePort}`,\n )\n }\n }\n\n // 9. ready.\n const publicLines = [\n ...publicUrls.map((u) => `remote: ${u}`),\n ...(tunnelUrl ? [`remote (tunnel): ${tunnelUrl} (stable — bookmark it; same URL every launch)`] : []),\n ]\n const publicBlock = publicLines.length ? `\\n${publicLines.join(\"\\n\")}` : \"\"\n consola.box(\n `🖥️ github-router control plane\\n\\nlocal: ${reverse.url}${publicBlock}\\n\\nsigned in as ${username} · model ${chosenSlug}\\nCtrl+C to stop`,\n )\n if (args[\"no-open\"] !== true) openBrowser(reverse.url)\n },\n})\n"],"mappings":"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"}
|