@kynver-app/runtime 0.1.99 → 0.1.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +628 -198
- package/dist/cli.js.map +4 -4
- package/dist/harness-worker-active.d.ts +12 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +623 -189
- package/dist/index.js.map +4 -4
- package/dist/lane/landing-maintainer-local.d.ts +15 -0
- package/dist/lane/landing-maintainer-tick.d.ts +16 -0
- package/dist/lane/lane-spec.d.ts +8 -0
- package/dist/lane/lane-tick-cli.d.ts +1 -0
- package/dist/local-pr-attention-reconcile.d.ts +25 -0
- package/dist/resource-gate.d.ts +2 -7
- package/dist/run-resolve.d.ts +4 -0
- package/dist/stale-reconcile.d.ts +2 -0
- package/dist/status.d.ts +1 -0
- package/dist/util.d.ts +2 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -90,6 +90,9 @@ function readMaybeFile(file) {
|
|
|
90
90
|
function sleepMs(ms) {
|
|
91
91
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
92
92
|
}
|
|
93
|
+
function sleepMsAsync(ms) {
|
|
94
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
95
|
+
}
|
|
93
96
|
function isPidAlive(pid) {
|
|
94
97
|
if (!pid) return false;
|
|
95
98
|
try {
|
|
@@ -461,7 +464,6 @@ function readMemAvailableBytes(meminfoText) {
|
|
|
461
464
|
|
|
462
465
|
// src/resource-gate.ts
|
|
463
466
|
import path7 from "node:path";
|
|
464
|
-
import { readFileSync as readFileSync7 } from "node:fs";
|
|
465
467
|
|
|
466
468
|
// src/disk-gate.ts
|
|
467
469
|
import { statfsSync as statfsSync2 } from "node:fs";
|
|
@@ -486,23 +488,23 @@ function isWslHost() {
|
|
|
486
488
|
function observeWslHostDisk(options = {}) {
|
|
487
489
|
const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
|
|
488
490
|
if (!wsl) return null;
|
|
489
|
-
const
|
|
491
|
+
const path71 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
|
|
490
492
|
const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
|
|
491
493
|
const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
|
|
492
494
|
const statfs = options.statfs ?? statfsSync;
|
|
493
495
|
let stats;
|
|
494
496
|
try {
|
|
495
|
-
stats = statfs(
|
|
497
|
+
stats = statfs(path71);
|
|
496
498
|
} catch (error) {
|
|
497
499
|
return {
|
|
498
500
|
ok: false,
|
|
499
|
-
path:
|
|
501
|
+
path: path71,
|
|
500
502
|
freeBytes: 0,
|
|
501
503
|
totalBytes: 0,
|
|
502
504
|
usedPercent: 100,
|
|
503
505
|
warnBelowBytes,
|
|
504
506
|
criticalBelowBytes,
|
|
505
|
-
reason: `Windows host disk probe failed at ${
|
|
507
|
+
reason: `Windows host disk probe failed at ${path71}: ${error.message}`,
|
|
506
508
|
probeError: error.message
|
|
507
509
|
};
|
|
508
510
|
}
|
|
@@ -516,11 +518,11 @@ function observeWslHostDisk(options = {}) {
|
|
|
516
518
|
let reason = null;
|
|
517
519
|
if (!ok) {
|
|
518
520
|
const tag = criticalFree ? "critical" : "warning";
|
|
519
|
-
reason = `Windows host disk ${
|
|
521
|
+
reason = `Windows host disk ${path71} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
|
|
520
522
|
}
|
|
521
523
|
return {
|
|
522
524
|
ok,
|
|
523
|
-
path:
|
|
525
|
+
path: path71,
|
|
524
526
|
freeBytes,
|
|
525
527
|
totalBytes,
|
|
526
528
|
usedPercent,
|
|
@@ -540,12 +542,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
540
542
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
541
543
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
542
544
|
function observeRunnerDiskGate(input = {}) {
|
|
543
|
-
const
|
|
545
|
+
const path71 = input.diskPath?.trim() || "/";
|
|
544
546
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
545
547
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
546
548
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
547
549
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
548
|
-
const stats = statfsSync2(
|
|
550
|
+
const stats = statfsSync2(path71);
|
|
549
551
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
550
552
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
551
553
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -568,7 +570,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
568
570
|
}
|
|
569
571
|
return {
|
|
570
572
|
ok,
|
|
571
|
-
path:
|
|
573
|
+
path: path71,
|
|
572
574
|
freeBytes,
|
|
573
575
|
totalBytes,
|
|
574
576
|
usedPercent,
|
|
@@ -702,6 +704,9 @@ function listRunWorkerNames(run) {
|
|
|
702
704
|
return [...names];
|
|
703
705
|
}
|
|
704
706
|
|
|
707
|
+
// src/harness-worker-active.ts
|
|
708
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
709
|
+
|
|
705
710
|
// src/heartbeat.ts
|
|
706
711
|
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
707
712
|
|
|
@@ -1782,6 +1787,9 @@ function computeAttention(input) {
|
|
|
1782
1787
|
return { state: "blocked", reason: input.completionBlocker };
|
|
1783
1788
|
}
|
|
1784
1789
|
if (input.finalResult) {
|
|
1790
|
+
if (input.localOnly && hasMergedTargetPrReconciliation(input.finalResult)) {
|
|
1791
|
+
return { state: "done", reason: "local-only worker superseded by merged PR" };
|
|
1792
|
+
}
|
|
1785
1793
|
const landingSnapshot = {
|
|
1786
1794
|
finalResult: input.finalResult,
|
|
1787
1795
|
changedFiles: input.changedFiles ?? [],
|
|
@@ -1847,9 +1855,24 @@ function computeAttention(input) {
|
|
|
1847
1855
|
}
|
|
1848
1856
|
return { state: "ok", reason: "recent activity" };
|
|
1849
1857
|
}
|
|
1858
|
+
function hasMergedTargetPrReconciliation(value) {
|
|
1859
|
+
let record = null;
|
|
1860
|
+
if (typeof value === "string") record = extractEmbeddedWorkerFinalResultRecord(value);
|
|
1861
|
+
else if (value && typeof value === "object" && !Array.isArray(value)) record = value;
|
|
1862
|
+
if (!record) return false;
|
|
1863
|
+
const raw = record.targetPrReconciliation ?? record.target_pr_reconciliation;
|
|
1864
|
+
if (!Array.isArray(raw)) return false;
|
|
1865
|
+
return raw.some((item) => {
|
|
1866
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return false;
|
|
1867
|
+
return String(item.outcome ?? "").trim() === "merged";
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1850
1870
|
function resolveFinalResult(worker, parsedFinalResult, heartbeat) {
|
|
1851
|
-
if (parsedFinalResult) return parsedFinalResult;
|
|
1852
1871
|
const ackSnapshot = worker.completionSnapshot?.finalResult;
|
|
1872
|
+
if (worker.completionAckSource === "local-pr-merged-reconcile" && ackSnapshot !== void 0 && ackSnapshot !== null) {
|
|
1873
|
+
return ackSnapshot;
|
|
1874
|
+
}
|
|
1875
|
+
if (parsedFinalResult) return parsedFinalResult;
|
|
1853
1876
|
if (ackSnapshot !== void 0 && ackSnapshot !== null) return ackSnapshot;
|
|
1854
1877
|
return terminalFinalResultFromHeartbeat(heartbeat);
|
|
1855
1878
|
}
|
|
@@ -1896,7 +1919,8 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
1896
1919
|
gitAncestry,
|
|
1897
1920
|
completionBlocker,
|
|
1898
1921
|
landingContract,
|
|
1899
|
-
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null
|
|
1922
|
+
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
|
|
1923
|
+
localOnly: worker.localOnly === true
|
|
1900
1924
|
});
|
|
1901
1925
|
const workerStatusLabel = completionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
|
|
1902
1926
|
return {
|
|
@@ -1950,6 +1974,33 @@ function deriveRunStatus(fallback, workers) {
|
|
|
1950
1974
|
return fallback;
|
|
1951
1975
|
}
|
|
1952
1976
|
|
|
1977
|
+
// src/harness-worker-active.ts
|
|
1978
|
+
function pidCommandLine(pid) {
|
|
1979
|
+
if (!pid || process.platform !== "linux") return null;
|
|
1980
|
+
try {
|
|
1981
|
+
return readFileSync7(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
|
|
1982
|
+
} catch {
|
|
1983
|
+
return null;
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
function workerProcessMatchesRecord(worker) {
|
|
1987
|
+
if (!worker.pid || process.platform !== "linux") return true;
|
|
1988
|
+
const cmdline = pidCommandLine(worker.pid);
|
|
1989
|
+
if (!cmdline) return false;
|
|
1990
|
+
const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
|
|
1991
|
+
(value) => typeof value === "string" && value.trim().length > 0
|
|
1992
|
+
);
|
|
1993
|
+
return probes.some((probe) => cmdline.includes(probe));
|
|
1994
|
+
}
|
|
1995
|
+
function isActiveHarnessWorker(worker) {
|
|
1996
|
+
if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
|
|
1997
|
+
return false;
|
|
1998
|
+
}
|
|
1999
|
+
const status = computeWorkerStatus(worker);
|
|
2000
|
+
if (status.alive && !workerProcessMatchesRecord(worker)) return false;
|
|
2001
|
+
return status.alive && !status.finalResult && status.attention.state !== "done";
|
|
2002
|
+
}
|
|
2003
|
+
|
|
1953
2004
|
// src/resource-gate.ts
|
|
1954
2005
|
var DEFAULT_PER_WORKER_MEM_BYTES = 500 * 1024 * 1024;
|
|
1955
2006
|
var DEFAULT_MEM_RESERVE_BYTES = 4 * 1024 * 1024 * 1024;
|
|
@@ -1992,31 +2043,6 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
|
|
|
1992
2043
|
function readAvailableMemBytes() {
|
|
1993
2044
|
return readMemAvailableBytes();
|
|
1994
2045
|
}
|
|
1995
|
-
function pidCommandLine(pid) {
|
|
1996
|
-
if (!pid || process.platform !== "linux") return null;
|
|
1997
|
-
try {
|
|
1998
|
-
return readFileSync7(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
|
|
1999
|
-
} catch {
|
|
2000
|
-
return null;
|
|
2001
|
-
}
|
|
2002
|
-
}
|
|
2003
|
-
function workerProcessMatchesRecord(worker) {
|
|
2004
|
-
if (!worker.pid || process.platform !== "linux") return true;
|
|
2005
|
-
const cmdline = pidCommandLine(worker.pid);
|
|
2006
|
-
if (!cmdline) return false;
|
|
2007
|
-
const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
|
|
2008
|
-
(value) => typeof value === "string" && value.trim().length > 0
|
|
2009
|
-
);
|
|
2010
|
-
return probes.some((probe) => cmdline.includes(probe));
|
|
2011
|
-
}
|
|
2012
|
-
function isActiveHarnessWorker(worker) {
|
|
2013
|
-
if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
|
|
2014
|
-
return false;
|
|
2015
|
-
}
|
|
2016
|
-
const status = computeWorkerStatus(worker);
|
|
2017
|
-
if (status.alive && !workerProcessMatchesRecord(worker)) return false;
|
|
2018
|
-
return status.alive && !status.finalResult && status.attention.state !== "done";
|
|
2019
|
-
}
|
|
2020
2046
|
function countActiveWorkersForRun(run) {
|
|
2021
2047
|
let active = 0;
|
|
2022
2048
|
for (const name of listRunWorkerNames(run)) {
|
|
@@ -5021,8 +5047,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
|
|
|
5021
5047
|
if (removed.length === 0) return false;
|
|
5022
5048
|
const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
|
|
5023
5049
|
return material.every((line) => {
|
|
5024
|
-
const
|
|
5025
|
-
return removedSet.has(
|
|
5050
|
+
const path71 = normalizeRelativePath(pathFromGitStatusLine(line));
|
|
5051
|
+
return removedSet.has(path71);
|
|
5026
5052
|
});
|
|
5027
5053
|
}
|
|
5028
5054
|
|
|
@@ -6316,7 +6342,7 @@ function collectRunActiveHarnessWorkers(runId) {
|
|
|
6316
6342
|
path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
6317
6343
|
void 0
|
|
6318
6344
|
);
|
|
6319
|
-
if (!worker?.taskId || !
|
|
6345
|
+
if (!worker?.taskId || !isActiveHarnessWorker(worker)) continue;
|
|
6320
6346
|
out.push({
|
|
6321
6347
|
runId: run.id,
|
|
6322
6348
|
workerName: name,
|
|
@@ -6940,6 +6966,11 @@ function readAdmissionExhaustion(result) {
|
|
|
6940
6966
|
if (!raw || typeof raw !== "object") return null;
|
|
6941
6967
|
return raw;
|
|
6942
6968
|
}
|
|
6969
|
+
function readDispatchSkipDrain(result) {
|
|
6970
|
+
const raw = result.dispatchSkipDrain;
|
|
6971
|
+
if (!raw || typeof raw !== "object") return null;
|
|
6972
|
+
return raw;
|
|
6973
|
+
}
|
|
6943
6974
|
function readHarnessWorkerContext(decision) {
|
|
6944
6975
|
const raw = decision.harnessWorkerContext;
|
|
6945
6976
|
if (!raw || typeof raw !== "object") return null;
|
|
@@ -7103,6 +7134,7 @@ async function dispatchRun(args) {
|
|
|
7103
7134
|
const result = first.result;
|
|
7104
7135
|
if (dryRun) {
|
|
7105
7136
|
const admissionExhaustion2 = readAdmissionExhaustion(result);
|
|
7137
|
+
const dispatchSkipDrain2 = readDispatchSkipDrain(result);
|
|
7106
7138
|
const summary2 = {
|
|
7107
7139
|
runId: run.id,
|
|
7108
7140
|
agentOsId,
|
|
@@ -7121,7 +7153,8 @@ async function dispatchRun(args) {
|
|
|
7121
7153
|
pagesScanned: result.pagesScanned ?? null,
|
|
7122
7154
|
candidatesExhausted: result.candidatesExhausted ?? null,
|
|
7123
7155
|
capacityIdle: admissionExhaustion2?.capacityIdle === true,
|
|
7124
|
-
admissionExhaustion: admissionExhaustion2
|
|
7156
|
+
admissionExhaustion: admissionExhaustion2,
|
|
7157
|
+
dispatchSkipDrain: dispatchSkipDrain2
|
|
7125
7158
|
};
|
|
7126
7159
|
if (pipeline) return { ok: true, ...summary2 };
|
|
7127
7160
|
console.log(JSON.stringify(summary2, null, 2));
|
|
@@ -7177,7 +7210,7 @@ async function dispatchRun(args) {
|
|
|
7177
7210
|
);
|
|
7178
7211
|
}
|
|
7179
7212
|
const attempt = Number(task.attempt) || 1;
|
|
7180
|
-
if (attempt
|
|
7213
|
+
if (attempt > retryLimits.maxTaskAttempts) {
|
|
7181
7214
|
return abortClaimedSpawn(
|
|
7182
7215
|
task,
|
|
7183
7216
|
`task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
|
|
@@ -7311,9 +7344,16 @@ async function dispatchRun(args) {
|
|
|
7311
7344
|
}
|
|
7312
7345
|
const startedCount = outcomes.filter((o) => o.started).length;
|
|
7313
7346
|
const admissionExhaustion = readAdmissionExhaustion(result);
|
|
7314
|
-
const
|
|
7347
|
+
const dispatchSkipDrain = readDispatchSkipDrain(result);
|
|
7348
|
+
const capacityIdle = startedCount === 0 && (admissionExhaustion?.capacityIdle === true || Number(result.resourceGate?.slotsAvailable) > 0);
|
|
7315
7349
|
if (capacityIdle && admissionExhaustion?.summary) {
|
|
7316
|
-
|
|
7350
|
+
const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
|
|
7351
|
+
const recovery = result.overAttemptIdleRecovery ?? admissionExhaustion.overAttemptIdleRecovery;
|
|
7352
|
+
const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
|
|
7353
|
+
const drainNote = dispatchSkipDrain ? `; dispatch_skip_drain scanned=${dispatchSkipDrain.scanned ?? 0} advanced=${dispatchSkipDrain.advanced ?? 0}` : "";
|
|
7354
|
+
console.error(
|
|
7355
|
+
`[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}${drainNote}`
|
|
7356
|
+
);
|
|
7317
7357
|
}
|
|
7318
7358
|
const summary = {
|
|
7319
7359
|
runId: run.id,
|
|
@@ -7323,6 +7363,7 @@ async function dispatchRun(args) {
|
|
|
7323
7363
|
startedCount,
|
|
7324
7364
|
capacityIdle,
|
|
7325
7365
|
admissionExhaustion,
|
|
7366
|
+
dispatchSkipDrain,
|
|
7326
7367
|
outcomes,
|
|
7327
7368
|
skipped: skipped.map((d) => ({
|
|
7328
7369
|
taskId: d.task.id,
|
|
@@ -7409,14 +7450,14 @@ async function sweepRun(args) {
|
|
|
7409
7450
|
|
|
7410
7451
|
// src/worktree.ts
|
|
7411
7452
|
import { existsSync as existsSync25, mkdirSync as mkdirSync6 } from "node:fs";
|
|
7412
|
-
import
|
|
7453
|
+
import path35 from "node:path";
|
|
7413
7454
|
|
|
7414
7455
|
// src/run-list.ts
|
|
7415
7456
|
import { existsSync as existsSync24, readFileSync as readFileSync11 } from "node:fs";
|
|
7416
|
-
import
|
|
7457
|
+
import path34 from "node:path";
|
|
7417
7458
|
|
|
7418
7459
|
// src/stale-reconcile.ts
|
|
7419
|
-
import
|
|
7460
|
+
import path33 from "node:path";
|
|
7420
7461
|
|
|
7421
7462
|
// src/finalize.ts
|
|
7422
7463
|
import path27 from "node:path";
|
|
@@ -7999,6 +8040,193 @@ function reconcileWorkerMetadata() {
|
|
|
7999
8040
|
return { workers: outcomes, runMetadataRetention };
|
|
8000
8041
|
}
|
|
8001
8042
|
|
|
8043
|
+
// src/local-pr-attention-reconcile.ts
|
|
8044
|
+
import { execFileSync } from "node:child_process";
|
|
8045
|
+
import path32 from "node:path";
|
|
8046
|
+
function normalizePrUrl3(url) {
|
|
8047
|
+
const m = url.trim().match(/github\.com\/([^/]+\/[^/]+)\/(?:pull|pulls)\/(\d+)/i);
|
|
8048
|
+
if (!m) return null;
|
|
8049
|
+
return `https://github.com/${m[1]}/pull/${m[2]}`;
|
|
8050
|
+
}
|
|
8051
|
+
function prNumberFromUrl(url) {
|
|
8052
|
+
const m = normalizePrUrl3(url)?.match(/\/pull\/(\d+)$/);
|
|
8053
|
+
if (!m) return null;
|
|
8054
|
+
const n = Number.parseInt(m[1], 10);
|
|
8055
|
+
return Number.isFinite(n) ? n : null;
|
|
8056
|
+
}
|
|
8057
|
+
function extractText(value) {
|
|
8058
|
+
if (value == null) return "";
|
|
8059
|
+
if (typeof value === "string") return value;
|
|
8060
|
+
try {
|
|
8061
|
+
return JSON.stringify(value);
|
|
8062
|
+
} catch {
|
|
8063
|
+
return "";
|
|
8064
|
+
}
|
|
8065
|
+
}
|
|
8066
|
+
function extractPrNumbersFromText(text) {
|
|
8067
|
+
const out = /* @__PURE__ */ new Set();
|
|
8068
|
+
for (const match of text.matchAll(/github\.com\/[^/\s)>"']+\/[^/\s)>"']+\/(?:pull|pulls)\/(\d+)/gi)) {
|
|
8069
|
+
const n = Number.parseInt(match[1], 10);
|
|
8070
|
+
if (Number.isFinite(n)) out.add(n);
|
|
8071
|
+
}
|
|
8072
|
+
for (const match of text.matchAll(/\bPR\s*#?\s*(\d{2,})\b/gi)) {
|
|
8073
|
+
const n = Number.parseInt(match[1], 10);
|
|
8074
|
+
if (Number.isFinite(n)) out.add(n);
|
|
8075
|
+
}
|
|
8076
|
+
for (const match of text.matchAll(/\bpr[-_]?(\d{2,})\b/gi)) {
|
|
8077
|
+
const n = Number.parseInt(match[1], 10);
|
|
8078
|
+
if (Number.isFinite(n)) out.add(n);
|
|
8079
|
+
}
|
|
8080
|
+
return [...out];
|
|
8081
|
+
}
|
|
8082
|
+
function extractTargetPrReconciliation(value) {
|
|
8083
|
+
let record = null;
|
|
8084
|
+
if (typeof value === "string") record = extractEmbeddedWorkerFinalResultRecord(value);
|
|
8085
|
+
else if (value && typeof value === "object" && !Array.isArray(value)) record = value;
|
|
8086
|
+
if (!record) return [];
|
|
8087
|
+
const raw = record.targetPrReconciliation ?? record.target_pr_reconciliation;
|
|
8088
|
+
if (!Array.isArray(raw)) return [];
|
|
8089
|
+
const out = [];
|
|
8090
|
+
for (const item of raw) {
|
|
8091
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
8092
|
+
const row = item;
|
|
8093
|
+
const prUrl = normalizePrUrl3(String(row.prUrl ?? row.pr_url ?? ""));
|
|
8094
|
+
const outcome = String(row.outcome ?? "").trim();
|
|
8095
|
+
if (!prUrl || outcome !== "merged") continue;
|
|
8096
|
+
out.push({
|
|
8097
|
+
prUrl,
|
|
8098
|
+
mergeCommit: typeof row.mergeCommit === "string" ? row.mergeCommit : typeof row.merge_commit === "string" ? row.merge_commit : null,
|
|
8099
|
+
reason: typeof row.reason === "string" ? row.reason : null
|
|
8100
|
+
});
|
|
8101
|
+
}
|
|
8102
|
+
return out;
|
|
8103
|
+
}
|
|
8104
|
+
function defaultLookupPr(input) {
|
|
8105
|
+
try {
|
|
8106
|
+
const repo = execFileSync("git", ["config", "--get", "remote.origin.url"], {
|
|
8107
|
+
cwd: input.repoDir,
|
|
8108
|
+
encoding: "utf8",
|
|
8109
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
8110
|
+
}).trim();
|
|
8111
|
+
const repoMatch = repo.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/i);
|
|
8112
|
+
const repoSlug = repoMatch?.[1];
|
|
8113
|
+
if (!repoSlug) return null;
|
|
8114
|
+
const raw = execFileSync(
|
|
8115
|
+
"gh",
|
|
8116
|
+
["pr", "view", String(input.prNumber), "--repo", repoSlug, "--json", "state,mergedAt,mergeCommit,url"],
|
|
8117
|
+
{ cwd: input.repoDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
8118
|
+
);
|
|
8119
|
+
const parsed = JSON.parse(raw);
|
|
8120
|
+
return {
|
|
8121
|
+
prUrl: normalizePrUrl3(parsed.url ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`) ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`,
|
|
8122
|
+
state: parsed.state ?? "",
|
|
8123
|
+
mergedAt: parsed.mergedAt ?? null,
|
|
8124
|
+
mergeCommit: parsed.mergeCommit?.oid ?? null
|
|
8125
|
+
};
|
|
8126
|
+
} catch {
|
|
8127
|
+
return null;
|
|
8128
|
+
}
|
|
8129
|
+
}
|
|
8130
|
+
function finalResultForWorker(worker) {
|
|
8131
|
+
const heartbeat = parseHeartbeat(worker.heartbeatPath);
|
|
8132
|
+
return worker.completionSnapshot?.finalResult ?? terminalFinalResultFromHeartbeat(heartbeat);
|
|
8133
|
+
}
|
|
8134
|
+
function collectMergedEvidenceByPr(run) {
|
|
8135
|
+
const byPr = /* @__PURE__ */ new Map();
|
|
8136
|
+
const runDir2 = runDirectory(run.id);
|
|
8137
|
+
for (const name of listRunWorkerNames(run)) {
|
|
8138
|
+
const worker = readJson(
|
|
8139
|
+
path32.join(runDir2, "workers", safeSlug(name), "worker.json"),
|
|
8140
|
+
void 0
|
|
8141
|
+
);
|
|
8142
|
+
if (!worker) continue;
|
|
8143
|
+
for (const evidence of extractTargetPrReconciliation(finalResultForWorker(worker))) {
|
|
8144
|
+
const n = prNumberFromUrl(evidence.prUrl);
|
|
8145
|
+
if (n != null && !byPr.has(n)) byPr.set(n, evidence);
|
|
8146
|
+
}
|
|
8147
|
+
}
|
|
8148
|
+
return byPr;
|
|
8149
|
+
}
|
|
8150
|
+
function candidatePrNumbers(worker, statusFinalResult) {
|
|
8151
|
+
const heartbeat = parseHeartbeat(worker.heartbeatPath);
|
|
8152
|
+
const text = [
|
|
8153
|
+
worker.name,
|
|
8154
|
+
worker.repairTargetPrUrl,
|
|
8155
|
+
worker.taskPrUrl,
|
|
8156
|
+
worker.branch,
|
|
8157
|
+
heartbeat.lastHeartbeatSummary,
|
|
8158
|
+
extractText(statusFinalResult)
|
|
8159
|
+
].filter(Boolean).join("\n");
|
|
8160
|
+
return extractPrNumbersFromText(text);
|
|
8161
|
+
}
|
|
8162
|
+
function isLocalOnlyAttentionNoise(worker) {
|
|
8163
|
+
return worker.localOnly === true && !worker.taskId && !worker.agentOsId;
|
|
8164
|
+
}
|
|
8165
|
+
function reconcileLocalOnlyMergedPrAttention(options = {}) {
|
|
8166
|
+
const lookupPr = options.lookupPr ?? defaultLookupPr;
|
|
8167
|
+
const outcomes = [];
|
|
8168
|
+
const liveLookupCache = /* @__PURE__ */ new Map();
|
|
8169
|
+
for (const run of listRunRecords()) {
|
|
8170
|
+
const mergedByPr = collectMergedEvidenceByPr(run);
|
|
8171
|
+
const runDir2 = runDirectory(run.id);
|
|
8172
|
+
for (const name of listRunWorkerNames(run)) {
|
|
8173
|
+
const workerPath = path32.join(runDir2, "workers", safeSlug(name), "worker.json");
|
|
8174
|
+
const worker = readJson(workerPath, void 0);
|
|
8175
|
+
if (!worker || !isLocalOnlyAttentionNoise(worker)) continue;
|
|
8176
|
+
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
8177
|
+
if (status.attention.state !== "needs_attention") continue;
|
|
8178
|
+
let merged = null;
|
|
8179
|
+
for (const prNumber of candidatePrNumbers(worker, status.finalResult)) {
|
|
8180
|
+
merged = mergedByPr.get(prNumber) ?? null;
|
|
8181
|
+
if (!merged) {
|
|
8182
|
+
const repoDir = run.repo || worker.worktreePath;
|
|
8183
|
+
const cacheKey = `${repoDir}#${prNumber}`;
|
|
8184
|
+
const live = liveLookupCache.has(cacheKey) ? liveLookupCache.get(cacheKey) ?? null : lookupPr({ repoDir, prNumber });
|
|
8185
|
+
if (!liveLookupCache.has(cacheKey)) liveLookupCache.set(cacheKey, live);
|
|
8186
|
+
if (live && (live.state === "MERGED" || live.mergedAt)) {
|
|
8187
|
+
merged = { prUrl: live.prUrl, mergeCommit: live.mergeCommit ?? null, reason: "GitHub reports PR merged" };
|
|
8188
|
+
}
|
|
8189
|
+
}
|
|
8190
|
+
if (merged) break;
|
|
8191
|
+
}
|
|
8192
|
+
if (!merged) {
|
|
8193
|
+
outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "no merged PR evidence" });
|
|
8194
|
+
continue;
|
|
8195
|
+
}
|
|
8196
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8197
|
+
worker.status = "done";
|
|
8198
|
+
worker.completionSnapshot = {
|
|
8199
|
+
prUrl: merged.prUrl,
|
|
8200
|
+
summary: `Local-only worker superseded by merged PR ${merged.prUrl}`,
|
|
8201
|
+
finalResult: {
|
|
8202
|
+
summary: `Local-only repair/salvage worker superseded by merged PR ${merged.prUrl}`,
|
|
8203
|
+
targetPrReconciliation: [
|
|
8204
|
+
{
|
|
8205
|
+
prUrl: merged.prUrl,
|
|
8206
|
+
outcome: "merged",
|
|
8207
|
+
mergeCommit: merged.mergeCommit ?? null,
|
|
8208
|
+
reason: merged.reason ?? "PR already merged; local-only worker no longer requires attention"
|
|
8209
|
+
}
|
|
8210
|
+
]
|
|
8211
|
+
}
|
|
8212
|
+
};
|
|
8213
|
+
worker.completionAckSource = "local-pr-merged-reconcile";
|
|
8214
|
+
worker.reconciledAt = now;
|
|
8215
|
+
worker.reconcileReason = "local-only needs_attention superseded by merged PR";
|
|
8216
|
+
saveWorker(run.id, worker);
|
|
8217
|
+
outcomes.push({
|
|
8218
|
+
runId: run.id,
|
|
8219
|
+
worker: name,
|
|
8220
|
+
action: "marked_done",
|
|
8221
|
+
reason: worker.reconcileReason,
|
|
8222
|
+
prUrl: merged.prUrl,
|
|
8223
|
+
mergeCommit: merged.mergeCommit ?? null
|
|
8224
|
+
});
|
|
8225
|
+
}
|
|
8226
|
+
}
|
|
8227
|
+
return { workers: outcomes };
|
|
8228
|
+
}
|
|
8229
|
+
|
|
8002
8230
|
// src/stale-reconcile.ts
|
|
8003
8231
|
var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
|
|
8004
8232
|
function staleReconcileDisabled() {
|
|
@@ -8007,13 +8235,14 @@ function staleReconcileDisabled() {
|
|
|
8007
8235
|
function reconcileStaleWorkers() {
|
|
8008
8236
|
const metadataReconcile = reconcileWorkerMetadata();
|
|
8009
8237
|
if (staleReconcileDisabled()) {
|
|
8010
|
-
|
|
8238
|
+
const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
|
|
8239
|
+
return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
|
|
8011
8240
|
}
|
|
8012
8241
|
const outcomes = [];
|
|
8013
8242
|
const now = Date.now();
|
|
8014
8243
|
for (const run of listRunRecords()) {
|
|
8015
8244
|
for (const name of listRunWorkerNames(run)) {
|
|
8016
|
-
const workerPath =
|
|
8245
|
+
const workerPath = path33.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
8017
8246
|
const worker = readJson(workerPath, void 0);
|
|
8018
8247
|
if (!worker || worker.status !== "running") {
|
|
8019
8248
|
outcomes.push({
|
|
@@ -8085,7 +8314,8 @@ function reconcileStaleWorkers() {
|
|
|
8085
8314
|
});
|
|
8086
8315
|
}
|
|
8087
8316
|
}
|
|
8088
|
-
|
|
8317
|
+
const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
|
|
8318
|
+
return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
|
|
8089
8319
|
}
|
|
8090
8320
|
function reconcileRunsCli() {
|
|
8091
8321
|
const result = reconcileStaleWorkers();
|
|
@@ -8096,6 +8326,10 @@ function reconcileRunsCli() {
|
|
|
8096
8326
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
8097
8327
|
return acc;
|
|
8098
8328
|
}, {});
|
|
8329
|
+
const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
|
|
8330
|
+
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
8331
|
+
return acc;
|
|
8332
|
+
}, {});
|
|
8099
8333
|
const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
|
|
8100
8334
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
8101
8335
|
return acc;
|
|
@@ -8113,10 +8347,15 @@ function reconcileRunsCli() {
|
|
|
8113
8347
|
total: result.metadataReconcile.runMetadataRetention.runs.length
|
|
8114
8348
|
}
|
|
8115
8349
|
},
|
|
8350
|
+
localPrAttentionReconcile: {
|
|
8351
|
+
totals: localPrAttentionTotals,
|
|
8352
|
+
total: result.localPrAttentionReconcile.workers.length
|
|
8353
|
+
},
|
|
8116
8354
|
finalizedRuns: result.finalizedRuns.length,
|
|
8117
8355
|
details: {
|
|
8118
8356
|
workers: result.workers,
|
|
8119
8357
|
metadataReconcile: result.metadataReconcile.workers,
|
|
8358
|
+
localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
|
|
8120
8359
|
runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
|
|
8121
8360
|
finalizedRuns: result.finalizedRuns
|
|
8122
8361
|
}
|
|
@@ -8137,7 +8376,7 @@ function heartbeatByteLength(heartbeatPath) {
|
|
|
8137
8376
|
}
|
|
8138
8377
|
}
|
|
8139
8378
|
function workerEvidence(run, workerName) {
|
|
8140
|
-
const workerPath =
|
|
8379
|
+
const workerPath = path34.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
|
|
8141
8380
|
const worker = readJson(workerPath, void 0);
|
|
8142
8381
|
if (!worker) {
|
|
8143
8382
|
return {
|
|
@@ -8194,7 +8433,7 @@ function aggregateRunAttention(workers) {
|
|
|
8194
8433
|
function countOpenWorkers(run) {
|
|
8195
8434
|
let open = 0;
|
|
8196
8435
|
for (const name of listRunWorkerNames(run)) {
|
|
8197
|
-
const workerPath =
|
|
8436
|
+
const workerPath = path34.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
8198
8437
|
const worker = readJson(workerPath, void 0);
|
|
8199
8438
|
if (!worker) continue;
|
|
8200
8439
|
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
@@ -8270,7 +8509,7 @@ function createRun(args) {
|
|
|
8270
8509
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8271
8510
|
workers: {}
|
|
8272
8511
|
};
|
|
8273
|
-
writeJson(
|
|
8512
|
+
writeJson(path35.join(dir, "run.json"), run);
|
|
8274
8513
|
console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
|
|
8275
8514
|
}
|
|
8276
8515
|
function listRuns() {
|
|
@@ -8283,7 +8522,7 @@ function failExists(message) {
|
|
|
8283
8522
|
|
|
8284
8523
|
// src/discard-disposable.ts
|
|
8285
8524
|
import { existsSync as existsSync26, rmSync as rmSync2 } from "node:fs";
|
|
8286
|
-
import
|
|
8525
|
+
import path36 from "node:path";
|
|
8287
8526
|
function normalizeRelativePath2(value) {
|
|
8288
8527
|
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
8289
8528
|
if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
|
|
@@ -8305,12 +8544,12 @@ function discardDisposableArtifacts(args) {
|
|
|
8305
8544
|
if (paths.length === 0) {
|
|
8306
8545
|
return { ok: false, removed: [], reason: "requires at least one --path" };
|
|
8307
8546
|
}
|
|
8308
|
-
const worktreeRoot =
|
|
8547
|
+
const worktreeRoot = path36.resolve(worker.worktreePath);
|
|
8309
8548
|
const removed = [];
|
|
8310
8549
|
for (const raw of paths) {
|
|
8311
8550
|
const rel = normalizeRelativePath2(raw);
|
|
8312
|
-
const abs =
|
|
8313
|
-
if (!abs.startsWith(worktreeRoot +
|
|
8551
|
+
const abs = path36.resolve(worktreeRoot, rel);
|
|
8552
|
+
if (!abs.startsWith(worktreeRoot + path36.sep) && abs !== worktreeRoot) {
|
|
8314
8553
|
return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
|
|
8315
8554
|
}
|
|
8316
8555
|
if (!existsSync26(abs)) {
|
|
@@ -8377,7 +8616,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
|
|
|
8377
8616
|
// src/cron/cron-env.ts
|
|
8378
8617
|
import { existsSync as existsSync27 } from "node:fs";
|
|
8379
8618
|
import { homedir as homedir11 } from "node:os";
|
|
8380
|
-
import
|
|
8619
|
+
import path37 from "node:path";
|
|
8381
8620
|
function envFlag(name, defaultValue) {
|
|
8382
8621
|
const raw = process.env[name]?.trim().toLowerCase();
|
|
8383
8622
|
if (!raw) return defaultValue;
|
|
@@ -8393,7 +8632,7 @@ function envInt(name, fallback, min = 1) {
|
|
|
8393
8632
|
function defaultKynverCronStorePath() {
|
|
8394
8633
|
const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
|
|
8395
8634
|
if (explicit) return explicit;
|
|
8396
|
-
return
|
|
8635
|
+
return path37.join(homedir11(), ".kynver", "agent-os-cron.json");
|
|
8397
8636
|
}
|
|
8398
8637
|
function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
|
|
8399
8638
|
const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
|
|
@@ -8635,7 +8874,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
|
|
|
8635
8874
|
// src/cron/cron-tick-state.ts
|
|
8636
8875
|
import { randomBytes } from "node:crypto";
|
|
8637
8876
|
import { promises as fs2 } from "node:fs";
|
|
8638
|
-
import
|
|
8877
|
+
import path38 from "node:path";
|
|
8639
8878
|
var EMPTY = { version: 1, jobs: {} };
|
|
8640
8879
|
async function readFileIfExists2(filePath) {
|
|
8641
8880
|
try {
|
|
@@ -8662,7 +8901,7 @@ async function loadCronTickState(statePath) {
|
|
|
8662
8901
|
return parseCronTickState(raw);
|
|
8663
8902
|
}
|
|
8664
8903
|
async function writeStateAtomic(statePath, state) {
|
|
8665
|
-
await fs2.mkdir(
|
|
8904
|
+
await fs2.mkdir(path38.dirname(statePath), { recursive: true });
|
|
8666
8905
|
const suffix = randomBytes(6).toString("hex");
|
|
8667
8906
|
const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
|
|
8668
8907
|
await fs2.writeFile(tmp, `${JSON.stringify(state, null, 2)}
|
|
@@ -8839,7 +9078,7 @@ async function runKynverCronTick(opts = {}) {
|
|
|
8839
9078
|
}
|
|
8840
9079
|
|
|
8841
9080
|
// src/pipeline-tick.ts
|
|
8842
|
-
import
|
|
9081
|
+
import path56 from "node:path";
|
|
8843
9082
|
|
|
8844
9083
|
// src/pipeline-dispatch.ts
|
|
8845
9084
|
var RESERVED_REVIEW_STARTS = 1;
|
|
@@ -8995,7 +9234,7 @@ function buildBoxResourceSnapshotFromGate(gate, input = {}) {
|
|
|
8995
9234
|
}
|
|
8996
9235
|
|
|
8997
9236
|
// src/plan-progress-daemon-sync.ts
|
|
8998
|
-
import
|
|
9237
|
+
import path39 from "node:path";
|
|
8999
9238
|
|
|
9000
9239
|
// src/plan-progress-sync.ts
|
|
9001
9240
|
async function syncPlanProgress(args) {
|
|
@@ -9019,7 +9258,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
9019
9258
|
const outcomes = [];
|
|
9020
9259
|
for (const name of Object.keys(run.workers || {})) {
|
|
9021
9260
|
const worker = readJson(
|
|
9022
|
-
|
|
9261
|
+
path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
9023
9262
|
void 0
|
|
9024
9263
|
);
|
|
9025
9264
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -9076,10 +9315,10 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
|
|
|
9076
9315
|
}
|
|
9077
9316
|
|
|
9078
9317
|
// src/cleanup.ts
|
|
9079
|
-
import
|
|
9318
|
+
import path53 from "node:path";
|
|
9080
9319
|
|
|
9081
9320
|
// src/cleanup-guards.ts
|
|
9082
|
-
import
|
|
9321
|
+
import path40 from "node:path";
|
|
9083
9322
|
|
|
9084
9323
|
// src/cleanup-build-cache-paths.ts
|
|
9085
9324
|
var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
|
|
@@ -9220,7 +9459,7 @@ function skipWorktreeRemoval(input) {
|
|
|
9220
9459
|
function skipDependencyCacheRemoval(input) {
|
|
9221
9460
|
const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
|
|
9222
9461
|
if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
9223
|
-
if (activeWorktreePaths.has(
|
|
9462
|
+
if (activeWorktreePaths.has(path40.resolve(worktreePath))) return "active_worker";
|
|
9224
9463
|
if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
|
|
9225
9464
|
if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
|
|
9226
9465
|
return null;
|
|
@@ -9247,11 +9486,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
|
9247
9486
|
function collectPreservedLivePaths(actions, skips) {
|
|
9248
9487
|
const out = [];
|
|
9249
9488
|
const seen = /* @__PURE__ */ new Set();
|
|
9250
|
-
const push = (
|
|
9251
|
-
const key = `${
|
|
9489
|
+
const push = (path71, reason, detail) => {
|
|
9490
|
+
const key = `${path71}\0${reason}`;
|
|
9252
9491
|
if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
|
|
9253
9492
|
seen.add(key);
|
|
9254
|
-
out.push({ path:
|
|
9493
|
+
out.push({ path: path71, reason, ...detail ? { detail } : {} });
|
|
9255
9494
|
};
|
|
9256
9495
|
for (const skip2 of skips) {
|
|
9257
9496
|
if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
|
|
@@ -9267,11 +9506,11 @@ function collectPreservedLivePaths(actions, skips) {
|
|
|
9267
9506
|
|
|
9268
9507
|
// src/cleanup-run-directory.ts
|
|
9269
9508
|
import { existsSync as existsSync30, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
|
|
9270
|
-
import
|
|
9509
|
+
import path42 from "node:path";
|
|
9271
9510
|
|
|
9272
9511
|
// src/cleanup-active-worktrees.ts
|
|
9273
9512
|
import { existsSync as existsSync29, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
|
|
9274
|
-
import
|
|
9513
|
+
import path41 from "node:path";
|
|
9275
9514
|
function workerHasRecentHarnessActivity(worker, now) {
|
|
9276
9515
|
const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
|
|
9277
9516
|
for (const target of paths) {
|
|
@@ -9297,11 +9536,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
|
|
|
9297
9536
|
let runHasLive = false;
|
|
9298
9537
|
for (const name of Object.keys(run.workers || {})) {
|
|
9299
9538
|
const worker = readJson(
|
|
9300
|
-
|
|
9539
|
+
path41.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
|
|
9301
9540
|
void 0
|
|
9302
9541
|
);
|
|
9303
9542
|
if (!worker?.worktreePath) continue;
|
|
9304
|
-
const worktreePath =
|
|
9543
|
+
const worktreePath = path41.resolve(worker.worktreePath);
|
|
9305
9544
|
if (!isActiveHarnessWorker2(worker, now)) continue;
|
|
9306
9545
|
runHasLive = true;
|
|
9307
9546
|
activeWorktreePaths.add(worktreePath);
|
|
@@ -9329,7 +9568,7 @@ function pathAgeMs(target, now) {
|
|
|
9329
9568
|
}
|
|
9330
9569
|
}
|
|
9331
9570
|
function loadRunStatus(harnessRoot, runId) {
|
|
9332
|
-
const runPath =
|
|
9571
|
+
const runPath = path42.join(harnessRoot, "runs", runId, "run.json");
|
|
9333
9572
|
if (!existsSync30(runPath)) return null;
|
|
9334
9573
|
return readJson(runPath, null);
|
|
9335
9574
|
}
|
|
@@ -9365,7 +9604,7 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
9365
9604
|
if (!runEntry.isDirectory()) continue;
|
|
9366
9605
|
const runId = runEntry.name;
|
|
9367
9606
|
if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
|
|
9368
|
-
const runPath =
|
|
9607
|
+
const runPath = path42.join(opts.worktreesDir, runId);
|
|
9369
9608
|
if (!runDirectoryIsEmpty(runPath)) continue;
|
|
9370
9609
|
candidates.push({
|
|
9371
9610
|
kind: "remove_run_directory",
|
|
@@ -9383,14 +9622,14 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
9383
9622
|
import { existsSync as existsSync33, rmSync as rmSync4 } from "node:fs";
|
|
9384
9623
|
|
|
9385
9624
|
// src/cleanup-dir-size.ts
|
|
9386
|
-
import { execFileSync } from "node:child_process";
|
|
9625
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
9387
9626
|
import { existsSync as existsSync31, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
|
|
9388
|
-
import
|
|
9627
|
+
import path43 from "node:path";
|
|
9389
9628
|
var DEFAULT_DU_TIMEOUT_MS = 2500;
|
|
9390
9629
|
function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
|
|
9391
9630
|
if (!existsSync31(root)) return 0;
|
|
9392
9631
|
try {
|
|
9393
|
-
const out =
|
|
9632
|
+
const out = execFileSync2("du", ["-sb", root], {
|
|
9394
9633
|
encoding: "utf8",
|
|
9395
9634
|
timeout: timeoutMs,
|
|
9396
9635
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -9419,7 +9658,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
|
|
|
9419
9658
|
}
|
|
9420
9659
|
for (const name of entries) {
|
|
9421
9660
|
if (seen++ > maxEntries) return null;
|
|
9422
|
-
const full =
|
|
9661
|
+
const full = path43.join(current, name);
|
|
9423
9662
|
let st;
|
|
9424
9663
|
try {
|
|
9425
9664
|
st = statSync8(full);
|
|
@@ -9470,20 +9709,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
9470
9709
|
|
|
9471
9710
|
// src/cleanup-privileged-remove.ts
|
|
9472
9711
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
9473
|
-
import
|
|
9712
|
+
import path45 from "node:path";
|
|
9474
9713
|
|
|
9475
9714
|
// src/cleanup-harness-path-validate.ts
|
|
9476
|
-
import
|
|
9715
|
+
import path44 from "node:path";
|
|
9477
9716
|
function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
|
|
9478
|
-
const resolved =
|
|
9479
|
-
const suffix = `${
|
|
9717
|
+
const resolved = path44.resolve(targetPath);
|
|
9718
|
+
const suffix = `${path44.sep}${cacheDirName}`;
|
|
9480
9719
|
const cachePath = resolved.endsWith(suffix) ? resolved : null;
|
|
9481
9720
|
if (!cachePath) return "path_outside_harness";
|
|
9482
|
-
const rel =
|
|
9483
|
-
if (rel.startsWith("..") ||
|
|
9484
|
-
const parts = rel.split(
|
|
9721
|
+
const rel = path44.relative(worktreesDir, cachePath);
|
|
9722
|
+
if (rel.startsWith("..") || path44.isAbsolute(rel)) return "path_outside_harness";
|
|
9723
|
+
const parts = rel.split(path44.sep);
|
|
9485
9724
|
if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
|
|
9486
|
-
if (!resolved.startsWith(
|
|
9725
|
+
if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
|
|
9487
9726
|
return null;
|
|
9488
9727
|
}
|
|
9489
9728
|
function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
|
|
@@ -9493,16 +9732,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
|
9493
9732
|
return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
|
|
9494
9733
|
}
|
|
9495
9734
|
function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
9496
|
-
const resolved =
|
|
9497
|
-
const relToWt =
|
|
9498
|
-
if (relToWt.startsWith("..") ||
|
|
9499
|
-
const parts = relToWt.split(
|
|
9735
|
+
const resolved = path44.resolve(targetPath);
|
|
9736
|
+
const relToWt = path44.relative(worktreesDir, resolved);
|
|
9737
|
+
if (relToWt.startsWith("..") || path44.isAbsolute(relToWt)) return "path_outside_harness";
|
|
9738
|
+
const parts = relToWt.split(path44.sep);
|
|
9500
9739
|
if (parts.length < 3) return "path_outside_harness";
|
|
9501
|
-
if (!resolved.startsWith(
|
|
9740
|
+
if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
|
|
9502
9741
|
return null;
|
|
9503
9742
|
}
|
|
9504
9743
|
function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
9505
|
-
const resolved =
|
|
9744
|
+
const resolved = path44.resolve(targetPath);
|
|
9506
9745
|
return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
|
|
9507
9746
|
}
|
|
9508
9747
|
|
|
@@ -9536,12 +9775,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
|
|
|
9536
9775
|
"chown",
|
|
9537
9776
|
"-R",
|
|
9538
9777
|
`${effectiveUid}:${effectiveGid}`,
|
|
9539
|
-
|
|
9778
|
+
path45.resolve(targetPath)
|
|
9540
9779
|
]);
|
|
9541
9780
|
if (chown.ok) {
|
|
9542
9781
|
return { ok: true, method: "chown_then_rm" };
|
|
9543
9782
|
}
|
|
9544
|
-
const rm = runSudoNonInteractive(["rm", "-rf",
|
|
9783
|
+
const rm = runSudoNonInteractive(["rm", "-rf", path45.resolve(targetPath)]);
|
|
9545
9784
|
if (rm.ok) {
|
|
9546
9785
|
return { ok: true, method: "sudo_rm" };
|
|
9547
9786
|
}
|
|
@@ -9743,7 +9982,7 @@ function removeWorktree(candidate, execute) {
|
|
|
9743
9982
|
|
|
9744
9983
|
// src/cleanup-scan.ts
|
|
9745
9984
|
import { existsSync as existsSync34, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
|
|
9746
|
-
import
|
|
9985
|
+
import path46 from "node:path";
|
|
9747
9986
|
function pathAgeMs2(target, now) {
|
|
9748
9987
|
try {
|
|
9749
9988
|
const mtime = statSync9(target).mtimeMs;
|
|
@@ -9753,16 +9992,16 @@ function pathAgeMs2(target, now) {
|
|
|
9753
9992
|
}
|
|
9754
9993
|
}
|
|
9755
9994
|
function isPathInside(child, parent) {
|
|
9756
|
-
const rel =
|
|
9757
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
9995
|
+
const rel = path46.relative(parent, child);
|
|
9996
|
+
return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
|
|
9758
9997
|
}
|
|
9759
9998
|
function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
|
|
9760
9999
|
const out = [];
|
|
9761
10000
|
for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
|
|
9762
10001
|
if (rel === ".next") continue;
|
|
9763
|
-
const target =
|
|
10002
|
+
const target = path46.join(worktreePath, rel);
|
|
9764
10003
|
if (!existsSync34(target)) continue;
|
|
9765
|
-
const resolved =
|
|
10004
|
+
const resolved = path46.resolve(target);
|
|
9766
10005
|
if (seen.has(resolved)) continue;
|
|
9767
10006
|
if (!isPathInside(resolved, opts.harnessRoot)) continue;
|
|
9768
10007
|
seen.add(resolved);
|
|
@@ -9794,10 +10033,10 @@ function scanBuildCacheCandidates(opts) {
|
|
|
9794
10033
|
if (!opts.includeOrphans || !existsSync34(opts.worktreesDir)) return candidates;
|
|
9795
10034
|
for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
|
|
9796
10035
|
if (!runEntry.isDirectory()) continue;
|
|
9797
|
-
const runPath =
|
|
10036
|
+
const runPath = path46.join(opts.worktreesDir, runEntry.name);
|
|
9798
10037
|
for (const workerEntry of readdirSync12(runPath, { withFileTypes: true })) {
|
|
9799
10038
|
if (!workerEntry.isDirectory()) continue;
|
|
9800
|
-
const worktreePath =
|
|
10039
|
+
const worktreePath = path46.join(runPath, workerEntry.name);
|
|
9801
10040
|
candidates.push(
|
|
9802
10041
|
...collectBuildCacheForWorktree(worktreePath, opts, seen, {
|
|
9803
10042
|
runId: runEntry.name,
|
|
@@ -9835,12 +10074,12 @@ function scanWorktreeCandidates(opts) {
|
|
|
9835
10074
|
if (!orphanEnabled || !existsSync34(opts.worktreesDir)) return candidates;
|
|
9836
10075
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
9837
10076
|
for (const entry of opts.index.values()) {
|
|
9838
|
-
indexedPaths.add(
|
|
10077
|
+
indexedPaths.add(path46.resolve(entry.worktreePath));
|
|
9839
10078
|
}
|
|
9840
10079
|
for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
|
|
9841
10080
|
if (!runEntry.isDirectory()) continue;
|
|
9842
10081
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
9843
|
-
const runPath =
|
|
10082
|
+
const runPath = path46.join(opts.worktreesDir, runEntry.name);
|
|
9844
10083
|
let workerEntries;
|
|
9845
10084
|
try {
|
|
9846
10085
|
workerEntries = readdirSync12(runPath, { withFileTypes: true });
|
|
@@ -9849,7 +10088,7 @@ function scanWorktreeCandidates(opts) {
|
|
|
9849
10088
|
}
|
|
9850
10089
|
for (const workerEntry of workerEntries) {
|
|
9851
10090
|
if (!workerEntry.isDirectory()) continue;
|
|
9852
|
-
const worktreePath =
|
|
10091
|
+
const worktreePath = path46.resolve(path46.join(runPath, workerEntry.name));
|
|
9853
10092
|
if (seen.has(worktreePath)) continue;
|
|
9854
10093
|
if (indexedPaths.has(worktreePath)) continue;
|
|
9855
10094
|
if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
|
|
@@ -9869,7 +10108,7 @@ function scanWorktreeCandidates(opts) {
|
|
|
9869
10108
|
|
|
9870
10109
|
// src/cleanup-dependency-scan.ts
|
|
9871
10110
|
import { existsSync as existsSync35, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
|
|
9872
|
-
import
|
|
10111
|
+
import path47 from "node:path";
|
|
9873
10112
|
var DEPENDENCY_CACHE_DIRS = [
|
|
9874
10113
|
{ dirName: "node_modules", kind: "remove_node_modules" },
|
|
9875
10114
|
{ dirName: ".next", kind: "remove_next_cache" }
|
|
@@ -9883,12 +10122,12 @@ function pathAgeMs3(target, now) {
|
|
|
9883
10122
|
}
|
|
9884
10123
|
}
|
|
9885
10124
|
function isPathInside2(child, parent) {
|
|
9886
|
-
const rel =
|
|
9887
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
10125
|
+
const rel = path47.relative(parent, child);
|
|
10126
|
+
return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
|
|
9888
10127
|
}
|
|
9889
10128
|
function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
9890
10129
|
if (!existsSync35(targetPath)) return;
|
|
9891
|
-
const resolved =
|
|
10130
|
+
const resolved = path47.resolve(targetPath);
|
|
9892
10131
|
if (seen.has(resolved)) return;
|
|
9893
10132
|
if (!isPathInside2(resolved, opts.harnessRoot)) return;
|
|
9894
10133
|
seen.add(resolved);
|
|
@@ -9905,7 +10144,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
|
9905
10144
|
}
|
|
9906
10145
|
function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
|
|
9907
10146
|
for (const entry of DEPENDENCY_CACHE_DIRS) {
|
|
9908
|
-
pushCandidate2(candidates, seen, opts,
|
|
10147
|
+
pushCandidate2(candidates, seen, opts, path47.join(worktreePath, entry.dirName), entry.kind, meta);
|
|
9909
10148
|
}
|
|
9910
10149
|
}
|
|
9911
10150
|
function scanDependencyCacheCandidates(opts) {
|
|
@@ -9923,7 +10162,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
9923
10162
|
for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
|
|
9924
10163
|
if (!runEntry.isDirectory()) continue;
|
|
9925
10164
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
9926
|
-
const runPath =
|
|
10165
|
+
const runPath = path47.join(opts.worktreesDir, runEntry.name);
|
|
9927
10166
|
let workerEntries;
|
|
9928
10167
|
try {
|
|
9929
10168
|
workerEntries = readdirSync13(runPath, { withFileTypes: true });
|
|
@@ -9932,7 +10171,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
9932
10171
|
}
|
|
9933
10172
|
for (const workerEntry of workerEntries) {
|
|
9934
10173
|
if (!workerEntry.isDirectory()) continue;
|
|
9935
|
-
const worktreePath =
|
|
10174
|
+
const worktreePath = path47.join(runPath, workerEntry.name);
|
|
9936
10175
|
scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
|
|
9937
10176
|
runId: runEntry.name,
|
|
9938
10177
|
worker: workerEntry.name
|
|
@@ -9944,7 +10183,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
9944
10183
|
|
|
9945
10184
|
// src/cleanup-duplicate-worktrees.ts
|
|
9946
10185
|
import { existsSync as existsSync36, statSync as statSync11 } from "node:fs";
|
|
9947
|
-
import
|
|
10186
|
+
import path48 from "node:path";
|
|
9948
10187
|
function pathAgeMs4(target, now) {
|
|
9949
10188
|
try {
|
|
9950
10189
|
const mtime = statSync11(target).mtimeMs;
|
|
@@ -9974,8 +10213,8 @@ function parseWorktreePorcelain(output) {
|
|
|
9974
10213
|
return records;
|
|
9975
10214
|
}
|
|
9976
10215
|
function isUnderWorktreesDir(worktreePath, worktreesDir) {
|
|
9977
|
-
const rel =
|
|
9978
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
10216
|
+
const rel = path48.relative(path48.resolve(worktreesDir), path48.resolve(worktreePath));
|
|
10217
|
+
return rel !== "" && !rel.startsWith("..") && !path48.isAbsolute(rel);
|
|
9979
10218
|
}
|
|
9980
10219
|
function isCleanWorktree(worktreePath, repoRoot) {
|
|
9981
10220
|
try {
|
|
@@ -9991,11 +10230,11 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
9991
10230
|
if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return [];
|
|
9992
10231
|
const repos = /* @__PURE__ */ new Set();
|
|
9993
10232
|
for (const entry of opts.index.values()) {
|
|
9994
|
-
if (entry.run.repo) repos.add(
|
|
10233
|
+
if (entry.run.repo) repos.add(path48.resolve(entry.run.repo));
|
|
9995
10234
|
}
|
|
9996
10235
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
9997
10236
|
for (const entry of opts.index.values()) {
|
|
9998
|
-
indexedPaths.add(
|
|
10237
|
+
indexedPaths.add(path48.resolve(entry.worktreePath));
|
|
9999
10238
|
}
|
|
10000
10239
|
const candidates = [];
|
|
10001
10240
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -10008,15 +10247,15 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
10008
10247
|
}
|
|
10009
10248
|
const worktrees = parseWorktreePorcelain(porcelain);
|
|
10010
10249
|
for (const wt of worktrees) {
|
|
10011
|
-
const resolved =
|
|
10012
|
-
if (resolved ===
|
|
10250
|
+
const resolved = path48.resolve(wt.path);
|
|
10251
|
+
if (resolved === path48.resolve(repoRoot)) continue;
|
|
10013
10252
|
if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
|
|
10014
10253
|
if (indexedPaths.has(resolved)) continue;
|
|
10015
10254
|
if (seen.has(resolved)) continue;
|
|
10016
10255
|
if (!existsSync36(resolved)) continue;
|
|
10017
10256
|
if (!isCleanWorktree(resolved, repoRoot)) continue;
|
|
10018
|
-
const rel =
|
|
10019
|
-
const parts = rel.split(
|
|
10257
|
+
const rel = path48.relative(opts.worktreesDir, resolved);
|
|
10258
|
+
const parts = rel.split(path48.sep);
|
|
10020
10259
|
const runId = parts[0];
|
|
10021
10260
|
const worker = parts[1] ?? "unknown";
|
|
10022
10261
|
seen.add(resolved);
|
|
@@ -10035,12 +10274,12 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
10035
10274
|
}
|
|
10036
10275
|
|
|
10037
10276
|
// src/cleanup-worktree-index.ts
|
|
10038
|
-
import
|
|
10277
|
+
import path49 from "node:path";
|
|
10039
10278
|
function buildWorktreeIndexAt(harnessRoot) {
|
|
10040
10279
|
const index = /* @__PURE__ */ new Map();
|
|
10041
10280
|
for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
|
|
10042
10281
|
for (const name of Object.keys(run.workers || {})) {
|
|
10043
|
-
const workerPath =
|
|
10282
|
+
const workerPath = path49.join(
|
|
10044
10283
|
runDirectoryAt(harnessRoot, run.id),
|
|
10045
10284
|
"workers",
|
|
10046
10285
|
safeSlug(name),
|
|
@@ -10048,9 +10287,9 @@ function buildWorktreeIndexAt(harnessRoot) {
|
|
|
10048
10287
|
);
|
|
10049
10288
|
const worker = readJson(workerPath, void 0);
|
|
10050
10289
|
if (!worker?.worktreePath) continue;
|
|
10051
|
-
index.set(
|
|
10290
|
+
index.set(path49.resolve(worker.worktreePath), {
|
|
10052
10291
|
harnessRoot,
|
|
10053
|
-
worktreePath:
|
|
10292
|
+
worktreePath: path49.resolve(worker.worktreePath),
|
|
10054
10293
|
runId: run.id,
|
|
10055
10294
|
workerName: name,
|
|
10056
10295
|
run,
|
|
@@ -10120,14 +10359,14 @@ function resolvePipelineHarnessRetention(runId) {
|
|
|
10120
10359
|
|
|
10121
10360
|
// src/cleanup-orphan-safety.ts
|
|
10122
10361
|
import { existsSync as existsSync37, statSync as statSync12 } from "node:fs";
|
|
10123
|
-
import
|
|
10362
|
+
import path50 from "node:path";
|
|
10124
10363
|
var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
|
|
10125
10364
|
function assessOrphanWorktreeSafety(input) {
|
|
10126
10365
|
const now = input.now ?? Date.now();
|
|
10127
10366
|
const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
|
|
10128
10367
|
if (!existsSync37(input.worktreePath)) return null;
|
|
10129
10368
|
if (input.runId && input.workerName) {
|
|
10130
|
-
const heartbeatPath =
|
|
10369
|
+
const heartbeatPath = path50.join(
|
|
10131
10370
|
input.harnessRoot,
|
|
10132
10371
|
"runs",
|
|
10133
10372
|
input.runId,
|
|
@@ -10141,7 +10380,7 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
10141
10380
|
} catch {
|
|
10142
10381
|
}
|
|
10143
10382
|
}
|
|
10144
|
-
const gitDir =
|
|
10383
|
+
const gitDir = path50.join(input.worktreePath, ".git");
|
|
10145
10384
|
if (!existsSync37(gitDir)) return null;
|
|
10146
10385
|
const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
|
|
10147
10386
|
if (porcelain.status !== 0) return "pr_or_unmerged_commits";
|
|
@@ -10172,7 +10411,7 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
10172
10411
|
|
|
10173
10412
|
// src/harness-storage-snapshot.ts
|
|
10174
10413
|
import { existsSync as existsSync38, readdirSync as readdirSync14, statSync as statSync13 } from "node:fs";
|
|
10175
|
-
import
|
|
10414
|
+
import path51 from "node:path";
|
|
10176
10415
|
function harnessStorageSnapshot(opts = {}) {
|
|
10177
10416
|
const harnessRoot = normalizeHarnessRoot(opts.harnessRoot ?? resolveHarnessRoot());
|
|
10178
10417
|
const worktreesDir = harnessWorktreesDir(harnessRoot);
|
|
@@ -10210,7 +10449,7 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
10210
10449
|
for (const runEntry of entries) {
|
|
10211
10450
|
if (!runEntry.isDirectory()) continue;
|
|
10212
10451
|
runCount += 1;
|
|
10213
|
-
const runPath =
|
|
10452
|
+
const runPath = path51.join(worktreesDir, runEntry.name);
|
|
10214
10453
|
try {
|
|
10215
10454
|
const st = statSync13(runPath);
|
|
10216
10455
|
oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
|
|
@@ -10247,10 +10486,10 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
10247
10486
|
// src/cleanup-harness-roots.ts
|
|
10248
10487
|
import { existsSync as existsSync39 } from "node:fs";
|
|
10249
10488
|
import { homedir as homedir12 } from "node:os";
|
|
10250
|
-
import
|
|
10489
|
+
import path52 from "node:path";
|
|
10251
10490
|
var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
|
|
10252
10491
|
"/var/tmp/kynver-harness",
|
|
10253
|
-
|
|
10492
|
+
path52.join(homedir12(), ".openclaw", "harness")
|
|
10254
10493
|
];
|
|
10255
10494
|
function addRoot(seen, roots, candidate) {
|
|
10256
10495
|
if (!candidate?.trim()) return;
|
|
@@ -10272,7 +10511,7 @@ function resolveHarnessScanRoots(options = {}) {
|
|
|
10272
10511
|
for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
|
|
10273
10512
|
if (shouldScanWellKnownRoots(options)) {
|
|
10274
10513
|
for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
|
|
10275
|
-
const resolved =
|
|
10514
|
+
const resolved = path52.resolve(candidate);
|
|
10276
10515
|
if (!seen.has(resolved) && existsSync39(resolved)) addRoot(seen, roots, resolved);
|
|
10277
10516
|
}
|
|
10278
10517
|
}
|
|
@@ -10427,9 +10666,9 @@ function mergeWorktreeIndexes(scanRoots) {
|
|
|
10427
10666
|
}
|
|
10428
10667
|
function worktreePathForCandidate(candidate, worktreesDir) {
|
|
10429
10668
|
if (candidate.runId && candidate.worker) {
|
|
10430
|
-
return
|
|
10669
|
+
return path53.join(worktreesDir, candidate.runId, candidate.worker);
|
|
10431
10670
|
}
|
|
10432
|
-
return
|
|
10671
|
+
return path53.resolve(candidate.path, "..");
|
|
10433
10672
|
}
|
|
10434
10673
|
function runHarnessCleanup(options = {}) {
|
|
10435
10674
|
let retention = resolveHarnessRetention(options);
|
|
@@ -10454,7 +10693,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10454
10693
|
for (const harnessRoot of paths.scanRoots) {
|
|
10455
10694
|
if (atSweepCap()) break;
|
|
10456
10695
|
emitCleanupProgress("root", harnessRoot);
|
|
10457
|
-
const worktreesDir =
|
|
10696
|
+
const worktreesDir = path53.join(harnessRoot, "worktrees");
|
|
10458
10697
|
const scanOpts = {
|
|
10459
10698
|
harnessRoot,
|
|
10460
10699
|
worktreesDir,
|
|
@@ -10467,7 +10706,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10467
10706
|
};
|
|
10468
10707
|
for (const raw of scanDependencyCacheCandidates(scanOpts)) {
|
|
10469
10708
|
if (atSweepCap()) break;
|
|
10470
|
-
const resolved =
|
|
10709
|
+
const resolved = path53.resolve(raw.path);
|
|
10471
10710
|
if (processedPaths.has(resolved)) continue;
|
|
10472
10711
|
processedPaths.add(resolved);
|
|
10473
10712
|
const candidate = { ...raw, path: resolved };
|
|
@@ -10478,7 +10717,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10478
10717
|
continue;
|
|
10479
10718
|
}
|
|
10480
10719
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
10481
|
-
const indexed = index.get(
|
|
10720
|
+
const indexed = index.get(path53.resolve(worktreePath)) ?? null;
|
|
10482
10721
|
const guardReason = skipDependencyCacheRemoval({
|
|
10483
10722
|
indexed,
|
|
10484
10723
|
includeOrphans: true,
|
|
@@ -10502,7 +10741,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10502
10741
|
}
|
|
10503
10742
|
for (const raw of scanBuildCacheCandidates(scanOpts)) {
|
|
10504
10743
|
if (atSweepCap()) break;
|
|
10505
|
-
const resolved =
|
|
10744
|
+
const resolved = path53.resolve(raw.path);
|
|
10506
10745
|
if (processedPaths.has(resolved)) continue;
|
|
10507
10746
|
processedPaths.add(resolved);
|
|
10508
10747
|
const candidate = { ...raw, path: resolved };
|
|
@@ -10513,7 +10752,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10513
10752
|
continue;
|
|
10514
10753
|
}
|
|
10515
10754
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
10516
|
-
const indexed = index.get(
|
|
10755
|
+
const indexed = index.get(path53.resolve(worktreePath)) ?? null;
|
|
10517
10756
|
const guardReason = skipBuildCacheRemoval({
|
|
10518
10757
|
indexed,
|
|
10519
10758
|
includeOrphans: true,
|
|
@@ -10543,11 +10782,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
10543
10782
|
const worktreeSeen = /* @__PURE__ */ new Set();
|
|
10544
10783
|
for (const raw of worktreeCandidates) {
|
|
10545
10784
|
if (atSweepCap()) break;
|
|
10546
|
-
const resolved =
|
|
10785
|
+
const resolved = path53.resolve(raw.path);
|
|
10547
10786
|
if (worktreeSeen.has(resolved)) continue;
|
|
10548
10787
|
worktreeSeen.add(resolved);
|
|
10549
10788
|
const candidate = { ...raw, path: resolved };
|
|
10550
|
-
const indexed = index.get(
|
|
10789
|
+
const indexed = index.get(path53.resolve(candidate.path)) ?? null;
|
|
10551
10790
|
const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
|
|
10552
10791
|
worktreePath: candidate.path,
|
|
10553
10792
|
harnessRoot,
|
|
@@ -10557,7 +10796,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10557
10796
|
});
|
|
10558
10797
|
const guardSkip = skipWorktreeRemoval({
|
|
10559
10798
|
indexed,
|
|
10560
|
-
worktreePath:
|
|
10799
|
+
worktreePath: path53.resolve(candidate.path),
|
|
10561
10800
|
includeOrphans: retention.includeOrphans,
|
|
10562
10801
|
worktreesAgeMs: retention.worktreesAgeMs,
|
|
10563
10802
|
terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
|
|
@@ -10589,11 +10828,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
10589
10828
|
now: paths.now
|
|
10590
10829
|
})) {
|
|
10591
10830
|
if (atSweepCap()) break;
|
|
10592
|
-
const resolved =
|
|
10831
|
+
const resolved = path53.resolve(raw.path);
|
|
10593
10832
|
if (processedPaths.has(resolved)) continue;
|
|
10594
10833
|
processedPaths.add(resolved);
|
|
10595
10834
|
const candidate = { ...raw, path: resolved };
|
|
10596
|
-
const runId = candidate.runId ??
|
|
10835
|
+
const runId = candidate.runId ?? path53.basename(resolved);
|
|
10597
10836
|
const dirSkip = skipRunDirectoryRemoval({
|
|
10598
10837
|
harnessRoot,
|
|
10599
10838
|
runId,
|
|
@@ -10729,11 +10968,11 @@ function isPipelineCleanupEnabled() {
|
|
|
10729
10968
|
// src/installed-package-versions.ts
|
|
10730
10969
|
import { readFile } from "node:fs/promises";
|
|
10731
10970
|
import { homedir as homedir13 } from "node:os";
|
|
10732
|
-
import
|
|
10971
|
+
import path55 from "node:path";
|
|
10733
10972
|
|
|
10734
10973
|
// src/memory-cost-package-version-guard.ts
|
|
10735
10974
|
import { existsSync as existsSync40, readFileSync as readFileSync13 } from "node:fs";
|
|
10736
|
-
import
|
|
10975
|
+
import path54 from "node:path";
|
|
10737
10976
|
var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
|
|
10738
10977
|
"@kynver-app/runtime": "0.1.83",
|
|
10739
10978
|
"@kynver-app/openclaw-agent-os": "0.1.43",
|
|
@@ -10795,8 +11034,8 @@ function resolveRepoRoot(cwd, explicitRepoRoot) {
|
|
|
10795
11034
|
(value) => Boolean(value?.trim())
|
|
10796
11035
|
);
|
|
10797
11036
|
for (const candidate of candidates) {
|
|
10798
|
-
const resolved =
|
|
10799
|
-
if (existsSync40(
|
|
11037
|
+
const resolved = path54.resolve(candidate);
|
|
11038
|
+
if (existsSync40(path54.join(resolved, "packages/kynver-runtime/package.json")) && existsSync40(path54.join(resolved, "package.json"))) {
|
|
10800
11039
|
return resolved;
|
|
10801
11040
|
}
|
|
10802
11041
|
}
|
|
@@ -10809,7 +11048,7 @@ function probeRepoPackageVersions(input = {}) {
|
|
|
10809
11048
|
if (!repoRoot) return {};
|
|
10810
11049
|
const out = {};
|
|
10811
11050
|
for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
|
|
10812
|
-
const packageJsonPath =
|
|
11051
|
+
const packageJsonPath = path54.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
|
|
10813
11052
|
const version = readPackageJsonVersion(packageJsonPath);
|
|
10814
11053
|
if (!version) continue;
|
|
10815
11054
|
out[packageName] = { version, source: "repo", path: packageJsonPath };
|
|
@@ -10929,12 +11168,12 @@ function unique(values) {
|
|
|
10929
11168
|
}
|
|
10930
11169
|
function moduleRoots() {
|
|
10931
11170
|
const home = homedir13();
|
|
10932
|
-
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ??
|
|
10933
|
-
const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ?
|
|
11171
|
+
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path55.join(home, ".openclaw", "npm");
|
|
11172
|
+
const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path55.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path55.join(home, ".npm-global", "lib", "node_modules"));
|
|
10934
11173
|
return unique([
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot :
|
|
11174
|
+
path55.join(openClawPrefix, "lib", "node_modules"),
|
|
11175
|
+
path55.join(openClawPrefix, "node_modules"),
|
|
11176
|
+
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path55.join(npmGlobalRoot, "lib", "node_modules")
|
|
10938
11177
|
]);
|
|
10939
11178
|
}
|
|
10940
11179
|
async function readVersion(packageJsonPath) {
|
|
@@ -10950,7 +11189,7 @@ function installedPackageJsonCandidates(packageName) {
|
|
|
10950
11189
|
const seen = /* @__PURE__ */ new Set();
|
|
10951
11190
|
const out = [];
|
|
10952
11191
|
for (const root of roots) {
|
|
10953
|
-
const candidate =
|
|
11192
|
+
const candidate = path55.join(root, packageName, "package.json");
|
|
10954
11193
|
if (seen.has(candidate)) continue;
|
|
10955
11194
|
seen.add(candidate);
|
|
10956
11195
|
out.push(candidate);
|
|
@@ -10981,7 +11220,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
10981
11220
|
const outcomes = [];
|
|
10982
11221
|
for (const name of Object.keys(run.workers || {})) {
|
|
10983
11222
|
const worker = readJson(
|
|
10984
|
-
|
|
11223
|
+
path56.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
10985
11224
|
void 0
|
|
10986
11225
|
);
|
|
10987
11226
|
if (!worker?.taskId || worker.localOnly) continue;
|
|
@@ -11128,6 +11367,15 @@ async function runPipelineTick(args) {
|
|
|
11128
11367
|
var DEFAULT_INTERVAL_MS = 6e4;
|
|
11129
11368
|
var IDLE_INTERVAL_MS = 5 * 6e4;
|
|
11130
11369
|
var MAX_IDLE_STREAK = 10;
|
|
11370
|
+
var SLEEP_POLL_MS = 250;
|
|
11371
|
+
async function awaitDaemonBackoff(ms, isStopping) {
|
|
11372
|
+
let remaining = ms;
|
|
11373
|
+
while (remaining > 0 && !isStopping()) {
|
|
11374
|
+
const step = Math.min(SLEEP_POLL_MS, remaining);
|
|
11375
|
+
await sleepMsAsync(step);
|
|
11376
|
+
remaining -= step;
|
|
11377
|
+
}
|
|
11378
|
+
}
|
|
11131
11379
|
async function runDaemon(args) {
|
|
11132
11380
|
const runId = String(required(String(args.run || ""), "--run"));
|
|
11133
11381
|
const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
|
|
@@ -11185,17 +11433,17 @@ async function runDaemon(args) {
|
|
|
11185
11433
|
idleStreak = 0;
|
|
11186
11434
|
}
|
|
11187
11435
|
const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
|
|
11188
|
-
await
|
|
11436
|
+
await awaitDaemonBackoff(backoff, () => stopping);
|
|
11189
11437
|
} catch (error) {
|
|
11190
11438
|
console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
|
|
11191
|
-
await
|
|
11439
|
+
await awaitDaemonBackoff(intervalMs, () => stopping);
|
|
11192
11440
|
}
|
|
11193
11441
|
}
|
|
11194
11442
|
console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
|
|
11195
11443
|
}
|
|
11196
11444
|
|
|
11197
11445
|
// src/plan-progress.ts
|
|
11198
|
-
import
|
|
11446
|
+
import path60 from "node:path";
|
|
11199
11447
|
|
|
11200
11448
|
// src/bounded-build/constants.ts
|
|
11201
11449
|
var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
|
|
@@ -11338,16 +11586,16 @@ import {
|
|
|
11338
11586
|
unlinkSync as unlinkSync4,
|
|
11339
11587
|
writeFileSync as writeFileSync5
|
|
11340
11588
|
} from "node:fs";
|
|
11341
|
-
import
|
|
11589
|
+
import path58 from "node:path";
|
|
11342
11590
|
|
|
11343
11591
|
// src/heavy-verification/paths.ts
|
|
11344
11592
|
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
11345
|
-
import
|
|
11593
|
+
import path57 from "node:path";
|
|
11346
11594
|
function resolveHeavyVerificationRoot() {
|
|
11347
|
-
return
|
|
11595
|
+
return path57.join(resolveKynverStateRoot(), "heavy-verification");
|
|
11348
11596
|
}
|
|
11349
11597
|
function heavyVerificationSlotsDir() {
|
|
11350
|
-
return
|
|
11598
|
+
return path57.join(resolveHeavyVerificationRoot(), "slots");
|
|
11351
11599
|
}
|
|
11352
11600
|
function ensureHeavyVerificationDirs() {
|
|
11353
11601
|
const dir = heavyVerificationSlotsDir();
|
|
@@ -11376,7 +11624,7 @@ function indexedSlotId(index) {
|
|
|
11376
11624
|
return `slot-${index}`;
|
|
11377
11625
|
}
|
|
11378
11626
|
function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
|
|
11379
|
-
return
|
|
11627
|
+
return path58.join(slotsDir, `${slotId}.json`);
|
|
11380
11628
|
}
|
|
11381
11629
|
function readSlotRecord(filePath) {
|
|
11382
11630
|
if (!existsSync41(filePath)) return null;
|
|
@@ -11415,7 +11663,7 @@ function reclaimStaleHeavyVerificationSlots(opts = {}) {
|
|
|
11415
11663
|
let reclaimed = 0;
|
|
11416
11664
|
for (const name of readdirSync15(slotsDir)) {
|
|
11417
11665
|
if (!name.endsWith(".json")) continue;
|
|
11418
|
-
const filePath =
|
|
11666
|
+
const filePath = path58.join(slotsDir, name);
|
|
11419
11667
|
const before = existsSync41(filePath);
|
|
11420
11668
|
reclaimStaleSlot(filePath, staleMs);
|
|
11421
11669
|
if (before && !existsSync41(filePath)) reclaimed += 1;
|
|
@@ -11429,7 +11677,7 @@ function listActiveHeavyVerificationSlots(opts = {}) {
|
|
|
11429
11677
|
const active = [];
|
|
11430
11678
|
for (const name of readdirSync15(slotsDir)) {
|
|
11431
11679
|
if (!name.endsWith(".json")) continue;
|
|
11432
|
-
const record = readSlotRecord(
|
|
11680
|
+
const record = readSlotRecord(path58.join(slotsDir, name));
|
|
11433
11681
|
if (record && !slotIsStale(record, staleMs)) active.push(record);
|
|
11434
11682
|
}
|
|
11435
11683
|
return active;
|
|
@@ -11549,11 +11797,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
|
|
|
11549
11797
|
}
|
|
11550
11798
|
|
|
11551
11799
|
// src/harness-worktree-build-guard.ts
|
|
11552
|
-
import
|
|
11800
|
+
import path59 from "node:path";
|
|
11553
11801
|
function isPathUnderHarnessWorktree(cwd) {
|
|
11554
11802
|
const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
|
|
11555
|
-
const rel =
|
|
11556
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
11803
|
+
const rel = path59.relative(worktreesDir, path59.resolve(cwd));
|
|
11804
|
+
return rel.length > 0 && !rel.startsWith("..") && !path59.isAbsolute(rel);
|
|
11557
11805
|
}
|
|
11558
11806
|
function assessHarnessWorktreeBuildGuard(cwd) {
|
|
11559
11807
|
if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
|
|
@@ -11765,7 +12013,7 @@ async function emitPlanProgress(args) {
|
|
|
11765
12013
|
}
|
|
11766
12014
|
function verifyPlanLocal(args) {
|
|
11767
12015
|
const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
|
|
11768
|
-
const cwd =
|
|
12016
|
+
const cwd = path60.resolve(worktree);
|
|
11769
12017
|
const summary = runHarnessVerifyCommands(cwd);
|
|
11770
12018
|
const emitJson = args.json === true || args.json === "true";
|
|
11771
12019
|
const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
|
|
@@ -11814,9 +12062,9 @@ async function verifyPlan(args) {
|
|
|
11814
12062
|
}
|
|
11815
12063
|
|
|
11816
12064
|
// src/harness-verify-cli.ts
|
|
11817
|
-
import
|
|
12065
|
+
import path61 from "node:path";
|
|
11818
12066
|
function runHarnessVerifyCli(args) {
|
|
11819
|
-
const cwd =
|
|
12067
|
+
const cwd = path61.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
|
|
11820
12068
|
const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
|
|
11821
12069
|
const commands = [];
|
|
11822
12070
|
const rawCmd = args.command;
|
|
@@ -12024,7 +12272,7 @@ function formatMonitorTickNotice(tick) {
|
|
|
12024
12272
|
}
|
|
12025
12273
|
|
|
12026
12274
|
// src/monitor/monitor.service.ts
|
|
12027
|
-
import
|
|
12275
|
+
import path63 from "node:path";
|
|
12028
12276
|
|
|
12029
12277
|
// src/monitor/monitor.classify.ts
|
|
12030
12278
|
function classifyWorkerHealth(input) {
|
|
@@ -12077,10 +12325,10 @@ function classifyWorkerHealth(input) {
|
|
|
12077
12325
|
|
|
12078
12326
|
// src/monitor/monitor.store.ts
|
|
12079
12327
|
import { existsSync as existsSync42, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
|
|
12080
|
-
import
|
|
12328
|
+
import path62 from "node:path";
|
|
12081
12329
|
function monitorsDir() {
|
|
12082
12330
|
const { harnessRoot } = getHarnessPaths();
|
|
12083
|
-
const dir =
|
|
12331
|
+
const dir = path62.join(harnessRoot, "monitors");
|
|
12084
12332
|
mkdirSync9(dir, { recursive: true });
|
|
12085
12333
|
return dir;
|
|
12086
12334
|
}
|
|
@@ -12088,7 +12336,7 @@ function monitorIdFor(runId, workerName) {
|
|
|
12088
12336
|
return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
|
|
12089
12337
|
}
|
|
12090
12338
|
function monitorPath(monitorId) {
|
|
12091
|
-
return
|
|
12339
|
+
return path62.join(monitorsDir(), `${monitorId}.json`);
|
|
12092
12340
|
}
|
|
12093
12341
|
function loadMonitorSession(monitorId) {
|
|
12094
12342
|
return readJson(monitorPath(monitorId), void 0);
|
|
@@ -12109,7 +12357,7 @@ function listMonitorSessions() {
|
|
|
12109
12357
|
for (const name of readdirSync16(dir)) {
|
|
12110
12358
|
if (!name.endsWith(".json")) continue;
|
|
12111
12359
|
const session = readJson(
|
|
12112
|
-
|
|
12360
|
+
path62.join(dir, name),
|
|
12113
12361
|
void 0
|
|
12114
12362
|
);
|
|
12115
12363
|
if (!session?.monitorId) continue;
|
|
@@ -12200,7 +12448,7 @@ async function fetchTaskLeasesForWorkers(input) {
|
|
|
12200
12448
|
// src/monitor/monitor.service.ts
|
|
12201
12449
|
function workerRecord2(runId, name) {
|
|
12202
12450
|
return readJson(
|
|
12203
|
-
|
|
12451
|
+
path63.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
|
|
12204
12452
|
void 0
|
|
12205
12453
|
);
|
|
12206
12454
|
}
|
|
@@ -12407,17 +12655,17 @@ async function runMonitorLoop(args) {
|
|
|
12407
12655
|
// src/monitor/monitor-spawn.ts
|
|
12408
12656
|
import { spawn as spawn6 } from "node:child_process";
|
|
12409
12657
|
import { closeSync as closeSync8, existsSync as existsSync43, openSync as openSync8 } from "node:fs";
|
|
12410
|
-
import
|
|
12658
|
+
import path64 from "node:path";
|
|
12411
12659
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12412
12660
|
function resolveDefaultCliPath2() {
|
|
12413
|
-
return
|
|
12661
|
+
return path64.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
|
|
12414
12662
|
}
|
|
12415
12663
|
function spawnMonitorSidecar(opts) {
|
|
12416
12664
|
const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
|
|
12417
12665
|
if (!existsSync43(cliPath)) return void 0;
|
|
12418
12666
|
const monitorId = monitorIdFor(opts.runId, opts.workerName);
|
|
12419
12667
|
const { harnessRoot } = getHarnessPaths();
|
|
12420
|
-
const logPath =
|
|
12668
|
+
const logPath = path64.join(harnessRoot, "monitors", `${monitorId}.log`);
|
|
12421
12669
|
let logFd;
|
|
12422
12670
|
try {
|
|
12423
12671
|
logFd = openSync8(logPath, "a");
|
|
@@ -12618,8 +12866,32 @@ function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
|
|
|
12618
12866
|
return false;
|
|
12619
12867
|
}
|
|
12620
12868
|
|
|
12869
|
+
// src/run-resolve.ts
|
|
12870
|
+
function resolveHarnessRunByName(runName) {
|
|
12871
|
+
const name = runName.trim();
|
|
12872
|
+
if (!name) return null;
|
|
12873
|
+
const rows = buildRunListRows();
|
|
12874
|
+
return rows.find((row) => row.name === name && row.status !== "completed") ?? null;
|
|
12875
|
+
}
|
|
12876
|
+
function resolveHarnessRunCli(args) {
|
|
12877
|
+
const name = String(required(String(args.name || ""), "--name"));
|
|
12878
|
+
const hit = resolveHarnessRunByName(name);
|
|
12879
|
+
console.log(
|
|
12880
|
+
JSON.stringify(
|
|
12881
|
+
{
|
|
12882
|
+
runId: hit?.id ?? null,
|
|
12883
|
+
name,
|
|
12884
|
+
status: hit?.status ?? null,
|
|
12885
|
+
effectiveStatus: hit?.effectiveStatus ?? null
|
|
12886
|
+
},
|
|
12887
|
+
null,
|
|
12888
|
+
2
|
|
12889
|
+
)
|
|
12890
|
+
);
|
|
12891
|
+
}
|
|
12892
|
+
|
|
12621
12893
|
// src/post-restart-unblock.ts
|
|
12622
|
-
import
|
|
12894
|
+
import path65 from "node:path";
|
|
12623
12895
|
function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
|
|
12624
12896
|
return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
|
|
12625
12897
|
}
|
|
@@ -12632,7 +12904,7 @@ async function postRestartUnblock(args) {
|
|
|
12632
12904
|
const errors = [];
|
|
12633
12905
|
for (const run of listRunRecords()) {
|
|
12634
12906
|
for (const name of Object.keys(run.workers ?? {})) {
|
|
12635
|
-
const workerPath =
|
|
12907
|
+
const workerPath = path65.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
12636
12908
|
const worker = readJson(workerPath, void 0);
|
|
12637
12909
|
if (!worker) {
|
|
12638
12910
|
skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
|
|
@@ -12744,9 +13016,9 @@ async function postRestartUnblockCli(args) {
|
|
|
12744
13016
|
}
|
|
12745
13017
|
|
|
12746
13018
|
// src/default-repo-cli.ts
|
|
12747
|
-
import
|
|
13019
|
+
import path66 from "node:path";
|
|
12748
13020
|
import { homedir as homedir14 } from "node:os";
|
|
12749
|
-
var CONFIG_FILE2 =
|
|
13021
|
+
var CONFIG_FILE2 = path66.join(homedir14(), ".kynver", "config.json");
|
|
12750
13022
|
function ensureDefaultRepo(opts) {
|
|
12751
13023
|
const existing = loadUserConfig();
|
|
12752
13024
|
const resolved = resolveDefaultRepo({ ...opts, config: existing });
|
|
@@ -12827,12 +13099,12 @@ function summarizeResolvedDefaultRepo(resolved) {
|
|
|
12827
13099
|
}
|
|
12828
13100
|
|
|
12829
13101
|
// src/doctor/runtime-takeover.ts
|
|
12830
|
-
import
|
|
13102
|
+
import path68 from "node:path";
|
|
12831
13103
|
|
|
12832
13104
|
// src/doctor/runtime-takeover.probes.ts
|
|
12833
13105
|
import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync17 } from "node:fs";
|
|
12834
13106
|
import { homedir as homedir15 } from "node:os";
|
|
12835
|
-
import
|
|
13107
|
+
import path67 from "node:path";
|
|
12836
13108
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12837
13109
|
function captureCommand(bin, args) {
|
|
12838
13110
|
try {
|
|
@@ -12874,10 +13146,10 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
12874
13146
|
commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
|
|
12875
13147
|
kynverVersion: (bin) => captureCommand(bin, ["--version"]),
|
|
12876
13148
|
loadConfig: () => loadUserConfig(),
|
|
12877
|
-
configFilePath: () =>
|
|
12878
|
-
credentialsFilePath: () =>
|
|
13149
|
+
configFilePath: () => path67.join(homedir15(), ".kynver", "config.json"),
|
|
13150
|
+
credentialsFilePath: () => path67.join(homedir15(), ".kynver", "credentials"),
|
|
12879
13151
|
readCredentials: () => {
|
|
12880
|
-
const credPath =
|
|
13152
|
+
const credPath = path67.join(homedir15(), ".kynver", "credentials");
|
|
12881
13153
|
if (!existsSync45(credPath)) {
|
|
12882
13154
|
return { hasApiKey: false };
|
|
12883
13155
|
}
|
|
@@ -12912,7 +13184,7 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
12912
13184
|
})()
|
|
12913
13185
|
}),
|
|
12914
13186
|
harnessRoot: () => resolveHarnessRoot(),
|
|
12915
|
-
legacyOpenclawHarnessRoot: () =>
|
|
13187
|
+
legacyOpenclawHarnessRoot: () => path67.join(homedir15(), ".openclaw", "harness"),
|
|
12916
13188
|
pathExists: (target) => existsSync45(target),
|
|
12917
13189
|
pathWritable: (target) => isWritable(target)
|
|
12918
13190
|
};
|
|
@@ -13319,8 +13591,8 @@ function assessVercelDeployEvidence(probes) {
|
|
|
13319
13591
|
}
|
|
13320
13592
|
function assessHarnessDirs(probes) {
|
|
13321
13593
|
const harnessRoot = probes.harnessRoot();
|
|
13322
|
-
const runsDir =
|
|
13323
|
-
const worktreesDir =
|
|
13594
|
+
const runsDir = path68.join(harnessRoot, "runs");
|
|
13595
|
+
const worktreesDir = path68.join(harnessRoot, "worktrees");
|
|
13324
13596
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
13325
13597
|
const displayRunsDir = redactHomePath(runsDir);
|
|
13326
13598
|
const displayWorktreesDir = redactHomePath(worktreesDir);
|
|
@@ -13584,9 +13856,9 @@ function applySchedulerCutoverAttestation(config) {
|
|
|
13584
13856
|
}
|
|
13585
13857
|
|
|
13586
13858
|
// src/scheduler-cutover-cli.ts
|
|
13587
|
-
import
|
|
13859
|
+
import path69 from "node:path";
|
|
13588
13860
|
import { homedir as homedir16 } from "node:os";
|
|
13589
|
-
var CONFIG_FILE3 =
|
|
13861
|
+
var CONFIG_FILE3 = path69.join(homedir16(), ".kynver", "config.json");
|
|
13590
13862
|
function runSchedulerCutoverCheckCli(json = false) {
|
|
13591
13863
|
const config = loadUserConfig();
|
|
13592
13864
|
const report = assessSchedulerCutover(config);
|
|
@@ -13723,6 +13995,157 @@ async function runCronTickCli(args) {
|
|
|
13723
13995
|
);
|
|
13724
13996
|
}
|
|
13725
13997
|
|
|
13998
|
+
// src/lane/landing-maintainer-tick.ts
|
|
13999
|
+
import os9 from "node:os";
|
|
14000
|
+
|
|
14001
|
+
// src/lane/lane-spec.ts
|
|
14002
|
+
var LANDING_MAINTAINER_LANE_SPEC = {
|
|
14003
|
+
slug: "landing-maintainer",
|
|
14004
|
+
originCron: "maintain-8-blocker-and-pr-landing-workers",
|
|
14005
|
+
defaultRepo: "Totalsolutionsync/Kynver",
|
|
14006
|
+
landScript: "scripts/agent-os-land-pr.mjs",
|
|
14007
|
+
landScriptArgs: ["--skip-not-ready"]
|
|
14008
|
+
};
|
|
14009
|
+
|
|
14010
|
+
// src/lane/landing-maintainer-local.ts
|
|
14011
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
14012
|
+
import path70 from "node:path";
|
|
14013
|
+
function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
14014
|
+
const script = path70.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
|
|
14015
|
+
const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
|
|
14016
|
+
if (!execute) {
|
|
14017
|
+
return {
|
|
14018
|
+
action: { kind: "land_pr", prNumber, reason: "dry-run" },
|
|
14019
|
+
executed: false,
|
|
14020
|
+
exitCode: 0,
|
|
14021
|
+
stdout: `dry-run: node ${args.join(" ")}`,
|
|
14022
|
+
stderr: ""
|
|
14023
|
+
};
|
|
14024
|
+
}
|
|
14025
|
+
const result = spawnSync10("node", args, {
|
|
14026
|
+
cwd: repoRoot,
|
|
14027
|
+
encoding: "utf8",
|
|
14028
|
+
timeout: 10 * 60 * 1e3
|
|
14029
|
+
});
|
|
14030
|
+
return {
|
|
14031
|
+
action: { kind: "land_pr", prNumber, reason: "landing wrapper invoked" },
|
|
14032
|
+
executed: true,
|
|
14033
|
+
exitCode: result.status,
|
|
14034
|
+
stdout: result.stdout ?? "",
|
|
14035
|
+
stderr: result.stderr ?? ""
|
|
14036
|
+
};
|
|
14037
|
+
}
|
|
14038
|
+
function resolveLandingMaintainerRepoRoot(args) {
|
|
14039
|
+
const explicit = args.repoPath ? String(args.repoPath).trim() : "";
|
|
14040
|
+
if (explicit) return path70.resolve(explicit);
|
|
14041
|
+
const resolved = resolveDefaultRepo();
|
|
14042
|
+
return resolved?.repo ?? process.cwd();
|
|
14043
|
+
}
|
|
14044
|
+
|
|
14045
|
+
// src/lane/landing-maintainer-tick.ts
|
|
14046
|
+
async function runLandingMaintainerLaneTick(args) {
|
|
14047
|
+
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
14048
|
+
const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
|
|
14049
|
+
const fleet = args.fleet === true || args.fleet === "true";
|
|
14050
|
+
const execute = args.execute === true || args.execute === "true";
|
|
14051
|
+
const runId = args.run ? String(args.run) : void 0;
|
|
14052
|
+
const resourceGate = observeRunnerResourceGate({
|
|
14053
|
+
runId: runId ?? "fleet-lane-tick"
|
|
14054
|
+
});
|
|
14055
|
+
const boxCapacity = {
|
|
14056
|
+
...buildBoxResourceSnapshotFromGate(resourceGate, {
|
|
14057
|
+
harnessRunId: runId,
|
|
14058
|
+
boxKind: resolveBoxKindFromConfig(loadUserConfig()),
|
|
14059
|
+
hostLabel: os9.hostname()
|
|
14060
|
+
}),
|
|
14061
|
+
providerHealthy: resourceGate.ok,
|
|
14062
|
+
authorizedForRepair: resourceGate.ok,
|
|
14063
|
+
authorizedForLanding: resourceGate.ok,
|
|
14064
|
+
systemHealthBlockers: resourceGate.ok ? [] : [resourceGate.reason ?? "resource_gate_blocked"],
|
|
14065
|
+
actionableWorkers: resourceGate.activeWorkers
|
|
14066
|
+
};
|
|
14067
|
+
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
14068
|
+
const secret = await resolveCallbackSecretWithMint(
|
|
14069
|
+
args.secret ? String(args.secret) : void 0,
|
|
14070
|
+
agentOsId,
|
|
14071
|
+
{ baseUrl: base }
|
|
14072
|
+
);
|
|
14073
|
+
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/tick`;
|
|
14074
|
+
const res = await postJson(url, secret, {
|
|
14075
|
+
repo: repoSlug,
|
|
14076
|
+
fleet,
|
|
14077
|
+
execute,
|
|
14078
|
+
runId,
|
|
14079
|
+
boxCapacity
|
|
14080
|
+
});
|
|
14081
|
+
const coordinator = res.response;
|
|
14082
|
+
const localOutcomes = [];
|
|
14083
|
+
const repoRoot = resolveLandingMaintainerRepoRoot(args);
|
|
14084
|
+
const actions = Array.isArray(coordinator?.localActions) ? coordinator.localActions : [];
|
|
14085
|
+
const holderBoxId = boxCapacity.boxId;
|
|
14086
|
+
for (const action of actions) {
|
|
14087
|
+
if (action.kind === "land_pr" && typeof action.prNumber === "number") {
|
|
14088
|
+
const outcome = runLandingWrapper(action.prNumber, repoRoot, execute);
|
|
14089
|
+
localOutcomes.push({
|
|
14090
|
+
action: outcome.action,
|
|
14091
|
+
executed: outcome.executed,
|
|
14092
|
+
exitCode: outcome.exitCode
|
|
14093
|
+
});
|
|
14094
|
+
if (execute && outcome.executed) {
|
|
14095
|
+
const merged = outcome.exitCode === 0;
|
|
14096
|
+
const prUrl = typeof action.prUrl === "string" && action.prUrl.trim() ? action.prUrl.trim() : `https://github.com/${repoSlug}/pull/${action.prNumber}`;
|
|
14097
|
+
try {
|
|
14098
|
+
await postJson(
|
|
14099
|
+
`${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/outcome`,
|
|
14100
|
+
secret,
|
|
14101
|
+
{ repo: repoSlug, prUrl, holderBoxId, merged }
|
|
14102
|
+
);
|
|
14103
|
+
} catch {
|
|
14104
|
+
}
|
|
14105
|
+
}
|
|
14106
|
+
continue;
|
|
14107
|
+
}
|
|
14108
|
+
localOutcomes.push({
|
|
14109
|
+
action,
|
|
14110
|
+
executed: false,
|
|
14111
|
+
exitCode: null
|
|
14112
|
+
});
|
|
14113
|
+
}
|
|
14114
|
+
return {
|
|
14115
|
+
repo: repoSlug,
|
|
14116
|
+
fleet,
|
|
14117
|
+
execute,
|
|
14118
|
+
coordinator,
|
|
14119
|
+
localOutcomes
|
|
14120
|
+
};
|
|
14121
|
+
}
|
|
14122
|
+
|
|
14123
|
+
// src/lane/lane-tick-cli.ts
|
|
14124
|
+
async function runLaneTickCli(args, laneFromArgv) {
|
|
14125
|
+
const lane = String(laneFromArgv ?? args.lane ?? "").trim();
|
|
14126
|
+
if (lane !== "landing-maintainer") {
|
|
14127
|
+
console.error(`unknown lane: ${lane || "(none)"}`);
|
|
14128
|
+
process.exit(1);
|
|
14129
|
+
}
|
|
14130
|
+
const result = await runLandingMaintainerLaneTick(args);
|
|
14131
|
+
if (args.json === true || args.json === "true") {
|
|
14132
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14133
|
+
return;
|
|
14134
|
+
}
|
|
14135
|
+
console.log(
|
|
14136
|
+
[
|
|
14137
|
+
`fleet landing-maintainer tick`,
|
|
14138
|
+
`repo=${result.repo}`,
|
|
14139
|
+
`fleet=${result.fleet}`,
|
|
14140
|
+
`execute=${result.execute}`,
|
|
14141
|
+
`localOutcomes=${result.localOutcomes.length}`
|
|
14142
|
+
].join(" ")
|
|
14143
|
+
);
|
|
14144
|
+
for (const row of result.localOutcomes) {
|
|
14145
|
+
console.log(` ${row.action.kind} pr=${row.action.prNumber ?? "-"} executed=${row.executed}`);
|
|
14146
|
+
}
|
|
14147
|
+
}
|
|
14148
|
+
|
|
13726
14149
|
// src/cli.ts
|
|
13727
14150
|
function isHelpFlag(arg) {
|
|
13728
14151
|
return arg === "help" || arg === "--help" || arg === "-h";
|
|
@@ -13743,6 +14166,7 @@ function usage(code = 0) {
|
|
|
13743
14166
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
|
|
13744
14167
|
" kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
|
|
13745
14168
|
" kynver run list",
|
|
14169
|
+
" kynver run resolve --name RUN_NAME",
|
|
13746
14170
|
" kynver run status --run RUN_ID [--json] [--compact]",
|
|
13747
14171
|
" 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]",
|
|
13748
14172
|
" kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
|
|
@@ -13778,6 +14202,7 @@ function usage(code = 0) {
|
|
|
13778
14202
|
" kynver scheduler attest-cutover [--json]",
|
|
13779
14203
|
" kynver cron status [--json]",
|
|
13780
14204
|
" kynver cron tick [--agent-os-id AOS_ID] [--json]",
|
|
14205
|
+
" kynver lane tick landing-maintainer [--fleet] [--repo OWNER/NAME] [--agent-os-id AOS_ID] [--execute] [--json]",
|
|
13781
14206
|
" kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
|
|
13782
14207
|
].join("\n")
|
|
13783
14208
|
);
|
|
@@ -13789,7 +14214,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
13789
14214
|
const scope = argv.shift();
|
|
13790
14215
|
let action;
|
|
13791
14216
|
let rest;
|
|
13792
|
-
if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "board") {
|
|
14217
|
+
if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "lane" || scope === "board") {
|
|
13793
14218
|
action = argv.shift();
|
|
13794
14219
|
rest = argv;
|
|
13795
14220
|
} else {
|
|
@@ -13846,11 +14271,16 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
13846
14271
|
if (scope === "cron" && action === "tick") {
|
|
13847
14272
|
return void await runCronTickCli(args);
|
|
13848
14273
|
}
|
|
14274
|
+
if (scope === "lane" && action === "tick") {
|
|
14275
|
+
const laneName = rest.shift();
|
|
14276
|
+
return void await runLaneTickCli(parseArgs(rest), laneName);
|
|
14277
|
+
}
|
|
13849
14278
|
if (scope === "board" && action === "contract") {
|
|
13850
14279
|
return void await runCommandCenterContractCli(args);
|
|
13851
14280
|
}
|
|
13852
14281
|
if (scope === "run" && action === "create") return createRun(args);
|
|
13853
14282
|
if (scope === "run" && action === "list") return listRuns();
|
|
14283
|
+
if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
|
|
13854
14284
|
if (scope === "run" && action === "status") return runStatus(args);
|
|
13855
14285
|
if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
|
|
13856
14286
|
if (scope === "run" && action === "sweep") return void await sweepRun(args);
|