create-op-node 0.10.5 → 0.10.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1989,6 +1989,10 @@ async function locateOrCloneRepo(input) {
1989
1989
  return { kind: "cloned", path: target };
1990
1990
  }
1991
1991
 
1992
+ // src/lib/assert.ts
1993
+ function assertNever(_value) {
1994
+ }
1995
+
1992
1996
  // src/commands/bootstrap.ts
1993
1997
  var PUBLIC_PROFILES = ["public"];
1994
1998
  var LOCAL_PROFILES = [];
@@ -2036,6 +2040,33 @@ var bootstrapCommand = new Command("bootstrap").description(
2036
2040
  ).default(false)
2037
2041
  ).addOption(new Option("-y, --yes", "Skip confirmation prompts").default(false)).action(async (opts) => {
2038
2042
  p3.intro(pc2.bgCyan(pc2.black(" create-op-node bootstrap ")));
2043
+ const nodeType = await selectNodeType(opts);
2044
+ const region = await resolveRegion(opts);
2045
+ const owner = opts.owner ?? "OpusPopuli";
2046
+ const repoName = `opuspopuli-node-${region}`;
2047
+ const composeFile = opts.composeFile ?? defaultComposeFiles(opts);
2048
+ const llmModelChoice = opts.llmModel ?? await selectLlmModel(opts);
2049
+ const [embeddingModel, llmModel] = resolveModels({
2050
+ llmModel: llmModelChoice,
2051
+ ...opts.embeddingModel !== void 0 ? { embeddingModel: opts.embeddingModel } : {}
2052
+ });
2053
+ await runSystemChecksPhase();
2054
+ await runBrewPhase(opts);
2055
+ await ensureGhAuth();
2056
+ await promptTailscaleSignin();
2057
+ const repoPath = await locateRegionRepoPhase(opts, owner, repoName);
2058
+ await unlockKeychainPhase();
2059
+ const secrets = await collectSecretsPhase({ region, nodeType, opts });
2060
+ await runLaunchAgentPhase({ opts, secrets, llmModel, embeddingModel });
2061
+ await installWrapperPhase({ repoPath, region, promptServiceUrl: secrets.promptServiceUrl });
2062
+ await loginGhcrPhase();
2063
+ await runOllamaPhase({ opts, embeddingModel, llmModel });
2064
+ await runStackPhase({ opts, repoPath, region, composeFile, secrets, llmModel, embeddingModel });
2065
+ });
2066
+ function defaultComposeFiles(opts) {
2067
+ return opts.localOnly ? ["docker-compose-prod.yml"] : ["docker-compose-prod.yml", "docker-compose-backup.yml"];
2068
+ }
2069
+ async function selectNodeType(opts) {
2039
2070
  const nodeType = opts.nodeType ? opts.nodeType : unwrap(
2040
2071
  await p3.select({
2041
2072
  message: "What kind of node are you provisioning?",
@@ -2062,22 +2093,18 @@ var bootstrapCommand = new Command("bootstrap").description(
2062
2093
  );
2063
2094
  process.exit(1);
2064
2095
  }
2065
- const region = opts.region ? opts.region : unwrap(
2096
+ return nodeType;
2097
+ }
2098
+ async function resolveRegion(opts) {
2099
+ return opts.region ? opts.region : unwrap(
2066
2100
  await p3.text({
2067
2101
  message: "Region label (the slug used during init \u2014 e.g. us-ca)?",
2068
2102
  placeholder: "us-ca",
2069
2103
  validate: (v) => /^[a-z0-9-]{2,32}$/.test(v ?? "") ? void 0 : "lowercase letters, digits, hyphens; 2\u201332 chars"
2070
2104
  })
2071
2105
  );
2072
- const owner = opts.owner ?? "OpusPopuli";
2073
- const repoName = `opuspopuli-node-${region}`;
2074
- const composeFileDefault = opts.localOnly ? ["docker-compose-prod.yml"] : ["docker-compose-prod.yml", "docker-compose-backup.yml"];
2075
- const composeFile = opts.composeFile ?? composeFileDefault;
2076
- const llmModelChoice = opts.llmModel ?? await selectLlmModel(opts);
2077
- const [embeddingModel, llmModel] = resolveModels({
2078
- llmModel: llmModelChoice,
2079
- ...opts.embeddingModel !== void 0 ? { embeddingModel: opts.embeddingModel } : {}
2080
- });
2106
+ }
2107
+ async function runSystemChecksPhase() {
2081
2108
  const sysSpin = p3.spinner();
2082
2109
  sysSpin.start("Inspecting macOS\u2026");
2083
2110
  const snap = await inspectSystem();
@@ -2122,54 +2149,55 @@ var bootstrapCommand = new Command("bootstrap").description(
2122
2149
  if (!r.ok) p3.note(`${pc2.red("\u2717")} ${r.reason}`, "pmset failed");
2123
2150
  }
2124
2151
  }
2125
- if (!opts.skipBrew) {
2126
- const brewInfo = await detectBrew();
2127
- if (!brewInfo.installed) {
2128
- p3.note(
2129
- [
2130
- `Homebrew is not installed. Open another shell and run:`,
2131
- "",
2132
- pc2.cyan(HOMEBREW_INSTALL_COMMAND),
2133
- "",
2134
- pc2.dim("Then come back and press Enter.")
2135
- ].join("\n"),
2136
- "Manual step"
2137
- );
2138
- unwrap(await p3.confirm({ message: "Homebrew installed?", initialValue: true }));
2139
- }
2140
- const brewSpin = p3.spinner();
2141
- brewSpin.start("Installing Studio packages\u2026 (~5\u201315 min first run)");
2142
- const report = await installPackages(STUDIO_PACKAGES, (pkg, status) => {
2143
- brewSpin.message(`${status}: ${pkg.name}`);
2144
- });
2145
- brewSpin.stop(
2146
- report.failed.length === 0 ? pc2.green(
2147
- `\u2713 ${report.installed.length} installed, ${report.alreadyPresent.length} already present`
2148
- ) : pc2.yellow(
2149
- `\u26A0 ${report.installed.length} installed, ${report.alreadyPresent.length} present, ${report.failed.length} failed`
2150
- )
2152
+ }
2153
+ async function runBrewPhase(opts) {
2154
+ if (opts.skipBrew) return;
2155
+ const brewInfo = await detectBrew();
2156
+ if (!brewInfo.installed) {
2157
+ p3.note(
2158
+ [
2159
+ `Homebrew is not installed. Open another shell and run:`,
2160
+ "",
2161
+ pc2.cyan(HOMEBREW_INSTALL_COMMAND),
2162
+ "",
2163
+ pc2.dim("Then come back and press Enter.")
2164
+ ].join("\n"),
2165
+ "Manual step"
2151
2166
  );
2152
- if (report.failed.length > 0) {
2153
- p3.note(
2154
- [
2155
- report.failed.map((f) => `${pc2.red("\u2022")} ${f.pkg.name}: ${f.reason}`).join("\n"),
2156
- "",
2157
- pc2.dim("A partial install usually fails downstream (Docker / Ollama / compose)."),
2158
- pc2.dim("Install the failed packages manually and re-run with --skip-brew.")
2159
- ].join("\n"),
2160
- "Brew failures"
2161
- );
2162
- const cont = unwrap(
2163
- await p3.confirm({
2164
- message: "Continue anyway? (Default no \u2014 recommended to fix and re-run.)",
2165
- initialValue: false
2166
- })
2167
- );
2168
- if (!cont) process.exit(1);
2169
- }
2167
+ unwrap(await p3.confirm({ message: "Homebrew installed?", initialValue: true }));
2170
2168
  }
2171
- await ensureGhAuth();
2172
- await promptTailscaleSignin();
2169
+ const brewSpin = p3.spinner();
2170
+ brewSpin.start("Installing Studio packages\u2026 (~5\u201315 min first run)");
2171
+ const report = await installPackages(STUDIO_PACKAGES, (pkg, status) => {
2172
+ brewSpin.message(`${status}: ${pkg.name}`);
2173
+ });
2174
+ brewSpin.stop(
2175
+ report.failed.length === 0 ? pc2.green(
2176
+ `\u2713 ${report.installed.length} installed, ${report.alreadyPresent.length} already present`
2177
+ ) : pc2.yellow(
2178
+ `\u26A0 ${report.installed.length} installed, ${report.alreadyPresent.length} present, ${report.failed.length} failed`
2179
+ )
2180
+ );
2181
+ if (report.failed.length > 0) {
2182
+ p3.note(
2183
+ [
2184
+ report.failed.map((f) => `${pc2.red("\u2022")} ${f.pkg.name}: ${f.reason}`).join("\n"),
2185
+ "",
2186
+ pc2.dim("A partial install usually fails downstream (Docker / Ollama / compose)."),
2187
+ pc2.dim("Install the failed packages manually and re-run with --skip-brew.")
2188
+ ].join("\n"),
2189
+ "Brew failures"
2190
+ );
2191
+ const cont = unwrap(
2192
+ await p3.confirm({
2193
+ message: "Continue anyway? (Default no \u2014 recommended to fix and re-run.)",
2194
+ initialValue: false
2195
+ })
2196
+ );
2197
+ if (!cont) process.exit(1);
2198
+ }
2199
+ }
2200
+ async function locateRegionRepoPhase(opts, owner, repoName) {
2173
2201
  const repoSpin = p3.spinner();
2174
2202
  repoSpin.start("Locating your region node repo\u2026");
2175
2203
  const located = await locateOrCloneRepo({
@@ -2187,6 +2215,9 @@ var bootstrapCommand = new Command("bootstrap").description(
2187
2215
  repoSpin.stop(
2188
2216
  located.kind === "cloned" ? pc2.green(`\u2713 Cloned ${owner}/${repoName} to ${repoPath}`) : pc2.green(`\u2713 Found region repo at ${repoPath}`)
2189
2217
  );
2218
+ return repoPath;
2219
+ }
2220
+ async function unlockKeychainPhase() {
2190
2221
  const keychain = await detectKeychain();
2191
2222
  if (!keychain.available) {
2192
2223
  p3.cancel(
@@ -2219,6 +2250,9 @@ var bootstrapCommand = new Command("bootstrap").description(
2219
2250
  }
2220
2251
  p3.note(`${pc2.green("\u2713")} Keychain unlocked for this session.`, "Keychain");
2221
2252
  }
2253
+ }
2254
+ async function collectCoreSecrets(args) {
2255
+ const { region, opts } = args;
2222
2256
  const pgsodiumKey = await loadSecret({
2223
2257
  region,
2224
2258
  account: "pgsodium-root-key",
@@ -2272,6 +2306,18 @@ var bootstrapCommand = new Command("bootstrap").description(
2272
2306
  validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)",
2273
2307
  generate: generateDashboardPassword
2274
2308
  });
2309
+ return {
2310
+ pgsodiumKey,
2311
+ tunnelToken,
2312
+ postgresPassword,
2313
+ jwtSecret,
2314
+ supabaseAnonKey,
2315
+ supabaseServiceRoleKey,
2316
+ dashboardPassword
2317
+ };
2318
+ }
2319
+ async function collectPromptServiceSecrets(args) {
2320
+ const { region, nodeType, opts } = args;
2275
2321
  if (nodeType === "region-with-prompts") {
2276
2322
  await loadSecret({
2277
2323
  region,
@@ -2315,6 +2361,9 @@ var bootstrapCommand = new Command("bootstrap").description(
2315
2361
  );
2316
2362
  process.exit(1);
2317
2363
  }
2364
+ return promptServiceUrl;
2365
+ }
2366
+ async function resolveSupabaseUrl(opts) {
2318
2367
  let supabaseUrl;
2319
2368
  if (opts.supabaseUrl) {
2320
2369
  supabaseUrl = opts.supabaseUrl;
@@ -2338,32 +2387,44 @@ var bootstrapCommand = new Command("bootstrap").description(
2338
2387
  );
2339
2388
  process.exit(1);
2340
2389
  }
2341
- if (!opts.skipLaunchAgent) {
2342
- const laSpin = p3.spinner();
2343
- laSpin.start("Writing pgsodium key file + LaunchAgent plist\u2026");
2344
- const la = await setupLaunchAgent({
2345
- pgsodiumKey,
2346
- ...tunnelToken !== void 0 ? { tunnelToken } : {},
2347
- llmModel,
2348
- embeddingModel,
2349
- postgresPassword,
2350
- jwtSecret,
2351
- supabaseAnonKey,
2352
- supabaseServiceRoleKey,
2353
- dashboardPassword,
2354
- supabaseUrl
2355
- });
2356
- if (!la.ok) {
2357
- laSpin.stop(pc2.red(`\u2717 LaunchAgent step ${la.step} failed.`));
2358
- p3.cancel(la.reason ?? "LaunchAgent setup failed.");
2359
- process.exit(1);
2360
- }
2361
- laSpin.stop(
2362
- pc2.green(
2363
- `\u2713 LaunchAgent loaded (${la.paths.plistFile})${opts.localOnly ? " \u2014 local-only mode, no TUNNEL_TOKEN set" : ""}.`
2364
- )
2365
- );
2390
+ return supabaseUrl;
2391
+ }
2392
+ async function collectSecretsPhase(args) {
2393
+ const core = await collectCoreSecrets({ region: args.region, opts: args.opts });
2394
+ const promptServiceUrl = await collectPromptServiceSecrets(args);
2395
+ const supabaseUrl = await resolveSupabaseUrl(args.opts);
2396
+ return { ...core, promptServiceUrl, supabaseUrl };
2397
+ }
2398
+ async function runLaunchAgentPhase(args) {
2399
+ const { opts, secrets, llmModel, embeddingModel } = args;
2400
+ if (opts.skipLaunchAgent) return;
2401
+ const laSpin = p3.spinner();
2402
+ laSpin.start("Writing pgsodium key file + LaunchAgent plist\u2026");
2403
+ const la = await setupLaunchAgent({
2404
+ pgsodiumKey: secrets.pgsodiumKey,
2405
+ ...secrets.tunnelToken !== void 0 ? { tunnelToken: secrets.tunnelToken } : {},
2406
+ llmModel,
2407
+ embeddingModel,
2408
+ postgresPassword: secrets.postgresPassword,
2409
+ jwtSecret: secrets.jwtSecret,
2410
+ supabaseAnonKey: secrets.supabaseAnonKey,
2411
+ supabaseServiceRoleKey: secrets.supabaseServiceRoleKey,
2412
+ dashboardPassword: secrets.dashboardPassword,
2413
+ supabaseUrl: secrets.supabaseUrl
2414
+ });
2415
+ if (!la.ok) {
2416
+ laSpin.stop(pc2.red(`\u2717 LaunchAgent step ${la.step} failed.`));
2417
+ p3.cancel(la.reason ?? "LaunchAgent setup failed.");
2418
+ process.exit(1);
2366
2419
  }
2420
+ laSpin.stop(
2421
+ pc2.green(
2422
+ `\u2713 LaunchAgent loaded (${la.paths.plistFile})${opts.localOnly ? " \u2014 local-only mode, no TUNNEL_TOKEN set" : ""}.`
2423
+ )
2424
+ );
2425
+ }
2426
+ async function installWrapperPhase(args) {
2427
+ const { repoPath, region, promptServiceUrl } = args;
2367
2428
  const wrapperSpin = p3.spinner();
2368
2429
  wrapperSpin.start("Installing bin/op-compose wrapper\u2026");
2369
2430
  const wrapper = await installOpComposeWrapper({ repoDir: repoPath, region, promptServiceUrl });
@@ -2373,6 +2434,8 @@ var bootstrapCommand = new Command("bootstrap").description(
2373
2434
  process.exit(1);
2374
2435
  }
2375
2436
  wrapperSpin.stop(pc2.green(`\u2713 Installed ${wrapper.path} (mode 0755).`));
2437
+ }
2438
+ async function loginGhcrPhase() {
2376
2439
  const ghcrSpin = p3.spinner();
2377
2440
  ghcrSpin.start("Authenticating Docker to ghcr.io\u2026");
2378
2441
  const ghcr = await loginToGhcr();
@@ -2382,45 +2445,66 @@ var bootstrapCommand = new Command("bootstrap").description(
2382
2445
  process.exit(1);
2383
2446
  }
2384
2447
  ghcrSpin.stop(pc2.green("\u2713 Logged in to ghcr.io."));
2385
- if (!opts.skipOllama) {
2386
- const olSpin = p3.spinner();
2387
- olSpin.start(`Pulling + warming Ollama models\u2026 (${estimatedPullTime(llmModel)})`);
2388
- let olHealth = await checkOllamaHealth();
2389
- if (!olHealth.reachable) {
2390
- olSpin.message("Ollama not reachable \u2014 running `brew services start ollama`\u2026");
2391
- const start = await startOllamaService();
2392
- if (!start.ok) {
2393
- olSpin.stop(pc2.red("\u2717 Couldn't start Ollama service."));
2394
- p3.cancel(
2395
- `${start.reason ?? "unknown"} \u2014 start it manually with \`brew services start ollama\` and re-run with --skip-brew --skip-launch-agent.`
2396
- );
2397
- process.exit(1);
2398
- }
2399
- await new Promise((res) => setTimeout(res, 3e3));
2400
- olHealth = await checkOllamaHealth();
2401
- if (!olHealth.reachable) {
2402
- olSpin.stop(
2403
- pc2.yellow("\u26A0 Ollama service started but daemon not yet answering on :11434.")
2404
- );
2405
- p3.cancel(
2406
- "Give it another few seconds, then re-run with --skip-brew --skip-launch-agent."
2407
- );
2408
- process.exit(1);
2409
- }
2448
+ }
2449
+ async function runOllamaPhase(args) {
2450
+ const { opts, embeddingModel, llmModel } = args;
2451
+ if (opts.skipOllama) return;
2452
+ const olSpin = p3.spinner();
2453
+ olSpin.start(`Pulling + warming Ollama models\u2026 (${estimatedPullTime(llmModel)})`);
2454
+ let olHealth = await checkOllamaHealth();
2455
+ if (!olHealth.reachable) {
2456
+ olSpin.message("Ollama not reachable \u2014 running `brew services start ollama`\u2026");
2457
+ const start = await startOllamaService();
2458
+ if (!start.ok) {
2459
+ olSpin.stop(pc2.red("\u2717 Couldn't start Ollama service."));
2460
+ p3.cancel(
2461
+ `${start.reason ?? "unknown"} \u2014 start it manually with \`brew services start ollama\` and re-run with --skip-brew --skip-launch-agent.`
2462
+ );
2463
+ process.exit(1);
2410
2464
  }
2411
- const modelReport = await setupModels([embeddingModel, llmModel], (model, status) => {
2412
- olSpin.message(`${status}: ${model}`);
2413
- });
2414
- olSpin.stop(
2415
- modelReport.failed.length === 0 ? pc2.green(
2416
- `\u2713 ${modelReport.pulled.length} pulled, ${modelReport.alreadyPresent.length} present, ${modelReport.warmed.length} warmed`
2417
- ) : pc2.yellow(`\u26A0 ${modelReport.failed.length} model pull(s) failed`)
2418
- );
2419
- const probe = await probeHostDockerInternal();
2420
- if (!probe.ok) {
2421
- p3.note(`${pc2.yellow("\u26A0")} ${probe.reason}`, "Docker host networking");
2465
+ await new Promise((res) => setTimeout(res, 3e3));
2466
+ olHealth = await checkOllamaHealth();
2467
+ if (!olHealth.reachable) {
2468
+ olSpin.stop(
2469
+ pc2.yellow("\u26A0 Ollama service started but daemon not yet answering on :11434.")
2470
+ );
2471
+ p3.cancel(
2472
+ "Give it another few seconds, then re-run with --skip-brew --skip-launch-agent."
2473
+ );
2474
+ process.exit(1);
2422
2475
  }
2423
2476
  }
2477
+ const modelReport = await setupModels([embeddingModel, llmModel], (model, status) => {
2478
+ olSpin.message(`${status}: ${model}`);
2479
+ });
2480
+ olSpin.stop(
2481
+ modelReport.failed.length === 0 ? pc2.green(
2482
+ `\u2713 ${modelReport.pulled.length} pulled, ${modelReport.alreadyPresent.length} present, ${modelReport.warmed.length} warmed`
2483
+ ) : pc2.yellow(`\u26A0 ${modelReport.failed.length} model pull(s) failed`)
2484
+ );
2485
+ const probe = await probeHostDockerInternal();
2486
+ if (!probe.ok) {
2487
+ p3.note(`${pc2.yellow("\u26A0")} ${probe.reason}`, "Docker host networking");
2488
+ }
2489
+ }
2490
+ function buildComposeEnv(args) {
2491
+ const { secrets, llmModel, embeddingModel } = args;
2492
+ return {
2493
+ PGSODIUM_ROOT_KEY: secrets.pgsodiumKey,
2494
+ POSTGRES_PASSWORD: secrets.postgresPassword,
2495
+ JWT_SECRET: secrets.jwtSecret,
2496
+ SUPABASE_ANON_KEY: secrets.supabaseAnonKey,
2497
+ SUPABASE_SERVICE_ROLE_KEY: secrets.supabaseServiceRoleKey,
2498
+ DASHBOARD_PASSWORD: secrets.dashboardPassword,
2499
+ SUPABASE_URL: secrets.supabaseUrl,
2500
+ AUTH_JWT_SECRET: process.env["AUTH_JWT_SECRET"] ?? secrets.jwtSecret,
2501
+ ...secrets.tunnelToken !== void 0 ? { TUNNEL_TOKEN: secrets.tunnelToken } : {},
2502
+ ...llmModel ? { LLM_MODEL: llmModel } : {},
2503
+ ...embeddingModel ? { EMBEDDINGS_MODEL: embeddingModel } : {}
2504
+ };
2505
+ }
2506
+ async function runStackPhase(args) {
2507
+ const { opts, repoPath, region, composeFile, secrets, llmModel, embeddingModel } = args;
2424
2508
  if (opts.skipStack) {
2425
2509
  const profileFlag = opts.localOnly ? "" : "--profile public ";
2426
2510
  p3.outro(
@@ -2431,19 +2515,7 @@ var bootstrapCommand = new Command("bootstrap").description(
2431
2515
  return;
2432
2516
  }
2433
2517
  const composeFiles = resolveComposeFiles(repoPath, composeFile);
2434
- const composeEnv = {
2435
- PGSODIUM_ROOT_KEY: pgsodiumKey,
2436
- POSTGRES_PASSWORD: postgresPassword,
2437
- JWT_SECRET: jwtSecret,
2438
- SUPABASE_ANON_KEY: supabaseAnonKey,
2439
- SUPABASE_SERVICE_ROLE_KEY: supabaseServiceRoleKey,
2440
- DASHBOARD_PASSWORD: dashboardPassword,
2441
- SUPABASE_URL: supabaseUrl,
2442
- AUTH_JWT_SECRET: process.env["AUTH_JWT_SECRET"] ?? jwtSecret,
2443
- ...tunnelToken !== void 0 ? { TUNNEL_TOKEN: tunnelToken } : {},
2444
- ...llmModel ? { LLM_MODEL: llmModel } : {},
2445
- ...embeddingModel ? { EMBEDDINGS_MODEL: embeddingModel } : {}
2446
- };
2518
+ const composeEnv = buildComposeEnv({ secrets, llmModel, embeddingModel });
2447
2519
  const composeOpts = {
2448
2520
  files: composeFiles,
2449
2521
  cwd: repoPath,
@@ -2489,9 +2561,25 @@ var bootstrapCommand = new Command("bootstrap").description(
2489
2561
  healthSpin.message(`${healthy}/${running} healthy`);
2490
2562
  }
2491
2563
  });
2564
+ healthSpin.stop(healthOutcomeHeadline(outcome));
2565
+ finishHealthOutcome(outcome, { region, opts, composeFiles });
2566
+ }
2567
+ function healthOutcomeHeadline(outcome) {
2568
+ switch (outcome.kind) {
2569
+ case "healthy":
2570
+ return pc2.green(`\u2713 All ${outcome.snapshots.length} containers healthy.`);
2571
+ case "unhealthy":
2572
+ return pc2.red(`\u2717 ${outcome.problem}`);
2573
+ case "timeout":
2574
+ return pc2.yellow("\u26A0 Timed out waiting for all containers to report healthy.");
2575
+ default:
2576
+ return assertNever();
2577
+ }
2578
+ }
2579
+ function finishHealthOutcome(outcome, args) {
2580
+ const { region, opts, composeFiles } = args;
2492
2581
  switch (outcome.kind) {
2493
2582
  case "healthy":
2494
- healthSpin.stop(pc2.green(`\u2713 All ${outcome.snapshots.length} containers healthy.`));
2495
2583
  if (opts.localOnly) {
2496
2584
  p3.outro(
2497
2585
  pc2.cyan(
@@ -2511,7 +2599,6 @@ var bootstrapCommand = new Command("bootstrap").description(
2511
2599
  }
2512
2600
  return;
2513
2601
  case "unhealthy":
2514
- healthSpin.stop(pc2.red(`\u2717 ${outcome.problem}`));
2515
2602
  p3.note(
2516
2603
  outcome.snapshots.map((s) => ` ${kvHealth(s.health)} ${s.name} (${s.state})`).join("\n"),
2517
2604
  "Container state"
@@ -2522,7 +2609,6 @@ var bootstrapCommand = new Command("bootstrap").description(
2522
2609
  process.exit(1);
2523
2610
  // eslint-disable-next-line no-fallthrough
2524
2611
  case "timeout":
2525
- healthSpin.stop(pc2.yellow("\u26A0 Timed out waiting for all containers to report healthy."));
2526
2612
  p3.note(
2527
2613
  outcome.snapshots.map((s) => ` ${kvHealth(s.health)} ${s.name} (${s.state})`).join("\n"),
2528
2614
  "Container state at timeout"
@@ -2532,11 +2618,10 @@ var bootstrapCommand = new Command("bootstrap").description(
2532
2618
  );
2533
2619
  process.exit(1);
2534
2620
  // eslint-disable-next-line no-fallthrough
2535
- default: {
2621
+ default:
2536
2622
  return;
2537
- }
2538
2623
  }
2539
- });
2624
+ }
2540
2625
  function resolveComposeFiles(repoPath, composeFile) {
2541
2626
  const inputs = composeFile ?? ["docker-compose-prod.yml"];
2542
2627
  return inputs.map((f) => f.startsWith("/") ? f : join(repoPath, f));
@@ -4492,7 +4577,7 @@ async function looksLikeRegionsRepo(dir) {
4492
4577
  }
4493
4578
 
4494
4579
  // src/cli.ts
4495
- var VERSION = "0.10.5";
4580
+ var VERSION = "0.10.6";
4496
4581
  var program = new Command();
4497
4582
  program.name("create-op-node").description(
4498
4583
  "Interactive bootstrap for an Opus Populi federation node.\nCloudflare infrastructure \u2192 Mac Studio \u2192 live public API."