infra-kit 0.1.129 → 0.1.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/boot-5GFZBDKL.js +2 -0
  2. package/dist/boot-5GFZBDKL.js.map +7 -0
  3. package/dist/chunk-4UUVKJYG.js +2 -0
  4. package/dist/chunk-4UUVKJYG.js.map +7 -0
  5. package/dist/chunk-52WL2IQX.js +4 -0
  6. package/dist/chunk-52WL2IQX.js.map +7 -0
  7. package/dist/chunk-6FU2TRU5.js +2 -0
  8. package/dist/chunk-6FU2TRU5.js.map +7 -0
  9. package/dist/chunk-6YAKVQZN.js +4 -0
  10. package/dist/chunk-6YAKVQZN.js.map +7 -0
  11. package/dist/chunk-C2HRONYA.js +6 -0
  12. package/dist/chunk-C2HRONYA.js.map +7 -0
  13. package/dist/chunk-CHETBZ6M.js +3 -0
  14. package/dist/chunk-CHETBZ6M.js.map +7 -0
  15. package/dist/chunk-F6VCGS3D.js +2 -0
  16. package/dist/chunk-F6VCGS3D.js.map +7 -0
  17. package/dist/chunk-LENH44GY.js +215 -0
  18. package/dist/chunk-LENH44GY.js.map +7 -0
  19. package/dist/chunk-LTKR3F4N.js +2 -0
  20. package/dist/chunk-LTKR3F4N.js.map +7 -0
  21. package/dist/chunk-O3YJM6IR.js +2 -0
  22. package/dist/chunk-O3YJM6IR.js.map +7 -0
  23. package/dist/chunk-SR2GFEY7.js +2 -0
  24. package/dist/chunk-SR2GFEY7.js.map +7 -0
  25. package/dist/chunk-TKZINCST.js +2 -0
  26. package/dist/chunk-TKZINCST.js.map +7 -0
  27. package/dist/chunk-VJPLVBRA.js +5 -0
  28. package/dist/chunk-VJPLVBRA.js.map +7 -0
  29. package/dist/chunk-X2L4F2VM.js +3 -0
  30. package/dist/chunk-X2L4F2VM.js.map +7 -0
  31. package/dist/cli.js +4 -19
  32. package/dist/cli.js.map +4 -4
  33. package/dist/dev-server.js +20 -16
  34. package/dist/dev-server.js.map +4 -4
  35. package/dist/dev-wizard-run-7HEKSAS2.js +2 -0
  36. package/dist/dev-wizard-run-7HEKSAS2.js.map +7 -0
  37. package/dist/lib/release-slug/release-slug.d.ts +26 -2
  38. package/dist/lib/vite/vite.d.ts +18 -4
  39. package/dist/mcp.js +1 -1
  40. package/dist/mcp.js.map +3 -3
  41. package/dist/persistent-ink-dev-ui-4U4B2B7C.js +8 -0
  42. package/dist/persistent-ink-dev-ui-4U4B2B7C.js.map +7 -0
  43. package/dist/program.js +2 -0
  44. package/dist/program.js.map +7 -0
  45. package/dist/update-check.js +2 -0
  46. package/dist/update-check.js.map +7 -0
  47. package/dist/vite.js +1 -1
  48. package/package.json +6 -3
  49. package/dist/boot-SGM5RVLJ.js +0 -2
  50. package/dist/boot-SGM5RVLJ.js.map +0 -7
  51. package/dist/chunk-2PZRQHWF.js +0 -2
  52. package/dist/chunk-2PZRQHWF.js.map +0 -7
  53. package/dist/chunk-6RRAK2QO.js +0 -5
  54. package/dist/chunk-6RRAK2QO.js.map +0 -7
  55. package/dist/chunk-IX2A34UU.js +0 -2
  56. package/dist/chunk-IX2A34UU.js.map +0 -7
  57. package/dist/chunk-V5IDN7ZM.js +0 -162
  58. package/dist/chunk-V5IDN7ZM.js.map +0 -7
