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.
package/dist/cli.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/entry/cli.ts", "../src/commands/config/config.ts", "../src/lib/env-autoload/env-autoload.ts", "../src/commands/env-autoload/env-autoload.ts", "../src/commands/vendor-config/vendor-config.ts", "../src/lib/json-output/json-output.ts", "../src/lib/render/render.ts"],
4
- "sourcesContent": ["import select, { Separator } from '@inquirer/select'\nimport { Command } from 'commander'\nimport process from 'node:process'\n\nimport { audit } from 'src/commands/audit'\nimport { configEdit, configPath } from 'src/commands/config'\nimport { doctor } from 'src/commands/doctor'\nimport { envAutoload } from 'src/commands/env-autoload'\nimport { envClear } from 'src/commands/env-clear'\nimport { envList } from 'src/commands/env-list'\nimport { envLoad } from 'src/commands/env-load'\nimport { envStatus } from 'src/commands/env-status'\nimport { ghMergeDev } from 'src/commands/gh-merge-dev'\nimport { ghReleaseDeliver } from 'src/commands/gh-release-deliver'\nimport { ghReleaseDeployAll } from 'src/commands/gh-release-deploy-all'\nimport { ghReleaseDeploySelected } from 'src/commands/gh-release-deploy-selected'\nimport { ghReleaseList } from 'src/commands/gh-release-list'\nimport { init } from 'src/commands/init'\nimport { releaseCreate } from 'src/commands/release-create'\nimport { releaseDescEdit } from 'src/commands/release-desc-edit'\nimport { vendorCheck } from 'src/commands/vendor-check'\nimport { vendorConfig } from 'src/commands/vendor-config'\nimport { vendorDiff } from 'src/commands/vendor-diff'\nimport { vendorManifest } from 'src/commands/vendor-manifest'\nimport { vendorSync } from 'src/commands/vendor-sync'\nimport { version } from 'src/commands/version'\nimport { worktreesAdd } from 'src/commands/worktrees-add'\nimport { worktreesList } from 'src/commands/worktrees-list'\nimport { worktreesReload } from 'src/commands/worktrees-reload'\nimport { worktreesRemove } from 'src/commands/worktrees-remove'\nimport { worktreesSync } from 'src/commands/worktrees-sync'\nimport { IDE_MODES } from 'src/integrations/ide'\nimport type { IdeMode } from 'src/integrations/ide'\nimport { getMenuGroupCommands } from 'src/lib/command-catalog'\nimport { runEnvAutoLoad } from 'src/lib/env-autoload'\nimport { isPromptCancellation } from 'src/lib/errors/is-prompt-cancellation'\nimport { addJsonOption, emit, jsonOutput } from 'src/lib/json-output'\nimport { logger } from 'src/lib/logger'\nimport { formatAlignedRows } from 'src/lib/render'\nimport { parseReleaseSpec } from 'src/lib/version-utils'\nimport type { ReleaseInput } from 'src/lib/version-utils'\n\nconst program = new Command()\n\nconst collectReleaseSpec = (value: string, prev: string[]): string[] => {\n return [...prev, value]\n}\n\n/** Parse a `--repos a,b,c` option into a target-name list (undefined = all). */\nconst parseRepos = (value: unknown): string[] | undefined => {\n return typeof value === 'string' ? value.split(',').filter(Boolean) : undefined\n}\n\nconst normalizeIdeMode = (value: unknown, flagName: '--ide' | '--cursor'): IdeMode | undefined => {\n if (typeof value === 'undefined') {\n return undefined\n }\n\n if (value === true) {\n return 'workspace'\n }\n\n if (value === false) {\n return 'none'\n }\n\n if (typeof value === 'string' && (IDE_MODES as readonly string[]).includes(value)) {\n return value as IdeMode\n }\n\n throw new Error(`Invalid ${flagName} value \"${String(value)}\". Expected one of: ${IDE_MODES.join(', ')}.`)\n}\n\nconst runProgram = async (argv?: string[]): Promise<void> => {\n try {\n if (argv) {\n await program.parseAsync(argv)\n } else {\n await program.parseAsync()\n }\n } catch (error) {\n // Ctrl-C / Esc out of any prompt is a deliberate back-out, not a failure:\n // exit quietly with success so it matches the explicit \"Operation cancelled.\"\n // decline path and never trips scripts/CI into treating a cancel as an error.\n if (isPromptCancellation(error)) {\n logger.info('Operation cancelled.')\n process.exit(0)\n }\n\n const message = error instanceof Error ? error.message : String(error)\n\n logger.error(message)\n process.exit(1)\n }\n}\n\n// --- Deprecation support for flat command aliases (Phase 3 grouping) ---\n// Flat names (`release-create`, `worktrees-add`, `vendor-config`, ...) are kept\n// as working aliases of the grouped forms (`release create`, ...) for one\n// release cycle. They warn once when invoked directly, but stay silent when the\n// interactive no-arg menu drives them (the menu is a guided surface).\nconst invokedViaMenu = { value: false }\n\nconst deprecatedAlias = (cmd: Command, preferred: string): Command => {\n return cmd.hook('preAction', () => {\n if (!invokedViaMenu.value) {\n logger.warn(`\"${cmd.name()}\" is a deprecated alias; use \"${preferred}\" instead.`)\n }\n })\n}\n\n// --- Command configurators (one source of options + action, shared by the\n// grouped form and its flat alias so the two can never diverge) ---\nconst configureMergeDev = (cmd: Command): Command => {\n return cmd\n .description('Merge dev branch into every release branch')\n .option('-a, --all', 'Select all active release branches')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(await ghMergeDev({ all: options.all, confirmedCommand: options.yes }))\n })\n}\n\nconst configureReleaseList = (cmd: Command): Command => {\n return cmd.description('List all release branches').action(async () => {\n emit(await ghReleaseList())\n })\n}\n\nconst configureReleaseCreate = (cmd: Command): Command => {\n return cmd\n .description('Create one or more release branches (each entry can mix regular/hotfix and its own description)')\n .option(\n '-r, --release <spec>',\n '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\".',\n collectReleaseSpec,\n [],\n )\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n const specs = options.release as string[]\n const inputs: ReleaseInput[] = specs.map(parseReleaseSpec)\n const releases = inputs.length > 0 ? inputs : undefined\n\n emit(\n await releaseCreate({\n releases,\n confirmedCommand: options.yes,\n }),\n )\n })\n}\n\nconst configureReleaseDescEdit = (cmd: Command): Command => {\n return cmd\n .description(\"Edit a release's description in Jira and in the matching GitHub PR body\")\n .option('-v, --version <version>', 'Release version (e.g. 1.2.5) or release name (e.g. checkout-redesign)')\n .option('-d, --description <description>', 'New description (use \"\" to clear)')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(\n await releaseDescEdit({\n version: options.version,\n description: options.description,\n confirmedCommand: options.yes,\n }),\n )\n })\n}\n\nconst configureReleaseDeployAll = (cmd: Command): Command => {\n return cmd\n .description('Deploy any release branch to any environment')\n .option(\n '-v, --version <version>',\n 'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deploy; \"dev\" deploys from the dev branch',\n )\n .option('-e, --env <env>', 'Specify the environment to deploy to, e.g. dev')\n .option('--skip-terraform', 'Skip terraform deployment step')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(\n await ghReleaseDeployAll({\n version: options.version,\n env: options.env,\n skipTerraform: options.skipTerraform,\n confirmedCommand: options.yes,\n }),\n )\n })\n}\n\nconst configureReleaseDeploySelected = (cmd: Command): Command => {\n return cmd\n .description('Deploy selected services from release branch to any environment')\n .option(\n '-v, --version <version>',\n 'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deploy; \"dev\" deploys from the dev branch',\n )\n .option('-e, --env <env>', 'Specify the environment to deploy to, e.g. dev')\n .option('-s, --services <services...>', 'Specify services to deploy, e.g. client-be client-fe')\n .option('--skip-terraform', 'Skip terraform deployment step')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(\n await ghReleaseDeploySelected({\n version: options.version,\n env: options.env,\n services: options.services,\n skipTerraform: options.skipTerraform,\n confirmedCommand: options.yes,\n }),\n )\n })\n}\n\nconst configureReleaseDeliver = (cmd: Command): Command => {\n return cmd\n .description('Release a new version to production')\n .option('-v, --version <version>', 'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deliver')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(await ghReleaseDeliver({ version: options.version, confirmedCommand: options.yes }))\n })\n}\n\nconst configureWorktreesSync = (cmd: Command): Command => {\n return cmd\n .description('Remove release worktrees whose PRs are no longer open')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(await worktreesSync({ confirmedCommand: options.yes }))\n })\n}\n\nconst configureWorktreesAdd = (cmd: Command): Command => {\n return cmd\n .description('Add git worktrees for release branches')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('-a, --all', 'Select all active release branches')\n .option('-v, --versions <versions>', 'Specify versions by comma, e.g. 1.2.5, 1.2.6')\n .option('-i, --ide [mode]', 'Editor mode for created worktrees: workspace (default) | none')\n .option('--no-ide', 'Skip the editor (alias for --ide none)')\n .option('-c, --cursor [mode]', 'Deprecated alias for --ide')\n .option('--no-cursor', 'Deprecated alias for --no-ide')\n .option('-g, --github-desktop', 'Open created worktrees in GitHub Desktop')\n .option('--no-github-desktop', 'Skip GitHub Desktop prompt')\n .option('-m, --cmux', 'Open created worktrees in cmux (3-pane layout)')\n .option('--no-cmux', 'Skip cmux prompt')\n .action(async (options) => {\n // `--ide` wins over the deprecated `--cursor` alias when both are provided.\n const ide = normalizeIdeMode(options.ide, '--ide') ?? normalizeIdeMode(options.cursor, '--cursor')\n\n emit(\n await worktreesAdd({\n confirmedCommand: options.yes,\n all: options.all,\n versions: options.versions,\n ide,\n githubDesktop: options.githubDesktop,\n cmux: options.cmux,\n }),\n )\n })\n}\n\nconst configureWorktreesList = (cmd: Command): Command => {\n return cmd.description('List all git worktrees with detailed information').action(async () => {\n emit(await worktreesList())\n })\n}\n\nconst configureWorktreesRemove = (cmd: Command): Command => {\n return cmd\n .description('Remove git worktrees for release branches')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('-a, --all', 'Select all active release branches')\n .option('-v, --versions <versions>', 'Specify versions by comma, e.g. 1.2.5, 1.2.6')\n .action(async (options) => {\n emit(await worktreesRemove({ confirmedCommand: options.yes, all: options.all, versions: options.versions }))\n })\n}\n\nconst configureWorktreesReload = (cmd: Command): Command => {\n return cmd\n .description(\n 'Close all cmux/editor worktree windows, then reopen the current release worktrees (also cold-start restore)',\n )\n .action(async () => {\n emit(await worktreesReload())\n })\n}\n\nconst configureVendorConfig = (cmd: Command): Command => {\n return cmd\n .description('Show the machine-local factory config (~/.infra-kit/vendor.json) or scaffold it with --init')\n .option('--init', 'Scaffold ~/.infra-kit/vendor.json (skips if it already exists)')\n .action(async (options) => {\n emit(await vendorConfig({ init: options.init }))\n })\n}\n\n// --- Grouped command surface (preferred form) ---\nconst releaseGroup = program.command('release').description('Release management commands')\n\nconfigureMergeDev(releaseGroup.command('merge-dev'))\nconfigureReleaseList(releaseGroup.command('list'))\nconfigureReleaseCreate(releaseGroup.command('create'))\nconfigureReleaseDescEdit(releaseGroup.command('desc-edit'))\nconfigureReleaseDeployAll(releaseGroup.command('deploy-all'))\nconfigureReleaseDeploySelected(releaseGroup.command('deploy-selected'))\nconfigureReleaseDeliver(releaseGroup.command('deliver'))\n\nconst worktreesGroup = program.command('worktrees').description('Git worktree management commands')\n\nconfigureWorktreesAdd(worktreesGroup.command('add'))\nconfigureWorktreesList(worktreesGroup.command('list'))\nconfigureWorktreesRemove(worktreesGroup.command('remove'))\nconfigureWorktreesSync(worktreesGroup.command('sync'))\nconfigureWorktreesReload(worktreesGroup.command('reload'))\n\n// --- Deprecated flat aliases (kept one release cycle; warn when used directly) ---\ndeprecatedAlias(configureMergeDev(program.command('merge-dev')), 'release merge-dev')\ndeprecatedAlias(configureReleaseList(program.command('release-list')), 'release list')\ndeprecatedAlias(configureReleaseCreate(program.command('release-create')), 'release create')\ndeprecatedAlias(configureReleaseDescEdit(program.command('release-desc-edit')), 'release desc-edit')\ndeprecatedAlias(configureReleaseDeployAll(program.command('release-deploy-all')), 'release deploy-all')\ndeprecatedAlias(configureReleaseDeploySelected(program.command('release-deploy-selected')), 'release deploy-selected')\ndeprecatedAlias(configureReleaseDeliver(program.command('release-deliver')), 'release deliver')\ndeprecatedAlias(configureWorktreesAdd(program.command('worktrees-add')), 'worktrees add')\ndeprecatedAlias(configureWorktreesList(program.command('worktrees-list')), 'worktrees list')\ndeprecatedAlias(configureWorktreesRemove(program.command('worktrees-remove')), 'worktrees remove')\ndeprecatedAlias(configureWorktreesSync(program.command('worktrees-sync')), 'worktrees sync')\ndeprecatedAlias(configureWorktreesReload(program.command('worktrees-reload')), 'worktrees reload')\n\nconst configCmd = program.command('config').description('Manage infra-kit configuration files')\n\nconfigCmd\n .command('path')\n .description('Show the resolved config merge chain and file paths')\n .action(async () => {\n emit(await configPath())\n })\n\nconfigCmd\n .command('edit')\n .description('Open the user-scope per-project override file in $EDITOR')\n .action(async () => {\n emit(await configEdit())\n })\n\nprogram\n .command('audit')\n .description('Audit against infra-kit.config.ts rules (--all for every package, --root for the monorepo root)')\n .option('-a, --all', 'Audit every non-vendor workspace package')\n .option('-r, --root', 'Audit the monorepo root (turbo pipeline + root commands)')\n .action(async (options) => {\n const result = await audit({ all: options.all, root: options.root })\n\n emit(result)\n\n if (!result.structuredContent.allPassed) {\n process.exitCode = 1\n }\n })\n\nconst vendorCmd = program.command('vendor').description('Verify and sync the mirrored vendor/ tree')\n\nvendorCmd\n .command('check')\n .description('Verify vendor/ matches vendor/.sync-manifest.json (self-contained; for any consumer repo)')\n .action(async () => {\n const result = await vendorCheck()\n\n emit(result)\n\n if (!result.structuredContent.ok) {\n process.exitCode = 1\n }\n })\n\nvendorCmd\n .command('sync')\n .description('Copy vendored files from the source repo into each target and regenerate manifests')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('-r, --repos <repos>', 'Restrict to comma-separated target repo names')\n .action(async (options) => {\n emit(await vendorSync({ confirmedCommand: options.yes, repos: parseRepos(options.repos) }))\n })\n\nvendorCmd\n .command('manifest')\n .description('Regenerate each target vendor/.sync-manifest.json + README from current content (no copy)')\n .option('-r, --repos <repos>', 'Restrict to comma-separated target repo names')\n .action(async (options) => {\n emit(await vendorManifest({ confirmedCommand: true, repos: parseRepos(options.repos) }))\n })\n\nvendorCmd\n .command('diff')\n .description('Source-aware drift check (rsync dry-run) of each target vendored subtree vs the source')\n .option('-r, --repos <repos>', 'Restrict to comma-separated target repo names')\n .action(async (options) => {\n const result = await vendorDiff({ repos: parseRepos(options.repos) })\n\n emit(result)\n\n if (!result.structuredContent.ok) {\n process.exitCode = 1\n }\n })\n\n// Grouped form (preferred); the flat `vendor-config` below is a deprecated alias.\nconfigureVendorConfig(vendorCmd.command('config'))\n\ndeprecatedAlias(configureVendorConfig(program.command('vendor-config')), 'vendor config')\n\nprogram\n .command('doctor')\n .description('Check installation and authentication status of gh and doppler CLIs')\n .action(async () => {\n emit(await doctor())\n })\n\nprogram\n .command('dev')\n .description('Run local dev servers for a named devPresets preset (or all apps); api + ui')\n .argument('[preset]', 'Named preset from devPresets (omit to run every app)')\n .option('-w, --watch', 'Rebuild and restart on file save')\n .option('--app <names>', 'Further narrow to these app folder names (comma-separated)')\n .option(\n '--cmux',\n 'Run each app in its own cmux pane (one workspace, N panes; falls back to single terminal if cmux is unavailable)',\n )\n .option('--self', 'Run only the app of the current directory (infer from cwd; use inside apps/<app>/\u2026)')\n .option(\n '-V, --verbose',\n 'Print full boot narration (default: quiet; full detail always in .infra-kit/dev-server.log)',\n )\n .action(async (preset, options) => {\n // Lazy import so fastify/chokidar (and the whole dev stack) never load on the\n // eager cli graph \u2014 they land in a split chunk reached only for `infra-kit dev`.\n const { runDevServer, toDevServerOptions } = await import('src/entry/dev-server')\n\n await runDevServer(toDevServerOptions({ ...options, preset }))\n })\n\nprogram\n .command('version')\n .description('Print the installed infra-kit CLI version')\n .action(async () => {\n emit(await version())\n })\n\nprogram\n .command('env-status')\n .description('Show which env is loaded in this session (local introspection; no Doppler call)')\n .action(async () => {\n emit(await envStatus())\n })\n\nprogram\n .command('env-list')\n .description('List available Doppler configs for the detected project')\n .action(async () => {\n emit(await envList())\n })\n\nprogram\n .command('init')\n .description('Inject shell integration into .zshrc and sync repo agent-instruction files')\n .action(async () => {\n emit(await init())\n })\n\nprogram\n .command('env-load')\n .description('Load Doppler env vars for a config. Source the returned file path to apply.')\n .option('-c, --config <config>', 'Environment config name to load (e.g. dev, arthur)')\n .action(async (options) => {\n emit(await envLoad({ config: options.config }))\n })\n\nprogram\n .command('env-clear')\n .description('Clear loaded env vars. Source the returned file path to apply.')\n .option('--purge', \"Also delete this project's warm cache outright (durable disable)\")\n .action(async (options) => {\n emit(await envClear({ purge: Boolean(options.purge) }))\n })\n\n// Internal: driven by the init shell-startup integration (backgrounded). Writes\n// env-load.sh when envAutoLoad is configured + eligible; the precmd hook sources\n// it. Hidden + no stdout output so it never pollutes the shell or the menu.\nprogram\n .command('env-autoload', { hidden: true })\n .description('Internal: prime env for the shell-startup auto-load trigger')\n // The shell passes its already-canonicalized (`${dir:A}`) project dir so node can\n // key the warm cache identically; see writeEnvLoadFile / shouldWriteWarm.\n .option('--project-dir <dir>', 'Canonical project dir for the warm-cache key (shell-startup only)')\n .action(async (options) => {\n await envAutoload({ projectDir: options.projectDir })\n })\n\n// Register `--json` on every command, then resolve the flag before each action\n// runs. In JSON mode we lower the logger to `warn` so the human-oriented info\n// lines stop cluttering stderr while errors still surface; the structured\n// payload is written to stdout by `emit`. No handler logic is affected.\nprogram.commands.forEach(addJsonOption)\n\n// Commands excluded from the cli-invocation auto-load trigger: the env-* family\n// (avoids recursion \u2014 `env-autoload`/`env-load` would re-enter), plus the\n// host-inspecting / meta commands where priming Doppler env would be surprising\n// (`init` bootstraps the shell block, `doctor` inspects auth, `version` prints a\n// string, `dev` is a long-running server that manages its own env). `--help`/\n// `--version`/the bare-arg menu don't fire preAction at all.\nconst isAutoLoadExcludedCommand = (name: string): boolean => {\n return name.startsWith('env-') || name === 'init' || name === 'doctor' || name === 'version' || name === 'dev'\n}\n\nprogram.hook('preAction', async (_thisCommand, actionCommand) => {\n // `optsWithGlobals` (not `opts`) so `--json` is seen on grouped subcommands:\n // for `release list --json` Commander binds the post-subcommand flag to the\n // parent `release` group, so the leaf's own `opts()` would not carry it.\n jsonOutput.enabled = Boolean(actionCommand.optsWithGlobals().json)\n\n if (jsonOutput.enabled) {\n logger.level = 'warn'\n }\n\n // cli-invocation auto-load: primes the shell env for SUBSEQUENT commands. The\n // current command does NOT see these vars \u2014 a child process can't mutate its\n // parent shell; the precmd hook sources the written file on the next prompt.\n // runEnvAutoLoad self-gates on config trigger and swallows transient failures,\n // so this is a no-op unless configured for cli-invocation and never blocks.\n if (!isAutoLoadExcludedCommand(actionCommand.name())) {\n await runEnvAutoLoad({ expectedTrigger: 'cli-invocation' })\n }\n})\n\nif (process.argv.length <= 2) {\n // Menu groups derive from the single command catalog (no hand-maintained\n // name arrays). Membership and order live in src/lib/command-catalog.\n const releaseCommands = getMenuGroupCommands('release')\n const worktreeCommands = getMenuGroupCommands('worktrees')\n const envCommands = getMenuGroupCommands('environment')\n\n const commandMap = new Map(\n program.commands.map((cmd) => {\n return [cmd.name(), cmd]\n }),\n )\n\n const groups = [\n { label: 'Release Management', names: releaseCommands },\n { label: 'Worktrees', names: worktreeCommands },\n { label: 'Environment', names: envCommands },\n ]\n\n // Flat {name, description, group} list shared by both the Ink palette and the\n // Inquirer fallback; descriptions come from Commander (single source).\n const paletteItems = groups.flatMap(({ label, names }) => {\n return names\n .filter((name) => {\n return commandMap.has(name)\n })\n .map((name) => {\n return { name, description: commandMap.get(name)!.description(), group: label }\n })\n })\n\n let selected: string | null = null\n\n // Interactive TTY \u2192 Ink command palette, loaded lazily via dynamic import so\n // React/Ink never touch the MCP / `--json` / non-TTY code paths. Otherwise fall\n // back to the Inquirer menu (scripts, pipes, CI). Ctrl-C / Esc at the menu\n // throws from the Inquirer fallback; treat it as a clean exit (nothing picked).\n try {\n if (process.stdout.isTTY && process.stdin.isTTY) {\n const { runCommandPalette } = await import('src/tui/boot')\n\n selected = await runCommandPalette(paletteItems)\n } else {\n const alignedLabels = formatAlignedRows(\n paletteItems.map((item) => {\n return [item.name, item.description] as const\n }),\n )\n const labelByName = new Map<string, string>()\n\n paletteItems.forEach((item, index) => {\n labelByName.set(item.name, alignedLabels[index] ?? item.name)\n })\n\n const toChoices = (names: string[]) => {\n return names\n .filter((name) => {\n return commandMap.has(name)\n })\n .map((name) => {\n return {\n name: labelByName.get(name) ?? name,\n value: name,\n }\n })\n }\n\n selected = await select(\n {\n message: 'Select a command to run',\n choices: [\n new Separator(' '),\n new Separator('\u2014 Release Management \u2014'),\n ...toChoices(releaseCommands),\n new Separator(' '),\n new Separator('\u2014 Worktrees \u2014'),\n ...toChoices(worktreeCommands),\n new Separator(' '),\n new Separator('\u2014 Environment \u2014'),\n ...toChoices(envCommands),\n ],\n },\n { output: process.stderr },\n )\n }\n } catch (error) {\n // Ctrl-C / Esc at the menu is a clean back-out; leave `selected` as null.\n if (!isPromptCancellation(error)) throw error\n }\n\n // The menu is a guided surface; don't nag about deprecated flat names here.\n if (selected) {\n invokedViaMenu.value = true\n\n await runProgram(['node', 'infra-kit', selected])\n }\n} else {\n await runProgram()\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { $ } from 'zx'\n\nimport { getInfraKitConfigPaths, resetInfraKitConfigCache } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { fileExists, tildify } from 'src/lib/path-display'\nimport type { ToolsExecutionResult } from 'src/types'\n\n/**\n * Print the file paths that participate in the config merge chain along with\n * existence markers, so the user can see at a glance which override layers\n * are active.\n *\n * @example\n * // CLI: `infra-kit config path`\n * // INFO: Project name: api\n * // INFO: Config merge chain (later overrides earlier):\n * // INFO: [\u2713] project (committed) ~/projects/api/infra-kit.json\n * // INFO: [ ] user global ~/.infra-kit/infra-kit.json\n * // INFO: [\u2713] user project ~/.infra-kit/projects/api/infra-kit.json\n */\nexport const configPath = async (): Promise<ToolsExecutionResult> => {\n const paths = await getInfraKitConfigPaths()\n\n const rows: { label: string; path: string; exists: boolean }[] = await Promise.all(\n [\n { label: 'project (committed)', path: paths.main },\n { label: 'user global', path: paths.userGlobal },\n { label: 'user project', path: paths.userProject },\n ].map(async (row) => {\n return { ...row, exists: await fileExists(row.path) }\n }),\n )\n\n logger.info(`Project name: ${paths.projectName}\\n`)\n logger.info('Config merge chain (later overrides earlier):\\n')\n\n for (const row of rows) {\n const marker = row.exists ? ' [\u2713]' : ' [ ]'\n\n logger.info(`${marker} ${row.label.padEnd(22)} ${tildify(row.path)}`)\n }\n\n const structuredContent = {\n projectName: paths.projectName,\n layers: rows.map((r) => {\n return { label: r.label, path: r.path, exists: r.exists }\n }),\n }\n\n return {\n content: [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],\n structuredContent,\n }\n}\n\n/**\n * Open the user-scope per-project override file in $EDITOR, creating the\n * parent directory and a stub file on first use. Resets the config cache\n * after the editor exits so subsequent reads pick up edits without a restart.\n *\n * @example\n * // CLI: `infra-kit config edit`\n * // first run \u2014 creates ~/.infra-kit/projects/api/infra-kit.json ({}) + a sibling\n * // infra-kit.example.jsonc reference, then $EDITOR opens the .json\n * // subsequent runs \u2014 opens the existing file as-is\n */\nexport const configEdit = async (): Promise<ToolsExecutionResult> => {\n const paths = await getInfraKitConfigPaths()\n const editor = process.env.EDITOR || process.env.VISUAL || 'vi'\n\n await fs.mkdir(path.dirname(paths.userProject), { recursive: true })\n\n if (!(await fileExists(paths.userProject))) {\n // JSON can't carry comments, so seed an empty-but-valid config and drop the\n // annotated guidance next to it in a non-loaded .example.jsonc the loader\n // never reads (it only globs the three exact `infra-kit.json` filenames).\n const examplePath = exampleSiblingPath(paths.userProject)\n\n await fs.writeFile(paths.userProject, '{}\\n', 'utf-8')\n await fs.writeFile(examplePath, buildUserProjectExample(paths.projectName), 'utf-8')\n\n logger.info(`Created ${tildify(paths.userProject)} \u2014 see ${tildify(examplePath)} for the annotated reference.`)\n }\n\n logger.info(`Opening ${tildify(paths.userProject)} in ${editor}`)\n\n await $({ stdio: 'inherit' })`${editor} ${paths.userProject}`\n\n resetInfraKitConfigCache()\n\n const structuredContent = { path: paths.userProject, editor }\n\n return {\n content: [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],\n structuredContent,\n }\n}\n\n/**\n * Derive the non-loaded `.example.jsonc` sibling for a config path.\n *\n * @example\n * exampleSiblingPath('/u/.infra-kit/projects/api/infra-kit.json')\n * // => '/u/.infra-kit/projects/api/infra-kit.example.jsonc'\n */\nconst exampleSiblingPath = (jsonPath: string): string => {\n return jsonPath.replace(/\\.json$/, '.example.jsonc')\n}\n\n/**\n * Annotated JSONC reference for the user-scope per-project override layer.\n * Written alongside the real `{}` config so the guidance the old YAML stub\n * carried in comments survives the move to JSON.\n *\n * @example\n * buildUserProjectExample('api')\n * // => '// infra-kit user override for api \u2026\\n{ \u2026 }\\n'\n */\nconst buildUserProjectExample = (projectName: string): string => {\n return `// infra-kit user override for ${projectName} \u2014 ~/.infra-kit/projects/${projectName}/infra-kit.json\n//\n// Layer 3 (highest precedence) of the config merge chain. Shallow-merged on top\n// of <repo>/infra-kit.json and ~/.infra-kit/infra-kit.json \u2014 top-level keys\n// (environments, envManagement, ide, taskManager, worktrees, envAutoLoad) replace wholesale.\n//\n// This .example.jsonc is reference only \u2014 it is NOT loaded. Put real overrides\n// in the sibling infra-kit.json (strict JSON: no comments, double-quoted keys).\n{\n // \"worktrees\": { \"openInGithubDesktop\": false, \"openInCmux\": true, \"cmux\": { \"layout\": \"two-columns\" } },\n // // Auto-load Doppler env here. trigger (pick one): shell-startup | cli-invocation; config: an environment name.\n // \"envAutoLoad\": { \"trigger\": \"shell-startup\", \"config\": \"dev\" }\n}\n`\n}\n", "import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport { writeEnvLoadFile } from 'src/commands/env-load'\nimport {\n ENV_CLEAR_FILE,\n ENV_LOAD_FILE,\n INFRA_KIT_ENV_AUTOLOADED_VAR,\n INFRA_KIT_ENV_CLEARED_VAR,\n INFRA_KIT_ENV_CONFIG_VAR,\n INFRA_KIT_ENV_PROJECT_VAR,\n INFRA_KIT_SESSION_VAR,\n getSessionCacheDir,\n} from 'src/lib/constants'\nimport type { EnvAutoLoadConfig } from 'src/lib/infra-kit-config'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\n\n/** Which moment a concrete callsite represents. Matches the config `trigger`. */\nexport type AutoLoadTrigger = EnvAutoLoadConfig['trigger']\n\n/** Per-session flag de-duping the MISCONFIG warning (bad envAutoLoad.config). */\nconst WARN_MISCONFIG_SENTINEL_FILE = 'autoload-warn-misconfig.flag'\n/** Per-session flag de-duping the transient-FAILURE warning (Doppler down/unauth). */\nconst WARN_FAIL_SENTINEL_FILE = 'autoload-warn-fail.flag'\n\n/** Per-session marker recording the last auto-load failure (mtime = when). */\nconst FAIL_SENTINEL_FILE = 'autoload-fail.flag'\n\n/**\n * After a failed auto-load, suppress retries for this long so a down/unauthenticated\n * Doppler isn't re-probed on every cli-invocation. A new shell starts a fresh\n * session cache dir, so this only throttles within one session.\n */\nconst FAIL_BACKOFF_MS = 30_000\n\n/** Resolved auto-load inputs: the chosen trigger + the env config and Doppler project to load. */\nexport interface ResolvedEnvAutoLoad {\n trigger: AutoLoadTrigger\n config: string\n project: string\n}\n\n/**\n * Read the resolved + validated env auto-load inputs, or `null` when auto-load\n * should not run. Returns `null` (never throws) when:\n * - we're not inside an infra-kit project (getInfraKitConfig throws), or config is unreadable;\n * - `envAutoLoad` is absent (feature off);\n * - `envAutoLoad.config` is not one of `environments` \u2014 warns once per session, then disables.\n *\n * Validation lives here (not in the schema) so a typo only disables this optional\n * feature instead of throwing inside the merged-config parse and breaking every command.\n * Resolves the Doppler project from the SAME config read (no second getInfraKitConfig),\n * so the skip path stays cheap.\n *\n * `canWarn` gates the misconfig warning: the shell-startup callsite runs backgrounded\n * with stderr discarded, so warning there is invisible AND would write the dedup flag,\n * poisoning the only channel (cli-invocation / interactive) that can actually surface\n * it. So shell-startup passes `false`; interactive callsites pass `true`.\n */\nexport const resolveEnvAutoLoad = async (canWarn = true): Promise<ResolvedEnvAutoLoad | null> => {\n let config\n\n try {\n config = await getInfraKitConfig()\n } catch {\n return null\n }\n\n const autoLoad = config.envAutoLoad\n\n if (!autoLoad) return null\n\n if (!config.environments.includes(autoLoad.config)) {\n if (canWarn) {\n warnOnce(\n `infra-kit: envAutoLoad.config \"${autoLoad.config}\" is not one of environments [${config.environments.join(\n ', ',\n )}] \u2014 env auto-load disabled.`,\n WARN_MISCONFIG_SENTINEL_FILE,\n )\n }\n\n return null\n }\n\n return {\n trigger: autoLoad.trigger,\n config: autoLoad.config,\n project: config.envManagement.config.name,\n }\n}\n\n/** The env-var snapshot the freshness/suppression guards read. */\nexport interface AutoLoadEnvSnapshot {\n session?: string\n cleared?: string\n currentConfig?: string\n currentProject?: string\n autoLoadedMarker?: string\n}\n\nexport interface AutoLoadDecisionInput {\n /** The configured trigger. */\n trigger: AutoLoadTrigger\n /** Which trigger this callsite represents. */\n expectedTrigger: AutoLoadTrigger\n targetConfig: string\n targetProject: string\n env: AutoLoadEnvSnapshot\n /**\n * Bypass ONLY the \"already auto-loaded, same config+project\" no-op skip, forcing\n * a fresh Doppler fetch. The shell-startup refresh sets this: after a WARM source\n * the shell has already exported INFRA_KIT_ENV_AUTOLOADED + the same config, which\n * the child process inherits \u2014 without `force` the refresh would self-skip and the\n * warm (possibly rotated) secrets would never be replaced this session. Does NOT\n * relax the clear/manual-load guards.\n */\n force?: boolean\n}\n\nexport type AutoLoadDecision = 'load' | 'skip'\n\n/**\n * Pure decision: should this callsite (auto-)load env right now? Encodes the full\n * guard matrix so it is exhaustively unit-testable without Doppler or a shell:\n * - the configured trigger must match this callsite;\n * - a session must exist (the cache dir is session-scoped);\n * - an explicit clear suppresses auto-load (M2);\n * - a MANUAL load (config set, no auto marker) is never clobbered (C1);\n * - an auto-load already fresh for the same config AND project is a no-op\n * (project-aware so two same-named configs across different-repo worktrees\n * sharing one session don't leak each other's secrets).\n */\nexport const decideAutoLoad = (input: AutoLoadDecisionInput): AutoLoadDecision => {\n const { trigger, expectedTrigger, targetConfig, targetProject, env, force } = input\n\n if (trigger !== expectedTrigger) return 'skip'\n\n if (!env.session) return 'skip'\n\n if (env.cleared) return 'skip'\n\n // Manual load present (a config is loaded but it wasn't auto-loaded) \u2014 leave it.\n if (env.currentConfig && !env.autoLoadedMarker) return 'skip'\n\n // Our own auto-load already matches the target config+project \u2014 normally a no-op,\n // but `force` (the shell-startup warm refresh) must re-fetch: the shell just warm\n // -sourced these same markers, so skipping here would strand stale secrets.\n if (!force && env.autoLoadedMarker && env.currentConfig === targetConfig && env.currentProject === targetProject) {\n return 'skip'\n }\n\n return 'load'\n}\n\nexport interface RunEnvAutoLoadArgs {\n expectedTrigger: AutoLoadTrigger\n /**\n * Canonical (realpath'd) project dir, forwarded to `writeEnvLoadFile` to enable\n * the project-scoped WARM cache. Only the shell-startup spawn passes it (via\n * `--project-dir`); the cli-invocation trigger omits it, so warm is a\n * shell-startup-only optimization.\n */\n projectDir?: string\n /**\n * Force a fresh fetch past the \"already auto-loaded, same config\" no-op skip. The\n * shell-startup refresh sets this so a preceding WARM source (which exports the\n * same markers the child inherits) is always replaced by fresh secrets. See\n * {@link decideAutoLoad}. The cli-invocation trigger leaves it false.\n */\n force?: boolean\n}\n\n/**\n * Resolve config, evaluate the guards, and (if it should) produce env-load.sh with\n * the auto-load marker. Returns the written file path, or `null` when auto-load was\n * skipped or failed. NEVER throws. Transient failures (Doppler offline / not\n * authenticated / network) record a backoff marker so they aren't re-probed on every\n * command, and are surfaced once per session on the interactive (cli-invocation)\n * channel. No producer-side lock \u2014 a rare cold-shell double-fetch is tolerated (the\n * second write is atomic and idempotent).\n */\nexport const runEnvAutoLoad = async ({\n expectedTrigger,\n projectDir,\n force,\n}: RunEnvAutoLoadArgs): Promise<string | null> => {\n // Only the cli-invocation / interactive callsite reaches a TTY; the shell-startup\n // spawn discards stderr, so warning there is invisible and would poison the dedup.\n const canWarn = expectedTrigger === 'cli-invocation'\n\n try {\n const resolved = await resolveEnvAutoLoad(canWarn)\n\n if (!resolved) return null\n\n const decision = decideAutoLoad({\n trigger: resolved.trigger,\n expectedTrigger,\n targetConfig: resolved.config,\n targetProject: resolved.project,\n env: readAutoLoadEnvSnapshot(),\n force,\n })\n\n if (decision === 'skip') return null\n\n // Disk-level clear signal: a clear that hasn't yet been sourced into this\n // process's env still suppresses auto-load (clear file newer than load file).\n if (isClearedOnDisk()) return null\n\n // Back off after a recent failure so a down/unauthenticated Doppler isn't\n // re-probed on every command in the same session.\n if (recentlyFailed()) return null\n\n const preWriteMtime = readLoadFileMtime()\n const result = await writeEnvLoadFile({\n config: resolved.config,\n autoLoaded: true,\n projectDir,\n // Re-check after the slow Doppler download: abort if a clear or a manual load\n // landed meanwhile, so a backgrounded auto-load never clobbers a deliberate action.\n beforeWrite: () => {\n return !isClearedOnDisk() && !manualLoadLandedSince(preWriteMtime)\n },\n })\n\n if (!result) return null\n\n clearFailure()\n\n return result.filePath\n } catch (error) {\n const reason = (error as Error).message\n\n recordFailure()\n\n // Surface the failure once per session on the interactive channel; stay silent\n // (debug only) on the backgrounded shell-startup path.\n if (canWarn) {\n warnOnce(`infra-kit: env auto-load failed \u2014 ${reason} (will retry later)`, WARN_FAIL_SENTINEL_FILE)\n } else {\n logger.debug(`env auto-load skipped: ${reason}`)\n }\n\n return null\n }\n}\n\n/** Snapshot the env vars the guards depend on. */\nconst readAutoLoadEnvSnapshot = (): AutoLoadEnvSnapshot => {\n return {\n session: process.env[INFRA_KIT_SESSION_VAR],\n cleared: process.env[INFRA_KIT_ENV_CLEARED_VAR],\n currentConfig: process.env[INFRA_KIT_ENV_CONFIG_VAR],\n currentProject: process.env[INFRA_KIT_ENV_PROJECT_VAR],\n autoLoadedMarker: process.env[INFRA_KIT_ENV_AUTOLOADED_VAR],\n }\n}\n\n/** mtime of the on-disk env-load.sh, or null if absent/unreadable. */\nconst readLoadFileMtime = (): number | null => {\n try {\n return fs.statSync(path.join(getSessionCacheDir(), ENV_LOAD_FILE)).mtimeMs\n } catch {\n return null\n }\n}\n\n/**\n * True when a MANUAL env-load landed on disk after `sinceMtime` (the file now\n * exists, is newer, and carries the manual-load `unset INFRA_KIT_ENV_AUTOLOADED`\n * line). Used to abort an in-flight auto-load so it never clobbers a deliberate load.\n */\nconst manualLoadLandedSince = (sinceMtime: number | null): boolean => {\n try {\n const p = path.join(getSessionCacheDir(), ENV_LOAD_FILE)\n\n if (!fs.existsSync(p)) return false\n\n const mtime = fs.statSync(p).mtimeMs\n\n if (sinceMtime !== null && mtime <= sinceMtime) return false\n\n // Anchor to a whole line so the marker can't match inside a single-quoted\n // secret value that happens to contain this literal text.\n const manualMarker = new RegExp(`^unset ${INFRA_KIT_ENV_AUTOLOADED_VAR}$`, 'm')\n\n return manualMarker.test(fs.readFileSync(p, 'utf-8'))\n } catch {\n return false\n }\n}\n\n/**\n * True when a clear is pending on disk: an env-clear.sh exists and is at least as\n * new as env-load.sh (or no load file remains). Belt-and-suspenders next to the\n * INFRA_KIT_ENV_CLEARED env guard for the window before the shell sources the clear.\n */\nconst isClearedOnDisk = (): boolean => {\n try {\n const dir = getSessionCacheDir()\n const clearPath = path.join(dir, ENV_CLEAR_FILE)\n\n if (!fs.existsSync(clearPath)) return false\n\n const loadPath = path.join(dir, ENV_LOAD_FILE)\n\n if (!fs.existsSync(loadPath)) return true\n\n return fs.statSync(clearPath).mtimeMs >= fs.statSync(loadPath).mtimeMs\n } catch {\n return false\n }\n}\n\n/** True when an auto-load failed within the backoff window (suppress retries). */\nconst recentlyFailed = (): boolean => {\n try {\n const flagPath = path.join(getSessionCacheDir(), FAIL_SENTINEL_FILE)\n\n if (!fs.existsSync(flagPath)) return false\n\n return Date.now() - fs.statSync(flagPath).mtimeMs < FAIL_BACKOFF_MS\n } catch {\n return false\n }\n}\n\n/** Record an auto-load failure (refreshes the backoff window). */\nconst recordFailure = (): void => {\n try {\n const dir = getSessionCacheDir()\n\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n fs.writeFileSync(path.join(dir, FAIL_SENTINEL_FILE), '', { mode: 0o600 })\n } catch {\n // No session cache dir \u2014 nothing to back off against.\n }\n}\n\n/** Clear the failure marker after a successful load. */\nconst clearFailure = (): void => {\n try {\n fs.rmSync(path.join(getSessionCacheDir(), FAIL_SENTINEL_FILE), { force: true })\n } catch {\n // No session cache dir \u2014 nothing to clear.\n }\n}\n\n/**\n * Emit a warning at most once per shell session. Keyed on a flag file in the\n * session cache dir so a misconfigured `envAutoLoad.config` (or a repeated failure)\n * does not spam a warning on every cli-invocation. Falls back to a plain warn when\n * no session cache dir is available.\n */\nconst warnOnce = (message: string, sentinelFile: string): void => {\n try {\n const dir = getSessionCacheDir()\n const flagPath = path.join(dir, sentinelFile)\n\n if (fs.existsSync(flagPath)) return\n\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n fs.writeFileSync(flagPath, '', { mode: 0o600 })\n } catch {\n // No session cache dir (e.g. INFRA_KIT_SESSION unset) \u2014 warn without de-dup.\n }\n\n logger.warn(message)\n}\n", "import { runEnvAutoLoad } from 'src/lib/env-autoload'\n\nexport interface EnvAutoloadArgs {\n /**\n * Canonical (realpath'd) project dir the shell computed (`${dir:A}`) and passed\n * via `--project-dir`. Forwarded to enable the project-scoped warm cache; absent\n * when invoked without the flag (then no warm copy is written).\n */\n projectDir?: string\n}\n\n/**\n * Internal command invoked (backgrounded) by the `infra-kit init` shell-startup\n * integration. Runs the 'shell-startup' trigger, writing env-load.sh when\n * envAutoLoad is configured for it + eligible; the shell precmd hook sources it on\n * a subsequent prompt. Intentionally writes NOTHING to stdout and never throws \u2014\n * auto-load must never disrupt shell startup.\n */\nexport const envAutoload = async ({ projectDir }: EnvAutoloadArgs = {}): Promise<void> => {\n // `force`: the shell may have just WARM-sourced the same config (exporting the\n // auto-load marker this process inherits); without it the refresh would self-skip\n // and stale warm secrets would never be replaced this session.\n await runEnvAutoLoad({ expectedTrigger: 'shell-startup', projectDir, force: true })\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { pathToFileURL } from 'node:url'\n\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { fileExists, tildify } from 'src/lib/path-display'\nimport { VENDOR_CONFIG_FILE } from 'src/lib/vendor/config-schema'\nimport { expandTilde, getFactoryConfigPath, loadFactoryConfig } from 'src/lib/vendor/factory-config'\n\ninterface VendorConfigOptions {\n /** Scaffold `~/.infra-kit/vendor.json` instead of printing the current one. */\n init?: boolean\n /** Source repo root used for legacy `targets` auto-seeding. Defaults to the git toplevel. */\n cwd?: string\n}\n\n/** Placeholder workspace dir written by `--init`; the user edits it to their layout. */\nconst PLACEHOLDER_WORKSPACE_DIR = '~/projects'\n\n/**\n * Surface or scaffold the machine-local factory config\n * (`~/.infra-kit/vendor.json`). CLI-only \u2014 NOT an MCP tool; returns nothing\n * and signals problems via `process.exitCode`.\n *\n * Without `--init`: prints the factory file path + existence, the resolved\n * `workspaceDir` + existence, and per-target reachability (`[\u2713]`/`[ ]`). Exits\n * non-zero if the file is missing, the workspace dir is missing, or any target\n * is unreachable, so it is usable as a doctor check.\n *\n * With `--init`: scaffolds the file (skipping if it already exists), seeding\n * `targets` from a legacy source `vendor.config.ts` when one is readable.\n */\nexport const vendorConfig = async (options: VendorConfigOptions = {}): Promise<void> => {\n if (options.init) {\n await initFactoryConfig(options.cwd)\n\n return\n }\n\n await printFactoryConfig()\n}\n\n/** Render the factory config chain with `[\u2713]`/`[ ]` reachability markers. */\nconst printFactoryConfig = async (): Promise<void> => {\n const factoryPath = getFactoryConfigPath()\n const exists = await fileExists(factoryPath)\n\n logger.info(`Factory config: ${tildify(factoryPath)} ${exists ? '[\u2713]' : '[ ]'}`)\n\n if (!exists) {\n logger.info('\\nNot found \u2014 run `infra-kit vendor-config --init` to scaffold it.')\n process.exitCode = 1\n\n return\n }\n\n const { workspaceDir, targets } = await loadFactoryConfig()\n const resolvedWorkspace = expandTilde(workspaceDir)\n const workspaceExists = await fileExists(resolvedWorkspace)\n\n logger.info(\n `workspaceDir: ${workspaceDir} (resolved: ${resolvedWorkspace}) ${workspaceExists ? '[\u2713 exists]' : '[ ] not found'}`,\n )\n logger.info('Targets:')\n\n let allReachable = workspaceExists\n\n for (const repo of targets) {\n const targetPath = path.join(resolvedWorkspace, repo)\n const reachable = await fileExists(targetPath)\n\n if (!reachable) {\n allReachable = false\n }\n\n const marker = reachable ? '[\u2713]' : '[ ]'\n const suffix = reachable ? '' : ' (not found \u2014 clone or remove)'\n\n logger.info(` ${marker} ${repo} ${tildify(targetPath)}${suffix}`)\n }\n\n if (!allReachable) {\n process.exitCode = 1\n }\n}\n\n/** Scaffold `~/.infra-kit/vendor.json`, skipping if it already exists. */\nconst initFactoryConfig = async (cwd?: string): Promise<void> => {\n const factoryPath = getFactoryConfigPath()\n\n if (await fileExists(factoryPath)) {\n logger.info(`Factory config already exists at ${tildify(factoryPath)} \u2014 leaving it untouched.`)\n\n return\n }\n\n const sourceRoot = cwd ?? (await getProjectRoot())\n const seededTargets = await readLegacyTargets(sourceRoot)\n\n await fs.mkdir(path.dirname(factoryPath), { recursive: true })\n await fs.writeFile(factoryPath, buildScaffold(seededTargets), 'utf-8')\n\n logger.info(`\u2713 Created ${tildify(factoryPath)}`)\n\n if (seededTargets.length > 0) {\n logger.info(` Seeded ${seededTargets.length} target(s) from the source ${VENDOR_CONFIG_FILE}.`)\n }\n\n logger.info(` Edit \\`workspaceDir\\` (placeholder: ${PLACEHOLDER_WORKSPACE_DIR}) to point at where your repos live.`)\n\n if (seededTargets.length === 0) {\n logger.info(' Add at least one repo name to `targets` before running vendor sync/manifest/diff.')\n }\n}\n\n/**\n * Best-effort read of a legacy `targets` array from the source repo's\n * `vendor.config.ts`. The current schema no longer accepts `targets`, so this\n * reads the raw default export directly (bypassing validation). Returns `[]` on\n * any failure \u2014 seeding is a convenience, never a hard requirement.\n */\nconst readLegacyTargets = async (sourceRoot: string): Promise<string[]> => {\n try {\n const configPath = path.join(sourceRoot, VENDOR_CONFIG_FILE)\n const stat = await fs.stat(configPath)\n const moduleUrl = `${pathToFileURL(configPath).href}?mtime=${Number(stat.mtimeMs)}`\n const imported = (await import(moduleUrl)) as { default?: unknown }\n const raw = imported.default\n const resolved = typeof raw === 'function' ? await (raw as () => unknown)() : raw\n\n if (resolved && typeof resolved === 'object' && 'targets' in resolved) {\n const targets = (resolved as { targets?: unknown }).targets\n\n if (\n Array.isArray(targets) &&\n targets.every((t) => {\n return typeof t === 'string'\n })\n ) {\n return targets as string[]\n }\n }\n } catch {\n // Absent or unreadable source config \u2014 fall through to an empty placeholder.\n }\n\n return []\n}\n\n/**\n * Render the scaffold file body as strict JSON (`vendor.json`). The factory config\n * is static JSON, loaded with `JSON.parse` \u2014 no comments, no executable code. When\n * `targets` is empty the stub writes `\"targets\": []`, which fails the schema's\n * `targets.min(1)` on load: this is intentional \u2014 `--init` produces an incomplete\n * stub the user must edit before running vendor sync/manifest/diff. The annotated\n * guidance lives in the sibling `vendor.example.jsonc` (seeded by `infra-kit init`).\n */\nconst buildScaffold = (targets: string[]): string => {\n return `${JSON.stringify({ workspaceDir: PLACEHOLDER_WORKSPACE_DIR, targets }, null, 2)}\\n`\n}\n", "import type { Command } from 'commander'\nimport process from 'node:process'\n\n/**\n * `--json` output mode for the CLI. This is a presentation concern only: every\n * command handler already returns a `structuredContent` payload (the same one\n * the MCP surface consumes). Human/log output goes to stderr (see lib/logger),\n * so writing the structured payload to stdout never collides with it.\n */\n\nexport interface CommandResult {\n structuredContent?: unknown\n}\n\n/** Mutable holder (object so `prefer-const` holds while the flag toggles per run). */\nexport const jsonOutput = { enabled: false }\n\n/**\n * In `--json` mode, write a command result's `structuredContent` to stdout as\n * pretty JSON and return the result unchanged. Outside `--json` mode it is a\n * no-op pass-through. The writer is injectable for testing.\n */\nexport const emit = <T extends CommandResult | void>(\n result: T,\n write: (text: string) => void = (text) => {\n process.stdout.write(text)\n },\n): T => {\n if (jsonOutput.enabled && result && result.structuredContent != null) {\n write(`${JSON.stringify(result.structuredContent, null, 2)}\\n`)\n }\n\n return result\n}\n\n/** Register `--json` on a command and every nested subcommand (idempotent). */\nexport const addJsonOption = (cmd: Command): void => {\n const alreadyHasJson = cmd.options.some((option) => {\n return option.long === '--json'\n })\n\n if (!alreadyHasJson) {\n cmd.option('--json', 'Output the structured result as JSON on stdout (human logs stay on stderr)')\n }\n\n cmd.commands.forEach(addJsonOption)\n}\n", "/**\n * Small shared rendering helpers for aligned terminal output. These replace\n * the ad-hoc \"compute the max width, then padEnd each row\" pattern that the\n * interactive menu (and similar list views) hand-rolled. Pure string helpers \u2014\n * no I/O, no color \u2014 so they are trivially testable and reusable.\n */\n\n/**\n * Align a list of two-column rows: pad every left cell to the widest left cell,\n * then join the two cells with `gap`. Returns one rendered line per row.\n *\n * @example\n * formatAlignedRows([['a', 'x'], ['bbb', 'y']])\n * // ['a x', 'bbb y'] (left padded to width 3, default 2-space gap)\n */\nexport const formatAlignedRows = (rows: ReadonlyArray<readonly [string, string]>, gap = ' '): string[] => {\n const width = rows.reduce((max, [left]) => {\n return Math.max(max, left.length)\n }, 0)\n\n return rows.map(([left, right]) => {\n return `${left.padEnd(width)}${gap}${right}`\n })\n}\n"],
5
- "mappings": "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",
6
- "names": ["select", "Separator", "Command", "process", "fs", "path", "process", "$", "configPath", "paths", "getInfraKitConfigPaths", "rows", "row", "fileExists", "logger", "marker", "tildify", "structuredContent", "configEdit", "editor", "process", "fs", "path", "examplePath", "exampleSiblingPath", "buildUserProjectExample", "$", "resetInfraKitConfigCache", "jsonPath", "projectName", "fs", "path", "process", "WARN_MISCONFIG_SENTINEL_FILE", "WARN_FAIL_SENTINEL_FILE", "FAIL_SENTINEL_FILE", "FAIL_BACKOFF_MS", "resolveEnvAutoLoad", "canWarn", "config", "getInfraKitConfig", "autoLoad", "warnOnce", "decideAutoLoad", "input", "trigger", "expectedTrigger", "targetConfig", "targetProject", "env", "force", "runEnvAutoLoad", "projectDir", "resolved", "readAutoLoadEnvSnapshot", "isClearedOnDisk", "recentlyFailed", "preWriteMtime", "readLoadFileMtime", "result", "writeEnvLoadFile", "manualLoadLandedSince", "clearFailure", "error", "reason", "recordFailure", "logger", "process", "INFRA_KIT_SESSION_VAR", "INFRA_KIT_ENV_CLEARED_VAR", "INFRA_KIT_ENV_CONFIG_VAR", "INFRA_KIT_ENV_PROJECT_VAR", "INFRA_KIT_ENV_AUTOLOADED_VAR", "fs", "path", "getSessionCacheDir", "ENV_LOAD_FILE", "sinceMtime", "p", "mtime", "dir", "clearPath", "ENV_CLEAR_FILE", "loadPath", "flagPath", "message", "sentinelFile", "envAutoload", "projectDir", "runEnvAutoLoad", "fs", "path", "process", "pathToFileURL", "PLACEHOLDER_WORKSPACE_DIR", "vendorConfig", "options", "initFactoryConfig", "printFactoryConfig", "factoryPath", "getFactoryConfigPath", "exists", "fileExists", "logger", "tildify", "process", "workspaceDir", "targets", "loadFactoryConfig", "resolvedWorkspace", "expandTilde", "workspaceExists", "allReachable", "repo", "targetPath", "path", "reachable", "marker", "suffix", "cwd", "sourceRoot", "getProjectRoot", "seededTargets", "readLegacyTargets", "fs", "buildScaffold", "VENDOR_CONFIG_FILE", "configPath", "stat", "raw", "pathToFileURL", "resolved", "t", "process", "jsonOutput", "emit", "result", "write", "text", "addJsonOption", "cmd", "option", "formatAlignedRows", "rows", "gap", "width", "max", "left", "right", "program", "Command", "collectReleaseSpec", "value", "prev", "parseRepos", "normalizeIdeMode", "flagName", "IDE_MODES", "runProgram", "argv", "error", "isPromptCancellation", "logger", "process", "message", "invokedViaMenu", "deprecatedAlias", "cmd", "preferred", "configureMergeDev", "options", "emit", "ghMergeDev", "configureReleaseList", "ghReleaseList", "configureReleaseCreate", "inputs", "parseReleaseSpec", "releases", "releaseCreate", "configureReleaseDescEdit", "releaseDescEdit", "configureReleaseDeployAll", "ghReleaseDeployAll", "configureReleaseDeploySelected", "ghReleaseDeploySelected", "configureReleaseDeliver", "ghReleaseDeliver", "configureWorktreesSync", "worktreesSync", "configureWorktreesAdd", "ide", "worktreesAdd", "configureWorktreesList", "worktreesList", "configureWorktreesRemove", "worktreesRemove", "configureWorktreesReload", "worktreesReload", "configureVendorConfig", "vendorConfig", "releaseGroup", "worktreesGroup", "configCmd", "configPath", "configEdit", "result", "audit", "vendorCmd", "vendorCheck", "vendorSync", "vendorManifest", "vendorDiff", "doctor", "preset", "runDevServer", "toDevServerOptions", "version", "envStatus", "envList", "init", "envLoad", "envClear", "envAutoload", "addJsonOption", "isAutoLoadExcludedCommand", "name", "_thisCommand", "actionCommand", "jsonOutput", "runEnvAutoLoad", "releaseCommands", "getMenuGroupCommands", "worktreeCommands", "envCommands", "commandMap", "paletteItems", "label", "names", "selected", "runCommandPalette", "alignedLabels", "formatAlignedRows", "item", "labelByName", "index", "toChoices", "select", "Separator"]
4
+ "sourcesContent": ["import select, { Separator } from '@inquirer/select'\nimport { Command } from 'commander'\nimport process from 'node:process'\n\nimport { audit } from 'src/commands/audit'\nimport { configEdit, configPath } from 'src/commands/config'\nimport { doctor } from 'src/commands/doctor'\nimport { envAutoload } from 'src/commands/env-autoload'\nimport { envClear } from 'src/commands/env-clear'\nimport { envList } from 'src/commands/env-list'\nimport { envLoad } from 'src/commands/env-load'\nimport { envStatus } from 'src/commands/env-status'\nimport { ghMergeDev } from 'src/commands/gh-merge-dev'\nimport { ghReleaseDeliver } from 'src/commands/gh-release-deliver'\nimport { ghReleaseDeployAll } from 'src/commands/gh-release-deploy-all'\nimport { ghReleaseDeploySelected } from 'src/commands/gh-release-deploy-selected'\nimport { ghReleaseList } from 'src/commands/gh-release-list'\nimport { init } from 'src/commands/init'\nimport { releaseCreate } from 'src/commands/release-create'\nimport { releaseDescEdit } from 'src/commands/release-desc-edit'\nimport { vendorCheck } from 'src/commands/vendor-check'\nimport { vendorConfig } from 'src/commands/vendor-config'\nimport { vendorDiff } from 'src/commands/vendor-diff'\nimport { vendorManifest } from 'src/commands/vendor-manifest'\nimport { vendorSync } from 'src/commands/vendor-sync'\nimport { version } from 'src/commands/version'\nimport { worktreesAdd } from 'src/commands/worktrees-add'\nimport { worktreesList } from 'src/commands/worktrees-list'\nimport { worktreesReload } from 'src/commands/worktrees-reload'\nimport { worktreesRemove } from 'src/commands/worktrees-remove'\nimport { worktreesSync } from 'src/commands/worktrees-sync'\nimport { IDE_MODES } from 'src/integrations/ide'\nimport type { IdeMode } from 'src/integrations/ide'\nimport { getMenuGroupCommands } from 'src/lib/command-catalog'\nimport { runEnvAutoLoad } from 'src/lib/env-autoload'\nimport { isPromptCancellation } from 'src/lib/errors/is-prompt-cancellation'\nimport { addJsonOption, emit, jsonOutput } from 'src/lib/json-output'\nimport { logger } from 'src/lib/logger'\nimport { formatAlignedRows } from 'src/lib/render'\nimport { parseReleaseSpec } from 'src/lib/version-utils'\nimport type { ReleaseInput } from 'src/lib/version-utils'\n\nconst program = new Command()\n\nconst collectReleaseSpec = (value: string, prev: string[]): string[] => {\n return [...prev, value]\n}\n\n/** Parse a `--repos a,b,c` option into a target-name list (undefined = all). */\nconst parseRepos = (value: unknown): string[] | undefined => {\n return typeof value === 'string' ? value.split(',').filter(Boolean) : undefined\n}\n\nconst normalizeIdeMode = (value: unknown, flagName: '--ide' | '--cursor'): IdeMode | undefined => {\n if (typeof value === 'undefined') {\n return undefined\n }\n\n if (value === true) {\n return 'workspace'\n }\n\n if (value === false) {\n return 'none'\n }\n\n if (typeof value === 'string' && (IDE_MODES as readonly string[]).includes(value)) {\n return value as IdeMode\n }\n\n throw new Error(`Invalid ${flagName} value \"${String(value)}\". Expected one of: ${IDE_MODES.join(', ')}.`)\n}\n\nconst runProgram = async (argv?: string[]): Promise<void> => {\n try {\n if (argv) {\n await program.parseAsync(argv)\n } else {\n await program.parseAsync()\n }\n } catch (error) {\n // Ctrl-C / Esc out of any prompt is a deliberate back-out, not a failure:\n // exit quietly with success so it matches the explicit \"Operation cancelled.\"\n // decline path and never trips scripts/CI into treating a cancel as an error.\n if (isPromptCancellation(error)) {\n logger.info('Operation cancelled.')\n process.exit(0)\n }\n\n const message = error instanceof Error ? error.message : String(error)\n\n logger.error(message)\n process.exit(1)\n }\n}\n\n// --- Deprecation support for flat command aliases (Phase 3 grouping) ---\n// Flat names (`release-create`, `worktrees-add`, `vendor-config`, ...) are kept\n// as working aliases of the grouped forms (`release create`, ...) for one\n// release cycle. They warn once when invoked directly, but stay silent when the\n// interactive no-arg menu drives them (the menu is a guided surface).\nconst invokedViaMenu = { value: false }\n\nconst deprecatedAlias = (cmd: Command, preferred: string): Command => {\n return cmd.hook('preAction', () => {\n if (!invokedViaMenu.value) {\n logger.warn(`\"${cmd.name()}\" is a deprecated alias; use \"${preferred}\" instead.`)\n }\n })\n}\n\n// --- Command configurators (one source of options + action, shared by the\n// grouped form and its flat alias so the two can never diverge) ---\nconst configureMergeDev = (cmd: Command): Command => {\n return cmd\n .description('Merge dev branch into every release branch')\n .option('-a, --all', 'Select all active release branches')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(await ghMergeDev({ all: options.all, confirmedCommand: options.yes }))\n })\n}\n\nconst configureReleaseList = (cmd: Command): Command => {\n return cmd.description('List all release branches').action(async () => {\n emit(await ghReleaseList())\n })\n}\n\nconst configureReleaseCreate = (cmd: Command): Command => {\n return cmd\n .description('Create one or more release branches (each entry can mix regular/hotfix and its own description)')\n .option(\n '-r, --release <spec>',\n '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\".',\n collectReleaseSpec,\n [],\n )\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n const specs = options.release as string[]\n const inputs: ReleaseInput[] = specs.map(parseReleaseSpec)\n const releases = inputs.length > 0 ? inputs : undefined\n\n emit(\n await releaseCreate({\n releases,\n confirmedCommand: options.yes,\n }),\n )\n })\n}\n\nconst configureReleaseDescEdit = (cmd: Command): Command => {\n return cmd\n .description(\"Edit a release's description in Jira and in the matching GitHub PR body\")\n .option('-v, --version <version>', 'Release version (e.g. 1.2.5) or release name (e.g. checkout-redesign)')\n .option('-d, --description <description>', 'New description (use \"\" to clear)')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(\n await releaseDescEdit({\n version: options.version,\n description: options.description,\n confirmedCommand: options.yes,\n }),\n )\n })\n}\n\nconst configureReleaseDeployAll = (cmd: Command): Command => {\n return cmd\n .description('Deploy any release branch to any environment')\n .option(\n '-v, --version <version>',\n 'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deploy; \"dev\" deploys from the dev branch',\n )\n .option('-e, --env <env>', 'Specify the environment to deploy to, e.g. dev')\n .option('--skip-terraform', 'Skip terraform deployment step')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(\n await ghReleaseDeployAll({\n version: options.version,\n env: options.env,\n skipTerraform: options.skipTerraform,\n confirmedCommand: options.yes,\n }),\n )\n })\n}\n\nconst configureReleaseDeploySelected = (cmd: Command): Command => {\n return cmd\n .description('Deploy selected services from release branch to any environment')\n .option(\n '-v, --version <version>',\n 'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deploy; \"dev\" deploys from the dev branch',\n )\n .option('-e, --env <env>', 'Specify the environment to deploy to, e.g. dev')\n .option('-s, --services <services...>', 'Specify services to deploy, e.g. client-be client-fe')\n .option('--skip-terraform', 'Skip terraform deployment step')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(\n await ghReleaseDeploySelected({\n version: options.version,\n env: options.env,\n services: options.services,\n skipTerraform: options.skipTerraform,\n confirmedCommand: options.yes,\n }),\n )\n })\n}\n\nconst configureReleaseDeliver = (cmd: Command): Command => {\n return cmd\n .description('Release a new version to production')\n .option('-v, --version <version>', 'Version (e.g. 1.2.5) or release name (e.g. checkout-redesign) to deliver')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(await ghReleaseDeliver({ version: options.version, confirmedCommand: options.yes }))\n })\n}\n\nconst configureWorktreesSync = (cmd: Command): Command => {\n return cmd\n .description('Remove release worktrees whose PRs are no longer open')\n .option('-y, --yes', 'Skip confirmation prompt')\n .action(async (options) => {\n emit(await worktreesSync({ confirmedCommand: options.yes }))\n })\n}\n\nconst configureWorktreesAdd = (cmd: Command): Command => {\n return cmd\n .description('Add git worktrees for release branches')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('-a, --all', 'Select all active release branches')\n .option('-v, --versions <versions>', 'Specify versions by comma, e.g. 1.2.5, 1.2.6')\n .option('-i, --ide [mode]', 'Editor mode for created worktrees: workspace (default) | none')\n .option('--no-ide', 'Skip the editor (alias for --ide none)')\n .option('-c, --cursor [mode]', 'Deprecated alias for --ide')\n .option('--no-cursor', 'Deprecated alias for --no-ide')\n .option('-g, --github-desktop', 'Open created worktrees in GitHub Desktop')\n .option('--no-github-desktop', 'Skip GitHub Desktop prompt')\n .option('-m, --cmux', 'Open created worktrees in cmux (3-pane layout)')\n .option('--no-cmux', 'Skip cmux prompt')\n .action(async (options) => {\n // `--ide` wins over the deprecated `--cursor` alias when both are provided.\n const ide = normalizeIdeMode(options.ide, '--ide') ?? normalizeIdeMode(options.cursor, '--cursor')\n\n emit(\n await worktreesAdd({\n confirmedCommand: options.yes,\n all: options.all,\n versions: options.versions,\n ide,\n githubDesktop: options.githubDesktop,\n cmux: options.cmux,\n }),\n )\n })\n}\n\nconst configureWorktreesList = (cmd: Command): Command => {\n return cmd.description('List all git worktrees with detailed information').action(async () => {\n emit(await worktreesList())\n })\n}\n\nconst configureWorktreesRemove = (cmd: Command): Command => {\n return cmd\n .description('Remove git worktrees for release branches')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('-a, --all', 'Select all active release branches')\n .option('-v, --versions <versions>', 'Specify versions by comma, e.g. 1.2.5, 1.2.6')\n .action(async (options) => {\n emit(await worktreesRemove({ confirmedCommand: options.yes, all: options.all, versions: options.versions }))\n })\n}\n\nconst configureWorktreesReload = (cmd: Command): Command => {\n return cmd\n .description(\n 'Close all cmux/editor worktree windows, then reopen the current release worktrees (also cold-start restore)',\n )\n .action(async () => {\n emit(await worktreesReload())\n })\n}\n\nconst configureVendorConfig = (cmd: Command): Command => {\n return cmd\n .description('Show the machine-local factory config (~/.infra-kit/vendor.json) or scaffold it with --init')\n .option('--init', 'Scaffold ~/.infra-kit/vendor.json (skips if it already exists)')\n .action(async (options) => {\n emit(await vendorConfig({ init: options.init }))\n })\n}\n\n// --- Grouped command surface (preferred form) ---\nconst releaseGroup = program.command('release').description('Release management commands')\n\nconfigureMergeDev(releaseGroup.command('merge-dev'))\nconfigureReleaseList(releaseGroup.command('list'))\nconfigureReleaseCreate(releaseGroup.command('create'))\nconfigureReleaseDescEdit(releaseGroup.command('desc-edit'))\nconfigureReleaseDeployAll(releaseGroup.command('deploy-all'))\nconfigureReleaseDeploySelected(releaseGroup.command('deploy-selected'))\nconfigureReleaseDeliver(releaseGroup.command('deliver'))\n\nconst worktreesGroup = program.command('worktrees').description('Git worktree management commands')\n\nconfigureWorktreesAdd(worktreesGroup.command('add'))\nconfigureWorktreesList(worktreesGroup.command('list'))\nconfigureWorktreesRemove(worktreesGroup.command('remove'))\nconfigureWorktreesSync(worktreesGroup.command('sync'))\nconfigureWorktreesReload(worktreesGroup.command('reload'))\n\n// --- Deprecated flat aliases (kept one release cycle; warn when used directly) ---\ndeprecatedAlias(configureMergeDev(program.command('merge-dev')), 'release merge-dev')\ndeprecatedAlias(configureReleaseList(program.command('release-list')), 'release list')\ndeprecatedAlias(configureReleaseCreate(program.command('release-create')), 'release create')\ndeprecatedAlias(configureReleaseDescEdit(program.command('release-desc-edit')), 'release desc-edit')\ndeprecatedAlias(configureReleaseDeployAll(program.command('release-deploy-all')), 'release deploy-all')\ndeprecatedAlias(configureReleaseDeploySelected(program.command('release-deploy-selected')), 'release deploy-selected')\ndeprecatedAlias(configureReleaseDeliver(program.command('release-deliver')), 'release deliver')\ndeprecatedAlias(configureWorktreesAdd(program.command('worktrees-add')), 'worktrees add')\ndeprecatedAlias(configureWorktreesList(program.command('worktrees-list')), 'worktrees list')\ndeprecatedAlias(configureWorktreesRemove(program.command('worktrees-remove')), 'worktrees remove')\ndeprecatedAlias(configureWorktreesSync(program.command('worktrees-sync')), 'worktrees sync')\ndeprecatedAlias(configureWorktreesReload(program.command('worktrees-reload')), 'worktrees reload')\n\nconst configCmd = program.command('config').description('Manage infra-kit configuration files')\n\nconfigCmd\n .command('path')\n .description('Show the resolved config merge chain and file paths')\n .action(async () => {\n emit(await configPath())\n })\n\nconfigCmd\n .command('edit')\n .description('Open the user-scope per-project override file in $EDITOR')\n .action(async () => {\n emit(await configEdit())\n })\n\nprogram\n .command('audit')\n .description('Audit against infra-kit.config.ts rules (--all for every package, --root for the monorepo root)')\n .option('-a, --all', 'Audit every non-vendor workspace package')\n .option('-r, --root', 'Audit the monorepo root (turbo pipeline + root commands)')\n .action(async (options) => {\n const result = await audit({ all: options.all, root: options.root })\n\n emit(result)\n\n if (!result.structuredContent.allPassed) {\n process.exitCode = 1\n }\n })\n\nconst vendorCmd = program.command('vendor').description('Verify and sync the mirrored vendor/ tree')\n\nvendorCmd\n .command('check')\n .description('Verify vendor/ matches vendor/.sync-manifest.json (self-contained; for any consumer repo)')\n .action(async () => {\n const result = await vendorCheck()\n\n emit(result)\n\n if (!result.structuredContent.ok) {\n process.exitCode = 1\n }\n })\n\nvendorCmd\n .command('sync')\n .description('Copy vendored files from the source repo into each target and regenerate manifests')\n .option('-y, --yes', 'Skip confirmation prompt')\n .option('-r, --repos <repos>', 'Restrict to comma-separated target repo names')\n .action(async (options) => {\n emit(await vendorSync({ confirmedCommand: options.yes, repos: parseRepos(options.repos) }))\n })\n\nvendorCmd\n .command('manifest')\n .description('Regenerate each target vendor/.sync-manifest.json + README from current content (no copy)')\n .option('-r, --repos <repos>', 'Restrict to comma-separated target repo names')\n .action(async (options) => {\n emit(await vendorManifest({ confirmedCommand: true, repos: parseRepos(options.repos) }))\n })\n\nvendorCmd\n .command('diff')\n .description('Source-aware drift check (rsync dry-run) of each target vendored subtree vs the source')\n .option('-r, --repos <repos>', 'Restrict to comma-separated target repo names')\n .action(async (options) => {\n const result = await vendorDiff({ repos: parseRepos(options.repos) })\n\n emit(result)\n\n if (!result.structuredContent.ok) {\n process.exitCode = 1\n }\n })\n\n// Grouped form (preferred); the flat `vendor-config` below is a deprecated alias.\nconfigureVendorConfig(vendorCmd.command('config'))\n\ndeprecatedAlias(configureVendorConfig(program.command('vendor-config')), 'vendor config')\n\nprogram\n .command('doctor')\n .description('Check installation and authentication status of gh and doppler CLIs')\n .action(async () => {\n emit(await doctor())\n })\n\nprogram\n .command('dev')\n .description('Run local dev servers for a named devServersPresets preset (or all apps); api + ui')\n .argument('[preset]', 'Named preset from devServersPresets (omit to run every app)')\n .option('-w, --watch', 'Rebuild and restart on file save')\n .option('--app <names>', 'Further narrow to these app folder names (comma-separated)')\n .option(\n '--cmux',\n 'Run each app in its own cmux pane (one workspace, N panes; falls back to single terminal if cmux is unavailable)',\n )\n .option('--self', 'Run only the app of the current directory (infer from cwd; use inside apps/<app>/\u2026)')\n .option(\n '-V, --verbose',\n 'Print full boot narration (default: quiet; full detail always in .infra-kit/dev-server.log)',\n )\n .option('--routes', 'Print each app\u2019s registered METHOD /path routes at startup (default: off)')\n .option('--proxy-port <port>', 'Portless proxy listen port for <release>.<package>.localhost URLs (default: 4000)')\n .action(async (preset, options) => {\n // Lazy import so fastify/chokidar (and the whole dev stack, plus the wizard's inquirer/config\n // graph) never load on the eager cli graph \u2014 they land in a split chunk reached only for `dev`.\n const { runDevServerCli } = await import('src/entry/dev-server')\n\n // A bare `infra-kit dev` in a TTY launches the interactive wizard; any flag/preset/pipe/--json\n // runs directly. `runDevServerCli` owns that decision (see `shouldRunWizard`).\n const tty = Boolean(process.stdout.isTTY && process.stdin.isTTY)\n\n await runDevServerCli({ ...options, preset }, tty, jsonOutput.enabled)\n })\n\nprogram\n .command('version')\n .description('Print the installed infra-kit CLI version')\n .action(async () => {\n emit(await version())\n })\n\nprogram\n .command('env-status')\n .description('Show which env is loaded in this session (local introspection; no Doppler call)')\n .action(async () => {\n emit(await envStatus())\n })\n\nprogram\n .command('env-list')\n .description('List available Doppler configs for the detected project')\n .action(async () => {\n emit(await envList())\n })\n\nprogram\n .command('init')\n .description('Inject shell integration into .zshrc and sync repo agent-instruction files')\n .action(async () => {\n emit(await init())\n })\n\nprogram\n .command('env-load')\n .description('Load Doppler env vars for a config. Source the returned file path to apply.')\n .option('-c, --config <config>', 'Environment config name to load (e.g. dev, arthur)')\n .action(async (options) => {\n emit(await envLoad({ config: options.config }))\n })\n\nprogram\n .command('env-clear')\n .description('Clear loaded env vars. Source the returned file path to apply.')\n .option('--purge', \"Also delete this project's warm cache outright (durable disable)\")\n .action(async (options) => {\n emit(await envClear({ purge: Boolean(options.purge) }))\n })\n\n// Internal: driven by the init shell-startup integration (backgrounded). Writes\n// env-load.sh when envAutoLoad is configured + eligible; the precmd hook sources\n// it. Hidden + no stdout output so it never pollutes the shell or the menu.\nprogram\n .command('env-autoload', { hidden: true })\n .description('Internal: prime env for the shell-startup auto-load trigger')\n // The shell passes its already-canonicalized (`${dir:A}`) project dir so node can\n // key the warm cache identically; see writeEnvLoadFile / shouldWriteWarm.\n .option('--project-dir <dir>', 'Canonical project dir for the warm-cache key (shell-startup only)')\n .action(async (options) => {\n await envAutoload({ projectDir: options.projectDir })\n })\n\n// Register `--json` on every command, then resolve the flag before each action\n// runs. In JSON mode we lower the logger to `warn` so the human-oriented info\n// lines stop cluttering stderr while errors still surface; the structured\n// payload is written to stdout by `emit`. No handler logic is affected.\nprogram.commands.forEach(addJsonOption)\n\n// Commands excluded from the cli-invocation auto-load trigger: the env-* family\n// (avoids recursion \u2014 `env-autoload`/`env-load` would re-enter), plus the\n// host-inspecting / meta commands where priming Doppler env would be surprising\n// (`init` bootstraps the shell block, `doctor` inspects auth, `version` prints a\n// string, `dev` is a long-running server that manages its own env). `--help`/\n// `--version`/the bare-arg menu don't fire preAction at all.\nconst isAutoLoadExcludedCommand = (name: string): boolean => {\n return name.startsWith('env-') || name === 'init' || name === 'doctor' || name === 'version' || name === 'dev'\n}\n\nprogram.hook('preAction', async (_thisCommand, actionCommand) => {\n // `optsWithGlobals` (not `opts`) so `--json` is seen on grouped subcommands:\n // for `release list --json` Commander binds the post-subcommand flag to the\n // parent `release` group, so the leaf's own `opts()` would not carry it.\n jsonOutput.enabled = Boolean(actionCommand.optsWithGlobals().json)\n\n if (jsonOutput.enabled) {\n logger.level = 'warn'\n }\n\n // cli-invocation auto-load: primes the shell env for SUBSEQUENT commands. The\n // current command does NOT see these vars \u2014 a child process can't mutate its\n // parent shell; the precmd hook sources the written file on the next prompt.\n // runEnvAutoLoad self-gates on config trigger and swallows transient failures,\n // so this is a no-op unless configured for cli-invocation and never blocks.\n if (!isAutoLoadExcludedCommand(actionCommand.name())) {\n await runEnvAutoLoad({ expectedTrigger: 'cli-invocation' })\n }\n})\n\nif (process.argv.length <= 2) {\n // Menu groups derive from the single command catalog (no hand-maintained\n // name arrays). Membership and order live in src/lib/command-catalog.\n const releaseCommands = getMenuGroupCommands('release')\n const worktreeCommands = getMenuGroupCommands('worktrees')\n const envCommands = getMenuGroupCommands('environment')\n\n const commandMap = new Map(\n program.commands.map((cmd) => {\n return [cmd.name(), cmd]\n }),\n )\n\n const groups = [\n { label: 'Release Management', names: releaseCommands },\n { label: 'Worktrees', names: worktreeCommands },\n { label: 'Environment', names: envCommands },\n ]\n\n // Flat {name, description, group} list shared by both the Ink palette and the\n // Inquirer fallback; descriptions come from Commander (single source).\n const paletteItems = groups.flatMap(({ label, names }) => {\n return names\n .filter((name) => {\n return commandMap.has(name)\n })\n .map((name) => {\n return { name, description: commandMap.get(name)!.description(), group: label }\n })\n })\n\n let selected: string | null = null\n\n // Interactive TTY \u2192 Ink command palette, loaded lazily via dynamic import so\n // React/Ink never touch the MCP / `--json` / non-TTY code paths. Otherwise fall\n // back to the Inquirer menu (scripts, pipes, CI). Ctrl-C / Esc at the menu\n // throws from the Inquirer fallback; treat it as a clean exit (nothing picked).\n try {\n if (process.stdout.isTTY && process.stdin.isTTY) {\n const { runCommandPalette } = await import('src/tui/boot')\n\n selected = await runCommandPalette(paletteItems)\n } else {\n const alignedLabels = formatAlignedRows(\n paletteItems.map((item) => {\n return [item.name, item.description] as const\n }),\n )\n const labelByName = new Map<string, string>()\n\n paletteItems.forEach((item, index) => {\n labelByName.set(item.name, alignedLabels[index] ?? item.name)\n })\n\n const toChoices = (names: string[]) => {\n return names\n .filter((name) => {\n return commandMap.has(name)\n })\n .map((name) => {\n return {\n name: labelByName.get(name) ?? name,\n value: name,\n }\n })\n }\n\n selected = await select(\n {\n message: 'Select a command to run',\n choices: [\n new Separator(' '),\n new Separator('\u2014 Release Management \u2014'),\n ...toChoices(releaseCommands),\n new Separator(' '),\n new Separator('\u2014 Worktrees \u2014'),\n ...toChoices(worktreeCommands),\n new Separator(' '),\n new Separator('\u2014 Environment \u2014'),\n ...toChoices(envCommands),\n ],\n },\n { output: process.stderr },\n )\n }\n } catch (error) {\n // Ctrl-C / Esc at the menu is a clean back-out; leave `selected` as null.\n if (!isPromptCancellation(error)) throw error\n }\n\n // The menu is a guided surface; don't nag about deprecated flat names here.\n if (selected) {\n invokedViaMenu.value = true\n\n await runProgram(['node', 'infra-kit', selected])\n }\n} else {\n await runProgram()\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { $ } from 'zx'\n\nimport { getInfraKitConfigPaths, resetInfraKitConfigCache } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { fileExists, tildify } from 'src/lib/path-display'\nimport type { ToolsExecutionResult } from 'src/types'\n\n/**\n * Print the file paths that participate in the config merge chain along with\n * existence markers, so the user can see at a glance which override layers\n * are active.\n *\n * @example\n * // CLI: `infra-kit config path`\n * // INFO: Project name: api\n * // INFO: Config merge chain (later overrides earlier):\n * // INFO: [\u2713] project (committed) ~/projects/api/infra-kit.json\n * // INFO: [ ] user global ~/.infra-kit/infra-kit.json\n * // INFO: [\u2713] user project ~/.infra-kit/projects/api/infra-kit.json\n */\nexport const configPath = async (): Promise<ToolsExecutionResult> => {\n const paths = await getInfraKitConfigPaths()\n\n const rows: { label: string; path: string; exists: boolean }[] = await Promise.all(\n [\n { label: 'project (committed)', path: paths.main },\n { label: 'user global', path: paths.userGlobal },\n { label: 'user project', path: paths.userProject },\n ].map(async (row) => {\n return { ...row, exists: await fileExists(row.path) }\n }),\n )\n\n logger.info(`Project name: ${paths.projectName}\\n`)\n logger.info('Config merge chain (later overrides earlier):\\n')\n\n for (const row of rows) {\n const marker = row.exists ? ' [\u2713]' : ' [ ]'\n\n logger.info(`${marker} ${row.label.padEnd(22)} ${tildify(row.path)}`)\n }\n\n const structuredContent = {\n projectName: paths.projectName,\n layers: rows.map((r) => {\n return { label: r.label, path: r.path, exists: r.exists }\n }),\n }\n\n return {\n content: [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],\n structuredContent,\n }\n}\n\n/**\n * Open the user-scope per-project override file in $EDITOR, creating the\n * parent directory and a stub file on first use. Resets the config cache\n * after the editor exits so subsequent reads pick up edits without a restart.\n *\n * @example\n * // CLI: `infra-kit config edit`\n * // first run \u2014 creates ~/.infra-kit/projects/api/infra-kit.json ({}) + a sibling\n * // infra-kit.example.jsonc reference, then $EDITOR opens the .json\n * // subsequent runs \u2014 opens the existing file as-is\n */\nexport const configEdit = async (): Promise<ToolsExecutionResult> => {\n const paths = await getInfraKitConfigPaths()\n const editor = process.env.EDITOR || process.env.VISUAL || 'vi'\n\n await fs.mkdir(path.dirname(paths.userProject), { recursive: true })\n\n if (!(await fileExists(paths.userProject))) {\n // JSON can't carry comments, so seed an empty-but-valid config and drop the\n // annotated guidance next to it in a non-loaded .example.jsonc the loader\n // never reads (it only globs the three exact `infra-kit.json` filenames).\n const examplePath = exampleSiblingPath(paths.userProject)\n\n await fs.writeFile(paths.userProject, '{}\\n', 'utf-8')\n await fs.writeFile(examplePath, buildUserProjectExample(paths.projectName), 'utf-8')\n\n logger.info(`Created ${tildify(paths.userProject)} \u2014 see ${tildify(examplePath)} for the annotated reference.`)\n }\n\n logger.info(`Opening ${tildify(paths.userProject)} in ${editor}`)\n\n await $({ stdio: 'inherit' })`${editor} ${paths.userProject}`\n\n resetInfraKitConfigCache()\n\n const structuredContent = { path: paths.userProject, editor }\n\n return {\n content: [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],\n structuredContent,\n }\n}\n\n/**\n * Derive the non-loaded `.example.jsonc` sibling for a config path.\n *\n * @example\n * exampleSiblingPath('/u/.infra-kit/projects/api/infra-kit.json')\n * // => '/u/.infra-kit/projects/api/infra-kit.example.jsonc'\n */\nconst exampleSiblingPath = (jsonPath: string): string => {\n return jsonPath.replace(/\\.json$/, '.example.jsonc')\n}\n\n/**\n * Annotated JSONC reference for the user-scope per-project override layer.\n * Written alongside the real `{}` config so the guidance the old YAML stub\n * carried in comments survives the move to JSON.\n *\n * @example\n * buildUserProjectExample('api')\n * // => '// infra-kit user override for api \u2026\\n{ \u2026 }\\n'\n */\nconst buildUserProjectExample = (projectName: string): string => {\n return `// infra-kit user override for ${projectName} \u2014 ~/.infra-kit/projects/${projectName}/infra-kit.json\n//\n// Layer 3 (highest precedence) of the config merge chain. Shallow-merged on top\n// of <repo>/infra-kit.json and ~/.infra-kit/infra-kit.json \u2014 top-level keys\n// (environments, envManagement, ide, taskManager, worktrees, envAutoLoad) replace wholesale.\n//\n// This .example.jsonc is reference only \u2014 it is NOT loaded. Put real overrides\n// in the sibling infra-kit.json (strict JSON: no comments, double-quoted keys).\n{\n // \"worktrees\": { \"openInGithubDesktop\": false, \"openInCmux\": true, \"cmux\": { \"layout\": \"two-columns\" } },\n // // Auto-load Doppler env here. trigger (pick one): shell-startup | cli-invocation; config: an environment name.\n // \"envAutoLoad\": { \"trigger\": \"shell-startup\", \"config\": \"dev\" }\n}\n`\n}\n", "import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport { writeEnvLoadFile } from 'src/commands/env-load'\nimport {\n ENV_CLEAR_FILE,\n ENV_LOAD_FILE,\n INFRA_KIT_ENV_AUTOLOADED_VAR,\n INFRA_KIT_ENV_CLEARED_VAR,\n INFRA_KIT_ENV_CONFIG_VAR,\n INFRA_KIT_ENV_PROJECT_VAR,\n INFRA_KIT_SESSION_VAR,\n getSessionCacheDir,\n} from 'src/lib/constants'\nimport type { EnvAutoLoadConfig } from 'src/lib/infra-kit-config'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\n\n/** Which moment a concrete callsite represents. Matches the config `trigger`. */\nexport type AutoLoadTrigger = EnvAutoLoadConfig['trigger']\n\n/** Per-session flag de-duping the MISCONFIG warning (bad envAutoLoad.config). */\nconst WARN_MISCONFIG_SENTINEL_FILE = 'autoload-warn-misconfig.flag'\n/** Per-session flag de-duping the transient-FAILURE warning (Doppler down/unauth). */\nconst WARN_FAIL_SENTINEL_FILE = 'autoload-warn-fail.flag'\n\n/** Per-session marker recording the last auto-load failure (mtime = when). */\nconst FAIL_SENTINEL_FILE = 'autoload-fail.flag'\n\n/**\n * After a failed auto-load, suppress retries for this long so a down/unauthenticated\n * Doppler isn't re-probed on every cli-invocation. A new shell starts a fresh\n * session cache dir, so this only throttles within one session.\n */\nconst FAIL_BACKOFF_MS = 30_000\n\n/** Resolved auto-load inputs: the chosen trigger + the env config and Doppler project to load. */\nexport interface ResolvedEnvAutoLoad {\n trigger: AutoLoadTrigger\n config: string\n project: string\n}\n\n/**\n * Read the resolved + validated env auto-load inputs, or `null` when auto-load\n * should not run. Returns `null` (never throws) when:\n * - we're not inside an infra-kit project (getInfraKitConfig throws), or config is unreadable;\n * - `envAutoLoad` is absent (feature off);\n * - `envAutoLoad.config` is not one of `environments` \u2014 warns once per session, then disables.\n *\n * Validation lives here (not in the schema) so a typo only disables this optional\n * feature instead of throwing inside the merged-config parse and breaking every command.\n * Resolves the Doppler project from the SAME config read (no second getInfraKitConfig),\n * so the skip path stays cheap.\n *\n * `canWarn` gates the misconfig warning: the shell-startup callsite runs backgrounded\n * with stderr discarded, so warning there is invisible AND would write the dedup flag,\n * poisoning the only channel (cli-invocation / interactive) that can actually surface\n * it. So shell-startup passes `false`; interactive callsites pass `true`.\n */\nexport const resolveEnvAutoLoad = async (canWarn = true): Promise<ResolvedEnvAutoLoad | null> => {\n let config\n\n try {\n config = await getInfraKitConfig()\n } catch {\n return null\n }\n\n const autoLoad = config.envAutoLoad\n\n if (!autoLoad) return null\n\n if (!config.environments.includes(autoLoad.config)) {\n if (canWarn) {\n warnOnce(\n `infra-kit: envAutoLoad.config \"${autoLoad.config}\" is not one of environments [${config.environments.join(\n ', ',\n )}] \u2014 env auto-load disabled.`,\n WARN_MISCONFIG_SENTINEL_FILE,\n )\n }\n\n return null\n }\n\n return {\n trigger: autoLoad.trigger,\n config: autoLoad.config,\n project: config.envManagement.config.name,\n }\n}\n\n/** The env-var snapshot the freshness/suppression guards read. */\nexport interface AutoLoadEnvSnapshot {\n session?: string\n cleared?: string\n currentConfig?: string\n currentProject?: string\n autoLoadedMarker?: string\n}\n\nexport interface AutoLoadDecisionInput {\n /** The configured trigger. */\n trigger: AutoLoadTrigger\n /** Which trigger this callsite represents. */\n expectedTrigger: AutoLoadTrigger\n targetConfig: string\n targetProject: string\n env: AutoLoadEnvSnapshot\n /**\n * Bypass ONLY the \"already auto-loaded, same config+project\" no-op skip, forcing\n * a fresh Doppler fetch. The shell-startup refresh sets this: after a WARM source\n * the shell has already exported INFRA_KIT_ENV_AUTOLOADED + the same config, which\n * the child process inherits \u2014 without `force` the refresh would self-skip and the\n * warm (possibly rotated) secrets would never be replaced this session. Does NOT\n * relax the clear/manual-load guards.\n */\n force?: boolean\n}\n\nexport type AutoLoadDecision = 'load' | 'skip'\n\n/**\n * Pure decision: should this callsite (auto-)load env right now? Encodes the full\n * guard matrix so it is exhaustively unit-testable without Doppler or a shell:\n * - the configured trigger must match this callsite;\n * - a session must exist (the cache dir is session-scoped);\n * - an explicit clear suppresses auto-load (M2);\n * - a MANUAL load (config set, no auto marker) is never clobbered (C1);\n * - an auto-load already fresh for the same config AND project is a no-op\n * (project-aware so two same-named configs across different-repo worktrees\n * sharing one session don't leak each other's secrets).\n */\nexport const decideAutoLoad = (input: AutoLoadDecisionInput): AutoLoadDecision => {\n const { trigger, expectedTrigger, targetConfig, targetProject, env, force } = input\n\n if (trigger !== expectedTrigger) return 'skip'\n\n if (!env.session) return 'skip'\n\n if (env.cleared) return 'skip'\n\n // Manual load present (a config is loaded but it wasn't auto-loaded) \u2014 leave it.\n if (env.currentConfig && !env.autoLoadedMarker) return 'skip'\n\n // Our own auto-load already matches the target config+project \u2014 normally a no-op,\n // but `force` (the shell-startup warm refresh) must re-fetch: the shell just warm\n // -sourced these same markers, so skipping here would strand stale secrets.\n if (!force && env.autoLoadedMarker && env.currentConfig === targetConfig && env.currentProject === targetProject) {\n return 'skip'\n }\n\n return 'load'\n}\n\nexport interface RunEnvAutoLoadArgs {\n expectedTrigger: AutoLoadTrigger\n /**\n * Canonical (realpath'd) project dir, forwarded to `writeEnvLoadFile` to enable\n * the project-scoped WARM cache. Only the shell-startup spawn passes it (via\n * `--project-dir`); the cli-invocation trigger omits it, so warm is a\n * shell-startup-only optimization.\n */\n projectDir?: string\n /**\n * Force a fresh fetch past the \"already auto-loaded, same config\" no-op skip. The\n * shell-startup refresh sets this so a preceding WARM source (which exports the\n * same markers the child inherits) is always replaced by fresh secrets. See\n * {@link decideAutoLoad}. The cli-invocation trigger leaves it false.\n */\n force?: boolean\n}\n\n/**\n * Resolve config, evaluate the guards, and (if it should) produce env-load.sh with\n * the auto-load marker. Returns the written file path, or `null` when auto-load was\n * skipped or failed. NEVER throws. Transient failures (Doppler offline / not\n * authenticated / network) record a backoff marker so they aren't re-probed on every\n * command, and are surfaced once per session on the interactive (cli-invocation)\n * channel. No producer-side lock \u2014 a rare cold-shell double-fetch is tolerated (the\n * second write is atomic and idempotent).\n */\nexport const runEnvAutoLoad = async ({\n expectedTrigger,\n projectDir,\n force,\n}: RunEnvAutoLoadArgs): Promise<string | null> => {\n // Only the cli-invocation / interactive callsite reaches a TTY; the shell-startup\n // spawn discards stderr, so warning there is invisible and would poison the dedup.\n const canWarn = expectedTrigger === 'cli-invocation'\n\n try {\n const resolved = await resolveEnvAutoLoad(canWarn)\n\n if (!resolved) return null\n\n const decision = decideAutoLoad({\n trigger: resolved.trigger,\n expectedTrigger,\n targetConfig: resolved.config,\n targetProject: resolved.project,\n env: readAutoLoadEnvSnapshot(),\n force,\n })\n\n if (decision === 'skip') return null\n\n // Disk-level clear signal: a clear that hasn't yet been sourced into this\n // process's env still suppresses auto-load (clear file newer than load file).\n if (isClearedOnDisk()) return null\n\n // Back off after a recent failure so a down/unauthenticated Doppler isn't\n // re-probed on every command in the same session.\n if (recentlyFailed()) return null\n\n const preWriteMtime = readLoadFileMtime()\n const result = await writeEnvLoadFile({\n config: resolved.config,\n autoLoaded: true,\n projectDir,\n // Re-check after the slow Doppler download: abort if a clear or a manual load\n // landed meanwhile, so a backgrounded auto-load never clobbers a deliberate action.\n beforeWrite: () => {\n return !isClearedOnDisk() && !manualLoadLandedSince(preWriteMtime)\n },\n })\n\n if (!result) return null\n\n clearFailure()\n\n return result.filePath\n } catch (error) {\n const reason = (error as Error).message\n\n recordFailure()\n\n // Surface the failure once per session on the interactive channel; stay silent\n // (debug only) on the backgrounded shell-startup path.\n if (canWarn) {\n warnOnce(`infra-kit: env auto-load failed \u2014 ${reason} (will retry later)`, WARN_FAIL_SENTINEL_FILE)\n } else {\n logger.debug(`env auto-load skipped: ${reason}`)\n }\n\n return null\n }\n}\n\n/** Snapshot the env vars the guards depend on. */\nconst readAutoLoadEnvSnapshot = (): AutoLoadEnvSnapshot => {\n return {\n session: process.env[INFRA_KIT_SESSION_VAR],\n cleared: process.env[INFRA_KIT_ENV_CLEARED_VAR],\n currentConfig: process.env[INFRA_KIT_ENV_CONFIG_VAR],\n currentProject: process.env[INFRA_KIT_ENV_PROJECT_VAR],\n autoLoadedMarker: process.env[INFRA_KIT_ENV_AUTOLOADED_VAR],\n }\n}\n\n/** mtime of the on-disk env-load.sh, or null if absent/unreadable. */\nconst readLoadFileMtime = (): number | null => {\n try {\n return fs.statSync(path.join(getSessionCacheDir(), ENV_LOAD_FILE)).mtimeMs\n } catch {\n return null\n }\n}\n\n/**\n * True when a MANUAL env-load landed on disk after `sinceMtime` (the file now\n * exists, is newer, and carries the manual-load `unset INFRA_KIT_ENV_AUTOLOADED`\n * line). Used to abort an in-flight auto-load so it never clobbers a deliberate load.\n */\nconst manualLoadLandedSince = (sinceMtime: number | null): boolean => {\n try {\n const p = path.join(getSessionCacheDir(), ENV_LOAD_FILE)\n\n if (!fs.existsSync(p)) return false\n\n const mtime = fs.statSync(p).mtimeMs\n\n if (sinceMtime !== null && mtime <= sinceMtime) return false\n\n // Anchor to a whole line so the marker can't match inside a single-quoted\n // secret value that happens to contain this literal text.\n const manualMarker = new RegExp(`^unset ${INFRA_KIT_ENV_AUTOLOADED_VAR}$`, 'm')\n\n return manualMarker.test(fs.readFileSync(p, 'utf-8'))\n } catch {\n return false\n }\n}\n\n/**\n * True when a clear is pending on disk: an env-clear.sh exists and is at least as\n * new as env-load.sh (or no load file remains). Belt-and-suspenders next to the\n * INFRA_KIT_ENV_CLEARED env guard for the window before the shell sources the clear.\n */\nconst isClearedOnDisk = (): boolean => {\n try {\n const dir = getSessionCacheDir()\n const clearPath = path.join(dir, ENV_CLEAR_FILE)\n\n if (!fs.existsSync(clearPath)) return false\n\n const loadPath = path.join(dir, ENV_LOAD_FILE)\n\n if (!fs.existsSync(loadPath)) return true\n\n return fs.statSync(clearPath).mtimeMs >= fs.statSync(loadPath).mtimeMs\n } catch {\n return false\n }\n}\n\n/** True when an auto-load failed within the backoff window (suppress retries). */\nconst recentlyFailed = (): boolean => {\n try {\n const flagPath = path.join(getSessionCacheDir(), FAIL_SENTINEL_FILE)\n\n if (!fs.existsSync(flagPath)) return false\n\n return Date.now() - fs.statSync(flagPath).mtimeMs < FAIL_BACKOFF_MS\n } catch {\n return false\n }\n}\n\n/** Record an auto-load failure (refreshes the backoff window). */\nconst recordFailure = (): void => {\n try {\n const dir = getSessionCacheDir()\n\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n fs.writeFileSync(path.join(dir, FAIL_SENTINEL_FILE), '', { mode: 0o600 })\n } catch {\n // No session cache dir \u2014 nothing to back off against.\n }\n}\n\n/** Clear the failure marker after a successful load. */\nconst clearFailure = (): void => {\n try {\n fs.rmSync(path.join(getSessionCacheDir(), FAIL_SENTINEL_FILE), { force: true })\n } catch {\n // No session cache dir \u2014 nothing to clear.\n }\n}\n\n/**\n * Emit a warning at most once per shell session. Keyed on a flag file in the\n * session cache dir so a misconfigured `envAutoLoad.config` (or a repeated failure)\n * does not spam a warning on every cli-invocation. Falls back to a plain warn when\n * no session cache dir is available.\n */\nconst warnOnce = (message: string, sentinelFile: string): void => {\n try {\n const dir = getSessionCacheDir()\n const flagPath = path.join(dir, sentinelFile)\n\n if (fs.existsSync(flagPath)) return\n\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n fs.writeFileSync(flagPath, '', { mode: 0o600 })\n } catch {\n // No session cache dir (e.g. INFRA_KIT_SESSION unset) \u2014 warn without de-dup.\n }\n\n logger.warn(message)\n}\n", "import { runEnvAutoLoad } from 'src/lib/env-autoload'\n\nexport interface EnvAutoloadArgs {\n /**\n * Canonical (realpath'd) project dir the shell computed (`${dir:A}`) and passed\n * via `--project-dir`. Forwarded to enable the project-scoped warm cache; absent\n * when invoked without the flag (then no warm copy is written).\n */\n projectDir?: string\n}\n\n/**\n * Internal command invoked (backgrounded) by the `infra-kit init` shell-startup\n * integration. Runs the 'shell-startup' trigger, writing env-load.sh when\n * envAutoLoad is configured for it + eligible; the shell precmd hook sources it on\n * a subsequent prompt. Intentionally writes NOTHING to stdout and never throws \u2014\n * auto-load must never disrupt shell startup.\n */\nexport const envAutoload = async ({ projectDir }: EnvAutoloadArgs = {}): Promise<void> => {\n // `force`: the shell may have just WARM-sourced the same config (exporting the\n // auto-load marker this process inherits); without it the refresh would self-skip\n // and stale warm secrets would never be replaced this session.\n await runEnvAutoLoad({ expectedTrigger: 'shell-startup', projectDir, force: true })\n}\n", "import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { pathToFileURL } from 'node:url'\n\nimport { getProjectRoot } from 'src/lib/git-utils'\nimport { logger } from 'src/lib/logger'\nimport { fileExists, tildify } from 'src/lib/path-display'\nimport { VENDOR_CONFIG_FILE } from 'src/lib/vendor/config-schema'\nimport { expandTilde, getFactoryConfigPath, loadFactoryConfig } from 'src/lib/vendor/factory-config'\n\ninterface VendorConfigOptions {\n /** Scaffold `~/.infra-kit/vendor.json` instead of printing the current one. */\n init?: boolean\n /** Source repo root used for legacy `targets` auto-seeding. Defaults to the git toplevel. */\n cwd?: string\n}\n\n/** Placeholder workspace dir written by `--init`; the user edits it to their layout. */\nconst PLACEHOLDER_WORKSPACE_DIR = '~/projects'\n\n/**\n * Surface or scaffold the machine-local factory config\n * (`~/.infra-kit/vendor.json`). CLI-only \u2014 NOT an MCP tool; returns nothing\n * and signals problems via `process.exitCode`.\n *\n * Without `--init`: prints the factory file path + existence, the resolved\n * `workspaceDir` + existence, and per-target reachability (`[\u2713]`/`[ ]`). Exits\n * non-zero if the file is missing, the workspace dir is missing, or any target\n * is unreachable, so it is usable as a doctor check.\n *\n * With `--init`: scaffolds the file (skipping if it already exists), seeding\n * `targets` from a legacy source `vendor.config.ts` when one is readable.\n */\nexport const vendorConfig = async (options: VendorConfigOptions = {}): Promise<void> => {\n if (options.init) {\n await initFactoryConfig(options.cwd)\n\n return\n }\n\n await printFactoryConfig()\n}\n\n/** Render the factory config chain with `[\u2713]`/`[ ]` reachability markers. */\nconst printFactoryConfig = async (): Promise<void> => {\n const factoryPath = getFactoryConfigPath()\n const exists = await fileExists(factoryPath)\n\n logger.info(`Factory config: ${tildify(factoryPath)} ${exists ? '[\u2713]' : '[ ]'}`)\n\n if (!exists) {\n logger.info('\\nNot found \u2014 run `infra-kit vendor-config --init` to scaffold it.')\n process.exitCode = 1\n\n return\n }\n\n const { workspaceDir, targets } = await loadFactoryConfig()\n const resolvedWorkspace = expandTilde(workspaceDir)\n const workspaceExists = await fileExists(resolvedWorkspace)\n\n logger.info(\n `workspaceDir: ${workspaceDir} (resolved: ${resolvedWorkspace}) ${workspaceExists ? '[\u2713 exists]' : '[ ] not found'}`,\n )\n logger.info('Targets:')\n\n let allReachable = workspaceExists\n\n for (const repo of targets) {\n const targetPath = path.join(resolvedWorkspace, repo)\n const reachable = await fileExists(targetPath)\n\n if (!reachable) {\n allReachable = false\n }\n\n const marker = reachable ? '[\u2713]' : '[ ]'\n const suffix = reachable ? '' : ' (not found \u2014 clone or remove)'\n\n logger.info(` ${marker} ${repo} ${tildify(targetPath)}${suffix}`)\n }\n\n if (!allReachable) {\n process.exitCode = 1\n }\n}\n\n/** Scaffold `~/.infra-kit/vendor.json`, skipping if it already exists. */\nconst initFactoryConfig = async (cwd?: string): Promise<void> => {\n const factoryPath = getFactoryConfigPath()\n\n if (await fileExists(factoryPath)) {\n logger.info(`Factory config already exists at ${tildify(factoryPath)} \u2014 leaving it untouched.`)\n\n return\n }\n\n const sourceRoot = cwd ?? (await getProjectRoot())\n const seededTargets = await readLegacyTargets(sourceRoot)\n\n await fs.mkdir(path.dirname(factoryPath), { recursive: true })\n await fs.writeFile(factoryPath, buildScaffold(seededTargets), 'utf-8')\n\n logger.info(`\u2713 Created ${tildify(factoryPath)}`)\n\n if (seededTargets.length > 0) {\n logger.info(` Seeded ${seededTargets.length} target(s) from the source ${VENDOR_CONFIG_FILE}.`)\n }\n\n logger.info(` Edit \\`workspaceDir\\` (placeholder: ${PLACEHOLDER_WORKSPACE_DIR}) to point at where your repos live.`)\n\n if (seededTargets.length === 0) {\n logger.info(' Add at least one repo name to `targets` before running vendor sync/manifest/diff.')\n }\n}\n\n/**\n * Best-effort read of a legacy `targets` array from the source repo's\n * `vendor.config.ts`. The current schema no longer accepts `targets`, so this\n * reads the raw default export directly (bypassing validation). Returns `[]` on\n * any failure \u2014 seeding is a convenience, never a hard requirement.\n */\nconst readLegacyTargets = async (sourceRoot: string): Promise<string[]> => {\n try {\n const configPath = path.join(sourceRoot, VENDOR_CONFIG_FILE)\n const stat = await fs.stat(configPath)\n const moduleUrl = `${pathToFileURL(configPath).href}?mtime=${Number(stat.mtimeMs)}`\n const imported = (await import(moduleUrl)) as { default?: unknown }\n const raw = imported.default\n const resolved = typeof raw === 'function' ? await (raw as () => unknown)() : raw\n\n if (resolved && typeof resolved === 'object' && 'targets' in resolved) {\n const targets = (resolved as { targets?: unknown }).targets\n\n if (\n Array.isArray(targets) &&\n targets.every((t) => {\n return typeof t === 'string'\n })\n ) {\n return targets as string[]\n }\n }\n } catch {\n // Absent or unreadable source config \u2014 fall through to an empty placeholder.\n }\n\n return []\n}\n\n/**\n * Render the scaffold file body as strict JSON (`vendor.json`). The factory config\n * is static JSON, loaded with `JSON.parse` \u2014 no comments, no executable code. When\n * `targets` is empty the stub writes `\"targets\": []`, which fails the schema's\n * `targets.min(1)` on load: this is intentional \u2014 `--init` produces an incomplete\n * stub the user must edit before running vendor sync/manifest/diff. The annotated\n * guidance lives in the sibling `vendor.example.jsonc` (seeded by `infra-kit init`).\n */\nconst buildScaffold = (targets: string[]): string => {\n return `${JSON.stringify({ workspaceDir: PLACEHOLDER_WORKSPACE_DIR, targets }, null, 2)}\\n`\n}\n", "import type { Command } from 'commander'\nimport process from 'node:process'\n\n/**\n * `--json` output mode for the CLI. This is a presentation concern only: every\n * command handler already returns a `structuredContent` payload (the same one\n * the MCP surface consumes). Human/log output goes to stderr (see lib/logger),\n * so writing the structured payload to stdout never collides with it.\n */\n\nexport interface CommandResult {\n structuredContent?: unknown\n}\n\n/** Mutable holder (object so `prefer-const` holds while the flag toggles per run). */\nexport const jsonOutput = { enabled: false }\n\n/**\n * In `--json` mode, write a command result's `structuredContent` to stdout as\n * pretty JSON and return the result unchanged. Outside `--json` mode it is a\n * no-op pass-through. The writer is injectable for testing.\n */\nexport const emit = <T extends CommandResult | void>(\n result: T,\n write: (text: string) => void = (text) => {\n process.stdout.write(text)\n },\n): T => {\n if (jsonOutput.enabled && result && result.structuredContent != null) {\n write(`${JSON.stringify(result.structuredContent, null, 2)}\\n`)\n }\n\n return result\n}\n\n/** Register `--json` on a command and every nested subcommand (idempotent). */\nexport const addJsonOption = (cmd: Command): void => {\n const alreadyHasJson = cmd.options.some((option) => {\n return option.long === '--json'\n })\n\n if (!alreadyHasJson) {\n cmd.option('--json', 'Output the structured result as JSON on stdout (human logs stay on stderr)')\n }\n\n cmd.commands.forEach(addJsonOption)\n}\n", "/**\n * Small shared rendering helpers for aligned terminal output. These replace\n * the ad-hoc \"compute the max width, then padEnd each row\" pattern that the\n * interactive menu (and similar list views) hand-rolled. Pure string helpers \u2014\n * no I/O, no color \u2014 so they are trivially testable and reusable.\n */\n\n/**\n * Align a list of two-column rows: pad every left cell to the widest left cell,\n * then join the two cells with `gap`. Returns one rendered line per row.\n *\n * @example\n * formatAlignedRows([['a', 'x'], ['bbb', 'y']])\n * // ['a x', 'bbb y'] (left padded to width 3, default 2-space gap)\n */\nexport const formatAlignedRows = (rows: ReadonlyArray<readonly [string, string]>, gap = ' '): string[] => {\n const width = rows.reduce((max, [left]) => {\n return Math.max(max, left.length)\n }, 0)\n\n return rows.map(([left, right]) => {\n return `${left.padEnd(width)}${gap}${right}`\n })\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["select", "Separator", "Command", "process", "fs", "path", "process", "$", "configPath", "paths", "getInfraKitConfigPaths", "rows", "row", "fileExists", "logger", "marker", "tildify", "structuredContent", "configEdit", "editor", "process", "fs", "path", "examplePath", "exampleSiblingPath", "buildUserProjectExample", "$", "resetInfraKitConfigCache", "jsonPath", "projectName", "fs", "path", "process", "WARN_MISCONFIG_SENTINEL_FILE", "WARN_FAIL_SENTINEL_FILE", "FAIL_SENTINEL_FILE", "FAIL_BACKOFF_MS", "resolveEnvAutoLoad", "canWarn", "config", "getInfraKitConfig", "autoLoad", "warnOnce", "decideAutoLoad", "input", "trigger", "expectedTrigger", "targetConfig", "targetProject", "env", "force", "runEnvAutoLoad", "projectDir", "resolved", "readAutoLoadEnvSnapshot", "isClearedOnDisk", "recentlyFailed", "preWriteMtime", "readLoadFileMtime", "result", "writeEnvLoadFile", "manualLoadLandedSince", "clearFailure", "error", "reason", "recordFailure", "logger", "process", "INFRA_KIT_SESSION_VAR", "INFRA_KIT_ENV_CLEARED_VAR", "INFRA_KIT_ENV_CONFIG_VAR", "INFRA_KIT_ENV_PROJECT_VAR", "INFRA_KIT_ENV_AUTOLOADED_VAR", "fs", "path", "getSessionCacheDir", "ENV_LOAD_FILE", "sinceMtime", "p", "mtime", "dir", "clearPath", "ENV_CLEAR_FILE", "loadPath", "flagPath", "message", "sentinelFile", "envAutoload", "projectDir", "runEnvAutoLoad", "fs", "path", "process", "pathToFileURL", "PLACEHOLDER_WORKSPACE_DIR", "vendorConfig", "options", "initFactoryConfig", "printFactoryConfig", "factoryPath", "getFactoryConfigPath", "exists", "fileExists", "logger", "tildify", "process", "workspaceDir", "targets", "loadFactoryConfig", "resolvedWorkspace", "expandTilde", "workspaceExists", "allReachable", "repo", "targetPath", "path", "reachable", "marker", "suffix", "cwd", "sourceRoot", "getProjectRoot", "seededTargets", "readLegacyTargets", "fs", "buildScaffold", "VENDOR_CONFIG_FILE", "configPath", "stat", "raw", "pathToFileURL", "resolved", "t", "process", "jsonOutput", "emit", "result", "write", "text", "addJsonOption", "cmd", "option", "formatAlignedRows", "rows", "gap", "width", "max", "left", "right", "program", "Command", "collectReleaseSpec", "value", "prev", "parseRepos", "normalizeIdeMode", "flagName", "IDE_MODES", "runProgram", "argv", "error", "isPromptCancellation", "logger", "process", "message", "invokedViaMenu", "deprecatedAlias", "cmd", "preferred", "configureMergeDev", "options", "emit", "ghMergeDev", "configureReleaseList", "ghReleaseList", "configureReleaseCreate", "inputs", "parseReleaseSpec", "releases", "releaseCreate", "configureReleaseDescEdit", "releaseDescEdit", "configureReleaseDeployAll", "ghReleaseDeployAll", "configureReleaseDeploySelected", "ghReleaseDeploySelected", "configureReleaseDeliver", "ghReleaseDeliver", "configureWorktreesSync", "worktreesSync", "configureWorktreesAdd", "ide", "worktreesAdd", "configureWorktreesList", "worktreesList", "configureWorktreesRemove", "worktreesRemove", "configureWorktreesReload", "worktreesReload", "configureVendorConfig", "vendorConfig", "releaseGroup", "worktreesGroup", "configCmd", "configPath", "configEdit", "result", "audit", "vendorCmd", "vendorCheck", "vendorSync", "vendorManifest", "vendorDiff", "doctor", "preset", "runDevServerCli", "tty", "jsonOutput", "version", "envStatus", "envList", "init", "envLoad", "envClear", "envAutoload", "addJsonOption", "isAutoLoadExcludedCommand", "name", "_thisCommand", "actionCommand", "runEnvAutoLoad", "releaseCommands", "getMenuGroupCommands", "worktreeCommands", "envCommands", "commandMap", "paletteItems", "label", "names", "selected", "runCommandPalette", "alignedLabels", "formatAlignedRows", "item", "labelByName", "index", "toChoices", "select", "Separator"]
7
7
  }
