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.
- package/dist/boot-5GFZBDKL.js +2 -0
- package/dist/boot-5GFZBDKL.js.map +7 -0
- package/dist/chunk-4UUVKJYG.js +2 -0
- package/dist/chunk-4UUVKJYG.js.map +7 -0
- package/dist/chunk-52WL2IQX.js +4 -0
- package/dist/chunk-52WL2IQX.js.map +7 -0
- package/dist/chunk-6FU2TRU5.js +2 -0
- package/dist/chunk-6FU2TRU5.js.map +7 -0
- package/dist/chunk-6YAKVQZN.js +4 -0
- package/dist/chunk-6YAKVQZN.js.map +7 -0
- package/dist/chunk-C2HRONYA.js +6 -0
- package/dist/chunk-C2HRONYA.js.map +7 -0
- package/dist/chunk-CHETBZ6M.js +3 -0
- package/dist/chunk-CHETBZ6M.js.map +7 -0
- package/dist/chunk-F6VCGS3D.js +2 -0
- package/dist/chunk-F6VCGS3D.js.map +7 -0
- package/dist/chunk-LENH44GY.js +215 -0
- package/dist/chunk-LENH44GY.js.map +7 -0
- package/dist/chunk-LTKR3F4N.js +2 -0
- package/dist/chunk-LTKR3F4N.js.map +7 -0
- package/dist/chunk-O3YJM6IR.js +2 -0
- package/dist/chunk-O3YJM6IR.js.map +7 -0
- package/dist/chunk-SR2GFEY7.js +2 -0
- package/dist/chunk-SR2GFEY7.js.map +7 -0
- package/dist/chunk-TKZINCST.js +2 -0
- package/dist/chunk-TKZINCST.js.map +7 -0
- package/dist/chunk-VJPLVBRA.js +5 -0
- package/dist/chunk-VJPLVBRA.js.map +7 -0
- package/dist/chunk-X2L4F2VM.js +3 -0
- package/dist/chunk-X2L4F2VM.js.map +7 -0
- package/dist/cli.js +4 -19
- package/dist/cli.js.map +4 -4
- package/dist/dev-server.js +20 -16
- package/dist/dev-server.js.map +4 -4
- package/dist/dev-wizard-run-7HEKSAS2.js +2 -0
- package/dist/dev-wizard-run-7HEKSAS2.js.map +7 -0
- package/dist/lib/release-slug/release-slug.d.ts +26 -2
- package/dist/lib/vite/vite.d.ts +18 -4
- package/dist/mcp.js +1 -1
- package/dist/mcp.js.map +3 -3
- package/dist/persistent-ink-dev-ui-4U4B2B7C.js +8 -0
- package/dist/persistent-ink-dev-ui-4U4B2B7C.js.map +7 -0
- package/dist/program.js +2 -0
- package/dist/program.js.map +7 -0
- package/dist/update-check.js +2 -0
- package/dist/update-check.js.map +7 -0
- package/dist/vite.js +1 -1
- package/package.json +6 -3
- package/dist/boot-SGM5RVLJ.js +0 -2
- package/dist/boot-SGM5RVLJ.js.map +0 -7
- package/dist/chunk-2PZRQHWF.js +0 -2
- package/dist/chunk-2PZRQHWF.js.map +0 -7
- package/dist/chunk-6RRAK2QO.js +0 -5
- package/dist/chunk-6RRAK2QO.js.map +0 -7
- package/dist/chunk-IX2A34UU.js +0 -2
- package/dist/chunk-IX2A34UU.js.map +0 -7
- package/dist/chunk-V5IDN7ZM.js +0 -162
- package/dist/chunk-V5IDN7ZM.js.map +0 -7
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as l}from"./chunk-52WL2IQX.js";import{I as k,S as w,v as d,w as v,x,y}from"./chunk-X2L4F2VM.js";import{e as W}from"./chunk-CHETBZ6M.js";import{c as b}from"./chunk-4UUVKJYG.js";import"./chunk-LTKR3F4N.js";import R from"@inquirer/checkbox";import N from"@inquirer/confirm";import $,{Separator as O}from"@inquirer/select";import B from"node:path";import h from"node:process";var f=e=>e.split("/")[0],z=e=>new Map(e.apps.map(r=>[r.name,r])),S=(e,r)=>{let a={},s=!1;for(let t of e.backends){let n=t.localCapable&&t.ownerApp!=null&&r.has(t.ownerApp);n||(s=!0);for(let o of t.routes)a[o]=n?"local":"cloud"}return{overrides:a,anyCloud:s}},M=(e,r)=>{let a=z(r),s=e.targets.filter(c=>c.endsWith("/ui")),t=new Set(e.targets.filter(c=>c.endsWith("/api")).map(f)),n={},o=!1;for(let c of s){let u=a.get(f(c)),{overrides:p,anyCloud:i}=u?S(u,t):{overrides:{},anyCloud:!1};i&&(o=!0),n[c]=Object.keys(p).length>0?{proxy:p}:{}}for(let c of t)n[`${c}/api`]??={};return{presetDef:{apps:n,cmux:e.cmux},anyCloudRoute:o,targetKeys:Object.keys(n).sort()}},C=(e,r,a)=>{let s=z(a),t=[...new Set(e.targetKeys.map(f))].sort(),n=new Set(e.targetKeys),o=t.every(u=>{let p=s.get(u);if(!p)return!1;let i=!p.hasUi||n.has(`${u}/ui`),m=!p.hasApi||n.has(`${u}/api`);return i&&m}),c=[`--app=${t.join(",")}`];return r.watch&&c.push("--watch"),r.cmux&&c.push("--cmux"),{flags:c.join(" "),exact:o}};var g={output:h.stderr,clearPromptOnDone:!0},j={select:e=>$({message:e.message,choices:e.choices,default:e.default},g),checkbox:e=>R({message:e.message,choices:e.choices},g),confirm:e=>N({message:e.message,default:e.default},g)},q=(e,r)=>{let a=new Map;for(let[s,t]of Object.entries(e)){let n=a.get(t.packageName),o=t.from.includes("local");n?(n.routes.push(s),n.localCapable=n.localCapable||o):a.set(t.packageName,{packageName:t.packageName,routes:[s],localCapable:o,ownerApp:r.get(t.packageName)})}return[...a.values()]},K=async e=>{let r=x(e),a=y(e),s=await w(),t=new Map(r.map(i=>[i.packageName,i.name])),n=new Map(r.map(i=>[i.name,i.packageName])),o=new Set(a.map(i=>i.name)),c=new Set(r.map(i=>i.name)),u=[...new Set([...c,...o])].sort();return{apps:await Promise.all(u.map(async i=>{let m=o.has(i),P=m?(await b(B.join(e,"apps",i,"ui")))?.proxy?.routes:void 0;return{name:i,hasApi:c.has(i),hasUi:m,apiPackage:n.get(i),backends:P?q(P,t):[]}})),presets:Object.keys(s.devServersPresets??{}),environments:s.environments}},U=e=>{let r=[];for(let a of e.apps){if(a.hasUi){let s=a.backends.flatMap(n=>n.routes),t=s.length>0?`frontend \u2014 proxies ${[...s].sort().join(", ")}`:"frontend";r.push({name:`${a.name}/ui`,value:`${a.name}/ui`,checked:!0,description:t})}a.hasApi&&r.push({name:`${a.name}/api`,value:`${a.name}/api`,checked:!0,description:"backend"})}return r},_=e=>{let r={api:e.apps.filter(t=>t.hasApi).map(t=>t.name),ui:e.apps.filter(t=>t.hasUi).map(t=>t.name)},a={},s=new Map;for(let t of e.apps){t.apiPackage!=null&&(a[t.name]=t.apiPackage);for(let n of t.backends)for(let o of n.routes)s.set(`${t.name} ${o}`,n.packageName)}return{discovered:r,apiPkgByApp:a,routePkg:(t,n)=>s.get(`${t} ${n}`)}},I=(e,r)=>k({__wizard__:e},_(r)).map(a=>a.message),A=async(e,r)=>{if(r.apps.length===0)return d.warn("No apps discovered to run."),null;let a=await e.checkbox({message:"\u{1F4E6} Which packages?",choices:U(r)});if(a.length===0)return d.warn("No packages selected."),null;let s=await e.confirm({message:"\u{1F440} Rebuild & restart on save (watch)?",default:!1}),t=await e.confirm({message:"\u{1F9E9} Run each app in its own cmux pane?",default:!1}),n={targets:a,watch:s,cmux:t},o=M(n,r);o.anyCloudRoute&&(n.env=await e.select({message:"\u2601\uFE0F Point cloud routes at which environment?",choices:r.environments.map(p=>({name:p,value:p}))}),h.env[W]=n.env);let c=I(o.presetDef,r);if(c.length>0){d.warn("\u26A0\uFE0F Proxy audit found issues with this selection:");for(let p of c)d.warn(` \u2022 ${p}`);return null}E(o,n,r);let u=o.targetKeys.filter(p=>p.endsWith("/api")).map(p=>p.split("/")[0]);return t&&u.length>0?{include:u,presetDef:o.presetDef,watch:s,cmux:!0}:(t&&d.info("\u2139\uFE0F cmux needs at least one local backend (panes are backend-only) \u2014 running in-process instead."),{presetDef:o.presetDef,watch:s,cmux:!1})},E=(e,r,a)=>{l.start("dev"),l.setInteractive();let s=C(e,r,a);l.addOption(s.flags,!0),l.print(),s.exact||d.info("\u2139\uFE0F This part-level selection has no exact single-flag form \u2014 save it as a devPreset to reproduce it.")},T=async(e,r)=>{let a=await e.confirm({message:"\u{1F440} Rebuild & restart on save (watch)?",default:!1});return l.start("dev"),l.setInteractive(),l.addOption(r,!0),a&&l.addOption("--watch",!0),l.print(),{preset:r,watch:a,cmux:!1}},D=" manual",F=async(e,r)=>{if(r.presets.length===0)return A(e,r);let a=await e.select({message:"\u{1F680} Start from a preset, or configure manually?",choices:[...r.presets.map(s=>({name:s,value:s})),new O(" "),{name:"Manual (custom)\u2026",value:D}]});return a===D?A(e,r):T(e,a)},se=async(e=j,r=v(h.cwd()))=>F(e,await K(r));export{D as MANUAL_CHOICE,I as auditManualPlan,j as defaultPrompts,K as gatherWizardModel,se as runDevWizard,F as runWizardFlow};
|
|
2
|
+
//# sourceMappingURL=dev-wizard-run-7HEKSAS2.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/dev/dev-wizard-run.ts", "../src/dev/dev-wizard.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Impure driver for the interactive `infra-kit dev` wizard: filesystem/config discovery, the inquirer\n * prompt flow (step-0 preset-or-manual, then the manual matrix), the pre-flight proxy audit, and the\n * `commandEcho` teaching line. The pure answer\u2192plan mapping lives in {@link file://./dev-wizard.ts}.\n *\n * Prompts are behind the injectable {@link WizardPrompts} seam so the flow is testable without a TTY.\n * This module is reached ONLY from the bare-invocation TTY branch of the entry point; every flagged /\n * non-TTY / `--json` / MCP invocation bypasses it entirely.\n */\nimport inquirerCheckbox from '@inquirer/checkbox'\nimport inquirerConfirm from '@inquirer/confirm'\nimport inquirerSelect, { Separator } from '@inquirer/select'\nimport path from 'node:path'\nimport process from 'node:process'\n\nimport { discoverApiApps, discoverUiApps, findMonorepoRoot } from 'src/dev/discovery'\nimport { commandEcho } from 'src/lib/command-echo'\nimport { INFRA_KIT_ENV_VAR } from 'src/lib/constants'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\nimport type { DevPreset } from 'src/lib/infra-kit-config'\nimport { logger } from 'src/lib/logger'\nimport { loadDev } from 'src/lib/vite/vite'\n\nimport { deriveManualPlan, equivalentCommand } from './dev-wizard.js'\nimport type { DerivedPlan, ManualSelection, ProxyBackend, WizardApp, WizardModel } from './dev-wizard.js'\nimport { validatePresetProxy } from './presets.js'\nimport type { DiscoveredParts, PresetProxyContext } from './presets.js'\n\n/** A wizard prompt choice \u2014 all values are strings, so the seam needs no generics. */\nexport interface WizardChoice {\n name: string\n value: string\n description?: string\n checked?: boolean\n disabled?: boolean | string\n}\n\n/**\n * Injectable prompt seam (a string-valued subset of `@inquirer/*`). The default implementation\n * ({@link defaultPrompts}) delegates to the real prompts, rendering to stderr so the dev-server's\n * stdout stays clean. Tests pass a scripted object.\n */\nexport interface WizardPrompts {\n select: (cfg: { message: string; choices: (WizardChoice | Separator)[]; default?: string }) => Promise<string>\n checkbox: (cfg: { message: string; choices: WizardChoice[] }) => Promise<string[]>\n confirm: (cfg: { message: string; default?: boolean }) => Promise<boolean>\n}\n\n/**\n * Shared `@inquirer/*` context. Rendered to stderr (mirrors the bare-`infra-kit` command palette).\n *\n * `clearPromptOnDone` erases each answered prompt instead of leaving a `\u2714 <question> <answer>` line\n * behind, so the wizard collapses to nothing once it finishes and the dev-server's ready header lands\n * at the top of a clean screen. The choices are not lost: {@link echoManual} / {@link runPresetBranch}\n * print the equivalent flag command, which is the durable record of what was picked.\n */\nconst promptContext = { output: process.stderr, clearPromptOnDone: true }\n\n/** Real `@inquirer/*` prompts, rendered to stderr and erased once answered. */\nexport const defaultPrompts: WizardPrompts = {\n select: (cfg) => {\n return inquirerSelect({ message: cfg.message, choices: cfg.choices, default: cfg.default }, promptContext)\n },\n checkbox: (cfg) => {\n return inquirerCheckbox({ message: cfg.message, choices: cfg.choices }, promptContext)\n },\n confirm: (cfg) => {\n return inquirerConfirm({ message: cfg.message, default: cfg.default }, promptContext)\n },\n}\n\n/** What the wizard hands back to the entry point (merged into `DevServerOptions`), or null on cancel. */\nexport interface WizardResult {\n /** Named preset (preset branch) \u2014 passed through as `options.preset`. */\n preset?: string\n /** In-memory preset (manual branch, non-cmux) \u2014 passed through as `options.presetDef`. */\n presetDef?: DevPreset\n /**\n * App-name include list for the cmux path only: which discovered API apps get a pane. Paired with\n * `presetDef`, which tells each pane the exact parts to run. An app selected UI-only gets no pane\n * (cmux panes are opened per API app). Unset for the non-cmux manual path.\n */\n include?: string[]\n watch: boolean\n cmux: boolean\n}\n\n/** Group one frontend's `dev.proxy.routes` into {@link ProxyBackend}s keyed by backend package. */\nconst groupBackends = (\n routes: Record<string, { packageName: string; from: readonly string[] }>,\n ownerByPkg: Map<string, string>,\n): ProxyBackend[] => {\n const byPkg = new Map<string, ProxyBackend>()\n\n for (const [route, def] of Object.entries(routes)) {\n const existing = byPkg.get(def.packageName)\n const localCapable = def.from.includes('local')\n\n if (existing) {\n existing.routes.push(route)\n existing.localCapable = existing.localCapable || localCapable\n } else {\n byPkg.set(def.packageName, {\n packageName: def.packageName,\n routes: [route],\n localCapable,\n ownerApp: ownerByPkg.get(def.packageName),\n })\n }\n }\n\n return [...byPkg.values()]\n}\n\n/** Discover apps + configs and assemble the {@link WizardModel} (backends resolved per frontend). */\nexport const gatherWizardModel = async (root: string): Promise<WizardModel> => {\n const apiApps = discoverApiApps(root)\n const uiApps = discoverUiApps(root)\n const config = await getInfraKitConfig()\n\n const ownerByPkg = new Map(\n apiApps.map((a) => {\n return [a.packageName, a.name] as const\n }),\n )\n const apiPkgByName = new Map(\n apiApps.map((a) => {\n return [a.name, a.packageName] as const\n }),\n )\n const uiNames = new Set(\n uiApps.map((a) => {\n return a.name\n }),\n )\n const apiNames = new Set(\n apiApps.map((a) => {\n return a.name\n }),\n )\n const allNames = [...new Set([...apiNames, ...uiNames])].sort()\n\n const apps: WizardApp[] = await Promise.all(\n allNames.map(async (name): Promise<WizardApp> => {\n const hasUi = uiNames.has(name)\n const routes = hasUi ? (await loadDev(path.join(root, 'apps', name, 'ui')))?.proxy?.routes : undefined\n\n return {\n name,\n hasApi: apiNames.has(name),\n hasUi,\n apiPackage: apiPkgByName.get(name),\n backends: routes ? groupBackends(routes, ownerByPkg) : [],\n }\n }),\n )\n\n return { apps, presets: Object.keys(config.devServersPresets ?? {}), environments: config.environments }\n}\n\n/**\n * Build the flat `<app>/<part>` checkbox choices (all checked by default), grouped per app as `ui` then\n * `api`. A frontend choice carries a description of the routes it proxies, so unticking the matching api\n * part reads as \"send that route to cloud\". This is the checkbox the user directly picks their targets from.\n */\nconst buildPartChoices = (model: WizardModel): WizardChoice[] => {\n const choices: WizardChoice[] = []\n\n for (const app of model.apps) {\n if (app.hasUi) {\n const routes = app.backends.flatMap((b) => {\n return b.routes\n })\n const description = routes.length > 0 ? `frontend \u2014 proxies ${[...routes].sort().join(', ')}` : 'frontend'\n\n choices.push({ name: `${app.name}/ui`, value: `${app.name}/ui`, checked: true, description })\n }\n if (app.hasApi) {\n choices.push({ name: `${app.name}/api`, value: `${app.name}/api`, checked: true, description: 'backend' })\n }\n }\n\n return choices\n}\n\n/** Build the audit context from the gathered model (no re-reading of configs). */\nconst buildAuditContext = (model: WizardModel): PresetProxyContext => {\n const discovered: DiscoveredParts = {\n api: model.apps\n .filter((a) => {\n return a.hasApi\n })\n .map((a) => {\n return a.name\n }),\n ui: model.apps\n .filter((a) => {\n return a.hasUi\n })\n .map((a) => {\n return a.name\n }),\n }\n const apiPkgByApp: Record<string, string> = {}\n const routeToPkg = new Map<string, string>()\n\n for (const app of model.apps) {\n // Map EVERY discovered api app to its own api package \u2014 including api-only apps a frontend proxies\n // to cross-app \u2014 so `launchedPkgs` in validatePresetProxy is faithful (mirrors preset-proxy-check.ts).\n // Deriving this from proxy backends alone would miss api-only owners and false-positive the audit.\n if (app.apiPackage != null) apiPkgByApp[app.name] = app.apiPackage\n for (const b of app.backends) {\n for (const route of b.routes) {\n routeToPkg.set(`${app.name} ${route}`, b.packageName)\n }\n }\n }\n\n return {\n discovered,\n apiPkgByApp,\n routePkg: (app, route) => {\n return routeToPkg.get(`${app} ${route}`)\n },\n }\n}\n\n/**\n * Run the assembled plan through the SAME proxy-locality rule the root audit uses. Returns issue\n * messages (empty when clean). Catches a `local` override whose backend won't launch \u2014 the wizard's\n * derivation launches owners, so a hit means the backend has no discoverable owning app.\n */\nexport const auditManualPlan = (presetDef: DevPreset, model: WizardModel): string[] => {\n return validatePresetProxy({ __wizard__: presetDef }, buildAuditContext(model)).map((i) => {\n return i.message\n })\n}\n\n/** The manual-branch flow: app + per-app proxy + env + watch + cmux \u2192 audited plan \u2192 echo. */\nconst runManualBranch = async (prompts: WizardPrompts, model: WizardModel): Promise<WizardResult | null> => {\n if (model.apps.length === 0) {\n logger.warn('No apps discovered to run.')\n\n return null\n }\n\n const selectedTargets = await prompts.checkbox({ message: '\uD83D\uDCE6 Which packages?', choices: buildPartChoices(model) })\n\n if (selectedTargets.length === 0) {\n logger.warn('No packages selected.')\n\n return null\n }\n\n const watch = await prompts.confirm({ message: '\uD83D\uDC40 Rebuild & restart on save (watch)?', default: false })\n const cmux = await prompts.confirm({ message: '\uD83E\uDDE9 Run each app in its own cmux pane?', default: false })\n\n const selection: ManualSelection = { targets: selectedTargets, watch, cmux }\n const plan = deriveManualPlan(selection, model)\n\n if (plan.anyCloudRoute) {\n selection.env = await prompts.select({\n message: '\u2601\uFE0F Point cloud routes at which environment?',\n choices: model.environments.map((e) => {\n return { name: e, value: e }\n }),\n })\n process.env[INFRA_KIT_ENV_VAR] = selection.env\n }\n\n const issues = auditManualPlan(plan.presetDef, model)\n\n if (issues.length > 0) {\n logger.warn('\u26A0\uFE0F Proxy audit found issues with this selection:')\n for (const issue of issues) {\n logger.warn(` \u2022 ${issue}`)\n }\n\n return null\n }\n\n echoManual(plan, selection, model)\n\n // cmux opens one pane per selected API app. `include` picks WHICH apps get a pane; `presetDef` tells\n // each pane exactly which of its parts to run. Without the latter a pane runs `--app=<name>`, which\n // expands to every part the app has \u2014 silently starting a UI the user just unticked.\n const apiApps = plan.targetKeys\n .filter((k) => {\n return k.endsWith('/api')\n })\n .map((k) => {\n return k.split('/')[0]!\n })\n\n // An empty include would collapse to `null` in `normalizeAppInclude` and make cmux run EVERY api app,\n // so a cmux run with no selected backends (e.g. an all-frontend selection) falls back to in-process.\n if (cmux && apiApps.length > 0) {\n return { include: apiApps, presetDef: plan.presetDef, watch, cmux: true }\n }\n\n if (cmux) {\n logger.info('\u2139\uFE0F cmux needs at least one local backend (panes are backend-only) \u2014 running in-process instead.')\n }\n\n return { presetDef: plan.presetDef, watch, cmux: false }\n}\n\n/** Print the equivalent flag command (and, for a part-level selection, the save-as-preset hint). */\nconst echoManual = (plan: DerivedPlan, selection: ManualSelection, model: WizardModel): void => {\n commandEcho.start('dev')\n commandEcho.setInteractive()\n\n const eq = equivalentCommand(plan, selection, model)\n\n commandEcho.addOption(eq.flags, true)\n commandEcho.print()\n\n if (!eq.exact) {\n logger.info('\u2139\uFE0F This part-level selection has no exact single-flag form \u2014 save it as a devPreset to reproduce it.')\n }\n}\n\n/** The preset-branch flow: run a named preset, asking only whether to watch (presets can't encode it). */\nconst runPresetBranch = async (prompts: WizardPrompts, preset: string): Promise<WizardResult> => {\n const watch = await prompts.confirm({ message: '\uD83D\uDC40 Rebuild & restart on save (watch)?', default: false })\n\n commandEcho.start('dev')\n commandEcho.setInteractive()\n commandEcho.addOption(preset, true)\n if (watch) commandEcho.addOption('--watch', true)\n commandEcho.print()\n\n return { preset, watch, cmux: false }\n}\n\n/** Sentinel value for the \"Manual (custom)\" step-0 choice (a preset name can never be empty). */\nexport const MANUAL_CHOICE = ' manual'\n\n/**\n * Drive the wizard's branch flow over an ALREADY-gathered model (no disk/config access). Split from\n * {@link runDevWizard} so the flow is unit-testable with scripted prompts + a fixture model.\n */\nexport const runWizardFlow = async (prompts: WizardPrompts, model: WizardModel): Promise<WizardResult | null> => {\n if (model.presets.length === 0) {\n return runManualBranch(prompts, model)\n }\n\n const choice = await prompts.select({\n message: '\uD83D\uDE80 Start from a preset, or configure manually?',\n choices: [\n ...model.presets.map((p) => {\n return { name: p, value: p }\n }),\n new Separator(' '),\n { name: 'Manual (custom)\u2026', value: MANUAL_CHOICE },\n ],\n })\n\n return choice === MANUAL_CHOICE ? runManualBranch(prompts, model) : runPresetBranch(prompts, choice)\n}\n\n/**\n * Gather the model from disk/config, then drive the wizard flow, returning the resolved run options (or\n * null on cancel / empty selection). The entry point calls this on a bare TTY `infra-kit dev`.\n */\nexport const runDevWizard = async (\n prompts: WizardPrompts = defaultPrompts,\n root: string = findMonorepoRoot(process.cwd()),\n): Promise<WizardResult | null> => {\n return runWizardFlow(prompts, await gatherWizardModel(root))\n}\n", "/**\n * Pure core of the interactive `infra-kit dev` wizard (the no-args, TTY path).\n *\n * The wizard is FRONTEND-CENTRIC: you pick apps to run, and for each frontend you choose, per backend\n * it proxies to, whether that backend runs LOCALLY (its `/api` is launched \u2192 the route resolves local)\n * or stays CLOUD (not launched \u2192 the route proxies to the `<env>` cloud target). A backend runs iff a\n * frontend points at it locally, or its app is selected and not demoted to cloud. This module turns\n * those answers into an in-memory {@link DevPreset} the runner consumes verbatim (part-level targets\n * that the app-name-only `--app` include cannot express) \u2014 and is side-effect-free so the mapping is\n * fully unit-testable. The impure I/O (discovery, inquirer prompts, audit, launch) lives in\n * {@link file://./dev-wizard-run.ts}.\n */\nimport type { DevPreset, ProxySource } from 'src/lib/infra-kit-config'\n\n/**\n * One backend a frontend's proxy routes can be pointed at, grouped by the backend package. A frontend\n * that maps `/api` and `/media` to the same package yields ONE {@link ProxyBackend} with both routes \u2014\n * launching the package flips every one of its `local`-capable routes together (see `pickSource`).\n */\nexport interface ProxyBackend {\n /** Backend package name (a route's `packageName`). */\n packageName: string\n /** Route paths that resolve to this package (for display + the audit's per-route overrides). */\n routes: string[]\n /** True when at least one of `routes` lists `local` in its `from` capabilities \u2014 i.e. it is toggleable. A `false` backend is fixed-cloud, shown as info. */\n localCapable: boolean\n /** App folder whose `api` package is {@link packageName}, or undefined when no discovered app owns it (a `local` choice then has nothing to launch \u2014 the audit flags it). */\n ownerApp?: string\n}\n\n/** A discovered app as the wizard sees it: which parts exist + which backends its frontend proxies to. */\nexport interface WizardApp {\n /** App folder name (e.g. `client`). */\n name: string\n /** Has `apps/<app>/api` (a launchable backend). */\n hasApi: boolean\n /** Has `apps/<app>/ui` (a launchable frontend). */\n hasUi: boolean\n /**\n * This app's OWN `api` package name (from `apps/<app>/api`), or undefined when it has no api. Carried\n * so the audit can build a faithful `app \u2192 apiPackage` map for EVERY discovered api app \u2014 including\n * api-only apps a frontend proxies to cross-app \u2014 mirroring the root audit (`preset-proxy-check.ts`).\n */\n apiPackage?: string\n /** Backends this app's frontend proxies to (empty for api-only apps or a frontend with no `dev.proxy`). */\n backends: ProxyBackend[]\n}\n\n/** Everything the wizard needs, gathered impurely by {@link file://./dev-wizard-run.ts}. */\nexport interface WizardModel {\n /** All discovered apps (api and/or ui), sorted by name. */\n apps: WizardApp[]\n /** `devServersPresets` names (drives the preset-or-manual step-0). */\n presets: string[]\n /** Configured `environments` (the cloud-env `select` options). */\n environments: string[]\n}\n\n/** The manual-branch answers collected from the prompts, fed to {@link deriveManualPlan}. */\nexport interface ManualSelection {\n /**\n * The `<app>/<part>` target keys the user ticked directly (e.g. `client/ui`, `client/api`). A frontend's\n * proxy route resolves LOCAL iff the backend's owner-api part is in this set \u2014 otherwise CLOUD \u2014 so the\n * part checkbox IS the local/cloud decision; there is no separate per-backend question.\n */\n targets: string[]\n /** Chosen cloud env for cloud routes (undefined when nothing resolves cloud). */\n env?: string\n /** Rebuild + restart on save. */\n watch: boolean\n /** One cmux pane per app. */\n cmux: boolean\n}\n\n/** The resolved run plan derived from a {@link ManualSelection}. */\nexport interface DerivedPlan {\n /** In-memory preset handed to the runner (`options.presetDef`). */\n presetDef: DevPreset\n /** At least one route resolves to cloud \u2192 an env must be chosen (pre-flight gate). */\n anyCloudRoute: boolean\n /** Resolved `<app>/<part>` target keys, sorted \u2014 for the echo + audit display. */\n targetKeys: string[]\n}\n\n/** App folder name from an `<app>/<part>` target key. */\nconst appOf = (key: string): string => {\n return key.split('/')[0]!\n}\n\n/** Index a model's apps by folder name for O(1) lookup. */\nconst byName = (model: WizardModel): Map<string, WizardApp> => {\n return new Map(\n model.apps.map((a) => {\n return [a.name, a]\n }),\n )\n}\n\n/**\n * Resolve ONE selected frontend's proxy routes against the set of api parts the user ticked: a route is\n * `local` iff its backend is `localCapable` AND that backend's owner-api part was selected \u2014 otherwise\n * `cloud`. Kept separate from {@link deriveManualPlan} so the per-app override loop stays flat.\n */\nconst resolveUiProxy = (\n app: WizardApp,\n selectedApiApps: ReadonlySet<string>,\n): { overrides: Record<string, ProxySource>; anyCloud: boolean } => {\n const overrides: Record<string, ProxySource> = {}\n let anyCloud = false\n\n for (const backend of app.backends) {\n const isLocal = backend.localCapable && backend.ownerApp != null && selectedApiApps.has(backend.ownerApp)\n\n if (!isLocal) anyCloud = true\n for (const route of backend.routes) {\n overrides[route] = isLocal ? 'local' : 'cloud'\n }\n }\n\n return { overrides, anyCloud }\n}\n\n/**\n * Turn a part-level {@link ManualSelection} into an in-memory {@link DevPreset}. Because the user picks\n * `<app>/<part>` targets directly, the mapping is straightforward:\n * - every selected `<app>/ui` runs that frontend;\n * - every selected `<app>/api` runs that backend locally;\n * - each frontend's proxy route resolves `local` iff the backend's owner-api part was also selected\n * (even cross-app), else `cloud` \u2014 i.e. ticking the api part IS the local choice.\n *\n * @example\n * // client frontend only (its /api left unticked \u2192 its proxy route resolves to cloud):\n * deriveManualPlan(\n * { targets: ['client/ui'], watch: false, cmux: false },\n * { apps: [{ name: 'client', hasApi: true, hasUi: true,\n * backends: [{ packageName: 'client-api', routes: ['/api'], localCapable: true, ownerApp: 'client' }] }],\n * presets: [], environments: ['dev'] },\n * ).targetKeys // => ['client/ui']\n */\nexport const deriveManualPlan = (selection: ManualSelection, model: WizardModel): DerivedPlan => {\n const apps = byName(model)\n const uiKeys = selection.targets.filter((t) => {\n return t.endsWith('/ui')\n })\n const apiApps = new Set(\n selection.targets\n .filter((t) => {\n return t.endsWith('/api')\n })\n .map(appOf),\n )\n\n const presetApps: Record<string, { proxy?: Record<string, ProxySource> }> = {}\n let anyCloud = false\n\n for (const key of uiKeys) {\n const app = apps.get(appOf(key))\n const { overrides, anyCloud: cloud } = app ? resolveUiProxy(app, apiApps) : { overrides: {}, anyCloud: false }\n\n if (cloud) anyCloud = true\n presetApps[key] = Object.keys(overrides).length > 0 ? { proxy: overrides } : {}\n }\n for (const name of apiApps) {\n presetApps[`${name}/api`] ??= {}\n }\n\n return {\n presetDef: { apps: presetApps, cmux: selection.cmux },\n anyCloudRoute: anyCloud,\n targetKeys: Object.keys(presetApps).sort(),\n }\n}\n\n/** The equivalent non-interactive command, plus whether it reproduces the selection exactly. */\nexport interface EquivalentCommand {\n /** The `--app=\u2026`-form flag string (no leading `infra-kit dev`). */\n flags: string\n /**\n * True when `--app` reproduces the plan exactly \u2014 i.e. every involved app runs ALL the parts it has.\n * False for a part-level selection (e.g. frontend-only), which `--app` (app-name granularity) cannot\n * express; the caller then hints \"save as a preset\" for exact reproduction.\n */\n exact: boolean\n}\n\n/**\n * Build the equivalent `infra-kit dev --app=\u2026 [--watch] [--cmux]` flag string for a derived plan.\n * `exact` is true only when the target set covers every part each involved app HAS (a whole-app\n * selection), since `--app` filters by app name, not part.\n */\nexport const equivalentCommand = (\n plan: DerivedPlan,\n selection: ManualSelection,\n model: WizardModel,\n): EquivalentCommand => {\n const apps = byName(model)\n const involved = [...new Set(plan.targetKeys.map(appOf))].sort()\n const present = new Set(plan.targetKeys)\n\n const exact = involved.every((name) => {\n const app = apps.get(name)\n\n if (!app) return false\n\n const uiOk = !app.hasUi || present.has(`${name}/ui`)\n const apiOk = !app.hasApi || present.has(`${name}/api`)\n\n return uiOk && apiOk\n })\n\n const parts = [`--app=${involved.join(',')}`]\n\n if (selection.watch) parts.push('--watch')\n if (selection.cmux) parts.push('--cmux')\n\n return { flags: parts.join(' '), exact }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["inquirerCheckbox", "inquirerConfirm", "inquirerSelect", "Separator", "path", "process", "appOf", "key", "byName", "model", "a", "resolveUiProxy", "app", "selectedApiApps", "overrides", "anyCloud", "backend", "isLocal", "route", "deriveManualPlan", "selection", "apps", "uiKeys", "t", "apiApps", "presetApps", "cloud", "name", "equivalentCommand", "plan", "involved", "present", "exact", "uiOk", "apiOk", "parts", "promptContext", "process", "defaultPrompts", "cfg", "inquirerSelect", "inquirerCheckbox", "inquirerConfirm", "groupBackends", "routes", "ownerByPkg", "byPkg", "route", "def", "existing", "localCapable", "gatherWizardModel", "root", "apiApps", "discoverApiApps", "uiApps", "discoverUiApps", "config", "getInfraKitConfig", "a", "apiPkgByName", "uiNames", "apiNames", "allNames", "name", "hasUi", "loadDev", "path", "buildPartChoices", "model", "choices", "app", "b", "description", "buildAuditContext", "discovered", "apiPkgByApp", "routeToPkg", "auditManualPlan", "presetDef", "validatePresetProxy", "i", "runManualBranch", "prompts", "logger", "selectedTargets", "watch", "cmux", "selection", "plan", "deriveManualPlan", "e", "INFRA_KIT_ENV_VAR", "issues", "issue", "echoManual", "k", "commandEcho", "eq", "equivalentCommand", "runPresetBranch", "preset", "MANUAL_CHOICE", "runWizardFlow", "choice", "p", "Separator", "runDevWizard", "findMonorepoRoot"]
|
|
7
|
+
}
|
|
@@ -7,13 +7,37 @@
|
|
|
7
7
|
* helper-computed slug can never drift. Pure (regex only, no imports) so importing it
|
|
8
8
|
* into the lightweight `infra-kit/vite` bundle stays cheap.
|
|
9
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* Slugify an arbitrary string into a single DNS label: lowercase, collapse every
|
|
12
|
+
* non-alphanumeric run to `-`, and drop leading/trailing separators. Returns `''`
|
|
13
|
+
* when the input carries no alphanumeric run (the caller must treat that as "no label").
|
|
14
|
+
*
|
|
15
|
+
* portless rejects any hostname outside `[a-z0-9.-]`, so every segment fed into a
|
|
16
|
+
* `<release>.<packageName>.localhost` alias must pass through here. A scoped npm name
|
|
17
|
+
* (`@hulyo/client-ui`) is the motivating case: registering it raw fails with
|
|
18
|
+
* `Invalid hostname`, and because the driver swallows that into a best-effort `false`,
|
|
19
|
+
* the whole hero-URL path degrades silently to `localhost:<port>`.
|
|
20
|
+
*
|
|
21
|
+
* Distinct from {@link slugifyRelease}: this does NOT strip a git-flow prefix, so a
|
|
22
|
+
* package legitimately named `fix-utils` keeps its `fix-` prefix.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* slugifyHostLabel('@hulyo/client-ui') // => 'hulyo-client-ui'
|
|
26
|
+
* slugifyHostLabel('backend-api') // => 'backend-api'
|
|
27
|
+
*/
|
|
28
|
+
export declare const slugifyHostLabel: (label: string) => string;
|
|
10
29
|
/**
|
|
11
30
|
* Slugify a git branch into a `<release>` token: strip a leading git-flow prefix
|
|
12
|
-
* (`feature/`, `release/`, …),
|
|
13
|
-
* and trim stray dashes.
|
|
31
|
+
* (`feature/`, `release/`, …), then reduce the remainder to a single DNS label.
|
|
14
32
|
*
|
|
15
33
|
* @example
|
|
16
34
|
* slugifyRelease('release/2.4') // => '2-4'
|
|
17
35
|
* slugifyRelease('feature/HUL-123') // => 'hul-123'
|
|
18
36
|
*/
|
|
19
37
|
export declare const slugifyRelease: (branch: string) => string;
|
|
38
|
+
/**
|
|
39
|
+
* The `<release>` label used when no git branch resolves (outside a repo, or a branch that slugifies to
|
|
40
|
+
* nothing). Both the dev-server's alias writer and `infra-kit/vite`'s template interpolation fall back
|
|
41
|
+
* to this SAME constant — a divergence here would emit a hostname no alias backs.
|
|
42
|
+
*/
|
|
43
|
+
export declare const DEFAULT_RELEASE_SLUG = "local";
|
package/dist/lib/vite/vite.d.ts
CHANGED
|
@@ -44,6 +44,13 @@ export interface InfraKitDevOptions {
|
|
|
44
44
|
* chosen URL. Pass a fixed number only when an external contract pins the port.
|
|
45
45
|
*/
|
|
46
46
|
port?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Interface the dev server binds. Defaults to {@link LOOPBACK_V4} so a portless alias can reach it
|
|
49
|
+
* (Vite's own `localhost` default binds `[::1]` only, which the proxy cannot dial). Override when
|
|
50
|
+
* the dev server must be reachable off the loopback — `'0.0.0.0'` or `true` inside a container, on
|
|
51
|
+
* a LAN, or for an e2e runner on another host.
|
|
52
|
+
*/
|
|
53
|
+
host?: string | boolean;
|
|
47
54
|
}
|
|
48
55
|
/**
|
|
49
56
|
* Re-exported from the shared, dependency-light `lib/release-slug` module so the
|
|
@@ -79,10 +86,14 @@ export declare const resolveProxyConfig: ({ proxy, localSet, env, getRelease, au
|
|
|
79
86
|
export declare const loadDev: (cwd: string) => Promise<InfraKitDev | undefined>;
|
|
80
87
|
/** Per-package runtime data merged from the dev-context fragment directory. */
|
|
81
88
|
export interface LocalPackageInfo {
|
|
82
|
-
/** The real bound port the runner recorded
|
|
89
|
+
/** The real bound port the runner recorded. Provenance only — the proxy target is the alias host. */
|
|
83
90
|
port: number;
|
|
84
91
|
/** The runner-recorded release slug, preferred over the helper's git derivation. */
|
|
85
92
|
release?: string;
|
|
93
|
+
/** `<release>.<packageName>.localhost`, present only when a portless alias was registered. */
|
|
94
|
+
alias?: string;
|
|
95
|
+
/** The port {@link alias} resolves on. `80` (the default) is implicit in the template. */
|
|
96
|
+
proxyPort?: number;
|
|
86
97
|
}
|
|
87
98
|
/**
|
|
88
99
|
* The locally-running package set plus a `package → { port, release }` map merged
|
|
@@ -115,9 +126,10 @@ export declare const readLocalSet: (cwd: string) => ReadonlySet<string>;
|
|
|
115
126
|
* present, else the slugified git branch (computed lazily, only when a local route needs it).
|
|
116
127
|
*
|
|
117
128
|
* `port` defaults to a fresh OS-assigned free port so simultaneous git worktrees never collide on
|
|
118
|
-
* Vite's `5173` (override via `options.port`).
|
|
119
|
-
*
|
|
120
|
-
*
|
|
129
|
+
* Vite's `5173` (override via `options.port`). `host` defaults to {@link LOOPBACK_V4} so the portless
|
|
130
|
+
* alias can actually reach it (override via `options.host` for containers/LAN). Pass `command` so
|
|
131
|
+
* `build` is a no-op (empty proxy, no port) — a build ignores `server` and proxy resolution would
|
|
132
|
+
* otherwise fail-fast on a cloud route with no sourced env.
|
|
121
133
|
*
|
|
122
134
|
* @example
|
|
123
135
|
* // vite.config.ts — the whole `server` field is the helper's output
|
|
@@ -126,6 +138,8 @@ export declare const readLocalSet: (cwd: string) => ReadonlySet<string>;
|
|
|
126
138
|
*/
|
|
127
139
|
export declare const infraKitDev: (options?: InfraKitDevOptions) => Promise<{
|
|
128
140
|
port?: number;
|
|
141
|
+
host?: string | boolean;
|
|
142
|
+
strictPort?: boolean;
|
|
129
143
|
proxy: InfraKitViteProxy;
|
|
130
144
|
}>;
|
|
131
145
|
/**
|
package/dist/mcp.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{b as l}from"./chunk-TKZINCST.js";import{g as m}from"./chunk-LENH44GY.js";import"./chunk-52WL2IQX.js";import"./chunk-6YAKVQZN.js";import{t as c,u as p,v as s}from"./chunk-X2L4F2VM.js";import"./chunk-Z6KTUVIC.js";import"./chunk-SR2GFEY7.js";import"./chunk-CHETBZ6M.js";import"./chunk-4UUVKJYG.js";import"./chunk-LTKR3F4N.js";import{StdioServerTransport as g}from"@modelcontextprotocol/sdk/server/stdio.js";import S from"node:process";import o from"node:process";var d=r=>{o.on("SIGINT",()=>{r.info({msg:"Received SIGINT. Shutting down..."}),o.exit(0)}),o.on("SIGTERM",()=>{r.info({msg:"Received SIGTERM. Shutting down..."}),o.exit(0)}),o.on("uncaughtException",e=>{r.fatal({err:e,msg:"Uncaught Exception"}),r.error(`Uncaught Exception! Check ${c}. Shutting down...`),r.flush(),o.exit(1)}),o.on("unhandledRejection",(e,n)=>{r.fatal({reason:e,promise:n,msg:"Unhandled Rejection"}),r.error(`Unhandled Rejection! Check ${c}. Shutting down...`),r.flush(),o.exit(1)})};import{McpServer as h}from"@modelcontextprotocol/sdk/server/mcp.js";var u=async r=>{};var f=async r=>{};var a=({toolName:r,handler:e})=>async n=>{s.info({msg:`Tool execution started: ${r}`,params:n});try{await m();let i=await e({...n,confirmedCommand:!0});return s.info({msg:`Tool execution successful: ${r}`}),i}catch(i){throw s.error({err:i,params:n,msg:`Tool execution failed: ${r}`}),i}};var v=async r=>{for(let e of l())r.registerTool(e.name,{description:e.description,inputSchema:e.inputSchema,outputSchema:e.outputSchema},a({toolName:e.name,handler:e.handler}))};async function x(){let r=new h({name:"infra-kit",version:"1.0.0"},{capabilities:{resources:{},tools:{},prompts:{}}});return await u(r),await f(r),await v(r),r}var t=p(),T=async()=>{let r;try{r=await x(),t.info("MCP Server instance created")}catch(e){t.error({err:e,msg:"Failed to create MCP server"}),t.error("Fatal error during server creation."),S.exit(1)}try{let e=new g;await r.connect(e),t.info({msg:"Server connected to transport. Ready."})}catch(e){t.error({err:e,msg:"Failed to initialize server"}),t.error("Fatal error during server transport init."),S.exit(1)}};d(t);T();
|
|
2
2
|
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/entry/mcp.ts", "../src/lib/error-handlers/index.ts", "../src/mcp/server.ts", "../src/mcp/prompts/index.ts", "../src/mcp/resources/index.ts", "../src/lib/tool-handler/tool-handler.ts", "../src/mcp/tools/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport process from 'node:process'\n\nimport { setupErrorHandlers } from 'src/lib/error-handlers'\nimport { initLoggerMcp } from 'src/lib/logger'\n\nimport { createMcpServer } from '../mcp/server'\n\nconst logger = initLoggerMcp()\n\nconst startServer = async () => {\n let server\n\n try {\n server = await createMcpServer()\n\n logger.info('MCP Server instance created')\n } catch (error) {\n logger.error({ err: error, msg: 'Failed to create MCP server' })\n logger.error(`Fatal error during server creation.`)\n\n process.exit(1)\n }\n\n try {\n const transport = new StdioServerTransport()\n\n await server.connect(transport)\n\n logger.info({ msg: 'Server connected to transport. Ready.' })\n } catch (error) {\n logger.error({ err: error, msg: 'Failed to initialize server' })\n logger.error(`Fatal error during server transport init.`)\n\n process.exit(1)\n }\n}\n\n// Setup error handlers\nsetupErrorHandlers(logger)\n\n// Start the server\nstartServer()\n", "import process from 'node:process'\nimport type { Logger } from 'pino'\n\nimport { LOG_FILE_PATH } from '../logger/index'\n\n/**\n * Setup error handlers for the application\n * @param logger - The logger instance\n *\n * ONLY FOR SERVER!\n */\nexport const setupErrorHandlers = (logger: Logger) => {\n process.on('SIGINT', () => {\n logger.info({ msg: 'Received SIGINT. Shutting down...' })\n process.exit(0)\n })\n\n process.on('SIGTERM', () => {\n logger.info({ msg: 'Received SIGTERM. Shutting down...' })\n process.exit(0)\n })\n\n process.on('uncaughtException', (error) => {\n logger.fatal({ err: error, msg: 'Uncaught Exception' })\n logger.error(`Uncaught Exception! Check ${LOG_FILE_PATH}. Shutting down...`)\n logger.flush()\n process.exit(1)\n })\n\n process.on('unhandledRejection', (reason, promise) => {\n logger.fatal({ reason, promise, msg: 'Unhandled Rejection' })\n logger.error(`Unhandled Rejection! Check ${LOG_FILE_PATH}. Shutting down...`)\n logger.flush()\n process.exit(1)\n })\n}\n", "import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { initializePrompts } from './prompts'\nimport { initializeResources } from './resources'\nimport { initializeTools } from './tools'\n\nexport async function createMcpServer() {\n const server = new McpServer(\n {\n name: 'infra-kit',\n version: '1.0.0',\n },\n {\n capabilities: {\n resources: {},\n tools: {},\n prompts: {},\n },\n },\n )\n\n await initializePrompts(server)\n await initializeResources(server)\n await initializeTools(server)\n\n return server\n}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nexport const initializePrompts = async (_server: McpServer) => {}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nexport const initializeResources = async (_server: McpServer) => {}\n", "import { logger } from 'src/lib/logger'\nimport type { ToolsExecutionResult } from 'src/types'\n\ninterface ToolHandlerArgs {\n toolName: string\n handler: (params: any) => Promise<ToolsExecutionResult>\n}\n\nexport const createToolHandler = (
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["StdioServerTransport", "process", "process", "setupErrorHandlers", "logger", "process", "error", "LOG_FILE_PATH", "reason", "promise", "McpServer", "initializePrompts", "_server", "initializeResources", "_server", "createToolHandler", "
|
|
4
|
+
"sourcesContent": ["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport process from 'node:process'\n\nimport { setupErrorHandlers } from 'src/lib/error-handlers'\nimport { initLoggerMcp } from 'src/lib/logger'\n\nimport { createMcpServer } from '../mcp/server'\n\nconst logger = initLoggerMcp()\n\nconst startServer = async () => {\n let server\n\n try {\n server = await createMcpServer()\n\n logger.info('MCP Server instance created')\n } catch (error) {\n logger.error({ err: error, msg: 'Failed to create MCP server' })\n logger.error(`Fatal error during server creation.`)\n\n process.exit(1)\n }\n\n try {\n const transport = new StdioServerTransport()\n\n await server.connect(transport)\n\n logger.info({ msg: 'Server connected to transport. Ready.' })\n } catch (error) {\n logger.error({ err: error, msg: 'Failed to initialize server' })\n logger.error(`Fatal error during server transport init.`)\n\n process.exit(1)\n }\n}\n\n// Setup error handlers\nsetupErrorHandlers(logger)\n\n// Start the server\nstartServer()\n", "import process from 'node:process'\nimport type { Logger } from 'pino'\n\nimport { LOG_FILE_PATH } from '../logger/index'\n\n/**\n * Setup error handlers for the application\n * @param logger - The logger instance\n *\n * ONLY FOR SERVER!\n */\nexport const setupErrorHandlers = (logger: Logger) => {\n process.on('SIGINT', () => {\n logger.info({ msg: 'Received SIGINT. Shutting down...' })\n process.exit(0)\n })\n\n process.on('SIGTERM', () => {\n logger.info({ msg: 'Received SIGTERM. Shutting down...' })\n process.exit(0)\n })\n\n process.on('uncaughtException', (error) => {\n logger.fatal({ err: error, msg: 'Uncaught Exception' })\n logger.error(`Uncaught Exception! Check ${LOG_FILE_PATH}. Shutting down...`)\n logger.flush()\n process.exit(1)\n })\n\n process.on('unhandledRejection', (reason, promise) => {\n logger.fatal({ reason, promise, msg: 'Unhandled Rejection' })\n logger.error(`Unhandled Rejection! Check ${LOG_FILE_PATH}. Shutting down...`)\n logger.flush()\n process.exit(1)\n })\n}\n", "import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { initializePrompts } from './prompts'\nimport { initializeResources } from './resources'\nimport { initializeTools } from './tools'\n\nexport async function createMcpServer() {\n const server = new McpServer(\n {\n name: 'infra-kit',\n version: '1.0.0',\n },\n {\n capabilities: {\n resources: {},\n tools: {},\n prompts: {},\n },\n },\n )\n\n await initializePrompts(server)\n await initializeResources(server)\n await initializeTools(server)\n\n return server\n}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nexport const initializePrompts = async (_server: McpServer) => {}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nexport const initializeResources = async (_server: McpServer) => {}\n", "import { ensureUserProjectConfig } from 'src/lib/config-bootstrap'\nimport { logger } from 'src/lib/logger'\nimport type { ToolsExecutionResult } from 'src/types'\n\ninterface ToolHandlerArgs {\n toolName: string\n handler: (params: any) => Promise<ToolsExecutionResult>\n}\n\nexport const createToolHandler = ({\n toolName,\n handler,\n}: ToolHandlerArgs): ((params: unknown) => Promise<ToolsExecutionResult>) => {\n return async (params: unknown) => {\n logger.info({ msg: `Tool execution started: ${toolName}`, params })\n try {\n // MCP entry-boundary seed. This wrapper is the sole chokepoint for every exposed tool\n // (`src/mcp/tools/index.ts` holds the only `registerTool` call), and the `mcp` command itself is\n // in program.ts's SEED_EXCLUDED \u2014 so an MCP server seeds lazily, on its FIRST tool invocation,\n // and a server that never receives one never writes to $HOME. The once-per-process guard inside\n // ensureUserProjectConfig makes calls 2..N free on this long-lived process.\n //\n // Deliberately INSIDE the try, not before it: defense-in-depth. ensureUserProjectConfig is\n // contractually never-throw, but if that contract ever broke, an unhandled rejection here would\n // take down the long-lived server. Inside the try it degrades to an ordinary tool error.\n await ensureUserProjectConfig()\n\n const payload = await handler({ ...(params as object), confirmedCommand: true })\n\n logger.info({ msg: `Tool execution successful: ${toolName}` })\n\n return payload\n } catch (error) {\n logger.error({\n err: error,\n params,\n msg: `Tool execution failed: ${toolName}`,\n })\n\n throw error\n }\n }\n}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { getExposedMcpTools } from 'src/lib/command-catalog'\nimport { createToolHandler } from 'src/lib/tool-handler'\n\nexport const initializeTools = async (server: McpServer) => {\n // The registered tool set is derived from the single command catalog, filtered\n // by its explicit `mcpExposed` allowlist. doctor / vendor-sync / vendor-manifest\n // are intentionally excluded there and must never be registered here.\n for (const tool of getExposedMcpTools()) {\n server.registerTool(\n tool.name,\n {\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n },\n createToolHandler({ toolName: tool.name, handler: tool.handler }),\n )\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["StdioServerTransport", "process", "process", "setupErrorHandlers", "logger", "process", "error", "LOG_FILE_PATH", "reason", "promise", "McpServer", "initializePrompts", "_server", "initializeResources", "_server", "createToolHandler", "toolName", "handler", "params", "logger", "ensureUserProjectConfig", "payload", "error", "initializeTools", "server", "tool", "getExposedMcpTools", "createToolHandler", "createMcpServer", "server", "McpServer", "initializePrompts", "initializeResources", "initializeTools", "logger", "initLoggerMcp", "startServer", "server", "createMcpServer", "error", "process", "transport", "StdioServerTransport", "setupErrorHandlers"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{a as h,c as f}from"./chunk-C2HRONYA.js";import{render as H}from"ink";import W from"node:process";import{Box as c,Static as D,Text as l}from"ink";import{useEffect as z,useState as F}from"react";import{jsx as d,jsxs as L}from"react/jsx-runtime";var R=r=>{let{phase:e,narration:t,readyLines:i}=r,[s,a]=F(0);return z(()=>{if(i)return;let n=setInterval(()=>{a(o=>o+1)},O);return()=>{clearInterval(n)}},[i]),i?d(D,{items:i,children:(n,o)=>d(l,{children:n},o)}):L(c,{flexDirection:"column",children:[L(c,{children:[d(l,{color:"cyan",children:m[s%m.length]}),d(l,{dimColor:!0,children:` ${e}`})]}),t?d(l,{dimColor:!0,children:t}):null]})},m=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],O=80;import{Box as T,Static as C,Text as y}from"ink";import{Fragment as P,jsx as u,jsxs as U}from"react/jsx-runtime";var x=r=>{let{headerLines:e,logLines:t,footerLines:i}=r,s=[...e,...t],a=E(i,r.maxFooterLines);return U(P,{children:[u(C,{items:s,children:(n,o)=>u(y,{children:n},o)}),u(T,{flexDirection:"column",children:a.map((n,o)=>u(y,{children:n},o))})]})},E=(r,e)=>{if(e==null||e<1||r.length<=e)return r;let t=r.slice(0,e-1);return[...t,` \u2026 +${r.length-t.length} more`]};import k from"node:process";var _="\x1B[2K";var S="\x1B[?25h",N=(r,e)=>`\x1B[${r};${e}r`,b=()=>"\x1B[r",v=(r,e)=>`\x1B[${r};${e}H`,I=r=>r<=0?"":`${`
|
|
2
|
+
`.repeat(r)}\x1B[${r}A`,g=(r,e)=>{if(r.length===0)return"";let t=e-r.length+1;return`\x1B7${r.map((s,a)=>`${v(t+a,1)}${_}${s}`).join("")}\x1B8`},w=(r,e)=>e-r<1?"":`${I(r)}${N(1,e-r)}`;var p=class{renderer;stdout;verbose;footerLines=[];installed=!1;resizeBound=!1;constructor(e){this.verbose=e.verbose,this.stdout=e.stdout??k.stdout,this.renderer=new f({appendLog:e.appendLog,verbose:this.verbose,isTTY:!!this.stdout.isTTY,write:t=>{this.stdout.write(t)}})}logFn=(e,t="info")=>{this.log(e,t)};rows(){let e=this.stdout.rows;return e!=null&&e>0?e:24}writeLines(e){for(let t of e)this.stdout.write(`${t}
|
|
3
|
+
`)}narrate(e){this.renderer.teeOnly(e,"info"),this.verbose&&this.stdout.write(`${e}
|
|
4
|
+
`)}log(e,t="info"){(t!=="debug"||this.verbose)&&this.stdout.write(`${e}
|
|
5
|
+
`),this.renderer.teeOnly(e,t)}bootStep(e){this.narrate(e)}stopSpinner(){}applyRegion(){let e=this.rows(),t=w(this.footerLines.length,e);return t===""?!1:(this.stdout.write(`${t}${g(this.footerLines,e)}`),!0)}bindResize(){this.resizeBound||typeof this.stdout.on!="function"||(this.stdout.on("resize",this.onResize),this.resizeBound=!0)}ready(e){this.footerLines=this.renderer.formatReadyLines(e);for(let t of this.footerLines)this.renderer.teeOnly(t,"info");if(!this.stdout.isTTY){this.writeLines(this.footerLines);return}if(!this.applyRegion()){this.writeLines(this.footerLines);return}this.installed=!0,this.bindResize()}event(e){let t=` ${h(new Date)} ${e.tag} ${e.text}`;this.stdout.write(`${t}
|
|
6
|
+
`),this.renderer.teeOnly(`${e.tag} ${e.text}`,"info")}refresh(e){this.footerLines=this.renderer.formatReadyLines(e),this.installed&&this.stdout.write(g(this.footerLines,this.rows()))}onResize=()=>{this.installed&&(this.applyRegion()||this.dispose())};dispose(){if(this.resizeBound&&typeof this.stdout.off=="function"&&(this.stdout.off("resize",this.onResize),this.resizeBound=!1),this.installed){let e=this.rows();this.stdout.write(`${b()}${v(e,1)}
|
|
7
|
+
${S}`),this.installed=!1}}};import{jsx as B}from"react/jsx-runtime";var $=class{renderer;renderFn;stdout;verbose;now;appendLog;instance=null;finalized=!1;handoff=null;persistent=!1;phase="";narration="";headerLines=[];logLines=[];footerLines=[];constructor(e){this.verbose=e.verbose??!1,this.renderFn=e.render??H,this.stdout=e.stdout??W.stdout,this.appendLog=e.appendLog,this.now=e.now??(()=>new Date),this.renderer=new f({appendLog:e.appendLog,verbose:this.verbose,isTTY:!0,write:t=>{this.stdout.write(t)},now:this.now})}logFn=(e,t="info")=>{this.log(e,t)};renderBoot(e){let t=B(R,{phase:this.phase,narration:this.narration,readyLines:e});if(this.instance){this.instance.rerender(t);return}this.instance=this.renderFn(t,{stdout:this.stdout,exitOnCtrlC:!1,patchConsole:!1})}unmount(){this.instance&&(this.instance.unmount(),this.instance=null)}narrate(e){if(this.handoff){this.handoff.narrate(e);return}if(this.renderer.teeOnly(e,"info"),this.persistent){this.verbose&&(this.logLines.push(e),this.rerenderPersistent());return}this.finalized||(this.narration=e,this.instance&&this.renderBoot())}log(e,t="info"){if(this.handoff){this.handoff.log(e,t);return}let i=t!=="debug"||this.verbose;if(this.persistent){this.renderer.teeOnly(e,t),i&&(this.logLines.push(e),this.rerenderPersistent());return}i&&this.unmount(),this.renderer.log(e,t)}bootStep(e){this.renderer.teeOnly(e,"info"),!this.finalized&&(this.phase=e,this.renderBoot())}stopSpinner(){this.persistent||this.unmount()}ready(e){if(e.hasUiChild){this.readyHandoff(e);return}this.readyPersistent(e)}readyHandoff(e){this.finalized=!0,this.unmount(),this.handoff=new p({appendLog:this.appendLog,verbose:this.verbose,stdout:this.stdout}),this.handoff.ready(e)}readyPersistent(e){this.headerLines=this.renderer.formatHeaderLines(e),this.footerLines=this.renderer.formatFooterLines(e),this.logLines=[];for(let t of this.headerLines)this.renderer.teeOnly(t,"info");this.finalized=!0,this.persistent=!0,typeof this.stdout.on=="function"&&this.stdout.on("resize",this.onResize),this.rerenderPersistent()}maxFooterLines(){return Math.max(3,(this.stdout.rows??24)-6)}rerenderPersistent(){let e=B(x,{headerLines:this.headerLines,logLines:this.logLines,footerLines:this.footerLines,maxFooterLines:this.maxFooterLines()});if(this.instance){this.instance.rerender(e);return}this.instance=this.renderFn(e,{stdout:this.stdout,exitOnCtrlC:!1,patchConsole:!1})}onResize=()=>{this.persistent&&this.rerenderPersistent()};event(e){if(this.handoff){this.handoff.event(e);return}if(this.persistent){let t=` ${h(this.now())} ${e.tag} ${e.text}`;this.renderer.teeOnly(`${e.tag} ${e.text}`,"info"),this.logLines.push(t),this.rerenderPersistent();return}this.unmount(),this.renderer.event(e)}refresh(e){if(this.handoff){this.handoff.refresh(e);return}this.persistent&&(this.footerLines=this.renderer.formatFooterLines(e),this.rerenderPersistent())}dispose(){if(this.handoff){this.handoff.dispose(),this.handoff=null;return}this.persistent&&(this.rerenderPersistent(),typeof this.stdout.off=="function"&&this.stdout.off("resize",this.onResize)),this.finalized=!0,this.persistent=!1,this.unmount()}};export{$ as PersistentInkDevUi};
|
|
8
|
+
//# sourceMappingURL=persistent-ink-dev-ui-4U4B2B7C.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/tui/dev-ui/persistent-ink-dev-ui.tsx", "../src/tui/dev-ui/boot-region.tsx", "../src/tui/dev-ui/live-region.tsx", "../src/tui/dev-ui/scroll-region-dev-ui.ts", "../src/dev/scroll-region.ts"],
|
|
4
|
+
"sourcesContent": ["import { render as inkRender } from 'ink'\nimport process from 'node:process'\nimport type { ReactElement } from 'react'\n\nimport type { DevUi, LogLevel, ReadySummary } from 'src/dev/dev-ui'\nimport { DevRenderer, formatClock } from 'src/dev/render'\n\nimport { BootRegion } from './boot-region'\nimport { LiveRegion } from './live-region'\nimport { ScrollRegionDevUi } from './scroll-region-dev-ui'\n\n/**\n * Ink-backed {@link DevUi} for `infra-kit dev` that keeps a PERSISTENT live footer after `ready` on a\n * backend-only session. It is the default UI on any interactive TTY: the boot phase is identical to the\n * plain path, but at {@link ready} it branches on `summary.hasUiChild`.\n *\n * - UI session (`hasUiChild`): unmount Ink and HAND OFF to a {@link ScrollRegionDevUi}, which pins the\n * ready header as a DECSTBM scroll-region footer. The `turbo run dev` child's piped output makes the\n * tail unbounded, and Ink repaints its live region on every appended line; the scroll region appends\n * with a single write instead. The scroll-region UI owns `ready`/`event`/`log`/`refresh`/`dispose`\n * for the rest of the session.\n * - Backend-only (`!hasUiChild`): mount {@link LiveRegion} and STAY mounted \u2014 the header is committed\n * once via `<Static>`, logs accrete above the footer, and `refresh` repaints the footer in place as\n * health flips.\n *\n * The embedded {@link DevRenderer} owns the file-tee format (`teeOnly`) exactly as in `InkDevUi`; in\n * persistent mode it is used ONLY for teeing (never its direct stdout write, which would collide with\n * the live region). Every component here is OUTPUT-ONLY (no `useInput`) \u2014 see live-region.tsx.\n */\n\n/** The subset of an Ink render handle this UI drives \u2014 shared by `ink` and `ink-testing-library`. */\ninterface RenderHandle {\n rerender: (tree: ReactElement) => void\n unmount: () => void\n}\n\n/** Render seam: `ink`'s `render` by default; `ink-testing-library`'s `render` is injected in tests. */\ntype RenderFn = (tree: ReactElement, options?: unknown) => RenderHandle\n\nexport interface PersistentInkDevUiDeps {\n /** File tee \u2014 the same appender the runner hands the plain renderer. */\n appendLog: (text: string) => void\n /** Mirrors the renderer's verbose gate: only in verbose does a `debug` log reach the terminal. */\n verbose?: boolean\n /** Clock seam (for deterministic tee + event timestamps in tests). */\n now?: () => Date\n /** Render seam (default `ink`'s `render`); tests inject `ink-testing-library`'s `render`. */\n render?: RenderFn\n /** Target stream (default `process.stdout`). */\n stdout?: NodeJS.WriteStream\n}\n\nexport class PersistentInkDevUi implements DevUi {\n private readonly renderer: DevRenderer\n private readonly renderFn: RenderFn\n private readonly stdout: NodeJS.WriteStream\n private readonly verbose: boolean\n private readonly now: () => Date\n /** The file tee appender \u2014 kept so a UI-session handoff can hand the same sink to {@link ScrollRegionDevUi}. */\n private readonly appendLog: (text: string) => void\n /** Live Ink handle, or `null` when not mounted. */\n private instance: RenderHandle | null = null\n /** True once boot is done: never mount the boot region again. */\n private finalized = false\n /**\n * The scroll-region UI a UI session hands off to at {@link ready}, or `null` for a backend-only\n * session. Once set, every post-ready seam (`log`/`event`/`refresh`/`dispose`) delegates to it.\n */\n private handoff: ScrollRegionDevUi | null = null\n /** True while the persistent live region owns the terminal (backend-only session after ready). */\n private persistent = false\n private phase = ''\n private narration = ''\n private headerLines: string[] = []\n private logLines: string[] = []\n private footerLines: string[] = []\n\n constructor(deps: PersistentInkDevUiDeps) {\n this.verbose = deps.verbose ?? false\n this.renderFn = deps.render ?? (inkRender as RenderFn)\n this.stdout = deps.stdout ?? process.stdout\n this.appendLog = deps.appendLog\n this.now =\n deps.now ??\n ((): Date => {\n return new Date()\n })\n this.renderer = new DevRenderer({\n appendLog: deps.appendLog,\n verbose: this.verbose,\n isTTY: true,\n write: (text) => {\n this.stdout.write(text)\n },\n now: this.now,\n })\n }\n\n /** `LogFn`-shaped adapter for the build runner seam. */\n readonly logFn = (message: string, level: LogLevel = 'info'): void => {\n this.log(message, level)\n }\n\n // ---- boot (identical to InkDevUi) --------------------------------------\n\n /** Mount the Ink boot region (or rerender it in place) with the current phase + narration. */\n private renderBoot(readyLines?: string[]): void {\n const tree = <BootRegion phase={this.phase} narration={this.narration} readyLines={readyLines} />\n\n if (this.instance) {\n this.instance.rerender(tree)\n\n return\n }\n this.instance = this.renderFn(tree, { stdout: this.stdout, exitOnCtrlC: false, patchConsole: false })\n }\n\n /** Unmount Ink if mounted (idempotent). */\n private unmount(): void {\n if (this.instance) {\n this.instance.unmount()\n this.instance = null\n }\n }\n\n narrate(message: string): void {\n if (this.handoff) {\n this.handoff.narrate(message)\n\n return\n }\n this.renderer.teeOnly(message, 'info')\n if (this.persistent) {\n // Visible only in verbose (matches the plain renderer); tee already happened above.\n if (this.verbose) {\n this.logLines.push(message)\n this.rerenderPersistent()\n }\n\n return\n }\n if (this.finalized) {\n return\n }\n this.narration = message\n if (this.instance) {\n this.renderBoot()\n }\n }\n\n log(message: string, level: LogLevel = 'info'): void {\n if (this.handoff) {\n this.handoff.log(message, level)\n\n return\n }\n const willWrite = level !== 'debug' || this.verbose\n\n if (this.persistent) {\n // Never the embedded renderer's direct stdout write here \u2014 append to the live region instead.\n this.renderer.teeOnly(message, level)\n if (willWrite) {\n this.logLines.push(message)\n this.rerenderPersistent()\n }\n\n return\n }\n if (willWrite) {\n this.unmount()\n }\n this.renderer.log(message, level)\n }\n\n bootStep(phase: string): void {\n this.renderer.teeOnly(phase, 'info')\n if (this.finalized) {\n return\n }\n this.phase = phase\n this.renderBoot()\n }\n\n stopSpinner(): void {\n if (!this.persistent) {\n this.unmount()\n }\n }\n\n // ---- ready + persistence -----------------------------------------------\n\n ready(summary: ReadySummary): void {\n if (summary.hasUiChild) {\n this.readyHandoff(summary)\n\n return\n }\n this.readyPersistent(summary)\n }\n\n /**\n * UI session: unmount Ink and hand off to a {@link ScrollRegionDevUi}, which pins the ready header as\n * a scroll-region footer above the `turbo run dev` child's high-volume piped tail. From here on every\n * post-ready seam delegates to the handoff (see {@link log}/{@link event}/{@link refresh}/{@link dispose}).\n */\n private readyHandoff(summary: ReadySummary): void {\n this.finalized = true\n this.unmount()\n this.handoff = new ScrollRegionDevUi({ appendLog: this.appendLog, verbose: this.verbose, stdout: this.stdout })\n this.handoff.ready(summary)\n }\n\n /** Backend-only session: commit the header, tee it, mount the live footer, and STAY mounted. */\n private readyPersistent(summary: ReadySummary): void {\n this.headerLines = this.renderer.formatHeaderLines(summary)\n this.footerLines = this.renderer.formatFooterLines(summary)\n this.logLines = []\n for (const line of this.headerLines) {\n this.renderer.teeOnly(line, 'info')\n }\n this.finalized = true\n this.persistent = true\n // Re-clamp the footer when the terminal resizes. Guarded: an injected (test/piped) stream may not\n // be an EventEmitter TTY.\n if (typeof this.stdout.on === 'function') {\n this.stdout.on('resize', this.onResize)\n }\n this.rerenderPersistent()\n }\n\n /**\n * Cap for the pinned footer: it is a live region and cannot scroll, so it must never exceed the\n * viewport. Reserve rows for the scrolling log tail above it; fall back to an 80\u00D724 assumption when\n * the stream reports no row count (piped / test stdout).\n */\n private maxFooterLines(): number {\n return Math.max(3, (this.stdout.rows ?? 24) - 6)\n }\n\n /** Mount or repaint the live region from the current header / logs / footer snapshot. */\n private rerenderPersistent(): void {\n const tree = (\n <LiveRegion\n headerLines={this.headerLines}\n logLines={this.logLines}\n footerLines={this.footerLines}\n maxFooterLines={this.maxFooterLines()}\n />\n )\n\n if (this.instance) {\n this.instance.rerender(tree)\n\n return\n }\n this.instance = this.renderFn(tree, { stdout: this.stdout, exitOnCtrlC: false, patchConsole: false })\n }\n\n /** Re-clamp + repaint the footer on terminal resize (SIGWINCH), so a shrink never overflows the viewport. */\n private readonly onResize = (): void => {\n if (this.persistent) {\n this.rerenderPersistent()\n }\n }\n\n event(input: { tag: string; text: string }): void {\n if (this.handoff) {\n this.handoff.event(input)\n\n return\n }\n if (this.persistent) {\n const line = ` ${formatClock(this.now())} ${input.tag} ${input.text}`\n\n this.renderer.teeOnly(`${input.tag} ${input.text}`, 'info')\n this.logLines.push(line)\n this.rerenderPersistent()\n\n return\n }\n this.unmount()\n this.renderer.event(input)\n }\n\n /** Repaint the live footer in place from a fresh summary (header + logs untouched). No-op if not persistent. */\n refresh(summary: ReadySummary): void {\n if (this.handoff) {\n this.handoff.refresh(summary)\n\n return\n }\n // A session still in boot has no live footer to refresh.\n if (!this.persistent) {\n return\n }\n this.footerLines = this.renderer.formatFooterLines(summary)\n this.rerenderPersistent()\n }\n\n dispose(): void {\n if (this.handoff) {\n this.handoff.dispose()\n this.handoff = null\n\n return\n }\n if (this.persistent) {\n // Commit the last footer state, then release the terminal.\n this.rerenderPersistent()\n if (typeof this.stdout.off === 'function') {\n this.stdout.off('resize', this.onResize)\n }\n }\n this.finalized = true\n this.persistent = false\n this.unmount()\n }\n}\n", "import { Box, Static, Text } from 'ink'\nimport { useEffect, useState } from 'react'\n\n/**\n * The Ink boot region for `infra-kit dev`: an animated spinner + phase line while the server boots,\n * then the ready header committed to scrollback via `<Static>`.\n *\n * HARD INVARIANT \u2014 this component (and every boot component under `src/tui/dev-ui/`) is OUTPUT-ONLY.\n * It MUST NEVER call `useInput`. `useInput` arms Ink's raw mode + Ctrl-C interception, which would\n * steal SIGINT from the process signal handler that owns dev-server shutdown. The spinner animates via\n * a self-contained interval only; there is no keyboard input during boot. Do not add `useInput` here.\n */\n\nexport interface BootRegionProps {\n /** Current boot phase, shown beside the spinner. */\n phase: string\n /** Latest narration detail (dim subtitle under the spinner); empty hides the line. */\n narration: string\n /**\n * When set, boot is done: the pre-formatted ready-header lines are committed to scrollback via\n * `<Static>` and the transient spinner region is gone. The renderer unmounts right after.\n */\n readyLines?: string[]\n}\n\n/** Animated boot spinner + phase/narration, or the committed ready header once `readyLines` is set. */\nexport const BootRegion = (props: BootRegionProps) => {\n const { phase, narration, readyLines } = props\n\n const [frame, setFrame] = useState(0)\n\n useEffect(() => {\n // No spinner once the ready header is committed.\n if (readyLines) {\n return\n }\n\n const timer = setInterval(() => {\n setFrame((f) => {\n return f + 1\n })\n }, SPINNER_INTERVAL_MS)\n\n return () => {\n clearInterval(timer)\n }\n }, [readyLines])\n\n if (readyLines) {\n // `<Static>` writes each line once, above the (now empty) live region, so the header persists in\n // scrollback after unmount. Lines are pre-formatted by DevRenderer.formatReadyLines.\n return (\n <Static items={readyLines}>\n {(line, index) => {\n return <Text key={index}>{line}</Text>\n }}\n </Static>\n )\n }\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color=\"cyan\">{SPINNER_FRAMES[frame % SPINNER_FRAMES.length]}</Text>\n <Text dimColor>{` ${phase}`}</Text>\n </Box>\n {narration ? <Text dimColor>{narration}</Text> : null}\n </Box>\n )\n}\n\nconst SPINNER_FRAMES = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F']\nconst SPINNER_INTERVAL_MS = 80\n", "import { Box, Static, Text } from 'ink'\n\n/**\n * The persistent Ink live region for `infra-kit dev` (backend-only sessions): the committed header +\n * growing log scrollback above, and a live health footer re-rendered in place below.\n *\n * HARD INVARIANT \u2014 this component (and every component under `src/tui/dev-ui/`) is OUTPUT-ONLY. It\n * MUST NEVER call `useInput`. `useInput` arms Ink's raw mode + Ctrl-C interception, which would steal\n * SIGINT from the process signal handler that owns dev-server shutdown. There is no keyboard input in\n * the live region \u2014 it is props only, with no state, no effects, no input. Do not add `useInput` here.\n */\n\nexport interface LiveRegionProps {\n /** Reference header, committed once to scrollback via `<Static>` (from `formatHeaderLines`). */\n headerLines: string[]\n /** Log lines committed above the footer as they arrive (also via the SAME `<Static>`). */\n logLines: string[]\n /** Live footer, re-rendered in place on every health change (from `formatFooterLines`). */\n footerLines: string[]\n /**\n * Cap on rendered footer rows so a many-backend session never eats a short viewport (the footer is a\n * live region \u2014 it cannot scroll). Overflow collapses into a trailing dim `\u2026 +N more` line. Omit for\n * no cap. The pinned footer must stay short; the full health picture is always in the log file.\n */\n maxFooterLines?: number\n}\n\n/**\n * A SINGLE `<Static>` streams the header then the growing logs (Ink writes only newly-appended items\n * each commit, so the header lands first and logs accrete beneath it \u2014 one block, never two). The\n * footer is an ordinary `<Box>`, so it repaints in place on each rerender without touching scrollback.\n */\nexport const LiveRegion = (props: LiveRegionProps) => {\n const { headerLines, logLines, footerLines } = props\n\n const staticItems = [...headerLines, ...logLines]\n const footer = clampFooter(footerLines, props.maxFooterLines)\n\n return (\n <>\n <Static items={staticItems}>\n {(line, index) => {\n return <Text key={index}>{line}</Text>\n }}\n </Static>\n <Box flexDirection=\"column\">\n {footer.map((line, index) => {\n return <Text key={index}>{line}</Text>\n })}\n </Box>\n </>\n )\n}\n\n/** Cap the footer to `max` rows, collapsing the overflow into one trailing ` \u2026 +N more` line. */\nexport const clampFooter = (lines: string[], max?: number): string[] => {\n if (max == null || max < 1 || lines.length <= max) {\n return lines\n }\n\n const shown = lines.slice(0, max - 1)\n\n return [...shown, ` \u2026 +${lines.length - shown.length} more`]\n}\n", "import process from 'node:process'\n\nimport type { DevUi, LogLevel, ReadySummary } from 'src/dev/dev-ui'\nimport { DevRenderer, formatClock } from 'src/dev/render'\nimport { SHOW_CURSOR, installRegion, moveTo, paintFooter, resetScrollRegion } from 'src/dev/scroll-region'\n\n/**\n * A {@link DevUi} that pins the `infra-kit dev` ready header to the bottom of the terminal as a STICKY\n * FOOTER using a DECSTBM scroll region, for sessions that run a `turbo run dev` UI child\n * (`summary.hasUiChild`). {@link PersistentInkDevUi} hands off to it at `ready`.\n *\n * Deliberately NO React / NO Ink. A UI session's tail is unbounded \u2014 every vite/HMR line the piped\n * child emits arrives as an `event` \u2014 and Ink's reconciler repaints its live region on each one. The\n * scroll region instead confines ALL scrolling (`log` + `event` lines) to the rows above the footer\n * using terminal escapes alone, so an append costs one `write` and the footer never repaints. See\n * {@link file://../../dev/scroll-region.ts}.\n *\n * The embedded {@link DevRenderer} is the single source of the file-tee format (`teeOnly`) and the\n * ready-header layout (`formatReadyLines`) \u2014 reused, never duplicated \u2014 exactly like the Ink UIs. Every\n * terminal write goes through the injected `stdout` so tests drive a fake stream.\n */\n\nexport interface ScrollRegionDevUiDeps {\n /** File tee \u2014 the same appender the runner hands every renderer. */\n appendLog: (text: string) => void\n /** Only in verbose does boot narration reach the terminal (it is always tee'd to the file). */\n verbose: boolean\n /** Target stream (default `process.stdout`). Its `isTTY`/`rows` gate the whole feature. */\n stdout?: NodeJS.WriteStream\n}\n\nexport class ScrollRegionDevUi implements DevUi {\n /** Embedded plain renderer: owns the file-tee format + the ready-header layout. Never drives a spinner. */\n private readonly renderer: DevRenderer\n private readonly stdout: NodeJS.WriteStream\n private readonly verbose: boolean\n /** The current footer block (the full ready header), recomputed on `ready`/`refresh`. */\n private footerLines: string[] = []\n /** True while a scroll region is installed and the footer is live (false when disabled / disposed). */\n private installed = false\n /** True once the resize listener is attached, so it is added at most once and removed exactly once. */\n private resizeBound = false\n\n constructor(deps: ScrollRegionDevUiDeps) {\n this.verbose = deps.verbose\n this.stdout = deps.stdout ?? process.stdout\n this.renderer = new DevRenderer({\n appendLog: deps.appendLog,\n verbose: this.verbose,\n // Colors follow the real stream: a TTY footer is colored; a piped / captured stream stays plain.\n isTTY: Boolean(this.stdout.isTTY),\n write: (text) => {\n this.stdout.write(text)\n },\n })\n }\n\n /** `LogFn`-shaped adapter for the build runner seam. */\n readonly logFn = (message: string, level: LogLevel = 'info'): void => {\n this.log(message, level)\n }\n\n /**\n * Row count, falling back to 24 (the classic terminal default) for any non-positive or absent value.\n *\n * `rows` is NOT merely `undefined` on a row-less stream: a pty allocated without a window size \u2014 what\n * `script(1)` gives us, and what some CI runners and terminal emulators report before their first\n * `SIGWINCH` \u2014 sets `isTTY === true` alongside `rows === 0`. A `??` fallback passes that `0` straight\n * through, `installRegion` then reads the terminal as \"too short\", and the footer silently never\n * installs on a terminal that is in fact perfectly tall enough.\n */\n private rows(): number {\n const reported = this.stdout.rows\n\n return reported != null && reported > 0 ? reported : 24\n }\n\n /** Write each line as its own terminal row (used for the non-TTY / too-short plain fallback). */\n private writeLines(lines: readonly string[]): void {\n for (const line of lines) {\n this.stdout.write(`${line}\\n`)\n }\n }\n\n narrate(message: string): void {\n this.renderer.teeOnly(message, 'info')\n if (this.verbose) {\n this.stdout.write(`${message}\\n`)\n }\n }\n\n /** A general message: lands inside the scroll region and scrolls normally, plus a file tee. */\n log(message: string, level: LogLevel = 'info'): void {\n if (level !== 'debug' || this.verbose) {\n this.stdout.write(`${message}\\n`)\n }\n this.renderer.teeOnly(message, level)\n }\n\n /** Boot narration only \u2014 no spinner (a spinner would fight the child for the cursor). */\n bootStep(phase: string): void {\n this.narrate(phase)\n }\n\n /** No-op: this UI never runs a spinner. */\n stopSpinner(): void {\n // Nothing to stop.\n }\n\n /**\n * Install (or re-install) the scroll region and paint the footer for the CURRENT terminal size.\n * Returns `false` when the terminal is too short to host both a region and the footer \u2014 the caller\n * then falls back to a one-shot plain print (or disposes, on a shrink).\n */\n private applyRegion(): boolean {\n const rows = this.rows()\n const region = installRegion(this.footerLines.length, rows)\n\n if (region === '') return false\n this.stdout.write(`${region}${paintFooter(this.footerLines, rows)}`)\n\n return true\n }\n\n /** Attach the resize listener once (guarded \u2014 an injected stream may not be an EventEmitter TTY). */\n private bindResize(): void {\n if (this.resizeBound || typeof this.stdout.on !== 'function') return\n this.stdout.on('resize', this.onResize)\n this.resizeBound = true\n }\n\n ready(summary: ReadySummary): void {\n this.footerLines = this.renderer.formatReadyLines(summary)\n for (const line of this.footerLines) {\n this.renderer.teeOnly(line, 'info')\n }\n\n // Not a real terminal (piped / test capture): just print the header once, no region.\n if (!this.stdout.isTTY) {\n this.writeLines(this.footerLines)\n\n return\n }\n\n // Too short for a region \u2192 print plainly and leave the feature disabled (installed stays false).\n if (!this.applyRegion()) {\n this.writeLines(this.footerLines)\n\n return\n }\n this.installed = true\n this.bindResize()\n }\n\n /** One tagged, timestamped tail line \u2014 scrolls inside the region like any other `log`. */\n event(input: { tag: string; text: string }): void {\n const line = ` ${formatClock(new Date())} ${input.tag} ${input.text}`\n\n this.stdout.write(`${line}\\n`)\n this.renderer.teeOnly(`${input.tag} ${input.text}`, 'info')\n }\n\n /** Recompute the footer and repaint it in place \u2014 nothing else is reprinted. No-op when not installed. */\n refresh(summary: ReadySummary): void {\n this.footerLines = this.renderer.formatReadyLines(summary)\n if (!this.installed) return\n this.stdout.write(paintFooter(this.footerLines, this.rows()))\n }\n\n /**\n * Re-clamp on terminal resize (SIGWINCH, surfaced as the stream's `resize` event): re-run the region\n * install + repaint for the new size. If the new size is too short to host the footer, dispose\n * gracefully rather than paint a broken region.\n */\n private readonly onResize = (): void => {\n if (!this.installed) return\n if (!this.applyRegion()) {\n this.dispose()\n }\n }\n\n /**\n * Release the terminal: drop the resize listener, reset the scroll region, drop the cursor onto the\n * last row with a fresh newline, and restore the cursor. Idempotent and safe if never installed \u2014 the\n * region escapes are emitted only while a region is actually live, so a second call (or a disposal of\n * a never-installed instance) writes nothing and leaves the terminal usable.\n */\n dispose(): void {\n if (this.resizeBound && typeof this.stdout.off === 'function') {\n this.stdout.off('resize', this.onResize)\n this.resizeBound = false\n }\n if (this.installed) {\n const rows = this.rows()\n\n this.stdout.write(`${resetScrollRegion()}${moveTo(rows, 1)}\\n${SHOW_CURSOR}`)\n this.installed = false\n }\n }\n}\n", "/**\n * Pure, side-effect-free ANSI builders for a terminal SCROLL REGION (DECSTBM) sticky footer.\n *\n * `infra-kit dev` uses these to pin the ready-header block to the bottom of the terminal while a\n * `turbo run dev` UI child owns the same TTY via inherited stdio. DECSTBM (`ESC [ top ; bottom r`)\n * confines normal scrolling \u2014 from both the runner's own writes AND the inherited child's raw bytes \u2014\n * to the region ABOVE the footer, so the footer rows are never scrolled away. That is why this works\n * where a piping approach could not: the child keeps inherited stdio and needs no per-framework flags.\n *\n * Every function here is a pure string builder: no I/O, no `process` access, no terminal queries. The\n * caller ({@link file://../tui/dev-ui/scroll-region-dev-ui.ts}) owns all writes, the row count, and the\n * TTY guard. Each builder is unit-tested with exact-string assertions.\n */\n\n/** Save the cursor position (DECSC). Paired with {@link RESTORE_CURSOR} around a footer repaint. */\nexport const SAVE_CURSOR = '\\x1B7'\n/** Restore the cursor position saved by {@link SAVE_CURSOR} (DECRC). */\nexport const RESTORE_CURSOR = '\\x1B8'\n/** Erase the entire current line without moving the cursor (EL 2), so a repaint leaves no stale glyphs. */\nexport const CLEAR_LINE = '\\x1B[2K'\n/** Hide the cursor (DECTCEM off) \u2014 avoids a cursor flicker while the footer repaints. */\nexport const HIDE_CURSOR = '\\x1B[?25l'\n/** Show the cursor (DECTCEM on) \u2014 restored on teardown so the terminal is left usable. */\nexport const SHOW_CURSOR = '\\x1B[?25h'\n\n/**\n * Set the vertical scroll region (DECSTBM) to lines `top..bottom` inclusive, 1-indexed. Scrolling is\n * thereafter confined to that band; the rows below `bottom` become a static footer area.\n */\nexport const setScrollRegion = (top: number, bottom: number): string => {\n return `\\x1B[${top};${bottom}r`\n}\n\n/** Reset the scroll region to the full screen (DECSTBM with no params) \u2014 the teardown counterpart. */\nexport const resetScrollRegion = (): string => {\n return '\\x1B[r'\n}\n\n/** Absolute cursor move (CUP) to `row`,`col`, both 1-indexed. */\nexport const moveTo = (row: number, col: number): string => {\n return `\\x1B[${row};${col}H`\n}\n\n/**\n * Scroll existing content up by `n` blank lines, then return the cursor to its starting row, so the\n * bottom `n` rows are cleared for a footer WITHOUT overwriting whatever was already on screen. Emits\n * `n` newlines (which scroll the viewport) followed by cursor-up-`n` (CUU). Returns `''` for `n <= 0`.\n */\nexport const reserveRows = (n: number): string => {\n if (n <= 0) return ''\n\n return `${'\\n'.repeat(n)}\\x1B[${n}A`\n}\n\n/**\n * Paint `lines` into the bottom `lines.length` rows of a `rows`-tall terminal, cursor-safe: save the\n * cursor, absolutely position + clear + write each line (top-most footer row first), then restore the\n * cursor so surrounding scroll output is undisturbed. Returns `''` when there are no lines to paint.\n */\nexport const paintFooter = (lines: readonly string[], rows: number): string => {\n if (lines.length === 0) return ''\n\n const base = rows - lines.length + 1\n const painted = lines\n .map((line, i) => {\n return `${moveTo(base + i, 1)}${CLEAR_LINE}${line}`\n })\n .join('')\n\n return `${SAVE_CURSOR}${painted}${RESTORE_CURSOR}`\n}\n\n/**\n * Reserve `footerHeight` rows at the bottom and confine scrolling to the rows above them: emit\n * {@link reserveRows} then a {@link setScrollRegion} of `1..rows - footerHeight`. Returns `''` when the\n * terminal is too short to host both a scroll region and the footer (`rows - footerHeight < 1`); the\n * caller MUST treat `''` as \"skip the whole feature\" and fall back to a one-shot plain print.\n */\nexport const installRegion = (footerHeight: number, rows: number): string => {\n if (rows - footerHeight < 1) return ''\n\n return `${reserveRows(footerHeight)}${setScrollRegion(1, rows - footerHeight)}`\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["inkRender", "process", "Box", "Static", "Text", "useEffect", "useState", "jsx", "jsxs", "BootRegion", "props", "phase", "narration", "readyLines", "frame", "setFrame", "timer", "f", "SPINNER_INTERVAL_MS", "line", "index", "SPINNER_FRAMES", "Box", "Static", "Text", "Fragment", "jsx", "jsxs", "LiveRegion", "props", "headerLines", "logLines", "footerLines", "staticItems", "footer", "clampFooter", "line", "index", "lines", "max", "shown", "process", "CLEAR_LINE", "SHOW_CURSOR", "setScrollRegion", "top", "bottom", "resetScrollRegion", "moveTo", "row", "col", "reserveRows", "n", "paintFooter", "lines", "rows", "base", "line", "i", "CLEAR_LINE", "installRegion", "footerHeight", "ScrollRegionDevUi", "deps", "process", "DevRenderer", "text", "message", "level", "reported", "lines", "line", "phase", "rows", "region", "installRegion", "paintFooter", "summary", "input", "formatClock", "resetScrollRegion", "moveTo", "SHOW_CURSOR", "jsx", "PersistentInkDevUi", "deps", "inkRender", "process", "DevRenderer", "text", "message", "level", "readyLines", "tree", "BootRegion", "willWrite", "phase", "summary", "ScrollRegionDevUi", "line", "LiveRegion", "input", "formatClock"]
|
|
7
|
+
}
|
package/dist/program.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{f as a,g as b}from"./chunk-VJPLVBRA.js";import"./chunk-LENH44GY.js";import"./chunk-52WL2IQX.js";import"./chunk-6YAKVQZN.js";import"./chunk-X2L4F2VM.js";import"./chunk-Z6KTUVIC.js";import"./chunk-F6VCGS3D.js";import"./chunk-6FU2TRU5.js";import"./chunk-SR2GFEY7.js";import"./chunk-CHETBZ6M.js";import"./chunk-4UUVKJYG.js";import"./chunk-LTKR3F4N.js";export{b as buildProgram,a as invokedViaMenu};
|
|
2
|
+
//# sourceMappingURL=program.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{d as i}from"./chunk-O3YJM6IR.js";import"./chunk-F6VCGS3D.js";import"./chunk-6FU2TRU5.js";import{a as t}from"./chunk-SR2GFEY7.js";import"./chunk-CHETBZ6M.js";import a from"node:fs";import o from"node:process";import{fileURLToPath as s}from"node:url";var m=r=>{let n=r.indexOf("--parent-pid");if(n===-1)return;let e=Number(r[n+1]);return Number.isInteger(e)&&e>0?e:void 0},p=()=>a.realpathSync(s(new URL("./cli.js",import.meta.url))),d=async()=>{await i(t.version,{selfRealPath:p(),parentPid:m(o.argv)})};d().catch(()=>{o.exitCode=0});
|
|
2
|
+
//# sourceMappingURL=update-check.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/entry/update-check.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Background auto-update worker. Spawned detached by `maybeAutoUpdate` as\n * `node dist/update-check.js --parent-pid <pid>`, and NEVER run by a human.\n *\n * This is a dedicated bundle, a sibling of `dist/cli.js`, for the same reason `dist/mcp.js` is: booting\n * `cli.js` here would re-run commander, the command catalog, `warnIfLocalInstall()`, and\n * `maybeAutoUpdate()` itself as top-level side effects \u2014 the last of which would spawn another child,\n * and another. Importing nothing from `src/entry/cli.ts` is a hard invariant, guarded by a test.\n *\n * It carries NO hashbang: nothing execs it directly, and `dist-shebang.test.ts` asserts its absence.\n * It writes nothing to stdout (it is spawned with `stdio: 'ignore'`, so there is nowhere to write).\n */\nimport fs from 'node:fs'\nimport process from 'node:process'\nimport { fileURLToPath } from 'node:url'\n\n// Deep import, not the `src/lib/update-check` barrel: the barrel re-exports `maybeAutoUpdate`, which would\n// drag the parent-side spawn path (and pino) into this worker's bundle for no reason.\nimport { runUpdateCheck } from 'src/lib/update-check/run-update-check'\n\nimport packageJson from '../../package.json' with { type: 'json' }\n\n/** `--parent-pid <pid>`, or undefined when absent/malformed (then we install without waiting). */\nconst parseParentPid = (argv: string[]): number | undefined => {\n const index = argv.indexOf('--parent-pid')\n\n if (index === -1) return undefined\n\n const pid = Number(argv[index + 1])\n\n return Number.isInteger(pid) && pid > 0 ? pid : undefined\n}\n\n/** The installed `dist/cli.js` beside this bundle \u2014 the path whose owning package manager we detect. */\nconst selfRealPath = (): string => {\n return fs.realpathSync(fileURLToPath(new URL('./cli.js', import.meta.url)))\n}\n\nconst main = async (): Promise<void> => {\n await runUpdateCheck(packageJson.version, {\n selfRealPath: selfRealPath(),\n parentPid: parseParentPid(process.argv),\n })\n}\n\n// A detached background worker must never surface a crash, a non-zero exit, or an unhandled rejection \u2014\n// there is no one to see it and nothing depends on it. `runUpdateCheck` already swallows its own\n// failures; this is the last line of defence around path resolution and JSON import.\nmain().catch(() => {\n process.exitCode = 0\n})\n"],
|
|
5
|
+
"mappings": "oKAYA,OAAOA,MAAQ,UACf,OAAOC,MAAa,eACpB,OAAS,iBAAAC,MAAqB,WAS9B,IAAMC,EAAkBC,GAAuC,CAC7D,IAAMC,EAAQD,EAAK,QAAQ,cAAc,EAEzC,GAAIC,IAAU,GAAI,OAElB,IAAMC,EAAM,OAAOF,EAAKC,EAAQ,CAAC,CAAC,EAElC,OAAO,OAAO,UAAUC,CAAG,GAAKA,EAAM,EAAIA,EAAM,MAClD,EAGMC,EAAe,IACZC,EAAG,aAAaC,EAAc,IAAI,IAAI,WAAY,YAAY,GAAG,CAAC,CAAC,EAGtEC,EAAO,SAA2B,CACtC,MAAMC,EAAeC,EAAY,QAAS,CACxC,aAAcL,EAAa,EAC3B,UAAWJ,EAAeU,EAAQ,IAAI,CACxC,CAAC,CACH,EAKAH,EAAK,EAAE,MAAM,IAAM,CACjBG,EAAQ,SAAW,CACrB,CAAC",
|
|
6
|
+
"names": ["fs", "process", "fileURLToPath", "parseParentPid", "argv", "index", "pid", "selfRealPath", "fs", "fileURLToPath", "main", "runUpdateCheck", "package_default", "process"]
|
|
7
|
+
}
|
package/dist/vite.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{b as r,d as t,e}from"./chunk-
|
|
1
|
+
import{b as r,d as t,e}from"./chunk-4UUVKJYG.js";import{b as i}from"./chunk-LTKR3F4N.js";export{t as infraKitDev,e as infraKitProxy,r as resolveProxyConfig,i as slugifyRelease};
|
|
2
2
|
//# sourceMappingURL=vite.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infra-kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.131",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
7
7
|
],
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"bin": {
|
|
23
|
-
"infra-kit": "dist/cli.js"
|
|
23
|
+
"infra-kit": "dist/cli.js",
|
|
24
|
+
"ik": "dist/cli.js"
|
|
24
25
|
},
|
|
25
26
|
"engines": {
|
|
26
27
|
"node": ">=24.x"
|
|
@@ -28,6 +29,8 @@
|
|
|
28
29
|
"scripts": {
|
|
29
30
|
"inspector": "npx @modelcontextprotocol/inspector node ./dist/mcp.js --debug",
|
|
30
31
|
"build": "pnpm run clean-artifacts && node ./scripts/build.js",
|
|
32
|
+
"prepack": "pnpm run build",
|
|
33
|
+
"prepublishOnly": "pnpm run build",
|
|
31
34
|
"infra-kit-check": "pnpm exec infra-kit audit",
|
|
32
35
|
"clean-artifacts": "rm -rf dist",
|
|
33
36
|
"clean-cache": "rm -rf node_modules/.cache .eslintcache tsconfig.tsbuildinfo .turbo .swc",
|
|
@@ -64,7 +67,7 @@
|
|
|
64
67
|
},
|
|
65
68
|
"devDependencies": {
|
|
66
69
|
"@types/aws-lambda": "^8.10.162",
|
|
67
|
-
"@types/react": "
|
|
70
|
+
"@types/react": "19.2.17",
|
|
68
71
|
"@wl/eslint-config": "workspace:*",
|
|
69
72
|
"@wl/vitest-config": "workspace:*",
|
|
70
73
|
"esbuild": "^0.28.1",
|
package/dist/boot-SGM5RVLJ.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{render as M}from"ink";import B from"node:process";import{Box as p,Text as o,useApp as v,useInput as A}from"ink";import{useEffect as b,useMemo as D,useState as C}from"react";import{jsx as r,jsxs as f}from"react/jsx-runtime";var y=x=>{let{items:l,onSelect:i,onCancel:g}=x,s={hint:"Select a command \u2014 type to filter, \u2191\u2193 to move, Enter to run, Esc to cancel",prompt:"\u276F ",empty:"No matching commands"},h=24,{exit:P}=v(),[m,T]=C(""),[E,a]=C(0),[u,w]=C(!1);b(()=>{u&&P()},[u,P]);let c=D(()=>{let e=m.trim().toLowerCase();return e?l.filter(t=>`${t.name} ${t.description}`.toLowerCase().includes(e)):l},[l,m]),d=Math.min(E,Math.max(0,c.length-1));if(A((e,t)=>{if(!u){if(t.escape||t.ctrl&&e==="c"){g(),w(!0);return}if(t.return){let n=c[d];n&&(i(n.name),w(!0));return}if(t.upArrow){let n=c.length;n>0&&a((d-1+n)%n);return}if(t.downArrow){let n=c.length;n>0&&a((d+1)%n);return}if(t.backspace||t.delete){T(m.slice(0,-1)),a(0);return}e&&!t.ctrl&&!t.meta&&(T(m+e),a(0))}}),u)return null;let S="";return f(p,{flexDirection:"column",children:[r(o,{dimColor:!0,children:s.hint}),f(p,{children:[r(o,{color:"cyan",children:s.prompt}),r(o,{children:m})]}),r(p,{flexDirection:"column",marginTop:1,children:c.length===0?r(o,{dimColor:!0,children:s.empty}):c.map((e,t)=>{let n=t===d,I=e.group!==S;return S=e.group,f(p,{flexDirection:"column",children:[I&&t>0?r(o,{children:" "}):null,I?r(o,{color:"yellow",children:`\u2014 ${e.group} \u2014`}):null,f(o,{color:n?"green":void 0,children:[n?"\u203A ":" ",e.name.padEnd(h)," ",r(o,{dimColor:!0,children:e.description})]})]},e.name)})})]})};import{jsx as $}from"react/jsx-runtime";var R=async x=>new Promise(l=>{let i=null,{waitUntilExit:g}=M($(y,{items:x,onSelect:h=>{i=h},onCancel:()=>{i=null}}),{stdout:B.stderr}),s=()=>{B.stdin.ref()};g().then(()=>{s(),l(i)}).catch(()=>{s(),l(null)})});export{R as runCommandPalette};
|
|
2
|
-
//# sourceMappingURL=boot-SGM5RVLJ.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/tui/boot.tsx", "../src/tui/screens/command-palette.tsx"],
|
|
4
|
-
"sourcesContent": ["import { render } from 'ink'\nimport process from 'node:process'\n\nimport { CommandPalette } from './screens/command-palette'\nimport type { PaletteItem } from './types'\n\n/**\n * Entry point for the Ink TUI. This module (and everything under `src/tui/`) is\n * the ONLY place allowed to import `ink`/`react`; it is reached exclusively via a\n * dynamic `await import('src/tui/boot')` from the TTY branch of entry/cli.ts, so\n * React never loads on the MCP / `--json` / non-TTY paths. Build splitting keeps\n * it in a separate lazy chunk (see scripts/build.js).\n */\n\n/**\n * Render the command palette and resolve to the chosen command name, or `null`\n * if the user cancels. Frames are written to stderr so stdout stays clean for\n * the command that runs afterwards (mirrors the previous Inquirer menu).\n */\nexport const runCommandPalette = async (items: PaletteItem[]): Promise<string | null> => {\n return new Promise((resolve) => {\n let selected: string | null = null\n\n const { waitUntilExit } = render(\n <CommandPalette\n items={items}\n onSelect={(name) => {\n selected = name\n }}\n onCancel={() => {\n selected = null\n }}\n />,\n { stdout: process.stderr as unknown as NodeJS.WriteStream },\n )\n\n // Ink `unref()`s process.stdin when it tears down raw mode on exit. That ref\n // is process-wide and never restored, so the next consumer of stdin \u2014 the\n // Inquirer prompt the selected command opens (e.g. the worktrees-remove\n // checkbox) \u2014 reads from an unref'd handle that no longer keeps the event\n // loop alive. Once the command's git/gh subprocesses settle, the loop drains\n // mid-prompt, Node flags entry/cli.ts's top-level await as unsettled, and the\n // process exits with code 13 (the prompt dies on arrival). Re-ref stdin so it\n // holds the loop while a prompt is reading; an idle ref'd stdin does not block\n // exit, so non-interactive follow-up commands still terminate normally.\n const restoreStdinRef = () => {\n process.stdin.ref()\n }\n\n waitUntilExit()\n .then(() => {\n restoreStdinRef()\n resolve(selected)\n })\n .catch(() => {\n restoreStdinRef()\n resolve(null)\n })\n })\n}\n", "import { Box, Text, useApp, useInput } from 'ink'\nimport { useEffect, useMemo, useState } from 'react'\n\nimport type { PaletteItem } from '../types'\n\ninterface CommandPaletteProps {\n items: PaletteItem[]\n /** Called with the chosen command name; the app then exits. */\n onSelect: (name: string) => void\n /** Called when the user cancels (Esc / Ctrl-C); the app then exits. */\n onCancel: () => void\n}\n\n/**\n * Fuzzy-ish command picker rendered with Ink. Pure presentation: it receives a\n * flat list of commands (already grouped/ordered by the catalog) and returns the\n * selected name. It never executes anything \u2014 the caller runs the command via\n * the existing Commander path.\n */\nexport const CommandPalette = (props: CommandPaletteProps) => {\n const { items, onSelect, onCancel } = props\n\n const T = {\n hint: 'Select a command \u2014 type to filter, \u2191\u2193 to move, Enter to run, Esc to cancel',\n prompt: '\u276F ',\n empty: 'No matching commands',\n }\n const nameWidth = 24\n\n const { exit } = useApp()\n const [query, setQuery] = useState('')\n const [index, setIndex] = useState(0)\n // When set, the component renders `null` for one frame so Ink erases the list\n // before unmount commits its final frame (Ink's `log.done()` would otherwise\n // freeze the last drawn frame \u2014 the command list \u2014 into the scrollback).\n const [submitted, setSubmitted] = useState(false)\n\n // Exit only after the empty frame has committed, so the list is wiped first.\n useEffect(() => {\n if (submitted) {\n exit()\n }\n }, [submitted, exit])\n\n const filtered = useMemo(() => {\n const q = query.trim().toLowerCase()\n\n if (!q) {\n return items\n }\n\n return items.filter((item) => {\n return `${item.name} ${item.description}`.toLowerCase().includes(q)\n })\n }, [items, query])\n\n const activeIndex = Math.min(index, Math.max(0, filtered.length - 1))\n\n useInput((input, key) => {\n if (submitted) {\n return\n }\n\n if (key.escape || (key.ctrl && input === 'c')) {\n onCancel()\n setSubmitted(true)\n\n return\n }\n\n if (key.return) {\n const selected = filtered[activeIndex]\n\n if (selected) {\n onSelect(selected.name)\n setSubmitted(true)\n }\n\n return\n }\n\n if (key.upArrow) {\n const n = filtered.length\n\n // Wrap to the bottom when stepping up past the first item. The `+ n`\n // sidesteps JS negative modulo (e.g. -1 % 5 === -1, not 4).\n if (n > 0) {\n setIndex((activeIndex - 1 + n) % n)\n }\n\n return\n }\n\n if (key.downArrow) {\n const n = filtered.length\n\n // Wrap to the top when stepping down past the last item.\n if (n > 0) {\n setIndex((activeIndex + 1) % n)\n }\n\n return\n }\n\n if (key.backspace || key.delete) {\n setQuery(query.slice(0, -1))\n setIndex(0)\n\n return\n }\n\n // Printable character: append to the filter.\n if (input && !key.ctrl && !key.meta) {\n setQuery(query + input)\n setIndex(0)\n }\n })\n\n // Final frame: render nothing so Ink erases the list on its way out.\n if (submitted) {\n return null\n }\n\n let lastGroup = ''\n\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>{T.hint}</Text>\n <Box>\n <Text color=\"cyan\">{T.prompt}</Text>\n <Text>{query}</Text>\n </Box>\n <Box flexDirection=\"column\" marginTop={1}>\n {filtered.length === 0 ? (\n <Text dimColor>{T.empty}</Text>\n ) : (\n filtered.map((item, position) => {\n const isActive = position === activeIndex\n const showGroup = item.group !== lastGroup\n\n lastGroup = item.group\n\n return (\n <Box flexDirection=\"column\" key={item.name}>\n {/* Blank line between groups (not before the very first group). */}\n {showGroup && position > 0 ? <Text> </Text> : null}\n {showGroup ? <Text color=\"yellow\">{`\u2014 ${item.group} \u2014`}</Text> : null}\n <Text color={isActive ? 'green' : undefined}>\n {isActive ? '\u203A ' : ' '}\n {item.name.padEnd(nameWidth)} <Text dimColor>{item.description}</Text>\n </Text>\n </Box>\n )\n })\n )}\n </Box>\n </Box>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["render", "process", "Box", "Text", "useApp", "useInput", "useEffect", "useMemo", "useState", "jsx", "jsxs", "CommandPalette", "props", "items", "onSelect", "onCancel", "T", "nameWidth", "exit", "query", "setQuery", "index", "setIndex", "submitted", "setSubmitted", "filtered", "q", "item", "activeIndex", "input", "key", "selected", "lastGroup", "position", "isActive", "showGroup", "jsx", "runCommandPalette", "items", "resolve", "selected", "waitUntilExit", "render", "CommandPalette", "name", "process", "restoreStdinRef"]
|
|
7
|
-
}
|
package/dist/chunk-2PZRQHWF.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/lib/release-slug/release-slug.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Shared, dependency-light release-slug helper.\n *\n * Extracted from `lib/vite/vite.ts` so BOTH the published `infra-kit/vite` helper\n * (which re-exports it) and the dev-server's dev-context fragment writer derive the\n * `<release>` hostname segment from ONE implementation \u2014 the recorded slug and the\n * helper-computed slug can never drift. Pure (regex only, no imports) so importing it\n * into the lightweight `infra-kit/vite` bundle stays cheap.\n */\n\n/**\n * Slugify a git branch into a `<release>` token: strip a leading git-flow prefix\n * (`feature/`, `release/`, \u2026), lowercase, collapse non-alphanumeric runs to `-`,\n * and trim stray dashes.\n *\n * @example\n * slugifyRelease('release/2.4') // => '2-4'\n * slugifyRelease('feature/HUL-123') // => 'hul-123'\n */\nexport const slugifyRelease = (branch: string): string => {\n // Collect the alphanumeric runs and join with `-`. Doing it this way (rather\n // than collapse-then-trim) is linear and sidesteps a super-linear trim regex,\n // while inherently dropping any leading/trailing separators.\n const runs = branch\n .replace(/^(?:feature|feat|release|hotfix|bugfix|fix|chore)\\//i, '')\n .toLowerCase()\n .match(/[a-z0-9]+/g)\n\n return runs ? runs.join('-') : ''\n}\n"],
|
|
5
|
-
"mappings": "AAmBO,IAAMA,EAAkBC,GAA2B,CAIxD,IAAMC,EAAOD,EACV,QAAQ,uDAAwD,EAAE,EAClE,YAAY,EACZ,MAAM,YAAY,EAErB,OAAOC,EAAOA,EAAK,KAAK,GAAG,EAAI,EACjC",
|
|
6
|
-
"names": ["slugifyRelease", "branch", "runs"]
|
|
7
|
-
}
|
package/dist/chunk-6RRAK2QO.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import O from"node:process";import $ from"pino";import se from"pino-pretty";var _="/tmp/mcp-infra-kit.log",Dt=()=>{let e=O.argv.includes("--debug")?"debug":"info",t=$({level:e},$.destination({dest:_}));return t.info(`Logger initialized with level: ${e}. Logging to: ${_}`),t},ie=()=>{let e=O.argv.includes("--debug")?"debug":"info",t=["time","pid","hostname"];return e==="debug"&&t.push("level"),$({level:e},se({destination:2,ignore:t.join(","),colorize:!0}))},g=ie();import*as u from"node:fs";import*as c from"node:path";function ae(e){let t=e;for(let r=0;r<10;r++){let n=c.join(t,"pnpm-workspace.yaml");if(u.existsSync(n))return t;t=c.dirname(t)}throw new Error("Could not find monorepo root (pnpm-workspace.yaml)")}function B(e,t){let r=c.join(e,"package.json");if(!u.existsSync(r))return t;try{let n=JSON.parse(u.readFileSync(r,"utf-8"));return typeof n.name=="string"?n.name:t}catch{return t}}function It(e){let t=c.join(e,"apps"),r=[];if(!u.existsSync(t))throw new Error(`Apps directory not found: ${t}`);let n=u.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory()).map(o=>o.name);for(let o of n){let i=c.join(t,o,"api"),a=c.join(i,"serverless.yml");u.existsSync(a)&&r.push({name:o,packageName:B(i,o),path:i})}return r}function ce(e){let t=c.join(e,"package.json");if(!u.existsSync(t))return!1;try{let r=JSON.parse(u.readFileSync(t,"utf-8"));return typeof r.scripts?.dev=="string"&&r.scripts.dev.length>0}catch{return!1}}function Et(e){let t=c.join(e,"apps"),r=[];if(!u.existsSync(t))return r;let n=u.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory()).map(o=>o.name);for(let o of n){let i=c.join(t,o,"ui");u.existsSync(i)&&ce(i)&&r.push({name:o,packageName:B(i,o),path:i})}return r}function jt(e){let t=e?.filter(Boolean)??[];return t.length>0?t:null}function Nt(e){let t=ae(e),r=c.relative(c.join(t,"apps"),e),n=r.split(c.sep)[0];if(r===""||r.startsWith("..")||c.isAbsolute(r)||!n)throw new Error(`--self: not inside an apps/<app> directory (cwd: ${e}). Run from an app folder or use --app=<name>.`);return n}function Lt(e){let t=c.join(e,"packages");if(!u.existsSync(t))return[];let r=u.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory()).map(o=>o.name),n=[];for(let o of r){let i=c.join(t,o,"dist");u.existsSync(i)&&u.statSync(i).isDirectory()&&n.push(i)}return n}function Tt(e){return e.map(t=>c.join(t.path,"dist")).filter(t=>u.existsSync(t))}function Wt(e,t,r){let n=c.normalize(e),o=r.find(a=>n.startsWith(c.normalize(a)));return o?{kind:"package",packageDir:o}:{kind:"app",app:t.find(a=>n.startsWith(c.normalize(a)))}}import pe from"node:process";import{$ as D}from"zx";var ue=async()=>{try{return await D`cmux --version`.quiet(),!0}catch{return!1}},le=async e=>{let{cwd:t,title:r,layout:n}=e,o=JSON.stringify(n),a=(await D({env:{...pe.env,CMUX_QUIET:"1"}})`cmux new-workspace --name ${r} --cwd ${t} --focus false --layout ${o}`).stdout;return fe(a)},me=async e=>{try{await D`cmux close-workspace --workspace ${e}`.quiet()}catch(t){g.debug({error:t,ref:e},"cmux: skipped closing dev workspace")}},fe=e=>{let t=e.match(/workspace:\d+/);if(!t)throw new Error("cmux: could not locate workspace ref in new-workspace output");return t[0]};var de=/\bv(\d+\.\d+\.\d+)\b/g,h=e=>e.trim().replace(/\s+/g," ").replace(de,"$1");import{$ as K}from"zx";var ge=async e=>{try{let t=(await K`cmux list-workspaces`.quiet()).stdout,r=he(t,e);if(!r)return;await K`cmux close-workspace --workspace ${r}`.quiet()}catch(t){g.debug({error:t,title:e},"cmux: skipped closing workspace")}},he=(e,t)=>{let r=h(t);for(let n of e.split(`
|
|
2
|
-
`)){let o=n.match(/^[* ]\s*(workspace:\d+)\s+(.+?)(?:\s+\[selected\])?\s*$/);if(!o)continue;let i=o[1],a=o[2]?.trim()??"";if(h(a)===r)return i}};import{$ as xe}from"zx";var we=async()=>{try{let e=(await xe`cmux list-workspaces`.quiet()).stdout,t=new Set;for(let r of e.split(`
|
|
3
|
-
`)){let n=r.match(/^[* ]\s*workspace:\d+\s+(.+?)(?:\s+\[selected\])?\s*$/);if(!n)continue;let o=n[1]?.trim();o&&t.add(h(o))}return t}catch(e){return g.debug({error:e},"cmux: skipped listing workspace titles"),new Set}};import{$ as P}from"zx";import L from"node:fs/promises";import _e from"node:os";import b from"node:path";import{z as s}from"zod";import V from"node:path";import{$ as m}from"zx";var ke=/^v?(\d+)\.(\d+)\.(\d+)$/,ve=/^(\d+)\.(\d+)\.(\d+)$/,ye=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,C="release/",A="release/v",M="refs/heads/",be="next";var z=new Set(["dev","main","next","hotfix","regular","release"]),d=class extends Error{constructor(t){super(t),this.name="InvalidReleaseNameError"}},x=class extends Error{constructor(t){super(t),this.name="InvalidReleaseRefError"}},G=e=>e.startsWith(M)?e.slice(M.length):e,U=(e,t,r)=>({kind:"version",semver:{major:e,minor:t,patch:r},raw:`${e}.${t}.${r}`}),I=e=>{if(e.length===0)throw new d('Release name is empty. Provide a kebab-case name like "checkout-redesign".');if(e.length>50)throw new d(`Release name "${e}" is ${e.length} characters; the maximum is 50.`);if(!ye.test(e))throw new d(`Release name "${e}" is not kebab-case. Use lowercase letters, digits, and single hyphens, e.g. "checkout-redesign".`);if(z.has(e))throw new d(`Release name "${e}" is reserved. Reserved names: ${[...z].join(", ")}.`)},v=e=>{let t=G(e.trim());if(!t.startsWith(C))return null;if(t.startsWith(A)){let n=t.slice(A.length),o=ve.exec(n);if(o)return U(Number(o[1]),Number(o[2]),Number(o[3]))}let r=t.slice(C.length);try{I(r)}catch{return null}return{kind:"name",name:r,raw:r}},Pe=e=>{let t=e.trim();if(G(t).startsWith(C)){let o=v(t);if(!o)throw new x(`"${e}" looks like a release branch but is not a valid release/v<semver> or release/<name> ref.`);return o}let n=ke.exec(t);if(n)return U(Number(n[1]),Number(n[2]),Number(n[3]));if(t.toLowerCase()===be)throw new x('The "next" token must be resolved to a concrete version (via computeNextVersion) before parsing a release ref.');try{I(t)}catch(o){let i=o instanceof Error?o.message:String(o);throw new x(`Cannot parse "${e}" as a release ref: ${i}`)}return{kind:"name",name:t,raw:t}},Ce=e=>e.kind==="version"?`${A}${e.raw}`:`${C}${e.name}`,Re=(e,t)=>{let r=t==="hotfix"?"Hotfix":"Release";return e.kind==="version"?`${r} v${e.raw}`:`${r} ${e.name}`},Se=e=>e.kind==="version"?`Release v${e.raw} (RC)`:`Release ${e.name} (RC)`,$e=e=>e.kind==="version"?`v${e.raw}`:e.name,E=e=>e.raw,j=e=>e==null?!1:v(e)!==null,F=e=>{if(e===void 0)return null;let t=e instanceof Date?e.getTime():new Date(e).getTime();return Number.isNaN(t)?null:t},De=(e,t,r)=>{if(e.kind==="version"&&t.kind==="version")return e.semver.major!==t.semver.major?e.semver.major-t.semver.major:e.semver.minor!==t.semver.minor?e.semver.minor-t.semver.minor:e.semver.patch-t.semver.patch;if(e.kind==="version")return-1;if(t.kind==="version")return 1;let n=F(r?.a),o=F(r?.b);return n!==null&&o!==null&&n!==o?n-o:e.name<t.name?-1:e.name>t.name?1:0};var Ae=async e=>{let r=(await m`git worktree list`).stdout.split(`
|
|
4
|
-
`).filter(Boolean),n={release:Ie,feature:Ee};return r.map(n[e]).filter(o=>o!==null)},q=e=>{let t=e.trimEnd();if(!t.endsWith("]"))return null;let r=t.lastIndexOf("[");if(r===-1)return null;let n=t.slice(r+1,-1);return n.length>0?n:null},Ie=e=>{let t=q(e);return j(t)?t:null},Ee=e=>{let t=q(e);return t?.startsWith("feature/")?t:null},y=async()=>(await m`git rev-parse --show-toplevel`).stdout.trim(),H=async()=>(await m`git rev-parse --abbrev-ref HEAD`).stdout.trim(),je=async()=>(await m`git status --porcelain`).stdout.trim().length===0,Ne=async()=>{let e=await y(),[t,r]=await Promise.all([m({cwd:e})`git rev-parse --absolute-git-dir`,m({cwd:e})`git rev-parse --git-common-dir`]),n=t.stdout.trim(),o=V.resolve(e,r.stdout.trim());return n!==o},Le=async()=>{let e=await y();return V.basename(e)},Te=async e=>{(await m`git branch --list ${e}`).stdout.trim().length!==0&&await H()!==e&&await m`git branch -D ${e}`},We=async e=>{(await m`git ls-remote --heads origin ${e}`).stdout.trim().length!==0&&await m`git push origin --delete ${e}`};var J="infra-kit.json",Y=".infra-kit",Oe="infra-kit.json",Be="projects",Ke=s.object({provider:s.literal("doppler"),config:s.object({name:s.string().min(1)})}),Me=s.discriminatedUnion("provider",[Ke]),ze=s.object({workspaceConfigPath:s.string().min(1)}),Fe=s.object({provider:s.literal("cursor"),config:ze}),Ge=s.object({}),Ue=s.object({provider:s.literal("zed"),config:Ge}),X=s.discriminatedUnion("provider",[Fe,Ue]),Ve=s.union([X,s.array(X).min(1)]),qe=s.object({provider:s.literal("jira"),config:s.object({baseUrl:s.string().url(),projectId:s.number().int().positive()})}),He=s.discriminatedUnion("provider",[qe]),Je=["two-columns","three-pane"],Xe=s.object({layout:s.enum(Je).optional()}),Ye=s.object({openInGithubDesktop:s.boolean().optional(),openInCmux:s.boolean().optional(),cmux:Xe.optional()}),Qe=s.object({port:s.number().int().positive().optional(),prefixUrl:s.string().min(1).optional()}).strict(),Ze=s.record(s.string().min(1),Qe),et=s.enum(["local","cloud"]),tt=s.object({watchDeps:s.boolean().optional(),proxy:s.record(s.string().min(1),et).optional()}).strict(),rt=s.object({apps:s.record(s.string().min(1),tt).optional(),cmux:s.boolean().optional()}).strict(),nt=s.record(s.string().min(1),rt),ot=s.object({trigger:s.enum(["shell-startup","cli-invocation"]),config:s.string().min(1)}).strict(),Q=s.object({environments:s.array(s.string().min(1)).min(1),envManagement:Me,ide:Ve.optional(),taskManager:He.optional(),worktrees:Ye.optional(),envAutoLoad:ot.optional(),dev:Ze.optional(),devPresets:nt.optional()}),Z=Q.superRefine((e,t)=>{if(!Array.isArray(e.ide))return;let r=new Set;for(let n of e.ide){if(r.has(n.provider)){t.addIssue({code:"custom",message:"each IDE provider may appear at most once",path:["ide"]});return}r.add(n.provider)}}),ee=Q.partial(),st=e=>{let t=e.ide;return t?Array.isArray(t)?t:[t]:[]},it="two-columns",T=e=>e.worktrees?.cmux?.layout??it,w=null,te=async()=>{let e=await y(),t=b.basename(e),r=b.join(_e.homedir(),Y);return{main:b.join(e,J),userGlobal:b.join(r,Oe),userProject:b.join(r,Be,t,J),projectName:t}},W=async()=>{let e=await te(),t;try{t=await L.stat(e.main)}catch{w=null;let l=e.main.replace(/\.json$/,".yml");throw await N(l)?new Error(`infra-kit.json not found at ${e.main}. A legacy infra-kit.yml exists \u2014 run \`infra-kit init\` to convert it.`):new Error(`infra-kit.json not found at ${e.main}`)}let[r,n]=await Promise.all([N(e.userGlobal),N(e.userProject)]),o={main:Number(t.mtimeMs),userGlobal:r?Number(r.mtimeMs):null,userProject:n?Number(n.mtimeMs):null};if(w&&pt(w.mtimes,o))return w.value;let i=[{label:"infra-kit.json",path:e.main,required:!0},{label:"~/.infra-kit/infra-kit.json",path:e.userGlobal,required:!1},{label:`~/.infra-kit/projects/${e.projectName}/infra-kit.json`,path:e.userProject,required:!1}],a={};for(let l of i){let f=await ut(l);f!==null&&(a={...a,...f})}let p=Z.safeParse(a);if(!p.success)throw new Error(`Invalid merged infra-kit config: ${s.prettifyError(p.error)}`);return w={mtimes:o,value:p.data},p.data},at=()=>{w=null},N=async e=>{try{return await L.stat(e)}catch{return null}},ct=async e=>{try{return await L.readFile(e,"utf-8")}catch{return null}},pt=(e,t)=>{let r=Object.keys(e);return r.length!==Object.keys(t).length?!1:r.every(n=>e[n]===t[n])},ut=async e=>{let t=await ct(e.path);if(t===null){if(e.required)throw new Error(`${e.label} not found at ${e.path}`);return null}let r;try{r=t.trim()===""?{}:JSON.parse(t)}catch(o){throw new Error(`Invalid JSON in ${e.label} at ${e.path}: ${o.message}`)}let n=ee.safeParse(r);if(!n.success)throw new Error(`Invalid ${e.label} at ${e.path}: ${s.prettifyError(n.error)}`);return n.data};var lt=async e=>{let{cwd:t,title:r}=e,n=T(await W()),o=(await P`cmux workspace create --cwd ${t}`).stdout,i=ft(o),a=(await P`cmux list-pane-surfaces --workspace ${i}`).stdout,p=mt(a);await P`cmux new-split right --workspace ${i} --surface ${p}`,n==="three-pane"&&await P`cmux new-split down --workspace ${i} --surface ${p}`,r&&await P`cmux workspace rename --workspace ${i} --title ${r}`},mt=e=>{let t=e.match(/surface:\d+/);if(!t)throw new Error("cmux: could not locate initial surface in list-pane-surfaces output");return t[0]},ft=e=>{let t=e.match(/workspace:\d+/);if(!t)throw new Error("cmux: could not locate workspace ref in workspace create output");return t[0]};var dt=e=>{let{repoName:t,branch:r}=e,n=v(r),o=n?E(n):r;return`${t} ${o}`};var gt=["api","ui"],ht=e=>{let t=e.split("/"),[r,n]=t;if(t.length>2||!r)throw new Error(`devPresets: invalid target "${e}" (expected "<app>" or "<app>/api|ui")`);if(n===void 0)return{appGlob:r,parts:gt,explicit:!1};if(n==="api"||n==="ui")return{appGlob:r,parts:[n],explicit:!0};throw new Error(`devPresets: invalid target part "${n}" in "${e}" (expected "api" or "ui")`)},xt=(e,t)=>t==="api"?e.api:e.ui,wt=e=>[...new Set([...e.api,...e.ui])].sort(),re=(e,t)=>`${e}/${t}`,kt=(e,t)=>(e?0:2)+(t?1:0),vt=(e,t)=>t.watchDeps===void 0?e??{watchDeps:!0,rank:t.rank,explicitValue:!1}:!e||!e.explicitValue?{watchDeps:t.watchDeps,rank:t.rank,explicitValue:!0}:t.rank>e.rank?{watchDeps:t.watchDeps,rank:t.rank,explicitValue:!0}:t.rank<e.rank?e:{watchDeps:e.watchDeps&&t.watchDeps,rank:e.rank,explicitValue:!0},yt=(e,t,r)=>{let{app:n,part:o,watchDeps:i,isGlob:a,explicit:p}=r;if(!xt(t,o).includes(n)){p&&!a&&e.unmatched.push(re(n,o));return}let l=re(n,o),f=vt(e.targets.get(l),{watchDeps:i,rank:kt(a,p)});e.targets.set(l,{app:n,part:o,...f})},bt=(e,t)=>{let r=Object.entries(e.apps??{"*":{}}),n={targets:new Map,proxy:{},unmatched:[]};for(let[a,p]of r){let{appGlob:l,parts:f,explicit:R}=ht(a),k=l==="*",ne=k?wt(t):[l];for(let S of ne){for(let oe of f)yt(n,t,{app:S,part:oe,watchDeps:p.watchDeps,isGlob:k,explicit:R});p.proxy&&(n.proxy[S]={...n.proxy[S]??{},...p.proxy})}}let o=[...n.targets.values()].map(({app:a,part:p,watchDeps:l})=>({app:a,part:p,watchDeps:l})),i=[...new Set(o.filter(a=>a.part==="api").map(a=>a.app))];return{targets:o,cmux:e.cmux??!1,proxy:n.proxy,localApps:i,unmatched:n.unmatched}},Pt=(e,t)=>Object.keys(e).find(r=>e[r]===t),Ct=({preset:e,app:t,route:r,launchedPkgs:n,ctx:o})=>{let i=o.routePkg(t,r);if(i===void 0)return{preset:e,app:t,route:r,kind:"unknown-route",message:`preset "${e}": proxy override "${r}" on "${t}" names a route not declared in ${t}'s infra-kit.config.ts dev.proxy.routes`};if(n.has(i))return null;let a=Pt(o.apiPkgByApp,i),p=a?`add "${a}/api" to the preset`:`launch the api whose package is "${i}"`;return{preset:e,app:t,route:r,pkg:i,kind:"backend-not-launched",message:`preset "${e}": proxy override "${r}" \u2192 "local" requires backend "${i}" to run locally, but the preset does not launch it \u2014 ${p}, or set "${r}" to "cloud"`}},Sr=(e,t)=>{let r=[];for(let[n,o]of Object.entries(e)){let i=bt(o,t.discovered),a=new Set(i.localApps.map(p=>t.apiPkgByApp[p]).filter(p=>p!==void 0));for(let[p,l]of Object.entries(i.proxy))for(let[f,R]of Object.entries(l)){if(R!=="local")continue;let k=Ct({preset:n,app:p,route:f,launchedPkgs:a,ctx:t});k&&r.push(k)}}return r};export{d as a,I as b,v as c,Pe as d,Ce as e,Re as f,Se as g,$e as h,E as i,j,De as k,Ae as l,y as m,H as n,je as o,Ne as p,Le as q,Te as r,We as s,_ as t,Dt as u,g as v,ae as w,It as x,Et as y,jt as z,Nt as A,Lt as B,Tt as C,Wt as D,bt as E,Sr as F,Y as G,Z as H,ee as I,st as J,te as K,W as L,at as M,h as N,ge as O,we as P,ue as Q,le as R,me as S,lt as T,dt as U};
|
|
5
|
-
//# sourceMappingURL=chunk-6RRAK2QO.js.map
|