package/dist/cli.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 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"]
3
+ "sources": ["../src/entry/cli.ts", "../src/lib/render/render.ts", "../src/lib/session/run-session.ts", "../src/lib/session/alt-screen.ts", "../src/lib/session/format-entry.ts", "../src/lib/session/outcome.ts", "../src/lib/update-check/auto-update.ts", "../src/lib/update-check/guards.ts"],
4
+ "sourcesContent": ["#!/usr/bin/env node\n// The hashbang must stay on line 1: `npm i -g` symlinks the bin and execs it\n// directly, unlike pnpm's `.bin` shim which invokes `node <path>` explicitly.\n// esbuild preserves it on this entry only \u2014 never on the library entries.\nimport select, { Separator } from '@inquirer/select'\nimport { realpathSync } from 'node:fs'\nimport process from 'node:process'\nimport { fileURLToPath } from 'node:url'\n\nimport { commandCatalog, getMenuGroupCommands } from 'src/lib/command-catalog'\nimport { isPromptCancellation } from 'src/lib/errors/is-prompt-cancellation'\nimport { isLocalNodeModulesInstall, safeRealpath } from 'src/lib/install-manager'\nimport { logger } from 'src/lib/logger'\nimport { formatAlignedRows } from 'src/lib/render'\nimport { captureSessionReportPath } from 'src/lib/session/report'\nimport { runSession, sessionGateEnabled } from 'src/lib/session/run-session'\nimport { maybeAutoUpdate } from 'src/lib/update-check'\n\nimport packageJson from '../../package.json' with { type: 'json' }\nimport { buildProgram, invokedViaMenu } from './program'\n\n// Capture (and delete from the env) the session-shell report path BEFORE any command runs, so the\n// running command can write its report while no descendant (gh / git / a hook / $EDITOR) inherits the\n// var and clobbers the file. A no-op for a normal top-level invocation (the var is unset).\ncaptureSessionReportPath()\n\nconst program = buildProgram()\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/**\n * Best-effort nudge when the CLI runs out of a project-local `node_modules`. Advisory only: it must never\n * throw and never change the exit code.\n *\n * The cwd clause inside `isLocalNodeModulesInstall` is what distinguishes a project-local install from a\n * global root \u2014 BOTH contain a `node_modules` segment (`pnpm root -g` is\n * `/Users/<me>/Library/pnpm/global/5/node_modules`). Without it we would nag every global pnpm user.\n *\n * `logger` is pino-pretty with `destination: 2`, i.e. stderr \u2014 never stdout, so `--json` payloads and the\n * MCP child's stdio framing stay clean. It is still suppressed for `--json` (machine consumers want no\n * chatter) and for `mcp` (that child's stdio should carry nothing but the transport).\n */\nconst warnIfLocalInstall = (): void => {\n try {\n if (process.env.INFRA_KIT_NO_LOCATION_WARN) return\n if (process.argv.includes('--json') || process.argv[2] === 'mcp') return\n\n if (isLocalNodeModulesInstall(realpathSync(fileURLToPath(import.meta.url)), process.cwd(), safeRealpath)) {\n logger.info('Running from a project-local node_modules. Install globally for faster startup: npm i -g infra-kit')\n }\n } catch {\n // Advisory only \u2014 a failure to advise is never a failure of the command.\n }\n}\n\nwarnIfLocalInstall()\n\n// Fire-and-forget: reads a cached timestamp, and at most once a day hands off to a detached\n// `dist/update-check.js` that fetches, and silently installs, AFTER this process exits. Adds no\n// startup latency, never throws, never changes the exit code. Opt out with INFRA_KIT_NO_AUTO_UPDATE.\nmaybeAutoUpdate(packageJson.version)\n\n/**\n * Bare-invocation interactive menu: build the grouped command palette from the single command\n * catalog and let the user pick one command (returns its name, or null when nothing is picked).\n * Interactive TTY \u2192 Ink command palette, loaded lazily via dynamic import so React/Ink never touch\n * the MCP / `--json` / non-TTY code paths. Otherwise falls back to the Inquirer menu (scripts, pipes,\n * CI). Ctrl-C / Esc at the menu backs out cleanly (returns null).\n */\nconst commandMapByName = (): Map<string, (typeof program.commands)[number]> => {\n return new Map(\n program.commands.map((cmd) => {\n return [cmd.name(), cmd]\n }),\n )\n}\n\n/**\n * Flat {name, description, group} palette list shared by the Ink palette, the Inquirer fallback, and\n * the session shell. Membership and order live in the command catalog; descriptions come from\n * Commander (single source).\n */\nconst buildPaletteItems = (): { name: string; description: string; group: string }[] => {\n const commandMap = commandMapByName()\n const groups = [\n { label: 'Release Management', names: getMenuGroupCommands('release') },\n { label: 'Worktrees', names: getMenuGroupCommands('worktrees') },\n { label: 'Environment', names: getMenuGroupCommands('environment') },\n ]\n\n return 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\nconst runInteractiveMenu = async (): Promise<string | null> => {\n const releaseCommands = getMenuGroupCommands('release')\n const worktreeCommands = getMenuGroupCommands('worktrees')\n const envCommands = getMenuGroupCommands('environment')\n const commandMap = commandMapByName()\n const paletteItems = buildPaletteItems()\n\n let selected: string | null = null\n\n // Ctrl-C / Esc at the menu throws from the Inquirer fallback; treat it as a clean exit.\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 return selected\n}\n\n/**\n * Persistent session shell: bare `infra-kit` in a capable interactive TTY keeps running after each\n * command. Each pick spawns a fresh `infra-kit <canonical argv>` child on the alternate screen; on\n * return only a committed `equivalent + report` transcript entry remains. React-free loop logic lives\n * in `src/lib/session`; the Ink palette is reached only through the lazy `src/tui/boot` import.\n */\nconst runSessionShell = async (): Promise<void> => {\n const { runCommandPalette } = await import('src/tui/boot')\n const cliPath = fileURLToPath(import.meta.url)\n const catalogByName = new Map(\n commandCatalog.map((entry) => {\n return [entry.cliName, entry]\n }),\n )\n\n await runSession(buildPaletteItems(), {\n renderPalette: runCommandPalette,\n resolveCommand: (name) => {\n const entry = catalogByName.get(name)\n\n return entry\n ? { groupPath: entry.groupPath, ownsScreen: entry.ownsScreen, sessionEnvNotice: entry.sessionEnvNotice }\n : undefined\n },\n cliPath,\n })\n}\n\nif (process.argv.length <= 2) {\n const streams = { stdoutIsTTY: Boolean(process.stdout.isTTY), stdinIsTTY: Boolean(process.stdin.isTTY) }\n\n if (sessionGateEnabled(process.env, streams)) {\n // A capable interactive TTY \u2192 the persistent session shell (exit code always 0).\n await runSessionShell()\n } else {\n // Pipes / CI / non-TTY / opt-out \u2192 today's one-shot behaviour and exit codes.\n const selected = await runInteractiveMenu()\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 }\n} else {\n await runProgram()\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", "import { spawn as nodeSpawn } from 'node:child_process'\nimport type { ChildProcess } from 'node:child_process'\nimport process from 'node:process'\n\nimport { withAltScreen } from './alt-screen'\nimport { equivalentLine } from './equivalent'\nimport { formatTranscriptEntry } from './format-entry'\nimport { classifyOutcome } from './outcome'\nimport { SESSION_REPORT_ENV, newReportPath, readAndUnlinkReport } from './report'\n\n/**\n * The persistent `infra-kit` session shell (React-free so it stays out of the eager Ink chunk and is\n * unit-testable with plain vitest). Each iteration: render the palette \u2192 spawn the picked command as a\n * fresh child on the alternate screen \u2192 reap \u2192 read the child's report file \u2192 commit one\n * `equivalent + report` transcript entry to the primary screen \u2192 repeat, until the palette returns\n * `null` (quit).\n *\n * The child boundary is what makes this safe: a `process.exit` in a command exits the child, not the\n * loop; a fresh process means fresh Commander/option state; and the child owns stdin via inherited\n * stdio, so the parent adds zero contention. The session exit code is always 0 \u2014 per-command results\n * live in the transcript, not the shell's exit status.\n */\n\n/** A menu-selectable command resolved from the catalog: what to spawn and how to present it. */\nexport interface SessionCommand {\n /** Canonical Commander argv, e.g. `['vendor', 'check']`. */\n groupPath: string[]\n /** Skip the alt-screen wrap (a full-screen `$EDITOR` owns the terminal itself). */\n ownsScreen?: boolean\n /** Append the \"applies after you exit\" notice (env-load/clear can't mutate the parent shell). */\n sessionEnvNotice?: boolean\n}\n\n/** A palette row \u2014 structurally compatible with the Ink palette's `PaletteItem`, no React dependency. */\nexport interface SessionPaletteItem {\n name: string\n description: string\n group: string\n}\n\nexport interface RunSessionDeps {\n /** Render the palette and resolve to the picked flat command name, or `null` to quit the session. */\n renderPalette: (items: SessionPaletteItem[]) => Promise<string | null>\n /** Map a picked flat name to its spawn/presentation info (from the command catalog). */\n resolveCommand: (name: string) => SessionCommand | undefined\n /** Absolute path to this bundle's `cli.js`, spawned as `process.execPath <cliPath> <...groupPath>`. */\n cliPath: string\n spawn?: typeof nodeSpawn\n now?: () => number\n /** Commit a finished transcript entry (default: stderr, consistent with all repo human output). */\n write?: (text: string) => void\n env?: NodeJS.ProcessEnv\n /** ASCII glyphs for the transcript (default: derived from stdout TTY / TERM). */\n ascii?: boolean\n /** Install real SIGINT/SIGTSTP handlers (default true; tests pass false to stay signal-free). */\n installSignals?: boolean\n}\n\nconst waitForExit = (child: ChildProcess): Promise<{ code: number | null; signal: NodeJS.Signals | null }> => {\n return new Promise((resolve) => {\n child.on('exit', (code, signal) => {\n resolve({ code, signal })\n })\n child.on('error', () => {\n // A spawn that never launched is a failed run, not a cancel.\n resolve({ code: 1, signal: null })\n })\n })\n}\n\n/** Run one picked command as a child and format its transcript entry. */\nconst runOne = async (\n command: SessionCommand,\n deps: Required<Pick<RunSessionDeps, 'spawn' | 'now' | 'env' | 'cliPath' | 'ascii'>>,\n setChildRunning: (running: boolean) => void,\n): Promise<string> => {\n const reportPath = newReportPath()\n const childEnv: NodeJS.ProcessEnv = {\n ...deps.env,\n [SESSION_REPORT_ENV]: reportPath,\n // Every child would otherwise re-run the daily auto-update check and spawn its own detached\n // worker; opt them all out. The parent's own startup check (before the palette) still runs.\n INFRA_KIT_NO_AUTO_UPDATE: '1',\n }\n const start = deps.now()\n\n let result: { code: number | null; signal: NodeJS.Signals | null } = { code: 1, signal: null }\n\n setChildRunning(true)\n try {\n await withAltScreen(\n async () => {\n const child = deps.spawn(process.execPath, [deps.cliPath, ...command.groupPath], {\n stdio: 'inherit',\n env: childEnv,\n })\n\n result = await waitForExit(child)\n },\n { ownsScreen: command.ownsScreen },\n )\n } catch {\n // A synchronous spawn throw (or a rejected run) must degrade to a `failed` transcript entry, never\n // tear the session down. `withAltScreen` already restored the primary buffer in its own finally.\n result = { code: 1, signal: null }\n } finally {\n setChildRunning(false)\n }\n\n const record = readAndUnlinkReport(reportPath)\n const outcome = classifyOutcome(result.code, result.signal, record != null)\n const equivalent = record?.equivalent ?? equivalentLine(`infra-kit ${command.groupPath.join(' ')}`, true)\n\n return formatTranscriptEntry({\n equivalent,\n outcome,\n durationMs: deps.now() - start,\n summary: record?.summary,\n envNotice: command.sessionEnvNotice,\n ascii: deps.ascii,\n })\n}\n\n/**\n * Install the session's state-aware signal owners. While a child runs, SIGINT is swallowed so the\n * tty-delivered signal kills only the child (which installs no SIGINT handler) and the parent\n * survives to render `\u2298 cancelled` and loop; between iterations (cooked mode) SIGINT quits cleanly.\n * SIGTSTP is ignored for the session's lifetime so a Ctrl-Z can never split alt-screen ownership.\n * Returns a cleanup that removes both handlers.\n */\nconst installSessionSignals = (isChildRunning: () => boolean): (() => void) => {\n const onSigint = (): void => {\n if (isChildRunning()) {\n return\n }\n // Cooked between-iterations window: a real Ctrl-C here should end the session (exit 0).\n process.exit(0)\n }\n const onSigtstp = (): void => {\n // Ignore unconditionally: suspending mid-child or mid-palette would strand the alt buffer.\n }\n\n process.on('SIGINT', onSigint)\n process.on('SIGTSTP', onSigtstp)\n\n return () => {\n process.off('SIGINT', onSigint)\n process.off('SIGTSTP', onSigtstp)\n }\n}\n\n/**\n * Pure gate: may bare `infra-kit` engage the persistent session shell? Requires an interactive TTY on\n * both ends, a capable terminal, and no opt-out. Deliberately does NOT depend on `INFRA_KIT_SESSION`\n * (the shell must work without `infra-kit init` having run). Everything else keeps the one-shot path.\n *\n * @example\n * sessionGateEnabled({ TERM: 'xterm' }, { stdoutIsTTY: true, stdinIsTTY: true }) // => true\n */\nexport const sessionGateEnabled = (\n env: NodeJS.ProcessEnv,\n streams: { stdoutIsTTY: boolean; stdinIsTTY: boolean },\n): boolean => {\n return Boolean(\n streams.stdoutIsTTY &&\n streams.stdinIsTTY &&\n env.TERM !== 'dumb' &&\n !env.INFRA_KIT_NO_SESSION &&\n !env.INFRA_KIT_SESSION_REPORT,\n )\n}\n\n/**\n * Run the session loop until the user quits at the palette.\n *\n * @example\n * await runSession(items, { renderPalette, resolveCommand, cliPath: '/g/dist/cli.js' })\n */\nexport const runSession = async (items: SessionPaletteItem[], deps: RunSessionDeps): Promise<void> => {\n const resolved = {\n spawn: deps.spawn ?? nodeSpawn,\n now:\n deps.now ??\n (() => {\n return Date.now()\n }),\n env: deps.env ?? process.env,\n cliPath: deps.cliPath,\n ascii: deps.ascii ?? !(process.stdout.isTTY && process.env.TERM !== 'dumb'),\n }\n const write =\n deps.write ??\n ((text: string) => {\n return process.stderr.write(text)\n })\n\n let childRunning = false\n const cleanupSignals =\n deps.installSignals === false\n ? () => {\n return undefined\n }\n : installSessionSignals(() => {\n return childRunning\n })\n\n try {\n for (;;) {\n const selected = await deps.renderPalette(items)\n\n if (selected == null) {\n return\n }\n\n const command = deps.resolveCommand(selected)\n\n if (!command) {\n continue\n }\n\n const entry = await runOne(command, resolved, (running) => {\n childRunning = running\n })\n\n write(`${entry}\\n`)\n }\n } finally {\n cleanupSignals()\n }\n}\n", "/**\n * Alternate-screen (xterm 1049) wrapper for a session that does NOT already own\n * the screen. Enters the alt buffer before the wrapped work and ALWAYS restores\n * the primary buffer afterwards \u2014 including on a throw, a hard `exit`, or a\n * `SIGTERM` \u2014 so the user is never stranded on a half-drawn alt screen. When the\n * caller already owns the screen (`ownsScreen: true`, e.g. an Ink TUI managing\n * its own buffer), it just runs the work untouched. Human output is stderr by\n * repo convention, so the escape codes go there too.\n */\nimport process from 'node:process'\n\n/** xterm private-mode escape codes for the alternate screen buffer. */\nconst ANSI = {\n /** Switch to the alternate screen buffer. */\n enter: '\\x1B[?1049h',\n /** Restore the primary screen buffer. */\n leave: '\\x1B[?1049l',\n}\n\n/** Injection seams for {@link withAltScreen} \u2014 defaulted for production, overridable in tests. */\nexport interface AltScreenDeps {\n /** Emit a control string. Defaults to writing to stderr. */\n write?: (s: string) => void\n /** Wire the idempotent restore into hard-exit paths. Defaults to `process` `exit`/`SIGTERM`. */\n onExitRestore?: (restore: () => void) => void\n}\n\n/** Default {@link AltScreenDeps.write}: all human/control output goes to stderr. */\nconst defaultWrite = (s: string): void => {\n process.stderr.write(s)\n}\n\n/** Default {@link AltScreenDeps.onExitRestore}: restore the primary buffer on `exit` and `SIGTERM`. */\nconst defaultOnExitRestore = (restore: () => void): void => {\n process.once('exit', restore)\n process.once('SIGTERM', restore)\n}\n\n/**\n * Run `fn` inside the alternate screen buffer unless the caller already owns the\n * screen. Enters before `fn`, restores in a `finally` (so a throw still restores),\n * and registers an idempotent restore on hard-exit paths so a `SIGTERM` never\n * strands the user. When `ownsScreen` is true, `fn` runs with no buffer switch.\n *\n * @example\n * const out: string[] = []\n * await withAltScreen(async () => 'done', { ownsScreen: false }, { write: (s) => out.push(s), onExitRestore: () => {} })\n * // out === ['\\x1b[?1049h', '\\x1b[?1049l']\n */\nexport const withAltScreen = async <T>(\n fn: () => Promise<T>,\n opts: { ownsScreen?: boolean },\n deps?: AltScreenDeps,\n): Promise<T> => {\n if (opts.ownsScreen === true) {\n return await fn()\n }\n\n const write = deps?.write ?? defaultWrite\n const onExitRestore = deps?.onExitRestore ?? defaultOnExitRestore\n let restored = false\n\n const restore = (): void => {\n if (restored) return\n restored = true\n write(ANSI.leave)\n }\n\n onExitRestore(restore)\n write(ANSI.enter)\n try {\n return await fn()\n } finally {\n restore()\n }\n}\n", "/**\n * Pure formatter for one committed transcript block \u2014 the two-line summary a\n * session leaves behind in the scrollback once it finishes. Line 1 echoes the\n * replayable command (`$ ` when it reproduces exactly, `\u2248 ` when it would\n * re-prompt); line 2 is a status glyph + label + duration + optional summary;\n * an optional third line carries the env-scope notice. No I/O \u2014 it returns a\n * string the caller writes wherever it likes.\n */\nimport type { EquivalentLine } from './equivalent'\nimport type { SessionOutcome } from './outcome'\n\n/** All literal, user-facing copy for the transcript entry. */\nconst T = {\n ok: 'ok',\n findingsPlain: 'completed with findings',\n failed: 'failed',\n cancelled: 'cancelled',\n findingsSuffix: 'findings',\n sep: ' \u00B7 ',\n reproPrefix: '$ ',\n nonReproPrefix: '\u2248 ',\n envNotice: 'Applies to your shell after you exit this session.',\n}\n\n/** Status glyphs keyed by outcome, in both a unicode and an ASCII-safe variant. */\nconst GLYPHS: Record<SessionOutcome, { unicode: string; ascii: string }> = {\n ok: { unicode: '\u2713', ascii: '[ok]' },\n findings: { unicode: '\u26A0', ascii: '[!]' },\n failed: { unicode: '\u2717', ascii: '[x]' },\n cancelled: { unicode: '\u2298', ascii: '[-]' },\n}\n\n/** Everything the formatter needs to render one transcript block. */\nexport interface TranscriptEntryInput {\n /** The replayable command line + whether it reproduces the selection. */\n equivalent: EquivalentLine\n /** Terminal state of the session. */\n outcome: SessionOutcome\n /** Wall-clock duration in milliseconds. */\n durationMs: number\n /** Optional short summary lines; the first is appended to the status line. */\n summary?: string[]\n /** Finding count, used to label a `findings` outcome as `${n} findings`. */\n findingsCount?: number\n /** Append the \"applies to your shell after you exit\" env notice as a third line. */\n envNotice?: boolean\n /** Use ASCII glyphs instead of unicode (for `!isTTY` / `TERM=dumb`). */\n ascii?: boolean\n}\n\n/** Format a duration compactly: sub-second as `820ms`, otherwise `4.2s`. */\nconst formatDuration = (durationMs: number): string => {\n if (durationMs < 1000) return `${Math.round(durationMs)}ms`\n\n return `${(durationMs / 1000).toFixed(1)}s`\n}\n\n/** The human label for an outcome; `findings` folds in the count when present. */\nconst labelFor = (outcome: SessionOutcome, findingsCount?: number): string => {\n if (outcome === 'findings') {\n return findingsCount != null ? `${findingsCount} ${T.findingsSuffix}` : T.findingsPlain\n }\n\n return { ok: T.ok, failed: T.failed, cancelled: T.cancelled }[outcome]\n}\n\n/**\n * Render a committed transcript block from a finished session. Line 1 echoes the\n * equivalent command; line 2 is `<glyph> <label> \u00B7 <duration>[ \u00B7 <summary>]`; an\n * optional third dim line carries the env notice.\n *\n * @example\n * formatTranscriptEntry({\n * equivalent: { line: 'infra-kit vendor check', reproducible: true },\n * outcome: 'ok', durationMs: 4200,\n * })\n * // '$ infra-kit vendor check\\n\u2713 ok \u00B7 4.2s'\n */\nexport const formatTranscriptEntry = (input: TranscriptEntryInput): string => {\n const prefix = input.equivalent.reproducible ? T.reproPrefix : T.nonReproPrefix\n const glyph = input.ascii === true ? GLYPHS[input.outcome].ascii : GLYPHS[input.outcome].unicode\n const label = labelFor(input.outcome, input.findingsCount)\n\n const statusParts = [label, formatDuration(input.durationMs)]\n const firstSummary = input.summary?.[0]\n\n if (firstSummary != null && firstSummary.length > 0) statusParts.push(firstSummary)\n\n const lines = [`${prefix}${input.equivalent.line}`, `${glyph} ${statusParts.join(T.sep)}`]\n\n if (input.envNotice === true) lines.push(T.envNotice)\n\n return lines.join('\\n')\n}\n", "/**\n * Pure classifier that turns a finished session's exit signals into a single\n * {@link SessionOutcome}. Report-presence is decided FIRST: a written report means\n * the command produced a verdict, so a late teardown signal (e.g. SIGINT during\n * cleanup after the report was flushed) is still a success. Only when NO report\n * exists does the signal/exit code decide between a user cancellation and a\n * genuine failure. Side-effect-free so the mapping is fully unit-testable.\n */\n\n/** The four terminal states a session can settle into. */\nexport type SessionOutcome = 'ok' | 'findings' | 'failed' | 'cancelled'\n\n/**\n * Classify a finished session. When a report was written, the run completed:\n * exit 0 (or any teardown signal) is `ok`, a non-zero exit is `findings`. When no\n * report was written, a `SIGINT` or clean exit is a user `cancelled`, anything\n * else is `failed`.\n *\n * @example\n * classifyOutcome(0, null, true) // => 'ok'\n * classifyOutcome(1, null, true) // => 'findings'\n * classifyOutcome(null, 'SIGINT', true) // => 'ok' (teardown after the report was written)\n * classifyOutcome(1, null, false) // => 'failed'\n * classifyOutcome(0, null, false) // => 'cancelled'\n */\nexport const classifyOutcome = (\n exitCode: number | null,\n signal: NodeJS.Signals | null,\n reportPresent: boolean,\n): SessionOutcome => {\n if (reportPresent) {\n return signal != null || exitCode === 0 ? 'ok' : 'findings'\n }\n\n return signal === 'SIGINT' || exitCode === 0 ? 'cancelled' : 'failed'\n}\n", "/**\n * The PARENT half of the auto-update: it never fetches, never installs, and never blocks.\n *\n * All it does is (a) surface a notice the background child left behind for installs we may not touch\n * ourselves, and (b) hand off to a detached child when the throttle window has elapsed. Everything\n * expensive or mutating happens in `src/entry/update-check.ts`, after this process has exited.\n */\nimport { spawn } from 'node:child_process'\nimport fs from 'node:fs'\nimport process from 'node:process'\nimport { fileURLToPath } from 'node:url'\n\nimport { safeRealpath } from 'src/lib/install-manager'\nimport { logger } from 'src/lib/logger'\n\nimport { autoUpdateSkipReason } from './guards'\nimport { isNewerVersion } from './semver'\nimport { isStale, readUpdateCache } from './update-cache'\nimport type { UpdateCache } from './update-cache'\n\nexport interface AutoUpdateDeps {\n argv?: string[]\n env?: NodeJS.ProcessEnv\n isTty?: boolean\n cwd?: string\n nowMs?: number\n /** Realpath of the running `dist/cli.js`. */\n selfRealPath?: string\n /** Absolute path to the `dist/update-check.js` sibling bundle. */\n childPath?: string\n readCache?: () => UpdateCache | null\n spawnChild?: (childPath: string) => void\n notify?: (line: string) => void\n fileExists?: (p: string) => boolean\n}\n\n/**\n * The refresh runs as `node dist/update-check.js --parent-pid <pid>`: a DEDICATED entry, never\n * `cli.js`. Re-invoking `cli.js` would re-run this very function (plus commander, the catalog, and\n * every top-level side effect) in the child. The `mcp` command already spawns a sibling bundle for the\n * same reason.\n *\n * `detached` + `unref` + `stdio: 'ignore'` is what lets the child outlive us: a `ik version` exits in\n * ~50ms, far sooner than any registry round-trip. `windowsHide` keeps a console window from flashing.\n */\nconst defaultSpawnChild = (childPath: string): void => {\n const child = spawn(process.execPath, [childPath, '--parent-pid', String(process.pid)], {\n detached: true,\n stdio: 'ignore',\n windowsHide: true,\n })\n\n child.unref()\n}\n\n/** `dist/update-check.js`, resolved beside this bundle exactly as `mcp.ts` resolves `dist/mcp.js`. */\nconst defaultChildPath = (): string => {\n return fileURLToPath(new URL('./update-check.js', import.meta.url))\n}\n\nconst defaultSelfRealPath = (): string => {\n return fs.realpathSync(fileURLToPath(new URL('./cli.js', import.meta.url)))\n}\n\n/**\n * The cache is never executed \u2014 only printed \u2014 but it is still attacker-shaped input if anything with\n * the user's uid can write it. An unconstrained token could carry ANSI escapes that rewrite the terminal\n * around the \"Run: \u2026\" line, turning it into a convincing copy-paste trap. Package-manager argv is drawn\n * from a static table, so a conservative charset costs nothing and closes that.\n */\nconst SAFE_COMMAND_TOKEN = /^[\\w@./+-]+$/i\n\nconst isSafeCommandToken = (token: string): boolean => {\n return SAFE_COMMAND_TOKEN.test(token)\n}\n\n/**\n * Print the pending notice for installs the worker was not allowed to touch (Homebrew, or a location it\n * could not identify). Those users get the one command that will actually work; running a guessed global\n * install on their behalf is worse than printing one.\n *\n * The verdict is READ from the cache, never recomputed: `detectInstallManager` may shell out to\n * `npm root -g`, and the CLI startup path must never pay for a subprocess. A null `updateCommand` means\n * the worker either installs it silently after we exit, or there is nothing to do.\n *\n * `logger` is pino with `destination: 2` \u2014 stderr. Nothing here may reach stdout.\n */\nconst notifyIfUninstallable = (\n cache: UpdateCache | null,\n currentVersion: string,\n notify: (line: string) => void,\n): void => {\n if (!cache?.latestVersion || !cache.updateCommand) return\n if (!isNewerVersion(cache.latestVersion, currentVersion)) return\n if (!cache.updateCommand.every(isSafeCommandToken)) return\n\n notify(\n `infra-kit ${cache.latestVersion} is available (you have ${currentVersion}). Run: ${cache.updateCommand.join(' ')}`,\n )\n}\n\n/**\n * Best-effort. Advisory and background only: it must never throw, never block, and never change the\n * exit code \u2014 a failure to update is not a failure of the command the user actually ran.\n *\n * @example\n * maybeAutoUpdate('0.1.130')\n */\nexport const maybeAutoUpdate = (currentVersion: string, deps: AutoUpdateDeps = {}): void => {\n try {\n const argv = deps.argv ?? process.argv\n const env = deps.env ?? process.env\n const isTty = deps.isTty ?? Boolean(process.stdout.isTTY)\n const cwd = deps.cwd ?? process.cwd()\n const nowMs = deps.nowMs ?? Date.now()\n const selfRealPath = deps.selfRealPath ?? defaultSelfRealPath()\n const readCache = deps.readCache ?? readUpdateCache\n const spawnChild = deps.spawnChild ?? defaultSpawnChild\n const fileExists =\n deps.fileExists ??\n ((p: string) => {\n return fs.existsSync(p)\n })\n const notify =\n deps.notify ??\n ((line: string) => {\n logger.info(line)\n })\n\n if (autoUpdateSkipReason({ argv, env, isTty, selfRealPath, cwd, realpath: safeRealpath }) !== null) return\n\n const cache = readCache()\n\n notifyIfUninstallable(cache, currentVersion, notify)\n\n if (!isStale(cache, nowMs)) return\n\n const childPath = deps.childPath ?? defaultChildPath()\n\n // Absent when running from source (tsx/vitest) rather than a built `dist/`.\n if (!fileExists(childPath)) return\n\n spawnChild(childPath)\n } catch {\n // Advisory only \u2014 see the module doc.\n }\n}\n", "/**\n * Whether this invocation may run the background auto-update at all. Pure: every signal is injected,\n * so the whole matrix is table-tested without spawning or touching argv/env globals.\n */\nimport { isLocalNodeModulesInstall } from 'src/lib/install-manager'\nimport type { RealpathFn } from 'src/lib/install-manager'\n\n/** Set any of these to disable the auto-update entirely. `NO_UPDATE_NOTIFIER`/`CI` are ecosystem convention. */\nexport const OPT_OUT_ENV_VARS = ['INFRA_KIT_NO_AUTO_UPDATE', 'NO_UPDATE_NOTIFIER', 'CI'] as const\n\nexport interface AutoUpdateGuardInput {\n /** Full `process.argv`. */\n argv: string[]\n env: NodeJS.ProcessEnv\n /** `process.stdout.isTTY`. */\n isTty: boolean\n /** Realpath of the running `dist/cli.js`. */\n selfRealPath: string\n cwd: string\n realpath: RealpathFn\n}\n\n/**\n * Reasons to skip, in the order checked. Returned (rather than a bare boolean) so tests assert WHICH\n * guard fired \u2014 a test that only sees `true` cannot tell a working `mcp` guard from a working `CI` one.\n */\nexport type SkipReason = 'opt-out' | 'json' | 'own-command' | 'not-a-tty' | 'local-install'\n\n/**\n * Commands that must never trigger the background updater.\n *\n * `mcp` hands its stdio to a child speaking JSON-RPC. `self-update` already performs the update itself:\n * without this, `ik self-update` would install interactively AND leave behind a detached worker that\n * waits for it to exit and then installs the very same `@latest` a second time.\n */\nconst SELF_MANAGING_COMMANDS = new Set(['mcp', 'self-update'])\n\n/**\n * Positional `argv[2]` mirrors the existing `warnIfLocalInstall` guard and is correct today because the\n * program registers no global options before the subcommand. It is defence-in-depth regardless: the\n * notice goes to stderr, and the `isTty` guard already rejects the MCP server (its stdout is a pipe).\n *\n * NOTE: adding a global `program.option()` would let a flag occupy argv[2] and silently defeat this arm.\n * The stderr + isTty arms are what actually guarantee framing safety.\n */\nconst isSelfManagingCommand = (argv: string[]): boolean => {\n return argv[2] != null && SELF_MANAGING_COMMANDS.has(argv[2])\n}\n\n/**\n * Why this invocation must not auto-update, or null when it may.\n *\n * `local-install` is the subtle one: a repo that pins infra-kit in its `devDependencies` has deliberately\n * chosen a version. Silently upgrading the user's GLOBAL install because their project-local copy is old\n * would be both useless (the project keeps using its pinned copy) and rude.\n *\n * @example\n * autoUpdateSkipReason({ argv: ['node', 'cli.js', 'mcp'], env: {}, isTty: true, selfRealPath: '/g/cli.js', cwd: '/p', realpath: (p) => p })\n * // => 'own-command'\n */\nexport const autoUpdateSkipReason = (input: AutoUpdateGuardInput): SkipReason | null => {\n const { argv, env, isTty, selfRealPath, cwd, realpath } = input\n\n const optedOut = OPT_OUT_ENV_VARS.some((name) => {\n const value = env[name]\n\n return value != null && value !== ''\n })\n\n if (optedOut) return 'opt-out'\n if (argv.includes('--json')) return 'json'\n if (isSelfManagingCommand(argv)) return 'own-command'\n // Never nag or mutate for piped/scripted runs; a human must be present to see the outcome.\n if (!isTty) return 'not-a-tty'\n if (isLocalNodeModulesInstall(selfRealPath, cwd, realpath)) return 'local-install'\n\n return null\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["select", "Separator", "realpathSync", "process", "fileURLToPath", "formatAlignedRows", "rows", "gap", "width", "max", "left", "right", "nodeSpawn", "process", "process", "ANSI", "defaultWrite", "s", "defaultOnExitRestore", "restore", "withAltScreen", "fn", "opts", "deps", "write", "onExitRestore", "restored", "T", "GLYPHS", "formatDuration", "durationMs", "labelFor", "outcome", "findingsCount", "formatTranscriptEntry", "input", "prefix", "glyph", "statusParts", "firstSummary", "lines", "classifyOutcome", "exitCode", "signal", "reportPresent", "waitForExit", "child", "resolve", "code", "signal", "runOne", "command", "deps", "setChildRunning", "reportPath", "newReportPath", "childEnv", "SESSION_REPORT_ENV", "start", "result", "withAltScreen", "process", "record", "readAndUnlinkReport", "outcome", "classifyOutcome", "equivalent", "equivalentLine", "formatTranscriptEntry", "installSessionSignals", "isChildRunning", "onSigint", "onSigtstp", "sessionGateEnabled", "env", "streams", "runSession", "items", "resolved", "nodeSpawn", "write", "text", "childRunning", "cleanupSignals", "selected", "entry", "running", "spawn", "fs", "process", "fileURLToPath", "OPT_OUT_ENV_VARS", "SELF_MANAGING_COMMANDS", "isSelfManagingCommand", "argv", "autoUpdateSkipReason", "input", "env", "isTty", "selfRealPath", "cwd", "realpath", "name", "value", "isLocalNodeModulesInstall", "defaultSpawnChild", "childPath", "spawn", "process", "defaultChildPath", "fileURLToPath", "defaultSelfRealPath", "fs", "SAFE_COMMAND_TOKEN", "isSafeCommandToken", "token", "notifyIfUninstallable", "cache", "currentVersion", "notify", "isNewerVersion", "maybeAutoUpdate", "deps", "argv", "env", "isTty", "cwd", "nowMs", "selfRealPath", "readCache", "readUpdateCache", "spawnChild", "fileExists", "p", "line", "logger", "autoUpdateSkipReason", "safeRealpath", "isStale", "captureSessionReportPath", "program", "buildProgram", "runProgram", "argv", "error", "isPromptCancellation", "logger", "process", "message", "warnIfLocalInstall", "isLocalNodeModulesInstall", "realpathSync", "fileURLToPath", "safeRealpath", "maybeAutoUpdate", "package_default", "commandMapByName", "cmd", "buildPaletteItems", "commandMap", "getMenuGroupCommands", "label", "names", "name", "runInteractiveMenu", "releaseCommands", "worktreeCommands", "envCommands", "paletteItems", "selected", "runCommandPalette", "alignedLabels", "formatAlignedRows", "item", "labelByName", "index", "toChoices", "select", "Separator", "runSessionShell", "cliPath", "catalogByName", "commandCatalog", "entry", "runSession", "streams", "sessionGateEnabled", "invokedViaMenu"]
7
7
  }
