@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 +209 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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) ?
|
|
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 =
|
|
6794
|
+
runDirs = readdirSync11(runsDir).filter((d) => {
|
|
6795
6795
|
try {
|
|
6796
|
-
return
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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) ?
|
|
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 =
|
|
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
|
|
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
|
|
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:
|
|
10028
|
-
runDirs =
|
|
10183
|
+
const { readdirSync: readdirSync12 } = await import("fs");
|
|
10184
|
+
runDirs = readdirSync12(runsDir).filter((d) => {
|
|
10029
10185
|
try {
|
|
10030
|
-
return
|
|
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:
|
|
10049
|
-
worktreeOutput =
|
|
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:
|
|
10171
|
-
const projectFiles =
|
|
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:
|
|
10376
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10221
10377
|
let pythonAvailable = false;
|
|
10222
10378
|
try {
|
|
10223
|
-
|
|
10379
|
+
execSync10("python --version", { timeout: 5e3, encoding: "utf-8" });
|
|
10224
10380
|
pythonAvailable = true;
|
|
10225
10381
|
} catch {
|
|
10226
10382
|
try {
|
|
10227
|
-
|
|
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:
|
|
10347
|
-
const projectFiles =
|
|
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:
|
|
10534
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10379
10535
|
let pythonCmd = "python";
|
|
10380
10536
|
let pythonAvailable = false;
|
|
10381
10537
|
try {
|
|
10382
|
-
|
|
10538
|
+
execSync10("python --version", { timeout: 5e3, encoding: "utf-8" });
|
|
10383
10539
|
pythonAvailable = true;
|
|
10384
10540
|
pythonCmd = "python";
|
|
10385
10541
|
} catch {
|
|
10386
10542
|
try {
|
|
10387
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
10675
|
+
nodePath = execSync8("which node", { encoding: "utf-8" }).trim();
|
|
10520
10676
|
} catch {
|
|
10521
10677
|
nodePath = process.execPath;
|
|
10522
10678
|
}
|
|
10523
10679
|
try {
|
|
10524
|
-
cliPath =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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();
|