infra-kit 0.5.1 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{boot-AJLBGRED.js → boot-BL4EQMZV.js} +2 -2
  2. package/dist/chunk-744Y674Y.js +10 -0
  3. package/dist/{chunk-Q23IJP6D.js.map → chunk-744Y674Y.js.map} +4 -4
  4. package/dist/chunk-GF2CA4CD.js +2 -0
  5. package/dist/chunk-GF2CA4CD.js.map +7 -0
  6. package/dist/{chunk-Y3FV2BRI.js → chunk-IERRU26T.js} +2 -2
  7. package/dist/chunk-LCNORKDV.js +8 -0
  8. package/dist/chunk-LCNORKDV.js.map +7 -0
  9. package/dist/chunk-MFAFQVTW.js +2 -0
  10. package/dist/chunk-MFAFQVTW.js.map +7 -0
  11. package/dist/chunk-PQG63EIV.js +4 -0
  12. package/dist/chunk-PQG63EIV.js.map +7 -0
  13. package/dist/chunk-QJQQ7ENL.js +445 -0
  14. package/dist/chunk-QJQQ7ENL.js.map +7 -0
  15. package/dist/{chunk-HXBIE4JP.js → chunk-QUXDC3A3.js} +2 -2
  16. package/dist/{chunk-HXBIE4JP.js.map → chunk-QUXDC3A3.js.map} +1 -1
  17. package/dist/cli.js +3 -3
  18. package/dist/cli.js.map +2 -2
  19. package/dist/dev-server.js +4 -4
  20. package/dist/dev-server.js.map +1 -1
  21. package/dist/dev-wizard-run-DWDA5R3A.js +2 -0
  22. package/dist/{dev-wizard-run-FNMMYTDX.js.map → dev-wizard-run-DWDA5R3A.js.map} +1 -1
  23. package/dist/mcp-proxy.js +11 -0
  24. package/dist/mcp-proxy.js.map +7 -0
  25. package/dist/mcp.js +110 -1
  26. package/dist/mcp.js.map +4 -4
  27. package/dist/update-check.js +1 -1
  28. package/package.json +10 -9
  29. package/dist/chunk-6GWRKIZ7.js +0 -2
  30. package/dist/chunk-6GWRKIZ7.js.map +0 -7
  31. package/dist/chunk-D4UYDBN5.js +0 -3
  32. package/dist/chunk-D4UYDBN5.js.map +0 -7
  33. package/dist/chunk-Q23IJP6D.js +0 -10
  34. package/dist/chunk-Q4HKBLQG.js +0 -7
  35. package/dist/chunk-Q4HKBLQG.js.map +0 -7
  36. package/dist/chunk-X2POR5VH.js +0 -422
  37. package/dist/chunk-X2POR5VH.js.map +0 -7
  38. package/dist/dev-wizard-run-FNMMYTDX.js +0 -2
  39. /package/dist/{boot-AJLBGRED.js.map → boot-BL4EQMZV.js.map} +0 -0
  40. /package/dist/{chunk-Y3FV2BRI.js.map → chunk-IERRU26T.js.map} +0 -0
@@ -1,7 +1,7 @@
1
- import{L as f,b as O,d as y,ea as T}from"./chunk-Q4HKBLQG.js";import{q as k}from"./chunk-D4UYDBN5.js";import{a as S,b as P}from"./chunk-QWVLAZ6N.js";import u from"node:fs/promises";import c from"node:path";import{z as s}from"zod";var i=class extends Error{env;constructor(t,o=null){super(t),this.name="EnvAuthError",this.env=o}},L=e=>e instanceof i;import N from"node:fs/promises";import I from"node:os";var j=async e=>{try{return await N.access(e),!0}catch{return!1}},d=e=>{let t=I.homedir();return e.startsWith(t)?`~${e.slice(t.length)}`:e};var _="tokens.json",h=1,b=384,C=448,A=s.object({version:s.literal(h).default(h),envs:s.record(s.string().min(1),s.string().min(1))}).strict(),g=async()=>{let e=await T();return c.join(c.dirname(e.userProject),_)},v=async()=>{let e=await g(),t;try{t=await u.readFile(e,"utf-8")}catch{return null}let o;try{o=JSON.parse(t)}catch(n){throw new i(`Invalid JSON in the token store at ${d(e)}: ${n.message}
1
+ import{L as f,S as T,b as O,d as y}from"./chunk-LCNORKDV.js";import{r as k}from"./chunk-PQG63EIV.js";import{a as S,b as P}from"./chunk-QWVLAZ6N.js";import u from"node:fs/promises";import c from"node:path";import{z as s}from"zod";var i=class extends Error{env;constructor(t,o=null){super(t),this.name="EnvAuthError",this.env=o}},L=e=>e instanceof i;import N from"node:fs/promises";import I from"node:os";var j=async e=>{try{return await N.access(e),!0}catch{return!1}},d=e=>{let t=I.homedir();return e.startsWith(t)?`~${e.slice(t.length)}`:e};var _="tokens.json",h=1,b=384,C=448,A=s.object({version:s.literal(h).default(h),envs:s.record(s.string().min(1),s.string().min(1))}).strict(),g=async()=>{let e=await T();return c.join(c.dirname(e.userProject),_)},v=async()=>{let e=await g(),t;try{t=await u.readFile(e,"utf-8")}catch{return null}let o;try{o=JSON.parse(t)}catch(n){throw new i(`Invalid JSON in the token store at ${d(e)}: ${n.message}
2
2
  Fix or delete the file, then re-add the token with \`infra-kit env-token-set <env>\`.`)}let r=A.safeParse(o);if(!r.success)throw new i(`Invalid token store at ${d(e)}: ${s.prettifyError(r.error)}
3
3
  Fix or delete the file, then re-add the token with \`infra-kit env-token-set <env>\`.`);return r.data},x=async e=>{let t=await g(),o=c.dirname(t),r=c.dirname(o),n=c.dirname(r);for(let a of[n,r,o])await u.mkdir(a,{recursive:!0,mode:C}),await u.chmod(a,C);k(t,`${JSON.stringify(e,null,2)}
4
4
  `,b),await u.chmod(t,b)},F=async(e,t)=>{let o=await v();await x({version:h,envs:{...o?.envs,[e]:t}})},H=async e=>{let t=await v();if(!t||!(e in t.envs))return;let o={...t.envs};delete o[e],await x({version:h,envs:o})};var M=()=>{let e="",t=[],o=!1;return{start(r){e=r,t=[],o=!1},setInteractive(){o=!0},addOption(r,n){t.push({flag:r,value:n})},formatOptions(){return t.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(" ")},snapshot(){return t.length===0?null:{formattedOptions:this.formatOptions()}},print(){!o||t.length===0||!e||f.info(`\u{1F4DF} Equivalent command:
5
5
  pnpm exec infra-kit ${e} ${this.formatOptions()}
6
6
  `)},reset(){e="",t=[],o=!1}}},w=M();import D from"node:process";var R=27,J=e=>{if(typeof e=="object")return e.value;throw new O(void 0,{operation:"interactive prompt",remediation:e==="unreachable"?"this prompt is declared unreachable under MCP because a required field should block the path \u2014 reaching it means that field was relaxed without updating the call site":"pass the value explicitly instead of relying on the prompt \u2014 MCP runs have no human to answer it",stderrExcerpt:"an interactive prompt was reached under MCP, where stdin carries JSON-RPC"})},$=async(e,t)=>{let o=new AbortController,{whenHeadless:r="refuse",...n}=t??{},a={...n,signal:o.signal};if(y())return J(r);if(!D.stdin.isTTY)return e(a);let m=D.stdin,E=p=>{p.length===1&&p[0]===R&&o.abort()};S();try{let p=e(a);return m.isPaused()&&m.resume(),m.on("data",E),await p}finally{m.removeListener("data",E),P()}};import B from"@inquirer/confirm";import K from"node:process";var l=class extends Error{constructor(t="Operation cancelled by the operator"){super(t),this.name="CommandDeclinedError"}},ue=e=>e instanceof l;var q=async(e,t,o={})=>{let r=e?!0:await $(n=>B({message:t},n),{whenHeadless:"refuse"});if(e||w.setInteractive(),!r){if(o.throwOnDecline)throw new l;f.info("Operation cancelled. Exiting..."),K.exit(0)}};export{i as a,L as b,j as c,d,g as e,v as f,F as g,H as h,w as i,ue as j,$ as k,q as l};