@@ -1,18 +1,22 @@
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{b as V,c as Pe}from"./chunk-C2HRONYA.js";import{d as ge,e as fe,f as ve,i as ye}from"./chunk-6YAKVQZN.js";import{A as ae,B as A,C as le,D as pe,E as ue,G as ce,H as de,K as I,L as N,M as w,S as O,v as S,w as k,x as L,y as se,z as $}from"./chunk-X2L4F2VM.js";import{a as G}from"./chunk-6FU2TRU5.js";import{d as me,m as he}from"./chunk-CHETBZ6M.js";import{a as H,b as j,c as U}from"./chunk-LTKR3F4N.js";import{Command as Lt}from"commander";import x from"node:process";import{pathToFileURL as $t}from"node:url";import*as Ee from"node:path";import vr from"node:process";var er=t=>({pane:{surfaces:[{type:"terminal",command:t}]}}),J=(t,e)=>{if(t.length===1)return er(t[0]);let r=Math.ceil(t.length/2),n=t.slice(0,r),i=t.slice(r);return{direction:e%2===0?"horizontal":"vertical",split:Math.round(r/t.length*100)/100,children:[J(n,e+1),J(i,e+1)]}},we=t=>{if(t.length===0)throw new Error("buildCmuxLayout: at least one command is required");return J(t,0)};import{constants as ur}from"node:os";import Y from"node:process";import{execFileSync as rr}from"node:child_process";import E from"node:process";var tr=5e3,nr=100,or=2e3,ir="/bin/ps",sr=t=>t.split(`
2
+ `).flatMap(e=>{let[r,n,i]=e.trim().split(/\s+/);if(r==null||n==null||i==null)return[];let o=Number(r),s=Number(n),a=Number(i);return!Number.isInteger(o)||!Number.isInteger(s)||!Number.isInteger(a)?[]:[{pid:o,ppid:s,pgid:a}]}),ar=(t,e,r)=>{let n=new Map;for(let a of e){let l=n.get(a.ppid);l?l.push(a):n.set(a.ppid,[a])}let i=new Set([t]),o=new Set([t]),s=[t];for(let a=0;a<s.length;a+=1)for(let l of n.get(s[a]??-1)??[])o.has(l.pid)||(o.add(l.pid),i.add(l.pgid),s.push(l.pid));return[...i].filter(a=>a>1&&a!==r)},K=()=>{try{return sr(rr(ir,["-eo","pid=,ppid=,pgid="],{encoding:"utf8",maxBuffer:8<<20}))}catch{return[]}},lr=(t,e,r)=>r.some(n=>n.pid===t&&n.ppid===e),z=(t,e)=>{let r=e.find(n=>n.pid===E.pid)?.pgid;return ar(t,e,r)},De=t=>{try{return E.kill(-t,0),!0}catch{return!1}},q=(t,e)=>{for(let r of t)try{E.kill(-r,e)}catch{}},xe=()=>{q(z(E.pid,K()),"SIGKILL")},pr=async t=>new Promise(e=>{setTimeout(e,t)}),be=async(t,e)=>{let r=Date.now()+e,n=t;for(;n.length>0&&Date.now()<r&&(n=n.filter(De),n.length!==0);)await pr(nr);return n};function _(t,e=tr,r){t.unref();let n=!1,i=o=>{n||r?.(o)};return t.on("exit",(o,s)=>{i(`exited unexpectedly (code=${o??"null"}, signal=${s??"null"})`)}),t.on("error",o=>{i(`failed to spawn: ${o.message}`)}),{kill:async()=>{n=!0;let o=t.pid;if(o==null||t.exitCode!==null||t.signalCode!==null)return;let s=K();if(s.length>0&&!lr(o,E.pid,s))return;let a=z(o,s);q(a,"SIGTERM");let l=await be(a,e);if(l.length===0)return;let u=z(o,K()),c=[...new Set([...l,...u])].filter(De);q(c,"SIGKILL"),await be(c,or)}}}var cr=["SIGINT","SIGTERM"],dr=ur.signals,mr=2,Se=t=>128+(dr[t]??mr),hr=t=>{Y.exit(t)},gr=(t,e)=>{Y.on(t,e)},Ae=t=>{try{Y.stderr.write(t)}catch{}},fr=(t,e)=>{let r=e instanceof Error?`${e.message}
3
+ ${e.stack??"(no stack)"}`:String(e);Ae(`
4
+ \u2717 Teardown failed while handling ${t}: ${r}
5
+ `)},F=({onSignal:t,exit:e=hr,register:r=gr,forceReap:n=xe})=>{let i=null,o=s=>{if(i!==null){if(s!==i)return;Ae(`
6
+ \u26A0 Received ${s} again \u2014 force-quitting.
7
+ `),n(),e(Se(s));return}i=s,(async()=>{try{await t(s)}catch(a){fr(s,a)}finally{e(Se(s))}})()};for(let s of cr)r(s,()=>o(s))};var yr=(t,e)=>t.map(({app:r,targets:n})=>`pnpm exec infra-kit dev ${n&&n.length>0?`--target=${n.join(",")}`:`--app=${r}`}${e?" --watch":""}`),Pr=t=>{let e=new Map;for(let r of Object.keys(t?.apps??{})){let n=r.split("/")[0];n===void 0||n==="*"||e.set(n,[...e.get(n)??[],r])}return e},wr=(t,e)=>{let r=L(t);return e?r.filter(n=>e.includes(n.name)):r},br=(t,e)=>{S.info(`\u{1F9E9} Opened cmux dev workspace ${e} with ${t.length} pane(s):`);for(let r of t)S.info(` \u2022 ${r.name} (infra-kit dev --app=${r.name})`)},Dr=t=>{F({onSignal:async e=>{S.info(`
8
+ Received ${e}, closing cmux dev workspace ${t}...`),await ve(t)}})},Re=async t=>{let e=k(vr.cwd()),r=wr(e,$(t.include));if(r.length===0){S.warn("No API apps found to run");return}let n=Pr(t.presetDef),i=yr(r.map(u=>({app:u.name,targets:n.get(u.name)})),t.watch??!1),o=we(i),s=`${Ee.basename(e)} dev`,a=await fe({cwd:e,title:s,layout:o});br(r,a),Dr(a);let l=setInterval(()=>{l.refresh()},2**30);await new Promise(()=>{})};import Ce from"node:process";var xr=t=>{try{Ce.stderr.write(t)}catch{}},Sr=(t,e)=>{let r=e instanceof Error?`${e.message}
9
+ ${e.stack??"(no stack)"}`:String(e);xr(`
10
+ \u26A0\uFE0F ${t}: ${r}
11
+ dev-server kept alive (likely a bug in a handler's async path); restart if it misbehaves.
12
+ `)},Ar=(t,e)=>{Ce.on(t,e)},Te=({onFault:t=Sr,register:e=Ar}={})=>{let r=n=>i=>{t(n,i)};e("uncaughtException",r("uncaughtException")),e("unhandledRejection",r("unhandledRejection"))};import yt from"chokidar";import{exec as Pt,execFileSync as wt}from"node:child_process";import*as m from"node:fs";import bt from"node:net";import Dt from"node:os";import*as g from"node:path";import d from"node:process";import xt from"node:util";import{execFile as Er}from"node:child_process";import*as ke from"node:fs";import*as M from"node:path";import{promisify as Rr}from"node:util";var Cr=Rr(Er),Tr=t=>{try{let e=JSON.parse(ke.readFileSync(M.join(t,"package.json"),"utf-8"));return typeof e.name=="string"?e.name:void 0}catch{return}},kr=t=>async e=>{let{stdout:r}=await Cr("pnpm",["exec","turbo","run","build","--dry=json",`--filter=...${e}`],{cwd:t,maxBuffer:33554432}),i=(JSON.parse(r).tasks??[]).map(o=>o.package).filter(o=>typeof o=="string");return[...new Set(i)]},Lr=t=>{let e=new Map,r=new Map;for(let n of A(t)){let i=Tr(M.dirname(n));i!==void 0&&(e.set(n,i),r.set(i,n))}return{packageNameByDir:e,dirByName:r}},Le=async(t,e,r=kr(t))=>{let{packageNameByDir:n,dirByName:i}=Lr(t),o=new Map,s=await Promise.all(e.map(async a=>({app:a,closure:await r(a.packageName)})));for(let{app:a,closure:l}of s)for(let u of l){let c=i.get(u);if(c===void 0)continue;let y=o.get(c)??new Set;y.add(a.name),o.set(c,y)}return{dependentsByPackageDir:o,packageNameByDir:n}},$e=t=>`__pkg__:${t}`,Ie=(t,e,r)=>{if(e===null||r===void 0)return null;let n=e.dependentsByPackageDir.get(r)??new Set;return t.filter(i=>i.watchDeps&&n.has(i.name))};var $r="/api/v1";function Ne(t){if(t==null||t==="")return;let e=parseInt(t.trim().replace(/^["']|["']$/g,""),10);return Number.isNaN(e)?void 0:e}function Oe(t,e,r){let i=`${t.replace(/-/g,"_").toUpperCase()}_PORT`,o=Ne(e[i]);if(o!=null)return o;let s=Ne(e.PORT);return s??r[t]?.port??void 0}function Ue(t,e){return e[t]?.prefixUrl??$r}function _e(t){let e=t.map(i=>i.port),r=e.filter((i,o)=>e.indexOf(i)!==o),n=t.filter(i=>r.includes(i.port));return{duplicatePorts:r,conflictingApps:n}}import{execFile as Ir,spawn as Nr}from"node:child_process";import{existsSync as Or,readFileSync as Z}from"node:fs";import Ur from"node:net";import{homedir as _r}from"node:os";import{dirname as X,join as R}from"node:path";import D from"node:process";import{fileURLToPath as Fr}from"node:url";import{promisify as Mr}from"node:util";var Br=Mr(Ir),Wr=t=>{let e=JSON.parse(Z(t,"utf-8")),r=typeof e.bin=="string"?e.bin:e.bin?.portless;return r==null||r===""?null:r},Hr=()=>{try{let t=X(Fr(import.meta.url));for(;;){let e=R(t,"node_modules","portless","package.json");if(Or(e)){let n=Wr(e);return n==null?null:R(X(e),n)}let r=X(t);if(r===t)return null;t=r}}catch{return null}},Q,Fe=()=>(Q===void 0&&(Q=Hr()),Q),jr=1500,Gr=10,Vr=200,Jr=500,Kr=t=>new Promise(e=>{let r=Ur.connect({host:"127.0.0.1",port:t}),n=i=>{r.destroy(),e(i)};r.setTimeout(Jr),r.once("connect",()=>{n(!0)}),r.once("timeout",()=>{n(!1)}),r.once("error",()=>{n(!1)})}),zr=async(t,{timeoutMs:e})=>{let r=Fe();if(r==null)throw new Error("portless is not installed (not resolvable from node_modules)");await Br(D.execPath,[r,...t],{signal:AbortSignal.timeout(e),encoding:"utf-8",env:G(D.env)})},qr=t=>{let e=Fe();if(e==null)return;Nr(D.execPath,[e,...t],{detached:!0,stdio:"ignore",env:G(D.env)}).unref()},Yr=t=>new Promise(e=>{setTimeout(e,t)}),Xr=()=>D.env.PORTLESS_STATE_DIR??R(_r(),".portless"),Qr=t=>{try{let e=Xr();if(Number(Z(R(e,"proxy.port"),"utf-8").trim())!==t)return!1;let r=Number(Z(R(e,"proxy.pid"),"utf-8").trim());if(!Number.isInteger(r)||r<=0)return!1;try{D.kill(r,0)}catch(n){return n.code==="EPERM"}return!0}catch{return!1}},Me=(t={})=>{let e=t.run??zr,r=t.spawnDaemon??qr,n=t.isListening??Kr,i=t.isPortlessOwned??Qr,o=t.timeoutMs??jr,s=null,a=async p=>{try{return await e(p,{timeoutMs:o}),!0}catch{return!1}},l=async()=>(s===null&&(s=await a(["--version"])),s);return{isAvailable:l,ensureDaemon:async p=>{if(!await l())return!1;if(await n(p))return i(p);if(p<N)return!1;r(["proxy","start","--no-tls","-p",String(p)]);for(let f=0;f<Gr;f+=1)if(await Yr(Vr),await n(p)&&i(p))return!0;return!1},registerAlias:async(p,f)=>await l()?a(["alias",p,String(f)]):!1,removeAlias:async p=>{await l()&&await a(["alias","--remove",p])}}};import{Logger as Zr}from"@aws-lambda-powertools/logger";import et from"fastify";import*as We from"node:fs";import*as C from"node:path";import ee from"node:process";import{pathToFileURL as rt}from"node:url";import{parse as tt}from"yaml";var nt=t=>t?.code==="EADDRINUSE",ot=3e4,it=()=>{let t=Number.parseInt(ee.env.DEV_SERVER_TIMEOUT_MS??"",10);return Number.isNaN(t)?ot:t},Be=()=>ee.env.DEV_SERVER_REQUEST_LOG==="1",B=class{constructor(e){this.serverConfig=e;this.importCacheBust=`${Date.now()}`,this.logger=new Zr({serviceName:"LocalServer",logLevel:"DEBUG"}),this.serverConfig.prefixUrl=this.serverConfig.prefixUrl??"",this.server=et({logger:!1}),this.server.addHook("onRequest",async(n,i)=>{i.header("Access-Control-Allow-Origin","*"),i.header("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),i.header("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),n.method==="OPTIONS"&&i.status(204).send()});let r=this.serverConfig.onRequestLog;(Be()||r)&&this.server.addHook("onResponse",async(n,i)=>{let o=Math.round(i.elapsedTime),s=n.url.split("?")[0]??n.url;r?.({method:n.method,path:s,status:i.statusCode,ms:o}),Be()&&ee.stdout.write(`${n.method} ${n.url} \u2192 ${i.statusCode} ${o}ms
13
+ `)})}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(!nt(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=C.join(this.serverConfig.controllersPath,"serverless.yml"),r=We.readFileSync(e,"utf8"),n=tt(r),i=[];if(!n?.functions)return i;for(let o of Object.values(n.functions))if(o?.events?.length)for(let s of o.events){let a=s?.http;a&&i.push(this.defineRoute(a,o))}return i}async defineRoute(e,r){let n=e.path.toString();n=n.replaceAll("{",":").replaceAll("}","");let i=C.posix.join(this.serverConfig.prefixUrl??"",n);i=i[0]==="/"?i:`/${i}`;let o=["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"],s=String(e.method).toUpperCase();if(!o.includes(s))throw new Error(`Invalid HTTP method: "${e.method}" for URL: ${i}`);let a=`${s} ${i}`;if(this.registeredRouteKeys.has(a))throw new Error(`Duplicate route: ${a}`);this.registeredRouteKeys.add(a);let u=(r.handler??"").split("."),c=u[0]??"",y=u[1]??"",p=C.join(this.serverConfig.controllersPath,`${c}.js`),f=rt(p);f.searchParams.set("v",this.importCacheBust);let h=await import(f.href);this.controllers[a]={action:h,handler:y};let P=this.logger.createChild({serviceName:"RequestLogger"});this.server.route({method:s,url:i,handler:async(b,oe)=>{let T=this.controllers[a];if(!T)throw new Error(`No controller for ${a}`);let ie=T.action[T.handler];if(!ie)throw new Error(`No handler ${T.handler} for ${i}`);let W=await ie(this.getEventObj(b.body,b.query,b.params,b.headers,s,i),this.getContext(),P),Ze=JSON.parse(W.body);return oe.headers(W.headers??{}),oe.code(W?.statusCode??500).send(Ze)}})}getEventObj(e,r,n,i,o="",s=""){let a={body:e?JSON.stringify(e):null,headers:i??{},multiValueHeaders:{},httpMethod:o,isBase64Encoded:!1,path:s,pathParameters:n??null,queryStringParameters:r??null,multiValueQueryStringParameters:null,stageVariables:null,requestContext:{accountId:"",apiId:"",authorizer:void 0,protocol:"",httpMethod:o,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=it(),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:(i,o)=>{},fail:i=>{},succeed:i=>{}}}};import{spawn as st}from"node:child_process";import*as He from"node:fs";var at=(t,e)=>[...t.map(r=>`--filter=...${r}`),...e.map(r=>`--filter=${r}^...`)],je=({depInclusive:t,depClosure:e,cwd:r,logFile:n,onUnexpectedExit:i})=>{let o=at(t,e),s=He.openSync(n,"a"),a=st("pnpm",["exec","turbo","watch","build",...o,"--continue=dependencies-successful","--env-mode=loose"],{cwd:r,detached:!0,stdio:["ignore",s,s]});return _(a,void 0,i)};import{spawn as lt}from"node:child_process";import pt from"node:process";var ut=/\u001B\][\s\S]*?(?:\u0007|\u001B\\)/g,ct=/\u001B\[[0-9;?]*[\u0020-\u002F]*[\u0040-\u007E]/g,dt=/[\u0000-\u0008\u000B-\u001F\u007F]/g,mt=t=>t.replace(ut,"").replace(ct,"").replace(dt,""),ht=/^([^\s:]+):dev:[ \t]?/,gt=t=>/^cache (?:bypass|hit|miss)/.test(t)||t.startsWith("$ "),ft=t=>{let e=mt(t).trimEnd(),r=ht.exec(e);if(r==null)return null;let n=e.slice(r[0].length);return n.trim()===""||gt(n.trim())?null:{pkg:r[1],text:n}},vt=64*1024,Ge=(t,e)=>{if(t==null)return;let r="",n=i=>{let o=ft(i);o!=null&&e.onLine?.(o)};t.setEncoding("utf-8"),t.on("data",i=>{e.appendLog?.(i),r+=i;let o=r.split(`
14
+ `);r=o.pop()??"";for(let s of o)n(s);r.length>vt&&(n(r),r="")}),t.on("end",()=>{r!==""&&(n(r),r="")}),t.on("error",i=>{e.appendLog?.(`[infra-kit] ui dev stream error: ${i.message}
15
+ `)})},Ve=({packageNames:t,cwd:e,concurrency:r,env:n,appendLog:i,onLine:o,onUnexpectedExit:s})=>{let a=t.map(u=>`--filter=${u}`),l=lt("pnpm",["exec","turbo","run","dev",...a,`--concurrency=${r}`,"--env-mode=loose","--only","--output-logs=new-only","--no-update-notifier","--ui=stream"],{cwd:e,detached:!0,stdio:["ignore","pipe","pipe"],env:{...pt.env,...n}});return Ge(l.stdout,{appendLog:i,onLine:o}),Ge(l.stderr,{appendLog:i,onLine:o}),_(l,void 0,s)};var v=ze();function ze(){return g.join(he(),d.env[me]??"no-session","logs.txt")}function St(){v=ze(),m.mkdirSync(g.dirname(v),{recursive:!0}),m.writeFileSync(v,`=== Dev Server Started: ${new Date().toISOString()} ===
4
16
 
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(`
16
- 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};
17
+ `)}function Je(t){let e=Dt.homedir();return t===e||t.startsWith(`${e}${g.sep}`)?`~${t.slice(e.length)}`:t}function Ke(t){return t.map(e=>e.packageName).join(", ")}var At=xt.promisify(Pt),Et=async(t,e)=>{try{let{stderr:r}=await At(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 qe(t){m.appendFileSync(v,t)}var Rt=()=>new Promise((t,e)=>{let r=bt.createServer();r.unref(),r.on("error",e),r.listen(0,"127.0.0.1",()=>{let n=r.address(),i=typeof n=="object"&&n!==null?n.port:0;r.close(()=>t(i))})}),Ct=async t=>{try{return(await fetch(t,{signal:AbortSignal.timeout(1500)})).ok}catch{return!1}},re=t=>{try{let e=wt("git",["rev-parse","--abbrev-ref","HEAD"],{cwd:t,encoding:"utf-8"}).trim(),r=j(e);return r===""?U:r}catch{return U}},Tt=t=>{let r=(t instanceof Error?t.message:String(t)).split(`
18
+ `)[0]?.trim()??"";return r===""?"unknown error (see the log)":r},te=class t{monorepoRoot;devContextDir;appServers=[];watchDebounceTimers=new Map;watcher=null;static WATCH_DEBOUNCE_MS=400;restartWorkChain=Promise.resolve();static PORT_RELEASE_DELAY_MS=200;livenessTimer=null;livenessFailures=new Map;static LIVENESS_INTERVAL_MS=5e3;static LIVENESS_FAILURE_THRESHOLD=2;options;runBuild;turboWatchFactory;turboWatch=null;uiDevFactory;uiDev=null;uiLogStream=null;dryRunner;failedApps=[];renderer;healthProbe;proxy;proxyPort;registeredAliases=new Set;uiPortMap={};shuttingDown=!1;closureMap=null;closureBuild=Promise.resolve();constructor(e={},r=Et,n=je,i=Ve,o,s,a=Ct,l=Me()){this.options=e,this.runBuild=r,this.turboWatchFactory=n,this.uiDevFactory=i,this.dryRunner=o,this.proxy=l,this.proxyPort=e.proxyPort??I,St(),this.renderer=s??new Pe({appendLog:qe,verbose:this.options.verbose??!1}),this.healthProbe=a,this.devContextDir=g.join(d.cwd(),".infra-kit","dev-context"),this.monorepoRoot=k(d.cwd()),(d.env.DOPPLER_PROJECT!=null||d.env.DOPPLER_ENVIRONMENT!=null)&&this.renderer.log("\u{1F510} Doppler env detected (DOPPLER_PROJECT / DOPPLER_ENVIRONMENT)","debug")}discoverApiApps(e){return L(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 $(this.options.include)}resolvePreferredPort(e,r){return Oe(e,d.env,r)}resolvePrefixUrl(e,r){return Ue(e,r)}async loadDevPresets(){try{return(await O()).devServersPresets??{}}catch{return{}}}resolvePresetDef(e){if(this.options.presetDef!=null)return this.options.presetDef;let r=this.options.preset;if(r==null)return{};let n=e[r];if(!n){let i=Object.keys(e);throw new Error(`Unknown dev preset "${r}". Available: ${i.length>0?i.join(", "):"(none defined in devServersPresets)"}`)}return n}async start(){let e=Date.now(),r=this.normalizeAppInclude(),n=this.options.watch??!1,i=await this.loadDevConfig();d.env.POWERTOOLS_DEV??="true",d.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");let{apps:o,uiApps:s,apiAppsAll:a,uiAppsAll:l}=await this.resolveRunPlan(i,r);if(o.length===0&&s.length===0){this.renderer.log("\u26A0\uFE0F No API or UI apps to run for this preset","warn");return}await this.bringUpProxy(o),await this.buildAll(o,s,n),o.length>0&&(this.renderer.bootStep("starting servers"),await this.startAllApps(o),this.renderer.narrate(this.failedApps.length===0?"\u{1F389} All servers started!":`\u26A0\uFE0F ${this.appServers.length}/${o.length} servers started \u2014 ${this.failedApps.length} failed`),this.renderer.narrate(`\u{1F4DD} Handler logs (AWS Powertools, logger.info/debug, etc.) \u2192 this terminal. Runner-only file: ${v}`));let u=de({preset:this.options.preset,running:[...o.map(c=>`${c.name}/api`),...s.map(c=>`${c.name}/ui`)],discovered:[...a.map(c=>`${c.name}/api`),...l.map(c=>`${c.name}/ui`)]});if(await this.printReady(o,s,e,u),this.options.routes&&this.printRouteDump(),this.appServers.length===0&&s.length===0&&this.failedApps.length>0)throw new Error(`infra-kit dev: no app started (${this.failedApps.length} of ${o.length} failed). First failure: ${this.failedApps[0]?.reason??"unknown"}`);this.armWatch(o,s,n),s.length>0&&this.startUiDev(s),this.appServers.length>0&&this.startLivenessMonitor()}async resolveRunPlan(e,r){let n=this.discoverApiApps(e),i=se(this.monorepoRoot),o=ce(this.resolvePresetDef(await this.loadDevPresets()),{api:n.map(p=>p.name),ui:i.map(p=>p.name)});o.unmatched.length>0&&this.renderer.log(`\u26A0\uFE0F Preset targets not found (skipped): ${o.unmatched.join(", ")}`,"warn");let s=new Set(o.targets.filter(p=>p.part==="api").map(p=>p.app)),a=new Set(o.targets.filter(p=>p.part==="ui").map(p=>p.app)),l=p=>!r||r.includes(p),u=new Map(o.targets.filter(p=>p.part==="api").map(p=>[p.app,p.watchDeps])),c=n.filter(p=>s.has(p.name)&&l(p.name)).map(p=>({...p,watchDeps:u.get(p.name)??p.watchDeps})),y=i.filter(p=>a.has(p.name)&&l(p.name));return{apps:c,uiApps:y,apiAppsAll:n,uiAppsAll:i}}async bringUpProxy(e){let r=await this.resolveProxyPort();this.proxyPort=r.port,await this.ensureProxy(r.explicit),e.length>0&&this.assertNoPortConflicts(e)}async buildAll(e,r,n){let i=[e.length>0?`building ${Ke(e)}`:"",r.length>0?`warming ${Ke(r)}`:""].filter(Boolean).join(" \xB7 ");this.renderer.bootStep(i),e.length>0&&await this.buildApps(e,n),r.length>0&&await this.buildUiApps(r)}armWatch(e,r,n){n&&(this.appServers.length>0||r.length>0)&&(this.setupWatch(e,r),this.closureBuild=this.buildClosureMapSafe(e).then(i=>{this.closureMap=i}))}async buildUiApps(e){let r=e.map(n=>`--filter=${n.packageName}^...`).join(" ");try{await this.runBuild(`pnpm exec turbo run build ${r} --env-mode=loose --output-logs=errors-only --no-update-notifier`,this.renderer.logFn),this.renderer.narrate("\u2705 UI deps built")}catch(n){this.renderer.log(`\u26A0\uFE0F UI dep build failed (continuing; vite will report unresolved deps): ${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 output is routed into the live tail; full detail in the runner log)");let n=Object.keys(this.uiPortMap).length>0?{INFRA_KIT_UI_PORTS:JSON.stringify(this.uiPortMap)}:void 0,i=new Map(e.map(o=>[o.packageName,`${o.name}/ui`]));this.uiLogStream=m.createWriteStream(v,{flags:"a"}),this.uiLogStream.on("error",()=>{this.uiLogStream=null}),this.uiDev=this.uiDevFactory({packageNames:e.map(o=>o.packageName),cwd:d.cwd(),concurrency:Math.max(e.length+4,12),env:n,appendLog:o=>{this.uiLogStream?.write(o)},onLine:({pkg:o,text:s})=>{this.renderer.event({tag:i.get(o)??`${o}/ui`,text:s})},onUnexpectedExit:o=>{this.reportEngineDeath("UI dev engine (`turbo run dev`)","frontends stopped reloading",o)}})}formatAppList(e){return e.map(r=>`${r.name}:${r.port}`).join(", ")}assertNoPortConflicts(e){let r=e.filter(o=>o.preferredPort!=null).map(o=>({name:o.name,port:o.preferredPort})),{duplicatePorts:n,conflictingApps:i}=_e(r);if(n.length!==0)throw this.renderer.log(`\u26A0\uFE0F Port conflict detected! ${n.join(", ")}`,"error"),this.renderer.log(`Conflicting apps: ${this.formatAppList(i)}`,"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 i=`pnpm exec turbo run build ${e.map(o=>`--filter=${o.packageName}`).join(" ")} --env-mode=loose --output-logs=errors-only --no-update-notifier${r?" --force":""}`;try{await this.runBuild(i,this.renderer.logFn),this.renderer.narrate("\u2705 Build complete")}catch(o){this.renderer.log(`\u274C Build failed: ${String(o)}`,"error"),o instanceof Error&&o.message&&this.renderer.log(` ${o.message}`,"error");let s=o;throw s.stdout&&this.renderer.log(` stdout: ${s.stdout.trim()}`,"error"),s.stderr&&this.renderer.log(` stderr: ${s.stderr.trim()}`,"error"),o}}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"),this.failedApps.push({app:r,reason:Tt(n)})}}))}async resolveProxyPort(){if(this.options.proxyPort!=null)return{port:this.options.proxyPort,explicit:!0};try{let e=(await O()).devProxy?.port;if(e!=null)return{port:e,explicit:!0}}catch{}return{port:I,explicit:!1}}async ensureProxy(e){if(!await this.proxy.isAvailable())throw new Error("infra-kit dev: portless is not installed, so no dev URL can resolve. It ships as a dependency of infra-kit \u2014 reinstall with `pnpm install`.");if(!await this.proxy.ensureDaemon(this.proxyPort)){if(e)throw new Error(`infra-kit dev: no portless proxy on :${this.proxyPort}, and it was requested explicitly (--proxy-port / devProxy.port), so it will not fall back. ${this.proxyPort<N?"That port is privileged \u2014 install the daemon once: `sudo portless service install --no-tls`.":"Free that port, or point at one that is already serving portless."}`);if(await this.proxy.ensureDaemon(w)){this.proxyPort=w,this.renderer.log(`\u2139\uFE0F portless proxy on :${w} (no sudo) \u2014 dev URLs keep their hostname`,"warn"),this.renderer.log(" want port-free URLs? sudo portless service install --no-tls","warn");return}throw new Error(`infra-kit dev: could not bring up a portless proxy on :${I} or :${w}. Something else may be holding :${w} \u2014 check with \`lsof -nP -iTCP:${w} -sTCP:LISTEN\`, or set \`devProxy.port\` to a free one.`)}}async registerAppAlias(e,r,n){let i=re(r),o=H(e);if(o==="")throw new Error(`infra-kit dev: package name "${e}" has no letters or digits to build a hostname from.`);let s=`${i}.${o}`;if(!await this.proxy.registerAlias(s,n))throw new Error(`infra-kit dev: portless refused the alias "${s}" \u2192 127.0.0.1:${n}.`);return this.registeredAliases.add(s),`${s}.localhost`}async startOneApp(e){this.renderer.narrate(`\u{1F504} Starting ${e.name}...`);let r=new B({controllersPath:e.path,prefixUrl:e.prefixUrl,port:e.preferredPort,appName:e.name,onRequestLog:({method:o,path:s,status:a,ms:l})=>{s!=="/__health"&&this.renderer.event({tag:`${e.name}/api`,text:`${o} ${s} ${a} ${l}ms`})}}),n=await r.start(),i;try{i=await this.registerAppAlias(e.packageName,e.path,n)}catch(o){throw await r.close().catch(()=>{}),o}try{this.writeDevContextFragment(e,n,i)}catch(o){this.renderer.log(`\u26A0\uFE0F Failed to write dev-context fragment for ${e.name}: ${String(o)}`,"warn")}return this.renderer.narrate(`\u2705 ${e.name} started on port ${n}`),{server:r,boundPort:n,alias:i}}writeDevContextFragment(e,r,n){let i={package:e.packageName,port:r,pid:d.pid,writtenAt:Date.now(),release:re(e.path),alias:n,proxyPort:this.proxyPort},o=g.join(this.devContextDir,`${e.name}.json`),s=g.join(this.devContextDir,`${e.name}.json.${d.pid}.tmp`);m.mkdirSync(this.devContextDir,{recursive:!0}),m.writeFileSync(s,JSON.stringify(i,null,2)),m.renameSync(s,o)}removeDevContextFragment(e){m.rmSync(g.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.shuttingDown?Promise.resolve():this.scheduleRestartWork(()=>this.shuttingDown?Promise.resolve():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:a})=>{try{await this.appServers[a].server.close()}catch(l){this.renderer.log(` Close warning: ${String(l)}`,"debug")}})),await this.delayPortRelease();let i=await Promise.all(r.map(async({idx:a,app:l})=>{try{let u=await this.startOneApp(l);if(u){let c={app:l,...u};return this.appServers[a]=c,{app:l,entry:c}}return{app:l,entry:null}}catch(u){return this.renderer.log(`\u274C Failed to restart ${l.name}: ${String(u)}`,"error"),{app:l,entry:null}}})),o=await Promise.all(i.map(async({app:a,entry:l})=>{let u=l?await this.healthProbe(t.healthUrl(l.boundPort)):!1;return{label:`${l?`${a.name}:${l.boundPort}`:a.name} ${u?"\u25CF up":"\u25CF down"}`,healthy:u}})),s=o.every(a=>a.healthy);this.renderer.log(`${s?"\u2705":"\u26A0\uFE0F "} Restarted ${o.map(a=>a.label).join(", ")}`)}async buildClosureMapSafe(e){try{return await Le(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}}reportEngineDeath(e,r,n){this.shuttingDown||this.renderer.log(`\u26A0\uFE0F ${e} ${n} \u2014 ${r}. Restart \`infra-kit dev\`.`,"warn")}setupWatch(e,r){this.turboWatch=this.turboWatchFactory({depInclusive:e.map(l=>l.packageName),depClosure:r.map(l=>l.packageName),cwd:d.cwd(),logFile:v,onUnexpectedExit:l=>{this.reportEngineDeath("Watch engine (`turbo watch build`)","file saves no longer rebuild",l)}}),this.renderer.narrate("\u{1F440} Watch mode: started `turbo watch build` engine; watching dist output");let n=le(e),i=A(this.monorepoRoot),o=[...n,...i];if(o.length===0){this.renderer.log("\u26A0\uFE0F No app or package dist directories found to watch (were they built?)","warn");return}let s=d.env.DEV_SERVER_CHOKIDAR_POLL==="1",a=yt.watch(o,{ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:200,pollInterval:100},ignored:l=>l.endsWith(".tsbuildinfo")||l.endsWith(".map"),...s?{usePolling:!0,interval:400}:{}});this.watcher=a,s&&this.renderer.log("\u{1F440} chokidar: usePolling enabled (DEV_SERVER_CHOKIDAR_POLL=1)","debug"),a.on("change",l=>{this.handleDistChange(l,e,n,i)}),this.renderer.narrate(`\u{1F440} Watching ${n.length} app dist + ${i.length} package dist dir(s) for changes...`)}handleDistChange(e,r,n,i){this.renderer.log(`\u{1F440} dist change detected: ${e}`,"debug");let o=pe(e,n,i);if(o.kind==="package"){let a=Ie(r,this.closureMap,o.packageDir);if(a===null){this.scheduleDebounced("__packages__",()=>this.restart(r));return}a.length>0&&o.packageDir!==void 0&&this.scheduleDebounced($e(o.packageDir),()=>this.restart(a));return}let s=r.find(a=>g.join(a.path,"dist")===o.app);s&&this.scheduleDebounced(s.name,()=>this.restart([s]))}scheduleDebounced(e,r){if(this.shuttingDown)return;let n=this.watchDebounceTimers.get(e);n&&clearTimeout(n);let i=setTimeout(()=>{this.watchDebounceTimers.delete(e),!this.shuttingDown&&r().catch(o=>{this.renderer.log(`Restart error (${e}): ${String(o)}`,"error")})},t.WATCH_DEBOUNCE_MS);this.watchDebounceTimers.set(e,i)}static healthUrl(e){return`http://127.0.0.1:${e}/__health`}startLivenessMonitor(){this.scheduleLivenessTick()}scheduleLivenessTick(){let e=this.options.livenessIntervalMs??t.LIVENESS_INTERVAL_MS;this.livenessTimer=setTimeout(()=>{this.livenessTick().finally(()=>{this.shuttingDown||this.scheduleLivenessTick()}).catch(()=>{})},e),this.livenessTimer.unref()}async livenessTick(){this.shuttingDown||await Promise.all(this.appServers.map(async({app:e,boundPort:r})=>{let n=await this.healthProbe(t.healthUrl(r));if(this.shuttingDown)return;let i=this.livenessFailures.get(e.name)??0;if(n){i>=t.LIVENESS_FAILURE_THRESHOLD&&this.renderer.log(`\u2705 ${e.name}/api recovered`),this.livenessFailures.set(e.name,0);return}let o=i+1;this.livenessFailures.set(e.name,o),o===t.LIVENESS_FAILURE_THRESHOLD&&this.renderer.log(`\u26A0\uFE0F ${e.name}/api unhealthy (/__health not responding)`,"warn")}))}async printReady(e,r,n,i){let o=Date.now()-n,s=await Promise.all(this.appServers.map(({boundPort:h})=>this.healthProbe(t.healthUrl(h)))),a=this.proxyPort,l=this.appServers.map(({app:h,alias:P},b)=>({tag:`${h.name}/api`,url:V({prefixUrl:h.prefixUrl,alias:P,proxyPort:a}),healthy:s[b]??null}));this.uiPortMap={};let u=[],c=[];for(let h of r){let P=await this.assignUiPort(h);P!=null?u.push({tag:`${h.name}/ui`,url:V({prefixUrl:"",alias:P.alias,proxyPort:a}),healthy:null}):c.push({tag:`${h.name}/ui`})}let y=this.options.watch??!1,p=e.length+r.length,f=A(this.monorepoRoot).length;this.renderer.ready({target:i,watch:y,hasUiChild:r.length>0,release:re(d.cwd()),elapsedMs:o,endpoints:[...l,...u],uiRefs:c,failed:this.failedApps.map(({app:h,reason:P})=>({tag:`${h.name}/api`,reason:P})),watchSummary:`${p} app${p===1?"":"s"} \xB7 ${f} package${f===1?"":"s"}`,logPath:Je(v),logHref:v})}async assignUiPort(e){if(!e.managedPort)return null;let r;try{r=await Rt()}catch{return null}let n=await this.registerAppAlias(e.packageName,e.path,r);return this.uiPortMap[e.packageName]=r,{port:r,alias:n}}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.shuttingDown=!0,this.renderer.dispose(),this.renderer.log("\u{1F6D1} Shutting down all servers...");for(let e of this.watchDebounceTimers.values())clearTimeout(e);this.watchDebounceTimers.clear(),this.livenessTimer&&(clearTimeout(this.livenessTimer),this.livenessTimer=null),this.watcher&&(await this.watcher.close(),this.watcher=null),await this.restartWorkChain,await this.closureBuild,await Promise.all([...this.registeredAliases].map(e=>this.proxy.removeAlias(e))),this.registeredAliases.clear(),this.turboWatch&&(await this.turboWatch.kill(),this.turboWatch=null),this.uiDev&&(await this.uiDev.kill(),this.uiDev=null),this.uiLogStream&&(this.uiLogStream.end(),this.uiLogStream=null);for(let{app:e,server:r}of this.appServers){try{await r.close()}catch{}this.removeDevContextFragment(e)}this.renderer.log(`\u2713 dev stopped \xB7 logs \u2192 ${Je(v)}`)}},kt=async t=>{if(!(t.tty??!!d.stdout.isTTY)||t.json)return;let{PersistentInkDevUi:r}=await import("./persistent-ink-dev-ui-4U4B2B7C.js");return new r({appendLog:qe,verbose:t.verbose??!1})};async function Ye(t={}){let e=await kt(t),r=new te(t,void 0,void 0,void 0,void 0,e);try{await r.start()}catch(n){throw await r.shutdown().catch(()=>{}),n}return r}var It=t=>{if(t!=null){for(let e of t)if(ue(e)!==null)throw new Error(`infra-kit dev: invalid --target "${e}" (expected "<app>/api" or "<app>/ui").`);return{apps:Object.fromEntries(t.map(e=>[e,{}]))}}},Nt=t=>{if(t==null)return;let e=Number.parseInt(t,10);return Number.isInteger(e)&&e>0?e:void 0},Xe=t=>{if(t==null)return null;let e=t.split(",").map(r=>r.trim()).filter(Boolean);return e.length>0?e:null},Qe=t=>({watch:t.watch??!1,include:Xe(t.app),preset:t.preset,presetDef:It(Xe(t.target)),cmux:t.cmux??!1,self:t.self??!1,verbose:t.verbose??!1,routes:t.routes??!1,proxyPort:Nt(t.proxyPort)}),Ot=t=>!t.self||t.include?t:{...t,include:[ae(x.cwd())]},ne=async t=>{let e=Ot(t);if(e.cmux){if(await ge()){await Re(e);return}x.stdout.write(`cmux not available; falling back to single-terminal dev
19
+ `)}let r=await Ye(e);Te(),F({onSignal:async n=>{x.stdout.write(`
20
+ Received ${n}, shutting down dev-server...
21
+ `),await r.shutdown()}})},Ut=(t,e,r)=>!t.preset&&!t.app&&!t.self&&!t.cmux&&!t.watch&&!t.verbose&&!t.routes&&!t.proxyPort&&e&&!r,_t=t=>({watch:t.watch,cmux:t.cmux,include:t.include??null,preset:t.preset,presetDef:t.presetDef,self:!1,verbose:!1,routes:!1}),uo=async(t,e,r)=>{if(Ut(t,e,r)){let{runDevWizard:n}=await import("./dev-wizard-run-7HEKSAS2.js"),i;try{i=await n()}catch(o){if(ye(o))return;throw o}if(i==null)return;await ne({..._t(i),tty:e,json:r});return}await ne({...Qe(t),tty:e,json:r})},Ft=async t=>{let e=new Lt;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 the session 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: 80)"),e.parse(t),await ne(Qe({...e.opts(),preset:e.args[0]}))};import.meta.url===$t(x.argv[1]??"").href&&Ft(x.argv).catch(t=>{console.error(t instanceof Error?t.message:String(t)),x.exit(1)});export{ne as runDevServer,uo as runDevServerCli,Ut as shouldRunWizard,Qe as toDevServerOptions};
18
22
  //# sourceMappingURL=dev-server.js.map