infra-kit 0.3.6 → 0.3.8

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.
Files changed (40) hide show
  1. package/dist/boot-HP5ORQEX.js +2 -0
  2. package/dist/boot-HP5ORQEX.js.map +7 -0
  3. package/dist/{chunk-T74NOBEP.js → chunk-3LPUAYWA.js} +2 -2
  4. package/dist/{chunk-T74NOBEP.js.map → chunk-3LPUAYWA.js.map} +1 -1
  5. package/dist/chunk-7DL27USH.js +7 -0
  6. package/dist/{chunk-GZNE5CBQ.js.map → chunk-7DL27USH.js.map} +3 -3
  7. package/dist/chunk-KAWDTA5H.js +2 -0
  8. package/dist/chunk-KAWDTA5H.js.map +7 -0
  9. package/dist/{chunk-A7FAXZGI.js → chunk-LKKKDB6V.js} +1 -1
  10. package/dist/{chunk-A7FAXZGI.js.map → chunk-LKKKDB6V.js.map} +1 -1
  11. package/dist/chunk-PZU6BLQR.js +4 -0
  12. package/dist/{chunk-IVWPZEMC.js.map → chunk-PZU6BLQR.js.map} +3 -3
  13. package/dist/chunk-RDRJX6TT.js +232 -0
  14. package/dist/chunk-RDRJX6TT.js.map +7 -0
  15. package/dist/chunk-X46DP23W.js +2 -0
  16. package/dist/chunk-X46DP23W.js.map +7 -0
  17. package/dist/chunk-XTRPE4SX.js +2 -0
  18. package/dist/chunk-XTRPE4SX.js.map +7 -0
  19. package/dist/cli.js +8 -8
  20. package/dist/cli.js.map +3 -3
  21. package/dist/dev-server.js +3 -3
  22. package/dist/dev-server.js.map +2 -2
  23. package/dist/dev-wizard-run-KIOSJM4D.js +2 -0
  24. package/dist/dev-wizard-run-KIOSJM4D.js.map +7 -0
  25. package/dist/mcp.js +1 -1
  26. package/dist/mcp.js.map +1 -1
  27. package/dist/update-check.js +1 -1
  28. package/package.json +1 -1
  29. package/dist/boot-FHXCA53R.js +0 -2
  30. package/dist/boot-FHXCA53R.js.map +0 -7
  31. package/dist/chunk-3G6O7MCC.js +0 -231
  32. package/dist/chunk-3G6O7MCC.js.map +0 -7
  33. package/dist/chunk-EX66ZFV5.js +0 -2
  34. package/dist/chunk-EX66ZFV5.js.map +0 -7
  35. package/dist/chunk-GZNE5CBQ.js +0 -7
  36. package/dist/chunk-IVWPZEMC.js +0 -4
  37. package/dist/chunk-S45AAYXO.js +0 -4
  38. package/dist/chunk-S45AAYXO.js.map +0 -7
  39. package/dist/dev-wizard-run-5ST3BJ4L.js +0 -2
  40. package/dist/dev-wizard-run-5ST3BJ4L.js.map +0 -7