7
- //# sourceMappingURL=chunk-HXBIE4JP.js.map
7
+ //# sourceMappingURL=chunk-QUXDC3A3.js.map
@@ -2,6 +2,6 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/lib/env-tokens/env-tokens.ts", "../src/lib/errors/env-auth-error.ts", "../src/lib/path-display/path-display.ts", "../src/lib/command-echo/command-echo.ts", "../src/lib/prompts/escapable-context.ts", "../src/lib/command-echo/confirm-or-exit.ts", "../src/lib/errors/command-declined-error.ts"],
4
4
  "sourcesContent": ["// DEFAULT import, deliberately \u2014 the same reason config-bootstrap.ts documents: the module is used as\n// `fs.readFile(...)` / `fs.mkdir(...)` so every call is a property lookup on the live module object,\n// which is what `vi.spyOn(fs, \u2026)` patches. A named import (`import { readFile }`) is bound at import\n// time and the spy silently does NOT intercept it \u2014 the assertion goes green while the real call\n// still hits disk. Do not \"modernize\" this import.\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { z } from 'zod'\n\nimport { atomicWriteFileSync } from 'src/lib/constants'\nimport { EnvAuthError } from 'src/lib/errors/env-auth-error'\nimport { getInfraKitConfigPaths } from 'src/lib/infra-kit-config'\nimport { tildify } from 'src/lib/path-display'\n\n/**\n * Basename of the token store. A SIBLING of the layer-3 `infra-kit.json`, never a key inside it:\n * `getInfraKitConfig` only ever globs the three hardcoded `infra-kit.json` paths and its schema is\n * `.strict()`, so an unknown key throws at the LAYER parse and bricks every command on every pinned\n * version. A sibling file is structurally invisible to that loader \u2014 `config-bootstrap.ts` already\n * relies on exactly this for its `.example.jsonc`. That invisibility is the blast-radius guarantee:\n * a corrupt `tokens.json` can only break the readers below, never `worktrees-list`.\n */\nconst TOKEN_STORE_FILE = 'tokens.json'\n\n/** Schema version of the on-disk store. Bump only alongside a migration. */\nconst TOKEN_STORE_VERSION = 1\n\n/** Mode of `tokens.json` itself \u2014 owner read/write only. It holds live credentials. */\nconst TOKEN_FILE_MODE = 0o600\n\n/** Mode of every directory on the way to the store \u2014 owner-only traversal. */\nconst TOKEN_DIR_MODE = 0o700\n\n/**\n * The store's OWN schema: `{ version?: 1, envs: { <env>: <token> } }`. Deliberately NOT derived from\n * `infraKitConfigObject`: this file is not a merge layer, so `.strict()` here is free (a typo is a\n * loud error in three readers, not a bricked CLI).\n */\n// HAND-AUTHORABILITY IS THE CONSTRAINT THIS SHAPE IS CHOSEN FOR, and two earlier fields were\n// dropped for it \u2014 do not re-add either without re-deciding that:\n//\n// - `version` is OPTIONAL (defaulted to 1) because a store typed by a human carries no version\n// key, and a store the CLI refuses to read is a store the human cannot write.\n// - `repoRoot` is GONE, and with it the refusal that compared it against the current checkout.\n// That guard was real: the store directory is keyed on `path.basename(mainRepoRoot)`, so\n// `~/work/api` and `~/oss/api` share ONE token directory and repo B could load (and then\n// overwrite) repo A's credentials. It was removed anyway, knowingly: it also made the CLI refuse\n// every hand-written store, which carries no such field. Hand-authorability wins; the\n// basename-collision risk is ACCEPTED. A weakened version of the guard (warn-and-continue, or\n// \"only check when present\") is NOT the compromise here \u2014 it would re-introduce the same refusal\n// for anyone who did write the field. If you want the guard back, key the store directory on\n// something collision-free instead.\nconst tokenStoreSchema = z\n .object({\n version: z.literal(TOKEN_STORE_VERSION).default(TOKEN_STORE_VERSION),\n envs: z.record(z.string().min(1), z.string().min(1)),\n })\n .strict()\n\n/** The parsed `tokens.json` shape: `{ version, envs }` \u2014 `version` defaulted to 1 when absent on disk. */\nexport type TokenStore = z.infer<typeof tokenStoreSchema>\n\n/**\n * Absolute path to the per-project token store: `~/.infra-kit/projects/<repo>/tokens.json`.\n *\n * Derived from {@link getInfraKitConfigPaths} rather than re-deriving the home/projects/repo chain \u2014\n * that resolver already keys the directory off the MAIN repo root (so every worktree of a repo shares\n * one store) and memoizes the two `git rev-parse` spawns.\n *\n * @example\n * await getTokenStorePath() // => '/Users/arthur/.infra-kit/projects/infra-kit/tokens.json'\n */\nexport const getTokenStorePath = async (): Promise<string> => {\n const paths = await getInfraKitConfigPaths()\n\n return path.join(path.dirname(paths.userProject), TOKEN_STORE_FILE)\n}\n\n/**\n * Read the token store, or `null` when it does not exist.\n *\n * ABSENCE IS NOT AN ERROR \u2014 a fresh machine has no store, and every caller must be able to say \"no\n * tokens yet\" without a try/catch. The two things that DO throw are malformed JSON and a schema\n * mismatch: both are states only a human can fix, so both are loud.\n *\n * @example\n * const store = await readTokenStore()\n * store?.envs.dev // => 'dp.st.dev.xxxx' | undefined\n * // no file on disk => null\n * // garbage on disk => throws 'Invalid token store at ~/.infra-kit/projects/api/tokens.json: \u2026'\n */\nexport const readTokenStore = async (): Promise<TokenStore | null> => {\n const storePath = await getTokenStorePath()\n\n let raw: string\n\n try {\n raw = await fs.readFile(storePath, 'utf-8')\n } catch {\n return null\n }\n\n // Every refusal below is an EnvAuthError, not a plain Error. A broken store is the MOST durable\n // env-auth failure there is \u2014 it cannot heal on a 30s retry, only a human can fix it \u2014 and env\n // auto-load's sticky marker is keyed on that CLASS. Thrown as a plain Error these were classified\n // TRANSIENT, so a corrupt store went silent on the backgrounded shell-startup spawn and the user's\n // env simply stopped loading with nothing ever said. `env` is null: no ONE environment is at fault.\n let parsedRaw: unknown\n\n try {\n parsedRaw = JSON.parse(raw)\n } catch (err) {\n throw new EnvAuthError(\n `Invalid JSON in the token store at ${tildify(storePath)}: ${(err as Error).message}\\n` +\n `Fix or delete the file, then re-add the token with \\`infra-kit env-token-set <env>\\`.`,\n )\n }\n\n const result = tokenStoreSchema.safeParse(parsedRaw)\n\n if (!result.success) {\n throw new EnvAuthError(\n `Invalid token store at ${tildify(storePath)}: ${z.prettifyError(result.error)}\\n` +\n `Fix or delete the file, then re-add the token with \\`infra-kit env-token-set <env>\\`.`,\n )\n }\n\n return result.data\n}\n\n/**\n * Persist the store atomically at 0600, tightening every directory on the way to it to 0700.\n *\n * The chmods are not belt-and-braces: the config bootstrap already seeds\n * `~/.infra-kit/projects/<repo>/` at the default umask (0755), so an EXISTING directory is\n * world-readable and `mkdir`'s mode argument is a no-op for it. The same holds for the file \u2014 a\n * hand-written `tokens.json` is 0644 \u2014 so the modes of the whole credential path are (re)asserted on\n * EVERY write, not only on the writes that created it. Self-healing by construction: a plain\n * `env-token-set` is enough to fix a store a human typed.\n *\n * @example\n * await writeTokenStore({ version: 1, envs: { dev: 'dp.st.dev.x' } })\n * // ~/.infra-kit/projects/api/tokens.json -rw-------\n * // ~/.infra-kit/projects/api/ drwx------\n */\nexport const writeTokenStore = async (store: TokenStore): Promise<void> => {\n const storePath = await getTokenStorePath()\n\n const projectDir = path.dirname(storePath)\n const projectsDir = path.dirname(projectDir)\n const userConfigDir = path.dirname(projectsDir)\n\n for (const dir of [userConfigDir, projectsDir, projectDir]) {\n await fs.mkdir(dir, { recursive: true, mode: TOKEN_DIR_MODE })\n await fs.chmod(dir, TOKEN_DIR_MODE)\n }\n\n atomicWriteFileSync(storePath, `${JSON.stringify(store, null, 2)}\\n`, TOKEN_FILE_MODE)\n\n // The rename above already lands a fresh 0600 inode, but the mode of a credential file is not a\n // thing to leave to the umask that happened to be in effect \u2014 assert it outright.\n await fs.chmod(storePath, TOKEN_FILE_MODE)\n}\n\n/**\n * Add or replace one environment's token, read-modify-write, stamping the current `version`. Inherits\n * {@link readTokenStore}'s refusals: a corrupt store is never silently overwritten.\n *\n * @example\n * await setToken('dev', 'dp.st.dev.xxxxxxxx')\n * // tokens.json => { version: 1, envs: { dev: 'dp.st.dev.xxxxxxxx' } }\n */\nexport const setToken = async (env: string, token: string): Promise<void> => {\n const existing = await readTokenStore()\n\n await writeTokenStore({\n version: TOKEN_STORE_VERSION,\n envs: { ...existing?.envs, [env]: token },\n })\n}\n\n/**\n * Drop one environment's token. A no-op when the store (or the key) is absent \u2014 removing something\n * that is not there is the state the caller asked for.\n *\n * Removing a token here does NOT revoke it in Doppler; callers say so.\n *\n * @example\n * await removeToken('dev')\n * // tokens.json => { version: 1, envs: {} }\n */\nexport const removeToken = async (env: string): Promise<void> => {\n const existing = await readTokenStore()\n\n if (!existing || !(env in existing.envs)) return\n\n const envs = { ...existing.envs }\n\n delete envs[env]\n\n await writeTokenStore({\n version: TOKEN_STORE_VERSION,\n envs,\n })\n}\n", "/**\n * The DURABLE, USER-FIXABLE env-auth failure \u2014 as a TYPE rather than a string.\n *\n * Throw this (or a subclass) \u2014 never a plain `Error` \u2014 for anything a user must fix before\n * env-load can work: no token minted, a corrupt token store, a foreign token store. Only this\n * class earns the STICKY marker that survives the backgrounded shell-startup spawn and is replayed\n * onto the next interactive command. `DopplerAuthError extends EnvAuthError` for the subset that\n * is specifically \"Doppler refused the token we sent\".\n *\n * @example\n * // no token minted for this env (integrations/doppler/token-resolver.ts)\n * throw new EnvAuthError(buildMissingTokenMessage('dev', storePath), 'dev')\n * @example\n * // the token store itself is unusable \u2014 no single env is at fault, so `env` stays null\n * throw new EnvAuthError(`Invalid JSON in the token store at ${storePath} \u2026`)\n */\n// WHY THIS EXISTS (and why it is deliberately NOT a Doppler type): env auto-load must tell a\n// TRANSIENT failure (network, timeout, Doppler down \u2014 heals itself, 30s backoff, stay quiet) apart\n// from a DURABLE one (never heals, and only a human can fix it). A durable failure misclassified as\n// transient is TOTAL SILENCE, forever, on the one path the whole feature exists to survive.\n//\n// That classification therefore has to travel WITH the error, not be re-derived by grepping a\n// message some other module owns \u2014 and it has to be throwable from EVERY module that can produce\n// the class. `lib/env-tokens` (the store) is a layer BELOW `integrations/doppler`, so a\n// Doppler-owned base type could not be thrown there without a layering inversion and an import\n// cycle. Hence a neutral base here in `lib/errors`.\n//\n// The defect class this closes is \"a durable failure that is not type-detectable\". Adding a new\n// durable failure and forgetting to type it is exactly how this shipped silent twice.\nexport class EnvAuthError extends Error {\n /**\n * The env (= Doppler config) whose token is at fault, or `null` when the failure is not attributable\n * to ONE env (a corrupt or foreign token store breaks every env at once). `null` is honest: do NOT\n * invent an env to fill it \u2014 the consumer names the env it was TRYING to load, which it already\n * knows.\n */\n readonly env: string | null\n\n constructor(message: string, env: string | null = null) {\n super(message)\n this.name = 'EnvAuthError'\n this.env = env\n }\n}\n\n/**\n * The TYPE GUARD every consumer of the auth channel classifies on \u2014 the single question \"is this a\n * durable, user-fixable env-auth failure?\", answered by the error's CLASS and never by its text.\n *\n * This is the default `isAuthFailure` of `lib/env-autoload`'s `runEnvAutoLoad`. It is deliberately\n * WIDER than `isDopplerAuthError`: that one means \"Doppler refused us\", which is only one of the\n * durable failures \u2014 a missing token never reaches Doppler at all, and a corrupt store never reaches\n * the resolver.\n *\n * @example\n * isEnvAuthFailure(new EnvAuthError('No Doppler service token for env \"dev\".', 'dev')) // => true\n * isEnvAuthFailure(new DopplerAuthError('dev', 'revoked')) // => true (subclass)\n * isEnvAuthFailure(new Error('connect ETIMEDOUT')) // => false (transient)\n */\nexport const isEnvAuthFailure = (error: unknown): error is EnvAuthError => {\n return error instanceof EnvAuthError\n}\n", "import fs from 'node:fs/promises'\nimport os from 'node:os'\n\n/**\n * Shared path-display helpers for CLI output. Extracted from `commands/config`\n * and `commands/init/migrate-config` (which each had private copies) so config\n * surfaces print paths consistently. Keep this dependency-light (node builtins\n * only) \u2014 it is imported by several commands.\n */\n\n/**\n * Resolve whether a file is reachable, suppressing ENOENT (and any access error)\n * into a boolean. Used to render `[\u2713]`/`[ ]` existence markers.\n *\n * @example\n * await fileExists('/etc/hosts') // => true\n * await fileExists('/nope.txt') // => false\n */\nexport const fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath)\n\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Replace the user's home prefix with `~` so logged paths stay short and portable\n * across machines. Leaves non-home paths untouched.\n *\n * @example\n * // os.homedir() === '/Users/arthur'\n * tildify('/Users/arthur/.infra-kit/infra-kit.json') // => '~/.infra-kit/infra-kit.json'\n * tildify('/etc/hosts') // => '/etc/hosts'\n */\nexport const tildify = (filePath: string): string => {\n const home = os.homedir()\n\n return filePath.startsWith(home) ? `~${filePath.slice(home.length)}` : filePath\n}\n", "import { logger } from 'src/lib/logger'\n\ninterface CommandOption {\n flag: string\n value: string | string[] | boolean\n}\n\nconst createCommandEcho = () => {\n let cliPath = ''\n let options: CommandOption[] = []\n let isInteractive = false\n\n return {\n /**\n * Bind the echo to the command about to run, and clear the previous one's recording.\n *\n * `cliPath` must be the argv Commander actually parsed (`release create`), which is why the ONLY\n * caller is program.ts's `preAction` hook, feeding it `commandPath(actionCommand)`. Commands used to\n * name themselves here, and that is precisely how the printed line rotted: they passed the flat\n * `release-create`, which stopped being a command the day the flat aliases were dropped, so the\n * \"equivalent command\" we told the user to retype no longer parsed. Commander is the only thing that\n * knows the real path, so it is the only thing allowed to say it.\n */\n start(path: string): void {\n cliPath = path\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 *\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 * Format the tracked options into a replayable flag string (empty when none).\n */\n formatOptions(): string {\n return 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\n /**\n * The recorded interactive flags for the session shell's report side channel, so the transcript's\n * equivalent line carries the resolved options (`--versions \"1.2.5\"`), not just the bare command.\n * `null` when nothing was recorded \u2014 the caller then falls back to the bare spawned argv.\n */\n snapshot(): { formattedOptions: string } | null {\n if (options.length === 0) {\n return null\n }\n\n return { formattedOptions: this.formatOptions() }\n },\n\n /**\n * Print the equivalent CLI command if there was interactive input.\n *\n * Silent without a bound `cliPath`: the only caller that binds one is Commander's `preAction`, so an\n * unbound echo means the command ran off the CLI (an MCP tool), where a `pnpm exec` line would be\n * nonsense. Printing a path-less `pnpm exec infra-kit --yes` would be worse than printing nothing.\n */\n print(): void {\n if (!isInteractive || options.length === 0 || !cliPath) {\n return\n }\n\n logger.info(`\uD83D\uDCDF Equivalent command: \\npnpm exec infra-kit ${cliPath} ${this.formatOptions()}\\n`)\n },\n\n /**\n * Reset state (useful for testing)\n */\n reset(): void {\n cliPath = ''\n options = []\n isInteractive = false\n },\n }\n}\n\n// Singleton instance (same pattern as logger)\nexport const commandEcho = createCommandEcho()\n", "import type { Buffer } from 'node:buffer'\nimport process from 'node:process'\n\nimport { OperationError } from 'src/lib/errors/operation-error'\nimport { isMcpMode } from 'src/lib/mcp-mode'\n\nimport { acquireStdin, releaseStdin } from './stdin-ref'\n\n/**\n * Structural mirror of `@inquirer/type`'s `Context` \u2014 the second argument every\n * `@inquirer/*` prompt takes. Declared here rather than imported because\n * `@inquirer/core` and `@inquirer/type` are transitive dependencies of this\n * package, not direct ones (only the four prompt packages are).\n */\nexport interface PromptContext {\n input?: NodeJS.ReadableStream\n output?: NodeJS.WritableStream\n clearPromptOnDone?: boolean\n signal?: AbortSignal\n}\n\n/**\n * What this call site does when there is no human: refuse, assert the path is blocked upstream, or\n * answer with a fixed value.\n *\n * `'unreachable'` is a CLAIM about the owning tool's MCP schema \u2014 \"a required field stops an agent\n * reaching this prompt at all\" \u2014 and it is checkable, which `'refuse'` is not. Reaching it at\n * runtime means the claim is false, so it throws rather than degrading quietly.\n */\nexport type HeadlessPolicy<T> = 'refuse' | 'unreachable' | { value: T }\n\n/** {@link withEscape}'s options: the prompt context, plus this site's headless policy. */\nexport type EscapeOptions<T> = PromptContext & { whenHeadless?: HeadlessPolicy<T> }\n\nconst ESC_BYTE = 0x1b\n\n// Widening the existing options object rather than adding a third parameter: six call sites already\n// pass a `PromptContext` (`entry/cli.ts`, `env-load`, `env-token-set`, and the wizard's shared\n// context used by three prompts), all of them `{ output: process.stderr }` for documented reasons \u2014\n// `env-token-set.ts:64-68` notes that some env commands run inside `$(\u2026)`, where a prompt on stdout\n// would leave the user typing a credential into a captured stream. A third parameter would have\n// forced every one of them to grow a positional `undefined`.\n//\n// Optional, and the default is `'refuse'`. That default is safe for the same reason the guard exists\n// at all: under `isMcpMode()` `process.stdin` IS the JSON-RPC transport, so no prompt at any site can\n// return a usable answer, and refusing removes no capability that exists. It is what CLI-only sites\n// (the palette, the dev wizard, `env-token-set`) keep, because they face a real human.\n//\n// It is NOT what an MCP-reachable site may keep. There, omitting `whenHeadless` is indistinguishable\n// from never having considered the question, so G7 (`every-inquirer-site-is-escapable`) requires the\n// answer to be written out \u2014 `'refuse'` included.\n//\n// What the default is NOT is *correct* everywhere \u2014 that distinction is the whole point of the\n// parameter. `worktrees-add` documents a `false` fallback for MCP in its own schema, so a refusal\n// there is right-outcome-by-accident at best and a regression at worst. The type cannot tell those\n// apart; G6 (description-to-policy) and G8 (schema-to-`'unreachable'`) are what check the answers,\n// because a wrong answer here compiles and can never fail a test on its own.\nconst resolveHeadless = <T>(policy: HeadlessPolicy<T>): T => {\n if (typeof policy === 'object') return policy.value\n\n throw new OperationError(undefined, {\n operation: 'interactive prompt',\n remediation:\n policy === 'unreachable'\n ? 'this prompt is declared unreachable under MCP because a required field should block the path \u2014 ' +\n 'reaching it means that field was relaxed without updating the call site'\n : 'pass the value explicitly instead of relying on the prompt \u2014 MCP runs have no human to answer it',\n stderrExcerpt: 'an interactive prompt was reached under MCP, where stdin carries JSON-RPC',\n })\n}\n\n/**\n * Run an `@inquirer/*` prompt with Esc bound to cancellation. On Esc the prompt rejects with an\n * `AbortPromptError`, which `entry/cli.ts` already catches at the top level -> \"Operation\n * cancelled.\" -> exit 0.\n *\n * The listener attaches only on a real interactive terminal. `isTTY` alone is NOT a sufficient\n * guard \u2014 see lib/mcp-mode for why `stdio: 'inherit'` defeats it.\n */\n// The cancel path is entirely pre-existing plumbing: an `AbortController`'s signal goes into the\n// prompt context, `@inquirer/core` rejects with an `AbortPromptError` when it fires, and that name\n// is already in `CANCELLATION_ERROR_NAMES` (lib/errors/is-prompt-cancellation). All this adds is\n// the thing that calls `abort()`.\n//\n// DETECTION \u2014 a raw `data` listener that aborts iff the chunk is exactly one `0x1b` byte.\n// Deliberately NOT readline `keypress`: that delivers a lone Esc only after its escape-sequence\n// timeout (~523ms measured), and the `escapeCodeTimeout` dial that would shorten it is\n// process-global and first-armer-wins \u2014 a second `emitKeypressEvents` call on `process.stdin` is\n// SILENTLY IGNORED, so the dial degrades to a no-op with no error and no failing test. The raw\n// listener costs ~24ms and touches no global state. Arrow keys (`\\x1b[B`, 3 bytes) and Alt combos\n// (`\\x1bb`, 2 bytes) are ignored because they are LONGER \u2014 no parsed key field is involved. Node\n// `data` listeners broadcast, so this one does not steal bytes from readline: arrows and Enter keep\n// working while it is attached.\n//\n// FALSE POSITIVES are a non-issue: there is no routine source of a lone-`0x1b` chunk. Focus events\n// (`\\x1b[I`/`\\x1b[O`), bracketed paste (`\\x1b[200~`) and mouse reports (`\\x1b[M\u2026`) are all\n// multi-byte, and inquirer enables none of them.\n//\n// FALSE NEGATIVE \u2014 measured and accepted: a COALESCED Esc (`\"\\x1bx\"` arriving in ONE chunk, i.e.\n// Esc immediately followed by another key) is silently dropped and the prompt stays open. The fix\n// would be Ink's 20ms deferred abort \u2014 hold the lone Esc, cancel it if a follow-up chunk lands.\n// That is a logged NON-GOAL; do not pre-build it.\nexport const withEscape = async <T>(\n run: (context: PromptContext) => Promise<T>,\n base?: EscapeOptions<T>,\n): Promise<T> => {\n const controller = new AbortController()\n // `whenHeadless` is ours, not inquirer's \u2014 split it off so it never reaches a prompt's context.\n const { whenHeadless = 'refuse', ...promptBase } = base ?? {}\n const context: PromptContext = { ...promptBase, signal: controller.signal }\n\n // Keyed on `isMcpMode()` alone. `!isTTY` deliberately does NOT ride along: a piped-but-human run\n // (`infra-kit worktrees add > log.txt`) has no TTY and still deserves its prompt, and the two\n // conditions were only ever collapsed here because both merely skipped the Esc listener. Now that\n // this branch decides an ANSWER rather than just a listener, conflating them would refuse prompts\n // no MCP server is waiting on. `release-picker.ts` keeps its own `!isTTY` clause for its own reason.\n if (isMcpMode()) return resolveHeadless(whenHeadless)\n\n if (!process.stdin.isTTY) return run(context)\n\n const input = process.stdin\n\n const onData = (chunk: Buffer) => {\n if (chunk.length === 1 && chunk[0] === ESC_BYTE) controller.abort()\n }\n\n // STDIN OWNERSHIP \u2014 this is the seam that keeps stdin alive for inquirer, which refs\n // nothing itself. It must sit BELOW the early return above: a non-TTY run never reads\n // stdin, so acquiring there would ref a handle nobody reads and hang the process. See\n // lib/prompts/stdin-ref for why an unref'd stdin kills a prompt with exit 13.\n acquireStdin()\n\n try {\n // Attach after the prompt has built its readline interface. Cheap ordering defence\n // only \u2014 there is no race today: readline attaches its own consumer synchronously\n // and `data` events are async.\n //\n // INSIDE the `try`, both of them: `run` is caller-supplied and `@inquirer/core` runs\n // synchronously up to readline construction, so a SYNCHRONOUS throw here would skip a\n // `finally` placed any lower \u2014 leaking the ref and hanging the CLI, which is the exact\n // failure this module exists to prevent.\n const pending = run(context)\n\n // Explicit, because `on('data')` alone is NOT enough to start the flow: `Readable` resumes on a\n // new `data` listener only when `flowing !== false`, and the palette's teardown now pauses stdin\n // (tui/boot.tsx) so a prompt opened after an Ink screen inherits `flowing === false`. Esc would be\n // silently dead. It happens to work anyway today \u2014 `run()` above builds inquirer's readline, whose\n // constructor resumes the stream \u2014 but that is an undocumented side effect of a TRANSITIVE\n // dependency, and the ordering it relies on is the very thing the comment below reserves the right\n // to change. One idempotent call is cheaper than that coupling.\n if (input.isPaused()) input.resume()\n\n input.on('data', onData)\n\n return await pending\n } finally {\n // Non-negotiable: the dev wizard runs ~5 prompts back to back, so a leaked\n // listener would abort the next one.\n input.removeListener('data', onData)\n // Same `finally` as the listener teardown, and for the same reason: every exit path\n // (answer, Esc abort, throw) must give the ref back, or node never exits.\n releaseStdin()\n }\n}\n", "/**\n * @fileoverview\n * Shared interactive-confirmation gate for mutating commands.\n *\n * When `confirmedCommand` is truthy (CLI `--yes` or an MCP call, which always\n * injects `confirmedCommand: true`) the prompt is skipped and execution\n * proceeds. Otherwise it prompts the user, marks the echo as interactive, and\n * exits cleanly if the user declines.\n *\n * Esc aborts the prompt (via `withEscape`) rather than answering it \u2014 the caller\n * sees an `AbortPromptError`, which every command's boundary already treats as a\n * clean back-out through `isPromptCancellation`. That is deliberately NOT the same\n * as declining: a decline is an answer of \"no\" and takes the `process.exit(0)` path\n * below, whereas Esc means \"I never answered\".\n *\n * Unlike every other prompt in the CLI this one renders to STDOUT, not stderr \u2014\n * left as-is, since changing it would move confirmation text out of the stream\n * callers have always read it from.\n *\n * The `process.exit(0)` only runs on the interactive-decline path, which is\n * unreachable under MCP \u2014 so this stays safe for the long-lived MCP server.\n *\n * Callers remain responsible for their own `commandEcho.addOption('--yes', \u2026)`\n * bookkeeping, which varies between commands.\n */\nimport confirm from '@inquirer/confirm'\nimport process from 'node:process'\n\nimport { CommandDeclinedError } from 'src/lib/errors/command-declined-error'\nimport { logger } from 'src/lib/logger'\nimport { withEscape } from 'src/lib/prompts/escapable-context'\n\nimport { commandEcho } from './command-echo'\n\nexport interface ConfirmOrExitOptions {\n /**\n * Throw {@link CommandDeclinedError} on a decline instead of `process.exit(0)`.\n *\n * **Defaults to false, and must stay that way.** Seven commands call this\n * helper; flipping the default would change the exit semantics of all of them\n * at once, inside a change whose tests cover none of them.\n *\n * Opt in when the command has already acquired something that needs releasing\n * by the time it prompts \u2014 `process.exit` skips every `finally`, so for those\n * commands the decline path is precisely where a resource leaks. The caller\n * owns catching it and exiting 0: a decline is a successful outcome.\n */\n throwOnDecline?: boolean\n}\n\nexport const confirmOrExit = async (\n confirmedCommand: boolean | undefined,\n message: string,\n options: ConfirmOrExitOptions = {},\n): Promise<void> => {\n const answer = confirmedCommand\n ? true\n : await withEscape(\n (context) => {\n return confirm({ message }, context)\n },\n // Refuse is the ANSWER, not an oversight: the ternary above short-circuits on `confirmedCommand`,\n // which the MCP chokepoint injects into every call it lets through, and all seven callers pass it.\n { whenHeadless: 'refuse' },\n )\n\n if (!confirmedCommand) {\n commandEcho.setInteractive()\n }\n\n if (!answer) {\n if (options.throwOnDecline) {\n throw new CommandDeclinedError()\n }\n\n logger.info('Operation cancelled. Exiting...')\n process.exit(0)\n }\n}\n", "/**\n * The operator answered \"no\" at a confirmation prompt.\n *\n * Deliberately NOT an {@link OperationError}: nothing failed. It is also not a prompt\n * cancellation \u2014 Esc means \"I never answered\" and has its own handling, while this means\n * \"I answered, and the answer was no.\" Callers that opt in via `throwOnDecline` are\n * responsible for catching it and exiting 0.\n */\n// It exists so a command that acquired a resource before prompting \u2014 a scratch worktree, a temp\n// directory, a lock \u2014 can unwind through its own `finally` blocks and release it, instead of\n// `confirmOrExit` calling `process.exit(0)` and skipping every one of them.\n//\n// That distinction is load-bearing rather than stylistic. In a plan -> confirm -> apply design,\n// declining is a first-class expected outcome, not an edge case, so the most ordinary non-happy\n// path is exactly the one that would leak.\nexport class CommandDeclinedError extends Error {\n constructor(message = 'Operation cancelled by the operator') {\n super(message)\n this.name = 'CommandDeclinedError'\n }\n}\n\n/** Narrow an unknown catch binding to a decline. */\nexport const isCommandDeclined = (error: unknown): error is CommandDeclinedError => {\n return error instanceof CommandDeclinedError\n}\n"],