@@ -1,18 +1,13 @@
1
- import{A as V,B as $,C as K,D as J,E as q,L as O,Q as z,R as Q,S as Y,v as P,w as C,x as S,y as G,z as R}from"./chunk-6RRAK2QO.js";import{a as L}from"./chunk-2PZRQHWF.js";import{Command as Ze}from"commander";import w from"node:process";import{pathToFileURL as er}from"node:url";import*as Z from"node:path";import T from"node:process";var ve=t=>({pane:{surfaces:[{type:"terminal",command:t}]}}),U=(t,e)=>{if(t.length===1)return ve(t[0]);let r=Math.ceil(t.length/2),n=t.slice(0,r),o=t.slice(r);return{direction:e%2===0?"horizontal":"vertical",split:Math.round(r/t.length*100)/100,children:[U(n,e+1),U(o,e+1)]}},X=t=>{if(t.length===0)throw new Error("buildCmuxLayout: at least one command is required");return U(t,0)};var ye=(t,e)=>t.map(r=>`pnpm exec infra-kit dev --app=${r}${e?" --watch":""}`),we=(t,e)=>{let r=S(t);return e?r.filter(n=>e.includes(n.name)):r},be=(t,e)=>{P.info(`\u{1F9E9} Opened cmux dev workspace ${e} with ${t.length} pane(s):`);for(let r of t)P.info(` \u2022 ${r.name} (infra-kit dev --app=${r.name})`)},Pe=t=>{let e=!1,r=n=>{e||(e=!0,(async()=>{try{P.info(`
2
- Received ${n}, closing cmux dev workspace ${t}...`),await Y(t)}finally{T.exit(0)}})())};T.on("SIGINT",()=>r("SIGINT")),T.on("SIGTERM",()=>r("SIGTERM"))},ee=async t=>{let e=C(T.cwd()),r=we(e,R(t.include));if(r.length===0){P.warn("No API apps found to run");return}let n=ye(r.map(g=>g.name),t.watch??!1),o=X(n),i=`${Z.basename(e)} dev`,s=await Q({cwd:e,title:i,layout:o});be(r,s),Pe(s);let p=setInterval(()=>{p.refresh()},2**30);await new Promise(()=>{})};import Be from"chokidar";import{exec as He,execFileSync as Ge}from"node:child_process";import*as v from"node:fs";import*as y from"node:path";import f from"node:process";import Ve from"node:util";import{execFile as De}from"node:child_process";import*as re from"node:fs";import*as E from"node:path";import{promisify as Ae}from"node:util";var xe=Ae(De),Ce=t=>{try{let e=JSON.parse(re.readFileSync(E.join(t,"package.json"),"utf-8"));return typeof e.name=="string"?e.name:void 0}catch{return}},Se=t=>async e=>{let{stdout:r}=await xe("pnpm",["exec","turbo","run","build","--dry=json",`--filter=...${e}`],{cwd:t,maxBuffer:33554432}),o=(JSON.parse(r).tasks??[]).map(i=>i.package).filter(i=>typeof i=="string");return[...new Set(o)]},Re=t=>{let e=new Map,r=new Map;for(let n of $(t)){let o=Ce(E.dirname(n));o!==void 0&&(e.set(n,o),r.set(o,n))}return{packageNameByDir:e,dirByName:r}},te=async(t,e,r=Se(t))=>{let{packageNameByDir:n,dirByName:o}=Re(t),i=new Map,s=await Promise.all(e.map(async p=>({app:p,closure:await r(p.packageName)})));for(let{app:p,closure:g}of s)for(let l of g){let d=o.get(l);if(d===void 0)continue;let u=i.get(d)??new Set;u.add(p.name),i.set(d,u)}return{dependentsByPackageDir:i,packageNameByDir:n}},ne=t=>`__pkg__:${t}`,oe=(t,e,r)=>{if(e===null||r===void 0)return null;let n=e.dependentsByPackageDir.get(r)??new Set;return t.filter(o=>o.watchDeps&&n.has(o.name))};var $e="/api/v1";function ie(t){if(t==null||t==="")return;let e=parseInt(t.trim().replace(/^["']|["']$/g,""),10);return Number.isNaN(e)?void 0:e}function se(t,e,r){let o=`${t.replace(/-/g,"_").toUpperCase()}_PORT`,i=ie(e[o]);if(i!=null)return i;let s=ie(e.PORT);return s??r[t]?.port??void 0}function ae(t,e){return e[t]?.prefixUrl??$e}function pe(t){let e=t.map(o=>o.port),r=e.filter((o,i)=>e.indexOf(o)!==i),n=t.filter(o=>r.includes(o.port));return{duplicatePorts:r,conflictingApps:n}}import{Logger as Te}from"@aws-lambda-powertools/logger";import Ee from"fastify";import*as le from"node:fs";import*as D from"node:path";import _ from"node:process";import{pathToFileURL as Ie}from"node:url";import{parse as ke}from"yaml";var Ne=t=>t?.code==="EADDRINUSE",Oe=3e4,Le=()=>{let t=Number.parseInt(_.env.DEV_SERVER_TIMEOUT_MS??"",10);return Number.isNaN(t)?Oe:t},Ue=()=>_.env.DEV_SERVER_REQUEST_LOG==="1",I=class{constructor(e){this.serverConfig=e;this.importCacheBust=`${Date.now()}`,this.logger=new Te({serviceName:"LocalServer",logLevel:"DEBUG"}),this.serverConfig.prefixUrl=this.serverConfig.prefixUrl??"",this.server=Ee({logger:!1}),this.server.addHook("onRequest",async(r,n)=>{n.header("Access-Control-Allow-Origin","*"),n.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),n.header("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),r.method==="OPTIONS"&&n.status(204).send()}),Ue()&&this.server.addHook("onResponse",async(r,n)=>{let o=Math.round(n.elapsedTime);_.stdout.write(`${r.method} ${r.url} \u2192 ${n.statusCode} ${o}ms
3
- `)})}serverConfig;importCacheBust;logger;server;controllers={};registeredRouteKeys=new Set;getRegisteredRoutes(){return[...this.registeredRouteKeys].sort()}async start(){this.registerHealthRoute(),await Promise.all(this.loadRoutes());let e=await this.listenWithFallback();return this.serverConfig.port=e,this.logger.info(`Server listening on http://127.0.0.1:${e}`,{address:`http://127.0.0.1:${e}`}),e}async listenWithFallback(){let e=this.serverConfig.port;if(e!=null)try{return await this.server.listen({port:e,host:"127.0.0.1"}),this.readBoundPort()}catch(r){if(!Ne(r))throw r}return await this.server.listen({port:0,host:"127.0.0.1"}),this.readBoundPort()}readBoundPort(){let e=this.server.server.address();if(e==null||typeof e=="string")throw new Error("Server address unavailable after listen()");return e.port}async close(){let e=this.server.server;typeof e.closeAllConnections=="function"&&e.closeAllConnections(),await this.server.close()}registerHealthRoute(){this.server.route({method:"GET",url:"/__health",handler:(e,r)=>r.code(200).send({status:"ok",app:this.serverConfig.appName??null,port:this.serverConfig.port})})}loadRoutes(){let e=D.join(this.serverConfig.controllersPath,"serverless.yml"),r=le.readFileSync(e,"utf8"),n=ke(r),o=[];if(!n?.functions)return o;for(let[i,s]of Object.entries(n.functions))if(s?.events?.length)for(let p of s.events){let g=p?.http;g&&(this.logger.debug(`Registering route: ${g.method} /${g.path} -> ${i}`),o.push(this.defineRoute(g,s)))}return o}async defineRoute(e,r){let n=e.path.toString();n=n.replaceAll("{",":").replaceAll("}","");let o=D.posix.join(this.serverConfig.prefixUrl??"",n);o=o[0]==="/"?o:`/${o}`;let i=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"],s=String(e.method).toUpperCase();if(!i.includes(s))throw new Error(`Invalid HTTP method: "${e.method}" for URL: ${o}`);let p=`${s} ${o}`;if(this.registeredRouteKeys.has(p))throw new Error(`Duplicate route: ${p}`);this.registeredRouteKeys.add(p);let l=(r.handler??"").split("."),d=l[0]??"",u=l[1]??"",h=D.join(this.serverConfig.controllersPath,`${d}.js`),a=Ie(h);a.searchParams.set("v",this.importCacheBust);let fe=await import(a.href);this.controllers[p]={action:fe,handler:u};let ge=this.logger.createChild({serviceName:"RequestLogger"});this.logger.debug(`Adding fastify route: ${s} ${o}`),this.server.route({method:s,url:o,handler:async(A,B)=>{let x=this.controllers[p];if(!x)throw new Error(`No controller for ${p}`);let H=x.action[x.handler];if(!H)throw new Error(`No handler ${x.handler} for ${o}`);let N=await H(this.getEventObj(A.body,A.query,A.params,A.headers,s,o),this.getContext(),ge),he=JSON.parse(N.body);return B.headers(N.headers??{}),B.code(N?.statusCode??500).send(he)}}),this.logger.debug(`Route added successfully: ${s} ${o}`)}getEventObj(e,r,n,o,i="",s=""){let p={body:e?JSON.stringify(e):null,headers:o??{},multiValueHeaders:{},httpMethod:i,isBase64Encoded:!1,path:s,pathParameters:n??null,queryStringParameters:r??null,multiValueQueryStringParameters:null,stageVariables:null,requestContext:{accountId:"",apiId:"",authorizer:void 0,protocol:"",httpMethod:i,identity:{accessKey:null,accountId:null,apiKey:null,apiKeyId:null,caller:null,clientCert:null,cognitoAuthenticationProvider:null,cognitoAuthenticationType:null,cognitoIdentityId:null,cognitoIdentityPoolId:null,principalOrgId:null,sourceIp:"devIp",user:null,userAgent:null,userArn:null},path:s,stage:"",requestId:"",requestTimeEpoch:0,resourceId:"",resourcePath:s},resource:s};return p.source="aws.events",p}getContext(){let e=Date.now(),r=Le(),n=new Date().toISOString().split("T")[0]??"";return{callbackWaitsForEmptyEventLoop:!1,functionName:"local-dev",functionVersion:"1.0.0",invokedFunctionArn:"arn:aws:lambda:local:000000000000:function:local-dev",memoryLimitInMB:"1024",awsRequestId:`local-${Date.now()}`,logGroupName:"/aws/lambda/local-dev",logStreamName:`${n}/local`,getRemainingTimeInMillis:()=>Math.max(0,r-(Date.now()-e)),done:(o,i)=>{},fail:o=>{},succeed:o=>{}}}};import{spawn as Me}from"node:child_process";import*as ce from"node:fs";import F from"node:process";var _e=2e3;function Fe(t){try{return F.kill(-t,0),!0}catch{return!1}}function k(t,e=_e){return t.unref(),{kill:async()=>{let r=t.pid;if(r==null)return;try{F.kill(-r,"SIGTERM")}catch{return}let n=Date.now()+e;for(;Date.now()<n;)if(await new Promise(o=>setTimeout(o,100)),!Fe(r))return;try{F.kill(-r,"SIGKILL")}catch{}}}}var je=(t,e)=>[...t.map(r=>`--filter=...${r}`),...e.map(r=>`--filter=${r}^...`)],ue=({depInclusive:t,depClosure:e,cwd:r,logFile:n})=>{let o=je(t,e),i=ce.openSync(n,"a"),s=Me("pnpm",["exec","turbo","watch","build",...o,"--continue=dependencies-successful","--env-mode=loose"],{cwd:r,detached:!0,stdio:["ignore",i,i]});return k(s)};import{spawn as We}from"node:child_process";var de=({packageNames:t,cwd:e,concurrency:r})=>{let n=t.map(i=>`--filter=${i}`),o=We("pnpm",["exec","turbo","run","dev",...n,`--concurrency=${r}`,"--env-mode=loose"],{cwd:e,detached:!0,stdio:["ignore","inherit","inherit"]});return k(o)};var b=y.join(f.cwd(),".infra-kit","dev-server.log"),j=!1;function Ke(){b=y.join(f.cwd(),".infra-kit","dev-server.log"),v.mkdirSync(y.dirname(b),{recursive:!0}),v.writeFileSync(b,`=== Dev Server Started: ${new Date().toISOString()} ===
1
+ import{d as re,e as te,f as ne,i as oe}from"./chunk-Q36WX5RP.js";import{A as Q,B as x,C as X,D as Z,E as ee,H as T,N as E,v as D,w as R,x as C,y as q,z as $}from"./chunk-4YU4TAGX.js";import{a as M}from"./chunk-2PZRQHWF.js";import{Command as Dr}from"commander";import P from"node:process";import{pathToFileURL as xr}from"node:url";import*as se from"node:path";import k from"node:process";var Ce=t=>({pane:{surfaces:[{type:"terminal",command:t}]}}),F=(t,e)=>{if(t.length===1)return Ce(t[0]);let r=Math.ceil(t.length/2),n=t.slice(0,r),o=t.slice(r);return{direction:e%2===0?"horizontal":"vertical",split:Math.round(r/t.length*100)/100,children:[F(n,e+1),F(o,e+1)]}},ie=t=>{if(t.length===0)throw new Error("buildCmuxLayout: at least one command is required");return F(t,0)};var $e=(t,e)=>t.map(r=>`pnpm exec infra-kit dev --app=${r}${e?" --watch":""}`),Te=(t,e)=>{let r=C(t);return e?r.filter(n=>e.includes(n.name)):r},Ee=(t,e)=>{D.info(`\u{1F9E9} Opened cmux dev workspace ${e} with ${t.length} pane(s):`);for(let r of t)D.info(` \u2022 ${r.name} (infra-kit dev --app=${r.name})`)},ke=t=>{let e=!1,r=n=>{e||(e=!0,(async()=>{try{D.info(`
2
+ Received ${n}, closing cmux dev workspace ${t}...`),await ne(t)}finally{k.exit(0)}})())};k.on("SIGINT",()=>r("SIGINT")),k.on("SIGTERM",()=>r("SIGTERM"))},ae=async t=>{let e=R(k.cwd()),r=Te(e,$(t.include));if(r.length===0){D.warn("No API apps found to run");return}let n=$e(r.map(u=>u.name),t.watch??!1),o=ie(n),i=`${se.basename(e)} dev`,s=await te({cwd:e,title:i,layout:o});Ee(r,s),ke(s);let a=setInterval(()=>{a.refresh()},2**30);await new Promise(()=>{})};import ur from"chokidar";import{exec as dr,execFileSync as hr}from"node:child_process";import*as y from"node:fs";import mr from"node:net";import*as v from"node:path";import f from"node:process";import gr from"node:util";import{execFile as Ie}from"node:child_process";import*as le from"node:fs";import*as I from"node:path";import{promisify as Le}from"node:util";var Ne=Le(Ie),Oe=t=>{try{let e=JSON.parse(le.readFileSync(I.join(t,"package.json"),"utf-8"));return typeof e.name=="string"?e.name:void 0}catch{return}},Ue=t=>async e=>{let{stdout:r}=await Ne("pnpm",["exec","turbo","run","build","--dry=json",`--filter=...${e}`],{cwd:t,maxBuffer:33554432}),o=(JSON.parse(r).tasks??[]).map(i=>i.package).filter(i=>typeof i=="string");return[...new Set(o)]},_e=t=>{let e=new Map,r=new Map;for(let n of x(t)){let o=Oe(I.dirname(n));o!==void 0&&(e.set(n,o),r.set(o,n))}return{packageNameByDir:e,dirByName:r}},pe=async(t,e,r=Ue(t))=>{let{packageNameByDir:n,dirByName:o}=_e(t),i=new Map,s=await Promise.all(e.map(async a=>({app:a,closure:await r(a.packageName)})));for(let{app:a,closure:u}of s)for(let h of u){let l=o.get(h);if(l===void 0)continue;let d=i.get(l)??new Set;d.add(a.name),i.set(l,d)}return{dependentsByPackageDir:i,packageNameByDir:n}},ce=t=>`__pkg__:${t}`,ue=(t,e,r)=>{if(e===null||r===void 0)return null;let n=e.dependentsByPackageDir.get(r)??new Set;return t.filter(o=>o.watchDeps&&n.has(o.name))};var Me="/api/v1";function de(t){if(t==null||t==="")return;let e=parseInt(t.trim().replace(/^["']|["']$/g,""),10);return Number.isNaN(e)?void 0:e}function he(t,e,r){let o=`${t.replace(/-/g,"_").toUpperCase()}_PORT`,i=de(e[o]);if(i!=null)return i;let s=de(e.PORT);return s??r[t]?.port??void 0}function me(t,e){return e[t]?.prefixUrl??Me}function ge(t){let e=t.map(o=>o.port),r=e.filter((o,i)=>e.indexOf(o)!==i),n=t.filter(o=>r.includes(o.port));return{duplicatePorts:r,conflictingApps:n}}import{execFile as Fe,spawn as We}from"node:child_process";import{promisify as je}from"node:util";var Be=je(Fe),He=1500,Ge=10,Ve=200,Ke=async(t,{timeoutMs:e})=>{await Be("portless",t,{signal:AbortSignal.timeout(e),encoding:"utf-8"})},ze=t=>{We("portless",t,{detached:!0,stdio:"ignore"}).unref()},Ye=t=>new Promise(e=>{setTimeout(e,t)}),fe=(t={})=>{let e=t.run??Ke,r=t.spawnDaemon??ze,n=t.timeoutMs??He,o=null,i=async l=>{try{return await e(l,{timeoutMs:n}),!0}catch{return!1}},s=async()=>(o===null&&(o=await i(["--version"])),o);return{isAvailable:s,ensureDaemon:async l=>{if(!await s())return!1;if(await i(["list"]))return!0;r(["proxy","start","--no-tls","-p",String(l)]);for(let d=0;d<Ge;d+=1)if(await Ye(Ve),await i(["list"]))return!0;return!1},registerAlias:async(l,d)=>await s()?i(["alias",l,String(d)]):!1,removeAlias:async l=>{await s()&&await i(["alias","--remove",l])}}};import ve from"node:process";var ye=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Je=80,g={reset:"\x1B[0m",dim:"\x1B[2m",bold:"\x1B[1m",teal:"\x1B[36m",green:"\x1B[32m",blue:"\x1B[34m",red:"\x1B[31m"},W="\r\x1B[2K",j=t=>String(t).padStart(2,"0"),qe=t=>`${j(t.getHours())}:${j(t.getMinutes())}:${j(t.getSeconds())}`,Qe=t=>`${(t/1e3).toFixed(1)}s`,B=t=>t.alias!=null&&t.alias!==""&&t.proxyPort!=null?`http://${t.alias}:${t.proxyPort}${t.prefixUrl}`:`http://localhost:${t.port}${t.prefixUrl}`,L=class{deps;spinnerTimer=null;spinnerPhase="";spinnerFrame=0;constructor(e={}){this.deps={write:e.write??(r=>{ve.stdout.write(r)}),appendLog:e.appendLog??(()=>{}),isTTY:e.isTTY??!!ve.stdout.isTTY,now:e.now??(()=>new Date),verbose:e.verbose??!1}}color(e,r){return this.deps.isTTY?`${e}${r}${g.reset}`:r}tee(e,r){this.deps.appendLog(`[${this.deps.now().toISOString()}] [${r.toUpperCase()}] ${e}
3
+ `)}emit(e){if(this.spinnerTimer!=null&&this.deps.isTTY){this.deps.write(`${W}${e}
4
+ `),this.paintSpinner();return}this.deps.write(`${e}
5
+ `)}log(e,r="info"){(r!=="debug"||this.deps.verbose)&&this.emit(e),this.tee(e,r)}narrate(e){this.deps.verbose&&this.emit(e),this.tee(e,"info")}logFn=(e,r="info")=>{this.log(e,r)};paintSpinner(){let e=ye[this.spinnerFrame%ye.length];this.deps.write(`${W}${this.color(g.teal,e)} ${this.color(g.dim,this.spinnerPhase)}`)}bootStep(e){if(this.tee(e,"info"),this.spinnerPhase=e,!this.deps.isTTY){this.deps.write(`${e}
6
+ `);return}this.spinnerTimer==null&&(this.spinnerTimer=setInterval(()=>{this.spinnerFrame+=1,this.paintSpinner()},Je),this.spinnerTimer.unref?.()),this.paintSpinner()}stopSpinner(){this.spinnerTimer!=null&&(clearInterval(this.spinnerTimer),this.spinnerTimer=null),this.deps.isTTY&&this.deps.write(W)}healthDot(e){return e===null?"":e?this.color(g.green,"\u25CF ok"):this.color(g.red,"\u25CF down")}ready(e){this.stopSpinner();let n=[...e.endpoints.map(l=>l.tag),...e.uiRefs.map(l=>l.tag)].reduce((l,d)=>Math.max(l,d.length),0),o=[""],i=[e.target,e.watch?"watch":null,e.release].filter(l=>!!l).join(" \xB7 ");o.push(` ${this.color(g.bold,"infra-kit dev")} ${this.color(g.dim,i)} ${this.color(g.green,`ready in ${Qe(e.elapsedMs)}`)}`,"");for(let l of e.endpoints){let d=this.healthDot(l.healthy),c=d?` ${d}`:"";o.push(` ${this.color(g.teal,l.tag.padEnd(n))} ${this.color(g.blue,l.url)}${c}`)}for(let l of e.uiRefs)o.push(` ${this.color(g.teal,l.tag.padEnd(n))} ${this.color(g.dim,"\u2192 starting below (vite prints its URL)")}`);o.push("");let a=`scheme ${e.release?`<release>.<package>.localhost \xB7 release ${e.release}`:"http://localhost:<port>"}`;o.push(` ${this.color(g.dim,a)}`);let h=`${e.watch&&e.watchSummary?`watching ${e.watchSummary}`:"watch off"} logs \u2192 ${e.logPath}`;o.push(` ${this.color(g.dim,h)}`),o.push(` ${this.color(g.dim,"\u2500".repeat(60))}`);for(let l of o)this.emit(l),this.tee(l,"info")}event(e){let r=qe(this.deps.now()),n=` ${this.color(g.dim,r)} ${this.color(g.teal,e.tag)} ${e.text}`;this.emit(n),this.tee(`${e.tag} ${e.text}`,"info")}};import{Logger as Xe}from"@aws-lambda-powertools/logger";import Ze from"fastify";import*as Pe from"node:fs";import*as A from"node:path";import H from"node:process";import{pathToFileURL as er}from"node:url";import{parse as rr}from"yaml";var tr=t=>t?.code==="EADDRINUSE",nr=3e4,or=()=>{let t=Number.parseInt(H.env.DEV_SERVER_TIMEOUT_MS??"",10);return Number.isNaN(t)?nr:t},we=()=>H.env.DEV_SERVER_REQUEST_LOG==="1",N=class{constructor(e){this.serverConfig=e;this.importCacheBust=`${Date.now()}`,this.logger=new Xe({serviceName:"LocalServer",logLevel:"DEBUG"}),this.serverConfig.prefixUrl=this.serverConfig.prefixUrl??"",this.server=Ze({logger:!1}),this.server.addHook("onRequest",async(n,o)=>{o.header("Access-Control-Allow-Origin","*"),o.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),o.header("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),n.method==="OPTIONS"&&o.status(204).send()});let r=this.serverConfig.onRequestLog;(we()||r)&&this.server.addHook("onResponse",async(n,o)=>{let i=Math.round(o.elapsedTime),s=n.url.split("?")[0]??n.url;r?.({method:n.method,path:s,status:o.statusCode,ms:i}),we()&&H.stdout.write(`${n.method} ${n.url} \u2192 ${o.statusCode} ${i}ms
7
+ `)})}serverConfig;importCacheBust;logger;server;controllers={};registeredRouteKeys=new Set;getRegisteredRoutes(){return[...this.registeredRouteKeys].sort()}async start(){this.registerHealthRoute(),await Promise.all(this.loadRoutes());let e=await this.listenWithFallback();return this.serverConfig.port=e,this.logger.info(`Server listening on http://127.0.0.1:${e}`,{address:`http://127.0.0.1:${e}`}),e}async listenWithFallback(){let e=this.serverConfig.port;if(e!=null)try{return await this.server.listen({port:e,host:"127.0.0.1"}),this.readBoundPort()}catch(r){if(!tr(r))throw r}return await this.server.listen({port:0,host:"127.0.0.1"}),this.readBoundPort()}readBoundPort(){let e=this.server.server.address();if(e==null||typeof e=="string")throw new Error("Server address unavailable after listen()");return e.port}async close(){let e=this.server.server;typeof e.closeAllConnections=="function"&&e.closeAllConnections(),await this.server.close()}registerHealthRoute(){this.server.route({method:"GET",url:"/__health",handler:(e,r)=>r.code(200).send({status:"ok",app:this.serverConfig.appName??null,port:this.serverConfig.port})})}loadRoutes(){let e=A.join(this.serverConfig.controllersPath,"serverless.yml"),r=Pe.readFileSync(e,"utf8"),n=rr(r),o=[];if(!n?.functions)return o;for(let[i,s]of Object.entries(n.functions))if(s?.events?.length)for(let a of s.events){let u=a?.http;u&&(this.logger.debug(`Registering route: ${u.method} /${u.path} -> ${i}`),o.push(this.defineRoute(u,s)))}return o}async defineRoute(e,r){let n=e.path.toString();n=n.replaceAll("{",":").replaceAll("}","");let o=A.posix.join(this.serverConfig.prefixUrl??"",n);o=o[0]==="/"?o:`/${o}`;let i=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"],s=String(e.method).toUpperCase();if(!i.includes(s))throw new Error(`Invalid HTTP method: "${e.method}" for URL: ${o}`);let a=`${s} ${o}`;if(this.registeredRouteKeys.has(a))throw new Error(`Duplicate route: ${a}`);this.registeredRouteKeys.add(a);let h=(r.handler??"").split("."),l=h[0]??"",d=h[1]??"",c=A.join(this.serverConfig.controllersPath,`${l}.js`),m=er(c);m.searchParams.set("v",this.importCacheBust);let p=await import(m.href);this.controllers[a]={action:p,handler:d};let U=this.logger.createChild({serviceName:"RequestLogger"});this.logger.debug(`Adding fastify route: ${s} ${o}`),this.server.route({method:s,url:o,handler:async(b,Y)=>{let S=this.controllers[a];if(!S)throw new Error(`No controller for ${a}`);let J=S.action[S.handler];if(!J)throw new Error(`No handler ${S.handler} for ${o}`);let _=await J(this.getEventObj(b.body,b.query,b.params,b.headers,s,o),this.getContext(),U),Re=JSON.parse(_.body);return Y.headers(_.headers??{}),Y.code(_?.statusCode??500).send(Re)}}),this.logger.debug(`Route added successfully: ${s} ${o}`)}getEventObj(e,r,n,o,i="",s=""){let a={body:e?JSON.stringify(e):null,headers:o??{},multiValueHeaders:{},httpMethod:i,isBase64Encoded:!1,path:s,pathParameters:n??null,queryStringParameters:r??null,multiValueQueryStringParameters:null,stageVariables:null,requestContext:{accountId:"",apiId:"",authorizer:void 0,protocol:"",httpMethod:i,identity:{accessKey:null,accountId:null,apiKey:null,apiKeyId:null,caller:null,clientCert:null,cognitoAuthenticationProvider:null,cognitoAuthenticationType:null,cognitoIdentityId:null,cognitoIdentityPoolId:null,principalOrgId:null,sourceIp:"devIp",user:null,userAgent:null,userArn:null},path:s,stage:"",requestId:"",requestTimeEpoch:0,resourceId:"",resourcePath:s},resource:s};return a.source="aws.events",a}getContext(){let e=Date.now(),r=or(),n=new Date().toISOString().split("T")[0]??"";return{callbackWaitsForEmptyEventLoop:!1,functionName:"local-dev",functionVersion:"1.0.0",invokedFunctionArn:"arn:aws:lambda:local:000000000000:function:local-dev",memoryLimitInMB:"1024",awsRequestId:`local-${Date.now()}`,logGroupName:"/aws/lambda/local-dev",logStreamName:`${n}/local`,getRemainingTimeInMillis:()=>Math.max(0,r-(Date.now()-e)),done:(o,i)=>{},fail:o=>{},succeed:o=>{}}}};import{spawn as ar}from"node:child_process";import*as be from"node:fs";import G from"node:process";var ir=2e3;function sr(t){try{return G.kill(-t,0),!0}catch{return!1}}function O(t,e=ir){return t.unref(),{kill:async()=>{let r=t.pid;if(r==null)return;try{G.kill(-r,"SIGTERM")}catch{return}let n=Date.now()+e;for(;Date.now()<n;)if(await new Promise(o=>setTimeout(o,100)),!sr(r))return;try{G.kill(-r,"SIGKILL")}catch{}}}}var lr=(t,e)=>[...t.map(r=>`--filter=...${r}`),...e.map(r=>`--filter=${r}^...`)],De=({depInclusive:t,depClosure:e,cwd:r,logFile:n})=>{let o=lr(t,e),i=be.openSync(n,"a"),s=ar("pnpm",["exec","turbo","watch","build",...o,"--continue=dependencies-successful","--env-mode=loose"],{cwd:r,detached:!0,stdio:["ignore",i,i]});return O(s)};import{spawn as pr}from"node:child_process";import cr from"node:process";var xe=({packageNames:t,cwd:e,concurrency:r,env:n})=>{let o=t.map(s=>`--filter=${s}`),i=pr("pnpm",["exec","turbo","run","dev",...o,`--concurrency=${r}`,"--env-mode=loose"],{cwd:e,detached:!0,stdio:["ignore","inherit","inherit"],env:{...cr.env,...n}});return O(i)};var w=v.join(f.cwd(),".infra-kit","dev-server.log");function fr(){w=v.join(f.cwd(),".infra-kit","dev-server.log"),y.mkdirSync(v.dirname(w),{recursive:!0}),y.writeFileSync(w,`=== Dev Server Started: ${new Date().toISOString()} ===
4
8
 
5
- `)}var Je=Ve.promisify(He),qe=async(t,e)=>{try{let{stderr:r}=await Je(t);r&&e&&e(` (build) ${r.trim()}`,"debug"),r&&!e&&console.error("stderr:",r)}catch(r){let n=r;throw e&&(n.stdout||n.stderr)&&(n.stdout&&e(` stdout: ${n.stdout.trim()}`,"error"),n.stderr&&e(` stderr: ${n.stderr.trim()}`,"error")),r}};function W(t){v.appendFileSync(b,t)}function c(t,e="info"){e==="error"?console.error(t):e==="warn"?console.warn(t):e==="debug"?j&&f.stdout.write(`${t}
6
- `):f.stdout.write(`${t}
7
- `),W(`[${new Date().toISOString()}] [${e.toUpperCase()}] ${t}
8
- `)}function m(t){j&&f.stdout.write(`${t}
9
- `),W(`[${new Date().toISOString()}] [INFO] ${t}
10
- `)}function ze(t){let e=`${t.join(`
11
- `)}
12
- `;f.stdout.write(`${e}
13
- `),W(`${e}
14
- `)}function Qe(t,e){let r=Math.max(0,e-t.length),n=Math.floor(r/2);return`${" ".repeat(n)}${t}${" ".repeat(r-n)}`}function Ye(t,e,r){let n=e.map((l,d)=>Math.max(l.length,...r.map(u=>(u[d]??"").length))),o=n.reduce((l,d)=>l+d+2,0)+(n.length-1),i=n.length-1;i>=0&&t.length+2>o&&(n[i]=(n[i]??0)+(t.length+2-o));let s=n.reduce((l,d)=>l+d+2,0)+(n.length-1),p=n.map(l=>"\u2500".repeat(l+2)),g=l=>`\u2502 ${l.map((d,u)=>(d??"").padEnd(n[u])).join(" \u2502 ")} \u2502`;return["",`\u250C${"\u2500".repeat(s)}\u2510`,`\u2502${Qe(t,s)}\u2502`,`\u251C${p.join("\u252C")}\u2524`,g(e),`\u251C${p.join("\u253C")}\u2524`,...r.map(g),`\u2514${p.join("\u2534")}\u2518`,""]}var Xe=t=>{try{let e=Ge("git",["rev-parse","--abbrev-ref","HEAD"],{cwd:t,encoding:"utf-8"}).trim(),r=L(e);return r===""?void 0:r}catch{return}},M=class t{monorepoRoot;devContextDir;appServers=[];watchDebounceTimers=new Map;watcher=null;static WATCH_DEBOUNCE_MS=400;restartWorkChain=Promise.resolve();static PORT_RELEASE_DELAY_MS=200;options;runBuild;turboWatchFactory;turboWatch=null;uiDevFactory;uiDev=null;dryRunner;constructor(e={},r=qe,n=ue,o=de,i){this.options=e,this.runBuild=r,this.turboWatchFactory=n,this.uiDevFactory=o,this.dryRunner=i,Ke(),j=this.options.verbose??!1,this.devContextDir=y.join(f.cwd(),".infra-kit","dev-context"),this.monorepoRoot=C(f.cwd()),m(`Monorepo root: ${this.monorepoRoot}`),(f.env.DOPPLER_PROJECT!=null||f.env.DOPPLER_ENVIRONMENT!=null)&&c("\u{1F510} Doppler env detected (DOPPLER_PROJECT / DOPPLER_ENVIRONMENT)","debug")}discoverApiApps(e){return S(this.monorepoRoot).map(r=>({...r,preferredPort:this.resolvePreferredPort(r.name,e),prefixUrl:this.resolvePrefixUrl(r.name,e),watchDeps:!0}))}async loadDevConfig(){try{return(await O()).dev??{}}catch{return{}}}normalizeAppInclude(){return R(this.options.include)}resolvePreferredPort(e,r){return se(e,f.env,r)}resolvePrefixUrl(e,r){return ae(e,r)}async loadDevPresets(){try{return(await O()).devPresets??{}}catch{return{}}}resolvePresetDef(e){let r=this.options.preset;if(r==null)return{apps:{"*":{}}};let n=e[r];if(!n){let o=Object.keys(e);throw new Error(`Unknown dev preset "${r}". Available: ${o.length>0?o.join(", "):"(none defined in devPresets)"}`)}return n}async start(){let e=this.normalizeAppInclude(),r=this.options.watch??!1,n=await this.loadDevConfig();f.env.POWERTOOLS_DEV??="true",f.env.LOG_LEVEL??="DEBUG",m("\u{1F680} Starting Development Server Runner"),r&&m("\u{1F440} Watch mode: will rebuild and restart on file save"),m(`\u{1F4C2} Monorepo root: ${this.monorepoRoot}`);let o=this.discoverApiApps(n),i=G(this.monorepoRoot),s=q(this.resolvePresetDef(await this.loadDevPresets()),{api:o.map(a=>a.name),ui:i.map(a=>a.name)});s.unmatched.length>0&&c(`\u26A0\uFE0F Preset targets not found (skipped): ${s.unmatched.join(", ")}`,"warn");let p=new Set(s.targets.filter(a=>a.part==="api").map(a=>a.app)),g=new Set(s.targets.filter(a=>a.part==="ui").map(a=>a.app)),l=a=>!e||e.includes(a),d=new Map(s.targets.filter(a=>a.part==="api").map(a=>[a.app,a.watchDeps])),u=o.filter(a=>p.has(a.name)&&l(a.name)).map(a=>({...a,watchDeps:d.get(a.name)??a.watchDeps})),h=i.filter(a=>g.has(a.name)&&l(a.name));if(u.length===0&&h.length===0){c("\u26A0\uFE0F No API or UI apps to run for this preset","warn");return}if(u.length>0&&(m(`\u{1F4E6} Discovered ${u.length} API app(s): ${u.map(a=>a.name).join(", ")}`),this.assertNoPortConflicts(u),await this.buildApps(u,r)),h.length>0&&await this.buildUiApps(h),u.length>0&&(await this.startAllApps(u),m("\u{1F389} All servers started!"),this.printServerTable(),this.printRouteDump(),m(`\u{1F4DD} Handler logs (AWS Powertools, logger.info/debug, etc.) \u2192 this terminal. Runner-only file: ${b}`)),r&&(this.appServers.length>0||h.length>0)){let a=await this.buildClosureMapSafe(u);this.setupWatch(u,h,a)}h.length>0&&this.startUiDev(h)}async buildUiApps(e){let r=e.map(n=>`--filter=${n.packageName}^...`).join(" ");m("\u{1F528} Warming UI dependency build cache (turbo)...");try{await this.runBuild(`pnpm exec turbo run build ${r} --env-mode=loose`,c),m("\u2705 UI deps built")}catch(n){c(`\u26A0\uFE0F UI dep warm build failed (continuing; turbo run dev will build): ${String(n)}`,"warn")}}startUiDev(e){let r=e.map(n=>n.name).join(", ");m(`\u{1F3A8} Starting ${e.length} UI dev server(s) via \`turbo run dev\`: ${r}`),m(" (framework dev output streams below; each prints its own local URL)"),this.uiDev=this.uiDevFactory({packageNames:e.map(n=>n.packageName),cwd:f.cwd(),concurrency:Math.max(e.length+4,12)})}formatAppList(e){return e.map(r=>`${r.name}:${r.port}`).join(", ")}assertNoPortConflicts(e){let r=e.filter(i=>i.preferredPort!=null).map(i=>({name:i.name,port:i.preferredPort})),{duplicatePorts:n,conflictingApps:o}=pe(r);if(n.length!==0)throw c(`\u26A0\uFE0F Port conflict detected! ${n.join(", ")}`,"error"),c(`Conflicting apps: ${this.formatAppList(o)}`,"error"),c("\n\u{1F4A1} Tip: give each app a distinct port via `{APP}_PORT` env (e.g. `CLIENT_PORT=`,","error"),c(" `SEARCH_ENGINE_PORT=`) or `dev.<app>.port` in infra-kit.json; or run a subset with","error"),c(" `--app=<name>,<name>`.\n","error"),new Error(`Port conflict detected: ${n.join(", ")}`)}async buildApps(e,r){let o=`pnpm exec turbo run build ${e.map(i=>`--filter=${i.packageName}`).join(" ")} --env-mode=loose${r?" --force":""}`;m("\u{1F528} Building API apps (turbo)...");try{await this.runBuild(o,c),m("\u2705 Build complete")}catch(i){c(`\u274C Build failed: ${String(i)}`,"error"),i instanceof Error&&i.message&&c(` ${i.message}`,"error");let s=i;throw s.stdout&&c(` stdout: ${s.stdout.trim()}`,"error"),s.stderr&&c(` stderr: ${s.stderr.trim()}`,"error"),i}}async startAllApps(e){await Promise.all(e.map(async r=>{try{let n=await this.startOneApp(r);n&&this.appServers.push({app:r,...n})}catch(n){c(`\u274C Failed to start ${r.name}: ${String(n)}`,"error")}}))}async startOneApp(e){m(`\u{1F504} Starting ${e.name}...`);let r=new I({controllersPath:e.path,prefixUrl:e.prefixUrl,port:e.preferredPort,appName:e.name}),n=await r.start();try{this.writeDevContextFragment(e,n)}catch(o){c(`\u26A0\uFE0F Failed to write dev-context fragment for ${e.name}: ${String(o)}`,"warn")}return m(`\u2705 ${e.name} started on port ${n}`),{server:r,boundPort:n}}writeDevContextFragment(e,r){let n=Xe(e.path),o={package:e.packageName,port:r,pid:f.pid,writtenAt:Date.now(),...n?{release:n}:{}},i=y.join(this.devContextDir,`${e.name}.json`),s=y.join(this.devContextDir,`${e.name}.json.${f.pid}.tmp`);v.mkdirSync(this.devContextDir,{recursive:!0}),v.writeFileSync(s,JSON.stringify(o,null,2)),v.renameSync(s,i)}removeDevContextFragment(e){v.rmSync(y.join(this.devContextDir,`${e.name}.json`),{force:!0})}scheduleRestartWork(e){let r=this.restartWorkChain.then(()=>e(),()=>e());return this.restartWorkChain=r.catch(()=>{}),r}async delayPortRelease(){await new Promise(e=>setTimeout(e,t.PORT_RELEASE_DELAY_MS))}restart(e){return this.scheduleRestartWork(()=>this.runRestart(e))}resolveRestartTargets(e){return e.map(r=>({idx:this.appServers.findIndex(n=>n.app.name===r.name),app:r})).filter(r=>r.idx>=0)}async runRestart(e){let r=this.resolveRestartTargets(e);if(r.length===0)return;let n=r.length===1?r[0].app.name:`${r.length} apps`;c(`\u{1F504} Restarting ${n}...`),await Promise.all(r.map(async({idx:i})=>{try{await this.appServers[i].server.close()}catch(s){c(` Close warning: ${String(s)}`,"debug")}})),await this.delayPortRelease(),await Promise.all(r.map(async({idx:i,app:s})=>{try{let p=await this.startOneApp(s);p&&(this.appServers[i]={app:s,...p})}catch(p){c(`\u274C Failed to restart ${s.name}: ${String(p)}`,"error")}}));let o=r.map(i=>{let s=this.appServers[i.idx];return s?`${i.app.name}:${s.boundPort}`:i.app.name}).join(", ");c(`\u2705 Restarted ${o}`)}async buildClosureMapSafe(e){try{return await te(this.monorepoRoot,e,this.dryRunner)}catch(r){return c(`\u26A0\uFE0F Dependency-closure map unavailable (${String(r)}); package changes restart all apps`,"warn"),null}}setupWatch(e,r,n){this.turboWatch=this.turboWatchFactory({depInclusive:e.map(l=>l.packageName),depClosure:r.map(l=>l.packageName),cwd:f.cwd(),logFile:b}),m("\u{1F440} Watch mode: started `turbo watch build` engine; watching dist output");let o=K(e),i=$(this.monorepoRoot),s=[...o,...i];if(s.length===0){c("\u26A0\uFE0F No app or package dist directories found to watch (were they built?)","warn");return}let p=f.env.DEV_SERVER_CHOKIDAR_POLL==="1",g=Be.watch(s,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:200,pollInterval:100},ignored:l=>l.endsWith(".tsbuildinfo")||l.endsWith(".map"),...p?{usePolling:!0,interval:400}:{}});this.watcher=g,p&&c("\u{1F440} chokidar: usePolling enabled (DEV_SERVER_CHOKIDAR_POLL=1)","debug"),g.on("change",l=>{c(`\u{1F440} dist change detected: ${l}`,"debug");let d=J(l,o,i);if(d.kind==="package"){let h=oe(e,n,d.packageDir);if(h===null){this.scheduleDebounced("__packages__",()=>this.restart(e));return}h.length>0&&d.packageDir!==void 0&&this.scheduleDebounced(ne(d.packageDir),()=>this.restart(h));return}let u=e.find(h=>y.join(h.path,"dist")===d.app);u&&this.scheduleDebounced(u.name,()=>this.restart([u]))}),m(`\u{1F440} Watching ${o.length} app dist + ${i.length} package dist dir(s) for changes...`)}scheduleDebounced(e,r){let n=this.watchDebounceTimers.get(e);n&&clearTimeout(n);let o=setTimeout(()=>{this.watchDebounceTimers.delete(e),r().catch(i=>{c(`Restart error (${e}): ${String(i)}`,"error")})},t.WATCH_DEBOUNCE_MS);this.watchDebounceTimers.set(e,o)}printServerTable(){let e=this.appServers.map(({app:r,boundPort:n})=>[r.name,String(n),`http://localhost:${n}${r.prefixUrl}`,`http://localhost:${n}/__health`]);ze(Ye("\u{1F5A5}\uFE0F Running Servers",["App","Port","Base URL","Health"],e))}printRouteDump(){if(this.appServers.length!==0){m("\u{1F5FA}\uFE0F Registered routes:");for(let{app:e,server:r}of this.appServers){let n=r.getRegisteredRoutes();m(` ${e.name} (${n.length}): ${n.length>0?n.join(", "):"(none)"}`)}}}async shutdown(){c("\u{1F6D1} Shutting down all servers..."),this.turboWatch&&(await this.turboWatch.kill(),this.turboWatch=null),this.uiDev&&(await this.uiDev.kill(),this.uiDev=null);for(let e of this.watchDebounceTimers.values())clearTimeout(e);this.watchDebounceTimers.clear(),this.watcher&&(await this.watcher.close(),this.watcher=null);for(let{app:e,server:r}of this.appServers){try{await r.close()}catch{}this.removeDevContextFragment(e)}m(`\u{1F4DD} Logs saved to: ${b}`)}};async function me(t={}){let e=new M(t);return await e.start(),e}var rr=t=>{if(t==null)return null;let e=t.split(",").map(r=>r.trim()).filter(Boolean);return e.length>0?e:null},tr=t=>({watch:t.watch??!1,include:rr(t.app),preset:t.preset,cmux:t.cmux??!1,self:t.self??!1,verbose:t.verbose??!1}),nr=t=>!t.self||t.include?t:{...t,include:[V(w.cwd())]},or=async t=>{let e=nr(t);if(e.cmux){if(await z()){await ee(e);return}w.stdout.write(`cmux not available; falling back to single-terminal dev
15
- `)}let r=await me(e),n=!1,o=i=>{n||(n=!0,(async()=>{try{w.stdout.write(`
9
+ `)}var vr=gr.promisify(dr),yr=async(t,e)=>{try{let{stderr:r}=await vr(t);r&&e&&e(` (build) ${r.trim()}`,"debug"),r&&!e&&console.error("stderr:",r)}catch(r){let n=r;throw e&&(n.stdout||n.stderr)&&(n.stdout&&e(` stdout: ${n.stdout.trim()}`,"error"),n.stderr&&e(` stderr: ${n.stderr.trim()}`,"error")),r}};function wr(t){y.appendFileSync(w,t)}var Pr=()=>new Promise((t,e)=>{let r=mr.createServer();r.unref(),r.on("error",e),r.listen(0,"127.0.0.1",()=>{let n=r.address(),o=typeof n=="object"&&n!==null?n.port:0;r.close(()=>t(o))})}),br=async t=>{try{return(await fetch(t,{signal:AbortSignal.timeout(1500)})).ok}catch{return!1}},V=t=>{try{let e=hr("git",["rev-parse","--abbrev-ref","HEAD"],{cwd:t,encoding:"utf-8"}).trim(),r=M(e);return r===""?void 0:r}catch{return}},K=class t{monorepoRoot;devContextDir;appServers=[];watchDebounceTimers=new Map;watcher=null;static WATCH_DEBOUNCE_MS=400;restartWorkChain=Promise.resolve();static PORT_RELEASE_DELAY_MS=200;options;runBuild;turboWatchFactory;turboWatch=null;uiDevFactory;uiDev=null;dryRunner;renderer;healthProbe;proxy;proxyPort;proxyActive=!1;registeredAliases=new Set;uiPortMap={};constructor(e={},r=yr,n=De,o=xe,i,s,a=br,u=fe()){this.options=e,this.runBuild=r,this.turboWatchFactory=n,this.uiDevFactory=o,this.dryRunner=i,this.proxy=u,this.proxyPort=e.proxyPort??T,fr(),this.renderer=s??new L({appendLog:wr,verbose:this.options.verbose??!1}),this.healthProbe=a,this.devContextDir=v.join(f.cwd(),".infra-kit","dev-context"),this.monorepoRoot=R(f.cwd()),this.renderer.narrate(`Monorepo root: ${this.monorepoRoot}`),(f.env.DOPPLER_PROJECT!=null||f.env.DOPPLER_ENVIRONMENT!=null)&&this.renderer.log("\u{1F510} Doppler env detected (DOPPLER_PROJECT / DOPPLER_ENVIRONMENT)","debug")}discoverApiApps(e){return C(this.monorepoRoot).map(r=>({...r,preferredPort:this.resolvePreferredPort(r.name,e),prefixUrl:this.resolvePrefixUrl(r.name,e),watchDeps:!0}))}async loadDevConfig(){try{return(await E()).dev??{}}catch{return{}}}normalizeAppInclude(){return $(this.options.include)}resolvePreferredPort(e,r){return he(e,f.env,r)}resolvePrefixUrl(e,r){return me(e,r)}async loadDevPresets(){try{return(await E()).devServersPresets??{}}catch{return{}}}resolvePresetDef(e){if(this.options.presetDef!=null)return this.options.presetDef;let r=this.options.preset;if(r==null)return{apps:{"*":{}}};let n=e[r];if(!n){let o=Object.keys(e);throw new Error(`Unknown dev preset "${r}". Available: ${o.length>0?o.join(", "):"(none defined in devServersPresets)"}`)}return n}async start(){let e=Date.now(),r=this.normalizeAppInclude(),n=this.options.watch??!1,o=await this.loadDevConfig();f.env.POWERTOOLS_DEV??="true",f.env.LOG_LEVEL??="DEBUG",this.renderer.narrate("\u{1F680} Starting Development Server Runner"),n&&this.renderer.narrate("\u{1F440} Watch mode: will rebuild and restart on file save"),this.renderer.narrate(`\u{1F4C2} Monorepo root: ${this.monorepoRoot}`);let i=this.discoverApiApps(o),s=q(this.monorepoRoot),a=ee(this.resolvePresetDef(await this.loadDevPresets()),{api:i.map(p=>p.name),ui:s.map(p=>p.name)});a.unmatched.length>0&&this.renderer.log(`\u26A0\uFE0F Preset targets not found (skipped): ${a.unmatched.join(", ")}`,"warn");let u=new Set(a.targets.filter(p=>p.part==="api").map(p=>p.app)),h=new Set(a.targets.filter(p=>p.part==="ui").map(p=>p.app)),l=p=>!r||r.includes(p),d=new Map(a.targets.filter(p=>p.part==="api").map(p=>[p.app,p.watchDeps])),c=i.filter(p=>u.has(p.name)&&l(p.name)).map(p=>({...p,watchDeps:d.get(p.name)??p.watchDeps})),m=s.filter(p=>h.has(p.name)&&l(p.name));if(c.length===0&&m.length===0){this.renderer.log("\u26A0\uFE0F No API or UI apps to run for this preset","warn");return}if(this.proxyPort=await this.resolveProxyPort(),await this.ensureProxy(),c.length>0&&(this.renderer.narrate(`\u{1F4E6} Discovered ${c.length} API app(s): ${c.map(p=>p.name).join(", ")}`),this.assertNoPortConflicts(c),this.renderer.bootStep("building api apps"),await this.buildApps(c,n)),m.length>0&&(this.renderer.bootStep("warming ui deps"),await this.buildUiApps(m)),c.length>0&&(this.renderer.bootStep("starting servers"),await this.startAllApps(c),this.renderer.narrate("\u{1F389} All servers started!"),this.renderer.narrate(`\u{1F4DD} Handler logs (AWS Powertools, logger.info/debug, etc.) \u2192 this terminal. Runner-only file: ${w}`)),await this.printReady(c,m,e),this.options.routes&&this.printRouteDump(),n&&(this.appServers.length>0||m.length>0)){let p=await this.buildClosureMapSafe(c);this.setupWatch(c,m,p)}m.length>0&&this.startUiDev(m)}async buildUiApps(e){let r=e.map(n=>`--filter=${n.packageName}^...`).join(" ");this.renderer.narrate("\u{1F528} Warming UI dependency build cache (turbo)...");try{await this.runBuild(`pnpm exec turbo run build ${r} --env-mode=loose`,this.renderer.logFn),this.renderer.narrate("\u2705 UI deps built")}catch(n){this.renderer.log(`\u26A0\uFE0F UI dep warm build failed (continuing; turbo run dev will build): ${String(n)}`,"warn")}}startUiDev(e){let r=e.map(o=>o.name).join(", ");this.renderer.narrate(`\u{1F3A8} Starting ${e.length} UI dev server(s) via \`turbo run dev\`: ${r}`),this.renderer.narrate(" (framework dev output streams below; each prints its own local URL)");let n=this.proxyActive&&Object.keys(this.uiPortMap).length>0?{INFRA_KIT_UI_PORTS:JSON.stringify(this.uiPortMap)}:void 0;this.uiDev=this.uiDevFactory({packageNames:e.map(o=>o.packageName),cwd:f.cwd(),concurrency:Math.max(e.length+4,12),env:n})}formatAppList(e){return e.map(r=>`${r.name}:${r.port}`).join(", ")}assertNoPortConflicts(e){let r=e.filter(i=>i.preferredPort!=null).map(i=>({name:i.name,port:i.preferredPort})),{duplicatePorts:n,conflictingApps:o}=ge(r);if(n.length!==0)throw this.renderer.log(`\u26A0\uFE0F Port conflict detected! ${n.join(", ")}`,"error"),this.renderer.log(`Conflicting apps: ${this.formatAppList(o)}`,"error"),this.renderer.log("\n\u{1F4A1} Tip: give each app a distinct port via `{APP}_PORT` env (e.g. `CLIENT_PORT=`,","error"),this.renderer.log(" `SEARCH_ENGINE_PORT=`) or `dev.<app>.port` in infra-kit.json; or run a subset with","error"),this.renderer.log(" `--app=<name>,<name>`.\n","error"),new Error(`Port conflict detected: ${n.join(", ")}`)}async buildApps(e,r){let o=`pnpm exec turbo run build ${e.map(i=>`--filter=${i.packageName}`).join(" ")} --env-mode=loose${r?" --force":""}`;this.renderer.narrate("\u{1F528} Building API apps (turbo)...");try{await this.runBuild(o,this.renderer.logFn),this.renderer.narrate("\u2705 Build complete")}catch(i){this.renderer.log(`\u274C Build failed: ${String(i)}`,"error"),i instanceof Error&&i.message&&this.renderer.log(` ${i.message}`,"error");let s=i;throw s.stdout&&this.renderer.log(` stdout: ${s.stdout.trim()}`,"error"),s.stderr&&this.renderer.log(` stderr: ${s.stderr.trim()}`,"error"),i}}async startAllApps(e){await Promise.all(e.map(async r=>{try{let n=await this.startOneApp(r);n&&this.appServers.push({app:r,...n})}catch(n){this.renderer.log(`\u274C Failed to start ${r.name}: ${String(n)}`,"error")}}))}async resolveProxyPort(){if(this.options.proxyPort!=null)return this.options.proxyPort;try{return(await E()).devProxy?.port??T}catch{return T}}async ensureProxy(){if(!await this.proxy.isAvailable()){this.renderer.narrate("portless not found \u2014 dev URLs stay on http://localhost:<port>");return}this.proxyActive=await this.proxy.ensureDaemon(this.proxyPort),this.proxyActive||this.renderer.log("\u26A0\uFE0F portless proxy did not come up \u2014 dev URLs stay on http://localhost:<port>","warn")}async registerAppAlias(e,r,n){if(!this.proxyActive)return;let o=V(r);if(o==null)return;let i=`${o}.${e}`;if(await this.proxy.registerAlias(i,n))return this.registeredAliases.add(i),`${i}.localhost`}async startOneApp(e){this.renderer.narrate(`\u{1F504} Starting ${e.name}...`);let r=new N({controllersPath:e.path,prefixUrl:e.prefixUrl,port:e.preferredPort,appName:e.name,onRequestLog:({method:i,path:s,status:a,ms:u})=>{s!=="/__health"&&this.renderer.event({tag:`${e.name}/api`,text:`${i} ${s} ${a} ${u}ms`})}}),n=await r.start();try{this.writeDevContextFragment(e,n)}catch(i){this.renderer.log(`\u26A0\uFE0F Failed to write dev-context fragment for ${e.name}: ${String(i)}`,"warn")}let o=await this.registerAppAlias(e.packageName,e.path,n);return this.renderer.narrate(`\u2705 ${e.name} started on port ${n}`),{server:r,boundPort:n,alias:o}}writeDevContextFragment(e,r){let n=V(e.path),o={package:e.packageName,port:r,pid:f.pid,writtenAt:Date.now(),...n?{release:n}:{}},i=v.join(this.devContextDir,`${e.name}.json`),s=v.join(this.devContextDir,`${e.name}.json.${f.pid}.tmp`);y.mkdirSync(this.devContextDir,{recursive:!0}),y.writeFileSync(s,JSON.stringify(o,null,2)),y.renameSync(s,i)}removeDevContextFragment(e){y.rmSync(v.join(this.devContextDir,`${e.name}.json`),{force:!0})}scheduleRestartWork(e){let r=this.restartWorkChain.then(()=>e(),()=>e());return this.restartWorkChain=r.catch(()=>{}),r}async delayPortRelease(){await new Promise(e=>setTimeout(e,t.PORT_RELEASE_DELAY_MS))}restart(e){return this.scheduleRestartWork(()=>this.runRestart(e))}resolveRestartTargets(e){return e.map(r=>({idx:this.appServers.findIndex(n=>n.app.name===r.name),app:r})).filter(r=>r.idx>=0)}async runRestart(e){let r=this.resolveRestartTargets(e);if(r.length===0)return;let n=r.length===1?r[0].app.name:`${r.length} apps`;this.renderer.log(`\u{1F504} Restarting ${n}...`),await Promise.all(r.map(async({idx:i})=>{try{await this.appServers[i].server.close()}catch(s){this.renderer.log(` Close warning: ${String(s)}`,"debug")}})),await this.delayPortRelease(),await Promise.all(r.map(async({idx:i,app:s})=>{try{let a=await this.startOneApp(s);a&&(this.appServers[i]={app:s,...a})}catch(a){this.renderer.log(`\u274C Failed to restart ${s.name}: ${String(a)}`,"error")}}));let o=r.map(i=>{let s=this.appServers[i.idx];return s?`${i.app.name}:${s.boundPort}`:i.app.name}).join(", ");this.renderer.log(`\u2705 Restarted ${o}`)}async buildClosureMapSafe(e){try{return await pe(this.monorepoRoot,e,this.dryRunner)}catch(r){return this.renderer.log(`\u26A0\uFE0F Dependency-closure map unavailable (${String(r)}); package changes restart all apps`,"warn"),null}}setupWatch(e,r,n){this.turboWatch=this.turboWatchFactory({depInclusive:e.map(h=>h.packageName),depClosure:r.map(h=>h.packageName),cwd:f.cwd(),logFile:w}),this.renderer.narrate("\u{1F440} Watch mode: started `turbo watch build` engine; watching dist output");let o=X(e),i=x(this.monorepoRoot),s=[...o,...i];if(s.length===0){this.renderer.log("\u26A0\uFE0F No app or package dist directories found to watch (were they built?)","warn");return}let a=f.env.DEV_SERVER_CHOKIDAR_POLL==="1",u=ur.watch(s,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:200,pollInterval:100},ignored:h=>h.endsWith(".tsbuildinfo")||h.endsWith(".map"),...a?{usePolling:!0,interval:400}:{}});this.watcher=u,a&&this.renderer.log("\u{1F440} chokidar: usePolling enabled (DEV_SERVER_CHOKIDAR_POLL=1)","debug"),u.on("change",h=>{this.renderer.log(`\u{1F440} dist change detected: ${h}`,"debug");let l=Z(h,o,i);if(l.kind==="package"){let c=ue(e,n,l.packageDir);if(c===null){this.scheduleDebounced("__packages__",()=>this.restart(e));return}c.length>0&&l.packageDir!==void 0&&this.scheduleDebounced(ce(l.packageDir),()=>this.restart(c));return}let d=e.find(c=>v.join(c.path,"dist")===l.app);d&&this.scheduleDebounced(d.name,()=>this.restart([d]))}),this.renderer.narrate(`\u{1F440} Watching ${o.length} app dist + ${i.length} package dist dir(s) for changes...`)}scheduleDebounced(e,r){let n=this.watchDebounceTimers.get(e);n&&clearTimeout(n);let o=setTimeout(()=>{this.watchDebounceTimers.delete(e),r().catch(i=>{this.renderer.log(`Restart error (${e}): ${String(i)}`,"error")})},t.WATCH_DEBOUNCE_MS);this.watchDebounceTimers.set(e,o)}async printReady(e,r,n){let o=Date.now()-n,i=await Promise.all(this.appServers.map(({boundPort:m})=>this.healthProbe(`http://localhost:${m}/__health`))),s=this.proxyActive?this.proxyPort:void 0,a=this.appServers.map(({app:m,boundPort:p,alias:U},b)=>({tag:`${m.name}/api`,url:B({port:p,prefixUrl:m.prefixUrl,alias:U,proxyPort:s}),healthy:i[b]??null}));this.uiPortMap={};let u=[],h=[];for(let m of r){let p=this.proxyActive?await this.aliasUi(m):void 0;p!=null?u.push({tag:`${m.name}/ui`,url:B({port:this.uiPortMap[m.packageName],prefixUrl:"",alias:p,proxyPort:s}),healthy:null}):h.push({tag:`${m.name}/ui`})}let l=this.options.watch??!1,d=e.length+r.length,c=x(this.monorepoRoot).length;this.renderer.ready({target:this.options.preset??"*",watch:l,release:V(f.cwd()),elapsedMs:o,endpoints:[...a,...u],uiRefs:h,watchSummary:`${d} app${d===1?"":"s"} \xB7 ${c} package${c===1?"":"s"}`,logPath:v.relative(f.cwd(),w)||w})}async aliasUi(e){try{let r=await Pr(),n=await this.registerAppAlias(e.packageName,e.path,r);return n==null?void 0:(this.uiPortMap[e.packageName]=r,n)}catch{return}}printRouteDump(){if(this.appServers.length!==0){this.renderer.log("\u{1F5FA}\uFE0F Registered routes:");for(let{app:e,server:r}of this.appServers){let n=r.getRegisteredRoutes();this.renderer.log(` ${e.name} (${n.length}): ${n.length>0?n.join(", "):"(none)"}`)}}}async shutdown(){this.renderer.log("\u{1F6D1} Shutting down all servers..."),this.turboWatch&&(await this.turboWatch.kill(),this.turboWatch=null),this.uiDev&&(await this.uiDev.kill(),this.uiDev=null);for(let e of this.watchDebounceTimers.values())clearTimeout(e);this.watchDebounceTimers.clear(),this.watcher&&(await this.watcher.close(),this.watcher=null);for(let{app:e,server:r}of this.appServers){try{await r.close()}catch{}this.removeDevContextFragment(e)}for(let e of this.registeredAliases)await this.proxy.removeAlias(e);this.registeredAliases.clear(),this.renderer.narrate(`\u{1F4DD} Logs saved to: ${w}`)}};async function Ae(t={}){let e=new K(t);return await e.start(),e}var Ar=t=>{if(t==null)return;let e=Number.parseInt(t,10);return Number.isInteger(e)&&e>0?e:void 0},Sr=t=>{if(t==null)return null;let e=t.split(",").map(r=>r.trim()).filter(Boolean);return e.length>0?e:null},Se=t=>({watch:t.watch??!1,include:Sr(t.app),preset:t.preset,cmux:t.cmux??!1,self:t.self??!1,verbose:t.verbose??!1,routes:t.routes??!1,proxyPort:Ar(t.proxyPort)}),Rr=t=>!t.self||t.include?t:{...t,include:[Q(P.cwd())]},z=async t=>{let e=Rr(t);if(e.cmux){if(await re()){await ae(e);return}P.stdout.write(`cmux not available; falling back to single-terminal dev
10
+ `)}let r=await Ae(e),n=!1,o=i=>{n||(n=!0,(async()=>{try{P.stdout.write(`
16
11
  Received ${i}, shutting down dev-server...
17
- `),await r.shutdown()}finally{w.exit(0)}})())};w.on("SIGINT",()=>o("SIGINT")),w.on("SIGTERM",()=>o("SIGTERM"))},ir=async t=>{let e=new Ze;e.name("infra-kit-dev-server").description("Run local dev servers for the apps in a named devPresets preset (or all apps)").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)"),e.parse(t),await or(tr({...e.opts(),preset:e.args[0]}))};import.meta.url===er(w.argv[1]??"").href&&ir(w.argv).catch(t=>{console.error(t),w.exit(1)});export{or as runDevServer,tr as toDevServerOptions};
12
+ `),await r.shutdown()}finally{P.exit(0)}})())};P.on("SIGINT",()=>o("SIGINT")),P.on("SIGTERM",()=>o("SIGTERM"))},Cr=(t,e,r)=>!t.preset&&!t.app&&!t.self&&!t.cmux&&!t.watch&&!t.verbose&&!t.routes&&!t.proxyPort&&e&&!r,$r=t=>({watch:t.watch,cmux:t.cmux,include:t.include??null,preset:t.preset,presetDef:t.presetDef,self:!1,verbose:!1,routes:!1}),Ot=async(t,e,r)=>{if(Cr(t,e,r)){let{runDevWizard:n}=await import("./dev-wizard-run-LYNLPQMV.js"),o;try{o=await n()}catch(i){if(oe(i))return;throw i}if(o==null)return;await z($r(o));return}await z(Se(t))},Tr=async t=>{let e=new Dr;e.name("infra-kit-dev-server").description("Run local dev servers for the apps in a named devServersPresets preset (or all apps)").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)"),e.parse(t),await z(Se({...e.opts(),preset:e.args[0]}))};import.meta.url===xr(P.argv[1]??"").href&&Tr(P.argv).catch(t=>{console.error(t),P.exit(1)});export{z as runDevServer,Ot as runDevServerCli,Cr as shouldRunWizard,Se as toDevServerOptions};
18
13
  //# sourceMappingURL=dev-server.js.map