infra-kit 0.1.131 → 0.1.133

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/boot-YCABHUPP.js +2 -0
  2. package/dist/boot-YCABHUPP.js.map +7 -0
  3. package/dist/chunk-3OMHS5RE.js +2 -0
  4. package/dist/chunk-3OMHS5RE.js.map +7 -0
  5. package/dist/chunk-6EPRH34O.js +2 -0
  6. package/dist/chunk-6EPRH34O.js.map +7 -0
  7. package/dist/{chunk-SR2GFEY7.js → chunk-6FDGBQGX.js} +2 -2
  8. package/dist/{chunk-SR2GFEY7.js.map → chunk-6FDGBQGX.js.map} +1 -1
  9. package/dist/chunk-FAR2L74S.js +2 -0
  10. package/dist/chunk-FAR2L74S.js.map +7 -0
  11. package/dist/{chunk-C2HRONYA.js → chunk-JD5T4VCB.js} +3 -3
  12. package/dist/chunk-JD5T4VCB.js.map +7 -0
  13. package/dist/chunk-RSF5XAQJ.js +4 -0
  14. package/dist/chunk-RSF5XAQJ.js.map +7 -0
  15. package/dist/{chunk-52WL2IQX.js → chunk-RXEMP3VI.js} +2 -2
  16. package/dist/{chunk-X2L4F2VM.js → chunk-V37MZDFR.js} +3 -3
  17. package/dist/{chunk-X2L4F2VM.js.map → chunk-V37MZDFR.js.map} +3 -3
  18. package/dist/chunk-WSO6DN6Q.js +220 -0
  19. package/dist/chunk-WSO6DN6Q.js.map +7 -0
  20. package/dist/cli.js +9 -3
  21. package/dist/cli.js.map +4 -4
  22. package/dist/dev-server.js +20 -15
  23. package/dist/dev-server.js.map +4 -4
  24. package/dist/{dev-wizard-run-7HEKSAS2.js → dev-wizard-run-TNA5YI7P.js} +2 -2
  25. package/dist/lib/vite/vite.d.ts +29 -10
  26. package/dist/mcp.js +1 -1
  27. package/dist/mcp.js.map +1 -1
  28. package/dist/persistent-ink-dev-ui-FUJQDFNY.js +8 -0
  29. package/dist/persistent-ink-dev-ui-FUJQDFNY.js.map +7 -0
  30. package/dist/update-check.js +1 -1
  31. package/dist/update-check.js.map +1 -1
  32. package/dist/vite.js +1 -1
  33. package/package.json +1 -1
  34. package/dist/boot-5GFZBDKL.js +0 -2
  35. package/dist/boot-5GFZBDKL.js.map +0 -7
  36. package/dist/chunk-4UUVKJYG.js +0 -2
  37. package/dist/chunk-4UUVKJYG.js.map +0 -7
  38. package/dist/chunk-6YAKVQZN.js +0 -4
  39. package/dist/chunk-6YAKVQZN.js.map +0 -7
  40. package/dist/chunk-C2HRONYA.js.map +0 -7
  41. package/dist/chunk-F6VCGS3D.js +0 -2
  42. package/dist/chunk-F6VCGS3D.js.map +0 -7
  43. package/dist/chunk-LENH44GY.js +0 -215
  44. package/dist/chunk-LENH44GY.js.map +0 -7
  45. package/dist/chunk-O3YJM6IR.js +0 -2
  46. package/dist/chunk-O3YJM6IR.js.map +0 -7
  47. package/dist/chunk-TKZINCST.js +0 -2
  48. package/dist/chunk-TKZINCST.js.map +0 -7
  49. package/dist/chunk-VJPLVBRA.js +0 -5
  50. package/dist/chunk-VJPLVBRA.js.map +0 -7
  51. package/dist/persistent-ink-dev-ui-4U4B2B7C.js +0 -8
  52. package/dist/persistent-ink-dev-ui-4U4B2B7C.js.map +0 -7
  53. package/dist/program.js +0 -2
  54. package/dist/program.js.map +0 -7
  55. /package/dist/{chunk-52WL2IQX.js.map → chunk-RXEMP3VI.js.map} +0 -0
  56. /package/dist/{dev-wizard-run-7HEKSAS2.js.map → dev-wizard-run-TNA5YI7P.js.map} +0 -0
@@ -1,2 +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
1
+ import{a as l}from"./chunk-RXEMP3VI.js";import{I as k,Q as w,v as d,w as v,x,y}from"./chunk-V37MZDFR.js";import{e as W}from"./chunk-CHETBZ6M.js";import{c as b}from"./chunk-6EPRH34O.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-TNA5YI7P.js.map
@@ -1,10 +1,11 @@
1
1
  import type { InfraKitDev, InfraKitDevProxy } from '../package-config/package-config';
2
2
  import { slugifyRelease } from '../release-slug/release-slug';
3
3
  /**
4
- * A single Vite `server.proxy` entry. `changeOrigin` is always set; the
5
- * cloud-only pair (`secure: false` + `cookieDomainRewrite: 'localhost'`) makes a
6
- * local FE talk to an HTTPS cloud BE without cert/cookie-domain breakage.
7
- * `headers` is present only when HTTP Basic Auth is injected (see
4
+ * A single Vite `server.proxy` entry. `changeOrigin` is always set. `cookieDomainRewrite` is cloud-only
5
+ * (it keeps an HTTPS cloud BE's cookies usable from a local FE). `secure: false` appears on BOTH sources
6
+ * but for different reasons: a cloud target may serve a cert the local store rejects, and a local target
7
+ * is served by portless from its own private CA — set there only for a loopback host (see
8
+ * {@link isLoopbackTarget}). `headers` is present only when HTTP Basic Auth is injected (see
8
9
  * {@link buildBasicAuthHeader}) — it carries the `Authorization` header applied
9
10
  * uniformly to every route so upstream environments behind auth (e2e/staging)
10
11
  * stay reachable.
@@ -88,11 +89,17 @@ export declare const loadDev: (cwd: string) => Promise<InfraKitDev | undefined>;
88
89
  export interface LocalPackageInfo {
89
90
  /** The real bound port the runner recorded. Provenance only — the proxy target is the alias host. */
90
91
  port: number;
91
- /** The runner-recorded release slug, preferred over the helper's git derivation. */
92
+ /**
93
+ * The authoritative local target (`https://<release>.<packageName>.localhost`), published by the
94
+ * runner. Present → {@link resolveLocalTarget} uses it verbatim and consults no template. Absent →
95
+ * the fragment came from an old CLI and the legacy template path runs.
96
+ */
97
+ origin?: string;
98
+ /** LEGACY (no `origin`): the runner-recorded release slug, preferred over the helper's git derivation. */
92
99
  release?: string;
93
- /** `<release>.<packageName>.localhost`, present only when a portless alias was registered. */
100
+ /** LEGACY (no `origin`): `<release>.<packageName>.localhost`, present only when an alias was registered. */
94
101
  alias?: string;
95
- /** The port {@link alias} resolves on. `80` (the default) is implicit in the template. */
102
+ /** LEGACY (no `origin`): the port {@link alias} resolves on. `80` (the default) is implicit in the template. */
96
103
  proxyPort?: number;
97
104
  }
98
105
  /**
@@ -118,6 +125,16 @@ export declare const readLocalContext: (cwd: string) => LocalContext;
118
125
  * contract. Absent dev-context → empty set (frontend-only cloud resolution).
119
126
  */
120
127
  export declare const readLocalSet: (cwd: string) => ReadonlySet<string>;