5
- "mappings": "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",
5
+ "mappings": "oJAKA,OAAOA,MAAQ,mBACf,OAAOC,MAAU,YACjB,OAAS,KAAAC,MAAS,MCsBX,IAAMC,EAAN,cAA2B,KAAM,CAO7B,IAET,YAAYC,EAAiBC,EAAqB,KAAM,CACtD,MAAMD,CAAO,EACb,KAAK,KAAO,eACZ,KAAK,IAAMC,CACb,CACF,EAgBaC,EAAoBC,GACxBA,aAAiBJ,EC5D1B,OAAOK,MAAQ,mBACf,OAAOC,MAAQ,UAiBR,IAAMC,EAAa,MAAOC,GAAuC,CACtE,GAAI,CACF,aAAMH,EAAG,OAAOG,CAAQ,EAEjB,EACT,MAAQ,CACN,MAAO,EACT,CACF,EAWaC,EAAWD,GAA6B,CACnD,IAAME,EAAOJ,EAAG,QAAQ,EAExB,OAAOE,EAAS,WAAWE,CAAI,EAAI,IAAIF,EAAS,MAAME,EAAK,MAAM,CAAC,GAAKF,CACzE,EFnBA,IAAMG,EAAmB,cAGnBC,EAAsB,EAGtBC,EAAkB,IAGlBC,EAAiB,IAqBjBC,EAAmBC,EACtB,OAAO,CACN,QAASA,EAAE,QAAQJ,CAAmB,EAAE,QAAQA,CAAmB,EACnE,KAAMI,EAAE,OAAOA,EAAE,OAAO,EAAE,IAAI,CAAC,EAAGA,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CACrD,CAAC,EACA,OAAO,EAeGC,EAAoB,SAA6B,CAC5D,IAAMC,EAAQ,MAAMC,EAAuB,EAE3C,OAAOC,EAAK,KAAKA,EAAK,QAAQF,EAAM,WAAW,EAAGP,CAAgB,CACpE,EAeaU,EAAiB,SAAwC,CACpE,IAAMC,EAAY,MAAML,EAAkB,EAEtCM,EAEJ,GAAI,CACFA,EAAM,MAAMC,EAAG,SAASF,EAAW,OAAO,CAC5C,MAAQ,CACN,OAAO,IACT,CAOA,IAAIG,EAEJ,GAAI,CACFA,EAAY,KAAK,MAAMF,CAAG,CAC5B,OAASG,EAAK,CACZ,MAAM,IAAIC,EACR,sCAAsCC,EAAQN,CAAS,CAAC,KAAMI,EAAc,OAAO;AAAA,sFAErF,CACF,CAEA,IAAMG,EAASd,EAAiB,UAAUU,CAAS,EAEnD,GAAI,CAACI,EAAO,QACV,MAAM,IAAIF,EACR,0BAA0BC,EAAQN,CAAS,CAAC,KAAKN,EAAE,cAAca,EAAO,KAAK,CAAC;AAAA,sFAEhF,EAGF,OAAOA,EAAO,IAChB,EAiBaC,EAAkB,MAAOC,GAAqC,CACzE,IAAMT,EAAY,MAAML,EAAkB,EAEpCe,EAAaZ,EAAK,QAAQE,CAAS,EACnCW,EAAcb,EAAK,QAAQY,CAAU,EACrCE,EAAgBd,EAAK,QAAQa,CAAW,EAE9C,QAAWE,IAAO,CAACD,EAAeD,EAAaD,CAAU,EACvD,MAAMR,EAAG,MAAMW,EAAK,CAAE,UAAW,GAAM,KAAMrB,CAAe,CAAC,EAC7D,MAAMU,EAAG,MAAMW,EAAKrB,CAAc,EAGpCsB,EAAoBd,EAAW,GAAG,KAAK,UAAUS,EAAO,KAAM,CAAC,CAAC;AAAA,EAAMlB,CAAe,EAIrF,MAAMW,EAAG,MAAMF,EAAWT,CAAe,CAC3C,EAUawB,EAAW,MAAOC,EAAaC,IAAiC,CAC3E,IAAMC,EAAW,MAAMnB,EAAe,EAEtC,MAAMS,EAAgB,CACpB,QAASlB,EACT,KAAM,CAAE,GAAG4B,GAAU,KAAM,CAACF,CAAG,EAAGC,CAAM,CAC1C,CAAC,CACH,EAYaE,EAAc,MAAOH,GAA+B,CAC/D,IAAME,EAAW,MAAMnB,EAAe,EAEtC,GAAI,CAACmB,GAAY,EAAEF,KAAOE,EAAS,MAAO,OAE1C,IAAME,EAAO,CAAE,GAAGF,EAAS,IAAK,EAEhC,OAAOE,EAAKJ,CAAG,EAEf,MAAMR,EAAgB,CACpB,QAASlB,EACT,KAAA8B,CACF,CAAC,CACH,EGrMA,IAAMC,EAAoB,IAAM,CAC9B,IAAIC,EAAU,GACVC,EAA2B,CAAC,EAC5BC,EAAgB,GAEpB,MAAO,CAWL,MAAMC,EAAoB,CACxBH,EAAUG,EACVF,EAAU,CAAC,EACXC,EAAgB,EAClB,EAMA,gBAAuB,CACrBA,EAAgB,EAClB,EAQA,UAAUE,EAAcC,EAA0C,CAChEJ,EAAQ,KAAK,CAAE,KAAAG,EAAM,MAAAC,CAAM,CAAC,CAC9B,EAKA,eAAwB,CACtB,OAAOJ,EACJ,IAAKK,GACA,OAAOA,EAAI,OAAU,UAChBA,EAAI,MAAQA,EAAI,KAAO,GAG5B,MAAM,QAAQA,EAAI,KAAK,EAClB,GAAGA,EAAI,IAAI,KAAKA,EAAI,MAAM,KAAK,IAAI,CAAC,IAGtC,GAAGA,EAAI,IAAI,KAAKA,EAAI,KAAK,GACjC,EACA,OAAO,OAAO,EACd,KAAK,GAAG,CACb,EAOA,UAAgD,CAC9C,OAAIL,EAAQ,SAAW,EACd,KAGF,CAAE,iBAAkB,KAAK,cAAc,CAAE,CAClD,EASA,OAAc,CACR,CAACC,GAAiBD,EAAQ,SAAW,GAAK,CAACD,GAI/CO,EAAO,KAAK;AAAA,sBAAgDP,CAAO,IAAI,KAAK,cAAc,CAAC;AAAA,CAAI,CACjG,EAKA,OAAc,CACZA,EAAU,GACVC,EAAU,CAAC,EACXC,EAAgB,EAClB,CACF,CACF,EAGaM,EAAcT,EAAkB,EC1G7C,OAAOU,MAAa,eAiCpB,IAAMC,EAAW,GAuBXC,EAAsBC,GAAiC,CAC3D,GAAI,OAAOA,GAAW,SAAU,OAAOA,EAAO,MAE9C,MAAM,IAAIC,EAAe,OAAW,CAClC,UAAW,qBACX,YACED,IAAW,cACP,8KAEA,wGACN,cAAe,2EACjB,CAAC,CACH,EAiCaE,EAAa,MACxBC,EACAC,IACe,CACf,IAAMC,EAAa,IAAI,gBAEjB,CAAE,aAAAC,EAAe,SAAU,GAAGC,CAAW,EAAIH,GAAQ,CAAC,EACtDI,EAAyB,CAAE,GAAGD,EAAY,OAAQF,EAAW,MAAO,EAO1E,GAAII,EAAU,EAAG,OAAOV,EAAgBO,CAAY,EAEpD,GAAI,CAACI,EAAQ,MAAM,MAAO,OAAOP,EAAIK,CAAO,EAE5C,IAAMG,EAAQD,EAAQ,MAEhBE,EAAUC,GAAkB,CAC5BA,EAAM,SAAW,GAAKA,EAAM,CAAC,IAAMf,GAAUO,EAAW,MAAM,CACpE,EAMAS,EAAa,EAEb,GAAI,CASF,IAAMC,EAAUZ,EAAIK,CAAO,EAS3B,OAAIG,EAAM,SAAS,GAAGA,EAAM,OAAO,EAEnCA,EAAM,GAAG,OAAQC,CAAM,EAEhB,MAAMG,CACf,QAAE,CAGAJ,EAAM,eAAe,OAAQC,CAAM,EAGnCI,EAAa,CACf,CACF,EC1IA,OAAOC,MAAa,oBACpB,OAAOC,MAAa,eCXb,IAAMC,EAAN,cAAmC,KAAM,CAC9C,YAAYC,EAAU,sCAAuC,CAC3D,MAAMA,CAAO,EACb,KAAK,KAAO,sBACd,CACF,EAGaC,GAAqBC,GACzBA,aAAiBH,ED0BnB,IAAMI,EAAgB,MAC3BC,EACAC,EACAC,EAAgC,CAAC,IACf,CAClB,IAAMC,EAASH,EACX,GACA,MAAMI,EACHC,GACQC,EAAQ,CAAE,QAAAL,CAAQ,EAAGI,CAAO,EAIrC,CAAE,aAAc,QAAS,CAC3B,EAMJ,GAJKL,GACHO,EAAY,eAAe,EAGzB,CAACJ,EAAQ,CACX,GAAID,EAAQ,eACV,MAAM,IAAIM,EAGZC,EAAO,KAAK,iCAAiC,EAC7CC,EAAQ,KAAK,CAAC,CAChB,CACF",
6
6
  "names": ["fs", "path", "z", "EnvAuthError", "message", "env", "isEnvAuthFailure", "error", "fs", "os", "fileExists", "filePath", "tildify", "home", "TOKEN_STORE_FILE", "TOKEN_STORE_VERSION", "TOKEN_FILE_MODE", "TOKEN_DIR_MODE", "tokenStoreSchema", "z", "getTokenStorePath", "paths", "getInfraKitConfigPaths", "path", "readTokenStore", "storePath", "raw", "fs", "parsedRaw", "err", "EnvAuthError", "tildify", "result", "writeTokenStore", "store", "projectDir", "projectsDir", "userConfigDir", "dir", "atomicWriteFileSync", "setToken", "env", "token", "existing", "removeToken", "envs", "createCommandEcho", "cliPath", "options", "isInteractive", "path", "flag", "value", "opt", "logger", "commandEcho", "process", "ESC_BYTE", "resolveHeadless", "policy", "OperationError", "withEscape", "run", "base", "controller", "whenHeadless", "promptBase", "context", "isMcpMode", "process", "input", "onData", "chunk", "acquireStdin", "pending", "releaseStdin", "confirm", "process", "CommandDeclinedError", "message", "isCommandDeclined", "error", "confirmOrExit", "confirmedCommand", "message", "options", "answer", "withEscape", "context", "confirm", "commandEcho", "CommandDeclinedError", "logger", "process"]
