@oscharko-dev/keiko-cli 0.2.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.
Files changed (77) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/context.d.ts +3 -0
  3. package/dist/context.d.ts.map +1 -0
  4. package/dist/context.js +103 -0
  5. package/dist/doctor.d.ts +24 -0
  6. package/dist/doctor.d.ts.map +1 -0
  7. package/dist/doctor.js +108 -0
  8. package/dist/evaluate.d.ts +8 -0
  9. package/dist/evaluate.d.ts.map +1 -0
  10. package/dist/evaluate.js +270 -0
  11. package/dist/evidence.d.ts +9 -0
  12. package/dist/evidence.d.ts.map +1 -0
  13. package/dist/evidence.js +129 -0
  14. package/dist/gateway-config.d.ts +12 -0
  15. package/dist/gateway-config.d.ts.map +1 -0
  16. package/dist/gateway-config.js +19 -0
  17. package/dist/gen-tests.d.ts +8 -0
  18. package/dist/gen-tests.d.ts.map +1 -0
  19. package/dist/gen-tests.js +216 -0
  20. package/dist/index.d.ts +18 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +26 -0
  23. package/dist/init.d.ts +9 -0
  24. package/dist/init.d.ts.map +1 -0
  25. package/dist/init.js +122 -0
  26. package/dist/install-layout.d.ts +19 -0
  27. package/dist/install-layout.d.ts.map +1 -0
  28. package/dist/install-layout.js +76 -0
  29. package/dist/investigate.d.ts +9 -0
  30. package/dist/investigate.d.ts.map +1 -0
  31. package/dist/investigate.js +249 -0
  32. package/dist/launcher-paths.d.ts +4 -0
  33. package/dist/launcher-paths.d.ts.map +1 -0
  34. package/dist/launcher-paths.js +69 -0
  35. package/dist/launcher-platforms.d.ts +25 -0
  36. package/dist/launcher-platforms.d.ts.map +1 -0
  37. package/dist/launcher-platforms.js +131 -0
  38. package/dist/launcher-state.d.ts +25 -0
  39. package/dist/launcher-state.d.ts.map +1 -0
  40. package/dist/launcher-state.js +228 -0
  41. package/dist/launcher.d.ts +21 -0
  42. package/dist/launcher.d.ts.map +1 -0
  43. package/dist/launcher.js +439 -0
  44. package/dist/lifecycle.d.ts +22 -0
  45. package/dist/lifecycle.d.ts.map +1 -0
  46. package/dist/lifecycle.js +425 -0
  47. package/dist/memory.d.ts +14 -0
  48. package/dist/memory.d.ts.map +1 -0
  49. package/dist/memory.js +290 -0
  50. package/dist/models.d.ts +4 -0
  51. package/dist/models.d.ts.map +1 -0
  52. package/dist/models.js +62 -0
  53. package/dist/prompt-enhancer.d.ts +13 -0
  54. package/dist/prompt-enhancer.d.ts.map +1 -0
  55. package/dist/prompt-enhancer.js +261 -0
  56. package/dist/repair.d.ts +10 -0
  57. package/dist/repair.d.ts.map +1 -0
  58. package/dist/repair.js +402 -0
  59. package/dist/run.d.ts +10 -0
  60. package/dist/run.d.ts.map +1 -0
  61. package/dist/run.js +269 -0
  62. package/dist/runner.d.ts +7 -0
  63. package/dist/runner.d.ts.map +1 -0
  64. package/dist/runner.js +108 -0
  65. package/dist/state-paths.d.ts +43 -0
  66. package/dist/state-paths.d.ts.map +1 -0
  67. package/dist/state-paths.js +396 -0
  68. package/dist/ui.d.ts +39 -0
  69. package/dist/ui.d.ts.map +1 -0
  70. package/dist/ui.js +450 -0
  71. package/dist/uninstall.d.ts +10 -0
  72. package/dist/uninstall.d.ts.map +1 -0
  73. package/dist/uninstall.js +345 -0
  74. package/dist/verify.d.ts +3 -0
  75. package/dist/verify.d.ts.map +1 -0
  76. package/dist/verify.js +108 -0
  77. package/package.json +42 -0
