@beastmode-develeap/beastmode 0.1.72 → 0.1.74

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/index.js CHANGED
@@ -6727,7 +6727,7 @@ var init_server = __esm({
6727
6727
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6728
6728
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6729
6729
  import { z as z2 } from "zod";
6730
- import { readFileSync as readFileSync27, writeFileSync as writeFileSync23, existsSync as existsSync30, readdirSync as readdirSync10, mkdirSync as mkdirSync18 } from "fs";
6730
+ import { readFileSync as readFileSync27, writeFileSync as writeFileSync23, existsSync as existsSync30, readdirSync as readdirSync11, mkdirSync as mkdirSync18 } from "fs";
6731
6731
  import { join as join28, resolve as resolve18, basename as basename5 } from "path";
6732
6732
  import { randomUUID as randomUUID3 } from "crypto";
6733
6733
  function readJsonFile2(filePath) {
@@ -6763,7 +6763,7 @@ function readFactoryStatus(factoryDir) {
6763
6763
  JSON.parse(readFileSync27(join28(bmDir, "factory.json"), "utf-8"))
6764
6764
  );
6765
6765
  const projectsDir = join28(bmDir, "projects");
6766
- const projectCount = existsSync30(projectsDir) ? readdirSync10(projectsDir).filter((f) => f.endsWith(".json")).length : 0;
6766
+ const projectCount = existsSync30(projectsDir) ? readdirSync11(projectsDir).filter((f) => f.endsWith(".json")).length : 0;
6767
6767
  const lockPath = join28(bmDir, "extensions.lock");
6768
6768
  let pluginNames = [];
6769
6769
  if (existsSync30(lockPath)) {
@@ -6791,9 +6791,9 @@ function readFactoryStatus(factoryDir) {
6791
6791
  const runsDir = join28(factoryDir, "runs");
6792
6792
  let runDirs = [];
6793
6793
  if (existsSync30(runsDir)) {
6794
- runDirs = readdirSync10(runsDir).filter((d) => {
6794
+ runDirs = readdirSync11(runsDir).filter((d) => {
6795
6795
  try {
6796
- return readdirSync10(join28(runsDir, d)).length > 0;
6796
+ return readdirSync11(join28(runsDir, d)).length > 0;
6797
6797
  } catch {
6798
6798
  return false;
6799
6799
  }
@@ -6920,7 +6920,7 @@ function createMcpServer() {
6920
6920
  if (!existsSync30(runsDir)) {
6921
6921
  return { content: [{ type: "text", text: "No runs directory found." }] };
6922
6922
  }
6923
- const runDirs = readdirSync10(runsDir).sort().reverse();
6923
+ const runDirs = readdirSync11(runsDir).sort().reverse();
6924
6924
  const activeRuns = [];
6925
6925
  for (const id of runDirs.slice(0, 10)) {
6926
6926
  const cp = readJsonFile2(join28(runsDir, id, "checkpoint.json"));
@@ -6946,7 +6946,7 @@ function createMcpServer() {
6946
6946
  const iterationsDir = join28(runDir, "iterations");
6947
6947
  const iterations = [];
6948
6948
  if (existsSync30(iterationsDir)) {
6949
- for (const dir of readdirSync10(iterationsDir).sort()) {
6949
+ for (const dir of readdirSync11(iterationsDir).sort()) {
6950
6950
  const satisfaction = readJsonFile2(join28(iterationsDir, dir, "satisfaction.json"));
6951
6951
  iterations.push({ number: parseInt(dir, 10), satisfaction });
6952
6952
  }
@@ -7036,7 +7036,7 @@ function createMcpServer() {
7036
7036
  if (!existsSync30(projectsDir)) {
7037
7037
  return { content: [{ type: "text", text: "[]" }] };
7038
7038
  }
7039
- const projects = readdirSync10(projectsDir).filter((f) => f.endsWith(".json")).map((f) => {
7039
+ const projects = readdirSync11(projectsDir).filter((f) => f.endsWith(".json")).map((f) => {
7040
7040
  try {
7041
7041
  return JSON.parse(readFileSync27(join28(projectsDir, f), "utf-8"));
7042
7042
  } catch {
@@ -8602,9 +8602,9 @@ init_schemas();
8602
8602
  init_version();
8603
8603
  init_plugin_resolver();
8604
8604
  import { Command as Command11 } from "commander";
8605
- import { existsSync as existsSync24, readFileSync as readFileSync21, readdirSync as readdirSync9 } from "fs";
8605
+ import { existsSync as existsSync24, readFileSync as readFileSync21, readdirSync as readdirSync10 } from "fs";
8606
8606
  import { join as join22, resolve as resolve15 } from "path";
8607
- import { execSync as execSync6 } from "child_process";
8607
+ import { execSync as execSync7 } from "child_process";
8608
8608
  import { homedir as homedir2, platform } from "os";
8609
8609
  import * as http3 from "http";
8610
8610
  import * as https from "https";
@@ -8612,7 +8612,8 @@ import * as https from "https";
8612
8612
  // src/cli/commands/board.ts
8613
8613
  import { Command as Command10 } from "commander";
8614
8614
  import { resolve as resolve14, join as join21 } from "path";
8615
- import { existsSync as existsSync23, readFileSync as readFileSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync17 } from "fs";
8615
+ import { existsSync as existsSync23, readFileSync as readFileSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync17, readdirSync as readdirSync9 } from "fs";
8616
+ import { execSync as execSync6 } from "child_process";
8616
8617
  var boardCommand = new Command10("board").description("Launch the BeastMode Board web UI").option("--port <number>", "Port to serve on", "7669").option("--host <host>", "Host to bind to (use 0.0.0.0 for external access)", "127.0.0.1").action(async (opts) => {
8617
8618
  try {
8618
8619
  await runBoard(opts);
@@ -8621,6 +8622,16 @@ var boardCommand = new Command10("board").description("Launch the BeastMode Boar
8621
8622
  process.exit(1);
8622
8623
  }
8623
8624
  });
8625
+ boardCommand.command("set-status <item_id> <status>").description(
8626
+ "Safely change a board item's status via the board HTTP API (never sqlite3 against the live DB \u2014 Gap 24)"
8627
+ ).option("--project <name>", "Project/board name (defaults to the factory's only project, or errors if ambiguous)").option("--board-url <url>", "Override board HTTP URL (default: use running board container via docker exec)").action(async (itemId, status, opts) => {
8628
+ try {
8629
+ await setItemStatus(itemId, status, opts);
8630
+ } catch (err) {
8631
+ error(err.message);
8632
+ process.exit(1);
8633
+ }
8634
+ });
8624
8635
  function findFactoryDir(startDir) {
8625
8636
  let dir = startDir || process.cwd();
8626
8637
  const root = resolve14("/");
@@ -8637,6 +8648,78 @@ function findFactoryDir(startDir) {
8637
8648
  }
8638
8649
  return null;
8639
8650
  }
8651
+ function inferProjectName(factoryDir) {
8652
+ const projectsDir = join21(factoryDir, ".beastmode", "projects");
8653
+ if (!existsSync23(projectsDir)) return null;
8654
+ const files = readdirSync9(projectsDir).filter((f) => f.endsWith(".json"));
8655
+ if (files.length === 1) return files[0].replace(/\.json$/, "");
8656
+ return null;
8657
+ }
8658
+ function tryExecSync(cmd, timeoutMs = 15e3) {
8659
+ try {
8660
+ return execSync6(cmd, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: timeoutMs }).trim();
8661
+ } catch {
8662
+ return null;
8663
+ }
8664
+ }
8665
+ async function setItemStatus(itemId, status, opts) {
8666
+ if (!itemId || !/^\d+$/.test(itemId)) {
8667
+ throw new Error(`Invalid item_id: ${itemId} (expected numeric id)`);
8668
+ }
8669
+ const trimmedStatus = status.trim();
8670
+ if (!trimmedStatus) {
8671
+ throw new Error("status cannot be empty");
8672
+ }
8673
+ let project = opts.project;
8674
+ if (!project) {
8675
+ const factoryDir = findFactoryDir();
8676
+ if (!factoryDir) {
8677
+ throw new Error("No .beastmode factory found in cwd tree. Pass --project <name> explicitly.");
8678
+ }
8679
+ const inferred = inferProjectName(factoryDir);
8680
+ if (!inferred) {
8681
+ throw new Error(
8682
+ "Could not determine project name from factory (0 or >1 projects configured). Pass --project <name>."
8683
+ );
8684
+ }
8685
+ project = inferred;
8686
+ }
8687
+ const payload = JSON.stringify({ status: trimmedStatus });
8688
+ if (opts.boardUrl) {
8689
+ const url2 = `${opts.boardUrl.replace(/\/+$/, "")}/api/items/${itemId}?board=${encodeURIComponent(project)}`;
8690
+ const cmd2 = `curl -sS -X PATCH '${url2}' -H 'Content-Type: application/json' -d '${payload.replace(/'/g, "'\\''")}'`;
8691
+ const out2 = tryExecSync(cmd2);
8692
+ if (!out2) throw new Error(`curl failed hitting ${url2}`);
8693
+ info(out2);
8694
+ return;
8695
+ }
8696
+ const container = tryExecSync(`docker ps --filter 'label=com.docker.compose.service=daemon' --format '{{.Names}}' | head -n1`) || tryExecSync(`docker ps --filter 'label=com.docker.compose.service=ui' --format '{{.Names}}' | head -n1`);
8697
+ if (!container) {
8698
+ throw new Error(
8699
+ "No running daemon or ui container found. Start the factory with `docker compose up -d`, or pass --board-url."
8700
+ );
8701
+ }
8702
+ const escapedPayload = payload.replace(/'/g, "'\\''");
8703
+ const url = `http://board:8080/api/items/${itemId}?board=${encodeURIComponent(project)}`;
8704
+ const cmd = `docker exec ${container} curl -sS -X PATCH '${url}' -H 'Content-Type: application/json' -d '${escapedPayload}'`;
8705
+ const out = tryExecSync(cmd);
8706
+ if (!out) {
8707
+ throw new Error(`docker exec curl failed hitting ${url} via ${container}`);
8708
+ }
8709
+ try {
8710
+ const parsed = JSON.parse(out);
8711
+ if (parsed.detail) {
8712
+ throw new Error(`Board rejected update: ${parsed.detail}`);
8713
+ }
8714
+ if (parsed.id && parsed.status) {
8715
+ info(`Item ${parsed.id} \u2192 ${parsed.status} (project=${project})`);
8716
+ return;
8717
+ }
8718
+ } catch (e) {
8719
+ if (e instanceof Error && e.message.startsWith("Board rejected update:")) throw e;
8720
+ }
8721
+ info(out);
8722
+ }
8640
8723
  async function runBoard(opts) {
8641
8724
  let factoryDir = findFactoryDir();
8642
8725
  if (!factoryDir) {
@@ -8694,7 +8777,7 @@ async function runBoard(opts) {
8694
8777
  // src/cli/commands/doctor.ts
8695
8778
  function tryExec(cmd, timeout = 8e3) {
8696
8779
  try {
8697
- return execSync6(cmd, { encoding: "utf-8", timeout, stdio: ["pipe", "pipe", "pipe"] }).trim();
8780
+ return execSync7(cmd, { encoding: "utf-8", timeout, stdio: ["pipe", "pipe", "pipe"] }).trim();
8698
8781
  } catch {
8699
8782
  return null;
8700
8783
  }
@@ -9308,6 +9391,78 @@ except Exception as e:
9308
9391
  detail: `repo=${repo || "<empty>"}, dir=${dir || "<empty>"}`
9309
9392
  };
9310
9393
  }
9394
+ function checkBoardDbIntegrity() {
9395
+ const container = tryExec(
9396
+ `docker ps --filter 'label=com.docker.compose.service=board' --format '{{.Names}}' 2>/dev/null | head -n1`
9397
+ );
9398
+ if (!container) {
9399
+ return {
9400
+ label: "Board DB integrity",
9401
+ status: "warn",
9402
+ detail: "no board container running \u2014 skipped",
9403
+ fix: "Run `docker compose up -d` in the factory directory"
9404
+ };
9405
+ }
9406
+ const pyScript = `
9407
+ import sqlite3, glob, os
9408
+ paths = sorted(glob.glob('/app/data/*.db') + glob.glob('/app/data/boards/*.db'))
9409
+ if not paths:
9410
+ print('NONE')
9411
+ else:
9412
+ for p in paths:
9413
+ try:
9414
+ c = sqlite3.connect(p)
9415
+ r = c.execute('PRAGMA integrity_check').fetchone()
9416
+ c.close()
9417
+ status = r[0] if r else 'unknown'
9418
+ except Exception as e:
9419
+ status = 'err:' + type(e).__name__
9420
+ print(os.path.relpath(p, '/app/data') + '|' + status)
9421
+ `.trim();
9422
+ const out = tryExec(
9423
+ `docker exec ${container} python3 -c "${pyScript.replace(/"/g, '\\"')}" 2>/dev/null`,
9424
+ 15e3
9425
+ );
9426
+ if (!out) {
9427
+ return {
9428
+ label: "Board DB integrity",
9429
+ status: "warn",
9430
+ detail: `could not execute python inside ${container}`
9431
+ };
9432
+ }
9433
+ if (out.trim() === "NONE") {
9434
+ return {
9435
+ label: "Board DB integrity",
9436
+ status: "warn",
9437
+ detail: "no .db files found under /app/data/ or /app/data/boards/"
9438
+ };
9439
+ }
9440
+ const lines = out.split("\n").map((l) => l.trim()).filter(Boolean);
9441
+ const bad = [];
9442
+ const good = [];
9443
+ for (const line of lines) {
9444
+ const [path, status] = line.split("|");
9445
+ if (!path || !status) continue;
9446
+ if (status === "ok") {
9447
+ good.push(path);
9448
+ } else {
9449
+ bad.push(`${path}: ${status}`);
9450
+ }
9451
+ }
9452
+ if (bad.length > 0) {
9453
+ return {
9454
+ label: "Board DB integrity",
9455
+ status: "fail",
9456
+ detail: `${bad.length} corrupted db(s): ${bad.join("; ")}`,
9457
+ fix: "Stop the factory (`docker compose down`), restore the most recent clean backup from /app/data/.gap24-backup-*, or run `sqlite3 <db> 'REINDEX;'` inside the board container. See Gap 24 in docs/zero-to-productive-readiness.md"
9458
+ };
9459
+ }
9460
+ return {
9461
+ label: "Board DB integrity",
9462
+ status: "pass",
9463
+ detail: `${good.length} db(s) ok (${good.join(", ")})`
9464
+ };
9465
+ }
9311
9466
  function checkGhcrAuth() {
9312
9467
  if (isGhcrAuthenticated()) {
9313
9468
  return {
@@ -9355,7 +9510,7 @@ function checkProjectDirectory(factoryDir) {
9355
9510
  fix: "Run: beastmode add project <path>"
9356
9511
  };
9357
9512
  }
9358
- const projectFiles = existsSync24(projectsDir) ? readdirSync9(projectsDir).filter((f) => f.endsWith(".json")) : [];
9513
+ const projectFiles = existsSync24(projectsDir) ? readdirSync10(projectsDir).filter((f) => f.endsWith(".json")) : [];
9359
9514
  if (projectFiles.length === 0) {
9360
9515
  return {
9361
9516
  label: "Project directory",
@@ -9445,7 +9600,7 @@ function checkStack(factoryDir) {
9445
9600
  if (!existsSync24(projectsDir)) {
9446
9601
  return { label: "Stack", status: "warn", detail: "no projects configured" };
9447
9602
  }
9448
- const projectFiles = readdirSync9(projectsDir).filter((f) => f.endsWith(".json"));
9603
+ const projectFiles = readdirSync10(projectsDir).filter((f) => f.endsWith(".json"));
9449
9604
  if (projectFiles.length === 0) {
9450
9605
  return { label: "Stack", status: "warn", detail: "no projects configured" };
9451
9606
  }
@@ -9557,7 +9712,7 @@ function doctorAction(factoryDir, env) {
9557
9712
  if (factoryDirExists) {
9558
9713
  const projectsDir = join22(bmDir, "projects");
9559
9714
  if (existsSync24(projectsDir)) {
9560
- for (const file of readdirSync9(projectsDir)) {
9715
+ for (const file of readdirSync10(projectsDir)) {
9561
9716
  if (file.endsWith(".json")) {
9562
9717
  try {
9563
9718
  const proj = JSON.parse(readFileSync21(join22(projectsDir, file), "utf-8"));
@@ -9578,7 +9733,7 @@ function doctorAction(factoryDir, env) {
9578
9733
  if (factoryDirExists) {
9579
9734
  const pluginsDir = join22(bmDir, "plugins");
9580
9735
  if (existsSync24(pluginsDir)) {
9581
- for (const pluginName of readdirSync9(pluginsDir)) {
9736
+ for (const pluginName of readdirSync10(pluginsDir)) {
9582
9737
  const manifestPath = join22(pluginsDir, pluginName, "manifest.json");
9583
9738
  if (existsSync24(manifestPath)) {
9584
9739
  try {
@@ -9643,6 +9798,7 @@ var doctorCommand = new Command11("doctor").description("Health check \u2014 val
9643
9798
  checks.push(await checkFactoryContainers(hasFactory ? factoryDir : null));
9644
9799
  checks.push(await checkUiServesBoard());
9645
9800
  checks.push(checkDaemonConfigLoad());
9801
+ checks.push(checkBoardDbIntegrity());
9646
9802
  checks.push(checkStack(hasFactory ? factoryDir : null));
9647
9803
  checks.push(checkPlaywright());
9648
9804
  checks.push(checkBoardPassword(env, hasFactory ? factoryDir : null));
@@ -10024,10 +10180,10 @@ async function runMigrate(opts) {
10024
10180
  let runDirs = [];
10025
10181
  const checkpoints = /* @__PURE__ */ new Map();
10026
10182
  if (existsSync27(runsDir)) {
10027
- const { readdirSync: readdirSync11 } = await import("fs");
10028
- runDirs = readdirSync11(runsDir).filter((d) => {
10183
+ const { readdirSync: readdirSync12 } = await import("fs");
10184
+ runDirs = readdirSync12(runsDir).filter((d) => {
10029
10185
  try {
10030
- return readdirSync11(join25(runsDir, d)).length > 0;
10186
+ return readdirSync12(join25(runsDir, d)).length > 0;
10031
10187
  } catch {
10032
10188
  return false;
10033
10189
  }
@@ -10045,8 +10201,8 @@ async function runMigrate(opts) {
10045
10201
  }
10046
10202
  let worktreeOutput = "";
10047
10203
  try {
10048
- const { execSync: execSync9 } = await import("child_process");
10049
- worktreeOutput = execSync9("git worktree list", {
10204
+ const { execSync: execSync10 } = await import("child_process");
10205
+ worktreeOutput = execSync10("git worktree list", {
10050
10206
  cwd,
10051
10207
  encoding: "utf-8",
10052
10208
  timeout: 5e3
@@ -10167,8 +10323,8 @@ async function runPipeline(projectName, opts) {
10167
10323
  let projectConfig = null;
10168
10324
  const projectsDir = join26(bmDir, "projects");
10169
10325
  if (existsSync28(projectsDir)) {
10170
- const { readdirSync: readdirSync11 } = await import("fs");
10171
- const projectFiles = readdirSync11(projectsDir).filter(
10326
+ const { readdirSync: readdirSync12 } = await import("fs");
10327
+ const projectFiles = readdirSync12(projectsDir).filter(
10172
10328
  (f) => f.endsWith(".json")
10173
10329
  );
10174
10330
  if (projectName) {
@@ -10217,14 +10373,14 @@ async function runPipeline(projectName, opts) {
10217
10373
  const daemonConfig = generateDaemonConfig(factoryConfig, projectConfig, factoryDir);
10218
10374
  writeFileSync21(daemonConfigPath, JSON.stringify(daemonConfig, null, 2), "utf-8");
10219
10375
  info(`Generated daemon config at: ${daemonConfigPath}`);
10220
- const { execSync: execSync9 } = await import("child_process");
10376
+ const { execSync: execSync10 } = await import("child_process");
10221
10377
  let pythonAvailable = false;
10222
10378
  try {
10223
- execSync9("python --version", { timeout: 5e3, encoding: "utf-8" });
10379
+ execSync10("python --version", { timeout: 5e3, encoding: "utf-8" });
10224
10380
  pythonAvailable = true;
10225
10381
  } catch {
10226
10382
  try {
10227
- execSync9("python3 --version", { timeout: 5e3, encoding: "utf-8" });
10383
+ execSync10("python3 --version", { timeout: 5e3, encoding: "utf-8" });
10228
10384
  pythonAvailable = true;
10229
10385
  } catch {
10230
10386
  }
@@ -10343,8 +10499,8 @@ async function runDaemon(opts) {
10343
10499
  let projectConfig = null;
10344
10500
  const projectsDir = join27(bmDir, "projects");
10345
10501
  if (existsSync29(projectsDir)) {
10346
- const { readdirSync: readdirSync11 } = await import("fs");
10347
- const projectFiles = readdirSync11(projectsDir).filter(
10502
+ const { readdirSync: readdirSync12 } = await import("fs");
10503
+ const projectFiles = readdirSync12(projectsDir).filter(
10348
10504
  (f) => f.endsWith(".json")
10349
10505
  );
10350
10506
  if (projectFiles.length > 0) {
@@ -10375,16 +10531,16 @@ async function runDaemon(opts) {
10375
10531
  console.log(JSON.stringify(daemonConfig, null, 2));
10376
10532
  return;
10377
10533
  }
10378
- const { execSync: execSync9 } = await import("child_process");
10534
+ const { execSync: execSync10 } = await import("child_process");
10379
10535
  let pythonCmd = "python";
10380
10536
  let pythonAvailable = false;
10381
10537
  try {
10382
- execSync9("python --version", { timeout: 5e3, encoding: "utf-8" });
10538
+ execSync10("python --version", { timeout: 5e3, encoding: "utf-8" });
10383
10539
  pythonAvailable = true;
10384
10540
  pythonCmd = "python";
10385
10541
  } catch {
10386
10542
  try {
10387
- execSync9("python3 --version", { timeout: 5e3, encoding: "utf-8" });
10543
+ execSync10("python3 --version", { timeout: 5e3, encoding: "utf-8" });
10388
10544
  pythonAvailable = true;
10389
10545
  pythonCmd = "python3";
10390
10546
  } catch {
@@ -10452,7 +10608,7 @@ var mcpCommand = new Command16("mcp").description("Start the BeastMode MCP serve
10452
10608
  import { Command as Command17 } from "commander";
10453
10609
  import { resolve as resolve19, join as join29 } from "path";
10454
10610
  import { existsSync as existsSync31, writeFileSync as writeFileSync24, readFileSync as readFileSync28 } from "fs";
10455
- import { execSync as execSync7 } from "child_process";
10611
+ import { execSync as execSync8 } from "child_process";
10456
10612
  import { randomBytes } from "crypto";
10457
10613
  import { fileURLToPath as fileURLToPath3 } from "url";
10458
10614
  import { dirname as dirname7 } from "path";
@@ -10474,7 +10630,7 @@ async function runDeploy(opts) {
10474
10630
  header("BeastMode Service Status");
10475
10631
  for (const name of SERVICE_NAMES) {
10476
10632
  try {
10477
- const out = execSync7(`systemctl status ${name} --no-pager 2>&1 || true`, {
10633
+ const out = execSync8(`systemctl status ${name} --no-pager 2>&1 || true`, {
10478
10634
  encoding: "utf-8",
10479
10635
  shell: "/bin/bash"
10480
10636
  });
@@ -10490,7 +10646,7 @@ async function runDeploy(opts) {
10490
10646
  for (const name of SERVICE_NAMES) {
10491
10647
  info(`Stopping ${name}...`);
10492
10648
  try {
10493
- execSync7(`sudo systemctl stop ${name}`, { stdio: "inherit" });
10649
+ execSync8(`sudo systemctl stop ${name}`, { stdio: "inherit" });
10494
10650
  success(`${name} stopped`);
10495
10651
  } catch {
10496
10652
  warn(`Could not stop ${name} (may not be running)`);
@@ -10516,12 +10672,12 @@ async function runDeploy(opts) {
10516
10672
  let nodePath;
10517
10673
  let cliPath;
10518
10674
  try {
10519
- nodePath = execSync7("which node", { encoding: "utf-8" }).trim();
10675
+ nodePath = execSync8("which node", { encoding: "utf-8" }).trim();
10520
10676
  } catch {
10521
10677
  nodePath = process.execPath;
10522
10678
  }
10523
10679
  try {
10524
- cliPath = execSync7(
10680
+ cliPath = execSync8(
10525
10681
  "readlink -f $(which beastmode) 2>/dev/null || which beastmode",
10526
10682
  { encoding: "utf-8", shell: "/bin/bash" }
10527
10683
  ).trim();
@@ -10535,7 +10691,7 @@ async function runDeploy(opts) {
10535
10691
  const daemonVenvPython = join29(factoryDir, "daemon", ".venv", "bin", "python");
10536
10692
  const boardPython = existsSync31(boardVenvPython) ? boardVenvPython : "python3";
10537
10693
  const daemonPython = existsSync31(daemonVenvPython) ? daemonVenvPython : "python3";
10538
- const user = execSync7("whoami", { encoding: "utf-8" }).trim();
10694
+ const user = execSync8("whoami", { encoding: "utf-8" }).trim();
10539
10695
  const home = process.env.HOME || `/home/${user}`;
10540
10696
  const port = opts.port;
10541
10697
  const host = opts.host;
@@ -10641,7 +10797,7 @@ BEASTMODE_UI_PASSWORD=${generated}
10641
10797
  try {
10642
10798
  const tmpPath = `/tmp/${svc.name}.service`;
10643
10799
  writeFileSync24(tmpPath, svc.content, "utf-8");
10644
- execSync7(`sudo cp ${tmpPath} ${svc.path}`, { stdio: "inherit" });
10800
+ execSync8(`sudo cp ${tmpPath} ${svc.path}`, { stdio: "inherit" });
10645
10801
  success(`${svc.name} service file installed`);
10646
10802
  } catch {
10647
10803
  error(
@@ -10652,22 +10808,22 @@ ${svc.content}`
10652
10808
  }
10653
10809
  }
10654
10810
  info("Reloading systemd...");
10655
- execSync7("sudo systemctl daemon-reload", { stdio: "inherit" });
10811
+ execSync8("sudo systemctl daemon-reload", { stdio: "inherit" });
10656
10812
  success("systemd reloaded");
10657
10813
  for (const svc of services) {
10658
10814
  info(`Enabling ${svc.name}...`);
10659
- execSync7(`sudo systemctl enable ${svc.name}`, { stdio: "inherit" });
10815
+ execSync8(`sudo systemctl enable ${svc.name}`, { stdio: "inherit" });
10660
10816
  success(`${svc.name} enabled (will start on boot)`);
10661
10817
  if (opts.start !== false) {
10662
10818
  info(`Starting ${svc.name}...`);
10663
- execSync7(`sudo systemctl restart ${svc.name}`, { stdio: "inherit" });
10819
+ execSync8(`sudo systemctl restart ${svc.name}`, { stdio: "inherit" });
10664
10820
  }
10665
10821
  }
10666
10822
  if (opts.start !== false) {
10667
10823
  await new Promise((r) => setTimeout(r, 2e3));
10668
10824
  for (const svc of services) {
10669
10825
  try {
10670
- const status = execSync7(`systemctl is-active ${svc.name}`, {
10826
+ const status = execSync8(`systemctl is-active ${svc.name}`, {
10671
10827
  encoding: "utf-8"
10672
10828
  }).trim();
10673
10829
  if (status === "active") {
@@ -10680,7 +10836,7 @@ ${svc.content}`
10680
10836
  }
10681
10837
  }
10682
10838
  try {
10683
- const ip = execSync7("hostname -I | awk '{print $1}'", {
10839
+ const ip = execSync8("hostname -I | awk '{print $1}'", {
10684
10840
  encoding: "utf-8",
10685
10841
  shell: "/bin/bash"
10686
10842
  }).trim();
@@ -10716,7 +10872,7 @@ async function deployToAWS(opts) {
10716
10872
  info(" - SSM parameter (board password)");
10717
10873
  console.log();
10718
10874
  try {
10719
- execSync7(`aws cloudformation describe-stacks --stack-name ${stackName2}`, {
10875
+ execSync8(`aws cloudformation describe-stacks --stack-name ${stackName2}`, {
10720
10876
  encoding: "utf-8",
10721
10877
  stdio: "pipe"
10722
10878
  });
@@ -10726,13 +10882,13 @@ async function deployToAWS(opts) {
10726
10882
  }
10727
10883
  info("Deleting CloudFormation stack...");
10728
10884
  try {
10729
- execSync7(`aws cloudformation delete-stack --stack-name ${stackName2}`, {
10885
+ execSync8(`aws cloudformation delete-stack --stack-name ${stackName2}`, {
10730
10886
  encoding: "utf-8",
10731
10887
  stdio: "pipe"
10732
10888
  });
10733
10889
  success("Stack deletion initiated");
10734
10890
  info("Waiting for deletion to complete (2-5 minutes)...");
10735
- execSync7(`aws cloudformation wait stack-delete-complete --stack-name ${stackName2}`, {
10891
+ execSync8(`aws cloudformation wait stack-delete-complete --stack-name ${stackName2}`, {
10736
10892
  encoding: "utf-8",
10737
10893
  stdio: "pipe",
10738
10894
  timeout: 6e5
@@ -10755,13 +10911,13 @@ async function deployToAWS(opts) {
10755
10911
  process.exit(1);
10756
10912
  }
10757
10913
  try {
10758
- execSync7("aws --version", { encoding: "utf-8" });
10914
+ execSync8("aws --version", { encoding: "utf-8" });
10759
10915
  } catch {
10760
10916
  error("AWS CLI not found. Install: https://aws.amazon.com/cli/");
10761
10917
  process.exit(1);
10762
10918
  }
10763
10919
  try {
10764
- execSync7("aws sts get-caller-identity", { encoding: "utf-8", stdio: "pipe" });
10920
+ execSync8("aws sts get-caller-identity", { encoding: "utf-8", stdio: "pipe" });
10765
10921
  success("AWS credentials valid");
10766
10922
  } catch {
10767
10923
  error("AWS credentials not configured. Run: aws configure");
@@ -10799,7 +10955,7 @@ async function deployToAWS(opts) {
10799
10955
  }
10800
10956
  let isUpdate = false;
10801
10957
  try {
10802
- execSync7(createCmd, { encoding: "utf-8", stdio: "pipe" });
10958
+ execSync8(createCmd, { encoding: "utf-8", stdio: "pipe" });
10803
10959
  success("Stack creation initiated");
10804
10960
  } catch (e) {
10805
10961
  const msg = e.stderr || e.message || "";
@@ -10808,7 +10964,7 @@ async function deployToAWS(opts) {
10808
10964
  info("Stack exists \u2014 updating...");
10809
10965
  try {
10810
10966
  const updateCmd = createCmd.replace("create-stack", "update-stack");
10811
- execSync7(updateCmd, { encoding: "utf-8", stdio: "pipe" });
10967
+ execSync8(updateCmd, { encoding: "utf-8", stdio: "pipe" });
10812
10968
  success("Stack update initiated");
10813
10969
  isUpdate = true;
10814
10970
  } catch (ue) {
@@ -10832,7 +10988,7 @@ async function deployToAWS(opts) {
10832
10988
  const waitAction = isUpdate ? "update" : "create";
10833
10989
  info(`Waiting for deployment to complete (3-10 minutes)...`);
10834
10990
  try {
10835
- execSync7(`aws cloudformation wait stack-${waitAction}-complete --stack-name ${stackName}`, {
10991
+ execSync8(`aws cloudformation wait stack-${waitAction}-complete --stack-name ${stackName}`, {
10836
10992
  encoding: "utf-8",
10837
10993
  stdio: "pipe",
10838
10994
  timeout: 9e5
@@ -10844,7 +11000,7 @@ async function deployToAWS(opts) {
10844
11000
  process.exit(1);
10845
11001
  }
10846
11002
  try {
10847
- const outputsJson = execSync7(
11003
+ const outputsJson = execSync8(
10848
11004
  `aws cloudformation describe-stacks --stack-name ${stackName} --query 'Stacks[0].Outputs' --output json`,
10849
11005
  { encoding: "utf-8", stdio: "pipe" }
10850
11006
  );
@@ -10874,7 +11030,7 @@ async function deployToAWS(opts) {
10874
11030
 
10875
11031
  // src/cli/commands/sync-claude-creds.ts
10876
11032
  import { Command as Command18 } from "commander";
10877
- import { execSync as execSync8, spawnSync as spawnSync2 } from "child_process";
11033
+ import { execSync as execSync9, spawnSync as spawnSync2 } from "child_process";
10878
11034
  import { writeFileSync as writeFileSync25, chmodSync, mkdirSync as mkdirSync19, existsSync as existsSync32, unlinkSync as unlinkSync4 } from "fs";
10879
11035
  import { join as join30 } from "path";
10880
11036
  import { homedir as homedir3, platform as platform2 } from "os";
@@ -10887,7 +11043,7 @@ function agentLogPath() {
10887
11043
  }
10888
11044
  function readKeychainToken() {
10889
11045
  try {
10890
- return execSync8(
11046
+ return execSync9(
10891
11047
  `security find-generic-password -s "Claude Code-credentials" -w`,
10892
11048
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
10893
11049
  ).trim();