128
+ /**
129
+ * Vite's `server.hmr` override, pointing the HMR client at the alias instead of at the raw dev-server port.
130
+ * The page is loaded over `https://<alias>`, so its websocket must be `wss://` on the same origin or the
131
+ * browser blocks it as mixed content; `clientPort` is the proxy's implicit `:443`, not vite's bound port.
132
+ */
133
+ export interface InfraKitViteHmr {
134
+ protocol: 'wss';
135
+ host: string;
136
+ clientPort: number;
137
+ }
121
138
  /**
122
139
  * Resolve a package's `dev` block into a ready-made Vite `server` config: a per-worktree dev-server
123
140
  * `port` plus the `proxy` map. Loads the package's `infra-kit.config.ts`, merges the local dev set
@@ -127,9 +144,10 @@ export declare const readLocalSet: (cwd: string) => ReadonlySet<string>;
127
144
  *
128
145
  * `port` defaults to a fresh OS-assigned free port so simultaneous git worktrees never collide on
129
146
  * 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.
147
+ * alias can actually reach it (override via `options.host` for containers/LAN). `hmr` is emitted only
148
+ * when `infra-kit dev` registered an alias for this UI, so a bare `vite dev` keeps vite's own HMR
149
+ * defaults. Pass `command` so `build` is a no-op (empty proxy, no port) — a build ignores `server` and
150
+ * proxy resolution would otherwise fail-fast on a cloud route with no sourced env.
133
151
  *
134
152
  * @example
135
153
  * // vite.config.ts — the whole `server` field is the helper's output
@@ -140,6 +158,7 @@ export declare const infraKitDev: (options?: InfraKitDevOptions) => Promise<{
140
158
  port?: number;
141
159
  host?: string | boolean;
142
160
  strictPort?: boolean;
161
+ hmr?: InfraKitViteHmr;
143
162
  proxy: InfraKitViteProxy;
144
163
  }>;
145
164
  /**
package/dist/mcp.js CHANGED
@@ -1,2 +1,2 @@
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();
1
+ import{Q as l,f as m}from"./chunk-WSO6DN6Q.js";import"./chunk-RXEMP3VI.js";import"./chunk-RSF5XAQJ.js";import{t as c,u as p,v as s}from"./chunk-V37MZDFR.js";import"./chunk-Z6KTUVIC.js";import"./chunk-6FDGBQGX.js";import"./chunk-6FU2TRU5.js";import"./chunk-CHETBZ6M.js";import"./chunk-6EPRH34O.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
@@ -2,6 +2,6 @@
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
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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",
5
+ "mappings": "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",
6
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-JD5T4VCB.js";import{a as c}from"./chunk-FAR2L74S.js";import{render as W}from"ink";import A from"node:process";import{Box as m,Static as z,Text as l}from"ink";import{useEffect as F,useState as O}from"react";import{jsx as d,jsxs as R}from"react/jsx-runtime";var y=r=>{let{phase:e,narration:t,readyLines:i}=r,[s,a]=O(0);return F(()=>{if(i)return;let n=setInterval(()=>{a(o=>o+1)},T);return()=>{clearInterval(n)}},[i]),i?d(z,{items:i,children:(n,o)=>d(l,{children:n},o)}):R(m,{flexDirection:"column",children:[R(m,{children:[d(l,{color:"cyan",children:L[s%L.length]}),d(l,{dimColor:!0,children:` ${e}`})]}),t?d(l,{dimColor:!0,children:t}):null]})},L=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],T=80;import{Box as C,Static as E,Text as x}from"ink";import{Fragment as U,jsx as u,jsxs as N}from"react/jsx-runtime";var S=r=>{let{headerLines:e,logLines:t,footerLines:i}=r,s=[...e,...t],a=P(i,r.maxFooterLines);return N(U,{children:[u(E,{items:s,children:(n,o)=>u(x,{children:n},o)}),u(C,{flexDirection:"column",children:a.map((n,o)=>u(x,{children:n},o))})]})},P=(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 H from"node:process";var _="\x1B[2K";var b="\x1B[?25h",I=(r,e)=>`\x1B[${r};${e}r`,w=()=>"\x1B[r",v=(r,e)=>`\x1B[${r};${e}H`,k=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`},$=(r,e)=>e-r<1?"":`${k(r)}${I(1,e-r)}`;var p=class{renderer;stdout;verbose;footerLines=[];installed=!1;resizeBound=!1;constructor(e){this.verbose=e.verbose,this.stdout=e.stdout??H.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=$(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(`${w()}${v(e,1)}
7
+ ${b}`),this.installed=!1}}};import{jsx as D}from"react/jsx-runtime";var B=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??W,this.stdout=e.stdout??c(A.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=D(y,{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=D(S,{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{B as PersistentInkDevUi};
8
+ //# sourceMappingURL=persistent-ink-dev-ui-FUJQDFNY.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'\nimport { createSafeStream } from 'src/tui/safe-stderr'\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` behind the scrollback-safe filter). */\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 // NOT raw `process.stdout`: Ink writes `ESC[3J` (erase saved lines) whenever a frame overflows the\n // viewport, which wipes the terminal's SCROLLBACK \u2014 the user's history, and every previous command's\n // output when `dev` runs as a session-shell child. `createSafeStream` drops that one escape and\n // forwards everything else live. The handoff at `ready` passes this same stream to ScrollRegionDevUi,\n // so both UIs are covered by this single default.\n this.stdout = deps.stdout ?? createSafeStream(process.stdout as NodeJS.WriteStream)\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", "createSafeStream", "process", "DevRenderer", "text", "message", "level", "readyLines", "tree", "BootRegion", "willWrite", "phase", "summary", "ScrollRegionDevUi", "line", "LiveRegion", "input", "formatClock"]
7
+ }
@@ -1,2 +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});
1
+ import{h as i}from"./chunk-3OMHS5RE.js";import{a as t}from"./chunk-6FDGBQGX.js";import"./chunk-6FU2TRU5.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
2
  //# sourceMappingURL=update-check.js.map
@@ -2,6 +2,6 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/entry/update-check.ts"],
4
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",
5
+ "mappings": "wIAYA,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
6
  "names": ["fs", "process", "fileURLToPath", "parseParentPid", "argv", "index", "pid", "selfRealPath", "fs", "fileURLToPath", "main", "runUpdateCheck", "package_default", "process"]
7
7
  }
package/dist/vite.js CHANGED
@@ -1,2 +1,2 @@
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};
1
+ import{b as r,d as t,e}from"./chunk-6EPRH34O.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.131",
4
+ "version": "0.1.133",
5
5
  "files": [
6
6
  "dist"
7
7
  ],
