@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/cli.js
CHANGED
|
@@ -175,7 +175,11 @@ function scrubWorkerEnv(env) {
|
|
|
175
175
|
|
|
176
176
|
// src/git.ts
|
|
177
177
|
function git(cwd, args, options = {}) {
|
|
178
|
-
const res = spawnSync(
|
|
178
|
+
const res = spawnSync(
|
|
179
|
+
"git",
|
|
180
|
+
args,
|
|
181
|
+
hiddenSpawnOptions({ cwd, encoding: "utf8" })
|
|
182
|
+
);
|
|
179
183
|
if (res.status !== 0 && !options.allowFailure) {
|
|
180
184
|
const message = `git ${args.join(" ")} failed: ${res.stderr || res.stdout}`;
|
|
181
185
|
if (options.throwError) throw new Error(message);
|
|
@@ -191,7 +195,11 @@ function gitStatusShort(worktreePath) {
|
|
|
191
195
|
}
|
|
192
196
|
function gitCapture(cwd, args) {
|
|
193
197
|
try {
|
|
194
|
-
const res = spawnSync(
|
|
198
|
+
const res = spawnSync(
|
|
199
|
+
"git",
|
|
200
|
+
args,
|
|
201
|
+
hiddenSpawnOptions({ cwd, encoding: "utf8" })
|
|
202
|
+
);
|
|
195
203
|
return {
|
|
196
204
|
status: res.status,
|
|
197
205
|
stdout: res.stdout || "",
|
|
@@ -5648,32 +5656,98 @@ function workerStatus(args) {
|
|
|
5648
5656
|
writeJson(path19.join(worker.workerDir, "last-status.json"), status);
|
|
5649
5657
|
console.log(JSON.stringify(status, null, 2));
|
|
5650
5658
|
}
|
|
5659
|
+
function buildWorkerListDrilldownCommands(runId) {
|
|
5660
|
+
return {
|
|
5661
|
+
full: `kynver worker list --run ${runId} --full`,
|
|
5662
|
+
blocked: `kynver worker list --run ${runId} --blocked`,
|
|
5663
|
+
running: `kynver worker list --run ${runId} --running`,
|
|
5664
|
+
task: `kynver worker list --run ${runId} --task <task-id>`,
|
|
5665
|
+
worker: `kynver worker list --run ${runId} --worker <worker>`,
|
|
5666
|
+
runFull: `kynver run status --run ${runId} --full`,
|
|
5667
|
+
workerFull: `kynver worker status --run ${runId} --name <worker>`,
|
|
5668
|
+
workerTail: `kynver worker tail --run ${runId} --name <worker> --lines 80`
|
|
5669
|
+
};
|
|
5670
|
+
}
|
|
5671
|
+
function asOptionalArg(value) {
|
|
5672
|
+
if (typeof value !== "string") return void 0;
|
|
5673
|
+
const trimmed = value.trim();
|
|
5674
|
+
return trimmed.length ? trimmed : void 0;
|
|
5675
|
+
}
|
|
5676
|
+
function activeWorkerFilters(args) {
|
|
5677
|
+
const filters = {};
|
|
5678
|
+
if (args.blocked === true || args.blocked === "true") filters.blocked = true;
|
|
5679
|
+
if (args.running === true || args.running === "true") filters.running = true;
|
|
5680
|
+
const task = asOptionalArg(args.task ?? args.taskId);
|
|
5681
|
+
if (task) filters.task = task;
|
|
5682
|
+
const worker = asOptionalArg(args.worker ?? args.name);
|
|
5683
|
+
if (worker) filters.worker = worker;
|
|
5684
|
+
const status = asOptionalArg(args.status);
|
|
5685
|
+
if (status) filters.status = status;
|
|
5686
|
+
return filters;
|
|
5687
|
+
}
|
|
5688
|
+
function workerMatchesFilters(worker, filters) {
|
|
5689
|
+
if (filters.blocked) {
|
|
5690
|
+
const attention = typeof worker.attention === "string" ? worker.attention : "";
|
|
5691
|
+
const status = typeof worker.status === "string" ? worker.status : "";
|
|
5692
|
+
if (!(attention === "blocked" || attention === "needs_attention" || attention === "stale" || status === "blocked")) {
|
|
5693
|
+
return false;
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
if (filters.running) {
|
|
5697
|
+
if (!(worker.status === "running" && worker.attention !== "blocked" && worker.attention !== "needs_attention")) {
|
|
5698
|
+
return false;
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
if (typeof filters.task === "string" && worker.taskId !== filters.task) return false;
|
|
5702
|
+
if (typeof filters.worker === "string" && worker.worker !== filters.worker) return false;
|
|
5703
|
+
if (typeof filters.status === "string" && worker.status !== filters.status) return false;
|
|
5704
|
+
return true;
|
|
5705
|
+
}
|
|
5706
|
+
function applyWorkerFilters(board, args) {
|
|
5707
|
+
const filters = activeWorkerFilters(args);
|
|
5708
|
+
if (Object.keys(filters).length === 0) return board;
|
|
5709
|
+
const workers = board.workers.filter((worker) => workerMatchesFilters(worker, filters));
|
|
5710
|
+
const filtered = {
|
|
5711
|
+
...board,
|
|
5712
|
+
workerCount: workers.length,
|
|
5713
|
+
needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
|
|
5714
|
+
workers,
|
|
5715
|
+
activeFilters: filters
|
|
5716
|
+
};
|
|
5717
|
+
if (board.summary) {
|
|
5718
|
+
filtered.summary = {
|
|
5719
|
+
statusCounts: countBy(workers, "status"),
|
|
5720
|
+
attentionCounts: countBy(workers, "attention"),
|
|
5721
|
+
lifecycleCounts: countBy(workers, "lifecycleStage")
|
|
5722
|
+
};
|
|
5723
|
+
}
|
|
5724
|
+
if (board.controller) filtered.controller = buildControllerSummary(workers);
|
|
5725
|
+
return filtered;
|
|
5726
|
+
}
|
|
5651
5727
|
function workerList(args) {
|
|
5652
5728
|
const runId = resolveRunTargetArg(args);
|
|
5653
|
-
const explicitCompact = args.compact === true || args.compact === "true";
|
|
5654
5729
|
const explicitFull = args.full === true || args.full === "true";
|
|
5655
|
-
const
|
|
5656
|
-
const
|
|
5657
|
-
const compact = explicitCompact || !explicitFull && workerCount > 100;
|
|
5658
|
-
const board = compact ? buildCompactRunBoard(runId) : buildRunBoard(runId);
|
|
5659
|
-
const autoCompact = compact && !explicitCompact;
|
|
5660
|
-
const outputWorkers = autoCompact ? board.workers.filter((worker) => worker.attention && worker.attention !== "done" && worker.attention !== "ok") : board.workers;
|
|
5730
|
+
const board = applyWorkerFilters(explicitFull ? buildRunBoard(runId) : buildCompactRunBoard(runId), args);
|
|
5731
|
+
const outputWorkers = board.workers;
|
|
5661
5732
|
console.log(
|
|
5662
5733
|
JSON.stringify(
|
|
5663
5734
|
{
|
|
5664
5735
|
runId: board.runId,
|
|
5665
5736
|
status: board.status,
|
|
5737
|
+
projection: explicitFull ? "full" : "compact",
|
|
5666
5738
|
workerCount: board.workerCount,
|
|
5667
|
-
...
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5739
|
+
...board.activeFilters ? { activeFilters: board.activeFilters } : {},
|
|
5740
|
+
...!explicitFull ? {
|
|
5741
|
+
resultContract: {
|
|
5742
|
+
projection: "compact",
|
|
5743
|
+
rawWorkerPayloadsOmitted: true,
|
|
5744
|
+
deepDetailAvailableVia: ["--full", "worker status", "worker tail", "run status --full"]
|
|
5745
|
+
},
|
|
5746
|
+
drilldownCommands: buildWorkerListDrilldownCommands(runId)
|
|
5673
5747
|
} : {},
|
|
5674
5748
|
needsAttention: board.needsAttention,
|
|
5675
|
-
...
|
|
5676
|
-
...
|
|
5749
|
+
..."summary" in board ? { summary: board.summary } : {},
|
|
5750
|
+
..."controller" in board ? { controller: board.controller } : {},
|
|
5677
5751
|
workers: outputWorkers
|
|
5678
5752
|
},
|
|
5679
5753
|
null,
|
|
@@ -5941,9 +6015,33 @@ async function publishHarnessBoardSnapshot(args, source) {
|
|
|
5941
6015
|
authRefreshFailure: res.authRefreshFailure
|
|
5942
6016
|
};
|
|
5943
6017
|
}
|
|
6018
|
+
function buildRunStatusDrilldownCommands(runId) {
|
|
6019
|
+
return {
|
|
6020
|
+
full: `kynver run status --run ${runId} --full`,
|
|
6021
|
+
blocked: `kynver status --run ${runId} --blocked`,
|
|
6022
|
+
running: `kynver status --run ${runId} --running`,
|
|
6023
|
+
task: `kynver status --run ${runId} --task <task-id>`,
|
|
6024
|
+
worker: `kynver status --run ${runId} --worker <worker>`,
|
|
6025
|
+
workersFull: `kynver worker list --run ${runId} --full`,
|
|
6026
|
+
workerFull: `kynver worker status --run ${runId} --name <worker>`,
|
|
6027
|
+
workerTail: `kynver worker tail --run ${runId} --name <worker> --lines 80`,
|
|
6028
|
+
monitorTick: `kynver monitor status --run ${runId} --tick`
|
|
6029
|
+
};
|
|
6030
|
+
}
|
|
5944
6031
|
function runStatus(args) {
|
|
5945
|
-
const
|
|
5946
|
-
const
|
|
6032
|
+
const runId = resolveRunTargetArg(args);
|
|
6033
|
+
const explicitFull = args.full === true || args.full === "true";
|
|
6034
|
+
const board = applyWorkerFilters(explicitFull ? buildRunBoard(runId) : buildCompactRunBoard(runId), args);
|
|
6035
|
+
board.projection = explicitFull ? "full" : "compact";
|
|
6036
|
+
if (!explicitFull) {
|
|
6037
|
+
board.resultContract = {
|
|
6038
|
+
projection: "compact",
|
|
6039
|
+
rawWorkerPayloadsOmitted: true,
|
|
6040
|
+
omittedWorkerFields: ["finalResult", "changedFiles", "gitAncestry", "completionResponse", "stdout/stderr tails"],
|
|
6041
|
+
deepDetailAvailableVia: ["--full", "worker status", "worker tail", "monitor status --tick"]
|
|
6042
|
+
};
|
|
6043
|
+
board.drilldownCommands = buildRunStatusDrilldownCommands(runId);
|
|
6044
|
+
}
|
|
5947
6045
|
console.log(JSON.stringify(board, null, 2));
|
|
5948
6046
|
}
|
|
5949
6047
|
function tailWorker(args) {
|
|
@@ -7038,6 +7136,344 @@ async function releaseDispatchClaimAfterSpawnFailure(input) {
|
|
|
7038
7136
|
};
|
|
7039
7137
|
}
|
|
7040
7138
|
|
|
7139
|
+
// src/landing/land-pr.ts
|
|
7140
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
7141
|
+
|
|
7142
|
+
// src/landing/cli-auth.ts
|
|
7143
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
7144
|
+
function ensureGitHubTokenFromCliAuth() {
|
|
7145
|
+
if (process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim()) {
|
|
7146
|
+
return {
|
|
7147
|
+
source: "env",
|
|
7148
|
+
configured: true,
|
|
7149
|
+
reason: "GitHub token already configured in environment"
|
|
7150
|
+
};
|
|
7151
|
+
}
|
|
7152
|
+
const result = spawnSync4("gh", ["auth", "token"], {
|
|
7153
|
+
encoding: "utf8",
|
|
7154
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7155
|
+
});
|
|
7156
|
+
if (result.status !== 0) {
|
|
7157
|
+
const reason = typeof result.stderr === "string" && result.stderr.trim() ? result.stderr.trim() : "gh auth token failed; run `gh auth login` on this machine";
|
|
7158
|
+
return { source: "missing", configured: false, reason };
|
|
7159
|
+
}
|
|
7160
|
+
const token = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
7161
|
+
if (!token) {
|
|
7162
|
+
return {
|
|
7163
|
+
source: "missing",
|
|
7164
|
+
configured: false,
|
|
7165
|
+
reason: "gh auth token returned an empty token; run `gh auth login` on this machine"
|
|
7166
|
+
};
|
|
7167
|
+
}
|
|
7168
|
+
process.env.GH_TOKEN = token;
|
|
7169
|
+
return {
|
|
7170
|
+
source: "gh-cli",
|
|
7171
|
+
configured: true,
|
|
7172
|
+
reason: "Using local GitHub CLI auth for daemon land_pr merge"
|
|
7173
|
+
};
|
|
7174
|
+
}
|
|
7175
|
+
|
|
7176
|
+
// src/landing/land-pr.ts
|
|
7177
|
+
var SUCCESSFUL_CHECK_CONCLUSIONS = /* @__PURE__ */ new Set(["SUCCESS", "SKIPPED", "NEUTRAL"]);
|
|
7178
|
+
var READY_MERGE_STATES = /* @__PURE__ */ new Set(["CLEAN", "HAS_HOOKS"]);
|
|
7179
|
+
function repoArgs(repo) {
|
|
7180
|
+
return repo?.trim() ? ["--repo", repo.trim()] : [];
|
|
7181
|
+
}
|
|
7182
|
+
function repoFromPrUrl(prUrl) {
|
|
7183
|
+
const m = /github\.com\/([^/]+)\/([^/]+)\/pull\/\d+/i.exec(prUrl.trim());
|
|
7184
|
+
return m ? `${m[1]}/${m[2]}` : null;
|
|
7185
|
+
}
|
|
7186
|
+
function ghJson(exec, cwd, args) {
|
|
7187
|
+
const res = exec.gh(cwd, args);
|
|
7188
|
+
if (res.status !== 0) {
|
|
7189
|
+
throw new Error(res.stderr || res.stdout || `gh ${args.join(" ")} failed`);
|
|
7190
|
+
}
|
|
7191
|
+
return JSON.parse(res.stdout || "{}");
|
|
7192
|
+
}
|
|
7193
|
+
function checkName(check3) {
|
|
7194
|
+
return typeof check3.name === "string" && check3.name || typeof check3.context === "string" && check3.context || typeof check3.workflowName === "string" && check3.workflowName || "unknown check";
|
|
7195
|
+
}
|
|
7196
|
+
function classifyChecks(statusCheckRollup) {
|
|
7197
|
+
const checks = Array.isArray(statusCheckRollup) ? statusCheckRollup : [];
|
|
7198
|
+
const pending = [];
|
|
7199
|
+
const failed = [];
|
|
7200
|
+
for (const raw of checks) {
|
|
7201
|
+
const check3 = raw && typeof raw === "object" ? raw : {};
|
|
7202
|
+
const conclusion = typeof check3.conclusion === "string" ? check3.conclusion.toUpperCase() : "";
|
|
7203
|
+
const status = typeof check3.status === "string" ? check3.status.toUpperCase() : "";
|
|
7204
|
+
const state = typeof check3.state === "string" ? check3.state.toUpperCase() : "";
|
|
7205
|
+
if (conclusion && SUCCESSFUL_CHECK_CONCLUSIONS.has(conclusion)) continue;
|
|
7206
|
+
if (conclusion) {
|
|
7207
|
+
failed.push(`${checkName(check3)}=${conclusion}`);
|
|
7208
|
+
continue;
|
|
7209
|
+
}
|
|
7210
|
+
if (state && SUCCESSFUL_CHECK_CONCLUSIONS.has(state)) continue;
|
|
7211
|
+
if (state && state !== "PENDING") {
|
|
7212
|
+
failed.push(`${checkName(check3)}=${state}`);
|
|
7213
|
+
continue;
|
|
7214
|
+
}
|
|
7215
|
+
if (state === "PENDING") {
|
|
7216
|
+
pending.push(`${checkName(check3)}=${state}`);
|
|
7217
|
+
continue;
|
|
7218
|
+
}
|
|
7219
|
+
if (status && status !== "COMPLETED") {
|
|
7220
|
+
pending.push(`${checkName(check3)}=${status}`);
|
|
7221
|
+
continue;
|
|
7222
|
+
}
|
|
7223
|
+
pending.push(`${checkName(check3)}=PENDING`);
|
|
7224
|
+
}
|
|
7225
|
+
return { pending, failed };
|
|
7226
|
+
}
|
|
7227
|
+
function vercelCheckSuccess(statusCheckRollup) {
|
|
7228
|
+
const checks = Array.isArray(statusCheckRollup) ? statusCheckRollup : [];
|
|
7229
|
+
return checks.some((raw) => {
|
|
7230
|
+
const check3 = raw && typeof raw === "object" ? raw : {};
|
|
7231
|
+
const name = typeof check3.name === "string" && check3.name || typeof check3.context === "string" && check3.context || "";
|
|
7232
|
+
const conclusion = typeof check3.conclusion === "string" ? check3.conclusion.toUpperCase() : "";
|
|
7233
|
+
const state = typeof check3.state === "string" ? check3.state.toUpperCase() : "";
|
|
7234
|
+
return /^vercel/i.test(name) && (conclusion === "SUCCESS" || state === "SUCCESS");
|
|
7235
|
+
});
|
|
7236
|
+
}
|
|
7237
|
+
var STRUCTURED_SECTION_RE = /^##\s*(test\s*plan|verification|test\s*evidence|verify)\b/im;
|
|
7238
|
+
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;
|
|
7239
|
+
var DOCS_TITLE_RE = /^docs[(:]/i;
|
|
7240
|
+
var DOCS_BODY_RE = /docs[- ]only|documentation only|no[- ]code change|no code changes|low[- ]risk|plan tracker only|markdown only/i;
|
|
7241
|
+
function runtimeVerificationEvidenceSufficient(input) {
|
|
7242
|
+
const title = typeof input.title === "string" ? input.title : "";
|
|
7243
|
+
const body = typeof input.body === "string" ? input.body : "";
|
|
7244
|
+
const docsLowRisk = DOCS_TITLE_RE.test(title) || DOCS_BODY_RE.test(body);
|
|
7245
|
+
const structuredSection = STRUCTURED_SECTION_RE.test(body);
|
|
7246
|
+
const localVerification = LOCAL_VERIFICATION_RE.test(body);
|
|
7247
|
+
return docsLowRisk || structuredSection || localVerification || input.vercelCheckSuccess === true;
|
|
7248
|
+
}
|
|
7249
|
+
function assertVerificationEvidence(pr) {
|
|
7250
|
+
const sufficient = runtimeVerificationEvidenceSufficient({
|
|
7251
|
+
title: pr.title,
|
|
7252
|
+
body: pr.body,
|
|
7253
|
+
vercelCheckSuccess: vercelCheckSuccess(pr.statusCheckRollup)
|
|
7254
|
+
});
|
|
7255
|
+
if (sufficient) return;
|
|
7256
|
+
throw new Error(
|
|
7257
|
+
`PR #${pr.number} lacks landing verification evidence \u2014 add ## Test plan / ## Verification with local commands, Vercel preview, or docs/no-code rationale`
|
|
7258
|
+
);
|
|
7259
|
+
}
|
|
7260
|
+
function assertLandingReady(pr) {
|
|
7261
|
+
if (pr.state !== "OPEN") throw new Error(`PR #${pr.number} is ${pr.state}, not OPEN`);
|
|
7262
|
+
if (pr.isDraft) throw new Error(`PR #${pr.number} is still a draft`);
|
|
7263
|
+
if (!READY_MERGE_STATES.has(pr.mergeStateStatus)) {
|
|
7264
|
+
throw new Error(`PR #${pr.number} mergeStateStatus is ${pr.mergeStateStatus}`);
|
|
7265
|
+
}
|
|
7266
|
+
const checks = classifyChecks(pr.statusCheckRollup);
|
|
7267
|
+
if (checks.failed.length > 0) {
|
|
7268
|
+
throw new Error(`PR #${pr.number} has failing checks: ${checks.failed.join(", ")}`);
|
|
7269
|
+
}
|
|
7270
|
+
if (checks.pending.length > 0) {
|
|
7271
|
+
throw new Error(`PR #${pr.number} has pending checks: ${checks.pending.join(", ")}`);
|
|
7272
|
+
}
|
|
7273
|
+
assertVerificationEvidence(pr);
|
|
7274
|
+
}
|
|
7275
|
+
function landingReadinessError(pr) {
|
|
7276
|
+
try {
|
|
7277
|
+
assertLandingReady(pr);
|
|
7278
|
+
return null;
|
|
7279
|
+
} catch (err) {
|
|
7280
|
+
return err instanceof Error ? err.message : String(err);
|
|
7281
|
+
}
|
|
7282
|
+
}
|
|
7283
|
+
function deleteRemoteBranch(exec, cwd, repo, branch) {
|
|
7284
|
+
if (!branch?.trim()) return;
|
|
7285
|
+
const refPath = encodeURI(`heads/${branch}`);
|
|
7286
|
+
exec.gh(cwd, ["api", "-X", "DELETE", `repos/${repo}/git/refs/${refPath}`]);
|
|
7287
|
+
}
|
|
7288
|
+
function resolveRepo(exec, cwd, repo) {
|
|
7289
|
+
if (repo?.trim()) return repo.trim();
|
|
7290
|
+
const res = exec.gh(cwd, ["repo", "view", "--json", "nameWithOwner"]);
|
|
7291
|
+
if (res.status !== 0) throw new Error(res.stderr || "Could not resolve GitHub repo");
|
|
7292
|
+
const parsed = JSON.parse(res.stdout || "{}");
|
|
7293
|
+
if (!parsed.nameWithOwner) throw new Error("Could not resolve GitHub repo");
|
|
7294
|
+
return parsed.nameWithOwner;
|
|
7295
|
+
}
|
|
7296
|
+
function removeBranchWorktrees(cwd, branch) {
|
|
7297
|
+
if (!branch?.trim()) return;
|
|
7298
|
+
const list = spawnSync5("git", ["worktree", "list", "--porcelain"], {
|
|
7299
|
+
cwd,
|
|
7300
|
+
encoding: "utf8"
|
|
7301
|
+
});
|
|
7302
|
+
if (list.status !== 0) return;
|
|
7303
|
+
const lines = String(list.stdout || "").split(/\r?\n/);
|
|
7304
|
+
let currentPath = null;
|
|
7305
|
+
let currentBranch = null;
|
|
7306
|
+
for (const line of lines) {
|
|
7307
|
+
if (line.startsWith("worktree ")) {
|
|
7308
|
+
currentPath = line.slice("worktree ".length).trim();
|
|
7309
|
+
currentBranch = null;
|
|
7310
|
+
continue;
|
|
7311
|
+
}
|
|
7312
|
+
if (line.startsWith("branch ") && currentPath) {
|
|
7313
|
+
currentBranch = line.slice("branch ".length).trim();
|
|
7314
|
+
if (currentBranch.endsWith(`/${branch}`)) {
|
|
7315
|
+
spawnSync5("git", ["worktree", "remove", "--force", currentPath], {
|
|
7316
|
+
cwd,
|
|
7317
|
+
encoding: "utf8"
|
|
7318
|
+
});
|
|
7319
|
+
}
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
}
|
|
7323
|
+
async function executeLandPrMerge(input) {
|
|
7324
|
+
const cwd = input.cwd ?? process.cwd();
|
|
7325
|
+
const exec = input.exec ?? defaultPrHandoffExec;
|
|
7326
|
+
const prTarget = input.prUrl.trim();
|
|
7327
|
+
if (!prTarget) throw new Error("prUrl is required");
|
|
7328
|
+
const auth = ensureGitHubTokenFromCliAuth();
|
|
7329
|
+
if (!auth.configured) {
|
|
7330
|
+
return {
|
|
7331
|
+
prUrl: prTarget,
|
|
7332
|
+
outcome: "blocked",
|
|
7333
|
+
reason: auth.reason
|
|
7334
|
+
};
|
|
7335
|
+
}
|
|
7336
|
+
const before = ghJson(exec, cwd, [
|
|
7337
|
+
"pr",
|
|
7338
|
+
"view",
|
|
7339
|
+
prTarget,
|
|
7340
|
+
...repoArgs(input.repo),
|
|
7341
|
+
"--json",
|
|
7342
|
+
[
|
|
7343
|
+
"number",
|
|
7344
|
+
"url",
|
|
7345
|
+
"title",
|
|
7346
|
+
"body",
|
|
7347
|
+
"state",
|
|
7348
|
+
"isDraft",
|
|
7349
|
+
"mergeStateStatus",
|
|
7350
|
+
"statusCheckRollup",
|
|
7351
|
+
"headRefName",
|
|
7352
|
+
"mergedAt",
|
|
7353
|
+
"mergeCommit"
|
|
7354
|
+
].join(",")
|
|
7355
|
+
]);
|
|
7356
|
+
if (before.state === "MERGED" || before.mergedAt) {
|
|
7357
|
+
return {
|
|
7358
|
+
prUrl: before.url || prTarget,
|
|
7359
|
+
outcome: "skipped",
|
|
7360
|
+
mergeCommit: before.mergeCommit?.oid ?? null,
|
|
7361
|
+
reason: `PR #${before.number} is already merged \u2014 land_pr no-op (redelivery)`
|
|
7362
|
+
};
|
|
7363
|
+
}
|
|
7364
|
+
const notReadyReason = landingReadinessError(before);
|
|
7365
|
+
if (notReadyReason) {
|
|
7366
|
+
if (input.skipNotReady) {
|
|
7367
|
+
return {
|
|
7368
|
+
prUrl: before.url || prTarget,
|
|
7369
|
+
outcome: "skipped",
|
|
7370
|
+
reason: notReadyReason
|
|
7371
|
+
};
|
|
7372
|
+
}
|
|
7373
|
+
return {
|
|
7374
|
+
prUrl: before.url || prTarget,
|
|
7375
|
+
outcome: "blocked",
|
|
7376
|
+
reason: notReadyReason
|
|
7377
|
+
};
|
|
7378
|
+
}
|
|
7379
|
+
if (input.dryRun) {
|
|
7380
|
+
return {
|
|
7381
|
+
prUrl: before.url || prTarget,
|
|
7382
|
+
outcome: "skipped",
|
|
7383
|
+
reason: "dry-run: PR is ready to merge"
|
|
7384
|
+
};
|
|
7385
|
+
}
|
|
7386
|
+
const target = String(before.number);
|
|
7387
|
+
const mergeRes = exec.gh(cwd, [
|
|
7388
|
+
"pr",
|
|
7389
|
+
"merge",
|
|
7390
|
+
target,
|
|
7391
|
+
...repoArgs(input.repo),
|
|
7392
|
+
"--squash"
|
|
7393
|
+
]);
|
|
7394
|
+
if (mergeRes.status !== 0) {
|
|
7395
|
+
return {
|
|
7396
|
+
prUrl: before.url || prTarget,
|
|
7397
|
+
outcome: "blocked",
|
|
7398
|
+
reason: mergeRes.stderr || mergeRes.stdout || "gh pr merge failed"
|
|
7399
|
+
};
|
|
7400
|
+
}
|
|
7401
|
+
const after = ghJson(exec, cwd, [
|
|
7402
|
+
"pr",
|
|
7403
|
+
"view",
|
|
7404
|
+
target,
|
|
7405
|
+
...repoArgs(input.repo),
|
|
7406
|
+
"--json",
|
|
7407
|
+
"number,url,mergedAt,mergeCommit,state"
|
|
7408
|
+
]);
|
|
7409
|
+
if (after.state !== "MERGED" && !after.mergedAt) {
|
|
7410
|
+
return {
|
|
7411
|
+
prUrl: after.url || before.url || prTarget,
|
|
7412
|
+
outcome: "blocked",
|
|
7413
|
+
reason: `PR #${after.number} did not verify as merged after gh pr merge`
|
|
7414
|
+
};
|
|
7415
|
+
}
|
|
7416
|
+
const repo = resolveRepo(exec, cwd, input.repo);
|
|
7417
|
+
deleteRemoteBranch(exec, cwd, repo, before.headRefName);
|
|
7418
|
+
removeBranchWorktrees(cwd, before.headRefName);
|
|
7419
|
+
const mergeCommit = after.mergeCommit?.oid ?? null;
|
|
7420
|
+
return {
|
|
7421
|
+
prUrl: after.url || before.url || prTarget,
|
|
7422
|
+
outcome: "merged",
|
|
7423
|
+
mergeCommit,
|
|
7424
|
+
reason: `Daemon land_pr merged PR #${after.number}`
|
|
7425
|
+
};
|
|
7426
|
+
}
|
|
7427
|
+
|
|
7428
|
+
// src/landing/land-pr-completion-post.ts
|
|
7429
|
+
async function postLandPrHarnessCompletion(input) {
|
|
7430
|
+
const secret = await resolveCallbackSecretWithMint(input.secret, input.agentOsId, {
|
|
7431
|
+
baseUrl: input.baseUrl
|
|
7432
|
+
});
|
|
7433
|
+
const taskId = String(input.task.id);
|
|
7434
|
+
const url = `${input.baseUrl}/api/agent-os/by-id/${encodeURIComponent(input.agentOsId)}/harness/completion`;
|
|
7435
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7436
|
+
const body = {
|
|
7437
|
+
source: "kynver-harness",
|
|
7438
|
+
agentOsId: input.agentOsId,
|
|
7439
|
+
runId: input.runId,
|
|
7440
|
+
workerName: `land-pr-${taskId}`,
|
|
7441
|
+
taskId,
|
|
7442
|
+
leaseToken: input.task.leaseToken ?? null,
|
|
7443
|
+
startedAt: finishedAt,
|
|
7444
|
+
finishedAt,
|
|
7445
|
+
status: {
|
|
7446
|
+
finalResult: input.report,
|
|
7447
|
+
prUrl: input.report.prUrl,
|
|
7448
|
+
summary: input.report.reason,
|
|
7449
|
+
leaseToken: input.task.leaseToken ?? null
|
|
7450
|
+
},
|
|
7451
|
+
workerInjection: null
|
|
7452
|
+
};
|
|
7453
|
+
const result = await postJsonWithCredentialRefresh(url, secret, body, {
|
|
7454
|
+
agentOsId: input.agentOsId,
|
|
7455
|
+
baseUrl: input.baseUrl
|
|
7456
|
+
});
|
|
7457
|
+
return { ok: result.ok, status: result.status };
|
|
7458
|
+
}
|
|
7459
|
+
|
|
7460
|
+
// src/verify-live/verify-live-prompt.ts
|
|
7461
|
+
function formatVerifyLiveCompletionContract() {
|
|
7462
|
+
return [
|
|
7463
|
+
"## verify_live completion contract",
|
|
7464
|
+
"Finish with a JSON finalResult object:",
|
|
7465
|
+
"{",
|
|
7466
|
+
' "prUrl": "<landed pr>",',
|
|
7467
|
+
' "outcome": "passed" | "failed" | "skipped" | "no_mcp_surface",',
|
|
7468
|
+
' "reason": "<human summary>",',
|
|
7469
|
+
' "mergeCommit": "<sha optional>",',
|
|
7470
|
+
' "mcpCalls": [{ "tool": "<name>", "ok": true, "summary": "..." }]',
|
|
7471
|
+
"}",
|
|
7472
|
+
"",
|
|
7473
|
+
"Drive the feature MCP tools against live production. Do not mark passed without exercising the MCP surface."
|
|
7474
|
+
].join("\n");
|
|
7475
|
+
}
|
|
7476
|
+
|
|
7041
7477
|
// src/dispatch.ts
|
|
7042
7478
|
var DEFAULT_DISPATCH_LEASE_MS = 60 * 60 * 1e3;
|
|
7043
7479
|
function readAdmissionExhaustion(result) {
|
|
@@ -7095,6 +7531,9 @@ function buildDispatchTaskText(task, agentOsId) {
|
|
|
7095
7531
|
`Board linkage: agentOsId=${agentOsId}, taskId=${task.id}, attempt=${task.attempt}, executor=${task.executor}${task.executorRef ? `, executorRef=${task.executorRef}` : ""}.`,
|
|
7096
7532
|
"This worker was dispatched from the AgentOS board. The harness reports your completion back to the board when you finish."
|
|
7097
7533
|
];
|
|
7534
|
+
if (task.executor === "verify_live") {
|
|
7535
|
+
lines.push("", formatVerifyLiveCompletionContract());
|
|
7536
|
+
}
|
|
7098
7537
|
const outboxRef = extractPlanOutboxFromTask(task);
|
|
7099
7538
|
if (outboxRef?.outboxId) {
|
|
7100
7539
|
const item = loadOutboxById(outboxRef.outboxId);
|
|
@@ -7128,6 +7567,12 @@ function requestedTargetTaskIds(args) {
|
|
|
7128
7567
|
async function dispatchRun(args) {
|
|
7129
7568
|
const pipeline = args.pipeline === true || args.pipeline === "true";
|
|
7130
7569
|
try {
|
|
7570
|
+
let isLandPrDecision2 = function(decision) {
|
|
7571
|
+
if (decision.landPrDispatch === true) return true;
|
|
7572
|
+
const task = decision.task;
|
|
7573
|
+
return task?.executor === "land_pr";
|
|
7574
|
+
};
|
|
7575
|
+
var isLandPrDecision = isLandPrDecision2;
|
|
7131
7576
|
const run = loadRun(String(required(String(args.run || ""), "--run")));
|
|
7132
7577
|
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
7133
7578
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
@@ -7262,6 +7707,66 @@ async function dispatchRun(args) {
|
|
|
7262
7707
|
});
|
|
7263
7708
|
return false;
|
|
7264
7709
|
}
|
|
7710
|
+
async function runLandPrClaimed(decision) {
|
|
7711
|
+
const task = decision.task;
|
|
7712
|
+
const taskId = String(task.id);
|
|
7713
|
+
const prUrl = task.prUrl ? String(task.prUrl) : "";
|
|
7714
|
+
if (!prUrl) {
|
|
7715
|
+
return abortClaimedSpawn(task, "land_pr task missing prUrl");
|
|
7716
|
+
}
|
|
7717
|
+
try {
|
|
7718
|
+
const report = await executeLandPrMerge({
|
|
7719
|
+
prUrl,
|
|
7720
|
+
repo: repoFromPrUrl(prUrl),
|
|
7721
|
+
cwd: run.repo
|
|
7722
|
+
});
|
|
7723
|
+
const post = await postLandPrHarnessCompletion({
|
|
7724
|
+
baseUrl: base,
|
|
7725
|
+
secret,
|
|
7726
|
+
agentOsId,
|
|
7727
|
+
runId: run.id,
|
|
7728
|
+
task,
|
|
7729
|
+
report
|
|
7730
|
+
});
|
|
7731
|
+
outcomes.push({
|
|
7732
|
+
taskId,
|
|
7733
|
+
started: true,
|
|
7734
|
+
landPr: true,
|
|
7735
|
+
outcome: report.outcome,
|
|
7736
|
+
completionStatus: post.status
|
|
7737
|
+
});
|
|
7738
|
+
if (!post.ok) {
|
|
7739
|
+
return abortClaimedSpawn(
|
|
7740
|
+
task,
|
|
7741
|
+
`land_pr completion POST failed (HTTP ${post.status})`
|
|
7742
|
+
);
|
|
7743
|
+
}
|
|
7744
|
+
return true;
|
|
7745
|
+
} catch (error) {
|
|
7746
|
+
return abortClaimedSpawn(task, error.message);
|
|
7747
|
+
}
|
|
7748
|
+
}
|
|
7749
|
+
async function releaseDuplicateLocalClaim(task) {
|
|
7750
|
+
const error = "duplicate_dispatch_prevented: live local worker already owns this task";
|
|
7751
|
+
const release = await releaseDispatchClaimAfterSpawnFailure({
|
|
7752
|
+
baseUrl: base,
|
|
7753
|
+
secret,
|
|
7754
|
+
agentOsId,
|
|
7755
|
+
taskId: String(task.id),
|
|
7756
|
+
leaseOwner,
|
|
7757
|
+
failureDetail: error
|
|
7758
|
+
});
|
|
7759
|
+
outcomes.push({
|
|
7760
|
+
taskId: task.id,
|
|
7761
|
+
started: false,
|
|
7762
|
+
error,
|
|
7763
|
+
alreadyRunning: true,
|
|
7764
|
+
nonFatal: true,
|
|
7765
|
+
released: release.released,
|
|
7766
|
+
releaseResponse: release.releaseResponse
|
|
7767
|
+
});
|
|
7768
|
+
return false;
|
|
7769
|
+
}
|
|
7265
7770
|
async function spawnClaimed(decision) {
|
|
7266
7771
|
const task = decision.task;
|
|
7267
7772
|
const harnessContext = readHarnessWorkerContext(decision);
|
|
@@ -7283,10 +7788,7 @@ async function dispatchRun(args) {
|
|
|
7283
7788
|
);
|
|
7284
7789
|
}
|
|
7285
7790
|
if (hasLiveWorkerForTask(run.id, taskId)) {
|
|
7286
|
-
return
|
|
7287
|
-
task,
|
|
7288
|
-
"duplicate_dispatch_prevented: live local worker already owns this task"
|
|
7289
|
-
);
|
|
7791
|
+
return releaseDuplicateLocalClaim(task);
|
|
7290
7792
|
}
|
|
7291
7793
|
const attempt = Number(task.attempt) || 1;
|
|
7292
7794
|
if (attempt > retryLimits.maxTaskAttempts) {
|
|
@@ -7379,7 +7881,8 @@ async function dispatchRun(args) {
|
|
|
7379
7881
|
}
|
|
7380
7882
|
let shouldContinueDispatch = true;
|
|
7381
7883
|
for (const decision of result.started) {
|
|
7382
|
-
|
|
7884
|
+
const admitted = isLandPrDecision2(decision) ? await runLandPrClaimed(decision) : await spawnClaimed(decision);
|
|
7885
|
+
shouldContinueDispatch = admitted && shouldContinueDispatch;
|
|
7383
7886
|
}
|
|
7384
7887
|
skipped.push(
|
|
7385
7888
|
...result.skipped ?? []
|
|
@@ -7417,7 +7920,8 @@ async function dispatchRun(args) {
|
|
|
7417
7920
|
if (started.length === 0) break;
|
|
7418
7921
|
for (const decision of started) {
|
|
7419
7922
|
if (outcomes.length >= cappedStarts) break;
|
|
7420
|
-
|
|
7923
|
+
const admitted = isLandPrDecision2(decision) ? await runLandPrClaimed(decision) : await spawnClaimed(decision);
|
|
7924
|
+
shouldContinueDispatch = admitted && shouldContinueDispatch;
|
|
7421
7925
|
if (!shouldContinueDispatch) break;
|
|
7422
7926
|
}
|
|
7423
7927
|
}
|
|
@@ -7454,11 +7958,12 @@ async function dispatchRun(args) {
|
|
|
7454
7958
|
diskGate: result.diskGate,
|
|
7455
7959
|
resourceGate: result.resourceGate
|
|
7456
7960
|
};
|
|
7961
|
+
const fatalOutcome = (outcome) => !outcome.started && outcome.nonFatal !== true;
|
|
7457
7962
|
if (pipeline) {
|
|
7458
|
-
return { ok: !outcomes.some(
|
|
7963
|
+
return { ok: !outcomes.some(fatalOutcome), ...summary };
|
|
7459
7964
|
}
|
|
7460
7965
|
console.log(JSON.stringify(summary, null, 2));
|
|
7461
|
-
if (outcomes.some(
|
|
7966
|
+
if (outcomes.some(fatalOutcome)) process.exit(1);
|
|
7462
7967
|
} catch (error) {
|
|
7463
7968
|
if (pipeline) return { ok: false, error: error.message };
|
|
7464
7969
|
console.error(`run dispatch failed: ${error.message}`);
|
|
@@ -8692,11 +9197,29 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
|
|
|
8692
9197
|
};
|
|
8693
9198
|
}
|
|
8694
9199
|
|
|
9200
|
+
// src/daemon-platform-guard.ts
|
|
9201
|
+
function envFlag(name) {
|
|
9202
|
+
const raw = process.env[name]?.trim().toLowerCase();
|
|
9203
|
+
return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
|
|
9204
|
+
}
|
|
9205
|
+
function assertNativeDaemonAllowed() {
|
|
9206
|
+
if (process.platform !== "win32") return;
|
|
9207
|
+
if (envFlag("KYNVER_DAEMON_ALLOW_NATIVE_WINDOWS")) return;
|
|
9208
|
+
console.error(
|
|
9209
|
+
JSON.stringify({
|
|
9210
|
+
event: "daemon_start_blocked",
|
|
9211
|
+
reason: "native_windows_console_flash",
|
|
9212
|
+
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)."
|
|
9213
|
+
})
|
|
9214
|
+
);
|
|
9215
|
+
process.exit(1);
|
|
9216
|
+
}
|
|
9217
|
+
|
|
8695
9218
|
// src/cron/cron-env.ts
|
|
8696
9219
|
import { existsSync as existsSync27 } from "node:fs";
|
|
8697
9220
|
import { homedir as homedir11 } from "node:os";
|
|
8698
9221
|
import path37 from "node:path";
|
|
8699
|
-
function
|
|
9222
|
+
function envFlag2(name, defaultValue) {
|
|
8700
9223
|
const raw = process.env[name]?.trim().toLowerCase();
|
|
8701
9224
|
if (!raw) return defaultValue;
|
|
8702
9225
|
if (raw === "0" || raw === "false" || raw === "no" || raw === "off") return false;
|
|
@@ -8732,14 +9255,14 @@ function resolveKynverCronEnv() {
|
|
|
8732
9255
|
const secret = resolveKynverCronSecret();
|
|
8733
9256
|
const credsReady = Boolean(fireBaseUrl && secret);
|
|
8734
9257
|
const storeExists = existsSync27(storePath);
|
|
8735
|
-
const defaultEnabled = credsReady && (storeExists ||
|
|
9258
|
+
const defaultEnabled = credsReady && (storeExists || envFlag2("KYNVER_CRON_TICK_FORCE", false));
|
|
8736
9259
|
return {
|
|
8737
9260
|
storePath,
|
|
8738
9261
|
statePath,
|
|
8739
9262
|
lockPath: `${statePath}.lock`,
|
|
8740
9263
|
fireBaseUrl,
|
|
8741
9264
|
secret,
|
|
8742
|
-
tickEnabled:
|
|
9265
|
+
tickEnabled: envFlag2("KYNVER_CRON_TICK_ENABLED", defaultEnabled),
|
|
8743
9266
|
tickIntervalMs: envInt("KYNVER_CRON_TICK_INTERVAL_MS", 6e4, 5e3),
|
|
8744
9267
|
missedRunPolicy: process.env.KYNVER_CRON_MISSED_RUN_POLICY?.trim().toLowerCase() === "skip" ? "skip" : "catch_up",
|
|
8745
9268
|
maxCatchUpPerTick: envInt("KYNVER_CRON_MAX_CATCH_UP_PER_TICK", 3, 0),
|
|
@@ -9426,23 +9949,6 @@ function materialWorktreeChanges2(changedFiles) {
|
|
|
9426
9949
|
});
|
|
9427
9950
|
}
|
|
9428
9951
|
|
|
9429
|
-
// src/cleanup-index-status.ts
|
|
9430
|
-
function indexedWorktreeStatus(entry) {
|
|
9431
|
-
if (!entry.status) {
|
|
9432
|
-
entry.status = computeWorkerStatus(entry.worker, {
|
|
9433
|
-
base: entry.run.base,
|
|
9434
|
-
baseCommit: entry.run.baseCommit
|
|
9435
|
-
});
|
|
9436
|
-
}
|
|
9437
|
-
return entry.status;
|
|
9438
|
-
}
|
|
9439
|
-
function indexedWorktreeHasMaterialChanges(entry) {
|
|
9440
|
-
if (entry.status) {
|
|
9441
|
-
return materialWorktreeChanges2(entry.status.changedFiles).length > 0;
|
|
9442
|
-
}
|
|
9443
|
-
return materialWorktreeChanges2(gitStatusShort(entry.worktreePath)).length > 0;
|
|
9444
|
-
}
|
|
9445
|
-
|
|
9446
9952
|
// src/cleanup-worktree-salvage.ts
|
|
9447
9953
|
function prUrlFromFinalResult(finalResult) {
|
|
9448
9954
|
if (typeof finalResult === "string") {
|
|
@@ -9469,6 +9975,83 @@ function isPrOrUnmergedWork(status) {
|
|
|
9469
9975
|
return false;
|
|
9470
9976
|
}
|
|
9471
9977
|
|
|
9978
|
+
// src/cleanup-index-status.ts
|
|
9979
|
+
function indexedWorktreeStatus(entry) {
|
|
9980
|
+
if (!entry.status) {
|
|
9981
|
+
entry.status = computeWorkerStatus(entry.worker, {
|
|
9982
|
+
base: entry.run.base,
|
|
9983
|
+
baseCommit: entry.run.baseCommit
|
|
9984
|
+
});
|
|
9985
|
+
}
|
|
9986
|
+
return entry.status;
|
|
9987
|
+
}
|
|
9988
|
+
function indexedWorktreeHasMaterialChanges(entry, gitStatusCache) {
|
|
9989
|
+
if (entry.status) {
|
|
9990
|
+
return materialWorktreeChanges2(entry.status.changedFiles).length > 0;
|
|
9991
|
+
}
|
|
9992
|
+
const porcelain = gitStatusCache ? gitStatusCache.porcelain(entry.worktreePath) : gitStatusShort(entry.worktreePath);
|
|
9993
|
+
return materialWorktreeChanges2(porcelain).length > 0;
|
|
9994
|
+
}
|
|
9995
|
+
function finalResultFromWorkerJson(entry) {
|
|
9996
|
+
const snapshot = entry.worker.completionSnapshot?.finalResult;
|
|
9997
|
+
if (snapshot !== void 0 && snapshot !== null) return snapshot;
|
|
9998
|
+
if (entry.worker.taskPrUrl) {
|
|
9999
|
+
return { prUrl: entry.worker.taskPrUrl };
|
|
10000
|
+
}
|
|
10001
|
+
return null;
|
|
10002
|
+
}
|
|
10003
|
+
function resolveWorktreeGuardStatus(entry, ctx) {
|
|
10004
|
+
if (entry.status) return entry.status;
|
|
10005
|
+
const worker = entry.worker;
|
|
10006
|
+
const completionAcknowledged = typeof worker.completionReportedAt === "string" && worker.completionReportedAt.trim().length > 0;
|
|
10007
|
+
const workerJsonTerminal = Boolean(worker.status && ["done", "exited", "blocked", "failed", "abandoned"].includes(worker.status)) || completionAcknowledged;
|
|
10008
|
+
const finalResult = finalResultFromWorkerJson(entry);
|
|
10009
|
+
if (workerJsonTerminal && !isPidAlive(worker.pid)) {
|
|
10010
|
+
const changedFiles = ctx?.gitStatusCache ? ctx.gitStatusCache.porcelain(entry.worktreePath) : gitStatusShort(entry.worktreePath);
|
|
10011
|
+
const baseLabel = entry.run.baseCommit?.trim() || entry.run.base?.trim() || "origin/main";
|
|
10012
|
+
const ahead = ctx?.gitRevCache?.countAheadOfMain(entry.worktreePath, baseLabel);
|
|
10013
|
+
const gitAncestry = ahead === 0 ? {
|
|
10014
|
+
checked: true,
|
|
10015
|
+
base: baseLabel,
|
|
10016
|
+
relation: "synced"
|
|
10017
|
+
} : computeGitAncestry(entry.worktreePath, {
|
|
10018
|
+
base: entry.run.base,
|
|
10019
|
+
baseCommit: entry.run.baseCommit
|
|
10020
|
+
});
|
|
10021
|
+
const status = {
|
|
10022
|
+
runId: entry.runId,
|
|
10023
|
+
worker: entry.workerName,
|
|
10024
|
+
pid: worker.pid,
|
|
10025
|
+
alive: false,
|
|
10026
|
+
status: worker.status ?? (completionAcknowledged ? "done" : "exited"),
|
|
10027
|
+
attention: { state: completionAcknowledged ? "done" : "stale" },
|
|
10028
|
+
branch: worker.branch,
|
|
10029
|
+
worktreePath: entry.worktreePath,
|
|
10030
|
+
ownedPaths: worker.ownedPaths,
|
|
10031
|
+
stdoutBytes: 0,
|
|
10032
|
+
stderrBytes: 0,
|
|
10033
|
+
heartbeatBytes: 0,
|
|
10034
|
+
firstEventAt: null,
|
|
10035
|
+
lastEventAt: null,
|
|
10036
|
+
lastActivityAt: worker.completionReportedAt ?? null,
|
|
10037
|
+
currentTool: null,
|
|
10038
|
+
heartbeatCount: 0,
|
|
10039
|
+
lastHeartbeatAt: null,
|
|
10040
|
+
lastHeartbeatPhase: null,
|
|
10041
|
+
lastHeartbeatSummary: null,
|
|
10042
|
+
heartbeatBlocker: null,
|
|
10043
|
+
changedFiles,
|
|
10044
|
+
gitAncestry,
|
|
10045
|
+
finalResult,
|
|
10046
|
+
completionBlocker: typeof worker.completionBlocker === "string" ? worker.completionBlocker.trim() || null : null,
|
|
10047
|
+
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? prUrlFromFinalResult(finalResult)
|
|
10048
|
+
};
|
|
10049
|
+
entry.status = status;
|
|
10050
|
+
return status;
|
|
10051
|
+
}
|
|
10052
|
+
return indexedWorktreeStatus(entry);
|
|
10053
|
+
}
|
|
10054
|
+
|
|
9472
10055
|
// src/cleanup-completion-blocker.ts
|
|
9473
10056
|
function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
9474
10057
|
const blocker = typeof indexed.worker.completionBlocker === "string" ? indexed.worker.completionBlocker.trim() : "";
|
|
@@ -9489,13 +10072,28 @@ function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
|
9489
10072
|
}
|
|
9490
10073
|
|
|
9491
10074
|
// src/cleanup-run-liveness.ts
|
|
10075
|
+
var TERMINAL_WORKER_JSON_STATUSES = /* @__PURE__ */ new Set([
|
|
10076
|
+
"done",
|
|
10077
|
+
"exited",
|
|
10078
|
+
"blocked",
|
|
10079
|
+
"failed",
|
|
10080
|
+
"abandoned"
|
|
10081
|
+
]);
|
|
9492
10082
|
function deriveRunTerminal(indexed, ctx) {
|
|
9493
10083
|
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
9494
10084
|
return deriveTerminalRunStatus(indexed.run);
|
|
9495
10085
|
}
|
|
9496
10086
|
function isWorkerProcessLive(indexed) {
|
|
9497
10087
|
if (isPidAlive(indexed.worker.pid)) return true;
|
|
9498
|
-
if (
|
|
10088
|
+
if (typeof indexed.worker.completionReportedAt === "string" && indexed.worker.completionReportedAt.trim().length > 0) {
|
|
10089
|
+
return false;
|
|
10090
|
+
}
|
|
10091
|
+
const workerStatus2 = indexed.worker.status;
|
|
10092
|
+
if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2)) return false;
|
|
10093
|
+
if (!indexed.worker.pid) {
|
|
10094
|
+
if (workerStatus2 !== "running") return false;
|
|
10095
|
+
return indexedWorktreeStatus(indexed).alive;
|
|
10096
|
+
}
|
|
9499
10097
|
return false;
|
|
9500
10098
|
}
|
|
9501
10099
|
function isRunStaleActive(indexed, ctx) {
|
|
@@ -9504,6 +10102,10 @@ function isRunStaleActive(indexed, ctx) {
|
|
|
9504
10102
|
}
|
|
9505
10103
|
function runBlocksWorktreeRemoval(indexed, ctx) {
|
|
9506
10104
|
if (isWorkerProcessLive(indexed)) return true;
|
|
10105
|
+
const workerStatus2 = indexed.worker.status;
|
|
10106
|
+
if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2) && !indexed.worker.completionBlocker) {
|
|
10107
|
+
return false;
|
|
10108
|
+
}
|
|
9507
10109
|
const status = indexedWorktreeStatus(indexed);
|
|
9508
10110
|
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return true;
|
|
9509
10111
|
if (isFinishedWorkerStatus(status)) return false;
|
|
@@ -9522,7 +10124,7 @@ function effectiveWorktreeAgeMs(input) {
|
|
|
9522
10124
|
if (input.liveness && isRunStaleActive(indexed, input.liveness)) {
|
|
9523
10125
|
return terminalWorktreesAgeMs;
|
|
9524
10126
|
}
|
|
9525
|
-
if (input.liveness && isFinishedWorkerStatus(
|
|
10127
|
+
if (input.liveness && isFinishedWorkerStatus(resolveWorktreeGuardStatus(indexed, input.liveness)) && !isWorkerProcessLive(indexed)) {
|
|
9526
10128
|
return terminalWorktreesAgeMs;
|
|
9527
10129
|
}
|
|
9528
10130
|
return worktreesAgeMs;
|
|
@@ -9536,8 +10138,13 @@ function skipWorktreeRemoval(input) {
|
|
|
9536
10138
|
const ageThresholdMs = effectiveWorktreeAgeMs(input);
|
|
9537
10139
|
if (worktreesAgeMs <= 0 && !includeOrphans && ageThresholdMs <= 0) return "worktrees_disabled";
|
|
9538
10140
|
if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
|
|
9539
|
-
const status = indexedWorktreeStatus(indexed);
|
|
9540
10141
|
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
10142
|
+
if (indexedWorktreeHasMaterialChanges(indexed, input.liveness?.gitStatusCache)) {
|
|
10143
|
+
return "dirty_worktree";
|
|
10144
|
+
}
|
|
10145
|
+
const ahead = input.liveness?.gitRevCache?.countAheadOfMain(input.worktreePath);
|
|
10146
|
+
if (ahead !== null && ahead !== void 0 && ahead > 0) return "pr_or_unmerged_commits";
|
|
10147
|
+
const status = resolveWorktreeGuardStatus(indexed, input.liveness);
|
|
9541
10148
|
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return "completion_blocked";
|
|
9542
10149
|
if (runBlocksWorktreeRemoval(indexed, input.liveness)) return "run_still_active";
|
|
9543
10150
|
if (!isFinishedWorkerStatus(status)) return "run_still_active";
|
|
@@ -9568,7 +10175,9 @@ function skipDependencyCacheRemoval(input) {
|
|
|
9568
10175
|
if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
9569
10176
|
if (activeWorktreePaths.has(path40.resolve(worktreePath))) return "active_worker";
|
|
9570
10177
|
if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
|
|
9571
|
-
if (indexed && indexedWorktreeHasMaterialChanges(indexed))
|
|
10178
|
+
if (indexed && indexedWorktreeHasMaterialChanges(indexed, input.gitStatusCache)) {
|
|
10179
|
+
return "dirty_worktree";
|
|
10180
|
+
}
|
|
9572
10181
|
return null;
|
|
9573
10182
|
}
|
|
9574
10183
|
function skipBuildCacheRemoval(input) {
|
|
@@ -9817,7 +10426,7 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
9817
10426
|
}
|
|
9818
10427
|
|
|
9819
10428
|
// src/cleanup-privileged-remove.ts
|
|
9820
|
-
import { spawnSync as
|
|
10429
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
9821
10430
|
import path45 from "node:path";
|
|
9822
10431
|
|
|
9823
10432
|
// src/cleanup-harness-path-validate.ts
|
|
@@ -9862,7 +10471,7 @@ function resolvePrivilegedCleanupMode() {
|
|
|
9862
10471
|
return "auto";
|
|
9863
10472
|
}
|
|
9864
10473
|
function runSudoNonInteractive(argv) {
|
|
9865
|
-
const res =
|
|
10474
|
+
const res = spawnSync6("sudo", ["-n", ...argv], {
|
|
9866
10475
|
encoding: "utf8",
|
|
9867
10476
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9868
10477
|
});
|
|
@@ -10410,7 +11019,7 @@ function buildWorktreeIndexAt(harnessRoot) {
|
|
|
10410
11019
|
}
|
|
10411
11020
|
|
|
10412
11021
|
// src/cleanup-retention-config.ts
|
|
10413
|
-
function
|
|
11022
|
+
function envFlag3(name) {
|
|
10414
11023
|
const v = process.env[name];
|
|
10415
11024
|
return v === "1" || v === "true" || v === "yes";
|
|
10416
11025
|
}
|
|
@@ -10421,17 +11030,17 @@ function envMs(name, fallback) {
|
|
|
10421
11030
|
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
10422
11031
|
}
|
|
10423
11032
|
function resolveHarnessRetention(options = {}) {
|
|
10424
|
-
const execute = options.execute === true || options.execute !== false &&
|
|
10425
|
-
const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !
|
|
11033
|
+
const execute = options.execute === true || options.execute !== false && envFlag3("KYNVER_CLEANUP_EXECUTE");
|
|
11034
|
+
const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag3("KYNVER_CLEANUP_SKIP_FINALIZE");
|
|
10426
11035
|
const nodeModulesAgeMs = options.nodeModulesAgeMs ?? envMs("KYNVER_CLEANUP_NODE_MODULES_AGE_MS", DEFAULT_NODE_MODULES_AGE_MS);
|
|
10427
11036
|
const worktreesAgeMs = options.worktreesAgeMs ?? envMs("KYNVER_CLEANUP_WORKTREES_AGE_MS", 0);
|
|
10428
11037
|
const terminalWorktreesAgeMs = options.terminalWorktreesAgeMs ?? envMs("KYNVER_CLEANUP_TERMINAL_WORKTREES_AGE_MS", DEFAULT_TERMINAL_WORKTREES_AGE_MS);
|
|
10429
11038
|
const runDirectoriesAgeMs = options.runDirectoriesAgeMs ?? envMs("KYNVER_CLEANUP_RUN_DIRECTORIES_AGE_MS", DEFAULT_RUN_DIRECTORIES_AGE_MS);
|
|
10430
11039
|
const maxActionsPerSweep = options.maxActionsPerSweep ?? envMs("KYNVER_CLEANUP_MAX_ACTIONS_PER_SWEEP", DEFAULT_MAX_ACTIONS_PER_SWEEP);
|
|
10431
|
-
const includeOrphans = options.includeOrphans === true ||
|
|
10432
|
-
const scopeAll =
|
|
11040
|
+
const includeOrphans = options.includeOrphans === true || envFlag3("KYNVER_CLEANUP_INCLUDE_ORPHANS");
|
|
11041
|
+
const scopeAll = envFlag3("KYNVER_CLEANUP_SCOPE_ALL") || process.env.KYNVER_CLEANUP_SCOPE === "all";
|
|
10433
11042
|
const runIdFilter = scopeAll ? options.runIdFilter : options.runIdFilter ?? (process.env.KYNVER_CLEANUP_RUN_ID || void 0);
|
|
10434
|
-
const accountBytes = options.accountBytes !== false && !
|
|
11043
|
+
const accountBytes = options.accountBytes !== false && !envFlag3("KYNVER_CLEANUP_SKIP_BYTE_ACCOUNTING");
|
|
10435
11044
|
const storageCapEnv = envMs("KYNVER_CLEANUP_STORAGE_ENTRY_CAP", 2e3);
|
|
10436
11045
|
const storagePerRunEntryCap = options.storagePerRunEntryCap !== void 0 ? options.storagePerRunEntryCap : accountBytes ? storageCapEnv > 0 ? storageCapEnv : null : null;
|
|
10437
11046
|
const byteCapEnv = envMs("KYNVER_CLEANUP_BYTE_ENTRY_CAP", 2e3);
|
|
@@ -10628,7 +11237,7 @@ function resolveHarnessScanRoots(options = {}) {
|
|
|
10628
11237
|
}
|
|
10629
11238
|
|
|
10630
11239
|
// src/cleanup-disk-pressure.ts
|
|
10631
|
-
function
|
|
11240
|
+
function envFlag4(name) {
|
|
10632
11241
|
const v = process.env[name];
|
|
10633
11242
|
return v === "1" || v === "true" || v === "yes";
|
|
10634
11243
|
}
|
|
@@ -10651,7 +11260,7 @@ function observeCleanupDiskPressure(input = {}) {
|
|
|
10651
11260
|
}
|
|
10652
11261
|
function applyDiskPressureToRetention(retention, pressure) {
|
|
10653
11262
|
if (!pressure.pressured) return retention;
|
|
10654
|
-
const executeOnPressure = retention.execute || !
|
|
11263
|
+
const executeOnPressure = retention.execute || !envFlag4("KYNVER_CLEANUP_DRY_RUN_ON_PRESSURE");
|
|
10655
11264
|
return {
|
|
10656
11265
|
...retention,
|
|
10657
11266
|
execute: executeOnPressure,
|
|
@@ -10676,6 +11285,37 @@ function emitCleanupProgress(phase, detail) {
|
|
|
10676
11285
|
console.error(`[kynver cleanup] ${phase}${suffix}`);
|
|
10677
11286
|
}
|
|
10678
11287
|
|
|
11288
|
+
// src/cleanup-git-rev-cache.ts
|
|
11289
|
+
var CleanupGitRevCache = class {
|
|
11290
|
+
aheadOfMain = /* @__PURE__ */ new Map();
|
|
11291
|
+
countAheadOfMain(worktreePath, base = "origin/main") {
|
|
11292
|
+
const key = `${worktreePath}\0${base}`;
|
|
11293
|
+
if (this.aheadOfMain.has(key)) return this.aheadOfMain.get(key) ?? null;
|
|
11294
|
+
const result = gitCapture(worktreePath, ["rev-list", "--count", `${base}..HEAD`]);
|
|
11295
|
+
if (result.status !== 0) {
|
|
11296
|
+
this.aheadOfMain.set(key, null);
|
|
11297
|
+
return null;
|
|
11298
|
+
}
|
|
11299
|
+
const count = Number(result.stdout.trim());
|
|
11300
|
+
const parsed = Number.isFinite(count) ? count : null;
|
|
11301
|
+
this.aheadOfMain.set(key, parsed);
|
|
11302
|
+
return parsed;
|
|
11303
|
+
}
|
|
11304
|
+
};
|
|
11305
|
+
|
|
11306
|
+
// src/cleanup-git-status-cache.ts
|
|
11307
|
+
var CleanupGitStatusCache = class {
|
|
11308
|
+
cache = /* @__PURE__ */ new Map();
|
|
11309
|
+
porcelain(worktreePath) {
|
|
11310
|
+
const resolved = worktreePath;
|
|
11311
|
+
const cached = this.cache.get(resolved);
|
|
11312
|
+
if (cached !== void 0) return cached;
|
|
11313
|
+
const lines = gitStatusShort(resolved);
|
|
11314
|
+
this.cache.set(resolved, lines);
|
|
11315
|
+
return lines;
|
|
11316
|
+
}
|
|
11317
|
+
};
|
|
11318
|
+
|
|
10679
11319
|
// src/cleanup-run-terminal-cache.ts
|
|
10680
11320
|
var CleanupRunTerminalCache = class {
|
|
10681
11321
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -10793,7 +11433,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
10793
11433
|
emitCleanupProgress("index", "building worktree index");
|
|
10794
11434
|
const index = mergeWorktreeIndexes(paths.scanRoots);
|
|
10795
11435
|
emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
|
|
10796
|
-
const liveness = {
|
|
11436
|
+
const liveness = {
|
|
11437
|
+
runTerminalCache: new CleanupRunTerminalCache(),
|
|
11438
|
+
gitStatusCache: new CleanupGitStatusCache(),
|
|
11439
|
+
gitRevCache: new CleanupGitRevCache()
|
|
11440
|
+
};
|
|
10797
11441
|
const skips = [];
|
|
10798
11442
|
const actions = [];
|
|
10799
11443
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
@@ -10813,8 +11457,15 @@ function runHarnessCleanup(options = {}) {
|
|
|
10813
11457
|
index,
|
|
10814
11458
|
now: paths.now
|
|
10815
11459
|
};
|
|
10816
|
-
|
|
11460
|
+
const dependencyCandidates = scanDependencyCacheCandidates(scanOpts);
|
|
11461
|
+
emitCleanupProgress("dependency", `${dependencyCandidates.length} cache candidate(s) at ${harnessRoot}`);
|
|
11462
|
+
let dependencyProcessed = 0;
|
|
11463
|
+
for (const raw of dependencyCandidates) {
|
|
10817
11464
|
if (atSweepCap()) break;
|
|
11465
|
+
dependencyProcessed += 1;
|
|
11466
|
+
if (dependencyProcessed % 50 === 0) {
|
|
11467
|
+
emitCleanupProgress("dependency", `${dependencyProcessed}/${dependencyCandidates.length} evaluated`);
|
|
11468
|
+
}
|
|
10818
11469
|
const resolved = path53.resolve(raw.path);
|
|
10819
11470
|
if (processedPaths.has(resolved)) continue;
|
|
10820
11471
|
processedPaths.add(resolved);
|
|
@@ -10834,7 +11485,8 @@ function runHarnessCleanup(options = {}) {
|
|
|
10834
11485
|
ageMs: candidate.ageMs,
|
|
10835
11486
|
worktreePath,
|
|
10836
11487
|
activeWorktreePaths: activeGuards.activeWorktreePaths,
|
|
10837
|
-
diskPressure: retention.diskPressure
|
|
11488
|
+
diskPressure: retention.diskPressure,
|
|
11489
|
+
gitStatusCache: liveness.gitStatusCache
|
|
10838
11490
|
});
|
|
10839
11491
|
if (guardReason) {
|
|
10840
11492
|
recordSkip(skips, candidate.path, guardReason);
|
|
@@ -10869,7 +11521,8 @@ function runHarnessCleanup(options = {}) {
|
|
|
10869
11521
|
ageMs: candidate.ageMs,
|
|
10870
11522
|
worktreePath,
|
|
10871
11523
|
activeWorktreePaths: activeGuards.activeWorktreePaths,
|
|
10872
|
-
diskPressure: retention.diskPressure
|
|
11524
|
+
diskPressure: retention.diskPressure,
|
|
11525
|
+
gitStatusCache: liveness.gitStatusCache
|
|
10873
11526
|
});
|
|
10874
11527
|
if (guardReason) {
|
|
10875
11528
|
recordSkip(skips, candidate.path, guardReason);
|
|
@@ -10889,8 +11542,13 @@ function runHarnessCleanup(options = {}) {
|
|
|
10889
11542
|
];
|
|
10890
11543
|
emitCleanupProgress("worktrees", `${worktreeCandidates.length} candidate(s) at ${harnessRoot}`);
|
|
10891
11544
|
const worktreeSeen = /* @__PURE__ */ new Set();
|
|
11545
|
+
let worktreeProcessed = 0;
|
|
10892
11546
|
for (const raw of worktreeCandidates) {
|
|
10893
11547
|
if (atSweepCap()) break;
|
|
11548
|
+
worktreeProcessed += 1;
|
|
11549
|
+
if (worktreeProcessed % 50 === 0) {
|
|
11550
|
+
emitCleanupProgress("worktrees", `${worktreeProcessed}/${worktreeCandidates.length} evaluated`);
|
|
11551
|
+
}
|
|
10894
11552
|
const resolved = path53.resolve(raw.path);
|
|
10895
11553
|
if (worktreeSeen.has(resolved)) continue;
|
|
10896
11554
|
worktreeSeen.add(resolved);
|
|
@@ -11486,6 +12144,7 @@ async function awaitDaemonBackoff(ms, isStopping) {
|
|
|
11486
12144
|
}
|
|
11487
12145
|
}
|
|
11488
12146
|
async function runDaemon(args) {
|
|
12147
|
+
assertNativeDaemonAllowed();
|
|
11489
12148
|
const runId = String(required(String(args.run || ""), "--run"));
|
|
11490
12149
|
const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
|
|
11491
12150
|
const execute = args.execute !== false && args.execute !== "false";
|
|
@@ -11590,7 +12249,7 @@ function formatNodeOptionsFlag(mb = resolveNodeOldSpaceSizeMb()) {
|
|
|
11590
12249
|
}
|
|
11591
12250
|
|
|
11592
12251
|
// src/bounded-build/systemd-wrap.ts
|
|
11593
|
-
import { spawnSync as
|
|
12252
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
11594
12253
|
var systemdAvailableCache;
|
|
11595
12254
|
function isSystemdRunAvailable() {
|
|
11596
12255
|
if (process.env.KYNVER_BUILD_SKIP_SYSTEMD === "1" || process.env.KYNVER_BUILD_SKIP_SYSTEMD === "true") {
|
|
@@ -11601,7 +12260,7 @@ function isSystemdRunAvailable() {
|
|
|
11601
12260
|
systemdAvailableCache = false;
|
|
11602
12261
|
return false;
|
|
11603
12262
|
}
|
|
11604
|
-
const res =
|
|
12263
|
+
const res = spawnSync7("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
|
|
11605
12264
|
systemdAvailableCache = res.status === 0;
|
|
11606
12265
|
return systemdAvailableCache;
|
|
11607
12266
|
}
|
|
@@ -11625,7 +12284,7 @@ function buildSystemdRunArgv(opts) {
|
|
|
11625
12284
|
}
|
|
11626
12285
|
|
|
11627
12286
|
// src/bounded-build/admission.ts
|
|
11628
|
-
import { spawnSync as
|
|
12287
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
11629
12288
|
function positiveInt4(value, fallback) {
|
|
11630
12289
|
const n = Number(value);
|
|
11631
12290
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
@@ -11661,7 +12320,7 @@ function assessBuildAdmission(opts = {}) {
|
|
|
11661
12320
|
}
|
|
11662
12321
|
function sleepMs2(ms) {
|
|
11663
12322
|
if (ms <= 0) return;
|
|
11664
|
-
|
|
12323
|
+
spawnSync8(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
11665
12324
|
stdio: "ignore"
|
|
11666
12325
|
});
|
|
11667
12326
|
}
|
|
@@ -11682,7 +12341,7 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
|
|
|
11682
12341
|
}
|
|
11683
12342
|
|
|
11684
12343
|
// src/bounded-build/exec.ts
|
|
11685
|
-
import { spawnSync as
|
|
12344
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
11686
12345
|
|
|
11687
12346
|
// src/heavy-verification/slot.ts
|
|
11688
12347
|
import {
|
|
@@ -11888,10 +12547,10 @@ function assessHeavyVerificationGate(command, opts = {}) {
|
|
|
11888
12547
|
}
|
|
11889
12548
|
|
|
11890
12549
|
// src/heavy-verification/gate.ts
|
|
11891
|
-
import { spawnSync as
|
|
12550
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
11892
12551
|
function sleepMs3(ms) {
|
|
11893
12552
|
if (ms <= 0) return;
|
|
11894
|
-
|
|
12553
|
+
spawnSync9(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
11895
12554
|
stdio: "ignore"
|
|
11896
12555
|
});
|
|
11897
12556
|
}
|
|
@@ -11934,7 +12593,7 @@ function envArgv(env) {
|
|
|
11934
12593
|
return out;
|
|
11935
12594
|
}
|
|
11936
12595
|
function runSpawn(argv, opts) {
|
|
11937
|
-
const res =
|
|
12596
|
+
const res = spawnSync10(argv[0], argv.slice(1), {
|
|
11938
12597
|
cwd: opts.cwd,
|
|
11939
12598
|
env: opts.env,
|
|
11940
12599
|
encoding: "utf8",
|
|
@@ -13214,10 +13873,10 @@ import path68 from "node:path";
|
|
|
13214
13873
|
import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync17 } from "node:fs";
|
|
13215
13874
|
import { homedir as homedir15 } from "node:os";
|
|
13216
13875
|
import path67 from "node:path";
|
|
13217
|
-
import { spawnSync as
|
|
13876
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
13218
13877
|
function captureCommand(bin, args) {
|
|
13219
13878
|
try {
|
|
13220
|
-
const res =
|
|
13879
|
+
const res = spawnSync11(bin, args, { encoding: "utf8" });
|
|
13221
13880
|
const stdout = (res.stdout || "").trim();
|
|
13222
13881
|
const stderr = (res.stderr || "").trim();
|
|
13223
13882
|
const ok = res.status === 0;
|
|
@@ -13466,12 +14125,12 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
|
|
|
13466
14125
|
return check({
|
|
13467
14126
|
id: "hotspot_openclaw_scheduler",
|
|
13468
14127
|
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
13469
|
-
status: "
|
|
13470
|
-
summary: "
|
|
13471
|
-
remediation: "
|
|
14128
|
+
status: "warn",
|
|
14129
|
+
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",
|
|
14130
|
+
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.",
|
|
13472
14131
|
details: {
|
|
13473
14132
|
schedulerProvider: null,
|
|
13474
|
-
resolvedFallback: "
|
|
14133
|
+
resolvedFallback: "none",
|
|
13475
14134
|
qstashTokenPresent: false,
|
|
13476
14135
|
hostedDeployment
|
|
13477
14136
|
}
|
|
@@ -14117,7 +14776,7 @@ var LANDING_MAINTAINER_LANE_SPEC = {
|
|
|
14117
14776
|
};
|
|
14118
14777
|
|
|
14119
14778
|
// src/lane/landing-maintainer-local.ts
|
|
14120
|
-
import { spawnSync as
|
|
14779
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
14121
14780
|
import path70 from "node:path";
|
|
14122
14781
|
function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
14123
14782
|
const script = path70.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
|
|
@@ -14131,7 +14790,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
|
14131
14790
|
stderr: ""
|
|
14132
14791
|
};
|
|
14133
14792
|
}
|
|
14134
|
-
const result =
|
|
14793
|
+
const result = spawnSync12("node", args, {
|
|
14135
14794
|
cwd: repoRoot,
|
|
14136
14795
|
encoding: "utf8",
|
|
14137
14796
|
timeout: 10 * 60 * 1e3
|
|
@@ -14273,14 +14932,15 @@ function usage(code = 0) {
|
|
|
14273
14932
|
" kynver runner credential [--agent-os-id ID] [--base-url URL]",
|
|
14274
14933
|
" 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]",
|
|
14275
14934
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
|
|
14935
|
+
" kynver status --run RUN_ID [--blocked] [--running] [--task TASK_ID] [--worker WORKER] [--full] # top-level compact run status",
|
|
14276
14936
|
" kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
|
|
14277
14937
|
" kynver run list",
|
|
14278
14938
|
" kynver run resolve --name RUN_NAME",
|
|
14279
|
-
" kynver run status --run RUN_ID [--
|
|
14939
|
+
" kynver run status --run RUN_ID [--full] # defaults to compact shallow map with drill-down commands",
|
|
14280
14940
|
" 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]",
|
|
14281
14941
|
" kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
|
|
14282
14942
|
' 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]',
|
|
14283
|
-
" kynver worker list --run RUN_ID [--
|
|
14943
|
+
" kynver worker list --run RUN_ID [--full] # defaults to compact shallow map with drill-down commands",
|
|
14284
14944
|
" kynver worker status --run RUN_ID --name worker",
|
|
14285
14945
|
" kynver worker tail --run RUN_ID --name worker [--lines 40] [--raw]",
|
|
14286
14946
|
" kynver worker stop --run RUN_ID --name worker",
|
|
@@ -14334,6 +14994,9 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
14334
14994
|
const { runsDir, worktreesDir } = getPaths();
|
|
14335
14995
|
mkdirSync10(runsDir, { recursive: true });
|
|
14336
14996
|
mkdirSync10(worktreesDir, { recursive: true });
|
|
14997
|
+
if (scope === "daemon") {
|
|
14998
|
+
assertNativeDaemonAllowed();
|
|
14999
|
+
}
|
|
14337
15000
|
if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
|
|
14338
15001
|
let repoRoot;
|
|
14339
15002
|
const runId = args.run ? String(args.run).trim() : "";
|
|
@@ -14350,6 +15013,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
14350
15013
|
});
|
|
14351
15014
|
}
|
|
14352
15015
|
if (scope === "login") return void await runLogin(args);
|
|
15016
|
+
if (scope === "status") return runStatus(args);
|
|
14353
15017
|
if (scope === "runner" && action === "credential") return void await mintRunnerCredential(args);
|
|
14354
15018
|
if (scope === "setup") return void await runSetup(args);
|
|
14355
15019
|
if (scope === "daemon") return void await runDaemon(args);
|