infra-kit 0.1.129 → 0.1.130

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.
@@ -0,0 +1,4 @@
1
+ import{L as w,N as x,c as l,i as f,v as n}from"./chunk-4YU4TAGX.js";import g from"node:process";import{$ as p}from"zx";var C=async()=>{try{return await p`cmux --version`.quiet(),!0}catch{return!1}},y=async t=>{let{cwd:e,title:r,layout:s}=t,o=JSON.stringify(s),i=(await p({env:{...g.env,CMUX_QUIET:"1"}})`cmux new-workspace --name ${r} --cwd ${e} --focus false --layout ${o}`).stdout;return W(i)},h=async t=>{try{await p`cmux close-workspace --workspace ${t}`.quiet()}catch(e){n.debug({error:e,ref:t},"cmux: skipped closing dev workspace")}},W=t=>{let e=t.match(/workspace:\d+/);if(!e)throw new Error("cmux: could not locate workspace ref in new-workspace output");return e[0]};var $=/\bv(\d+\.\d+\.\d+)\b/g,a=t=>t.trim().replace(/\s+/g," ").replace($,"$1");import{$ as d}from"zx";var T=async t=>{try{let e=(await d`cmux list-workspaces`.quiet()).stdout,r=b(e,t);if(!r)return;await d`cmux close-workspace --workspace ${r}`.quiet()}catch(e){n.debug({error:e,title:t},"cmux: skipped closing workspace")}},b=(t,e)=>{let r=a(e);for(let s of t.split(`
2
+ `)){let o=s.match(/^[* ]\s*(workspace:\d+)\s+(.+?)(?:\s+\[selected\])?\s*$/);if(!o)continue;let c=o[1],i=o[2]?.trim()??"";if(a(i)===r)return c}};import{$ as v}from"zx";var E=async()=>{try{let t=(await v`cmux list-workspaces`.quiet()).stdout,e=new Set;for(let r of t.split(`
3
+ `)){let s=r.match(/^[* ]\s*workspace:\d+\s+(.+?)(?:\s+\[selected\])?\s*$/);if(!s)continue;let o=s[1]?.trim();o&&e.add(a(o))}return e}catch(t){return n.debug({error:t},"cmux: skipped listing workspace titles"),new Set}};import{$ as u}from"zx";var A=async t=>{let{cwd:e,title:r}=t,s=w(await x()),o=(await u`cmux workspace create --cwd ${e}`).stdout,c=O(o),i=(await u`cmux list-pane-surfaces --workspace ${c}`).stdout,m=N(i);await u`cmux new-split right --workspace ${c} --surface ${m}`,s==="three-pane"&&await u`cmux new-split down --workspace ${c} --surface ${m}`,r&&await u`cmux workspace rename --workspace ${c} --title ${r}`},N=t=>{let e=t.match(/surface:\d+/);if(!e)throw new Error("cmux: could not locate initial surface in list-pane-surfaces output");return e[0]},O=t=>{let e=t.match(/workspace:\d+/);if(!e)throw new Error("cmux: could not locate workspace ref in workspace create output");return e[0]};var R=t=>{let{repoName:e,branch:r}=t,s=l(r),o=s?f(s):r;return`${e} ${o}`};var L=new Set(["ExitPromptError","AbortPromptError"]),k=t=>t instanceof Error&&L.has(t.name),st=t=>{if(k(t))return!0;let e=t?.cause;return k(e)};export{a,T as b,E as c,C as d,y as e,h as f,A as g,R as h,st as i};
4
+ //# sourceMappingURL=chunk-Q36WX5RP.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/integrations/cmux/open-dev-workspace.ts", "../src/integrations/cmux/canonicalize-cmux-title.ts", "../src/integrations/cmux/close-workspace-by-title.ts", "../src/integrations/cmux/list-workspace-titles.ts", "../src/integrations/cmux/open-workspace-with-layout.ts", "../src/integrations/cmux/workspace-title.ts", "../src/lib/errors/is-prompt-cancellation.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-title.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", "/** Matches a `v`-prefixed semver token (e.g. `v1.48.0`) anchored on shape. */\nconst V_SEMVER_TOKEN_RE = /\\bv(\\d+\\.\\d+\\.\\d+)\\b/g\n\n/**\n * Canonicalizes a cmux workspace title into a stable dedup/close key.\n *\n * cmux workspace titles are human display strings built by\n * `buildCmuxWorkspaceTitle`, so the value stored when a workspace is created can\n * drift from the value rebuilt later \u2014 across whitespace and across CLI versions\n * (an older build titled version releases `v1.48.0`; the current build titles\n * them `1.48.0`). Keying dedup or close on the raw title silently creates\n * duplicate / unclosable workspaces whenever that drift occurs.\n *\n * Canonicalization collapses the known drift axes so both sides round-trip to an\n * equal key:\n * - trims and collapses internal whitespace to single spaces;\n * - normalizes a `v`-prefixed semver token to its bare form\n * (`v1.48.0` \u2192 `1.48.0`), anchored on semver shape so named releases that\n * merely start with `v` (e.g. `vega-redesign`) are left untouched.\n *\n * Non-release fallback titles (which may contain `/`, e.g. `feature/foo`) are\n * preserved as-is apart from whitespace normalization.\n *\n * @example\n * canonicalizeCmuxTitle('hulyo-monorepo v1.48.0') // => 'hulyo-monorepo 1.48.0'\n * canonicalizeCmuxTitle('hulyo-monorepo 1.48.0') // => 'hulyo-monorepo 1.48.0'\n * canonicalizeCmuxTitle('hulyo-monorepo vega-redesign') // => 'hulyo-monorepo vega-redesign'\n */\nexport const canonicalizeCmuxTitle = (raw: string): string => {\n return raw.trim().replace(/\\s+/g, ' ').replace(V_SEMVER_TOKEN_RE, '$1')\n}\n", "import { $ } from 'zx'\n\nimport { logger } from 'src/lib/logger'\n\nimport { canonicalizeCmuxTitle } from './canonicalize-cmux-title'\n\n/**\n * Best-effort close of the cmux workspace whose title matches `title` (compared\n * via {@link canonicalizeCmuxTitle}, so a drifted stored title still resolves).\n * Silently no-ops if cmux isn't running, the workspace isn't found, or close fails.\n */\nexport const closeCmuxWorkspaceByTitle = async (title: string): Promise<void> => {\n try {\n const listOutput = (await $`cmux list-workspaces`.quiet()).stdout\n\n const ref = findWorkspaceRefByTitle(listOutput, title)\n\n if (!ref) {\n return\n }\n\n await $`cmux close-workspace --workspace ${ref}`.quiet()\n } catch (error) {\n logger.debug({ error, title }, 'cmux: skipped closing workspace')\n }\n}\n\n/**\n * Parses `cmux list-workspaces` output and returns the workspace ref whose\n * title matches `title`, or undefined if no match. Both sides are compared via\n * {@link canonicalizeCmuxTitle} so a workspace stored under a drifted title\n * (whitespace, or an older CLI's `v`-prefixed semver) is still found \u2014 keeping\n * close symmetric with the cmux open dedup in `worktrees-reload`.\n *\n * Each line looks like:\n * \" workspace:8 hulyo-monorepo 1.48.0\"\n * \"* workspace:6 obsidian-workspace [selected]\"\n */\nconst findWorkspaceRefByTitle = (output: string, title: string): string | undefined => {\n const target = canonicalizeCmuxTitle(title)\n\n for (const rawLine of output.split('\\n')) {\n // eslint-disable-next-line sonarjs/super-linear-regex, regexp/no-super-linear-backtracking\n const match = rawLine.match(/^[* ]\\s*(workspace:\\d+)\\s+(.+?)(?:\\s+\\[selected\\])?\\s*$/)\n\n if (!match) {\n continue\n }\n\n const ref = match[1]\n const lineTitle = match[2]?.trim() ?? ''\n\n if (canonicalizeCmuxTitle(lineTitle) === target) {\n return ref\n }\n }\n\n return undefined\n}\n", "import { $ } from 'zx'\n\nimport { logger } from 'src/lib/logger'\n\nimport { canonicalizeCmuxTitle } from './canonicalize-cmux-title'\n\n/**\n * Returns the set of **canonical** titles for all currently-open cmux\n * workspaces (see {@link canonicalizeCmuxTitle}). Keying on the canonical form\n * lets callers match a workspace even when its stored title drifted from the\n * title they rebuild (whitespace, or an older CLI's `v`-prefixed semver).\n * Returns an empty set if cmux isn't running, the call fails, or the output\n * can't be parsed \u2014 callers should treat \"empty\" as \"unknown, proceed as if\n * nothing is open\".\n *\n * Each line of `cmux list-workspaces` looks like:\n * \" workspace:8 hulyo-monorepo 1.48.0\"\n * \"* workspace:6 obsidian-workspace [selected]\"\n */\nexport const listCmuxWorkspaceTitles = async (): Promise<Set<string>> => {\n try {\n const output = (await $`cmux list-workspaces`.quiet()).stdout\n\n const titles = new Set<string>()\n\n for (const rawLine of output.split('\\n')) {\n // eslint-disable-next-line sonarjs/super-linear-regex, regexp/no-super-linear-backtracking\n const match = rawLine.match(/^[* ]\\s*workspace:\\d+\\s+(.+?)(?:\\s+\\[selected\\])?\\s*$/)\n\n if (!match) {\n continue\n }\n\n const title = match[1]?.trim()\n\n if (title) {\n titles.add(canonicalizeCmuxTitle(title))\n }\n }\n\n return titles\n } catch (error) {\n logger.debug({ error }, 'cmux: skipped listing workspace titles')\n\n return new Set()\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\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.\n */\nexport const openCmuxWorkspaceWithLayout = async (args: OpenCmuxWorkspaceArgs): Promise<void> => {\n const { cwd, title } = args\n\n const layout = resolveCmuxLayout(await getInfraKitConfig())\n\n const newWorkspaceOutput = (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\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 repoName: string\n branch: string\n}\n\n/**\n * Builds the cmux workspace title used by `worktrees-add` and looked up by\n * `worktrees-remove`. Release branches are rendered via their release-id\n * display label so the title reads e.g. `\"hulyo-monorepo 1.48.0\"` for\n * `\"release/v1.48.0\"` and `\"hulyo-monorepo checkout-redesign\"` for\n * `\"release/checkout-redesign\"`. Non-release branches (cmux titles them too)\n * fall back to the raw branch string.\n */\nexport const buildCmuxWorkspaceTitle = (args: BuildCmuxWorkspaceTitleArgs): string => {\n const { repoName, branch } = args\n\n const id = parseBranchName(branch)\n const label = id ? displayLabel(id) : branch\n\n return `${repoName} ${label}`\n}\n", "/**\n * Names of the `@inquirer/core` error classes thrown when an interactive prompt\n * ends without a value: `ExitPromptError` (user pressed Ctrl-C / Esc) and\n * `AbortPromptError` (the prompt was aborted via an `AbortSignal`). Both are\n * intentional cancellations, not failures.\n */\nconst CANCELLATION_ERROR_NAMES = new Set(['ExitPromptError', 'AbortPromptError'])\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\n * `@inquirer/*` prompt \u2014 i.e. pressing Ctrl-C / Esc in the branch picker or a\n * confirm step. Matched by `name` rather 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"],
5
+ "mappings": "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",
6
+ "names": ["process", "$", "isCmuxAvailable", "$", "openCmuxDevWorkspace", "args", "cwd", "title", "layout", "layoutJson", "output", "process", "parseWorkspaceRef", "closeCmuxDevWorkspace", "ref", "error", "logger", "match", "V_SEMVER_TOKEN_RE", "canonicalizeCmuxTitle", "raw", "$", "closeCmuxWorkspaceByTitle", "title", "listOutput", "$", "ref", "findWorkspaceRefByTitle", "error", "logger", "output", "target", "canonicalizeCmuxTitle", "rawLine", "match", "lineTitle", "$", "listCmuxWorkspaceTitles", "output", "$", "titles", "rawLine", "match", "title", "canonicalizeCmuxTitle", "error", "logger", "$", "openCmuxWorkspaceWithLayout", "args", "cwd", "title", "layout", "resolveCmuxLayout", "getInfraKitConfig", "newWorkspaceOutput", "$", "workspaceRef", "parseWorkspaceRef", "surfacesOutput", "leftTopRef", "parseFirstSurfaceRef", "output", "match", "buildCmuxWorkspaceTitle", "args", "repoName", "branch", "id", "parseBranchName", "label", "displayLabel", "CANCELLATION_ERROR_NAMES", "hasCancellationName", "value", "isPromptCancellation", "error", "cause"]
7
+ }
@@ -0,0 +1,5 @@
1
+ import{v as A}from"./chunk-4YU4TAGX.js";import m from"node:crypto";import _ from"node:fs";import T from"node:os";import i from"node:path";import c from"node:process";var p="env-load.sh",F="env-clear.sh",V="projects",g=7200,s="INFRA_KIT_SESSION",l="INFRA_KIT_ENV",O="INFRA_KIT_ENV_CONFIG",f="INFRA_KIT_ENV_PROJECT",u="INFRA_KIT_ENV_PROJECT_ROOT",C="INFRA_KIT_ENV_LOADED_AT",h="INFRA_KIT_ENV_AUTOLOADED",x="INFRA_KIT_ENV_CLEARED",R=/^([A-Z_]\w*)=/i,D=(t,n)=>{let o=n;for(let e=0;e<t.length;e++){if(!o&&t[e]==="\\"){e++;continue}t[e]==="'"&&(o=!o)}return o},S=t=>{if(!_.existsSync(t))return[];let n=_.readFileSync(t,"utf-8"),o=[],e=!1;for(let r of n.split(`
2
+ `)){if(!e){let E=R.exec(r);E&&o.push(E[1])}e=D(r,e)}return o},a=()=>{let t=c.env.XDG_CACHE_HOME,n=t&&t.length>0?t:i.join(T.homedir(),".cache");return i.join(n,"infra-kit")},K=()=>{let t=c.env[s];if(!t)throw new Error(`${s} is not set. Run \`infra-kit init\` then \`source ~/.zshrc\`.`);return i.join(a(),t)},I=t=>m.createHash("sha256").update(t,"utf8").digest("hex"),N=()=>i.join(a(),V),L=t=>i.join(N(),I(t)),d=(t,n,o)=>{let e=`${t}.tmp.${c.pid}`;_.writeFileSync(e,n,{mode:o});try{_.renameSync(e,t)}catch(r){throw _.rmSync(e,{force:!0}),r}},v="-worktrees";var y=()=>{let t="",n=[],o=!1;return{start(e){t=e,n=[],o=!1},setInteractive(){o=!0},addOption(e,r){n.push({flag:e,value:r})},print(){if(!o||n.length===0)return;let e=n.map(r=>typeof r.value=="boolean"?r.value?r.flag:"":Array.isArray(r.value)?`${r.flag} "${r.value.join(", ")}"`:`${r.flag} "${r.value}"`).filter(Boolean).join(" ");A.info(`\u{1F4DF} Equivalent command:
3
+ pnpm exec infra-kit ${t} ${e}
4
+ `)},reset(){t="",n=[],o=!1}}},P=y();export{p as a,F as b,g as c,s as d,l as e,O as f,f as g,u as h,C as i,h as j,x as k,S as l,K as m,N as n,L as o,d as p,v as q,P as r};
5
+ //# sourceMappingURL=chunk-V375VXOL.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/lib/constants/constants.ts", "../src/lib/command-echo/command-echo.ts"],
4
+ "sourcesContent": ["import crypto from 'node:crypto'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\n\nexport const ENV_LOAD_FILE = 'env-load.sh'\nexport const ENV_CLEAR_FILE = 'env-clear.sh'\n\n/** Subdir of the cache root holding the project-scoped WARM caches (one dir per\n * project, keyed by {@link warmCacheKey}). Kept in sync with the zsh block in\n * init.ts, which reads `$cache_root/projects/$key/env-load.sh`. */\nexport const WARM_CACHE_SUBDIR = 'projects'\n\n/**\n * Default warm-cache TTL in seconds (2h). Governs BOTH the node-side eviction\n * sweep and the zsh-side source gate (`_INFRA_KIT_WARM_TTL` in the shell block);\n * the two MUST stay equal, so this constant is the single source of truth and the\n * shell default is emitted from it. Short by design: a warm file older than this\n * is never sourced (bounds a revoked/rotated secret served at prompt-0) and is\n * deleted on the next write. The background refresh lands in ~1-2s regardless.\n */\nexport const DEFAULT_WARM_TTL_SECONDS = 2 * 60 * 60\n\nexport const INFRA_KIT_SESSION_VAR = 'INFRA_KIT_SESSION'\n/**\n * The active environment/config NAME (e.g. `dev`, `arthur`), exported plainly so\n * non-shell consumers can read it from `process.env`. Notably `infra-kit/vite`'s\n * `infraKitDev()` reads this to interpolate the `<env>` placeholder in cloud proxy\n * targets. Mirrors {@link INFRA_KIT_ENV_CONFIG_VAR} (same value) but is the stable,\n * purpose-named handle for tooling rather than the shell session-metadata var.\n */\nexport const INFRA_KIT_ENV_VAR = 'INFRA_KIT_ENV'\nexport const INFRA_KIT_ENV_CONFIG_VAR = 'INFRA_KIT_ENV_CONFIG'\nexport const INFRA_KIT_ENV_PROJECT_VAR = 'INFRA_KIT_ENV_PROJECT'\n/**\n * Absolute project root (git top-level) the loaded env belongs to. Lets the shell\n * startup gate tell a same-project subshell (skip) from a NEW project (load), so a\n * different project's secrets are never silently kept after `cd`/new shell.\n */\nexport const INFRA_KIT_ENV_PROJECT_ROOT_VAR = 'INFRA_KIT_ENV_PROJECT_ROOT'\nexport const INFRA_KIT_ENV_LOADED_AT_VAR = 'INFRA_KIT_ENV_LOADED_AT'\n/**\n * Marker exported into env-load.sh ONLY when the load was triggered automatically\n * (see lib/env-autoload). Its presence is the sole signal distinguishing an\n * auto-loaded env from a deliberate manual `env-load`, so auto-load never\n * clobbers a manual choice. A manual load unsets it.\n */\nexport const INFRA_KIT_ENV_AUTOLOADED_VAR = 'INFRA_KIT_ENV_AUTOLOADED'\n/**\n * Suppression sentinel exported by env-clear. While set in a shell, cli-invocation\n * auto-load stays silent (a deliberate clear must not be immediately re-loaded).\n * Lifted by a manual `env-load` or a new shell.\n */\nexport const INFRA_KIT_ENV_CLEARED_VAR = 'INFRA_KIT_ENV_CLEARED'\n\n/**\n * Matches a line of the form `KEY=...` where KEY is an env-var identifier\n * (letter or underscore, then word chars). Capture group 1 is the name. Shared\n * between env-load (validation, var counting) and parseVarNamesFromEnvFile.\n */\nexport const ENV_VAR_LINE_PATTERN = /^([A-Z_]\\w*)=/i\n\n/**\n * Track whether a physical line leaves us inside an open single-quoted value,\n * mirroring how `shellSingleQuote` emits values (`'\u2026'`, with literal quotes as\n * `'\\''`). Outside a quote a backslash escapes the next char; inside a quote a\n * `'` closes it. Lets the parser skip the continuation lines of a multiline value.\n */\nconst advanceSingleQuoteState = (line: string, startInQuote: boolean): boolean => {\n let inQuote = startInQuote\n\n for (let i = 0; i < line.length; i++) {\n if (!inQuote && line[i] === '\\\\') {\n i++\n continue\n }\n\n if (line[i] === \"'\") {\n inQuote = !inQuote\n }\n }\n\n return inQuote\n}\n\nexport const parseVarNamesFromEnvFile = (filePath: string): string[] => {\n if (!fs.existsSync(filePath)) return []\n\n const content = fs.readFileSync(filePath, 'utf-8')\n const names: string[] = []\n let inQuote = false\n\n for (const line of content.split('\\n')) {\n // Only a line that starts OUTSIDE a quoted value can be a real assignment;\n // continuation lines of a multiline secret value are skipped.\n if (!inQuote) {\n const match = ENV_VAR_LINE_PATTERN.exec(line)\n\n if (match) {\n names.push(match[1]!)\n }\n }\n\n inQuote = advanceSingleQuoteState(line, inQuote)\n }\n\n return names\n}\n\n/**\n * Root cache dir for infra-kit across all sessions. Resolved from\n * $XDG_CACHE_HOME when set, falling back to ~/.cache/infra-kit. Keep in sync\n * with the shell block emitted by `infra-kit init` (src/commands/init/init.ts).\n */\nexport const getCacheRoot = (): string => {\n const xdg = process.env.XDG_CACHE_HOME\n const base = xdg && xdg.length > 0 ? xdg : path.join(os.homedir(), '.cache')\n\n return path.join(base, 'infra-kit')\n}\n\nexport const getSessionCacheDir = (): string => {\n const session = process.env[INFRA_KIT_SESSION_VAR]\n\n if (!session) {\n throw new Error(`${INFRA_KIT_SESSION_VAR} is not set. Run \\`infra-kit init\\` then \\`source ~/.zshrc\\`.`)\n }\n\n return path.join(getCacheRoot(), session)\n}\n\n/**\n * The warm-cache key for a project: the hex SHA-256 of its CANONICAL (realpath'd)\n * directory. This MUST be byte-identical to the zsh block's\n * `printf %s \"$canon\" | shasum -a 256 | cut -c1-64` \u2014 same input string (no\n * trailing newline), same digest, full 64-hex output \u2014 or a warm file written by\n * node is never found by the shell. The shell passes the already-canonicalized\n * dir via `--project-dir`, so node hashes that VERBATIM (it does not re-resolve).\n */\nexport const warmCacheKey = (canonicalProjectDir: string): string => {\n return crypto.createHash('sha256').update(canonicalProjectDir, 'utf8').digest('hex')\n}\n\n/** Root holding all project-scoped warm caches: `$cacheRoot/projects`. */\nexport const getWarmCacheRoot = (): string => {\n return path.join(getCacheRoot(), WARM_CACHE_SUBDIR)\n}\n\n/** This project's warm-cache dir: `$cacheRoot/projects/<warmCacheKey>`. */\nexport const getProjectWarmCacheDir = (canonicalProjectDir: string): string => {\n return path.join(getWarmCacheRoot(), warmCacheKey(canonicalProjectDir))\n}\n\n/**\n * Write content atomically: write to a pid-suffixed temp file in the same\n * directory, then rename. fs.renameSync is atomic on a single filesystem, so\n * concurrent writers can't produce a half-written secret file.\n */\nexport const atomicWriteFileSync = (filePath: string, content: string, mode: number): void => {\n const tmpPath = `${filePath}.tmp.${process.pid}`\n\n fs.writeFileSync(tmpPath, content, { mode })\n\n try {\n fs.renameSync(tmpPath, filePath)\n } catch (error) {\n fs.rmSync(tmpPath, { force: true })\n throw error\n }\n}\n\nexport const WORKTREES_DIR_SUFFIX = '-worktrees'\n// eslint-disable-next-line sonarjs/publicly-writable-directories\nexport const LOG_FILE_PATH = '/tmp/mcp-infra-kit.log'\n", "import { logger } from 'src/lib/logger'\n\ninterface CommandOption {\n flag: string\n value: string | string[] | boolean\n}\n\nconst createCommandEcho = () => {\n let commandName = ''\n let options: CommandOption[] = []\n let isInteractive = false\n\n return {\n /**\n * Initialize command echo for a new command\n */\n start(name: string): void {\n commandName = name\n options = []\n isInteractive = false\n },\n\n /**\n * Mark that the command had interactive input (prompts)\n * Call this once when ANY prompt happens\n */\n setInteractive(): void {\n isInteractive = true\n },\n\n /**\n * Track an option selection\n * @param flag The CLI flag (e.g., \"--versions\")\n * @param value The selected value\n */\n addOption(flag: string, value: string | string[] | boolean): void {\n options.push({ flag, value })\n },\n\n /**\n * Print the equivalent CLI command if there was interactive input\n */\n print(): void {\n if (!isInteractive || options.length === 0) {\n return\n }\n\n const formattedOptions = options\n .map((opt) => {\n if (typeof opt.value === 'boolean') {\n return opt.value ? opt.flag : ''\n }\n\n if (Array.isArray(opt.value)) {\n return `${opt.flag} \"${opt.value.join(', ')}\"`\n }\n\n return `${opt.flag} \"${opt.value}\"`\n })\n .filter(Boolean)\n .join(' ')\n\n logger.info(`\uD83D\uDCDF Equivalent command: \\npnpm exec infra-kit ${commandName} ${formattedOptions}\\n`)\n },\n\n /**\n * Reset state (useful for testing)\n */\n reset(): void {\n commandName = ''\n options = []\n isInteractive = false\n },\n }\n}\n\n// Singleton instance (same pattern as logger)\nexport const commandEcho = createCommandEcho()\n"],
5
+ "mappings": "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",
6
+ "names": ["crypto", "fs", "os", "path", "process", "ENV_LOAD_FILE", "ENV_CLEAR_FILE", "WARM_CACHE_SUBDIR", "DEFAULT_WARM_TTL_SECONDS", "INFRA_KIT_SESSION_VAR", "INFRA_KIT_ENV_VAR", "INFRA_KIT_ENV_CONFIG_VAR", "INFRA_KIT_ENV_PROJECT_VAR", "INFRA_KIT_ENV_PROJECT_ROOT_VAR", "INFRA_KIT_ENV_LOADED_AT_VAR", "INFRA_KIT_ENV_AUTOLOADED_VAR", "INFRA_KIT_ENV_CLEARED_VAR", "ENV_VAR_LINE_PATTERN", "advanceSingleQuoteState", "line", "startInQuote", "inQuote", "i", "parseVarNamesFromEnvFile", "filePath", "content", "names", "match", "getCacheRoot", "xdg", "base", "getSessionCacheDir", "session", "warmCacheKey", "canonicalProjectDir", "getWarmCacheRoot", "getProjectWarmCacheDir", "atomicWriteFileSync", "mode", "tmpPath", "error", "WORKTREES_DIR_SUFFIX", "createCommandEcho", "commandName", "options", "isInteractive", "name", "flag", "value", "formattedOptions", "opt", "logger", "commandEcho"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{a as x}from"./chunk-2PZRQHWF.js";import{Buffer as h}from"node:buffer";import{execFileSync as R}from"node:child_process";import f from"node:fs";import w from"node:net";import l from"node:path";import m from"node:process";import{pathToFileURL as b}from"node:url";import{z as u}from"zod";import{z as a}from"zod";var k=a.strictObject({requiredScripts:a.array(a.string().min(1)).optional(),requiredFiles:a.array(a.string().min(1)).optional(),turbo:a.strictObject({requiredTasks:a.array(a.string().min(1)).optional()}).optional(),dev:a.strictObject({proxy:a.strictObject({templates:a.strictObject({local:a.string().min(1),cloud:a.string().min(1)}),routes:a.record(a.string().min(1),a.strictObject({packageName:a.string().min(1),from:a.array(a.enum(["local","cloud"])).min(1),default:a.enum(["local","cloud"]).optional()}).refine(e=>e.from.length<=1||e.default!==void 0,{message:"default is required when `from` has more than one source"}).refine(e=>e.default===void 0||e.from.includes(e.default),{message:"default must be listed in `from`"}))}).optional()}).optional()});var S="INFRA_KIT_ENV",v="infra-kit.config.ts",A=l.join(".infra-kit","dev-context"),K=l.join(".infra-kit","dev-context.json"),N=u.object({package:u.string(),port:u.number(),pid:u.number().optional(),writtenAt:u.number().optional(),release:u.string().optional()}),E=(e,t)=>{let r=t?.username??e.E2E__BASIC_AUTH_USERNAME,o=t?.password??e.E2E__BASIC_AUTH_PASSWORD;return!r||!o?void 0:`Basic ${h.from(`${r}:${o}`).toString("base64")}`};var P=(e,t)=>e.replaceAll("<release>",t.release).replaceAll("<packageName>",t.packageName).replaceAll("<env>",t.env),D=(e,t)=>{let r=e.default??e.from[0];return e.from.includes("local")&&t.has(e.packageName)?"local":r},O=({routePath:e,route:t,templates:r,localSet:o,env:n,getRelease:i,localInfo:c})=>{if(D(t,o)==="local"){let d=c?.get(t.packageName)?.release??i();return{target:P(r.local,{release:d,packageName:t.packageName,env:n??""}),changeOrigin:!0}}if(!n)throw new Error(`infra-kit/vite: proxy route "${e}" resolves to a cloud backend but ${S} is not set. Source an environment from Doppler first (e.g. \`infra-kit env-load dev\`).`);return{target:P(r.cloud,{release:"",packageName:t.packageName,env:n}),changeOrigin:!0,secure:!1,cookieDomainRewrite:"localhost"}},_=({proxy:e,localSet:t,env:r,getRelease:o,authHeader:n,localInfo:i})=>{let c={},s=n?{Authorization:n}:void 0;for(let[g,d]of Object.entries(e.routes)){let p=O({routePath:g,route:d,templates:e.templates,localSet:t,env:r,getRelease:o,localInfo:i});c[g]=s?{...p,headers:s}:p}return c},j=e=>{let t;return()=>(t??={value:e()},t.value)},T=async e=>{let t=l.join(e,v);if(!f.existsSync(t))return;let r=f.statSync(t),i=(await import(`${b(t).href}?mtime=${Number(r.mtimeMs)}`)).default;if(i===void 0)return;let c=typeof i=="function"?await i():i,s=k.safeParse(c);if(!s.success)throw new Error(`infra-kit/vite: invalid ${v} at ${t}: ${u.prettifyError(s.error)}`);return s.data.dev},C=e=>{if(Array.isArray(e))return e.filter(t=>typeof t=="string");if(e!==null&&typeof e=="object"){let t=e.packages??e.localPackages;if(Array.isArray(t))return t.filter(r=>typeof r=="string")}return[]},I=(e,t)=>{let r=l.resolve(e);for(;;){let o=l.join(r,t);if(f.existsSync(o))return o;let n=l.dirname(r);if(n===r)return;r=n}},y=()=>({packages:new Set,info:new Map}),F=e=>{let t;try{t=f.readdirSync(e)}catch{return y()}let r=new Set,o=new Map;for(let n of t)if(n.endsWith(".json"))try{let i=N.safeParse(JSON.parse(f.readFileSync(l.join(e,n),"utf-8")));if(!i.success)continue;r.add(i.data.package),o.set(i.data.package,{port:i.data.port,release:i.data.release})}catch{continue}return{packages:r,info:o}},L=e=>{let t=I(e,A);if(t)return F(t);let r=I(e,K);if(!r)return y();try{return{packages:new Set(C(JSON.parse(f.readFileSync(r,"utf-8")))),info:new Map}}catch{return y()}};var V=e=>R("git",["rev-parse","--abbrev-ref","HEAD"],{cwd:e,encoding:"utf-8"}).trim(),U=()=>new Promise((e,t)=>{let r=w.createServer();r.unref(),r.on("error",t),r.listen(0,"127.0.0.1",()=>{let o=r.address(),n=typeof o=="object"&&o!==null?o.port:0;r.close(()=>e(n))})}),$="INFRA_KIT_UI_PORTS",B=e=>{try{let t=JSON.parse(f.readFileSync(l.join(e,"package.json"),"utf-8"));return typeof t.name=="string"?t.name:null}catch{return null}},M=e=>{let t=m.env[$];if(t==null||t==="")return null;let r=B(e);if(r==null)return null;try{let n=JSON.parse(t)[r];return typeof n=="number"?n:null}catch{return null}},H=async(e={})=>{let t=e.cwd??m.cwd();if(e.command==="build")return{proxy:{}};let r=e.port==null?M(t):null,o=e.port??r??await U(),n=r!=null,i=await T(t);if(!i?.proxy)return{port:o,...n?{strictPort:n}:{},proxy:{}};let{packages:c,info:s}=L(t),g=m.env[S],d=E(m.env,e.basicAuth),p=j(()=>x(V(t)));return{port:o,...n?{strictPort:n}:{},proxy:_({proxy:i.proxy,localSet:c,env:g,getRelease:p,authHeader:d,localInfo:s})}},ne=async(e={})=>(await H(e)).proxy;export{k as a,_ as b,T as c,H as d,ne as e};
2
+ //# sourceMappingURL=chunk-YGCVOVNX.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/lib/vite/vite.ts", "../src/lib/package-config/package-config-schema.ts"],
4
- "sourcesContent": ["import { Buffer } from 'node:buffer'\nimport { execFileSync } from 'node:child_process'\nimport fs from 'node:fs'\nimport net from 'node:net'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { pathToFileURL } from 'node:url'\nimport { z } from 'zod'\n\nimport type {\n InfraKitDev,\n InfraKitDevProxy,\n InfraKitDevProxyRoute,\n InfraKitDevProxySource,\n} from '../package-config/package-config'\nimport { packageConfigSchema } from '../package-config/package-config-schema'\nimport { slugifyRelease } from '../release-slug/release-slug'\n\n/**\n * Env-name handle read at vite-config time to fill the `<env>` placeholder in a\n * cloud proxy target. Inlined (not imported from `src/lib/constants`) to keep the\n * `infra-kit/vite` bundle's import graph tiny \u2014 see the module header. Kept\n * byte-identical to `INFRA_KIT_ENV_VAR` in `src/lib/constants/constants.ts`.\n */\nconst INFRA_KIT_ENV = 'INFRA_KIT_ENV'\n\n/** Per-package config filename, mirrored from the CLI's `PACKAGE_CONFIG_FILE`. */\nconst PACKAGE_CONFIG_FILE = 'infra-kit.config.ts'\n\n/**\n * Repo-relative dev-context fragment DIRECTORY, searched upward from cwd. Each\n * runner (single-process or cmux pane) writes its OWN `<app>.json` fragment here\n * recording its real bound port + release; the helper merges them (see\n * {@link readLocalContext}). This is the current source of truth.\n */\nconst DEV_CONTEXT_DIR = path.join('.infra-kit', 'dev-context')\n\n/**\n * Legacy single-file dev-context manifest, searched upward from cwd. Read only as\n * a transitional back-compat path when {@link DEV_CONTEXT_DIR} is absent (the\n * directory wins per-package when both exist).\n */\nconst DEV_CONTEXT_FILE = path.join('.infra-kit', 'dev-context.json')\n\n/**\n * One `.infra-kit/dev-context/<app>.json` fragment. `package` + `port` are the\n * load-bearing fields the helper reads (`package` \u2192 localSet, `port` \u2192 the real\n * bound port for a future `127.0.0.1:<port>` mode / Layer B route); `release` lets\n * the helper prefer the runner-recorded slug over its own git derivation.\n * `pid`/`writtenAt` are staleness metadata (unused on the read path here). Kept\n * lenient (not `.strict()`) so extra writer fields never reject a valid fragment.\n */\nconst devContextFragmentSchema = z.object({\n package: z.string(),\n port: z.number(),\n pid: z.number().optional(),\n writtenAt: z.number().optional(),\n release: z.string().optional(),\n})\n\n/**\n * A single Vite `server.proxy` entry. `changeOrigin` is always set; the\n * cloud-only pair (`secure: false` + `cookieDomainRewrite: 'localhost'`) makes a\n * local FE talk to an HTTPS cloud BE without cert/cookie-domain breakage.\n * `headers` is present only when HTTP Basic Auth is injected (see\n * {@link buildBasicAuthHeader}) \u2014 it carries the `Authorization` header applied\n * uniformly to every route so upstream environments behind auth (e2e/staging)\n * stay reachable.\n */\nexport interface InfraKitViteProxyEntry {\n target: string\n changeOrigin: true\n secure?: false\n cookieDomainRewrite?: 'localhost'\n headers?: Record<string, string>\n}\n\n/** A Vite `server.proxy`-shaped map: path-prefix \u2192 proxy entry. */\nexport type InfraKitViteProxy = Record<string, InfraKitViteProxyEntry>\n\n/** Explicit HTTP Basic Auth credentials, overriding the `E2E__BASIC_AUTH_*` env vars. */\nexport interface InfraKitBasicAuth {\n username: string\n password: string\n}\n\nexport interface InfraKitDevOptions {\n /** Package dir whose `infra-kit.config.ts` is loaded. Defaults to `process.cwd()`. */\n cwd?: string\n /**\n * Explicit HTTP Basic Auth credentials injected into every proxy route as an\n * `Authorization` header. Takes precedence over the `E2E__BASIC_AUTH_USERNAME`\n * / `E2E__BASIC_AUTH_PASSWORD` env vars. Omit to use the env-based default.\n */\n basicAuth?: InfraKitBasicAuth\n /**\n * Vite's `command`. Pass it (`defineConfig(async ({ command }) => ({ server: await infraKitDev({ command }) }))`)\n * so `build` is a no-op: `server` is irrelevant to a build and proxy resolution would otherwise\n * fail-fast on a cloud route with no sourced env. Omit (or `'serve'`) for the dev-server config.\n */\n command?: 'build' | 'serve'\n /**\n * Explicit dev-server port. Omit for a **per-worktree dynamic** free port (a fresh OS-assigned\n * port) so N simultaneous git worktrees never collide on Vite's default `5173`; Vite prints the\n * chosen URL. Pass a fixed number only when an external contract pins the port.\n */\n port?: number\n}\n\n/**\n * Build the `Authorization: Basic <base64(user:pass)>` header value. Credentials\n * come from `override` when given, else from `E2E__BASIC_AUTH_USERNAME` /\n * `E2E__BASIC_AUTH_PASSWORD` (NOTE: double underscore). Returns `undefined` when\n * either half is missing, so callers add no `headers` key at all.\n */\nconst buildBasicAuthHeader = (env: NodeJS.ProcessEnv, override?: InfraKitBasicAuth): string | undefined => {\n const username = override?.username ?? env.E2E__BASIC_AUTH_USERNAME\n const password = override?.password ?? env.E2E__BASIC_AUTH_PASSWORD\n\n if (!username || !password) return undefined\n\n const encoded = Buffer.from(`${username}:${password}`).toString('base64')\n\n return `Basic ${encoded}`\n}\n\n/**\n * Re-exported from the shared, dependency-light `lib/release-slug` module so the\n * published `infra-kit/vite` surface (`entry/vite.ts:7`) is unchanged while the\n * dev-server's fragment writer derives `<release>` from the SAME implementation\n * (no slug drift). See {@link slugifyRelease}.\n */\nexport { slugifyRelease }\n\n/** Fill the `<release>`/`<packageName>`/`<env>` placeholders in a URL template. */\nconst interpolate = (template: string, values: { release: string; packageName: string; env: string }): string => {\n return template\n .replaceAll('<release>', values.release)\n .replaceAll('<packageName>', values.packageName)\n .replaceAll('<env>', values.env)\n}\n\n/**\n * Pick the effective source for a route: `local` when the route lists `local` as\n * a capability AND its packageName is in the local dev set; otherwise the fallback\n * \u2014 the declared `default` for a multi-source route, or the sole `from` entry for\n * a single-source one. Always resolves (the schema guarantees a usable fallback).\n */\nconst pickSource = (route: InfraKitDevProxyRoute, localSet: ReadonlySet<string>): InfraKitDevProxySource => {\n // `from` is guaranteed non-empty by the schema, so `from[0]` is always present.\n const fallback = route.default ?? route.from[0]!\n\n return route.from.includes('local') && localSet.has(route.packageName) ? 'local' : fallback\n}\n\ninterface ResolveRouteArgs {\n routePath: string\n route: InfraKitDevProxyRoute\n templates: InfraKitDevProxy['templates']\n localSet: ReadonlySet<string>\n env: string | undefined\n getRelease: () => string\n /** Per-package runtime data (recorded release/port) from the dev-context merge. */\n localInfo?: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/** Resolve one route into a Vite proxy entry (or throw an actionable error). */\nconst resolveRoute = ({\n routePath,\n route,\n templates,\n localSet,\n env,\n getRelease,\n localInfo,\n}: ResolveRouteArgs): InfraKitViteProxyEntry => {\n const source = pickSource(route, localSet)\n\n if (source === 'local') {\n // Prefer THIS package's runner-recorded release slug (R4) over the single\n // global git derivation, so cross-branch FE/BE pairings don't drift the\n // emitted `<release>` from the segment the runner aliased. Fall back to the\n // global git slug only when the fragment carried no release.\n const release = localInfo?.get(route.packageName)?.release ?? getRelease()\n\n const target = interpolate(templates.local, {\n release,\n packageName: route.packageName,\n env: env ?? '',\n })\n\n return { target, changeOrigin: true }\n }\n\n if (!env) {\n throw new Error(\n `infra-kit/vite: proxy route \"${routePath}\" resolves to a cloud backend but ${INFRA_KIT_ENV} is not set. Source an environment from Doppler first (e.g. \\`infra-kit env-load dev\\`).`,\n )\n }\n\n const target = interpolate(templates.cloud, { release: '', packageName: route.packageName, env })\n\n return { target, changeOrigin: true, secure: false, cookieDomainRewrite: 'localhost' }\n}\n\ninterface ResolveProxyArgs {\n proxy: InfraKitDevProxy\n localSet: ReadonlySet<string>\n env: string | undefined\n getRelease: () => string\n /** Pre-computed `Authorization` header value applied uniformly to every route. */\n authHeader?: string\n /** Per-package runtime data (recorded release/port) from the dev-context merge. */\n localInfo?: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/**\n * Pure resolver: turn a `dev.proxy` config + resolved inputs (local set, env,\n * lazy release) into a Vite `server.proxy` map. Side-effect-free so the\n * resolution shape is fully unit-testable. When `authHeader` is set, every route\n * entry gets a matching `headers.Authorization`; otherwise no `headers` key.\n */\nexport const resolveProxyConfig = ({\n proxy,\n localSet,\n env,\n getRelease,\n authHeader,\n localInfo,\n}: ResolveProxyArgs): InfraKitViteProxy => {\n const result: InfraKitViteProxy = {}\n const headers = authHeader ? { Authorization: authHeader } : undefined\n\n for (const [routePath, route] of Object.entries(proxy.routes)) {\n const entry = resolveRoute({ routePath, route, templates: proxy.templates, localSet, env, getRelease, localInfo })\n\n result[routePath] = headers ? { ...entry, headers } : entry\n }\n\n return result\n}\n\n/** Memoize a zero-arg thunk so `<release>` git resolution runs at most once. */\nconst once = <T>(fn: () => T): (() => T) => {\n let cached: { value: T } | undefined\n\n return () => {\n cached ??= { value: fn() }\n\n return cached.value\n }\n}\n\n/**\n * Load a package's `infra-kit.config.ts` and return its `dev` block, or\n * `undefined` when the config or the `dev` key is absent. The `.ts` config is\n * evaluated via Node's native type stripping (Node >= 24) \u2014 the same mechanism\n * the CLI's config loader uses. Cache-busted by mtime so repeated dev-server\n * reloads pick up edits.\n */\nexport const loadDev = async (cwd: string): Promise<InfraKitDev | undefined> => {\n const configPath = path.join(cwd, PACKAGE_CONFIG_FILE)\n\n if (!fs.existsSync(configPath)) return undefined\n\n const stat = fs.statSync(configPath)\n const moduleUrl = `${pathToFileURL(configPath).href}?mtime=${Number(stat.mtimeMs)}`\n\n const imported = (await import(moduleUrl)) as { default?: unknown }\n const rawExport = imported.default\n\n if (rawExport === undefined) return undefined\n\n const resolved = typeof rawExport === 'function' ? await (rawExport as () => unknown)() : rawExport\n\n const parsed = packageConfigSchema.safeParse(resolved)\n\n if (!parsed.success) {\n throw new Error(`infra-kit/vite: invalid ${PACKAGE_CONFIG_FILE} at ${configPath}: ${z.prettifyError(parsed.error)}`)\n }\n\n return parsed.data.dev\n}\n\n/** Coerce a parsed dev-context.json into the set of locally-running package names. */\nconst extractPackages = (parsed: unknown): string[] => {\n if (Array.isArray(parsed)) {\n return parsed.filter((v): v is string => {\n return typeof v === 'string'\n })\n }\n\n if (parsed !== null && typeof parsed === 'object') {\n const candidate = (parsed as Record<string, unknown>).packages ?? (parsed as Record<string, unknown>).localPackages\n\n if (Array.isArray(candidate)) {\n return candidate.filter((v): v is string => {\n return typeof v === 'string'\n })\n }\n }\n\n return []\n}\n\n/** Search upward from `start` for `relative`, returning the first hit or undefined. */\nconst findUp = (start: string, relative: string): string | undefined => {\n let dir = path.resolve(start)\n\n for (;;) {\n const candidate = path.join(dir, relative)\n\n if (fs.existsSync(candidate)) return candidate\n\n const parent = path.dirname(dir)\n\n if (parent === dir) return undefined\n\n dir = parent\n }\n}\n\n/** Per-package runtime data merged from the dev-context fragment directory. */\nexport interface LocalPackageInfo {\n /** The real bound port the runner recorded (available for a future direct mode). */\n port: number\n /** The runner-recorded release slug, preferred over the helper's git derivation. */\n release?: string\n}\n\n/**\n * The locally-running package set plus a `package \u2192 { port, release }` map merged\n * from the dev-context fragments. `packages` feeds `pickSource`; `info` lets a\n * `local` route emit the per-package recorded release (see {@link resolveRoute}).\n */\nexport interface LocalContext {\n packages: ReadonlySet<string>\n info: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/** An empty {@link LocalContext} (frontend-only / dev-context absent). */\nconst emptyLocalContext = (): LocalContext => {\n return { packages: new Set(), info: new Map() }\n}\n\n/**\n * Merge every `<app>.json` fragment in the dev-context directory into a\n * {@link LocalContext}. Two DISTINCT failure branches (do NOT collapse them into\n * one directory-wide catch):\n * - `readdir`-ENOENT (directory vanished mid-race) \u2192 empty context, matching the\n * dir-absent back-compat behaviour.\n * - a single corrupt/truncated/invalid `<app>.json` \u2192 that ONE fragment is\n * SKIPPED (per-fragment `safeParse` isolation); the rest still merge, so one bad\n * fragment never collapses the whole localSet to empty (which would silently\n * drop a started package to cloud).\n */\nconst readFragmentDir = (dir: string): LocalContext => {\n let entries: string[]\n\n try {\n entries = fs.readdirSync(dir)\n } catch {\n return emptyLocalContext()\n }\n\n const packages = new Set<string>()\n const info = new Map<string, LocalPackageInfo>()\n\n for (const name of entries) {\n if (!name.endsWith('.json')) continue\n\n try {\n const parsed = devContextFragmentSchema.safeParse(JSON.parse(fs.readFileSync(path.join(dir, name), 'utf-8')))\n\n if (!parsed.success) continue\n\n packages.add(parsed.data.package)\n info.set(parsed.data.package, { port: parsed.data.port, release: parsed.data.release })\n } catch {\n // Corrupt/truncated fragment (JSON.parse / read failure): skip ONLY this one.\n continue\n }\n }\n\n return { packages, info }\n}\n\n/**\n * Read the locally-running package context, searched upward from `cwd`. Prefers\n * the `.infra-kit/dev-context/` fragment DIRECTORY (merged via\n * {@link readFragmentDir}); when it is absent, falls back to the legacy single\n * `.infra-kit/dev-context.json` file (read as before \u2014 no per-package release);\n * when neither exists \u2192 empty context (frontend-only, every route resolves cloud).\n */\nexport const readLocalContext = (cwd: string): LocalContext => {\n const dir = findUp(cwd, DEV_CONTEXT_DIR)\n\n if (dir) return readFragmentDir(dir)\n\n const legacy = findUp(cwd, DEV_CONTEXT_FILE)\n\n if (!legacy) return emptyLocalContext()\n\n try {\n return { packages: new Set(extractPackages(JSON.parse(fs.readFileSync(legacy, 'utf-8')))), info: new Map() }\n } catch {\n return emptyLocalContext()\n }\n}\n\n/**\n * Read the set of locally-running packages (searched upward from `cwd`). Thin\n * wrapper over {@link readLocalContext} preserving the historical `Set`-returning\n * contract. Absent dev-context \u2192 empty set (frontend-only cloud resolution).\n */\nexport const readLocalSet = (cwd: string): ReadonlySet<string> => {\n return readLocalContext(cwd).packages\n}\n\n/** Current git branch of `cwd` (raw, un-slugified). */\nconst readGitBranch = (cwd: string): string => {\n // eslint-disable-next-line sonarjs/no-os-command-from-path\n return execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, encoding: 'utf-8' }).trim()\n}\n\n/**\n * An OS-assigned free TCP port on 127.0.0.1 \u2014 the per-worktree dev-server port (mirrors the\n * backend's `listen(0)`). This probes then releases the port, so there is a small TOCTOU window\n * before Vite binds it; Vite's default `strictPort: false` absorbs a lost race by stepping to the\n * next free port. Only a consumer that sets `strictPort: true` would hard-fail on the rare collision.\n */\nconst getFreePort = (): Promise<number> => {\n return new Promise((resolve, reject) => {\n const srv = net.createServer()\n\n srv.unref()\n srv.on('error', reject)\n srv.listen(0, '127.0.0.1', () => {\n const address = srv.address()\n const port = typeof address === 'object' && address !== null ? address.port : 0\n\n srv.close(() => {\n return resolve(port)\n })\n })\n })\n}\n\n/**\n * Resolve a package's `dev` block into a ready-made Vite `server` config: a per-worktree dev-server\n * `port` plus the `proxy` map. Loads the package's `infra-kit.config.ts`, merges the local dev set\n * from the `.infra-kit/dev-context/` fragment directory, and interpolates the local/cloud templates.\n * `<env>` comes from `INFRA_KIT_ENV`; `<release>` from each package's runner-recorded fragment when\n * present, else the slugified git branch (computed lazily, only when a local route needs it).\n *\n * `port` defaults to a fresh OS-assigned free port so simultaneous git worktrees never collide on\n * Vite's `5173` (override via `options.port`). Pass `command` so `build` is a no-op (empty proxy, no\n * port) \u2014 a build ignores `server` and proxy resolution would otherwise fail-fast on a cloud route\n * with no sourced env.\n *\n * @example\n * // vite.config.ts \u2014 the whole `server` field is the helper's output\n * import { infraKitDev } from 'infra-kit/vite'\n * export default defineConfig(async ({ command }) => ({ server: await infraKitDev({ command }) }))\n */\nexport const infraKitDev = async (\n options: InfraKitDevOptions = {},\n): Promise<{ port?: number; proxy: InfraKitViteProxy }> => {\n const cwd = options.cwd ?? process.cwd()\n\n // A build ignores `server`; skip the port + proxy work (proxy resolution would fail-fast on a\n // cloud route with no sourced env).\n if (options.command === 'build') return { proxy: {} }\n\n const port = options.port ?? (await getFreePort())\n const dev = await loadDev(cwd)\n\n if (!dev?.proxy) return { port, proxy: {} }\n\n const { packages: localSet, info: localInfo } = readLocalContext(cwd)\n const env = process.env[INFRA_KIT_ENV]\n const authHeader = buildBasicAuthHeader(process.env, options.basicAuth)\n const getRelease = once(() => {\n return slugifyRelease(readGitBranch(cwd))\n })\n\n return { port, proxy: resolveProxyConfig({ proxy: dev.proxy, localSet, env, getRelease, authHeader, localInfo }) }\n}\n\n/**\n * Convenience wrapper returning just the proxy map (for spreading into\n * `server.proxy` directly). Equivalent to `(await infraKitDev(options)).proxy`.\n */\nexport const infraKitProxy = async (options: InfraKitDevOptions = {}): Promise<InfraKitViteProxy> => {\n return (await infraKitDev(options)).proxy\n}\n", "import { z } from 'zod'\n\n/**\n * Schema for the resolved (post-factory) package config object. `strictObject`\n * rejects unknown keys so typos in `infra-kit.config.ts` surface as validation\n * errors instead of being silently ignored.\n *\n * Kept in its own module \u2014 separate from the public `defineConfig`/types entry \u2014\n * so the published `infra-kit` type surface stays free of a `zod` import.\n */\nexport const packageConfigSchema = z.strictObject({\n requiredScripts: z.array(z.string().min(1)).optional(),\n requiredFiles: z.array(z.string().min(1)).optional(),\n turbo: z\n .strictObject({\n requiredTasks: z.array(z.string().min(1)).optional(),\n })\n .optional(),\n dev: z\n .strictObject({\n proxy: z\n .strictObject({\n templates: z.strictObject({\n local: z.string().min(1),\n cloud: z.string().min(1),\n }),\n routes: z.record(\n z.string().min(1),\n z\n .strictObject({\n packageName: z.string().min(1),\n from: z.array(z.enum(['local', 'cloud'])).min(1),\n default: z.enum(['local', 'cloud']).optional(),\n })\n .refine(\n (route) => {\n return route.from.length <= 1 || route.default !== undefined\n },\n {\n message: 'default is required when `from` has more than one source',\n },\n )\n .refine(\n (route) => {\n return route.default === undefined || route.from.includes(route.default)\n },\n {\n message: 'default must be listed in `from`',\n },\n ),\n ),\n })\n .optional(),\n })\n .optional(),\n})\n"],
5
- "mappings": "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",
6
- "names": ["Buffer", "execFileSync", "fs", "net", "path", "process", "pathToFileURL", "z", "z", "packageConfigSchema", "route", "INFRA_KIT_ENV", "PACKAGE_CONFIG_FILE", "DEV_CONTEXT_DIR", "path", "DEV_CONTEXT_FILE", "devContextFragmentSchema", "z", "buildBasicAuthHeader", "env", "override", "username", "password", "Buffer", "interpolate", "template", "values", "pickSource", "route", "localSet", "fallback", "resolveRoute", "routePath", "templates", "env", "getRelease", "localInfo", "release", "INFRA_KIT_ENV", "resolveProxyConfig", "proxy", "authHeader", "result", "headers", "entry", "once", "fn", "cached", "loadDev", "cwd", "configPath", "path", "PACKAGE_CONFIG_FILE", "fs", "stat", "rawExport", "pathToFileURL", "resolved", "parsed", "packageConfigSchema", "z", "extractPackages", "v", "candidate", "findUp", "start", "relative", "dir", "parent", "emptyLocalContext", "readFragmentDir", "entries", "packages", "info", "name", "devContextFragmentSchema", "readLocalContext", "DEV_CONTEXT_DIR", "legacy", "DEV_CONTEXT_FILE", "readGitBranch", "cwd", "execFileSync", "getFreePort", "resolve", "reject", "srv", "net", "address", "port", "infraKitDev", "options", "process", "dev", "loadDev", "localSet", "localInfo", "readLocalContext", "env", "INFRA_KIT_ENV", "authHeader", "buildBasicAuthHeader", "getRelease", "once", "slugifyRelease", "resolveProxyConfig", "infraKitProxy"]
4
+ "sourcesContent": ["import { Buffer } from 'node:buffer'\nimport { execFileSync } from 'node:child_process'\nimport fs from 'node:fs'\nimport net from 'node:net'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { pathToFileURL } from 'node:url'\nimport { z } from 'zod'\n\nimport type {\n InfraKitDev,\n InfraKitDevProxy,\n InfraKitDevProxyRoute,\n InfraKitDevProxySource,\n} from '../package-config/package-config'\nimport { packageConfigSchema } from '../package-config/package-config-schema'\nimport { slugifyRelease } from '../release-slug/release-slug'\n\n/**\n * Env-name handle read at vite-config time to fill the `<env>` placeholder in a\n * cloud proxy target. Inlined (not imported from `src/lib/constants`) to keep the\n * `infra-kit/vite` bundle's import graph tiny \u2014 see the module header. Kept\n * byte-identical to `INFRA_KIT_ENV_VAR` in `src/lib/constants/constants.ts`.\n */\nconst INFRA_KIT_ENV = 'INFRA_KIT_ENV'\n\n/** Per-package config filename, mirrored from the CLI's `PACKAGE_CONFIG_FILE`. */\nconst PACKAGE_CONFIG_FILE = 'infra-kit.config.ts'\n\n/**\n * Repo-relative dev-context fragment DIRECTORY, searched upward from cwd. Each\n * runner (single-process or cmux pane) writes its OWN `<app>.json` fragment here\n * recording its real bound port + release; the helper merges them (see\n * {@link readLocalContext}). This is the current source of truth.\n */\nconst DEV_CONTEXT_DIR = path.join('.infra-kit', 'dev-context')\n\n/**\n * Legacy single-file dev-context manifest, searched upward from cwd. Read only as\n * a transitional back-compat path when {@link DEV_CONTEXT_DIR} is absent (the\n * directory wins per-package when both exist).\n */\nconst DEV_CONTEXT_FILE = path.join('.infra-kit', 'dev-context.json')\n\n/**\n * One `.infra-kit/dev-context/<app>.json` fragment. `package` + `port` are the\n * load-bearing fields the helper reads (`package` \u2192 localSet, `port` \u2192 the real\n * bound port for a future `127.0.0.1:<port>` mode / Layer B route); `release` lets\n * the helper prefer the runner-recorded slug over its own git derivation.\n * `pid`/`writtenAt` are staleness metadata (unused on the read path here). Kept\n * lenient (not `.strict()`) so extra writer fields never reject a valid fragment.\n */\nconst devContextFragmentSchema = z.object({\n package: z.string(),\n port: z.number(),\n pid: z.number().optional(),\n writtenAt: z.number().optional(),\n release: z.string().optional(),\n})\n\n/**\n * A single Vite `server.proxy` entry. `changeOrigin` is always set; the\n * cloud-only pair (`secure: false` + `cookieDomainRewrite: 'localhost'`) makes a\n * local FE talk to an HTTPS cloud BE without cert/cookie-domain breakage.\n * `headers` is present only when HTTP Basic Auth is injected (see\n * {@link buildBasicAuthHeader}) \u2014 it carries the `Authorization` header applied\n * uniformly to every route so upstream environments behind auth (e2e/staging)\n * stay reachable.\n */\nexport interface InfraKitViteProxyEntry {\n target: string\n changeOrigin: true\n secure?: false\n cookieDomainRewrite?: 'localhost'\n headers?: Record<string, string>\n}\n\n/** A Vite `server.proxy`-shaped map: path-prefix \u2192 proxy entry. */\nexport type InfraKitViteProxy = Record<string, InfraKitViteProxyEntry>\n\n/** Explicit HTTP Basic Auth credentials, overriding the `E2E__BASIC_AUTH_*` env vars. */\nexport interface InfraKitBasicAuth {\n username: string\n password: string\n}\n\nexport interface InfraKitDevOptions {\n /** Package dir whose `infra-kit.config.ts` is loaded. Defaults to `process.cwd()`. */\n cwd?: string\n /**\n * Explicit HTTP Basic Auth credentials injected into every proxy route as an\n * `Authorization` header. Takes precedence over the `E2E__BASIC_AUTH_USERNAME`\n * / `E2E__BASIC_AUTH_PASSWORD` env vars. Omit to use the env-based default.\n */\n basicAuth?: InfraKitBasicAuth\n /**\n * Vite's `command`. Pass it (`defineConfig(async ({ command }) => ({ server: await infraKitDev({ command }) }))`)\n * so `build` is a no-op: `server` is irrelevant to a build and proxy resolution would otherwise\n * fail-fast on a cloud route with no sourced env. Omit (or `'serve'`) for the dev-server config.\n */\n command?: 'build' | 'serve'\n /**\n * Explicit dev-server port. Omit for a **per-worktree dynamic** free port (a fresh OS-assigned\n * port) so N simultaneous git worktrees never collide on Vite's default `5173`; Vite prints the\n * chosen URL. Pass a fixed number only when an external contract pins the port.\n */\n port?: number\n}\n\n/**\n * Build the `Authorization: Basic <base64(user:pass)>` header value. Credentials\n * come from `override` when given, else from `E2E__BASIC_AUTH_USERNAME` /\n * `E2E__BASIC_AUTH_PASSWORD` (NOTE: double underscore). Returns `undefined` when\n * either half is missing, so callers add no `headers` key at all.\n */\nconst buildBasicAuthHeader = (env: NodeJS.ProcessEnv, override?: InfraKitBasicAuth): string | undefined => {\n const username = override?.username ?? env.E2E__BASIC_AUTH_USERNAME\n const password = override?.password ?? env.E2E__BASIC_AUTH_PASSWORD\n\n if (!username || !password) return undefined\n\n const encoded = Buffer.from(`${username}:${password}`).toString('base64')\n\n return `Basic ${encoded}`\n}\n\n/**\n * Re-exported from the shared, dependency-light `lib/release-slug` module so the\n * published `infra-kit/vite` surface (`entry/vite.ts:7`) is unchanged while the\n * dev-server's fragment writer derives `<release>` from the SAME implementation\n * (no slug drift). See {@link slugifyRelease}.\n */\nexport { slugifyRelease }\n\n/** Fill the `<release>`/`<packageName>`/`<env>` placeholders in a URL template. */\nconst interpolate = (template: string, values: { release: string; packageName: string; env: string }): string => {\n return template\n .replaceAll('<release>', values.release)\n .replaceAll('<packageName>', values.packageName)\n .replaceAll('<env>', values.env)\n}\n\n/**\n * Pick the effective source for a route: `local` when the route lists `local` as\n * a capability AND its packageName is in the local dev set; otherwise the fallback\n * \u2014 the declared `default` for a multi-source route, or the sole `from` entry for\n * a single-source one. Always resolves (the schema guarantees a usable fallback).\n */\nconst pickSource = (route: InfraKitDevProxyRoute, localSet: ReadonlySet<string>): InfraKitDevProxySource => {\n // `from` is guaranteed non-empty by the schema, so `from[0]` is always present.\n const fallback = route.default ?? route.from[0]!\n\n return route.from.includes('local') && localSet.has(route.packageName) ? 'local' : fallback\n}\n\ninterface ResolveRouteArgs {\n routePath: string\n route: InfraKitDevProxyRoute\n templates: InfraKitDevProxy['templates']\n localSet: ReadonlySet<string>\n env: string | undefined\n getRelease: () => string\n /** Per-package runtime data (recorded release/port) from the dev-context merge. */\n localInfo?: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/** Resolve one route into a Vite proxy entry (or throw an actionable error). */\nconst resolveRoute = ({\n routePath,\n route,\n templates,\n localSet,\n env,\n getRelease,\n localInfo,\n}: ResolveRouteArgs): InfraKitViteProxyEntry => {\n const source = pickSource(route, localSet)\n\n if (source === 'local') {\n // Prefer THIS package's runner-recorded release slug (R4) over the single\n // global git derivation, so cross-branch FE/BE pairings don't drift the\n // emitted `<release>` from the segment the runner aliased. Fall back to the\n // global git slug only when the fragment carried no release.\n const release = localInfo?.get(route.packageName)?.release ?? getRelease()\n\n const target = interpolate(templates.local, {\n release,\n packageName: route.packageName,\n env: env ?? '',\n })\n\n return { target, changeOrigin: true }\n }\n\n if (!env) {\n throw new Error(\n `infra-kit/vite: proxy route \"${routePath}\" resolves to a cloud backend but ${INFRA_KIT_ENV} is not set. Source an environment from Doppler first (e.g. \\`infra-kit env-load dev\\`).`,\n )\n }\n\n const target = interpolate(templates.cloud, { release: '', packageName: route.packageName, env })\n\n return { target, changeOrigin: true, secure: false, cookieDomainRewrite: 'localhost' }\n}\n\ninterface ResolveProxyArgs {\n proxy: InfraKitDevProxy\n localSet: ReadonlySet<string>\n env: string | undefined\n getRelease: () => string\n /** Pre-computed `Authorization` header value applied uniformly to every route. */\n authHeader?: string\n /** Per-package runtime data (recorded release/port) from the dev-context merge. */\n localInfo?: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/**\n * Pure resolver: turn a `dev.proxy` config + resolved inputs (local set, env,\n * lazy release) into a Vite `server.proxy` map. Side-effect-free so the\n * resolution shape is fully unit-testable. When `authHeader` is set, every route\n * entry gets a matching `headers.Authorization`; otherwise no `headers` key.\n */\nexport const resolveProxyConfig = ({\n proxy,\n localSet,\n env,\n getRelease,\n authHeader,\n localInfo,\n}: ResolveProxyArgs): InfraKitViteProxy => {\n const result: InfraKitViteProxy = {}\n const headers = authHeader ? { Authorization: authHeader } : undefined\n\n for (const [routePath, route] of Object.entries(proxy.routes)) {\n const entry = resolveRoute({ routePath, route, templates: proxy.templates, localSet, env, getRelease, localInfo })\n\n result[routePath] = headers ? { ...entry, headers } : entry\n }\n\n return result\n}\n\n/** Memoize a zero-arg thunk so `<release>` git resolution runs at most once. */\nconst once = <T>(fn: () => T): (() => T) => {\n let cached: { value: T } | undefined\n\n return () => {\n cached ??= { value: fn() }\n\n return cached.value\n }\n}\n\n/**\n * Load a package's `infra-kit.config.ts` and return its `dev` block, or\n * `undefined` when the config or the `dev` key is absent. The `.ts` config is\n * evaluated via Node's native type stripping (Node >= 24) \u2014 the same mechanism\n * the CLI's config loader uses. Cache-busted by mtime so repeated dev-server\n * reloads pick up edits.\n */\nexport const loadDev = async (cwd: string): Promise<InfraKitDev | undefined> => {\n const configPath = path.join(cwd, PACKAGE_CONFIG_FILE)\n\n if (!fs.existsSync(configPath)) return undefined\n\n const stat = fs.statSync(configPath)\n const moduleUrl = `${pathToFileURL(configPath).href}?mtime=${Number(stat.mtimeMs)}`\n\n const imported = (await import(moduleUrl)) as { default?: unknown }\n const rawExport = imported.default\n\n if (rawExport === undefined) return undefined\n\n const resolved = typeof rawExport === 'function' ? await (rawExport as () => unknown)() : rawExport\n\n const parsed = packageConfigSchema.safeParse(resolved)\n\n if (!parsed.success) {\n throw new Error(`infra-kit/vite: invalid ${PACKAGE_CONFIG_FILE} at ${configPath}: ${z.prettifyError(parsed.error)}`)\n }\n\n return parsed.data.dev\n}\n\n/** Coerce a parsed dev-context.json into the set of locally-running package names. */\nconst extractPackages = (parsed: unknown): string[] => {\n if (Array.isArray(parsed)) {\n return parsed.filter((v): v is string => {\n return typeof v === 'string'\n })\n }\n\n if (parsed !== null && typeof parsed === 'object') {\n const candidate = (parsed as Record<string, unknown>).packages ?? (parsed as Record<string, unknown>).localPackages\n\n if (Array.isArray(candidate)) {\n return candidate.filter((v): v is string => {\n return typeof v === 'string'\n })\n }\n }\n\n return []\n}\n\n/** Search upward from `start` for `relative`, returning the first hit or undefined. */\nconst findUp = (start: string, relative: string): string | undefined => {\n let dir = path.resolve(start)\n\n for (;;) {\n const candidate = path.join(dir, relative)\n\n if (fs.existsSync(candidate)) return candidate\n\n const parent = path.dirname(dir)\n\n if (parent === dir) return undefined\n\n dir = parent\n }\n}\n\n/** Per-package runtime data merged from the dev-context fragment directory. */\nexport interface LocalPackageInfo {\n /** The real bound port the runner recorded (available for a future direct mode). */\n port: number\n /** The runner-recorded release slug, preferred over the helper's git derivation. */\n release?: string\n}\n\n/**\n * The locally-running package set plus a `package \u2192 { port, release }` map merged\n * from the dev-context fragments. `packages` feeds `pickSource`; `info` lets a\n * `local` route emit the per-package recorded release (see {@link resolveRoute}).\n */\nexport interface LocalContext {\n packages: ReadonlySet<string>\n info: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/** An empty {@link LocalContext} (frontend-only / dev-context absent). */\nconst emptyLocalContext = (): LocalContext => {\n return { packages: new Set(), info: new Map() }\n}\n\n/**\n * Merge every `<app>.json` fragment in the dev-context directory into a\n * {@link LocalContext}. Two DISTINCT failure branches (do NOT collapse them into\n * one directory-wide catch):\n * - `readdir`-ENOENT (directory vanished mid-race) \u2192 empty context, matching the\n * dir-absent back-compat behaviour.\n * - a single corrupt/truncated/invalid `<app>.json` \u2192 that ONE fragment is\n * SKIPPED (per-fragment `safeParse` isolation); the rest still merge, so one bad\n * fragment never collapses the whole localSet to empty (which would silently\n * drop a started package to cloud).\n */\nconst readFragmentDir = (dir: string): LocalContext => {\n let entries: string[]\n\n try {\n entries = fs.readdirSync(dir)\n } catch {\n return emptyLocalContext()\n }\n\n const packages = new Set<string>()\n const info = new Map<string, LocalPackageInfo>()\n\n for (const name of entries) {\n if (!name.endsWith('.json')) continue\n\n try {\n const parsed = devContextFragmentSchema.safeParse(JSON.parse(fs.readFileSync(path.join(dir, name), 'utf-8')))\n\n if (!parsed.success) continue\n\n packages.add(parsed.data.package)\n info.set(parsed.data.package, { port: parsed.data.port, release: parsed.data.release })\n } catch {\n // Corrupt/truncated fragment (JSON.parse / read failure): skip ONLY this one.\n continue\n }\n }\n\n return { packages, info }\n}\n\n/**\n * Read the locally-running package context, searched upward from `cwd`. Prefers\n * the `.infra-kit/dev-context/` fragment DIRECTORY (merged via\n * {@link readFragmentDir}); when it is absent, falls back to the legacy single\n * `.infra-kit/dev-context.json` file (read as before \u2014 no per-package release);\n * when neither exists \u2192 empty context (frontend-only, every route resolves cloud).\n */\nexport const readLocalContext = (cwd: string): LocalContext => {\n const dir = findUp(cwd, DEV_CONTEXT_DIR)\n\n if (dir) return readFragmentDir(dir)\n\n const legacy = findUp(cwd, DEV_CONTEXT_FILE)\n\n if (!legacy) return emptyLocalContext()\n\n try {\n return { packages: new Set(extractPackages(JSON.parse(fs.readFileSync(legacy, 'utf-8')))), info: new Map() }\n } catch {\n return emptyLocalContext()\n }\n}\n\n/**\n * Read the set of locally-running packages (searched upward from `cwd`). Thin\n * wrapper over {@link readLocalContext} preserving the historical `Set`-returning\n * contract. Absent dev-context \u2192 empty set (frontend-only cloud resolution).\n */\nexport const readLocalSet = (cwd: string): ReadonlySet<string> => {\n return readLocalContext(cwd).packages\n}\n\n/** Current git branch of `cwd` (raw, un-slugified). */\nconst readGitBranch = (cwd: string): string => {\n // eslint-disable-next-line sonarjs/no-os-command-from-path\n return execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, encoding: 'utf-8' }).trim()\n}\n\n/**\n * An OS-assigned free TCP port on 127.0.0.1 \u2014 the per-worktree dev-server port (mirrors the\n * backend's `listen(0)`). This probes then releases the port, so there is a small TOCTOU window\n * before Vite binds it; Vite's default `strictPort: false` absorbs a lost race by stepping to the\n * next free port. Only a consumer that sets `strictPort: true` would hard-fail on the rare collision.\n */\nconst getFreePort = (): Promise<number> => {\n return new Promise((resolve, reject) => {\n const srv = net.createServer()\n\n srv.unref()\n srv.on('error', reject)\n srv.listen(0, '127.0.0.1', () => {\n const address = srv.address()\n const port = typeof address === 'object' && address !== null ? address.port : 0\n\n srv.close(() => {\n return resolve(port)\n })\n })\n })\n}\n\n/** Env var carrying the runner-assigned `{ \"<ui-package>\": <port> }` map (Layer B). */\nconst UI_PORTS_ENV = 'INFRA_KIT_UI_PORTS'\n\n/** This package's `package.json` `name`, or `null` (unreadable / nameless). */\nconst readPackageName = (cwd: string): string | null => {\n try {\n const parsed = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8')) as { name?: unknown }\n\n return typeof parsed.name === 'string' ? parsed.name : null\n } catch {\n return null\n }\n}\n\n/**\n * The port `infra-kit dev` assigned THIS UI via `INFRA_KIT_UI_PORTS` (Layer B), or `null` when the map\n * is absent / this package isn't in it / it's malformed. Present \u2192 the helper binds it with\n * `strictPort` so vite lands on exactly the port the runner already aliased. Absent (portless off, or a\n * standalone `vite dev`) \u2192 the caller falls back to a free port, byte-identical to today.\n */\nconst resolveManagedUiPort = (cwd: string): number | null => {\n const raw = process.env[UI_PORTS_ENV]\n\n if (raw == null || raw === '') return null\n\n const name = readPackageName(cwd)\n\n if (name == null) return null\n\n try {\n const map = JSON.parse(raw) as Record<string, unknown>\n const port = map[name]\n\n return typeof port === 'number' ? port : null\n } catch {\n return null\n }\n}\n\n/**\n * Resolve a package's `dev` block into a ready-made Vite `server` config: a per-worktree dev-server\n * `port` plus the `proxy` map. Loads the package's `infra-kit.config.ts`, merges the local dev set\n * from the `.infra-kit/dev-context/` fragment directory, and interpolates the local/cloud templates.\n * `<env>` comes from `INFRA_KIT_ENV`; `<release>` from each package's runner-recorded fragment when\n * present, else the slugified git branch (computed lazily, only when a local route needs it).\n *\n * `port` defaults to a fresh OS-assigned free port so simultaneous git worktrees never collide on\n * Vite's `5173` (override via `options.port`). Pass `command` so `build` is a no-op (empty proxy, no\n * port) \u2014 a build ignores `server` and proxy resolution would otherwise fail-fast on a cloud route\n * with no sourced env.\n *\n * @example\n * // vite.config.ts \u2014 the whole `server` field is the helper's output\n * import { infraKitDev } from 'infra-kit/vite'\n * export default defineConfig(async ({ command }) => ({ server: await infraKitDev({ command }) }))\n */\nexport const infraKitDev = async (\n options: InfraKitDevOptions = {},\n): Promise<{ port?: number; strictPort?: boolean; proxy: InfraKitViteProxy }> => {\n const cwd = options.cwd ?? process.cwd()\n\n // A build ignores `server`; skip the port + proxy work (proxy resolution would fail-fast on a\n // cloud route with no sourced env).\n if (options.command === 'build') return { proxy: {} }\n\n // Port precedence: explicit `options.port` > the runner-assigned Layer-B port (bound `strictPort` so\n // vite lands on exactly the aliased port) > a fresh free port (today's default; the portless-off path).\n const managedPort = options.port == null ? resolveManagedUiPort(cwd) : null\n const port = options.port ?? managedPort ?? (await getFreePort())\n const strictPort = managedPort != null\n const dev = await loadDev(cwd)\n\n if (!dev?.proxy) return { port, ...(strictPort ? { strictPort } : {}), proxy: {} }\n\n const { packages: localSet, info: localInfo } = readLocalContext(cwd)\n const env = process.env[INFRA_KIT_ENV]\n const authHeader = buildBasicAuthHeader(process.env, options.basicAuth)\n const getRelease = once(() => {\n return slugifyRelease(readGitBranch(cwd))\n })\n\n return {\n port,\n ...(strictPort ? { strictPort } : {}),\n proxy: resolveProxyConfig({ proxy: dev.proxy, localSet, env, getRelease, authHeader, localInfo }),\n }\n}\n\n/**\n * Convenience wrapper returning just the proxy map (for spreading into\n * `server.proxy` directly). Equivalent to `(await infraKitDev(options)).proxy`.\n */\nexport const infraKitProxy = async (options: InfraKitDevOptions = {}): Promise<InfraKitViteProxy> => {\n return (await infraKitDev(options)).proxy\n}\n", "import { z } from 'zod'\n\n/**\n * Schema for the resolved (post-factory) package config object. `strictObject`\n * rejects unknown keys so typos in `infra-kit.config.ts` surface as validation\n * errors instead of being silently ignored.\n *\n * Kept in its own module \u2014 separate from the public `defineConfig`/types entry \u2014\n * so the published `infra-kit` type surface stays free of a `zod` import.\n */\nexport const packageConfigSchema = z.strictObject({\n requiredScripts: z.array(z.string().min(1)).optional(),\n requiredFiles: z.array(z.string().min(1)).optional(),\n turbo: z\n .strictObject({\n requiredTasks: z.array(z.string().min(1)).optional(),\n })\n .optional(),\n dev: z\n .strictObject({\n proxy: z\n .strictObject({\n templates: z.strictObject({\n local: z.string().min(1),\n cloud: z.string().min(1),\n }),\n routes: z.record(\n z.string().min(1),\n z\n .strictObject({\n packageName: z.string().min(1),\n from: z.array(z.enum(['local', 'cloud'])).min(1),\n default: z.enum(['local', 'cloud']).optional(),\n })\n .refine(\n (route) => {\n return route.from.length <= 1 || route.default !== undefined\n },\n {\n message: 'default is required when `from` has more than one source',\n },\n )\n .refine(\n (route) => {\n return route.default === undefined || route.from.includes(route.default)\n },\n {\n message: 'default must be listed in `from`',\n },\n ),\n ),\n })\n .optional(),\n })\n .optional(),\n})\n"],
5
+ "mappings": "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",
6
+ "names": ["Buffer", "execFileSync", "fs", "net", "path", "process", "pathToFileURL", "z", "z", "packageConfigSchema", "route", "INFRA_KIT_ENV", "PACKAGE_CONFIG_FILE", "DEV_CONTEXT_DIR", "path", "DEV_CONTEXT_FILE", "devContextFragmentSchema", "z", "buildBasicAuthHeader", "env", "override", "username", "password", "Buffer", "interpolate", "template", "values", "pickSource", "route", "localSet", "fallback", "resolveRoute", "routePath", "templates", "env", "getRelease", "localInfo", "release", "INFRA_KIT_ENV", "resolveProxyConfig", "proxy", "authHeader", "result", "headers", "entry", "once", "fn", "cached", "loadDev", "cwd", "configPath", "path", "PACKAGE_CONFIG_FILE", "fs", "stat", "rawExport", "pathToFileURL", "resolved", "parsed", "packageConfigSchema", "z", "extractPackages", "v", "candidate", "findUp", "start", "relative", "dir", "parent", "emptyLocalContext", "readFragmentDir", "entries", "packages", "info", "name", "devContextFragmentSchema", "readLocalContext", "DEV_CONTEXT_DIR", "legacy", "DEV_CONTEXT_FILE", "readGitBranch", "cwd", "execFileSync", "getFreePort", "resolve", "reject", "srv", "net", "address", "port", "UI_PORTS_ENV", "readPackageName", "parsed", "fs", "path", "resolveManagedUiPort", "raw", "process", "name", "infraKitDev", "options", "managedPort", "strictPort", "dev", "loadDev", "localSet", "localInfo", "readLocalContext", "env", "INFRA_KIT_ENV", "authHeader", "buildBasicAuthHeader", "getRelease", "once", "slugifyRelease", "resolveProxyConfig", "infraKitProxy"]
7
7
  }
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
- import{A as ye,B as he,C as we,D as ke,E as Ce,F as Ae,G as xe,H as Ee,I as M,J as O,K as Se,L as Re,M as be,N as Le,O as je,Q as P,a as X,b as w,c as u,d as I,e as Z,f as ee,g as _,h as j,i as oe,j as te,k as re,l as ne,m as $,n as ie,o as k,p as ae,q as se,r as ce,s as de,t as me,u as le,v as pe,w as fe,x as ue,y as ge,z as ve}from"./chunk-V5IDN7ZM.js";import{K as T,L as Y,M as Q,m as z,v as a}from"./chunk-6RRAK2QO.js";import{e as N}from"./chunk-Z6KTUVIC.js";import"./chunk-IX2A34UU.js";import"./chunk-2PZRQHWF.js";import ko,{Separator as x}from"@inquirer/select";import{Command as Co}from"commander";import h from"node:process";import F from"node:fs/promises";import eo from"node:path";import Pe from"node:process";import{$ as oo}from"zx";var V=async()=>{let e=await T(),o=await Promise.all([{label:"project (committed)",path:e.main},{label:"user global",path:e.userGlobal},{label:"user project",path:e.userProject}].map(async r=>({...r,exists:await w(r.path)})));a.info(`Project name: ${e.projectName}
1
+ import{A as M,B as Se,C as Re,D as be,E as Le,F as je,H as P,a as X,b as w,c as g,d as I,e as Z,f as ee,g as _,h as ae,i as se,j as ce,k as de,l as me,m as le,n as pe,o as fe,p as ue,q as ge,r as ve,s as ye,t as he,u as we,v as ke,w as Ce,x as Ae,y as xe,z as Ee}from"./chunk-AP63Y7KE.js";import{i as O}from"./chunk-Q36WX5RP.js";import{e as N}from"./chunk-Z6KTUVIC.js";import{a as j,b as oe,d as te,f as re,g as ne,j as $,k as ie,m as k}from"./chunk-V375VXOL.js";import{M as D,N as z,O as Q,m as q,v as a}from"./chunk-4YU4TAGX.js";import"./chunk-YGCVOVNX.js";import"./chunk-2PZRQHWF.js";import ko,{Separator as E}from"@inquirer/select";import{Command as Co}from"commander";import u from"node:process";import F from"node:fs/promises";import eo from"node:path";import Pe from"node:process";import{$ as oo}from"zx";var V=async()=>{let e=await D(),o=await Promise.all([{label:"project (committed)",path:e.main},{label:"user global",path:e.userGlobal},{label:"user project",path:e.userProject}].map(async r=>({...r,exists:await w(r.path)})));a.info(`Project name: ${e.projectName}
2
2
  `),a.info(`Config merge chain (later overrides earlier):
3
- `);for(let r of o){let s=r.exists?" [\u2713]":" [ ]";a.info(`${s} ${r.label.padEnd(22)} ${u(r.path)}`)}let t={projectName:e.projectName,layers:o.map(r=>({label:r.label,path:r.path,exists:r.exists}))};return{content:[{type:"text",text:JSON.stringify(t,null,2)}],structuredContent:t}},G=async()=>{let e=await T(),o=Pe.env.EDITOR||Pe.env.VISUAL||"vi";if(await F.mkdir(eo.dirname(e.userProject),{recursive:!0}),!await w(e.userProject)){let r=to(e.userProject);await F.writeFile(e.userProject,`{}
4
- `,"utf-8"),await F.writeFile(r,ro(e.projectName),"utf-8"),a.info(`Created ${u(e.userProject)} \u2014 see ${u(r)} for the annotated reference.`)}a.info(`Opening ${u(e.userProject)} in ${o}`),await oo({stdio:"inherit"})`${o} ${e.userProject}`,Q();let t={path:e.userProject,editor:o};return{content:[{type:"text",text:JSON.stringify(t,null,2)}],structuredContent:t}},to=e=>e.replace(/\.json$/,".example.jsonc"),ro=e=>`// infra-kit user override for ${e} \u2014 ~/.infra-kit/projects/${e}/infra-kit.json
3
+ `);for(let r of o){let s=r.exists?" [\u2713]":" [ ]";a.info(`${s} ${r.label.padEnd(22)} ${g(r.path)}`)}let t={projectName:e.projectName,layers:o.map(r=>({label:r.label,path:r.path,exists:r.exists}))};return{content:[{type:"text",text:JSON.stringify(t,null,2)}],structuredContent:t}},G=async()=>{let e=await D(),o=Pe.env.EDITOR||Pe.env.VISUAL||"vi";if(await F.mkdir(eo.dirname(e.userProject),{recursive:!0}),!await w(e.userProject)){let r=to(e.userProject);await F.writeFile(e.userProject,`{}
4
+ `,"utf-8"),await F.writeFile(r,ro(e.projectName),"utf-8"),a.info(`Created ${g(e.userProject)} \u2014 see ${g(r)} for the annotated reference.`)}a.info(`Opening ${g(e.userProject)} in ${o}`),await oo({stdio:"inherit"})`${o} ${e.userProject}`,Q();let t={path:e.userProject,editor:o};return{content:[{type:"text",text:JSON.stringify(t,null,2)}],structuredContent:t}},to=e=>e.replace(/\.json$/,".example.jsonc"),ro=e=>`// infra-kit user override for ${e} \u2014 ~/.infra-kit/projects/${e}/infra-kit.json
5
5
  //
6
6
  // Layer 3 (highest precedence) of the config merge chain. Shallow-merged on top
7
7
  // of <repo>/infra-kit.json and ~/.infra-kit/infra-kit.json \u2014 top-level keys
@@ -14,7 +14,7 @@ import{A as ye,B as he,C as we,D as ke,E as Ce,F as Ae,G as xe,H as Ee,I as M,J
14
14
  // // Auto-load Doppler env here. trigger (pick one): shell-startup | cli-invocation; config: an environment name.
15
15
  // "envAutoLoad": { "trigger": "shell-startup", "config": "dev" }
16
16
  }
17
- `;import l from"node:fs";import C from"node:path";import E from"node:process";var no="autoload-warn-misconfig.flag",io="autoload-warn-fail.flag",W="autoload-fail.flag",ao=3e4,Te=async(e=!0)=>{let o;try{o=await Y()}catch{return null}let t=o.envAutoLoad;return t?o.environments.includes(t.config)?{trigger:t.trigger,config:t.config,project:o.envManagement.config.name}:(e&&_e(`infra-kit: envAutoLoad.config "${t.config}" is not one of environments [${o.environments.join(", ")}] \u2014 env auto-load disabled.`,no),null):null},Ie=e=>{let{trigger:o,expectedTrigger:t,targetConfig:r,targetProject:s,env:c,force:m}=e;return o!==t||!c.session||c.cleared||c.currentConfig&&!c.autoLoadedMarker||!m&&c.autoLoadedMarker&&c.currentConfig===r&&c.currentProject===s?"skip":"load"},S=async({expectedTrigger:e,projectDir:o,force:t})=>{let r=e==="cli-invocation";try{let s=await Te(r);if(!s||Ie({trigger:s.trigger,expectedTrigger:e,targetConfig:s.config,targetProject:s.project,env:so(),force:t})==="skip"||De()||lo())return null;let m=co(),d=await se({config:s.config,autoLoaded:!0,projectDir:o,beforeWrite:()=>!De()&&!mo(m)});return d?(fo(),d.filePath):null}catch(s){let c=s.message;return po(),r?_e(`infra-kit: env auto-load failed \u2014 ${c} (will retry later)`,io):a.debug(`env auto-load skipped: ${c}`),null}},so=()=>({session:E.env[te],cleared:E.env[ie],currentConfig:E.env[re],currentProject:E.env[ne],autoLoadedMarker:E.env[$]}),co=()=>{try{return l.statSync(C.join(k(),j)).mtimeMs}catch{return null}},mo=e=>{try{let o=C.join(k(),j);if(!l.existsSync(o))return!1;let t=l.statSync(o).mtimeMs;return e!==null&&t<=e?!1:new RegExp(`^unset ${$}$`,"m").test(l.readFileSync(o,"utf-8"))}catch{return!1}},De=()=>{try{let e=k(),o=C.join(e,oe);if(!l.existsSync(o))return!1;let t=C.join(e,j);return l.existsSync(t)?l.statSync(o).mtimeMs>=l.statSync(t).mtimeMs:!0}catch{return!1}},lo=()=>{try{let e=C.join(k(),W);return l.existsSync(e)?Date.now()-l.statSync(e).mtimeMs<ao:!1}catch{return!1}},po=()=>{try{let e=k();l.mkdirSync(e,{recursive:!0,mode:448}),l.writeFileSync(C.join(e,W),"",{mode:384})}catch{}},fo=()=>{try{l.rmSync(C.join(k(),W),{force:!0})}catch{}},_e=(e,o)=>{try{let t=k(),r=C.join(t,o);if(l.existsSync(r))return;l.mkdirSync(t,{recursive:!0,mode:448}),l.writeFileSync(r,"",{mode:384})}catch{}a.warn(e)};var J=async({projectDir:e}={})=>{await S({expectedTrigger:"shell-startup",projectDir:e,force:!0})};import K from"node:fs/promises";import H from"node:path";import $e from"node:process";import{pathToFileURL as uo}from"node:url";var Ne="~/projects",U=async(e={})=>{if(e.init){await vo(e.cwd);return}await go()},go=async()=>{let e=I(),o=await w(e);if(a.info(`Factory config: ${u(e)} ${o?"[\u2713]":"[ ]"}`),!o){a.info("\nNot found \u2014 run `infra-kit vendor-config --init` to scaffold it."),$e.exitCode=1;return}let{workspaceDir:t,targets:r}=await ee(),s=Z(t),c=await w(s);a.info(`workspaceDir: ${t} (resolved: ${s}) ${c?"[\u2713 exists]":"[ ] not found"}`),a.info("Targets:");let m=c;for(let d of r){let g=H.join(s,d),f=await w(g);f||(m=!1);let v=f?"[\u2713]":"[ ]",y=f?"":" (not found \u2014 clone or remove)";a.info(` ${v} ${d} ${u(g)}${y}`)}m||($e.exitCode=1)},vo=async e=>{let o=I();if(await w(o)){a.info(`Factory config already exists at ${u(o)} \u2014 leaving it untouched.`);return}let t=e??await z(),r=await yo(t);await K.mkdir(H.dirname(o),{recursive:!0}),await K.writeFile(o,ho(r),"utf-8"),a.info(`\u2713 Created ${u(o)}`),r.length>0&&a.info(` Seeded ${r.length} target(s) from the source ${N}.`),a.info(` Edit \`workspaceDir\` (placeholder: ${Ne}) to point at where your repos live.`),r.length===0&&a.info(" Add at least one repo name to `targets` before running vendor sync/manifest/diff.")},yo=async e=>{try{let o=H.join(e,N),t=await K.stat(o),c=(await import(`${uo(o).href}?mtime=${Number(t.mtimeMs)}`)).default,m=typeof c=="function"?await c():c;if(m&&typeof m=="object"&&"targets"in m){let d=m.targets;if(Array.isArray(d)&&d.every(g=>typeof g=="string"))return d}}catch{}return[]},ho=e=>`${JSON.stringify({workspaceDir:Ne,targets:e},null,2)}
18
- `;import wo from"node:process";var R={enabled:!1},i=(e,o=t=>{wo.stdout.write(t)})=>(R.enabled&&e&&e.structuredContent!=null&&o(`${JSON.stringify(e.structuredContent,null,2)}
19
- `),e),D=e=>{e.options.some(t=>t.long==="--json")||e.option("--json","Output the structured result as JSON on stdout (human logs stay on stderr)"),e.commands.forEach(D)};var B=(e,o=" ")=>{let t=e.reduce((r,[s])=>Math.max(r,s.length),0);return e.map(([r,s])=>`${r.padEnd(t)}${o}${s}`)};var n=new Co,Ao=(e,o)=>[...o,e],q=e=>typeof e=="string"?e.split(",").filter(Boolean):void 0,Me=(e,o)=>{if(!(typeof e>"u")){if(e===!0)return"workspace";if(e===!1)return"none";if(typeof e=="string"&&M.includes(e))return e;throw new Error(`Invalid ${o} value "${String(e)}". Expected one of: ${M.join(", ")}.`)}},Oe=async e=>{try{e?await n.parseAsync(e):await n.parseAsync()}catch(o){O(o)&&(a.info("Operation cancelled."),h.exit(0));let t=o instanceof Error?o.message:String(o);a.error(t),h.exit(1)}},Fe={value:!1},p=(e,o)=>e.hook("preAction",()=>{Fe.value||a.warn(`"${e.name()}" is a deprecated alias; use "${o}" instead.`)}),Ve=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 o=>{i(await pe({all:o.all,confirmedCommand:o.yes}))}),Ge=e=>e.description("List all release branches").action(async()=>{i(await ve())}),We=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".',Ao,[]).option("-y, --yes","Skip confirmation prompt").action(async o=>{let r=o.release.map(ye),s=r.length>0?r:void 0;i(await he({releases:s,confirmedCommand:o.yes}))}),Je=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 o=>{i(await we({version:o.version,description:o.description,confirmedCommand:o.yes}))}),Ke=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 o=>{i(await ue({version:o.version,env:o.env,skipTerraform:o.skipTerraform,confirmedCommand:o.yes}))}),He=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 o=>{i(await ge({version:o.version,env:o.env,services:o.services,skipTerraform:o.skipTerraform,confirmedCommand:o.yes}))}),Ue=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 o=>{i(await fe({version:o.version,confirmedCommand:o.yes}))}),Be=e=>e.description("Remove release worktrees whose PRs are no longer open").option("-y, --yes","Skip confirmation prompt").action(async o=>{i(await je({confirmedCommand:o.yes}))}),qe=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 o=>{let t=Me(o.ide,"--ide")??Me(o.cursor,"--cursor");i(await Se({confirmedCommand:o.yes,all:o.all,versions:o.versions,ide:t,githubDesktop:o.githubDesktop,cmux:o.cmux}))}),ze=e=>e.description("List all git worktrees with detailed information").action(async()=>{i(await Re())}),Ye=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 o=>{i(await Le({confirmedCommand:o.yes,all:o.all,versions:o.versions}))}),Qe=e=>e.description("Close all cmux/editor worktree windows, then reopen the current release worktrees (also cold-start restore)").action(async()=>{i(await be())}),Xe=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 o=>{i(await U({init:o.init}))}),A=n.command("release").description("Release management commands");Ve(A.command("merge-dev"));Ge(A.command("list"));We(A.command("create"));Je(A.command("desc-edit"));Ke(A.command("deploy-all"));He(A.command("deploy-selected"));Ue(A.command("deliver"));var b=n.command("worktrees").description("Git worktree management commands");qe(b.command("add"));ze(b.command("list"));Ye(b.command("remove"));Be(b.command("sync"));Qe(b.command("reload"));p(Ve(n.command("merge-dev")),"release merge-dev");p(Ge(n.command("release-list")),"release list");p(We(n.command("release-create")),"release create");p(Je(n.command("release-desc-edit")),"release desc-edit");p(Ke(n.command("release-deploy-all")),"release deploy-all");p(He(n.command("release-deploy-selected")),"release deploy-selected");p(Ue(n.command("release-deliver")),"release deliver");p(qe(n.command("worktrees-add")),"worktrees add");p(ze(n.command("worktrees-list")),"worktrees list");p(Ye(n.command("worktrees-remove")),"worktrees remove");p(Be(n.command("worktrees-sync")),"worktrees sync");p(Qe(n.command("worktrees-reload")),"worktrees reload");var Ze=n.command("config").description("Manage infra-kit configuration files");Ze.command("path").description("Show the resolved config merge chain and file paths").action(async()=>{i(await V())});Ze.command("edit").description("Open the user-scope per-project override file in $EDITOR").action(async()=>{i(await G())});n.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 e=>{let o=await X({all:e.all,root:e.root});i(o),o.structuredContent.allPassed||(h.exitCode=1)});var L=n.command("vendor").description("Verify and sync the mirrored vendor/ tree");L.command("check").description("Verify vendor/ matches vendor/.sync-manifest.json (self-contained; for any consumer repo)").action(async()=>{let e=await ke();i(e),e.structuredContent.ok||(h.exitCode=1)});L.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 e=>{i(await xe({confirmedCommand:e.yes,repos:q(e.repos)}))});L.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 e=>{i(await Ae({confirmedCommand:!0,repos:q(e.repos)}))});L.command("diff").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 e=>{let o=await Ce({repos:q(e.repos)});i(o),o.structuredContent.ok||(h.exitCode=1)});Xe(L.command("config"));p(Xe(n.command("vendor-config")),"vendor config");n.command("doctor").description("Check installation and authentication status of gh and doppler CLIs").action(async()=>{i(await ae())});n.command("dev").description("Run local dev servers for a named devPresets preset (or all apps); api + ui").argument("[preset]","Named preset from devPresets (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("--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 .infra-kit/dev-server.log)").action(async(e,o)=>{let{runDevServer:t,toDevServerOptions:r}=await import("./dev-server.js");await t(r({...o,preset:e}))});n.command("version").description("Print the installed infra-kit CLI version").action(async()=>{i(await Ee())});n.command("env-status").description("Show which env is loaded in this session (local introspection; no Doppler call)").action(async()=>{i(await le())});n.command("env-list").description("List available Doppler configs for the detected project").action(async()=>{i(await me())});n.command("init").description("Inject shell integration into .zshrc and sync repo agent-instruction files").action(async()=>{i(await _())});n.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 e=>{i(await ce({config:e.config}))});n.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 e=>{i(await de({purge:!!e.purge}))});n.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 e=>{await J({projectDir:e.projectDir})});n.commands.forEach(D);var xo=e=>e.startsWith("env-")||e==="init"||e==="doctor"||e==="version"||e==="dev";n.hook("preAction",async(e,o)=>{R.enabled=!!o.optsWithGlobals().json,R.enabled&&(a.level="warn"),xo(o.name())||await S({expectedTrigger:"cli-invocation"})});if(h.argv.length<=2){let e=P("release"),o=P("worktrees"),t=P("environment"),r=new Map(n.commands.map(d=>[d.name(),d])),c=[{label:"Release Management",names:e},{label:"Worktrees",names:o},{label:"Environment",names:t}].flatMap(({label:d,names:g})=>g.filter(f=>r.has(f)).map(f=>({name:f,description:r.get(f).description(),group:d}))),m=null;try{if(h.stdout.isTTY&&h.stdin.isTTY){let{runCommandPalette:d}=await import("./boot-SGM5RVLJ.js");m=await d(c)}else{let d=B(c.map(v=>[v.name,v.description])),g=new Map;c.forEach((v,y)=>{g.set(v.name,d[y]??v.name)});let f=v=>v.filter(y=>r.has(y)).map(y=>({name:g.get(y)??y,value:y}));m=await ko({message:"Select a command to run",choices:[new x(" "),new x("\u2014 Release Management \u2014"),...f(e),new x(" "),new x("\u2014 Worktrees \u2014"),...f(o),new x(" "),new x("\u2014 Environment \u2014"),...f(t)]},{output:h.stderr})}}catch(d){if(!O(d))throw d}m&&(Fe.value=!0,await Oe(["node","infra-kit",m]))}else await Oe();
17
+ `;import l from"node:fs";import C from"node:path";import S from"node:process";var no="autoload-warn-misconfig.flag",io="autoload-warn-fail.flag",W="autoload-fail.flag",ao=3e4,De=async(e=!0)=>{let o;try{o=await z()}catch{return null}let t=o.envAutoLoad;return t?o.environments.includes(t.config)?{trigger:t.trigger,config:t.config,project:o.envManagement.config.name}:(e&&_e(`infra-kit: envAutoLoad.config "${t.config}" is not one of environments [${o.environments.join(", ")}] \u2014 env auto-load disabled.`,no),null):null},Ie=e=>{let{trigger:o,expectedTrigger:t,targetConfig:r,targetProject:s,env:c,force:m}=e;return o!==t||!c.session||c.cleared||c.currentConfig&&!c.autoLoadedMarker||!m&&c.autoLoadedMarker&&c.currentConfig===r&&c.currentProject===s?"skip":"load"},R=async({expectedTrigger:e,projectDir:o,force:t})=>{let r=e==="cli-invocation";try{let s=await De(r);if(!s||Ie({trigger:s.trigger,expectedTrigger:e,targetConfig:s.config,targetProject:s.project,env:so(),force:t})==="skip"||Te()||lo())return null;let m=co(),d=await se({config:s.config,autoLoaded:!0,projectDir:o,beforeWrite:()=>!Te()&&!mo(m)});return d?(fo(),d.filePath):null}catch(s){let c=s.message;return po(),r?_e(`infra-kit: env auto-load failed \u2014 ${c} (will retry later)`,io):a.debug(`env auto-load skipped: ${c}`),null}},so=()=>({session:S.env[te],cleared:S.env[ie],currentConfig:S.env[re],currentProject:S.env[ne],autoLoadedMarker:S.env[$]}),co=()=>{try{return l.statSync(C.join(k(),j)).mtimeMs}catch{return null}},mo=e=>{try{let o=C.join(k(),j);if(!l.existsSync(o))return!1;let t=l.statSync(o).mtimeMs;return e!==null&&t<=e?!1:new RegExp(`^unset ${$}$`,"m").test(l.readFileSync(o,"utf-8"))}catch{return!1}},Te=()=>{try{let e=k(),o=C.join(e,oe);if(!l.existsSync(o))return!1;let t=C.join(e,j);return l.existsSync(t)?l.statSync(o).mtimeMs>=l.statSync(t).mtimeMs:!0}catch{return!1}},lo=()=>{try{let e=C.join(k(),W);return l.existsSync(e)?Date.now()-l.statSync(e).mtimeMs<ao:!1}catch{return!1}},po=()=>{try{let e=k();l.mkdirSync(e,{recursive:!0,mode:448}),l.writeFileSync(C.join(e,W),"",{mode:384})}catch{}},fo=()=>{try{l.rmSync(C.join(k(),W),{force:!0})}catch{}},_e=(e,o)=>{try{let t=k(),r=C.join(t,o);if(l.existsSync(r))return;l.mkdirSync(t,{recursive:!0,mode:448}),l.writeFileSync(r,"",{mode:384})}catch{}a.warn(e)};var J=async({projectDir:e}={})=>{await R({expectedTrigger:"shell-startup",projectDir:e,force:!0})};import K from"node:fs/promises";import H from"node:path";import $e from"node:process";import{pathToFileURL as uo}from"node:url";var Ne="~/projects",U=async(e={})=>{if(e.init){await vo(e.cwd);return}await go()},go=async()=>{let e=I(),o=await w(e);if(a.info(`Factory config: ${g(e)} ${o?"[\u2713]":"[ ]"}`),!o){a.info("\nNot found \u2014 run `infra-kit vendor-config --init` to scaffold it."),$e.exitCode=1;return}let{workspaceDir:t,targets:r}=await ee(),s=Z(t),c=await w(s);a.info(`workspaceDir: ${t} (resolved: ${s}) ${c?"[\u2713 exists]":"[ ] not found"}`),a.info("Targets:");let m=c;for(let d of r){let v=H.join(s,d),f=await w(v);f||(m=!1);let y=f?"[\u2713]":"[ ]",h=f?"":" (not found \u2014 clone or remove)";a.info(` ${y} ${d} ${g(v)}${h}`)}m||($e.exitCode=1)},vo=async e=>{let o=I();if(await w(o)){a.info(`Factory config already exists at ${g(o)} \u2014 leaving it untouched.`);return}let t=e??await q(),r=await yo(t);await K.mkdir(H.dirname(o),{recursive:!0}),await K.writeFile(o,ho(r),"utf-8"),a.info(`\u2713 Created ${g(o)}`),r.length>0&&a.info(` Seeded ${r.length} target(s) from the source ${N}.`),a.info(` Edit \`workspaceDir\` (placeholder: ${Ne}) to point at where your repos live.`),r.length===0&&a.info(" Add at least one repo name to `targets` before running vendor sync/manifest/diff.")},yo=async e=>{try{let o=H.join(e,N),t=await K.stat(o),c=(await import(`${uo(o).href}?mtime=${Number(t.mtimeMs)}`)).default,m=typeof c=="function"?await c():c;if(m&&typeof m=="object"&&"targets"in m){let d=m.targets;if(Array.isArray(d)&&d.every(v=>typeof v=="string"))return d}}catch{}return[]},ho=e=>`${JSON.stringify({workspaceDir:Ne,targets:e},null,2)}
18
+ `;import wo from"node:process";var x={enabled:!1},i=(e,o=t=>{wo.stdout.write(t)})=>(x.enabled&&e&&e.structuredContent!=null&&o(`${JSON.stringify(e.structuredContent,null,2)}
19
+ `),e),T=e=>{e.options.some(t=>t.long==="--json")||e.option("--json","Output the structured result as JSON on stdout (human logs stay on stderr)"),e.commands.forEach(T)};var B=(e,o=" ")=>{let t=e.reduce((r,[s])=>Math.max(r,s.length),0);return e.map(([r,s])=>`${r.padEnd(t)}${o}${s}`)};var n=new Co,Ao=(e,o)=>[...o,e],Y=e=>typeof e=="string"?e.split(",").filter(Boolean):void 0,Me=(e,o)=>{if(!(typeof e>"u")){if(e===!0)return"workspace";if(e===!1)return"none";if(typeof e=="string"&&M.includes(e))return e;throw new Error(`Invalid ${o} value "${String(e)}". Expected one of: ${M.join(", ")}.`)}},Oe=async e=>{try{e?await n.parseAsync(e):await n.parseAsync()}catch(o){O(o)&&(a.info("Operation cancelled."),u.exit(0));let t=o instanceof Error?o.message:String(o);a.error(t),u.exit(1)}},Fe={value:!1},p=(e,o)=>e.hook("preAction",()=>{Fe.value||a.warn(`"${e.name()}" is a deprecated alias; use "${o}" instead.`)}),Ve=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 o=>{i(await pe({all:o.all,confirmedCommand:o.yes}))}),Ge=e=>e.description("List all release branches").action(async()=>{i(await ve())}),We=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".',Ao,[]).option("-y, --yes","Skip confirmation prompt").action(async o=>{let r=o.release.map(ye),s=r.length>0?r:void 0;i(await he({releases:s,confirmedCommand:o.yes}))}),Je=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 o=>{i(await we({version:o.version,description:o.description,confirmedCommand:o.yes}))}),Ke=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 o=>{i(await ue({version:o.version,env:o.env,skipTerraform:o.skipTerraform,confirmedCommand:o.yes}))}),He=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 o=>{i(await ge({version:o.version,env:o.env,services:o.services,skipTerraform:o.skipTerraform,confirmedCommand:o.yes}))}),Ue=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 o=>{i(await fe({version:o.version,confirmedCommand:o.yes}))}),Be=e=>e.description("Remove release worktrees whose PRs are no longer open").option("-y, --yes","Skip confirmation prompt").action(async o=>{i(await je({confirmedCommand:o.yes}))}),Ye=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 o=>{let t=Me(o.ide,"--ide")??Me(o.cursor,"--cursor");i(await Se({confirmedCommand:o.yes,all:o.all,versions:o.versions,ide:t,githubDesktop:o.githubDesktop,cmux:o.cmux}))}),qe=e=>e.description("List all git worktrees with detailed information").action(async()=>{i(await Re())}),ze=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 o=>{i(await Le({confirmedCommand:o.yes,all:o.all,versions:o.versions}))}),Qe=e=>e.description("Close all cmux/editor worktree windows, then reopen the current release worktrees (also cold-start restore)").action(async()=>{i(await be())}),Xe=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 o=>{i(await U({init:o.init}))}),A=n.command("release").description("Release management commands");Ve(A.command("merge-dev"));Ge(A.command("list"));We(A.command("create"));Je(A.command("desc-edit"));Ke(A.command("deploy-all"));He(A.command("deploy-selected"));Ue(A.command("deliver"));var b=n.command("worktrees").description("Git worktree management commands");Ye(b.command("add"));qe(b.command("list"));ze(b.command("remove"));Be(b.command("sync"));Qe(b.command("reload"));p(Ve(n.command("merge-dev")),"release merge-dev");p(Ge(n.command("release-list")),"release list");p(We(n.command("release-create")),"release create");p(Je(n.command("release-desc-edit")),"release desc-edit");p(Ke(n.command("release-deploy-all")),"release deploy-all");p(He(n.command("release-deploy-selected")),"release deploy-selected");p(Ue(n.command("release-deliver")),"release deliver");p(Ye(n.command("worktrees-add")),"worktrees add");p(qe(n.command("worktrees-list")),"worktrees list");p(ze(n.command("worktrees-remove")),"worktrees remove");p(Be(n.command("worktrees-sync")),"worktrees sync");p(Qe(n.command("worktrees-reload")),"worktrees reload");var Ze=n.command("config").description("Manage infra-kit configuration files");Ze.command("path").description("Show the resolved config merge chain and file paths").action(async()=>{i(await V())});Ze.command("edit").description("Open the user-scope per-project override file in $EDITOR").action(async()=>{i(await G())});n.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 e=>{let o=await X({all:e.all,root:e.root});i(o),o.structuredContent.allPassed||(u.exitCode=1)});var L=n.command("vendor").description("Verify and sync the mirrored vendor/ tree");L.command("check").description("Verify vendor/ matches vendor/.sync-manifest.json (self-contained; for any consumer repo)").action(async()=>{let e=await ke();i(e),e.structuredContent.ok||(u.exitCode=1)});L.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 e=>{i(await xe({confirmedCommand:e.yes,repos:Y(e.repos)}))});L.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 e=>{i(await Ae({confirmedCommand:!0,repos:Y(e.repos)}))});L.command("diff").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 e=>{let o=await Ce({repos:Y(e.repos)});i(o),o.structuredContent.ok||(u.exitCode=1)});Xe(L.command("config"));p(Xe(n.command("vendor-config")),"vendor config");n.command("doctor").description("Check installation and authentication status of gh and doppler CLIs").action(async()=>{i(await ae())});n.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("--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 .infra-kit/dev-server.log)").option("--routes","Print each app\u2019s registered METHOD /path routes at startup (default: off)").option("--proxy-port <port>","Portless proxy listen port for <release>.<package>.localhost URLs (default: 4000)").action(async(e,o)=>{let{runDevServerCli:t}=await import("./dev-server.js"),r=!!(u.stdout.isTTY&&u.stdin.isTTY);await t({...o,preset:e},r,x.enabled)});n.command("version").description("Print the installed infra-kit CLI version").action(async()=>{i(await Ee())});n.command("env-status").description("Show which env is loaded in this session (local introspection; no Doppler call)").action(async()=>{i(await le())});n.command("env-list").description("List available Doppler configs for the detected project").action(async()=>{i(await me())});n.command("init").description("Inject shell integration into .zshrc and sync repo agent-instruction files").action(async()=>{i(await _())});n.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 e=>{i(await ce({config:e.config}))});n.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 e=>{i(await de({purge:!!e.purge}))});n.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 e=>{await J({projectDir:e.projectDir})});n.commands.forEach(T);var xo=e=>e.startsWith("env-")||e==="init"||e==="doctor"||e==="version"||e==="dev";n.hook("preAction",async(e,o)=>{x.enabled=!!o.optsWithGlobals().json,x.enabled&&(a.level="warn"),xo(o.name())||await R({expectedTrigger:"cli-invocation"})});if(u.argv.length<=2){let e=P("release"),o=P("worktrees"),t=P("environment"),r=new Map(n.commands.map(d=>[d.name(),d])),c=[{label:"Release Management",names:e},{label:"Worktrees",names:o},{label:"Environment",names:t}].flatMap(({label:d,names:v})=>v.filter(f=>r.has(f)).map(f=>({name:f,description:r.get(f).description(),group:d}))),m=null;try{if(u.stdout.isTTY&&u.stdin.isTTY){let{runCommandPalette:d}=await import("./boot-SGM5RVLJ.js");m=await d(c)}else{let d=B(c.map(y=>[y.name,y.description])),v=new Map;c.forEach((y,h)=>{v.set(y.name,d[h]??y.name)});let f=y=>y.filter(h=>r.has(h)).map(h=>({name:v.get(h)??h,value:h}));m=await ko({message:"Select a command to run",choices:[new E(" "),new E("\u2014 Release Management \u2014"),...f(e),new E(" "),new E("\u2014 Worktrees \u2014"),...f(o),new E(" "),new E("\u2014 Environment \u2014"),...f(t)]},{output:u.stderr})}}catch(d){if(!O(d))throw d}m&&(Fe.value=!0,await Oe(["node","infra-kit",m]))}else await Oe();
20
20
  //# sourceMappingURL=cli.js.map