substrate-ai 0.19.28 → 0.19.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +169 -25
- package/dist/{health-CVfyC7j0.js → health-Bb5KxPlE.js} +1 -1
- package/dist/{health-M0iCuP26.js → health-DKallkoo.js} +1218 -22
- package/dist/{run-DabSV2xH.js → run-B0Pe5kol.js} +2502 -2651
- package/dist/{run-CjwCYY8Q.js → run-BIVex2-V.js} +2 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { FileStateStore, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot } from "../health-
|
|
2
|
+
import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest } from "../health-DKallkoo.js";
|
|
3
3
|
import { createLogger } from "../logger-KeHncl-f.js";
|
|
4
4
|
import { createEventBus } from "../helpers-CElYrONe.js";
|
|
5
5
|
import { AdapterRegistry, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, ConfigError, CostTrackerConfigSchema, DEFAULT_CONFIG, DoltClient, DoltNotInstalled, GlobalSettingsSchema, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, createDoltClient, createPipelineRun, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, tagRunAsBaseline, updatePipelineRun } from "../dist-R0W4ofKv.js";
|
|
6
6
|
import "../adapter-registry-DXLMTmfD.js";
|
|
7
|
-
import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-
|
|
7
|
+
import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-B0Pe5kol.js";
|
|
8
8
|
import "../errors-BJRMJyGb.js";
|
|
9
9
|
import "../routing-CcBOCuC9.js";
|
|
10
10
|
import "../decisions-C0pz9Clx.js";
|
|
@@ -21,6 +21,7 @@ import * as path$3 from "node:path";
|
|
|
21
21
|
import * as path$2 from "node:path";
|
|
22
22
|
import * as path$1 from "node:path";
|
|
23
23
|
import { join as join$1 } from "node:path";
|
|
24
|
+
import { randomUUID } from "node:crypto";
|
|
24
25
|
import { z } from "zod";
|
|
25
26
|
import * as fs from "node:fs/promises";
|
|
26
27
|
import { access as access$1, readFile as readFile$1, readdir as readdir$1 } from "node:fs/promises";
|
|
@@ -29,7 +30,7 @@ import { homedir } from "os";
|
|
|
29
30
|
import { createRequire } from "node:module";
|
|
30
31
|
import { fileURLToPath as fileURLToPath$1 } from "node:url";
|
|
31
32
|
import { createInterface } from "node:readline";
|
|
32
|
-
import { randomUUID } from "crypto";
|
|
33
|
+
import { randomUUID as randomUUID$1 } from "crypto";
|
|
33
34
|
import { createInterface as createInterface$1 } from "readline";
|
|
34
35
|
|
|
35
36
|
//#region packages/core/dist/git/git-utils.js
|
|
@@ -2761,6 +2762,23 @@ async function runResumeAction(options) {
|
|
|
2761
2762
|
if (Array.isArray(config.explicitStories) && config.explicitStories.length > 0) scopedStories = config.explicitStories;
|
|
2762
2763
|
} catch {}
|
|
2763
2764
|
const dbDir = dbPath.replace("/substrate.db", "");
|
|
2765
|
+
if (options.stories === void 0 || options.stories.length === 0) {
|
|
2766
|
+
const { manifest: resolvedManifest } = await resolveRunManifest(dbRoot, runId);
|
|
2767
|
+
if (resolvedManifest !== null) try {
|
|
2768
|
+
const manifestData = await resolvedManifest.read();
|
|
2769
|
+
const manifestStories = manifestData.cli_flags["stories"] ?? manifestData.story_scope;
|
|
2770
|
+
if (Array.isArray(manifestStories) && manifestStories.length > 0) {
|
|
2771
|
+
scopedStories = manifestStories;
|
|
2772
|
+
logger$13.debug({
|
|
2773
|
+
runId,
|
|
2774
|
+
stories: scopedStories
|
|
2775
|
+
}, "resume scope loaded from manifest");
|
|
2776
|
+
}
|
|
2777
|
+
} catch {
|
|
2778
|
+
logger$13.debug({ runId }, "manifest read failed in resume — using legacy config_json scope");
|
|
2779
|
+
}
|
|
2780
|
+
else logger$13.debug({ runId }, "Run manifest not found for scope preservation — using legacy config_json scope");
|
|
2781
|
+
}
|
|
2764
2782
|
return runFullPipelineFromPhase({
|
|
2765
2783
|
packName,
|
|
2766
2784
|
packPath,
|
|
@@ -3134,6 +3152,55 @@ function registerResumeCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
3134
3152
|
//#endregion
|
|
3135
3153
|
//#region src/cli/commands/status.ts
|
|
3136
3154
|
const logger$12 = createLogger("status-cmd");
|
|
3155
|
+
/**
|
|
3156
|
+
* Map a manifest per-story status string to the appropriate WorkGraphCounts bucket.
|
|
3157
|
+
* Unknown strings are treated as `inProgress` (safe default).
|
|
3158
|
+
*/
|
|
3159
|
+
function manifestStatusToWorkGraphBucket(status) {
|
|
3160
|
+
switch (status) {
|
|
3161
|
+
case "complete": return "complete";
|
|
3162
|
+
case "escalated": return "escalated";
|
|
3163
|
+
case "failed":
|
|
3164
|
+
case "verification-failed": return "failed";
|
|
3165
|
+
case "dispatched":
|
|
3166
|
+
case "in-review":
|
|
3167
|
+
case "recovered": return "inProgress";
|
|
3168
|
+
case "gated":
|
|
3169
|
+
case "pending": return "ready";
|
|
3170
|
+
default: return "inProgress";
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
/**
|
|
3174
|
+
* Build a WorkGraphSummary from manifest `per_story_state`.
|
|
3175
|
+
* readyStories and blockedStories are left empty — manifest does not carry
|
|
3176
|
+
* dependency-graph detail (only status counts).
|
|
3177
|
+
*/
|
|
3178
|
+
function buildWorkGraphFromManifest(perStoryState) {
|
|
3179
|
+
const counts = {
|
|
3180
|
+
ready: 0,
|
|
3181
|
+
blocked: 0,
|
|
3182
|
+
inProgress: 0,
|
|
3183
|
+
complete: 0,
|
|
3184
|
+
escalated: 0,
|
|
3185
|
+
failed: 0
|
|
3186
|
+
};
|
|
3187
|
+
for (const entry of Object.values(perStoryState)) {
|
|
3188
|
+
const bucket = manifestStatusToWorkGraphBucket(entry.status);
|
|
3189
|
+
counts[bucket]++;
|
|
3190
|
+
}
|
|
3191
|
+
return {
|
|
3192
|
+
summary: {
|
|
3193
|
+
ready: counts.ready,
|
|
3194
|
+
blocked: counts.blocked,
|
|
3195
|
+
inProgress: counts.inProgress,
|
|
3196
|
+
complete: counts.complete,
|
|
3197
|
+
escalated: counts.escalated,
|
|
3198
|
+
failed: counts.failed
|
|
3199
|
+
},
|
|
3200
|
+
readyStories: [],
|
|
3201
|
+
blockedStories: []
|
|
3202
|
+
};
|
|
3203
|
+
}
|
|
3137
3204
|
async function runStatusAction(options) {
|
|
3138
3205
|
const { outputFormat, runId, projectRoot, stateStore, history } = options;
|
|
3139
3206
|
if (history === true) {
|
|
@@ -3176,8 +3243,29 @@ async function runStatusAction(options) {
|
|
|
3176
3243
|
});
|
|
3177
3244
|
try {
|
|
3178
3245
|
await initSchema(adapter);
|
|
3246
|
+
let run;
|
|
3247
|
+
if (runId !== void 0 && runId !== "") run = await getPipelineRunById(adapter, runId);
|
|
3248
|
+
else {
|
|
3249
|
+
let currentRunId;
|
|
3250
|
+
try {
|
|
3251
|
+
const currentRunIdPath = join(dbRoot, ".substrate", "current-run-id");
|
|
3252
|
+
const content = readFileSync$1(currentRunIdPath, "utf-8").trim();
|
|
3253
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3254
|
+
if (UUID_RE.test(content)) currentRunId = content;
|
|
3255
|
+
} catch {}
|
|
3256
|
+
if (currentRunId !== void 0) run = await getPipelineRunById(adapter, currentRunId);
|
|
3257
|
+
if (run === void 0) run = await getLatestRun(adapter);
|
|
3258
|
+
}
|
|
3179
3259
|
let workGraph;
|
|
3180
|
-
|
|
3260
|
+
const { manifest: resolvedManifest } = await resolveRunManifest(dbRoot, run?.id);
|
|
3261
|
+
if (resolvedManifest !== null) try {
|
|
3262
|
+
const manifestData = await resolvedManifest.read();
|
|
3263
|
+
workGraph = buildWorkGraphFromManifest(manifestData.per_story_state);
|
|
3264
|
+
logger$12.debug({ runId: run?.id }, "status: workGraph built from manifest per_story_state");
|
|
3265
|
+
} catch {
|
|
3266
|
+
logger$12.debug({ runId: run?.id }, "status: manifest read failed — falling back to wg_stories");
|
|
3267
|
+
}
|
|
3268
|
+
if (workGraph === void 0) try {
|
|
3181
3269
|
const wgRepo = new WorkGraphRepository(adapter);
|
|
3182
3270
|
const allStories = await adapter.query(`SELECT story_key, title, status FROM wg_stories`);
|
|
3183
3271
|
if (allStories.length > 0) {
|
|
@@ -3214,21 +3302,8 @@ async function runStatusAction(options) {
|
|
|
3214
3302
|
} catch (err) {
|
|
3215
3303
|
logger$12.debug({ err }, "Work graph query failed, continuing without work graph data");
|
|
3216
3304
|
}
|
|
3217
|
-
let run;
|
|
3218
|
-
if (runId !== void 0 && runId !== "") run = await getPipelineRunById(adapter, runId);
|
|
3219
|
-
else {
|
|
3220
|
-
let currentRunId;
|
|
3221
|
-
try {
|
|
3222
|
-
const currentRunIdPath = join(dbRoot, ".substrate", "current-run-id");
|
|
3223
|
-
const content = readFileSync$1(currentRunIdPath, "utf-8").trim();
|
|
3224
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3225
|
-
if (UUID_RE.test(content)) currentRunId = content;
|
|
3226
|
-
} catch {}
|
|
3227
|
-
if (currentRunId !== void 0) run = await getPipelineRunById(adapter, currentRunId);
|
|
3228
|
-
if (run === void 0) run = await getLatestRun(adapter);
|
|
3229
|
-
}
|
|
3230
3305
|
if (run === void 0) {
|
|
3231
|
-
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-
|
|
3306
|
+
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-Bb5KxPlE.js");
|
|
3232
3307
|
const substrateDirPath = join(projectRoot, ".substrate");
|
|
3233
3308
|
const processInfo = inspectProcessTree$1({
|
|
3234
3309
|
projectRoot,
|
|
@@ -3822,7 +3897,7 @@ async function runAmendAction(options) {
|
|
|
3822
3897
|
}
|
|
3823
3898
|
parentRunId = latestCompleted.id;
|
|
3824
3899
|
}
|
|
3825
|
-
const amendmentRunId = randomUUID();
|
|
3900
|
+
const amendmentRunId = randomUUID$1();
|
|
3826
3901
|
let methodology = packName;
|
|
3827
3902
|
try {
|
|
3828
3903
|
const packLoader$1 = createPackLoader();
|
|
@@ -4044,6 +4119,7 @@ function registerAmendCommand(program, _version = "0.0.0", projectRoot = process
|
|
|
4044
4119
|
|
|
4045
4120
|
//#endregion
|
|
4046
4121
|
//#region src/cli/commands/supervisor.ts
|
|
4122
|
+
const supervisorLogger = createLogger("supervisor-cmd");
|
|
4047
4123
|
function defaultSupervisorDeps() {
|
|
4048
4124
|
return {
|
|
4049
4125
|
getHealth: getAutoHealthData,
|
|
@@ -4370,7 +4446,10 @@ async function handleStallRecovery(health, state, config, deps, io) {
|
|
|
4370
4446
|
};
|
|
4371
4447
|
}
|
|
4372
4448
|
const newRestartCount = state.restartCount + 1;
|
|
4373
|
-
if (health.run_id !== null)
|
|
4449
|
+
if (health.run_id !== null) {
|
|
4450
|
+
await incrementRestarts(health.run_id, projectRoot);
|
|
4451
|
+
RunManifest.open(health.run_id, join(projectRoot, ".substrate", "runs")).update({ restart_count: newRestartCount }).catch(() => {});
|
|
4452
|
+
}
|
|
4374
4453
|
emitEvent({
|
|
4375
4454
|
type: "supervisor:restart",
|
|
4376
4455
|
run_id: health.run_id,
|
|
@@ -4379,7 +4458,13 @@ async function handleStallRecovery(health, state, config, deps, io) {
|
|
|
4379
4458
|
log(`Supervisor: Restarting pipeline (attempt ${newRestartCount}/${maxRestarts})`);
|
|
4380
4459
|
try {
|
|
4381
4460
|
let scopedStories;
|
|
4382
|
-
if (
|
|
4461
|
+
if (health.run_id !== null) try {
|
|
4462
|
+
const manifest = RunManifest.open(health.run_id, projectRoot);
|
|
4463
|
+
const data = await manifest.read();
|
|
4464
|
+
const manifestStories = data?.cli_flags?.stories;
|
|
4465
|
+
if (Array.isArray(manifestStories) && manifestStories.length > 0) scopedStories = manifestStories;
|
|
4466
|
+
} catch {}
|
|
4467
|
+
if (scopedStories === void 0 && deps.getRunConfig !== void 0 && health.run_id !== null) try {
|
|
4383
4468
|
const runConfig = await deps.getRunConfig(health.run_id, projectRoot);
|
|
4384
4469
|
if (runConfig?.explicitStories !== void 0 && runConfig.explicitStories.length > 0) scopedStories = runConfig.explicitStories;
|
|
4385
4470
|
} catch {}
|
|
@@ -4442,7 +4527,7 @@ async function handleStallRecovery(health, state, config, deps, io) {
|
|
|
4442
4527
|
* 2 — max restarts exceeded (safety valve triggered)
|
|
4443
4528
|
*/
|
|
4444
4529
|
async function runSupervisorAction(options, deps = {}) {
|
|
4445
|
-
const { pollInterval, stallThreshold, maxRestarts, outputFormat, projectRoot, runId, pack, experiment, maxExperiments } = options;
|
|
4530
|
+
const { pollInterval, stallThreshold, maxRestarts, outputFormat, projectRoot, runId, pack, experiment, maxExperiments, force } = options;
|
|
4446
4531
|
const resolvedDeps = {
|
|
4447
4532
|
...defaultSupervisorDeps(),
|
|
4448
4533
|
...deps
|
|
@@ -4455,6 +4540,62 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
4455
4540
|
};
|
|
4456
4541
|
let maxRestartsExhausted = false;
|
|
4457
4542
|
const startTime = Date.now();
|
|
4543
|
+
const sessionId = randomUUID();
|
|
4544
|
+
let supervisorLock = null;
|
|
4545
|
+
/** Track whether process exit handlers have been registered for this supervisor. */
|
|
4546
|
+
let exitHandlersRegistered = false;
|
|
4547
|
+
/**
|
|
4548
|
+
* Register process.once exit handlers to release the lock on exit.
|
|
4549
|
+
* Called exactly once, after the first successful lock acquisition.
|
|
4550
|
+
* Using process.once (not process.on) per Story 52-2 spec.
|
|
4551
|
+
*/
|
|
4552
|
+
function registerExitHandlers(lock) {
|
|
4553
|
+
if (exitHandlersRegistered) return;
|
|
4554
|
+
exitHandlersRegistered = true;
|
|
4555
|
+
process.once("exit", () => {
|
|
4556
|
+
lock.release().catch((e) => {
|
|
4557
|
+
supervisorLogger.debug({ error: e }, "lock release on exit failed");
|
|
4558
|
+
});
|
|
4559
|
+
});
|
|
4560
|
+
process.once("SIGTERM", () => {
|
|
4561
|
+
lock.release().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
4562
|
+
});
|
|
4563
|
+
process.once("SIGINT", () => {
|
|
4564
|
+
lock.release().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
4565
|
+
});
|
|
4566
|
+
}
|
|
4567
|
+
/**
|
|
4568
|
+
* Acquire the supervisor lock for a given run ID.
|
|
4569
|
+
* Non-fatal: logs and continues on failure so the supervisor can still
|
|
4570
|
+
* function in degraded mode without blocking the pipeline.
|
|
4571
|
+
*/
|
|
4572
|
+
async function acquireLockForRun(targetRunId) {
|
|
4573
|
+
if (supervisorLock !== null) return;
|
|
4574
|
+
try {
|
|
4575
|
+
const runsDir = join(projectRoot, ".substrate", "runs");
|
|
4576
|
+
const manifest = RunManifest.open(targetRunId, runsDir);
|
|
4577
|
+
const lock = new SupervisorLock(targetRunId, manifest, supervisorLogger);
|
|
4578
|
+
await lock.acquire(process.pid, sessionId, { force: force ?? false });
|
|
4579
|
+
supervisorLock = lock;
|
|
4580
|
+
supervisorLogger.debug({ runId: targetRunId }, "Supervisor lock acquired");
|
|
4581
|
+
registerExitHandlers(lock);
|
|
4582
|
+
} catch (lockErr) {
|
|
4583
|
+
const msg = lockErr instanceof Error ? lockErr.message : String(lockErr);
|
|
4584
|
+
supervisorLogger.warn({
|
|
4585
|
+
runId: targetRunId,
|
|
4586
|
+
error: msg
|
|
4587
|
+
}, "Supervisor lock acquisition failed");
|
|
4588
|
+
if (outputFormat === "json") process.stdout.write(JSON.stringify({
|
|
4589
|
+
type: "supervisor:lock-failed",
|
|
4590
|
+
run_id: targetRunId,
|
|
4591
|
+
reason: msg,
|
|
4592
|
+
ts: new Date().toISOString()
|
|
4593
|
+
}) + "\n");
|
|
4594
|
+
else process.stderr.write(`Warning: Supervisor lock acquisition failed: ${msg}\n`);
|
|
4595
|
+
if (msg.includes("is already supervised by PID") && !force) throw lockErr;
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
if (runId !== void 0) await acquireLockForRun(runId);
|
|
4458
4599
|
function emitEvent(event) {
|
|
4459
4600
|
if (outputFormat === "json") {
|
|
4460
4601
|
const stamped = {
|
|
@@ -4479,6 +4620,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
4479
4620
|
runId: health.run_id
|
|
4480
4621
|
};
|
|
4481
4622
|
log(`Supervisor: auto-bound to active run ${health.run_id}`);
|
|
4623
|
+
await acquireLockForRun(health.run_id);
|
|
4482
4624
|
}
|
|
4483
4625
|
if (outputFormat === "json") {
|
|
4484
4626
|
const tokenSnapshot = health.run_id !== null ? await getTokenSnapshot(health.run_id, projectRoot) : {
|
|
@@ -4571,7 +4713,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
4571
4713
|
await initSchema(expAdapter);
|
|
4572
4714
|
const { runRunAction: runPipeline } = await import(
|
|
4573
4715
|
/* @vite-ignore */
|
|
4574
|
-
"../run-
|
|
4716
|
+
"../run-BIVex2-V.js"
|
|
4575
4717
|
);
|
|
4576
4718
|
const runStoryFn = async (opts) => {
|
|
4577
4719
|
const exitCode = await runPipeline({
|
|
@@ -4780,7 +4922,7 @@ async function runMultiProjectSupervisor(options, deps = {}) {
|
|
|
4780
4922
|
}
|
|
4781
4923
|
}
|
|
4782
4924
|
function registerSupervisorCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
|
|
4783
|
-
program.command("supervisor").description("Monitor a pipeline run and automatically recover from stalls").option("--poll-interval <seconds>", "Health poll interval in seconds", (v) => parseInt(v, 10), 60).option("--stall-threshold <seconds>", "Staleness in seconds before killing a stalled pipeline", (v) => parseInt(v, 10), 600).option("--max-restarts <n>", "Maximum automatic restarts before aborting", (v) => parseInt(v, 10), 3).option("--run-id <id>", "Pipeline run ID to monitor (defaults to latest)").option("--pack <name>", "Methodology pack name", "bmad").option("--project-root <path>", "Project root directory", projectRoot).option("--projects <paths>", "Comma-separated project root directories to monitor (multi-project mode)").option("--output-format <format>", "Output format: human (default) or json", "human").option("--experiment", "After post-run analysis, enter experiment mode: create branches, apply modifications, run single-story experiments, and report verdicts (Story 17-4)", false).option("--max-experiments <n>", "Maximum number of experiments to run per analysis cycle (default: 2, Story 17-4 AC6)", (v) => parseInt(v, 10), 2).action(async (opts) => {
|
|
4925
|
+
program.command("supervisor").description("Monitor a pipeline run and automatically recover from stalls").option("--poll-interval <seconds>", "Health poll interval in seconds", (v) => parseInt(v, 10), 60).option("--stall-threshold <seconds>", "Staleness in seconds before killing a stalled pipeline", (v) => parseInt(v, 10), 600).option("--max-restarts <n>", "Maximum automatic restarts before aborting", (v) => parseInt(v, 10), 3).option("--run-id <id>", "Pipeline run ID to monitor (defaults to latest)").option("--pack <name>", "Methodology pack name", "bmad").option("--project-root <path>", "Project root directory", projectRoot).option("--projects <paths>", "Comma-separated project root directories to monitor (multi-project mode)").option("--output-format <format>", "Output format: human (default) or json", "human").option("--experiment", "After post-run analysis, enter experiment mode: create branches, apply modifications, run single-story experiments, and report verdicts (Story 17-4)", false).option("--max-experiments <n>", "Maximum number of experiments to run per analysis cycle (default: 2, Story 17-4 AC6)", (v) => parseInt(v, 10), 2).option("--force", "Forcefully evict an existing supervisor process (SIGTERM + 500ms) before attaching (Story 52-2)", false).action(async (opts) => {
|
|
4784
4926
|
const outputFormat = opts.outputFormat === "json" ? "json" : "human";
|
|
4785
4927
|
if (opts.stallThreshold < 120) console.warn(`Warning: --stall-threshold ${opts.stallThreshold}s is below 120s. Agent steps typically take 45-90s. This may cause false stall detections and wasted restarts.`);
|
|
4786
4928
|
if (opts.projects) {
|
|
@@ -4811,7 +4953,8 @@ function registerSupervisorCommand(program, _version = "0.0.0", projectRoot = pr
|
|
|
4811
4953
|
outputFormat,
|
|
4812
4954
|
projectRoot: opts.projectRoot,
|
|
4813
4955
|
experiment: opts.experiment,
|
|
4814
|
-
maxExperiments: opts.maxExperiments
|
|
4956
|
+
maxExperiments: opts.maxExperiments,
|
|
4957
|
+
force: opts.force
|
|
4815
4958
|
});
|
|
4816
4959
|
process.exitCode = exitCode;
|
|
4817
4960
|
});
|
|
@@ -7504,6 +7647,7 @@ async function runCancelAction(options) {
|
|
|
7504
7647
|
const runningRuns = await getRunningPipelineRuns(adapter);
|
|
7505
7648
|
for (const run of runningRuns) {
|
|
7506
7649
|
await updatePipelineRun(adapter, run.id, { status: "stopped" });
|
|
7650
|
+
RunManifest.open(run.id, join(dbRoot, "runs")).update({ run_status: "stopped" }).catch(() => {});
|
|
7507
7651
|
if (outputFormat === "human") process.stdout.write(`Marked pipeline run ${run.id} as stopped.\n`);
|
|
7508
7652
|
}
|
|
7509
7653
|
} finally {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-
|
|
1
|
+
import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-DKallkoo.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./dist-R0W4ofKv.js";
|
|
4
4
|
import "./decisions-C0pz9Clx.js";
|