@kynver-app/runtime 0.1.106 → 0.1.112
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/cleanup-git-rev-cache.d.ts +5 -0
- package/dist/cleanup-git-status-cache.d.ts +5 -0
- package/dist/cleanup-guards.d.ts +2 -0
- package/dist/cleanup-index-status.d.ts +8 -1
- package/dist/cleanup-run-liveness.d.ts +8 -1
- package/dist/cli.js +747 -83
- package/dist/cli.js.map +4 -4
- package/dist/daemon-platform-guard.d.ts +2 -0
- package/dist/index.js +741 -77
- package/dist/index.js.map +4 -4
- package/dist/landing/cli-auth.d.ts +11 -0
- package/dist/landing/land-pr-completion-post.d.ts +12 -0
- package/dist/landing/land-pr.d.ts +28 -0
- package/dist/server/cleanup.js +185 -38
- package/dist/server/cleanup.js.map +3 -3
- package/dist/server/default-repo.js +11 -1
- package/dist/server/default-repo.js.map +3 -3
- package/dist/server/memory-cost-enforce.js +11 -1
- package/dist/server/memory-cost-enforce.js.map +3 -3
- package/dist/server/monitor.js +14 -2
- package/dist/server/monitor.js.map +2 -2
- package/dist/verify-live/verify-live-prompt.d.ts +2 -0
- package/dist/worker-ops.d.ts +4 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -214,7 +214,11 @@ function scrubClaudeEnv(env) {
|
|
|
214
214
|
|
|
215
215
|
// src/git.ts
|
|
216
216
|
function git(cwd, args, options = {}) {
|
|
217
|
-
const res = spawnSync(
|
|
217
|
+
const res = spawnSync(
|
|
218
|
+
"git",
|
|
219
|
+
args,
|
|
220
|
+
hiddenSpawnOptions({ cwd, encoding: "utf8" })
|
|
221
|
+
);
|
|
218
222
|
if (res.status !== 0 && !options.allowFailure) {
|
|
219
223
|
const message = `git ${args.join(" ")} failed: ${res.stderr || res.stdout}`;
|
|
220
224
|
if (options.throwError) throw new Error(message);
|
|
@@ -230,7 +234,11 @@ function gitStatusShort(worktreePath) {
|
|
|
230
234
|
}
|
|
231
235
|
function gitCapture(cwd, args) {
|
|
232
236
|
try {
|
|
233
|
-
const res = spawnSync(
|
|
237
|
+
const res = spawnSync(
|
|
238
|
+
"git",
|
|
239
|
+
args,
|
|
240
|
+
hiddenSpawnOptions({ cwd, encoding: "utf8" })
|
|
241
|
+
);
|
|
234
242
|
return {
|
|
235
243
|
status: res.status,
|
|
236
244
|
stdout: res.stdout || "",
|
|
@@ -6049,32 +6057,98 @@ function workerStatus(args) {
|
|
|
6049
6057
|
writeJson(path21.join(worker.workerDir, "last-status.json"), status);
|
|
6050
6058
|
console.log(JSON.stringify(status, null, 2));
|
|
6051
6059
|
}
|
|
6060
|
+
function buildWorkerListDrilldownCommands(runId) {
|
|
6061
|
+
return {
|
|
6062
|
+
full: `kynver worker list --run ${runId} --full`,
|
|
6063
|
+
blocked: `kynver worker list --run ${runId} --blocked`,
|
|
6064
|
+
running: `kynver worker list --run ${runId} --running`,
|
|
6065
|
+
task: `kynver worker list --run ${runId} --task <task-id>`,
|
|
6066
|
+
worker: `kynver worker list --run ${runId} --worker <worker>`,
|
|
6067
|
+
runFull: `kynver run status --run ${runId} --full`,
|
|
6068
|
+
workerFull: `kynver worker status --run ${runId} --name <worker>`,
|
|
6069
|
+
workerTail: `kynver worker tail --run ${runId} --name <worker> --lines 80`
|
|
6070
|
+
};
|
|
6071
|
+
}
|
|
6072
|
+
function asOptionalArg(value) {
|
|
6073
|
+
if (typeof value !== "string") return void 0;
|
|
6074
|
+
const trimmed = value.trim();
|
|
6075
|
+
return trimmed.length ? trimmed : void 0;
|
|
6076
|
+
}
|
|
6077
|
+
function activeWorkerFilters(args) {
|
|
6078
|
+
const filters = {};
|
|
6079
|
+
if (args.blocked === true || args.blocked === "true") filters.blocked = true;
|
|
6080
|
+
if (args.running === true || args.running === "true") filters.running = true;
|
|
6081
|
+
const task = asOptionalArg(args.task ?? args.taskId);
|
|
6082
|
+
if (task) filters.task = task;
|
|
6083
|
+
const worker = asOptionalArg(args.worker ?? args.name);
|
|
6084
|
+
if (worker) filters.worker = worker;
|
|
6085
|
+
const status = asOptionalArg(args.status);
|
|
6086
|
+
if (status) filters.status = status;
|
|
6087
|
+
return filters;
|
|
6088
|
+
}
|
|
6089
|
+
function workerMatchesFilters(worker, filters) {
|
|
6090
|
+
if (filters.blocked) {
|
|
6091
|
+
const attention = typeof worker.attention === "string" ? worker.attention : "";
|
|
6092
|
+
const status = typeof worker.status === "string" ? worker.status : "";
|
|
6093
|
+
if (!(attention === "blocked" || attention === "needs_attention" || attention === "stale" || status === "blocked")) {
|
|
6094
|
+
return false;
|
|
6095
|
+
}
|
|
6096
|
+
}
|
|
6097
|
+
if (filters.running) {
|
|
6098
|
+
if (!(worker.status === "running" && worker.attention !== "blocked" && worker.attention !== "needs_attention")) {
|
|
6099
|
+
return false;
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
if (typeof filters.task === "string" && worker.taskId !== filters.task) return false;
|
|
6103
|
+
if (typeof filters.worker === "string" && worker.worker !== filters.worker) return false;
|
|
6104
|
+
if (typeof filters.status === "string" && worker.status !== filters.status) return false;
|
|
6105
|
+
return true;
|
|
6106
|
+
}
|
|
6107
|
+
function applyWorkerFilters(board, args) {
|
|
6108
|
+
const filters = activeWorkerFilters(args);
|
|
6109
|
+
if (Object.keys(filters).length === 0) return board;
|
|
6110
|
+
const workers = board.workers.filter((worker) => workerMatchesFilters(worker, filters));
|
|
6111
|
+
const filtered = {
|
|
6112
|
+
...board,
|
|
6113
|
+
workerCount: workers.length,
|
|
6114
|
+
needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
|
|
6115
|
+
workers,
|
|
6116
|
+
activeFilters: filters
|
|
6117
|
+
};
|
|
6118
|
+
if (board.summary) {
|
|
6119
|
+
filtered.summary = {
|
|
6120
|
+
statusCounts: countBy(workers, "status"),
|
|
6121
|
+
attentionCounts: countBy(workers, "attention"),
|
|
6122
|
+
lifecycleCounts: countBy(workers, "lifecycleStage")
|
|
6123
|
+
};
|
|
6124
|
+
}
|
|
6125
|
+
if (board.controller) filtered.controller = buildControllerSummary(workers);
|
|
6126
|
+
return filtered;
|
|
6127
|
+
}
|
|
6052
6128
|
function workerList(args) {
|
|
6053
6129
|
const runId = resolveRunTargetArg(args);
|
|
6054
|
-
const explicitCompact = args.compact === true || args.compact === "true";
|
|
6055
6130
|
const explicitFull = args.full === true || args.full === "true";
|
|
6056
|
-
const
|
|
6057
|
-
const
|
|
6058
|
-
const compact = explicitCompact || !explicitFull && workerCount > 100;
|
|
6059
|
-
const board = compact ? buildCompactRunBoard(runId) : buildRunBoard(runId);
|
|
6060
|
-
const autoCompact = compact && !explicitCompact;
|
|
6061
|
-
const outputWorkers = autoCompact ? board.workers.filter((worker) => worker.attention && worker.attention !== "done" && worker.attention !== "ok") : board.workers;
|
|
6131
|
+
const board = applyWorkerFilters(explicitFull ? buildRunBoard(runId) : buildCompactRunBoard(runId), args);
|
|
6132
|
+
const outputWorkers = board.workers;
|
|
6062
6133
|
console.log(
|
|
6063
6134
|
JSON.stringify(
|
|
6064
6135
|
{
|
|
6065
6136
|
runId: board.runId,
|
|
6066
6137
|
status: board.status,
|
|
6138
|
+
projection: explicitFull ? "full" : "compact",
|
|
6067
6139
|
workerCount: board.workerCount,
|
|
6068
|
-
...
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6140
|
+
...board.activeFilters ? { activeFilters: board.activeFilters } : {},
|
|
6141
|
+
...!explicitFull ? {
|
|
6142
|
+
resultContract: {
|
|
6143
|
+
projection: "compact",
|
|
6144
|
+
rawWorkerPayloadsOmitted: true,
|
|
6145
|
+
deepDetailAvailableVia: ["--full", "worker status", "worker tail", "run status --full"]
|
|
6146
|
+
},
|
|
6147
|
+
drilldownCommands: buildWorkerListDrilldownCommands(runId)
|
|
6074
6148
|
} : {},
|
|
6075
6149
|
needsAttention: board.needsAttention,
|
|
6076
|
-
...
|
|
6077
|
-
...
|
|
6150
|
+
..."summary" in board ? { summary: board.summary } : {},
|
|
6151
|
+
..."controller" in board ? { controller: board.controller } : {},
|
|
6078
6152
|
workers: outputWorkers
|
|
6079
6153
|
},
|
|
6080
6154
|
null,
|
|
@@ -6342,9 +6416,33 @@ async function publishHarnessBoardSnapshot(args, source) {
|
|
|
6342
6416
|
authRefreshFailure: res.authRefreshFailure
|
|
6343
6417
|
};
|
|
6344
6418
|
}
|
|
6419
|
+
function buildRunStatusDrilldownCommands(runId) {
|
|
6420
|
+
return {
|
|
6421
|
+
full: `kynver run status --run ${runId} --full`,
|
|
6422
|
+
blocked: `kynver status --run ${runId} --blocked`,
|
|
6423
|
+
running: `kynver status --run ${runId} --running`,
|
|
6424
|
+
task: `kynver status --run ${runId} --task <task-id>`,
|
|
6425
|
+
worker: `kynver status --run ${runId} --worker <worker>`,
|
|
6426
|
+
workersFull: `kynver worker list --run ${runId} --full`,
|
|
6427
|
+
workerFull: `kynver worker status --run ${runId} --name <worker>`,
|
|
6428
|
+
workerTail: `kynver worker tail --run ${runId} --name <worker> --lines 80`,
|
|
6429
|
+
monitorTick: `kynver monitor status --run ${runId} --tick`
|
|
6430
|
+
};
|
|
6431
|
+
}
|
|
6345
6432
|
function runStatus(args) {
|
|
6346
|
-
const
|
|
6347
|
-
const
|
|
6433
|
+
const runId = resolveRunTargetArg(args);
|
|
6434
|
+
const explicitFull = args.full === true || args.full === "true";
|
|
6435
|
+
const board = applyWorkerFilters(explicitFull ? buildRunBoard(runId) : buildCompactRunBoard(runId), args);
|
|
6436
|
+
board.projection = explicitFull ? "full" : "compact";
|
|
6437
|
+
if (!explicitFull) {
|
|
6438
|
+
board.resultContract = {
|
|
6439
|
+
projection: "compact",
|
|
6440
|
+
rawWorkerPayloadsOmitted: true,
|
|
6441
|
+
omittedWorkerFields: ["finalResult", "changedFiles", "gitAncestry", "completionResponse", "stdout/stderr tails"],
|
|
6442
|
+
deepDetailAvailableVia: ["--full", "worker status", "worker tail", "monitor status --tick"]
|
|
6443
|
+
};
|
|
6444
|
+
board.drilldownCommands = buildRunStatusDrilldownCommands(runId);
|
|
6445
|
+
}
|
|
6348
6446
|
console.log(JSON.stringify(board, null, 2));
|
|
6349
6447
|
}
|
|
6350
6448
|
function tailWorker(args) {
|
|
@@ -7439,6 +7537,344 @@ async function releaseDispatchClaimAfterSpawnFailure(input) {
|
|
|
7439
7537
|
};
|
|
7440
7538
|
}
|
|
7441
7539
|
|
|
7540
|
+
// src/landing/land-pr.ts
|
|
7541
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
7542
|
+
|
|
7543
|
+
// src/landing/cli-auth.ts
|
|
7544
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
7545
|
+
function ensureGitHubTokenFromCliAuth() {
|
|
7546
|
+
if (process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim()) {
|
|
7547
|
+
return {
|
|
7548
|
+
source: "env",
|
|
7549
|
+
configured: true,
|
|
7550
|
+
reason: "GitHub token already configured in environment"
|
|
7551
|
+
};
|
|
7552
|
+
}
|
|
7553
|
+
const result = spawnSync4("gh", ["auth", "token"], {
|
|
7554
|
+
encoding: "utf8",
|
|
7555
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7556
|
+
});
|
|
7557
|
+
if (result.status !== 0) {
|
|
7558
|
+
const reason = typeof result.stderr === "string" && result.stderr.trim() ? result.stderr.trim() : "gh auth token failed; run `gh auth login` on this machine";
|
|
7559
|
+
return { source: "missing", configured: false, reason };
|
|
7560
|
+
}
|
|
7561
|
+
const token = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
7562
|
+
if (!token) {
|
|
7563
|
+
return {
|
|
7564
|
+
source: "missing",
|
|
7565
|
+
configured: false,
|
|
7566
|
+
reason: "gh auth token returned an empty token; run `gh auth login` on this machine"
|
|
7567
|
+
};
|
|
7568
|
+
}
|
|
7569
|
+
process.env.GH_TOKEN = token;
|
|
7570
|
+
return {
|
|
7571
|
+
source: "gh-cli",
|
|
7572
|
+
configured: true,
|
|
7573
|
+
reason: "Using local GitHub CLI auth for daemon land_pr merge"
|
|
7574
|
+
};
|
|
7575
|
+
}
|
|
7576
|
+
|
|
7577
|
+
// src/landing/land-pr.ts
|
|
7578
|
+
var SUCCESSFUL_CHECK_CONCLUSIONS = /* @__PURE__ */ new Set(["SUCCESS", "SKIPPED", "NEUTRAL"]);
|
|
7579
|
+
var READY_MERGE_STATES = /* @__PURE__ */ new Set(["CLEAN", "HAS_HOOKS"]);
|
|
7580
|
+
function repoArgs(repo) {
|
|
7581
|
+
return repo?.trim() ? ["--repo", repo.trim()] : [];
|
|
7582
|
+
}
|
|
7583
|
+
function repoFromPrUrl(prUrl) {
|
|
7584
|
+
const m = /github\.com\/([^/]+)\/([^/]+)\/pull\/\d+/i.exec(prUrl.trim());
|
|
7585
|
+
return m ? `${m[1]}/${m[2]}` : null;
|
|
7586
|
+
}
|
|
7587
|
+
function ghJson(exec, cwd, args) {
|
|
7588
|
+
const res = exec.gh(cwd, args);
|
|
7589
|
+
if (res.status !== 0) {
|
|
7590
|
+
throw new Error(res.stderr || res.stdout || `gh ${args.join(" ")} failed`);
|
|
7591
|
+
}
|
|
7592
|
+
return JSON.parse(res.stdout || "{}");
|
|
7593
|
+
}
|
|
7594
|
+
function checkName(check3) {
|
|
7595
|
+
return typeof check3.name === "string" && check3.name || typeof check3.context === "string" && check3.context || typeof check3.workflowName === "string" && check3.workflowName || "unknown check";
|
|
7596
|
+
}
|
|
7597
|
+
function classifyChecks(statusCheckRollup) {
|
|
7598
|
+
const checks = Array.isArray(statusCheckRollup) ? statusCheckRollup : [];
|
|
7599
|
+
const pending = [];
|
|
7600
|
+
const failed = [];
|
|
7601
|
+
for (const raw of checks) {
|
|
7602
|
+
const check3 = raw && typeof raw === "object" ? raw : {};
|
|
7603
|
+
const conclusion = typeof check3.conclusion === "string" ? check3.conclusion.toUpperCase() : "";
|
|
7604
|
+
const status = typeof check3.status === "string" ? check3.status.toUpperCase() : "";
|
|
7605
|
+
const state = typeof check3.state === "string" ? check3.state.toUpperCase() : "";
|
|
7606
|
+
if (conclusion && SUCCESSFUL_CHECK_CONCLUSIONS.has(conclusion)) continue;
|
|
7607
|
+
if (conclusion) {
|
|
7608
|
+
failed.push(`${checkName(check3)}=${conclusion}`);
|
|
7609
|
+
continue;
|
|
7610
|
+
}
|
|
7611
|
+
if (state && SUCCESSFUL_CHECK_CONCLUSIONS.has(state)) continue;
|
|
7612
|
+
if (state && state !== "PENDING") {
|
|
7613
|
+
failed.push(`${checkName(check3)}=${state}`);
|
|
7614
|
+
continue;
|
|
7615
|
+
}
|
|
7616
|
+
if (state === "PENDING") {
|
|
7617
|
+
pending.push(`${checkName(check3)}=${state}`);
|
|
7618
|
+
continue;
|
|
7619
|
+
}
|
|
7620
|
+
if (status && status !== "COMPLETED") {
|
|
7621
|
+
pending.push(`${checkName(check3)}=${status}`);
|
|
7622
|
+
continue;
|
|
7623
|
+
}
|
|
7624
|
+
pending.push(`${checkName(check3)}=PENDING`);
|
|
7625
|
+
}
|
|
7626
|
+
return { pending, failed };
|
|
7627
|
+
}
|
|
7628
|
+
function vercelCheckSuccess(statusCheckRollup) {
|
|
7629
|
+
const checks = Array.isArray(statusCheckRollup) ? statusCheckRollup : [];
|
|
7630
|
+
return checks.some((raw) => {
|
|
7631
|
+
const check3 = raw && typeof raw === "object" ? raw : {};
|
|
7632
|
+
const name = typeof check3.name === "string" && check3.name || typeof check3.context === "string" && check3.context || "";
|
|
7633
|
+
const conclusion = typeof check3.conclusion === "string" ? check3.conclusion.toUpperCase() : "";
|
|
7634
|
+
const state = typeof check3.state === "string" ? check3.state.toUpperCase() : "";
|
|
7635
|
+
return /^vercel/i.test(name) && (conclusion === "SUCCESS" || state === "SUCCESS");
|
|
7636
|
+
});
|
|
7637
|
+
}
|
|
7638
|
+
var STRUCTURED_SECTION_RE = /^##\s*(test\s*plan|verification|test\s*evidence|verify)\b/im;
|
|
7639
|
+
var LOCAL_VERIFICATION_RE = /typecheck|npm run (test|build|typecheck)|vitest|tsc\b|verify-pr-local|local verify|local-verify|tests? (pass|green)|build green|node --test/i;
|
|
7640
|
+
var DOCS_TITLE_RE = /^docs[(:]/i;
|
|
7641
|
+
var DOCS_BODY_RE = /docs[- ]only|documentation only|no[- ]code change|no code changes|low[- ]risk|plan tracker only|markdown only/i;
|
|
7642
|
+
function runtimeVerificationEvidenceSufficient(input) {
|
|
7643
|
+
const title = typeof input.title === "string" ? input.title : "";
|
|
7644
|
+
const body = typeof input.body === "string" ? input.body : "";
|
|
7645
|
+
const docsLowRisk = DOCS_TITLE_RE.test(title) || DOCS_BODY_RE.test(body);
|
|
7646
|
+
const structuredSection = STRUCTURED_SECTION_RE.test(body);
|
|
7647
|
+
const localVerification = LOCAL_VERIFICATION_RE.test(body);
|
|
7648
|
+
return docsLowRisk || structuredSection || localVerification || input.vercelCheckSuccess === true;
|
|
7649
|
+
}
|
|
7650
|
+
function assertVerificationEvidence(pr) {
|
|
7651
|
+
const sufficient = runtimeVerificationEvidenceSufficient({
|
|
7652
|
+
title: pr.title,
|
|
7653
|
+
body: pr.body,
|
|
7654
|
+
vercelCheckSuccess: vercelCheckSuccess(pr.statusCheckRollup)
|
|
7655
|
+
});
|
|
7656
|
+
if (sufficient) return;
|
|
7657
|
+
throw new Error(
|
|
7658
|
+
`PR #${pr.number} lacks landing verification evidence \u2014 add ## Test plan / ## Verification with local commands, Vercel preview, or docs/no-code rationale`
|
|
7659
|
+
);
|
|
7660
|
+
}
|
|
7661
|
+
function assertLandingReady(pr) {
|
|
7662
|
+
if (pr.state !== "OPEN") throw new Error(`PR #${pr.number} is ${pr.state}, not OPEN`);
|
|
7663
|
+
if (pr.isDraft) throw new Error(`PR #${pr.number} is still a draft`);
|
|
7664
|
+
if (!READY_MERGE_STATES.has(pr.mergeStateStatus)) {
|
|
7665
|
+
throw new Error(`PR #${pr.number} mergeStateStatus is ${pr.mergeStateStatus}`);
|
|
7666
|
+
}
|
|
7667
|
+
const checks = classifyChecks(pr.statusCheckRollup);
|
|
7668
|
+
if (checks.failed.length > 0) {
|
|
7669
|
+
throw new Error(`PR #${pr.number} has failing checks: ${checks.failed.join(", ")}`);
|
|
7670
|
+
}
|
|
7671
|
+
if (checks.pending.length > 0) {
|
|
7672
|
+
throw new Error(`PR #${pr.number} has pending checks: ${checks.pending.join(", ")}`);
|
|
7673
|
+
}
|
|
7674
|
+
assertVerificationEvidence(pr);
|
|
7675
|
+
}
|
|
7676
|
+
function landingReadinessError(pr) {
|
|
7677
|
+
try {
|
|
7678
|
+
assertLandingReady(pr);
|
|
7679
|
+
return null;
|
|
7680
|
+
} catch (err) {
|
|
7681
|
+
return err instanceof Error ? err.message : String(err);
|
|
7682
|
+
}
|
|
7683
|
+
}
|
|
7684
|
+
function deleteRemoteBranch(exec, cwd, repo, branch) {
|
|
7685
|
+
if (!branch?.trim()) return;
|
|
7686
|
+
const refPath = encodeURI(`heads/${branch}`);
|
|
7687
|
+
exec.gh(cwd, ["api", "-X", "DELETE", `repos/${repo}/git/refs/${refPath}`]);
|
|
7688
|
+
}
|
|
7689
|
+
function resolveRepo(exec, cwd, repo) {
|
|
7690
|
+
if (repo?.trim()) return repo.trim();
|
|
7691
|
+
const res = exec.gh(cwd, ["repo", "view", "--json", "nameWithOwner"]);
|
|
7692
|
+
if (res.status !== 0) throw new Error(res.stderr || "Could not resolve GitHub repo");
|
|
7693
|
+
const parsed = JSON.parse(res.stdout || "{}");
|
|
7694
|
+
if (!parsed.nameWithOwner) throw new Error("Could not resolve GitHub repo");
|
|
7695
|
+
return parsed.nameWithOwner;
|
|
7696
|
+
}
|
|
7697
|
+
function removeBranchWorktrees(cwd, branch) {
|
|
7698
|
+
if (!branch?.trim()) return;
|
|
7699
|
+
const list = spawnSync5("git", ["worktree", "list", "--porcelain"], {
|
|
7700
|
+
cwd,
|
|
7701
|
+
encoding: "utf8"
|
|
7702
|
+
});
|
|
7703
|
+
if (list.status !== 0) return;
|
|
7704
|
+
const lines = String(list.stdout || "").split(/\r?\n/);
|
|
7705
|
+
let currentPath = null;
|
|
7706
|
+
let currentBranch = null;
|
|
7707
|
+
for (const line of lines) {
|
|
7708
|
+
if (line.startsWith("worktree ")) {
|
|
7709
|
+
currentPath = line.slice("worktree ".length).trim();
|
|
7710
|
+
currentBranch = null;
|
|
7711
|
+
continue;
|
|
7712
|
+
}
|
|
7713
|
+
if (line.startsWith("branch ") && currentPath) {
|
|
7714
|
+
currentBranch = line.slice("branch ".length).trim();
|
|
7715
|
+
if (currentBranch.endsWith(`/${branch}`)) {
|
|
7716
|
+
spawnSync5("git", ["worktree", "remove", "--force", currentPath], {
|
|
7717
|
+
cwd,
|
|
7718
|
+
encoding: "utf8"
|
|
7719
|
+
});
|
|
7720
|
+
}
|
|
7721
|
+
}
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7724
|
+
async function executeLandPrMerge(input) {
|
|
7725
|
+
const cwd = input.cwd ?? process.cwd();
|
|
7726
|
+
const exec = input.exec ?? defaultPrHandoffExec;
|
|
7727
|
+
const prTarget = input.prUrl.trim();
|
|
7728
|
+
if (!prTarget) throw new Error("prUrl is required");
|
|
7729
|
+
const auth = ensureGitHubTokenFromCliAuth();
|
|
7730
|
+
if (!auth.configured) {
|
|
7731
|
+
return {
|
|
7732
|
+
prUrl: prTarget,
|
|
7733
|
+
outcome: "blocked",
|
|
7734
|
+
reason: auth.reason
|
|
7735
|
+
};
|
|
7736
|
+
}
|
|
7737
|
+
const before = ghJson(exec, cwd, [
|
|
7738
|
+
"pr",
|
|
7739
|
+
"view",
|
|
7740
|
+
prTarget,
|
|
7741
|
+
...repoArgs(input.repo),
|
|
7742
|
+
"--json",
|
|
7743
|
+
[
|
|
7744
|
+
"number",
|
|
7745
|
+
"url",
|
|
7746
|
+
"title",
|
|
7747
|
+
"body",
|
|
7748
|
+
"state",
|
|
7749
|
+
"isDraft",
|
|
7750
|
+
"mergeStateStatus",
|
|
7751
|
+
"statusCheckRollup",
|
|
7752
|
+
"headRefName",
|
|
7753
|
+
"mergedAt",
|
|
7754
|
+
"mergeCommit"
|
|
7755
|
+
].join(",")
|
|
7756
|
+
]);
|
|
7757
|
+
if (before.state === "MERGED" || before.mergedAt) {
|
|
7758
|
+
return {
|
|
7759
|
+
prUrl: before.url || prTarget,
|
|
7760
|
+
outcome: "skipped",
|
|
7761
|
+
mergeCommit: before.mergeCommit?.oid ?? null,
|
|
7762
|
+
reason: `PR #${before.number} is already merged \u2014 land_pr no-op (redelivery)`
|
|
7763
|
+
};
|
|
7764
|
+
}
|
|
7765
|
+
const notReadyReason = landingReadinessError(before);
|
|
7766
|
+
if (notReadyReason) {
|
|
7767
|
+
if (input.skipNotReady) {
|
|
7768
|
+
return {
|
|
7769
|
+
prUrl: before.url || prTarget,
|
|
7770
|
+
outcome: "skipped",
|
|
7771
|
+
reason: notReadyReason
|
|
7772
|
+
};
|
|
7773
|
+
}
|
|
7774
|
+
return {
|
|
7775
|
+
prUrl: before.url || prTarget,
|
|
7776
|
+
outcome: "blocked",
|
|
7777
|
+
reason: notReadyReason
|
|
7778
|
+
};
|
|
7779
|
+
}
|
|
7780
|
+
if (input.dryRun) {
|
|
7781
|
+
return {
|
|
7782
|
+
prUrl: before.url || prTarget,
|
|
7783
|
+
outcome: "skipped",
|
|
7784
|
+
reason: "dry-run: PR is ready to merge"
|
|
7785
|
+
};
|
|
7786
|
+
}
|
|
7787
|
+
const target = String(before.number);
|
|
7788
|
+
const mergeRes = exec.gh(cwd, [
|
|
7789
|
+
"pr",
|
|
7790
|
+
"merge",
|
|
7791
|
+
target,
|
|
7792
|
+
...repoArgs(input.repo),
|
|
7793
|
+
"--squash"
|
|
7794
|
+
]);
|
|
7795
|
+
if (mergeRes.status !== 0) {
|
|
7796
|
+
return {
|
|
7797
|
+
prUrl: before.url || prTarget,
|
|
7798
|
+
outcome: "blocked",
|
|
7799
|
+
reason: mergeRes.stderr || mergeRes.stdout || "gh pr merge failed"
|
|
7800
|
+
};
|
|
7801
|
+
}
|
|
7802
|
+
const after = ghJson(exec, cwd, [
|
|
7803
|
+
"pr",
|
|
7804
|
+
"view",
|
|
7805
|
+
target,
|
|
7806
|
+
...repoArgs(input.repo),
|
|
7807
|
+
"--json",
|
|
7808
|
+
"number,url,mergedAt,mergeCommit,state"
|
|
7809
|
+
]);
|
|
7810
|
+
if (after.state !== "MERGED" && !after.mergedAt) {
|
|
7811
|
+
return {
|
|
7812
|
+
prUrl: after.url || before.url || prTarget,
|
|
7813
|
+
outcome: "blocked",
|
|
7814
|
+
reason: `PR #${after.number} did not verify as merged after gh pr merge`
|
|
7815
|
+
};
|
|
7816
|
+
}
|
|
7817
|
+
const repo = resolveRepo(exec, cwd, input.repo);
|
|
7818
|
+
deleteRemoteBranch(exec, cwd, repo, before.headRefName);
|
|
7819
|
+
removeBranchWorktrees(cwd, before.headRefName);
|
|
7820
|
+
const mergeCommit = after.mergeCommit?.oid ?? null;
|
|
7821
|
+
return {
|
|
7822
|
+
prUrl: after.url || before.url || prTarget,
|
|
7823
|
+
outcome: "merged",
|
|
7824
|
+
mergeCommit,
|
|
7825
|
+
reason: `Daemon land_pr merged PR #${after.number}`
|
|
7826
|
+
};
|
|
7827
|
+
}
|
|
7828
|
+
|
|
7829
|
+
// src/landing/land-pr-completion-post.ts
|
|
7830
|
+
async function postLandPrHarnessCompletion(input) {
|
|
7831
|
+
const secret = await resolveCallbackSecretWithMint(input.secret, input.agentOsId, {
|
|
7832
|
+
baseUrl: input.baseUrl
|
|
7833
|
+
});
|
|
7834
|
+
const taskId = String(input.task.id);
|
|
7835
|
+
const url = `${input.baseUrl}/api/agent-os/by-id/${encodeURIComponent(input.agentOsId)}/harness/completion`;
|
|
7836
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7837
|
+
const body = {
|
|
7838
|
+
source: "kynver-harness",
|
|
7839
|
+
agentOsId: input.agentOsId,
|
|
7840
|
+
runId: input.runId,
|
|
7841
|
+
workerName: `land-pr-${taskId}`,
|
|
7842
|
+
taskId,
|
|
7843
|
+
leaseToken: input.task.leaseToken ?? null,
|
|
7844
|
+
startedAt: finishedAt,
|
|
7845
|
+
finishedAt,
|
|
7846
|
+
status: {
|
|
7847
|
+
finalResult: input.report,
|
|
7848
|
+
prUrl: input.report.prUrl,
|
|
7849
|
+
summary: input.report.reason,
|
|
7850
|
+
leaseToken: input.task.leaseToken ?? null
|
|
7851
|
+
},
|
|
7852
|
+
workerInjection: null
|
|
7853
|
+
};
|
|
7854
|
+
const result = await postJsonWithCredentialRefresh(url, secret, body, {
|
|
7855
|
+
agentOsId: input.agentOsId,
|
|
7856
|
+
baseUrl: input.baseUrl
|
|
7857
|
+
});
|
|
7858
|
+
return { ok: result.ok, status: result.status };
|
|
7859
|
+
}
|
|
7860
|
+
|
|
7861
|
+
// src/verify-live/verify-live-prompt.ts
|
|
7862
|
+
function formatVerifyLiveCompletionContract() {
|
|
7863
|
+
return [
|
|
7864
|
+
"## verify_live completion contract",
|
|
7865
|
+
"Finish with a JSON finalResult object:",
|
|
7866
|
+
"{",
|
|
7867
|
+
' "prUrl": "<landed pr>",',
|
|
7868
|
+
' "outcome": "passed" | "failed" | "skipped" | "no_mcp_surface",',
|
|
7869
|
+
' "reason": "<human summary>",',
|
|
7870
|
+
' "mergeCommit": "<sha optional>",',
|
|
7871
|
+
' "mcpCalls": [{ "tool": "<name>", "ok": true, "summary": "..." }]',
|
|
7872
|
+
"}",
|
|
7873
|
+
"",
|
|
7874
|
+
"Drive the feature MCP tools against live production. Do not mark passed without exercising the MCP surface."
|
|
7875
|
+
].join("\n");
|
|
7876
|
+
}
|
|
7877
|
+
|
|
7442
7878
|
// src/dispatch.ts
|
|
7443
7879
|
var DEFAULT_DISPATCH_LEASE_MS = 60 * 60 * 1e3;
|
|
7444
7880
|
function readAdmissionExhaustion(result) {
|
|
@@ -7496,6 +7932,9 @@ function buildDispatchTaskText(task, agentOsId) {
|
|
|
7496
7932
|
`Board linkage: agentOsId=${agentOsId}, taskId=${task.id}, attempt=${task.attempt}, executor=${task.executor}${task.executorRef ? `, executorRef=${task.executorRef}` : ""}.`,
|
|
7497
7933
|
"This worker was dispatched from the AgentOS board. The harness reports your completion back to the board when you finish."
|
|
7498
7934
|
];
|
|
7935
|
+
if (task.executor === "verify_live") {
|
|
7936
|
+
lines.push("", formatVerifyLiveCompletionContract());
|
|
7937
|
+
}
|
|
7499
7938
|
const outboxRef = extractPlanOutboxFromTask(task);
|
|
7500
7939
|
if (outboxRef?.outboxId) {
|
|
7501
7940
|
const item = loadOutboxById(outboxRef.outboxId);
|
|
@@ -7529,6 +7968,12 @@ function requestedTargetTaskIds(args) {
|
|
|
7529
7968
|
async function dispatchRun(args) {
|
|
7530
7969
|
const pipeline = args.pipeline === true || args.pipeline === "true";
|
|
7531
7970
|
try {
|
|
7971
|
+
let isLandPrDecision2 = function(decision) {
|
|
7972
|
+
if (decision.landPrDispatch === true) return true;
|
|
7973
|
+
const task = decision.task;
|
|
7974
|
+
return task?.executor === "land_pr";
|
|
7975
|
+
};
|
|
7976
|
+
var isLandPrDecision = isLandPrDecision2;
|
|
7532
7977
|
const run = loadRun(String(required(String(args.run || ""), "--run")));
|
|
7533
7978
|
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
7534
7979
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
@@ -7663,6 +8108,66 @@ async function dispatchRun(args) {
|
|
|
7663
8108
|
});
|
|
7664
8109
|
return false;
|
|
7665
8110
|
}
|
|
8111
|
+
async function runLandPrClaimed(decision) {
|
|
8112
|
+
const task = decision.task;
|
|
8113
|
+
const taskId = String(task.id);
|
|
8114
|
+
const prUrl = task.prUrl ? String(task.prUrl) : "";
|
|
8115
|
+
if (!prUrl) {
|
|
8116
|
+
return abortClaimedSpawn(task, "land_pr task missing prUrl");
|
|
8117
|
+
}
|
|
8118
|
+
try {
|
|
8119
|
+
const report = await executeLandPrMerge({
|
|
8120
|
+
prUrl,
|
|
8121
|
+
repo: repoFromPrUrl(prUrl),
|
|
8122
|
+
cwd: run.repo
|
|
8123
|
+
});
|
|
8124
|
+
const post = await postLandPrHarnessCompletion({
|
|
8125
|
+
baseUrl: base,
|
|
8126
|
+
secret,
|
|
8127
|
+
agentOsId,
|
|
8128
|
+
runId: run.id,
|
|
8129
|
+
task,
|
|
8130
|
+
report
|
|
8131
|
+
});
|
|
8132
|
+
outcomes.push({
|
|
8133
|
+
taskId,
|
|
8134
|
+
started: true,
|
|
8135
|
+
landPr: true,
|
|
8136
|
+
outcome: report.outcome,
|
|
8137
|
+
completionStatus: post.status
|
|
8138
|
+
});
|
|
8139
|
+
if (!post.ok) {
|
|
8140
|
+
return abortClaimedSpawn(
|
|
8141
|
+
task,
|
|
8142
|
+
`land_pr completion POST failed (HTTP ${post.status})`
|
|
8143
|
+
);
|
|
8144
|
+
}
|
|
8145
|
+
return true;
|
|
8146
|
+
} catch (error) {
|
|
8147
|
+
return abortClaimedSpawn(task, error.message);
|
|
8148
|
+
}
|
|
8149
|
+
}
|
|
8150
|
+
async function releaseDuplicateLocalClaim(task) {
|
|
8151
|
+
const error = "duplicate_dispatch_prevented: live local worker already owns this task";
|
|
8152
|
+
const release = await releaseDispatchClaimAfterSpawnFailure({
|
|
8153
|
+
baseUrl: base,
|
|
8154
|
+
secret,
|
|
8155
|
+
agentOsId,
|
|
8156
|
+
taskId: String(task.id),
|
|
8157
|
+
leaseOwner,
|
|
8158
|
+
failureDetail: error
|
|
8159
|
+
});
|
|
8160
|
+
outcomes.push({
|
|
8161
|
+
taskId: task.id,
|
|
8162
|
+
started: false,
|
|
8163
|
+
error,
|
|
8164
|
+
alreadyRunning: true,
|
|
8165
|
+
nonFatal: true,
|
|
8166
|
+
released: release.released,
|
|
8167
|
+
releaseResponse: release.releaseResponse
|
|
8168
|
+
});
|
|
8169
|
+
return false;
|
|
8170
|
+
}
|
|
7666
8171
|
async function spawnClaimed(decision) {
|
|
7667
8172
|
const task = decision.task;
|
|
7668
8173
|
const harnessContext = readHarnessWorkerContext(decision);
|
|
@@ -7684,10 +8189,7 @@ async function dispatchRun(args) {
|
|
|
7684
8189
|
);
|
|
7685
8190
|
}
|
|
7686
8191
|
if (hasLiveWorkerForTask(run.id, taskId)) {
|
|
7687
|
-
return
|
|
7688
|
-
task,
|
|
7689
|
-
"duplicate_dispatch_prevented: live local worker already owns this task"
|
|
7690
|
-
);
|
|
8192
|
+
return releaseDuplicateLocalClaim(task);
|
|
7691
8193
|
}
|
|
7692
8194
|
const attempt = Number(task.attempt) || 1;
|
|
7693
8195
|
if (attempt > retryLimits.maxTaskAttempts) {
|
|
@@ -7780,7 +8282,8 @@ async function dispatchRun(args) {
|
|
|
7780
8282
|
}
|
|
7781
8283
|
let shouldContinueDispatch = true;
|
|
7782
8284
|
for (const decision of result.started) {
|
|
7783
|
-
|
|
8285
|
+
const admitted = isLandPrDecision2(decision) ? await runLandPrClaimed(decision) : await spawnClaimed(decision);
|
|
8286
|
+
shouldContinueDispatch = admitted && shouldContinueDispatch;
|
|
7784
8287
|
}
|
|
7785
8288
|
skipped.push(
|
|
7786
8289
|
...result.skipped ?? []
|
|
@@ -7818,7 +8321,8 @@ async function dispatchRun(args) {
|
|
|
7818
8321
|
if (started.length === 0) break;
|
|
7819
8322
|
for (const decision of started) {
|
|
7820
8323
|
if (outcomes.length >= cappedStarts) break;
|
|
7821
|
-
|
|
8324
|
+
const admitted = isLandPrDecision2(decision) ? await runLandPrClaimed(decision) : await spawnClaimed(decision);
|
|
8325
|
+
shouldContinueDispatch = admitted && shouldContinueDispatch;
|
|
7822
8326
|
if (!shouldContinueDispatch) break;
|
|
7823
8327
|
}
|
|
7824
8328
|
}
|
|
@@ -7855,11 +8359,12 @@ async function dispatchRun(args) {
|
|
|
7855
8359
|
diskGate: result.diskGate,
|
|
7856
8360
|
resourceGate: result.resourceGate
|
|
7857
8361
|
};
|
|
8362
|
+
const fatalOutcome = (outcome) => !outcome.started && outcome.nonFatal !== true;
|
|
7858
8363
|
if (pipeline) {
|
|
7859
|
-
return { ok: !outcomes.some(
|
|
8364
|
+
return { ok: !outcomes.some(fatalOutcome), ...summary };
|
|
7860
8365
|
}
|
|
7861
8366
|
console.log(JSON.stringify(summary, null, 2));
|
|
7862
|
-
if (outcomes.some(
|
|
8367
|
+
if (outcomes.some(fatalOutcome)) process.exit(1);
|
|
7863
8368
|
} catch (error) {
|
|
7864
8369
|
if (pipeline) return { ok: false, error: error.message };
|
|
7865
8370
|
console.error(`run dispatch failed: ${error.message}`);
|
|
@@ -8248,7 +8753,7 @@ function discoverProductionDbEnvFilePaths(options = {}) {
|
|
|
8248
8753
|
}
|
|
8249
8754
|
|
|
8250
8755
|
// src/db-credential-resolver.ts
|
|
8251
|
-
import { execFileSync, spawnSync as
|
|
8756
|
+
import { execFileSync, spawnSync as spawnSync6 } from "node:child_process";
|
|
8252
8757
|
import * as fs2 from "node:fs";
|
|
8253
8758
|
import * as path29 from "node:path";
|
|
8254
8759
|
|
|
@@ -8334,7 +8839,7 @@ function defaultNeonConnectionString(args) {
|
|
|
8334
8839
|
if (args.pooled) argv.push("--pooled");
|
|
8335
8840
|
let stdout;
|
|
8336
8841
|
try {
|
|
8337
|
-
const result =
|
|
8842
|
+
const result = spawnSync6(neonBin, argv, {
|
|
8338
8843
|
encoding: "utf8",
|
|
8339
8844
|
env: process.env,
|
|
8340
8845
|
timeout: 3e4
|
|
@@ -9786,23 +10291,6 @@ function materialWorktreeChanges2(changedFiles) {
|
|
|
9786
10291
|
});
|
|
9787
10292
|
}
|
|
9788
10293
|
|
|
9789
|
-
// src/cleanup-index-status.ts
|
|
9790
|
-
function indexedWorktreeStatus(entry) {
|
|
9791
|
-
if (!entry.status) {
|
|
9792
|
-
entry.status = computeWorkerStatus(entry.worker, {
|
|
9793
|
-
base: entry.run.base,
|
|
9794
|
-
baseCommit: entry.run.baseCommit
|
|
9795
|
-
});
|
|
9796
|
-
}
|
|
9797
|
-
return entry.status;
|
|
9798
|
-
}
|
|
9799
|
-
function indexedWorktreeHasMaterialChanges(entry) {
|
|
9800
|
-
if (entry.status) {
|
|
9801
|
-
return materialWorktreeChanges2(entry.status.changedFiles).length > 0;
|
|
9802
|
-
}
|
|
9803
|
-
return materialWorktreeChanges2(gitStatusShort(entry.worktreePath)).length > 0;
|
|
9804
|
-
}
|
|
9805
|
-
|
|
9806
10294
|
// src/cleanup-worktree-salvage.ts
|
|
9807
10295
|
function prUrlFromFinalResult(finalResult) {
|
|
9808
10296
|
if (typeof finalResult === "string") {
|
|
@@ -9829,6 +10317,83 @@ function isPrOrUnmergedWork(status) {
|
|
|
9829
10317
|
return false;
|
|
9830
10318
|
}
|
|
9831
10319
|
|
|
10320
|
+
// src/cleanup-index-status.ts
|
|
10321
|
+
function indexedWorktreeStatus(entry) {
|
|
10322
|
+
if (!entry.status) {
|
|
10323
|
+
entry.status = computeWorkerStatus(entry.worker, {
|
|
10324
|
+
base: entry.run.base,
|
|
10325
|
+
baseCommit: entry.run.baseCommit
|
|
10326
|
+
});
|
|
10327
|
+
}
|
|
10328
|
+
return entry.status;
|
|
10329
|
+
}
|
|
10330
|
+
function indexedWorktreeHasMaterialChanges(entry, gitStatusCache) {
|
|
10331
|
+
if (entry.status) {
|
|
10332
|
+
return materialWorktreeChanges2(entry.status.changedFiles).length > 0;
|
|
10333
|
+
}
|
|
10334
|
+
const porcelain = gitStatusCache ? gitStatusCache.porcelain(entry.worktreePath) : gitStatusShort(entry.worktreePath);
|
|
10335
|
+
return materialWorktreeChanges2(porcelain).length > 0;
|
|
10336
|
+
}
|
|
10337
|
+
function finalResultFromWorkerJson(entry) {
|
|
10338
|
+
const snapshot = entry.worker.completionSnapshot?.finalResult;
|
|
10339
|
+
if (snapshot !== void 0 && snapshot !== null) return snapshot;
|
|
10340
|
+
if (entry.worker.taskPrUrl) {
|
|
10341
|
+
return { prUrl: entry.worker.taskPrUrl };
|
|
10342
|
+
}
|
|
10343
|
+
return null;
|
|
10344
|
+
}
|
|
10345
|
+
function resolveWorktreeGuardStatus(entry, ctx) {
|
|
10346
|
+
if (entry.status) return entry.status;
|
|
10347
|
+
const worker = entry.worker;
|
|
10348
|
+
const completionAcknowledged = typeof worker.completionReportedAt === "string" && worker.completionReportedAt.trim().length > 0;
|
|
10349
|
+
const workerJsonTerminal = Boolean(worker.status && ["done", "exited", "blocked", "failed", "abandoned"].includes(worker.status)) || completionAcknowledged;
|
|
10350
|
+
const finalResult = finalResultFromWorkerJson(entry);
|
|
10351
|
+
if (workerJsonTerminal && !isPidAlive(worker.pid)) {
|
|
10352
|
+
const changedFiles = ctx?.gitStatusCache ? ctx.gitStatusCache.porcelain(entry.worktreePath) : gitStatusShort(entry.worktreePath);
|
|
10353
|
+
const baseLabel = entry.run.baseCommit?.trim() || entry.run.base?.trim() || "origin/main";
|
|
10354
|
+
const ahead = ctx?.gitRevCache?.countAheadOfMain(entry.worktreePath, baseLabel);
|
|
10355
|
+
const gitAncestry = ahead === 0 ? {
|
|
10356
|
+
checked: true,
|
|
10357
|
+
base: baseLabel,
|
|
10358
|
+
relation: "synced"
|
|
10359
|
+
} : computeGitAncestry(entry.worktreePath, {
|
|
10360
|
+
base: entry.run.base,
|
|
10361
|
+
baseCommit: entry.run.baseCommit
|
|
10362
|
+
});
|
|
10363
|
+
const status = {
|
|
10364
|
+
runId: entry.runId,
|
|
10365
|
+
worker: entry.workerName,
|
|
10366
|
+
pid: worker.pid,
|
|
10367
|
+
alive: false,
|
|
10368
|
+
status: worker.status ?? (completionAcknowledged ? "done" : "exited"),
|
|
10369
|
+
attention: { state: completionAcknowledged ? "done" : "stale" },
|
|
10370
|
+
branch: worker.branch,
|
|
10371
|
+
worktreePath: entry.worktreePath,
|
|
10372
|
+
ownedPaths: worker.ownedPaths,
|
|
10373
|
+
stdoutBytes: 0,
|
|
10374
|
+
stderrBytes: 0,
|
|
10375
|
+
heartbeatBytes: 0,
|
|
10376
|
+
firstEventAt: null,
|
|
10377
|
+
lastEventAt: null,
|
|
10378
|
+
lastActivityAt: worker.completionReportedAt ?? null,
|
|
10379
|
+
currentTool: null,
|
|
10380
|
+
heartbeatCount: 0,
|
|
10381
|
+
lastHeartbeatAt: null,
|
|
10382
|
+
lastHeartbeatPhase: null,
|
|
10383
|
+
lastHeartbeatSummary: null,
|
|
10384
|
+
heartbeatBlocker: null,
|
|
10385
|
+
changedFiles,
|
|
10386
|
+
gitAncestry,
|
|
10387
|
+
finalResult,
|
|
10388
|
+
completionBlocker: typeof worker.completionBlocker === "string" ? worker.completionBlocker.trim() || null : null,
|
|
10389
|
+
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? prUrlFromFinalResult(finalResult)
|
|
10390
|
+
};
|
|
10391
|
+
entry.status = status;
|
|
10392
|
+
return status;
|
|
10393
|
+
}
|
|
10394
|
+
return indexedWorktreeStatus(entry);
|
|
10395
|
+
}
|
|
10396
|
+
|
|
9832
10397
|
// src/cleanup-completion-blocker.ts
|
|
9833
10398
|
function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
9834
10399
|
const blocker = typeof indexed.worker.completionBlocker === "string" ? indexed.worker.completionBlocker.trim() : "";
|
|
@@ -9849,13 +10414,28 @@ function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
|
9849
10414
|
}
|
|
9850
10415
|
|
|
9851
10416
|
// src/cleanup-run-liveness.ts
|
|
10417
|
+
var TERMINAL_WORKER_JSON_STATUSES = /* @__PURE__ */ new Set([
|
|
10418
|
+
"done",
|
|
10419
|
+
"exited",
|
|
10420
|
+
"blocked",
|
|
10421
|
+
"failed",
|
|
10422
|
+
"abandoned"
|
|
10423
|
+
]);
|
|
9852
10424
|
function deriveRunTerminal(indexed, ctx) {
|
|
9853
10425
|
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
9854
10426
|
return deriveTerminalRunStatus(indexed.run);
|
|
9855
10427
|
}
|
|
9856
10428
|
function isWorkerProcessLive(indexed) {
|
|
9857
10429
|
if (isPidAlive(indexed.worker.pid)) return true;
|
|
9858
|
-
if (
|
|
10430
|
+
if (typeof indexed.worker.completionReportedAt === "string" && indexed.worker.completionReportedAt.trim().length > 0) {
|
|
10431
|
+
return false;
|
|
10432
|
+
}
|
|
10433
|
+
const workerStatus2 = indexed.worker.status;
|
|
10434
|
+
if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2)) return false;
|
|
10435
|
+
if (!indexed.worker.pid) {
|
|
10436
|
+
if (workerStatus2 !== "running") return false;
|
|
10437
|
+
return indexedWorktreeStatus(indexed).alive;
|
|
10438
|
+
}
|
|
9859
10439
|
return false;
|
|
9860
10440
|
}
|
|
9861
10441
|
function isRunStaleActive(indexed, ctx) {
|
|
@@ -9864,6 +10444,10 @@ function isRunStaleActive(indexed, ctx) {
|
|
|
9864
10444
|
}
|
|
9865
10445
|
function runBlocksWorktreeRemoval(indexed, ctx) {
|
|
9866
10446
|
if (isWorkerProcessLive(indexed)) return true;
|
|
10447
|
+
const workerStatus2 = indexed.worker.status;
|
|
10448
|
+
if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2) && !indexed.worker.completionBlocker) {
|
|
10449
|
+
return false;
|
|
10450
|
+
}
|
|
9867
10451
|
const status = indexedWorktreeStatus(indexed);
|
|
9868
10452
|
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return true;
|
|
9869
10453
|
if (isFinishedWorkerStatus(status)) return false;
|
|
@@ -9882,7 +10466,7 @@ function effectiveWorktreeAgeMs(input) {
|
|
|
9882
10466
|
if (input.liveness && isRunStaleActive(indexed, input.liveness)) {
|
|
9883
10467
|
return terminalWorktreesAgeMs;
|
|
9884
10468
|
}
|
|
9885
|
-
if (input.liveness && isFinishedWorkerStatus(
|
|
10469
|
+
if (input.liveness && isFinishedWorkerStatus(resolveWorktreeGuardStatus(indexed, input.liveness)) && !isWorkerProcessLive(indexed)) {
|
|
9886
10470
|
return terminalWorktreesAgeMs;
|
|
9887
10471
|
}
|
|
9888
10472
|
return worktreesAgeMs;
|
|
@@ -9896,8 +10480,13 @@ function skipWorktreeRemoval(input) {
|
|
|
9896
10480
|
const ageThresholdMs = effectiveWorktreeAgeMs(input);
|
|
9897
10481
|
if (worktreesAgeMs <= 0 && !includeOrphans && ageThresholdMs <= 0) return "worktrees_disabled";
|
|
9898
10482
|
if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
|
|
9899
|
-
const status = indexedWorktreeStatus(indexed);
|
|
9900
10483
|
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
10484
|
+
if (indexedWorktreeHasMaterialChanges(indexed, input.liveness?.gitStatusCache)) {
|
|
10485
|
+
return "dirty_worktree";
|
|
10486
|
+
}
|
|
10487
|
+
const ahead = input.liveness?.gitRevCache?.countAheadOfMain(input.worktreePath);
|
|
10488
|
+
if (ahead !== null && ahead !== void 0 && ahead > 0) return "pr_or_unmerged_commits";
|
|
10489
|
+
const status = resolveWorktreeGuardStatus(indexed, input.liveness);
|
|
9901
10490
|
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return "completion_blocked";
|
|
9902
10491
|
if (runBlocksWorktreeRemoval(indexed, input.liveness)) return "run_still_active";
|
|
9903
10492
|
if (!isFinishedWorkerStatus(status)) return "run_still_active";
|
|
@@ -9928,7 +10517,9 @@ function skipDependencyCacheRemoval(input) {
|
|
|
9928
10517
|
if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
9929
10518
|
if (activeWorktreePaths.has(path42.resolve(worktreePath))) return "active_worker";
|
|
9930
10519
|
if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
|
|
9931
|
-
if (indexed && indexedWorktreeHasMaterialChanges(indexed))
|
|
10520
|
+
if (indexed && indexedWorktreeHasMaterialChanges(indexed, input.gitStatusCache)) {
|
|
10521
|
+
return "dirty_worktree";
|
|
10522
|
+
}
|
|
9932
10523
|
return null;
|
|
9933
10524
|
}
|
|
9934
10525
|
function skipBuildCacheRemoval(input) {
|
|
@@ -10126,7 +10717,7 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
10126
10717
|
}
|
|
10127
10718
|
|
|
10128
10719
|
// src/cleanup-privileged-remove.ts
|
|
10129
|
-
import { spawnSync as
|
|
10720
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
10130
10721
|
import path46 from "node:path";
|
|
10131
10722
|
|
|
10132
10723
|
// src/cleanup-harness-path-validate.ts
|
|
@@ -10171,7 +10762,7 @@ function resolvePrivilegedCleanupMode() {
|
|
|
10171
10762
|
return "auto";
|
|
10172
10763
|
}
|
|
10173
10764
|
function runSudoNonInteractive(argv) {
|
|
10174
|
-
const res =
|
|
10765
|
+
const res = spawnSync7("sudo", ["-n", ...argv], {
|
|
10175
10766
|
encoding: "utf8",
|
|
10176
10767
|
stdio: ["ignore", "pipe", "pipe"]
|
|
10177
10768
|
});
|
|
@@ -10911,6 +11502,37 @@ function emitCleanupProgress(phase, detail) {
|
|
|
10911
11502
|
console.error(`[kynver cleanup] ${phase}${suffix}`);
|
|
10912
11503
|
}
|
|
10913
11504
|
|
|
11505
|
+
// src/cleanup-git-rev-cache.ts
|
|
11506
|
+
var CleanupGitRevCache = class {
|
|
11507
|
+
aheadOfMain = /* @__PURE__ */ new Map();
|
|
11508
|
+
countAheadOfMain(worktreePath, base = "origin/main") {
|
|
11509
|
+
const key = `${worktreePath}\0${base}`;
|
|
11510
|
+
if (this.aheadOfMain.has(key)) return this.aheadOfMain.get(key) ?? null;
|
|
11511
|
+
const result = gitCapture(worktreePath, ["rev-list", "--count", `${base}..HEAD`]);
|
|
11512
|
+
if (result.status !== 0) {
|
|
11513
|
+
this.aheadOfMain.set(key, null);
|
|
11514
|
+
return null;
|
|
11515
|
+
}
|
|
11516
|
+
const count = Number(result.stdout.trim());
|
|
11517
|
+
const parsed = Number.isFinite(count) ? count : null;
|
|
11518
|
+
this.aheadOfMain.set(key, parsed);
|
|
11519
|
+
return parsed;
|
|
11520
|
+
}
|
|
11521
|
+
};
|
|
11522
|
+
|
|
11523
|
+
// src/cleanup-git-status-cache.ts
|
|
11524
|
+
var CleanupGitStatusCache = class {
|
|
11525
|
+
cache = /* @__PURE__ */ new Map();
|
|
11526
|
+
porcelain(worktreePath) {
|
|
11527
|
+
const resolved = worktreePath;
|
|
11528
|
+
const cached = this.cache.get(resolved);
|
|
11529
|
+
if (cached !== void 0) return cached;
|
|
11530
|
+
const lines = gitStatusShort(resolved);
|
|
11531
|
+
this.cache.set(resolved, lines);
|
|
11532
|
+
return lines;
|
|
11533
|
+
}
|
|
11534
|
+
};
|
|
11535
|
+
|
|
10914
11536
|
// src/cleanup-run-terminal-cache.ts
|
|
10915
11537
|
var CleanupRunTerminalCache = class {
|
|
10916
11538
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -11028,7 +11650,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
11028
11650
|
emitCleanupProgress("index", "building worktree index");
|
|
11029
11651
|
const index = mergeWorktreeIndexes(paths.scanRoots);
|
|
11030
11652
|
emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
|
|
11031
|
-
const liveness = {
|
|
11653
|
+
const liveness = {
|
|
11654
|
+
runTerminalCache: new CleanupRunTerminalCache(),
|
|
11655
|
+
gitStatusCache: new CleanupGitStatusCache(),
|
|
11656
|
+
gitRevCache: new CleanupGitRevCache()
|
|
11657
|
+
};
|
|
11032
11658
|
const skips = [];
|
|
11033
11659
|
const actions = [];
|
|
11034
11660
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
@@ -11048,8 +11674,15 @@ function runHarnessCleanup(options = {}) {
|
|
|
11048
11674
|
index,
|
|
11049
11675
|
now: paths.now
|
|
11050
11676
|
};
|
|
11051
|
-
|
|
11677
|
+
const dependencyCandidates = scanDependencyCacheCandidates(scanOpts);
|
|
11678
|
+
emitCleanupProgress("dependency", `${dependencyCandidates.length} cache candidate(s) at ${harnessRoot}`);
|
|
11679
|
+
let dependencyProcessed = 0;
|
|
11680
|
+
for (const raw of dependencyCandidates) {
|
|
11052
11681
|
if (atSweepCap()) break;
|
|
11682
|
+
dependencyProcessed += 1;
|
|
11683
|
+
if (dependencyProcessed % 50 === 0) {
|
|
11684
|
+
emitCleanupProgress("dependency", `${dependencyProcessed}/${dependencyCandidates.length} evaluated`);
|
|
11685
|
+
}
|
|
11053
11686
|
const resolved = path53.resolve(raw.path);
|
|
11054
11687
|
if (processedPaths.has(resolved)) continue;
|
|
11055
11688
|
processedPaths.add(resolved);
|
|
@@ -11069,7 +11702,8 @@ function runHarnessCleanup(options = {}) {
|
|
|
11069
11702
|
ageMs: candidate.ageMs,
|
|
11070
11703
|
worktreePath,
|
|
11071
11704
|
activeWorktreePaths: activeGuards.activeWorktreePaths,
|
|
11072
|
-
diskPressure: retention.diskPressure
|
|
11705
|
+
diskPressure: retention.diskPressure,
|
|
11706
|
+
gitStatusCache: liveness.gitStatusCache
|
|
11073
11707
|
});
|
|
11074
11708
|
if (guardReason) {
|
|
11075
11709
|
recordSkip(skips, candidate.path, guardReason);
|
|
@@ -11104,7 +11738,8 @@ function runHarnessCleanup(options = {}) {
|
|
|
11104
11738
|
ageMs: candidate.ageMs,
|
|
11105
11739
|
worktreePath,
|
|
11106
11740
|
activeWorktreePaths: activeGuards.activeWorktreePaths,
|
|
11107
|
-
diskPressure: retention.diskPressure
|
|
11741
|
+
diskPressure: retention.diskPressure,
|
|
11742
|
+
gitStatusCache: liveness.gitStatusCache
|
|
11108
11743
|
});
|
|
11109
11744
|
if (guardReason) {
|
|
11110
11745
|
recordSkip(skips, candidate.path, guardReason);
|
|
@@ -11124,8 +11759,13 @@ function runHarnessCleanup(options = {}) {
|
|
|
11124
11759
|
];
|
|
11125
11760
|
emitCleanupProgress("worktrees", `${worktreeCandidates.length} candidate(s) at ${harnessRoot}`);
|
|
11126
11761
|
const worktreeSeen = /* @__PURE__ */ new Set();
|
|
11762
|
+
let worktreeProcessed = 0;
|
|
11127
11763
|
for (const raw of worktreeCandidates) {
|
|
11128
11764
|
if (atSweepCap()) break;
|
|
11765
|
+
worktreeProcessed += 1;
|
|
11766
|
+
if (worktreeProcessed % 50 === 0) {
|
|
11767
|
+
emitCleanupProgress("worktrees", `${worktreeProcessed}/${worktreeCandidates.length} evaluated`);
|
|
11768
|
+
}
|
|
11129
11769
|
const resolved = path53.resolve(raw.path);
|
|
11130
11770
|
if (worktreeSeen.has(resolved)) continue;
|
|
11131
11771
|
worktreeSeen.add(resolved);
|
|
@@ -11406,11 +12046,29 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
|
|
|
11406
12046
|
};
|
|
11407
12047
|
}
|
|
11408
12048
|
|
|
12049
|
+
// src/daemon-platform-guard.ts
|
|
12050
|
+
function envFlag3(name) {
|
|
12051
|
+
const raw = process.env[name]?.trim().toLowerCase();
|
|
12052
|
+
return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
|
|
12053
|
+
}
|
|
12054
|
+
function assertNativeDaemonAllowed() {
|
|
12055
|
+
if (process.platform !== "win32") return;
|
|
12056
|
+
if (envFlag3("KYNVER_DAEMON_ALLOW_NATIVE_WINDOWS")) return;
|
|
12057
|
+
console.error(
|
|
12058
|
+
JSON.stringify({
|
|
12059
|
+
event: "daemon_start_blocked",
|
|
12060
|
+
reason: "native_windows_console_flash",
|
|
12061
|
+
remedy: "Run the daemon inside WSL: .\\scripts\\start-tier2-wsl.ps1 \u2014 or set KYNVER_DAEMON_ALLOW_NATIVE_WINDOWS=1 to override (flashes visible consoles)."
|
|
12062
|
+
})
|
|
12063
|
+
);
|
|
12064
|
+
process.exit(1);
|
|
12065
|
+
}
|
|
12066
|
+
|
|
11409
12067
|
// src/cron/cron-env.ts
|
|
11410
12068
|
import { existsSync as existsSync42 } from "node:fs";
|
|
11411
12069
|
import { homedir as homedir14 } from "node:os";
|
|
11412
12070
|
import path55 from "node:path";
|
|
11413
|
-
function
|
|
12071
|
+
function envFlag4(name, defaultValue) {
|
|
11414
12072
|
const raw = process.env[name]?.trim().toLowerCase();
|
|
11415
12073
|
if (!raw) return defaultValue;
|
|
11416
12074
|
if (raw === "0" || raw === "false" || raw === "no" || raw === "off") return false;
|
|
@@ -11446,14 +12104,14 @@ function resolveKynverCronEnv() {
|
|
|
11446
12104
|
const secret = resolveKynverCronSecret();
|
|
11447
12105
|
const credsReady = Boolean(fireBaseUrl && secret);
|
|
11448
12106
|
const storeExists = existsSync42(storePath);
|
|
11449
|
-
const defaultEnabled = credsReady && (storeExists ||
|
|
12107
|
+
const defaultEnabled = credsReady && (storeExists || envFlag4("KYNVER_CRON_TICK_FORCE", false));
|
|
11450
12108
|
return {
|
|
11451
12109
|
storePath,
|
|
11452
12110
|
statePath,
|
|
11453
12111
|
lockPath: `${statePath}.lock`,
|
|
11454
12112
|
fireBaseUrl,
|
|
11455
12113
|
secret,
|
|
11456
|
-
tickEnabled:
|
|
12114
|
+
tickEnabled: envFlag4("KYNVER_CRON_TICK_ENABLED", defaultEnabled),
|
|
11457
12115
|
tickIntervalMs: envInt("KYNVER_CRON_TICK_INTERVAL_MS", 6e4, 5e3),
|
|
11458
12116
|
missedRunPolicy: process.env.KYNVER_CRON_MISSED_RUN_POLICY?.trim().toLowerCase() === "skip" ? "skip" : "catch_up",
|
|
11459
12117
|
maxCatchUpPerTick: envInt("KYNVER_CRON_MAX_CATCH_UP_PER_TICK", 3, 0),
|
|
@@ -12245,6 +12903,7 @@ async function awaitDaemonBackoff(ms, isStopping) {
|
|
|
12245
12903
|
}
|
|
12246
12904
|
}
|
|
12247
12905
|
async function runDaemon(args) {
|
|
12906
|
+
assertNativeDaemonAllowed();
|
|
12248
12907
|
const runId = String(required(String(args.run || ""), "--run"));
|
|
12249
12908
|
const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
|
|
12250
12909
|
const execute = args.execute !== false && args.execute !== "false";
|
|
@@ -12349,7 +13008,7 @@ function formatNodeOptionsFlag(mb = resolveNodeOldSpaceSizeMb()) {
|
|
|
12349
13008
|
}
|
|
12350
13009
|
|
|
12351
13010
|
// src/bounded-build/systemd-wrap.ts
|
|
12352
|
-
import { spawnSync as
|
|
13011
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12353
13012
|
var systemdAvailableCache;
|
|
12354
13013
|
function isSystemdRunAvailable() {
|
|
12355
13014
|
if (process.env.KYNVER_BUILD_SKIP_SYSTEMD === "1" || process.env.KYNVER_BUILD_SKIP_SYSTEMD === "true") {
|
|
@@ -12360,7 +13019,7 @@ function isSystemdRunAvailable() {
|
|
|
12360
13019
|
systemdAvailableCache = false;
|
|
12361
13020
|
return false;
|
|
12362
13021
|
}
|
|
12363
|
-
const res =
|
|
13022
|
+
const res = spawnSync8("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
|
|
12364
13023
|
systemdAvailableCache = res.status === 0;
|
|
12365
13024
|
return systemdAvailableCache;
|
|
12366
13025
|
}
|
|
@@ -12384,7 +13043,7 @@ function buildSystemdRunArgv(opts) {
|
|
|
12384
13043
|
}
|
|
12385
13044
|
|
|
12386
13045
|
// src/bounded-build/admission.ts
|
|
12387
|
-
import { spawnSync as
|
|
13046
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12388
13047
|
function positiveInt4(value, fallback) {
|
|
12389
13048
|
const n = Number(value);
|
|
12390
13049
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
@@ -12420,7 +13079,7 @@ function assessBuildAdmission(opts = {}) {
|
|
|
12420
13079
|
}
|
|
12421
13080
|
function sleepMs2(ms) {
|
|
12422
13081
|
if (ms <= 0) return;
|
|
12423
|
-
|
|
13082
|
+
spawnSync9(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
12424
13083
|
stdio: "ignore"
|
|
12425
13084
|
});
|
|
12426
13085
|
}
|
|
@@ -12441,7 +13100,7 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
|
|
|
12441
13100
|
}
|
|
12442
13101
|
|
|
12443
13102
|
// src/bounded-build/exec.ts
|
|
12444
|
-
import { spawnSync as
|
|
13103
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
12445
13104
|
|
|
12446
13105
|
// src/heavy-verification/slot.ts
|
|
12447
13106
|
import {
|
|
@@ -12647,10 +13306,10 @@ function assessHeavyVerificationGate(command, opts = {}) {
|
|
|
12647
13306
|
}
|
|
12648
13307
|
|
|
12649
13308
|
// src/heavy-verification/gate.ts
|
|
12650
|
-
import { spawnSync as
|
|
13309
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
12651
13310
|
function sleepMs3(ms) {
|
|
12652
13311
|
if (ms <= 0) return;
|
|
12653
|
-
|
|
13312
|
+
spawnSync10(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
12654
13313
|
stdio: "ignore"
|
|
12655
13314
|
});
|
|
12656
13315
|
}
|
|
@@ -12693,7 +13352,7 @@ function envArgv(env) {
|
|
|
12693
13352
|
return out;
|
|
12694
13353
|
}
|
|
12695
13354
|
function runSpawn(argv, opts) {
|
|
12696
|
-
const res =
|
|
13355
|
+
const res = spawnSync11(argv[0], argv.slice(1), {
|
|
12697
13356
|
cwd: opts.cwd,
|
|
12698
13357
|
env: opts.env,
|
|
12699
13358
|
encoding: "utf8",
|
|
@@ -14096,10 +14755,10 @@ import path70 from "node:path";
|
|
|
14096
14755
|
import { accessSync, constants, existsSync as existsSync47, readFileSync as readFileSync19 } from "node:fs";
|
|
14097
14756
|
import { homedir as homedir16 } from "node:os";
|
|
14098
14757
|
import path69 from "node:path";
|
|
14099
|
-
import { spawnSync as
|
|
14758
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
14100
14759
|
function captureCommand(bin, args) {
|
|
14101
14760
|
try {
|
|
14102
|
-
const res =
|
|
14761
|
+
const res = spawnSync12(bin, args, { encoding: "utf8" });
|
|
14103
14762
|
const stdout = (res.stdout || "").trim();
|
|
14104
14763
|
const stderr = (res.stderr || "").trim();
|
|
14105
14764
|
const ok = res.status === 0;
|
|
@@ -14348,12 +15007,12 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
|
|
|
14348
15007
|
return check({
|
|
14349
15008
|
id: "hotspot_openclaw_scheduler",
|
|
14350
15009
|
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
14351
|
-
status: "
|
|
14352
|
-
summary: "
|
|
14353
|
-
remediation: "
|
|
15010
|
+
status: "warn",
|
|
15011
|
+
summary: "Hosted deployment with no QSTASH_TOKEN and no KYNVER_SCHEDULER_PROVIDER \u2014 kynver-cron is an in-process stub here and will NOT fire; no firing scheduler is configured",
|
|
15012
|
+
remediation: "Set QSTASH_TOKEN + KYNVER_SCHEDULER_PROVIDER=qstash on the Kynver server for hosted AgentOS schedules. Only use kynver-cron when a `kynver daemon` (or local cron store) actually owns firing on this box.",
|
|
14354
15013
|
details: {
|
|
14355
15014
|
schedulerProvider: null,
|
|
14356
|
-
resolvedFallback: "
|
|
15015
|
+
resolvedFallback: "none",
|
|
14357
15016
|
qstashTokenPresent: false,
|
|
14358
15017
|
hostedDeployment
|
|
14359
15018
|
}
|
|
@@ -14999,7 +15658,7 @@ var LANDING_MAINTAINER_LANE_SPEC = {
|
|
|
14999
15658
|
};
|
|
15000
15659
|
|
|
15001
15660
|
// src/lane/landing-maintainer-local.ts
|
|
15002
|
-
import { spawnSync as
|
|
15661
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
15003
15662
|
import path72 from "node:path";
|
|
15004
15663
|
function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
15005
15664
|
const script = path72.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
|
|
@@ -15013,7 +15672,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
|
15013
15672
|
stderr: ""
|
|
15014
15673
|
};
|
|
15015
15674
|
}
|
|
15016
|
-
const result =
|
|
15675
|
+
const result = spawnSync13("node", args, {
|
|
15017
15676
|
cwd: repoRoot,
|
|
15018
15677
|
encoding: "utf8",
|
|
15019
15678
|
timeout: 10 * 60 * 1e3
|
|
@@ -15155,14 +15814,15 @@ function usage(code = 0) {
|
|
|
15155
15814
|
" kynver runner credential [--agent-os-id ID] [--base-url URL]",
|
|
15156
15815
|
" kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--box-kind forge|ghost] [--repo PATH] [--discover-repo] [--max-workers N] [--provider claude|cursor]",
|
|
15157
15816
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
|
|
15817
|
+
" kynver status --run RUN_ID [--blocked] [--running] [--task TASK_ID] [--worker WORKER] [--full] # top-level compact run status",
|
|
15158
15818
|
" kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
|
|
15159
15819
|
" kynver run list",
|
|
15160
15820
|
" kynver run resolve --name RUN_NAME",
|
|
15161
|
-
" kynver run status --run RUN_ID [--
|
|
15821
|
+
" kynver run status --run RUN_ID [--full] # defaults to compact shallow map with drill-down commands",
|
|
15162
15822
|
" kynver run dispatch --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--execute] [--lane any|implementation|review|landing] [--target-task-id TASK_ID] [--executor harness] [--max-starts 1] [--lease-ms MS] [--owned path[,path]] [--model claude-opus-4-8] [--disk-path /] [--reconcile-stale-blockers]",
|
|
15163
15823
|
" kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
|
|
15164
15824
|
' kynver worker start --run RUN_ID --name worker --task "..." [--owned path[,path]] [--model MODEL] [--provider claude|cursor] [--agent-os-id AOS_ID] [--task-id TASK_ID] [--wait]',
|
|
15165
|
-
" kynver worker list --run RUN_ID [--
|
|
15825
|
+
" kynver worker list --run RUN_ID [--full] # defaults to compact shallow map with drill-down commands",
|
|
15166
15826
|
" kynver worker status --run RUN_ID --name worker",
|
|
15167
15827
|
" kynver worker tail --run RUN_ID --name worker [--lines 40] [--raw]",
|
|
15168
15828
|
" kynver worker stop --run RUN_ID --name worker",
|
|
@@ -15216,6 +15876,9 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
15216
15876
|
const { runsDir, worktreesDir } = getPaths();
|
|
15217
15877
|
mkdirSync10(runsDir, { recursive: true });
|
|
15218
15878
|
mkdirSync10(worktreesDir, { recursive: true });
|
|
15879
|
+
if (scope === "daemon") {
|
|
15880
|
+
assertNativeDaemonAllowed();
|
|
15881
|
+
}
|
|
15219
15882
|
if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
|
|
15220
15883
|
let repoRoot;
|
|
15221
15884
|
const runId = args.run ? String(args.run).trim() : "";
|
|
@@ -15232,6 +15895,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
15232
15895
|
});
|
|
15233
15896
|
}
|
|
15234
15897
|
if (scope === "login") return void await runLogin(args);
|
|
15898
|
+
if (scope === "status") return runStatus(args);
|
|
15235
15899
|
if (scope === "runner" && action === "credential") return void await mintRunnerCredential(args);
|
|
15236
15900
|
if (scope === "setup") return void await runSetup(args);
|
|
15237
15901
|
if (scope === "daemon") return void await runDaemon(args);
|