@@ -1,2 +0,0 @@
1
- import{render as ae}from"ink";import U from"node:process";import{Box as k,Text as w,useApp as X,useInput as Y}from"ink";import{useEffect as Z,useState as D}from"react";import{useMemo as O,useState as G}from"react";var M=(t,e)=>{let[n,h]=G(""),[x,g]=G(0),m=O(()=>{let p=n.trim().toLowerCase();return p?t.filter(i=>e(i).toLowerCase().includes(p)):t},[t,n,e]),a=Math.min(x,Math.max(0,m.length-1));return{filtered:m,activeIndex:a,query:n,handleNavigation:(p,i)=>{let c=m.length;return i.upArrow?(c>0&&g((a-1+c)%c),!0):i.downArrow?(c>0&&g((a+1)%c),!0):i.backspace||i.delete?(h(n.slice(0,-1)),g(0),!0):p&&!i.ctrl&&!i.meta?(h(n+p),g(0),!0):!1}}};import{jsx as y,jsxs as $}from"react/jsx-runtime";var Q=t=>{let{items:e,onSubmit:n,onCancel:h,required:x=!0,allowSelectAll:g=!0}=t,m={hint:"Select branches \u2014 type to filter, \u2191\u2193 to move, Space to toggle, Ctrl-A all, Enter to submit, Esc to cancel",prompt:"\u276F ",empty:"No matching branches",requiredHint:"select at least one"},a=e.reduce((s,f)=>Math.max(s,f.label.length),0),{exit:b}=X(),{filtered:p,activeIndex:i,query:c,handleNavigation:v}=M(e,_),[o,u]=D(()=>new Set),[P,A]=D(!1),[B,l]=D(!1);Z(()=>{B&&b()},[B,b]);let r=s=>{u(f=>{let d=new Set(f);return d.has(s)?d.delete(s):d.add(s),d})},C=()=>{u(s=>e.every(d=>s.has(d.value))?new Set:new Set(e.map(d=>d.value)))},E=()=>{if(x&&o.size===0){A(!0);return}n([...o]),l(!0)};return Y((s,f)=>{if(!B){if(f.escape||f.ctrl&&s==="c"){h(),l(!0);return}if(f.ctrl&&s==="a"){g&&C();return}if(s===" "){let d=p[i];d&&r(d.value);return}if(f.return){E();return}v(s,f)}}),B?null:$(k,{flexDirection:"column",children:[y(w,{dimColor:!0,children:m.hint}),$(k,{children:[y(w,{color:"cyan",children:m.prompt}),y(w,{children:c})]}),y(k,{flexDirection:"column",marginTop:1,children:p.length===0?y(w,{dimColor:!0,children:m.empty}):p.map((s,f)=>{let d=f===i,V=o.has(s.value);return $(w,{color:d?"green":void 0,children:[d?"\u203A ":" ",V?"[x] ":"[ ] ",s.label.padEnd(a)," ",y(w,{dimColor:!0,children:s.description??""})]},s.value)})}),P?y(w,{dimColor:!0,children:m.requiredHint}):null]})};function _(t){return`${t.label} ${t.description??""}`}import{Box as F,Text as I,useApp as j,useInput as ee}from"ink";import{useEffect as te,useState as re}from"react";import{jsx as q,jsxs as H}from"react/jsx-runtime";var z=t=>{let{items:e,onSelect:n,onCancel:h}=t,x={hint:"Select a branch \u2014 type to filter, \u2191\u2193 to move, Enter to select, Esc to cancel",prompt:"\u276F ",empty:"No matching branches"},g=e.reduce((o,u)=>Math.max(o,u.label.length),0),{exit:m}=j(),{filtered:a,activeIndex:b,query:p,handleNavigation:i}=M(e,oe),[c,v]=re(!1);return te(()=>{c&&m()},[c,m]),ee((o,u)=>{if(!c){if(u.escape||u.ctrl&&o==="c"){h(),v(!0);return}if(u.return){let P=a[b];P&&(n(P.value),v(!0));return}i(o,u)}}),c?null:H(F,{flexDirection:"column",children:[q(I,{dimColor:!0,children:x.hint}),H(F,{children:[q(I,{color:"cyan",children:x.prompt}),q(I,{children:p})]}),q(F,{flexDirection:"column",marginTop:1,children:a.length===0?q(I,{dimColor:!0,children:x.empty}):a.map((o,u)=>{let P=u===b;return H(I,{color:ne(o.type,P),children:[P?"\u203A ":" ",o.label.padEnd(g)," ",q(I,{dimColor:!0,children:o.description??""})]},o.value)})})]})};function ne(t,e){return t==="hotfix"?"red":e?"green":void 0}function oe(t){return`${t.label} ${t.description??""}`}import{Box as L,Text as S,useApp as ie,useInput as le}from"ink";import{useEffect as ce,useMemo as se,useState as R}from"react";import{jsx as T,jsxs as N}from"react/jsx-runtime";var J=t=>{let{items:e,onSelect:n,onCancel:h}=t,x={hint:"Select a command \u2014 type to filter, \u2191\u2193 to move, Enter to run, Esc to cancel",prompt:"\u276F ",empty:"No matching commands"},g=24,{exit:m}=ie(),[a,b]=R(""),[p,i]=R(0),[c,v]=R(!1);ce(()=>{c&&m()},[c,m]);let o=se(()=>{let l=a.trim().toLowerCase();return l?e.filter(r=>`${r.name} ${r.description}`.toLowerCase().includes(l)):e},[e,a]),u=Math.min(p,Math.max(0,o.length-1)),P=()=>{h(),v(!0)},A=l=>{let r=o.length;r>0&&i((u+l+r)%r)};if(le((l,r)=>{if(!c){if(r.ctrl&&l==="c"){P();return}if(r.escape&&a){b(""),i(0);return}if(r.escape){P();return}if(r.return){let C=o[u];C&&(n(C.name),v(!0));return}if(r.upArrow){A(-1);return}if(r.downArrow){A(1);return}if(r.backspace||r.delete){b(a.slice(0,-1)),i(0);return}l&&!r.ctrl&&!r.meta&&(b(a+l),i(0))}}),c)return null;let B="";return N(L,{flexDirection:"column",children:[T(S,{dimColor:!0,children:x.hint}),N(L,{children:[T(S,{color:"cyan",children:x.prompt}),T(S,{children:a})]}),T(L,{flexDirection:"column",marginTop:1,children:o.length===0?T(S,{dimColor:!0,children:x.empty}):o.map((l,r)=>{let C=r===u,E=l.group!==B;return B=l.group,N(L,{flexDirection:"column",children:[E&&r>0?T(S,{children:" "}):null,E?T(S,{color:"yellow",children:`\u2014 ${l.group} \u2014`}):null,N(S,{color:C?"green":void 0,children:[C?"\u203A ":" ",l.name.padEnd(g)," ",T(S,{dimColor:!0,children:l.description})]})]},l.name)})})]})};import{jsx as K}from"react/jsx-runtime";var W=async t=>{let{waitUntilExit:e}=ae(t,{stdout:U.stderr});try{await e()}catch{}finally{U.stdin.ref()}},Me=async t=>{let e=null;return await W(K(J,{items:t,onSelect:n=>{e=n},onCancel:()=>{e=null}})),e},Le=async t=>{let e=null;return await W(K(z,{items:t,onSelect:n=>{e=n},onCancel:()=>{e=null}})),e},Ne=async(t,e)=>{let n=null;return await W(K(Q,{items:t,required:e?.required,allowSelectAll:e?.allowSelectAll,onSubmit:h=>{n=h},onCancel:()=>{n=null}})),n};export{Ne as runBranchMultiPicker,Le as runBranchPicker,Me as runCommandPalette};
2
- //# sourceMappingURL=boot-5GFZBDKL.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/tui/boot.tsx", "../src/tui/screens/branch-multi-picker.tsx", "../src/tui/hooks/use-list-filter.ts", "../src/tui/screens/branch-picker.tsx", "../src/tui/screens/command-palette.tsx"],
4
- "sourcesContent": ["import { render } from 'ink'\nimport process from 'node:process'\nimport type { ReactElement } from 'react'\n\nimport type { BranchPickerItem } from 'src/lib/prompts/types'\n\nimport { BranchMultiPicker } from './screens/branch-multi-picker'\nimport { BranchPicker } from './screens/branch-picker'\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 an Ink element to stderr and resolve once it tears down. Frames are\n * written to stderr so stdout stays clean for the command that runs afterwards\n * (mirrors the previous Inquirer menu).\n *\n * Ink `unref()`s process.stdin when it tears down raw mode on exit. That ref is\n * process-wide and never restored, so the next consumer of stdin \u2014 the Inquirer\n * prompt the selected command opens (e.g. the worktrees-remove checkbox), or a\n * SECOND Ink render in the same process (the bare-menu palette \u2192 picker hop) \u2014\n * reads from an unref'd handle that no longer keeps the event loop alive. Once\n * the command's git/gh subprocesses settle, the loop drains mid-prompt, Node\n * flags entry/cli.ts's top-level await as unsettled, and the process exits with\n * code 13 (the prompt dies on arrival). Re-ref stdin on BOTH the resolve and the\n * reject path so it holds the loop while the next prompt is reading; an idle\n * ref'd stdin does not block exit, so non-interactive follow-up commands still\n * terminate normally. Every render must go through here so the re-ref is never\n * skipped.\n */\nconst renderToStderr = async (element: ReactElement): Promise<void> => {\n const { waitUntilExit } = render(element, {\n stdout: process.stderr as unknown as NodeJS.WriteStream,\n })\n\n try {\n await waitUntilExit()\n } catch {\n // Swallow a teardown rejection: the caller reports cancellation via its own\n // captured sentinel (which stays at its initial value), not via a throw.\n } finally {\n process.stdin.ref()\n }\n}\n\n/**\n * Render the command palette and resolve to the chosen command name, or `null`\n * if the user cancels.\n */\nexport const runCommandPalette = async (items: PaletteItem[]): Promise<string | null> => {\n let selected: string | null = null\n\n await renderToStderr(\n <CommandPalette\n items={items}\n onSelect={(name) => {\n selected = name\n }}\n onCancel={() => {\n selected = null\n }}\n />,\n )\n\n return selected\n}\n\n/**\n * Render the searchable single-select branch picker and resolve to the chosen\n * value, or `null` if the user cancels.\n */\nexport const runBranchPicker = async (items: BranchPickerItem[]): Promise<string | null> => {\n let selected: string | null = null\n\n await renderToStderr(\n <BranchPicker\n items={items}\n onSelect={(value) => {\n selected = value\n }}\n onCancel={() => {\n selected = null\n }}\n />,\n )\n\n return selected\n}\n\n/**\n * Render the searchable multi-select branch picker and resolve to the chosen\n * values, or `null` if the user cancels. The captured variable starts as `null`\n * and is only assigned an array in `onSubmit`, so a cancel is distinguishable\n * from an empty submit.\n */\nexport const runBranchMultiPicker = async (\n items: BranchPickerItem[],\n opts?: { required?: boolean; allowSelectAll?: boolean },\n): Promise<string[] | null> => {\n let selected: string[] | null = null\n\n await renderToStderr(\n <BranchMultiPicker\n items={items}\n required={opts?.required}\n allowSelectAll={opts?.allowSelectAll}\n onSubmit={(values) => {\n selected = values\n }}\n onCancel={() => {\n selected = null\n }}\n />,\n )\n\n return selected\n}\n", "import { Box, Text, useApp, useInput } from 'ink'\nimport { useEffect, useState } from 'react'\n\nimport type { BranchPickerItem } from '../../lib/prompts/types'\nimport { useListFilter } from '../hooks/use-list-filter'\n\ninterface BranchMultiPickerProps {\n items: BranchPickerItem[]\n /** Called with the selected item values; the app then exits. */\n onSubmit: (values: string[]) => void\n /** Called when the user cancels (Esc / Ctrl-C); the app then exits. */\n onCancel: () => void\n /** When true (default), Enter with an empty selection is ignored. */\n required?: boolean\n /** When true (default), Ctrl-A toggles select-all across all items. */\n allowSelectAll?: boolean\n}\n\n/**\n * Searchable multi-select branch picker rendered with Ink. Same filter/move/cancel\n * behaviour as `BranchPicker`, plus a selection `Set` keyed by item *value* so the\n * selection survives filter changes. Space toggles the active row; Ctrl-A toggles\n * select-all across all items; Enter submits the selected values.\n */\nexport const BranchMultiPicker = (props: BranchMultiPickerProps) => {\n const { items, onSubmit, onCancel, required = true, allowSelectAll = true } = props\n\n const T = {\n hint: 'Select branches \u2014 type to filter, \u2191\u2193 to move, Space to toggle, Ctrl-A all, Enter to submit, Esc to cancel',\n prompt: '\u276F ',\n empty: 'No matching branches',\n requiredHint: 'select at least one',\n }\n const labelWidth = items.reduce((max, item) => {\n return Math.max(max, item.label.length)\n }, 0)\n\n const { exit } = useApp()\n const { filtered, activeIndex, query, handleNavigation } = useListFilter(items, filterText)\n // Selection keyed by value so it persists across filter changes.\n const [selected, setSelected] = useState<Set<string>>(() => {\n return new Set()\n })\n const [showRequiredHint, setShowRequiredHint] = useState(false)\n // When set, the component renders `null` for one frame so Ink erases the list\n // before unmount commits its final frame.\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 toggleValue = (value: string) => {\n setSelected((prev) => {\n const next = new Set(prev)\n\n if (next.has(value)) {\n next.delete(value)\n } else {\n next.add(value)\n }\n\n return next\n })\n }\n\n const toggleSelectAll = () => {\n setSelected((prev) => {\n // Clear only when every item (all, not just filtered) is already selected.\n const allSelected = items.every((item) => {\n return prev.has(item.value)\n })\n\n if (allSelected) {\n return new Set()\n }\n\n return new Set(\n items.map((item) => {\n return item.value\n }),\n )\n })\n }\n\n const submit = () => {\n if (required && selected.size === 0) {\n setShowRequiredHint(true)\n\n return\n }\n\n onSubmit([...selected])\n setSubmitted(true)\n }\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.ctrl && input === 'a') {\n if (allowSelectAll) {\n toggleSelectAll()\n }\n\n return\n }\n\n if (input === ' ') {\n const active = filtered[activeIndex]\n\n if (active) {\n toggleValue(active.value)\n }\n\n return\n }\n\n if (key.return) {\n submit()\n\n return\n }\n\n handleNavigation(input, key)\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 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 isChecked = selected.has(item.value)\n\n return (\n <Text color={isActive ? 'green' : undefined} key={item.value}>\n {isActive ? '\u203A ' : ' '}\n {isChecked ? '[x] ' : '[ ] '}\n {item.label.padEnd(labelWidth)} <Text dimColor>{item.description ?? ''}</Text>\n </Text>\n )\n })\n )}\n </Box>\n {showRequiredHint ? <Text dimColor>{T.requiredHint}</Text> : null}\n </Box>\n )\n}\n\nfunction filterText(item: BranchPickerItem): string {\n return `${item.label} ${item.description ?? ''}`\n}\n", "import type { Key } from 'ink'\nimport { useMemo, useState } from 'react'\n\ninterface ListFilter<T> {\n /** Items matching the current query (all items when the query is empty). */\n filtered: T[]\n /** Active row index, always clamped into `filtered`'s bounds. */\n activeIndex: number\n /** Current filter query. */\n query: string\n /**\n * Handle the shared navigation/filter keys (\u2191\u2193 wrap-around, backspace/delete,\n * printable chars). Returns true when the key was consumed so the caller can\n * `return` early; false leaves the key for the component's own handlers.\n */\n handleNavigation: (input: string, key: Key) => boolean\n}\n\n/**\n * Shared list-filter state for the branch pickers: substring filtering,\n * wrap-around \u2191\u2193 movement, and query editing. Extracted so each `useInput`\n * handler stays under the sonarjs cognitive-complexity \u226415 gate.\n */\nexport const useListFilter = <T>(items: T[], toText: (item: T) => string): ListFilter<T> => {\n const [query, setQuery] = useState('')\n const [index, setIndex] = useState(0)\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 toText(item).toLowerCase().includes(q)\n })\n }, [items, query, toText])\n\n const activeIndex = Math.min(index, Math.max(0, filtered.length - 1))\n\n const handleNavigation = (input: string, key: Key): boolean => {\n const n = filtered.length\n\n if (key.upArrow) {\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 true\n }\n\n if (key.downArrow) {\n if (n > 0) {\n setIndex((activeIndex + 1) % n)\n }\n\n return true\n }\n\n if (key.backspace || key.delete) {\n setQuery(query.slice(0, -1))\n setIndex(0)\n\n return true\n }\n\n // Printable character: append to the filter and reset to the first row.\n if (input && !key.ctrl && !key.meta) {\n setQuery(query + input)\n setIndex(0)\n\n return true\n }\n\n return false\n }\n\n return { filtered, activeIndex, query, handleNavigation }\n}\n", "import { Box, Text, useApp, useInput } from 'ink'\nimport { useEffect, useState } from 'react'\n\nimport type { BranchPickerItem } from '../../lib/prompts/types'\nimport { useListFilter } from '../hooks/use-list-filter'\n\ninterface BranchPickerProps {\n items: BranchPickerItem[]\n /** Called with the chosen item's value; the app then exits. */\n onSelect: (value: string) => void\n /** Called when the user cancels (Esc / Ctrl-C); the app then exits. */\n onCancel: () => void\n}\n\n/**\n * Searchable single-select branch picker rendered with Ink. Mirrors\n * `CommandPalette` minus the group headers: type-to-filter over label +\n * description, \u2191\u2193 wrap-around, Enter selects the active item's *value*.\n */\nexport const BranchPicker = (props: BranchPickerProps) => {\n const { items, onSelect, onCancel } = props\n\n const T = {\n hint: 'Select a branch \u2014 type to filter, \u2191\u2193 to move, Enter to select, Esc to cancel',\n prompt: '\u276F ',\n empty: 'No matching branches',\n }\n const labelWidth = items.reduce((max, item) => {\n return Math.max(max, item.label.length)\n }, 0)\n\n const { exit } = useApp()\n const { filtered, activeIndex, query, handleNavigation } = useListFilter(items, filterText)\n // When set, the component renders `null` for one frame so Ink erases the list\n // before unmount commits its final frame.\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 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.value)\n setSubmitted(true)\n }\n\n return\n }\n\n handleNavigation(input, key)\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 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\n return (\n <Text color={labelColor(item.type, isActive)} key={item.value}>\n {isActive ? '\u203A ' : ' '}\n {item.label.padEnd(labelWidth)} <Text dimColor>{item.description ?? ''}</Text>\n </Text>\n )\n })\n )}\n </Box>\n </Box>\n )\n}\n\n/**\n * Colour for a row's label, derived from the release type. Kept a plain lookup so\n * an unknown/absent type simply falls through to the default (green when active).\n */\nfunction labelColor(type: string | undefined, isActive: boolean): string | undefined {\n if (type === 'hotfix') {\n return 'red'\n }\n\n return isActive ? 'green' : undefined\n}\n\nfunction filterText(item: BranchPickerItem): string {\n return `${item.label} ${item.description ?? ''}`\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 // Back out with nothing picked (Ctrl-C, or Esc on an empty filter).\n const quit = () => {\n onCancel()\n setSubmitted(true)\n }\n\n // Move the active row by `delta`, wrapping. The `+ n` sidesteps JS negative modulo\n // (e.g. -1 % 5 === -1, not 4). No-op on an empty list.\n const step = (delta: number) => {\n const n = filtered.length\n\n if (n > 0) {\n setIndex((activeIndex + delta + n) % n)\n }\n }\n\n useInput((input, key) => {\n if (submitted) {\n return\n }\n\n // Ctrl-C always quits; Esc clears a non-empty filter first, and only quits on an empty filter \u2014\n // the standard REPL/palette split, so the session shell keeps running while the user narrows.\n if (key.ctrl && input === 'c') {\n quit()\n\n return\n }\n\n if (key.escape && query) {\n setQuery('')\n setIndex(0)\n\n return\n }\n\n if (key.escape) {\n quit()\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 step(-1)\n\n return\n }\n\n if (key.downArrow) {\n step(1)\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", "useState", "useMemo", "useState", "useListFilter", "items", "toText", "query", "setQuery", "index", "setIndex", "filtered", "q", "item", "activeIndex", "input", "key", "n", "jsx", "jsxs", "BranchMultiPicker", "props", "items", "onSubmit", "onCancel", "required", "allowSelectAll", "T", "labelWidth", "max", "item", "exit", "useApp", "filtered", "activeIndex", "query", "handleNavigation", "useListFilter", "filterText", "selected", "setSelected", "useState", "showRequiredHint", "setShowRequiredHint", "submitted", "setSubmitted", "useEffect", "toggleValue", "value", "prev", "next", "toggleSelectAll", "submit", "useInput", "input", "key", "active", "Box", "Text", "position", "isActive", "isChecked", "Box", "Text", "useApp", "useInput", "useEffect", "useState", "jsx", "jsxs", "BranchPicker", "props", "items", "onSelect", "onCancel", "T", "labelWidth", "max", "item", "exit", "useApp", "filtered", "activeIndex", "query", "handleNavigation", "useListFilter", "filterText", "submitted", "setSubmitted", "useState", "useEffect", "useInput", "input", "key", "selected", "Box", "Text", "position", "isActive", "labelColor", "type", "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", "quit", "step", "delta", "n", "input", "key", "selected", "lastGroup", "position", "isActive", "showGroup", "jsx", "renderToStderr", "element", "waitUntilExit", "render", "process", "runCommandPalette", "items", "selected", "CommandPalette", "name", "runBranchPicker", "BranchPicker", "value", "runBranchMultiPicker", "opts", "BranchMultiPicker", "values"]
7
- }
@@ -1,2 +0,0 @@
1
- import{a as v,b as P,c as y}from"./chunk-LTKR3F4N.js";import{Buffer as w}from"node:buffer";import{execFileSync as A}from"node:child_process";import f from"node:fs";import E from"node:net";import u from"node:path";import g from"node:process";import{pathToFileURL as K}from"node:url";import{z as l}from"zod";import{z as i}from"zod";var k=i.strictObject({requiredScripts:i.array(i.string().min(1)).optional(),requiredFiles:i.array(i.string().min(1)).optional(),turbo:i.strictObject({requiredTasks:i.array(i.string().min(1)).optional()}).optional(),dev:i.strictObject({proxy:i.strictObject({templates:i.strictObject({local:i.string().min(1),cloud:i.string().min(1)}),routes:i.record(i.string().min(1),i.strictObject({packageName:i.string().min(1),from:i.array(i.enum(["local","cloud"])).min(1),default:i.enum(["local","cloud"]).optional()}).refine(e=>e.from.length<=1||e.default!==void 0,{message:"default is required when `from` has more than one source"}).refine(e=>e.default===void 0||e.from.includes(e.default),{message:"default must be listed in `from`"}))}).optional()}).optional()});var R="INFRA_KIT_ENV",h="infra-kit.config.ts",D=u.join(".infra-kit","dev-context"),N=u.join(".infra-kit","dev-context.json"),_=l.object({package:l.string(),port:l.number(),pid:l.number().optional(),writtenAt:l.number().optional(),release:l.string().optional(),alias:l.string().optional(),proxyPort:l.number().optional()}),L=(e,t)=>{let r=t?.username??e.E2E__BASIC_AUTH_USERNAME,n=t?.password??e.E2E__BASIC_AUTH_PASSWORD;return!r||!n?void 0:`Basic ${w.from(`${r}:${n}`).toString("base64")}`};var S=(e,t)=>e.replaceAll("<release>",t.release).replaceAll("<packageName>",t.packageName).replaceAll("<env>",t.env),O=80,T="127.0.0.1",j=(e,t)=>{if(t==null||t===O)return e;let r;try{r=new URL(e)}catch{throw new Error(`infra-kit/vite: dev.proxy templates.local resolved to "${e}", which is not a valid URL, so the proxy port ${t} cannot be applied.`)}if(r.port!=="")return e;r.port=String(t);let n=r.toString();return!e.endsWith("/")&&n.endsWith("/")?n.slice(0,-1):n},F=e=>{let{route:t,templates:r,env:n,getRelease:o,info:a}=e,s=S(r.local,{release:a?.release??o(),packageName:v(t.packageName),env:n??""});return j(s,a?.proxyPort)},C=(e,t)=>{let r=e.default??e.from[0];return e.from.includes("local")&&t.has(e.packageName)?"local":r},U=({routePath:e,route:t,templates:r,localSet:n,env:o,getRelease:a,localInfo:s})=>{if(C(t,n)==="local")return{target:F({route:t,templates:r,env:o,getRelease:a,info:s?.get(t.packageName)}),changeOrigin:!0};if(!o)throw new Error(`infra-kit/vite: proxy route "${e}" resolves to a cloud backend but ${R} is not set. Source an environment from Doppler first (e.g. \`infra-kit env-load -c dev\`).`);return{target:S(r.cloud,{release:"",packageName:t.packageName,env:o}),changeOrigin:!0,secure:!1,cookieDomainRewrite:"localhost"}},V=({proxy:e,localSet:t,env:r,getRelease:n,authHeader:o,localInfo:a})=>{let s={},c=o?{Authorization:o}:void 0;for(let[d,p]of Object.entries(e.routes)){let m=U({routePath:d,route:p,templates:e.templates,localSet:t,env:r,getRelease:n,localInfo:a});s[d]=c?{...m,headers:c}:m}return s},$=e=>{let t;return()=>(t??={value:e()},t.value)},B=async e=>{let t=u.join(e,h);if(!f.existsSync(t))return;let r=f.statSync(t),a=(await import(`${K(t).href}?mtime=${Number(r.mtimeMs)}`)).default;if(a===void 0)return;let s=typeof a=="function"?await a():a,c=k.safeParse(s);if(!c.success)throw new Error(`infra-kit/vite: invalid ${h} at ${t}: ${l.prettifyError(c.error)}`);return c.data.dev},M=e=>{if(Array.isArray(e))return e.filter(t=>typeof t=="string");if(e!==null&&typeof e=="object"){let t=e.packages??e.localPackages;if(Array.isArray(t))return t.filter(r=>typeof r=="string")}return[]},I=(e,t)=>{let r=u.resolve(e);for(;;){let n=u.join(r,t);if(f.existsSync(n))return n;let o=u.dirname(r);if(o===r)return;r=o}},x=()=>({packages:new Set,info:new Map}),H=e=>{if(e==null)return!0;try{return g.kill(e,0),!0}catch(t){return t.code==="EPERM"}},J=e=>{let t;try{t=f.readdirSync(e)}catch{return x()}let r=new Set,n=new Map;for(let o of t)if(o.endsWith(".json"))try{let a=_.safeParse(JSON.parse(f.readFileSync(u.join(e,o),"utf-8")));if(!a.success||!H(a.data.pid))continue;r.add(a.data.package),n.set(a.data.package,{port:a.data.port,release:a.data.release,alias:a.data.alias,proxyPort:a.data.proxyPort})}catch{continue}return{packages:r,info:n}},W=e=>{let t=I(e,D);if(t)return J(t);let r=I(e,N);if(!r)return x();try{return{packages:new Set(M(JSON.parse(f.readFileSync(r,"utf-8")))),info:new Map}}catch{return x()}};var q=e=>A("git",["rev-parse","--abbrev-ref","HEAD"],{cwd:e,encoding:"utf-8"}).trim(),G=()=>new Promise((e,t)=>{let r=E.createServer();r.unref(),r.on("error",t),r.listen(0,"127.0.0.1",()=>{let n=r.address(),o=typeof n=="object"&&n!==null?n.port:0;r.close(()=>e(o))})}),z="INFRA_KIT_UI_PORTS",X=e=>{try{let t=JSON.parse(f.readFileSync(u.join(e,"package.json"),"utf-8"));return typeof t.name=="string"?t.name:null}catch{return null}},Q=e=>{let t=g.env[z];if(t==null||t==="")return null;let r=X(e);if(r==null)return null;try{let o=JSON.parse(t)[r];return typeof o=="number"?o:null}catch{return null}},Y=async(e={})=>{let t=e.cwd??g.cwd();if(e.command==="build")return{proxy:{}};let r=e.port==null?Q(t):null,n=e.port??r??await G(),o=r!=null,a=e.host??T,s=await B(t);if(!s?.proxy)return{port:n,host:a,...o?{strictPort:o}:{},proxy:{}};let{packages:c,info:d}=W(t),p=g.env[R],m=L(g.env,e.basicAuth),b=$(()=>{try{return P(q(t))||y}catch{return y}});return{port:n,host:a,...o?{strictPort:o}:{},proxy:V({proxy:s.proxy,localSet:c,env:p,getRelease:b,authHeader:m,localInfo:d})}},fe=async(e={})=>(await Y(e)).proxy;export{k as a,V as b,B as c,Y as d,fe as e};
2
- //# sourceMappingURL=chunk-4UUVKJYG.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/lib/vite/vite.ts", "../src/lib/package-config/package-config-schema.ts"],
4
- "sourcesContent": ["import { Buffer } from 'node:buffer'\nimport { execFileSync } from 'node:child_process'\nimport fs from 'node:fs'\nimport net from 'node:net'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { pathToFileURL } from 'node:url'\nimport { z } from 'zod'\n\nimport type {\n InfraKitDev,\n InfraKitDevProxy,\n InfraKitDevProxyRoute,\n InfraKitDevProxySource,\n} from '../package-config/package-config'\nimport { packageConfigSchema } from '../package-config/package-config-schema'\nimport { DEFAULT_RELEASE_SLUG, slugifyHostLabel, slugifyRelease } from '../release-slug/release-slug'\n\n/**\n * Env-name handle read at vite-config time to fill the `<env>` placeholder in a\n * cloud proxy target. Inlined (not imported from `src/lib/constants`) to keep the\n * `infra-kit/vite` bundle's import graph tiny \u2014 see the module header. Kept\n * byte-identical to `INFRA_KIT_ENV_VAR` in `src/lib/constants/constants.ts`.\n */\nconst INFRA_KIT_ENV = 'INFRA_KIT_ENV'\n\n/** Per-package config filename, mirrored from the CLI's `PACKAGE_CONFIG_FILE`. */\nconst PACKAGE_CONFIG_FILE = 'infra-kit.config.ts'\n\n/**\n * Repo-relative dev-context fragment DIRECTORY, searched upward from cwd. Each\n * runner (single-process or cmux pane) writes its OWN `<app>.json` fragment here\n * recording its real bound port + release; the helper merges them (see\n * {@link readLocalContext}). This is the current source of truth.\n */\nconst DEV_CONTEXT_DIR = path.join('.infra-kit', 'dev-context')\n\n/**\n * Legacy single-file dev-context manifest, searched upward from cwd. Read only as\n * a transitional back-compat path when {@link DEV_CONTEXT_DIR} is absent (the\n * directory wins per-package when both exist).\n */\nconst DEV_CONTEXT_FILE = path.join('.infra-kit', 'dev-context.json')\n\n/**\n * One `.infra-kit/dev-context/<app>.json` fragment. `package` is the load-bearing\n * field the helper reads (\u2192 localSet); `release` lets the helper prefer the\n * runner-recorded slug over its own git derivation, and `proxyPort` is the port the\n * alias resolves on \u2014 see {@link resolveRoute}. `alias` is recorded for provenance;\n * the target is rebuilt from the template, not read from it. `port` is no longer a\n * proxy target (the direct `127.0.0.1:<port>` branch is gone) \u2014 it survives as\n * diagnostic provenance. `pid` is the staleness guard: {@link readFragmentDir} drops a\n * fragment whose writer is no longer alive, so a crashed runner cannot keep a package\n * pinned `local` at a dead alias. `writtenAt` is provenance only. Kept lenient (not\n * `.strict()`) so extra writer fields never reject a valid fragment.\n */\nconst devContextFragmentSchema = z.object({\n package: z.string(),\n port: z.number(),\n pid: z.number().optional(),\n writtenAt: z.number().optional(),\n release: z.string().optional(),\n alias: z.string().optional(),\n proxyPort: z.number().optional(),\n})\n\n/**\n * A single Vite `server.proxy` entry. `changeOrigin` is always set; the\n * cloud-only pair (`secure: false` + `cookieDomainRewrite: 'localhost'`) makes a\n * local FE talk to an HTTPS cloud BE without cert/cookie-domain breakage.\n * `headers` is present only when HTTP Basic Auth is injected (see\n * {@link buildBasicAuthHeader}) \u2014 it carries the `Authorization` header applied\n * uniformly to every route so upstream environments behind auth (e2e/staging)\n * stay reachable.\n */\nexport interface InfraKitViteProxyEntry {\n target: string\n changeOrigin: true\n secure?: false\n cookieDomainRewrite?: 'localhost'\n headers?: Record<string, string>\n}\n\n/** A Vite `server.proxy`-shaped map: path-prefix \u2192 proxy entry. */\nexport type InfraKitViteProxy = Record<string, InfraKitViteProxyEntry>\n\n/** Explicit HTTP Basic Auth credentials, overriding the `E2E__BASIC_AUTH_*` env vars. */\nexport interface InfraKitBasicAuth {\n username: string\n password: string\n}\n\nexport interface InfraKitDevOptions {\n /** Package dir whose `infra-kit.config.ts` is loaded. Defaults to `process.cwd()`. */\n cwd?: string\n /**\n * Explicit HTTP Basic Auth credentials injected into every proxy route as an\n * `Authorization` header. Takes precedence over the `E2E__BASIC_AUTH_USERNAME`\n * / `E2E__BASIC_AUTH_PASSWORD` env vars. Omit to use the env-based default.\n */\n basicAuth?: InfraKitBasicAuth\n /**\n * Vite's `command`. Pass it (`defineConfig(async ({ command }) => ({ server: await infraKitDev({ command }) }))`)\n * so `build` is a no-op: `server` is irrelevant to a build and proxy resolution would otherwise\n * fail-fast on a cloud route with no sourced env. Omit (or `'serve'`) for the dev-server config.\n */\n command?: 'build' | 'serve'\n /**\n * Explicit dev-server port. Omit for a **per-worktree dynamic** free port (a fresh OS-assigned\n * port) so N simultaneous git worktrees never collide on Vite's default `5173`; Vite prints the\n * chosen URL. Pass a fixed number only when an external contract pins the port.\n */\n port?: number\n /**\n * Interface the dev server binds. Defaults to {@link LOOPBACK_V4} so a portless alias can reach it\n * (Vite's own `localhost` default binds `[::1]` only, which the proxy cannot dial). Override when\n * the dev server must be reachable off the loopback \u2014 `'0.0.0.0'` or `true` inside a container, on\n * a LAN, or for an e2e runner on another host.\n */\n host?: string | boolean\n}\n\n/**\n * Build the `Authorization: Basic <base64(user:pass)>` header value. Credentials\n * come from `override` when given, else from `E2E__BASIC_AUTH_USERNAME` /\n * `E2E__BASIC_AUTH_PASSWORD` (NOTE: double underscore). Returns `undefined` when\n * either half is missing, so callers add no `headers` key at all.\n */\nconst buildBasicAuthHeader = (env: NodeJS.ProcessEnv, override?: InfraKitBasicAuth): string | undefined => {\n const username = override?.username ?? env.E2E__BASIC_AUTH_USERNAME\n const password = override?.password ?? env.E2E__BASIC_AUTH_PASSWORD\n\n if (!username || !password) return undefined\n\n const encoded = Buffer.from(`${username}:${password}`).toString('base64')\n\n return `Basic ${encoded}`\n}\n\n/**\n * Re-exported from the shared, dependency-light `lib/release-slug` module so the\n * published `infra-kit/vite` surface (`entry/vite.ts:7`) is unchanged while the\n * dev-server's fragment writer derives `<release>` from the SAME implementation\n * (no slug drift). See {@link slugifyRelease}.\n */\nexport { slugifyRelease }\n\n/** Fill the `<release>`/`<packageName>`/`<env>` placeholders in a URL template. */\nconst interpolate = (template: string, values: { release: string; packageName: string; env: string }): string => {\n return template\n .replaceAll('<release>', values.release)\n .replaceAll('<packageName>', values.packageName)\n .replaceAll('<env>', values.env)\n}\n\n/** The port a bare `http://` target implies, and the proxy's default \u2014 never spelled out in a URL. */\nconst DEFAULT_HTTP_PORT = 80\n\n/**\n * The interface the vite dev server binds. Pinned to IPv4 loopback because portless dials its routes\n * on `127.0.0.1`, while vite's default `host: 'localhost'` combined with its\n * `dns.setDefaultResultOrder('verbatim')` binds `[::1]` ONLY \u2014 the alias then resolves, the proxy\n * connects, and the connection is refused, surfacing as a 502 on every UI request. Backends never hit\n * this because `ServerlessLocalRun` already binds `127.0.0.1`. Still loopback-only: this narrows the\n * address family, it does not expose the dev server to the network.\n */\nconst LOOPBACK_V4 = '127.0.0.1'\n\n/**\n * Graft the proxy's listen port onto an interpolated local target. The `dev.proxy` local template is\n * written port-free (`http://<release>.<packageName>.localhost`) because the proxy defaults to `:80`.\n * A consumer who trades the clean URL for a zero-sudo unprivileged port still needs the frontend to\n * reach it, so the runner records the port it actually bound and we append it here. A template that\n * already pins its own port keeps it.\n */\nconst withProxyPort = (target: string, proxyPort: number | undefined): string => {\n if (proxyPort == null || proxyPort === DEFAULT_HTTP_PORT) return target\n\n let url: URL\n\n try {\n url = new URL(target)\n } catch {\n throw new Error(\n `infra-kit/vite: dev.proxy templates.local resolved to \"${target}\", which is not a valid URL, so the proxy port ${proxyPort} cannot be applied.`,\n )\n }\n\n if (url.port !== '') return target\n url.port = String(proxyPort)\n\n // `URL.toString()` synthesizes a `/` pathname for an authority-only URL, so the common port-free\n // template would come back as `\u2026localhost:1355/`. Strip that ONLY when the template had no trailing\n // slash of its own \u2014 a template ending in `/` (e.g. `\u2026localhost/base/`) means it, and silently\n // trimming it would change the path the frontend proxies to.\n const grafted = url.toString()\n\n return !target.endsWith('/') && grafted.endsWith('/') ? grafted.slice(0, -1) : grafted\n}\n\n/**\n * The proxy target for a route resolved to `local`: always the interpolated template, i.e. the portless\n * hostname the runner aliased. `infra-kit dev` refuses to start an app it could not alias, so membership\n * in the local set now also proves the package is reachable BY NAME \u2014 there is no direct-port target.\n *\n * The target proxies to a stable HOSTNAME, and the runner re-points that alias at the freshly-bound port\n * on every `--watch` restart, so it self-heals without Vite reloading its config.\n */\nconst resolveLocalTarget = (args: {\n route: InfraKitDevProxyRoute\n templates: InfraKitDevProxy['templates']\n env: string | undefined\n getRelease: () => string\n info: LocalPackageInfo | undefined\n}): string => {\n const { route, templates, env, getRelease, info } = args\n\n const target = interpolate(templates.local, {\n // Prefer THIS package's runner-recorded release slug (R4) over the single global git derivation,\n // so cross-branch FE/BE pairings don't drift the emitted `<release>` from the segment the runner\n // aliased. Fall back to the global git slug only when the fragment carried no release.\n release: info?.release ?? getRelease(),\n // The local template resolves to a `<release>.<packageName>.localhost` alias the dev-server\n // registered with portless \u2014 which slugifies the package name to a legal DNS label. Slugify\n // identically here or a scoped name (`@hulyo/client-ui`) emits a target that no alias backs.\n packageName: slugifyHostLabel(route.packageName),\n env: env ?? '',\n })\n\n return withProxyPort(target, info?.proxyPort)\n}\n\n/**\n * Pick the effective source for a route: `local` when the route lists `local` as\n * a capability AND its packageName is in the local dev set; otherwise the fallback\n * \u2014 the declared `default` for a multi-source route, or the sole `from` entry for\n * a single-source one. Always resolves (the schema guarantees a usable fallback).\n */\nconst pickSource = (route: InfraKitDevProxyRoute, localSet: ReadonlySet<string>): InfraKitDevProxySource => {\n // `from` is guaranteed non-empty by the schema, so `from[0]` is always present.\n const fallback = route.default ?? route.from[0]!\n\n return route.from.includes('local') && localSet.has(route.packageName) ? 'local' : fallback\n}\n\ninterface ResolveRouteArgs {\n routePath: string\n route: InfraKitDevProxyRoute\n templates: InfraKitDevProxy['templates']\n localSet: ReadonlySet<string>\n env: string | undefined\n getRelease: () => string\n /** Per-package runtime data (recorded release/port) from the dev-context merge. */\n localInfo?: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/** Resolve one route into a Vite proxy entry (or throw an actionable error). */\nconst resolveRoute = ({\n routePath,\n route,\n templates,\n localSet,\n env,\n getRelease,\n localInfo,\n}: ResolveRouteArgs): InfraKitViteProxyEntry => {\n const source = pickSource(route, localSet)\n\n if (source === 'local') {\n const target = resolveLocalTarget({ route, templates, env, getRelease, info: localInfo?.get(route.packageName) })\n\n return { target, changeOrigin: true }\n }\n\n if (!env) {\n throw new Error(\n `infra-kit/vite: proxy route \"${routePath}\" resolves to a cloud backend but ${INFRA_KIT_ENV} is not set. Source an environment from Doppler first (e.g. \\`infra-kit env-load -c dev\\`).`,\n )\n }\n\n const target = interpolate(templates.cloud, { release: '', packageName: route.packageName, env })\n\n return { target, changeOrigin: true, secure: false, cookieDomainRewrite: 'localhost' }\n}\n\ninterface ResolveProxyArgs {\n proxy: InfraKitDevProxy\n localSet: ReadonlySet<string>\n env: string | undefined\n getRelease: () => string\n /** Pre-computed `Authorization` header value applied uniformly to every route. */\n authHeader?: string\n /** Per-package runtime data (recorded release/port) from the dev-context merge. */\n localInfo?: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/**\n * Pure resolver: turn a `dev.proxy` config + resolved inputs (local set, env,\n * lazy release) into a Vite `server.proxy` map. Side-effect-free so the\n * resolution shape is fully unit-testable. When `authHeader` is set, every route\n * entry gets a matching `headers.Authorization`; otherwise no `headers` key.\n */\nexport const resolveProxyConfig = ({\n proxy,\n localSet,\n env,\n getRelease,\n authHeader,\n localInfo,\n}: ResolveProxyArgs): InfraKitViteProxy => {\n const result: InfraKitViteProxy = {}\n const headers = authHeader ? { Authorization: authHeader } : undefined\n\n for (const [routePath, route] of Object.entries(proxy.routes)) {\n const entry = resolveRoute({ routePath, route, templates: proxy.templates, localSet, env, getRelease, localInfo })\n\n result[routePath] = headers ? { ...entry, headers } : entry\n }\n\n return result\n}\n\n/** Memoize a zero-arg thunk so `<release>` git resolution runs at most once. */\nconst once = <T>(fn: () => T): (() => T) => {\n let cached: { value: T } | undefined\n\n return () => {\n cached ??= { value: fn() }\n\n return cached.value\n }\n}\n\n/**\n * Load a package's `infra-kit.config.ts` and return its `dev` block, or\n * `undefined` when the config or the `dev` key is absent. The `.ts` config is\n * evaluated via Node's native type stripping (Node >= 24) \u2014 the same mechanism\n * the CLI's config loader uses. Cache-busted by mtime so repeated dev-server\n * reloads pick up edits.\n */\nexport const loadDev = async (cwd: string): Promise<InfraKitDev | undefined> => {\n const configPath = path.join(cwd, PACKAGE_CONFIG_FILE)\n\n if (!fs.existsSync(configPath)) return undefined\n\n const stat = fs.statSync(configPath)\n const moduleUrl = `${pathToFileURL(configPath).href}?mtime=${Number(stat.mtimeMs)}`\n\n const imported = (await import(moduleUrl)) as { default?: unknown }\n const rawExport = imported.default\n\n if (rawExport === undefined) return undefined\n\n const resolved = typeof rawExport === 'function' ? await (rawExport as () => unknown)() : rawExport\n\n const parsed = packageConfigSchema.safeParse(resolved)\n\n if (!parsed.success) {\n throw new Error(`infra-kit/vite: invalid ${PACKAGE_CONFIG_FILE} at ${configPath}: ${z.prettifyError(parsed.error)}`)\n }\n\n return parsed.data.dev\n}\n\n/** Coerce a parsed dev-context.json into the set of locally-running package names. */\nconst extractPackages = (parsed: unknown): string[] => {\n if (Array.isArray(parsed)) {\n return parsed.filter((v): v is string => {\n return typeof v === 'string'\n })\n }\n\n if (parsed !== null && typeof parsed === 'object') {\n const candidate = (parsed as Record<string, unknown>).packages ?? (parsed as Record<string, unknown>).localPackages\n\n if (Array.isArray(candidate)) {\n return candidate.filter((v): v is string => {\n return typeof v === 'string'\n })\n }\n }\n\n return []\n}\n\n/** Search upward from `start` for `relative`, returning the first hit or undefined. */\nconst findUp = (start: string, relative: string): string | undefined => {\n let dir = path.resolve(start)\n\n for (;;) {\n const candidate = path.join(dir, relative)\n\n if (fs.existsSync(candidate)) return candidate\n\n const parent = path.dirname(dir)\n\n if (parent === dir) return undefined\n\n dir = parent\n }\n}\n\n/** Per-package runtime data merged from the dev-context fragment directory. */\nexport interface LocalPackageInfo {\n /** The real bound port the runner recorded. Provenance only \u2014 the proxy target is the alias host. */\n port: number\n /** The runner-recorded release slug, preferred over the helper's git derivation. */\n release?: string\n /** `<release>.<packageName>.localhost`, present only when a portless alias was registered. */\n alias?: string\n /** The port {@link alias} resolves on. `80` (the default) is implicit in the template. */\n proxyPort?: number\n}\n\n/**\n * The locally-running package set plus a `package \u2192 { port, release }` map merged\n * from the dev-context fragments. `packages` feeds `pickSource`; `info` lets a\n * `local` route emit the per-package recorded release (see {@link resolveRoute}).\n */\nexport interface LocalContext {\n packages: ReadonlySet<string>\n info: ReadonlyMap<string, LocalPackageInfo>\n}\n\n/** An empty {@link LocalContext} (frontend-only / dev-context absent). */\nconst emptyLocalContext = (): LocalContext => {\n return { packages: new Set(), info: new Map() }\n}\n\n/**\n * Is the runner that wrote a fragment still alive? Signal `0` performs the permission and existence\n * checks without delivering anything. `ESRCH` is the one answer that means \"gone\"; `EPERM` means the\n * pid exists under another uid, which is still a live process. A fragment with no `pid` predates the\n * field and is trusted (back-compat) rather than dropped.\n */\nconst isFragmentWriterAlive = (pid: number | undefined): boolean => {\n if (pid == null) return true\n\n try {\n process.kill(pid, 0)\n\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/**\n * Merge every `<app>.json` fragment in the dev-context directory into a\n * {@link LocalContext}. Two DISTINCT failure branches (do NOT collapse them into\n * one directory-wide catch):\n * - `readdir`-ENOENT (directory vanished mid-race) \u2192 empty context, matching the\n * dir-absent back-compat behaviour.\n * - a single corrupt/truncated/invalid `<app>.json` \u2192 that ONE fragment is\n * SKIPPED (per-fragment `safeParse` isolation); the rest still merge, so one bad\n * fragment never collapses the whole localSet to empty (which would silently\n * drop a started package to cloud).\n */\nconst readFragmentDir = (dir: string): LocalContext => {\n let entries: string[]\n\n try {\n entries = fs.readdirSync(dir)\n } catch {\n return emptyLocalContext()\n }\n\n const packages = new Set<string>()\n const info = new Map<string, LocalPackageInfo>()\n\n for (const name of entries) {\n if (!name.endsWith('.json')) continue\n\n try {\n const parsed = devContextFragmentSchema.safeParse(JSON.parse(fs.readFileSync(path.join(dir, name), 'utf-8')))\n\n if (!parsed.success) continue\n\n // A runner killed with SIGKILL (or crashed) leaves its fragment AND its portless alias behind.\n // Trusting it would route this package's `/api` at an alias nothing serves \u2014 a silent 502 with\n // no diagnostic. The writer already stamps its pid; honour it.\n if (!isFragmentWriterAlive(parsed.data.pid)) continue\n\n packages.add(parsed.data.package)\n info.set(parsed.data.package, {\n port: parsed.data.port,\n release: parsed.data.release,\n alias: parsed.data.alias,\n proxyPort: parsed.data.proxyPort,\n })\n } catch {\n // Corrupt/truncated fragment (JSON.parse / read failure): skip ONLY this one.\n continue\n }\n }\n\n return { packages, info }\n}\n\n/**\n * Read the locally-running package context, searched upward from `cwd`. Prefers\n * the `.infra-kit/dev-context/` fragment DIRECTORY (merged via\n * {@link readFragmentDir}); when it is absent, falls back to the legacy single\n * `.infra-kit/dev-context.json` file (read as before \u2014 no per-package release);\n * when neither exists \u2192 empty context (frontend-only, every route resolves cloud).\n */\nexport const readLocalContext = (cwd: string): LocalContext => {\n const dir = findUp(cwd, DEV_CONTEXT_DIR)\n\n if (dir) return readFragmentDir(dir)\n\n const legacy = findUp(cwd, DEV_CONTEXT_FILE)\n\n if (!legacy) return emptyLocalContext()\n\n try {\n return { packages: new Set(extractPackages(JSON.parse(fs.readFileSync(legacy, 'utf-8')))), info: new Map() }\n } catch {\n return emptyLocalContext()\n }\n}\n\n/**\n * Read the set of locally-running packages (searched upward from `cwd`). Thin\n * wrapper over {@link readLocalContext} preserving the historical `Set`-returning\n * contract. Absent dev-context \u2192 empty set (frontend-only cloud resolution).\n */\nexport const readLocalSet = (cwd: string): ReadonlySet<string> => {\n return readLocalContext(cwd).packages\n}\n\n/** Current git branch of `cwd` (raw, un-slugified). */\nconst readGitBranch = (cwd: string): string => {\n // eslint-disable-next-line sonarjs/no-os-command-from-path\n return execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, encoding: 'utf-8' }).trim()\n}\n\n/**\n * An OS-assigned free TCP port on 127.0.0.1 \u2014 the per-worktree dev-server port (mirrors the\n * backend's `listen(0)`). This probes then releases the port, so there is a small TOCTOU window\n * before Vite binds it; Vite's default `strictPort: false` absorbs a lost race by stepping to the\n * next free port. Only a consumer that sets `strictPort: true` would hard-fail on the rare collision.\n */\nconst getFreePort = (): Promise<number> => {\n return new Promise((resolve, reject) => {\n const srv = net.createServer()\n\n srv.unref()\n srv.on('error', reject)\n srv.listen(0, '127.0.0.1', () => {\n const address = srv.address()\n const port = typeof address === 'object' && address !== null ? address.port : 0\n\n srv.close(() => {\n return resolve(port)\n })\n })\n })\n}\n\n/** Env var carrying the runner-assigned `{ \"<ui-package>\": <port> }` map (Layer B). */\nconst UI_PORTS_ENV = 'INFRA_KIT_UI_PORTS'\n\n/** This package's `package.json` `name`, or `null` (unreadable / nameless). */\nconst readPackageName = (cwd: string): string | null => {\n try {\n const parsed = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8')) as { name?: unknown }\n\n return typeof parsed.name === 'string' ? parsed.name : null\n } catch {\n return null\n }\n}\n\n/**\n * The port `infra-kit dev` assigned THIS UI via `INFRA_KIT_UI_PORTS` (Layer B), or `null` when the map\n * is absent / this package isn't in it / it's malformed. Present \u2192 the helper binds it with\n * `strictPort` so vite lands on exactly the port the runner already aliased. Absent (portless off, or a\n * standalone `vite dev`) \u2192 the caller falls back to a free port, byte-identical to today.\n */\nconst resolveManagedUiPort = (cwd: string): number | null => {\n const raw = process.env[UI_PORTS_ENV]\n\n if (raw == null || raw === '') return null\n\n const name = readPackageName(cwd)\n\n if (name == null) return null\n\n try {\n const map = JSON.parse(raw) as Record<string, unknown>\n const port = map[name]\n\n return typeof port === 'number' ? port : null\n } catch {\n return null\n }\n}\n\n/**\n * Resolve a package's `dev` block into a ready-made Vite `server` config: a per-worktree dev-server\n * `port` plus the `proxy` map. Loads the package's `infra-kit.config.ts`, merges the local dev set\n * from the `.infra-kit/dev-context/` fragment directory, and interpolates the local/cloud templates.\n * `<env>` comes from `INFRA_KIT_ENV`; `<release>` from each package's runner-recorded fragment when\n * present, else the slugified git branch (computed lazily, only when a local route needs it).\n *\n * `port` defaults to a fresh OS-assigned free port so simultaneous git worktrees never collide on\n * Vite's `5173` (override via `options.port`). `host` defaults to {@link LOOPBACK_V4} so the portless\n * alias can actually reach it (override via `options.host` for containers/LAN). Pass `command` so\n * `build` is a no-op (empty proxy, no port) \u2014 a build ignores `server` and proxy resolution would\n * otherwise fail-fast on a cloud route with no sourced env.\n *\n * @example\n * // vite.config.ts \u2014 the whole `server` field is the helper's output\n * import { infraKitDev } from 'infra-kit/vite'\n * export default defineConfig(async ({ command }) => ({ server: await infraKitDev({ command }) }))\n */\nexport const infraKitDev = async (\n options: InfraKitDevOptions = {},\n): Promise<{ port?: number; host?: string | boolean; strictPort?: boolean; proxy: InfraKitViteProxy }> => {\n const cwd = options.cwd ?? process.cwd()\n\n // A build ignores `server`; skip the port + proxy work (proxy resolution would fail-fast on a\n // cloud route with no sourced env).\n if (options.command === 'build') return { proxy: {} }\n\n // Port precedence: explicit `options.port` > the runner-assigned Layer-B port (bound `strictPort` so\n // vite lands on exactly the aliased port) > a fresh free port (today's default; the portless-off path).\n const managedPort = options.port == null ? resolveManagedUiPort(cwd) : null\n const port = options.port ?? managedPort ?? (await getFreePort())\n const strictPort = managedPort != null\n const host = options.host ?? LOOPBACK_V4\n const dev = await loadDev(cwd)\n\n if (!dev?.proxy) return { port, host, ...(strictPort ? { strictPort } : {}), proxy: {} }\n\n const { packages: localSet, info: localInfo } = readLocalContext(cwd)\n const env = process.env[INFRA_KIT_ENV]\n const authHeader = buildBasicAuthHeader(process.env, options.basicAuth)\n const getRelease = once(() => {\n // Mirrors the dev-server's `readAppRelease`: outside a git repo both sides must land on the SAME\n // label, or this target names a host the runner never aliased.\n try {\n return slugifyRelease(readGitBranch(cwd)) || DEFAULT_RELEASE_SLUG\n } catch {\n return DEFAULT_RELEASE_SLUG\n }\n })\n\n return {\n port,\n host,\n ...(strictPort ? { strictPort } : {}),\n proxy: resolveProxyConfig({ proxy: dev.proxy, localSet, env, getRelease, authHeader, localInfo }),\n }\n}\n\n/**\n * Convenience wrapper returning just the proxy map (for spreading into\n * `server.proxy` directly). Equivalent to `(await infraKitDev(options)).proxy`.\n */\nexport const infraKitProxy = async (options: InfraKitDevOptions = {}): Promise<InfraKitViteProxy> => {\n return (await infraKitDev(options)).proxy\n}\n", "import { z } from 'zod'\n\n/**\n * Schema for the resolved (post-factory) package config object. `strictObject`\n * rejects unknown keys so typos in `infra-kit.config.ts` surface as validation\n * errors instead of being silently ignored.\n *\n * Kept in its own module \u2014 separate from the public `defineConfig`/types entry \u2014\n * so the published `infra-kit` type surface stays free of a `zod` import.\n */\nexport const packageConfigSchema = z.strictObject({\n requiredScripts: z.array(z.string().min(1)).optional(),\n requiredFiles: z.array(z.string().min(1)).optional(),\n turbo: z\n .strictObject({\n requiredTasks: z.array(z.string().min(1)).optional(),\n })\n .optional(),\n dev: z\n .strictObject({\n proxy: z\n .strictObject({\n templates: z.strictObject({\n local: z.string().min(1),\n cloud: z.string().min(1),\n }),\n routes: z.record(\n z.string().min(1),\n z\n .strictObject({\n packageName: z.string().min(1),\n from: z.array(z.enum(['local', 'cloud'])).min(1),\n default: z.enum(['local', 'cloud']).optional(),\n })\n .refine(\n (route) => {\n return route.from.length <= 1 || route.default !== undefined\n },\n {\n message: 'default is required when `from` has more than one source',\n },\n )\n .refine(\n (route) => {\n return route.default === undefined || route.from.includes(route.default)\n },\n {\n message: 'default must be listed in `from`',\n },\n ),\n ),\n })\n .optional(),\n })\n .optional(),\n})\n"],
5
- "mappings": "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",
6
- "names": ["Buffer", "execFileSync", "fs", "net", "path", "process", "pathToFileURL", "z", "z", "packageConfigSchema", "route", "INFRA_KIT_ENV", "PACKAGE_CONFIG_FILE", "DEV_CONTEXT_DIR", "path", "DEV_CONTEXT_FILE", "devContextFragmentSchema", "z", "buildBasicAuthHeader", "env", "override", "username", "password", "Buffer", "interpolate", "template", "values", "DEFAULT_HTTP_PORT", "LOOPBACK_V4", "withProxyPort", "target", "proxyPort", "url", "grafted", "resolveLocalTarget", "args", "route", "templates", "env", "getRelease", "info", "slugifyHostLabel", "pickSource", "localSet", "fallback", "resolveRoute", "routePath", "localInfo", "INFRA_KIT_ENV", "resolveProxyConfig", "proxy", "authHeader", "result", "headers", "entry", "once", "fn", "cached", "loadDev", "cwd", "configPath", "path", "PACKAGE_CONFIG_FILE", "fs", "stat", "rawExport", "pathToFileURL", "resolved", "parsed", "packageConfigSchema", "z", "extractPackages", "v", "candidate", "findUp", "start", "relative", "dir", "parent", "emptyLocalContext", "isFragmentWriterAlive", "pid", "process", "error", "readFragmentDir", "entries", "packages", "name", "devContextFragmentSchema", "readLocalContext", "DEV_CONTEXT_DIR", "legacy", "DEV_CONTEXT_FILE", "readGitBranch", "cwd", "execFileSync", "getFreePort", "resolve", "reject", "srv", "net", "address", "port", "UI_PORTS_ENV", "readPackageName", "parsed", "fs", "path", "resolveManagedUiPort", "raw", "process", "name", "infraKitDev", "options", "managedPort", "strictPort", "host", "LOOPBACK_V4", "dev", "loadDev", "localSet", "localInfo", "readLocalContext", "env", "INFRA_KIT_ENV", "authHeader", "buildBasicAuthHeader", "getRelease", "once", "slugifyRelease", "DEFAULT_RELEASE_SLUG", "resolveProxyConfig", "infraKitProxy"]
7
- }
@@ -1,4 +0,0 @@
1
- import{Q as w,S as x,c as l,i as f,v as n}from"./chunk-X2L4F2VM.js";import g from"node:process";import{$ as p}from"zx";var C=async()=>{try{return await p`cmux --version`.quiet(),!0}catch{return!1}},y=async t=>{let{cwd:e,title:r,layout:s}=t,o=JSON.stringify(s),i=(await p({env:{...g.env,CMUX_QUIET:"1"}})`cmux new-workspace --name ${r} --cwd ${e} --focus false --layout ${o}`).stdout;return W(i)},h=async t=>{try{await p`cmux close-workspace --workspace ${t}`.quiet()}catch(e){n.debug({error:e,ref:t},"cmux: skipped closing dev workspace")}},W=t=>{let e=t.match(/workspace:\d+/);if(!e)throw new Error("cmux: could not locate workspace ref in new-workspace output");return e[0]};var $=/\bv(\d+\.\d+\.\d+)\b/g,a=t=>t.trim().replace(/\s+/g," ").replace($,"$1");import{$ as d}from"zx";var T=async t=>{try{let e=(await d`cmux list-workspaces`.quiet()).stdout,r=b(e,t);if(!r)return;await d`cmux close-workspace --workspace ${r}`.quiet()}catch(e){n.debug({error:e,title:t},"cmux: skipped closing workspace")}},b=(t,e)=>{let r=a(e);for(let s of t.split(`
2
- `)){let o=s.match(/^[* ]\s*(workspace:\d+)\s+(.+?)(?:\s+\[selected\])?\s*$/);if(!o)continue;let c=o[1],i=o[2]?.trim()??"";if(a(i)===r)return c}};import{$ as v}from"zx";var E=async()=>{try{let t=(await v`cmux list-workspaces`.quiet()).stdout,e=new Set;for(let r of t.split(`
3
- `)){let s=r.match(/^[* ]\s*workspace:\d+\s+(.+?)(?:\s+\[selected\])?\s*$/);if(!s)continue;let o=s[1]?.trim();o&&e.add(a(o))}return e}catch(t){return n.debug({error:t},"cmux: skipped listing workspace titles"),new Set}};import{$ as u}from"zx";var A=async t=>{let{cwd:e,title:r}=t,s=w(await x()),o=(await u`cmux workspace create --cwd ${e}`).stdout,c=O(o),i=(await u`cmux list-pane-surfaces --workspace ${c}`).stdout,m=N(i);await u`cmux new-split right --workspace ${c} --surface ${m}`,s==="three-pane"&&await u`cmux new-split down --workspace ${c} --surface ${m}`,r&&await u`cmux workspace rename --workspace ${c} --title ${r}`},N=t=>{let e=t.match(/surface:\d+/);if(!e)throw new Error("cmux: could not locate initial surface in list-pane-surfaces output");return e[0]},O=t=>{let e=t.match(/workspace:\d+/);if(!e)throw new Error("cmux: could not locate workspace ref in workspace create output");return e[0]};var R=t=>{let{repoName:e,branch:r}=t,s=l(r),o=s?f(s):r;return`${e} ${o}`};var L=new Set(["ExitPromptError","AbortPromptError","PromptCancelledError"]),k=t=>t instanceof Error&&L.has(t.name),st=t=>{if(k(t))return!0;let e=t?.cause;return k(e)};export{a,T as b,E as c,C as d,y as e,h as f,A as g,R as h,st as i};
4
- //# sourceMappingURL=chunk-6YAKVQZN.js.map