@aiviatic/kindling 0.1.0

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.
@@ -0,0 +1,234 @@
1
+ import {
2
+ AGENT_CLI_TABLE,
3
+ Engine,
4
+ EngineEmitter,
5
+ NODE_FLOOR_MAJOR,
6
+ SCHEMA_VERSION,
7
+ bmadVersionLabel,
8
+ buildValidationSummary,
9
+ cliLoginGuidance,
10
+ cliMissing,
11
+ composeInstallArgs,
12
+ defaultBmadInstalled,
13
+ defaultLogDir,
14
+ detectDependencies,
15
+ eligibleAgentClis,
16
+ errorMessages,
17
+ installAgentCli,
18
+ parseMajor,
19
+ pins,
20
+ provisionGitUnix,
21
+ provisionMessages,
22
+ readInstalledBmadVersion,
23
+ recoveryGuidance,
24
+ runBmadInstall,
25
+ runSelfCheck,
26
+ scaffold,
27
+ stepMessages,
28
+ writeFailureLog
29
+ } from "../chunk-MW7UAGER.js";
30
+ import {
31
+ ErrorCode,
32
+ NON_FATAL_STEPS,
33
+ Phase,
34
+ Status,
35
+ StepId,
36
+ exec
37
+ } from "../chunk-OU3WSB6B.js";
38
+
39
+ // engine/orchestrate/launch.ts
40
+ import { dirname, join } from "path";
41
+ function npxCliPath(nodeExe) {
42
+ return join(dirname(nodeExe), "node_modules", "npm", "bin", "npx-cli.js");
43
+ }
44
+ function composeLaunchCommand({ nodeExe, kindlingVersion }) {
45
+ const spec = `@aiviatic/kindling@${kindlingVersion}`;
46
+ if (nodeExe === null) {
47
+ return { cmd: "npx", args: ["-y", spec] };
48
+ }
49
+ return { cmd: nodeExe, args: [npxCliPath(nodeExe), "-y", spec] };
50
+ }
51
+
52
+ // engine/provision/node-windows.ts
53
+ import { join as join2 } from "path";
54
+ import { mkdir, writeFile } from "fs/promises";
55
+ import { randomUUID } from "crypto";
56
+ function nodeDistUrl(version, arch = "x64") {
57
+ const v = version.startsWith("v") ? version : `v${version}`;
58
+ return `https://nodejs.org/dist/${v}/node-${v}-win-${arch}.zip`;
59
+ }
60
+ function nodeExePath(baseDir, version, arch = "x64") {
61
+ const v = version.startsWith("v") ? version : `v${version}`;
62
+ return join2(baseDir, `node-${v}-win-${arch}`, "node.exe");
63
+ }
64
+ async function defaultDownload(url, dest) {
65
+ const res = await fetch(url);
66
+ if (!res.ok) throw new Error(`download failed: ${res.status} ${url}`);
67
+ const buf = Buffer.from(await res.arrayBuffer());
68
+ await writeFile(dest, buf);
69
+ }
70
+ async function provisionNodeWindows(opts) {
71
+ const arch = opts.arch ?? "x64";
72
+ const exec2 = opts.exec ?? exec;
73
+ const download = opts.download ?? defaultDownload;
74
+ const extract = opts.extract ?? (async (zip, destDir) => {
75
+ const r = await exec2("tar", ["-xf", zip, "-C", destDir]);
76
+ if (r.code !== 0) throw new Error(`extract failed (code ${r.code}): ${r.stderr.trim()}`);
77
+ });
78
+ const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
79
+ const emit = (status, humanMessage, level = "info", errorCode) => {
80
+ opts.emitter.emit({
81
+ id: randomUUID(),
82
+ phase: Phase.Provision,
83
+ step: StepId.ProvisionNode,
84
+ status,
85
+ humanMessage,
86
+ level,
87
+ timestamp: now(),
88
+ errorCode
89
+ });
90
+ };
91
+ if (opts.alreadyOk) {
92
+ emit(Status.Skipped, provisionMessages.nodePresent);
93
+ return { ok: true, nodeExe: null };
94
+ }
95
+ emit(Status.Working, stepMessages[StepId.ProvisionNode]);
96
+ try {
97
+ await mkdir(opts.baseDir, { recursive: true });
98
+ const url = nodeDistUrl(opts.version, arch);
99
+ const zip = join2(opts.baseDir, `node-${opts.version}-win-${arch}.zip`);
100
+ await download(url, zip);
101
+ await extract(zip, opts.baseDir);
102
+ } catch (err) {
103
+ emit(Status.Failed, "Setting up Node ran into a problem downloading or unpacking it. Check your connection, then press Retry.", "error", ErrorCode.NetworkLost);
104
+ throw err;
105
+ }
106
+ const nodeExe = nodeExePath(opts.baseDir, opts.version, arch);
107
+ emit(Status.Done, provisionMessages.nodePresent);
108
+ return { ok: true, nodeExe };
109
+ }
110
+
111
+ // engine/provision/node-unix.ts
112
+ import { randomUUID as randomUUID2 } from "crypto";
113
+ var NVM_VERSION = "v0.40.3";
114
+ function nvmNodePath(nvmDir, version) {
115
+ const v = version.startsWith("v") ? version : `v${version}`;
116
+ return `${nvmDir}/versions/node/${v}/bin/node`;
117
+ }
118
+ function makeDefaults(exec2) {
119
+ const run = async (script, args, what) => {
120
+ const r = await exec2("bash", ["-c", script, "kindling", ...args]);
121
+ if (r.code !== 0) throw new Error(`${what} failed (code ${r.code}): ${r.stderr.trim()}`);
122
+ };
123
+ return {
124
+ // Install nvm only if it's not already present at $1 (idempotent).
125
+ installNvm: (nvmDir) => run(
126
+ `export NVM_DIR="$1"; [ -s "$NVM_DIR/nvm.sh" ] || curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh | bash`,
127
+ [nvmDir],
128
+ "nvm install"
129
+ ),
130
+ // Source nvm in the same shell, then install the pinned version. We do NOT touch `nvm alias
131
+ // default` — clobbering the user's global default would break their other projects (FR4); the
132
+ // launch step uses the absolute nodeExe path instead.
133
+ nvmInstallNode: (nvmDir, version) => run(
134
+ `export NVM_DIR="$1"; . "$NVM_DIR/nvm.sh"; nvm install "$2"`,
135
+ [nvmDir, version],
136
+ "nvm install node"
137
+ )
138
+ };
139
+ }
140
+ async function provisionNodeUnix(opts) {
141
+ const exec2 = opts.exec ?? exec;
142
+ const defaults = makeDefaults(exec2);
143
+ const installNvm = opts.installNvm ?? defaults.installNvm;
144
+ const nvmInstallNode = opts.nvmInstallNode ?? defaults.nvmInstallNode;
145
+ const nodeInstalled = opts.nodeInstalled ?? (async (nvmDir, version) => {
146
+ const want = version.startsWith("v") ? version : `v${version}`;
147
+ try {
148
+ const r = await exec2(nvmNodePath(nvmDir, version), ["--version"]);
149
+ return r.code === 0 && r.stdout.trim() === want;
150
+ } catch {
151
+ return false;
152
+ }
153
+ });
154
+ const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
155
+ const emit = (status, humanMessage, level = "info", errorCode) => {
156
+ opts.emitter.emit({
157
+ id: randomUUID2(),
158
+ phase: Phase.Provision,
159
+ step: StepId.ProvisionNode,
160
+ status,
161
+ humanMessage,
162
+ level,
163
+ timestamp: now(),
164
+ errorCode
165
+ });
166
+ };
167
+ if (opts.alreadyOk && opts.platform !== "darwin") {
168
+ emit(Status.Skipped, provisionMessages.nodePresent);
169
+ return { ok: true, nodeExe: null };
170
+ }
171
+ if (await nodeInstalled(opts.nvmDir, opts.version)) {
172
+ emit(Status.Skipped, provisionMessages.nodePresent);
173
+ return { ok: true, nodeExe: nvmNodePath(opts.nvmDir, opts.version) };
174
+ }
175
+ emit(Status.Working, stepMessages[StepId.ProvisionNode]);
176
+ try {
177
+ await installNvm(opts.nvmDir);
178
+ await nvmInstallNode(opts.nvmDir, opts.version);
179
+ } catch (err) {
180
+ emit(
181
+ Status.Failed,
182
+ "Setting up Node ran into a problem downloading or installing it. Check your connection, then press Retry.",
183
+ "error",
184
+ ErrorCode.NetworkLost
185
+ );
186
+ throw err;
187
+ }
188
+ const nodeExe = nvmNodePath(opts.nvmDir, opts.version);
189
+ emit(Status.Done, provisionMessages.nodePresent);
190
+ return { ok: true, nodeExe };
191
+ }
192
+ export {
193
+ AGENT_CLI_TABLE,
194
+ Engine,
195
+ EngineEmitter,
196
+ ErrorCode,
197
+ NODE_FLOOR_MAJOR,
198
+ NON_FATAL_STEPS,
199
+ NVM_VERSION,
200
+ Phase,
201
+ SCHEMA_VERSION,
202
+ Status,
203
+ StepId,
204
+ bmadVersionLabel,
205
+ buildValidationSummary,
206
+ cliLoginGuidance,
207
+ cliMissing,
208
+ composeInstallArgs,
209
+ composeLaunchCommand,
210
+ defaultBmadInstalled,
211
+ defaultLogDir,
212
+ detectDependencies,
213
+ eligibleAgentClis,
214
+ errorMessages,
215
+ exec,
216
+ installAgentCli,
217
+ nodeDistUrl,
218
+ nodeExePath,
219
+ npxCliPath,
220
+ nvmNodePath,
221
+ parseMajor,
222
+ pins,
223
+ provisionGitUnix,
224
+ provisionNodeUnix,
225
+ provisionNodeWindows,
226
+ readInstalledBmadVersion,
227
+ recoveryGuidance,
228
+ runBmadInstall,
229
+ runSelfCheck,
230
+ scaffold,
231
+ stepMessages,
232
+ writeFailureLog
233
+ };
234
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../engine/orchestrate/launch.ts","../../engine/provision/node-windows.ts","../../engine/provision/node-unix.ts"],"sourcesContent":["import { dirname, join } from 'node:path';\n\nexport interface LaunchRuntime {\n /** Absolute path to the provisioned node binary, or null to use `node`/`npx` on PATH\n * (a reused system Node, or nvm sourced same-shell). See ProvisionResult.nodeExe. */\n nodeExe: string | null;\n /** Pinned Kindling version (pins.kindling). */\n kindlingVersion: string;\n}\n\nexport interface LaunchCommand {\n cmd: string;\n args: string[];\n}\n\n/** Path to npx's CLI script beside a provisioned node binary (the npm bundled with the dist). */\nexport function npxCliPath(nodeExe: string): string {\n return join(dirname(nodeExe), 'node_modules', 'npm', 'bin', 'npx-cli.js');\n}\n\n/**\n * Compose the command to launch Kindling once Node is provisioned (Story 2.6). The clean-runtime\n * rule (AR6): never depend on a freshly-mutated PATH —\n * - `nodeExe === null` → Node is already on PATH (nvm sourced same-shell, or a reused system\n * Node) → plain `npx -y @aiviatic/kindling@<pin>`.\n * - `nodeExe` set (Windows portable, absolute) → invoke npx's CLI through that exact node binary,\n * so the launch works without the portable Node ever being on PATH.\n * Pure — the bootstrap (or a future Windows launcher) spawns the returned command.\n */\nexport function composeLaunchCommand({ nodeExe, kindlingVersion }: LaunchRuntime): LaunchCommand {\n const spec = `@aiviatic/kindling@${kindlingVersion}`;\n if (nodeExe === null) {\n return { cmd: 'npx', args: ['-y', spec] };\n }\n return { cmd: nodeExe, args: [npxCliPath(nodeExe), '-y', spec] };\n}\n","import { join } from 'node:path';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { randomUUID } from 'node:crypto';\nimport { exec as defaultExec, type ExecResult } from '../exec';\nimport type { EngineEmitter } from '../emitter';\nimport { Phase, StepId, Status, ErrorCode, type Level } from '../contract';\nimport { stepMessages, provisionMessages } from '../messages';\n\nexport type NodeArch = 'x64' | 'arm64';\n\n/** Official Node Windows .zip URL for a pinned version. Pure. */\nexport function nodeDistUrl(version: string, arch: NodeArch = 'x64'): string {\n const v = version.startsWith('v') ? version : `v${version}`;\n return `https://nodejs.org/dist/${v}/node-${v}-win-${arch}.zip`;\n}\n\n/** Absolute path to node.exe inside the extracted distribution. Pure. */\nexport function nodeExePath(baseDir: string, version: string, arch: NodeArch = 'x64'): string {\n const v = version.startsWith('v') ? version : `v${version}`;\n return join(baseDir, `node-${v}-win-${arch}`, 'node.exe');\n}\n\nexport interface ProvisionNodeWindowsOptions {\n /** Pinned Node version (pins.node). */\n version: string;\n /** Install root, e.g. %LOCALAPPDATA%\\kindling\\node. */\n baseDir: string;\n arch?: NodeArch;\n emitter: EngineEmitter;\n /** From 2.1 detection: a Node ≥ floor is already present → skip the download. */\n alreadyOk?: boolean;\n /** Injectable so tests run without network/Windows. Defaults below are spike-validation-pending. */\n download?: (url: string, dest: string) => Promise<void>;\n extract?: (zip: string, destDir: string) => Promise<void>;\n exec?: (cmd: string, args: string[]) => Promise<ExecResult>;\n now?: () => string;\n}\n\n// Default download: global fetch → file. UNVALIDATED on real Windows (spike / rehearsal).\nasync function defaultDownload(url: string, dest: string): Promise<void> {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`download failed: ${res.status} ${url}`);\n const buf = Buffer.from(await res.arrayBuffer());\n await writeFile(dest, buf);\n}\n\nexport interface ProvisionResult {\n ok: boolean;\n /**\n * Absolute path to the provisioned node binary for the launch step (2.6) to invoke directly\n * (the clean-runtime rule — never rely on a mutated PATH). `null` means an existing system\n * Node was reused → the launch resolves `node` from PATH instead.\n */\n nodeExe: string | null;\n}\n\nexport async function provisionNodeWindows(opts: ProvisionNodeWindowsOptions): Promise<ProvisionResult> {\n const arch = opts.arch ?? 'x64';\n const exec = opts.exec ?? defaultExec;\n const download = opts.download ?? defaultDownload;\n // Default extract via Windows 10+ bsdtar (handles .zip); UNVALIDATED here.\n const extract =\n opts.extract ??\n (async (zip: string, destDir: string): Promise<void> => {\n const r = await exec('tar', ['-xf', zip, '-C', destDir]);\n if (r.code !== 0) throw new Error(`extract failed (code ${r.code}): ${r.stderr.trim()}`);\n });\n const now = opts.now ?? (() => new Date().toISOString());\n\n const emit = (\n status: Status,\n humanMessage: string,\n level: Level = 'info',\n errorCode?: ErrorCode,\n ): void => {\n opts.emitter.emit({\n id: randomUUID(),\n phase: Phase.Provision,\n step: StepId.ProvisionNode,\n status,\n humanMessage,\n level,\n timestamp: now(),\n errorCode,\n });\n };\n\n if (opts.alreadyOk) {\n emit(Status.Skipped, provisionMessages.nodePresent);\n return { ok: true, nodeExe: null };\n }\n\n emit(Status.Working, stepMessages[StepId.ProvisionNode]);\n try {\n await mkdir(opts.baseDir, { recursive: true }); // download dest + extract dir must exist\n const url = nodeDistUrl(opts.version, arch);\n const zip = join(opts.baseDir, `node-${opts.version}-win-${arch}.zip`);\n await download(url, zip);\n await extract(zip, opts.baseDir);\n } catch (err) {\n emit(Status.Failed, 'Setting up Node ran into a problem downloading or unpacking it. Check your connection, then press Retry.', 'error', ErrorCode.NetworkLost);\n throw err;\n }\n\n const nodeExe = nodeExePath(opts.baseDir, opts.version, arch);\n emit(Status.Done, provisionMessages.nodePresent);\n return { ok: true, nodeExe };\n}\n","import { randomUUID } from 'node:crypto';\nimport { exec as defaultExec, type ExecResult } from '../exec';\nimport type { EngineEmitter } from '../emitter';\nimport { Phase, StepId, Status, ErrorCode, type Level } from '../contract';\nimport { stepMessages, provisionMessages } from '../messages';\nimport type { ProvisionResult } from './node-windows';\n\n// nvm release the bootstrap pins to (AC: nvm-sh v0.40.x). Embedded so the install is reproducible.\nexport const NVM_VERSION = 'v0.40.3';\n\n/** Absolute path to the nvm-installed node binary for a version. Pure. */\nexport function nvmNodePath(nvmDir: string, version: string): string {\n const v = version.startsWith('v') ? version : `v${version}`;\n return `${nvmDir}/versions/node/${v}/bin/node`;\n}\n\nexport interface ProvisionNodeUnixOptions {\n /** Pinned Node version (pins.node). */\n version: string;\n /** nvm install dir (default ~/.nvm). */\n nvmDir: string;\n emitter: EngineEmitter;\n /** From 2.1 detection: a system Node ≥ floor is already present. Honored only OFF macOS — on\n * macOS we always install the exact pin via nvm so the workshop is reproducible (AC2 reuse is\n * Linux-only; AC3 resolved-version-equals-pins.node). */\n alreadyOk?: boolean;\n /** Host platform — gates the reuse rule above. */\n platform?: NodeJS.Platform;\n /** Injectable side effects so tests run without network/a real shell. Defaults below are\n * shell-based and UNVALIDATED until the dress rehearsal (macOS + Linux). */\n installNvm?: (nvmDir: string) => Promise<void>;\n nvmInstallNode?: (nvmDir: string, version: string) => Promise<void>;\n /** Idempotent re-run check (2.7): is the PINNED node already installed AND runnable? Default\n * runs the resolved binary with --version. Verifying it RUNS (not just that a dir exists)\n * means a partial/dirty prior install is repaired (re-installed), not skipped-into-broken. */\n nodeInstalled?: (nvmDir: string, version: string) => Promise<boolean>;\n exec?: (cmd: string, args: string[]) => Promise<ExecResult>;\n now?: () => string;\n}\n\n// nvm is a shell function, not an executable — so every default runs `bash -c` (a single binary\n// + args, safe under exec's shell:false). Caller values are passed as POSITIONAL args ($1/$2),\n// never interpolated into the script, so a path/version can't break out of the shell context.\n// (NVM_VERSION is our own frozen constant, so it stays inline in the URL.)\nfunction makeDefaults(exec: (cmd: string, args: string[]) => Promise<ExecResult>) {\n const run = async (script: string, args: string[], what: string): Promise<void> => {\n // `kindling` becomes $0; the rest are $1, $2, …\n const r = await exec('bash', ['-c', script, 'kindling', ...args]);\n if (r.code !== 0) throw new Error(`${what} failed (code ${r.code}): ${r.stderr.trim()}`);\n };\n return {\n // Install nvm only if it's not already present at $1 (idempotent).\n installNvm: (nvmDir: string): Promise<void> =>\n run(\n `export NVM_DIR=\"$1\"; [ -s \"$NVM_DIR/nvm.sh\" ] || ` +\n `curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh | bash`,\n [nvmDir],\n 'nvm install',\n ),\n // Source nvm in the same shell, then install the pinned version. We do NOT touch `nvm alias\n // default` — clobbering the user's global default would break their other projects (FR4); the\n // launch step uses the absolute nodeExe path instead.\n nvmInstallNode: (nvmDir: string, version: string): Promise<void> =>\n run(\n `export NVM_DIR=\"$1\"; . \"$NVM_DIR/nvm.sh\"; nvm install \"$2\"`,\n [nvmDir, version],\n 'nvm install node',\n ),\n };\n}\n\n/**\n * Provision the pinned Node on macOS/Linux via nvm, returning the ABSOLUTE node path so the\n * launch step (2.6) never depends on a mutated PATH (the clean-runtime rule). A system Node ≥\n * floor is reused (Skipped, AC2). Emits provision.node events; Failed + rethrow on error.\n *\n * The real nvm install + same-shell source is validated at the dress rehearsal; here the shell\n * effects are injectable and the orchestration/events/skip/fail paths are unit-tested.\n */\nexport async function provisionNodeUnix(opts: ProvisionNodeUnixOptions): Promise<ProvisionResult> {\n const exec = opts.exec ?? defaultExec;\n const defaults = makeDefaults(exec);\n const installNvm = opts.installNvm ?? defaults.installNvm;\n const nvmInstallNode = opts.nvmInstallNode ?? defaults.nvmInstallNode;\n const nodeInstalled =\n opts.nodeInstalled ??\n (async (nvmDir: string, version: string): Promise<boolean> => {\n const want = version.startsWith('v') ? version : `v${version}`;\n try {\n const r = await exec(nvmNodePath(nvmDir, version), ['--version']);\n // Require it to RUN and report the EXACT pinned version — a missing/broken/wrong-version\n // binary at that path → not installed → (re)install repairs it.\n return r.code === 0 && r.stdout.trim() === want;\n } catch {\n return false;\n }\n });\n const now = opts.now ?? (() => new Date().toISOString());\n\n const emit = (\n status: Status,\n humanMessage: string,\n level: Level = 'info',\n errorCode?: ErrorCode,\n ): void => {\n opts.emitter.emit({\n id: randomUUID(),\n phase: Phase.Provision,\n step: StepId.ProvisionNode,\n status,\n humanMessage,\n level,\n timestamp: now(),\n errorCode,\n });\n };\n\n // Reuse a system Node only OFF macOS (AC2 Linux-only); on macOS always install the exact pin.\n if (opts.alreadyOk && opts.platform !== 'darwin') {\n emit(Status.Skipped, provisionMessages.nodePresent);\n return { ok: true, nodeExe: null }; // null → the launch uses the system `node` on PATH\n }\n\n // Idempotent re-run (2.7): the pinned nvm node is already installed AND runs → skip, don't\n // reinstall. (A partial/broken install fails this check → falls through to a repairing install.)\n if (await nodeInstalled(opts.nvmDir, opts.version)) {\n emit(Status.Skipped, provisionMessages.nodePresent);\n return { ok: true, nodeExe: nvmNodePath(opts.nvmDir, opts.version) };\n }\n\n emit(Status.Working, stepMessages[StepId.ProvisionNode]);\n try {\n await installNvm(opts.nvmDir);\n await nvmInstallNode(opts.nvmDir, opts.version);\n } catch (err) {\n emit(\n Status.Failed,\n 'Setting up Node ran into a problem downloading or installing it. Check your connection, then press Retry.',\n 'error',\n ErrorCode.NetworkLost,\n );\n throw err;\n }\n\n const nodeExe = nvmNodePath(opts.nvmDir, opts.version);\n emit(Status.Done, provisionMessages.nodePresent);\n return { ok: true, nodeExe };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,SAAS,YAAY;AAgBvB,SAAS,WAAW,SAAyB;AAClD,SAAO,KAAK,QAAQ,OAAO,GAAG,gBAAgB,OAAO,OAAO,YAAY;AAC1E;AAWO,SAAS,qBAAqB,EAAE,SAAS,gBAAgB,GAAiC;AAC/F,QAAM,OAAO,sBAAsB,eAAe;AAClD,MAAI,YAAY,MAAM;AACpB,WAAO,EAAE,KAAK,OAAO,MAAM,CAAC,MAAM,IAAI,EAAE;AAAA,EAC1C;AACA,SAAO,EAAE,KAAK,SAAS,MAAM,CAAC,WAAW,OAAO,GAAG,MAAM,IAAI,EAAE;AACjE;;;ACnCA,SAAS,QAAAA,aAAY;AACrB,SAAS,OAAO,iBAAiB;AACjC,SAAS,kBAAkB;AASpB,SAAS,YAAY,SAAiB,OAAiB,OAAe;AAC3E,QAAM,IAAI,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI,OAAO;AACzD,SAAO,2BAA2B,CAAC,SAAS,CAAC,QAAQ,IAAI;AAC3D;AAGO,SAAS,YAAY,SAAiB,SAAiB,OAAiB,OAAe;AAC5F,QAAM,IAAI,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI,OAAO;AACzD,SAAOC,MAAK,SAAS,QAAQ,CAAC,QAAQ,IAAI,IAAI,UAAU;AAC1D;AAmBA,eAAe,gBAAgB,KAAa,MAA6B;AACvE,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,oBAAoB,IAAI,MAAM,IAAI,GAAG,EAAE;AACpE,QAAM,MAAM,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC;AAC/C,QAAM,UAAU,MAAM,GAAG;AAC3B;AAYA,eAAsB,qBAAqB,MAA6D;AACtG,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAMC,QAAO,KAAK,QAAQ;AAC1B,QAAM,WAAW,KAAK,YAAY;AAElC,QAAM,UACJ,KAAK,YACJ,OAAO,KAAa,YAAmC;AACtD,UAAM,IAAI,MAAMA,MAAK,OAAO,CAAC,OAAO,KAAK,MAAM,OAAO,CAAC;AACvD,QAAI,EAAE,SAAS,EAAG,OAAM,IAAI,MAAM,wBAAwB,EAAE,IAAI,MAAM,EAAE,OAAO,KAAK,CAAC,EAAE;AAAA,EACzF;AACF,QAAM,MAAM,KAAK,QAAQ,OAAM,oBAAI,KAAK,GAAE,YAAY;AAEtD,QAAM,OAAO,CACX,QACA,cACA,QAAe,QACf,cACS;AACT,SAAK,QAAQ,KAAK;AAAA,MAChB,IAAI,WAAW;AAAA,MACf,OAAO,MAAM;AAAA,MACb,MAAM,OAAO;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,IAAI;AAAA,MACf;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,KAAK,WAAW;AAClB,SAAK,OAAO,SAAS,kBAAkB,WAAW;AAClD,WAAO,EAAE,IAAI,MAAM,SAAS,KAAK;AAAA,EACnC;AAEA,OAAK,OAAO,SAAS,aAAa,OAAO,aAAa,CAAC;AACvD,MAAI;AACF,UAAM,MAAM,KAAK,SAAS,EAAE,WAAW,KAAK,CAAC;AAC7C,UAAM,MAAM,YAAY,KAAK,SAAS,IAAI;AAC1C,UAAM,MAAMD,MAAK,KAAK,SAAS,QAAQ,KAAK,OAAO,QAAQ,IAAI,MAAM;AACrE,UAAM,SAAS,KAAK,GAAG;AACvB,UAAM,QAAQ,KAAK,KAAK,OAAO;AAAA,EACjC,SAAS,KAAK;AACZ,SAAK,OAAO,QAAQ,4GAA4G,SAAS,UAAU,WAAW;AAC9J,UAAM;AAAA,EACR;AAEA,QAAM,UAAU,YAAY,KAAK,SAAS,KAAK,SAAS,IAAI;AAC5D,OAAK,OAAO,MAAM,kBAAkB,WAAW;AAC/C,SAAO,EAAE,IAAI,MAAM,QAAQ;AAC7B;;;AC3GA,SAAS,cAAAE,mBAAkB;AAQpB,IAAM,cAAc;AAGpB,SAAS,YAAY,QAAgB,SAAyB;AACnE,QAAM,IAAI,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI,OAAO;AACzD,SAAO,GAAG,MAAM,kBAAkB,CAAC;AACrC;AA8BA,SAAS,aAAaC,OAA4D;AAChF,QAAM,MAAM,OAAO,QAAgB,MAAgB,SAAgC;AAEjF,UAAM,IAAI,MAAMA,MAAK,QAAQ,CAAC,MAAM,QAAQ,YAAY,GAAG,IAAI,CAAC;AAChE,QAAI,EAAE,SAAS,EAAG,OAAM,IAAI,MAAM,GAAG,IAAI,iBAAiB,EAAE,IAAI,MAAM,EAAE,OAAO,KAAK,CAAC,EAAE;AAAA,EACzF;AACA,SAAO;AAAA;AAAA,IAEL,YAAY,CAAC,WACX;AAAA,MACE,4GAC6D,WAAW;AAAA,MACxE,CAAC,MAAM;AAAA,MACP;AAAA,IACF;AAAA;AAAA;AAAA;AAAA,IAIF,gBAAgB,CAAC,QAAgB,YAC/B;AAAA,MACE;AAAA,MACA,CAAC,QAAQ,OAAO;AAAA,MAChB;AAAA,IACF;AAAA,EACJ;AACF;AAUA,eAAsB,kBAAkB,MAA0D;AAChG,QAAMA,QAAO,KAAK,QAAQ;AAC1B,QAAM,WAAW,aAAaA,KAAI;AAClC,QAAM,aAAa,KAAK,cAAc,SAAS;AAC/C,QAAM,iBAAiB,KAAK,kBAAkB,SAAS;AACvD,QAAM,gBACJ,KAAK,kBACJ,OAAO,QAAgB,YAAsC;AAC5D,UAAM,OAAO,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI,OAAO;AAC5D,QAAI;AACF,YAAM,IAAI,MAAMA,MAAK,YAAY,QAAQ,OAAO,GAAG,CAAC,WAAW,CAAC;AAGhE,aAAO,EAAE,SAAS,KAAK,EAAE,OAAO,KAAK,MAAM;AAAA,IAC7C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF,QAAM,MAAM,KAAK,QAAQ,OAAM,oBAAI,KAAK,GAAE,YAAY;AAEtD,QAAM,OAAO,CACX,QACA,cACA,QAAe,QACf,cACS;AACT,SAAK,QAAQ,KAAK;AAAA,MAChB,IAAIC,YAAW;AAAA,MACf,OAAO,MAAM;AAAA,MACb,MAAM,OAAO;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,IAAI;AAAA,MACf;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MAAI,KAAK,aAAa,KAAK,aAAa,UAAU;AAChD,SAAK,OAAO,SAAS,kBAAkB,WAAW;AAClD,WAAO,EAAE,IAAI,MAAM,SAAS,KAAK;AAAA,EACnC;AAIA,MAAI,MAAM,cAAc,KAAK,QAAQ,KAAK,OAAO,GAAG;AAClD,SAAK,OAAO,SAAS,kBAAkB,WAAW;AAClD,WAAO,EAAE,IAAI,MAAM,SAAS,YAAY,KAAK,QAAQ,KAAK,OAAO,EAAE;AAAA,EACrE;AAEA,OAAK,OAAO,SAAS,aAAa,OAAO,aAAa,CAAC;AACvD,MAAI;AACF,UAAM,WAAW,KAAK,MAAM;AAC5B,UAAM,eAAe,KAAK,QAAQ,KAAK,OAAO;AAAA,EAChD,SAAS,KAAK;AACZ;AAAA,MACE,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,UAAU;AAAA,IACZ;AACA,UAAM;AAAA,EACR;AAEA,QAAM,UAAU,YAAY,KAAK,QAAQ,KAAK,OAAO;AACrD,OAAK,OAAO,MAAM,kBAAkB,WAAW;AAC/C,SAAO,EAAE,IAAI,MAAM,QAAQ;AAC7B;","names":["join","join","exec","randomUUID","exec","randomUUID"]}
@@ -0,0 +1,8 @@
1
+ interface ExecResult {
2
+ code: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ }
6
+ declare function exec(cmd: string, args?: string[]): Promise<ExecResult>;
7
+
8
+ export { type ExecResult as E, exec as e };
@@ -0,0 +1,39 @@
1
+ import { Server } from 'node:http';
2
+ import { C as Config, e as StepId, I as InspectResult, a as EngineEmitter } from '../emitter-oidLJDmn.js';
3
+ import { E as ExecResult } from '../exec-JnCZZPZU.js';
4
+
5
+ interface ServerCommands {
6
+ start(config: Config): unknown | Promise<unknown>;
7
+ cancel(): void;
8
+ retry(step: StepId): unknown | Promise<unknown>;
9
+ /**
10
+ * Read-only filesystem probe for the Configure-time "existing project?" detection (Story 7.2 /
11
+ * `POST /inspect`). OPTIONAL so existing `ServerCommands` fakes (e.g. server.test.ts) don't break;
12
+ * when absent the handler returns the neutral `{ isKindlingProject: false, installedBmadVersion: null }`.
13
+ */
14
+ inspect?(projectDir: string): Promise<InspectResult>;
15
+ }
16
+ interface StartServerOptions {
17
+ emitter: EngineEmitter;
18
+ commands: ServerCommands;
19
+ /** Built UI directory (dist/ui) to serve statically; omit to skip static serving. */
20
+ uiDir?: string;
21
+ /** Called when the Welcome screen render-acks (POST /ack). The host uses this to write the
22
+ * static welcome.html and exit the ephemeral server — only fired on success (3.7 lifecycle). */
23
+ onWelcomeAck?: () => void;
24
+ }
25
+ interface RunningServer {
26
+ server: Server;
27
+ url: string;
28
+ port: number;
29
+ close(): Promise<void>;
30
+ }
31
+ declare function startServer(opts: StartServerOptions): Promise<RunningServer>;
32
+
33
+ interface OpenBrowserOptions {
34
+ exec?: (cmd: string, args: string[]) => Promise<ExecResult>;
35
+ platform?: NodeJS.Platform;
36
+ }
37
+ declare function openBrowser(url: string, opts?: OpenBrowserOptions): Promise<boolean>;
38
+
39
+ export { type OpenBrowserOptions, type RunningServer, type ServerCommands, type StartServerOptions, openBrowser, startServer };
@@ -0,0 +1,10 @@
1
+ import {
2
+ openBrowser,
3
+ startServer
4
+ } from "../chunk-IS6LC3HK.js";
5
+ import "../chunk-OU3WSB6B.js";
6
+ export {
7
+ openBrowser,
8
+ startServer
9
+ };
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1 @@
1
+ :root{--color-stage-deep:#16100c;--color-stage:#1b1410;--color-surface:#221913;--color-surface-tint:#2c1c12;--color-surface-raised:#2c211a;--color-surface-raised-hi:#34271e;--color-surface-overlay:#463429;--color-text-primary:#fff6ee;--color-text-secondary:#d6b9a3;--color-text-muted:#9a8170;--color-accent:#ff6a1f;--color-accent-gold:#ffc24a;--color-accent-pink:#ff3d6e;--color-on-flame:#1b1410;--color-success:#4fd784;--color-border:#463429;--color-border-warm:#5a3c20;--color-focus-ring:#ffc24a;--gradient-flame:linear-gradient(92deg, #ffc24a 0%, #ff6a1f 45%, #ff3d6e 100%);--gradient-flame-2:linear-gradient(90deg, #ffc24a, #ff6a1f);--gradient-stage:linear-gradient(135deg, #2c1c12 0%, #221913 60%);--radius-sm:6px;--radius-md:8px;--radius-lg:10px;--radius-xl:12px;--radius-2xl:14px;--radius-full:999px;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:24px;--space-6:32px;--space-7:44px;--space-8:56px;--font-family:-apple-system, "Segoe UI Variable", "Segoe UI", Roboto, Helvetica, Arial, sans-serif}*{box-sizing:border-box}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto!important}*,:before,:after{transition-duration:.001ms!important;animation-duration:.001ms!important;animation-iteration-count:1!important;animation-play-state:paused!important}}html,body{height:100%;margin:0}body{font-family:var(--font-family);color:var(--color-text-primary);background:var(--color-stage);font-size:16px;font-weight:500}.app-shell{background:var(--color-stage);justify-content:center;align-items:flex-start;min-height:100vh;display:flex}.app-window{width:100%;max-width:1000px;margin:var(--space-6) var(--space-4);border-radius:var(--radius-2xl);border:1px solid var(--color-border);background:var(--color-surface);overflow:hidden;box-shadow:0 24px 60px #00000080}.app-titlebar{align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);background:var(--color-stage-deep);border-bottom:1px solid var(--color-border);display:flex}.app-traffic{gap:var(--space-2);display:flex}.app-traffic span{border-radius:var(--radius-full);background:var(--color-surface-overlay);width:12px;height:12px}.app-urlbar{padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);background:var(--color-surface-raised-hi);color:var(--color-text-secondary);flex:1;font-size:13px}.app-stage{padding:var(--space-8) var(--space-7);background:var(--gradient-stage);min-height:420px}.btn{font-family:var(--font-family);cursor:pointer;padding:var(--space-3) var(--space-5);border-radius:var(--radius-xl);border:0;font-size:16px;font-weight:800;transition:filter .12s}.btn:hover{filter:brightness(1.08)}.btn:focus-visible{outline:3px solid var(--color-focus-ring);outline-offset:3px}.btn--primary{background:var(--gradient-flame-2);color:var(--color-on-flame);box-shadow:0 10px 30px #ff6a1f73}.btn--secondary{color:var(--color-text-primary);border:2px solid var(--color-accent);background:0 0}.btn--ghost{background:var(--color-surface-raised-hi);color:var(--color-text-primary);border:1px solid var(--color-border-warm);border-radius:var(--radius-lg)}.field{gap:var(--space-2);flex-direction:column;display:flex}.field-label{color:var(--color-text-secondary);font-size:13px;font-weight:800}.field-input{font-family:var(--font-family);padding:var(--space-3) var(--space-4);border-radius:var(--radius-lg);background:var(--color-stage);color:var(--color-text-primary);border:1px solid var(--color-border);font-size:16px}.field-input::placeholder{color:var(--color-text-muted)}.field-input:focus-visible{outline:3px solid var(--color-focus-ring);outline-offset:2px}.screen{gap:var(--space-4);flex-direction:column;display:flex}.eyebrow{letter-spacing:.16em;text-transform:uppercase;color:var(--color-accent-gold);margin:0;font-size:12px;font-weight:800}.screen h1{letter-spacing:-.02em;color:var(--color-text-primary);margin:0;font-size:34px;font-weight:900}.lede{color:var(--color-text-secondary);margin:0;font-size:20px}.lede b{color:var(--color-text-primary)}.field legend.field-label,.field-label{padding:0}.field-note{color:var(--color-text-secondary);margin:0;font-size:13px}fieldset.field{border:0;margin:0;padding:0}.picker,.modules{margin:var(--space-2) 0 0;gap:var(--space-2);flex-direction:column;padding:0;list-style:none;display:flex}.picker-row,.mod-row{align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);border-radius:var(--radius-lg);background:var(--color-surface-raised);border:1px solid var(--color-border);cursor:pointer;display:flex}.picker-name,.mod-name{color:var(--color-text-primary);font-weight:700}.mod-desc{color:var(--color-text-secondary);font-size:13px}.picker-tag{letter-spacing:.08em;text-transform:uppercase;color:var(--color-accent-gold);font-size:11px;font-weight:800}.picker-check{color:var(--color-success);margin-left:auto;font-weight:800}.disclosure{gap:var(--space-3);flex-direction:column;display:flex}.disclosure-panel{gap:var(--space-4);padding:var(--space-4);border-radius:var(--radius-2xl);background:var(--color-surface-raised);border:1px solid var(--color-border);flex-direction:column;display:flex}.screen-actions{align-items:center;gap:var(--space-4);margin-top:var(--space-4);flex-wrap:wrap;display:flex}.start-note{color:var(--color-text-secondary);font-size:15px}.versions{border-collapse:collapse;width:100%;margin:var(--space-4) 0;text-align:left}.versions caption{text-align:left;color:var(--color-text-secondary);margin-bottom:var(--space-2);font-weight:700}.versions th,.versions td{padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--color-border)}.versions th{color:var(--color-text-secondary);font-weight:700}.versions td{color:var(--color-text-primary);font-variant-numeric:tabular-nums}.field-row{align-items:center;gap:var(--space-3);margin-top:var(--space-2);display:flex}.field-value{color:var(--color-text-primary);font-weight:700}.default-pill{letter-spacing:.06em;text-transform:uppercase;color:var(--color-success);padding:var(--space-1) var(--space-3);border-radius:var(--radius-full);background:var(--color-surface-raised-hi);border:1px solid var(--color-border-warm);font-size:11px;font-weight:800}.optin-form{gap:var(--space-3);margin-top:var(--space-3);flex-direction:column;align-items:flex-start;display:flex}.start-error{padding:var(--space-3) var(--space-4);border-radius:var(--radius-lg);background:var(--color-surface-raised);border:1px solid var(--color-border-warm);color:var(--color-text-primary);margin:0}.progressbar{border-radius:var(--radius-lg);background:var(--color-surface-raised);border:1px solid var(--color-border);height:12px;overflow:hidden}.progressbar-fill{background:var(--gradient-flame);height:100%;transition:width .32s}.progressbar--activity .progressbar-fill{animation:1.4s ease-in-out infinite kindling-pulse}@keyframes kindling-pulse{0%,to{opacity:.55}50%{opacity:1}}.sr-live{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.steps{margin:var(--space-4) 0 0;gap:var(--space-2);flex-direction:column;padding:0;list-style:none;display:flex}.step-row{align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);border-radius:var(--radius-2xl);background:var(--color-surface-raised);border:1px solid var(--color-border);display:flex}.step-icon{text-align:center;width:1.4em;font-size:18px}.step-word{letter-spacing:.04em;text-transform:uppercase;min-width:7.5em;font-size:12px;font-weight:900}.step-message{color:var(--color-text-secondary);flex:1}.step-row[data-tone=wait] .step-word{color:var(--color-text-secondary)}.step-row[data-tone=busy] .step-word{color:var(--color-accent-gold)}.step-row[data-tone=success] .step-word{color:var(--color-success)}.step-row[data-tone=error] .step-word{color:var(--color-text-primary)}.step-row[data-tone=busy] .step-icon{color:var(--color-accent)}.step-row[data-tone=error] .step-icon{color:var(--color-accent-pink)}.step-activity{color:var(--color-accent-gold);animation:1.2s ease-in-out infinite kindling-pulse}.steps--compact .step-row{padding:var(--space-2) var(--space-3);background:var(--color-surface-raised-hi)}.fix-command{align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);border-radius:var(--radius-lg);background:var(--color-stage);border:1px solid var(--color-border-warm);flex-wrap:wrap;display:flex}.fix-command-code{color:var(--color-accent-gold);word-break:break-all;flex:1;font-family:ui-monospace,SF Mono,Menlo,Consolas,monospace;font-size:14px}