@beastmode-develeap/beastmode 0.1.1 → 0.1.3
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 +232 -45
- package/dist/index.js.map +1 -1
- package/dist/web/board.html +644 -88
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ var init_schemas = __esm({
|
|
|
37
37
|
prod_verification: StageConfigSchema.default({ enabled: false })
|
|
38
38
|
}).default({}),
|
|
39
39
|
satisfaction_threshold: z.number().min(0).max(1).default(0.85),
|
|
40
|
+
prod_accept_floor: z.number().min(0).max(1).default(0.65),
|
|
40
41
|
max_iterations: z.number().int().min(1).max(20).default(5),
|
|
41
42
|
parallel_coders: z.number().int().min(1).max(10).default(2),
|
|
42
43
|
fail_fast_threshold: z.number().min(0).max(1).default(0.3)
|
|
@@ -2420,6 +2421,7 @@ function mapDaemonToFactory(daemon) {
|
|
|
2420
2421
|
const factoryRaw = {
|
|
2421
2422
|
pipeline: {
|
|
2422
2423
|
satisfaction_threshold: daemon.verification?.satisfaction_threshold ?? 0.85,
|
|
2424
|
+
prod_accept_floor: daemon.verification?.prod_accept_floor ?? 0.65,
|
|
2423
2425
|
max_iterations: daemon.convergence?.max_iterations ?? 5,
|
|
2424
2426
|
fail_fast_threshold: daemon.convergence?.fail_fast_threshold ?? 0.3
|
|
2425
2427
|
},
|
|
@@ -2594,6 +2596,13 @@ function generateMigration(factoryName, daemon, activeState) {
|
|
|
2594
2596
|
value: factory.pipeline.satisfaction_threshold
|
|
2595
2597
|
});
|
|
2596
2598
|
}
|
|
2599
|
+
if (daemon.verification?.prod_accept_floor !== void 0) {
|
|
2600
|
+
configMappings.push({
|
|
2601
|
+
from: "verification.prod_accept_floor",
|
|
2602
|
+
to: "pipeline.prod_accept_floor",
|
|
2603
|
+
value: factory.pipeline.prod_accept_floor
|
|
2604
|
+
});
|
|
2605
|
+
}
|
|
2597
2606
|
if (daemon.convergence?.max_iterations !== void 0) {
|
|
2598
2607
|
configMappings.push({
|
|
2599
2608
|
from: "convergence.max_iterations",
|
|
@@ -2714,7 +2723,7 @@ function generateDaemonConfig(factoryConfig, projectConfig, factoryPath) {
|
|
|
2714
2723
|
enabled: true,
|
|
2715
2724
|
satisfaction_threshold: pipeline.satisfaction_threshold,
|
|
2716
2725
|
timeout_minutes: 30,
|
|
2717
|
-
prod_accept_floor:
|
|
2726
|
+
prod_accept_floor: pipeline.prod_accept_floor
|
|
2718
2727
|
},
|
|
2719
2728
|
review: {
|
|
2720
2729
|
enabled: humanGates.pr_review !== "disabled",
|
|
@@ -4474,6 +4483,11 @@ import { join as join13, basename as basename3, resolve as resolve4, dirname as
|
|
|
4474
4483
|
import { homedir } from "os";
|
|
4475
4484
|
import { execSync as execSync3, spawnSync } from "child_process";
|
|
4476
4485
|
import http2 from "http";
|
|
4486
|
+
function assertSafeName(name) {
|
|
4487
|
+
if (!name || /[/\\]/.test(name) || name === "." || name === "..") {
|
|
4488
|
+
throw new Error(`Invalid name: ${name}`);
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4477
4491
|
function getBoardUrl2(factoryDir) {
|
|
4478
4492
|
if (process.env.BEASTMODE_BOARD_URL) return process.env.BEASTMODE_BOARD_URL;
|
|
4479
4493
|
const configPath = join13(factoryDir, ".beastmode", "config.json");
|
|
@@ -4562,6 +4576,69 @@ function getRunsDir(factoryDir) {
|
|
|
4562
4576
|
}
|
|
4563
4577
|
return join13(factoryDir, "runs");
|
|
4564
4578
|
}
|
|
4579
|
+
function scanStrandedRuns(runsDir, newThreshold) {
|
|
4580
|
+
if (!existsSync15(runsDir)) return [];
|
|
4581
|
+
const stranded = [];
|
|
4582
|
+
const walkRun = (runPath, projectId) => {
|
|
4583
|
+
const ckptPath = join13(runPath, "checkpoint.json");
|
|
4584
|
+
if (!existsSync15(ckptPath)) return;
|
|
4585
|
+
let ckpt;
|
|
4586
|
+
try {
|
|
4587
|
+
ckpt = JSON.parse(readFileSync12(ckptPath, "utf-8"));
|
|
4588
|
+
} catch {
|
|
4589
|
+
return;
|
|
4590
|
+
}
|
|
4591
|
+
const stage = String(ckpt.current_stage || "");
|
|
4592
|
+
if (_TERMINAL_STAGES.has(stage)) return;
|
|
4593
|
+
const bestSat = ckpt.best_satisfaction;
|
|
4594
|
+
if (typeof bestSat !== "number") return;
|
|
4595
|
+
if (bestSat >= newThreshold) return;
|
|
4596
|
+
stranded.push({
|
|
4597
|
+
run_id: String(ckpt.run_id || ""),
|
|
4598
|
+
item_id: typeof ckpt.item_id === "string" || typeof ckpt.item_id === "number" ? String(ckpt.item_id) : null,
|
|
4599
|
+
best_satisfaction: bestSat,
|
|
4600
|
+
project_id: projectId,
|
|
4601
|
+
current_stage: stage
|
|
4602
|
+
});
|
|
4603
|
+
};
|
|
4604
|
+
try {
|
|
4605
|
+
const entries = readdirSync6(runsDir);
|
|
4606
|
+
for (const entry of entries) {
|
|
4607
|
+
const entryPath = join13(runsDir, entry);
|
|
4608
|
+
let stat;
|
|
4609
|
+
try {
|
|
4610
|
+
stat = statSync5(entryPath);
|
|
4611
|
+
} catch {
|
|
4612
|
+
continue;
|
|
4613
|
+
}
|
|
4614
|
+
if (!stat.isDirectory()) continue;
|
|
4615
|
+
if (entry.startsWith("run-")) {
|
|
4616
|
+
walkRun(entryPath, null);
|
|
4617
|
+
} else if (!entry.startsWith(".")) {
|
|
4618
|
+
let subEntries;
|
|
4619
|
+
try {
|
|
4620
|
+
subEntries = readdirSync6(entryPath);
|
|
4621
|
+
} catch {
|
|
4622
|
+
continue;
|
|
4623
|
+
}
|
|
4624
|
+
for (const sub of subEntries) {
|
|
4625
|
+
if (!sub.startsWith("run-")) continue;
|
|
4626
|
+
const subPath = join13(entryPath, sub);
|
|
4627
|
+
try {
|
|
4628
|
+
if (statSync5(subPath).isDirectory()) {
|
|
4629
|
+
walkRun(subPath, entry);
|
|
4630
|
+
}
|
|
4631
|
+
} catch {
|
|
4632
|
+
continue;
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4637
|
+
} catch {
|
|
4638
|
+
return stranded;
|
|
4639
|
+
}
|
|
4640
|
+
return stranded;
|
|
4641
|
+
}
|
|
4565
4642
|
function getBoardRoutes(factoryDir) {
|
|
4566
4643
|
return [
|
|
4567
4644
|
// ── Status ──
|
|
@@ -4778,10 +4855,14 @@ function getBoardRoutes(factoryDir) {
|
|
|
4778
4855
|
const boardUrl = getBoardUrl2(factoryDir);
|
|
4779
4856
|
const bq = scopedQuery(query);
|
|
4780
4857
|
const poll = await proxyToBoard(boardUrl, "GET", "/api/poll", void 0, bq);
|
|
4858
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4781
4859
|
const allItems = [];
|
|
4782
4860
|
for (const [status, items] of Object.entries(poll)) {
|
|
4783
4861
|
if (Array.isArray(items)) {
|
|
4784
4862
|
for (const item of items) {
|
|
4863
|
+
const id = String(item.id ?? "");
|
|
4864
|
+
if (id && seen.has(id)) continue;
|
|
4865
|
+
if (id) seen.add(id);
|
|
4785
4866
|
allItems.push({ ...item, status_bucket: status });
|
|
4786
4867
|
}
|
|
4787
4868
|
}
|
|
@@ -5041,6 +5122,7 @@ function getBoardRoutes(factoryDir) {
|
|
|
5041
5122
|
pattern: "/api/projects/:name",
|
|
5042
5123
|
handler: (_body, params) => {
|
|
5043
5124
|
const { name } = params;
|
|
5125
|
+
assertSafeName(name);
|
|
5044
5126
|
const subDirPath = join13(factoryDir, ".beastmode", "projects", name, "project.json");
|
|
5045
5127
|
const flatPath = join13(factoryDir, ".beastmode", "projects", `${name}.json`);
|
|
5046
5128
|
const filePath = existsSync15(subDirPath) ? subDirPath : flatPath;
|
|
@@ -5053,6 +5135,7 @@ function getBoardRoutes(factoryDir) {
|
|
|
5053
5135
|
pattern: "/api/projects/:name/extensions",
|
|
5054
5136
|
handler: (_body, params) => {
|
|
5055
5137
|
const { name } = params;
|
|
5138
|
+
assertSafeName(name);
|
|
5056
5139
|
const extPath = join13(factoryDir, ".beastmode", "projects", name, "extensions.json");
|
|
5057
5140
|
if (!existsSync15(extPath)) {
|
|
5058
5141
|
return { plugins: { add: [], remove: [] }, mcps: { add: {}, remove: [] }, skills: { add: [], remove: [] } };
|
|
@@ -5198,6 +5281,7 @@ function getBoardRoutes(factoryDir) {
|
|
|
5198
5281
|
pattern: "/api/projects/:name",
|
|
5199
5282
|
handler: (_body, params) => {
|
|
5200
5283
|
const { name } = params;
|
|
5284
|
+
assertSafeName(name);
|
|
5201
5285
|
const projectsDir = join13(factoryDir, ".beastmode", "projects");
|
|
5202
5286
|
const subDir = join13(projectsDir, name);
|
|
5203
5287
|
const flatPath = join13(projectsDir, `${name}.json`);
|
|
@@ -5252,28 +5336,60 @@ Path: ${projConfig.path}
|
|
|
5252
5336
|
handler: () => {
|
|
5253
5337
|
const runsDir = getRunsDir(factoryDir);
|
|
5254
5338
|
if (!existsSync15(runsDir)) return { runs: [] };
|
|
5255
|
-
const
|
|
5256
|
-
|
|
5339
|
+
const runEntries = [];
|
|
5340
|
+
for (const entry of readdirSync6(runsDir)) {
|
|
5341
|
+
if (entry.startsWith(".")) continue;
|
|
5342
|
+
const entryPath = join13(runsDir, entry);
|
|
5257
5343
|
try {
|
|
5258
|
-
const
|
|
5259
|
-
|
|
5344
|
+
const children = readdirSync6(entryPath);
|
|
5345
|
+
if (entry.startsWith("run-")) {
|
|
5346
|
+
if (children.length > 0) {
|
|
5347
|
+
runEntries.push({ id: entry, dir: entryPath, projectId: "" });
|
|
5348
|
+
}
|
|
5349
|
+
} else {
|
|
5350
|
+
for (const child of children) {
|
|
5351
|
+
if (!child.startsWith("run-") || child.startsWith(".")) continue;
|
|
5352
|
+
const childPath = join13(entryPath, child);
|
|
5353
|
+
try {
|
|
5354
|
+
const grandchildren = readdirSync6(childPath);
|
|
5355
|
+
if (grandchildren.length > 0) {
|
|
5356
|
+
runEntries.push({ id: child, dir: childPath, projectId: entry });
|
|
5357
|
+
}
|
|
5358
|
+
} catch {
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
}
|
|
5260
5362
|
} catch {
|
|
5261
|
-
|
|
5363
|
+
continue;
|
|
5262
5364
|
}
|
|
5263
|
-
}
|
|
5264
|
-
|
|
5265
|
-
|
|
5365
|
+
}
|
|
5366
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
5367
|
+
for (const entry of runEntries) {
|
|
5368
|
+
const existing = deduped.get(entry.id);
|
|
5369
|
+
if (!existing || entry.projectId && !existing.projectId) {
|
|
5370
|
+
deduped.set(entry.id, entry);
|
|
5371
|
+
}
|
|
5372
|
+
}
|
|
5373
|
+
const runs = Array.from(deduped.values()).sort((a, b) => b.id.localeCompare(a.id)).map(({ id, dir, projectId }) => {
|
|
5374
|
+
const manifest = readJsonFile(join13(dir, "manifest.json"));
|
|
5375
|
+
const checkpoint = readJsonFile(join13(dir, "checkpoint.json"));
|
|
5376
|
+
const prodVerif = readJsonFile(join13(dir, "prod-verification.json"));
|
|
5266
5377
|
const manifestData = manifest;
|
|
5378
|
+
const cpData = checkpoint;
|
|
5379
|
+
const prodAcceptFloor = 0.65;
|
|
5380
|
+
const prodSat = prodVerif?.satisfaction;
|
|
5381
|
+
const prodVerified = typeof prodSat === "number" && prodSat >= prodAcceptFloor || cpData?.current_stage === "done" || cpData?.current_stage === "shipped";
|
|
5267
5382
|
return {
|
|
5268
5383
|
id,
|
|
5269
|
-
taskName: manifestData?.task_description || manifestData?.item_name || manifestData?.task_name || id,
|
|
5270
|
-
projectId: manifestData?.project_id || "",
|
|
5384
|
+
taskName: manifestData?.description || manifestData?.task_description || manifestData?.item_name || manifestData?.task_name || id,
|
|
5385
|
+
projectId: projectId || manifestData?.project_id || "",
|
|
5271
5386
|
itemId: manifestData?.item_id || manifestData?.monday_item_id || "",
|
|
5272
5387
|
taskType: manifestData?.task_type || "",
|
|
5273
5388
|
createdAt: manifestData?.created_at || "",
|
|
5274
5389
|
manifest: manifest || {},
|
|
5275
5390
|
checkpoint: checkpoint || {},
|
|
5276
|
-
pinned: isRunPinned(runsDir, id)
|
|
5391
|
+
pinned: isRunPinned(runsDir, id),
|
|
5392
|
+
prodVerified
|
|
5277
5393
|
};
|
|
5278
5394
|
});
|
|
5279
5395
|
return { runs };
|
|
@@ -5282,9 +5398,20 @@ Path: ${projConfig.path}
|
|
|
5282
5398
|
{
|
|
5283
5399
|
method: "GET",
|
|
5284
5400
|
pattern: "/api/runs/:id",
|
|
5285
|
-
handler: (_body, params) => {
|
|
5401
|
+
handler: (_body, params, query) => {
|
|
5286
5402
|
const { id } = params;
|
|
5287
|
-
const
|
|
5403
|
+
const runsDir = getRunsDir(factoryDir);
|
|
5404
|
+
let runDir = join13(runsDir, id);
|
|
5405
|
+
if (!existsSync15(runDir)) {
|
|
5406
|
+
for (const proj of readdirSync6(runsDir)) {
|
|
5407
|
+
if (proj.startsWith(".") || proj.startsWith("run-")) continue;
|
|
5408
|
+
const candidate = join13(runsDir, proj, id);
|
|
5409
|
+
if (existsSync15(candidate)) {
|
|
5410
|
+
runDir = candidate;
|
|
5411
|
+
break;
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5288
5415
|
if (!existsSync15(runDir)) throw new Error(`Run not found: ${id}`);
|
|
5289
5416
|
const manifest = readJsonFile(join13(runDir, "manifest.json"));
|
|
5290
5417
|
const checkpoint = readJsonFile(join13(runDir, "checkpoint.json"));
|
|
@@ -5305,7 +5432,7 @@ Path: ${projConfig.path}
|
|
|
5305
5432
|
const manifestData = manifest;
|
|
5306
5433
|
return {
|
|
5307
5434
|
id,
|
|
5308
|
-
taskName: manifestData?.task_description || manifestData?.item_name || manifestData?.task_name || id,
|
|
5435
|
+
taskName: manifestData?.description || manifestData?.task_description || manifestData?.item_name || manifestData?.task_name || id,
|
|
5309
5436
|
projectId: manifestData?.project_id || "",
|
|
5310
5437
|
itemId: manifestData?.item_id || manifestData?.monday_item_id || "",
|
|
5311
5438
|
taskType: manifestData?.task_type || "",
|
|
@@ -5367,15 +5494,64 @@ Path: ${projConfig.path}
|
|
|
5367
5494
|
if (!updates || typeof updates !== "object") {
|
|
5368
5495
|
throw new Error("Request body must be a JSON object");
|
|
5369
5496
|
}
|
|
5497
|
+
const force = Boolean(
|
|
5498
|
+
updates.force || updates._force
|
|
5499
|
+
);
|
|
5500
|
+
const updatesClean = { ...updates };
|
|
5501
|
+
delete updatesClean.force;
|
|
5502
|
+
delete updatesClean._force;
|
|
5370
5503
|
const configPath = join13(factoryDir, ".beastmode", "config.json");
|
|
5371
5504
|
const current = existsSync15(configPath) ? JSON.parse(readFileSync12(configPath, "utf-8")) : generateDefaults();
|
|
5372
|
-
|
|
5505
|
+
if (!force) {
|
|
5506
|
+
const oldPipeline = current.pipeline || {};
|
|
5507
|
+
const newPipeline = updatesClean.pipeline || {};
|
|
5508
|
+
const oldSat = typeof oldPipeline.satisfaction_threshold === "number" ? oldPipeline.satisfaction_threshold : null;
|
|
5509
|
+
const newSat = typeof newPipeline.satisfaction_threshold === "number" ? newPipeline.satisfaction_threshold : null;
|
|
5510
|
+
const oldFloor = typeof oldPipeline.prod_accept_floor === "number" ? oldPipeline.prod_accept_floor : null;
|
|
5511
|
+
const newFloor = typeof newPipeline.prod_accept_floor === "number" ? newPipeline.prod_accept_floor : null;
|
|
5512
|
+
const satRaised = oldSat !== null && newSat !== null && newSat > oldSat;
|
|
5513
|
+
const floorRaised = oldFloor !== null && newFloor !== null && newFloor > oldFloor;
|
|
5514
|
+
if (satRaised || floorRaised) {
|
|
5515
|
+
const candidates = [];
|
|
5516
|
+
if (satRaised && newSat !== null) candidates.push(newSat);
|
|
5517
|
+
if (floorRaised && newFloor !== null) candidates.push(newFloor);
|
|
5518
|
+
const effectiveThreshold = Math.max(...candidates);
|
|
5519
|
+
const stranded = scanStrandedRuns(
|
|
5520
|
+
getRunsDir(factoryDir),
|
|
5521
|
+
effectiveThreshold
|
|
5522
|
+
);
|
|
5523
|
+
if (stranded.length > 0) {
|
|
5524
|
+
return {
|
|
5525
|
+
warning: `${stranded.length} active run(s) cannot converge at the new threshold.`,
|
|
5526
|
+
stranded_runs: stranded,
|
|
5527
|
+
confirm_required: true,
|
|
5528
|
+
attempted_threshold: effectiveThreshold,
|
|
5529
|
+
old_threshold: satRaised ? oldSat : oldFloor
|
|
5530
|
+
};
|
|
5531
|
+
}
|
|
5532
|
+
}
|
|
5533
|
+
}
|
|
5534
|
+
const merged = deepMerge2(current, updatesClean);
|
|
5373
5535
|
writeFileSync12(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
5374
|
-
const
|
|
5375
|
-
|
|
5536
|
+
const daemonConfigPaths = [
|
|
5537
|
+
join13(factoryDir, "config", "beastmode.daemon.json"),
|
|
5538
|
+
join13(factoryDir, "config", "beastmode.docker.json")
|
|
5539
|
+
].filter(existsSync15);
|
|
5540
|
+
const pipelineToDaemonMap = {
|
|
5541
|
+
satisfaction_threshold: ["verification", "satisfaction_threshold"],
|
|
5542
|
+
prod_accept_floor: ["verification", "prod_accept_floor"],
|
|
5543
|
+
max_iterations: ["convergence", "max_iterations"],
|
|
5544
|
+
fail_fast_threshold: ["convergence", "fail_fast_threshold"]
|
|
5545
|
+
};
|
|
5546
|
+
const daemonFields = ["max_slots", "max_projects", "poll_interval_seconds", "stale_task_hours", "runs_path"];
|
|
5547
|
+
const flatDictSections = [
|
|
5548
|
+
"models",
|
|
5549
|
+
"migration_safety",
|
|
5550
|
+
"alerts"
|
|
5551
|
+
];
|
|
5552
|
+
for (const daemonConfigPath of daemonConfigPaths) {
|
|
5376
5553
|
try {
|
|
5377
5554
|
const daemonConfig = JSON.parse(readFileSync12(daemonConfigPath, "utf-8"));
|
|
5378
|
-
const daemonFields = ["max_slots", "max_projects", "poll_interval_seconds", "stale_task_hours", "runs_path"];
|
|
5379
5555
|
let changed = false;
|
|
5380
5556
|
for (const field of daemonFields) {
|
|
5381
5557
|
if (field in merged && merged[field] !== daemonConfig[field]) {
|
|
@@ -5384,38 +5560,33 @@ Path: ${projConfig.path}
|
|
|
5384
5560
|
}
|
|
5385
5561
|
}
|
|
5386
5562
|
if (merged.pipeline && typeof merged.pipeline === "object") {
|
|
5387
|
-
|
|
5388
|
-
for (const [
|
|
5389
|
-
if (
|
|
5390
|
-
|
|
5391
|
-
|
|
5563
|
+
const mergedPipeline = merged.pipeline;
|
|
5564
|
+
for (const [srcKey, [section, destKey]] of Object.entries(pipelineToDaemonMap)) {
|
|
5565
|
+
if (!(srcKey in mergedPipeline)) continue;
|
|
5566
|
+
const val = mergedPipeline[srcKey];
|
|
5567
|
+
if (val === void 0) continue;
|
|
5568
|
+
if (!daemonConfig[section] || typeof daemonConfig[section] !== "object") {
|
|
5569
|
+
daemonConfig[section] = {};
|
|
5392
5570
|
}
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
if (!daemonConfig.models) daemonConfig.models = {};
|
|
5397
|
-
for (const [k, v] of Object.entries(merged.models)) {
|
|
5398
|
-
if (daemonConfig.models[k] !== v) {
|
|
5399
|
-
daemonConfig.models[k] = v;
|
|
5571
|
+
const bucket = daemonConfig[section];
|
|
5572
|
+
if (bucket[destKey] !== val) {
|
|
5573
|
+
bucket[destKey] = val;
|
|
5400
5574
|
changed = true;
|
|
5401
5575
|
}
|
|
5402
5576
|
}
|
|
5403
5577
|
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
if (daemonConfig
|
|
5408
|
-
daemonConfig
|
|
5409
|
-
changed = true;
|
|
5578
|
+
for (const section of flatDictSections) {
|
|
5579
|
+
const src = merged[section];
|
|
5580
|
+
if (src && typeof src === "object") {
|
|
5581
|
+
if (!daemonConfig[section] || typeof daemonConfig[section] !== "object") {
|
|
5582
|
+
daemonConfig[section] = {};
|
|
5410
5583
|
}
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
daemonConfig.alerts[k] = v;
|
|
5418
|
-
changed = true;
|
|
5584
|
+
const bucket = daemonConfig[section];
|
|
5585
|
+
for (const [k, v] of Object.entries(src)) {
|
|
5586
|
+
if (bucket[k] !== v) {
|
|
5587
|
+
bucket[k] = v;
|
|
5588
|
+
changed = true;
|
|
5589
|
+
}
|
|
5419
5590
|
}
|
|
5420
5591
|
}
|
|
5421
5592
|
}
|
|
@@ -5727,12 +5898,21 @@ function matchBoardRoute(routes, method, url) {
|
|
|
5727
5898
|
}
|
|
5728
5899
|
return null;
|
|
5729
5900
|
}
|
|
5901
|
+
var _TERMINAL_STAGES;
|
|
5730
5902
|
var init_board_api_routes = __esm({
|
|
5731
5903
|
"src/cli/ui/board-api-routes.ts"() {
|
|
5732
5904
|
"use strict";
|
|
5733
5905
|
init_archival();
|
|
5734
5906
|
init_chat_handler();
|
|
5735
5907
|
init_engine();
|
|
5908
|
+
_TERMINAL_STAGES = /* @__PURE__ */ new Set([
|
|
5909
|
+
"done",
|
|
5910
|
+
"shipped",
|
|
5911
|
+
"stuck",
|
|
5912
|
+
"stuck_oscillating",
|
|
5913
|
+
"stuck_infra_gap",
|
|
5914
|
+
"ready_for_review"
|
|
5915
|
+
]);
|
|
5736
5916
|
}
|
|
5737
5917
|
});
|
|
5738
5918
|
|
|
@@ -6754,6 +6934,9 @@ function generateComposeYaml(tag) {
|
|
|
6754
6934
|
- BEASTMODE_UI_PASSWORD=\${BEASTMODE_UI_PASSWORD:-}
|
|
6755
6935
|
volumes:
|
|
6756
6936
|
- ./.beastmode:/app/.beastmode
|
|
6937
|
+
# Daemon config files. The Settings UI writes pipeline.* changes here
|
|
6938
|
+
# so they propagate to the daemon container on next restart.
|
|
6939
|
+
- ./config:/app/config
|
|
6757
6940
|
- ./runs:/app/runs
|
|
6758
6941
|
- ./daemon/logs:/app/daemon/logs:ro
|
|
6759
6942
|
- \${HOME}/.claude:/root/.claude:ro
|
|
@@ -6775,6 +6958,10 @@ function generateComposeYaml(tag) {
|
|
|
6775
6958
|
- BEASTMODE_ROOT=/app
|
|
6776
6959
|
- BEASTMODE_BOARD_URL=http://board:8080
|
|
6777
6960
|
volumes:
|
|
6961
|
+
# Daemon config files. Mounted from the host so Settings UI writes
|
|
6962
|
+
# (to beastmode.docker.json) are picked up on daemon restart without
|
|
6963
|
+
# rebuilding the image.
|
|
6964
|
+
- ./config:/app/config
|
|
6778
6965
|
- ./runs:/app/runs
|
|
6779
6966
|
- ./daemon/logs:/app/daemon/logs
|
|
6780
6967
|
- /var/run/docker.sock:/var/run/docker.sock
|