@ai-dossier/cli 0.92.1 → 0.93.0-next.714.1
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/README.md +21 -2
- package/dist/batch-suite-runner.d.ts +2 -0
- package/dist/batch-suite-runner.d.ts.map +1 -1
- package/dist/batch-suite-runner.js +4 -3
- package/dist/batch-suite-runner.js.map +1 -1
- package/dist/cap-envelope.d.ts +18 -1
- package/dist/cap-envelope.d.ts.map +1 -1
- package/dist/cap-envelope.js +28 -0
- package/dist/cap-envelope.js.map +1 -1
- package/dist/cap-run-tracked.d.ts +74 -0
- package/dist/cap-run-tracked.d.ts.map +1 -0
- package/dist/cap-run-tracked.js +371 -0
- package/dist/cap-run-tracked.js.map +1 -0
- package/dist/commands/sched-service.d.ts +15 -0
- package/dist/commands/sched-service.d.ts.map +1 -1
- package/dist/commands/sched-service.js +10 -4
- package/dist/commands/sched-service.js.map +1 -1
- package/dist/commands/sched.d.ts +16 -1
- package/dist/commands/sched.d.ts.map +1 -1
- package/dist/commands/sched.js +135 -63
- package/dist/commands/sched.js.map +1 -1
- package/dist/sched-service.d.ts +1 -0
- package/dist/sched-service.d.ts.map +1 -1
- package/dist/sched-service.js +25 -0
- package/dist/sched-service.js.map +1 -1
- package/dist/sched-tick-runner.d.ts +26 -0
- package/dist/sched-tick-runner.d.ts.map +1 -0
- package/dist/sched-tick-runner.js +102 -0
- package/dist/sched-tick-runner.js.map +1 -0
- package/dist/sched-tick-worker.d.ts +40 -0
- package/dist/sched-tick-worker.d.ts.map +1 -0
- package/dist/sched-tick-worker.js +89 -0
- package/dist/sched-tick-worker.js.map +1 -0
- package/package.json +4 -4
package/dist/commands/sched.js
CHANGED
|
@@ -49,6 +49,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
49
49
|
exports.UPGRADE_RESTART_EXIT_CODE = void 0;
|
|
50
50
|
exports.createBatchCapabilityRunner = createBatchCapabilityRunner;
|
|
51
51
|
exports.reexecUpdatedCli = reexecUpdatedCli;
|
|
52
|
+
exports.buildEngineDeps = buildEngineDeps;
|
|
52
53
|
exports.registerSchedCommand = registerSchedCommand;
|
|
53
54
|
const node_child_process_1 = require("node:child_process");
|
|
54
55
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
@@ -73,9 +74,11 @@ const runstate_1 = require("../runstate");
|
|
|
73
74
|
const runstate_stats_1 = require("../runstate-stats");
|
|
74
75
|
const sched_alert_1 = require("../sched-alert");
|
|
75
76
|
const sched_run_stats_1 = require("../sched-run-stats");
|
|
77
|
+
const sched_service_1 = require("../sched-service");
|
|
78
|
+
const sched_tick_runner_1 = require("../sched-tick-runner");
|
|
76
79
|
const table_1 = require("../table");
|
|
77
80
|
const batch_prep_1 = require("../usage/batch-prep");
|
|
78
|
-
const
|
|
81
|
+
const sched_service_2 = require("./sched-service");
|
|
79
82
|
/**
|
|
80
83
|
* Batch-worktree `ai-dossier cap run <id>` runner for the per-member
|
|
81
84
|
* incremental gate (#523 AC2). `cap run`'s `task-failed` outcome — a
|
|
@@ -93,13 +96,17 @@ const sched_service_1 = require("./sched-service");
|
|
|
93
96
|
*/
|
|
94
97
|
function createBatchCapabilityRunner(opts) {
|
|
95
98
|
const timeoutMs = opts?.timeoutMs ?? batch_suite_runner_1.BATCH_SUITE_TIMEOUT_MS;
|
|
99
|
+
const inFlightDir = opts?.inFlightDir;
|
|
96
100
|
return (worktree, capabilityId) => {
|
|
97
|
-
const run = (0, cap_envelope_1.spawnCapRun)(capabilityId, worktree, {
|
|
101
|
+
const run = (0, cap_envelope_1.spawnCapRun)(capabilityId, worktree, {
|
|
102
|
+
timeoutMs,
|
|
103
|
+
...(inFlightDir !== undefined ? { inFlightDir } : {}),
|
|
104
|
+
});
|
|
98
105
|
const result = run.spawned;
|
|
99
106
|
// Captured subprocess output — the evidence fallback on every path below.
|
|
100
107
|
const captured = `${result.stdout ?? ''}${result.stderr ?? ''}`;
|
|
101
108
|
if (result.error && !run.exitedWithVerdict) {
|
|
102
|
-
// #681: this runner's OWN timeout (spawnSync's,
|
|
109
|
+
// #681: this runner's OWN timeout (`BATCH_SUITE_TIMEOUT_MS` — spawnSync's, or the tracked launcher's process-group kill with a synthetic ETIMEDOUT, #1233)
|
|
103
110
|
// must be distinguishable from a genuine machinery failure — before, it
|
|
104
111
|
// collapsed into the evidence-free `{outcome: 'automation-broken'}`
|
|
105
112
|
// below, byte-for-byte the #679 shape. Emit the same
|
|
@@ -1233,11 +1240,24 @@ function registerStatusSubcommand(cmd) {
|
|
|
1233
1240
|
// — it reads whatever `sched start` last cached rather than risking
|
|
1234
1241
|
// a multi-second hang on an unreachable npm registry.
|
|
1235
1242
|
const staleness = await (0, engine_version_1.checkEngineStaleness)({ noFetch: true });
|
|
1243
|
+
// #1233: gates the engine launched and has not consumed — after an
|
|
1244
|
+
// engine death this is where "gate pid N is still running" shows.
|
|
1245
|
+
const gatesInFlight = (0, sched_1.listInFlight)((0, sched_1.inFlightDir)(store.dir));
|
|
1236
1246
|
if (opts.json) {
|
|
1237
|
-
console.log(JSON.stringify({
|
|
1247
|
+
console.log(JSON.stringify({
|
|
1248
|
+
...report,
|
|
1249
|
+
engine_staleness: staleness,
|
|
1250
|
+
gates_in_flight: gatesInFlight.map((g) => ({
|
|
1251
|
+
...g.record,
|
|
1252
|
+
alive: g.alive,
|
|
1253
|
+
finished: g.finished,
|
|
1254
|
+
})),
|
|
1255
|
+
}, null, 2));
|
|
1238
1256
|
}
|
|
1239
1257
|
else {
|
|
1240
1258
|
console.log(renderReport(report, staleness));
|
|
1259
|
+
for (const g of gatesInFlight)
|
|
1260
|
+
console.log(`Gate in flight: ${(0, sched_1.describeInFlight)(g)}`);
|
|
1241
1261
|
}
|
|
1242
1262
|
}
|
|
1243
1263
|
catch (err) {
|
|
@@ -1282,8 +1302,9 @@ function resumeBatchGate(opts) {
|
|
|
1282
1302
|
exec: (0, sched_1.createExecFn)(sched_1.TEARDOWN_TIMEOUT_MS, {
|
|
1283
1303
|
onError: (file, args, err) => process.stderr.write(`⚠ sched resume --batch: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
1284
1304
|
}),
|
|
1285
|
-
|
|
1286
|
-
|
|
1305
|
+
// #1233: adopt (never duplicate) a gate the engine already has in flight here.
|
|
1306
|
+
runSuite: (0, batch_suite_runner_1.createBatchSuiteRunner)(config, { inFlightDir: (0, sched_1.inFlightDir)(store.dir) }),
|
|
1307
|
+
runCapability: createBatchCapabilityRunner({ inFlightDir: (0, sched_1.inFlightDir)(store.dir) }),
|
|
1287
1308
|
};
|
|
1288
1309
|
try {
|
|
1289
1310
|
const result = (0, sched_1.resumeBlockedGate)(deps, config, (0, sched_1.resolveDispatch)(config), opts.batch, new Date());
|
|
@@ -2048,6 +2069,90 @@ function reexecUpdatedCli(execveOverride) {
|
|
|
2048
2069
|
return false;
|
|
2049
2070
|
}
|
|
2050
2071
|
}
|
|
2072
|
+
/**
|
|
2073
|
+
* #1233: the off-thread tick runner for the continuous loop, or null for the
|
|
2074
|
+
* inline tick (`DOSSIER_SCHED_INLINE_TICK=1`, or no compiled worker entry —
|
|
2075
|
+
* running from sources). The inline fallback is announced: it is the mode in
|
|
2076
|
+
* which a long gate freezes the heartbeat and defers signals.
|
|
2077
|
+
*/
|
|
2078
|
+
function resolveTickRunner(data) {
|
|
2079
|
+
if (process.env[sched_tick_runner_1.INLINE_TICK_ENV] === '1') {
|
|
2080
|
+
console.error(`⚠ sched: ${sched_tick_runner_1.INLINE_TICK_ENV}=1 — ticks run on the main thread; a long gate freezes the lease heartbeat and defers SIGTERM/SIGINT`);
|
|
2081
|
+
return null;
|
|
2082
|
+
}
|
|
2083
|
+
const file = (0, sched_tick_runner_1.tickWorkerFile)();
|
|
2084
|
+
if (file === null) {
|
|
2085
|
+
console.error('⚠ sched: tick worker entry not found next to the CLI build — ticks run on the main thread');
|
|
2086
|
+
return null;
|
|
2087
|
+
}
|
|
2088
|
+
return (0, sched_tick_runner_1.createWorkerTickRunner)(file, data);
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* The engine's dependencies for `sched start` — one place, so the inline tick
|
|
2092
|
+
* and the tick worker (#1233, `sched-tick-worker.ts`) build identical engines.
|
|
2093
|
+
*/
|
|
2094
|
+
function buildEngineDeps(opts) {
|
|
2095
|
+
const { store, journal, anchorRepo, getTickConfig } = opts;
|
|
2096
|
+
const repoDir = opts.repoDir ?? process.cwd();
|
|
2097
|
+
const gatesInFlightDir = (0, sched_1.inFlightDir)(store.dir);
|
|
2098
|
+
return {
|
|
2099
|
+
store,
|
|
2100
|
+
journal,
|
|
2101
|
+
groundTruth: (0, sched_1.createExecGroundTruth)(undefined, {
|
|
2102
|
+
repoDir,
|
|
2103
|
+
...(anchorRepo !== undefined ? { repo: anchorRepo } : {}),
|
|
2104
|
+
}),
|
|
2105
|
+
...(anchorRepo !== undefined ? { anchorRepo } : {}),
|
|
2106
|
+
spawnDeps: (0, sched_1.createSpawnDeps)(repoDir),
|
|
2107
|
+
now: () => new Date(),
|
|
2108
|
+
repoDir,
|
|
2109
|
+
teardownExec: (0, sched_1.createExecFn)(sched_1.TEARDOWN_TIMEOUT_MS, {
|
|
2110
|
+
onError: (file, args, err) => process.stderr.write(`⚠ sched teardown: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2111
|
+
}),
|
|
2112
|
+
// #945: takeover rescue (git commit-tree + push of a dead agent's WIP), run
|
|
2113
|
+
// OUTSIDE the state lock. Bounded like every other engine exec; failures
|
|
2114
|
+
// are per-call diagnostics — a failed rescue never blocks the respawn.
|
|
2115
|
+
rescueExec: (0, sched_1.createExecFn)(RESCUE_TIMEOUT_MS, {
|
|
2116
|
+
// A push that wants credentials must fail, never hang on a prompt.
|
|
2117
|
+
env: { GIT_TERMINAL_PROMPT: '0' },
|
|
2118
|
+
onError: (file, args, err) => process.stderr.write(`⚠ sched rescue: '${file} ${args.slice(0, 2).join(' ')}…' failed: ${err.message}\n`),
|
|
2119
|
+
}),
|
|
2120
|
+
// #504: the ladder fences a superseded run before respawning its takeover.
|
|
2121
|
+
// Its own exec rather than the ground-truth one: a fence is a WRITE, and
|
|
2122
|
+
// borrowing `groundTruthExec` would file the only diagnostic for a failed write
|
|
2123
|
+
// under `sched ground truth`, where nobody debugging a fence would look.
|
|
2124
|
+
fencer: (0, sched_1.createExecRunFencer)((0, sched_1.createExecFn)(sched_1.FENCE_TIMEOUT_MS, {
|
|
2125
|
+
onError: (file, args, err) => process.stderr.write(`⚠ sched fence: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2126
|
+
}), { repoDir }),
|
|
2127
|
+
// #840: a requeued parked batch member's first dispatch seeds its
|
|
2128
|
+
// resume trail (a `setup done` milestone on its member branch) so the
|
|
2129
|
+
// full-cycle gate resumes ON that branch. A write, so its own exec +
|
|
2130
|
+
// diagnostic prefix, on the fence's budget (two short CLI calls).
|
|
2131
|
+
resumeSeeder: (0, sched_1.createExecResumeSeeder)((0, sched_1.createExecFn)(sched_1.FENCE_TIMEOUT_MS, {
|
|
2132
|
+
onError: (file, args, err) => process.stderr.write(`⚠ sched resume-seed: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2133
|
+
}), { repoDir }),
|
|
2134
|
+
// #523: batch git/milestone-CLI operations (worktree claim, commit-range
|
|
2135
|
+
// recording, milestone posting, PR watch) and the aggregate suite runner
|
|
2136
|
+
// that gates `executing → reviewing`. Reuses the same timeout as teardown
|
|
2137
|
+
// (worktree/git ops). The cold-path warm-up install/build (#561) does
|
|
2138
|
+
// NOT reuse this — see `batchWarmExec` below — a real `npm ci`+build
|
|
2139
|
+
// routinely exceeds this budget.
|
|
2140
|
+
batchExec: (0, sched_1.createExecFn)(sched_1.TEARDOWN_TIMEOUT_MS, {
|
|
2141
|
+
onError: (file, args, err) => process.stderr.write(`⚠ sched batch: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2142
|
+
}),
|
|
2143
|
+
// #561: batch-setup's cold-path warm-up (install + build) on its own
|
|
2144
|
+
// budget, matching `@ai-dossier/worktree-pool`'s own per-issue warm-up
|
|
2145
|
+
// budget for the identical work (`WARM_COMMAND_TIMEOUT_MS`) rather than
|
|
2146
|
+
// the git-op-tuned `batchExec` above.
|
|
2147
|
+
batchWarmExec: (0, sched_1.createExecFn)(worktree_pool_1.WARM_COMMAND_TIMEOUT_MS, {
|
|
2148
|
+
onError: (file, args, err) => process.stderr.write(`⚠ sched batch warm-up: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2149
|
+
}),
|
|
2150
|
+
// One config per tick: the suite runner reads the snapshot the tick started with.
|
|
2151
|
+
// #1233: engine gates are tracked in the in-flight registry (adopted, never duplicated).
|
|
2152
|
+
runBatchSuite: (0, batch_suite_runner_1.createBatchSuiteRunner)(getTickConfig, { inFlightDir: gatesInFlightDir }),
|
|
2153
|
+
runBatchCapability: createBatchCapabilityRunner({ inFlightDir: gatesInFlightDir }),
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2051
2156
|
function registerStartSubcommand(cmd) {
|
|
2052
2157
|
cmd
|
|
2053
2158
|
.command('start')
|
|
@@ -2057,6 +2162,7 @@ function registerStartSubcommand(cmd) {
|
|
|
2057
2162
|
.option('--once', 'Run a single reconcile+refill tick and exit (cron-style)')
|
|
2058
2163
|
.option('--auto-upgrade', 'After a completed tick, install and re-exec @ai-dossier/cli@latest when this engine is stale')
|
|
2059
2164
|
.option('--alert-issue <n>', 'Comment on this issue when the engine restarts after a crash (#945); default $DOSSIER_SCHED_ALERT_ISSUE. The alert is always journaled and printed to stderr')
|
|
2165
|
+
.option('--unsupervised', 'Acknowledge running without a supervisor (no `sched service install`) and silence the warning (#1233)')
|
|
2060
2166
|
.option('--project <slug>', 'Project slug (default: owner-repo of the current directory)')
|
|
2061
2167
|
.option('--json', 'Output tick results as JSON')
|
|
2062
2168
|
.action(async (opts) => {
|
|
@@ -2119,7 +2225,7 @@ function registerStartSubcommand(cmd) {
|
|
|
2119
2225
|
if (acquisition.reclaimed) {
|
|
2120
2226
|
(0, sched_1.reportCrashRestart)(journalAtStart, acquisition.reclaimed, (0, sched_alert_1.createAlertNotifier)(project, () => (0, sched_1.resolveProjectRepo)(project, sched_1.defaultExec) ?? undefined, alertIssue, { stateDir: store.dir }), new Date(), previousStop !== null && previousStop.pid === acquisition.reclaimed.pid
|
|
2121
2227
|
? previousStop
|
|
2122
|
-
: null);
|
|
2228
|
+
: null, { stateDir: store.dir });
|
|
2123
2229
|
}
|
|
2124
2230
|
try {
|
|
2125
2231
|
let config;
|
|
@@ -2228,62 +2334,12 @@ function registerStartSubcommand(cmd) {
|
|
|
2228
2334
|
if (anchorRepo === undefined && !(opts.once && opts.json)) {
|
|
2229
2335
|
console.log(`▶ sched anchor close: off — the current directory is not ${project}'s GitHub repository`);
|
|
2230
2336
|
}
|
|
2231
|
-
const deps = {
|
|
2337
|
+
const deps = buildEngineDeps({
|
|
2232
2338
|
store,
|
|
2233
2339
|
journal: engineJournal,
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
}),
|
|
2238
|
-
...(anchorRepo !== undefined ? { anchorRepo } : {}),
|
|
2239
|
-
spawnDeps: (0, sched_1.createSpawnDeps)(process.cwd()),
|
|
2240
|
-
now: () => new Date(),
|
|
2241
|
-
repoDir: process.cwd(),
|
|
2242
|
-
teardownExec: (0, sched_1.createExecFn)(sched_1.TEARDOWN_TIMEOUT_MS, {
|
|
2243
|
-
onError: (file, args, err) => process.stderr.write(`⚠ sched teardown: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2244
|
-
}),
|
|
2245
|
-
// #945: takeover rescue (git commit-tree + push of a dead agent's WIP), run
|
|
2246
|
-
// OUTSIDE the state lock. Bounded like every other engine exec; failures
|
|
2247
|
-
// are per-call diagnostics — a failed rescue never blocks the respawn.
|
|
2248
|
-
rescueExec: (0, sched_1.createExecFn)(RESCUE_TIMEOUT_MS, {
|
|
2249
|
-
// A push that wants credentials must fail, never hang on a prompt.
|
|
2250
|
-
env: { GIT_TERMINAL_PROMPT: '0' },
|
|
2251
|
-
onError: (file, args, err) => process.stderr.write(`⚠ sched rescue: '${file} ${args.slice(0, 2).join(' ')}…' failed: ${err.message}\n`),
|
|
2252
|
-
}),
|
|
2253
|
-
// #504: the ladder fences a superseded run before respawning its takeover.
|
|
2254
|
-
// Its own exec rather than the ground-truth one: a fence is a WRITE, and
|
|
2255
|
-
// borrowing `groundTruthExec` would file the only diagnostic for a failed write
|
|
2256
|
-
// under `sched ground truth`, where nobody debugging a fence would look.
|
|
2257
|
-
fencer: (0, sched_1.createExecRunFencer)((0, sched_1.createExecFn)(sched_1.FENCE_TIMEOUT_MS, {
|
|
2258
|
-
onError: (file, args, err) => process.stderr.write(`⚠ sched fence: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2259
|
-
}), { repoDir: process.cwd() }),
|
|
2260
|
-
// #840: a requeued parked batch member's first dispatch seeds its
|
|
2261
|
-
// resume trail (a `setup done` milestone on its member branch) so the
|
|
2262
|
-
// full-cycle gate resumes ON that branch. A write, so its own exec +
|
|
2263
|
-
// diagnostic prefix, on the fence's budget (two short CLI calls).
|
|
2264
|
-
resumeSeeder: (0, sched_1.createExecResumeSeeder)((0, sched_1.createExecFn)(sched_1.FENCE_TIMEOUT_MS, {
|
|
2265
|
-
onError: (file, args, err) => process.stderr.write(`⚠ sched resume-seed: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2266
|
-
}), { repoDir: process.cwd() }),
|
|
2267
|
-
// #523: batch git/milestone-CLI operations (worktree claim, commit-range
|
|
2268
|
-
// recording, milestone posting, PR watch) and the aggregate suite runner
|
|
2269
|
-
// that gates `executing → reviewing`. Reuses the same timeout as teardown
|
|
2270
|
-
// (worktree/git ops). The cold-path warm-up install/build (#561) does
|
|
2271
|
-
// NOT reuse this — see `batchWarmExec` below — a real `npm ci`+build
|
|
2272
|
-
// routinely exceeds this budget.
|
|
2273
|
-
batchExec: (0, sched_1.createExecFn)(sched_1.TEARDOWN_TIMEOUT_MS, {
|
|
2274
|
-
onError: (file, args, err) => process.stderr.write(`⚠ sched batch: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2275
|
-
}),
|
|
2276
|
-
// #561: batch-setup's cold-path warm-up (install + build) on its own
|
|
2277
|
-
// budget, matching `@ai-dossier/worktree-pool`'s own per-issue warm-up
|
|
2278
|
-
// budget for the identical work (`WARM_COMMAND_TIMEOUT_MS`) rather than
|
|
2279
|
-
// the git-op-tuned `batchExec` above.
|
|
2280
|
-
batchWarmExec: (0, sched_1.createExecFn)(worktree_pool_1.WARM_COMMAND_TIMEOUT_MS, {
|
|
2281
|
-
onError: (file, args, err) => process.stderr.write(`⚠ sched batch warm-up: '${file} ${args.join(' ')}' failed: ${err.message}\n`),
|
|
2282
|
-
}),
|
|
2283
|
-
// One config per tick: the suite runner reads the snapshot the tick started with.
|
|
2284
|
-
runBatchSuite: (0, batch_suite_runner_1.createBatchSuiteRunner)(() => tickConfig),
|
|
2285
|
-
runBatchCapability: createBatchCapabilityRunner(),
|
|
2286
|
-
};
|
|
2340
|
+
anchorRepo,
|
|
2341
|
+
getTickConfig: () => tickConfig,
|
|
2342
|
+
});
|
|
2287
2343
|
const describe = (result) => {
|
|
2288
2344
|
const parts = [];
|
|
2289
2345
|
if (result.spawned.length > 0)
|
|
@@ -2368,8 +2424,23 @@ function registerStartSubcommand(cmd) {
|
|
|
2368
2424
|
exitLogger.logNormalExit('once tick complete');
|
|
2369
2425
|
return;
|
|
2370
2426
|
}
|
|
2427
|
+
// #1233: an unsupervised engine dies for good the first time anything
|
|
2428
|
+
// kills it (the 2026-09-29 engine sat dead for 15 days). Say so loudly.
|
|
2429
|
+
if (!opts.unsupervised) {
|
|
2430
|
+
const warning = (0, sched_service_1.unsupervisedWarning)(project, () => (0, sched_service_1.serviceStatus)(project, (0, sched_service_2.serviceIo)()));
|
|
2431
|
+
if (warning !== null)
|
|
2432
|
+
console.error(warning);
|
|
2433
|
+
}
|
|
2371
2434
|
const interval = (engineConfig.reconcile_interval_ms ?? sched_1.DEFAULT_RECONCILE_INTERVAL_MS) / 1000;
|
|
2372
2435
|
console.log(`▶ Scheduler engine running for ${project} (tick every ${interval}s, Ctrl-C to stop)`);
|
|
2436
|
+
// #1233: run each tick in a worker thread so this event loop stays free
|
|
2437
|
+
// during a long gate — the lease keeps its heartbeat and SIGTERM/SIGINT
|
|
2438
|
+
// are handled (and journaled) instead of piling up behind a spawnSync.
|
|
2439
|
+
const tickRunner = resolveTickRunner({
|
|
2440
|
+
stateDir: store.dir,
|
|
2441
|
+
repoDir: process.cwd(),
|
|
2442
|
+
...(anchorRepo !== undefined ? { anchorRepo } : {}),
|
|
2443
|
+
});
|
|
2373
2444
|
let upgradeHandoff = false;
|
|
2374
2445
|
await (0, sched_1.runLoop)(deps, () => {
|
|
2375
2446
|
tickConfig = configReloader.current();
|
|
@@ -2393,7 +2464,8 @@ function registerStartSubcommand(cmd) {
|
|
|
2393
2464
|
upgradeHandoff = true;
|
|
2394
2465
|
stopping = true;
|
|
2395
2466
|
}
|
|
2396
|
-
});
|
|
2467
|
+
}, tickRunner !== null ? { runTick: (cfg) => tickRunner.runTick(cfg) } : {});
|
|
2468
|
+
await tickRunner?.close();
|
|
2397
2469
|
if (upgradeHandoff) {
|
|
2398
2470
|
exitLogger.logNormalExit('auto-upgrade handoff');
|
|
2399
2471
|
releaseLease();
|
|
@@ -2427,6 +2499,6 @@ function registerSchedCommand(program) {
|
|
|
2427
2499
|
registerReprioritizeSubcommand(schedCmd);
|
|
2428
2500
|
registerStartSubcommand(schedCmd);
|
|
2429
2501
|
registerStatsSubcommand(schedCmd);
|
|
2430
|
-
(0,
|
|
2502
|
+
(0, sched_service_2.registerSchedServiceCommands)(schedCmd);
|
|
2431
2503
|
}
|
|
2432
2504
|
//# sourceMappingURL=sched.js.map
|