package/dist/ui.js ADDED
@@ -0,0 +1,450 @@
1
+ // `keiko ui` — launches the local Wave 1 UI BFF (ADR-0011). It resolves the packaged static export
2
+ // (dist/ui/static) and the precomputed CSP hashes (dist/ui/csp-hashes.json) relative to this
3
+ // compiled module, builds the CSP, starts createUiServer bound to 127.0.0.1 only, prints the local
4
+ // URL, and keeps running until the process is signalled. Exit 2 on a usage error (bad --host/--port
5
+ // or a flag missing its value), 1 when the static export is not present (the package was built with
6
+ // `npm run build` but not `npm run build:ui`).
7
+ //
8
+ // ADR-0013 D2 site 1 — Detect-and-re-exec guard. Node 22.0–22.11 builds require
9
+ // --experimental-sqlite to import node:sqlite; 22.22+ loads it without the flag. The guard tries
10
+ // the import; on failure it re-spawns the current process with --experimental-sqlite prepended,
11
+ // inheriting stdio and forwarding SIGINT/SIGTERM to the child, then propagates the child's exit
12
+ // code. Injected-test invocations skip the guard entirely.
13
+ import { existsSync, readFileSync, unwatchFile, watchFile } from "node:fs";
14
+ import { readdir, readFile } from "node:fs/promises";
15
+ import { dirname, isAbsolute, join, resolve } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+ import { createRequire } from "node:module";
18
+ import { spawn } from "node:child_process";
19
+ import { buildCspHeader, createUiServer, buildUiHandlerDeps, DEFAULT_UI_PORT, UI_HOST, UiStoreError, extractInlineScriptHashes, } from "@oscharko-dev/keiko-server";
20
+ import { resolvePreferredInstallLayout } from "./install-layout.js";
21
+ const ALLOWED_HOSTS = new Set(["127.0.0.1", "localhost"]);
22
+ const KEIKO_PROCESS_TITLE = "Keiko";
23
+ const SQLITE_FLAG = "--experimental-sqlite";
24
+ // Deliberately a closed allowlist: repo-local .env discovery must not import KEIKO_*
25
+ // runtime configuration because the UI may be launched from an untrusted project root.
26
+ // FIGMA_ACCESS_TOKEN is the documented Figma connector exception (#751); it is consumed
27
+ // server-side only and never sent to the browser.
28
+ const LOCAL_DOTENV_ENV_NAME_ALLOWLIST = new Set(["FIGMA_ACCESS_TOKEN"]);
29
+ const DEFAULT_STATE_DIR = ".keiko";
30
+ const USAGE = `Usage:
31
+ keiko ui [--port PORT] [--host 127.0.0.1|localhost] [--evidence-dir PATH] [--config PATH] [--ui-db PATH]
32
+
33
+ Launches the local Keiko UI on the loopback interface and prints its URL. The server
34
+ binds 127.0.0.1 only and serves the packaged UI assets (built with \`npm run build:ui\`).
35
+ `;
36
+ async function collectHtmlFiles(dir) {
37
+ const entries = await readdir(dir, { withFileTypes: true });
38
+ const files = [];
39
+ for (const entry of entries) {
40
+ const full = join(dir, entry.name);
41
+ if (entry.isDirectory()) {
42
+ files.push(...(await collectHtmlFiles(full)));
43
+ continue;
44
+ }
45
+ if (entry.name.endsWith(".html")) {
46
+ files.push(full);
47
+ }
48
+ }
49
+ return files;
50
+ }
51
+ function parseHashList(raw) {
52
+ const parsed = JSON.parse(raw);
53
+ if (!Array.isArray(parsed))
54
+ return [];
55
+ return parsed.filter((entry) => typeof entry === "string");
56
+ }
57
+ async function loadCspMaterial(staticRoot, hashesFile) {
58
+ const htmlFiles = await collectHtmlFiles(staticRoot);
59
+ const documents = await Promise.all(htmlFiles.map((file) => readFile(file, "utf8")));
60
+ const runtimeHashes = extractInlineScriptHashes(documents);
61
+ try {
62
+ const raw = await readFile(hashesFile, "utf8");
63
+ const storedHashes = parseHashList(raw);
64
+ if (storedHashes.length === runtimeHashes.length &&
65
+ storedHashes.every((hash, index) => hash === runtimeHashes[index])) {
66
+ return { hashes: storedHashes, header: buildCspHeader(storedHashes) };
67
+ }
68
+ }
69
+ catch {
70
+ // Fall back to the runtime export-derived hashes below.
71
+ }
72
+ return { hashes: runtimeHashes, header: buildCspHeader(runtimeHashes) };
73
+ }
74
+ function parsePort(raw) {
75
+ if (!/^\d{1,5}$/.test(raw)) {
76
+ return null;
77
+ }
78
+ const port = Number(raw);
79
+ return port >= 1 && port <= 65535 ? port : null;
80
+ }
81
+ function readFlagValue(args, index) {
82
+ const value = args[index + 1];
83
+ return value === undefined || value.startsWith("--") ? null : value;
84
+ }
85
+ function isUiFlag(arg) {
86
+ return (arg === "--port" ||
87
+ arg === "--host" ||
88
+ arg === "--evidence-dir" ||
89
+ arg === "--config" ||
90
+ arg === "--ui-db");
91
+ }
92
+ function setRawUiOption(raw, flag, value) {
93
+ switch (flag) {
94
+ case "--port":
95
+ raw.portRaw = value;
96
+ return;
97
+ case "--host":
98
+ raw.hostRaw = value;
99
+ return;
100
+ case "--evidence-dir":
101
+ raw.evidenceRaw = value;
102
+ return;
103
+ case "--config":
104
+ raw.configRaw = value;
105
+ return;
106
+ case "--ui-db":
107
+ raw.uiDbRaw = value;
108
+ return;
109
+ }
110
+ }
111
+ function collectUiOptions(args) {
112
+ const raw = {};
113
+ for (let i = 0; i < args.length; i += 1) {
114
+ const arg = args[i];
115
+ if (arg === undefined)
116
+ return null;
117
+ if (arg === "--help" || arg === "-h")
118
+ return "help";
119
+ if (!isUiFlag(arg))
120
+ return null;
121
+ const value = readFlagValue(args, i);
122
+ if (value === null)
123
+ return null;
124
+ setRawUiOption(raw, arg, value);
125
+ i += 1;
126
+ }
127
+ return raw;
128
+ }
129
+ // Parses flags. Returns the parsed args, or null on any usage error (missing flag value, invalid
130
+ // port, or a host other than the two loopback names).
131
+ export function parseUiArgs(args) {
132
+ const raw = collectUiOptions(args);
133
+ if (raw === "help" || raw === null)
134
+ return raw;
135
+ const { portRaw, hostRaw, evidenceRaw, configRaw, uiDbRaw } = raw;
136
+ if (hostRaw !== undefined && !ALLOWED_HOSTS.has(hostRaw)) {
137
+ return null;
138
+ }
139
+ const port = portRaw === undefined ? DEFAULT_UI_PORT : parsePort(portRaw);
140
+ if (port === null) {
141
+ return null;
142
+ }
143
+ return { port, evidenceDir: evidenceRaw, config: configRaw, uiDbPath: uiDbRaw };
144
+ }
145
+ function defaultStaticRoot(cwd) {
146
+ const preferredLayout = resolvePreferredInstallLayout(cwd);
147
+ if (preferredLayout !== undefined)
148
+ return preferredLayout.staticRoot;
149
+ // The root bin shim (`dist/cli/index.js`) surfaces `KEIKO_UI_STATIC_ROOT` so the
150
+ // cli package does not have to know its own installation layout. The
151
+ // import.meta.url fallback preserves the standalone behaviour for callers that
152
+ // construct UiCliDeps without going through the bin shim (e.g. in tests).
153
+ const fromEnv = process.env.KEIKO_UI_STATIC_ROOT;
154
+ if (fromEnv !== undefined && fromEnv !== "")
155
+ return fromEnv;
156
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "ui", "static");
157
+ }
158
+ function parseEnvValue(raw) {
159
+ const value = raw.trim();
160
+ if (value.length >= 2) {
161
+ const first = value[0];
162
+ const last = value[value.length - 1];
163
+ if ((first === `"` && last === `"`) || (first === "'" && last === "'")) {
164
+ return value.slice(1, -1);
165
+ }
166
+ }
167
+ return value;
168
+ }
169
+ function loadLocalKeikoEnv(cwd, env) {
170
+ const file = join(cwd, ".env");
171
+ if (!existsSync(file)) {
172
+ return env;
173
+ }
174
+ const merged = { ...env };
175
+ const text = readFileSync(file, "utf8");
176
+ for (const rawLine of text.split(/\r?\n/)) {
177
+ const line = rawLine.trim();
178
+ if (line.length === 0 || line.startsWith("#"))
179
+ continue;
180
+ const equals = line.indexOf("=");
181
+ if (equals <= 0)
182
+ continue;
183
+ const key = line.slice(0, equals).trim();
184
+ if (!LOCAL_DOTENV_ENV_NAME_ALLOWLIST.has(key))
185
+ continue;
186
+ if (merged[key] !== undefined)
187
+ continue;
188
+ merged[key] = parseEnvValue(line.slice(equals + 1));
189
+ }
190
+ return merged;
191
+ }
192
+ function resolveUiConfigPath(parsed, env) {
193
+ return parsed.config ?? env.KEIKO_CONFIG_FILE;
194
+ }
195
+ function hasEnvValue(value) {
196
+ return value !== undefined && value.length > 0;
197
+ }
198
+ function resolveRuntimeStateDir(cwd, env) {
199
+ const raw = env.KEIKO_STATE_DIR;
200
+ if (hasEnvValue(raw)) {
201
+ return isAbsolute(raw) ? raw : resolve(cwd, raw);
202
+ }
203
+ return resolve(cwd, DEFAULT_STATE_DIR);
204
+ }
205
+ function withDefaultLocalRuntimeStateEnv(cwd, parsed, env) {
206
+ const stateDir = resolveRuntimeStateDir(cwd, env);
207
+ const next = { ...env, KEIKO_STATE_DIR: stateDir };
208
+ if (parsed.uiDbPath === undefined && !hasEnvValue(next.KEIKO_UI_DATA_DIR)) {
209
+ next.KEIKO_UI_DATA_DIR = join(stateDir, "ui");
210
+ }
211
+ if (!hasEnvValue(next.KEIKO_MEMORY_DIR)) {
212
+ next.KEIKO_MEMORY_DIR = join(stateDir, "memory");
213
+ }
214
+ return next;
215
+ }
216
+ // Conservative request/header timeouts on the loopback BFF (defense in depth, L2/L3): even on
217
+ // 127.0.0.1 a slow or stuck client must not hold a connection indefinitely. headersTimeout must be
218
+ // at or below requestTimeout so an incomplete request line/header set is cut first.
219
+ const REQUEST_TIMEOUT_MS = 30_000;
220
+ const HEADERS_TIMEOUT_MS = 10_000;
221
+ export function applyServerTimeouts(server) {
222
+ server.requestTimeout = REQUEST_TIMEOUT_MS;
223
+ server.headersTimeout = HEADERS_TIMEOUT_MS;
224
+ }
225
+ async function listen(server, port) {
226
+ await new Promise((res, rej) => {
227
+ server.once("error", rej);
228
+ server.listen(port, UI_HOST, res);
229
+ });
230
+ }
231
+ // Keeps the real-CLI process alive until a shutdown signal or server close. Resolves cleanly so
232
+ // the caller can return 0. Registered listeners are removed on resolve to prevent leaks.
233
+ export function waitForShutdown(server) {
234
+ return new Promise((resolve) => {
235
+ const onClose = () => {
236
+ process.removeListener("SIGINT", onSignal);
237
+ process.removeListener("SIGTERM", onSignal);
238
+ resolve();
239
+ };
240
+ const onSignal = () => {
241
+ server.removeListener("close", onClose);
242
+ server.close(() => {
243
+ resolve();
244
+ });
245
+ };
246
+ server.once("close", onClose);
247
+ process.once("SIGINT", onSignal);
248
+ process.once("SIGTERM", onSignal);
249
+ });
250
+ }
251
+ // Default probe: try to require node:sqlite. Any failure (ERR_UNKNOWN_BUILTIN_MODULE on early
252
+ // 22.x, or a thrown ExperimentalWarning that escaped) means we need the flag. The require is
253
+ // guarded inside a try; we never throw past here.
254
+ function defaultSqliteProbe() {
255
+ try {
256
+ const req = createRequire(import.meta.url);
257
+ req("node:sqlite");
258
+ return true;
259
+ }
260
+ catch {
261
+ return false;
262
+ }
263
+ }
264
+ // Returns true if `--experimental-sqlite` is already on execArgv, meaning we are inside the child
265
+ // already and must not loop. Inspects both the supplied execArgv source (Node-level flags the
266
+ // parent inherited) AND NODE_OPTIONS (where the flag may live in env).
267
+ function alreadyFlagged(env, execArgv) {
268
+ if (execArgv.includes(SQLITE_FLAG))
269
+ return true;
270
+ const nodeOptions = env.NODE_OPTIONS;
271
+ return typeof nodeOptions === "string" && nodeOptions.includes(SQLITE_FLAG);
272
+ }
273
+ // Re-spawns the current process with --experimental-sqlite prepended, inheriting stdio. Returns
274
+ // the exit code the child terminated with so the parent can propagate it.
275
+ export async function reExecWithSqliteFlag(_env, spawnFn, cwd) {
276
+ const entry = resolvePreferredInstallLayout(cwd)?.binPath ?? process.argv[1];
277
+ if (entry === undefined)
278
+ return 1;
279
+ const childArgs = [SQLITE_FLAG, ...process.execArgv, entry, ...process.argv.slice(2)];
280
+ const child = spawnFn(process.execPath, childArgs, {
281
+ argv0: KEIKO_PROCESS_TITLE,
282
+ stdio: "inherit",
283
+ });
284
+ const forwardSigint = () => {
285
+ child.kill("SIGINT");
286
+ };
287
+ const forwardSigterm = () => {
288
+ child.kill("SIGTERM");
289
+ };
290
+ process.on("SIGINT", forwardSigint);
291
+ process.on("SIGTERM", forwardSigterm);
292
+ return new Promise((res) => {
293
+ child.on("exit", (code, signal) => {
294
+ process.removeListener("SIGINT", forwardSigint);
295
+ process.removeListener("SIGTERM", forwardSigterm);
296
+ if (typeof code === "number") {
297
+ res(code);
298
+ return;
299
+ }
300
+ res(signal === null ? 1 : 128);
301
+ });
302
+ });
303
+ }
304
+ // Returns undefined when no re-exec is needed; returns the child's exit code when a re-exec
305
+ // happened. The guard never runs when an injected test factory bypasses the production loop, and
306
+ // it never re-loops once --experimental-sqlite is already in execArgv.
307
+ async function maybeReExecForSqlite(env, deps, cwd) {
308
+ if (deps.createServer !== undefined)
309
+ return undefined;
310
+ const execArgv = (deps.currentExecArgv ?? (() => process.execArgv))();
311
+ if (alreadyFlagged(env, execArgv))
312
+ return undefined;
313
+ const probe = deps.sqliteProbe ?? defaultSqliteProbe;
314
+ if (probe())
315
+ return undefined;
316
+ const spawnFn = deps.spawnFn ?? spawn;
317
+ return reExecWithSqliteFlag(env, spawnFn, cwd);
318
+ }
319
+ function buildHandlerDepsOrReport(parsed, cwd, effectiveEnv, io) {
320
+ try {
321
+ return buildUiHandlerDeps({
322
+ configPath: resolveUiConfigPath(parsed, effectiveEnv),
323
+ evidenceDir: parsed.evidenceDir,
324
+ uiDbPath: parsed.uiDbPath,
325
+ initialProjectPath: cwd,
326
+ env: effectiveEnv,
327
+ });
328
+ }
329
+ catch (error) {
330
+ if (error instanceof UiStoreError) {
331
+ io.err(`keiko ui: ${error.message}\n`);
332
+ return 2;
333
+ }
334
+ throw error;
335
+ }
336
+ }
337
+ function registerLaunchProjectOrReport(cwd, handlerDeps, io) {
338
+ try {
339
+ handlerDeps.store.createProject(cwd);
340
+ return null;
341
+ }
342
+ catch (error) {
343
+ if (error instanceof UiStoreError) {
344
+ io.err(`keiko ui: ${error.message}\n`);
345
+ return 2;
346
+ }
347
+ throw error;
348
+ }
349
+ }
350
+ function ensureStaticRoot(staticRoot, io) {
351
+ if (existsSync(staticRoot)) {
352
+ return true;
353
+ }
354
+ io.err(`keiko ui: UI assets not found at ${staticRoot}. Run \`npm run build:ui\` first.\n`);
355
+ return false;
356
+ }
357
+ function parseUiArgsOrExit(args, io) {
358
+ const parsed = parseUiArgs(args);
359
+ if (parsed === "help") {
360
+ io.out(USAGE);
361
+ return 0;
362
+ }
363
+ if (parsed === null) {
364
+ io.err(USAGE);
365
+ return 2;
366
+ }
367
+ return parsed;
368
+ }
369
+ async function maybeWaitForShutdown(server, deps) {
370
+ if (deps.createServer !== undefined) {
371
+ return;
372
+ }
373
+ await waitForShutdown(server);
374
+ }
375
+ async function startUiServer(staticRoot, csp, parsed, handlerDeps, io, deps) {
376
+ const factory = deps.createServer ?? createUiServer;
377
+ const server = await factory({ staticRoot, csp, port: parsed.port, handlerDeps });
378
+ applyServerTimeouts(server);
379
+ await listen(server, parsed.port);
380
+ io.out(`Keiko UI listening on http://${UI_HOST}:${String(parsed.port)}\n`);
381
+ // Block only in the real CLI path (no injected factory). Injected-server tests skip blocking so
382
+ // they don't hang; the real process must stay alive until signalled.
383
+ await maybeWaitForShutdown(server, deps);
384
+ }
385
+ export async function createLiveCspSource(staticRoot, hashesFile, io) {
386
+ let current = (await loadCspMaterial(staticRoot, hashesFile)).header;
387
+ let reloading = false;
388
+ let disposed = false;
389
+ let warned = false;
390
+ const reload = async () => {
391
+ if (reloading || disposed)
392
+ return;
393
+ reloading = true;
394
+ try {
395
+ current = (await loadCspMaterial(staticRoot, hashesFile)).header;
396
+ warned = false;
397
+ }
398
+ catch (error) {
399
+ if (!warned) {
400
+ const message = error instanceof Error ? error.message : String(error);
401
+ io.err(`Warning: failed to reload CSP hashes from ${hashesFile}: ${message}\n`);
402
+ warned = true;
403
+ }
404
+ }
405
+ finally {
406
+ reloading = false;
407
+ }
408
+ };
409
+ // Issue #1214 follow-up: when the UI process starts while build:ui is still writing the static
410
+ // export and `csp-hashes.json`, a one-shot CSP read can freeze a stale policy into the process.
411
+ // Watching the hash file keeps fresh page loads aligned with the latest exported inline scripts.
412
+ watchFile(hashesFile, { interval: 500 }, () => {
413
+ void reload();
414
+ });
415
+ return {
416
+ csp: () => current,
417
+ dispose: () => {
418
+ disposed = true;
419
+ unwatchFile(hashesFile);
420
+ },
421
+ };
422
+ }
423
+ export async function runUiCli(args, io, env, deps = {}) {
424
+ const cwd = deps.cwd ?? process.cwd();
425
+ const effectiveEnv = loadLocalKeikoEnv(cwd, env);
426
+ const parsed = parseUiArgsOrExit(args, io);
427
+ if (typeof parsed === "number")
428
+ return parsed;
429
+ const reExec = await maybeReExecForSqlite(effectiveEnv, deps, cwd);
430
+ if (reExec !== undefined)
431
+ return reExec;
432
+ const staticRoot = deps.staticRoot ?? defaultStaticRoot(cwd);
433
+ if (!ensureStaticRoot(staticRoot, io)) {
434
+ return 1;
435
+ }
436
+ const cspRuntime = await createLiveCspSource(staticRoot, deps.hashesFile ?? join(staticRoot, "..", "csp-hashes.json"), io);
437
+ const handlerDeps = buildHandlerDepsOrReport(parsed, cwd, withDefaultLocalRuntimeStateEnv(cwd, parsed, effectiveEnv), io);
438
+ if (typeof handlerDeps === "number")
439
+ return handlerDeps;
440
+ const launchProjectResult = registerLaunchProjectOrReport(cwd, handlerDeps, io);
441
+ if (launchProjectResult !== null)
442
+ return launchProjectResult;
443
+ try {
444
+ await startUiServer(staticRoot, cspRuntime.csp(), parsed, handlerDeps, io, deps);
445
+ return 0;
446
+ }
447
+ finally {
448
+ cspRuntime.dispose();
449
+ }
450
+ }
@@ -0,0 +1,10 @@
1
+ import type { EnvSource } from "@oscharko-dev/keiko-model-gateway";
2
+ import type { CliIo } from "./runner.js";
3
+ export interface UninstallCliDeps {
4
+ readonly cwd?: string | undefined;
5
+ readonly homedir?: () => string;
6
+ readonly isProcessAlive?: (pid: number) => boolean;
7
+ readonly killProcess?: (pid: number, signal?: NodeJS.Signals | 0) => void;
8
+ }
9
+ export declare function runUninstallCli(args: readonly string[], io: CliIo, env: EnvSource, deps?: UninstallCliDeps): number;
10
+ //# sourceMappingURL=uninstall.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.d.ts","sourceRoot":"","sources":["../src/uninstall.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAsDzC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,IAAI,CAAC;CAC3E;AAoTD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,KAAK,EACT,GAAG,EAAE,SAAS,EACd,IAAI,GAAE,gBAAqB,GAC1B,MAAM,CA+BR"}