@@ -0,0 +1,2 @@
1
+ import r from"node:process";var e=0,n=()=>{e+=1,e===1&&r.stdin.ref()},i=()=>{e!==0&&(e-=1,e===0&&r.stdin.unref())},o=()=>e;export{n as a,i as b,o as c};
2
+ //# sourceMappingURL=chunk-X46DP23W.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/lib/prompts/stdin-ref.ts"],
4
+ "sourcesContent": ["import process from 'node:process'\n\n/**\n * Who is holding `process.stdin` open, and therefore whether node may exit.\n *\n * THE PROBLEM THIS SOLVES \u2014 Ink `unref()`s stdin when it drops raw mode on teardown\n * (ink/build/components/App.js:137), and `ref()`s it again when it arms raw mode\n * (App.js:225). That is correct and self-balancing FOR INK. But an `@inquirer/*` prompt\n * refs nothing: opened after an Ink screen has torn down, it reads a stdin that is still\n * unref'd, so once the command's other work settles the event loop drains MID-PROMPT and\n * node exits 13 with the question still on screen. (Verified: an Ink teardown followed by\n * a bare `confirm()` exits 13; the same flow with a `ref()` before the prompt completes\n * normally.) The old fix re-ref'd stdin after EVERY Ink render (tui/boot.tsx), which\n * bought the prompt its handle and cost the session shell its exit \u2014 a ref'd tty\n * ReadStream holds the loop open whether or not anything is listening to it.\n *\n * THE RULE \u2014 stdin is ref'd exactly while an in-process reader intends to read it. Ink\n * asserts that for itself; `withEscape` asserts it for every inquirer prompt by acquiring\n * here. Nobody else may touch `process.stdin.ref` / `.unref`.\n *\n * WHY A COUNTER AND NOT A BOOLEAN \u2014 `ref()`/`unref()` on a tty `ReadStream` is a sticky\n * flag, not a counter: last writer wins, process-wide. So nested readers (the dev wizard\n * runs ~5 prompts back to back, and `release-create` nests) need us to remember how many\n * are live, or the FIRST one to finish would unref a handle the outer one is still\n * reading from.\n *\n * BOUNDARY \u2014 no Ink render may happen inside a `withEscape` callback. Ink's teardown\n * `unref()`s unconditionally, clobbering an outer reader's ref while this counter still\n * says someone is reading; the counter cannot stop it, because `unref` is not a hook\n * point. Nothing STRUCTURALLY enforces this today (the sweep in\n * `__tests__/every-inquirer-site-is-escapable.test.ts` proves prompts sit inside\n * `withEscape` \u2014 a different claim, and it would not catch an Ink screen rendered there).\n * It holds by construction: every `withEscape` callback in the tree contains an\n * `@inquirer/*` call and nothing else. `tui/boot.tsx` re-asserts the ref whenever this\n * counter reports a live reader, which makes a future violation survivable rather than a\n * silent exit 13 \u2014 but it is a net, not a fence.\n *\n * Process-wide state is correct here and is NOT shared across the session shell's spawn\n * boundary: the parent and each spawned child are separate processes with separate\n * counters (lib/session/run-session.ts:152).\n */\nlet readers = 0\n\n/**\n * Register a reader and ref stdin if it is the first. Pair with {@link releaseStdin} in a\n * `finally`, or node will never exit.\n *\n * @example\n * acquireStdin() // => stdin ref'd on 0 -> 1\n */\nexport const acquireStdin = (): void => {\n readers += 1\n\n if (readers === 1) process.stdin.ref()\n}\n\n/**\n * Drop a reader and unref stdin once the last one leaves, letting node exit naturally.\n *\n * Clamped at zero rather than trusting callers: a release without a matching acquire\n * would otherwise drive the count negative and silently disarm the next `acquireStdin`\n * (`readers === 1` would never hold again), turning this guard into the bug it prevents.\n *\n * @example\n * releaseStdin() // => stdin unref'd on 1 -> 0\n */\nexport const releaseStdin = (): void => {\n if (readers === 0) return\n\n readers -= 1\n\n if (readers === 0) process.stdin.unref()\n}\n\n/**\n * How many in-process readers currently intend to read stdin. Consulted by `tui/boot.tsx`\n * to decide whether Ink's teardown `unref()` just clobbered somebody.\n *\n * @example\n * stdinReaderCount() // => 0 when no prompt is open\n */\nexport const stdinReaderCount = (): number => {\n return readers\n}\n"],
5
+ "mappings": "AAAA,OAAOA,MAAa,eAyCpB,IAAIC,EAAU,EASDC,EAAe,IAAY,CACtCD,GAAW,EAEPA,IAAY,GAAGD,EAAQ,MAAM,IAAI,CACvC,EAYaG,EAAe,IAAY,CAClCF,IAAY,IAEhBA,GAAW,EAEPA,IAAY,GAAGD,EAAQ,MAAM,MAAM,EACzC,EASaI,EAAmB,IACvBH",
6
+ "names": ["process", "readers", "acquireStdin", "releaseStdin", "stdinReaderCount"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{a as L}from"./chunk-LKKKDB6V.js";import{O as R,Q as A,c as v,i as S,v as i}from"./chunk-PZU6BLQR.js";import $ from"node:process";import{$ as w}from"zx";var B=async()=>{try{return await w`cmux --version`.quiet(),!0}catch{return!1}},D=async r=>{let{cwd:t,title:e,layout:o}=r,s=JSON.stringify(o),n=(await w({env:{...$.env,CMUX_QUIET:"1"}})`cmux new-workspace --name ${e} --cwd ${t} --focus false --layout ${s}`).stdout;return I(n)},M=async r=>{try{await w`cmux close-workspace --workspace ${r}`.quiet()}catch(t){i.debug({error:t,ref:r},"cmux: skipped closing dev workspace")}},I=r=>{let t=r.match(/workspace:\d+/);if(!t)throw new Error("cmux: could not locate workspace ref in new-workspace output");return t[0]};import{$ as U}from"zx";import{realpath as F}from"node:fs/promises";import{$ as O}from"zx";var g=async r=>{try{return await F(r)}catch{return r}},y=async()=>{try{let[r,t]=await Promise.all([O`cmux workspace list --json`.quiet(),O`cmux workspace-group list --json`.quiet()]),e=JSON.parse(r.stdout).workspaces??[],o=JSON.parse(t.stdout).groups??[],s=new Set(o.map(n=>n.anchor_workspace_ref).filter(n=>typeof n=="string")),a=new Map;for(let n of e)!n.current_directory||s.has(n.ref)||a.set(await g(n.current_directory),n.ref);return a}catch(r){return i.debug({error:r},"cmux: skipped listing workspaces by cwd"),new Map}};var q=async r=>{try{let e=(await y()).get(await g(r));if(!e)return;await U`cmux workspace close ${e}`.quiet()}catch(t){i.debug({error:t,cwd:r},"cmux: skipped closing workspace by cwd")}};import{$ as k}from"zx";var _=async r=>{try{let t=(await k`cmux workspace-group list --json`.quiet()).stdout;return(JSON.parse(t).groups??[]).find(s=>s.name===r)?.ref??null}catch(t){return i.debug({error:t,name:r},"cmux: skipped group lookup"),null}},j=async(r,t)=>{try{let o=(await k`cmux workspace-group create --name ${r} --from ${t.join(",")}`).stdout.match(/workspace_group:\d+/)?.[0]??await _(r);return o&&await k`cmux workspace-group pin ${o}`.quiet(),o??null}catch(e){return i.warn({error:e,name:r},"\u26A0\uFE0F cmux: failed to create workspace group"),null}};import{$ as p}from"zx";var J=async r=>{let{cwd:t,title:e,group:o}=r,s=R(await A()),a=o?(await p`cmux workspace create --cwd ${t} --group ${o} --group-placement end`).stdout:(await p`cmux workspace create --cwd ${t}`).stdout,n=H(a),l=(await p`cmux list-pane-surfaces --workspace ${n}`).stdout,u=G(l);return await p`cmux new-split right --workspace ${n} --surface ${u}`,s==="three-pane"&&await p`cmux new-split down --workspace ${n} --surface ${u}`,e&&await p`cmux workspace rename --workspace ${n} --title ${e}`,n},G=r=>{let t=r.match(/surface:\d+/);if(!t)throw new Error("cmux: could not locate initial surface in list-pane-surfaces output");return t[0]},H=r=>{let t=r.match(/workspace:\d+/);if(!t)throw new Error("cmux: could not locate workspace ref in workspace create output");return t[0]};var z=r=>{let{branch:t}=r,e=v(t);return e?S(e):t};var K=new Set(["ExitPromptError","AbortPromptError","PromptCancelledError"]),W=r=>r instanceof Error&&K.has(r.name),Qr=r=>{if(W(r))return!0;let t=r?.cause;return W(t)};import{execFile as Q}from"node:child_process";import{createHash as V}from"node:crypto";import{existsSync as X,readFileSync as f}from"node:fs";import Y from"node:http";import Z from"node:https";import rr from"node:net";import{homedir as tr}from"node:os";import{dirname as h,join as m}from"node:path";import d from"node:process";import er from"node:tls";import{fileURLToPath as or}from"node:url";import{promisify as sr}from"node:util";var nr=sr(Q),ar=r=>{let t=JSON.parse(f(r,"utf-8")),e=typeof t.bin=="string"?t.bin:t.bin?.portless;return e==null||e===""?null:e},ir=()=>{try{let r=h(or(import.meta.url));for(;;){let t=m(r,"node_modules","portless","package.json");if(X(t)){let o=ar(t);return o==null?null:m(h(t),o)}let e=h(r);if(e===r)return null;r=e}}catch{return null}},P,N=()=>(P===void 0&&(P=ir()),P),cr=/^[\w@%+=:,./-]+$/,ur="'\\''",pr=r=>cr.test(r)?r:`'${r.replaceAll("'",ur)}'`,pt=(r,t)=>{let e=[t.execPath??d.execPath,t.bin,...r];return(t.sudo===!0?"sudo ":"")+e.map(pr).join(" ")},mr=1500,C=1500,lr="x-portless",b="127.0.0.1",fr="localhost",gr=r=>new Promise(t=>{let e=rr.connect({host:b,port:r}),o=s=>{e.destroy(),t(s)};e.setTimeout(C),e.once("connect",()=>{o(!0)}),e.once("timeout",()=>{o(!1)}),e.once("error",()=>{o(!1)})}),dr=(r,t)=>new Promise(e=>{let s=(t?Z.request:Y.request)({host:b,port:r,method:"HEAD",path:"/",timeout:C,...t?{rejectUnauthorized:!1,servername:fr}:{}},a=>{a.resume(),e(a.headers[lr]==="1")});s.on("error",()=>{e(!1)}),s.on("timeout",()=>{s.destroy(),e(!1)}),s.end()}),xr=async(r,{timeoutMs:t})=>{let e=N();if(e==null)throw new Error("portless is not installed (not resolvable from node_modules)");await nr(d.execPath,[e,...r],{signal:AbortSignal.timeout(t),encoding:"utf-8",env:L(d.env)})},E=()=>d.env.PORTLESS_STATE_DIR??m(tr(),".portless"),wr="ca.pem",yr="ca.trusted",T=()=>m(E(),wr),mt=()=>{try{let r=f(m(E(),yr),"utf-8").trim();return r===""?!1:V("sha256").update(f(T())).digest("hex")===r.toLowerCase()}catch{return!1}},lt=(r,t)=>new Promise(e=>{let o;try{o=f(T())}catch{e({ok:!1,code:"ENOENT"});return}let s=er.connect({host:b,port:r,servername:t,ca:[o],rejectUnauthorized:!0},()=>{let a=s.authorizationError,n=s.authorized;s.destroy(),e(n?{ok:!0}:{ok:!1,code:a?.code??a?.message??"UNKNOWN"})});s.setTimeout(C),s.once("timeout",()=>{s.destroy(),e({ok:!1,code:"ETIMEDOUT"})}),s.once("error",a=>{s.destroy(),e({ok:!1,code:a.code??"UNKNOWN"})})}),ft=()=>{try{let r=JSON.parse(f(m(E(),"routes.json"),"utf-8"));return Array.isArray(r)?r.flatMap(t=>{let{hostname:e,port:o}=t??{};return typeof e!="string"||e===""||typeof o!="number"?[]:[{name:e,port:o}]}):[]}catch{return[]}},gt=(r={})=>{let t=r.bin===void 0?N():r.bin,e=r.run??xr,o=r.isListening??gr,s=r.isProxyServing??dr,a=r.timeoutMs??mr,n=null,l=async c=>{try{return await e(c,{timeoutMs:a}),!0}catch{return!1}},u=async()=>(n??=await l(["--version"]),n);return{binPath:()=>t,isAvailable:u,isProxyServing:async(c,x)=>!await u()||!await o(c)?!1:s(c,x),registerAlias:async(c,x)=>await u()?l(["alias",c,String(x)]):!1,removeAlias:async c=>{await u()&&await l(["alias","--remove",c])}}};export{ir as a,pt as b,gr as c,dr as d,T as e,mt as f,lt as g,ft as h,gt as i,g as j,y as k,q as l,_ as m,j as n,B as o,D as p,M as q,J as r,z as s,Qr as t};
2
+ //# sourceMappingURL=chunk-XTRPE4SX.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/integrations/cmux/open-dev-workspace.ts", "../src/integrations/cmux/close-workspace-by-cwd.ts", "../src/integrations/cmux/list-workspaces-by-cwd.ts", "../src/integrations/cmux/cmux-groups.ts", "../src/integrations/cmux/open-workspace-with-layout.ts", "../src/integrations/cmux/workspace-title.ts", "../src/lib/errors/is-prompt-cancellation.ts", "../src/dev/proxy/portless-driver.ts"],
4
+ "sourcesContent": ["import process from 'node:process'\nimport { $ } from 'zx'\n\nimport type { CmuxLayoutNode } from 'src/dev/cmux-layout'\nimport { logger } from 'src/lib/logger'\n\n/** Args for {@link openCmuxDevWorkspace}: the workspace root, title, and pane layout tree. */\ninterface OpenCmuxDevWorkspaceArgs {\n cwd: string\n title: string\n layout: CmuxLayoutNode\n}\n\n/**\n * True iff the `cmux` CLI is invokable (i.e. `cmux --version` resolves). Used to\n * gate `--cmux` mode and fall back to single-process dev when cmux is absent.\n */\nexport const isCmuxAvailable = async (): Promise<boolean> => {\n try {\n await $`cmux --version`.quiet()\n\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Open ONE cmux workspace rooted at `cwd`, laid out per `layout` (one pane per\n * command). Runs in a `CMUX_QUIET=1` scoped env to suppress cmux's one-time compat\n * notice, then parses and returns the `workspace:<id>` ref from stdout.\n */\nexport const openCmuxDevWorkspace = async (args: OpenCmuxDevWorkspaceArgs): Promise<string> => {\n const { cwd, title, layout } = args\n const layoutJson = JSON.stringify(layout)\n\n const $cmux = $({ env: { ...process.env, CMUX_QUIET: '1' } })\n const output = (await $cmux`cmux new-workspace --name ${title} --cwd ${cwd} --focus false --layout ${layoutJson}`)\n .stdout\n\n return parseWorkspaceRef(output)\n}\n\n/**\n * Best-effort close of the cmux workspace `ref`, tearing down the workspace and\n * every pane process. Silently no-ops (debug-logged) if cmux isn't running or the\n * close fails, mirroring {@link file://./close-workspace-by-cwd.ts}.\n */\nexport const closeCmuxDevWorkspace = async (ref: string): Promise<void> => {\n try {\n await $`cmux close-workspace --workspace ${ref}`.quiet()\n } catch (error) {\n logger.debug({ error, ref }, 'cmux: skipped closing dev workspace')\n }\n}\n\n/**\n * Extract the `workspace:<id>` ref from `cmux new-workspace` output (e.g.\n * `OK workspace:5`). Throws a clear error when no ref is present.\n *\n * @example\n * parseWorkspaceRef('OK workspace:5\\n') // => 'workspace:5'\n */\nconst parseWorkspaceRef = (output: string): string => {\n const match = output.match(/workspace:\\d+/)\n\n if (!match) {\n throw new Error('cmux: could not locate workspace ref in new-workspace output')\n }\n\n return match[0]\n}\n", "import { $ } from 'zx'\n\nimport { logger } from 'src/lib/logger'\n\nimport { listCmuxWorkspacesByCwd, realpathForCmuxCwd } from './list-workspaces-by-cwd'\n\n/**\n * Best-effort close of the cmux workspace rooted at `cwd`. Resolves the workspace\n * ref via {@link listCmuxWorkspacesByCwd} (which excludes group anchors, so this\n * can never close a group header) and closes it. Silently no-ops if cmux isn't\n * running, no workspace matches the cwd, or the close fails.\n *\n * Called BEFORE `git worktree remove`, so `cwd` still exists and normalizes the\n * same way as cmux's reported `current_directory`.\n */\nexport const closeCmuxWorkspaceByCwd = async (cwd: string): Promise<void> => {\n try {\n const byCwd = await listCmuxWorkspacesByCwd()\n\n const ref = byCwd.get(await realpathForCmuxCwd(cwd))\n\n if (!ref) {\n return\n }\n\n await $`cmux workspace close ${ref}`.quiet()\n } catch (error) {\n logger.debug({ error, cwd }, 'cmux: skipped closing workspace by cwd')\n }\n}\n", "import { realpath } from 'node:fs/promises'\nimport { $ } from 'zx'\n\nimport { logger } from 'src/lib/logger'\n\ninterface CmuxWorkspaceListEntry {\n ref: string\n current_directory: string | null\n}\n\ninterface CmuxGroupListEntry {\n anchor_workspace_ref: string | null\n}\n\n/**\n * Resolve `path` through the filesystem, falling back to the input when it can't\n * be resolved (e.g. the directory was already removed). Both the cmux-reported\n * cwd and the caller's target cwd are normalized the same way so a macOS\n * `/var`\u2192`/private/var` style symlink can't make two equal paths compare unequal\n * (the realpath-asymmetry footgun this repo has been burned by before).\n */\nconst safeRealpath = async (path: string): Promise<string> => {\n try {\n return await realpath(path)\n } catch {\n return path\n }\n}\n\n/**\n * Build a `realpath(cwd) \u2192 workspace ref` map of every currently-open cmux\n * workspace, keyed on its working directory. This is the dedup/close identity for\n * infra-kit's worktree workspaces \u2014 unique per worktree, and (unlike the title)\n * stable after the repo-name title prefix was dropped (two repos can share a\n * branch name like `fix-post-script-ci-cd`, but never a worktree path).\n *\n * Two entries are deliberately excluded:\n * - workspaces with no `current_directory` (e.g. `infra-kit dev` panes) \u2014 they\n * carry no worktree identity;\n * - group ANCHOR workspaces \u2014 cmux reports the anchor's cwd as the main-repo\n * root, so it collides with the real main-checkout workspace. Keying an\n * anchor into the map would shadow the main workspace (breaking dedup) and,\n * worse, let close-by-cwd close the group header. Anchors are dropped up front\n * via `workspace-group list`'s `anchor_workspace_ref`.\n *\n * Returns an empty map if cmux isn't running or the output can't be parsed \u2014\n * callers treat \"empty\" as \"unknown, proceed as if nothing is open\".\n */\nexport const listCmuxWorkspacesByCwd = async (): Promise<Map<string, string>> => {\n try {\n const [workspacesOutput, groupsOutput] = await Promise.all([\n $`cmux workspace list --json`.quiet(),\n $`cmux workspace-group list --json`.quiet(),\n ])\n\n const workspaces = (JSON.parse(workspacesOutput.stdout).workspaces ?? []) as CmuxWorkspaceListEntry[]\n const groups = (JSON.parse(groupsOutput.stdout).groups ?? []) as CmuxGroupListEntry[]\n\n const anchorRefs = new Set(\n groups\n .map((group) => {\n return group.anchor_workspace_ref\n })\n .filter((ref): ref is string => {\n return typeof ref === 'string'\n }),\n )\n\n const byCwd = new Map<string, string>()\n\n for (const workspace of workspaces) {\n if (!workspace.current_directory || anchorRefs.has(workspace.ref)) {\n continue\n }\n\n byCwd.set(await safeRealpath(workspace.current_directory), workspace.ref)\n }\n\n return byCwd\n } catch (error) {\n logger.debug({ error }, 'cmux: skipped listing workspaces by cwd')\n\n return new Map()\n }\n}\n\n/** Exported for callers that need the same normalization when looking up a target cwd. */\nexport { safeRealpath as realpathForCmuxCwd }\n", "import { $ } from 'zx'\n\nimport { logger } from 'src/lib/logger'\n\ninterface CmuxGroupListEntry {\n ref: string\n name: string\n}\n\n/**\n * Return the `workspace_group:N` ref of the sidebar group titled exactly `name`,\n * or `null` if no such group exists (or cmux is unreachable). Group names are the\n * repo name (basename of the main-repo root), so worktree workspaces for one repo\n * all resolve to the same group regardless of which worktree the command runs in.\n */\nexport const findCmuxGroupRefByName = async (name: string): Promise<string | null> => {\n try {\n const output = (await $`cmux workspace-group list --json`.quiet()).stdout\n\n const groups = (JSON.parse(output).groups ?? []) as CmuxGroupListEntry[]\n\n const match = groups.find((group) => {\n return group.name === name\n })\n\n return match?.ref ?? null\n } catch (error) {\n logger.debug({ error, name }, 'cmux: skipped group lookup')\n\n return null\n }\n}\n\n/**\n * Create a pinned sidebar group named `name` seeded with EXACTLY `fromRefs`, and\n * return its ref (or `null` on failure).\n *\n * `--from` is load-bearing: `cmux workspace-group create` defaults `--from` to the\n * currently-selected/caller workspace, so omitting it silently captures an\n * unrelated workspace (and, under the `--all` fan-out, steals a sibling repo's\n * freshly-created workspace). Passing the seed refs explicitly makes creation\n * deterministic and capture-free \u2014 no snapshot/self-heal needed. The group also\n * gets a synthetic anchor workspace (its header row), matching how the user's\n * groups are already shaped.\n */\nexport const createCmuxGroupFrom = async (name: string, fromRefs: string[]): Promise<string | null> => {\n try {\n const output = (await $`cmux workspace-group create --name ${name} --from ${fromRefs.join(',')}`).stdout\n\n const ref = output.match(/workspace_group:\\d+/)?.[0] ?? (await findCmuxGroupRefByName(name))\n\n if (ref) {\n await $`cmux workspace-group pin ${ref}`.quiet()\n }\n\n return ref ?? null\n } catch (error) {\n logger.warn({ error, name }, '\u26A0\uFE0F cmux: failed to create workspace group')\n\n return null\n }\n}\n", "import { $ } from 'zx'\n\nimport { getInfraKitConfig, resolveCmuxLayout } from 'src/lib/infra-kit-config'\n\ninterface OpenCmuxWorkspaceArgs {\n cwd: string\n title?: string\n /**\n * When set, the workspace is created directly inside this sidebar group\n * (`workspace_group:N` ref) via `--group`, appended at the end so existing\n * members aren't reordered. Omit to create an ungrouped workspace (e.g. the\n * first worktree of a repo, which then seeds a new group).\n */\n group?: string\n}\n\n/**\n * Opens a new cmux workspace rooted at `cwd`, with panes arranged per the\n * configured `worktrees.cmux.layout` (resolved via {@link resolveCmuxLayout},\n * default `two-columns`):\n * two-columns \u2014 left | right, both full-height (two panes)\n * three-pane \u2014 left-top / left-bottom | full-height right (three panes)\n * All panes inherit `cwd` from the workspace. Returns the created\n * `workspace:N` ref so callers can seed a group from it.\n */\nexport const openCmuxWorkspaceWithLayout = async (args: OpenCmuxWorkspaceArgs): Promise<string> => {\n const { cwd, title, group } = args\n\n const layout = resolveCmuxLayout(await getInfraKitConfig())\n\n const newWorkspaceOutput = group\n ? (await $`cmux workspace create --cwd ${cwd} --group ${group} --group-placement end`).stdout\n : (await $`cmux workspace create --cwd ${cwd}`).stdout\n\n const workspaceRef = parseWorkspaceRef(newWorkspaceOutput)\n\n const surfacesOutput = (await $`cmux list-pane-surfaces --workspace ${workspaceRef}`).stdout\n\n const leftTopRef = parseFirstSurfaceRef(surfacesOutput)\n\n // Both layouts share the vertical split into left | right columns; only the\n // legacy three-pane layout additionally splits the left column top/bottom.\n await $`cmux new-split right --workspace ${workspaceRef} --surface ${leftTopRef}`\n\n if (layout === 'three-pane') {\n await $`cmux new-split down --workspace ${workspaceRef} --surface ${leftTopRef}`\n }\n\n if (title) {\n await $`cmux workspace rename --workspace ${workspaceRef} --title ${title}`\n }\n\n return workspaceRef\n}\n\n/**\n * Extracts the first `surface:<id>` reference from the output of\n * `cmux list-pane-surfaces`. Used to locate the initial (primary) pane\n * surface so subsequent splits can be anchored relative to it.\n *\n * @example\n * const output = 'surface:12 (active)\\nsurface:13\\n'\n * parseFirstSurfaceRef(output) // => 'surface:12'\n */\nconst parseFirstSurfaceRef = (output: string): string => {\n const match = output.match(/surface:\\d+/)\n\n if (!match) {\n throw new Error('cmux: could not locate initial surface in list-pane-surfaces output')\n }\n\n return match[0]\n}\n\n/**\n * Extracts the `workspace:<id>` reference from the output of\n * `cmux workspace create`. The returned ref is used to target the newly\n * created workspace in follow-up `cmux` commands (splits, rename, etc.).\n *\n * @example\n * const output = 'created workspace:7\\n'\n * parseWorkspaceRef(output) // => 'workspace:7'\n */\nconst parseWorkspaceRef = (output: string): string => {\n const match = output.match(/workspace:\\d+/)\n\n if (!match) {\n throw new Error('cmux: could not locate workspace ref in workspace create output')\n }\n\n return match[0]\n}\n", "import { displayLabel, parseBranchName } from 'src/lib/release-id'\n\ninterface BuildCmuxWorkspaceTitleArgs {\n branch: string\n}\n\n/**\n * Builds the cmux workspace title for a worktree. The title is now just the\n * branch's display label \u2014 the repo name is NOT prefixed, because every worktree\n * workspace lives inside a per-repo sidebar GROUP whose header already carries the\n * repo name (e.g. group `hulyo-monorepo` \u2192 workspaces titled `1.48.0`,\n * `checkout-redesign`, `dev`). Release branches render via their release-id label\n * (`release/v1.48.0` \u2192 `1.48.0`, `release/checkout-redesign` \u2192 `checkout-redesign`);\n * non-release branches fall back to the raw branch string.\n *\n * Note: the title is NO LONGER a dedup/close key \u2014 that role moved to the\n * workspace cwd (see {@link listCmuxWorkspacesByCwd}), because dropping the repo\n * prefix makes titles collide across repos (both hulyo and travelist can have a\n * `fix-post-script-ci-cd` branch).\n */\nexport const buildCmuxWorkspaceTitle = (args: BuildCmuxWorkspaceTitleArgs): string => {\n const { branch } = args\n\n const id = parseBranchName(branch)\n\n return id ? displayLabel(id) : branch\n}\n", "/**\n * Names of the error classes thrown when an interactive prompt ends without a\n * value. From `@inquirer/core`: `ExitPromptError` (Ctrl-C \u2014 and ONLY Ctrl-C:\n * inquirer binds no escape key, so it raises this from readline's SIGINT) and\n * `AbortPromptError` (the prompt was aborted via an `AbortSignal`, which is how\n * Esc arrives \u2014 see lib/prompts/escapable-context). From our own Ink pickers:\n * `PromptCancelledError` (see ./prompt-cancelled-error), which is registered here\n * rather than impersonating an inquirer class name. All are intentional\n * cancellations, not failures.\n */\nconst CANCELLATION_ERROR_NAMES = new Set(['ExitPromptError', 'AbortPromptError', 'PromptCancelledError'])\n\nconst hasCancellationName = (value: unknown): boolean => {\n return value instanceof Error && CANCELLATION_ERROR_NAMES.has(value.name)\n}\n\n/**\n * True when `error` represents a user (or signal) cancellation of an interactive\n * prompt \u2014 Ctrl-C anywhere, or Esc, which reaches an `@inquirer` prompt as an\n * abort and an Ink picker as a `PromptCancelledError`. Matched by `name` rather\n * than `instanceof` so it stays correct\n * even when pnpm dedupes more than one copy of `@inquirer/core` into the tree\n * (an `instanceof` check fails across realms/duplicate classes).\n *\n * Also unwraps one level of `cause`, so a cancellation re-wrapped in an\n * {@link ./operation-error.OperationError} is still recognised at the top-level\n * error boundary.\n *\n * @example\n * try {\n * await checkbox({ message: 'Select release branches', choices })\n * } catch (err) {\n * if (isPromptCancellation(err)) process.exit(0) // clean back-out, not an error\n * throw err\n * }\n */\nexport const isPromptCancellation = (error: unknown): boolean => {\n if (hasCancellationName(error)) return true\n\n const cause = (error as { cause?: unknown } | null | undefined)?.cause\n\n return hasCancellationName(cause)\n}\n", "/**\n * Thin, injectable driver for the `portless` daemon (Layer B \u2014 see `.omc/plans/dev-https-portless.md`).\n *\n * `infra-kit dev` uses it to register `<release>.<package>.localhost \u2192 127.0.0.1:<port>` routes so the\n * hero URLs resolve over **HTTPS on :443, with no port in the URL**. Every call here is **time-bounded and\n * never throws**: a missing binary, a non-zero exit, or a wedged process resolves to `false`/no-op. That is\n * a reporting contract, not a tolerance one \u2014 portless IS a hard dependency of the dev loop, and\n * `DevServerRunner.ensureProxy` turns a `false` from this driver into a fatal, actionable start error.\n *\n * The binary is NOT resolved from `PATH`: `portless` is a normal npm dependency living in\n * `node_modules/.bin`, which is on `PATH` only when the process was launched via pnpm/npm. Since\n * `infra-kit dev` is often launched otherwise (a global bin, a cmux runner, a foreign cwd), we resolve\n * portless's own `dist/cli.js` by walking `node_modules` from this file (see {@link resolvePortlessBin})\n * and run it with the current `node` (`process.execPath`) \u2014 so it works regardless of how `dev` was\n * invoked. Args are fixed literals plus discovered release/package names + a numeric port, never\n * shell-interpolated.\n *\n * **The daemon is PROBED, never started.** `:443` is privileged, and portless binds it by re-execing\n * itself through `sudo` with an inherited stdio \u2014 which a detached `stdio:'ignore'` child can never\n * satisfy: the password prompt has nowhere to go. Setup is one-time and out-of-band: a root\n * `portless service install`, printed for the user by {@link formatPortlessCommand} (never as a bare\n * `portless`, which no shell can resolve \u2014 see there).\n *\n * All process I/O is injected (`run` for awaited commands, `isProxyServing` for the wire probe) so tests\n * never shell out and can assert the exact portless argv.\n */\nimport type { Buffer } from 'node:buffer'\nimport { execFile } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport { existsSync, readFileSync } from 'node:fs'\nimport http from 'node:http'\nimport https from 'node:https'\nimport net from 'node:net'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\nimport process from 'node:process'\nimport tls from 'node:tls'\nimport { fileURLToPath } from 'node:url'\nimport { promisify } from 'node:util'\n\nimport { withoutPackageManagerEnv } from 'src/lib/pm-env'\n\nconst execFileAsync = promisify(execFile)\n\n/** Read `bin.portless` (the `dist/cli.js` relative path) from a portless `package.json` on disk. */\nconst readBinRel = (pkgJsonPath: string): string | null => {\n const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8')) as { bin?: string | Record<string, string> }\n const rel = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.portless\n\n return rel == null || rel === '' ? null : rel\n}\n\n/**\n * Resolve the absolute path to portless's CLI entry (`portless/dist/cli.js`) from node_modules,\n * independent of `PATH`. Returns `null` when portless is not installed, degrading the whole driver to\n * a no-op.\n *\n * portless is ESM-only (its `.` export exposes only `import`/`types`, no `require`), so\n * `createRequire().resolve` can't see it. We instead walk `node_modules` upward from this file \u2014 the\n * standard resolution path \u2014 and read the package's `package.json` straight off disk, which bypasses the\n * exports map that would otherwise hide both `package.json` and the main entry.\n */\nexport const resolvePortlessBin = (): string | null => {\n try {\n let dir = dirname(fileURLToPath(import.meta.url))\n\n for (;;) {\n const pkgJsonPath = join(dir, 'node_modules', 'portless', 'package.json')\n\n if (existsSync(pkgJsonPath)) {\n const rel = readBinRel(pkgJsonPath)\n\n return rel == null ? null : join(dirname(pkgJsonPath), rel)\n }\n const parent = dirname(dir)\n\n if (parent === dir) return null\n dir = parent\n }\n } catch {\n return null\n }\n}\n\n/** Resolve portless's CLI once per process \u2014 the on-disk location never changes within a run. */\nlet cachedBin: string | null | undefined\nconst portlessBin = (): string | null => {\n if (cachedBin === undefined) cachedBin = resolvePortlessBin()\n\n return cachedBin\n}\n\n/**\n * Characters that survive a POSIX shell unquoted. Anything outside this set (a space, a paren \u2014 both of\n * which appear in real install paths like `/Applications/My Editor.app`) gets single-quoted.\n */\nconst SHELL_SAFE = /^[\\w@%+=:,./-]+$/\n\n/** A literal `'` inside single quotes: close, emit an escaped quote, reopen \u2014 the only way a shell allows it. */\nconst SINGLE_QUOTE_ESCAPE = \"'\\\\''\"\n\n/** Single-quote `value` for a POSIX shell unless it is already inert. */\nconst shellQuote = (value: string): string => {\n return SHELL_SAFE.test(value) ? value : `'${value.replaceAll(\"'\", SINGLE_QUOTE_ESCAPE)}'`\n}\n\n/** Seams for {@link formatPortlessCommand}, injected so tests never depend on the real node_modules layout. */\nexport interface FormatPortlessCommandOptions {\n /**\n * Absolute path to portless's `dist/cli.js`. **Required and non-nullable on purpose.** The caller must\n * have resolved portless before it can describe how to run it, so \"I could not find the binary\" cannot be\n * silently rendered as a plausible-looking command \u2014 the type makes that unwritable rather than merely\n * discouraged. A `null` bin is a different report (\"run `pnpm install`\"), which every caller makes first.\n */\n bin: string\n /** Prefix with `sudo` \u2014 only `service install`, which binds the privileged `:443`, needs it. */\n sudo?: boolean\n execPath?: string\n}\n\n/**\n * Render a portless command the user can actually paste into a shell.\n *\n * This exists because the obvious string is a lie. `portless` is a plain npm dependency living in\n * `node_modules/.bin`, which is on `PATH` only inside a pnpm/npm script \u2014 so printing `sudo portless service\n * install` hands the user a command that dies with `sudo: portless: command not found`. `sudo` makes it\n * strictly worse: it replaces `PATH` with `secure_path`, so even a shell that *could* resolve `portless`\n * loses it the moment the command is elevated.\n *\n * We therefore print what the driver itself runs (see {@link defaultRun}): the current interpreter, by\n * absolute path, invoking portless's `dist/cli.js`, by absolute path. Nothing is resolved from `PATH`, so the\n * command works under `sudo`, from any cwd, and however `infra-kit` was launched.\n *\n * This is not merely cosmetic. portless's `service install` writes **the interpreter and script path it was\n * invoked with** straight into the launchd plist's `ProgramArguments` (`nodePath: process.execPath` plus\n * `process.argv[1]`), so the command printed here is the command that gets installed as a **root system\n * daemon**. Printing a name for the shell to resolve would not just fail \u2014 it would decide what runs as root.\n *\n * @example\n * formatPortlessCommand(['service', 'install'], { sudo: true, bin })\n * // 'sudo /usr/local/bin/node /repo/node_modules/portless/dist/cli.js service install'\n */\nexport const formatPortlessCommand = (args: string[], options: FormatPortlessCommandOptions): string => {\n const words = [options.execPath ?? process.execPath, options.bin, ...args]\n const prefix = options.sudo === true ? 'sudo ' : ''\n\n return prefix + words.map(shellQuote).join(' ')\n}\n\n/** Awaited portless invocation. Rejects on non-zero exit / timeout; the driver swallows that into a no-op. */\nexport type PortlessRun = (args: string[], opts: { timeoutMs: number }) => Promise<void>\n\n/** Cheap \"is anything at all accepting TCP here?\" pre-filter in front of the wire probe. */\nexport type IsListening = (port: number) => Promise<boolean>\n\n/**\n * Ground-truth identity: is the process serving `port` actually **portless**, and (when `tls`) is it\n * serving **TLS**? See {@link defaultIsProxyServing} for why this cannot be answered from state files.\n */\nexport type IsProxyServing = (port: number, tls: boolean) => Promise<boolean>\n\nconst DEFAULT_TIMEOUT_MS = 1500\nconst PROBE_TIMEOUT_MS = 1500\n\n/** Response header portless sets on every response it serves. Node lower-cases response header names. */\nconst PORTLESS_HEADER = 'x-portless'\n\n/** IPv4 loopback: portless binds and dials `127.0.0.1`. */\nconst LOOPBACK = '127.0.0.1'\n\n/**\n * SNI for the probe. Node sends **no SNI to an IP literal** (RFC 6066), which would drop portless onto its\n * default certificate \u2014 whose SANs are `localhost`, `*.localhost`, `*.local` and contain **no IP entry**.\n * `localhost` is always in that set, so it is the one name guaranteed to work even on a machine with zero\n * aliases registered.\n */\nconst PROBE_SERVERNAME = 'localhost'\n\nexport const defaultIsListening: IsListening = (port) => {\n return new Promise((resolve) => {\n const socket = net.connect({ host: LOOPBACK, port })\n const finish = (result: boolean): void => {\n socket.destroy()\n resolve(result)\n }\n\n socket.setTimeout(PROBE_TIMEOUT_MS)\n socket.once('connect', () => {\n finish(true)\n })\n socket.once('timeout', () => {\n finish(false)\n })\n socket.once('error', () => {\n finish(false)\n })\n })\n}\n\n/**\n * Is the listener on `port` portless itself, serving `tls`? Proven **on the wire**, by asking it: portless\n * sets `X-Portless: 1` on every response, before route lookup \u2014 so an unrouted host still answers the probe\n * (a 404 with the header is a pass). This mirrors portless's own `isProxyRunning`.\n *\n * This replaces the old state-file check (`proxy.port` + `proxy.pid`), which was **unsound**: portless's\n * `resolveStateDir(_port)` ignores its port argument, so `proxy.port` / `proxy.pid` / `proxy.tls` are\n * process-global singletons shared by every daemon on every port. Starting ANY daemon rewrites them, and\n * stopping ANY daemon DELETES them \u2014 so a second, unrelated daemon (or a stale sibling repo still on the\n * old CLI, falling back to an unprivileged port) makes a perfectly healthy `:443` daemon look dead. Both\n * were reproduced against portless 0.15.1; see `.omc/research/portless-https-spike.md`.\n *\n * `rejectUnauthorized: false` is deliberate and load-bearing: this probe answers *\"is portless serving\n * here?\"*, **never** *\"is its CA trusted?\"*. Validating the chain here would collapse two different\n * failures \u2014 a daemon that is down, and a CA that was never trusted \u2014 into one indistinguishable error,\n * with two different fixes (a root `service install` vs the sudo-free `trust`). Trust is a separate,\n * explicitly-validating probe (doctor's CA check).\n */\nexport const defaultIsProxyServing: IsProxyServing = (port, tls) => {\n return new Promise((resolve) => {\n const request = tls ? https.request : http.request\n const req = request(\n {\n host: LOOPBACK,\n port,\n method: 'HEAD',\n path: '/',\n timeout: PROBE_TIMEOUT_MS,\n ...(tls ? { rejectUnauthorized: false, servername: PROBE_SERVERNAME } : {}),\n },\n (res) => {\n res.resume()\n resolve(res.headers[PORTLESS_HEADER] === '1')\n },\n )\n\n req.on('error', () => {\n resolve(false)\n })\n req.on('timeout', () => {\n req.destroy()\n resolve(false)\n })\n req.end()\n })\n}\n\nconst defaultRun: PortlessRun = async (args, { timeoutMs }) => {\n const bin = portlessBin()\n\n if (bin == null) throw new Error('portless is not installed (not resolvable from node_modules)')\n await execFileAsync(process.execPath, [bin, ...args], {\n signal: AbortSignal.timeout(timeoutMs),\n encoding: 'utf-8',\n env: withoutPackageManagerEnv(process.env),\n })\n}\n\n/**\n * portless's state directory. Exported so `doctor` reports on the same directory the driver reads.\n *\n * The default is deliberately **unchanged** (`~/.portless`): portless's `service install` bakes\n * `PORTLESS_STATE_DIR`, resolved from `SUDO_USER`, into the launchd plist \u2014 so the root daemon reads the\n * *invoking user's* home. Pointing this anywhere else by default would manufacture the very split it looks\n * like it prevents.\n */\nexport const portlessStateDir = (): string => {\n return process.env.PORTLESS_STATE_DIR ?? join(homedir(), '.portless')\n}\n\n/** portless's local CA certificate \u2014 the root every host cert it mints is signed by. */\nconst CA_CERT_FILE = 'ca.pem'\n\n/**\n * Marker portless's `trust` writes: the **hex sha256 of `ca.pem`'s bytes** that was added to the login\n * keychain (`writeTrustMarker` \u2192 `caFingerprint`, `cli.js:78-101`). It records WHICH CA was trusted, so a\n * regenerated CA leaves a marker that no longer matches.\n */\nconst CA_TRUST_MARKER_FILE = 'ca.trusted'\n\n/** A route portless is serving: `<name> \u2192 127.0.0.1:<port>`. */\nexport interface PortlessRoute {\n /**\n * The registered hostname (e.g. `2-4.client-api.localhost`). Usable verbatim as a\n * `portless alias --remove <name>` argument \u2014 portless strips a trailing TLD off the name it is handed\n * (`parseHostnames`, `chunk-SD2PIWJU.js:68-79`) \u2014 and as a TLS `servername`.\n */\n name: string\n port: number\n}\n\n/** Absolute path to portless's local CA certificate, in whichever state dir {@link portlessStateDir} names. */\nexport const readCaPath = (): string => {\n return join(portlessStateDir(), CA_CERT_FILE)\n}\n\n/**\n * Was `portless trust` run for the CA that is on disk right now? Compares `sha256(ca.pem)` against the\n * fingerprint recorded in `ca.trusted`. `false` when either file is missing; never throws.\n *\n * **This proves the marker was written for THIS fingerprint \u2014 not that the keychain still trusts it.** A\n * user who deletes the certificate from Keychain Access by hand leaves the marker behind and gets a false\n * pass here. That residual is accepted (reading the keychain would mean shelling out to `security` on a\n * check that must stay cheap); it is why this is a *separate* check from the chain handshake\n * ({@link handshakeChainsToCa}), which proves what the daemon actually serves.\n */\nexport const caFingerprintMatches = (): boolean => {\n try {\n const recorded = readFileSync(join(portlessStateDir(), CA_TRUST_MARKER_FILE), 'utf-8').trim()\n\n if (recorded === '') return false\n\n const actual = createHash('sha256').update(readFileSync(readCaPath())).digest('hex')\n\n return actual === recorded.toLowerCase()\n } catch {\n return false\n }\n}\n\n/** Outcome of {@link handshakeChainsToCa}: `code` is the Node TLS error code, which the caller discriminates on. */\nexport type HandshakeResult = { ok: true } | { ok: false; code: string }\n\n/**\n * Does the certificate served on `port` chain to the CA in `ca.pem`? A **validating** TLS handshake \u2014 the\n * complement of {@link defaultIsProxyServing}, which deliberately does not validate.\n *\n * `servername` is **mandatory and load-bearing**, never optional: Node sends no SNI to an IP literal\n * (RFC 6066), which drops portless onto its default certificate, whose SANs (`localhost`, `*.localhost`,\n * `*.local`) contain **no IP entry** \u2014 so a validating probe of `127.0.0.1` with no `servername` fails with\n * `ERR_TLS_CERT_ALTNAME_INVALID` against a perfectly healthy daemon. Any such code coming back from here is\n * therefore a bug in the CALLER's probe, never a finding about the user's trust store. Passing an\n * unregistered name is safe: portless's SNI callback mints a cert on demand for any servername.\n *\n * Time-bounded; never throws.\n */\nexport const handshakeChainsToCa = (port: number, servername: string): Promise<HandshakeResult> => {\n return new Promise((resolve) => {\n let ca: Buffer<ArrayBufferLike>\n\n try {\n ca = readFileSync(readCaPath())\n } catch {\n resolve({ ok: false, code: 'ENOENT' })\n\n return\n }\n\n const socket = tls.connect({ host: LOOPBACK, port, servername, ca: [ca], rejectUnauthorized: true }, () => {\n // With `rejectUnauthorized: true` a chain failure normally surfaces as an 'error' event and this\n // callback never runs; the check is here so a future Node that connects-then-reports can't slip a\n // rejected chain through as a pass.\n const authError = socket.authorizationError as NodeJS.ErrnoException | undefined\n const authorized = socket.authorized\n\n socket.destroy()\n resolve(authorized ? { ok: true } : { ok: false, code: authError?.code ?? authError?.message ?? 'UNKNOWN' })\n })\n\n socket.setTimeout(PROBE_TIMEOUT_MS)\n socket.once('timeout', () => {\n socket.destroy()\n resolve({ ok: false, code: 'ETIMEDOUT' })\n })\n socket.once('error', (err: NodeJS.ErrnoException) => {\n socket.destroy()\n resolve({ ok: false, code: err.code ?? 'UNKNOWN' })\n })\n })\n}\n\n/**\n * Routes portless currently has registered, read from `routes.json` in {@link portlessStateDir}. `[]` on any\n * failure (absent file, malformed JSON, unexpected shape) \u2014 an unreadable route list is reported as \"no\n * routes\", never as an error, because every caller uses this for diagnostics only.\n */\nexport const listRoutes = (): PortlessRoute[] => {\n try {\n const raw: unknown = JSON.parse(readFileSync(join(portlessStateDir(), 'routes.json'), 'utf-8'))\n\n if (!Array.isArray(raw)) return []\n\n return raw.flatMap((entry): PortlessRoute[] => {\n const { hostname, port } = (entry ?? {}) as { hostname?: unknown; port?: unknown }\n\n if (typeof hostname !== 'string' || hostname === '' || typeof port !== 'number') return []\n\n return [{ name: hostname, port }]\n })\n } catch {\n return []\n }\n}\n\nexport interface PortlessDriver {\n /**\n * Absolute path to the `dist/cli.js` this driver executes, or `null` when portless is not installed.\n *\n * Exposed so a caller rendering a remediation ({@link formatPortlessCommand}) names the binary THIS driver\n * would run, rather than re-resolving one behind its back \u2014 which, under an injected driver, would print a\n * fix derived from the real machine instead of the one under test.\n */\n binPath: () => string | null\n /** Resolve (and memoize) whether the `portless` binary is usable. Absent \u2192 every other call no-ops. */\n isAvailable: () => Promise<boolean>\n /**\n * Is a portless daemon serving `port` over `tls`? **Probe only \u2014 this never starts anything.** Binding\n * the privileged `:443` needs root, and portless's sudo re-exec cannot prompt from a detached child, so\n * the daemon is installed once, out-of-band (a root `service install`). A `false` here is turned into a\n * fatal, actionable start error by the caller.\n */\n isProxyServing: (port: number, tls: boolean) => Promise<boolean>\n /**\n * Register `<name> \u2192 127.0.0.1:<port>` (`name` = `<release>.<package>`). Returns `true` on success so\n * the caller shows the hero URL only for an alias that actually resolves (best-effort otherwise).\n */\n registerAlias: (name: string, port: number) => Promise<boolean>\n /** Deregister `<name>`. Best-effort. */\n removeAlias: (name: string) => Promise<void>\n}\n\nexport interface PortlessDriverDeps {\n /** Override the resolved `dist/cli.js` path (default: the real node_modules walk). Injected in tests. */\n bin?: string | null\n run?: PortlessRun\n /** TCP liveness pre-filter (default: real `net` connect). Injected in tests. */\n isListening?: IsListening\n /** Wire-probe identity check (default: real `HEAD /` + `X-Portless`). Injected in tests. */\n isProxyServing?: IsProxyServing\n timeoutMs?: number\n}\n\n/**\n * Build a {@link PortlessDriver}. Inject `run` in tests to assert argv without shelling out.\n * `isAvailable` memoizes so the binary is probed at most once per runner.\n */\nexport const createPortlessDriver = (deps: PortlessDriverDeps = {}): PortlessDriver => {\n const bin = deps.bin === undefined ? portlessBin() : deps.bin\n const run = deps.run ?? defaultRun\n const isListening = deps.isListening ?? defaultIsListening\n const isProxyServing = deps.isProxyServing ?? defaultIsProxyServing\n const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS\n let availability: boolean | null = null\n\n /** Run a portless subcommand, swallowing any failure into `false` (best-effort contract). */\n const tryRun = async (args: string[]): Promise<boolean> => {\n try {\n await run(args, { timeoutMs })\n\n return true\n } catch {\n return false\n }\n }\n\n const isAvailable = async (): Promise<boolean> => {\n availability ??= await tryRun(['--version'])\n\n return availability\n }\n\n const serving = async (port: number, tls: boolean): Promise<boolean> => {\n if (!(await isAvailable())) return false\n // Nothing is even accepting TCP \u2192 skip the (more expensive) wire probe entirely.\n if (!(await isListening(port))) return false\n\n return isProxyServing(port, tls)\n }\n\n const registerAlias = async (name: string, port: number): Promise<boolean> => {\n if (!(await isAvailable())) return false\n\n return tryRun(['alias', name, String(port)])\n }\n\n const removeAlias = async (name: string): Promise<void> => {\n if (!(await isAvailable())) return\n await tryRun(['alias', '--remove', name])\n }\n\n return {\n binPath: () => {\n return bin\n },\n isAvailable,\n isProxyServing: serving,\n registerAlias,\n removeAlias,\n }\n}\n"],
5
+ "mappings": "4GAAA,OAAOA,MAAa,eACpB,OAAS,KAAAC,MAAS,KAgBX,IAAMC,EAAkB,SAA8B,CAC3D,GAAI,CACF,aAAMC,kBAAkB,MAAM,EAEvB,EACT,MAAQ,CACN,MAAO,EACT,CACF,EAOaC,EAAuB,MAAOC,GAAoD,CAC7F,GAAM,CAAE,IAAAC,EAAK,MAAAC,EAAO,OAAAC,CAAO,EAAIH,EACzBI,EAAa,KAAK,UAAUD,CAAM,EAGlCE,GAAU,MADFP,EAAE,CAAE,IAAK,CAAE,GAAGQ,EAAQ,IAAK,WAAY,GAAI,CAAE,CAAC,8BACJJ,CAAK,UAAUD,CAAG,2BAA2BG,CAAU,IAC5G,OAEH,OAAOG,EAAkBF,CAAM,CACjC,EAOaG,EAAwB,MAAOC,GAA+B,CACzE,GAAI,CACF,MAAMX,qCAAqCW,CAAG,GAAG,MAAM,CACzD,OAASC,EAAO,CACdC,EAAO,MAAM,CAAE,MAAAD,EAAO,IAAAD,CAAI,EAAG,qCAAqC,CACpE,CACF,EASMF,EAAqBF,GAA2B,CACpD,IAAMO,EAAQP,EAAO,MAAM,eAAe,EAE1C,GAAI,CAACO,EACH,MAAM,IAAI,MAAM,8DAA8D,EAGhF,OAAOA,EAAM,CAAC,CAChB,ECvEA,OAAS,KAAAC,MAAS,KCAlB,OAAS,YAAAC,MAAgB,mBACzB,OAAS,KAAAC,MAAS,KAoBlB,IAAMC,EAAe,MAAOC,GAAkC,CAC5D,GAAI,CACF,OAAO,MAAMC,EAASD,CAAI,CAC5B,MAAQ,CACN,OAAOA,CACT,CACF,EAqBaE,EAA0B,SAA0C,CAC/E,GAAI,CACF,GAAM,CAACC,EAAkBC,CAAY,EAAI,MAAM,QAAQ,IAAI,CACzDC,8BAA8B,MAAM,EACpCA,oCAAoC,MAAM,CAC5C,CAAC,EAEKC,EAAc,KAAK,MAAMH,EAAiB,MAAM,EAAE,YAAc,CAAC,EACjEI,EAAU,KAAK,MAAMH,EAAa,MAAM,EAAE,QAAU,CAAC,EAErDI,EAAa,IAAI,IACrBD,EACG,IAAKE,GACGA,EAAM,oBACd,EACA,OAAQC,GACA,OAAOA,GAAQ,QACvB,CACL,EAEMC,EAAQ,IAAI,IAElB,QAAWC,KAAaN,EAClB,CAACM,EAAU,mBAAqBJ,EAAW,IAAII,EAAU,GAAG,GAIhED,EAAM,IAAI,MAAMZ,EAAaa,EAAU,iBAAiB,EAAGA,EAAU,GAAG,EAG1E,OAAOD,CACT,OAASE,EAAO,CACd,OAAAC,EAAO,MAAM,CAAE,MAAAD,CAAM,EAAG,yCAAyC,EAE1D,IAAI,GACb,CACF,EDrEO,IAAME,EAA0B,MAAOC,GAA+B,CAC3E,GAAI,CAGF,IAAMC,GAFQ,MAAMC,EAAwB,GAE1B,IAAI,MAAMC,EAAmBH,CAAG,CAAC,EAEnD,GAAI,CAACC,EACH,OAGF,MAAMG,yBAAyBH,CAAG,GAAG,MAAM,CAC7C,OAASI,EAAO,CACdC,EAAO,MAAM,CAAE,MAAAD,EAAO,IAAAL,CAAI,EAAG,wCAAwC,CACvE,CACF,EE7BA,OAAS,KAAAO,MAAS,KAeX,IAAMC,EAAyB,MAAOC,GAAyC,CACpF,GAAI,CACF,IAAMC,GAAU,MAAMC,oCAAoC,MAAM,GAAG,OAQnE,OANgB,KAAK,MAAMD,CAAM,EAAE,QAAU,CAAC,GAEzB,KAAME,GAClBA,EAAM,OAASH,CACvB,GAEa,KAAO,IACvB,OAASI,EAAO,CACd,OAAAC,EAAO,MAAM,CAAE,MAAAD,EAAO,KAAAJ,CAAK,EAAG,4BAA4B,EAEnD,IACT,CACF,EAcaM,EAAsB,MAAON,EAAcO,IAA+C,CACrG,GAAI,CAGF,IAAMC,GAFU,MAAMN,uCAAuCF,CAAI,WAAWO,EAAS,KAAK,GAAG,CAAC,IAAI,OAE/E,MAAM,qBAAqB,IAAI,CAAC,GAAM,MAAMR,EAAuBC,CAAI,EAE1F,OAAIQ,GACF,MAAMN,6BAA6BM,CAAG,GAAG,MAAM,EAG1CA,GAAO,IAChB,OAASJ,EAAO,CACd,OAAAC,EAAO,KAAK,CAAE,MAAAD,EAAO,KAAAJ,CAAK,EAAG,qDAA2C,EAEjE,IACT,CACF,EC7DA,OAAS,KAAAS,MAAS,KAyBX,IAAMC,EAA8B,MAAOC,GAAiD,CACjG,GAAM,CAAE,IAAAC,EAAK,MAAAC,EAAO,MAAAC,CAAM,EAAIH,EAExBI,EAASC,EAAkB,MAAMC,EAAkB,CAAC,EAEpDC,EAAqBJ,GACtB,MAAMK,gCAAgCP,CAAG,YAAYE,CAAK,0BAA0B,QACpF,MAAMK,gCAAgCP,CAAG,IAAI,OAE5CQ,EAAeC,EAAkBH,CAAkB,EAEnDI,GAAkB,MAAMH,wCAAwCC,CAAY,IAAI,OAEhFG,EAAaC,EAAqBF,CAAc,EAItD,aAAMH,qCAAqCC,CAAY,cAAcG,CAAU,GAE3ER,IAAW,cACb,MAAMI,oCAAoCC,CAAY,cAAcG,CAAU,GAG5EV,GACF,MAAMM,sCAAsCC,CAAY,YAAYP,CAAK,GAGpEO,CACT,EAWMI,EAAwBC,GAA2B,CACvD,IAAMC,EAAQD,EAAO,MAAM,aAAa,EAExC,GAAI,CAACC,EACH,MAAM,IAAI,MAAM,qEAAqE,EAGvF,OAAOA,EAAM,CAAC,CAChB,EAWML,EAAqBI,GAA2B,CACpD,IAAMC,EAAQD,EAAO,MAAM,eAAe,EAE1C,GAAI,CAACC,EACH,MAAM,IAAI,MAAM,iEAAiE,EAGnF,OAAOA,EAAM,CAAC,CAChB,ECvEO,IAAMC,EAA2BC,GAA8C,CACpF,GAAM,CAAE,OAAAC,CAAO,EAAID,EAEbE,EAAKC,EAAgBF,CAAM,EAEjC,OAAOC,EAAKE,EAAaF,CAAE,EAAID,CACjC,EChBA,IAAMI,EAA2B,IAAI,IAAI,CAAC,kBAAmB,mBAAoB,sBAAsB,CAAC,EAElGC,EAAuBC,GACpBA,aAAiB,OAASF,EAAyB,IAAIE,EAAM,IAAI,EAuB7DC,GAAwBC,GAA4B,CAC/D,GAAIH,EAAoBG,CAAK,EAAG,MAAO,GAEvC,IAAMC,EAASD,GAAkD,MAEjE,OAAOH,EAAoBI,CAAK,CAClC,ECfA,OAAS,YAAAC,MAAgB,qBACzB,OAAS,cAAAC,MAAkB,cAC3B,OAAS,cAAAC,EAAY,gBAAAC,MAAoB,UACzC,OAAOC,MAAU,YACjB,OAAOC,MAAW,aAClB,OAAOC,OAAS,WAChB,OAAS,WAAAC,OAAe,UACxB,OAAS,WAAAC,EAAS,QAAAC,MAAY,YAC9B,OAAOC,MAAa,eACpB,OAAOC,OAAS,WAChB,OAAS,iBAAAC,OAAqB,WAC9B,OAAS,aAAAC,OAAiB,YAI1B,IAAMC,GAAgBC,GAAUC,CAAQ,EAGlCC,GAAcC,GAAuC,CACzD,IAAMC,EAAM,KAAK,MAAMC,EAAaF,EAAa,OAAO,CAAC,EACnDG,EAAM,OAAOF,EAAI,KAAQ,SAAWA,EAAI,IAAMA,EAAI,KAAK,SAE7D,OAAOE,GAAO,MAAQA,IAAQ,GAAK,KAAOA,CAC5C,EAYaC,GAAqB,IAAqB,CACrD,GAAI,CACF,IAAIC,EAAMC,EAAQC,GAAc,YAAY,GAAG,CAAC,EAEhD,OAAS,CACP,IAAMP,EAAcQ,EAAKH,EAAK,eAAgB,WAAY,cAAc,EAExE,GAAII,EAAWT,CAAW,EAAG,CAC3B,IAAMG,EAAMJ,GAAWC,CAAW,EAElC,OAAOG,GAAO,KAAO,KAAOK,EAAKF,EAAQN,CAAW,EAAGG,CAAG,CAC5D,CACA,IAAMO,EAASJ,EAAQD,CAAG,EAE1B,GAAIK,IAAWL,EAAK,OAAO,KAC3BA,EAAMK,CACR,CACF,MAAQ,CACN,OAAO,IACT,CACF,EAGIC,EACEC,EAAc,KACdD,IAAc,SAAWA,EAAYP,GAAmB,GAErDO,GAOHE,GAAa,mBAGbC,GAAsB,QAGtBC,GAAcC,GACXH,GAAW,KAAKG,CAAK,EAAIA,EAAQ,IAAIA,EAAM,WAAW,IAAKF,EAAmB,CAAC,IAuC3EG,GAAwB,CAACC,EAAgBC,IAAkD,CACtG,IAAMC,EAAQ,CAACD,EAAQ,UAAYE,EAAQ,SAAUF,EAAQ,IAAK,GAAGD,CAAI,EAGzE,OAFeC,EAAQ,OAAS,GAAO,QAAU,IAEjCC,EAAM,IAAIL,EAAU,EAAE,KAAK,GAAG,CAChD,EAcMO,GAAqB,KACrBC,EAAmB,KAGnBC,GAAkB,aAGlBC,EAAW,YAQXC,GAAmB,YAEZC,GAAmCC,GACvC,IAAI,QAASC,GAAY,CAC9B,IAAMC,EAASC,GAAI,QAAQ,CAAE,KAAMN,EAAU,KAAAG,CAAK,CAAC,EAC7CI,EAAUC,GAA0B,CACxCH,EAAO,QAAQ,EACfD,EAAQI,CAAM,CAChB,EAEAH,EAAO,WAAWP,CAAgB,EAClCO,EAAO,KAAK,UAAW,IAAM,CAC3BE,EAAO,EAAI,CACb,CAAC,EACDF,EAAO,KAAK,UAAW,IAAM,CAC3BE,EAAO,EAAK,CACd,CAAC,EACDF,EAAO,KAAK,QAAS,IAAM,CACzBE,EAAO,EAAK,CACd,CAAC,CACH,CAAC,EAqBUE,GAAwC,CAACN,EAAMO,IACnD,IAAI,QAASN,GAAY,CAE9B,IAAMO,GADUD,EAAME,EAAM,QAAUC,EAAK,SAEzC,CACE,KAAMb,EACN,KAAAG,EACA,OAAQ,OACR,KAAM,IACN,QAASL,EACT,GAAIY,EAAM,CAAE,mBAAoB,GAAO,WAAYT,EAAiB,EAAI,CAAC,CAC3E,EACCa,GAAQ,CACPA,EAAI,OAAO,EACXV,EAAQU,EAAI,QAAQf,EAAe,IAAM,GAAG,CAC9C,CACF,EAEAY,EAAI,GAAG,QAAS,IAAM,CACpBP,EAAQ,EAAK,CACf,CAAC,EACDO,EAAI,GAAG,UAAW,IAAM,CACtBA,EAAI,QAAQ,EACZP,EAAQ,EAAK,CACf,CAAC,EACDO,EAAI,IAAI,CACV,CAAC,EAGGI,GAA0B,MAAOtB,EAAM,CAAE,UAAAuB,CAAU,IAAM,CAC7D,IAAMC,EAAM9B,EAAY,EAExB,GAAI8B,GAAO,KAAM,MAAM,IAAI,MAAM,8DAA8D,EAC/F,MAAM9C,GAAcyB,EAAQ,SAAU,CAACqB,EAAK,GAAGxB,CAAI,EAAG,CACpD,OAAQ,YAAY,QAAQuB,CAAS,EACrC,SAAU,QACV,IAAKE,EAAyBtB,EAAQ,GAAG,CAC3C,CAAC,CACH,EAUauB,EAAmB,IACvBvB,EAAQ,IAAI,oBAAsBb,EAAKqC,GAAQ,EAAG,WAAW,EAIhEC,GAAe,SAOfC,GAAuB,aAchBC,EAAa,IACjBxC,EAAKoC,EAAiB,EAAGE,EAAY,EAajCG,GAAuB,IAAe,CACjD,GAAI,CACF,IAAMC,EAAWhD,EAAaM,EAAKoC,EAAiB,EAAGG,EAAoB,EAAG,OAAO,EAAE,KAAK,EAE5F,OAAIG,IAAa,GAAW,GAEbC,EAAW,QAAQ,EAAE,OAAOjD,EAAa8C,EAAW,CAAC,CAAC,EAAE,OAAO,KAAK,IAEjEE,EAAS,YAAY,CACzC,MAAQ,CACN,MAAO,EACT,CACF,EAkBaE,GAAsB,CAACxB,EAAcyB,IACzC,IAAI,QAASxB,GAAY,CAC9B,IAAIyB,EAEJ,GAAI,CACFA,EAAKpD,EAAa8C,EAAW,CAAC,CAChC,MAAQ,CACNnB,EAAQ,CAAE,GAAI,GAAO,KAAM,QAAS,CAAC,EAErC,MACF,CAEA,IAAMC,EAASK,GAAI,QAAQ,CAAE,KAAMV,EAAU,KAAAG,EAAM,WAAAyB,EAAY,GAAI,CAACC,CAAE,EAAG,mBAAoB,EAAK,EAAG,IAAM,CAIzG,IAAMC,EAAYzB,EAAO,mBACnB0B,EAAa1B,EAAO,WAE1BA,EAAO,QAAQ,EACfD,EAAQ2B,EAAa,CAAE,GAAI,EAAK,EAAI,CAAE,GAAI,GAAO,KAAMD,GAAW,MAAQA,GAAW,SAAW,SAAU,CAAC,CAC7G,CAAC,EAEDzB,EAAO,WAAWP,CAAgB,EAClCO,EAAO,KAAK,UAAW,IAAM,CAC3BA,EAAO,QAAQ,EACfD,EAAQ,CAAE,GAAI,GAAO,KAAM,WAAY,CAAC,CAC1C,CAAC,EACDC,EAAO,KAAK,QAAU2B,GAA+B,CACnD3B,EAAO,QAAQ,EACfD,EAAQ,CAAE,GAAI,GAAO,KAAM4B,EAAI,MAAQ,SAAU,CAAC,CACpD,CAAC,CACH,CAAC,EAQUC,GAAa,IAAuB,CAC/C,GAAI,CACF,IAAMC,EAAe,KAAK,MAAMzD,EAAaM,EAAKoC,EAAiB,EAAG,aAAa,EAAG,OAAO,CAAC,EAE9F,OAAK,MAAM,QAAQe,CAAG,EAEfA,EAAI,QAASC,GAA2B,CAC7C,GAAM,CAAE,SAAAC,EAAU,KAAAjC,CAAK,EAAKgC,GAAS,CAAC,EAEtC,OAAI,OAAOC,GAAa,UAAYA,IAAa,IAAM,OAAOjC,GAAS,SAAiB,CAAC,EAElF,CAAC,CAAE,KAAMiC,EAAU,KAAAjC,CAAK,CAAC,CAClC,CAAC,EAR+B,CAAC,CASnC,MAAQ,CACN,MAAO,CAAC,CACV,CACF,EA4CakC,GAAuB,CAACC,EAA2B,CAAC,IAAsB,CACrF,IAAMrB,EAAMqB,EAAK,MAAQ,OAAYnD,EAAY,EAAImD,EAAK,IACpDC,EAAMD,EAAK,KAAOvB,GAClByB,EAAcF,EAAK,aAAepC,GAClCuC,EAAiBH,EAAK,gBAAkB7B,GACxCO,EAAYsB,EAAK,WAAazC,GAChC6C,EAA+B,KAG7BC,EAAS,MAAOlD,GAAqC,CACzD,GAAI,CACF,aAAM8C,EAAI9C,EAAM,CAAE,UAAAuB,CAAU,CAAC,EAEtB,EACT,MAAQ,CACN,MAAO,EACT,CACF,EAEM4B,EAAc,UAClBF,IAAiB,MAAMC,EAAO,CAAC,WAAW,CAAC,EAEpCD,GAsBT,MAAO,CACL,QAAS,IACAzB,EAET,YAAA2B,EACA,eAxBc,MAAOzC,EAAcO,IAC/B,CAAE,MAAMkC,EAAY,GAEpB,CAAE,MAAMJ,EAAYrC,CAAI,EAAW,GAEhCsC,EAAetC,EAAMO,CAAG,EAoB/B,cAjBoB,MAAOmC,EAAc1C,IACnC,MAAMyC,EAAY,EAEjBD,EAAO,CAAC,QAASE,EAAM,OAAO1C,CAAI,CAAC,CAAC,EAFR,GAiBnC,YAZkB,MAAO0C,GAAgC,CACnD,MAAMD,EAAY,GACxB,MAAMD,EAAO,CAAC,QAAS,WAAYE,CAAI,CAAC,CAC1C,CAUA,CACF",
6
+ "names": ["process", "$", "isCmuxAvailable", "$", "openCmuxDevWorkspace", "args", "cwd", "title", "layout", "layoutJson", "output", "process", "parseWorkspaceRef", "closeCmuxDevWorkspace", "ref", "error", "logger", "match", "$", "realpath", "$", "safeRealpath", "path", "realpath", "listCmuxWorkspacesByCwd", "workspacesOutput", "groupsOutput", "$", "workspaces", "groups", "anchorRefs", "group", "ref", "byCwd", "workspace", "error", "logger", "closeCmuxWorkspaceByCwd", "cwd", "ref", "listCmuxWorkspacesByCwd", "safeRealpath", "$", "error", "logger", "$", "findCmuxGroupRefByName", "name", "output", "$", "group", "error", "logger", "createCmuxGroupFrom", "fromRefs", "ref", "$", "openCmuxWorkspaceWithLayout", "args", "cwd", "title", "group", "layout", "resolveCmuxLayout", "getInfraKitConfig", "newWorkspaceOutput", "$", "workspaceRef", "parseWorkspaceRef", "surfacesOutput", "leftTopRef", "parseFirstSurfaceRef", "output", "match", "buildCmuxWorkspaceTitle", "args", "branch", "id", "parseBranchName", "displayLabel", "CANCELLATION_ERROR_NAMES", "hasCancellationName", "value", "isPromptCancellation", "error", "cause", "execFile", "createHash", "existsSync", "readFileSync", "http", "https", "net", "homedir", "dirname", "join", "process", "tls", "fileURLToPath", "promisify", "execFileAsync", "promisify", "execFile", "readBinRel", "pkgJsonPath", "pkg", "readFileSync", "rel", "resolvePortlessBin", "dir", "dirname", "fileURLToPath", "join", "existsSync", "parent", "cachedBin", "portlessBin", "SHELL_SAFE", "SINGLE_QUOTE_ESCAPE", "shellQuote", "value", "formatPortlessCommand", "args", "options", "words", "process", "DEFAULT_TIMEOUT_MS", "PROBE_TIMEOUT_MS", "PORTLESS_HEADER", "LOOPBACK", "PROBE_SERVERNAME", "defaultIsListening", "port", "resolve", "socket", "net", "finish", "result", "defaultIsProxyServing", "tls", "req", "https", "http", "res", "defaultRun", "timeoutMs", "bin", "withoutPackageManagerEnv", "portlessStateDir", "homedir", "CA_CERT_FILE", "CA_TRUST_MARKER_FILE", "readCaPath", "caFingerprintMatches", "recorded", "createHash", "handshakeChainsToCa", "servername", "ca", "authError", "authorized", "err", "listRoutes", "raw", "entry", "hostname", "createPortlessDriver", "deps", "run", "isListening", "isProxyServing", "availability", "tryRun", "isAvailable", "name"]
7
+ }
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import{$ as $t,A as te,B as ft,C as U,D as d,E as gt,F as ht,G as vt,H as yt,I as wt,J as St,K as kt,L as Rt,M as Et,N as xt,O as Tt,P as At,Q as Ct,R as bt,S as oe,T as Pt,U as It,V as Nt,W as Lt,X as _t,Y as Dt,Z as Ot,a as D,aa as Ft,b as De,ba as Ut,c as $e,d as Fe,e as Me,f as O,g as He,h as j,i as Ye,j as ze,k as Xe,l as Qe,m as Ze,n as tt,o as ot,p as rt,q as Z,r as nt,s as it,t as ee,u as ct,v as lt,w as mt,x as dt,y as ut,z as pt}from"./chunk-3G6O7MCC.js";import{r as re}from"./chunk-S45AAYXO.js";import{a as Mt,b as G,c as jt,d as C,e as ne,f as ie,g as se}from"./chunk-EX66ZFV5.js";import{a as et}from"./chunk-T74NOBEP.js";import{a as st,b as at}from"./chunk-A7FAXZGI.js";import{b as Oe,c as T,d as h,e as F,f as We,g as Ke,h as Be,i as A,l as M}from"./chunk-GZNE5CBQ.js";import{O as X,P as Le,R as _e,m as Ne,u as l}from"./chunk-IVWPZEMC.js";import{a as $,b as je,d as Ue,f as Ge,g as Ve,j as Q,k as qe,n as v,q as Je}from"./chunk-CHETBZ6M.js";import en,{Separator as Eo}from"@inquirer/select";import{realpathSync as tn}from"node:fs";import g from"node:process";import{fileURLToPath as To}from"node:url";var Co=(e,t)=>{let o=e,r;for(let s of t){if(r=o.find(n=>n.name()===s),!r)return;o=r.commands}return r},Gt=e=>Dt.flatMap(({key:t,label:o})=>$t(t).flatMap(r=>{let s=Co(e,r.groupPath);return s?[{name:r.groupPath.join(" "),description:s.description(),group:o}]:[]}));import{Command as lr}from"commander";import _ from"node:process";import Vt from"node:process";import{$ as bo}from"zx";var ae=async()=>{let e=await X(),t=await Promise.all([{label:"project (committed)",path:e.main},{label:"user global",path:e.userGlobal},{label:"user project",path:e.userProject}].map(async i=>({...i,exists:await T(i.path)}))),o=t.at(-1)?.exists===!0,r=await ct(e.userProject,o),s=o?lt(r):"";l.info(`Project name: ${e.projectName}
2
+ import{A as ft,B as te,C as gt,D as I,E as d,F as ht,G as vt,H as yt,I as wt,J as St,K as kt,L as Rt,M as Et,N as xt,O as oe,P as Tt,Q as At,R as Ct,S as bt,T as Pt,U as It,V as Nt,W as Lt,X as Dt,Y as _t,Z as Ot,_ as $t,a as O,aa as Ft,b as _e,ba as Mt,c as $e,ca as Gt,d as Fe,e as Me,f as $,g as He,h as U,i as Ye,j as ze,k as Xe,l as Qe,m as Ze,n as tt,o as ot,p as rt,q as Z,r as nt,s as it,t as ee,u as ct,v as lt,w as mt,x as dt,y as ut,z as pt}from"./chunk-RDRJX6TT.js";import{t as re}from"./chunk-XTRPE4SX.js";import{a as jt,b as G,c as Ut,d as C,e as ne,f as ie,g as se}from"./chunk-KAWDTA5H.js";import{a as et}from"./chunk-3LPUAYWA.js";import{a as st,b as at}from"./chunk-LKKKDB6V.js";import{b as Oe,c as T,d as h,e as M,f as We,g as Be,h as Ke,i as A,l as j}from"./chunk-7DL27USH.js";import{P as X,Q as Le,S as De,n as Ne,v as l}from"./chunk-PZU6BLQR.js";import{a as F,b as je,d as Ue,f as Ge,g as qe,j as Q,k as Ve,n as v,q as Je}from"./chunk-CHETBZ6M.js";import"./chunk-X46DP23W.js";import tn,{Separator as xo}from"@inquirer/select";import{realpathSync as on}from"node:fs";import g from"node:process";import{fileURLToPath as Ao}from"node:url";var bo=(e,t)=>{let o=e,r;for(let s of t){if(r=o.find(n=>n.name()===s),!r)return;o=r.commands}return r},qt=e=>Ot.flatMap(({key:t,label:o})=>Ft(t).flatMap(r=>{let s=bo(e,r.groupPath);return s?[{name:r.groupPath.join(" "),description:s.description(),group:o}]:[]}));import{Command as mr}from"commander";import _ from"node:process";import Vt from"node:process";import{$ as Po}from"zx";var ae=async()=>{let e=await X(),t=await Promise.all([{label:"project (committed)",path:e.main},{label:"user global",path:e.userGlobal},{label:"user project",path:e.userProject}].map(async i=>({...i,exists:await T(i.path)}))),o=t.at(-1)?.exists===!0,r=await ct(e.userProject,o),s=o?lt(r):"";l.info(`Project name: ${e.projectName}
3
3
  `),l.info(`Config merge chain (later overrides earlier):
4
- `);for(let i of t){let c=i.exists?" [\u2713]":" [ ]",a=i.path===e.userProject&&s!==""?` ${s}`:"";l.info(`${c} ${i.label.padEnd(22)} ${h(i.path)}${a}`)}let n={projectName:e.projectName,layers:t.map(i=>({label:i.label,path:i.path,exists:i.exists})),hasOverrides:r.hasOverrides,overrideKeys:r.overrideKeys};return{content:[{type:"text",text:JSON.stringify(n,null,2)}],structuredContent:n}},ce=async()=>{let e=await X(),t=Vt.env.EDITOR||Vt.env.VISUAL||"vi",o=await tt(e);o.createdConfig&&l.info(ot(o)),l.info(`Opening ${h(e.userProject)} in ${t}`),await bo({stdio:"inherit"})`${t} ${e.userProject}`,_e();let r={path:e.userProject,editor:t};return{content:[{type:"text",text:JSON.stringify(r,null,2)}],structuredContent:r}};var le=e=>{if(typeof e!="object"||e===null)return null;let{config:t,reason:o,at:r}=e;return typeof t!="string"||t.length===0||typeof o!="string"||typeof r!="number"||!Number.isFinite(r)?null:{config:t,reason:o,at:r}},I=e=>`infra-kit: Doppler token for env "${e}" is missing, invalid, or unreadable \u2014 env auto-load is not running. Fix: run \`infra-kit env-token-set ${e}\` (mint one at https://dashboard.doppler.com under this config's Access tab).`;import p from"node:fs";import y from"node:path";import N from"node:process";var Po="autoload-warn-fail.flag",me="autoload-warn-auth-fail.flag",de="autoload-fail.flag",ue="autoload-auth-fail.json",Io=3e4,Jt=async(e=!0)=>{let t;try{t=await Le()}catch{return null}let o=t.envAutoLoad;if(!o)return null;let r={trigger:o.trigger,config:o.config,project:t.envManagement.config.name};try{if(await He(o.config))return r}catch{return r}return Kt({config:o.config,reason:`No Doppler service token for env "${o.config}"`,at:Date.now()}),e&&V(I(o.config),me),null},Wt=e=>{let{trigger:t,expectedTrigger:o,targetConfig:r,targetProject:s,env:n,force:i}=e;return t!==o||!n.session||n.cleared||n.currentConfig&&!n.autoLoadedMarker||!i&&n.autoLoadedMarker&&n.currentConfig===r&&n.currentProject===s?"skip":"load"},L=async({expectedTrigger:e,projectDir:t,force:o,isAuthFailure:r=Oe})=>{let s=e==="cli-invocation";s&&q();let n=null;try{if(n=await Jt(s),!n||Wt({trigger:n.trigger,expectedTrigger:e,targetConfig:n.config,targetProject:n.project,env:_o(),force:o})==="skip"||qt()||$o())return null;let c=Do(),a=await Ye({config:n.config,autoLoaded:!0,projectDir:t,beforeWrite:()=>!qt()&&!Oo(c)});return a?(Mo(),Lo(),a.filePath):null}catch(i){let c=i.message,a=r(i);return Fo(),a&&n&&Kt({config:n.config,reason:c,at:Date.now()}),s&&a&&n?(V(I(n.config),me),l.debug(`env auto-load auth failure: ${c}`)):s?V(`infra-kit: env auto-load failed \u2014 ${c} (will retry later)`,Po):l.debug(`env auto-load skipped: ${c}`),null}},q=()=>{let e=No();e&&(V(I(e.config),me),l.debug(`env auto-load auth failure (recorded ${new Date(e.at).toISOString()}): ${e.reason}`))},No=()=>{try{let e=p.readFileSync(y.join(v(),ue),"utf-8");return le(JSON.parse(e))}catch{return null}},Kt=e=>{try{let t=v();p.mkdirSync(t,{recursive:!0,mode:448}),Je(y.join(t,ue),JSON.stringify(e),384)}catch{}},Lo=()=>{try{p.rmSync(y.join(v(),ue),{force:!0})}catch{}},_o=()=>({session:N.env[Ue],cleared:N.env[qe],currentConfig:N.env[Ge],currentProject:N.env[Ve],autoLoadedMarker:N.env[Q]}),Do=()=>{try{return p.statSync(y.join(v(),$)).mtimeMs}catch{return null}},Oo=e=>{try{let t=y.join(v(),$);if(!p.existsSync(t))return!1;let o=p.statSync(t).mtimeMs;return e!==null&&o<=e?!1:new RegExp(`^unset ${Q}$`,"m").test(p.readFileSync(t,"utf-8"))}catch{return!1}},qt=()=>{try{let e=v(),t=y.join(e,je);if(!p.existsSync(t))return!1;let o=y.join(e,$);return p.existsSync(o)?p.statSync(t).mtimeMs>=p.statSync(o).mtimeMs:!0}catch{return!1}},$o=()=>{try{let e=y.join(v(),de);return p.existsSync(e)?Date.now()-p.statSync(e).mtimeMs<Io:!1}catch{return!1}},Fo=()=>{try{let e=v();p.mkdirSync(e,{recursive:!0,mode:448}),p.writeFileSync(y.join(e,de),"",{mode:384})}catch{}},Mo=()=>{try{p.rmSync(y.join(v(),de),{force:!0})}catch{}},V=(e,t)=>{try{let o=v(),r=y.join(o,t);if(p.existsSync(r))return;p.mkdirSync(o,{recursive:!0,mode:448}),p.writeFileSync(r,"",{mode:384})}catch{}l.warn(e)};var pe=async({projectDir:e}={})=>{await L({expectedTrigger:"shell-startup",projectDir:e,force:!0})};var Bt="https://dashboard.doppler.com/workplace/projects",jo=(e,t)=>["Removing it locally does NOT revoke it \u2014 the token still works anywhere else it is stored.",`Revoke it in Doppler (project "${e}", config "${t}"): ${Bt}/${e}`],fe=async({env:e})=>{let o=!!(await We())?.envs[e];await Be(e);let r=await j(),s=await O(),n=await F();o?l.info(`Removed the "${e}" service token from ${h(n)}.`):l.info(`No "${e}" service token was stored in ${h(n)} \u2014 nothing to remove.`),r.length>0&&l.info(`Purged ${r.length} warm cache(s) across this repo's worktrees.`);for(let c of jo(s,e))l.warn(c);let i={env:e,removed:o,storePath:n,warmCachesPurged:r.length,revokeUrl:`${Bt}/${s}`};return{content:D(JSON.stringify(i,null,2)),structuredContent:i}};import Uo from"@inquirer/password";import W from"node:process";import{$ as J}from"zx";var Go=async({stdin:e,fromEnv:t})=>{if(e)return{token:await Vo(),source:"stdin"};if(t){let r=W.env[t];if(!r)throw new Error(`${t} is not set (or is empty) \u2014 nothing to store.`);return{token:r,source:"env"}}return A.setInteractive(),{token:(await M(r=>Uo({message:"Paste the Doppler service token (input is hidden)",mask:!0},r),{output:W.stderr})).trim(),source:"prompt"}},Vo=async()=>{let e=[];W.stdin.setEncoding("utf8");for await(let t of W.stdin)e.push(t);return e.join("").trim()},ge="DOPPLER_CONFIG",qo=3e4,Jo=async(e,t,o)=>{let r=J.quiet;J.quiet=!0;let s;try{s=(await J({env:Xe(e)})`doppler secrets download --no-file --format json --project ${t} --config ${o}`.timeout(qo)).stdout}catch(n){throw Wo(n,o)}finally{J.quiet=r}return Ze(s)},Wo=(e,t)=>{let o=Me(e)??(e instanceof Error?e.message:String(e));if($e(o)!=="auth")return e instanceof Error?e:new Error(String(e));let r=Fe(o)==="mis-scoped"?"it is scoped to a DIFFERENT config (pasting another environment's token here is the mistake this check exists to catch).":"it is invalid or has been revoked.";return new Error([`Doppler refused this token for config "${t}" \u2014 ${r}`,"Nothing was written. Issue a service token scoped to that config and try again."].join(`
4
+ `);for(let i of t){let c=i.exists?" [\u2713]":" [ ]",a=i.path===e.userProject&&s!==""?` ${s}`:"";l.info(`${c} ${i.label.padEnd(22)} ${h(i.path)}${a}`)}let n={projectName:e.projectName,layers:t.map(i=>({label:i.label,path:i.path,exists:i.exists})),hasOverrides:r.hasOverrides,overrideKeys:r.overrideKeys};return{content:[{type:"text",text:JSON.stringify(n,null,2)}],structuredContent:n}},ce=async()=>{let e=await X(),t=Vt.env.EDITOR||Vt.env.VISUAL||"vi",o=await tt(e);o.createdConfig&&l.info(ot(o)),l.info(`Opening ${h(e.userProject)} in ${t}`),await Po({stdio:"inherit"})`${t} ${e.userProject}`,De();let r={path:e.userProject,editor:t};return{content:[{type:"text",text:JSON.stringify(r,null,2)}],structuredContent:r}};var le=e=>{if(typeof e!="object"||e===null)return null;let{config:t,reason:o,at:r}=e;return typeof t!="string"||t.length===0||typeof o!="string"||typeof r!="number"||!Number.isFinite(r)?null:{config:t,reason:o,at:r}},N=e=>`infra-kit: Doppler token for env "${e}" is missing, invalid, or unreadable \u2014 env auto-load is not running. Fix: run \`infra-kit env-token-set ${e}\` (mint one at https://dashboard.doppler.com under this config's Access tab).`;import p from"node:fs";import y from"node:path";import L from"node:process";var Io="autoload-warn-fail.flag",me="autoload-warn-auth-fail.flag",de="autoload-fail.flag",ue="autoload-auth-fail.json",No=3e4,Wt=async(e=!0)=>{let t;try{t=await Le()}catch{return null}let o=t.envAutoLoad;if(!o)return null;let r={trigger:o.trigger,config:o.config,project:t.envManagement.config.name};try{if(await He(o.config))return r}catch{return r}return Kt({config:o.config,reason:`No Doppler service token for env "${o.config}"`,at:Date.now()}),e&&q(N(o.config),me),null},Bt=e=>{let{trigger:t,expectedTrigger:o,targetConfig:r,targetProject:s,env:n,force:i}=e;return t!==o||!n.session||n.cleared||n.currentConfig&&!n.autoLoadedMarker||!i&&n.autoLoadedMarker&&n.currentConfig===r&&n.currentProject===s?"skip":"load"},D=async({expectedTrigger:e,projectDir:t,force:o,isAuthFailure:r=Oe})=>{let s=e==="cli-invocation";s&&V();let n=null;try{if(n=await Wt(s),!n||Bt({trigger:n.trigger,expectedTrigger:e,targetConfig:n.config,targetProject:n.project,env:_o(),force:o})==="skip"||Jt()||Fo())return null;let c=Oo(),a=await Ye({config:n.config,autoLoaded:!0,projectDir:t,beforeWrite:()=>!Jt()&&!$o(c)});return a?(jo(),Do(),a.filePath):null}catch(i){let c=i.message,a=r(i);return Mo(),a&&n&&Kt({config:n.config,reason:c,at:Date.now()}),s&&a&&n?(q(N(n.config),me),l.debug(`env auto-load auth failure: ${c}`)):s?q(`infra-kit: env auto-load failed \u2014 ${c} (will retry later)`,Io):l.debug(`env auto-load skipped: ${c}`),null}},V=()=>{let e=Lo();e&&(q(N(e.config),me),l.debug(`env auto-load auth failure (recorded ${new Date(e.at).toISOString()}): ${e.reason}`))},Lo=()=>{try{let e=p.readFileSync(y.join(v(),ue),"utf-8");return le(JSON.parse(e))}catch{return null}},Kt=e=>{try{let t=v();p.mkdirSync(t,{recursive:!0,mode:448}),Je(y.join(t,ue),JSON.stringify(e),384)}catch{}},Do=()=>{try{p.rmSync(y.join(v(),ue),{force:!0})}catch{}},_o=()=>({session:L.env[Ue],cleared:L.env[Ve],currentConfig:L.env[Ge],currentProject:L.env[qe],autoLoadedMarker:L.env[Q]}),Oo=()=>{try{return p.statSync(y.join(v(),F)).mtimeMs}catch{return null}},$o=e=>{try{let t=y.join(v(),F);if(!p.existsSync(t))return!1;let o=p.statSync(t).mtimeMs;return e!==null&&o<=e?!1:new RegExp(`^unset ${Q}$`,"m").test(p.readFileSync(t,"utf-8"))}catch{return!1}},Jt=()=>{try{let e=v(),t=y.join(e,je);if(!p.existsSync(t))return!1;let o=y.join(e,F);return p.existsSync(o)?p.statSync(t).mtimeMs>=p.statSync(o).mtimeMs:!0}catch{return!1}},Fo=()=>{try{let e=y.join(v(),de);return p.existsSync(e)?Date.now()-p.statSync(e).mtimeMs<No:!1}catch{return!1}},Mo=()=>{try{let e=v();p.mkdirSync(e,{recursive:!0,mode:448}),p.writeFileSync(y.join(e,de),"",{mode:384})}catch{}},jo=()=>{try{p.rmSync(y.join(v(),de),{force:!0})}catch{}},q=(e,t)=>{try{let o=v(),r=y.join(o,t);if(p.existsSync(r))return;p.mkdirSync(o,{recursive:!0,mode:448}),p.writeFileSync(r,"",{mode:384})}catch{}l.warn(e)};var pe=async({projectDir:e}={})=>{await D({expectedTrigger:"shell-startup",projectDir:e,force:!0})};var Ht="https://dashboard.doppler.com/workplace/projects",Uo=(e,t)=>["Removing it locally does NOT revoke it \u2014 the token still works anywhere else it is stored.",`Revoke it in Doppler (project "${e}", config "${t}"): ${Ht}/${e}`],fe=async({env:e})=>{let o=!!(await We())?.envs[e];await Ke(e);let r=await U(),s=await $(),n=await M();o?l.info(`Removed the "${e}" service token from ${h(n)}.`):l.info(`No "${e}" service token was stored in ${h(n)} \u2014 nothing to remove.`),r.length>0&&l.info(`Purged ${r.length} warm cache(s) across this repo's worktrees.`);for(let c of Uo(s,e))l.warn(c);let i={env:e,removed:o,storePath:n,warmCachesPurged:r.length,revokeUrl:`${Ht}/${s}`};return{content:O(JSON.stringify(i,null,2)),structuredContent:i}};import Go from"@inquirer/password";import W from"node:process";import{$ as J}from"zx";var qo=async({stdin:e,fromEnv:t})=>{if(e)return{token:await Vo(),source:"stdin"};if(t){let r=W.env[t];if(!r)throw new Error(`${t} is not set (or is empty) \u2014 nothing to store.`);return{token:r,source:"env"}}return A.setInteractive(),{token:(await j(r=>Go({message:"Paste the Doppler service token (input is hidden)",mask:!0},r),{output:W.stderr})).trim(),source:"prompt"}},Vo=async()=>{let e=[];W.stdin.setEncoding("utf8");for await(let t of W.stdin)e.push(t);return e.join("").trim()},ge="DOPPLER_CONFIG",Jo=3e4,Wo=async(e,t,o)=>{let r=J.quiet;J.quiet=!0;let s;try{s=(await J({env:Xe(e)})`doppler secrets download --no-file --format json --project ${t} --config ${o}`.timeout(Jo)).stdout}catch(n){throw Bo(n,o)}finally{J.quiet=r}return Ze(s)},Bo=(e,t)=>{let o=Me(e)??(e instanceof Error?e.message:String(e));if($e(o)!=="auth")return e instanceof Error?e:new Error(String(e));let r=Fe(o)==="mis-scoped"?"it is scoped to a DIFFERENT config (pasting another environment's token here is the mistake this check exists to catch).":"it is invalid or has been revoked.";return new Error([`Doppler refused this token for config "${t}" \u2014 ${r}`,"Nothing was written. Issue a service token scoped to that config and try again."].join(`
5
5
  `))},Ko=e=>[`Could not verify this token's scope: the Doppler payload for "${e}" carries no ${ge}.`,"Refusing to store a credential whose scope is unknown (a token for the wrong environment would load","the wrong secrets into every shell).","Re-run with --force if you are certain the token is scoped to this config."].join(`
6
- `),he=async({env:e,stdin:t,fromEnv:o,force:r})=>{let s=await O(),{token:n,source:i}=await Go({stdin:t,fromEnv:o});if(!n)throw new Error("No token provided \u2014 nothing was written.");let c=await Jo(n,s,e);Qe(c,e);let a=c.some(([k])=>k===ge);if(!a&&!r)throw new Error(Ko(e));await Ke(e,n);let m=await j(),u=await F();l.info(`Stored the "${e}" service token (${te(n)}) in ${h(u)} (mode 0600).`),a||l.warn(`Scope was NOT verified (no ${ge} in the payload) \u2014 written because --force was given.`),m.length>0&&l.info(`Purged ${m.length} warm cache(s) so the next shell cannot serve secrets fetched with an old token.`);let f={env:e,source:i,redactedToken:te(n),storePath:u,scopeVerified:a,warmCachesPurged:m.length};return A.print(),{content:D(JSON.stringify(f,null,2)),structuredContent:f}};import{spawn as Bo}from"node:child_process";import K from"node:process";import{fileURLToPath as Ho}from"node:url";var Yo=["SIGINT","SIGTERM"],Ht=()=>Ho(new URL("./mcp.js",import.meta.url)),ve=(e={})=>{let t=e.spawn??Bo,o=e.exit??(i=>K.exit(i)),r=e.env??K.env,s=e.onError??(i=>l.error(i)),n=t(K.execPath,[Ht()],{stdio:"inherit",env:st(r)});n.on("error",i=>{s(`failed to launch the MCP server: ${i.message}`),o(1)}),Yo.forEach(i=>{K.on(i,()=>{n.kill(i)})}),n.on("exit",(i,c)=>{o(c?1:i??1)})};import{spawnSync as zo}from"node:child_process";import{realpathSync as Xo}from"node:fs";import{homedir as Qo}from"node:os";import ye from"node:process";import{fileURLToPath as Zo}from"node:url";var er=()=>Xo(Zo(import.meta.url)),tr=(e,t,o,r)=>e.error?(o(`${t} not found on PATH: ${e.error.message}`),r(1)):e.signal?(o(`update terminated by signal ${e.signal}`),r(1)):r(e.status??1),we=({dryRun:e},t={})=>{let o=t.spawnSync??zo,r=t.print??(R=>l.info(R)),s=t.exit??(R=>ye.exit(R)),n=t.env??ye.env,i=t.selfRealPath??er(),c=t.lazyNpmRoot??jt,{manager:a,updateCommand:m,canSelfSpawn:u}=Mt({selfRealPath:i,env:n,realpath:C,lazyNpmRoot:c}),f=m.join(" ");if(e){r(`Detected install manager: ${a}`),r(`Would run: ${f}`);return}if(!u){r(`Detected install manager: ${a}`),r(`Run this yourself: ${f}`),r(a==="homebrew"?"Not run for you: Homebrew manages this install; running a package manager would create a split-brain install.":"Not run for you: the install location is unrecognized, so the command above is a guess \u2014 a guessed global install is worse than a printed one.");return}let k=o(m[0],m.slice(1),{stdio:"inherit",shell:ye.platform==="win32",cwd:Qo(),env:at(n)});tr(k,a,r,s)};import{VENDOR_CONFIG_FILE as zt}from"@slip-stream-kit/config/internal";import Se from"node:fs/promises";import ke from"node:path";import Yt from"node:process";import{pathToFileURL as or}from"node:url";var Xt="~/projects",Re=async(e={})=>{if(e.init){await nr(e.cwd);return}await rr()},rr=async()=>{let e=Z(),t=await T(e);if(l.info(`Factory config: ${h(e)} ${t?"[\u2713]":"[ ]"}`),!t){l.info("\nNot found \u2014 run `infra-kit vendor config --init` to scaffold it."),Yt.exitCode=1;return}let{workspaceDir:o,targets:r}=await it(),s=nt(o),n=await T(s);l.info(`workspaceDir: ${o} (resolved: ${s}) ${n?"[\u2713 exists]":"[ ] not found"}`),l.info("Targets:");let i=n;for(let c of r){let a=ke.join(s,c),m=await T(a);m||(i=!1);let u=m?"[\u2713]":"[ ]",f=m?"":" (not found \u2014 clone or remove)";l.info(` ${u} ${c} ${h(a)}${f}`)}i||(Yt.exitCode=1)},nr=async e=>{let t=Z();if(await T(t)){l.info(`Factory config already exists at ${h(t)} \u2014 leaving it untouched.`);return}let o=e??await Ne(),r=await ir(o);await Se.mkdir(ke.dirname(t),{recursive:!0}),await Se.writeFile(t,sr(r),"utf-8"),l.info(`\u2713 Created ${h(t)}`),r.length>0&&l.info(` Seeded ${r.length} target(s) from the source ${zt}.`),l.info(` Edit \`workspaceDir\` (placeholder: ${Xt}) to point at where your repos live.`),r.length===0&&l.info(" Add at least one repo name to `targets` before running vendor sync/manifest/diff.")},ir=async e=>{try{let t=ke.join(e,zt),o=await Se.stat(t),n=(await import(`${or(t).href}?mtime=${Number(o.mtimeMs)}`)).default,i=typeof n=="function"?await n():n;if(i&&typeof i=="object"&&"targets"in i){let c=i.targets;if(Array.isArray(c)&&c.every(a=>typeof a=="string"))return c}}catch{}return[]},sr=e=>`${JSON.stringify({workspaceDir:Xt,targets:e},null,2)}
7
- `;var B=(e,t=!0)=>({line:e,reproducible:t});import H from"node:fs";import ar from"node:os";import cr from"node:path";import to from"node:process";var Y="INFRA_KIT_SESSION_REPORT",Ee=null,Qt=!1,Zt=[],oo=(e=to.env)=>{Qt||(Ee=e[Y]??null,Qt=!0,delete e[Y])};var ro=(e,t)=>{if(!Ee)return;let o=e.summary??(Zt.length>0?[...Zt]:void 0),r={...e,...o?{summary:o}:{}},s=t?.write??((n,i)=>{H.writeFileSync(n,i)});try{s(Ee,JSON.stringify(r))}catch{}},eo=0,no=e=>{let t=e?.tmpdir?.()??ar.tmpdir(),o=e?.pid??to.pid;return eo+=1,cr.join(t,`infra-kit-session-${o}-${eo}.json`)},io=(e,t)=>{if(!(t?.exists??(i=>H.existsSync(i)))(e))return null;let r=t?.read??(i=>H.readFileSync(i,"utf-8")),s=t?.unlink??(i=>H.unlinkSync(i)),n;try{n=r(e)}catch{return null}finally{try{s(e)}catch{}}try{return JSON.parse(n)}catch{return null}};var mr=(e,t)=>[...t,e],xe=e=>typeof e=="string"?e.split(",").filter(Boolean):void 0,so=(e,t)=>{if(!(typeof e>"u")){if(e===!0)return"workspace";if(e===!1)return"none";if(typeof e=="string"&&oe.includes(e))return e;throw new Error(`Invalid ${t} value "${String(e)}". Expected one of: ${oe.join(", ")}.`)}},dr=e=>e.description("Merge dev branch into every release branch").option("-a, --all","Select all active release branches").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await ht({all:t.all,confirmedCommand:t.yes}))}),ur=e=>e.description("List all release branches").action(async()=>{d(await St())}),pr=e=>e.description("Create one or more release branches (each entry can mix regular/hotfix and its own description)").option("-r, --release <spec>",'Release spec "<version|next|name>[:type[:description]]" (repeatable). The token is a semver ("1.2.5"), the literal "next", or a kebab-case name ("checkout-redesign"). Type is regular|hotfix (default regular). Examples: "1.2.5", "1.2.5:hotfix", "next:regular:Holiday backend", "checkout-redesign:regular:Q3 redesign".',mr,[]).option("-y, --yes","Skip confirmation prompt").action(async t=>{let r=t.release.map(kt),s=r.length>0?r:void 0;d(await Rt({releases:s,confirmedCommand:t.yes}))}),fr=e=>e.description("Edit a release's description in Jira and in the matching GitHub PR body").option("-v, --version <version>","Release version (e.g. 1.2.5) or release name (e.g. checkout-redesign)").option("-d, --description <description>",'New description (use "" to clear)').option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await Et({version:t.version,description:t.description,confirmedCommand:t.yes}))}),gr=e=>e.description("Deploy any release branch to any environment").option("-v, --version <version>",'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deploy; "dev" deploys from the dev branch').option("-e, --env <env>","Specify the environment to deploy to, e.g. dev").option("--skip-terraform","Skip terraform deployment step").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await yt({version:t.version,env:t.env,skipTerraform:t.skipTerraform,confirmedCommand:t.yes}))}),hr=e=>e.description("Deploy selected services from release branch to any environment").option("-v, --version <version>",'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deploy; "dev" deploys from the dev branch').option("-e, --env <env>","Specify the environment to deploy to, e.g. dev").option("-s, --services <services...>","Specify services to deploy, e.g. client-be client-fe").option("--skip-terraform","Skip terraform deployment step").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await wt({version:t.version,env:t.env,services:t.services,skipTerraform:t.skipTerraform,confirmedCommand:t.yes}))}),vr=e=>e.description("Release a new version to production").option("-v, --version <version>","Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deliver").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await vt({version:t.version,confirmedCommand:t.yes}))}),yr=e=>e.description("Remove release worktrees whose PRs are no longer open").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await _t({confirmedCommand:t.yes}))}),wr=e=>e.description("Add git worktrees for release branches").option("-y, --yes","Skip confirmation prompt").option("-a, --all","Select all active release branches").option("-v, --versions <versions>","Specify versions by comma, e.g. 1.2.5, 1.2.6").option("-i, --ide [mode]","Editor mode for created worktrees: workspace (default) | none").option("--no-ide","Skip the editor (alias for --ide none)").option("-c, --cursor [mode]","Deprecated alias for --ide").option("--no-cursor","Deprecated alias for --no-ide").option("-g, --github-desktop","Open created worktrees in GitHub Desktop").option("--no-github-desktop","Skip GitHub Desktop prompt").option("-m, --cmux","Open created worktrees in cmux (3-pane layout)").option("--no-cmux","Skip cmux prompt").action(async t=>{let o=so(t.ide,"--ide")??so(t.cursor,"--cursor");d(await Pt({confirmedCommand:t.yes,all:t.all,versions:t.versions,ide:o,githubDesktop:t.githubDesktop,cmux:t.cmux}))}),Sr=e=>e.description("List all git worktrees with detailed information").action(async()=>{d(await It())}),kr=e=>e.description("Remove git worktrees for release branches").option("-y, --yes","Skip confirmation prompt").option("-a, --all","Select all active release branches").option("-v, --versions <versions>","Specify versions by comma, e.g. 1.2.5, 1.2.6").action(async t=>{d(await Lt({confirmedCommand:t.yes,all:t.all,versions:t.versions}))}),Rr=e=>e.description("Close all cmux/editor worktree windows, then reopen the current release worktrees (also cold-start restore)").action(async()=>{d(await Nt())}),Er=e=>e.description("Show the machine-local factory config (~/.infra-kit/vendor.json) or scaffold it with --init").option("--init","Scaffold ~/.infra-kit/vendor.json (skips if it already exists)").action(async t=>{d(await Re({init:t.init}))}),xr=e=>e.description("Verify vendor/ matches vendor/.sync-manifest.json (self-contained; for any consumer repo)").action(async()=>{let t=await xt();d(t),t.structuredContent.ok||(_.exitCode=1)}),Tr=e=>e.description("Source-aware drift check (rsync dry-run) of each target vendored subtree vs the source").option("-r, --repos <repos>","Restrict to comma-separated target repo names").action(async t=>{let o=await Tt({repos:xe(t.repos)});d(o),o.structuredContent.ok||(_.exitCode=1)}),Ar=e=>e.description("Show the resolved config merge chain and file paths").action(async()=>{d(await ae())}),Cr=e=>e.description("Open the user-scope per-project override file in $EDITOR").action(async()=>{d(await ce())}),br=new Set(["init","doctor","version","dev","self-update","mcp"]),Pr=e=>e.startsWith("env-")||br.has(e),Ir=new Set(["env-autoload","mcp","version","self-update"]),z=e=>{let t=[];for(let o=e;o&&o.parent;o=o.parent)t.unshift(o.name());return t.join(" ")},Te=()=>{let e=new lr,t=e.command("release").description("Release management commands");dr(t.command("merge-dev")),ur(t.command("list")),pr(t.command("create")),fr(t.command("desc-edit")),gr(t.command("deploy-all")),hr(t.command("deploy-selected")),vr(t.command("deliver"));let o=e.command("worktrees").description("Git worktree management commands");wr(o.command("add")),Sr(o.command("list")),kr(o.command("remove")),yr(o.command("sync")),Rr(o.command("reload"));let r=e.command("config").description("Manage infra-kit configuration files");Ar(r.command("path")),Cr(r.command("edit")),e.command("audit").description("Audit against infra-kit.config.ts rules (--all for every package, --root for the monorepo root)").option("-a, --all","Audit every non-vendor workspace package").option("-r, --root","Audit the monorepo root (turbo pipeline + root commands)").action(async n=>{let i=await De({all:n.all,root:n.root});d(i),i.structuredContent.allPassed||(_.exitCode=1)});let s=e.command("vendor").description("Verify and sync the mirrored vendor/ tree");return xr(s.command("check")),s.command("sync").description("Copy vendored files from the source repo into each target and regenerate manifests").option("-y, --yes","Skip confirmation prompt").option("-r, --repos <repos>","Restrict to comma-separated target repo names").action(async n=>{d(await Ct({confirmedCommand:n.yes,repos:xe(n.repos)}))}),s.command("manifest").description("Regenerate each target vendor/.sync-manifest.json + README from current content (no copy)").option("-r, --repos <repos>","Restrict to comma-separated target repo names").action(async n=>{d(await At({confirmedCommand:!0,repos:xe(n.repos)}))}),Tr(s.command("diff")),Er(s.command("config")),e.command("doctor").description("Check installation and authentication status of gh and doppler CLIs").option("--fix","Remove portless routes left behind by a dev-server that was killed (kill -9, OOM, force-quit). Refuses while a dev session is running, when a booting UI is indistinguishable from a dead route.").action(async n=>{d(await mt({fix:!!n.fix}))}),e.command("self-update").description("Update this CLI using the package manager that installed it").option("--dry-run","Print the detected manager and the command that would run; install nothing").action(n=>{we({dryRun:!!n.dryRun})}),e.command("mcp").description("Run the infra-kit MCP server (stdio transport)").action(()=>{ve()}),e.command("dev").description("Run local dev servers for a named devServersPresets preset (or all apps); api + ui").argument("[preset]","Named preset from devServersPresets (omit to run every app)").option("-w, --watch","Rebuild and restart on file save").option("--app <names>","Further narrow to these app folder names (comma-separated)").option("--target <keys>","Run exactly these <app>/api|<app>/ui packages (comma-separated); part-level, unlike --app").option("--cmux","Run each app in its own cmux pane (one workspace, N panes; falls back to single terminal if cmux is unavailable)").option("--self","Run only the app of the current directory (infer from cwd; use inside apps/<app>/\u2026)").option("-V, --verbose","Print full boot narration (default: quiet; full detail always in the session log)").option("--routes","Print each app\u2019s registered METHOD /path routes at startup (default: off)").option("--no-ui-health","Do not probe the frontends\u2019 liveness (vite\u2019s HMR ping); their rows carry no health dot (also: INFRA_KIT_NO_UI_HEALTH=1)").action(async(n,i)=>{let{runDevServerCli:c}=await import("./dev-server.js"),a=!!(_.stdout.isTTY&&_.stdin.isTTY);await c({...i,preset:n},a,U.enabled)}),e.command("version").description("Print the installed infra-kit CLI version").action(async()=>{d(await bt())}),e.command("env-status").description("Show which env is loaded in this session (local introspection; no Doppler call)").action(async()=>{d(await pt())}),e.command("env-list").description("List available Doppler configs for the detected project, and whether a service token resolves for each").action(async()=>{d(await ut())}),e.command("init").description("Inject shell integration into .zshrc and sync repo agent-instruction files").action(async()=>{d(await ee())}),e.command("env-load").description("Load Doppler env vars for a config. Source the returned file path to apply.").option("-c, --config <config>","Environment config name to load (e.g. dev, arthur)").action(async n=>{d(await ze({config:n.config}))}),e.command("env-clear").description("Clear loaded env vars. Source the returned file path to apply.").option("--purge","Also delete this project's warm cache outright (durable disable)").action(async n=>{d(await dt({purge:!!n.purge}))}),e.command("env-token-set").description("Store the Doppler service token for an env (masked prompt; validated against Doppler before writing)").argument("<env>","Environment / Doppler config the token is scoped to (e.g. dev)").option("--stdin","Read the token from stdin instead of prompting (e.g. from a password manager)").option("--from-env <var>","Read the token from the named environment variable (the NAME, never the value)").option("--force","Store even when the token\u2019s scope could not be verified. Never overrides a real mismatch.").action(async(n,i)=>{d(await he({env:n,stdin:i.stdin,fromEnv:i.fromEnv,force:i.force}))}),e.command("env-token-list").description("Show which envs have a Doppler service token (redacted), and where it came from").option("--check","Also ask Doppler whether each token is valid and correctly scoped").action(async n=>{d(await ft({check:!!n.check}))}),e.command("env-token-remove").description("Delete an env\u2019s Doppler service token from the local store (does NOT revoke it in Doppler)").argument("<env>","Environment / Doppler config whose token to remove").action(async n=>{d(await fe({env:n}))}),e.command("env-autoload",{hidden:!0}).description("Internal: prime env for the shell-startup auto-load trigger").option("--project-dir <dir>","Canonical project dir for the warm-cache key (shell-startup only)").action(async n=>{await pe({projectDir:n.projectDir})}),e.commands.forEach(gt),e.hook("preAction",async(n,i)=>{A.start(z(i)),U.enabled=!!i.optsWithGlobals().json,U.enabled&&(l.level="warn"),Ir.has(z(i))||await rt(),q(),Pr(i.name())||await L({expectedTrigger:"cli-invocation"})}),e.hook("postAction",(n,i)=>{let c=z(i);if(Ft(c))return;let m=A.snapshot()?.formattedOptions??"",u=m?` ${m}`:"",f=`infra-kit ${c}${u}`;ro({equivalent:B(f,!0)})}),e};var Ae=(e,t=" ")=>{let o=e.reduce((r,[s])=>Math.max(r,s.length),0);return e.map(([r,s])=>`${r.padEnd(o)}${t}${s}`)};import{chalkStderr as jr}from"chalk";import{spawn as Ur}from"node:child_process";import S from"node:process";import{Chalk as Nr}from"chalk";var b=new Nr({level:1}),w={ok:"ok",findingsPlain:"completed with findings",failed:"failed",cancelled:"cancelled",findingsSuffix:"findings",sep:" \xB7 ",reproPrefix:"$ ",nonReproPrefix:"\u2248 ",envNotice:"Applies to your shell after you exit this session."},ao={ok:{unicode:"\u2713",ascii:"[ok]"},findings:{unicode:"\u26A0",ascii:"[!]"},failed:{unicode:"\u2717",ascii:"[x]"},cancelled:{unicode:"\u2298",ascii:"[-]"}},co={unicode:"\u2500",ascii:"-"},Lr=3,Ce=e=>e,_r={ok:e=>b.green(e),findings:e=>b.yellow(e),failed:e=>b.red(e),cancelled:e=>b.gray(e)},mo=e=>e?{dim:t=>b.dim(t),bold:t=>b.bold(t)}:{dim:Ce,bold:Ce},Dr=(e,t)=>e?_r[t]:Ce,lo=e=>[...e].length,uo=(e,t={})=>{let o=mo(t.color===!0);return`${o.dim(w.reproPrefix.trim())} ${o.bold(e)}`},Or=e=>e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(1)}s`,$r=(e,t)=>e==="findings"?t!=null?`${t} ${w.findingsSuffix}`:w.findingsPlain:{ok:w.ok,failed:w.failed,cancelled:w.cancelled}[e],Fr=(e,t,o,r)=>{if(t==null)return"";let s=t-2-e;return s<Lr?"":` ${r(o.repeat(s))}`},po=e=>{let t=e.color===!0,o=mo(t),r=Dr(t,e.outcome),s=e.ascii===!0,n=e.equivalent.reproducible?w.reproPrefix:w.nonReproPrefix,i=s?ao[e.outcome].ascii:ao[e.outcome].unicode,c=$r(e.outcome,e.findingsCount),a=[Or(e.durationMs)],m=e.summary?.[0]?.split(`
8
- `)[0];m!=null&&m.length>0&&a.push(m);let u=`${i} ${c}`,f=`${w.sep}${a.join(w.sep)}`,k=lo(u)+lo(f),R=Fr(k,e.width,s?co.ascii:co.unicode,o.dim),x=[`${r(u)}${o.dim(f)}${R}`];return e.showEquivalent!==!1&&x.unshift(`${o.dim(n.trim())} ${o.bold(e.equivalent.line)}`),e.envNotice===!0&&x.push(o.dim(w.envNotice)),x.join(`
9
- `)};var Mr=new Set([130,143]),fo=(e,t,o,r=!1)=>o?t!=null||e===0?"ok":"findings":r&&e!=null&&Mr.has(e)||t==="SIGINT"||e===0?"cancelled":"failed";import go from"node:process";var E={column:"\r",sgr:"\x1B[0m",cursor:"\x1B[?25h",wrap:"\x1B[?7h",saveCursor:"\x1B7",restoreCursor:"\x1B8",scrollRegion:"\x1B[r",primaryBuffer:"\x1B[?1049l"},ho=(e,t)=>{let o=t?.write??(n=>{go.stderr.write(n)}),r=t?.stdin??go.stdin,s=[E.saveCursor,E.scrollRegion,E.restoreCursor,E.column,E.sgr,E.cursor,E.wrap];e.entersAltScreen===!0&&s.push(E.primaryBuffer),o(s.join("")),r.isTTY===!0&&r.isRaw===!0&&r.setRawMode?.(!1)};var vo=e=>`infra-kit ${e.groupPath.join(" ")}`,Gr=()=>{let e=S.stderr.columns;return e!=null&&e>0?e:void 0},Vr=e=>new Promise(t=>{e.on("exit",(o,r)=>{t({code:o,signal:r})}),e.on("error",()=>{t({code:1,signal:null})})}),qr=async(e,t,o)=>{let r=no(),s={...t.env,[Y]:r,INFRA_KIT_NO_AUTO_UPDATE:"1"},n=t.now(),i={code:1,signal:null};o();try{let f=t.spawn(S.execPath,[t.cliPath,...e.groupPath],{stdio:"inherit",env:s});i=await Vr(f)}catch{i={code:1,signal:null}}finally{t.resetTerminal({entersAltScreen:e.entersAltScreen})}let c=io(r),a=fo(i.code,i.signal,c!=null,e.longRunning),m=vo(e),u=c?.equivalent??B(m,!0);return po({equivalent:u,outcome:a,durationMs:t.now()-n,summary:c?.summary,envNotice:e.sessionEnvNotice,ascii:t.ascii,color:t.color,width:t.columns(),showEquivalent:!(u.reproducible&&u.line===m)})},Jr=(e,t)=>{let o=!1,r=!1,s=()=>{if(e()){o=!0;return}t.exit(0)},n=()=>{if(!e()){t.exit(0);return}o||(r=!0)},i=()=>{t.exit(129)},c=()=>{e()&&t.raise("SIGSTOP")};return t.register("SIGINT",s),t.register("SIGTERM",n),t.register("SIGHUP",i),t.register("SIGTSTP",c),{dispose:()=>{t.unregister("SIGINT",s),t.unregister("SIGTERM",n),t.unregister("SIGHUP",i),t.unregister("SIGTSTP",c)},childStarted:()=>{o=!1},quitRequested:()=>r}},yo=(e,t)=>!!(t.stdoutIsTTY&&t.stdinIsTTY&&t.stderrIsTTY&&e.TERM!=="dumb"&&!e.INFRA_KIT_NO_SESSION&&!e.INFRA_KIT_SESSION_REPORT),wo=async(e,t)=>{let o={spawn:t.spawn??Ur,now:t.now??(()=>Date.now()),env:t.env??S.env,cliPath:t.cliPath,ascii:t.ascii??!(S.stdout.isTTY&&S.env.TERM!=="dumb"),color:t.color??jr.level>0,columns:t.columns??Gr,resetTerminal:t.resetTerminal??(a=>{ho(a)})},r=t.write??(a=>S.stderr.write(a)),s=t.signals??{register:(a,m)=>{S.on(a,m)},unregister:(a,m)=>{S.off(a,m)},exit:a=>{S.exit(a)},raise:a=>{S.kill(S.pid,a)}},n=!1,i={dispose:()=>{},childStarted:()=>{},quitRequested:()=>!1},c=t.installSignals===!1?i:Jr(()=>n,s);try{for(;;){if(c.quitRequested())return;n=!1;let a=await t.renderPalette(e);if(a==null)return;let m=t.resolveCommand(a);if(!m)continue;let u=uo(vo(m),{color:o.color});r(`
6
+ `),he=async({env:e,stdin:t,fromEnv:o,force:r})=>{let s=await $(),{token:n,source:i}=await qo({stdin:t,fromEnv:o});if(!n)throw new Error("No token provided \u2014 nothing was written.");let c=await Wo(n,s,e);Qe(c,e);let a=c.some(([k])=>k===ge);if(!a&&!r)throw new Error(Ko(e));await Be(e,n);let m=await U(),u=await M();l.info(`Stored the "${e}" service token (${te(n)}) in ${h(u)} (mode 0600).`),a||l.warn(`Scope was NOT verified (no ${ge} in the payload) \u2014 written because --force was given.`),m.length>0&&l.info(`Purged ${m.length} warm cache(s) so the next shell cannot serve secrets fetched with an old token.`);let f={env:e,source:i,redactedToken:te(n),storePath:u,scopeVerified:a,warmCachesPurged:m.length};return A.print(),{content:O(JSON.stringify(f,null,2)),structuredContent:f}};import{spawn as Ho}from"node:child_process";import B from"node:process";import{fileURLToPath as Yo}from"node:url";var zo=["SIGINT","SIGTERM"],Yt=()=>Yo(new URL("./mcp.js",import.meta.url)),ve=(e={})=>{let t=e.spawn??Ho,o=e.exit??(i=>B.exit(i)),r=e.env??B.env,s=e.onError??(i=>l.error(i)),n=t(B.execPath,[Yt()],{stdio:"inherit",env:st(r)});n.on("error",i=>{s(`failed to launch the MCP server: ${i.message}`),o(1)}),zo.forEach(i=>{B.on(i,()=>{n.kill(i)})}),n.on("exit",(i,c)=>{o(c?1:i??1)})};import{spawnSync as Xo}from"node:child_process";import{realpathSync as Qo}from"node:fs";import{homedir as Zo}from"node:os";import ye from"node:process";import{fileURLToPath as er}from"node:url";var tr=()=>Qo(er(import.meta.url)),or=(e,t,o,r)=>e.error?(o(`${t} not found on PATH: ${e.error.message}`),r(1)):e.signal?(o(`update terminated by signal ${e.signal}`),r(1)):r(e.status??1),we=({dryRun:e},t={})=>{let o=t.spawnSync??Xo,r=t.print??(R=>l.info(R)),s=t.exit??(R=>ye.exit(R)),n=t.env??ye.env,i=t.selfRealPath??tr(),c=t.lazyNpmRoot??Ut,{manager:a,updateCommand:m,canSelfSpawn:u}=jt({selfRealPath:i,env:n,realpath:C,lazyNpmRoot:c}),f=m.join(" ");if(e){r(`Detected install manager: ${a}`),r(`Would run: ${f}`);return}if(!u){r(`Detected install manager: ${a}`),r(`Run this yourself: ${f}`),r(a==="homebrew"?"Not run for you: Homebrew manages this install; running a package manager would create a split-brain install.":"Not run for you: the install location is unrecognized, so the command above is a guess \u2014 a guessed global install is worse than a printed one.");return}let k=o(m[0],m.slice(1),{stdio:"inherit",shell:ye.platform==="win32",cwd:Zo(),env:at(n)});or(k,a,r,s)};import{VENDOR_CONFIG_FILE as Xt}from"@slip-stream-kit/config/internal";import Se from"node:fs/promises";import ke from"node:path";import zt from"node:process";import{pathToFileURL as rr}from"node:url";var Qt="~/projects",Re=async(e={})=>{if(e.init){await ir(e.cwd);return}await nr()},nr=async()=>{let e=Z(),t=await T(e);if(l.info(`Factory config: ${h(e)} ${t?"[\u2713]":"[ ]"}`),!t){l.info("\nNot found \u2014 run `infra-kit vendor config --init` to scaffold it."),zt.exitCode=1;return}let{workspaceDir:o,targets:r}=await it(),s=nt(o),n=await T(s);l.info(`workspaceDir: ${o} (resolved: ${s}) ${n?"[\u2713 exists]":"[ ] not found"}`),l.info("Targets:");let i=n;for(let c of r){let a=ke.join(s,c),m=await T(a);m||(i=!1);let u=m?"[\u2713]":"[ ]",f=m?"":" (not found \u2014 clone or remove)";l.info(` ${u} ${c} ${h(a)}${f}`)}i||(zt.exitCode=1)},ir=async e=>{let t=Z();if(await T(t)){l.info(`Factory config already exists at ${h(t)} \u2014 leaving it untouched.`);return}let o=e??await Ne(),r=await sr(o);await Se.mkdir(ke.dirname(t),{recursive:!0}),await Se.writeFile(t,ar(r),"utf-8"),l.info(`\u2713 Created ${h(t)}`),r.length>0&&l.info(` Seeded ${r.length} target(s) from the source ${Xt}.`),l.info(` Edit \`workspaceDir\` (placeholder: ${Qt}) to point at where your repos live.`),r.length===0&&l.info(" Add at least one repo name to `targets` before running vendor sync/manifest/diff.")},sr=async e=>{try{let t=ke.join(e,Xt),o=await Se.stat(t),n=(await import(`${rr(t).href}?mtime=${Number(o.mtimeMs)}`)).default,i=typeof n=="function"?await n():n;if(i&&typeof i=="object"&&"targets"in i){let c=i.targets;if(Array.isArray(c)&&c.every(a=>typeof a=="string"))return c}}catch{}return[]},ar=e=>`${JSON.stringify({workspaceDir:Qt,targets:e},null,2)}
7
+ `;var K=(e,t=!0)=>({line:e,reproducible:t});import H from"node:fs";import cr from"node:os";import lr from"node:path";import oo from"node:process";var Y="INFRA_KIT_SESSION_REPORT",Ee=null,Zt=!1,eo=[],ro=(e=oo.env)=>{Zt||(Ee=e[Y]??null,Zt=!0,delete e[Y])};var no=(e,t)=>{if(!Ee)return;let o=e.summary??(eo.length>0?[...eo]:void 0),r={...e,...o?{summary:o}:{}},s=t?.write??((n,i)=>{H.writeFileSync(n,i)});try{s(Ee,JSON.stringify(r))}catch{}},to=0,io=e=>{let t=e?.tmpdir?.()??cr.tmpdir(),o=e?.pid??oo.pid;return to+=1,lr.join(t,`infra-kit-session-${o}-${to}.json`)},so=(e,t)=>{if(!(t?.exists??(i=>H.existsSync(i)))(e))return null;let r=t?.read??(i=>H.readFileSync(i,"utf-8")),s=t?.unlink??(i=>H.unlinkSync(i)),n;try{n=r(e)}catch{return null}finally{try{s(e)}catch{}}try{return JSON.parse(n)}catch{return null}};var dr=(e,t)=>[...t,e],xe=e=>typeof e=="string"?e.split(",").filter(Boolean):void 0,ao=(e,t)=>{if(!(typeof e>"u")){if(e===!0)return"workspace";if(e===!1)return"none";if(typeof e=="string"&&oe.includes(e))return e;throw new Error(`Invalid ${t} value "${String(e)}". Expected one of: ${oe.join(", ")}.`)}},ur=e=>e.description("Merge dev branch into every release branch").option("-a, --all","Select all active release branches").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await vt({all:t.all,confirmedCommand:t.yes}))}),pr=e=>e.description("List all release branches").action(async()=>{d(await kt())}),fr=e=>e.description("Create one or more release branches (each entry can mix regular/hotfix and its own description)").option("-r, --release <spec>",'Release spec "<version|next|name>[:type[:description]]" (repeatable). The token is a semver ("1.2.5"), the literal "next", or a kebab-case name ("checkout-redesign"). Type is regular|hotfix (default regular). Examples: "1.2.5", "1.2.5:hotfix", "next:regular:Holiday backend", "checkout-redesign:regular:Q3 redesign".',dr,[]).option("-y, --yes","Skip confirmation prompt").action(async t=>{let r=t.release.map(Rt),s=r.length>0?r:void 0;d(await Et({releases:s,confirmedCommand:t.yes}))}),gr=e=>e.description("Edit a release's description in Jira and in the matching GitHub PR body").option("-v, --version <version>","Release version (e.g. 1.2.5) or release name (e.g. checkout-redesign)").option("-d, --description <description>",'New description (use "" to clear)').option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await xt({version:t.version,description:t.description,confirmedCommand:t.yes}))}),hr=e=>e.description("Deploy any release branch to any environment").option("-v, --version <version>",'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deploy; "dev" deploys from the dev branch').option("-e, --env <env>","Specify the environment to deploy to, e.g. dev").option("--skip-terraform","Skip terraform deployment step").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await wt({version:t.version,env:t.env,skipTerraform:t.skipTerraform,confirmedCommand:t.yes}))}),vr=e=>e.description("Deploy selected services from release branch to any environment").option("-v, --version <version>",'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deploy; "dev" deploys from the dev branch').option("-e, --env <env>","Specify the environment to deploy to, e.g. dev").option("-s, --services <services...>","Specify services to deploy, e.g. client-be client-fe").option("--skip-terraform","Skip terraform deployment step").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await St({version:t.version,env:t.env,services:t.services,skipTerraform:t.skipTerraform,confirmedCommand:t.yes}))}),yr=e=>e.description("Release a new version to production").option("-v, --version <version>","Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deliver").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await yt({version:t.version,confirmedCommand:t.yes}))}),wr=e=>e.description("Remove release worktrees whose PRs are no longer open").option("-y, --yes","Skip confirmation prompt").action(async t=>{d(await _t({confirmedCommand:t.yes}))}),Sr=e=>e.description("Add git worktrees for release branches").option("-y, --yes","Skip confirmation prompt").option("-a, --all","Select all active release branches").option("-v, --versions <versions>","Specify versions by comma, e.g. 1.2.5, 1.2.6").option("-i, --ide [mode]","Editor mode for created worktrees: workspace (default) | none").option("--no-ide","Skip the editor (alias for --ide none)").option("-c, --cursor [mode]","Deprecated alias for --ide").option("--no-cursor","Deprecated alias for --no-ide").option("-g, --github-desktop","Open created worktrees in GitHub Desktop").option("--no-github-desktop","Skip GitHub Desktop prompt").option("-m, --cmux","Open created worktrees in cmux (3-pane layout)").option("--no-cmux","Skip cmux prompt").action(async t=>{let o=ao(t.ide,"--ide")??ao(t.cursor,"--cursor");d(await Nt({confirmedCommand:t.yes,all:t.all,versions:t.versions,ide:o,githubDesktop:t.githubDesktop,cmux:t.cmux}))}),kr=e=>e.description("List all git worktrees with detailed information").action(async()=>{d(await Lt())}),Rr=e=>e.description("Remove git worktrees for release branches").option("-y, --yes","Skip confirmation prompt").option("-a, --all","Select all active release branches").option("-v, --versions <versions>","Specify versions by comma, e.g. 1.2.5, 1.2.6").action(async t=>{d(await Dt({confirmedCommand:t.yes,all:t.all,versions:t.versions}))}),Er=e=>e.description("Reopen editor + cmux windows for every active worktree in the current project (additive, idempotent)").option("--all","Reopen across every discovered infra-kit project (Stage 3 \u2014 not yet implemented)").option("--project <names...>","Restrict --all to these project names").option("--root <paths...>","Discovery roots for --all (repeatable)").option("--release-only","Restrict to release worktrees (reproduces the legacy worktrees-reload scope)").option("--force","Close each cmux workspace first, then reopen (the legacy worktrees-reload behaviour)").option("--dry-run","Print the plan (paths + cmux titles) and spawn nothing").action(async t=>{d(await Tt({all:t.all,project:t.project,root:t.root,releaseOnly:t.releaseOnly,force:t.force,dryRun:t.dryRun}))}),xr=e=>e.description("Show the machine-local factory config (~/.infra-kit/vendor.json) or scaffold it with --init").option("--init","Scaffold ~/.infra-kit/vendor.json (skips if it already exists)").action(async t=>{d(await Re({init:t.init}))}),Tr=e=>e.description("Verify vendor/ matches vendor/.sync-manifest.json (self-contained; for any consumer repo)").action(async()=>{let t=await At();d(t),t.structuredContent.ok||(_.exitCode=1)}),Ar=e=>e.description("Source-aware drift check (rsync dry-run) of each target vendored subtree vs the source").option("-r, --repos <repos>","Restrict to comma-separated target repo names").action(async t=>{let o=await Ct({repos:xe(t.repos)});d(o),o.structuredContent.ok||(_.exitCode=1)}),Cr=e=>e.description("Show the resolved config merge chain and file paths").action(async()=>{d(await ae())}),br=e=>e.description("Open the user-scope per-project override file in $EDITOR").action(async()=>{d(await ce())}),Pr=new Set(["init","doctor","version","dev","self-update","mcp"]),Ir=e=>e.startsWith("env-")||Pr.has(e),Nr=new Set(["env-autoload","mcp","version","self-update"]),z=e=>{let t=[];for(let o=e;o&&o.parent;o=o.parent)t.unshift(o.name());return t.join(" ")},Te=()=>{let e=new mr,t=e.command("release").description("Release management commands");ur(t.command("merge-dev")),pr(t.command("list")),fr(t.command("create")),gr(t.command("desc-edit")),hr(t.command("deploy-all")),vr(t.command("deploy-selected")),yr(t.command("deliver"));let o=e.command("worktrees").description("Git worktree management commands");Sr(o.command("add")),kr(o.command("list")),Rr(o.command("remove")),wr(o.command("sync")),Er(e.command("reopen"));let r=e.command("config").description("Manage infra-kit configuration files");Cr(r.command("path")),br(r.command("edit")),e.command("audit").description("Audit against infra-kit.config.ts rules (--all for every package, --root for the monorepo root)").option("-a, --all","Audit every non-vendor workspace package").option("-r, --root","Audit the monorepo root (turbo pipeline + root commands)").action(async n=>{let i=await _e({all:n.all,root:n.root});d(i),i.structuredContent.allPassed||(_.exitCode=1)});let s=e.command("vendor").description("Verify and sync the mirrored vendor/ tree");return Tr(s.command("check")),s.command("sync").description("Copy vendored files from the source repo into each target and regenerate manifests").option("-y, --yes","Skip confirmation prompt").option("-r, --repos <repos>","Restrict to comma-separated target repo names").action(async n=>{d(await Pt({confirmedCommand:n.yes,repos:xe(n.repos)}))}),s.command("manifest").description("Regenerate each target vendor/.sync-manifest.json + README from current content (no copy)").option("-r, --repos <repos>","Restrict to comma-separated target repo names").action(async n=>{d(await bt({confirmedCommand:!0,repos:xe(n.repos)}))}),Ar(s.command("diff")),xr(s.command("config")),e.command("doctor").description("Check installation and authentication status of gh and doppler CLIs").option("--fix","Remove portless routes left behind by a dev-server that was killed (kill -9, OOM, force-quit). Refuses while a dev session is running, when a booting UI is indistinguishable from a dead route.").option("--ascii","Render the report with ASCII markers instead of unicode glyphs (check messages are unchanged)").action(async n=>{let i=await mt({fix:!!n.fix});I.enabled||dt(i.structuredContent.checks,{ascii:!!n.ascii}),d(i)}),e.command("self-update").description("Update this CLI using the package manager that installed it").option("--dry-run","Print the detected manager and the command that would run; install nothing").action(n=>{we({dryRun:!!n.dryRun})}),e.command("mcp").description("Run the infra-kit MCP server (stdio transport)").action(()=>{ve()}),e.command("dev").description("Run local dev servers for a named devServersPresets preset (or all apps); api + ui").argument("[preset]","Named preset from devServersPresets (omit to run every app)").option("-w, --watch","Rebuild and restart on file save").option("--app <names>","Further narrow to these app folder names (comma-separated)").option("--target <keys>","Run exactly these <app>/api|<app>/ui packages (comma-separated); part-level, unlike --app").option("--cmux","Run each app in its own cmux pane (one workspace, N panes; falls back to single terminal if cmux is unavailable)").option("--self","Run only the app of the current directory (infer from cwd; use inside apps/<app>/\u2026)").option("-V, --verbose","Print full boot narration (default: quiet; full detail always in the session log)").option("--routes","Print each app\u2019s registered METHOD /path routes at startup (default: off)").option("--no-ui-health","Do not probe the frontends\u2019 liveness (vite\u2019s HMR ping); their rows carry no health dot (also: INFRA_KIT_NO_UI_HEALTH=1)").action(async(n,i)=>{let{runDevServerCli:c}=await import("./dev-server.js"),a=!!(_.stdout.isTTY&&_.stdin.isTTY);await c({...i,preset:n},a,I.enabled)}),e.command("version").description("Print the installed infra-kit CLI version").action(async()=>{d(await It())}),e.command("env-status").description("Show which env is loaded in this session (local introspection; no Doppler call)").action(async()=>{d(await ft())}),e.command("env-list").description("List available Doppler configs for the detected project, and whether a service token resolves for each").action(async()=>{d(await pt())}),e.command("init").description("Inject shell integration into .zshrc and sync repo agent-instruction files").action(async()=>{d(await ee())}),e.command("env-load").description("Load Doppler env vars for a config. Source the returned file path to apply.").option("-c, --config <config>","Environment config name to load (e.g. dev, arthur)").action(async n=>{d(await ze({config:n.config}))}),e.command("env-clear").description("Clear loaded env vars. Source the returned file path to apply.").option("--purge","Also delete this project's warm cache outright (durable disable)").action(async n=>{d(await ut({purge:!!n.purge}))}),e.command("env-token-set").description("Store the Doppler service token for an env (masked prompt; validated against Doppler before writing)").argument("<env>","Environment / Doppler config the token is scoped to (e.g. dev)").option("--stdin","Read the token from stdin instead of prompting (e.g. from a password manager)").option("--from-env <var>","Read the token from the named environment variable (the NAME, never the value)").option("--force","Store even when the token\u2019s scope could not be verified. Never overrides a real mismatch.").action(async(n,i)=>{d(await he({env:n,stdin:i.stdin,fromEnv:i.fromEnv,force:i.force}))}),e.command("env-token-list").description("Show which envs have a Doppler service token (redacted), and where it came from").option("--check","Also ask Doppler whether each token is valid and correctly scoped").action(async n=>{d(await gt({check:!!n.check}))}),e.command("env-token-remove").description("Delete an env\u2019s Doppler service token from the local store (does NOT revoke it in Doppler)").argument("<env>","Environment / Doppler config whose token to remove").action(async n=>{d(await fe({env:n}))}),e.command("env-autoload",{hidden:!0}).description("Internal: prime env for the shell-startup auto-load trigger").option("--project-dir <dir>","Canonical project dir for the warm-cache key (shell-startup only)").action(async n=>{await pe({projectDir:n.projectDir})}),e.commands.forEach(ht),e.hook("preAction",async(n,i)=>{A.start(z(i)),I.enabled=!!i.optsWithGlobals().json,I.enabled&&(l.level="warn"),Nr.has(z(i))||await rt(),V(),Ir(i.name())||await D({expectedTrigger:"cli-invocation"})}),e.hook("postAction",(n,i)=>{let c=z(i);if(Mt(c))return;let m=A.snapshot()?.formattedOptions??"",u=m?` ${m}`:"",f=`infra-kit ${c}${u}`;no({equivalent:K(f,!0)})}),e};var Ae=(e,t=" ")=>{let o=e.reduce((r,[s])=>Math.max(r,s.length),0);return e.map(([r,s])=>`${r.padEnd(o)}${t}${s}`)};import{chalkStderr as Ur}from"chalk";import{spawn as Gr}from"node:child_process";import S from"node:process";import{Chalk as Lr}from"chalk";var b=new Lr({level:1}),w={ok:"ok",findingsPlain:"completed with findings",failed:"failed",cancelled:"cancelled",findingsSuffix:"findings",sep:" \xB7 ",reproPrefix:"$ ",nonReproPrefix:"\u2248 ",envNotice:"Applies to your shell after you exit this session."},co={ok:{unicode:"\u2713",ascii:"[ok]"},findings:{unicode:"\u26A0",ascii:"[!]"},failed:{unicode:"\u2717",ascii:"[x]"},cancelled:{unicode:"\u2298",ascii:"[-]"}},lo={unicode:"\u2500",ascii:"-"},Dr=3,Ce=e=>e,_r={ok:e=>b.green(e),findings:e=>b.yellow(e),failed:e=>b.red(e),cancelled:e=>b.gray(e)},uo=e=>e?{dim:t=>b.dim(t),bold:t=>b.bold(t)}:{dim:Ce,bold:Ce},Or=(e,t)=>e?_r[t]:Ce,mo=e=>[...e].length,po=(e,t={})=>{let o=uo(t.color===!0);return`${o.dim(w.reproPrefix.trim())} ${o.bold(e)}`},$r=e=>e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(1)}s`,Fr=(e,t)=>e==="findings"?t!=null?`${t} ${w.findingsSuffix}`:w.findingsPlain:{ok:w.ok,failed:w.failed,cancelled:w.cancelled}[e],Mr=(e,t,o,r)=>{if(t==null)return"";let s=t-2-e;return s<Dr?"":` ${r(o.repeat(s))}`},fo=e=>{let t=e.color===!0,o=uo(t),r=Or(t,e.outcome),s=e.ascii===!0,n=e.equivalent.reproducible?w.reproPrefix:w.nonReproPrefix,i=s?co[e.outcome].ascii:co[e.outcome].unicode,c=Fr(e.outcome,e.findingsCount),a=[$r(e.durationMs)],m=e.summary?.[0]?.split(`
8
+ `)[0];m!=null&&m.length>0&&a.push(m);let u=`${i} ${c}`,f=`${w.sep}${a.join(w.sep)}`,k=mo(u)+mo(f),R=Mr(k,e.width,s?lo.ascii:lo.unicode,o.dim),x=[`${r(u)}${o.dim(f)}${R}`];return e.showEquivalent!==!1&&x.unshift(`${o.dim(n.trim())} ${o.bold(e.equivalent.line)}`),e.envNotice===!0&&x.push(o.dim(w.envNotice)),x.join(`
9
+ `)};var jr=new Set([130,143]),go=(e,t,o,r=!1)=>o?t!=null||e===0?"ok":"findings":r&&e!=null&&jr.has(e)||t==="SIGINT"||e===0?"cancelled":"failed";import ho from"node:process";var E={column:"\r",sgr:"\x1B[0m",cursor:"\x1B[?25h",wrap:"\x1B[?7h",saveCursor:"\x1B7",restoreCursor:"\x1B8",scrollRegion:"\x1B[r",primaryBuffer:"\x1B[?1049l"},vo=(e,t)=>{let o=t?.write??(n=>{ho.stderr.write(n)}),r=t?.stdin??ho.stdin,s=[E.saveCursor,E.scrollRegion,E.restoreCursor,E.column,E.sgr,E.cursor,E.wrap];e.entersAltScreen===!0&&s.push(E.primaryBuffer),o(s.join("")),r.isTTY===!0&&r.isRaw===!0&&r.setRawMode?.(!1)};var yo=e=>`infra-kit ${e.groupPath.join(" ")}`,qr=()=>{let e=S.stderr.columns;return e!=null&&e>0?e:void 0},Vr=e=>new Promise(t=>{e.on("exit",(o,r)=>{t({code:o,signal:r})}),e.on("error",()=>{t({code:1,signal:null})})}),Jr=async(e,t,o)=>{let r=io(),s={...t.env,[Y]:r,INFRA_KIT_NO_AUTO_UPDATE:"1"},n=t.now(),i={code:1,signal:null};o();try{let f=t.spawn(S.execPath,[t.cliPath,...e.groupPath],{stdio:"inherit",env:s});i=await Vr(f)}catch{i={code:1,signal:null}}finally{t.resetTerminal({entersAltScreen:e.entersAltScreen})}let c=so(r),a=go(i.code,i.signal,c!=null,e.longRunning),m=yo(e),u=c?.equivalent??K(m,!0);return fo({equivalent:u,outcome:a,durationMs:t.now()-n,summary:c?.summary,envNotice:e.sessionEnvNotice,ascii:t.ascii,color:t.color,width:t.columns(),showEquivalent:!(u.reproducible&&u.line===m)})},Wr=(e,t)=>{let o=!1,r=!1,s=()=>{if(e()){o=!0;return}t.resetTerminal(),t.exit(0)},n=()=>{if(!e()){t.resetTerminal(),t.exit(0);return}o||(r=!0)},i=()=>{t.exit(129)},c=()=>{e()&&t.raise("SIGSTOP")};return t.register("SIGINT",s),t.register("SIGTERM",n),t.register("SIGHUP",i),t.register("SIGTSTP",c),{dispose:()=>{t.unregister("SIGINT",s),t.unregister("SIGTERM",n),t.unregister("SIGHUP",i),t.unregister("SIGTSTP",c)},childStarted:()=>{o=!1},quitRequested:()=>r}},wo=(e,t)=>!!(t.stdoutIsTTY&&t.stdinIsTTY&&t.stderrIsTTY&&e.TERM!=="dumb"&&!e.INFRA_KIT_NO_SESSION&&!e.INFRA_KIT_SESSION_REPORT),So=async(e,t)=>{let o={spawn:t.spawn??Gr,now:t.now??(()=>Date.now()),env:t.env??S.env,cliPath:t.cliPath,ascii:t.ascii??!(S.stdout.isTTY&&S.env.TERM!=="dumb"),color:t.color??Ur.level>0,columns:t.columns??qr,resetTerminal:t.resetTerminal??(a=>{vo(a)})},r=t.write??(a=>S.stderr.write(a)),s=t.signals??{register:(a,m)=>{S.on(a,m)},unregister:(a,m)=>{S.off(a,m)},exit:a=>{S.exit(a)},raise:a=>{S.kill(S.pid,a)},resetTerminal:()=>{o.resetTerminal({entersAltScreen:!1})}},n=!1,i={dispose:()=>{},childStarted:()=>{},quitRequested:()=>!1},c=t.installSignals===!1?i:Wr(()=>n,s);try{for(;;){if(c.quitRequested())return;n=!1;let a=await t.renderPalette(e);if(a==null)return;let m=t.resolveCommand(a);if(!m)continue;let u=po(yo(m),{color:o.color});r(`
10
10
  ${u}
