@beastmode-develeap/beastmode 0.1.299 → 0.1.301
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 +236 -75
- package/dist/index.js.map +1 -1
- package/dist/web/board.html +84 -35
- package/dist/web/build-commit.txt +1 -1
- package/dist/web/build-stamp.txt +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -5432,6 +5432,116 @@ function deepMerge2(target, source) {
|
|
|
5432
5432
|
}
|
|
5433
5433
|
return result;
|
|
5434
5434
|
}
|
|
5435
|
+
function applyPresetOverrides(current, patch, presetName) {
|
|
5436
|
+
const preset = PRESETS[presetName];
|
|
5437
|
+
if (!preset) return current;
|
|
5438
|
+
const next = { ...current };
|
|
5439
|
+
const pipelineNext = { ...next.pipeline ?? {} };
|
|
5440
|
+
const patchPipeline = patch.pipeline ?? {};
|
|
5441
|
+
const explicitlySet = (key) => key in patchPipeline;
|
|
5442
|
+
if (!explicitlySet("satisfaction_threshold")) {
|
|
5443
|
+
pipelineNext.satisfaction_threshold = preset.satisfaction_threshold;
|
|
5444
|
+
}
|
|
5445
|
+
if (!explicitlySet("max_iterations")) {
|
|
5446
|
+
pipelineNext.max_iterations = preset.max_iterations;
|
|
5447
|
+
}
|
|
5448
|
+
if (!explicitlySet("stages")) {
|
|
5449
|
+
pipelineNext.stages = preset.stages;
|
|
5450
|
+
}
|
|
5451
|
+
pipelineNext.preset = presetName;
|
|
5452
|
+
next.pipeline = pipelineNext;
|
|
5453
|
+
return next;
|
|
5454
|
+
}
|
|
5455
|
+
function syncToDaemonConfigs(merged, factoryDir) {
|
|
5456
|
+
const daemonConfigPaths = [
|
|
5457
|
+
join14(factoryDir, "config", "beastmode.daemon.json"),
|
|
5458
|
+
join14(factoryDir, "config", "beastmode.docker.json")
|
|
5459
|
+
].filter(existsSync16);
|
|
5460
|
+
const pipelineToDaemonMap = {
|
|
5461
|
+
satisfaction_threshold: ["verification", "satisfaction_threshold"],
|
|
5462
|
+
prod_accept_floor: ["verification", "prod_accept_floor"],
|
|
5463
|
+
max_iterations: ["convergence", "max_iterations"],
|
|
5464
|
+
fail_fast_threshold: ["convergence", "fail_fast_threshold"],
|
|
5465
|
+
preset: ["pipeline", "preset"]
|
|
5466
|
+
// FR-1a: live preset
|
|
5467
|
+
};
|
|
5468
|
+
const resilienceToDaemonMap = {
|
|
5469
|
+
max_failures: ["retry", "max_failures"],
|
|
5470
|
+
healing_enabled: ["healing", "enabled"],
|
|
5471
|
+
healing_max_attempts: ["healing", "max_attempts"]
|
|
5472
|
+
};
|
|
5473
|
+
const daemonFields = ["max_slots", "max_projects", "poll_interval_seconds", "stale_task_hours", "runs_path"];
|
|
5474
|
+
const flatDictSections = [
|
|
5475
|
+
"models",
|
|
5476
|
+
"migration_safety",
|
|
5477
|
+
"alerts",
|
|
5478
|
+
"human_gates",
|
|
5479
|
+
"cost"
|
|
5480
|
+
];
|
|
5481
|
+
for (const daemonConfigPath of daemonConfigPaths) {
|
|
5482
|
+
try {
|
|
5483
|
+
const daemonConfig = JSON.parse(readFileSync13(daemonConfigPath, "utf-8"));
|
|
5484
|
+
let changed = false;
|
|
5485
|
+
for (const field of daemonFields) {
|
|
5486
|
+
if (field in merged && merged[field] !== daemonConfig[field]) {
|
|
5487
|
+
daemonConfig[field] = merged[field];
|
|
5488
|
+
changed = true;
|
|
5489
|
+
}
|
|
5490
|
+
}
|
|
5491
|
+
if (merged.pipeline && typeof merged.pipeline === "object") {
|
|
5492
|
+
const mergedPipeline = merged.pipeline;
|
|
5493
|
+
for (const [srcKey, [section, destKey]] of Object.entries(pipelineToDaemonMap)) {
|
|
5494
|
+
if (!(srcKey in mergedPipeline)) continue;
|
|
5495
|
+
const val = mergedPipeline[srcKey];
|
|
5496
|
+
if (val === void 0) continue;
|
|
5497
|
+
if (!daemonConfig[section] || typeof daemonConfig[section] !== "object") {
|
|
5498
|
+
daemonConfig[section] = {};
|
|
5499
|
+
}
|
|
5500
|
+
const bucket = daemonConfig[section];
|
|
5501
|
+
if (bucket[destKey] !== val) {
|
|
5502
|
+
bucket[destKey] = val;
|
|
5503
|
+
changed = true;
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
if (merged.resilience && typeof merged.resilience === "object") {
|
|
5508
|
+
const mergedResilience = merged.resilience;
|
|
5509
|
+
for (const [srcKey, [section, destKey]] of Object.entries(resilienceToDaemonMap)) {
|
|
5510
|
+
if (!(srcKey in mergedResilience)) continue;
|
|
5511
|
+
const val = mergedResilience[srcKey];
|
|
5512
|
+
if (val === void 0) continue;
|
|
5513
|
+
if (!daemonConfig[section] || typeof daemonConfig[section] !== "object") {
|
|
5514
|
+
daemonConfig[section] = {};
|
|
5515
|
+
}
|
|
5516
|
+
const bucket = daemonConfig[section];
|
|
5517
|
+
if (bucket[destKey] !== val) {
|
|
5518
|
+
bucket[destKey] = val;
|
|
5519
|
+
changed = true;
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5523
|
+
for (const section of flatDictSections) {
|
|
5524
|
+
const src = merged[section];
|
|
5525
|
+
if (src && typeof src === "object") {
|
|
5526
|
+
if (!daemonConfig[section] || typeof daemonConfig[section] !== "object") {
|
|
5527
|
+
daemonConfig[section] = {};
|
|
5528
|
+
}
|
|
5529
|
+
const bucket = daemonConfig[section];
|
|
5530
|
+
for (const [k, v] of Object.entries(src)) {
|
|
5531
|
+
if (bucket[k] !== v) {
|
|
5532
|
+
bucket[k] = v;
|
|
5533
|
+
changed = true;
|
|
5534
|
+
}
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
if (changed) {
|
|
5539
|
+
writeFileSync13(daemonConfigPath, JSON.stringify(daemonConfig, null, 2) + "\n", "utf-8");
|
|
5540
|
+
}
|
|
5541
|
+
} catch {
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
}
|
|
5435
5545
|
function getRunsDir(factoryDir) {
|
|
5436
5546
|
const configPath = join14(factoryDir, ".beastmode", "config.json");
|
|
5437
5547
|
if (existsSync16(configPath)) {
|
|
@@ -6438,8 +6548,13 @@ Path: ${projectPath}
|
|
|
6438
6548
|
// { status: "running", job_id: string, started_at: string }
|
|
6439
6549
|
// { status: "complete", analyzed_at: string, duration_seconds: number, target_repo_head_sha: string }
|
|
6440
6550
|
// { status: "cached", analyzed_at: string, duration_seconds: number, target_repo_head_sha: string }
|
|
6441
|
-
// { status: "failed", job_id?: string, error: string, started_at?: string, completed_at?: string }
|
|
6551
|
+
// { status: "failed", job_id?: string, error: string, started_at?: string, completed_at?: string, failure_reason?: string }
|
|
6442
6552
|
// { status: "idle" } — no analysis has been requested for this project
|
|
6553
|
+
//
|
|
6554
|
+
// Stuck-job recovery: when a `.analyze-in-progress.json` file is older than 15
|
|
6555
|
+
// minutes (hard timeout) or its `last_heartbeat` field is older than 90 seconds,
|
|
6556
|
+
// the file is deleted on read and the response flips to `status: "failed"` with
|
|
6557
|
+
// `failure_reason` set to `"timeout"` or `"heartbeat_timeout"` respectively.
|
|
6443
6558
|
method: "GET",
|
|
6444
6559
|
pattern: "/api/projects/:name/analyze/status",
|
|
6445
6560
|
handler: (_body, params) => {
|
|
@@ -6453,7 +6568,34 @@ Path: ${projectPath}
|
|
|
6453
6568
|
const job2 = _analyzeJobs.get(name);
|
|
6454
6569
|
try {
|
|
6455
6570
|
const p = existsSync16(triggerPath) ? triggerPath : inProgressPath;
|
|
6456
|
-
const
|
|
6571
|
+
const raw = readFileSync13(p, "utf-8");
|
|
6572
|
+
const t = JSON.parse(raw);
|
|
6573
|
+
const now = Date.now();
|
|
6574
|
+
const startedMs = new Date(t.requested_at).getTime();
|
|
6575
|
+
const HARD_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
6576
|
+
const HEARTBEAT_STALE_MS = 90 * 1e3;
|
|
6577
|
+
const hardTimedOut = now - startedMs > HARD_TIMEOUT_MS;
|
|
6578
|
+
const heartbeatStale = t.last_heartbeat ? now - new Date(t.last_heartbeat).getTime() > HEARTBEAT_STALE_MS : false;
|
|
6579
|
+
if (hardTimedOut || heartbeatStale) {
|
|
6580
|
+
try {
|
|
6581
|
+
unlinkSync4(p);
|
|
6582
|
+
} catch {
|
|
6583
|
+
}
|
|
6584
|
+
const failReason = hardTimedOut ? "timeout" : "heartbeat_timeout";
|
|
6585
|
+
_analyzeJobs.set(name, {
|
|
6586
|
+
job_id: t.job_id,
|
|
6587
|
+
project: name,
|
|
6588
|
+
status: "failed",
|
|
6589
|
+
started_at: t.requested_at,
|
|
6590
|
+
error: failReason
|
|
6591
|
+
});
|
|
6592
|
+
return {
|
|
6593
|
+
status: "failed",
|
|
6594
|
+
failure_reason: failReason,
|
|
6595
|
+
job_id: t.job_id,
|
|
6596
|
+
started_at: t.requested_at
|
|
6597
|
+
};
|
|
6598
|
+
}
|
|
6457
6599
|
if (!job2 || job2.status !== "running") {
|
|
6458
6600
|
_analyzeJobs.set(name, { job_id: t.job_id, project: name, status: "running", started_at: t.requested_at });
|
|
6459
6601
|
}
|
|
@@ -6683,6 +6825,8 @@ Path: ${projectPath}
|
|
|
6683
6825
|
handler: () => {
|
|
6684
6826
|
const configPath = join14(factoryDir, ".beastmode", "config.json");
|
|
6685
6827
|
const raw = existsSync16(configPath) ? JSON.parse(readFileSync13(configPath, "utf-8")) : generateDefaults();
|
|
6828
|
+
syncToDaemonConfigs(raw, factoryDir);
|
|
6829
|
+
delete raw.archive_s3_bucket;
|
|
6686
6830
|
return raw;
|
|
6687
6831
|
}
|
|
6688
6832
|
},
|
|
@@ -6731,72 +6875,15 @@ Path: ${projectPath}
|
|
|
6731
6875
|
}
|
|
6732
6876
|
}
|
|
6733
6877
|
}
|
|
6734
|
-
const
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
].filter(existsSync16);
|
|
6740
|
-
const pipelineToDaemonMap = {
|
|
6741
|
-
satisfaction_threshold: ["verification", "satisfaction_threshold"],
|
|
6742
|
-
prod_accept_floor: ["verification", "prod_accept_floor"],
|
|
6743
|
-
max_iterations: ["convergence", "max_iterations"],
|
|
6744
|
-
fail_fast_threshold: ["convergence", "fail_fast_threshold"]
|
|
6745
|
-
};
|
|
6746
|
-
const daemonFields = ["max_slots", "max_projects", "poll_interval_seconds", "stale_task_hours", "runs_path"];
|
|
6747
|
-
const flatDictSections = [
|
|
6748
|
-
"models",
|
|
6749
|
-
"migration_safety",
|
|
6750
|
-
"alerts",
|
|
6751
|
-
"human_gates"
|
|
6752
|
-
];
|
|
6753
|
-
for (const daemonConfigPath of daemonConfigPaths) {
|
|
6754
|
-
try {
|
|
6755
|
-
const daemonConfig = JSON.parse(readFileSync13(daemonConfigPath, "utf-8"));
|
|
6756
|
-
let changed = false;
|
|
6757
|
-
for (const field of daemonFields) {
|
|
6758
|
-
if (field in merged && merged[field] !== daemonConfig[field]) {
|
|
6759
|
-
daemonConfig[field] = merged[field];
|
|
6760
|
-
changed = true;
|
|
6761
|
-
}
|
|
6762
|
-
}
|
|
6763
|
-
if (merged.pipeline && typeof merged.pipeline === "object") {
|
|
6764
|
-
const mergedPipeline = merged.pipeline;
|
|
6765
|
-
for (const [srcKey, [section, destKey]] of Object.entries(pipelineToDaemonMap)) {
|
|
6766
|
-
if (!(srcKey in mergedPipeline)) continue;
|
|
6767
|
-
const val = mergedPipeline[srcKey];
|
|
6768
|
-
if (val === void 0) continue;
|
|
6769
|
-
if (!daemonConfig[section] || typeof daemonConfig[section] !== "object") {
|
|
6770
|
-
daemonConfig[section] = {};
|
|
6771
|
-
}
|
|
6772
|
-
const bucket = daemonConfig[section];
|
|
6773
|
-
if (bucket[destKey] !== val) {
|
|
6774
|
-
bucket[destKey] = val;
|
|
6775
|
-
changed = true;
|
|
6776
|
-
}
|
|
6777
|
-
}
|
|
6778
|
-
}
|
|
6779
|
-
for (const section of flatDictSections) {
|
|
6780
|
-
const src = merged[section];
|
|
6781
|
-
if (src && typeof src === "object") {
|
|
6782
|
-
if (!daemonConfig[section] || typeof daemonConfig[section] !== "object") {
|
|
6783
|
-
daemonConfig[section] = {};
|
|
6784
|
-
}
|
|
6785
|
-
const bucket = daemonConfig[section];
|
|
6786
|
-
for (const [k, v] of Object.entries(src)) {
|
|
6787
|
-
if (bucket[k] !== v) {
|
|
6788
|
-
bucket[k] = v;
|
|
6789
|
-
changed = true;
|
|
6790
|
-
}
|
|
6791
|
-
}
|
|
6792
|
-
}
|
|
6793
|
-
}
|
|
6794
|
-
if (changed) {
|
|
6795
|
-
writeFileSync13(daemonConfigPath, JSON.stringify(daemonConfig, null, 2) + "\n", "utf-8");
|
|
6796
|
-
}
|
|
6797
|
-
} catch {
|
|
6798
|
-
}
|
|
6878
|
+
const incomingPreset = (updatesClean.pipeline ?? {}).preset;
|
|
6879
|
+
const currentPreset = (current.pipeline ?? {}).preset;
|
|
6880
|
+
let baseline = current;
|
|
6881
|
+
if (incomingPreset && incomingPreset !== currentPreset) {
|
|
6882
|
+
baseline = applyPresetOverrides(current, updatesClean, incomingPreset);
|
|
6799
6883
|
}
|
|
6884
|
+
const merged = deepMerge2(baseline, updatesClean);
|
|
6885
|
+
writeFileSync13(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
6886
|
+
syncToDaemonConfigs(merged, factoryDir);
|
|
6800
6887
|
return merged;
|
|
6801
6888
|
}
|
|
6802
6889
|
},
|
|
@@ -9072,7 +9159,7 @@ var init_mcp = __esm({
|
|
|
9072
9159
|
});
|
|
9073
9160
|
|
|
9074
9161
|
// src/index.ts
|
|
9075
|
-
import { Command as
|
|
9162
|
+
import { Command as Command26 } from "commander";
|
|
9076
9163
|
|
|
9077
9164
|
// src/cli/commands/init.ts
|
|
9078
9165
|
init_engine();
|
|
@@ -14223,11 +14310,14 @@ import {
|
|
|
14223
14310
|
writeFileSync as writeFileSync30
|
|
14224
14311
|
} from "fs";
|
|
14225
14312
|
import { dirname as dirname9, join as join36 } from "path";
|
|
14226
|
-
var TARGET_LABEL = "[self-hosted, beastmode]";
|
|
14313
|
+
var TARGET_LABEL = "[self-hosted, self-hosted-beastmode]";
|
|
14227
14314
|
var TARGET_WORKFLOWS = [
|
|
14228
14315
|
".github/workflows/test.yml",
|
|
14229
|
-
".github/workflows/dogfood-e2e.yml"
|
|
14316
|
+
".github/workflows/dogfood-e2e.yml",
|
|
14317
|
+
".github/workflows/docker-publish.yml",
|
|
14318
|
+
".github/workflows/audit-settings.yml"
|
|
14230
14319
|
];
|
|
14320
|
+
var RUNNER_REQUIRED_LABEL = "self-hosted-beastmode";
|
|
14231
14321
|
var STATE_FILE = ".beastmode/runner-workflow-state.json";
|
|
14232
14322
|
var RUNS_ON_REGEX = /^(\s+runs-on:\s*)(.+)$/;
|
|
14233
14323
|
function replaceRunsOn(content, targetLabel) {
|
|
@@ -14260,26 +14350,48 @@ function restoreRunsOn(content, originals) {
|
|
|
14260
14350
|
});
|
|
14261
14351
|
return newLines.join("\n");
|
|
14262
14352
|
}
|
|
14353
|
+
async function assertOnlineRunnerExists() {
|
|
14354
|
+
const config = resolveGitHubConfig();
|
|
14355
|
+
const list = await listRunners(config);
|
|
14356
|
+
const matching = list.runners.filter(
|
|
14357
|
+
(r) => r.status === "online" && r.labels.some((l) => l.name === RUNNER_REQUIRED_LABEL)
|
|
14358
|
+
);
|
|
14359
|
+
if (matching.length === 0) {
|
|
14360
|
+
throw new Error(
|
|
14361
|
+
"Target label `[self-hosted, self-hosted-beastmode]` has no online runners; check `beastmode runner status`."
|
|
14362
|
+
);
|
|
14363
|
+
}
|
|
14364
|
+
}
|
|
14263
14365
|
async function switchWorkflows(projectDir) {
|
|
14264
14366
|
const statePath = join36(projectDir, STATE_FILE);
|
|
14265
14367
|
if (existsSync37(statePath)) {
|
|
14266
|
-
return { alreadySwitched: true, files: [] };
|
|
14368
|
+
return { alreadySwitched: true, files: [], skipped: [] };
|
|
14267
14369
|
}
|
|
14370
|
+
await assertOnlineRunnerExists();
|
|
14268
14371
|
const state = {
|
|
14269
14372
|
switched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14270
14373
|
target_label: TARGET_LABEL,
|
|
14271
14374
|
files: []
|
|
14272
14375
|
};
|
|
14273
14376
|
const resultFiles = [];
|
|
14377
|
+
const skipped = [];
|
|
14274
14378
|
for (const relPath of TARGET_WORKFLOWS) {
|
|
14275
14379
|
const absPath = join36(projectDir, relPath);
|
|
14276
14380
|
if (!existsSync37(absPath)) {
|
|
14277
|
-
|
|
14381
|
+
skipped.push({ relativePath: relPath, reason: "missing" });
|
|
14382
|
+
continue;
|
|
14278
14383
|
}
|
|
14279
14384
|
const content = readFileSync34(absPath, "utf-8");
|
|
14280
14385
|
const { newContent, originals } = replaceRunsOn(content, TARGET_LABEL);
|
|
14281
14386
|
if (originals.length === 0) {
|
|
14282
|
-
|
|
14387
|
+
skipped.push({ relativePath: relPath, reason: "no_runs_on" });
|
|
14388
|
+
continue;
|
|
14389
|
+
}
|
|
14390
|
+
const allAtTarget = originals.every(
|
|
14391
|
+
(o) => o.line.match(RUNS_ON_REGEX)?.[2] === TARGET_LABEL
|
|
14392
|
+
);
|
|
14393
|
+
if (allAtTarget) {
|
|
14394
|
+
skipped.push({ relativePath: relPath, reason: "already_at_target" });
|
|
14283
14395
|
}
|
|
14284
14396
|
writeFileSync30(absPath, newContent, "utf-8");
|
|
14285
14397
|
state.files.push({ relativePath: relPath, originals });
|
|
@@ -14287,7 +14399,7 @@ async function switchWorkflows(projectDir) {
|
|
|
14287
14399
|
}
|
|
14288
14400
|
mkdirSync23(dirname9(statePath), { recursive: true });
|
|
14289
14401
|
writeFileSync30(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
14290
|
-
return { alreadySwitched: false, files: resultFiles };
|
|
14402
|
+
return { alreadySwitched: false, files: resultFiles, skipped };
|
|
14291
14403
|
}
|
|
14292
14404
|
async function restoreWorkflows(projectDir) {
|
|
14293
14405
|
const statePath = join36(projectDir, STATE_FILE);
|
|
@@ -14629,6 +14741,9 @@ runnerCommand.command("switch-workflows").description("Switch workflow runs-on t
|
|
|
14629
14741
|
for (const file of result.files) {
|
|
14630
14742
|
success(`${file.relativePath}: ${file.jobCount} job(s) switched`);
|
|
14631
14743
|
}
|
|
14744
|
+
for (const skip of result.skipped) {
|
|
14745
|
+
info(`${skip.relativePath}: skipped (${skip.reason})`);
|
|
14746
|
+
}
|
|
14632
14747
|
info("State saved to .beastmode/runner-workflow-state.json");
|
|
14633
14748
|
} catch (err) {
|
|
14634
14749
|
error(err.message);
|
|
@@ -14848,9 +14963,54 @@ projectCommand.command("remove <name>").description("Archive a project").action(
|
|
|
14848
14963
|
console.log(`Project archived: ${name}`);
|
|
14849
14964
|
});
|
|
14850
14965
|
|
|
14966
|
+
// src/cli/commands/redeploy.ts
|
|
14967
|
+
init_display();
|
|
14968
|
+
init_board();
|
|
14969
|
+
import { Command as Command25 } from "commander";
|
|
14970
|
+
import { join as join39 } from "path";
|
|
14971
|
+
import { existsSync as existsSync40, writeFileSync as writeFileSync31, mkdirSync as mkdirSync25 } from "fs";
|
|
14972
|
+
var redeployCommand = new Command25("redeploy").description("Trigger a rebuild + restart of a BeastMode service").argument("<service>", "Service to redeploy (currently: daemon)").action(async (service) => {
|
|
14973
|
+
try {
|
|
14974
|
+
await runRedeploy(service);
|
|
14975
|
+
} catch (err) {
|
|
14976
|
+
error(err.message);
|
|
14977
|
+
process.exit(1);
|
|
14978
|
+
}
|
|
14979
|
+
});
|
|
14980
|
+
async function runRedeploy(service) {
|
|
14981
|
+
if (service !== "daemon") {
|
|
14982
|
+
throw new Error(
|
|
14983
|
+
`Unknown service "${service}". Currently supported: daemon`
|
|
14984
|
+
);
|
|
14985
|
+
}
|
|
14986
|
+
const factoryDir = findFactoryDir();
|
|
14987
|
+
if (!factoryDir) {
|
|
14988
|
+
throw new Error(
|
|
14989
|
+
"No BeastMode factory found. Run 'beastmode init' first."
|
|
14990
|
+
);
|
|
14991
|
+
}
|
|
14992
|
+
const markerDir = join39(factoryDir, "daemon", "logs");
|
|
14993
|
+
const markerPath = join39(markerDir, ".daemon-rebuild-requested");
|
|
14994
|
+
if (!existsSync40(markerDir)) {
|
|
14995
|
+
mkdirSync25(markerDir, { recursive: true });
|
|
14996
|
+
}
|
|
14997
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
14998
|
+
writeFileSync31(
|
|
14999
|
+
markerPath,
|
|
15000
|
+
`pr=manual-operator
|
|
15001
|
+
timestamp=${timestamp}
|
|
15002
|
+
`
|
|
15003
|
+
);
|
|
15004
|
+
header("Redeploy requested");
|
|
15005
|
+
success(`Wrote rebuild marker to ${markerPath}`);
|
|
15006
|
+
info(
|
|
15007
|
+
"The daemon-rebuilder sidecar will pick this up within ~10 seconds, wait for the daemon to idle, then rebuild + restart."
|
|
15008
|
+
);
|
|
15009
|
+
}
|
|
15010
|
+
|
|
14851
15011
|
// src/index.ts
|
|
14852
15012
|
init_version();
|
|
14853
|
-
var program = new
|
|
15013
|
+
var program = new Command26();
|
|
14854
15014
|
program.name("beastmode").description("BeastMode Dark Factory \u2014 turn intent into verified software").version(ENGINE_VERSION);
|
|
14855
15015
|
program.addCommand(initCommand);
|
|
14856
15016
|
program.addCommand(boardCommand);
|
|
@@ -14876,5 +15036,6 @@ program.addCommand(logsCommand);
|
|
|
14876
15036
|
program.addCommand(updateCommand);
|
|
14877
15037
|
program.addCommand(runnerCommand);
|
|
14878
15038
|
program.addCommand(projectCommand);
|
|
15039
|
+
program.addCommand(redeployCommand);
|
|
14879
15040
|
program.parse();
|
|
14880
15041
|
//# sourceMappingURL=index.js.map
|