7
7
  }
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{$ as Kt,A as kt,B as Rt,C as Pt,D as te,E as xt,F as D,G as m,H as Ct,I as bt,J as At,K as Tt,L as Dt,M as It,N as Lt,O as _t,P as Nt,Q as Ot,R as Mt,S as Ft,T as $t,U as jt,V as oe,W as Ut,X as qt,Y as Gt,Z as Wt,_ as Vt,a as N,aa as Ht,b as Ue,ba as Jt,c as qe,ca as Bt,da as Yt,e as We,ea as zt,f as Ve,g as He,ga as Xt,h as Je,ha as Qt,i as O,ia as Zt,j as rt,k as j,l as st,m as at,n as ct,o as lt,p as dt,q as ut,r as mt,s as pt,t as ee,u as ft,v as gt,w as ht,x as vt,y as wt,z as St}from"./chunk-X2POR5VH.js";import{y as re}from"./chunk-Q23IJP6D.js";import{a as ne,b as ie,c as se}from"./chunk-Y3FV2BRI.js";import{a as Fe,f as yt,h as U,j as q}from"./chunk-6GWRKIZ7.js";import{a as Ge}from"./chunk-XJOY7J3G.js";import{a as Et}from"./chunk-TVG47UHG.js";import{b as Ke,c as E,d as h,e as F,f as et,g as tt,h as ot,i as P,k as $}from"./chunk-HXBIE4JP.js";import{L as l,a as Oe,ea as Q,fa as $e,ha as je,r as Me}from"./chunk-Q4HKBLQG.js";import{a as M,b as Be,d as Ye,f as ze,g as Xe,j as Z,k as Qe,n as w,q as Ze}from"./chunk-D4UYDBN5.js";import{a as nt,b as it}from"./chunk-QWVLAZ6N.js";import In,{Separator as Go}from"@inquirer/select";import{realpathSync as Ln}from"node:fs";import g from"node:process";import{fileURLToPath as Vo}from"node:url";var Ho=(e,t)=>{let o=e,r;for(let n of t){if(r=o.find(s=>s.name()===n),!r)return;o=r.commands}return r},eo=e=>Yt.flatMap(({key:t,label:o})=>Xt(t).flatMap(r=>{let n=Ho(e,r.groupPath);return n?[{name:r.groupPath.join(" "),description:n.description(),group:o}]:[]}));import{Command as Rr}from"commander";import b from"node:process";import to from"node:process";import{$ as Jo}from"zx";var ae=async()=>{let e=await Q(),t=await Promise.all([{label:"project (committed)",path:e.main},{label:"user global",path:e.userGlobal},{label:"user project",path:e.userProject}].map(async a=>({...a,exists:await E(a.path)}))),o=t.at(-1)?.exists===!0,r=await ht(e.userProject,o),n=o?vt(r):"";l.info(`Project name: ${e.projectName}
2
+ import{$ as Kt,A as kt,B as Rt,C as Pt,D as te,E as xt,F as D,G as m,H as Ct,I as bt,J as At,K as Tt,L as Dt,M as It,N as Lt,O as _t,P as Nt,Q as Ot,R as Mt,S as Ft,T as $t,U as jt,V as oe,W as Ut,X as qt,Y as Gt,Z as Wt,_ as Vt,a as N,aa as Ht,b as Ue,ba as Jt,c as qe,ca as Bt,da as Yt,e as We,ea as zt,f as Ve,g as He,ga as Xt,h as Je,ha as Qt,i as O,ia as Zt,j as rt,k as j,l as st,m as at,n as ct,o as lt,p as dt,q as ut,r as mt,s as pt,t as ee,u as ft,v as gt,w as ht,x as vt,y as wt,z as St}from"./chunk-QJQQ7ENL.js";import{b as Ke,c as E,d as h,e as F,f as et,g as tt,h as ot,i as P,k as $}from"./chunk-QUXDC3A3.js";import{z as re}from"./chunk-744Y674Y.js";import{L as l,S as Q,T as $e,V as je,a as Oe,r as Me}from"./chunk-LCNORKDV.js";import"./chunk-MFAFQVTW.js";import{a as ne,b as ie,c as se}from"./chunk-IERRU26T.js";import{a as Fe,g as yt,i as U,k as q}from"./chunk-GF2CA4CD.js";import{a as Ge}from"./chunk-XJOY7J3G.js";import{a as M,b as Be,d as Ye,f as ze,g as Xe,j as Z,k as Qe,o as w,r as Ze}from"./chunk-PQG63EIV.js";import{a as Et}from"./chunk-TVG47UHG.js";import{a as nt,b as it}from"./chunk-QWVLAZ6N.js";import In,{Separator as Go}from"@inquirer/select";import{realpathSync as Ln}from"node:fs";import g from"node:process";import{fileURLToPath as Vo}from"node:url";var Ho=(e,t)=>{let o=e,r;for(let n of t){if(r=o.find(s=>s.name()===n),!r)return;o=r.commands}return r},eo=e=>Yt.flatMap(({key:t,label:o})=>Xt(t).flatMap(r=>{let n=Ho(e,r.groupPath);return n?[{name:r.groupPath.join(" "),description:n.description(),group:o}]:[]}));import{Command as Rr}from"commander";import b from"node:process";import to from"node:process";import{$ as Jo}from"zx";var ae=async()=>{let e=await Q(),t=await Promise.all([{label:"project (committed)",path:e.main},{label:"user global",path:e.userGlobal},{label:"user project",path:e.userProject}].map(async a=>({...a,exists:await E(a.path)}))),o=t.at(-1)?.exists===!0,r=await ht(e.userProject,o),n=o?vt(r):"";l.info(`Project name: ${e.projectName}
3
3
  `),l.info(`Config merge chain (later overrides earlier):
4
- `);for(let a of t){let i=a.exists?" [\u2713]":" [ ]",c=a.path===e.userProject&&n!==""?` ${n}`:"";l.info(`${i} ${a.label.padEnd(22)} ${h(a.path)}${c}`)}let s={projectName:e.projectName,layers:t.map(a=>({label:a.label,path:a.path,exists:a.exists})),hasOverrides:r.hasOverrides,overrideKeys:r.overrideKeys};return{content:[{type:"text",text:JSON.stringify(s,null,2)}],structuredContent:s}},ce=async()=>{let e=await Q(),t=to.env.EDITOR||to.env.VISUAL||"vi",o=await ut(e);o.createdConfig&&l.info(mt(o)),l.info(`Opening ${h(e.userProject)} in ${t}`),await Jo({stdio:"inherit"})`${t} ${e.userProject}`,je();let r={path:e.userProject,editor:t};return{content:[{type:"text",text:JSON.stringify(r,null,2)}],structuredContent:r}};var le=e=>{if(typeof e!="object"||e===null)return null;let{config:t,reason:o,at:r}=e;return typeof t!="string"||t.length===0||typeof o!="string"||typeof r!="number"||!Number.isFinite(r)?null:{config:t,reason:o,at:r}},I=e=>`infra-kit: Doppler token for env "${e}" is missing, invalid, or unreadable \u2014 env auto-load is not running. Fix: run \`infra-kit env-token-set ${e}\` (mint one at https://dashboard.doppler.com under this config's Access tab).`;import f from"node:fs";import S from"node:path";import L from"node:process";var Bo="autoload-warn-fail.flag",de="autoload-warn-auth-fail.flag",ue="autoload-fail.flag",me="autoload-auth-fail.json",Yo=3e4,ro=async(e=!0)=>{let t;try{t=await $e()}catch{return null}let o=t.envAutoLoad;if(!o)return null;let r={trigger:o.trigger,config:o.config,project:t.envManagement.config.name};try{if(await rt(o.config))return r}catch{return r}return io({config:o.config,reason:`No Doppler service token for env "${o.config}"`,at:Date.now()}),e&&G(I(o.config),de),null},no=e=>{let{trigger:t,expectedTrigger:o,targetConfig:r,targetProject:n,env:s,force:a}=e;return t!==o||!s.session||s.cleared||s.currentConfig&&!s.autoLoadedMarker||!a&&s.autoLoadedMarker&&s.currentConfig===r&&s.currentProject===n?"skip":"load"},_=async({expectedTrigger:e,projectDir:t,force:o,isAuthFailure:r=Ke})=>{let n=e==="cli-invocation";n&&W();let s=null;try{if(s=await ro(n),!s||no({trigger:s.trigger,expectedTrigger:e,targetConfig:s.config,targetProject:s.project,env:Qo(),force:o})==="skip"||oo()||tr())return null;let i=Zo(),c=await st({config:s.config,autoLoaded:!0,projectDir:t,beforeWrite:()=>!oo()&&!er(i)});return c?(rr(),Xo(),c.filePath):null}catch(a){let i=a.message,c=r(a);return or(),c&&s&&io({config:s.config,reason:i,at:Date.now()}),n&&c&&s?(G(I(s.config),de),l.debug(`env auto-load auth failure: ${i}`)):n?G(`infra-kit: env auto-load failed \u2014 ${i} (will retry later)`,Bo):l.debug(`env auto-load skipped: ${i}`),null}},W=()=>{let e=zo();e&&(G(I(e.config),de),l.debug(`env auto-load auth failure (recorded ${new Date(e.at).toISOString()}): ${e.reason}`))},zo=()=>{try{let e=f.readFileSync(S.join(w(),me),"utf-8");return le(JSON.parse(e))}catch{return null}},io=e=>{try{let t=w();f.mkdirSync(t,{recursive:!0,mode:448}),Ze(S.join(t,me),JSON.stringify(e),384)}catch{}},Xo=()=>{try{f.rmSync(S.join(w(),me),{force:!0})}catch{}},Qo=()=>({session:L.env[Ye],cleared:L.env[Qe],currentConfig:L.env[ze],currentProject:L.env[Xe],autoLoadedMarker:L.env[Z]}),Zo=()=>{try{return f.statSync(S.join(w(),M)).mtimeMs}catch{return null}},er=e=>{try{let t=S.join(w(),M);if(!f.existsSync(t))return!1;let o=f.statSync(t).mtimeMs;return e!==null&&o<=e?!1:new RegExp(`^unset ${Z}$`,"m").test(f.readFileSync(t,"utf-8"))}catch{return!1}},oo=()=>{try{let e=w(),t=S.join(e,Be);if(!f.existsSync(t))return!1;let o=S.join(e,M);return f.existsSync(o)?f.statSync(t).mtimeMs>=f.statSync(o).mtimeMs:!0}catch{return!1}},tr=()=>{try{let e=S.join(w(),ue);return f.existsSync(e)?Date.now()-f.statSync(e).mtimeMs<Yo:!1}catch{return!1}},or=()=>{try{let e=w();f.mkdirSync(e,{recursive:!0,mode:448}),f.writeFileSync(S.join(e,ue),"",{mode:384})}catch{}},rr=()=>{try{f.rmSync(S.join(w(),ue),{force:!0})}catch{}},G=(e,t)=>{try{let o=w(),r=S.join(o,t);if(f.existsSync(r))return;f.mkdirSync(o,{recursive:!0,mode:448}),f.writeFileSync(r,"",{mode:384})}catch{}l.warn(e)};var pe=async({projectDir:e}={})=>{await _({expectedTrigger:"shell-startup",projectDir:e,force:!0})};var so="https://dashboard.doppler.com/workplace/projects",nr=(e,t)=>["Removing it locally does NOT revoke it \u2014 the token still works anywhere else it is stored.",`Revoke it in Doppler (project "${e}", config "${t}"): ${so}/${e}`],fe=async({env:e})=>{let o=!!(await et())?.envs[e];await ot(e);let r=await j(),n=await O(),s=await F();o?l.info(`Removed the "${e}" service token from ${h(s)}.`):l.info(`No "${e}" service token was stored in ${h(s)} \u2014 nothing to remove.`),r.length>0&&l.info(`Purged ${r.length} warm cache(s) across this repo's worktrees.`);for(let i of nr(n,e))l.warn(i);let a={env:e,removed:o,storePath:s,warmCachesPurged:r.length,revokeUrl:`${so}/${n}`};return{content:N(JSON.stringify(a,null,2)),structuredContent:a}};import ir from"@inquirer/password";import K from"node:process";import{$ as V}from"zx";var sr=async({stdin:e,fromEnv:t})=>{if(e)return{token:await ar(),source:"stdin"};if(t){let r=K.env[t];if(!r)throw new Error(`${t} is not set (or is empty) \u2014 nothing to store.`);return{token:r,source:"env"}}return P.setInteractive(),{token:(await $(r=>ir({message:"Paste the Doppler service token (input is hidden)",mask:!0},r),{output:K.stderr})).trim(),source:"prompt"}},ar=async()=>{let e=[];K.stdin.setEncoding("utf8");for await(let t of K.stdin)e.push(t);return e.join("").trim()},ge="DOPPLER_CONFIG",cr=3e4,lr=async(e,t,o)=>{let r=V.quiet;V.quiet=!0;let n;try{n=(await V({env:ct(e)})`doppler secrets download --no-file --format json --project ${t} --config ${o}`.timeout(cr)).stdout}catch(s){throw dr(s,o)}finally{V.quiet=r}return dt(n)},dr=(e,t)=>{let o=Oe(e)??(e instanceof Error?e.message:String(e));if(He(o)!=="auth")return e instanceof Error?e:new Error(String(e));let r=Je(o)==="mis-scoped"?"it is scoped to a DIFFERENT config (pasting another environment's token here is the mistake this check exists to catch).":"it is invalid or has been revoked.";return new Error([`Doppler refused this token for config "${t}" \u2014 ${r}`,"Nothing was written. Issue a service token scoped to that config and try again."].join(`
4
+ `);for(let a of t){let i=a.exists?" [\u2713]":" [ ]",c=a.path===e.userProject&&n!==""?` ${n}`:"";l.info(`${i} ${a.label.padEnd(22)} ${h(a.path)}${c}`)}let s={projectName:e.projectName,layers:t.map(a=>({label:a.label,path:a.path,exists:a.exists})),hasOverrides:r.hasOverrides,overrideKeys:r.overrideKeys};return{content:[{type:"text",text:JSON.stringify(s,null,2)}],structuredContent:s}},ce=async()=>{let e=await Q(),t=to.env.EDITOR||to.env.VISUAL||"vi",o=await ut(e);o.createdConfig&&l.info(mt(o)),l.info(`Opening ${h(e.userProject)} in ${t} (per-machine override \u2014 every key except "mcp")`),await Jo({stdio:"inherit"})`${t} ${e.userProject}`,je();let r={path:e.userProject,editor:t};return{content:[{type:"text",text:JSON.stringify(r,null,2)}],structuredContent:r}};var le=e=>{if(typeof e!="object"||e===null)return null;let{config:t,reason:o,at:r}=e;return typeof t!="string"||t.length===0||typeof o!="string"||typeof r!="number"||!Number.isFinite(r)?null:{config:t,reason:o,at:r}},I=e=>`infra-kit: Doppler token for env "${e}" is missing, invalid, or unreadable \u2014 env auto-load is not running. Fix: run \`infra-kit env-token-set ${e}\` (mint one at https://dashboard.doppler.com under this config's Access tab).`;import f from"node:fs";import S from"node:path";import L from"node:process";var Bo="autoload-warn-fail.flag",de="autoload-warn-auth-fail.flag",ue="autoload-fail.flag",me="autoload-auth-fail.json",Yo=3e4,ro=async(e=!0)=>{let t;try{t=await $e()}catch{return null}let o=t.envAutoLoad;if(!o)return null;let r={trigger:o.trigger,config:o.config,project:t.envManagement.config.name};try{if(await rt(o.config))return r}catch{return r}return io({config:o.config,reason:`No Doppler service token for env "${o.config}"`,at:Date.now()}),e&&G(I(o.config),de),null},no=e=>{let{trigger:t,expectedTrigger:o,targetConfig:r,targetProject:n,env:s,force:a}=e;return t!==o||!s.session||s.cleared||s.currentConfig&&!s.autoLoadedMarker||!a&&s.autoLoadedMarker&&s.currentConfig===r&&s.currentProject===n?"skip":"load"},_=async({expectedTrigger:e,projectDir:t,force:o,isAuthFailure:r=Ke})=>{let n=e==="cli-invocation";n&&W();let s=null;try{if(s=await ro(n),!s||no({trigger:s.trigger,expectedTrigger:e,targetConfig:s.config,targetProject:s.project,env:Qo(),force:o})==="skip"||oo()||tr())return null;let i=Zo(),c=await st({config:s.config,autoLoaded:!0,projectDir:t,beforeWrite:()=>!oo()&&!er(i)});return c?(rr(),Xo(),c.filePath):null}catch(a){let i=a.message,c=r(a);return or(),c&&s&&io({config:s.config,reason:i,at:Date.now()}),n&&c&&s?(G(I(s.config),de),l.debug(`env auto-load auth failure: ${i}`)):n?G(`infra-kit: env auto-load failed \u2014 ${i} (will retry later)`,Bo):l.debug(`env auto-load skipped: ${i}`),null}},W=()=>{let e=zo();e&&(G(I(e.config),de),l.debug(`env auto-load auth failure (recorded ${new Date(e.at).toISOString()}): ${e.reason}`))},zo=()=>{try{let e=f.readFileSync(S.join(w(),me),"utf-8");return le(JSON.parse(e))}catch{return null}},io=e=>{try{let t=w();f.mkdirSync(t,{recursive:!0,mode:448}),Ze(S.join(t,me),JSON.stringify(e),384)}catch{}},Xo=()=>{try{f.rmSync(S.join(w(),me),{force:!0})}catch{}},Qo=()=>({session:L.env[Ye],cleared:L.env[Qe],currentConfig:L.env[ze],currentProject:L.env[Xe],autoLoadedMarker:L.env[Z]}),Zo=()=>{try{return f.statSync(S.join(w(),M)).mtimeMs}catch{return null}},er=e=>{try{let t=S.join(w(),M);if(!f.existsSync(t))return!1;let o=f.statSync(t).mtimeMs;return e!==null&&o<=e?!1:new RegExp(`^unset ${Z}$`,"m").test(f.readFileSync(t,"utf-8"))}catch{return!1}},oo=()=>{try{let e=w(),t=S.join(e,Be);if(!f.existsSync(t))return!1;let o=S.join(e,M);return f.existsSync(o)?f.statSync(t).mtimeMs>=f.statSync(o).mtimeMs:!0}catch{return!1}},tr=()=>{try{let e=S.join(w(),ue);return f.existsSync(e)?Date.now()-f.statSync(e).mtimeMs<Yo:!1}catch{return!1}},or=()=>{try{let e=w();f.mkdirSync(e,{recursive:!0,mode:448}),f.writeFileSync(S.join(e,ue),"",{mode:384})}catch{}},rr=()=>{try{f.rmSync(S.join(w(),ue),{force:!0})}catch{}},G=(e,t)=>{try{let o=w(),r=S.join(o,t);if(f.existsSync(r))return;f.mkdirSync(o,{recursive:!0,mode:448}),f.writeFileSync(r,"",{mode:384})}catch{}l.warn(e)};var pe=async({projectDir:e}={})=>{await _({expectedTrigger:"shell-startup",projectDir:e,force:!0})};var so="https://dashboard.doppler.com/workplace/projects",nr=(e,t)=>["Removing it locally does NOT revoke it \u2014 the token still works anywhere else it is stored.",`Revoke it in Doppler (project "${e}", config "${t}"): ${so}/${e}`],fe=async({env:e})=>{let o=!!(await et())?.envs[e];await ot(e);let r=await j(),n=await O(),s=await F();o?l.info(`Removed the "${e}" service token from ${h(s)}.`):l.info(`No "${e}" service token was stored in ${h(s)} \u2014 nothing to remove.`),r.length>0&&l.info(`Purged ${r.length} warm cache(s) across this repo's worktrees.`);for(let i of nr(n,e))l.warn(i);let a={env:e,removed:o,storePath:s,warmCachesPurged:r.length,revokeUrl:`${so}/${n}`};return{content:N(JSON.stringify(a,null,2)),structuredContent:a}};import ir from"@inquirer/password";import K from"node:process";import{$ as V}from"zx";var sr=async({stdin:e,fromEnv:t})=>{if(e)return{token:await ar(),source:"stdin"};if(t){let r=K.env[t];if(!r)throw new Error(`${t} is not set (or is empty) \u2014 nothing to store.`);return{token:r,source:"env"}}return P.setInteractive(),{token:(await $(r=>ir({message:"Paste the Doppler service token (input is hidden)",mask:!0},r),{output:K.stderr})).trim(),source:"prompt"}},ar=async()=>{let e=[];K.stdin.setEncoding("utf8");for await(let t of K.stdin)e.push(t);return e.join("").trim()},ge="DOPPLER_CONFIG",cr=3e4,lr=async(e,t,o)=>{let r=V.quiet;V.quiet=!0;let n;try{n=(await V({env:ct(e)})`doppler secrets download --no-file --format json --project ${t} --config ${o}`.timeout(cr)).stdout}catch(s){throw dr(s,o)}finally{V.quiet=r}return dt(n)},dr=(e,t)=>{let o=Oe(e)??(e instanceof Error?e.message:String(e));if(He(o)!=="auth")return e instanceof Error?e:new Error(String(e));let r=Je(o)==="mis-scoped"?"it is scoped to a DIFFERENT config (pasting another environment's token here is the mistake this check exists to catch).":"it is invalid or has been revoked.";return new Error([`Doppler refused this token for config "${t}" \u2014 ${r}`,"Nothing was written. Issue a service token scoped to that config and try again."].join(`
5
5
  `))},ur=e=>[`Could not verify this token's scope: the Doppler payload for "${e}" carries no ${ge}.`,"Refusing to store a credential whose scope is unknown (a token for the wrong environment would load","the wrong secrets into every shell).","Re-run with --force if you are certain the token is scoped to this config."].join(`
6
6
  `),he=async({env:e,stdin:t,fromEnv:o,force:r})=>{let n=await O(),{token:s,source:a}=await sr({stdin:t,fromEnv:o});if(!s)throw new Error("No token provided \u2014 nothing was written.");let i=await lr(s,n,e);lt(i,e);let c=i.some(([y])=>y===ge);if(!c&&!r)throw new Error(ur(e));await tt(e,s);let u=await j(),d=await F();l.info(`Stored the "${e}" service token (${te(s)}) in ${h(d)} (mode 0600).`),c||l.warn(`Scope was NOT verified (no ${ge} in the payload) \u2014 written because --force was given.`),u.length>0&&l.info(`Purged ${u.length} warm cache(s) so the next shell cannot serve secrets fetched with an old token.`);let p={env:e,source:a,redactedToken:te(s),storePath:d,scopeVerified:c,warmCachesPurged:u.length};return P.print(),{content:N(JSON.stringify(p,null,2)),structuredContent:p}};import{spawn as mr}from"node:child_process";import H from"node:process";import{fileURLToPath as pr}from"node:url";var fr=["SIGINT","SIGTERM"],ao=()=>pr(new URL("./mcp.js",import.meta.url)),ve=(e={})=>{let t=e.spawn??mr,o=e.exit??(a=>H.exit(a)),r=e.env??H.env,n=e.onError??(a=>l.error(a)),s=t(H.execPath,[ao()],{stdio:"inherit",env:Ge(r)});s.on("error",a=>{n(`failed to launch the MCP server: ${a.message}`),o(1)}),fr.forEach(a=>{H.on(a,()=>{s.kill(a)})}),s.on("exit",(a,i)=>{o(i?1:a??1)})};var ye=async(e,t,o=!0)=>{let{from:r,version:n,env:s,services:a,skipTerraform:i,yes:c,dryRun:u,printEnv:d}=e,p=await Lt(r);return o&&P.addOption("--from",p),It(p,{"--version":n,"--skip-terraform":i,"--dry-run":u,"--print-env":d}),p==="local"?t==="all"?Mt({env:s,confirmedCommand:c,dryRun:u,printEnv:d}):Ft({env:s,service:a,confirmedCommand:c,dryRun:u,printEnv:d}):t==="all"?_t({version:n,env:s,skipTerraform:i,confirmedCommand:c}):Nt({version:n,env:s,services:a,skipTerraform:i,confirmedCommand:c})},we=async e=>ye(e,"all"),Se=async e=>ye(e,"selected"),J=async(e,t)=>{let o=`release deploy-${t} --from local`;return l.warn(`\`local deploy-${t}\` is deprecated and will be removed \u2014 use \`${o}\` instead.`),ye({...e,from:"local"},t,!1)};import{VENDOR_CONFIG_FILE as lo}from"@slip-stream-kit/config/internal";import ke from"node:fs/promises";import Re from"node:path";import co from"node:process";import{pathToFileURL as gr}from"node:url";var uo="~/projects",Pe=async(e={})=>{if(e.init){await vr(e.cwd);return}await hr()},hr=async()=>{let e=ee(),t=await E(e);if(l.info(`Factory config: ${h(e)} ${t?"[\u2713]":"[ ]"}`),!t){l.info("\nNot found \u2014 run `infra-kit vendor config --init` to scaffold it."),co.exitCode=1;return}let{workspaceDir:o,targets:r}=await gt(),n=ft(o),s=await E(n);l.info(`workspaceDir: ${o} (resolved: ${n}) ${s?"[\u2713 exists]":"[ ] not found"}`),l.info("Targets:");let a=s;for(let i of r){let c=Re.join(n,i),u=await E(c);u||(a=!1);let d=u?"[\u2713]":"[ ]",p=u?"":" (not found \u2014 clone or remove)";l.info(` ${d} ${i} ${h(c)}${p}`)}a||(co.exitCode=1)},vr=async e=>{let t=ee();if(await E(t)){l.info(`Factory config already exists at ${h(t)} \u2014 leaving it untouched.`);return}let o=e??await Me(),r=await yr(o);await ke.mkdir(Re.dirname(t),{recursive:!0}),await ke.writeFile(t,wr(r),"utf-8"),l.info(`\u2713 Created ${h(t)}`),r.length>0&&l.info(` Seeded ${r.length} target(s) from the source ${lo}.`),l.info(` Edit \`workspaceDir\` (placeholder: ${uo}) to point at where your repos live.`),r.length===0&&l.info(" Add at least one repo name to `targets` before running vendor sync/manifest/diff.")},yr=async e=>{try{let t=Re.join(e,lo),o=await ke.stat(t),s=(await import(`${gr(t).href}?mtime=${Number(o.mtimeMs)}`)).default,a=typeof s=="function"?await s():s;if(a&&typeof a=="object"&&"targets"in a){let i=a.targets;if(Array.isArray(i)&&i.every(c=>typeof c=="string"))return i}}catch{}return[]},wr=e=>`${JSON.stringify({workspaceDir:uo,targets:e},null,2)}
7
7
  `;var B=(e,t=!0)=>({line:e,reproducible:t});import Y from"node:fs";import Sr from"node:os";import kr from"node:path";import go from"node:process";var z="INFRA_KIT_SESSION_REPORT",xe=null,mo=!1,po=[],ho=(e=go.env)=>{mo||(xe=e[z]??null,mo=!0,delete e[z])};var vo=(e,t)=>{if(!xe)return;let o=e.summary??(po.length>0?[...po]:void 0),r={...e,...o?{summary:o}:{}},n=t?.write??((s,a)=>{Y.writeFileSync(s,a)});try{n(xe,JSON.stringify(r))}catch{}},fo=0,yo=e=>{let t=e?.tmpdir?.()??Sr.tmpdir(),o=e?.pid??go.pid;return fo+=1,kr.join(t,`infra-kit-session-${o}-${fo}.json`)},wo=(e,t)=>{if(!(t?.exists??(a=>Y.existsSync(a)))(e))return null;let r=t?.read??(a=>Y.readFileSync(a,"utf-8")),n=t?.unlink??(a=>Y.unlinkSync(a)),s;try{s=r(e)}catch{return null}finally{try{n(e)}catch{}}try{return JSON.parse(s)}catch{return null}};var Ce=e=>{e.options.some(o=>o.long==="--debug")||e.option("--debug","Raise the log level to debug (logs stay on stderr)"),e.commands.forEach(Ce)};var Pr=(e,t)=>[...t,e],So=(e,t)=>{if(!(typeof e>"u")){if(e===!0)return"workspace";if(e===!1)return"none";if(typeof e=="string"&&oe.includes(e))return e;throw new Error(`Invalid ${t} value "${String(e)}". Expected one of: ${oe.join(", ")}.`)}},xr=e=>e.description("Merge dev branch into every release branch").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("-y, --yes","Skip confirmation prompt").option("--dry-run","Compute and print the merge plan without pushing anything").option("--verify [command]","Check each merge before pushing it. Bare runs `pnpm install --frozen-lockfile`; pass a command to run that instead. A branch that fails is dropped from the push, never rolled back").action(async t=>{let o=await bt(()=>At({all:t.all,versions:t.versions,dryRun:t.dryRun,verify:t.verify,confirmedCommand:t.yes}));m(o),o.structuredContent.failedMerges>0&&(b.exitCode=1)}),Cr=e=>e.description("List all release branches").action(async()=>{m(await Ot())}),Er=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".',Pr,[]).option("-y, --yes","Skip confirmation prompt").action(async t=>{let r=t.release.map(wt),n=r.length>0?r:void 0;m(await $t({releases:n,confirmedCommand:t.yes}))}),br=e=>e.description("Edit a release's description in Jira and in the matching GitHub PR body").option("-v, --version <version>","Release version (e.g. 1.2.5) or release name (e.g. checkout-redesign)").option("-d, --description <description>",'New description (use "" to clear)').option("-y, --yes","Skip confirmation prompt").action(async t=>{m(await jt({version:t.version,description:t.description,confirmedCommand:t.yes}))}),ko=e=>e.option("-f, --from <where>",`Where the deploy runs: ${Dt.join(" | ")} (required)`).option("-v, --version <version>",'With --from ci: version (e.g. 1.2.5) or release name to deploy; "dev" deploys from the dev branch. Prompts when omitted').option("-e, --env <env>","Specify the environment to deploy to, e.g. dev").option("-y, --yes","Skip confirmation prompt").option("--skip-terraform","With --from ci: skip the terraform deployment step").option("--dry-run","With --from local: resolve target, contract and commands without deploying").option("--print-env","With --from local: print the resolved build contract"),Ar=e=>ko(e).description("Deploy every service to any environment, in CI or from this machine").action(async t=>{m(await we({from:t.from,version:t.version,env:t.env,skipTerraform:t.skipTerraform,yes:t.yes,dryRun:t.dryRun,printEnv:t.printEnv}))}),Tr=e=>ko(e).description("Deploy selected services to any environment, in CI or from this machine").option("-s, --services <services...>","Specify services to deploy, e.g. client-be client-fe").action(async t=>{m(await Se({from:t.from,version:t.version,env:t.env,services:t.services,skipTerraform:t.skipTerraform,yes:t.yes,dryRun:t.dryRun,printEnv:t.printEnv}))}),Dr=e=>e.description("Release a new version to production").option("-v, --version <version>","Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deliver").option("-y, --yes","Skip confirmation prompt").action(async t=>{m(await Tt({version:t.version,confirmedCommand:t.yes}))}),Ir=e=>e.description("Tear down a release: worktree, PR, branches, and the Jira fix version").option("-v, --version <version>","Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to remove").option("--move-issues-to <version>","Reassign issues carrying the fix version to this version before removing it").option("--skip-jira","Remove everything except the Jira fix version").option("-y, --yes","Skip confirmation prompt").action(async t=>{m(await Ut({version:t.version,moveIssuesTo:t.moveIssuesTo,skipJira:t.skipJira,confirmedCommand:t.yes}))}),Lr=e=>e.description("Remove release worktrees whose PRs are no longer open").option("-y, --yes","Skip confirmation prompt").action(async t=>{m(await Bt({confirmedCommand:t.yes}))}),_r=e=>e.description("Add git worktrees for release branches").option("-y, --yes","Skip confirmation prompt").option("-a, --all","Select all active release branches").option("-v, --versions <versions>","Specify versions by comma, e.g. 1.2.5, 1.2.6").option("-i, --ide [mode]","Editor mode for created worktrees: workspace (default) | none").option("--no-ide","Skip the editor (alias for --ide none)").option("-c, --cursor [mode]","Deprecated alias for --ide").option("--no-cursor","Deprecated alias for --no-ide").option("-g, --github-desktop","Open created worktrees in GitHub Desktop").option("--no-github-desktop","Skip GitHub Desktop prompt").option("-m, --cmux","Open created worktrees in cmux (3-pane layout)").option("--no-cmux","Skip cmux prompt").action(async t=>{let o=So(t.ide,"--ide")??So(t.cursor,"--cursor");m(await Kt({confirmedCommand:t.yes,all:t.all,versions:t.versions,ide:o,githubDesktop:t.githubDesktop,cmux:t.cmux}))}),Nr=e=>e.description("List all git worktrees with detailed information").action(async()=>{m(await Ht())}),Or=e=>e.description("Remove git worktrees for release branches").option("-y, --yes","Skip confirmation prompt").option("-a, --all","Select all active release branches").option("-v, --versions <versions>","Specify versions by comma, e.g. 1.2.5, 1.2.6").action(async t=>{m(await Jt({confirmedCommand:t.yes,all:t.all,versions:t.versions}))}),Mr=e=>e.description("Reopen editor + cmux windows for every active worktree in the current project (additive, idempotent)").option("--all","Reopen across every discovered infra-kit project (Stage 3 \u2014 not yet implemented)").option("--project <names...>","Restrict --all to these project names").option("--root <paths...>","Discovery roots for --all (repeatable)").option("--release-only","Restrict to release worktrees (reproduces the legacy worktrees-reload scope)").option("--force","Close each cmux workspace first, then reopen (the legacy worktrees-reload behaviour)").option("--dry-run","Print the plan (paths + cmux titles) and spawn nothing").action(async t=>{m(await qt({all:t.all,project:t.project,root:t.root,releaseOnly:t.releaseOnly,force:t.force,dryRun:t.dryRun}))}),Fr=e=>e.description("Show the machine-local factory config (~/.infra-kit/vendor.json) or scaffold it with --init").option("--init","Scaffold ~/.infra-kit/vendor.json (skips if it already exists)").action(async t=>{m(await Pe({init:t.init}))}),$r=e=>e.description("Verify vendor/ matches vendor/.sync-manifest.json (self-contained; for any consumer repo)").action(async()=>{let t=await Wt();m(t),t.structuredContent.ok||(b.exitCode=1)}),jr=e=>e.description("Show the resolved config merge chain and file paths").action(async()=>{m(await ae())}),Ur=e=>e.description("Open the user-scope per-project override file in $EDITOR").action(async()=>{m(await ce())}),qr=new Set(["setup","doctor","version","dev","mcp"]),Gr=e=>e.startsWith("env-")||qr.has(e),Wr=new Set(["env-autoload","mcp","version"]),X=e=>{let t=[];for(let o=e;o&&o.parent;o=o.parent)t.unshift(o.name());return t.join(" ")},Ee=()=>{let e=new Rr,t=e.command("release").description("Release management commands");xr(t.command("merge-dev")),Cr(t.command("list")),Er(t.command("create")),br(t.command("desc-edit")),Ar(t.command("deploy-all")),Tr(t.command("deploy-selected")),Dr(t.command("deliver")),Ir(t.command("remove"));let o=e.command("worktrees").description("Git worktree management commands");_r(o.command("add")),Nr(o.command("list")),Or(o.command("remove")),Lr(o.command("sync")),Mr(e.command("reopen"));let r=e.command("config").description("Manage infra-kit configuration files");jr(r.command("path")),Ur(r.command("edit")),e.command("config-get").description("Print the fully merged infra-kit config (project + user-global + per-project override layers)").action(async()=>{m(await qe())});let n=e.command("local").description("Deprecated \u2014 use `release deploy-all|deploy-selected --from local`"),s=i=>i.option("-e, --env <name>","Target environment (prompts when omitted)").option("-y, --yes","Skip the confirmation prompt").option("--dry-run","Resolve target, contract and commands without deploying").option("--print-env","Print the resolved build contract");s(n.command("deploy-all")).description("Deprecated \u2014 use `release deploy-all --from local`").action(async i=>{m(await J({env:i.env,yes:i.yes,dryRun:i.dryRun,printEnv:i.printEnv},"all"))}),s(n.command("deploy-selected")).description("Deprecated \u2014 use `release deploy-selected --from local`").option("-s, --service <name...>","Service name(s), as in deploy-<name>.sh (prompts when omitted)").action(async i=>{m(await J({env:i.env,services:i.service,yes:i.yes,dryRun:i.dryRun,printEnv:i.printEnv},"selected"))}),e.command("audit").description("Audit against infra-kit.config.ts rules (--all for every package, --root for the monorepo root)").option("-a, --all","Audit every non-vendor workspace package").option("-r, --root","Audit the monorepo root (turbo pipeline + root commands)").option("--fix","Write the infra-kit guidance block into CLAUDE.md for the audited scope before checking (CLI-only)").option("--design","With --fix: scaffold DESIGN.md for frontend/mobile packages that lack one").action(async i=>{if(i.design&&!i.fix){l.error("--design requires --fix (it scaffolds DESIGN.md; there is nothing to scaffold without a fix run)"),b.exitCode=1;return}let c=await Ue({all:i.all,root:i.root,fix:i.fix,design:i.design});m(c);let{allPassed:u,fixed:d}=c.structuredContent,p=(d??[]).some(y=>y.action==="failed");(!u||p)&&(b.exitCode=1)});let a=e.command("vendor").description("Verify the mirrored vendor/ tree");return $r(a.command("check")),Fr(a.command("config")),e.command("doctor").description("Check installation and authentication status of gh and doppler CLIs").option("--fix","Remove portless routes left behind by a dev-server that was killed (kill -9, OOM, force-quit). Refuses while a dev session is running, when a booting UI is indistinguishable from a dead route.").option("--ascii","Render the report with ASCII markers instead of unicode glyphs (check messages are unchanged)").action(async i=>{let c=await St({fix:!!i.fix});D.enabled||Ve(c.structuredContent.checks,{ascii:!!i.ascii}),c.structuredContent.checks.some(d=>d.name==="plugin installed"&&d.status==="fail")&&(b.exitCode=1),m(c)}),e.command("mcp").description("Run the infra-kit MCP server (stdio transport)").action(()=>{ve()}),e.command("dev").description("Run local dev servers for a named devServersPresets preset (or all apps); api + ui").argument("[preset]","Named preset from devServersPresets (omit to run every app)").option("-w, --watch","Rebuild and restart on file save").option("--app <names>","Further narrow to these app folder names (comma-separated)").option("--target <keys>","Run exactly these <app>/api|<app>/ui packages (comma-separated); part-level, unlike --app").option("--cmux","Run each app in its own cmux pane (one workspace, N panes; falls back to single terminal if cmux is unavailable)").option("--self","Run only the app of the current directory (infer from cwd; use inside apps/<app>/\u2026)").option("-V, --verbose","Print full boot narration (default: quiet; full detail always in the session log)").option("--routes","Print each app\u2019s registered METHOD /path routes at startup (default: off)").option("--no-ui-health","Do not probe the frontends\u2019 liveness (vite\u2019s HMR ping); their rows carry no health dot (also: INFRA_KIT_NO_UI_HEALTH=1)").action(async(i,c)=>{let{runDevServerCli:u}=await import("./dev-server.js"),d=!!(b.stdout.isTTY&&b.stdin.isTTY);await u({...c,preset:i},d,D.enabled)}),e.command("dev-status").description("Show what `infra-kit dev` currently has running (reads dev-context fragments; starts nothing)").action(async()=>{m(await We())}),e.command("setup").description("Set this machine up: shell integration, agent files, MCP server, then the external tools").option("--tools <ids...>","Limit to these tools (brew, aws, gh, doppler, portless)").option("--update [ids...]","Update what is already installed; never install a missing tool").option("--skip-tools","Do the local setup only, then REPORT what each tool needs \u2014 installs nothing").action(async i=>{m(await Gt({tools:i.tools,update:i.update,skipTools:i.skipTools}))}),e.command("version").description("Print the installed infra-kit CLI version").action(async()=>{m(await Vt())}),e.command("env-status").description("Show which env is loaded in this session (local introspection; no Doppler call)").action(async()=>{m(await Pt())}),e.command("env-list").description("List available Doppler configs for the detected project, and whether a service token resolves for each").action(async()=>{m(await Rt())}),e.command("env-load").description("Load Doppler env vars for a config. Source the returned file path to apply.").option("-c, --config <config>","Environment config name to load (e.g. dev, arthur)").action(async i=>{m(await at({config:i.config}))}),e.command("env-clear").description("Clear loaded env vars. Source the returned file path to apply.").option("--purge","Also delete this project's warm cache outright (durable disable)").action(async i=>{m(await kt({purge:!!i.purge}))}),e.command("env-token-set").description("Store the Doppler service token for an env (masked prompt; validated against Doppler before writing)").argument("<env>","Environment / Doppler config the token is scoped to (e.g. dev)").option("--stdin","Read the token from stdin instead of prompting (e.g. from a password manager)").option("--from-env <var>","Read the token from the named environment variable (the NAME, never the value)").option("--force","Store even when the token\u2019s scope could not be verified. Never overrides a real mismatch.").action(async(i,c)=>{m(await he({env:i,stdin:c.stdin,fromEnv:c.fromEnv,force:c.force}))}),e.command("env-token-list").description("Show which envs have a Doppler service token (redacted), and where it came from").option("--check","Also ask Doppler whether each token is valid and correctly scoped").action(async i=>{m(await xt({check:!!i.check}))}),e.command("env-token-remove").description("Delete an env\u2019s Doppler service token from the local store (does NOT revoke it in Doppler)").argument("<env>","Environment / Doppler config whose token to remove").action(async i=>{m(await fe({env:i}))}),e.command("env-autoload",{hidden:!0}).description("Internal: prime env for the shell-startup auto-load trigger").option("--project-dir <dir>","Canonical project dir for the warm-cache key (shell-startup only)").action(async i=>{await pe({projectDir:i.projectDir})}),e.commands.forEach(Ct),Ce(e),e.hook("preAction",async(i,c)=>{P.start(X(c)),D.enabled=!!c.optsWithGlobals().json,D.enabled&&(l.level="warn"),Wr.has(X(c))||await pt(),W(),Gr(c.name())||await _({expectedTrigger:"cli-invocation"})}),e.hook("postAction",(i,c)=>{let u=X(c);if(Qt(u))return;let p=P.snapshot()?.formattedOptions??"",y=p?` ${p}`:"",R=`infra-kit ${u}${y}`;vo({equivalent:B(R,!0)})}),e};var be=(e,t=" ")=>{let o=e.reduce((r,[n])=>Math.max(r,n.length),0);return e.map(([r,n])=>`${r.padEnd(o)}${t}${n}`)};import{chalkStderr as un}from"chalk";import{spawn as mn}from"node:child_process";import v from"node:process";import{Chalk as Vr}from"chalk";var A=new Vr({level:1}),k={ok:"ok",findingsPlain:"completed with findings",failed:"failed",cancelled:"cancelled",findingsSuffix:"findings",sep:" \xB7 ",reproPrefix:"$ ",nonReproPrefix:"\u2248 ",envNotice:"Applies to your shell after you exit this session."},Kr={suspend:{unicode:"any key commands \xB7 Esc / Ctrl-C quit \xB7 Ctrl-Z suspend",ascii:"any key commands - Esc / Ctrl-C quit - Ctrl-Z suspend"},plain:{unicode:"any key commands \xB7 Esc / Ctrl-C quit",ascii:"any key commands - Esc / Ctrl-C quit"}},Ro={ok:{unicode:"\u2713",ascii:"[ok]"},findings:{unicode:"\u26A0",ascii:"[!]"},failed:{unicode:"\u2717",ascii:"[x]"},cancelled:{unicode:"\u2298",ascii:"[-]"}},Po={unicode:"\u2500",ascii:"-"},Hr=3,Ae=e=>e,Jr={ok:e=>A.green(e),findings:e=>A.yellow(e),failed:e=>A.red(e),cancelled:e=>A.gray(e)},De=e=>e?{dim:t=>A.dim(t),bold:t=>A.bold(t)}:{dim:Ae,bold:Ae},Br=(e,t)=>e?Jr[t]:Ae,Te=e=>[...e].length,Yr=(e,t)=>{if(t==null)return e;let o=Math.max(0,t-1);return Te(e)<=o?e:[...e].slice(0,o).join("")},xo=e=>{let t=De(e.color===!0).dim,o=Kr[e.canSuspend?"suspend":"plain"],r=e.ascii===!0?o.ascii:o.unicode;return t(Yr(r,e.width))},Co=(e,t={})=>{let o=De(t.color===!0);return`${o.dim(k.reproPrefix.trim())} ${o.bold(e)}`},zr=e=>e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(1)}s`,Xr=(e,t)=>e==="findings"?t!=null?`${t} ${k.findingsSuffix}`:k.findingsPlain:{ok:k.ok,failed:k.failed,cancelled:k.cancelled}[e],Qr=(e,t,o,r)=>{if(t==null)return"";let n=t-2-e;return n<Hr?"":` ${r(o.repeat(n))}`},Eo=e=>{let t=e.color===!0,o=De(t),r=Br(t,e.outcome),n=e.ascii===!0,s=e.equivalent.reproducible?k.reproPrefix:k.nonReproPrefix,a=n?Ro[e.outcome].ascii:Ro[e.outcome].unicode,i=Xr(e.outcome,e.findingsCount),c=[zr(e.durationMs)],u=e.summary?.[0]?.split(`
@@ -11,5 +11,5 @@ import{$ as Kt,A as kt,B as Rt,C as Pt,D as te,E as xt,F as D,G as m,H as Ct,I a
11
11
  ${y}
12
12
  `),i.childStarted();let R=await hn(p,o,()=>{n="child"});if(r(`
13
13
  ${R}
14
- `),await u(c)==="quit")return}}finally{i.dispose()}};import{spawn as xn}from"node:child_process";import Uo from"node:fs";import T from"node:process";import{fileURLToPath as qo}from"node:url";var jo=["INFRA_KIT_NO_AUTO_UPDATE","NO_UPDATE_NOTIFIER","CI"],Rn=new Set(["mcp"]),Pn=e=>e[2]!=null&&Rn.has(e[2]),Le=e=>{let{argv:t,env:o,isTty:r,selfRealPath:n,cwd:s,realpath:a}=e;return jo.some(c=>{let u=o[c];return u!=null&&u!==""})?"opt-out":t.includes("--json")?"json":Pn(t)?"own-command":r?U(n,s,a)?"local-install":null:"not-a-tty"};var Cn=e=>{xn(T.execPath,[e,"--parent-pid",String(T.pid)],{detached:!0,stdio:"ignore",windowsHide:!0}).unref()},En=()=>qo(new URL("./update-check.js",import.meta.url)),bn=()=>Uo.realpathSync(qo(new URL("./cli.js",import.meta.url))),An=/^[\w@./+ -]+$/i,Tn=e=>An.test(e),Dn=(e,t,o)=>{!e?.latestVersion||!e.updateCommand||ne(e.latestVersion,t)&&e.updateCommand.every(Tn)&&o(`infra-kit ${e.latestVersion} is available (you have ${t}). Run: ${yt(e.updateCommand)}`)},_e=(e,t={})=>{try{let o=t.argv??T.argv,r=t.env??T.env,n=t.isTty??!!T.stdout.isTTY,s=t.cwd??T.cwd(),a=t.nowMs??Date.now(),i=t.selfRealPath??bn(),c=t.readCache??ie,u=t.spawnChild??Cn,d=t.fileExists??(C=>Uo.existsSync(C)),p=t.notify??(C=>{l.info(C)});if(Le({argv:o,env:r,isTty:n,selfRealPath:i,cwd:s,realpath:q})!==null)return;let y=c();if(Dn(y,e,p),!se(y,a))return;let R=t.childPath??En();if(!d(R))return;u(R)}catch{}};Zt();ho();var Ne=Ee(),Wo=async e=>{try{e?await Ne.parseAsync(e):await Ne.parseAsync()}catch(t){re(t)&&(l.info("Operation cancelled."),g.exit(0));let o=t instanceof Error?t.message:String(t);l.error(o),g.exit(1)}},_n=()=>{try{if(g.env.INFRA_KIT_NO_LOCATION_WARN||g.argv.includes("--json")||g.argv[2]==="mcp")return;U(Ln(Vo(import.meta.url)),g.cwd(),q)&&l.info("Running from a project-local node_modules. Install globally for faster startup: npm i -g infra-kit")}catch{}};_n();_e(Fe.version);var Ko=()=>eo(Ne.commands),Nn=async()=>{let e=Ko(),t=null;try{if(g.stdout.isTTY&&g.stdin.isTTY){let{runCommandPalette:o}=await import("./boot-AJLBGRED.js");t=await o(e)}else{let o=be(e.map(n=>[n.name,n.description])),r=e.flatMap((n,s)=>[...e[s-1]?.group===n.group?[]:[new Go(" "),new Go(`\u2014 ${n.group} \u2014`)],{name:o[s]??n.name,value:n.name}]);t=await $(n=>In({message:"Select a command to run",choices:r},n),{output:g.stderr})}}catch(o){if(!re(o))throw o}return t},On=async()=>{let{runCommandPalette:e}=await import("./boot-AJLBGRED.js"),t=Vo(import.meta.url),o=new Map(zt.map(r=>[r.groupPath.join(" "),r]));await $o(Ko(),{renderPalette:e,resolveCommand:r=>{let n=o.get(r);return n?{groupPath:n.groupPath,entersAltScreen:n.entersAltScreen,sessionEnvNotice:n.sessionEnvNotice,longRunning:n.longRunning}:void 0},cliPath:t})};if(g.argv.length<=2){let e={stdoutIsTTY:!!g.stdout.isTTY,stdinIsTTY:!!g.stdin.isTTY,stderrIsTTY:!!g.stderr.isTTY};if(Fo(g.env,e))await On();else{let t=await Nn();t&&await Wo(["node","infra-kit",...t.split(" ")])}}else await Wo();
14
+ `),await u(c)==="quit")return}}finally{i.dispose()}};import{spawn as xn}from"node:child_process";import Uo from"node:fs";import T from"node:process";import{fileURLToPath as qo}from"node:url";var jo=["INFRA_KIT_NO_AUTO_UPDATE","NO_UPDATE_NOTIFIER","CI"],Rn=new Set(["mcp"]),Pn=e=>e[2]!=null&&Rn.has(e[2]),Le=e=>{let{argv:t,env:o,isTty:r,selfRealPath:n,cwd:s,realpath:a}=e;return jo.some(c=>{let u=o[c];return u!=null&&u!==""})?"opt-out":t.includes("--json")?"json":Pn(t)?"own-command":r?U(n,s,a)?"local-install":null:"not-a-tty"};var Cn=e=>{xn(T.execPath,[e,"--parent-pid",String(T.pid)],{detached:!0,stdio:"ignore",windowsHide:!0}).unref()},En=()=>qo(new URL("./update-check.js",import.meta.url)),bn=()=>Uo.realpathSync(qo(new URL("./cli.js",import.meta.url))),An=/^[\w@./+ -]+$/i,Tn=e=>An.test(e),Dn=(e,t,o)=>{!e?.latestVersion||!e.updateCommand||ne(e.latestVersion,t)&&e.updateCommand.every(Tn)&&o(`infra-kit ${e.latestVersion} is available (you have ${t}). Run: ${yt(e.updateCommand)}`)},_e=(e,t={})=>{try{let o=t.argv??T.argv,r=t.env??T.env,n=t.isTty??!!T.stdout.isTTY,s=t.cwd??T.cwd(),a=t.nowMs??Date.now(),i=t.selfRealPath??bn(),c=t.readCache??ie,u=t.spawnChild??Cn,d=t.fileExists??(C=>Uo.existsSync(C)),p=t.notify??(C=>{l.info(C)});if(Le({argv:o,env:r,isTty:n,selfRealPath:i,cwd:s,realpath:q})!==null)return;let y=c();if(Dn(y,e,p),!se(y,a))return;let R=t.childPath??En();if(!d(R))return;u(R)}catch{}};Zt();ho();var Ne=Ee(),Wo=async e=>{try{e?await Ne.parseAsync(e):await Ne.parseAsync()}catch(t){re(t)&&(l.info("Operation cancelled."),g.exit(0));let o=t instanceof Error?t.message:String(t);l.error(o),g.exit(1)}},_n=()=>{try{if(g.env.INFRA_KIT_NO_LOCATION_WARN||g.argv.includes("--json")||g.argv[2]==="mcp")return;U(Ln(Vo(import.meta.url)),g.cwd(),q)&&l.info("Running from a project-local node_modules. Install globally for faster startup: npm i -g infra-kit")}catch{}};_n();_e(Fe.version);var Ko=()=>eo(Ne.commands),Nn=async()=>{let e=Ko(),t=null;try{if(g.stdout.isTTY&&g.stdin.isTTY){let{runCommandPalette:o}=await import("./boot-BL4EQMZV.js");t=await o(e)}else{let o=be(e.map(n=>[n.name,n.description])),r=e.flatMap((n,s)=>[...e[s-1]?.group===n.group?[]:[new Go(" "),new Go(`\u2014 ${n.group} \u2014`)],{name:o[s]??n.name,value:n.name}]);t=await $(n=>In({message:"Select a command to run",choices:r},n),{output:g.stderr})}}catch(o){if(!re(o))throw o}return t},On=async()=>{let{runCommandPalette:e}=await import("./boot-BL4EQMZV.js"),t=Vo(import.meta.url),o=new Map(zt.map(r=>[r.groupPath.join(" "),r]));await $o(Ko(),{renderPalette:e,resolveCommand:r=>{let n=o.get(r);return n?{groupPath:n.groupPath,entersAltScreen:n.entersAltScreen,sessionEnvNotice:n.sessionEnvNotice,longRunning:n.longRunning}:void 0},cliPath:t})};if(g.argv.length<=2){let e={stdoutIsTTY:!!g.stdout.isTTY,stdinIsTTY:!!g.stdin.isTTY,stderrIsTTY:!!g.stderr.isTTY};if(Fo(g.env,e))await On();else{let t=await Nn();t&&await Wo(["node","infra-kit",...t.split(" ")])}}else await Wo();
15
15
  //# sourceMappingURL=cli.js.map