@kynver-app/runtime 0.1.95 → 0.1.102
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/bounded-build/admission.d.ts +2 -2
- package/dist/bounded-build/exec.d.ts +6 -2
- package/dist/cli.js +970 -262
- package/dist/cli.js.map +4 -4
- package/dist/harness-verify.d.ts +3 -2
- package/dist/harness-worker-active.d.ts +12 -0
- package/dist/heavy-verification/gate.d.ts +10 -0
- package/dist/heavy-verification/index.d.ts +3 -0
- package/dist/heavy-verification/paths.d.ts +3 -0
- package/dist/heavy-verification/slot.d.ts +49 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +970 -248
- 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 -0
- 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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { mkdirSync as
|
|
4
|
+
import { mkdirSync as mkdirSync10, realpathSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
6
6
|
|
|
7
7
|
// src/config.ts
|
|
8
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as
|
|
8
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "node:fs";
|
|
9
9
|
import { homedir as homedir4, totalmem } from "node:os";
|
|
10
10
|
import path8 from "node:path";
|
|
11
11
|
|
|
@@ -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 {
|
|
@@ -485,23 +488,23 @@ function isWslHost() {
|
|
|
485
488
|
function observeWslHostDisk(options = {}) {
|
|
486
489
|
const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
|
|
487
490
|
if (!wsl) return null;
|
|
488
|
-
const
|
|
491
|
+
const path71 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
|
|
489
492
|
const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
|
|
490
493
|
const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
|
|
491
494
|
const statfs = options.statfs ?? statfsSync;
|
|
492
495
|
let stats;
|
|
493
496
|
try {
|
|
494
|
-
stats = statfs(
|
|
497
|
+
stats = statfs(path71);
|
|
495
498
|
} catch (error) {
|
|
496
499
|
return {
|
|
497
500
|
ok: false,
|
|
498
|
-
path:
|
|
501
|
+
path: path71,
|
|
499
502
|
freeBytes: 0,
|
|
500
503
|
totalBytes: 0,
|
|
501
504
|
usedPercent: 100,
|
|
502
505
|
warnBelowBytes,
|
|
503
506
|
criticalBelowBytes,
|
|
504
|
-
reason: `Windows host disk probe failed at ${
|
|
507
|
+
reason: `Windows host disk probe failed at ${path71}: ${error.message}`,
|
|
505
508
|
probeError: error.message
|
|
506
509
|
};
|
|
507
510
|
}
|
|
@@ -515,11 +518,11 @@ function observeWslHostDisk(options = {}) {
|
|
|
515
518
|
let reason = null;
|
|
516
519
|
if (!ok) {
|
|
517
520
|
const tag = criticalFree ? "critical" : "warning";
|
|
518
|
-
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()}`;
|
|
519
522
|
}
|
|
520
523
|
return {
|
|
521
524
|
ok,
|
|
522
|
-
path:
|
|
525
|
+
path: path71,
|
|
523
526
|
freeBytes,
|
|
524
527
|
totalBytes,
|
|
525
528
|
usedPercent,
|
|
@@ -539,12 +542,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
539
542
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
540
543
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
541
544
|
function observeRunnerDiskGate(input = {}) {
|
|
542
|
-
const
|
|
545
|
+
const path71 = input.diskPath?.trim() || "/";
|
|
543
546
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
544
547
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
545
548
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
546
549
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
547
|
-
const stats = statfsSync2(
|
|
550
|
+
const stats = statfsSync2(path71);
|
|
548
551
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
549
552
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
550
553
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -567,7 +570,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
567
570
|
}
|
|
568
571
|
return {
|
|
569
572
|
ok,
|
|
570
|
-
path:
|
|
573
|
+
path: path71,
|
|
571
574
|
freeBytes,
|
|
572
575
|
totalBytes,
|
|
573
576
|
usedPercent,
|
|
@@ -701,6 +704,9 @@ function listRunWorkerNames(run) {
|
|
|
701
704
|
return [...names];
|
|
702
705
|
}
|
|
703
706
|
|
|
707
|
+
// src/harness-worker-active.ts
|
|
708
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
709
|
+
|
|
704
710
|
// src/heartbeat.ts
|
|
705
711
|
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
706
712
|
|
|
@@ -1781,6 +1787,9 @@ function computeAttention(input) {
|
|
|
1781
1787
|
return { state: "blocked", reason: input.completionBlocker };
|
|
1782
1788
|
}
|
|
1783
1789
|
if (input.finalResult) {
|
|
1790
|
+
if (input.localOnly && hasMergedTargetPrReconciliation(input.finalResult)) {
|
|
1791
|
+
return { state: "done", reason: "local-only worker superseded by merged PR" };
|
|
1792
|
+
}
|
|
1784
1793
|
const landingSnapshot = {
|
|
1785
1794
|
finalResult: input.finalResult,
|
|
1786
1795
|
changedFiles: input.changedFiles ?? [],
|
|
@@ -1846,9 +1855,24 @@ function computeAttention(input) {
|
|
|
1846
1855
|
}
|
|
1847
1856
|
return { state: "ok", reason: "recent activity" };
|
|
1848
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
|
+
}
|
|
1849
1870
|
function resolveFinalResult(worker, parsedFinalResult, heartbeat) {
|
|
1850
|
-
if (parsedFinalResult) return parsedFinalResult;
|
|
1851
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;
|
|
1852
1876
|
if (ackSnapshot !== void 0 && ackSnapshot !== null) return ackSnapshot;
|
|
1853
1877
|
return terminalFinalResultFromHeartbeat(heartbeat);
|
|
1854
1878
|
}
|
|
@@ -1895,7 +1919,8 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
1895
1919
|
gitAncestry,
|
|
1896
1920
|
completionBlocker,
|
|
1897
1921
|
landingContract,
|
|
1898
|
-
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null
|
|
1922
|
+
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
|
|
1923
|
+
localOnly: worker.localOnly === true
|
|
1899
1924
|
});
|
|
1900
1925
|
const workerStatusLabel = completionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
|
|
1901
1926
|
return {
|
|
@@ -1949,6 +1974,33 @@ function deriveRunStatus(fallback, workers) {
|
|
|
1949
1974
|
return fallback;
|
|
1950
1975
|
}
|
|
1951
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
|
+
|
|
1952
2004
|
// src/resource-gate.ts
|
|
1953
2005
|
var DEFAULT_PER_WORKER_MEM_BYTES = 500 * 1024 * 1024;
|
|
1954
2006
|
var DEFAULT_MEM_RESERVE_BYTES = 4 * 1024 * 1024 * 1024;
|
|
@@ -1991,13 +2043,6 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
|
|
|
1991
2043
|
function readAvailableMemBytes() {
|
|
1992
2044
|
return readMemAvailableBytes();
|
|
1993
2045
|
}
|
|
1994
|
-
function isActiveHarnessWorker(worker) {
|
|
1995
|
-
if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
|
|
1996
|
-
return false;
|
|
1997
|
-
}
|
|
1998
|
-
const status = computeWorkerStatus(worker);
|
|
1999
|
-
return status.alive && !status.finalResult && status.attention.state !== "done";
|
|
2000
|
-
}
|
|
2001
2046
|
function countActiveWorkersForRun(run) {
|
|
2002
2047
|
let active = 0;
|
|
2003
2048
|
for (const name of listRunWorkerNames(run)) {
|
|
@@ -2146,7 +2191,7 @@ var CREDENTIALS_FILE = path8.join(CONFIG_DIR, "credentials");
|
|
|
2146
2191
|
function loadUserConfig() {
|
|
2147
2192
|
if (!existsSync9(CONFIG_FILE)) return {};
|
|
2148
2193
|
try {
|
|
2149
|
-
return JSON.parse(
|
|
2194
|
+
return JSON.parse(readFileSync8(CONFIG_FILE, "utf8"));
|
|
2150
2195
|
} catch {
|
|
2151
2196
|
return {};
|
|
2152
2197
|
}
|
|
@@ -2223,7 +2268,7 @@ function resolveSetupWorkerConfig(existing, args, totalMemBytes = totalmem()) {
|
|
|
2223
2268
|
function loadCredentialsFile() {
|
|
2224
2269
|
if (!existsSync9(CREDENTIALS_FILE)) return {};
|
|
2225
2270
|
try {
|
|
2226
|
-
return JSON.parse(
|
|
2271
|
+
return JSON.parse(readFileSync8(CREDENTIALS_FILE, "utf8"));
|
|
2227
2272
|
} catch {
|
|
2228
2273
|
return {};
|
|
2229
2274
|
}
|
|
@@ -2493,15 +2538,30 @@ async function withTimeout(fn) {
|
|
|
2493
2538
|
clearTimeout(timeout);
|
|
2494
2539
|
}
|
|
2495
2540
|
}
|
|
2541
|
+
function callbackFetchError(error) {
|
|
2542
|
+
return {
|
|
2543
|
+
ok: false,
|
|
2544
|
+
status: 0,
|
|
2545
|
+
response: {
|
|
2546
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2547
|
+
timeoutMs: callbackTimeoutMs()
|
|
2548
|
+
}
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2496
2551
|
async function postJson(url, secret, body) {
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2552
|
+
let res;
|
|
2553
|
+
try {
|
|
2554
|
+
res = await withTimeout(
|
|
2555
|
+
(signal) => fetch(url, {
|
|
2556
|
+
method: "POST",
|
|
2557
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
2558
|
+
body: JSON.stringify(body),
|
|
2559
|
+
signal
|
|
2560
|
+
})
|
|
2561
|
+
);
|
|
2562
|
+
} catch (error) {
|
|
2563
|
+
return callbackFetchError(error);
|
|
2564
|
+
}
|
|
2505
2565
|
let response = null;
|
|
2506
2566
|
try {
|
|
2507
2567
|
response = await res.json();
|
|
@@ -2519,13 +2579,18 @@ async function postJsonWithCredentialRefresh(url, secret, body, opts) {
|
|
|
2519
2579
|
return { ...retry, refreshedAuth: true };
|
|
2520
2580
|
}
|
|
2521
2581
|
async function getJson(url, secret) {
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2582
|
+
let res;
|
|
2583
|
+
try {
|
|
2584
|
+
res = await withTimeout(
|
|
2585
|
+
(signal) => fetch(url, {
|
|
2586
|
+
method: "GET",
|
|
2587
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
2588
|
+
signal
|
|
2589
|
+
})
|
|
2590
|
+
);
|
|
2591
|
+
} catch (error) {
|
|
2592
|
+
return callbackFetchError(error);
|
|
2593
|
+
}
|
|
2529
2594
|
let response = null;
|
|
2530
2595
|
try {
|
|
2531
2596
|
response = await res.json();
|
|
@@ -2535,41 +2600,6 @@ async function getJson(url, secret) {
|
|
|
2535
2600
|
return { ok: res.ok, status: res.status, response };
|
|
2536
2601
|
}
|
|
2537
2602
|
|
|
2538
|
-
// src/dispatch-lane-normalization.ts
|
|
2539
|
-
function trimLower(value) {
|
|
2540
|
-
return (value ?? "").trim().toLowerCase();
|
|
2541
|
-
}
|
|
2542
|
-
function roleLaneToDispatchLane(roleLane) {
|
|
2543
|
-
switch (trimLower(roleLane)) {
|
|
2544
|
-
case "implementer":
|
|
2545
|
-
case "repair_implementer":
|
|
2546
|
-
case "plan_author":
|
|
2547
|
-
case "runtime_verifier":
|
|
2548
|
-
return "implementation";
|
|
2549
|
-
case "plan_reviewer":
|
|
2550
|
-
case "report_reviewer":
|
|
2551
|
-
case "deep_reviewer":
|
|
2552
|
-
return "review";
|
|
2553
|
-
default:
|
|
2554
|
-
return null;
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
function normalizeDispatchNextLaneFilter(raw) {
|
|
2558
|
-
const key = trimLower(raw);
|
|
2559
|
-
if (!key) return void 0;
|
|
2560
|
-
if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
|
|
2561
|
-
return key;
|
|
2562
|
-
}
|
|
2563
|
-
const mapped = roleLaneToDispatchLane(key);
|
|
2564
|
-
if (mapped) return mapped;
|
|
2565
|
-
if (key === "implement" || key === "repair" || key === "coding") return "implementation";
|
|
2566
|
-
if (key === "land" || key === "merge") return "landing";
|
|
2567
|
-
return void 0;
|
|
2568
|
-
}
|
|
2569
|
-
function resolveDispatchNextLaneFilter(raw) {
|
|
2570
|
-
return normalizeDispatchNextLaneFilter(raw) ?? "any";
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
2603
|
// src/worker-persona-catalog.ts
|
|
2574
2604
|
var WORKER_PERSONA_CATALOG = [
|
|
2575
2605
|
{
|
|
@@ -2672,6 +2702,41 @@ function workerPersonaReviewSlugs() {
|
|
|
2672
2702
|
);
|
|
2673
2703
|
}
|
|
2674
2704
|
|
|
2705
|
+
// src/dispatch-lane-normalization.ts
|
|
2706
|
+
function trimLower(value) {
|
|
2707
|
+
return (value ?? "").trim().toLowerCase();
|
|
2708
|
+
}
|
|
2709
|
+
function roleLaneToDispatchLane(roleLane) {
|
|
2710
|
+
switch (trimLower(roleLane)) {
|
|
2711
|
+
case "implementer":
|
|
2712
|
+
case "repair_implementer":
|
|
2713
|
+
case "plan_author":
|
|
2714
|
+
case "runtime_verifier":
|
|
2715
|
+
return "implementation";
|
|
2716
|
+
case "plan_reviewer":
|
|
2717
|
+
case "report_reviewer":
|
|
2718
|
+
case "deep_reviewer":
|
|
2719
|
+
return "review";
|
|
2720
|
+
default:
|
|
2721
|
+
return null;
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
function normalizeDispatchNextLaneFilter(raw) {
|
|
2725
|
+
const key = trimLower(raw);
|
|
2726
|
+
if (!key) return void 0;
|
|
2727
|
+
if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
|
|
2728
|
+
return key;
|
|
2729
|
+
}
|
|
2730
|
+
const mapped = roleLaneToDispatchLane(key);
|
|
2731
|
+
if (mapped) return mapped;
|
|
2732
|
+
if (key === "implement" || key === "repair" || key === "coding") return "implementation";
|
|
2733
|
+
if (key === "land" || key === "merge") return "landing";
|
|
2734
|
+
return void 0;
|
|
2735
|
+
}
|
|
2736
|
+
function resolveDispatchNextLaneFilter(raw) {
|
|
2737
|
+
return normalizeDispatchNextLaneFilter(raw) ?? "any";
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2675
2740
|
// src/model-routing-task-enrich.ts
|
|
2676
2741
|
function taskString(task, key) {
|
|
2677
2742
|
const v = task[key];
|
|
@@ -3129,7 +3194,7 @@ function probeCursorOAuthBinding(nowIso = (/* @__PURE__ */ new Date()).toISOStri
|
|
|
3129
3194
|
}
|
|
3130
3195
|
|
|
3131
3196
|
// src/orchestration-providers/hermes-cli-adapter.ts
|
|
3132
|
-
import { existsSync as existsSync14, readFileSync as
|
|
3197
|
+
import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
|
|
3133
3198
|
import { homedir as homedir9 } from "node:os";
|
|
3134
3199
|
import path13 from "node:path";
|
|
3135
3200
|
var PROFILE_ENV_KEYS = [
|
|
@@ -3145,7 +3210,7 @@ function hermesProfileEnvPath() {
|
|
|
3145
3210
|
}
|
|
3146
3211
|
function profileEnvKeyPresence(envPath) {
|
|
3147
3212
|
try {
|
|
3148
|
-
const text =
|
|
3213
|
+
const text = readFileSync9(envPath, "utf8");
|
|
3149
3214
|
const present = [];
|
|
3150
3215
|
for (const key of PROFILE_ENV_KEYS) {
|
|
3151
3216
|
const re = new RegExp(`^${key}=`, "m");
|
|
@@ -4106,7 +4171,7 @@ function buildPrompt(input) {
|
|
|
4106
4171
|
"Completion handoff (required): before you stop, ensure the harness records a final result \u2014 summarize outcome in your last message and append a heartbeat line with phase `complete`. If you leave uncommitted changes or committed work without a PR, the orchestrator blocks completion until a GitHub PR exists (or you discard/commit cleanly). One-off helper scripts must be removed (`kynver worker discard-disposable --path <file>`) or committed before completion \u2014 maintenance/board-drain workers are not exempt. Exiting with only dirty files and no PR routes to salvage review, not production review.",
|
|
4107
4172
|
"PR-ready handoff: for substantial implementation work, commit, push, and open a GitHub PR (draft OK) on your branch before finishing \u2014 or rely on the harness to run `gh pr create` at completion when `gh` is authenticated.",
|
|
4108
4173
|
"Expert review / production-review workers (Dalton/Lorentz, plan-review-task, scheduledJob reviewer children): do NOT open new implementation PRs \u2014 review the parent task's existing PR and record reviewVerdict in finalResult; landing-contract targetPrReconciliation does not apply.",
|
|
4109
|
-
"Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates.",
|
|
4174
|
+
"Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates. When heavy verification is required, route through `node scripts/verify-pr-local.mjs` or `kynver harness verify` \u2014 they acquire the global heavy-verification lease so parallel workers do not launch simultaneous typechecks.",
|
|
4110
4175
|
"npm publish boundary: do not run `npm publish`, do not republish `@kynver-app/*` packages, and do not block on an operator to publish. When you need newer runtime code than npm, use this repo checkout (`npm run kynver:build`, `npm run kynver`) and record evidence: packages/kynver-runtime/package.json version + git ref in your completion report.",
|
|
4111
4176
|
"If verification fails (including OOM), append a heartbeat line immediately with the last command, failure reason, dirty-file status, commit/PR handoff state, and next action so recovery does not require log spelunking.",
|
|
4112
4177
|
"Landing-wrapper cleanup on a git ref: use `node scripts/agent-os-land-pr-cleanup-verify.mjs --ref origin/main` (JSON, exit 0). Do not use `git show <ref>:scripts/agent-os-land-pr.mjs | rg \u2026` \u2014 ripgrep exit 1 when markers are absent is reported as a failed command in Telegram/status tooling.",
|
|
@@ -4982,8 +5047,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
|
|
|
4982
5047
|
if (removed.length === 0) return false;
|
|
4983
5048
|
const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
|
|
4984
5049
|
return material.every((line) => {
|
|
4985
|
-
const
|
|
4986
|
-
return removedSet.has(
|
|
5050
|
+
const path71 = normalizeRelativePath(pathFromGitStatusLine(line));
|
|
5051
|
+
return removedSet.has(path71);
|
|
4987
5052
|
});
|
|
4988
5053
|
}
|
|
4989
5054
|
|
|
@@ -6277,7 +6342,7 @@ function collectRunActiveHarnessWorkers(runId) {
|
|
|
6277
6342
|
path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
6278
6343
|
void 0
|
|
6279
6344
|
);
|
|
6280
|
-
if (!worker?.taskId || !
|
|
6345
|
+
if (!worker?.taskId || !isActiveHarnessWorker(worker)) continue;
|
|
6281
6346
|
out.push({
|
|
6282
6347
|
runId: run.id,
|
|
6283
6348
|
workerName: name,
|
|
@@ -6516,7 +6581,7 @@ function isTmpOnlyPath(filePath) {
|
|
|
6516
6581
|
// src/plan-persist/outbox-store.ts
|
|
6517
6582
|
import {
|
|
6518
6583
|
existsSync as existsSync21,
|
|
6519
|
-
readFileSync as
|
|
6584
|
+
readFileSync as readFileSync10,
|
|
6520
6585
|
renameSync,
|
|
6521
6586
|
readdirSync as readdirSync5,
|
|
6522
6587
|
writeFileSync as writeFileSync3,
|
|
@@ -6544,7 +6609,7 @@ function findOutboxByIdempotencyKey(key) {
|
|
|
6544
6609
|
function readOutboxItem(jsonPath) {
|
|
6545
6610
|
if (!existsSync21(jsonPath)) return null;
|
|
6546
6611
|
try {
|
|
6547
|
-
return JSON.parse(
|
|
6612
|
+
return JSON.parse(readFileSync10(jsonPath, "utf8"));
|
|
6548
6613
|
} catch {
|
|
6549
6614
|
return null;
|
|
6550
6615
|
}
|
|
@@ -6552,7 +6617,7 @@ function readOutboxItem(jsonPath) {
|
|
|
6552
6617
|
function readOutboxBody(item) {
|
|
6553
6618
|
const { outboxDir } = ensurePlanOutboxDirs();
|
|
6554
6619
|
const bodyFile = path24.join(outboxDir, item.bodyPath);
|
|
6555
|
-
return
|
|
6620
|
+
return readFileSync10(bodyFile, "utf8");
|
|
6556
6621
|
}
|
|
6557
6622
|
function writeOutboxItem(input, opts) {
|
|
6558
6623
|
const { outboxDir } = ensurePlanOutboxDirs();
|
|
@@ -7138,7 +7203,7 @@ async function dispatchRun(args) {
|
|
|
7138
7203
|
);
|
|
7139
7204
|
}
|
|
7140
7205
|
const attempt = Number(task.attempt) || 1;
|
|
7141
|
-
if (attempt
|
|
7206
|
+
if (attempt > retryLimits.maxTaskAttempts) {
|
|
7142
7207
|
return abortClaimedSpawn(
|
|
7143
7208
|
task,
|
|
7144
7209
|
`task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
|
|
@@ -7274,7 +7339,12 @@ async function dispatchRun(args) {
|
|
|
7274
7339
|
const admissionExhaustion = readAdmissionExhaustion(result);
|
|
7275
7340
|
const capacityIdle = admissionExhaustion?.capacityIdle === true || startedCount === 0 && Number(result.resourceGate?.slotsAvailable) > 0;
|
|
7276
7341
|
if (capacityIdle && admissionExhaustion?.summary) {
|
|
7277
|
-
|
|
7342
|
+
const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
|
|
7343
|
+
const recovery = admissionExhaustion.overAttemptIdleRecovery;
|
|
7344
|
+
const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
|
|
7345
|
+
console.error(
|
|
7346
|
+
`[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}`
|
|
7347
|
+
);
|
|
7278
7348
|
}
|
|
7279
7349
|
const summary = {
|
|
7280
7350
|
runId: run.id,
|
|
@@ -7370,14 +7440,14 @@ async function sweepRun(args) {
|
|
|
7370
7440
|
|
|
7371
7441
|
// src/worktree.ts
|
|
7372
7442
|
import { existsSync as existsSync25, mkdirSync as mkdirSync6 } from "node:fs";
|
|
7373
|
-
import
|
|
7443
|
+
import path35 from "node:path";
|
|
7374
7444
|
|
|
7375
7445
|
// src/run-list.ts
|
|
7376
|
-
import { existsSync as existsSync24, readFileSync as
|
|
7377
|
-
import
|
|
7446
|
+
import { existsSync as existsSync24, readFileSync as readFileSync11 } from "node:fs";
|
|
7447
|
+
import path34 from "node:path";
|
|
7378
7448
|
|
|
7379
7449
|
// src/stale-reconcile.ts
|
|
7380
|
-
import
|
|
7450
|
+
import path33 from "node:path";
|
|
7381
7451
|
|
|
7382
7452
|
// src/finalize.ts
|
|
7383
7453
|
import path27 from "node:path";
|
|
@@ -7960,6 +8030,193 @@ function reconcileWorkerMetadata() {
|
|
|
7960
8030
|
return { workers: outcomes, runMetadataRetention };
|
|
7961
8031
|
}
|
|
7962
8032
|
|
|
8033
|
+
// src/local-pr-attention-reconcile.ts
|
|
8034
|
+
import { execFileSync } from "node:child_process";
|
|
8035
|
+
import path32 from "node:path";
|
|
8036
|
+
function normalizePrUrl3(url) {
|
|
8037
|
+
const m = url.trim().match(/github\.com\/([^/]+\/[^/]+)\/(?:pull|pulls)\/(\d+)/i);
|
|
8038
|
+
if (!m) return null;
|
|
8039
|
+
return `https://github.com/${m[1]}/pull/${m[2]}`;
|
|
8040
|
+
}
|
|
8041
|
+
function prNumberFromUrl(url) {
|
|
8042
|
+
const m = normalizePrUrl3(url)?.match(/\/pull\/(\d+)$/);
|
|
8043
|
+
if (!m) return null;
|
|
8044
|
+
const n = Number.parseInt(m[1], 10);
|
|
8045
|
+
return Number.isFinite(n) ? n : null;
|
|
8046
|
+
}
|
|
8047
|
+
function extractText(value) {
|
|
8048
|
+
if (value == null) return "";
|
|
8049
|
+
if (typeof value === "string") return value;
|
|
8050
|
+
try {
|
|
8051
|
+
return JSON.stringify(value);
|
|
8052
|
+
} catch {
|
|
8053
|
+
return "";
|
|
8054
|
+
}
|
|
8055
|
+
}
|
|
8056
|
+
function extractPrNumbersFromText(text) {
|
|
8057
|
+
const out = /* @__PURE__ */ new Set();
|
|
8058
|
+
for (const match of text.matchAll(/github\.com\/[^/\s)>"']+\/[^/\s)>"']+\/(?:pull|pulls)\/(\d+)/gi)) {
|
|
8059
|
+
const n = Number.parseInt(match[1], 10);
|
|
8060
|
+
if (Number.isFinite(n)) out.add(n);
|
|
8061
|
+
}
|
|
8062
|
+
for (const match of text.matchAll(/\bPR\s*#?\s*(\d{2,})\b/gi)) {
|
|
8063
|
+
const n = Number.parseInt(match[1], 10);
|
|
8064
|
+
if (Number.isFinite(n)) out.add(n);
|
|
8065
|
+
}
|
|
8066
|
+
for (const match of text.matchAll(/\bpr[-_]?(\d{2,})\b/gi)) {
|
|
8067
|
+
const n = Number.parseInt(match[1], 10);
|
|
8068
|
+
if (Number.isFinite(n)) out.add(n);
|
|
8069
|
+
}
|
|
8070
|
+
return [...out];
|
|
8071
|
+
}
|
|
8072
|
+
function extractTargetPrReconciliation(value) {
|
|
8073
|
+
let record = null;
|
|
8074
|
+
if (typeof value === "string") record = extractEmbeddedWorkerFinalResultRecord(value);
|
|
8075
|
+
else if (value && typeof value === "object" && !Array.isArray(value)) record = value;
|
|
8076
|
+
if (!record) return [];
|
|
8077
|
+
const raw = record.targetPrReconciliation ?? record.target_pr_reconciliation;
|
|
8078
|
+
if (!Array.isArray(raw)) return [];
|
|
8079
|
+
const out = [];
|
|
8080
|
+
for (const item of raw) {
|
|
8081
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
8082
|
+
const row = item;
|
|
8083
|
+
const prUrl = normalizePrUrl3(String(row.prUrl ?? row.pr_url ?? ""));
|
|
8084
|
+
const outcome = String(row.outcome ?? "").trim();
|
|
8085
|
+
if (!prUrl || outcome !== "merged") continue;
|
|
8086
|
+
out.push({
|
|
8087
|
+
prUrl,
|
|
8088
|
+
mergeCommit: typeof row.mergeCommit === "string" ? row.mergeCommit : typeof row.merge_commit === "string" ? row.merge_commit : null,
|
|
8089
|
+
reason: typeof row.reason === "string" ? row.reason : null
|
|
8090
|
+
});
|
|
8091
|
+
}
|
|
8092
|
+
return out;
|
|
8093
|
+
}
|
|
8094
|
+
function defaultLookupPr(input) {
|
|
8095
|
+
try {
|
|
8096
|
+
const repo = execFileSync("git", ["config", "--get", "remote.origin.url"], {
|
|
8097
|
+
cwd: input.repoDir,
|
|
8098
|
+
encoding: "utf8",
|
|
8099
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
8100
|
+
}).trim();
|
|
8101
|
+
const repoMatch = repo.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/i);
|
|
8102
|
+
const repoSlug = repoMatch?.[1];
|
|
8103
|
+
if (!repoSlug) return null;
|
|
8104
|
+
const raw = execFileSync(
|
|
8105
|
+
"gh",
|
|
8106
|
+
["pr", "view", String(input.prNumber), "--repo", repoSlug, "--json", "state,mergedAt,mergeCommit,url"],
|
|
8107
|
+
{ cwd: input.repoDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
8108
|
+
);
|
|
8109
|
+
const parsed = JSON.parse(raw);
|
|
8110
|
+
return {
|
|
8111
|
+
prUrl: normalizePrUrl3(parsed.url ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`) ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`,
|
|
8112
|
+
state: parsed.state ?? "",
|
|
8113
|
+
mergedAt: parsed.mergedAt ?? null,
|
|
8114
|
+
mergeCommit: parsed.mergeCommit?.oid ?? null
|
|
8115
|
+
};
|
|
8116
|
+
} catch {
|
|
8117
|
+
return null;
|
|
8118
|
+
}
|
|
8119
|
+
}
|
|
8120
|
+
function finalResultForWorker(worker) {
|
|
8121
|
+
const heartbeat = parseHeartbeat(worker.heartbeatPath);
|
|
8122
|
+
return worker.completionSnapshot?.finalResult ?? terminalFinalResultFromHeartbeat(heartbeat);
|
|
8123
|
+
}
|
|
8124
|
+
function collectMergedEvidenceByPr(run) {
|
|
8125
|
+
const byPr = /* @__PURE__ */ new Map();
|
|
8126
|
+
const runDir2 = runDirectory(run.id);
|
|
8127
|
+
for (const name of listRunWorkerNames(run)) {
|
|
8128
|
+
const worker = readJson(
|
|
8129
|
+
path32.join(runDir2, "workers", safeSlug(name), "worker.json"),
|
|
8130
|
+
void 0
|
|
8131
|
+
);
|
|
8132
|
+
if (!worker) continue;
|
|
8133
|
+
for (const evidence of extractTargetPrReconciliation(finalResultForWorker(worker))) {
|
|
8134
|
+
const n = prNumberFromUrl(evidence.prUrl);
|
|
8135
|
+
if (n != null && !byPr.has(n)) byPr.set(n, evidence);
|
|
8136
|
+
}
|
|
8137
|
+
}
|
|
8138
|
+
return byPr;
|
|
8139
|
+
}
|
|
8140
|
+
function candidatePrNumbers(worker, statusFinalResult) {
|
|
8141
|
+
const heartbeat = parseHeartbeat(worker.heartbeatPath);
|
|
8142
|
+
const text = [
|
|
8143
|
+
worker.name,
|
|
8144
|
+
worker.repairTargetPrUrl,
|
|
8145
|
+
worker.taskPrUrl,
|
|
8146
|
+
worker.branch,
|
|
8147
|
+
heartbeat.lastHeartbeatSummary,
|
|
8148
|
+
extractText(statusFinalResult)
|
|
8149
|
+
].filter(Boolean).join("\n");
|
|
8150
|
+
return extractPrNumbersFromText(text);
|
|
8151
|
+
}
|
|
8152
|
+
function isLocalOnlyAttentionNoise(worker) {
|
|
8153
|
+
return worker.localOnly === true && !worker.taskId && !worker.agentOsId;
|
|
8154
|
+
}
|
|
8155
|
+
function reconcileLocalOnlyMergedPrAttention(options = {}) {
|
|
8156
|
+
const lookupPr = options.lookupPr ?? defaultLookupPr;
|
|
8157
|
+
const outcomes = [];
|
|
8158
|
+
const liveLookupCache = /* @__PURE__ */ new Map();
|
|
8159
|
+
for (const run of listRunRecords()) {
|
|
8160
|
+
const mergedByPr = collectMergedEvidenceByPr(run);
|
|
8161
|
+
const runDir2 = runDirectory(run.id);
|
|
8162
|
+
for (const name of listRunWorkerNames(run)) {
|
|
8163
|
+
const workerPath = path32.join(runDir2, "workers", safeSlug(name), "worker.json");
|
|
8164
|
+
const worker = readJson(workerPath, void 0);
|
|
8165
|
+
if (!worker || !isLocalOnlyAttentionNoise(worker)) continue;
|
|
8166
|
+
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
8167
|
+
if (status.attention.state !== "needs_attention") continue;
|
|
8168
|
+
let merged = null;
|
|
8169
|
+
for (const prNumber of candidatePrNumbers(worker, status.finalResult)) {
|
|
8170
|
+
merged = mergedByPr.get(prNumber) ?? null;
|
|
8171
|
+
if (!merged) {
|
|
8172
|
+
const repoDir = run.repo || worker.worktreePath;
|
|
8173
|
+
const cacheKey = `${repoDir}#${prNumber}`;
|
|
8174
|
+
const live = liveLookupCache.has(cacheKey) ? liveLookupCache.get(cacheKey) ?? null : lookupPr({ repoDir, prNumber });
|
|
8175
|
+
if (!liveLookupCache.has(cacheKey)) liveLookupCache.set(cacheKey, live);
|
|
8176
|
+
if (live && (live.state === "MERGED" || live.mergedAt)) {
|
|
8177
|
+
merged = { prUrl: live.prUrl, mergeCommit: live.mergeCommit ?? null, reason: "GitHub reports PR merged" };
|
|
8178
|
+
}
|
|
8179
|
+
}
|
|
8180
|
+
if (merged) break;
|
|
8181
|
+
}
|
|
8182
|
+
if (!merged) {
|
|
8183
|
+
outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "no merged PR evidence" });
|
|
8184
|
+
continue;
|
|
8185
|
+
}
|
|
8186
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8187
|
+
worker.status = "done";
|
|
8188
|
+
worker.completionSnapshot = {
|
|
8189
|
+
prUrl: merged.prUrl,
|
|
8190
|
+
summary: `Local-only worker superseded by merged PR ${merged.prUrl}`,
|
|
8191
|
+
finalResult: {
|
|
8192
|
+
summary: `Local-only repair/salvage worker superseded by merged PR ${merged.prUrl}`,
|
|
8193
|
+
targetPrReconciliation: [
|
|
8194
|
+
{
|
|
8195
|
+
prUrl: merged.prUrl,
|
|
8196
|
+
outcome: "merged",
|
|
8197
|
+
mergeCommit: merged.mergeCommit ?? null,
|
|
8198
|
+
reason: merged.reason ?? "PR already merged; local-only worker no longer requires attention"
|
|
8199
|
+
}
|
|
8200
|
+
]
|
|
8201
|
+
}
|
|
8202
|
+
};
|
|
8203
|
+
worker.completionAckSource = "local-pr-merged-reconcile";
|
|
8204
|
+
worker.reconciledAt = now;
|
|
8205
|
+
worker.reconcileReason = "local-only needs_attention superseded by merged PR";
|
|
8206
|
+
saveWorker(run.id, worker);
|
|
8207
|
+
outcomes.push({
|
|
8208
|
+
runId: run.id,
|
|
8209
|
+
worker: name,
|
|
8210
|
+
action: "marked_done",
|
|
8211
|
+
reason: worker.reconcileReason,
|
|
8212
|
+
prUrl: merged.prUrl,
|
|
8213
|
+
mergeCommit: merged.mergeCommit ?? null
|
|
8214
|
+
});
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
return { workers: outcomes };
|
|
8218
|
+
}
|
|
8219
|
+
|
|
7963
8220
|
// src/stale-reconcile.ts
|
|
7964
8221
|
var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
|
|
7965
8222
|
function staleReconcileDisabled() {
|
|
@@ -7968,13 +8225,14 @@ function staleReconcileDisabled() {
|
|
|
7968
8225
|
function reconcileStaleWorkers() {
|
|
7969
8226
|
const metadataReconcile = reconcileWorkerMetadata();
|
|
7970
8227
|
if (staleReconcileDisabled()) {
|
|
7971
|
-
|
|
8228
|
+
const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
|
|
8229
|
+
return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
|
|
7972
8230
|
}
|
|
7973
8231
|
const outcomes = [];
|
|
7974
8232
|
const now = Date.now();
|
|
7975
8233
|
for (const run of listRunRecords()) {
|
|
7976
8234
|
for (const name of listRunWorkerNames(run)) {
|
|
7977
|
-
const workerPath =
|
|
8235
|
+
const workerPath = path33.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
7978
8236
|
const worker = readJson(workerPath, void 0);
|
|
7979
8237
|
if (!worker || worker.status !== "running") {
|
|
7980
8238
|
outcomes.push({
|
|
@@ -8046,7 +8304,8 @@ function reconcileStaleWorkers() {
|
|
|
8046
8304
|
});
|
|
8047
8305
|
}
|
|
8048
8306
|
}
|
|
8049
|
-
|
|
8307
|
+
const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
|
|
8308
|
+
return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
|
|
8050
8309
|
}
|
|
8051
8310
|
function reconcileRunsCli() {
|
|
8052
8311
|
const result = reconcileStaleWorkers();
|
|
@@ -8057,6 +8316,10 @@ function reconcileRunsCli() {
|
|
|
8057
8316
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
8058
8317
|
return acc;
|
|
8059
8318
|
}, {});
|
|
8319
|
+
const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
|
|
8320
|
+
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
8321
|
+
return acc;
|
|
8322
|
+
}, {});
|
|
8060
8323
|
const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
|
|
8061
8324
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
8062
8325
|
return acc;
|
|
@@ -8074,10 +8337,15 @@ function reconcileRunsCli() {
|
|
|
8074
8337
|
total: result.metadataReconcile.runMetadataRetention.runs.length
|
|
8075
8338
|
}
|
|
8076
8339
|
},
|
|
8340
|
+
localPrAttentionReconcile: {
|
|
8341
|
+
totals: localPrAttentionTotals,
|
|
8342
|
+
total: result.localPrAttentionReconcile.workers.length
|
|
8343
|
+
},
|
|
8077
8344
|
finalizedRuns: result.finalizedRuns.length,
|
|
8078
8345
|
details: {
|
|
8079
8346
|
workers: result.workers,
|
|
8080
8347
|
metadataReconcile: result.metadataReconcile.workers,
|
|
8348
|
+
localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
|
|
8081
8349
|
runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
|
|
8082
8350
|
finalizedRuns: result.finalizedRuns
|
|
8083
8351
|
}
|
|
@@ -8092,13 +8360,13 @@ function reconcileRunsCli() {
|
|
|
8092
8360
|
function heartbeatByteLength(heartbeatPath) {
|
|
8093
8361
|
if (!heartbeatPath || !existsSync24(heartbeatPath)) return 0;
|
|
8094
8362
|
try {
|
|
8095
|
-
return
|
|
8363
|
+
return readFileSync11(heartbeatPath, "utf8").trim().length;
|
|
8096
8364
|
} catch {
|
|
8097
8365
|
return 0;
|
|
8098
8366
|
}
|
|
8099
8367
|
}
|
|
8100
8368
|
function workerEvidence(run, workerName) {
|
|
8101
|
-
const workerPath =
|
|
8369
|
+
const workerPath = path34.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
|
|
8102
8370
|
const worker = readJson(workerPath, void 0);
|
|
8103
8371
|
if (!worker) {
|
|
8104
8372
|
return {
|
|
@@ -8155,7 +8423,7 @@ function aggregateRunAttention(workers) {
|
|
|
8155
8423
|
function countOpenWorkers(run) {
|
|
8156
8424
|
let open = 0;
|
|
8157
8425
|
for (const name of listRunWorkerNames(run)) {
|
|
8158
|
-
const workerPath =
|
|
8426
|
+
const workerPath = path34.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
8159
8427
|
const worker = readJson(workerPath, void 0);
|
|
8160
8428
|
if (!worker) continue;
|
|
8161
8429
|
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
@@ -8231,7 +8499,7 @@ function createRun(args) {
|
|
|
8231
8499
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8232
8500
|
workers: {}
|
|
8233
8501
|
};
|
|
8234
|
-
writeJson(
|
|
8502
|
+
writeJson(path35.join(dir, "run.json"), run);
|
|
8235
8503
|
console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
|
|
8236
8504
|
}
|
|
8237
8505
|
function listRuns() {
|
|
@@ -8244,7 +8512,7 @@ function failExists(message) {
|
|
|
8244
8512
|
|
|
8245
8513
|
// src/discard-disposable.ts
|
|
8246
8514
|
import { existsSync as existsSync26, rmSync as rmSync2 } from "node:fs";
|
|
8247
|
-
import
|
|
8515
|
+
import path36 from "node:path";
|
|
8248
8516
|
function normalizeRelativePath2(value) {
|
|
8249
8517
|
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
8250
8518
|
if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
|
|
@@ -8266,12 +8534,12 @@ function discardDisposableArtifacts(args) {
|
|
|
8266
8534
|
if (paths.length === 0) {
|
|
8267
8535
|
return { ok: false, removed: [], reason: "requires at least one --path" };
|
|
8268
8536
|
}
|
|
8269
|
-
const worktreeRoot =
|
|
8537
|
+
const worktreeRoot = path36.resolve(worker.worktreePath);
|
|
8270
8538
|
const removed = [];
|
|
8271
8539
|
for (const raw of paths) {
|
|
8272
8540
|
const rel = normalizeRelativePath2(raw);
|
|
8273
|
-
const abs =
|
|
8274
|
-
if (!abs.startsWith(worktreeRoot +
|
|
8541
|
+
const abs = path36.resolve(worktreeRoot, rel);
|
|
8542
|
+
if (!abs.startsWith(worktreeRoot + path36.sep) && abs !== worktreeRoot) {
|
|
8275
8543
|
return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
|
|
8276
8544
|
}
|
|
8277
8545
|
if (!existsSync26(abs)) {
|
|
@@ -8338,7 +8606,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
|
|
|
8338
8606
|
// src/cron/cron-env.ts
|
|
8339
8607
|
import { existsSync as existsSync27 } from "node:fs";
|
|
8340
8608
|
import { homedir as homedir11 } from "node:os";
|
|
8341
|
-
import
|
|
8609
|
+
import path37 from "node:path";
|
|
8342
8610
|
function envFlag(name, defaultValue) {
|
|
8343
8611
|
const raw = process.env[name]?.trim().toLowerCase();
|
|
8344
8612
|
if (!raw) return defaultValue;
|
|
@@ -8354,7 +8622,7 @@ function envInt(name, fallback, min = 1) {
|
|
|
8354
8622
|
function defaultKynverCronStorePath() {
|
|
8355
8623
|
const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
|
|
8356
8624
|
if (explicit) return explicit;
|
|
8357
|
-
return
|
|
8625
|
+
return path37.join(homedir11(), ".kynver", "agent-os-cron.json");
|
|
8358
8626
|
}
|
|
8359
8627
|
function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
|
|
8360
8628
|
const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
|
|
@@ -8427,12 +8695,12 @@ async function fireKynverCronJob(input) {
|
|
|
8427
8695
|
}
|
|
8428
8696
|
|
|
8429
8697
|
// src/cron/cron-lock.ts
|
|
8430
|
-
import { closeSync as closeSync6, existsSync as existsSync28, openSync as openSync6, readFileSync as
|
|
8698
|
+
import { closeSync as closeSync6, existsSync as existsSync28, openSync as openSync6, readFileSync as readFileSync12, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8431
8699
|
var STALE_LOCK_MS = 10 * 6e4;
|
|
8432
8700
|
function readLockInfo(lockPath) {
|
|
8433
8701
|
if (!existsSync28(lockPath)) return null;
|
|
8434
8702
|
try {
|
|
8435
|
-
const parsed = JSON.parse(
|
|
8703
|
+
const parsed = JSON.parse(readFileSync12(lockPath, "utf8"));
|
|
8436
8704
|
if (typeof parsed.pid === "number" && typeof parsed.at === "string") return parsed;
|
|
8437
8705
|
} catch {
|
|
8438
8706
|
return null;
|
|
@@ -8596,7 +8864,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
|
|
|
8596
8864
|
// src/cron/cron-tick-state.ts
|
|
8597
8865
|
import { randomBytes } from "node:crypto";
|
|
8598
8866
|
import { promises as fs2 } from "node:fs";
|
|
8599
|
-
import
|
|
8867
|
+
import path38 from "node:path";
|
|
8600
8868
|
var EMPTY = { version: 1, jobs: {} };
|
|
8601
8869
|
async function readFileIfExists2(filePath) {
|
|
8602
8870
|
try {
|
|
@@ -8623,7 +8891,7 @@ async function loadCronTickState(statePath) {
|
|
|
8623
8891
|
return parseCronTickState(raw);
|
|
8624
8892
|
}
|
|
8625
8893
|
async function writeStateAtomic(statePath, state) {
|
|
8626
|
-
await fs2.mkdir(
|
|
8894
|
+
await fs2.mkdir(path38.dirname(statePath), { recursive: true });
|
|
8627
8895
|
const suffix = randomBytes(6).toString("hex");
|
|
8628
8896
|
const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
|
|
8629
8897
|
await fs2.writeFile(tmp, `${JSON.stringify(state, null, 2)}
|
|
@@ -8800,7 +9068,7 @@ async function runKynverCronTick(opts = {}) {
|
|
|
8800
9068
|
}
|
|
8801
9069
|
|
|
8802
9070
|
// src/pipeline-tick.ts
|
|
8803
|
-
import
|
|
9071
|
+
import path56 from "node:path";
|
|
8804
9072
|
|
|
8805
9073
|
// src/pipeline-dispatch.ts
|
|
8806
9074
|
var RESERVED_REVIEW_STARTS = 1;
|
|
@@ -8956,7 +9224,7 @@ function buildBoxResourceSnapshotFromGate(gate, input = {}) {
|
|
|
8956
9224
|
}
|
|
8957
9225
|
|
|
8958
9226
|
// src/plan-progress-daemon-sync.ts
|
|
8959
|
-
import
|
|
9227
|
+
import path39 from "node:path";
|
|
8960
9228
|
|
|
8961
9229
|
// src/plan-progress-sync.ts
|
|
8962
9230
|
async function syncPlanProgress(args) {
|
|
@@ -8980,7 +9248,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
8980
9248
|
const outcomes = [];
|
|
8981
9249
|
for (const name of Object.keys(run.workers || {})) {
|
|
8982
9250
|
const worker = readJson(
|
|
8983
|
-
|
|
9251
|
+
path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
8984
9252
|
void 0
|
|
8985
9253
|
);
|
|
8986
9254
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -9037,10 +9305,10 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
|
|
|
9037
9305
|
}
|
|
9038
9306
|
|
|
9039
9307
|
// src/cleanup.ts
|
|
9040
|
-
import
|
|
9308
|
+
import path53 from "node:path";
|
|
9041
9309
|
|
|
9042
9310
|
// src/cleanup-guards.ts
|
|
9043
|
-
import
|
|
9311
|
+
import path40 from "node:path";
|
|
9044
9312
|
|
|
9045
9313
|
// src/cleanup-build-cache-paths.ts
|
|
9046
9314
|
var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
|
|
@@ -9181,7 +9449,7 @@ function skipWorktreeRemoval(input) {
|
|
|
9181
9449
|
function skipDependencyCacheRemoval(input) {
|
|
9182
9450
|
const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
|
|
9183
9451
|
if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
9184
|
-
if (activeWorktreePaths.has(
|
|
9452
|
+
if (activeWorktreePaths.has(path40.resolve(worktreePath))) return "active_worker";
|
|
9185
9453
|
if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
|
|
9186
9454
|
if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
|
|
9187
9455
|
return null;
|
|
@@ -9208,11 +9476,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
|
9208
9476
|
function collectPreservedLivePaths(actions, skips) {
|
|
9209
9477
|
const out = [];
|
|
9210
9478
|
const seen = /* @__PURE__ */ new Set();
|
|
9211
|
-
const push = (
|
|
9212
|
-
const key = `${
|
|
9479
|
+
const push = (path71, reason, detail) => {
|
|
9480
|
+
const key = `${path71}\0${reason}`;
|
|
9213
9481
|
if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
|
|
9214
9482
|
seen.add(key);
|
|
9215
|
-
out.push({ path:
|
|
9483
|
+
out.push({ path: path71, reason, ...detail ? { detail } : {} });
|
|
9216
9484
|
};
|
|
9217
9485
|
for (const skip2 of skips) {
|
|
9218
9486
|
if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
|
|
@@ -9228,11 +9496,11 @@ function collectPreservedLivePaths(actions, skips) {
|
|
|
9228
9496
|
|
|
9229
9497
|
// src/cleanup-run-directory.ts
|
|
9230
9498
|
import { existsSync as existsSync30, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
|
|
9231
|
-
import
|
|
9499
|
+
import path42 from "node:path";
|
|
9232
9500
|
|
|
9233
9501
|
// src/cleanup-active-worktrees.ts
|
|
9234
9502
|
import { existsSync as existsSync29, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
|
|
9235
|
-
import
|
|
9503
|
+
import path41 from "node:path";
|
|
9236
9504
|
function workerHasRecentHarnessActivity(worker, now) {
|
|
9237
9505
|
const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
|
|
9238
9506
|
for (const target of paths) {
|
|
@@ -9258,11 +9526,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
|
|
|
9258
9526
|
let runHasLive = false;
|
|
9259
9527
|
for (const name of Object.keys(run.workers || {})) {
|
|
9260
9528
|
const worker = readJson(
|
|
9261
|
-
|
|
9529
|
+
path41.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
|
|
9262
9530
|
void 0
|
|
9263
9531
|
);
|
|
9264
9532
|
if (!worker?.worktreePath) continue;
|
|
9265
|
-
const worktreePath =
|
|
9533
|
+
const worktreePath = path41.resolve(worker.worktreePath);
|
|
9266
9534
|
if (!isActiveHarnessWorker2(worker, now)) continue;
|
|
9267
9535
|
runHasLive = true;
|
|
9268
9536
|
activeWorktreePaths.add(worktreePath);
|
|
@@ -9290,7 +9558,7 @@ function pathAgeMs(target, now) {
|
|
|
9290
9558
|
}
|
|
9291
9559
|
}
|
|
9292
9560
|
function loadRunStatus(harnessRoot, runId) {
|
|
9293
|
-
const runPath =
|
|
9561
|
+
const runPath = path42.join(harnessRoot, "runs", runId, "run.json");
|
|
9294
9562
|
if (!existsSync30(runPath)) return null;
|
|
9295
9563
|
return readJson(runPath, null);
|
|
9296
9564
|
}
|
|
@@ -9326,7 +9594,7 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
9326
9594
|
if (!runEntry.isDirectory()) continue;
|
|
9327
9595
|
const runId = runEntry.name;
|
|
9328
9596
|
if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
|
|
9329
|
-
const runPath =
|
|
9597
|
+
const runPath = path42.join(opts.worktreesDir, runId);
|
|
9330
9598
|
if (!runDirectoryIsEmpty(runPath)) continue;
|
|
9331
9599
|
candidates.push({
|
|
9332
9600
|
kind: "remove_run_directory",
|
|
@@ -9344,14 +9612,14 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
9344
9612
|
import { existsSync as existsSync33, rmSync as rmSync4 } from "node:fs";
|
|
9345
9613
|
|
|
9346
9614
|
// src/cleanup-dir-size.ts
|
|
9347
|
-
import { execFileSync } from "node:child_process";
|
|
9615
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
9348
9616
|
import { existsSync as existsSync31, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
|
|
9349
|
-
import
|
|
9617
|
+
import path43 from "node:path";
|
|
9350
9618
|
var DEFAULT_DU_TIMEOUT_MS = 2500;
|
|
9351
9619
|
function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
|
|
9352
9620
|
if (!existsSync31(root)) return 0;
|
|
9353
9621
|
try {
|
|
9354
|
-
const out =
|
|
9622
|
+
const out = execFileSync2("du", ["-sb", root], {
|
|
9355
9623
|
encoding: "utf8",
|
|
9356
9624
|
timeout: timeoutMs,
|
|
9357
9625
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -9380,7 +9648,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
|
|
|
9380
9648
|
}
|
|
9381
9649
|
for (const name of entries) {
|
|
9382
9650
|
if (seen++ > maxEntries) return null;
|
|
9383
|
-
const full =
|
|
9651
|
+
const full = path43.join(current, name);
|
|
9384
9652
|
let st;
|
|
9385
9653
|
try {
|
|
9386
9654
|
st = statSync8(full);
|
|
@@ -9431,20 +9699,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
9431
9699
|
|
|
9432
9700
|
// src/cleanup-privileged-remove.ts
|
|
9433
9701
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
9434
|
-
import
|
|
9702
|
+
import path45 from "node:path";
|
|
9435
9703
|
|
|
9436
9704
|
// src/cleanup-harness-path-validate.ts
|
|
9437
|
-
import
|
|
9705
|
+
import path44 from "node:path";
|
|
9438
9706
|
function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
|
|
9439
|
-
const resolved =
|
|
9440
|
-
const suffix = `${
|
|
9707
|
+
const resolved = path44.resolve(targetPath);
|
|
9708
|
+
const suffix = `${path44.sep}${cacheDirName}`;
|
|
9441
9709
|
const cachePath = resolved.endsWith(suffix) ? resolved : null;
|
|
9442
9710
|
if (!cachePath) return "path_outside_harness";
|
|
9443
|
-
const rel =
|
|
9444
|
-
if (rel.startsWith("..") ||
|
|
9445
|
-
const parts = rel.split(
|
|
9711
|
+
const rel = path44.relative(worktreesDir, cachePath);
|
|
9712
|
+
if (rel.startsWith("..") || path44.isAbsolute(rel)) return "path_outside_harness";
|
|
9713
|
+
const parts = rel.split(path44.sep);
|
|
9446
9714
|
if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
|
|
9447
|
-
if (!resolved.startsWith(
|
|
9715
|
+
if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
|
|
9448
9716
|
return null;
|
|
9449
9717
|
}
|
|
9450
9718
|
function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
|
|
@@ -9454,16 +9722,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
|
9454
9722
|
return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
|
|
9455
9723
|
}
|
|
9456
9724
|
function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
9457
|
-
const resolved =
|
|
9458
|
-
const relToWt =
|
|
9459
|
-
if (relToWt.startsWith("..") ||
|
|
9460
|
-
const parts = relToWt.split(
|
|
9725
|
+
const resolved = path44.resolve(targetPath);
|
|
9726
|
+
const relToWt = path44.relative(worktreesDir, resolved);
|
|
9727
|
+
if (relToWt.startsWith("..") || path44.isAbsolute(relToWt)) return "path_outside_harness";
|
|
9728
|
+
const parts = relToWt.split(path44.sep);
|
|
9461
9729
|
if (parts.length < 3) return "path_outside_harness";
|
|
9462
|
-
if (!resolved.startsWith(
|
|
9730
|
+
if (!resolved.startsWith(path44.resolve(harnessRoot))) return "path_outside_harness";
|
|
9463
9731
|
return null;
|
|
9464
9732
|
}
|
|
9465
9733
|
function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
9466
|
-
const resolved =
|
|
9734
|
+
const resolved = path44.resolve(targetPath);
|
|
9467
9735
|
return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
|
|
9468
9736
|
}
|
|
9469
9737
|
|
|
@@ -9497,12 +9765,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
|
|
|
9497
9765
|
"chown",
|
|
9498
9766
|
"-R",
|
|
9499
9767
|
`${effectiveUid}:${effectiveGid}`,
|
|
9500
|
-
|
|
9768
|
+
path45.resolve(targetPath)
|
|
9501
9769
|
]);
|
|
9502
9770
|
if (chown.ok) {
|
|
9503
9771
|
return { ok: true, method: "chown_then_rm" };
|
|
9504
9772
|
}
|
|
9505
|
-
const rm = runSudoNonInteractive(["rm", "-rf",
|
|
9773
|
+
const rm = runSudoNonInteractive(["rm", "-rf", path45.resolve(targetPath)]);
|
|
9506
9774
|
if (rm.ok) {
|
|
9507
9775
|
return { ok: true, method: "sudo_rm" };
|
|
9508
9776
|
}
|
|
@@ -9704,7 +9972,7 @@ function removeWorktree(candidate, execute) {
|
|
|
9704
9972
|
|
|
9705
9973
|
// src/cleanup-scan.ts
|
|
9706
9974
|
import { existsSync as existsSync34, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
|
|
9707
|
-
import
|
|
9975
|
+
import path46 from "node:path";
|
|
9708
9976
|
function pathAgeMs2(target, now) {
|
|
9709
9977
|
try {
|
|
9710
9978
|
const mtime = statSync9(target).mtimeMs;
|
|
@@ -9714,16 +9982,16 @@ function pathAgeMs2(target, now) {
|
|
|
9714
9982
|
}
|
|
9715
9983
|
}
|
|
9716
9984
|
function isPathInside(child, parent) {
|
|
9717
|
-
const rel =
|
|
9718
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
9985
|
+
const rel = path46.relative(parent, child);
|
|
9986
|
+
return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
|
|
9719
9987
|
}
|
|
9720
9988
|
function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
|
|
9721
9989
|
const out = [];
|
|
9722
9990
|
for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
|
|
9723
9991
|
if (rel === ".next") continue;
|
|
9724
|
-
const target =
|
|
9992
|
+
const target = path46.join(worktreePath, rel);
|
|
9725
9993
|
if (!existsSync34(target)) continue;
|
|
9726
|
-
const resolved =
|
|
9994
|
+
const resolved = path46.resolve(target);
|
|
9727
9995
|
if (seen.has(resolved)) continue;
|
|
9728
9996
|
if (!isPathInside(resolved, opts.harnessRoot)) continue;
|
|
9729
9997
|
seen.add(resolved);
|
|
@@ -9755,10 +10023,10 @@ function scanBuildCacheCandidates(opts) {
|
|
|
9755
10023
|
if (!opts.includeOrphans || !existsSync34(opts.worktreesDir)) return candidates;
|
|
9756
10024
|
for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
|
|
9757
10025
|
if (!runEntry.isDirectory()) continue;
|
|
9758
|
-
const runPath =
|
|
10026
|
+
const runPath = path46.join(opts.worktreesDir, runEntry.name);
|
|
9759
10027
|
for (const workerEntry of readdirSync12(runPath, { withFileTypes: true })) {
|
|
9760
10028
|
if (!workerEntry.isDirectory()) continue;
|
|
9761
|
-
const worktreePath =
|
|
10029
|
+
const worktreePath = path46.join(runPath, workerEntry.name);
|
|
9762
10030
|
candidates.push(
|
|
9763
10031
|
...collectBuildCacheForWorktree(worktreePath, opts, seen, {
|
|
9764
10032
|
runId: runEntry.name,
|
|
@@ -9796,12 +10064,12 @@ function scanWorktreeCandidates(opts) {
|
|
|
9796
10064
|
if (!orphanEnabled || !existsSync34(opts.worktreesDir)) return candidates;
|
|
9797
10065
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
9798
10066
|
for (const entry of opts.index.values()) {
|
|
9799
|
-
indexedPaths.add(
|
|
10067
|
+
indexedPaths.add(path46.resolve(entry.worktreePath));
|
|
9800
10068
|
}
|
|
9801
10069
|
for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
|
|
9802
10070
|
if (!runEntry.isDirectory()) continue;
|
|
9803
10071
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
9804
|
-
const runPath =
|
|
10072
|
+
const runPath = path46.join(opts.worktreesDir, runEntry.name);
|
|
9805
10073
|
let workerEntries;
|
|
9806
10074
|
try {
|
|
9807
10075
|
workerEntries = readdirSync12(runPath, { withFileTypes: true });
|
|
@@ -9810,7 +10078,7 @@ function scanWorktreeCandidates(opts) {
|
|
|
9810
10078
|
}
|
|
9811
10079
|
for (const workerEntry of workerEntries) {
|
|
9812
10080
|
if (!workerEntry.isDirectory()) continue;
|
|
9813
|
-
const worktreePath =
|
|
10081
|
+
const worktreePath = path46.resolve(path46.join(runPath, workerEntry.name));
|
|
9814
10082
|
if (seen.has(worktreePath)) continue;
|
|
9815
10083
|
if (indexedPaths.has(worktreePath)) continue;
|
|
9816
10084
|
if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
|
|
@@ -9830,7 +10098,7 @@ function scanWorktreeCandidates(opts) {
|
|
|
9830
10098
|
|
|
9831
10099
|
// src/cleanup-dependency-scan.ts
|
|
9832
10100
|
import { existsSync as existsSync35, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
|
|
9833
|
-
import
|
|
10101
|
+
import path47 from "node:path";
|
|
9834
10102
|
var DEPENDENCY_CACHE_DIRS = [
|
|
9835
10103
|
{ dirName: "node_modules", kind: "remove_node_modules" },
|
|
9836
10104
|
{ dirName: ".next", kind: "remove_next_cache" }
|
|
@@ -9844,12 +10112,12 @@ function pathAgeMs3(target, now) {
|
|
|
9844
10112
|
}
|
|
9845
10113
|
}
|
|
9846
10114
|
function isPathInside2(child, parent) {
|
|
9847
|
-
const rel =
|
|
9848
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
10115
|
+
const rel = path47.relative(parent, child);
|
|
10116
|
+
return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
|
|
9849
10117
|
}
|
|
9850
10118
|
function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
9851
10119
|
if (!existsSync35(targetPath)) return;
|
|
9852
|
-
const resolved =
|
|
10120
|
+
const resolved = path47.resolve(targetPath);
|
|
9853
10121
|
if (seen.has(resolved)) return;
|
|
9854
10122
|
if (!isPathInside2(resolved, opts.harnessRoot)) return;
|
|
9855
10123
|
seen.add(resolved);
|
|
@@ -9866,7 +10134,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
|
9866
10134
|
}
|
|
9867
10135
|
function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
|
|
9868
10136
|
for (const entry of DEPENDENCY_CACHE_DIRS) {
|
|
9869
|
-
pushCandidate2(candidates, seen, opts,
|
|
10137
|
+
pushCandidate2(candidates, seen, opts, path47.join(worktreePath, entry.dirName), entry.kind, meta);
|
|
9870
10138
|
}
|
|
9871
10139
|
}
|
|
9872
10140
|
function scanDependencyCacheCandidates(opts) {
|
|
@@ -9884,7 +10152,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
9884
10152
|
for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
|
|
9885
10153
|
if (!runEntry.isDirectory()) continue;
|
|
9886
10154
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
9887
|
-
const runPath =
|
|
10155
|
+
const runPath = path47.join(opts.worktreesDir, runEntry.name);
|
|
9888
10156
|
let workerEntries;
|
|
9889
10157
|
try {
|
|
9890
10158
|
workerEntries = readdirSync13(runPath, { withFileTypes: true });
|
|
@@ -9893,7 +10161,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
9893
10161
|
}
|
|
9894
10162
|
for (const workerEntry of workerEntries) {
|
|
9895
10163
|
if (!workerEntry.isDirectory()) continue;
|
|
9896
|
-
const worktreePath =
|
|
10164
|
+
const worktreePath = path47.join(runPath, workerEntry.name);
|
|
9897
10165
|
scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
|
|
9898
10166
|
runId: runEntry.name,
|
|
9899
10167
|
worker: workerEntry.name
|
|
@@ -9905,7 +10173,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
9905
10173
|
|
|
9906
10174
|
// src/cleanup-duplicate-worktrees.ts
|
|
9907
10175
|
import { existsSync as existsSync36, statSync as statSync11 } from "node:fs";
|
|
9908
|
-
import
|
|
10176
|
+
import path48 from "node:path";
|
|
9909
10177
|
function pathAgeMs4(target, now) {
|
|
9910
10178
|
try {
|
|
9911
10179
|
const mtime = statSync11(target).mtimeMs;
|
|
@@ -9935,8 +10203,8 @@ function parseWorktreePorcelain(output) {
|
|
|
9935
10203
|
return records;
|
|
9936
10204
|
}
|
|
9937
10205
|
function isUnderWorktreesDir(worktreePath, worktreesDir) {
|
|
9938
|
-
const rel =
|
|
9939
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
10206
|
+
const rel = path48.relative(path48.resolve(worktreesDir), path48.resolve(worktreePath));
|
|
10207
|
+
return rel !== "" && !rel.startsWith("..") && !path48.isAbsolute(rel);
|
|
9940
10208
|
}
|
|
9941
10209
|
function isCleanWorktree(worktreePath, repoRoot) {
|
|
9942
10210
|
try {
|
|
@@ -9952,11 +10220,11 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
9952
10220
|
if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return [];
|
|
9953
10221
|
const repos = /* @__PURE__ */ new Set();
|
|
9954
10222
|
for (const entry of opts.index.values()) {
|
|
9955
|
-
if (entry.run.repo) repos.add(
|
|
10223
|
+
if (entry.run.repo) repos.add(path48.resolve(entry.run.repo));
|
|
9956
10224
|
}
|
|
9957
10225
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
9958
10226
|
for (const entry of opts.index.values()) {
|
|
9959
|
-
indexedPaths.add(
|
|
10227
|
+
indexedPaths.add(path48.resolve(entry.worktreePath));
|
|
9960
10228
|
}
|
|
9961
10229
|
const candidates = [];
|
|
9962
10230
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -9969,15 +10237,15 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
9969
10237
|
}
|
|
9970
10238
|
const worktrees = parseWorktreePorcelain(porcelain);
|
|
9971
10239
|
for (const wt of worktrees) {
|
|
9972
|
-
const resolved =
|
|
9973
|
-
if (resolved ===
|
|
10240
|
+
const resolved = path48.resolve(wt.path);
|
|
10241
|
+
if (resolved === path48.resolve(repoRoot)) continue;
|
|
9974
10242
|
if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
|
|
9975
10243
|
if (indexedPaths.has(resolved)) continue;
|
|
9976
10244
|
if (seen.has(resolved)) continue;
|
|
9977
10245
|
if (!existsSync36(resolved)) continue;
|
|
9978
10246
|
if (!isCleanWorktree(resolved, repoRoot)) continue;
|
|
9979
|
-
const rel =
|
|
9980
|
-
const parts = rel.split(
|
|
10247
|
+
const rel = path48.relative(opts.worktreesDir, resolved);
|
|
10248
|
+
const parts = rel.split(path48.sep);
|
|
9981
10249
|
const runId = parts[0];
|
|
9982
10250
|
const worker = parts[1] ?? "unknown";
|
|
9983
10251
|
seen.add(resolved);
|
|
@@ -9996,12 +10264,12 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
9996
10264
|
}
|
|
9997
10265
|
|
|
9998
10266
|
// src/cleanup-worktree-index.ts
|
|
9999
|
-
import
|
|
10267
|
+
import path49 from "node:path";
|
|
10000
10268
|
function buildWorktreeIndexAt(harnessRoot) {
|
|
10001
10269
|
const index = /* @__PURE__ */ new Map();
|
|
10002
10270
|
for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
|
|
10003
10271
|
for (const name of Object.keys(run.workers || {})) {
|
|
10004
|
-
const workerPath =
|
|
10272
|
+
const workerPath = path49.join(
|
|
10005
10273
|
runDirectoryAt(harnessRoot, run.id),
|
|
10006
10274
|
"workers",
|
|
10007
10275
|
safeSlug(name),
|
|
@@ -10009,9 +10277,9 @@ function buildWorktreeIndexAt(harnessRoot) {
|
|
|
10009
10277
|
);
|
|
10010
10278
|
const worker = readJson(workerPath, void 0);
|
|
10011
10279
|
if (!worker?.worktreePath) continue;
|
|
10012
|
-
index.set(
|
|
10280
|
+
index.set(path49.resolve(worker.worktreePath), {
|
|
10013
10281
|
harnessRoot,
|
|
10014
|
-
worktreePath:
|
|
10282
|
+
worktreePath: path49.resolve(worker.worktreePath),
|
|
10015
10283
|
runId: run.id,
|
|
10016
10284
|
workerName: name,
|
|
10017
10285
|
run,
|
|
@@ -10081,14 +10349,14 @@ function resolvePipelineHarnessRetention(runId) {
|
|
|
10081
10349
|
|
|
10082
10350
|
// src/cleanup-orphan-safety.ts
|
|
10083
10351
|
import { existsSync as existsSync37, statSync as statSync12 } from "node:fs";
|
|
10084
|
-
import
|
|
10352
|
+
import path50 from "node:path";
|
|
10085
10353
|
var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
|
|
10086
10354
|
function assessOrphanWorktreeSafety(input) {
|
|
10087
10355
|
const now = input.now ?? Date.now();
|
|
10088
10356
|
const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
|
|
10089
10357
|
if (!existsSync37(input.worktreePath)) return null;
|
|
10090
10358
|
if (input.runId && input.workerName) {
|
|
10091
|
-
const heartbeatPath =
|
|
10359
|
+
const heartbeatPath = path50.join(
|
|
10092
10360
|
input.harnessRoot,
|
|
10093
10361
|
"runs",
|
|
10094
10362
|
input.runId,
|
|
@@ -10102,7 +10370,7 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
10102
10370
|
} catch {
|
|
10103
10371
|
}
|
|
10104
10372
|
}
|
|
10105
|
-
const gitDir =
|
|
10373
|
+
const gitDir = path50.join(input.worktreePath, ".git");
|
|
10106
10374
|
if (!existsSync37(gitDir)) return null;
|
|
10107
10375
|
const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
|
|
10108
10376
|
if (porcelain.status !== 0) return "pr_or_unmerged_commits";
|
|
@@ -10133,7 +10401,7 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
10133
10401
|
|
|
10134
10402
|
// src/harness-storage-snapshot.ts
|
|
10135
10403
|
import { existsSync as existsSync38, readdirSync as readdirSync14, statSync as statSync13 } from "node:fs";
|
|
10136
|
-
import
|
|
10404
|
+
import path51 from "node:path";
|
|
10137
10405
|
function harnessStorageSnapshot(opts = {}) {
|
|
10138
10406
|
const harnessRoot = normalizeHarnessRoot(opts.harnessRoot ?? resolveHarnessRoot());
|
|
10139
10407
|
const worktreesDir = harnessWorktreesDir(harnessRoot);
|
|
@@ -10171,7 +10439,7 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
10171
10439
|
for (const runEntry of entries) {
|
|
10172
10440
|
if (!runEntry.isDirectory()) continue;
|
|
10173
10441
|
runCount += 1;
|
|
10174
|
-
const runPath =
|
|
10442
|
+
const runPath = path51.join(worktreesDir, runEntry.name);
|
|
10175
10443
|
try {
|
|
10176
10444
|
const st = statSync13(runPath);
|
|
10177
10445
|
oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
|
|
@@ -10208,10 +10476,10 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
10208
10476
|
// src/cleanup-harness-roots.ts
|
|
10209
10477
|
import { existsSync as existsSync39 } from "node:fs";
|
|
10210
10478
|
import { homedir as homedir12 } from "node:os";
|
|
10211
|
-
import
|
|
10479
|
+
import path52 from "node:path";
|
|
10212
10480
|
var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
|
|
10213
10481
|
"/var/tmp/kynver-harness",
|
|
10214
|
-
|
|
10482
|
+
path52.join(homedir12(), ".openclaw", "harness")
|
|
10215
10483
|
];
|
|
10216
10484
|
function addRoot(seen, roots, candidate) {
|
|
10217
10485
|
if (!candidate?.trim()) return;
|
|
@@ -10233,7 +10501,7 @@ function resolveHarnessScanRoots(options = {}) {
|
|
|
10233
10501
|
for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
|
|
10234
10502
|
if (shouldScanWellKnownRoots(options)) {
|
|
10235
10503
|
for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
|
|
10236
|
-
const resolved =
|
|
10504
|
+
const resolved = path52.resolve(candidate);
|
|
10237
10505
|
if (!seen.has(resolved) && existsSync39(resolved)) addRoot(seen, roots, resolved);
|
|
10238
10506
|
}
|
|
10239
10507
|
}
|
|
@@ -10388,9 +10656,9 @@ function mergeWorktreeIndexes(scanRoots) {
|
|
|
10388
10656
|
}
|
|
10389
10657
|
function worktreePathForCandidate(candidate, worktreesDir) {
|
|
10390
10658
|
if (candidate.runId && candidate.worker) {
|
|
10391
|
-
return
|
|
10659
|
+
return path53.join(worktreesDir, candidate.runId, candidate.worker);
|
|
10392
10660
|
}
|
|
10393
|
-
return
|
|
10661
|
+
return path53.resolve(candidate.path, "..");
|
|
10394
10662
|
}
|
|
10395
10663
|
function runHarnessCleanup(options = {}) {
|
|
10396
10664
|
let retention = resolveHarnessRetention(options);
|
|
@@ -10415,7 +10683,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10415
10683
|
for (const harnessRoot of paths.scanRoots) {
|
|
10416
10684
|
if (atSweepCap()) break;
|
|
10417
10685
|
emitCleanupProgress("root", harnessRoot);
|
|
10418
|
-
const worktreesDir =
|
|
10686
|
+
const worktreesDir = path53.join(harnessRoot, "worktrees");
|
|
10419
10687
|
const scanOpts = {
|
|
10420
10688
|
harnessRoot,
|
|
10421
10689
|
worktreesDir,
|
|
@@ -10428,7 +10696,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10428
10696
|
};
|
|
10429
10697
|
for (const raw of scanDependencyCacheCandidates(scanOpts)) {
|
|
10430
10698
|
if (atSweepCap()) break;
|
|
10431
|
-
const resolved =
|
|
10699
|
+
const resolved = path53.resolve(raw.path);
|
|
10432
10700
|
if (processedPaths.has(resolved)) continue;
|
|
10433
10701
|
processedPaths.add(resolved);
|
|
10434
10702
|
const candidate = { ...raw, path: resolved };
|
|
@@ -10439,7 +10707,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10439
10707
|
continue;
|
|
10440
10708
|
}
|
|
10441
10709
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
10442
|
-
const indexed = index.get(
|
|
10710
|
+
const indexed = index.get(path53.resolve(worktreePath)) ?? null;
|
|
10443
10711
|
const guardReason = skipDependencyCacheRemoval({
|
|
10444
10712
|
indexed,
|
|
10445
10713
|
includeOrphans: true,
|
|
@@ -10463,7 +10731,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10463
10731
|
}
|
|
10464
10732
|
for (const raw of scanBuildCacheCandidates(scanOpts)) {
|
|
10465
10733
|
if (atSweepCap()) break;
|
|
10466
|
-
const resolved =
|
|
10734
|
+
const resolved = path53.resolve(raw.path);
|
|
10467
10735
|
if (processedPaths.has(resolved)) continue;
|
|
10468
10736
|
processedPaths.add(resolved);
|
|
10469
10737
|
const candidate = { ...raw, path: resolved };
|
|
@@ -10474,7 +10742,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10474
10742
|
continue;
|
|
10475
10743
|
}
|
|
10476
10744
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
10477
|
-
const indexed = index.get(
|
|
10745
|
+
const indexed = index.get(path53.resolve(worktreePath)) ?? null;
|
|
10478
10746
|
const guardReason = skipBuildCacheRemoval({
|
|
10479
10747
|
indexed,
|
|
10480
10748
|
includeOrphans: true,
|
|
@@ -10504,11 +10772,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
10504
10772
|
const worktreeSeen = /* @__PURE__ */ new Set();
|
|
10505
10773
|
for (const raw of worktreeCandidates) {
|
|
10506
10774
|
if (atSweepCap()) break;
|
|
10507
|
-
const resolved =
|
|
10775
|
+
const resolved = path53.resolve(raw.path);
|
|
10508
10776
|
if (worktreeSeen.has(resolved)) continue;
|
|
10509
10777
|
worktreeSeen.add(resolved);
|
|
10510
10778
|
const candidate = { ...raw, path: resolved };
|
|
10511
|
-
const indexed = index.get(
|
|
10779
|
+
const indexed = index.get(path53.resolve(candidate.path)) ?? null;
|
|
10512
10780
|
const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
|
|
10513
10781
|
worktreePath: candidate.path,
|
|
10514
10782
|
harnessRoot,
|
|
@@ -10518,7 +10786,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10518
10786
|
});
|
|
10519
10787
|
const guardSkip = skipWorktreeRemoval({
|
|
10520
10788
|
indexed,
|
|
10521
|
-
worktreePath:
|
|
10789
|
+
worktreePath: path53.resolve(candidate.path),
|
|
10522
10790
|
includeOrphans: retention.includeOrphans,
|
|
10523
10791
|
worktreesAgeMs: retention.worktreesAgeMs,
|
|
10524
10792
|
terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
|
|
@@ -10550,11 +10818,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
10550
10818
|
now: paths.now
|
|
10551
10819
|
})) {
|
|
10552
10820
|
if (atSweepCap()) break;
|
|
10553
|
-
const resolved =
|
|
10821
|
+
const resolved = path53.resolve(raw.path);
|
|
10554
10822
|
if (processedPaths.has(resolved)) continue;
|
|
10555
10823
|
processedPaths.add(resolved);
|
|
10556
10824
|
const candidate = { ...raw, path: resolved };
|
|
10557
|
-
const runId = candidate.runId ??
|
|
10825
|
+
const runId = candidate.runId ?? path53.basename(resolved);
|
|
10558
10826
|
const dirSkip = skipRunDirectoryRemoval({
|
|
10559
10827
|
harnessRoot,
|
|
10560
10828
|
runId,
|
|
@@ -10690,11 +10958,11 @@ function isPipelineCleanupEnabled() {
|
|
|
10690
10958
|
// src/installed-package-versions.ts
|
|
10691
10959
|
import { readFile } from "node:fs/promises";
|
|
10692
10960
|
import { homedir as homedir13 } from "node:os";
|
|
10693
|
-
import
|
|
10961
|
+
import path55 from "node:path";
|
|
10694
10962
|
|
|
10695
10963
|
// src/memory-cost-package-version-guard.ts
|
|
10696
|
-
import { existsSync as existsSync40, readFileSync as
|
|
10697
|
-
import
|
|
10964
|
+
import { existsSync as existsSync40, readFileSync as readFileSync13 } from "node:fs";
|
|
10965
|
+
import path54 from "node:path";
|
|
10698
10966
|
var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
|
|
10699
10967
|
"@kynver-app/runtime": "0.1.83",
|
|
10700
10968
|
"@kynver-app/openclaw-agent-os": "0.1.43",
|
|
@@ -10745,7 +11013,7 @@ function maxSemver(versions) {
|
|
|
10745
11013
|
}
|
|
10746
11014
|
function readPackageJsonVersion(packageJsonPath) {
|
|
10747
11015
|
try {
|
|
10748
|
-
const parsed = JSON.parse(
|
|
11016
|
+
const parsed = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
|
|
10749
11017
|
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : null;
|
|
10750
11018
|
} catch {
|
|
10751
11019
|
return null;
|
|
@@ -10756,8 +11024,8 @@ function resolveRepoRoot(cwd, explicitRepoRoot) {
|
|
|
10756
11024
|
(value) => Boolean(value?.trim())
|
|
10757
11025
|
);
|
|
10758
11026
|
for (const candidate of candidates) {
|
|
10759
|
-
const resolved =
|
|
10760
|
-
if (existsSync40(
|
|
11027
|
+
const resolved = path54.resolve(candidate);
|
|
11028
|
+
if (existsSync40(path54.join(resolved, "packages/kynver-runtime/package.json")) && existsSync40(path54.join(resolved, "package.json"))) {
|
|
10761
11029
|
return resolved;
|
|
10762
11030
|
}
|
|
10763
11031
|
}
|
|
@@ -10770,7 +11038,7 @@ function probeRepoPackageVersions(input = {}) {
|
|
|
10770
11038
|
if (!repoRoot) return {};
|
|
10771
11039
|
const out = {};
|
|
10772
11040
|
for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
|
|
10773
|
-
const packageJsonPath =
|
|
11041
|
+
const packageJsonPath = path54.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
|
|
10774
11042
|
const version = readPackageJsonVersion(packageJsonPath);
|
|
10775
11043
|
if (!version) continue;
|
|
10776
11044
|
out[packageName] = { version, source: "repo", path: packageJsonPath };
|
|
@@ -10890,12 +11158,12 @@ function unique(values) {
|
|
|
10890
11158
|
}
|
|
10891
11159
|
function moduleRoots() {
|
|
10892
11160
|
const home = homedir13();
|
|
10893
|
-
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ??
|
|
10894
|
-
const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ?
|
|
11161
|
+
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path55.join(home, ".openclaw", "npm");
|
|
11162
|
+
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"));
|
|
10895
11163
|
return unique([
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot :
|
|
11164
|
+
path55.join(openClawPrefix, "lib", "node_modules"),
|
|
11165
|
+
path55.join(openClawPrefix, "node_modules"),
|
|
11166
|
+
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path55.join(npmGlobalRoot, "lib", "node_modules")
|
|
10899
11167
|
]);
|
|
10900
11168
|
}
|
|
10901
11169
|
async function readVersion(packageJsonPath) {
|
|
@@ -10911,7 +11179,7 @@ function installedPackageJsonCandidates(packageName) {
|
|
|
10911
11179
|
const seen = /* @__PURE__ */ new Set();
|
|
10912
11180
|
const out = [];
|
|
10913
11181
|
for (const root of roots) {
|
|
10914
|
-
const candidate =
|
|
11182
|
+
const candidate = path55.join(root, packageName, "package.json");
|
|
10915
11183
|
if (seen.has(candidate)) continue;
|
|
10916
11184
|
seen.add(candidate);
|
|
10917
11185
|
out.push(candidate);
|
|
@@ -10942,7 +11210,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
10942
11210
|
const outcomes = [];
|
|
10943
11211
|
for (const name of Object.keys(run.workers || {})) {
|
|
10944
11212
|
const worker = readJson(
|
|
10945
|
-
|
|
11213
|
+
path56.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
10946
11214
|
void 0
|
|
10947
11215
|
);
|
|
10948
11216
|
if (!worker?.taskId || worker.localOnly) continue;
|
|
@@ -11089,6 +11357,15 @@ async function runPipelineTick(args) {
|
|
|
11089
11357
|
var DEFAULT_INTERVAL_MS = 6e4;
|
|
11090
11358
|
var IDLE_INTERVAL_MS = 5 * 6e4;
|
|
11091
11359
|
var MAX_IDLE_STREAK = 10;
|
|
11360
|
+
var SLEEP_POLL_MS = 250;
|
|
11361
|
+
async function awaitDaemonBackoff(ms, isStopping) {
|
|
11362
|
+
let remaining = ms;
|
|
11363
|
+
while (remaining > 0 && !isStopping()) {
|
|
11364
|
+
const step = Math.min(SLEEP_POLL_MS, remaining);
|
|
11365
|
+
await sleepMsAsync(step);
|
|
11366
|
+
remaining -= step;
|
|
11367
|
+
}
|
|
11368
|
+
}
|
|
11092
11369
|
async function runDaemon(args) {
|
|
11093
11370
|
const runId = String(required(String(args.run || ""), "--run"));
|
|
11094
11371
|
const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
|
|
@@ -11146,17 +11423,17 @@ async function runDaemon(args) {
|
|
|
11146
11423
|
idleStreak = 0;
|
|
11147
11424
|
}
|
|
11148
11425
|
const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
|
|
11149
|
-
|
|
11426
|
+
await awaitDaemonBackoff(backoff, () => stopping);
|
|
11150
11427
|
} catch (error) {
|
|
11151
11428
|
console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
|
|
11152
|
-
|
|
11429
|
+
await awaitDaemonBackoff(intervalMs, () => stopping);
|
|
11153
11430
|
}
|
|
11154
11431
|
}
|
|
11155
11432
|
console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
|
|
11156
11433
|
}
|
|
11157
11434
|
|
|
11158
11435
|
// src/plan-progress.ts
|
|
11159
|
-
import
|
|
11436
|
+
import path60 from "node:path";
|
|
11160
11437
|
|
|
11161
11438
|
// src/bounded-build/constants.ts
|
|
11162
11439
|
var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
|
|
@@ -11286,14 +11563,235 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
|
|
|
11286
11563
|
}
|
|
11287
11564
|
|
|
11288
11565
|
// src/bounded-build/exec.ts
|
|
11566
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
11567
|
+
|
|
11568
|
+
// src/heavy-verification/slot.ts
|
|
11569
|
+
import {
|
|
11570
|
+
closeSync as closeSync7,
|
|
11571
|
+
existsSync as existsSync41,
|
|
11572
|
+
mkdirSync as mkdirSync8,
|
|
11573
|
+
openSync as openSync7,
|
|
11574
|
+
readdirSync as readdirSync15,
|
|
11575
|
+
readFileSync as readFileSync14,
|
|
11576
|
+
unlinkSync as unlinkSync4,
|
|
11577
|
+
writeFileSync as writeFileSync5
|
|
11578
|
+
} from "node:fs";
|
|
11579
|
+
import path58 from "node:path";
|
|
11580
|
+
|
|
11581
|
+
// src/heavy-verification/paths.ts
|
|
11582
|
+
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
11583
|
+
import path57 from "node:path";
|
|
11584
|
+
function resolveHeavyVerificationRoot() {
|
|
11585
|
+
return path57.join(resolveKynverStateRoot(), "heavy-verification");
|
|
11586
|
+
}
|
|
11587
|
+
function heavyVerificationSlotsDir() {
|
|
11588
|
+
return path57.join(resolveHeavyVerificationRoot(), "slots");
|
|
11589
|
+
}
|
|
11590
|
+
function ensureHeavyVerificationDirs() {
|
|
11591
|
+
const dir = heavyVerificationSlotsDir();
|
|
11592
|
+
mkdirSync7(dir, { recursive: true });
|
|
11593
|
+
return dir;
|
|
11594
|
+
}
|
|
11595
|
+
|
|
11596
|
+
// src/heavy-verification/slot.ts
|
|
11597
|
+
var DEFAULT_HEAVY_VERIFICATION_STALE_MS = 2 * 60 * 6e4;
|
|
11598
|
+
var DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT = 1;
|
|
11599
|
+
function positiveInt5(value, fallback) {
|
|
11600
|
+
const n = Number(value);
|
|
11601
|
+
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
11602
|
+
return Math.floor(n);
|
|
11603
|
+
}
|
|
11604
|
+
function isHeavyVerificationGateSkipped() {
|
|
11605
|
+
const v = process.env.KYNVER_HEAVY_VERIFICATION_SKIP?.trim().toLowerCase();
|
|
11606
|
+
return v === "1" || v === "true" || v === "yes";
|
|
11607
|
+
}
|
|
11608
|
+
function resolveHeavyVerificationMaxConcurrent() {
|
|
11609
|
+
const env = process.env.KYNVER_HEAVY_VERIFICATION_MAX_CONCURRENT;
|
|
11610
|
+
if (env) return positiveInt5(env, DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT);
|
|
11611
|
+
return DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT;
|
|
11612
|
+
}
|
|
11613
|
+
function indexedSlotId(index) {
|
|
11614
|
+
return `slot-${index}`;
|
|
11615
|
+
}
|
|
11616
|
+
function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
|
|
11617
|
+
return path58.join(slotsDir, `${slotId}.json`);
|
|
11618
|
+
}
|
|
11619
|
+
function readSlotRecord(filePath) {
|
|
11620
|
+
if (!existsSync41(filePath)) return null;
|
|
11621
|
+
try {
|
|
11622
|
+
const parsed = JSON.parse(readFileSync14(filePath, "utf8"));
|
|
11623
|
+
if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
|
|
11624
|
+
return parsed;
|
|
11625
|
+
}
|
|
11626
|
+
} catch {
|
|
11627
|
+
return null;
|
|
11628
|
+
}
|
|
11629
|
+
return null;
|
|
11630
|
+
}
|
|
11631
|
+
function slotIsStale(record, staleMs = DEFAULT_HEAVY_VERIFICATION_STALE_MS) {
|
|
11632
|
+
if (!record) return true;
|
|
11633
|
+
if (!isPidAlive(record.pid)) return true;
|
|
11634
|
+
const atMs = Date.parse(record.acquiredAt);
|
|
11635
|
+
if (Number.isNaN(atMs)) return true;
|
|
11636
|
+
return Date.now() - atMs > staleMs;
|
|
11637
|
+
}
|
|
11638
|
+
function reclaimStaleSlot(filePath, staleMs) {
|
|
11639
|
+
const record = readSlotRecord(filePath);
|
|
11640
|
+
if (!slotIsStale(record, staleMs)) return;
|
|
11641
|
+
try {
|
|
11642
|
+
unlinkSync4(filePath);
|
|
11643
|
+
} catch {
|
|
11644
|
+
}
|
|
11645
|
+
}
|
|
11646
|
+
function ensureSlotsDir(slotsDir) {
|
|
11647
|
+
mkdirSync8(slotsDir, { recursive: true });
|
|
11648
|
+
return slotsDir;
|
|
11649
|
+
}
|
|
11650
|
+
function reclaimStaleHeavyVerificationSlots(opts = {}) {
|
|
11651
|
+
const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
|
|
11652
|
+
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
11653
|
+
let reclaimed = 0;
|
|
11654
|
+
for (const name of readdirSync15(slotsDir)) {
|
|
11655
|
+
if (!name.endsWith(".json")) continue;
|
|
11656
|
+
const filePath = path58.join(slotsDir, name);
|
|
11657
|
+
const before = existsSync41(filePath);
|
|
11658
|
+
reclaimStaleSlot(filePath, staleMs);
|
|
11659
|
+
if (before && !existsSync41(filePath)) reclaimed += 1;
|
|
11660
|
+
}
|
|
11661
|
+
return reclaimed;
|
|
11662
|
+
}
|
|
11663
|
+
function listActiveHeavyVerificationSlots(opts = {}) {
|
|
11664
|
+
const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
|
|
11665
|
+
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
11666
|
+
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
11667
|
+
const active = [];
|
|
11668
|
+
for (const name of readdirSync15(slotsDir)) {
|
|
11669
|
+
if (!name.endsWith(".json")) continue;
|
|
11670
|
+
const record = readSlotRecord(path58.join(slotsDir, name));
|
|
11671
|
+
if (record && !slotIsStale(record, staleMs)) active.push(record);
|
|
11672
|
+
}
|
|
11673
|
+
return active;
|
|
11674
|
+
}
|
|
11675
|
+
function countActiveHeavyVerificationSlots(opts = {}) {
|
|
11676
|
+
return listActiveHeavyVerificationSlots(opts).length;
|
|
11677
|
+
}
|
|
11678
|
+
function tryAcquireHeavyVerificationSlot(command, opts = {}) {
|
|
11679
|
+
if (isHeavyVerificationGateSkipped()) {
|
|
11680
|
+
return {
|
|
11681
|
+
admitted: true,
|
|
11682
|
+
slotId: null,
|
|
11683
|
+
activeSlots: 0,
|
|
11684
|
+
maxSlots: resolveHeavyVerificationMaxConcurrent(),
|
|
11685
|
+
reason: null
|
|
11686
|
+
};
|
|
11687
|
+
}
|
|
11688
|
+
const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
|
|
11689
|
+
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
11690
|
+
const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
|
|
11691
|
+
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
11692
|
+
for (let index = 0; index < maxSlots; index += 1) {
|
|
11693
|
+
const slotId = indexedSlotId(index);
|
|
11694
|
+
const filePath = slotFilePath(slotId, slotsDir);
|
|
11695
|
+
const existing = readSlotRecord(filePath);
|
|
11696
|
+
if (existing && slotIsStale(existing, staleMs)) {
|
|
11697
|
+
try {
|
|
11698
|
+
unlinkSync4(filePath);
|
|
11699
|
+
} catch {
|
|
11700
|
+
}
|
|
11701
|
+
} else if (existing && !slotIsStale(existing, staleMs)) {
|
|
11702
|
+
continue;
|
|
11703
|
+
}
|
|
11704
|
+
const record = {
|
|
11705
|
+
slotId,
|
|
11706
|
+
pid: process.pid,
|
|
11707
|
+
acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11708
|
+
command
|
|
11709
|
+
};
|
|
11710
|
+
try {
|
|
11711
|
+
const fd = openSync7(filePath, "wx");
|
|
11712
|
+
writeFileSync5(fd, JSON.stringify(record, null, 2), "utf8");
|
|
11713
|
+
closeSync7(fd);
|
|
11714
|
+
const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
11715
|
+
return {
|
|
11716
|
+
admitted: true,
|
|
11717
|
+
slotId,
|
|
11718
|
+
activeSlots: activeSlots2,
|
|
11719
|
+
maxSlots,
|
|
11720
|
+
reason: null
|
|
11721
|
+
};
|
|
11722
|
+
} catch (err) {
|
|
11723
|
+
if (err.code === "EEXIST") {
|
|
11724
|
+
continue;
|
|
11725
|
+
}
|
|
11726
|
+
throw err;
|
|
11727
|
+
}
|
|
11728
|
+
}
|
|
11729
|
+
const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
11730
|
+
return {
|
|
11731
|
+
admitted: false,
|
|
11732
|
+
slotId: null,
|
|
11733
|
+
activeSlots,
|
|
11734
|
+
maxSlots,
|
|
11735
|
+
reason: `heavy verification at capacity (${activeSlots}/${maxSlots} slots)`
|
|
11736
|
+
};
|
|
11737
|
+
}
|
|
11738
|
+
function releaseHeavyVerificationSlot(slotId, opts = {}) {
|
|
11739
|
+
if (!slotId) return;
|
|
11740
|
+
const filePath = slotFilePath(slotId, opts.slotsDir ?? heavyVerificationSlotsDir());
|
|
11741
|
+
try {
|
|
11742
|
+
unlinkSync4(filePath);
|
|
11743
|
+
} catch {
|
|
11744
|
+
}
|
|
11745
|
+
}
|
|
11746
|
+
function assessHeavyVerificationGate(command, opts = {}) {
|
|
11747
|
+
if (isHeavyVerificationGateSkipped()) {
|
|
11748
|
+
return {
|
|
11749
|
+
admitted: true,
|
|
11750
|
+
slotId: null,
|
|
11751
|
+
activeSlots: 0,
|
|
11752
|
+
maxSlots: resolveHeavyVerificationMaxConcurrent(),
|
|
11753
|
+
reason: null
|
|
11754
|
+
};
|
|
11755
|
+
}
|
|
11756
|
+
const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
|
|
11757
|
+
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
11758
|
+
const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
|
|
11759
|
+
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
11760
|
+
const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
11761
|
+
const admitted = activeSlots < maxSlots;
|
|
11762
|
+
return {
|
|
11763
|
+
admitted,
|
|
11764
|
+
slotId: null,
|
|
11765
|
+
activeSlots,
|
|
11766
|
+
maxSlots,
|
|
11767
|
+
reason: admitted ? null : `heavy verification at capacity (${activeSlots}/${maxSlots} slots); waiting for ${command}`
|
|
11768
|
+
};
|
|
11769
|
+
}
|
|
11770
|
+
|
|
11771
|
+
// src/heavy-verification/gate.ts
|
|
11289
11772
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
11773
|
+
function sleepMs3(ms) {
|
|
11774
|
+
if (ms <= 0) return;
|
|
11775
|
+
spawnSync7(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
11776
|
+
stdio: "ignore"
|
|
11777
|
+
});
|
|
11778
|
+
}
|
|
11779
|
+
function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {}) {
|
|
11780
|
+
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
11781
|
+
let verdict = tryAcquireHeavyVerificationSlot(command, opts);
|
|
11782
|
+
while (!verdict.admitted && Date.now() < deadline) {
|
|
11783
|
+
sleepMs3(Math.min(pollMs, deadline - Date.now()));
|
|
11784
|
+
verdict = tryAcquireHeavyVerificationSlot(command, opts);
|
|
11785
|
+
}
|
|
11786
|
+
return verdict;
|
|
11787
|
+
}
|
|
11290
11788
|
|
|
11291
11789
|
// src/harness-worktree-build-guard.ts
|
|
11292
|
-
import
|
|
11790
|
+
import path59 from "node:path";
|
|
11293
11791
|
function isPathUnderHarnessWorktree(cwd) {
|
|
11294
11792
|
const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
|
|
11295
|
-
const rel =
|
|
11296
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
11793
|
+
const rel = path59.relative(worktreesDir, path59.resolve(cwd));
|
|
11794
|
+
return rel.length > 0 && !rel.startsWith("..") && !path59.isAbsolute(rel);
|
|
11297
11795
|
}
|
|
11298
11796
|
function assessHarnessWorktreeBuildGuard(cwd) {
|
|
11299
11797
|
if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
|
|
@@ -11317,7 +11815,7 @@ function envArgv(env) {
|
|
|
11317
11815
|
return out;
|
|
11318
11816
|
}
|
|
11319
11817
|
function runSpawn(argv, opts) {
|
|
11320
|
-
const res =
|
|
11818
|
+
const res = spawnSync8(argv[0], argv.slice(1), {
|
|
11321
11819
|
cwd: opts.cwd,
|
|
11322
11820
|
env: opts.env,
|
|
11323
11821
|
encoding: "utf8",
|
|
@@ -11333,8 +11831,25 @@ function runSpawn(argv, opts) {
|
|
|
11333
11831
|
}
|
|
11334
11832
|
function runBoundedBuildCheck(input) {
|
|
11335
11833
|
const waitMs = input.waitForAdmissionMs ?? 6e5;
|
|
11834
|
+
const verificationGate = waitMs > 0 ? waitForHeavyVerificationSlot(input.command, waitMs) : tryAcquireOrAssessVerificationGate(input.command);
|
|
11835
|
+
if (!verificationGate.admitted) {
|
|
11836
|
+
return {
|
|
11837
|
+
ok: false,
|
|
11838
|
+
exitCode: 1,
|
|
11839
|
+
stdout: "",
|
|
11840
|
+
stderr: verificationGate.reason ?? "heavy verification gate denied",
|
|
11841
|
+
admitted: false,
|
|
11842
|
+
wrappedWithSystemd: false,
|
|
11843
|
+
nodeOptionsFlag: formatNodeOptionsFlag(),
|
|
11844
|
+
admission: assessBuildAdmission(),
|
|
11845
|
+
verificationGate,
|
|
11846
|
+
command: input.command
|
|
11847
|
+
};
|
|
11848
|
+
}
|
|
11849
|
+
const slotId = verificationGate.slotId;
|
|
11336
11850
|
const admission = waitMs > 0 ? waitForBuildAdmission(waitMs) : assessBuildAdmission();
|
|
11337
11851
|
if (!admission.admitted) {
|
|
11852
|
+
releaseHeavyVerificationSlot(slotId);
|
|
11338
11853
|
return {
|
|
11339
11854
|
ok: false,
|
|
11340
11855
|
exitCode: 1,
|
|
@@ -11344,11 +11859,13 @@ function runBoundedBuildCheck(input) {
|
|
|
11344
11859
|
wrappedWithSystemd: false,
|
|
11345
11860
|
nodeOptionsFlag: formatNodeOptionsFlag(),
|
|
11346
11861
|
admission,
|
|
11862
|
+
verificationGate,
|
|
11347
11863
|
command: input.command
|
|
11348
11864
|
};
|
|
11349
11865
|
}
|
|
11350
11866
|
const worktreeGuard = assessHarnessWorktreeBuildGuard(input.cwd);
|
|
11351
11867
|
if (!worktreeGuard.ok) {
|
|
11868
|
+
releaseHeavyVerificationSlot(slotId);
|
|
11352
11869
|
return {
|
|
11353
11870
|
ok: false,
|
|
11354
11871
|
exitCode: 1,
|
|
@@ -11358,6 +11875,7 @@ function runBoundedBuildCheck(input) {
|
|
|
11358
11875
|
wrappedWithSystemd: false,
|
|
11359
11876
|
nodeOptionsFlag: formatNodeOptionsFlag(),
|
|
11360
11877
|
admission,
|
|
11878
|
+
verificationGate,
|
|
11361
11879
|
command: input.command
|
|
11362
11880
|
};
|
|
11363
11881
|
}
|
|
@@ -11390,12 +11908,19 @@ function runBoundedBuildCheck(input) {
|
|
|
11390
11908
|
wrappedWithSystemd: useSystemd,
|
|
11391
11909
|
nodeOptionsFlag,
|
|
11392
11910
|
admission,
|
|
11911
|
+
verificationGate,
|
|
11393
11912
|
command: input.command
|
|
11394
11913
|
};
|
|
11395
11914
|
} finally {
|
|
11396
11915
|
registerBuildEnd();
|
|
11916
|
+
releaseHeavyVerificationSlot(slotId);
|
|
11397
11917
|
}
|
|
11398
11918
|
}
|
|
11919
|
+
function tryAcquireOrAssessVerificationGate(command) {
|
|
11920
|
+
const acquired = waitForHeavyVerificationSlot(command, 0);
|
|
11921
|
+
if (acquired.admitted) return acquired;
|
|
11922
|
+
return { ...assessHeavyVerificationGate(command), slotId: null };
|
|
11923
|
+
}
|
|
11399
11924
|
|
|
11400
11925
|
// src/harness-verify.ts
|
|
11401
11926
|
var DEFAULT_HARNESS_VERIFY_COMMANDS = ["npm run typecheck", "npm run test"];
|
|
@@ -11478,7 +12003,7 @@ async function emitPlanProgress(args) {
|
|
|
11478
12003
|
}
|
|
11479
12004
|
function verifyPlanLocal(args) {
|
|
11480
12005
|
const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
|
|
11481
|
-
const cwd =
|
|
12006
|
+
const cwd = path60.resolve(worktree);
|
|
11482
12007
|
const summary = runHarnessVerifyCommands(cwd);
|
|
11483
12008
|
const emitJson = args.json === true || args.json === "true";
|
|
11484
12009
|
const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
|
|
@@ -11527,9 +12052,9 @@ async function verifyPlan(args) {
|
|
|
11527
12052
|
}
|
|
11528
12053
|
|
|
11529
12054
|
// src/harness-verify-cli.ts
|
|
11530
|
-
import
|
|
12055
|
+
import path61 from "node:path";
|
|
11531
12056
|
function runHarnessVerifyCli(args) {
|
|
11532
|
-
const cwd =
|
|
12057
|
+
const cwd = path61.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
|
|
11533
12058
|
const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
|
|
11534
12059
|
const commands = [];
|
|
11535
12060
|
const rawCmd = args.command;
|
|
@@ -11557,6 +12082,7 @@ function runHarnessVerifyCli(args) {
|
|
|
11557
12082
|
wrappedWithSystemd: s.result.wrappedWithSystemd,
|
|
11558
12083
|
nodeOptionsFlag: s.result.nodeOptionsFlag,
|
|
11559
12084
|
admission: s.result.admission,
|
|
12085
|
+
verificationGate: s.result.verificationGate,
|
|
11560
12086
|
stderr: s.result.stderr.slice(0, 4e3)
|
|
11561
12087
|
}))
|
|
11562
12088
|
};
|
|
@@ -11573,7 +12099,7 @@ function runHarnessVerifyCli(args) {
|
|
|
11573
12099
|
}
|
|
11574
12100
|
|
|
11575
12101
|
// src/plan-persist-cli.ts
|
|
11576
|
-
import { readFileSync as
|
|
12102
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
11577
12103
|
var OPERATIONS = ["create", "add_version", "update_metadata"];
|
|
11578
12104
|
var FAILURE_KINDS = [
|
|
11579
12105
|
"approval_guard",
|
|
@@ -11585,7 +12111,7 @@ var FAILURE_KINDS = [
|
|
|
11585
12111
|
function readBodyArg(args) {
|
|
11586
12112
|
const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
|
|
11587
12113
|
if (bodyFile) {
|
|
11588
|
-
return { body:
|
|
12114
|
+
return { body: readFileSync15(bodyFile, "utf8"), bodyPathHint: bodyFile };
|
|
11589
12115
|
}
|
|
11590
12116
|
const inline = args.body ? String(args.body) : void 0;
|
|
11591
12117
|
if (inline) return { body: inline };
|
|
@@ -11736,7 +12262,7 @@ function formatMonitorTickNotice(tick) {
|
|
|
11736
12262
|
}
|
|
11737
12263
|
|
|
11738
12264
|
// src/monitor/monitor.service.ts
|
|
11739
|
-
import
|
|
12265
|
+
import path63 from "node:path";
|
|
11740
12266
|
|
|
11741
12267
|
// src/monitor/monitor.classify.ts
|
|
11742
12268
|
function classifyWorkerHealth(input) {
|
|
@@ -11788,19 +12314,19 @@ function classifyWorkerHealth(input) {
|
|
|
11788
12314
|
}
|
|
11789
12315
|
|
|
11790
12316
|
// src/monitor/monitor.store.ts
|
|
11791
|
-
import { existsSync as
|
|
11792
|
-
import
|
|
12317
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
|
|
12318
|
+
import path62 from "node:path";
|
|
11793
12319
|
function monitorsDir() {
|
|
11794
12320
|
const { harnessRoot } = getHarnessPaths();
|
|
11795
|
-
const dir =
|
|
11796
|
-
|
|
12321
|
+
const dir = path62.join(harnessRoot, "monitors");
|
|
12322
|
+
mkdirSync9(dir, { recursive: true });
|
|
11797
12323
|
return dir;
|
|
11798
12324
|
}
|
|
11799
12325
|
function monitorIdFor(runId, workerName) {
|
|
11800
12326
|
return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
|
|
11801
12327
|
}
|
|
11802
12328
|
function monitorPath(monitorId) {
|
|
11803
|
-
return
|
|
12329
|
+
return path62.join(monitorsDir(), `${monitorId}.json`);
|
|
11804
12330
|
}
|
|
11805
12331
|
function loadMonitorSession(monitorId) {
|
|
11806
12332
|
return readJson(monitorPath(monitorId), void 0);
|
|
@@ -11810,18 +12336,18 @@ function saveMonitorSession(session) {
|
|
|
11810
12336
|
}
|
|
11811
12337
|
function deleteMonitorSession(monitorId) {
|
|
11812
12338
|
const file = monitorPath(monitorId);
|
|
11813
|
-
if (!
|
|
11814
|
-
|
|
12339
|
+
if (!existsSync42(file)) return false;
|
|
12340
|
+
unlinkSync5(file);
|
|
11815
12341
|
return true;
|
|
11816
12342
|
}
|
|
11817
12343
|
function listMonitorSessions() {
|
|
11818
12344
|
const dir = monitorsDir();
|
|
11819
|
-
if (!
|
|
12345
|
+
if (!existsSync42(dir)) return [];
|
|
11820
12346
|
const entries = [];
|
|
11821
|
-
for (const name of
|
|
12347
|
+
for (const name of readdirSync16(dir)) {
|
|
11822
12348
|
if (!name.endsWith(".json")) continue;
|
|
11823
12349
|
const session = readJson(
|
|
11824
|
-
|
|
12350
|
+
path62.join(dir, name),
|
|
11825
12351
|
void 0
|
|
11826
12352
|
);
|
|
11827
12353
|
if (!session?.monitorId) continue;
|
|
@@ -11912,7 +12438,7 @@ async function fetchTaskLeasesForWorkers(input) {
|
|
|
11912
12438
|
// src/monitor/monitor.service.ts
|
|
11913
12439
|
function workerRecord2(runId, name) {
|
|
11914
12440
|
return readJson(
|
|
11915
|
-
|
|
12441
|
+
path63.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
|
|
11916
12442
|
void 0
|
|
11917
12443
|
);
|
|
11918
12444
|
}
|
|
@@ -12118,21 +12644,21 @@ async function runMonitorLoop(args) {
|
|
|
12118
12644
|
|
|
12119
12645
|
// src/monitor/monitor-spawn.ts
|
|
12120
12646
|
import { spawn as spawn6 } from "node:child_process";
|
|
12121
|
-
import { closeSync as
|
|
12122
|
-
import
|
|
12647
|
+
import { closeSync as closeSync8, existsSync as existsSync43, openSync as openSync8 } from "node:fs";
|
|
12648
|
+
import path64 from "node:path";
|
|
12123
12649
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12124
12650
|
function resolveDefaultCliPath2() {
|
|
12125
|
-
return
|
|
12651
|
+
return path64.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
|
|
12126
12652
|
}
|
|
12127
12653
|
function spawnMonitorSidecar(opts) {
|
|
12128
12654
|
const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
|
|
12129
|
-
if (!
|
|
12655
|
+
if (!existsSync43(cliPath)) return void 0;
|
|
12130
12656
|
const monitorId = monitorIdFor(opts.runId, opts.workerName);
|
|
12131
12657
|
const { harnessRoot } = getHarnessPaths();
|
|
12132
|
-
const logPath =
|
|
12658
|
+
const logPath = path64.join(harnessRoot, "monitors", `${monitorId}.log`);
|
|
12133
12659
|
let logFd;
|
|
12134
12660
|
try {
|
|
12135
|
-
logFd =
|
|
12661
|
+
logFd = openSync8(logPath, "a");
|
|
12136
12662
|
} catch {
|
|
12137
12663
|
logFd = void 0;
|
|
12138
12664
|
}
|
|
@@ -12172,7 +12698,7 @@ function spawnMonitorSidecar(opts) {
|
|
|
12172
12698
|
env: process.env
|
|
12173
12699
|
})
|
|
12174
12700
|
);
|
|
12175
|
-
if (logFd !== void 0)
|
|
12701
|
+
if (logFd !== void 0) closeSync8(logFd);
|
|
12176
12702
|
child.unref();
|
|
12177
12703
|
const session = {
|
|
12178
12704
|
monitorId,
|
|
@@ -12189,7 +12715,7 @@ function spawnMonitorSidecar(opts) {
|
|
|
12189
12715
|
} catch {
|
|
12190
12716
|
if (logFd !== void 0) {
|
|
12191
12717
|
try {
|
|
12192
|
-
|
|
12718
|
+
closeSync8(logFd);
|
|
12193
12719
|
} catch {
|
|
12194
12720
|
}
|
|
12195
12721
|
}
|
|
@@ -12249,13 +12775,13 @@ async function monitorTickCli(args) {
|
|
|
12249
12775
|
}
|
|
12250
12776
|
|
|
12251
12777
|
// src/package-version.ts
|
|
12252
|
-
import { existsSync as
|
|
12778
|
+
import { existsSync as existsSync44, readFileSync as readFileSync16 } from "node:fs";
|
|
12253
12779
|
import { dirname, join } from "node:path";
|
|
12254
12780
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
12255
12781
|
function resolvePackageRoot(moduleUrl) {
|
|
12256
12782
|
let dir = dirname(fileURLToPath4(moduleUrl));
|
|
12257
12783
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
12258
|
-
if (
|
|
12784
|
+
if (existsSync44(join(dir, "package.json"))) return dir;
|
|
12259
12785
|
const parent = dirname(dir);
|
|
12260
12786
|
if (parent === dir) break;
|
|
12261
12787
|
dir = parent;
|
|
@@ -12264,7 +12790,7 @@ function resolvePackageRoot(moduleUrl) {
|
|
|
12264
12790
|
}
|
|
12265
12791
|
function readOwnPackageVersion(moduleUrl = import.meta.url) {
|
|
12266
12792
|
const pkgPath = join(resolvePackageRoot(moduleUrl), "package.json");
|
|
12267
|
-
const pkg = JSON.parse(
|
|
12793
|
+
const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
|
|
12268
12794
|
if (typeof pkg.version !== "string" || !pkg.version.trim()) {
|
|
12269
12795
|
throw new Error(`Missing package.json version at ${pkgPath}`);
|
|
12270
12796
|
}
|
|
@@ -12330,8 +12856,32 @@ function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
|
|
|
12330
12856
|
return false;
|
|
12331
12857
|
}
|
|
12332
12858
|
|
|
12859
|
+
// src/run-resolve.ts
|
|
12860
|
+
function resolveHarnessRunByName(runName) {
|
|
12861
|
+
const name = runName.trim();
|
|
12862
|
+
if (!name) return null;
|
|
12863
|
+
const rows = buildRunListRows();
|
|
12864
|
+
return rows.find((row) => row.name === name && row.status !== "completed") ?? null;
|
|
12865
|
+
}
|
|
12866
|
+
function resolveHarnessRunCli(args) {
|
|
12867
|
+
const name = String(required(String(args.name || ""), "--name"));
|
|
12868
|
+
const hit = resolveHarnessRunByName(name);
|
|
12869
|
+
console.log(
|
|
12870
|
+
JSON.stringify(
|
|
12871
|
+
{
|
|
12872
|
+
runId: hit?.id ?? null,
|
|
12873
|
+
name,
|
|
12874
|
+
status: hit?.status ?? null,
|
|
12875
|
+
effectiveStatus: hit?.effectiveStatus ?? null
|
|
12876
|
+
},
|
|
12877
|
+
null,
|
|
12878
|
+
2
|
|
12879
|
+
)
|
|
12880
|
+
);
|
|
12881
|
+
}
|
|
12882
|
+
|
|
12333
12883
|
// src/post-restart-unblock.ts
|
|
12334
|
-
import
|
|
12884
|
+
import path65 from "node:path";
|
|
12335
12885
|
function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
|
|
12336
12886
|
return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
|
|
12337
12887
|
}
|
|
@@ -12344,7 +12894,7 @@ async function postRestartUnblock(args) {
|
|
|
12344
12894
|
const errors = [];
|
|
12345
12895
|
for (const run of listRunRecords()) {
|
|
12346
12896
|
for (const name of Object.keys(run.workers ?? {})) {
|
|
12347
|
-
const workerPath =
|
|
12897
|
+
const workerPath = path65.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
12348
12898
|
const worker = readJson(workerPath, void 0);
|
|
12349
12899
|
if (!worker) {
|
|
12350
12900
|
skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
|
|
@@ -12456,9 +13006,9 @@ async function postRestartUnblockCli(args) {
|
|
|
12456
13006
|
}
|
|
12457
13007
|
|
|
12458
13008
|
// src/default-repo-cli.ts
|
|
12459
|
-
import
|
|
13009
|
+
import path66 from "node:path";
|
|
12460
13010
|
import { homedir as homedir14 } from "node:os";
|
|
12461
|
-
var CONFIG_FILE2 =
|
|
13011
|
+
var CONFIG_FILE2 = path66.join(homedir14(), ".kynver", "config.json");
|
|
12462
13012
|
function ensureDefaultRepo(opts) {
|
|
12463
13013
|
const existing = loadUserConfig();
|
|
12464
13014
|
const resolved = resolveDefaultRepo({ ...opts, config: existing });
|
|
@@ -12539,16 +13089,16 @@ function summarizeResolvedDefaultRepo(resolved) {
|
|
|
12539
13089
|
}
|
|
12540
13090
|
|
|
12541
13091
|
// src/doctor/runtime-takeover.ts
|
|
12542
|
-
import
|
|
13092
|
+
import path68 from "node:path";
|
|
12543
13093
|
|
|
12544
13094
|
// src/doctor/runtime-takeover.probes.ts
|
|
12545
|
-
import { accessSync, constants, existsSync as
|
|
13095
|
+
import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync17 } from "node:fs";
|
|
12546
13096
|
import { homedir as homedir15 } from "node:os";
|
|
12547
|
-
import
|
|
12548
|
-
import { spawnSync as
|
|
13097
|
+
import path67 from "node:path";
|
|
13098
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12549
13099
|
function captureCommand(bin, args) {
|
|
12550
13100
|
try {
|
|
12551
|
-
const res =
|
|
13101
|
+
const res = spawnSync9(bin, args, { encoding: "utf8" });
|
|
12552
13102
|
const stdout = (res.stdout || "").trim();
|
|
12553
13103
|
const stderr = (res.stderr || "").trim();
|
|
12554
13104
|
const ok = res.status === 0;
|
|
@@ -12573,7 +13123,7 @@ function tokenPrefix(token) {
|
|
|
12573
13123
|
return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
|
|
12574
13124
|
}
|
|
12575
13125
|
function isWritable(target) {
|
|
12576
|
-
if (!
|
|
13126
|
+
if (!existsSync45(target)) return false;
|
|
12577
13127
|
try {
|
|
12578
13128
|
accessSync(target, constants.W_OK);
|
|
12579
13129
|
return true;
|
|
@@ -12586,15 +13136,15 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
12586
13136
|
commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
|
|
12587
13137
|
kynverVersion: (bin) => captureCommand(bin, ["--version"]),
|
|
12588
13138
|
loadConfig: () => loadUserConfig(),
|
|
12589
|
-
configFilePath: () =>
|
|
12590
|
-
credentialsFilePath: () =>
|
|
13139
|
+
configFilePath: () => path67.join(homedir15(), ".kynver", "config.json"),
|
|
13140
|
+
credentialsFilePath: () => path67.join(homedir15(), ".kynver", "credentials"),
|
|
12591
13141
|
readCredentials: () => {
|
|
12592
|
-
const credPath =
|
|
12593
|
-
if (!
|
|
13142
|
+
const credPath = path67.join(homedir15(), ".kynver", "credentials");
|
|
13143
|
+
if (!existsSync45(credPath)) {
|
|
12594
13144
|
return { hasApiKey: false };
|
|
12595
13145
|
}
|
|
12596
13146
|
try {
|
|
12597
|
-
const parsed = JSON.parse(
|
|
13147
|
+
const parsed = JSON.parse(readFileSync17(credPath, "utf8"));
|
|
12598
13148
|
return {
|
|
12599
13149
|
hasApiKey: Boolean(parsed.apiKey?.trim()),
|
|
12600
13150
|
runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
|
|
@@ -12624,8 +13174,8 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
12624
13174
|
})()
|
|
12625
13175
|
}),
|
|
12626
13176
|
harnessRoot: () => resolveHarnessRoot(),
|
|
12627
|
-
legacyOpenclawHarnessRoot: () =>
|
|
12628
|
-
pathExists: (target) =>
|
|
13177
|
+
legacyOpenclawHarnessRoot: () => path67.join(homedir15(), ".openclaw", "harness"),
|
|
13178
|
+
pathExists: (target) => existsSync45(target),
|
|
12629
13179
|
pathWritable: (target) => isWritable(target)
|
|
12630
13180
|
};
|
|
12631
13181
|
|
|
@@ -13031,8 +13581,8 @@ function assessVercelDeployEvidence(probes) {
|
|
|
13031
13581
|
}
|
|
13032
13582
|
function assessHarnessDirs(probes) {
|
|
13033
13583
|
const harnessRoot = probes.harnessRoot();
|
|
13034
|
-
const runsDir =
|
|
13035
|
-
const worktreesDir =
|
|
13584
|
+
const runsDir = path68.join(harnessRoot, "runs");
|
|
13585
|
+
const worktreesDir = path68.join(harnessRoot, "worktrees");
|
|
13036
13586
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
13037
13587
|
const displayRunsDir = redactHomePath(runsDir);
|
|
13038
13588
|
const displayWorktreesDir = redactHomePath(worktreesDir);
|
|
@@ -13296,9 +13846,9 @@ function applySchedulerCutoverAttestation(config) {
|
|
|
13296
13846
|
}
|
|
13297
13847
|
|
|
13298
13848
|
// src/scheduler-cutover-cli.ts
|
|
13299
|
-
import
|
|
13849
|
+
import path69 from "node:path";
|
|
13300
13850
|
import { homedir as homedir16 } from "node:os";
|
|
13301
|
-
var CONFIG_FILE3 =
|
|
13851
|
+
var CONFIG_FILE3 = path69.join(homedir16(), ".kynver", "config.json");
|
|
13302
13852
|
function runSchedulerCutoverCheckCli(json = false) {
|
|
13303
13853
|
const config = loadUserConfig();
|
|
13304
13854
|
const report = assessSchedulerCutover(config);
|
|
@@ -13435,6 +13985,157 @@ async function runCronTickCli(args) {
|
|
|
13435
13985
|
);
|
|
13436
13986
|
}
|
|
13437
13987
|
|
|
13988
|
+
// src/lane/landing-maintainer-tick.ts
|
|
13989
|
+
import os9 from "node:os";
|
|
13990
|
+
|
|
13991
|
+
// src/lane/lane-spec.ts
|
|
13992
|
+
var LANDING_MAINTAINER_LANE_SPEC = {
|
|
13993
|
+
slug: "landing-maintainer",
|
|
13994
|
+
originCron: "maintain-8-blocker-and-pr-landing-workers",
|
|
13995
|
+
defaultRepo: "Totalsolutionsync/Kynver",
|
|
13996
|
+
landScript: "scripts/agent-os-land-pr.mjs",
|
|
13997
|
+
landScriptArgs: ["--skip-not-ready"]
|
|
13998
|
+
};
|
|
13999
|
+
|
|
14000
|
+
// src/lane/landing-maintainer-local.ts
|
|
14001
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
14002
|
+
import path70 from "node:path";
|
|
14003
|
+
function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
14004
|
+
const script = path70.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
|
|
14005
|
+
const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
|
|
14006
|
+
if (!execute) {
|
|
14007
|
+
return {
|
|
14008
|
+
action: { kind: "land_pr", prNumber, reason: "dry-run" },
|
|
14009
|
+
executed: false,
|
|
14010
|
+
exitCode: 0,
|
|
14011
|
+
stdout: `dry-run: node ${args.join(" ")}`,
|
|
14012
|
+
stderr: ""
|
|
14013
|
+
};
|
|
14014
|
+
}
|
|
14015
|
+
const result = spawnSync10("node", args, {
|
|
14016
|
+
cwd: repoRoot,
|
|
14017
|
+
encoding: "utf8",
|
|
14018
|
+
timeout: 10 * 60 * 1e3
|
|
14019
|
+
});
|
|
14020
|
+
return {
|
|
14021
|
+
action: { kind: "land_pr", prNumber, reason: "landing wrapper invoked" },
|
|
14022
|
+
executed: true,
|
|
14023
|
+
exitCode: result.status,
|
|
14024
|
+
stdout: result.stdout ?? "",
|
|
14025
|
+
stderr: result.stderr ?? ""
|
|
14026
|
+
};
|
|
14027
|
+
}
|
|
14028
|
+
function resolveLandingMaintainerRepoRoot(args) {
|
|
14029
|
+
const explicit = args.repoPath ? String(args.repoPath).trim() : "";
|
|
14030
|
+
if (explicit) return path70.resolve(explicit);
|
|
14031
|
+
const resolved = resolveDefaultRepo();
|
|
14032
|
+
return resolved?.repo ?? process.cwd();
|
|
14033
|
+
}
|
|
14034
|
+
|
|
14035
|
+
// src/lane/landing-maintainer-tick.ts
|
|
14036
|
+
async function runLandingMaintainerLaneTick(args) {
|
|
14037
|
+
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
14038
|
+
const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
|
|
14039
|
+
const fleet = args.fleet === true || args.fleet === "true";
|
|
14040
|
+
const execute = args.execute !== false && args.execute !== "false";
|
|
14041
|
+
const runId = args.run ? String(args.run) : void 0;
|
|
14042
|
+
const resourceGate = observeRunnerResourceGate({
|
|
14043
|
+
runId: runId ?? "fleet-lane-tick"
|
|
14044
|
+
});
|
|
14045
|
+
const boxCapacity = {
|
|
14046
|
+
...buildBoxResourceSnapshotFromGate(resourceGate, {
|
|
14047
|
+
harnessRunId: runId,
|
|
14048
|
+
boxKind: resolveBoxKindFromConfig(loadUserConfig()),
|
|
14049
|
+
hostLabel: os9.hostname()
|
|
14050
|
+
}),
|
|
14051
|
+
providerHealthy: resourceGate.ok,
|
|
14052
|
+
authorizedForRepair: resourceGate.ok,
|
|
14053
|
+
authorizedForLanding: resourceGate.ok,
|
|
14054
|
+
systemHealthBlockers: resourceGate.ok ? [] : [resourceGate.reason ?? "resource_gate_blocked"],
|
|
14055
|
+
actionableWorkers: resourceGate.activeWorkers
|
|
14056
|
+
};
|
|
14057
|
+
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
14058
|
+
const secret = await resolveCallbackSecretWithMint(
|
|
14059
|
+
args.secret ? String(args.secret) : void 0,
|
|
14060
|
+
agentOsId,
|
|
14061
|
+
{ baseUrl: base }
|
|
14062
|
+
);
|
|
14063
|
+
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/tick`;
|
|
14064
|
+
const res = await postJson(url, secret, {
|
|
14065
|
+
repo: repoSlug,
|
|
14066
|
+
fleet,
|
|
14067
|
+
execute,
|
|
14068
|
+
runId,
|
|
14069
|
+
boxCapacity
|
|
14070
|
+
});
|
|
14071
|
+
const coordinator = res.response;
|
|
14072
|
+
const localOutcomes = [];
|
|
14073
|
+
const repoRoot = resolveLandingMaintainerRepoRoot(args);
|
|
14074
|
+
const actions = Array.isArray(coordinator?.localActions) ? coordinator.localActions : [];
|
|
14075
|
+
const holderBoxId = boxCapacity.boxId;
|
|
14076
|
+
for (const action of actions) {
|
|
14077
|
+
if (action.kind === "land_pr" && typeof action.prNumber === "number") {
|
|
14078
|
+
const outcome = runLandingWrapper(action.prNumber, repoRoot, execute);
|
|
14079
|
+
localOutcomes.push({
|
|
14080
|
+
action: outcome.action,
|
|
14081
|
+
executed: outcome.executed,
|
|
14082
|
+
exitCode: outcome.exitCode
|
|
14083
|
+
});
|
|
14084
|
+
if (execute && outcome.executed) {
|
|
14085
|
+
const merged = outcome.exitCode === 0;
|
|
14086
|
+
const prUrl = typeof action.prUrl === "string" && action.prUrl.trim() ? action.prUrl.trim() : `https://github.com/${repoSlug}/pull/${action.prNumber}`;
|
|
14087
|
+
try {
|
|
14088
|
+
await postJson(
|
|
14089
|
+
`${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/outcome`,
|
|
14090
|
+
secret,
|
|
14091
|
+
{ repo: repoSlug, prUrl, holderBoxId, merged }
|
|
14092
|
+
);
|
|
14093
|
+
} catch {
|
|
14094
|
+
}
|
|
14095
|
+
}
|
|
14096
|
+
continue;
|
|
14097
|
+
}
|
|
14098
|
+
localOutcomes.push({
|
|
14099
|
+
action,
|
|
14100
|
+
executed: false,
|
|
14101
|
+
exitCode: null
|
|
14102
|
+
});
|
|
14103
|
+
}
|
|
14104
|
+
return {
|
|
14105
|
+
repo: repoSlug,
|
|
14106
|
+
fleet,
|
|
14107
|
+
execute,
|
|
14108
|
+
coordinator,
|
|
14109
|
+
localOutcomes
|
|
14110
|
+
};
|
|
14111
|
+
}
|
|
14112
|
+
|
|
14113
|
+
// src/lane/lane-tick-cli.ts
|
|
14114
|
+
async function runLaneTickCli(args, laneFromArgv) {
|
|
14115
|
+
const lane = String(laneFromArgv ?? args.lane ?? "").trim();
|
|
14116
|
+
if (lane !== "landing-maintainer") {
|
|
14117
|
+
console.error(`unknown lane: ${lane || "(none)"}`);
|
|
14118
|
+
process.exit(1);
|
|
14119
|
+
}
|
|
14120
|
+
const result = await runLandingMaintainerLaneTick(args);
|
|
14121
|
+
if (args.json === true || args.json === "true") {
|
|
14122
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14123
|
+
return;
|
|
14124
|
+
}
|
|
14125
|
+
console.log(
|
|
14126
|
+
[
|
|
14127
|
+
`fleet landing-maintainer tick`,
|
|
14128
|
+
`repo=${result.repo}`,
|
|
14129
|
+
`fleet=${result.fleet}`,
|
|
14130
|
+
`execute=${result.execute}`,
|
|
14131
|
+
`localOutcomes=${result.localOutcomes.length}`
|
|
14132
|
+
].join(" ")
|
|
14133
|
+
);
|
|
14134
|
+
for (const row of result.localOutcomes) {
|
|
14135
|
+
console.log(` ${row.action.kind} pr=${row.action.prNumber ?? "-"} executed=${row.executed}`);
|
|
14136
|
+
}
|
|
14137
|
+
}
|
|
14138
|
+
|
|
13438
14139
|
// src/cli.ts
|
|
13439
14140
|
function isHelpFlag(arg) {
|
|
13440
14141
|
return arg === "help" || arg === "--help" || arg === "-h";
|
|
@@ -13455,6 +14156,7 @@ function usage(code = 0) {
|
|
|
13455
14156
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
|
|
13456
14157
|
" kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
|
|
13457
14158
|
" kynver run list",
|
|
14159
|
+
" kynver run resolve --name RUN_NAME",
|
|
13458
14160
|
" kynver run status --run RUN_ID [--json] [--compact]",
|
|
13459
14161
|
" 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]",
|
|
13460
14162
|
" kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
|
|
@@ -13490,6 +14192,7 @@ function usage(code = 0) {
|
|
|
13490
14192
|
" kynver scheduler attest-cutover [--json]",
|
|
13491
14193
|
" kynver cron status [--json]",
|
|
13492
14194
|
" kynver cron tick [--agent-os-id AOS_ID] [--json]",
|
|
14195
|
+
" kynver lane tick landing-maintainer [--fleet] [--repo OWNER/NAME] [--agent-os-id AOS_ID] [--execute] [--json]",
|
|
13493
14196
|
" kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
|
|
13494
14197
|
].join("\n")
|
|
13495
14198
|
);
|
|
@@ -13501,7 +14204,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
13501
14204
|
const scope = argv.shift();
|
|
13502
14205
|
let action;
|
|
13503
14206
|
let rest;
|
|
13504
|
-
if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "board") {
|
|
14207
|
+
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") {
|
|
13505
14208
|
action = argv.shift();
|
|
13506
14209
|
rest = argv;
|
|
13507
14210
|
} else {
|
|
@@ -13510,8 +14213,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
13510
14213
|
if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
|
|
13511
14214
|
const args = parseArgs(rest);
|
|
13512
14215
|
const { runsDir, worktreesDir } = getPaths();
|
|
13513
|
-
|
|
13514
|
-
|
|
14216
|
+
mkdirSync10(runsDir, { recursive: true });
|
|
14217
|
+
mkdirSync10(worktreesDir, { recursive: true });
|
|
13515
14218
|
if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
|
|
13516
14219
|
let repoRoot;
|
|
13517
14220
|
const runId = args.run ? String(args.run).trim() : "";
|
|
@@ -13558,11 +14261,16 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
13558
14261
|
if (scope === "cron" && action === "tick") {
|
|
13559
14262
|
return void await runCronTickCli(args);
|
|
13560
14263
|
}
|
|
14264
|
+
if (scope === "lane" && action === "tick") {
|
|
14265
|
+
const laneName = rest.shift();
|
|
14266
|
+
return void await runLaneTickCli(parseArgs(rest), laneName);
|
|
14267
|
+
}
|
|
13561
14268
|
if (scope === "board" && action === "contract") {
|
|
13562
14269
|
return void await runCommandCenterContractCli(args);
|
|
13563
14270
|
}
|
|
13564
14271
|
if (scope === "run" && action === "create") return createRun(args);
|
|
13565
14272
|
if (scope === "run" && action === "list") return listRuns();
|
|
14273
|
+
if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
|
|
13566
14274
|
if (scope === "run" && action === "status") return runStatus(args);
|
|
13567
14275
|
if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
|
|
13568
14276
|
if (scope === "run" && action === "sweep") return void await sweepRun(args);
|