11
- `),c.childStarted();let f=await qr(m,o,()=>{n=!0});r(`
11
+ `),c.childStarted();let f=await Jr(m,o,()=>{n=!0});r(`
12
12
  ${f}
13
- `)}}finally{c.dispose()}};import{spawn as Br}from"node:child_process";import ko from"node:fs";import P from"node:process";import{fileURLToPath as Ro}from"node:url";var So=["INFRA_KIT_NO_AUTO_UPDATE","NO_UPDATE_NOTIFIER","CI"],Wr=new Set(["mcp","self-update"]),Kr=e=>e[2]!=null&&Wr.has(e[2]),be=e=>{let{argv:t,env:o,isTty:r,selfRealPath:s,cwd:n,realpath:i}=e;return So.some(a=>{let m=o[a];return m!=null&&m!==""})?"opt-out":t.includes("--json")?"json":Kr(t)?"own-command":r?G(s,n,i)?"local-install":null:"not-a-tty"};var Hr=e=>{Br(P.execPath,[e,"--parent-pid",String(P.pid)],{detached:!0,stdio:"ignore",windowsHide:!0}).unref()},Yr=()=>Ro(new URL("./update-check.js",import.meta.url)),zr=()=>ko.realpathSync(Ro(new URL("./cli.js",import.meta.url))),Xr=/^[\w@./+-]+$/i,Qr=e=>Xr.test(e),Zr=(e,t,o)=>{!e?.latestVersion||!e.updateCommand||ne(e.latestVersion,t)&&e.updateCommand.every(Qr)&&o(`infra-kit ${e.latestVersion} is available (you have ${t}). Run: ${e.updateCommand.join(" ")}`)},Pe=(e,t={})=>{try{let o=t.argv??P.argv,r=t.env??P.env,s=t.isTty??!!P.stdout.isTTY,n=t.cwd??P.cwd(),i=t.nowMs??Date.now(),c=t.selfRealPath??zr(),a=t.readCache??ie,m=t.spawnChild??Hr,u=t.fileExists??(x=>ko.existsSync(x)),f=t.notify??(x=>{l.info(x)});if(be({argv:o,env:r,isTty:s,selfRealPath:c,cwd:n,realpath:C})!==null)return;let k=a();if(Zr(k,e,f),!se(k,i))return;let R=t.childPath??Yr();if(!u(R))return;m(R)}catch{}};Ut();oo();var Ie=Te(),xo=async e=>{try{e?await Ie.parseAsync(e):await Ie.parseAsync()}catch(t){re(t)&&(l.info("Operation cancelled."),g.exit(0));let o=t instanceof Error?t.message:String(t);l.error(o),g.exit(1)}},on=()=>{try{if(g.env.INFRA_KIT_NO_LOCATION_WARN||g.argv.includes("--json")||g.argv[2]==="mcp")return;G(tn(To(import.meta.url)),g.cwd(),C)&&l.info("Running from a project-local node_modules. Install globally for faster startup: npm i -g infra-kit")}catch{}};on();Pe(et.version);var Ao=()=>Gt(Ie.commands),rn=async()=>{let e=Ao(),t=null;try{if(g.stdout.isTTY&&g.stdin.isTTY){let{runCommandPalette:o}=await import("./boot-FHXCA53R.js");t=await o(e)}else{let o=Ae(e.map(s=>[s.name,s.description])),r=e.flatMap((s,n)=>[...e[n-1]?.group===s.group?[]:[new Eo(" "),new Eo(`\u2014 ${s.group} \u2014`)],{name:o[n]??s.name,value:s.name}]);t=await M(s=>en({message:"Select a command to run",choices:r},s),{output:g.stderr})}}catch(o){if(!re(o))throw o}return t},nn=async()=>{let{runCommandPalette:e}=await import("./boot-FHXCA53R.js"),t=To(import.meta.url),o=new Map(Ot.map(r=>[r.groupPath.join(" "),r]));await wo(Ao(),{renderPalette:e,resolveCommand:r=>{let s=o.get(r);return s?{groupPath:s.groupPath,entersAltScreen:s.entersAltScreen,sessionEnvNotice:s.sessionEnvNotice,longRunning:s.longRunning}:void 0},cliPath:t})};if(g.argv.length<=2){let e={stdoutIsTTY:!!g.stdout.isTTY,stdinIsTTY:!!g.stdin.isTTY,stderrIsTTY:!!g.stderr.isTTY};if(yo(g.env,e))await nn();else{let t=await rn();t&&await xo(["node","infra-kit",...t.split(" ")])}}else await xo();
13
+ `)}}finally{c.dispose()}};import{spawn as Hr}from"node:child_process";import Ro from"node:fs";import P from"node:process";import{fileURLToPath as Eo}from"node:url";var ko=["INFRA_KIT_NO_AUTO_UPDATE","NO_UPDATE_NOTIFIER","CI"],Br=new Set(["mcp","self-update"]),Kr=e=>e[2]!=null&&Br.has(e[2]),be=e=>{let{argv:t,env:o,isTty:r,selfRealPath:s,cwd:n,realpath:i}=e;return ko.some(a=>{let m=o[a];return m!=null&&m!==""})?"opt-out":t.includes("--json")?"json":Kr(t)?"own-command":r?G(s,n,i)?"local-install":null:"not-a-tty"};var Yr=e=>{Hr(P.execPath,[e,"--parent-pid",String(P.pid)],{detached:!0,stdio:"ignore",windowsHide:!0}).unref()},zr=()=>Eo(new URL("./update-check.js",import.meta.url)),Xr=()=>Ro.realpathSync(Eo(new URL("./cli.js",import.meta.url))),Qr=/^[\w@./+-]+$/i,Zr=e=>Qr.test(e),en=(e,t,o)=>{!e?.latestVersion||!e.updateCommand||ne(e.latestVersion,t)&&e.updateCommand.every(Zr)&&o(`infra-kit ${e.latestVersion} is available (you have ${t}). Run: ${e.updateCommand.join(" ")}`)},Pe=(e,t={})=>{try{let o=t.argv??P.argv,r=t.env??P.env,s=t.isTty??!!P.stdout.isTTY,n=t.cwd??P.cwd(),i=t.nowMs??Date.now(),c=t.selfRealPath??Xr(),a=t.readCache??ie,m=t.spawnChild??Yr,u=t.fileExists??(x=>Ro.existsSync(x)),f=t.notify??(x=>{l.info(x)});if(be({argv:o,env:r,isTty:s,selfRealPath:c,cwd:n,realpath:C})!==null)return;let k=a();if(en(k,e,f),!se(k,i))return;let R=t.childPath??zr();if(!u(R))return;m(R)}catch{}};Gt();ro();var Ie=Te(),To=async e=>{try{e?await Ie.parseAsync(e):await Ie.parseAsync()}catch(t){re(t)&&(l.info("Operation cancelled."),g.exit(0));let o=t instanceof Error?t.message:String(t);l.error(o),g.exit(1)}},rn=()=>{try{if(g.env.INFRA_KIT_NO_LOCATION_WARN||g.argv.includes("--json")||g.argv[2]==="mcp")return;G(on(Ao(import.meta.url)),g.cwd(),C)&&l.info("Running from a project-local node_modules. Install globally for faster startup: npm i -g infra-kit")}catch{}};rn();Pe(et.version);var Co=()=>qt(Ie.commands),nn=async()=>{let e=Co(),t=null;try{if(g.stdout.isTTY&&g.stdin.isTTY){let{runCommandPalette:o}=await import("./boot-HP5ORQEX.js");t=await o(e)}else{let o=Ae(e.map(s=>[s.name,s.description])),r=e.flatMap((s,n)=>[...e[n-1]?.group===s.group?[]:[new xo(" "),new xo(`\u2014 ${s.group} \u2014`)],{name:o[n]??s.name,value:s.name}]);t=await j(s=>tn({message:"Select a command to run",choices:r},s),{output:g.stderr})}}catch(o){if(!re(o))throw o}return t},sn=async()=>{let{runCommandPalette:e}=await import("./boot-HP5ORQEX.js"),t=Ao(import.meta.url),o=new Map($t.map(r=>[r.groupPath.join(" "),r]));await So(Co(),{renderPalette:e,resolveCommand:r=>{let s=o.get(r);return s?{groupPath:s.groupPath,entersAltScreen:s.entersAltScreen,sessionEnvNotice:s.sessionEnvNotice,longRunning:s.longRunning}:void 0},cliPath:t})};if(g.argv.length<=2){let e={stdoutIsTTY:!!g.stdout.isTTY,stdinIsTTY:!!g.stdin.isTTY,stderrIsTTY:!!g.stderr.isTTY};if(wo(g.env,e))await sn();else{let t=await nn();t&&await To(["node","infra-kit",...t.split(" ")])}}else await To();
14
14
  //# sourceMappingURL=cli.js.map