@kynver-app/runtime 0.1.120 → 0.1.123
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/chat/anthropic-credentials.d.ts +3 -1
- package/dist/chat/chat-claim-loop.d.ts +3 -1
- package/dist/cleanup-duplicate-worktrees.d.ts +3 -2
- package/dist/cleanup-git-probe.d.ts +5 -0
- package/dist/cleanup-guards.d.ts +4 -0
- package/dist/cleanup-retention-config.d.ts +2 -0
- package/dist/cleanup-run-liveness.d.ts +3 -6
- package/dist/cleanup-salvage-evidence.d.ts +33 -0
- package/dist/cleanup-types.d.ts +2 -0
- package/dist/cleanup-worker-harness-live.d.ts +9 -0
- package/dist/cleanup-worktree-index.d.ts +2 -0
- package/dist/cli.js +884 -514
- package/dist/cli.js.map +4 -4
- package/dist/config.d.ts +22 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +835 -463
- package/dist/index.js.map +4 -4
- package/dist/run-metadata-retention.d.ts +15 -0
- package/dist/server/cleanup.js +452 -308
- package/dist/server/cleanup.js.map +4 -4
- package/dist/server/default-repo.js.map +2 -2
- package/dist/server/monitor.js.map +2 -2
- package/dist/server/worker-policy.js.map +2 -2
- package/dist/stale-reconcile.d.ts +2 -0
- package/dist/start.d.ts +6 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -523,23 +523,23 @@ function isWslHost() {
|
|
|
523
523
|
function observeWslHostDisk(options = {}) {
|
|
524
524
|
const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
|
|
525
525
|
if (!wsl) return null;
|
|
526
|
-
const
|
|
526
|
+
const path79 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
|
|
527
527
|
const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
|
|
528
528
|
const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
|
|
529
529
|
const statfs = options.statfs ?? statfsSync;
|
|
530
530
|
let stats;
|
|
531
531
|
try {
|
|
532
|
-
stats = statfs(
|
|
532
|
+
stats = statfs(path79);
|
|
533
533
|
} catch (error) {
|
|
534
534
|
return {
|
|
535
535
|
ok: false,
|
|
536
|
-
path:
|
|
536
|
+
path: path79,
|
|
537
537
|
freeBytes: 0,
|
|
538
538
|
totalBytes: 0,
|
|
539
539
|
usedPercent: 100,
|
|
540
540
|
warnBelowBytes,
|
|
541
541
|
criticalBelowBytes,
|
|
542
|
-
reason: `Windows host disk probe failed at ${
|
|
542
|
+
reason: `Windows host disk probe failed at ${path79}: ${error.message}`,
|
|
543
543
|
probeError: error.message
|
|
544
544
|
};
|
|
545
545
|
}
|
|
@@ -553,11 +553,11 @@ function observeWslHostDisk(options = {}) {
|
|
|
553
553
|
let reason = null;
|
|
554
554
|
if (!ok) {
|
|
555
555
|
const tag = criticalFree ? "critical" : "warning";
|
|
556
|
-
reason = `Windows host disk ${
|
|
556
|
+
reason = `Windows host disk ${path79} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
|
|
557
557
|
}
|
|
558
558
|
return {
|
|
559
559
|
ok,
|
|
560
|
-
path:
|
|
560
|
+
path: path79,
|
|
561
561
|
freeBytes,
|
|
562
562
|
totalBytes,
|
|
563
563
|
usedPercent,
|
|
@@ -583,12 +583,12 @@ var init_wsl_host = __esm({
|
|
|
583
583
|
// src/disk-gate.ts
|
|
584
584
|
import { statfsSync as statfsSync2 } from "node:fs";
|
|
585
585
|
function observeRunnerDiskGate(input = {}) {
|
|
586
|
-
const
|
|
586
|
+
const path79 = input.diskPath?.trim() || "/";
|
|
587
587
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
588
588
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
589
589
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
590
590
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
591
|
-
const stats = statfsSync2(
|
|
591
|
+
const stats = statfsSync2(path79);
|
|
592
592
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
593
593
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
594
594
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -611,7 +611,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
611
611
|
}
|
|
612
612
|
return {
|
|
613
613
|
ok,
|
|
614
|
-
path:
|
|
614
|
+
path: path79,
|
|
615
615
|
freeBytes,
|
|
616
616
|
totalBytes,
|
|
617
617
|
usedPercent,
|
|
@@ -2616,23 +2616,29 @@ function resolveConfiguredCallbackSecret(argsSecret, agentOsId) {
|
|
|
2616
2616
|
}
|
|
2617
2617
|
return void 0;
|
|
2618
2618
|
}
|
|
2619
|
-
async function
|
|
2619
|
+
async function tryResolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
|
|
2620
2620
|
const configured = resolveConfiguredCallbackSecret(argsSecret, agentOsId);
|
|
2621
|
-
if (configured) return configured;
|
|
2621
|
+
if (configured) return { ok: true, secret: configured };
|
|
2622
2622
|
const apiKey = loadApiKey();
|
|
2623
2623
|
const baseUrl = resolveConfiguredBaseUrl(opts?.baseUrl);
|
|
2624
2624
|
if (apiKey && agentOsId && baseUrl) {
|
|
2625
2625
|
try {
|
|
2626
2626
|
const token = await fetchRunnerCredential(agentOsId, { baseUrl, apiKey });
|
|
2627
2627
|
saveRunnerToken(agentOsId, token);
|
|
2628
|
-
return token;
|
|
2628
|
+
return { ok: true, secret: token };
|
|
2629
2629
|
} catch (error) {
|
|
2630
|
-
|
|
2630
|
+
return { ok: false, reason: `runner credential mint failed: ${error.message}` };
|
|
2631
2631
|
}
|
|
2632
2632
|
}
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2633
|
+
return {
|
|
2634
|
+
ok: false,
|
|
2635
|
+
reason: "no runner credential: requires --secret, KYNVER_RUNNER_TOKEN, a scoped runner token (`kynver runner credential`), ~/.kynver/credentials runnerToken, or KYNVER_API_KEY + API base URL to mint one"
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2638
|
+
async function resolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
|
|
2639
|
+
const result = await tryResolveCallbackSecretWithMint(argsSecret, agentOsId, opts);
|
|
2640
|
+
if (result.ok) return result.secret;
|
|
2641
|
+
failConfig(result.reason);
|
|
2636
2642
|
}
|
|
2637
2643
|
async function refreshRunnerToken(agentOsId, opts) {
|
|
2638
2644
|
const apiKey = loadApiKey();
|
|
@@ -2752,7 +2758,10 @@ async function runSetup(args) {
|
|
|
2752
2758
|
...existing,
|
|
2753
2759
|
...inferSetupFields(existing, args),
|
|
2754
2760
|
...workerConfig,
|
|
2755
|
-
workerProvider: typeof args.provider === "string" ? args.provider : existing.workerProvider || "cursor"
|
|
2761
|
+
workerProvider: typeof args.provider === "string" ? args.provider : existing.workerProvider || "cursor",
|
|
2762
|
+
// M5.4 chat-turn delegation: `--chat-oauth` persists consent to use the
|
|
2763
|
+
// Claude Code CLI's local OAuth token for delegated chat turns.
|
|
2764
|
+
...args.chatOauth === true ? { chatUseClaudeOauth: true } : {}
|
|
2756
2765
|
});
|
|
2757
2766
|
saveUserConfig(config);
|
|
2758
2767
|
const boxIdentity = resolveBoxIdentity(process.env, config);
|
|
@@ -2820,7 +2829,7 @@ var init_config = __esm({
|
|
|
2820
2829
|
|
|
2821
2830
|
// src/cli.ts
|
|
2822
2831
|
init_config();
|
|
2823
|
-
import { mkdirSync as
|
|
2832
|
+
import { mkdirSync as mkdirSync15, realpathSync } from "node:fs";
|
|
2824
2833
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
2825
2834
|
|
|
2826
2835
|
// src/bootstrap.ts
|
|
@@ -3508,7 +3517,7 @@ async function runKynverCronTick(opts = {}) {
|
|
|
3508
3517
|
init_util();
|
|
3509
3518
|
|
|
3510
3519
|
// src/pipeline-tick.ts
|
|
3511
|
-
import
|
|
3520
|
+
import path57 from "node:path";
|
|
3512
3521
|
|
|
3513
3522
|
// src/active-harness-workers.ts
|
|
3514
3523
|
init_run_store();
|
|
@@ -6176,8 +6185,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
|
|
|
6176
6185
|
if (removed.length === 0) return false;
|
|
6177
6186
|
const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
|
|
6178
6187
|
return material.every((line) => {
|
|
6179
|
-
const
|
|
6180
|
-
return removedSet.has(
|
|
6188
|
+
const path79 = normalizeRelativePath(pathFromGitStatusLine(line));
|
|
6189
|
+
return removedSet.has(path79);
|
|
6181
6190
|
});
|
|
6182
6191
|
}
|
|
6183
6192
|
|
|
@@ -7013,6 +7022,9 @@ function buildBoard(run, workers, compact) {
|
|
|
7013
7022
|
}
|
|
7014
7023
|
return board;
|
|
7015
7024
|
}
|
|
7025
|
+
function isTerminalDoneBoardWorker(worker) {
|
|
7026
|
+
return worker.status === "done" && worker.attention === "done";
|
|
7027
|
+
}
|
|
7016
7028
|
function buildRunBoard(runId) {
|
|
7017
7029
|
const run = loadRun(runId);
|
|
7018
7030
|
const names = Object.keys(run.workers || {});
|
|
@@ -7033,7 +7045,7 @@ function buildRunBoard(runId) {
|
|
|
7033
7045
|
function buildCompactRunBoard(runId) {
|
|
7034
7046
|
const run = loadRun(runId);
|
|
7035
7047
|
const names = Object.keys(run.workers || {});
|
|
7036
|
-
const
|
|
7048
|
+
const allWorkers = names.map((name) => {
|
|
7037
7049
|
const worker = readJson(
|
|
7038
7050
|
path23.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
7039
7051
|
void 0
|
|
@@ -7044,7 +7056,17 @@ function buildCompactRunBoard(runId) {
|
|
|
7044
7056
|
if (isMetadataTerminalDone(worker)) return buildCompactDoneEntry(name, worker);
|
|
7045
7057
|
return buildWorkerBoardEntry({ run, workerName: name, worker });
|
|
7046
7058
|
});
|
|
7059
|
+
const workers = allWorkers.filter((worker) => !isTerminalDoneBoardWorker(worker));
|
|
7047
7060
|
const board = buildBoard(run, workers, true);
|
|
7061
|
+
board.status = deriveRunStatus(run.status, allWorkers);
|
|
7062
|
+
board.summary = {
|
|
7063
|
+
...board.summary ?? {},
|
|
7064
|
+
totalWorkerCount: allWorkers.length,
|
|
7065
|
+
shownWorkerCount: workers.length,
|
|
7066
|
+
omittedTerminalDoneWorkerCount: allWorkers.length - workers.length,
|
|
7067
|
+
allStatusCounts: countBy(allWorkers, "status"),
|
|
7068
|
+
allAttentionCounts: countBy(allWorkers, "attention")
|
|
7069
|
+
};
|
|
7048
7070
|
writeJson(path23.join(runDirectory(run.id), "last-board-compact.json"), board);
|
|
7049
7071
|
return board;
|
|
7050
7072
|
}
|
|
@@ -9251,7 +9273,7 @@ init_util();
|
|
|
9251
9273
|
// src/worker-metadata-reconcile.ts
|
|
9252
9274
|
init_heartbeat();
|
|
9253
9275
|
init_stream();
|
|
9254
|
-
import { existsSync as existsSync25, lstatSync, readdirSync as readdirSync7, readlinkSync, renameSync as
|
|
9276
|
+
import { existsSync as existsSync25, lstatSync, readdirSync as readdirSync7, readlinkSync, renameSync as renameSync4, rmSync } from "node:fs";
|
|
9255
9277
|
import path33 from "node:path";
|
|
9256
9278
|
|
|
9257
9279
|
// src/worker-metadata-paths.ts
|
|
@@ -9300,7 +9322,7 @@ function resolveWorkerJsonPath(input) {
|
|
|
9300
9322
|
}
|
|
9301
9323
|
|
|
9302
9324
|
// src/run-metadata-retention.ts
|
|
9303
|
-
import { existsSync as existsSync24, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
|
|
9325
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync7, readdirSync as readdirSync6, renameSync as renameSync3, statSync as statSync5 } from "node:fs";
|
|
9304
9326
|
import path32 from "node:path";
|
|
9305
9327
|
|
|
9306
9328
|
// src/default-repo.ts
|
|
@@ -9362,6 +9384,7 @@ init_paths();
|
|
|
9362
9384
|
init_run_store();
|
|
9363
9385
|
init_util();
|
|
9364
9386
|
var RUN_METADATA_ACTIVE_SIGNAL_MS = 15 * 60 * 1e3;
|
|
9387
|
+
var TERMINAL_WORKER_ARCHIVE_AGE_MS = 60 * 60 * 1e3;
|
|
9365
9388
|
function isHarnessRunMetadataPath(targetPath, harnessRoot) {
|
|
9366
9389
|
const resolved = path32.resolve(targetPath);
|
|
9367
9390
|
const runsDir = path32.resolve(harnessRunsDir(harnessRoot));
|
|
@@ -9385,6 +9408,14 @@ function listWorkerNamesOnDisk(runDir2) {
|
|
|
9385
9408
|
return [];
|
|
9386
9409
|
}
|
|
9387
9410
|
}
|
|
9411
|
+
function workerArchiveDir(runDir2) {
|
|
9412
|
+
return path32.join(runDir2, "archived-workers");
|
|
9413
|
+
}
|
|
9414
|
+
function uniqueArchivePath(runDir2, workerName) {
|
|
9415
|
+
const base = path32.join(workerArchiveDir(runDir2), safeSlug(workerName));
|
|
9416
|
+
if (!existsSync24(base)) return base;
|
|
9417
|
+
return `${base}-${Date.now()}`;
|
|
9418
|
+
}
|
|
9388
9419
|
function pathRecentlyTouched(target, now, windowMs) {
|
|
9389
9420
|
if (!existsSync24(target)) return false;
|
|
9390
9421
|
try {
|
|
@@ -9411,6 +9442,41 @@ function workerDirHasActiveRetentionSignals(workerDir, now = Date.now(), windowM
|
|
|
9411
9442
|
if (pathRecentlyTouched(artifacts.heartbeatPath, now, windowMs)) return true;
|
|
9412
9443
|
return false;
|
|
9413
9444
|
}
|
|
9445
|
+
function workerTerminalArchiveAgeMs(workerDir, worker, now) {
|
|
9446
|
+
const completionReportedAt = typeof worker.completionReportedAt === "string" ? Date.parse(worker.completionReportedAt) : NaN;
|
|
9447
|
+
if (Number.isFinite(completionReportedAt)) return now - completionReportedAt;
|
|
9448
|
+
const completionSnapshotAt = (() => {
|
|
9449
|
+
const raw = worker.completionResponse;
|
|
9450
|
+
if (!raw || typeof raw !== "object") return NaN;
|
|
9451
|
+
const task = raw.task;
|
|
9452
|
+
if (!task || typeof task !== "object") return NaN;
|
|
9453
|
+
const updatedAt = task.updatedAt;
|
|
9454
|
+
return typeof updatedAt === "string" ? Date.parse(updatedAt) : NaN;
|
|
9455
|
+
})();
|
|
9456
|
+
if (Number.isFinite(completionSnapshotAt)) return now - completionSnapshotAt;
|
|
9457
|
+
try {
|
|
9458
|
+
return now - statSync5(path32.join(workerDir, "worker.json")).mtimeMs;
|
|
9459
|
+
} catch {
|
|
9460
|
+
return 0;
|
|
9461
|
+
}
|
|
9462
|
+
}
|
|
9463
|
+
function isArchiveEligibleTerminalWorker(workerDir, worker, now, archiveAgeMs) {
|
|
9464
|
+
if (workerDirHasActiveRetentionSignals(workerDir, now)) {
|
|
9465
|
+
return { eligible: false, reason: "worker has active/recent retention signals" };
|
|
9466
|
+
}
|
|
9467
|
+
const status = typeof worker.status === "string" ? worker.status : "";
|
|
9468
|
+
const completionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim().length > 0;
|
|
9469
|
+
if (completionBlocker) return { eligible: false, reason: "worker has completion blocker" };
|
|
9470
|
+
const completionAcknowledged = typeof worker.completionReportedAt === "string" || worker.completionOutcome === "acknowledged";
|
|
9471
|
+
if (status !== "done" && !completionAcknowledged) {
|
|
9472
|
+
return { eligible: false, reason: `worker status is ${status || "unknown"}` };
|
|
9473
|
+
}
|
|
9474
|
+
const age = workerTerminalArchiveAgeMs(workerDir, worker, now);
|
|
9475
|
+
if (!Number.isFinite(age) || age < archiveAgeMs) {
|
|
9476
|
+
return { eligible: false, reason: "terminal worker is still within archive grace window" };
|
|
9477
|
+
}
|
|
9478
|
+
return { eligible: true, reason: "terminal worker metadata archived" };
|
|
9479
|
+
}
|
|
9414
9480
|
function runDirHasActiveRetentionSignals(runDir2, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
|
|
9415
9481
|
for (const name of listWorkerNamesOnDisk(runDir2)) {
|
|
9416
9482
|
if (workerDirHasActiveRetentionSignals(path32.join(runDir2, "workers", name), now, windowMs)) {
|
|
@@ -9488,6 +9554,59 @@ function repairMissingRunMetadata(harnessRootInput) {
|
|
|
9488
9554
|
}
|
|
9489
9555
|
return { runs: outcomes };
|
|
9490
9556
|
}
|
|
9557
|
+
function archiveTerminalWorkerMetadata(harnessRootInput, options = {}) {
|
|
9558
|
+
const harnessRoot = normalizeHarnessRoot(harnessRootInput ?? resolveHarnessRoot());
|
|
9559
|
+
const runsDir = harnessRunsDir(harnessRoot);
|
|
9560
|
+
const now = options.now ?? Date.now();
|
|
9561
|
+
const archiveAgeMs = options.archiveAgeMs ?? TERMINAL_WORKER_ARCHIVE_AGE_MS;
|
|
9562
|
+
const outcomes = [];
|
|
9563
|
+
repairMissingRunMetadata(harnessRoot);
|
|
9564
|
+
for (const runId of listRunDirIds(runsDir)) {
|
|
9565
|
+
const runDir2 = path32.join(runsDir, runId);
|
|
9566
|
+
const runJsonPath = path32.join(runDir2, "run.json");
|
|
9567
|
+
const run = readJson(runJsonPath, void 0);
|
|
9568
|
+
if (!run?.id) continue;
|
|
9569
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(run.workers || {}), ...listWorkerNamesOnDisk(runDir2)]);
|
|
9570
|
+
let changed = false;
|
|
9571
|
+
for (const name of names) {
|
|
9572
|
+
const workerDir = path32.join(runDir2, "workers", safeSlug(name));
|
|
9573
|
+
const workerJsonPath = path32.join(workerDir, "worker.json");
|
|
9574
|
+
const worker = readJson(workerJsonPath, void 0);
|
|
9575
|
+
if (!worker) {
|
|
9576
|
+
const archivedWorkerDir = path32.join(workerArchiveDir(runDir2), safeSlug(name));
|
|
9577
|
+
if (existsSync24(archivedWorkerDir) && (run.workers[safeSlug(name)] || run.workers[name])) {
|
|
9578
|
+
delete run.workers[safeSlug(name)];
|
|
9579
|
+
delete run.workers[name];
|
|
9580
|
+
changed = true;
|
|
9581
|
+
outcomes.push({
|
|
9582
|
+
runId: run.id,
|
|
9583
|
+
worker: name,
|
|
9584
|
+
action: "archived",
|
|
9585
|
+
reason: "removed stale live index entry for archived worker metadata",
|
|
9586
|
+
archivePath: archivedWorkerDir
|
|
9587
|
+
});
|
|
9588
|
+
continue;
|
|
9589
|
+
}
|
|
9590
|
+
outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "worker.json missing" });
|
|
9591
|
+
continue;
|
|
9592
|
+
}
|
|
9593
|
+
const eligibility = isArchiveEligibleTerminalWorker(workerDir, worker, now, archiveAgeMs);
|
|
9594
|
+
if (!eligibility.eligible) {
|
|
9595
|
+
outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: eligibility.reason });
|
|
9596
|
+
continue;
|
|
9597
|
+
}
|
|
9598
|
+
const archivePath = uniqueArchivePath(runDir2, name);
|
|
9599
|
+
mkdirSync7(path32.dirname(archivePath), { recursive: true });
|
|
9600
|
+
renameSync3(workerDir, archivePath);
|
|
9601
|
+
delete run.workers[safeSlug(name)];
|
|
9602
|
+
delete run.workers[name];
|
|
9603
|
+
changed = true;
|
|
9604
|
+
outcomes.push({ runId: run.id, worker: name, action: "archived", reason: eligibility.reason, archivePath });
|
|
9605
|
+
}
|
|
9606
|
+
if (changed) saveRun(run);
|
|
9607
|
+
}
|
|
9608
|
+
return { workers: outcomes };
|
|
9609
|
+
}
|
|
9491
9610
|
function collectFilesystemLiveRunKeys(harnessRoot, now = Date.now()) {
|
|
9492
9611
|
const keys = /* @__PURE__ */ new Set();
|
|
9493
9612
|
const runsDir = harnessRunsDir(harnessRoot);
|
|
@@ -9517,9 +9636,9 @@ function materializeSymlinkedRunDir(harnessRoot, runId) {
|
|
|
9517
9636
|
return null;
|
|
9518
9637
|
}
|
|
9519
9638
|
const staging = `${canonical}.materialize-${Date.now()}`;
|
|
9520
|
-
|
|
9639
|
+
renameSync4(linkedTarget, staging);
|
|
9521
9640
|
rmSync(canonical);
|
|
9522
|
-
|
|
9641
|
+
renameSync4(staging, canonical);
|
|
9523
9642
|
return linkedTarget;
|
|
9524
9643
|
}
|
|
9525
9644
|
function relocateArtifacts(input) {
|
|
@@ -9528,7 +9647,7 @@ function relocateArtifacts(input) {
|
|
|
9528
9647
|
const from = path33.join(input.fromDir, fileName);
|
|
9529
9648
|
const to = path33.join(input.toDir, fileName);
|
|
9530
9649
|
if (!existsSync25(from) || existsSync25(to)) continue;
|
|
9531
|
-
|
|
9650
|
+
renameSync4(from, to);
|
|
9532
9651
|
moved.push(fileName);
|
|
9533
9652
|
}
|
|
9534
9653
|
return moved;
|
|
@@ -9986,7 +10105,14 @@ function reconcileStaleWorkers() {
|
|
|
9986
10105
|
const metadataReconcile = reconcileWorkerMetadata();
|
|
9987
10106
|
if (staleReconcileDisabled()) {
|
|
9988
10107
|
const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
|
|
9989
|
-
|
|
10108
|
+
const terminalWorkerArchive2 = archiveTerminalWorkerMetadata();
|
|
10109
|
+
return {
|
|
10110
|
+
workers: [],
|
|
10111
|
+
finalizedRuns: finalizeStaleRuns(),
|
|
10112
|
+
metadataReconcile,
|
|
10113
|
+
localPrAttentionReconcile: localPrAttentionReconcile2,
|
|
10114
|
+
terminalWorkerArchive: terminalWorkerArchive2
|
|
10115
|
+
};
|
|
9990
10116
|
}
|
|
9991
10117
|
const outcomes = [];
|
|
9992
10118
|
const now = Date.now();
|
|
@@ -10065,7 +10191,8 @@ function reconcileStaleWorkers() {
|
|
|
10065
10191
|
}
|
|
10066
10192
|
}
|
|
10067
10193
|
const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
|
|
10068
|
-
|
|
10194
|
+
const terminalWorkerArchive = archiveTerminalWorkerMetadata();
|
|
10195
|
+
return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile, terminalWorkerArchive };
|
|
10069
10196
|
}
|
|
10070
10197
|
function reconcileRunsCli() {
|
|
10071
10198
|
const result = reconcileStaleWorkers();
|
|
@@ -10084,6 +10211,10 @@ function reconcileRunsCli() {
|
|
|
10084
10211
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
10085
10212
|
return acc;
|
|
10086
10213
|
}, {});
|
|
10214
|
+
const archiveTotals = result.terminalWorkerArchive.workers.reduce((acc, row) => {
|
|
10215
|
+
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
10216
|
+
return acc;
|
|
10217
|
+
}, {});
|
|
10087
10218
|
console.log(
|
|
10088
10219
|
JSON.stringify(
|
|
10089
10220
|
{
|
|
@@ -10101,11 +10232,16 @@ function reconcileRunsCli() {
|
|
|
10101
10232
|
totals: localPrAttentionTotals,
|
|
10102
10233
|
total: result.localPrAttentionReconcile.workers.length
|
|
10103
10234
|
},
|
|
10235
|
+
terminalWorkerArchive: {
|
|
10236
|
+
totals: archiveTotals,
|
|
10237
|
+
total: result.terminalWorkerArchive.workers.length
|
|
10238
|
+
},
|
|
10104
10239
|
finalizedRuns: result.finalizedRuns.length,
|
|
10105
10240
|
details: {
|
|
10106
10241
|
workers: result.workers,
|
|
10107
10242
|
metadataReconcile: result.metadataReconcile.workers,
|
|
10108
10243
|
localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
|
|
10244
|
+
terminalWorkerArchive: result.terminalWorkerArchive.workers,
|
|
10109
10245
|
runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
|
|
10110
10246
|
finalizedRuns: result.finalizedRuns
|
|
10111
10247
|
}
|
|
@@ -10312,11 +10448,11 @@ init_box_identity();
|
|
|
10312
10448
|
|
|
10313
10449
|
// src/cleanup.ts
|
|
10314
10450
|
init_paths();
|
|
10315
|
-
import
|
|
10451
|
+
import path53 from "node:path";
|
|
10316
10452
|
|
|
10317
10453
|
// src/cleanup-guards.ts
|
|
10318
10454
|
init_landing_gate();
|
|
10319
|
-
import
|
|
10455
|
+
import path39 from "node:path";
|
|
10320
10456
|
|
|
10321
10457
|
// src/cleanup-index-status.ts
|
|
10322
10458
|
init_git();
|
|
@@ -10459,6 +10595,44 @@ init_status();
|
|
|
10459
10595
|
// src/cleanup-run-liveness.ts
|
|
10460
10596
|
init_status();
|
|
10461
10597
|
|
|
10598
|
+
// src/cleanup-worker-harness-live.ts
|
|
10599
|
+
init_heartbeat();
|
|
10600
|
+
init_util();
|
|
10601
|
+
function heartbeatContentIsFresh(worker, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
|
|
10602
|
+
const heartbeat = parseHeartbeat(worker.heartbeatPath);
|
|
10603
|
+
if (!heartbeat.lastHeartbeatAt) return false;
|
|
10604
|
+
const age = now - Date.parse(heartbeat.lastHeartbeatAt);
|
|
10605
|
+
return Number.isFinite(age) && age >= 0 && age < windowMs;
|
|
10606
|
+
}
|
|
10607
|
+
function isHarnessWorkerHarnessLive(worker, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
|
|
10608
|
+
if (isPidAlive(worker.pid)) return true;
|
|
10609
|
+
return heartbeatContentIsFresh(worker, now, windowMs);
|
|
10610
|
+
}
|
|
10611
|
+
|
|
10612
|
+
// src/cleanup-run-liveness.ts
|
|
10613
|
+
function deriveRunTerminal(indexed, ctx) {
|
|
10614
|
+
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
10615
|
+
return deriveTerminalRunStatus(indexed.run);
|
|
10616
|
+
}
|
|
10617
|
+
function isWorkerProcessLive(indexed, now = Date.now()) {
|
|
10618
|
+
return isHarnessWorkerHarnessLive(indexed.worker, now);
|
|
10619
|
+
}
|
|
10620
|
+
function isRunStaleActive(indexed, ctx) {
|
|
10621
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
10622
|
+
return deriveRunTerminal(indexed, ctx) !== null;
|
|
10623
|
+
}
|
|
10624
|
+
function runBlocksWorktreeRemoval(indexed, ctx, now = Date.now()) {
|
|
10625
|
+
if (isWorkerProcessLive(indexed, now)) return true;
|
|
10626
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
10627
|
+
if (isRunStaleActive(indexed, ctx)) return false;
|
|
10628
|
+
const status = indexedWorktreeStatus(indexed);
|
|
10629
|
+
if (!isFinishedWorkerStatus(status)) {
|
|
10630
|
+
if (!isWorkerProcessLive(indexed, now)) return false;
|
|
10631
|
+
return true;
|
|
10632
|
+
}
|
|
10633
|
+
return false;
|
|
10634
|
+
}
|
|
10635
|
+
|
|
10462
10636
|
// src/cleanup-completion-blocker.ts
|
|
10463
10637
|
init_landing_gate();
|
|
10464
10638
|
init_status();
|
|
@@ -10480,51 +10654,80 @@ function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
|
10480
10654
|
return false;
|
|
10481
10655
|
}
|
|
10482
10656
|
|
|
10483
|
-
// src/cleanup-
|
|
10657
|
+
// src/cleanup-salvage-evidence.ts
|
|
10658
|
+
init_git();
|
|
10484
10659
|
init_util();
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
"
|
|
10489
|
-
"failed",
|
|
10490
|
-
"abandoned"
|
|
10491
|
-
]);
|
|
10492
|
-
function deriveRunTerminal(indexed, ctx) {
|
|
10493
|
-
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
10494
|
-
return deriveTerminalRunStatus(indexed.run);
|
|
10495
|
-
}
|
|
10496
|
-
function isWorkerProcessLive(indexed) {
|
|
10497
|
-
if (isPidAlive(indexed.worker.pid)) return true;
|
|
10498
|
-
if (typeof indexed.worker.completionReportedAt === "string" && indexed.worker.completionReportedAt.trim().length > 0) {
|
|
10499
|
-
return false;
|
|
10500
|
-
}
|
|
10501
|
-
const workerStatus2 = indexed.worker.status;
|
|
10502
|
-
if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2)) return false;
|
|
10503
|
-
if (!indexed.worker.pid) {
|
|
10504
|
-
if (workerStatus2 !== "running") return false;
|
|
10505
|
-
return indexedWorktreeStatus(indexed).alive;
|
|
10506
|
-
}
|
|
10507
|
-
return false;
|
|
10660
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6 } from "node:fs";
|
|
10661
|
+
import path38 from "node:path";
|
|
10662
|
+
function salvageEvidenceDir(harnessRoot, runId, workerName) {
|
|
10663
|
+
return path38.join(harnessRoot, "salvage", safeSlug(runId), safeSlug(workerName));
|
|
10508
10664
|
}
|
|
10509
|
-
function
|
|
10510
|
-
|
|
10511
|
-
return deriveRunTerminal(indexed, ctx) !== null;
|
|
10665
|
+
function hasSalvageEvidence(input) {
|
|
10666
|
+
return existsSync26(path38.join(salvageEvidenceDir(input.harnessRoot, input.runId, input.workerName), "evidence.json"));
|
|
10512
10667
|
}
|
|
10513
|
-
function
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10668
|
+
function writeWorktreeSalvageEvidence(input) {
|
|
10669
|
+
const dir = salvageEvidenceDir(input.harnessRoot, input.indexed.runId, input.indexed.workerName);
|
|
10670
|
+
mkdirSync8(dir, { recursive: true });
|
|
10671
|
+
const patchPath = path38.join(dir, "salvage.patch");
|
|
10672
|
+
let wrotePatch = false;
|
|
10673
|
+
if (existsSync26(input.indexed.worktreePath)) {
|
|
10674
|
+
const diff = gitCapture(input.indexed.worktreePath, ["diff", "HEAD"]);
|
|
10675
|
+
const staged = gitCapture(input.indexed.worktreePath, ["diff", "--cached"]);
|
|
10676
|
+
const patch = [diff.stdout, staged.stdout].filter((chunk) => chunk.trim()).join("\n");
|
|
10677
|
+
if (patch.trim()) {
|
|
10678
|
+
writeFileSync6(patchPath, patch.endsWith("\n") ? patch : `${patch}
|
|
10679
|
+
`);
|
|
10680
|
+
wrotePatch = true;
|
|
10681
|
+
}
|
|
10682
|
+
}
|
|
10683
|
+
const record = {
|
|
10684
|
+
capturedAt: new Date(input.now ?? Date.now()).toISOString(),
|
|
10685
|
+
skipReason: input.skipReason,
|
|
10686
|
+
runId: input.indexed.runId,
|
|
10687
|
+
workerName: input.indexed.workerName,
|
|
10688
|
+
worktreePath: input.indexed.worktreePath,
|
|
10689
|
+
taskId: input.indexed.worker.taskId,
|
|
10690
|
+
agentOsId: input.indexed.worker.agentOsId,
|
|
10691
|
+
branch: input.indexed.worker.branch,
|
|
10692
|
+
completionBlocker: input.indexed.worker.completionBlocker,
|
|
10693
|
+
finalResult: input.status.finalResult,
|
|
10694
|
+
changedFiles: input.status.changedFiles,
|
|
10695
|
+
gitAncestry: input.status.gitAncestry,
|
|
10696
|
+
prUrl: input.status.prUrl ?? null,
|
|
10697
|
+
...wrotePatch ? { patchPath } : {}
|
|
10698
|
+
};
|
|
10699
|
+
writeFileSync6(path38.join(dir, "evidence.json"), `${JSON.stringify(record, null, 2)}
|
|
10700
|
+
`);
|
|
10701
|
+
return record;
|
|
10525
10702
|
}
|
|
10526
10703
|
|
|
10527
10704
|
// src/cleanup-guards.ts
|
|
10705
|
+
var SALVAGE_ELIGIBLE_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
10706
|
+
"completion_blocked",
|
|
10707
|
+
"dirty_worktree",
|
|
10708
|
+
"pr_or_unmerged_commits",
|
|
10709
|
+
"landing_blocked"
|
|
10710
|
+
]);
|
|
10711
|
+
function maybeSalvageBlockedWorktree(input) {
|
|
10712
|
+
if (!SALVAGE_ELIGIBLE_SKIP_REASONS.has(input.skipReason)) return false;
|
|
10713
|
+
if (isWorkerProcessLive(input.indexed, input.now)) return false;
|
|
10714
|
+
if (hasSalvageEvidence({
|
|
10715
|
+
harnessRoot: input.harnessRoot,
|
|
10716
|
+
runId: input.indexed.runId,
|
|
10717
|
+
workerName: input.indexed.workerName
|
|
10718
|
+
})) {
|
|
10719
|
+
return true;
|
|
10720
|
+
}
|
|
10721
|
+
if (!input.writeSalvageEvidence) return false;
|
|
10722
|
+
writeWorktreeSalvageEvidence({
|
|
10723
|
+
harnessRoot: input.harnessRoot,
|
|
10724
|
+
indexed: input.indexed,
|
|
10725
|
+
skipReason: input.skipReason,
|
|
10726
|
+
status: input.status,
|
|
10727
|
+
now: input.now
|
|
10728
|
+
});
|
|
10729
|
+
return true;
|
|
10730
|
+
}
|
|
10528
10731
|
function effectiveWorktreeAgeMs(input) {
|
|
10529
10732
|
const { indexed, includeOrphans, worktreesAgeMs, terminalWorktreesAgeMs } = input;
|
|
10530
10733
|
if (!indexed) return includeOrphans ? terminalWorktreesAgeMs : worktreesAgeMs;
|
|
@@ -10539,8 +10742,18 @@ function effectiveWorktreeAgeMs(input) {
|
|
|
10539
10742
|
}
|
|
10540
10743
|
return worktreesAgeMs;
|
|
10541
10744
|
}
|
|
10745
|
+
function resolveHarnessRoot2(input, indexed) {
|
|
10746
|
+
if (input.harnessRoot?.trim()) return path39.resolve(input.harnessRoot);
|
|
10747
|
+
const workerDir = indexed.worker.workerDir;
|
|
10748
|
+
if (!workerDir) return null;
|
|
10749
|
+
const runsMarker = `${path39.sep}runs${path39.sep}`;
|
|
10750
|
+
const idx = workerDir.indexOf(runsMarker);
|
|
10751
|
+
if (idx < 0) return null;
|
|
10752
|
+
return workerDir.slice(0, idx + runsMarker.length - 1);
|
|
10753
|
+
}
|
|
10542
10754
|
function skipWorktreeRemoval(input) {
|
|
10543
10755
|
const { indexed, includeOrphans, worktreesAgeMs, ageMs, orphanSafety, worktreeRemovalGuard } = input;
|
|
10756
|
+
const now = input.now ?? Date.now();
|
|
10544
10757
|
if (!indexed) {
|
|
10545
10758
|
if (!includeOrphans) return "orphan_without_flag";
|
|
10546
10759
|
return orphanSafety ?? null;
|
|
@@ -10548,25 +10761,55 @@ function skipWorktreeRemoval(input) {
|
|
|
10548
10761
|
const ageThresholdMs = effectiveWorktreeAgeMs(input);
|
|
10549
10762
|
if (worktreesAgeMs <= 0 && !includeOrphans && ageThresholdMs <= 0) return "worktrees_disabled";
|
|
10550
10763
|
if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
|
|
10551
|
-
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
10764
|
+
if (isWorkerProcessLive(indexed, now)) return "active_worker";
|
|
10765
|
+
const status = resolveWorktreeGuardStatus(indexed, input.liveness);
|
|
10766
|
+
const salvageHarnessRoot = resolveHarnessRoot2(input, indexed);
|
|
10767
|
+
const salvageOrBlock = (skipReason) => {
|
|
10768
|
+
if (salvageHarnessRoot && maybeSalvageBlockedWorktree({
|
|
10769
|
+
indexed,
|
|
10770
|
+
harnessRoot: salvageHarnessRoot,
|
|
10771
|
+
skipReason,
|
|
10772
|
+
status,
|
|
10773
|
+
now,
|
|
10774
|
+
writeSalvageEvidence: input.writeSalvageEvidence === true
|
|
10775
|
+
})) {
|
|
10776
|
+
return null;
|
|
10777
|
+
}
|
|
10778
|
+
return skipReason;
|
|
10779
|
+
};
|
|
10780
|
+
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) {
|
|
10781
|
+
const blocked = salvageOrBlock("completion_blocked");
|
|
10782
|
+
if (blocked) return blocked;
|
|
10783
|
+
}
|
|
10552
10784
|
if (indexedWorktreeHasMaterialChanges(indexed, input.liveness?.gitStatusCache)) {
|
|
10553
|
-
|
|
10785
|
+
const blocked = salvageOrBlock("dirty_worktree");
|
|
10786
|
+
if (blocked) return blocked;
|
|
10554
10787
|
}
|
|
10555
10788
|
const ahead = input.liveness?.gitRevCache?.countAheadOfMain(input.worktreePath);
|
|
10556
|
-
if (ahead !== null && ahead !== void 0 && ahead > 0)
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10789
|
+
if (ahead !== null && ahead !== void 0 && ahead > 0) {
|
|
10790
|
+
const blocked = salvageOrBlock("pr_or_unmerged_commits");
|
|
10791
|
+
if (blocked) return blocked;
|
|
10792
|
+
}
|
|
10793
|
+
if (runBlocksWorktreeRemoval(indexed, input.liveness, now)) return "run_still_active";
|
|
10560
10794
|
if (!isFinishedWorkerStatus(status)) return "run_still_active";
|
|
10561
|
-
if (isPrOrUnmergedWork(status))
|
|
10562
|
-
|
|
10795
|
+
if (isPrOrUnmergedWork(status)) {
|
|
10796
|
+
const blocked = salvageOrBlock("pr_or_unmerged_commits");
|
|
10797
|
+
if (blocked) return blocked;
|
|
10798
|
+
}
|
|
10799
|
+
if (materialWorktreeChanges2(status.changedFiles).length > 0) {
|
|
10800
|
+
const blocked = salvageOrBlock("dirty_worktree");
|
|
10801
|
+
if (blocked) return blocked;
|
|
10802
|
+
}
|
|
10563
10803
|
const landing = assessWorkerLanding({
|
|
10564
10804
|
finalResult: status.finalResult,
|
|
10565
10805
|
changedFiles: status.changedFiles,
|
|
10566
10806
|
gitAncestry: status.gitAncestry,
|
|
10567
10807
|
prUrl: prUrlFromFinalResult(status.finalResult)
|
|
10568
10808
|
});
|
|
10569
|
-
if (landing.blocked)
|
|
10809
|
+
if (landing.blocked) {
|
|
10810
|
+
const blocked = salvageOrBlock("landing_blocked");
|
|
10811
|
+
if (blocked) return blocked;
|
|
10812
|
+
}
|
|
10570
10813
|
if (worktreeRemovalGuard && input.worktreePath) {
|
|
10571
10814
|
const overlay = worktreeRemovalGuard({
|
|
10572
10815
|
worktreePath: input.worktreePath,
|
|
@@ -10583,7 +10826,7 @@ function skipWorktreeRemoval(input) {
|
|
|
10583
10826
|
function skipDependencyCacheRemoval(input) {
|
|
10584
10827
|
const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
|
|
10585
10828
|
if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
10586
|
-
if (activeWorktreePaths.has(
|
|
10829
|
+
if (activeWorktreePaths.has(path39.resolve(worktreePath))) return "active_worker";
|
|
10587
10830
|
if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
|
|
10588
10831
|
if (indexed && indexedWorktreeHasMaterialChanges(indexed, input.gitStatusCache)) {
|
|
10589
10832
|
return "dirty_worktree";
|
|
@@ -10612,11 +10855,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
|
10612
10855
|
function collectPreservedLivePaths(actions, skips) {
|
|
10613
10856
|
const out = [];
|
|
10614
10857
|
const seen = /* @__PURE__ */ new Set();
|
|
10615
|
-
const push = (
|
|
10616
|
-
const key = `${
|
|
10858
|
+
const push = (path79, reason, detail) => {
|
|
10859
|
+
const key = `${path79}\0${reason}`;
|
|
10617
10860
|
if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
|
|
10618
10861
|
seen.add(key);
|
|
10619
|
-
out.push({ path:
|
|
10862
|
+
out.push({ path: path79, reason, ...detail ? { detail } : {} });
|
|
10620
10863
|
};
|
|
10621
10864
|
for (const skip2 of skips) {
|
|
10622
10865
|
if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
|
|
@@ -10631,31 +10874,16 @@ function collectPreservedLivePaths(actions, skips) {
|
|
|
10631
10874
|
}
|
|
10632
10875
|
|
|
10633
10876
|
// src/cleanup-run-directory.ts
|
|
10634
|
-
import { existsSync as existsSync27, readdirSync as
|
|
10635
|
-
import
|
|
10877
|
+
import { existsSync as existsSync27, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
|
|
10878
|
+
import path41 from "node:path";
|
|
10636
10879
|
|
|
10637
10880
|
// src/cleanup-active-worktrees.ts
|
|
10638
10881
|
init_run_store();
|
|
10639
10882
|
init_paths();
|
|
10640
|
-
import
|
|
10641
|
-
import path39 from "node:path";
|
|
10883
|
+
import path40 from "node:path";
|
|
10642
10884
|
init_util();
|
|
10643
|
-
function workerHasRecentHarnessActivity(worker, now) {
|
|
10644
|
-
const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
|
|
10645
|
-
for (const target of paths) {
|
|
10646
|
-
if (!existsSync26(target)) continue;
|
|
10647
|
-
try {
|
|
10648
|
-
const age = now - statSync6(target).mtimeMs;
|
|
10649
|
-
if (Number.isFinite(age) && age >= 0 && age < RUN_METADATA_ACTIVE_SIGNAL_MS) return true;
|
|
10650
|
-
} catch {
|
|
10651
|
-
}
|
|
10652
|
-
}
|
|
10653
|
-
return false;
|
|
10654
|
-
}
|
|
10655
10885
|
function isActiveHarnessWorker2(worker, now) {
|
|
10656
|
-
|
|
10657
|
-
if (worker.status === "running" && workerHasRecentHarnessActivity(worker, now)) return true;
|
|
10658
|
-
return false;
|
|
10886
|
+
return isHarnessWorkerHarnessLive(worker, now);
|
|
10659
10887
|
}
|
|
10660
10888
|
function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
|
|
10661
10889
|
const activeWorktreePaths = /* @__PURE__ */ new Set();
|
|
@@ -10665,11 +10893,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
|
|
|
10665
10893
|
let runHasLive = false;
|
|
10666
10894
|
for (const name of Object.keys(run.workers || {})) {
|
|
10667
10895
|
const worker = readJson(
|
|
10668
|
-
|
|
10896
|
+
path40.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
|
|
10669
10897
|
void 0
|
|
10670
10898
|
);
|
|
10671
10899
|
if (!worker?.worktreePath) continue;
|
|
10672
|
-
const worktreePath =
|
|
10900
|
+
const worktreePath = path40.resolve(worker.worktreePath);
|
|
10673
10901
|
if (!isActiveHarnessWorker2(worker, now)) continue;
|
|
10674
10902
|
runHasLive = true;
|
|
10675
10903
|
activeWorktreePaths.add(worktreePath);
|
|
@@ -10691,20 +10919,20 @@ function isWorktreeOnLiveRun(worktreePath, harnessRoot, runId, liveRunKeys) {
|
|
|
10691
10919
|
init_util();
|
|
10692
10920
|
function pathAgeMs(target, now) {
|
|
10693
10921
|
try {
|
|
10694
|
-
const mtime =
|
|
10922
|
+
const mtime = statSync6(target).mtimeMs;
|
|
10695
10923
|
return Math.max(0, now - mtime);
|
|
10696
10924
|
} catch {
|
|
10697
10925
|
return 0;
|
|
10698
10926
|
}
|
|
10699
10927
|
}
|
|
10700
10928
|
function loadRunStatus(harnessRoot, runId) {
|
|
10701
|
-
const runPath =
|
|
10929
|
+
const runPath = path41.join(harnessRoot, "runs", runId, "run.json");
|
|
10702
10930
|
if (!existsSync27(runPath)) return null;
|
|
10703
10931
|
return readJson(runPath, null);
|
|
10704
10932
|
}
|
|
10705
10933
|
function runDirectoryIsEmpty(runPath) {
|
|
10706
10934
|
try {
|
|
10707
|
-
const entries =
|
|
10935
|
+
const entries = readdirSync8(runPath);
|
|
10708
10936
|
return entries.length === 0;
|
|
10709
10937
|
} catch {
|
|
10710
10938
|
return false;
|
|
@@ -10728,7 +10956,7 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
10728
10956
|
const candidates = [];
|
|
10729
10957
|
let entries;
|
|
10730
10958
|
try {
|
|
10731
|
-
entries =
|
|
10959
|
+
entries = readdirSync8(opts.worktreesDir, { withFileTypes: true });
|
|
10732
10960
|
} catch {
|
|
10733
10961
|
return [];
|
|
10734
10962
|
}
|
|
@@ -10736,7 +10964,7 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
10736
10964
|
if (!runEntry.isDirectory()) continue;
|
|
10737
10965
|
const runId = runEntry.name;
|
|
10738
10966
|
if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
|
|
10739
|
-
const runPath =
|
|
10967
|
+
const runPath = path41.join(opts.worktreesDir, runId);
|
|
10740
10968
|
if (!runDirectoryIsEmpty(runPath)) continue;
|
|
10741
10969
|
candidates.push({
|
|
10742
10970
|
kind: "remove_run_directory",
|
|
@@ -10756,8 +10984,8 @@ import { existsSync as existsSync30, rmSync as rmSync3 } from "node:fs";
|
|
|
10756
10984
|
|
|
10757
10985
|
// src/cleanup-dir-size.ts
|
|
10758
10986
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
10759
|
-
import { existsSync as existsSync28, readdirSync as
|
|
10760
|
-
import
|
|
10987
|
+
import { existsSync as existsSync28, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
|
|
10988
|
+
import path42 from "node:path";
|
|
10761
10989
|
var DEFAULT_DU_TIMEOUT_MS = 2500;
|
|
10762
10990
|
function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
|
|
10763
10991
|
if (!existsSync28(root)) return 0;
|
|
@@ -10785,16 +11013,16 @@ function directorySizeBytes(root, maxEntries = 5e4) {
|
|
|
10785
11013
|
const current = stack.pop();
|
|
10786
11014
|
let entries;
|
|
10787
11015
|
try {
|
|
10788
|
-
entries =
|
|
11016
|
+
entries = readdirSync9(current);
|
|
10789
11017
|
} catch {
|
|
10790
11018
|
continue;
|
|
10791
11019
|
}
|
|
10792
11020
|
for (const name of entries) {
|
|
10793
11021
|
if (seen++ > maxEntries) return null;
|
|
10794
|
-
const full =
|
|
11022
|
+
const full = path42.join(current, name);
|
|
10795
11023
|
let st;
|
|
10796
11024
|
try {
|
|
10797
|
-
st =
|
|
11025
|
+
st = statSync7(full);
|
|
10798
11026
|
} catch {
|
|
10799
11027
|
continue;
|
|
10800
11028
|
}
|
|
@@ -10810,7 +11038,7 @@ import { existsSync as existsSync29, rmSync as rmSync2 } from "node:fs";
|
|
|
10810
11038
|
init_paths();
|
|
10811
11039
|
|
|
10812
11040
|
// src/cleanup-path-ownership.ts
|
|
10813
|
-
import { lstatSync as lstatSync2, readdirSync as
|
|
11041
|
+
import { lstatSync as lstatSync2, readdirSync as readdirSync10 } from "node:fs";
|
|
10814
11042
|
function readPathOwnership(targetPath) {
|
|
10815
11043
|
try {
|
|
10816
11044
|
const st = lstatSync2(targetPath);
|
|
@@ -10827,7 +11055,7 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
10827
11055
|
if (!root) return false;
|
|
10828
11056
|
if (root.foreign) return true;
|
|
10829
11057
|
try {
|
|
10830
|
-
const names =
|
|
11058
|
+
const names = readdirSync10(targetPath);
|
|
10831
11059
|
let checked = 0;
|
|
10832
11060
|
for (const name of names) {
|
|
10833
11061
|
if (checked >= maxEntries) break;
|
|
@@ -10843,20 +11071,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
10843
11071
|
|
|
10844
11072
|
// src/cleanup-privileged-remove.ts
|
|
10845
11073
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
10846
|
-
import
|
|
11074
|
+
import path44 from "node:path";
|
|
10847
11075
|
|
|
10848
11076
|
// src/cleanup-harness-path-validate.ts
|
|
10849
|
-
import
|
|
11077
|
+
import path43 from "node:path";
|
|
10850
11078
|
function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
|
|
10851
|
-
const resolved =
|
|
10852
|
-
const suffix = `${
|
|
11079
|
+
const resolved = path43.resolve(targetPath);
|
|
11080
|
+
const suffix = `${path43.sep}${cacheDirName}`;
|
|
10853
11081
|
const cachePath = resolved.endsWith(suffix) ? resolved : null;
|
|
10854
11082
|
if (!cachePath) return "path_outside_harness";
|
|
10855
|
-
const rel =
|
|
10856
|
-
if (rel.startsWith("..") ||
|
|
10857
|
-
const parts = rel.split(
|
|
11083
|
+
const rel = path43.relative(worktreesDir, cachePath);
|
|
11084
|
+
if (rel.startsWith("..") || path43.isAbsolute(rel)) return "path_outside_harness";
|
|
11085
|
+
const parts = rel.split(path43.sep);
|
|
10858
11086
|
if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
|
|
10859
|
-
if (!resolved.startsWith(
|
|
11087
|
+
if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
|
|
10860
11088
|
return null;
|
|
10861
11089
|
}
|
|
10862
11090
|
function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
|
|
@@ -10866,16 +11094,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
|
10866
11094
|
return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
|
|
10867
11095
|
}
|
|
10868
11096
|
function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
10869
|
-
const resolved =
|
|
10870
|
-
const relToWt =
|
|
10871
|
-
if (relToWt.startsWith("..") ||
|
|
10872
|
-
const parts = relToWt.split(
|
|
11097
|
+
const resolved = path43.resolve(targetPath);
|
|
11098
|
+
const relToWt = path43.relative(worktreesDir, resolved);
|
|
11099
|
+
if (relToWt.startsWith("..") || path43.isAbsolute(relToWt)) return "path_outside_harness";
|
|
11100
|
+
const parts = relToWt.split(path43.sep);
|
|
10873
11101
|
if (parts.length < 3) return "path_outside_harness";
|
|
10874
|
-
if (!resolved.startsWith(
|
|
11102
|
+
if (!resolved.startsWith(path43.resolve(harnessRoot))) return "path_outside_harness";
|
|
10875
11103
|
return null;
|
|
10876
11104
|
}
|
|
10877
11105
|
function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
10878
|
-
const resolved =
|
|
11106
|
+
const resolved = path43.resolve(targetPath);
|
|
10879
11107
|
return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
|
|
10880
11108
|
}
|
|
10881
11109
|
|
|
@@ -10909,12 +11137,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
|
|
|
10909
11137
|
"chown",
|
|
10910
11138
|
"-R",
|
|
10911
11139
|
`${effectiveUid}:${effectiveGid}`,
|
|
10912
|
-
|
|
11140
|
+
path44.resolve(targetPath)
|
|
10913
11141
|
]);
|
|
10914
11142
|
if (chown.ok) {
|
|
10915
11143
|
return { ok: true, method: "chown_then_rm" };
|
|
10916
11144
|
}
|
|
10917
|
-
const rm = runSudoNonInteractive(["rm", "-rf",
|
|
11145
|
+
const rm = runSudoNonInteractive(["rm", "-rf", path44.resolve(targetPath)]);
|
|
10918
11146
|
if (rm.ok) {
|
|
10919
11147
|
return { ok: true, method: "sudo_rm" };
|
|
10920
11148
|
}
|
|
@@ -11115,27 +11343,27 @@ function removeWorktree(candidate, execute) {
|
|
|
11115
11343
|
}
|
|
11116
11344
|
|
|
11117
11345
|
// src/cleanup-scan.ts
|
|
11118
|
-
import { existsSync as existsSync31, readdirSync as
|
|
11119
|
-
import
|
|
11346
|
+
import { existsSync as existsSync31, readdirSync as readdirSync11, statSync as statSync8 } from "node:fs";
|
|
11347
|
+
import path45 from "node:path";
|
|
11120
11348
|
function pathAgeMs2(target, now) {
|
|
11121
11349
|
try {
|
|
11122
|
-
const mtime =
|
|
11350
|
+
const mtime = statSync8(target).mtimeMs;
|
|
11123
11351
|
return Math.max(0, now - mtime);
|
|
11124
11352
|
} catch {
|
|
11125
11353
|
return 0;
|
|
11126
11354
|
}
|
|
11127
11355
|
}
|
|
11128
11356
|
function isPathInside(child, parent) {
|
|
11129
|
-
const rel =
|
|
11130
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
11357
|
+
const rel = path45.relative(parent, child);
|
|
11358
|
+
return rel === "" || !rel.startsWith("..") && !path45.isAbsolute(rel);
|
|
11131
11359
|
}
|
|
11132
11360
|
function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
|
|
11133
11361
|
const out = [];
|
|
11134
11362
|
for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
|
|
11135
11363
|
if (rel === ".next") continue;
|
|
11136
|
-
const target =
|
|
11364
|
+
const target = path45.join(worktreePath, rel);
|
|
11137
11365
|
if (!existsSync31(target)) continue;
|
|
11138
|
-
const resolved =
|
|
11366
|
+
const resolved = path45.resolve(target);
|
|
11139
11367
|
if (seen.has(resolved)) continue;
|
|
11140
11368
|
if (!isPathInside(resolved, opts.harnessRoot)) continue;
|
|
11141
11369
|
seen.add(resolved);
|
|
@@ -11165,12 +11393,12 @@ function scanBuildCacheCandidates(opts) {
|
|
|
11165
11393
|
);
|
|
11166
11394
|
}
|
|
11167
11395
|
if (!opts.includeOrphans || !existsSync31(opts.worktreesDir)) return candidates;
|
|
11168
|
-
for (const runEntry of
|
|
11396
|
+
for (const runEntry of readdirSync11(opts.worktreesDir, { withFileTypes: true })) {
|
|
11169
11397
|
if (!runEntry.isDirectory()) continue;
|
|
11170
|
-
const runPath =
|
|
11171
|
-
for (const workerEntry of
|
|
11398
|
+
const runPath = path45.join(opts.worktreesDir, runEntry.name);
|
|
11399
|
+
for (const workerEntry of readdirSync11(runPath, { withFileTypes: true })) {
|
|
11172
11400
|
if (!workerEntry.isDirectory()) continue;
|
|
11173
|
-
const worktreePath =
|
|
11401
|
+
const worktreePath = path45.join(runPath, workerEntry.name);
|
|
11174
11402
|
candidates.push(
|
|
11175
11403
|
...collectBuildCacheForWorktree(worktreePath, opts, seen, {
|
|
11176
11404
|
runId: runEntry.name,
|
|
@@ -11208,21 +11436,21 @@ function scanWorktreeCandidates(opts) {
|
|
|
11208
11436
|
if (!orphanEnabled || !existsSync31(opts.worktreesDir)) return candidates;
|
|
11209
11437
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
11210
11438
|
for (const entry of opts.index.values()) {
|
|
11211
|
-
indexedPaths.add(
|
|
11439
|
+
indexedPaths.add(path45.resolve(entry.worktreePath));
|
|
11212
11440
|
}
|
|
11213
|
-
for (const runEntry of
|
|
11441
|
+
for (const runEntry of readdirSync11(opts.worktreesDir, { withFileTypes: true })) {
|
|
11214
11442
|
if (!runEntry.isDirectory()) continue;
|
|
11215
11443
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
11216
|
-
const runPath =
|
|
11444
|
+
const runPath = path45.join(opts.worktreesDir, runEntry.name);
|
|
11217
11445
|
let workerEntries;
|
|
11218
11446
|
try {
|
|
11219
|
-
workerEntries =
|
|
11447
|
+
workerEntries = readdirSync11(runPath, { withFileTypes: true });
|
|
11220
11448
|
} catch {
|
|
11221
11449
|
continue;
|
|
11222
11450
|
}
|
|
11223
11451
|
for (const workerEntry of workerEntries) {
|
|
11224
11452
|
if (!workerEntry.isDirectory()) continue;
|
|
11225
|
-
const worktreePath =
|
|
11453
|
+
const worktreePath = path45.resolve(path45.join(runPath, workerEntry.name));
|
|
11226
11454
|
if (seen.has(worktreePath)) continue;
|
|
11227
11455
|
if (indexedPaths.has(worktreePath)) continue;
|
|
11228
11456
|
if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
|
|
@@ -11241,27 +11469,27 @@ function scanWorktreeCandidates(opts) {
|
|
|
11241
11469
|
}
|
|
11242
11470
|
|
|
11243
11471
|
// src/cleanup-dependency-scan.ts
|
|
11244
|
-
import { existsSync as existsSync32, readdirSync as
|
|
11245
|
-
import
|
|
11472
|
+
import { existsSync as existsSync32, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
|
|
11473
|
+
import path46 from "node:path";
|
|
11246
11474
|
var DEPENDENCY_CACHE_DIRS = [
|
|
11247
11475
|
{ dirName: "node_modules", kind: "remove_node_modules" },
|
|
11248
11476
|
{ dirName: ".next", kind: "remove_next_cache" }
|
|
11249
11477
|
];
|
|
11250
11478
|
function pathAgeMs3(target, now) {
|
|
11251
11479
|
try {
|
|
11252
|
-
const mtime =
|
|
11480
|
+
const mtime = statSync9(target).mtimeMs;
|
|
11253
11481
|
return Math.max(0, now - mtime);
|
|
11254
11482
|
} catch {
|
|
11255
11483
|
return 0;
|
|
11256
11484
|
}
|
|
11257
11485
|
}
|
|
11258
11486
|
function isPathInside2(child, parent) {
|
|
11259
|
-
const rel =
|
|
11260
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
11487
|
+
const rel = path46.relative(parent, child);
|
|
11488
|
+
return rel === "" || !rel.startsWith("..") && !path46.isAbsolute(rel);
|
|
11261
11489
|
}
|
|
11262
11490
|
function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
11263
11491
|
if (!existsSync32(targetPath)) return;
|
|
11264
|
-
const resolved =
|
|
11492
|
+
const resolved = path46.resolve(targetPath);
|
|
11265
11493
|
if (seen.has(resolved)) return;
|
|
11266
11494
|
if (!isPathInside2(resolved, opts.harnessRoot)) return;
|
|
11267
11495
|
seen.add(resolved);
|
|
@@ -11278,7 +11506,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
|
11278
11506
|
}
|
|
11279
11507
|
function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
|
|
11280
11508
|
for (const entry of DEPENDENCY_CACHE_DIRS) {
|
|
11281
|
-
pushCandidate2(candidates, seen, opts,
|
|
11509
|
+
pushCandidate2(candidates, seen, opts, path46.join(worktreePath, entry.dirName), entry.kind, meta);
|
|
11282
11510
|
}
|
|
11283
11511
|
}
|
|
11284
11512
|
function scanDependencyCacheCandidates(opts) {
|
|
@@ -11293,19 +11521,19 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
11293
11521
|
});
|
|
11294
11522
|
}
|
|
11295
11523
|
if (!opts.includeOrphans || !existsSync32(opts.worktreesDir)) return candidates;
|
|
11296
|
-
for (const runEntry of
|
|
11524
|
+
for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
|
|
11297
11525
|
if (!runEntry.isDirectory()) continue;
|
|
11298
11526
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
11299
|
-
const runPath =
|
|
11527
|
+
const runPath = path46.join(opts.worktreesDir, runEntry.name);
|
|
11300
11528
|
let workerEntries;
|
|
11301
11529
|
try {
|
|
11302
|
-
workerEntries =
|
|
11530
|
+
workerEntries = readdirSync12(runPath, { withFileTypes: true });
|
|
11303
11531
|
} catch {
|
|
11304
11532
|
continue;
|
|
11305
11533
|
}
|
|
11306
11534
|
for (const workerEntry of workerEntries) {
|
|
11307
11535
|
if (!workerEntry.isDirectory()) continue;
|
|
11308
|
-
const worktreePath =
|
|
11536
|
+
const worktreePath = path46.join(runPath, workerEntry.name);
|
|
11309
11537
|
scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
|
|
11310
11538
|
runId: runEntry.name,
|
|
11311
11539
|
worker: workerEntry.name
|
|
@@ -11317,11 +11545,11 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
11317
11545
|
|
|
11318
11546
|
// src/cleanup-duplicate-worktrees.ts
|
|
11319
11547
|
init_git();
|
|
11320
|
-
import { existsSync as existsSync33, statSync as
|
|
11321
|
-
import
|
|
11548
|
+
import { existsSync as existsSync33, statSync as statSync10 } from "node:fs";
|
|
11549
|
+
import path47 from "node:path";
|
|
11322
11550
|
function pathAgeMs4(target, now) {
|
|
11323
11551
|
try {
|
|
11324
|
-
const mtime =
|
|
11552
|
+
const mtime = statSync10(target).mtimeMs;
|
|
11325
11553
|
return Math.max(0, now - mtime);
|
|
11326
11554
|
} catch {
|
|
11327
11555
|
return 0;
|
|
@@ -11348,32 +11576,24 @@ function parseWorktreePorcelain(output) {
|
|
|
11348
11576
|
return records;
|
|
11349
11577
|
}
|
|
11350
11578
|
function isUnderWorktreesDir(worktreePath, worktreesDir) {
|
|
11351
|
-
const rel =
|
|
11352
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
11353
|
-
}
|
|
11354
|
-
function isCleanWorktree(worktreePath, repoRoot) {
|
|
11355
|
-
try {
|
|
11356
|
-
const porcelain = git(repoRoot, ["-C", worktreePath, "status", "--porcelain"], {
|
|
11357
|
-
allowFailure: true
|
|
11358
|
-
});
|
|
11359
|
-
return !String(porcelain || "").trim();
|
|
11360
|
-
} catch {
|
|
11361
|
-
return false;
|
|
11362
|
-
}
|
|
11579
|
+
const rel = path47.relative(path47.resolve(worktreesDir), path47.resolve(worktreePath));
|
|
11580
|
+
return rel !== "" && !rel.startsWith("..") && !path47.isAbsolute(rel);
|
|
11363
11581
|
}
|
|
11582
|
+
var MAX_DUPLICATE_WORKTREE_CANDIDATES_PER_REPO = 200;
|
|
11364
11583
|
function scanDuplicateWorktreeCandidates(opts) {
|
|
11365
11584
|
if (!opts.includeOrphans || !existsSync33(opts.worktreesDir)) return [];
|
|
11366
11585
|
const repos = /* @__PURE__ */ new Set();
|
|
11367
11586
|
for (const entry of opts.index.values()) {
|
|
11368
|
-
if (entry.run.repo) repos.add(
|
|
11587
|
+
if (entry.run.repo) repos.add(path47.resolve(entry.run.repo));
|
|
11369
11588
|
}
|
|
11370
11589
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
11371
11590
|
for (const entry of opts.index.values()) {
|
|
11372
|
-
indexedPaths.add(
|
|
11591
|
+
indexedPaths.add(path47.resolve(entry.worktreePath));
|
|
11373
11592
|
}
|
|
11374
11593
|
const candidates = [];
|
|
11375
11594
|
const seen = /* @__PURE__ */ new Set();
|
|
11376
11595
|
for (const repoRoot of repos) {
|
|
11596
|
+
let repoCandidateCount = 0;
|
|
11377
11597
|
let porcelain;
|
|
11378
11598
|
try {
|
|
11379
11599
|
porcelain = git(repoRoot, ["worktree", "list", "--porcelain"], { allowFailure: true });
|
|
@@ -11382,18 +11602,19 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
11382
11602
|
}
|
|
11383
11603
|
const worktrees = parseWorktreePorcelain(porcelain);
|
|
11384
11604
|
for (const wt of worktrees) {
|
|
11385
|
-
|
|
11386
|
-
|
|
11605
|
+
if (repoCandidateCount >= MAX_DUPLICATE_WORKTREE_CANDIDATES_PER_REPO) break;
|
|
11606
|
+
const resolved = path47.resolve(wt.path);
|
|
11607
|
+
if (resolved === path47.resolve(repoRoot)) continue;
|
|
11387
11608
|
if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
|
|
11388
11609
|
if (indexedPaths.has(resolved)) continue;
|
|
11389
11610
|
if (seen.has(resolved)) continue;
|
|
11390
11611
|
if (!existsSync33(resolved)) continue;
|
|
11391
|
-
|
|
11392
|
-
const
|
|
11393
|
-
const parts = rel.split(path46.sep);
|
|
11612
|
+
const rel = path47.relative(opts.worktreesDir, resolved);
|
|
11613
|
+
const parts = rel.split(path47.sep);
|
|
11394
11614
|
const runId = parts[0];
|
|
11395
11615
|
const worker = parts[1] ?? "unknown";
|
|
11396
11616
|
seen.add(resolved);
|
|
11617
|
+
repoCandidateCount += 1;
|
|
11397
11618
|
candidates.push({
|
|
11398
11619
|
kind: "remove_worktree",
|
|
11399
11620
|
path: resolved,
|
|
@@ -11411,12 +11632,22 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
11411
11632
|
// src/cleanup-worktree-index.ts
|
|
11412
11633
|
init_run_store();
|
|
11413
11634
|
init_util();
|
|
11414
|
-
import
|
|
11635
|
+
import path48 from "node:path";
|
|
11636
|
+
function filterWorktreeIndexForRoot(index, harnessRoot) {
|
|
11637
|
+
const resolvedRoot = path48.resolve(harnessRoot);
|
|
11638
|
+
const scoped = /* @__PURE__ */ new Map();
|
|
11639
|
+
for (const [key, entry] of index) {
|
|
11640
|
+
const entryRoot = entry.harnessRoot ? path48.resolve(entry.harnessRoot) : null;
|
|
11641
|
+
if (entryRoot && entryRoot !== resolvedRoot) continue;
|
|
11642
|
+
scoped.set(key, entry);
|
|
11643
|
+
}
|
|
11644
|
+
return scoped;
|
|
11645
|
+
}
|
|
11415
11646
|
function buildWorktreeIndexAt(harnessRoot) {
|
|
11416
11647
|
const index = /* @__PURE__ */ new Map();
|
|
11417
11648
|
for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
|
|
11418
11649
|
for (const name of Object.keys(run.workers || {})) {
|
|
11419
|
-
const workerPath =
|
|
11650
|
+
const workerPath = path48.join(
|
|
11420
11651
|
runDirectoryAt(harnessRoot, run.id),
|
|
11421
11652
|
"workers",
|
|
11422
11653
|
safeSlug(name),
|
|
@@ -11424,9 +11655,9 @@ function buildWorktreeIndexAt(harnessRoot) {
|
|
|
11424
11655
|
);
|
|
11425
11656
|
const worker = readJson(workerPath, void 0);
|
|
11426
11657
|
if (!worker?.worktreePath) continue;
|
|
11427
|
-
index.set(
|
|
11658
|
+
index.set(path48.resolve(worker.worktreePath), {
|
|
11428
11659
|
harnessRoot,
|
|
11429
|
-
worktreePath:
|
|
11660
|
+
worktreePath: path48.resolve(worker.worktreePath),
|
|
11430
11661
|
runId: run.id,
|
|
11431
11662
|
workerName: name,
|
|
11432
11663
|
run,
|
|
@@ -11452,6 +11683,7 @@ function resolveHarnessRetention(options = {}) {
|
|
|
11452
11683
|
const execute = options.execute === true || options.execute !== false && envFlag3("KYNVER_CLEANUP_EXECUTE");
|
|
11453
11684
|
const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag3("KYNVER_CLEANUP_SKIP_FINALIZE");
|
|
11454
11685
|
const nodeModulesAgeMs = options.nodeModulesAgeMs ?? envMs("KYNVER_CLEANUP_NODE_MODULES_AGE_MS", DEFAULT_NODE_MODULES_AGE_MS);
|
|
11686
|
+
const scanDependencyCaches = options.scanDependencyCaches ?? (options.nodeModulesAgeMs !== void 0 || process.env.KYNVER_CLEANUP_NODE_MODULES_AGE_MS != null);
|
|
11455
11687
|
const worktreesAgeMs = options.worktreesAgeMs ?? envMs("KYNVER_CLEANUP_WORKTREES_AGE_MS", 0);
|
|
11456
11688
|
const terminalWorktreesAgeMs = options.terminalWorktreesAgeMs ?? envMs("KYNVER_CLEANUP_TERMINAL_WORKTREES_AGE_MS", DEFAULT_TERMINAL_WORKTREES_AGE_MS);
|
|
11457
11689
|
const runDirectoriesAgeMs = options.runDirectoriesAgeMs ?? envMs("KYNVER_CLEANUP_RUN_DIRECTORIES_AGE_MS", DEFAULT_RUN_DIRECTORIES_AGE_MS);
|
|
@@ -11467,6 +11699,7 @@ function resolveHarnessRetention(options = {}) {
|
|
|
11467
11699
|
return {
|
|
11468
11700
|
execute,
|
|
11469
11701
|
finalizeStaleRuns: finalizeStaleRuns2,
|
|
11702
|
+
scanDependencyCaches,
|
|
11470
11703
|
nodeModulesAgeMs,
|
|
11471
11704
|
worktreesAgeMs: worktreesAgeMs > 0 ? worktreesAgeMs : 0,
|
|
11472
11705
|
terminalWorktreesAgeMs: terminalWorktreesAgeMs >= 0 ? terminalWorktreesAgeMs : 0,
|
|
@@ -11490,35 +11723,38 @@ function resolvePipelineHarnessRetention(runId) {
|
|
|
11490
11723
|
runDirectoriesAgeMs: scopeAll ? DEFAULT_RUN_DIRECTORIES_AGE_MS : void 0,
|
|
11491
11724
|
includeOrphans: scopeAll ? true : void 0,
|
|
11492
11725
|
finalizeStaleRuns: true,
|
|
11493
|
-
accountBytes: true
|
|
11726
|
+
accountBytes: true,
|
|
11727
|
+
scanDependencyCaches: true
|
|
11494
11728
|
});
|
|
11495
11729
|
}
|
|
11496
11730
|
|
|
11497
11731
|
// src/cleanup-orphan-safety.ts
|
|
11498
11732
|
init_git();
|
|
11499
|
-
import { existsSync as existsSync34
|
|
11500
|
-
import
|
|
11733
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
11734
|
+
import path49 from "node:path";
|
|
11735
|
+
init_util();
|
|
11501
11736
|
var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
|
|
11502
11737
|
function assessOrphanWorktreeSafety(input) {
|
|
11503
11738
|
const now = input.now ?? Date.now();
|
|
11504
11739
|
const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
|
|
11505
11740
|
if (!existsSync34(input.worktreePath)) return null;
|
|
11506
11741
|
if (input.runId && input.workerName) {
|
|
11507
|
-
const
|
|
11742
|
+
const workerDir = path49.join(
|
|
11508
11743
|
input.harnessRoot,
|
|
11509
11744
|
"runs",
|
|
11510
11745
|
input.runId,
|
|
11511
11746
|
"workers",
|
|
11512
|
-
input.workerName
|
|
11513
|
-
"heartbeat.jsonl"
|
|
11747
|
+
input.workerName
|
|
11514
11748
|
);
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11749
|
+
const worker = readJson(
|
|
11750
|
+
path49.join(workerDir, "worker.json"),
|
|
11751
|
+
void 0
|
|
11752
|
+
);
|
|
11753
|
+
if (worker && heartbeatContentIsFresh(worker, now, heartbeatFreshMs)) {
|
|
11754
|
+
return "active_worker";
|
|
11519
11755
|
}
|
|
11520
11756
|
}
|
|
11521
|
-
const gitDir =
|
|
11757
|
+
const gitDir = path49.join(input.worktreePath, ".git");
|
|
11522
11758
|
if (!existsSync34(gitDir)) return null;
|
|
11523
11759
|
const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
|
|
11524
11760
|
if (porcelain.status !== 0) return "pr_or_unmerged_commits";
|
|
@@ -11549,8 +11785,8 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
11549
11785
|
|
|
11550
11786
|
// src/harness-storage-snapshot.ts
|
|
11551
11787
|
init_paths();
|
|
11552
|
-
import { existsSync as existsSync35, readdirSync as
|
|
11553
|
-
import
|
|
11788
|
+
import { existsSync as existsSync35, readdirSync as readdirSync13, statSync as statSync11 } from "node:fs";
|
|
11789
|
+
import path50 from "node:path";
|
|
11554
11790
|
function harnessStorageSnapshot(opts = {}) {
|
|
11555
11791
|
const harnessRoot = normalizeHarnessRoot(opts.harnessRoot ?? resolveHarnessRoot());
|
|
11556
11792
|
const worktreesDir = harnessWorktreesDir(harnessRoot);
|
|
@@ -11573,7 +11809,7 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
11573
11809
|
let oldestMs = null;
|
|
11574
11810
|
let entries;
|
|
11575
11811
|
try {
|
|
11576
|
-
entries =
|
|
11812
|
+
entries = readdirSync13(worktreesDir, { withFileTypes: true });
|
|
11577
11813
|
} catch {
|
|
11578
11814
|
return {
|
|
11579
11815
|
harnessRoot,
|
|
@@ -11588,14 +11824,14 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
11588
11824
|
for (const runEntry of entries) {
|
|
11589
11825
|
if (!runEntry.isDirectory()) continue;
|
|
11590
11826
|
runCount += 1;
|
|
11591
|
-
const runPath =
|
|
11827
|
+
const runPath = path50.join(worktreesDir, runEntry.name);
|
|
11592
11828
|
try {
|
|
11593
|
-
const st =
|
|
11829
|
+
const st = statSync11(runPath);
|
|
11594
11830
|
oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
|
|
11595
11831
|
} catch {
|
|
11596
11832
|
}
|
|
11597
11833
|
try {
|
|
11598
|
-
for (const workerEntry of
|
|
11834
|
+
for (const workerEntry of readdirSync13(runPath, { withFileTypes: true })) {
|
|
11599
11835
|
if (workerEntry.isDirectory()) workerCount += 1;
|
|
11600
11836
|
}
|
|
11601
11837
|
} catch {
|
|
@@ -11626,10 +11862,10 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
11626
11862
|
init_paths();
|
|
11627
11863
|
import { existsSync as existsSync36 } from "node:fs";
|
|
11628
11864
|
import { homedir as homedir13 } from "node:os";
|
|
11629
|
-
import
|
|
11865
|
+
import path51 from "node:path";
|
|
11630
11866
|
var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
|
|
11631
11867
|
"/var/tmp/kynver-harness",
|
|
11632
|
-
|
|
11868
|
+
path51.join(homedir13(), ".openclaw", "harness")
|
|
11633
11869
|
];
|
|
11634
11870
|
function addRoot(seen, roots, candidate) {
|
|
11635
11871
|
if (!candidate?.trim()) return;
|
|
@@ -11651,7 +11887,7 @@ function resolveHarnessScanRoots(options = {}) {
|
|
|
11651
11887
|
for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
|
|
11652
11888
|
if (shouldScanWellKnownRoots(options)) {
|
|
11653
11889
|
for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
|
|
11654
|
-
const resolved =
|
|
11890
|
+
const resolved = path51.resolve(candidate);
|
|
11655
11891
|
if (!seen.has(resolved) && existsSync36(resolved)) addRoot(seen, roots, resolved);
|
|
11656
11892
|
}
|
|
11657
11893
|
}
|
|
@@ -11730,15 +11966,52 @@ var CleanupGitRevCache = class {
|
|
|
11730
11966
|
}
|
|
11731
11967
|
};
|
|
11732
11968
|
|
|
11969
|
+
// src/cleanup-git-probe.ts
|
|
11970
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
11971
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
11972
|
+
import path52 from "node:path";
|
|
11973
|
+
var CLEANUP_GIT_PROBE_TIMEOUT_MS = 5e3;
|
|
11974
|
+
function cleanupGitCapture(cwd, args) {
|
|
11975
|
+
if (!existsSync37(path52.join(cwd, ".git"))) {
|
|
11976
|
+
return { status: null, stdout: "", stderr: "", error: "not_a_git_repo" };
|
|
11977
|
+
}
|
|
11978
|
+
try {
|
|
11979
|
+
const res = spawnSync7("git", args, {
|
|
11980
|
+
cwd,
|
|
11981
|
+
encoding: "utf8",
|
|
11982
|
+
timeout: CLEANUP_GIT_PROBE_TIMEOUT_MS
|
|
11983
|
+
});
|
|
11984
|
+
const timedOut = res.error?.message?.includes("ETIMEDOUT") === true;
|
|
11985
|
+
return {
|
|
11986
|
+
status: timedOut ? null : res.status,
|
|
11987
|
+
stdout: res.stdout || "",
|
|
11988
|
+
stderr: res.stderr || "",
|
|
11989
|
+
error: timedOut ? "git_timeout" : res.error ? res.error.message : null
|
|
11990
|
+
};
|
|
11991
|
+
} catch (error) {
|
|
11992
|
+
return {
|
|
11993
|
+
status: null,
|
|
11994
|
+
stdout: "",
|
|
11995
|
+
stderr: "",
|
|
11996
|
+
error: error.message
|
|
11997
|
+
};
|
|
11998
|
+
}
|
|
11999
|
+
}
|
|
12000
|
+
function gitStatusShortForCleanup(worktreePath) {
|
|
12001
|
+
const captured = cleanupGitCapture(worktreePath, ["status", "--short"]);
|
|
12002
|
+
if (captured.error === "not_a_git_repo") return [];
|
|
12003
|
+
if (captured.error === "git_timeout" || captured.status !== 0) return null;
|
|
12004
|
+
return captured.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
12005
|
+
}
|
|
12006
|
+
|
|
11733
12007
|
// src/cleanup-git-status-cache.ts
|
|
11734
|
-
init_git();
|
|
11735
12008
|
var CleanupGitStatusCache = class {
|
|
11736
12009
|
cache = /* @__PURE__ */ new Map();
|
|
11737
12010
|
porcelain(worktreePath) {
|
|
11738
12011
|
const resolved = worktreePath;
|
|
11739
12012
|
const cached = this.cache.get(resolved);
|
|
11740
12013
|
if (cached !== void 0) return cached;
|
|
11741
|
-
const lines =
|
|
12014
|
+
const lines = gitStatusShortForCleanup(resolved) ?? [];
|
|
11742
12015
|
this.cache.set(resolved, lines);
|
|
11743
12016
|
return lines;
|
|
11744
12017
|
}
|
|
@@ -11843,9 +12116,9 @@ function mergeWorktreeIndexes(scanRoots) {
|
|
|
11843
12116
|
}
|
|
11844
12117
|
function worktreePathForCandidate(candidate, worktreesDir) {
|
|
11845
12118
|
if (candidate.runId && candidate.worker) {
|
|
11846
|
-
return
|
|
12119
|
+
return path53.join(worktreesDir, candidate.runId, candidate.worker);
|
|
11847
12120
|
}
|
|
11848
|
-
return
|
|
12121
|
+
return path53.resolve(candidate.path, "..");
|
|
11849
12122
|
}
|
|
11850
12123
|
function runHarnessCleanup(options = {}) {
|
|
11851
12124
|
let retention = resolveHarnessRetention(options);
|
|
@@ -11874,7 +12147,8 @@ function runHarnessCleanup(options = {}) {
|
|
|
11874
12147
|
for (const harnessRoot of paths.scanRoots) {
|
|
11875
12148
|
if (atSweepCap()) break;
|
|
11876
12149
|
emitCleanupProgress("root", harnessRoot);
|
|
11877
|
-
const worktreesDir =
|
|
12150
|
+
const worktreesDir = path53.join(harnessRoot, "worktrees");
|
|
12151
|
+
const rootIndex = filterWorktreeIndexForRoot(index, harnessRoot);
|
|
11878
12152
|
const scanOpts = {
|
|
11879
12153
|
harnessRoot,
|
|
11880
12154
|
worktreesDir,
|
|
@@ -11882,11 +12156,14 @@ function runHarnessCleanup(options = {}) {
|
|
|
11882
12156
|
worktreesAgeMs: retention.worktreesAgeMs,
|
|
11883
12157
|
includeOrphans: retention.includeOrphans,
|
|
11884
12158
|
runIdFilter: retention.runIdFilter,
|
|
11885
|
-
index,
|
|
12159
|
+
index: rootIndex,
|
|
11886
12160
|
now: paths.now
|
|
11887
12161
|
};
|
|
11888
|
-
const dependencyCandidates = scanDependencyCacheCandidates(scanOpts);
|
|
11889
|
-
emitCleanupProgress(
|
|
12162
|
+
const dependencyCandidates = retention.scanDependencyCaches ? scanDependencyCacheCandidates(scanOpts) : [];
|
|
12163
|
+
emitCleanupProgress(
|
|
12164
|
+
"dependency",
|
|
12165
|
+
retention.scanDependencyCaches ? `${dependencyCandidates.length} cache candidate(s) at ${harnessRoot}` : "skipped (worktree-only sweep)"
|
|
12166
|
+
);
|
|
11890
12167
|
let dependencyProcessed = 0;
|
|
11891
12168
|
for (const raw of dependencyCandidates) {
|
|
11892
12169
|
if (atSweepCap()) break;
|
|
@@ -11894,7 +12171,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
11894
12171
|
if (dependencyProcessed % 50 === 0) {
|
|
11895
12172
|
emitCleanupProgress("dependency", `${dependencyProcessed}/${dependencyCandidates.length} evaluated`);
|
|
11896
12173
|
}
|
|
11897
|
-
const resolved =
|
|
12174
|
+
const resolved = path53.resolve(raw.path);
|
|
11898
12175
|
if (processedPaths.has(resolved)) continue;
|
|
11899
12176
|
processedPaths.add(resolved);
|
|
11900
12177
|
const candidate = { ...raw, path: resolved };
|
|
@@ -11905,7 +12182,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
11905
12182
|
continue;
|
|
11906
12183
|
}
|
|
11907
12184
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
11908
|
-
const indexed =
|
|
12185
|
+
const indexed = rootIndex.get(path53.resolve(worktreePath)) ?? null;
|
|
11909
12186
|
const guardReason = skipDependencyCacheRemoval({
|
|
11910
12187
|
indexed,
|
|
11911
12188
|
includeOrphans: true,
|
|
@@ -11928,9 +12205,9 @@ function runHarnessCleanup(options = {}) {
|
|
|
11928
12205
|
)
|
|
11929
12206
|
);
|
|
11930
12207
|
}
|
|
11931
|
-
for (const raw of scanBuildCacheCandidates(scanOpts)) {
|
|
12208
|
+
if (retention.scanDependencyCaches) for (const raw of scanBuildCacheCandidates(scanOpts)) {
|
|
11932
12209
|
if (atSweepCap()) break;
|
|
11933
|
-
const resolved =
|
|
12210
|
+
const resolved = path53.resolve(raw.path);
|
|
11934
12211
|
if (processedPaths.has(resolved)) continue;
|
|
11935
12212
|
processedPaths.add(resolved);
|
|
11936
12213
|
const candidate = { ...raw, path: resolved };
|
|
@@ -11941,7 +12218,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
11941
12218
|
continue;
|
|
11942
12219
|
}
|
|
11943
12220
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
11944
|
-
const indexed =
|
|
12221
|
+
const indexed = rootIndex.get(path53.resolve(worktreePath)) ?? null;
|
|
11945
12222
|
const guardReason = skipBuildCacheRemoval({
|
|
11946
12223
|
indexed,
|
|
11947
12224
|
includeOrphans: true,
|
|
@@ -11977,11 +12254,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
11977
12254
|
if (worktreeProcessed % 50 === 0) {
|
|
11978
12255
|
emitCleanupProgress("worktrees", `${worktreeProcessed}/${worktreeCandidates.length} evaluated`);
|
|
11979
12256
|
}
|
|
11980
|
-
const resolved =
|
|
12257
|
+
const resolved = path53.resolve(raw.path);
|
|
11981
12258
|
if (worktreeSeen.has(resolved)) continue;
|
|
11982
12259
|
worktreeSeen.add(resolved);
|
|
11983
12260
|
const candidate = { ...raw, path: resolved };
|
|
11984
|
-
const indexed =
|
|
12261
|
+
const indexed = rootIndex.get(path53.resolve(candidate.path)) ?? null;
|
|
11985
12262
|
const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
|
|
11986
12263
|
worktreePath: candidate.path,
|
|
11987
12264
|
harnessRoot,
|
|
@@ -11991,14 +12268,17 @@ function runHarnessCleanup(options = {}) {
|
|
|
11991
12268
|
});
|
|
11992
12269
|
const guardSkip = skipWorktreeRemoval({
|
|
11993
12270
|
indexed,
|
|
11994
|
-
worktreePath:
|
|
12271
|
+
worktreePath: path53.resolve(candidate.path),
|
|
11995
12272
|
includeOrphans: retention.includeOrphans,
|
|
11996
12273
|
worktreesAgeMs: retention.worktreesAgeMs,
|
|
11997
12274
|
terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
|
|
11998
12275
|
ageMs: candidate.ageMs,
|
|
11999
12276
|
orphanSafety,
|
|
12000
12277
|
worktreeRemovalGuard: options.worktreeRemovalGuard,
|
|
12001
|
-
liveness
|
|
12278
|
+
liveness,
|
|
12279
|
+
now: paths.now,
|
|
12280
|
+
harnessRoot,
|
|
12281
|
+
writeSalvageEvidence: retention.execute
|
|
12002
12282
|
});
|
|
12003
12283
|
if (guardSkip) {
|
|
12004
12284
|
const { reason: guardReason, detail: guardDetail } = normalizeGuardSkip(guardSkip);
|
|
@@ -12023,11 +12303,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
12023
12303
|
now: paths.now
|
|
12024
12304
|
})) {
|
|
12025
12305
|
if (atSweepCap()) break;
|
|
12026
|
-
const resolved =
|
|
12306
|
+
const resolved = path53.resolve(raw.path);
|
|
12027
12307
|
if (processedPaths.has(resolved)) continue;
|
|
12028
12308
|
processedPaths.add(resolved);
|
|
12029
12309
|
const candidate = { ...raw, path: resolved };
|
|
12030
|
-
const runId = candidate.runId ??
|
|
12310
|
+
const runId = candidate.runId ?? path53.basename(resolved);
|
|
12031
12311
|
const dirSkip = skipRunDirectoryRemoval({
|
|
12032
12312
|
harnessRoot,
|
|
12033
12313
|
runId,
|
|
@@ -12163,12 +12443,12 @@ function isPipelineCleanupEnabled() {
|
|
|
12163
12443
|
// src/installed-package-versions.ts
|
|
12164
12444
|
import { readFile } from "node:fs/promises";
|
|
12165
12445
|
import { homedir as homedir14 } from "node:os";
|
|
12166
|
-
import
|
|
12446
|
+
import path55 from "node:path";
|
|
12167
12447
|
|
|
12168
12448
|
// src/memory-cost-package-version-guard.ts
|
|
12169
12449
|
init_default_repo_discovery();
|
|
12170
|
-
import { existsSync as
|
|
12171
|
-
import
|
|
12450
|
+
import { existsSync as existsSync38, readFileSync as readFileSync13 } from "node:fs";
|
|
12451
|
+
import path54 from "node:path";
|
|
12172
12452
|
var MEMORY_COST_PACKAGE_MIN_VERSIONS = {
|
|
12173
12453
|
"@kynver-app/runtime": "0.1.83",
|
|
12174
12454
|
"@kynver-app/openclaw-agent-os": "0.1.43",
|
|
@@ -12230,8 +12510,8 @@ function resolveRepoRoot(cwd, explicitRepoRoot) {
|
|
|
12230
12510
|
(value) => Boolean(value?.trim())
|
|
12231
12511
|
);
|
|
12232
12512
|
for (const candidate of candidates) {
|
|
12233
|
-
const resolved =
|
|
12234
|
-
if (
|
|
12513
|
+
const resolved = path54.resolve(candidate);
|
|
12514
|
+
if (existsSync38(path54.join(resolved, "packages/kynver-runtime/package.json")) && existsSync38(path54.join(resolved, "package.json"))) {
|
|
12235
12515
|
return resolved;
|
|
12236
12516
|
}
|
|
12237
12517
|
}
|
|
@@ -12244,7 +12524,7 @@ function probeRepoPackageVersions(input = {}) {
|
|
|
12244
12524
|
if (!repoRoot) return {};
|
|
12245
12525
|
const out = {};
|
|
12246
12526
|
for (const packageName of MEMORY_COST_MANAGED_PACKAGES) {
|
|
12247
|
-
const packageJsonPath =
|
|
12527
|
+
const packageJsonPath = path54.join(repoRoot, REPO_PACKAGE_JSON_RELATIVE[packageName]);
|
|
12248
12528
|
const version = readPackageJsonVersion(packageJsonPath);
|
|
12249
12529
|
if (!version) continue;
|
|
12250
12530
|
out[packageName] = { version, source: "repo", path: packageJsonPath };
|
|
@@ -12364,12 +12644,12 @@ function unique(values) {
|
|
|
12364
12644
|
}
|
|
12365
12645
|
function moduleRoots() {
|
|
12366
12646
|
const home = homedir14();
|
|
12367
|
-
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ??
|
|
12368
|
-
const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ?
|
|
12647
|
+
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path55.join(home, ".openclaw", "npm");
|
|
12648
|
+
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"));
|
|
12369
12649
|
return unique([
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot :
|
|
12650
|
+
path55.join(openClawPrefix, "lib", "node_modules"),
|
|
12651
|
+
path55.join(openClawPrefix, "node_modules"),
|
|
12652
|
+
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path55.join(npmGlobalRoot, "lib", "node_modules")
|
|
12373
12653
|
]);
|
|
12374
12654
|
}
|
|
12375
12655
|
async function readVersion(packageJsonPath) {
|
|
@@ -12385,7 +12665,7 @@ function installedPackageJsonCandidates(packageName) {
|
|
|
12385
12665
|
const seen = /* @__PURE__ */ new Set();
|
|
12386
12666
|
const out = [];
|
|
12387
12667
|
for (const root of roots) {
|
|
12388
|
-
const candidate =
|
|
12668
|
+
const candidate = path55.join(root, packageName, "package.json");
|
|
12389
12669
|
if (seen.has(candidate)) continue;
|
|
12390
12670
|
seen.add(candidate);
|
|
12391
12671
|
out.push(candidate);
|
|
@@ -12411,12 +12691,12 @@ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new
|
|
|
12411
12691
|
}
|
|
12412
12692
|
|
|
12413
12693
|
// src/provider-evidence/exec.ts
|
|
12414
|
-
import { spawnSync as
|
|
12694
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12415
12695
|
var DEFAULT_CLI_TIMEOUT_MS = 1e4;
|
|
12416
12696
|
var MAX_CLI_BUFFER_BYTES = 4 * 1024 * 1024;
|
|
12417
12697
|
var defaultCliRunner = (cmd, args, opts) => {
|
|
12418
12698
|
try {
|
|
12419
|
-
const result =
|
|
12699
|
+
const result = spawnSync8(cmd, args, {
|
|
12420
12700
|
encoding: "utf8",
|
|
12421
12701
|
stdio: ["ignore", "pipe", "pipe"],
|
|
12422
12702
|
timeout: opts?.timeoutMs ?? DEFAULT_CLI_TIMEOUT_MS,
|
|
@@ -12716,10 +12996,10 @@ function collectProviderEvidence(wanted, opts = {}) {
|
|
|
12716
12996
|
// src/provider-evidence/wanted-store.ts
|
|
12717
12997
|
init_run_store();
|
|
12718
12998
|
init_util();
|
|
12719
|
-
import
|
|
12999
|
+
import path56 from "node:path";
|
|
12720
13000
|
var WANTED_FILE = "provider-evidence-wanted.json";
|
|
12721
13001
|
function wantedFilePath(runId) {
|
|
12722
|
-
return
|
|
13002
|
+
return path56.join(runDirectory(runId), WANTED_FILE);
|
|
12723
13003
|
}
|
|
12724
13004
|
function parseWantedItems(value) {
|
|
12725
13005
|
if (!Array.isArray(value)) return [];
|
|
@@ -12758,7 +13038,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
12758
13038
|
const outcomes = [];
|
|
12759
13039
|
for (const name of Object.keys(run.workers || {})) {
|
|
12760
13040
|
const worker = readJson(
|
|
12761
|
-
|
|
13041
|
+
path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
12762
13042
|
void 0
|
|
12763
13043
|
);
|
|
12764
13044
|
if (!worker?.taskId || worker.localOnly) continue;
|
|
@@ -12933,7 +13213,7 @@ import os11 from "node:os";
|
|
|
12933
13213
|
// src/chat/anthropic-credentials.ts
|
|
12934
13214
|
import { readFileSync as readFileSync14 } from "node:fs";
|
|
12935
13215
|
import { homedir as homedir15, platform } from "node:os";
|
|
12936
|
-
import
|
|
13216
|
+
import path58 from "node:path";
|
|
12937
13217
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
12938
13218
|
function parseClaudeCredentials(raw, now = Date.now()) {
|
|
12939
13219
|
try {
|
|
@@ -12961,17 +13241,18 @@ function readClaudeCliToken() {
|
|
|
12961
13241
|
}
|
|
12962
13242
|
}
|
|
12963
13243
|
try {
|
|
12964
|
-
const raw = readFileSync14(
|
|
13244
|
+
const raw = readFileSync14(path58.join(homedir15(), ".claude", ".credentials.json"), "utf8");
|
|
12965
13245
|
return parseClaudeCredentials(raw);
|
|
12966
13246
|
} catch {
|
|
12967
13247
|
return null;
|
|
12968
13248
|
}
|
|
12969
13249
|
}
|
|
12970
|
-
function resolveLocalAnthropicCredentials(env = process.env) {
|
|
13250
|
+
function resolveLocalAnthropicCredentials(env = process.env, opts = {}) {
|
|
12971
13251
|
const apiKey = env.ANTHROPIC_API_KEY?.trim();
|
|
12972
13252
|
if (apiKey) return { kind: "api_key", key: apiKey };
|
|
12973
|
-
const
|
|
12974
|
-
|
|
13253
|
+
const envOptIn = env.KYNVER_CHAT_USE_CLAUDE_OAUTH;
|
|
13254
|
+
const optedIn = opts.oauthOptIn === true || envOptIn === "1" || envOptIn === "true" || envOptIn === "yes";
|
|
13255
|
+
if (optedIn) {
|
|
12975
13256
|
const token = readClaudeCliToken();
|
|
12976
13257
|
if (token) return { kind: "oauth", token };
|
|
12977
13258
|
}
|
|
@@ -13157,20 +13438,43 @@ var CLAIM_WAIT_MS = 25e3;
|
|
|
13157
13438
|
var CLAIM_FETCH_TIMEOUT_MS = 32e3;
|
|
13158
13439
|
var ERROR_BACKOFF_MS = 5e3;
|
|
13159
13440
|
var AUTH_BACKOFF_MS = 6e4;
|
|
13441
|
+
var BRIDGE_REDISCOVER_MS = 5 * 6e4;
|
|
13442
|
+
var DISCOVERY_TIMEOUT_MS = 5e3;
|
|
13160
13443
|
var DEFAULT_CHAT_MODEL = "claude-sonnet-4-6";
|
|
13161
|
-
function
|
|
13162
|
-
const
|
|
13163
|
-
if (
|
|
13444
|
+
async function discoverBridgeUrl(apiBaseUrl, apiKey, env) {
|
|
13445
|
+
const override = env.KYNVER_RUNTIME_CHAT_BRIDGE_URL?.trim();
|
|
13446
|
+
if (override) return override;
|
|
13447
|
+
if (!apiBaseUrl) return null;
|
|
13448
|
+
const controller = new AbortController();
|
|
13449
|
+
const timer = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT_MS);
|
|
13450
|
+
try {
|
|
13451
|
+
const res = await fetch(`${apiBaseUrl.replace(/\/$/, "")}/api/runtime/chat-bridge`, {
|
|
13452
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
13453
|
+
signal: controller.signal
|
|
13454
|
+
});
|
|
13455
|
+
if (!res.ok) return null;
|
|
13456
|
+
const body = await res.json().catch(() => null);
|
|
13457
|
+
return typeof body?.bridgeUrl === "string" && body.bridgeUrl.trim() ? body.bridgeUrl.trim() : null;
|
|
13458
|
+
} catch {
|
|
13459
|
+
return null;
|
|
13460
|
+
} finally {
|
|
13461
|
+
clearTimeout(timer);
|
|
13462
|
+
}
|
|
13463
|
+
}
|
|
13464
|
+
async function resolveChatBridgeTarget(env = process.env) {
|
|
13164
13465
|
const config = loadUserConfig();
|
|
13165
13466
|
const agentOsId = config.agentOsId?.trim();
|
|
13166
13467
|
const apiKey = loadApiKey();
|
|
13167
13468
|
if (!agentOsId || !apiKey) return null;
|
|
13469
|
+
const bridgeUrl = await discoverBridgeUrl(config.apiBaseUrl, apiKey, env);
|
|
13470
|
+
if (!bridgeUrl) return null;
|
|
13168
13471
|
return {
|
|
13169
13472
|
bridgeUrl: bridgeUrl.replace(/\/$/, ""),
|
|
13170
13473
|
agentOsId,
|
|
13171
13474
|
apiKey,
|
|
13172
13475
|
boxId: os11.hostname(),
|
|
13173
|
-
model: env.KYNVER_CHAT_MODEL?.trim() || config.defaultModel?.trim() || DEFAULT_CHAT_MODEL
|
|
13476
|
+
model: env.KYNVER_CHAT_MODEL?.trim() || config.defaultModel?.trim() || DEFAULT_CHAT_MODEL,
|
|
13477
|
+
useClaudeOauth: config.chatUseClaudeOauth === true
|
|
13174
13478
|
};
|
|
13175
13479
|
}
|
|
13176
13480
|
async function claimOnce(target, shouldStop) {
|
|
@@ -13231,7 +13535,7 @@ async function executeChatTurn(target, turn) {
|
|
|
13231
13535
|
});
|
|
13232
13536
|
};
|
|
13233
13537
|
const batcher = new DeltaBatcher(sendBatch);
|
|
13234
|
-
const creds = resolveLocalAnthropicCredentials();
|
|
13538
|
+
const creds = resolveLocalAnthropicCredentials(process.env, { oauthOptIn: target.useClaudeOauth });
|
|
13235
13539
|
if (!creds) {
|
|
13236
13540
|
await pushEvents(target, turn.turnId, [
|
|
13237
13541
|
{ seq: 0, kind: "error", error: "no_local_credentials" }
|
|
@@ -13276,15 +13580,31 @@ async function sleep2(ms, shouldStop) {
|
|
|
13276
13580
|
}
|
|
13277
13581
|
}
|
|
13278
13582
|
async function runChatClaimLoop(opts) {
|
|
13279
|
-
|
|
13280
|
-
if (!target)
|
|
13583
|
+
let target = await resolveChatBridgeTarget();
|
|
13584
|
+
if (!target) {
|
|
13585
|
+
console.error(
|
|
13586
|
+
JSON.stringify({
|
|
13587
|
+
event: "chat_claim_loop_waiting",
|
|
13588
|
+
reason: !loadApiKey() || !loadUserConfig().agentOsId?.trim() ? "machine_not_linked" : "bridge_not_discovered (rollout off, discovery route missing/404, or API unreachable)",
|
|
13589
|
+
rediscoverMs: BRIDGE_REDISCOVER_MS
|
|
13590
|
+
})
|
|
13591
|
+
);
|
|
13592
|
+
}
|
|
13593
|
+
while (!target && !opts.shouldStop()) {
|
|
13594
|
+
if (!loadApiKey() || !loadUserConfig().agentOsId?.trim()) return;
|
|
13595
|
+
await sleep2(BRIDGE_REDISCOVER_MS, opts.shouldStop);
|
|
13596
|
+
if (opts.shouldStop()) return;
|
|
13597
|
+
target = await resolveChatBridgeTarget();
|
|
13598
|
+
}
|
|
13599
|
+
if (!target || opts.shouldStop()) return;
|
|
13281
13600
|
console.error(
|
|
13282
13601
|
JSON.stringify({
|
|
13283
13602
|
event: "chat_claim_loop_start",
|
|
13284
13603
|
bridgeUrl: target.bridgeUrl,
|
|
13285
13604
|
agentOsId: target.agentOsId,
|
|
13286
13605
|
boxId: target.boxId,
|
|
13287
|
-
model: target.model
|
|
13606
|
+
model: target.model,
|
|
13607
|
+
useClaudeOauth: target.useClaudeOauth
|
|
13288
13608
|
})
|
|
13289
13609
|
);
|
|
13290
13610
|
while (!opts.shouldStop()) {
|
|
@@ -13369,10 +13689,34 @@ async function runDaemon(args) {
|
|
|
13369
13689
|
})
|
|
13370
13690
|
);
|
|
13371
13691
|
});
|
|
13692
|
+
let credentialMissingLogged = false;
|
|
13372
13693
|
while (!stopping) {
|
|
13373
13694
|
try {
|
|
13374
13695
|
writeDaemonHeartbeat({ agentOsId, runId });
|
|
13375
|
-
|
|
13696
|
+
const credential = await tryResolveCallbackSecretWithMint(
|
|
13697
|
+
args.secret ? String(args.secret) : void 0,
|
|
13698
|
+
agentOsId
|
|
13699
|
+
);
|
|
13700
|
+
if (!credential.ok) {
|
|
13701
|
+
if (!credentialMissingLogged) {
|
|
13702
|
+
credentialMissingLogged = true;
|
|
13703
|
+
console.error(
|
|
13704
|
+
JSON.stringify({
|
|
13705
|
+
event: "daemon_runner_credential_missing",
|
|
13706
|
+
agentOsId,
|
|
13707
|
+
reason: credential.reason,
|
|
13708
|
+
remedy: `run \`kynver runner credential --agent-os-id ${agentOsId}\` (or \`kynver bootstrap\`); ticks resume automatically once the credential exists`
|
|
13709
|
+
})
|
|
13710
|
+
);
|
|
13711
|
+
}
|
|
13712
|
+
await awaitDaemonBackoff(intervalMs, () => stopping);
|
|
13713
|
+
continue;
|
|
13714
|
+
}
|
|
13715
|
+
if (credentialMissingLogged) {
|
|
13716
|
+
credentialMissingLogged = false;
|
|
13717
|
+
console.error(JSON.stringify({ event: "daemon_runner_credential_recovered", agentOsId }));
|
|
13718
|
+
}
|
|
13719
|
+
if (cronEnv.tickEnabled) {
|
|
13376
13720
|
const cronTick = await runKynverCronTick({
|
|
13377
13721
|
env: cronEnv,
|
|
13378
13722
|
agentOsIdFilter: agentOsId
|
|
@@ -13399,21 +13743,152 @@ async function runDaemon(args) {
|
|
|
13399
13743
|
console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
|
|
13400
13744
|
}
|
|
13401
13745
|
|
|
13746
|
+
// src/daemon-keeper.ts
|
|
13747
|
+
init_config();
|
|
13748
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
13749
|
+
init_util();
|
|
13750
|
+
var DEFAULT_STALL_MS = 15 * 6e4;
|
|
13751
|
+
var STARTUP_GRACE_MS = 2 * 6e4;
|
|
13752
|
+
var KILL_GRACE_MS = 1e4;
|
|
13753
|
+
var BACKOFF_BASE_MS = 5e3;
|
|
13754
|
+
var BACKOFF_CAP_MS = 5 * 6e4;
|
|
13755
|
+
var HEALTHY_RESET_MS = 30 * 6e4;
|
|
13756
|
+
var POLL_MS = 5e3;
|
|
13757
|
+
function resolveKeeperStallMs(flag, env = process.env) {
|
|
13758
|
+
const fromFlag = typeof flag === "string" ? Number.parseInt(flag, 10) : NaN;
|
|
13759
|
+
if (Number.isFinite(fromFlag) && fromFlag > 0) return fromFlag;
|
|
13760
|
+
const fromEnv = Number.parseInt(env.KYNVER_DAEMON_STALL_MS ?? "", 10);
|
|
13761
|
+
if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv;
|
|
13762
|
+
return DEFAULT_STALL_MS;
|
|
13763
|
+
}
|
|
13764
|
+
function shouldRunDaemonKeeper(args, env = process.env) {
|
|
13765
|
+
if (args.keeperChild === true || args.keeperChild === "true") return false;
|
|
13766
|
+
if (args.noSupervise === true || args.noSupervise === "true") return false;
|
|
13767
|
+
if (args.supervised === "false") return false;
|
|
13768
|
+
const envFlag5 = (env.KYNVER_DAEMON_SUPERVISED ?? "").trim().toLowerCase();
|
|
13769
|
+
if (envFlag5 === "0" || envFlag5 === "false" || envFlag5 === "no" || envFlag5 === "off") {
|
|
13770
|
+
return false;
|
|
13771
|
+
}
|
|
13772
|
+
return true;
|
|
13773
|
+
}
|
|
13774
|
+
function nextKeeperBackoffMs(consecutiveFailures, base = BACKOFF_BASE_MS, cap = BACKOFF_CAP_MS) {
|
|
13775
|
+
const exp = Math.min(Math.max(consecutiveFailures, 1) - 1, 10);
|
|
13776
|
+
return Math.min(base * 2 ** exp, cap);
|
|
13777
|
+
}
|
|
13778
|
+
function keeperRunWasHealthy(startedAtMs, endedAtMs, healthyMs = HEALTHY_RESET_MS) {
|
|
13779
|
+
return endedAtMs - startedAtMs >= healthyMs;
|
|
13780
|
+
}
|
|
13781
|
+
function keeperLog(event, detail = {}) {
|
|
13782
|
+
console.error(JSON.stringify({ event: `daemon_keeper_${event}`, ...detail }));
|
|
13783
|
+
}
|
|
13784
|
+
function buildKeeperChildArgv(argv) {
|
|
13785
|
+
const out = [];
|
|
13786
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
13787
|
+
const arg = argv[i];
|
|
13788
|
+
if (arg === "--supervised" || arg === "--no-supervise" || arg === "--keeper-child") continue;
|
|
13789
|
+
if (arg === "--stall-ms") {
|
|
13790
|
+
if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) i += 1;
|
|
13791
|
+
continue;
|
|
13792
|
+
}
|
|
13793
|
+
if (arg.startsWith("--stall-ms=") || arg.startsWith("--supervised=")) continue;
|
|
13794
|
+
out.push(arg);
|
|
13795
|
+
}
|
|
13796
|
+
out.push("--keeper-child");
|
|
13797
|
+
return out;
|
|
13798
|
+
}
|
|
13799
|
+
async function runDaemonKeeper(args, rawArgv = process.argv.slice(2)) {
|
|
13800
|
+
const agentOsId = String(
|
|
13801
|
+
required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id")
|
|
13802
|
+
);
|
|
13803
|
+
const stallMs = resolveKeeperStallMs(args.stallMs);
|
|
13804
|
+
const childArgv = buildKeeperChildArgv(rawArgv);
|
|
13805
|
+
const cliEntry = process.argv[1];
|
|
13806
|
+
let stopping = false;
|
|
13807
|
+
let child = null;
|
|
13808
|
+
let consecutiveFailures = 0;
|
|
13809
|
+
const stop = (signal) => {
|
|
13810
|
+
stopping = true;
|
|
13811
|
+
keeperLog("stop", { signal });
|
|
13812
|
+
if (child?.pid) child.kill(signal);
|
|
13813
|
+
};
|
|
13814
|
+
process.on("SIGINT", () => stop("SIGINT"));
|
|
13815
|
+
process.on("SIGTERM", () => stop("SIGTERM"));
|
|
13816
|
+
keeperLog("start", { agentOsId, stallMs, childArgv });
|
|
13817
|
+
while (!stopping) {
|
|
13818
|
+
const startedAt = Date.now();
|
|
13819
|
+
let exited = false;
|
|
13820
|
+
let exitCode = null;
|
|
13821
|
+
let exitSignal = null;
|
|
13822
|
+
child = spawn6(process.execPath, [cliEntry, ...childArgv], {
|
|
13823
|
+
stdio: "inherit",
|
|
13824
|
+
env: process.env
|
|
13825
|
+
});
|
|
13826
|
+
keeperLog("child_spawned", { pid: child.pid ?? null });
|
|
13827
|
+
child.on("exit", (code, signal) => {
|
|
13828
|
+
exited = true;
|
|
13829
|
+
exitCode = code;
|
|
13830
|
+
exitSignal = signal;
|
|
13831
|
+
});
|
|
13832
|
+
while (!exited && !stopping) {
|
|
13833
|
+
await sleepMsAsync(POLL_MS);
|
|
13834
|
+
if (exited || stopping) break;
|
|
13835
|
+
if (Date.now() - startedAt < STARTUP_GRACE_MS) continue;
|
|
13836
|
+
const beat = readDaemonHeartbeat(agentOsId);
|
|
13837
|
+
const ownBeat = beat && beat.pid === child.pid ? beat : null;
|
|
13838
|
+
if (ownBeat && isDaemonHeartbeatStale(ownBeat, stallMs)) {
|
|
13839
|
+
keeperLog("stall_detected", {
|
|
13840
|
+
pid: child.pid ?? null,
|
|
13841
|
+
lastBeatAt: ownBeat.observedAt,
|
|
13842
|
+
stallMs
|
|
13843
|
+
});
|
|
13844
|
+
child.kill("SIGTERM");
|
|
13845
|
+
await sleepMsAsync(KILL_GRACE_MS);
|
|
13846
|
+
if (!exited) child.kill("SIGKILL");
|
|
13847
|
+
break;
|
|
13848
|
+
}
|
|
13849
|
+
if (!ownBeat && Date.now() - startedAt > stallMs + STARTUP_GRACE_MS) {
|
|
13850
|
+
keeperLog("no_heartbeat_detected", { pid: child.pid ?? null, stallMs });
|
|
13851
|
+
child.kill("SIGTERM");
|
|
13852
|
+
await sleepMsAsync(KILL_GRACE_MS);
|
|
13853
|
+
if (!exited) child.kill("SIGKILL");
|
|
13854
|
+
break;
|
|
13855
|
+
}
|
|
13856
|
+
}
|
|
13857
|
+
while (!exited && !stopping) {
|
|
13858
|
+
await sleepMsAsync(POLL_MS);
|
|
13859
|
+
}
|
|
13860
|
+
if (stopping) break;
|
|
13861
|
+
const endedAt = Date.now();
|
|
13862
|
+
if (keeperRunWasHealthy(startedAt, endedAt)) consecutiveFailures = 0;
|
|
13863
|
+
consecutiveFailures += 1;
|
|
13864
|
+
const backoff = nextKeeperBackoffMs(consecutiveFailures);
|
|
13865
|
+
keeperLog("child_exited", {
|
|
13866
|
+
code: exitCode,
|
|
13867
|
+
signal: exitSignal,
|
|
13868
|
+
uptimeMs: endedAt - startedAt,
|
|
13869
|
+
consecutiveFailures,
|
|
13870
|
+
respawnInMs: backoff
|
|
13871
|
+
});
|
|
13872
|
+
await sleepMsAsync(backoff);
|
|
13873
|
+
}
|
|
13874
|
+
keeperLog("stopped", { agentOsId });
|
|
13875
|
+
}
|
|
13876
|
+
|
|
13402
13877
|
// src/worktree.ts
|
|
13403
13878
|
init_git();
|
|
13404
13879
|
init_run_store();
|
|
13405
|
-
import { existsSync as
|
|
13406
|
-
import
|
|
13880
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync9 } from "node:fs";
|
|
13881
|
+
import path60 from "node:path";
|
|
13407
13882
|
|
|
13408
13883
|
// src/run-list.ts
|
|
13409
13884
|
init_run_store();
|
|
13410
13885
|
init_run_worker_index();
|
|
13411
|
-
import { existsSync as
|
|
13412
|
-
import
|
|
13886
|
+
import { existsSync as existsSync39, readFileSync as readFileSync15 } from "node:fs";
|
|
13887
|
+
import path59 from "node:path";
|
|
13413
13888
|
init_status();
|
|
13414
13889
|
init_util();
|
|
13415
13890
|
function heartbeatByteLength(heartbeatPath) {
|
|
13416
|
-
if (!heartbeatPath || !
|
|
13891
|
+
if (!heartbeatPath || !existsSync39(heartbeatPath)) return 0;
|
|
13417
13892
|
try {
|
|
13418
13893
|
return readFileSync15(heartbeatPath, "utf8").trim().length;
|
|
13419
13894
|
} catch {
|
|
@@ -13421,7 +13896,7 @@ function heartbeatByteLength(heartbeatPath) {
|
|
|
13421
13896
|
}
|
|
13422
13897
|
}
|
|
13423
13898
|
function workerEvidence(run, workerName) {
|
|
13424
|
-
const workerPath =
|
|
13899
|
+
const workerPath = path59.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
|
|
13425
13900
|
const worker = readJson(workerPath, void 0);
|
|
13426
13901
|
if (!worker) {
|
|
13427
13902
|
return {
|
|
@@ -13478,7 +13953,7 @@ function aggregateRunAttention(workers) {
|
|
|
13478
13953
|
function countOpenWorkers(run) {
|
|
13479
13954
|
let open = 0;
|
|
13480
13955
|
for (const name of listRunWorkerNames(run)) {
|
|
13481
|
-
const workerPath =
|
|
13956
|
+
const workerPath = path59.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
13482
13957
|
const worker = readJson(workerPath, void 0);
|
|
13483
13958
|
if (!worker) continue;
|
|
13484
13959
|
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
@@ -13541,8 +14016,8 @@ function createRun(args) {
|
|
|
13541
14016
|
ensureGitRepo(repo);
|
|
13542
14017
|
const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
|
|
13543
14018
|
const dir = runDirectory(id);
|
|
13544
|
-
if (
|
|
13545
|
-
|
|
14019
|
+
if (existsSync40(dir)) failExists(`run already exists: ${id}`);
|
|
14020
|
+
mkdirSync9(dir, { recursive: true });
|
|
13546
14021
|
const base = String(args.base || "origin/main");
|
|
13547
14022
|
const baseCommit = git(repo, ["rev-parse", base]).trim();
|
|
13548
14023
|
const run = {
|
|
@@ -13555,7 +14030,7 @@ function createRun(args) {
|
|
|
13555
14030
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13556
14031
|
workers: {}
|
|
13557
14032
|
};
|
|
13558
|
-
writeJson(
|
|
14033
|
+
writeJson(path60.join(dir, "run.json"), run);
|
|
13559
14034
|
const info = { runId: id, runDir: dir, repo, base, baseCommit };
|
|
13560
14035
|
console.log(JSON.stringify(info, null, 2));
|
|
13561
14036
|
return info;
|
|
@@ -13587,6 +14062,11 @@ async function runStart(args) {
|
|
|
13587
14062
|
console.error("No AgentOS workspace configured \u2014 run `kynver bootstrap` (or pass --agent-os-id).");
|
|
13588
14063
|
process.exit(1);
|
|
13589
14064
|
}
|
|
14065
|
+
if (args.chatOauth === true && config.chatUseClaudeOauth !== true) {
|
|
14066
|
+
saveUserConfig({ ...config, chatUseClaudeOauth: true });
|
|
14067
|
+
config = loadUserConfig();
|
|
14068
|
+
console.log(" Chat: Claude Code OAuth opt-in saved (delegated turns may use your local subscription).");
|
|
14069
|
+
}
|
|
13590
14070
|
const repo = (typeof args.repo === "string" ? args.repo.trim() : "") || config.defaultRepo?.trim() || resolveDefaultRepo()?.repo || "";
|
|
13591
14071
|
if (!repo) {
|
|
13592
14072
|
console.error("No repo configured \u2014 pass --repo /path/to/repo or run `kynver setup --discover-repo`.");
|
|
@@ -13608,7 +14088,25 @@ async function runStart(args) {
|
|
|
13608
14088
|
console.log(` run: ${runId}`);
|
|
13609
14089
|
console.log(" Ctrl-C stops the agent. (Advanced control: `kynver daemon --help`.)");
|
|
13610
14090
|
console.log("");
|
|
13611
|
-
|
|
14091
|
+
const daemonArgs = { ...args, run: runId, agentOsId };
|
|
14092
|
+
if (shouldRunDaemonKeeper(daemonArgs)) {
|
|
14093
|
+
await runDaemonKeeper(daemonArgs, buildStartDaemonArgv(runId, agentOsId, args));
|
|
14094
|
+
return;
|
|
14095
|
+
}
|
|
14096
|
+
await runDaemon(daemonArgs);
|
|
14097
|
+
}
|
|
14098
|
+
function buildStartDaemonArgv(runId, agentOsId, args) {
|
|
14099
|
+
const argv = ["daemon", "--run", runId, "--agent-os-id", agentOsId];
|
|
14100
|
+
if (typeof args.intervalMs === "string" && args.intervalMs.trim()) {
|
|
14101
|
+
argv.push("--interval-ms", args.intervalMs.trim());
|
|
14102
|
+
}
|
|
14103
|
+
if (args.execute === false || args.execute === "false") {
|
|
14104
|
+
argv.push("--execute", "false");
|
|
14105
|
+
}
|
|
14106
|
+
if (typeof args.stallMs === "string" && args.stallMs.trim()) {
|
|
14107
|
+
argv.push("--stall-ms", args.stallMs.trim());
|
|
14108
|
+
}
|
|
14109
|
+
return argv;
|
|
13612
14110
|
}
|
|
13613
14111
|
|
|
13614
14112
|
// src/cli.ts
|
|
@@ -13617,8 +14115,8 @@ init_run_store();
|
|
|
13617
14115
|
// src/discard-disposable.ts
|
|
13618
14116
|
init_run_store();
|
|
13619
14117
|
init_status();
|
|
13620
|
-
import { existsSync as
|
|
13621
|
-
import
|
|
14118
|
+
import { existsSync as existsSync41, rmSync as rmSync4 } from "node:fs";
|
|
14119
|
+
import path61 from "node:path";
|
|
13622
14120
|
function normalizeRelativePath2(value) {
|
|
13623
14121
|
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
13624
14122
|
if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
|
|
@@ -13640,15 +14138,15 @@ function discardDisposableArtifacts(args) {
|
|
|
13640
14138
|
if (paths.length === 0) {
|
|
13641
14139
|
return { ok: false, removed: [], reason: "requires at least one --path" };
|
|
13642
14140
|
}
|
|
13643
|
-
const worktreeRoot =
|
|
14141
|
+
const worktreeRoot = path61.resolve(worker.worktreePath);
|
|
13644
14142
|
const removed = [];
|
|
13645
14143
|
for (const raw of paths) {
|
|
13646
14144
|
const rel = normalizeRelativePath2(raw);
|
|
13647
|
-
const abs =
|
|
13648
|
-
if (!abs.startsWith(worktreeRoot +
|
|
14145
|
+
const abs = path61.resolve(worktreeRoot, rel);
|
|
14146
|
+
if (!abs.startsWith(worktreeRoot + path61.sep) && abs !== worktreeRoot) {
|
|
13649
14147
|
return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
|
|
13650
14148
|
}
|
|
13651
|
-
if (!
|
|
14149
|
+
if (!existsSync41(abs)) {
|
|
13652
14150
|
return { ok: false, removed, reason: `path not found: ${raw}` };
|
|
13653
14151
|
}
|
|
13654
14152
|
rmSync4(abs, { recursive: true, force: true });
|
|
@@ -13670,140 +14168,9 @@ function discardDisposableCli(args) {
|
|
|
13670
14168
|
if (!result.ok) process.exit(1);
|
|
13671
14169
|
}
|
|
13672
14170
|
|
|
13673
|
-
// src/daemon-keeper.ts
|
|
13674
|
-
init_config();
|
|
13675
|
-
import { spawn as spawn6 } from "node:child_process";
|
|
13676
|
-
init_util();
|
|
13677
|
-
var DEFAULT_STALL_MS = 15 * 6e4;
|
|
13678
|
-
var STARTUP_GRACE_MS = 2 * 6e4;
|
|
13679
|
-
var KILL_GRACE_MS = 1e4;
|
|
13680
|
-
var BACKOFF_BASE_MS = 5e3;
|
|
13681
|
-
var BACKOFF_CAP_MS = 5 * 6e4;
|
|
13682
|
-
var HEALTHY_RESET_MS = 30 * 6e4;
|
|
13683
|
-
var POLL_MS = 5e3;
|
|
13684
|
-
function resolveKeeperStallMs(flag, env = process.env) {
|
|
13685
|
-
const fromFlag = typeof flag === "string" ? Number.parseInt(flag, 10) : NaN;
|
|
13686
|
-
if (Number.isFinite(fromFlag) && fromFlag > 0) return fromFlag;
|
|
13687
|
-
const fromEnv = Number.parseInt(env.KYNVER_DAEMON_STALL_MS ?? "", 10);
|
|
13688
|
-
if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv;
|
|
13689
|
-
return DEFAULT_STALL_MS;
|
|
13690
|
-
}
|
|
13691
|
-
function shouldRunDaemonKeeper(args, env = process.env) {
|
|
13692
|
-
if (args.keeperChild === true || args.keeperChild === "true") return false;
|
|
13693
|
-
if (args.noSupervise === true || args.noSupervise === "true") return false;
|
|
13694
|
-
if (args.supervised === "false") return false;
|
|
13695
|
-
const envFlag5 = (env.KYNVER_DAEMON_SUPERVISED ?? "").trim().toLowerCase();
|
|
13696
|
-
if (envFlag5 === "0" || envFlag5 === "false" || envFlag5 === "no" || envFlag5 === "off") {
|
|
13697
|
-
return false;
|
|
13698
|
-
}
|
|
13699
|
-
return true;
|
|
13700
|
-
}
|
|
13701
|
-
function nextKeeperBackoffMs(consecutiveFailures, base = BACKOFF_BASE_MS, cap = BACKOFF_CAP_MS) {
|
|
13702
|
-
const exp = Math.min(Math.max(consecutiveFailures, 1) - 1, 10);
|
|
13703
|
-
return Math.min(base * 2 ** exp, cap);
|
|
13704
|
-
}
|
|
13705
|
-
function keeperRunWasHealthy(startedAtMs, endedAtMs, healthyMs = HEALTHY_RESET_MS) {
|
|
13706
|
-
return endedAtMs - startedAtMs >= healthyMs;
|
|
13707
|
-
}
|
|
13708
|
-
function keeperLog(event, detail = {}) {
|
|
13709
|
-
console.error(JSON.stringify({ event: `daemon_keeper_${event}`, ...detail }));
|
|
13710
|
-
}
|
|
13711
|
-
function buildKeeperChildArgv(argv) {
|
|
13712
|
-
const out = [];
|
|
13713
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
13714
|
-
const arg = argv[i];
|
|
13715
|
-
if (arg === "--supervised" || arg === "--no-supervise" || arg === "--keeper-child") continue;
|
|
13716
|
-
if (arg === "--stall-ms") {
|
|
13717
|
-
if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) i += 1;
|
|
13718
|
-
continue;
|
|
13719
|
-
}
|
|
13720
|
-
if (arg.startsWith("--stall-ms=") || arg.startsWith("--supervised=")) continue;
|
|
13721
|
-
out.push(arg);
|
|
13722
|
-
}
|
|
13723
|
-
out.push("--keeper-child");
|
|
13724
|
-
return out;
|
|
13725
|
-
}
|
|
13726
|
-
async function runDaemonKeeper(args, rawArgv = process.argv.slice(2)) {
|
|
13727
|
-
const agentOsId = String(
|
|
13728
|
-
required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id")
|
|
13729
|
-
);
|
|
13730
|
-
const stallMs = resolveKeeperStallMs(args.stallMs);
|
|
13731
|
-
const childArgv = buildKeeperChildArgv(rawArgv);
|
|
13732
|
-
const cliEntry = process.argv[1];
|
|
13733
|
-
let stopping = false;
|
|
13734
|
-
let child = null;
|
|
13735
|
-
let consecutiveFailures = 0;
|
|
13736
|
-
const stop = (signal) => {
|
|
13737
|
-
stopping = true;
|
|
13738
|
-
keeperLog("stop", { signal });
|
|
13739
|
-
if (child?.pid) child.kill(signal);
|
|
13740
|
-
};
|
|
13741
|
-
process.on("SIGINT", () => stop("SIGINT"));
|
|
13742
|
-
process.on("SIGTERM", () => stop("SIGTERM"));
|
|
13743
|
-
keeperLog("start", { agentOsId, stallMs, childArgv });
|
|
13744
|
-
while (!stopping) {
|
|
13745
|
-
const startedAt = Date.now();
|
|
13746
|
-
let exited = false;
|
|
13747
|
-
let exitCode = null;
|
|
13748
|
-
let exitSignal = null;
|
|
13749
|
-
child = spawn6(process.execPath, [cliEntry, ...childArgv], {
|
|
13750
|
-
stdio: "inherit",
|
|
13751
|
-
env: process.env
|
|
13752
|
-
});
|
|
13753
|
-
keeperLog("child_spawned", { pid: child.pid ?? null });
|
|
13754
|
-
child.on("exit", (code, signal) => {
|
|
13755
|
-
exited = true;
|
|
13756
|
-
exitCode = code;
|
|
13757
|
-
exitSignal = signal;
|
|
13758
|
-
});
|
|
13759
|
-
while (!exited && !stopping) {
|
|
13760
|
-
await sleepMsAsync(POLL_MS);
|
|
13761
|
-
if (exited || stopping) break;
|
|
13762
|
-
if (Date.now() - startedAt < STARTUP_GRACE_MS) continue;
|
|
13763
|
-
const beat = readDaemonHeartbeat(agentOsId);
|
|
13764
|
-
const ownBeat = beat && beat.pid === child.pid ? beat : null;
|
|
13765
|
-
if (ownBeat && isDaemonHeartbeatStale(ownBeat, stallMs)) {
|
|
13766
|
-
keeperLog("stall_detected", {
|
|
13767
|
-
pid: child.pid ?? null,
|
|
13768
|
-
lastBeatAt: ownBeat.observedAt,
|
|
13769
|
-
stallMs
|
|
13770
|
-
});
|
|
13771
|
-
child.kill("SIGTERM");
|
|
13772
|
-
await sleepMsAsync(KILL_GRACE_MS);
|
|
13773
|
-
if (!exited) child.kill("SIGKILL");
|
|
13774
|
-
break;
|
|
13775
|
-
}
|
|
13776
|
-
if (!ownBeat && Date.now() - startedAt > stallMs + STARTUP_GRACE_MS) {
|
|
13777
|
-
keeperLog("no_heartbeat_detected", { pid: child.pid ?? null, stallMs });
|
|
13778
|
-
child.kill("SIGTERM");
|
|
13779
|
-
await sleepMsAsync(KILL_GRACE_MS);
|
|
13780
|
-
if (!exited) child.kill("SIGKILL");
|
|
13781
|
-
break;
|
|
13782
|
-
}
|
|
13783
|
-
}
|
|
13784
|
-
while (!exited && !stopping) {
|
|
13785
|
-
await sleepMsAsync(POLL_MS);
|
|
13786
|
-
}
|
|
13787
|
-
if (stopping) break;
|
|
13788
|
-
const endedAt = Date.now();
|
|
13789
|
-
if (keeperRunWasHealthy(startedAt, endedAt)) consecutiveFailures = 0;
|
|
13790
|
-
consecutiveFailures += 1;
|
|
13791
|
-
const backoff = nextKeeperBackoffMs(consecutiveFailures);
|
|
13792
|
-
keeperLog("child_exited", {
|
|
13793
|
-
code: exitCode,
|
|
13794
|
-
signal: exitSignal,
|
|
13795
|
-
uptimeMs: endedAt - startedAt,
|
|
13796
|
-
consecutiveFailures,
|
|
13797
|
-
respawnInMs: backoff
|
|
13798
|
-
});
|
|
13799
|
-
await sleepMsAsync(backoff);
|
|
13800
|
-
}
|
|
13801
|
-
keeperLog("stopped", { agentOsId });
|
|
13802
|
-
}
|
|
13803
|
-
|
|
13804
14171
|
// src/plan-progress.ts
|
|
13805
14172
|
init_config();
|
|
13806
|
-
import
|
|
14173
|
+
import path65 from "node:path";
|
|
13807
14174
|
|
|
13808
14175
|
// src/bounded-build/constants.ts
|
|
13809
14176
|
var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
|
|
@@ -13844,7 +14211,7 @@ function formatNodeOptionsFlag(mb = resolveNodeOldSpaceSizeMb()) {
|
|
|
13844
14211
|
}
|
|
13845
14212
|
|
|
13846
14213
|
// src/bounded-build/systemd-wrap.ts
|
|
13847
|
-
import { spawnSync as
|
|
14214
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
13848
14215
|
var systemdAvailableCache;
|
|
13849
14216
|
function isSystemdRunAvailable() {
|
|
13850
14217
|
if (process.env.KYNVER_BUILD_SKIP_SYSTEMD === "1" || process.env.KYNVER_BUILD_SKIP_SYSTEMD === "true") {
|
|
@@ -13855,7 +14222,7 @@ function isSystemdRunAvailable() {
|
|
|
13855
14222
|
systemdAvailableCache = false;
|
|
13856
14223
|
return false;
|
|
13857
14224
|
}
|
|
13858
|
-
const res =
|
|
14225
|
+
const res = spawnSync9("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
|
|
13859
14226
|
systemdAvailableCache = res.status === 0;
|
|
13860
14227
|
return systemdAvailableCache;
|
|
13861
14228
|
}
|
|
@@ -13880,7 +14247,7 @@ function buildSystemdRunArgv(opts) {
|
|
|
13880
14247
|
|
|
13881
14248
|
// src/bounded-build/admission.ts
|
|
13882
14249
|
init_config();
|
|
13883
|
-
import { spawnSync as
|
|
14250
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
13884
14251
|
init_meminfo();
|
|
13885
14252
|
function positiveInt4(value, fallback) {
|
|
13886
14253
|
const n = Number(value);
|
|
@@ -13917,7 +14284,7 @@ function assessBuildAdmission(opts = {}) {
|
|
|
13917
14284
|
}
|
|
13918
14285
|
function sleepMs2(ms) {
|
|
13919
14286
|
if (ms <= 0) return;
|
|
13920
|
-
|
|
14287
|
+
spawnSync10(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
13921
14288
|
stdio: "ignore"
|
|
13922
14289
|
});
|
|
13923
14290
|
}
|
|
@@ -13938,34 +14305,34 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
|
|
|
13938
14305
|
}
|
|
13939
14306
|
|
|
13940
14307
|
// src/bounded-build/exec.ts
|
|
13941
|
-
import { spawnSync as
|
|
14308
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
13942
14309
|
|
|
13943
14310
|
// src/heavy-verification/slot.ts
|
|
13944
14311
|
init_util();
|
|
13945
14312
|
import {
|
|
13946
14313
|
closeSync as closeSync7,
|
|
13947
|
-
existsSync as
|
|
13948
|
-
mkdirSync as
|
|
14314
|
+
existsSync as existsSync42,
|
|
14315
|
+
mkdirSync as mkdirSync11,
|
|
13949
14316
|
openSync as openSync7,
|
|
13950
|
-
readdirSync as
|
|
14317
|
+
readdirSync as readdirSync14,
|
|
13951
14318
|
readFileSync as readFileSync16,
|
|
13952
14319
|
unlinkSync as unlinkSync4,
|
|
13953
|
-
writeFileSync as
|
|
14320
|
+
writeFileSync as writeFileSync7
|
|
13954
14321
|
} from "node:fs";
|
|
13955
|
-
import
|
|
14322
|
+
import path63 from "node:path";
|
|
13956
14323
|
|
|
13957
14324
|
// src/heavy-verification/paths.ts
|
|
13958
|
-
import { mkdirSync as
|
|
13959
|
-
import
|
|
14325
|
+
import { mkdirSync as mkdirSync10 } from "node:fs";
|
|
14326
|
+
import path62 from "node:path";
|
|
13960
14327
|
function resolveHeavyVerificationRoot() {
|
|
13961
|
-
return
|
|
14328
|
+
return path62.join(resolveKynverStateRoot(), "heavy-verification");
|
|
13962
14329
|
}
|
|
13963
14330
|
function heavyVerificationSlotsDir() {
|
|
13964
|
-
return
|
|
14331
|
+
return path62.join(resolveHeavyVerificationRoot(), "slots");
|
|
13965
14332
|
}
|
|
13966
14333
|
function ensureHeavyVerificationDirs() {
|
|
13967
14334
|
const dir = heavyVerificationSlotsDir();
|
|
13968
|
-
|
|
14335
|
+
mkdirSync10(dir, { recursive: true });
|
|
13969
14336
|
return dir;
|
|
13970
14337
|
}
|
|
13971
14338
|
|
|
@@ -13990,10 +14357,10 @@ function indexedSlotId(index) {
|
|
|
13990
14357
|
return `slot-${index}`;
|
|
13991
14358
|
}
|
|
13992
14359
|
function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
|
|
13993
|
-
return
|
|
14360
|
+
return path63.join(slotsDir, `${slotId}.json`);
|
|
13994
14361
|
}
|
|
13995
14362
|
function readSlotRecord(filePath) {
|
|
13996
|
-
if (!
|
|
14363
|
+
if (!existsSync42(filePath)) return null;
|
|
13997
14364
|
try {
|
|
13998
14365
|
const parsed = JSON.parse(readFileSync16(filePath, "utf8"));
|
|
13999
14366
|
if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
|
|
@@ -14020,19 +14387,19 @@ function reclaimStaleSlot(filePath, staleMs) {
|
|
|
14020
14387
|
}
|
|
14021
14388
|
}
|
|
14022
14389
|
function ensureSlotsDir(slotsDir) {
|
|
14023
|
-
|
|
14390
|
+
mkdirSync11(slotsDir, { recursive: true });
|
|
14024
14391
|
return slotsDir;
|
|
14025
14392
|
}
|
|
14026
14393
|
function reclaimStaleHeavyVerificationSlots(opts = {}) {
|
|
14027
14394
|
const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
|
|
14028
14395
|
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
14029
14396
|
let reclaimed = 0;
|
|
14030
|
-
for (const name of
|
|
14397
|
+
for (const name of readdirSync14(slotsDir)) {
|
|
14031
14398
|
if (!name.endsWith(".json")) continue;
|
|
14032
|
-
const filePath =
|
|
14033
|
-
const before =
|
|
14399
|
+
const filePath = path63.join(slotsDir, name);
|
|
14400
|
+
const before = existsSync42(filePath);
|
|
14034
14401
|
reclaimStaleSlot(filePath, staleMs);
|
|
14035
|
-
if (before && !
|
|
14402
|
+
if (before && !existsSync42(filePath)) reclaimed += 1;
|
|
14036
14403
|
}
|
|
14037
14404
|
return reclaimed;
|
|
14038
14405
|
}
|
|
@@ -14041,9 +14408,9 @@ function listActiveHeavyVerificationSlots(opts = {}) {
|
|
|
14041
14408
|
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
14042
14409
|
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
14043
14410
|
const active = [];
|
|
14044
|
-
for (const name of
|
|
14411
|
+
for (const name of readdirSync14(slotsDir)) {
|
|
14045
14412
|
if (!name.endsWith(".json")) continue;
|
|
14046
|
-
const record = readSlotRecord(
|
|
14413
|
+
const record = readSlotRecord(path63.join(slotsDir, name));
|
|
14047
14414
|
if (record && !slotIsStale(record, staleMs)) active.push(record);
|
|
14048
14415
|
}
|
|
14049
14416
|
return active;
|
|
@@ -14085,7 +14452,7 @@ function tryAcquireHeavyVerificationSlot(command, opts = {}) {
|
|
|
14085
14452
|
};
|
|
14086
14453
|
try {
|
|
14087
14454
|
const fd = openSync7(filePath, "wx");
|
|
14088
|
-
|
|
14455
|
+
writeFileSync7(fd, JSON.stringify(record, null, 2), "utf8");
|
|
14089
14456
|
closeSync7(fd);
|
|
14090
14457
|
const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
14091
14458
|
return {
|
|
@@ -14145,10 +14512,10 @@ function assessHeavyVerificationGate(command, opts = {}) {
|
|
|
14145
14512
|
}
|
|
14146
14513
|
|
|
14147
14514
|
// src/heavy-verification/gate.ts
|
|
14148
|
-
import { spawnSync as
|
|
14515
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
14149
14516
|
function sleepMs3(ms) {
|
|
14150
14517
|
if (ms <= 0) return;
|
|
14151
|
-
|
|
14518
|
+
spawnSync11(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
14152
14519
|
stdio: "ignore"
|
|
14153
14520
|
});
|
|
14154
14521
|
}
|
|
@@ -14164,11 +14531,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
|
|
|
14164
14531
|
|
|
14165
14532
|
// src/harness-worktree-build-guard.ts
|
|
14166
14533
|
init_paths();
|
|
14167
|
-
import
|
|
14534
|
+
import path64 from "node:path";
|
|
14168
14535
|
function isPathUnderHarnessWorktree(cwd) {
|
|
14169
14536
|
const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
|
|
14170
|
-
const rel =
|
|
14171
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
14537
|
+
const rel = path64.relative(worktreesDir, path64.resolve(cwd));
|
|
14538
|
+
return rel.length > 0 && !rel.startsWith("..") && !path64.isAbsolute(rel);
|
|
14172
14539
|
}
|
|
14173
14540
|
function assessHarnessWorktreeBuildGuard(cwd) {
|
|
14174
14541
|
if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
|
|
@@ -14192,7 +14559,7 @@ function envArgv(env) {
|
|
|
14192
14559
|
return out;
|
|
14193
14560
|
}
|
|
14194
14561
|
function runSpawn(argv, opts) {
|
|
14195
|
-
const res =
|
|
14562
|
+
const res = spawnSync12(argv[0], argv.slice(1), {
|
|
14196
14563
|
cwd: opts.cwd,
|
|
14197
14564
|
env: opts.env,
|
|
14198
14565
|
encoding: "utf8",
|
|
@@ -14381,7 +14748,7 @@ async function emitPlanProgress(args) {
|
|
|
14381
14748
|
}
|
|
14382
14749
|
function verifyPlanLocal(args) {
|
|
14383
14750
|
const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
|
|
14384
|
-
const cwd =
|
|
14751
|
+
const cwd = path65.resolve(worktree);
|
|
14385
14752
|
const summary = runHarnessVerifyCommands(cwd);
|
|
14386
14753
|
const emitJson = args.json === true || args.json === "true";
|
|
14387
14754
|
const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
|
|
@@ -14430,10 +14797,10 @@ async function verifyPlan(args) {
|
|
|
14430
14797
|
}
|
|
14431
14798
|
|
|
14432
14799
|
// src/harness-verify-cli.ts
|
|
14433
|
-
import
|
|
14800
|
+
import path66 from "node:path";
|
|
14434
14801
|
init_util();
|
|
14435
14802
|
function runHarnessVerifyCli(args) {
|
|
14436
|
-
const cwd =
|
|
14803
|
+
const cwd = path66.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
|
|
14437
14804
|
const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
|
|
14438
14805
|
const commands = [];
|
|
14439
14806
|
const rawCmd = args.command;
|
|
@@ -14565,7 +14932,9 @@ function runCleanupCli(args) {
|
|
|
14565
14932
|
const skipFinalize = args.skipFinalize === true || args.skipFinalize === "true";
|
|
14566
14933
|
const accountBytes = args.accountBytes === true || args.accountBytes === "true";
|
|
14567
14934
|
const compact = args.compact === true || args.compact === "true";
|
|
14568
|
-
const
|
|
14935
|
+
const nodeModulesAgeMsRaw = args.nodeModulesAgeMs;
|
|
14936
|
+
const scanDependencyCaches = nodeModulesAgeMsRaw != null && nodeModulesAgeMsRaw !== "";
|
|
14937
|
+
const nodeModulesAgeMs = scanDependencyCaches ? Number(nodeModulesAgeMsRaw) : void 0;
|
|
14569
14938
|
const worktreesAgeMs = args.worktreesAgeMs ? Number(args.worktreesAgeMs) : 0;
|
|
14570
14939
|
const includeOrphans = args.includeOrphans === true || args.includeOrphans === "true";
|
|
14571
14940
|
const harnessRoot = args.harnessRoot ? String(args.harnessRoot) : void 0;
|
|
@@ -14573,7 +14942,8 @@ function runCleanupCli(args) {
|
|
|
14573
14942
|
execute,
|
|
14574
14943
|
finalizeStaleRuns: !skipFinalize,
|
|
14575
14944
|
accountBytes,
|
|
14576
|
-
|
|
14945
|
+
scanDependencyCaches,
|
|
14946
|
+
nodeModulesAgeMs: nodeModulesAgeMs !== void 0 && Number.isFinite(nodeModulesAgeMs) ? nodeModulesAgeMs : scanDependencyCaches ? DEFAULT_NODE_MODULES_AGE_MS : void 0,
|
|
14577
14947
|
worktreesAgeMs: Number.isFinite(worktreesAgeMs) ? worktreesAgeMs : 0,
|
|
14578
14948
|
includeOrphans,
|
|
14579
14949
|
harnessRoot
|
|
@@ -14643,7 +15013,7 @@ function formatMonitorTickNotice(tick) {
|
|
|
14643
15013
|
}
|
|
14644
15014
|
|
|
14645
15015
|
// src/monitor/monitor.service.ts
|
|
14646
|
-
import
|
|
15016
|
+
import path68 from "node:path";
|
|
14647
15017
|
init_run_store();
|
|
14648
15018
|
init_status();
|
|
14649
15019
|
init_util();
|
|
@@ -14702,19 +15072,19 @@ function classifyWorkerHealth(input) {
|
|
|
14702
15072
|
// src/monitor/monitor.store.ts
|
|
14703
15073
|
init_paths();
|
|
14704
15074
|
init_util();
|
|
14705
|
-
import { existsSync as
|
|
14706
|
-
import
|
|
15075
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync12, readdirSync as readdirSync15, unlinkSync as unlinkSync5 } from "node:fs";
|
|
15076
|
+
import path67 from "node:path";
|
|
14707
15077
|
function monitorsDir() {
|
|
14708
15078
|
const { harnessRoot } = getHarnessPaths();
|
|
14709
|
-
const dir =
|
|
14710
|
-
|
|
15079
|
+
const dir = path67.join(harnessRoot, "monitors");
|
|
15080
|
+
mkdirSync12(dir, { recursive: true });
|
|
14711
15081
|
return dir;
|
|
14712
15082
|
}
|
|
14713
15083
|
function monitorIdFor(runId, workerName) {
|
|
14714
15084
|
return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
|
|
14715
15085
|
}
|
|
14716
15086
|
function monitorPath(monitorId) {
|
|
14717
|
-
return
|
|
15087
|
+
return path67.join(monitorsDir(), `${monitorId}.json`);
|
|
14718
15088
|
}
|
|
14719
15089
|
function loadMonitorSession(monitorId) {
|
|
14720
15090
|
return readJson(monitorPath(monitorId), void 0);
|
|
@@ -14724,18 +15094,18 @@ function saveMonitorSession(session) {
|
|
|
14724
15094
|
}
|
|
14725
15095
|
function deleteMonitorSession(monitorId) {
|
|
14726
15096
|
const file = monitorPath(monitorId);
|
|
14727
|
-
if (!
|
|
15097
|
+
if (!existsSync43(file)) return false;
|
|
14728
15098
|
unlinkSync5(file);
|
|
14729
15099
|
return true;
|
|
14730
15100
|
}
|
|
14731
15101
|
function listMonitorSessions() {
|
|
14732
15102
|
const dir = monitorsDir();
|
|
14733
|
-
if (!
|
|
15103
|
+
if (!existsSync43(dir)) return [];
|
|
14734
15104
|
const entries = [];
|
|
14735
|
-
for (const name of
|
|
15105
|
+
for (const name of readdirSync15(dir)) {
|
|
14736
15106
|
if (!name.endsWith(".json")) continue;
|
|
14737
15107
|
const session = readJson(
|
|
14738
|
-
|
|
15108
|
+
path67.join(dir, name),
|
|
14739
15109
|
void 0
|
|
14740
15110
|
);
|
|
14741
15111
|
if (!session?.monitorId) continue;
|
|
@@ -14828,7 +15198,7 @@ async function fetchTaskLeasesForWorkers(input) {
|
|
|
14828
15198
|
// src/monitor/monitor.service.ts
|
|
14829
15199
|
function workerRecord2(runId, name) {
|
|
14830
15200
|
return readJson(
|
|
14831
|
-
|
|
15201
|
+
path68.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
|
|
14832
15202
|
void 0
|
|
14833
15203
|
);
|
|
14834
15204
|
}
|
|
@@ -15037,18 +15407,18 @@ async function runMonitorLoop(args) {
|
|
|
15037
15407
|
init_util();
|
|
15038
15408
|
init_paths();
|
|
15039
15409
|
import { spawn as spawn7 } from "node:child_process";
|
|
15040
|
-
import { closeSync as closeSync8, existsSync as
|
|
15041
|
-
import
|
|
15410
|
+
import { closeSync as closeSync8, existsSync as existsSync44, openSync as openSync8 } from "node:fs";
|
|
15411
|
+
import path69 from "node:path";
|
|
15042
15412
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
15043
15413
|
function resolveDefaultCliPath2() {
|
|
15044
|
-
return
|
|
15414
|
+
return path69.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
|
|
15045
15415
|
}
|
|
15046
15416
|
function spawnMonitorSidecar(opts) {
|
|
15047
15417
|
const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
|
|
15048
|
-
if (!
|
|
15418
|
+
if (!existsSync44(cliPath)) return void 0;
|
|
15049
15419
|
const monitorId = monitorIdFor(opts.runId, opts.workerName);
|
|
15050
15420
|
const { harnessRoot } = getHarnessPaths();
|
|
15051
|
-
const logPath =
|
|
15421
|
+
const logPath = path69.join(harnessRoot, "monitors", `${monitorId}.log`);
|
|
15052
15422
|
let logFd;
|
|
15053
15423
|
try {
|
|
15054
15424
|
logFd = openSync8(logPath, "a");
|
|
@@ -15169,13 +15539,13 @@ async function monitorTickCli(args) {
|
|
|
15169
15539
|
}
|
|
15170
15540
|
|
|
15171
15541
|
// src/package-version.ts
|
|
15172
|
-
import { existsSync as
|
|
15542
|
+
import { existsSync as existsSync45, readFileSync as readFileSync18 } from "node:fs";
|
|
15173
15543
|
import { dirname, join } from "node:path";
|
|
15174
15544
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
15175
15545
|
function resolvePackageRoot(moduleUrl) {
|
|
15176
15546
|
let dir = dirname(fileURLToPath4(moduleUrl));
|
|
15177
15547
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
15178
|
-
if (
|
|
15548
|
+
if (existsSync45(join(dir, "package.json"))) return dir;
|
|
15179
15549
|
const parent = dirname(dir);
|
|
15180
15550
|
if (parent === dir) break;
|
|
15181
15551
|
dir = parent;
|
|
@@ -15281,7 +15651,7 @@ init_run_store();
|
|
|
15281
15651
|
init_status();
|
|
15282
15652
|
init_util();
|
|
15283
15653
|
init_config();
|
|
15284
|
-
import
|
|
15654
|
+
import path70 from "node:path";
|
|
15285
15655
|
function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
|
|
15286
15656
|
return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
|
|
15287
15657
|
}
|
|
@@ -15294,7 +15664,7 @@ async function postRestartUnblock(args) {
|
|
|
15294
15664
|
const errors = [];
|
|
15295
15665
|
for (const run of listRunRecords()) {
|
|
15296
15666
|
for (const name of Object.keys(run.workers ?? {})) {
|
|
15297
|
-
const workerPath =
|
|
15667
|
+
const workerPath = path70.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
15298
15668
|
const worker = readJson(workerPath, void 0);
|
|
15299
15669
|
if (!worker) {
|
|
15300
15670
|
skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
|
|
@@ -15408,9 +15778,9 @@ async function postRestartUnblockCli(args) {
|
|
|
15408
15778
|
// src/default-repo-cli.ts
|
|
15409
15779
|
init_path_values();
|
|
15410
15780
|
init_config();
|
|
15411
|
-
import
|
|
15781
|
+
import path71 from "node:path";
|
|
15412
15782
|
import { homedir as homedir16 } from "node:os";
|
|
15413
|
-
var CONFIG_FILE2 =
|
|
15783
|
+
var CONFIG_FILE2 = path71.join(homedir16(), ".kynver", "config.json");
|
|
15414
15784
|
function ensureDefaultRepo(opts) {
|
|
15415
15785
|
const existing = loadUserConfig();
|
|
15416
15786
|
const resolved = resolveDefaultRepo({ ...opts, config: existing });
|
|
@@ -15491,19 +15861,19 @@ function summarizeResolvedDefaultRepo(resolved) {
|
|
|
15491
15861
|
}
|
|
15492
15862
|
|
|
15493
15863
|
// src/doctor/runtime-takeover.ts
|
|
15494
|
-
import
|
|
15864
|
+
import path73 from "node:path";
|
|
15495
15865
|
init_path_values();
|
|
15496
15866
|
|
|
15497
15867
|
// src/doctor/runtime-takeover.probes.ts
|
|
15498
15868
|
init_config();
|
|
15499
|
-
import { accessSync, constants, existsSync as
|
|
15869
|
+
import { accessSync, constants, existsSync as existsSync46, readFileSync as readFileSync19 } from "node:fs";
|
|
15500
15870
|
import { homedir as homedir17 } from "node:os";
|
|
15501
|
-
import
|
|
15502
|
-
import { spawnSync as
|
|
15871
|
+
import path72 from "node:path";
|
|
15872
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
15503
15873
|
init_paths();
|
|
15504
15874
|
function captureCommand(bin, args) {
|
|
15505
15875
|
try {
|
|
15506
|
-
const res =
|
|
15876
|
+
const res = spawnSync13(bin, args, { encoding: "utf8" });
|
|
15507
15877
|
const stdout = (res.stdout || "").trim();
|
|
15508
15878
|
const stderr = (res.stderr || "").trim();
|
|
15509
15879
|
const ok = res.status === 0;
|
|
@@ -15528,7 +15898,7 @@ function tokenPrefix(token) {
|
|
|
15528
15898
|
return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
|
|
15529
15899
|
}
|
|
15530
15900
|
function isWritable(target) {
|
|
15531
|
-
if (!
|
|
15901
|
+
if (!existsSync46(target)) return false;
|
|
15532
15902
|
try {
|
|
15533
15903
|
accessSync(target, constants.W_OK);
|
|
15534
15904
|
return true;
|
|
@@ -15541,11 +15911,11 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
15541
15911
|
commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
|
|
15542
15912
|
kynverVersion: (bin) => captureCommand(bin, ["--version"]),
|
|
15543
15913
|
loadConfig: () => loadUserConfig(),
|
|
15544
|
-
configFilePath: () =>
|
|
15545
|
-
credentialsFilePath: () =>
|
|
15914
|
+
configFilePath: () => path72.join(homedir17(), ".kynver", "config.json"),
|
|
15915
|
+
credentialsFilePath: () => path72.join(homedir17(), ".kynver", "credentials"),
|
|
15546
15916
|
readCredentials: () => {
|
|
15547
|
-
const credPath =
|
|
15548
|
-
if (!
|
|
15917
|
+
const credPath = path72.join(homedir17(), ".kynver", "credentials");
|
|
15918
|
+
if (!existsSync46(credPath)) {
|
|
15549
15919
|
return { hasApiKey: false };
|
|
15550
15920
|
}
|
|
15551
15921
|
try {
|
|
@@ -15579,8 +15949,8 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
15579
15949
|
})()
|
|
15580
15950
|
}),
|
|
15581
15951
|
harnessRoot: () => resolveHarnessRoot(),
|
|
15582
|
-
legacyOpenclawHarnessRoot: () =>
|
|
15583
|
-
pathExists: (target) =>
|
|
15952
|
+
legacyOpenclawHarnessRoot: () => path72.join(homedir17(), ".openclaw", "harness"),
|
|
15953
|
+
pathExists: (target) => existsSync46(target),
|
|
15584
15954
|
pathWritable: (target) => isWritable(target)
|
|
15585
15955
|
};
|
|
15586
15956
|
|
|
@@ -15986,8 +16356,8 @@ function assessVercelDeployEvidence(probes) {
|
|
|
15986
16356
|
}
|
|
15987
16357
|
function assessHarnessDirs(probes) {
|
|
15988
16358
|
const harnessRoot = probes.harnessRoot();
|
|
15989
|
-
const runsDir =
|
|
15990
|
-
const worktreesDir =
|
|
16359
|
+
const runsDir = path73.join(harnessRoot, "runs");
|
|
16360
|
+
const worktreesDir = path73.join(harnessRoot, "worktrees");
|
|
15991
16361
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
15992
16362
|
const displayRunsDir = redactHomePath(runsDir);
|
|
15993
16363
|
const displayWorktreesDir = redactHomePath(worktreesDir);
|
|
@@ -16256,9 +16626,9 @@ function applySchedulerCutoverAttestation(config) {
|
|
|
16256
16626
|
|
|
16257
16627
|
// src/scheduler-cutover-cli.ts
|
|
16258
16628
|
init_config();
|
|
16259
|
-
import
|
|
16629
|
+
import path74 from "node:path";
|
|
16260
16630
|
import { homedir as homedir18 } from "node:os";
|
|
16261
|
-
var CONFIG_FILE3 =
|
|
16631
|
+
var CONFIG_FILE3 = path74.join(homedir18(), ".kynver", "config.json");
|
|
16262
16632
|
function runSchedulerCutoverCheckCli(json = false) {
|
|
16263
16633
|
const config = loadUserConfig();
|
|
16264
16634
|
const report = assessSchedulerCutover(config);
|
|
@@ -16402,7 +16772,7 @@ init_config();
|
|
|
16402
16772
|
init_config();
|
|
16403
16773
|
init_path_values();
|
|
16404
16774
|
init_util();
|
|
16405
|
-
import { existsSync as
|
|
16775
|
+
import { existsSync as existsSync50 } from "node:fs";
|
|
16406
16776
|
|
|
16407
16777
|
// src/cron/cron-id.ts
|
|
16408
16778
|
import { createHash as createHash4 } from "node:crypto";
|
|
@@ -16421,13 +16791,13 @@ function deterministicCronProviderId(spec) {
|
|
|
16421
16791
|
|
|
16422
16792
|
// src/cron/cron-install-plan.ts
|
|
16423
16793
|
import { homedir as homedir20 } from "node:os";
|
|
16424
|
-
import
|
|
16794
|
+
import path76 from "node:path";
|
|
16425
16795
|
|
|
16426
16796
|
// src/cron/cron-env-file.ts
|
|
16427
|
-
import { existsSync as
|
|
16797
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync13, readFileSync as readFileSync20, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16428
16798
|
import { homedir as homedir19 } from "node:os";
|
|
16429
|
-
import
|
|
16430
|
-
var DEFAULT_KYNVER_ENV_FILE =
|
|
16799
|
+
import path75 from "node:path";
|
|
16800
|
+
var DEFAULT_KYNVER_ENV_FILE = path75.join(homedir19(), ".kynver", ".env");
|
|
16431
16801
|
function parseEnvFile(content) {
|
|
16432
16802
|
const map = /* @__PURE__ */ new Map();
|
|
16433
16803
|
for (const line of content.split(/\r?\n/)) {
|
|
@@ -16454,12 +16824,12 @@ function serializeEnvFile(values, header = "# Managed by kynver cron install \u2
|
|
|
16454
16824
|
return lines.join("\n");
|
|
16455
16825
|
}
|
|
16456
16826
|
function readEnvFile(filePath = DEFAULT_KYNVER_ENV_FILE) {
|
|
16457
|
-
if (!
|
|
16827
|
+
if (!existsSync47(filePath)) return /* @__PURE__ */ new Map();
|
|
16458
16828
|
return parseEnvFile(readFileSync20(filePath, "utf8"));
|
|
16459
16829
|
}
|
|
16460
16830
|
function mergeEnvFile(updates, options = {}) {
|
|
16461
16831
|
const filePath = options.filePath ?? DEFAULT_KYNVER_ENV_FILE;
|
|
16462
|
-
const existing =
|
|
16832
|
+
const existing = existsSync47(filePath) ? readFileSync20(filePath, "utf8") : "";
|
|
16463
16833
|
const map = parseEnvFile(existing);
|
|
16464
16834
|
const keysWritten = [];
|
|
16465
16835
|
const keysRemoved = [];
|
|
@@ -16484,8 +16854,8 @@ function mergeEnvFile(updates, options = {}) {
|
|
|
16484
16854
|
}
|
|
16485
16855
|
const nextContent = serializeEnvFile(map);
|
|
16486
16856
|
if (changed) {
|
|
16487
|
-
|
|
16488
|
-
|
|
16857
|
+
mkdirSync13(path75.dirname(filePath), { recursive: true });
|
|
16858
|
+
writeFileSync8(filePath, nextContent, { mode: 384 });
|
|
16489
16859
|
}
|
|
16490
16860
|
return { path: filePath, changed, keysWritten, keysRemoved };
|
|
16491
16861
|
}
|
|
@@ -16502,7 +16872,7 @@ var VERCEL_KYNVER_CRON_CUTOVER_STEPS = [
|
|
|
16502
16872
|
function buildCronInstallPlan(input) {
|
|
16503
16873
|
const storePath = input.storePath?.trim() || defaultKynverCronStorePath();
|
|
16504
16874
|
const envFilePath = input.envFilePath?.trim() || DEFAULT_KYNVER_ENV_FILE;
|
|
16505
|
-
const configPath =
|
|
16875
|
+
const configPath = path76.join(homedir20(), ".kynver", "config.json");
|
|
16506
16876
|
const callbackPath = `/api/agent-os/by-id/${input.agentOsId}/scheduler/fire`;
|
|
16507
16877
|
const prerequisites = [];
|
|
16508
16878
|
if (!input.apiBaseUrl?.trim()) prerequisites.push("apiBaseUrl \u2014 run `kynver setup --api-base-url \u2026`");
|
|
@@ -16654,13 +17024,13 @@ function resolveCronSecretForInstall(envFilePath = DEFAULT_KYNVER_ENV_FILE) {
|
|
|
16654
17024
|
}
|
|
16655
17025
|
|
|
16656
17026
|
// src/cron/cron-install-systemd.ts
|
|
16657
|
-
import { existsSync as
|
|
17027
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync14, writeFileSync as writeFileSync9 } from "node:fs";
|
|
16658
17028
|
import { homedir as homedir21 } from "node:os";
|
|
16659
|
-
import
|
|
16660
|
-
import { spawnSync as
|
|
17029
|
+
import path77 from "node:path";
|
|
17030
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
16661
17031
|
var KYNVER_CRON_DAEMON_UNIT = "kynver-cron-daemon.service";
|
|
16662
17032
|
function defaultSystemdUserUnitDir() {
|
|
16663
|
-
return
|
|
17033
|
+
return path77.join(homedir21(), ".config", "systemd", "user");
|
|
16664
17034
|
}
|
|
16665
17035
|
function renderKynverCronDaemonService(input) {
|
|
16666
17036
|
const kynverBin = input.kynverBin?.trim() || "kynver";
|
|
@@ -16693,7 +17063,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
16693
17063
|
};
|
|
16694
17064
|
}
|
|
16695
17065
|
const unitDir = defaultSystemdUserUnitDir();
|
|
16696
|
-
const unitPath =
|
|
17066
|
+
const unitPath = path77.join(unitDir, KYNVER_CRON_DAEMON_UNIT);
|
|
16697
17067
|
const content = renderKynverCronDaemonService(input);
|
|
16698
17068
|
if (!execute) {
|
|
16699
17069
|
return {
|
|
@@ -16705,10 +17075,10 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
16705
17075
|
note: "Dry-run \u2014 pass --execute to write and enable the user unit."
|
|
16706
17076
|
};
|
|
16707
17077
|
}
|
|
16708
|
-
|
|
16709
|
-
const existed =
|
|
16710
|
-
|
|
16711
|
-
const reload =
|
|
17078
|
+
mkdirSync14(unitDir, { recursive: true });
|
|
17079
|
+
const existed = existsSync48(unitPath);
|
|
17080
|
+
writeFileSync9(unitPath, content, "utf8");
|
|
17081
|
+
const reload = spawnSync14("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
|
|
16712
17082
|
if (reload.status !== 0) {
|
|
16713
17083
|
return {
|
|
16714
17084
|
supported: true,
|
|
@@ -16719,7 +17089,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
16719
17089
|
note: `Wrote ${unitPath} but systemctl --user daemon-reload failed: ${reload.stderr || reload.stdout}`
|
|
16720
17090
|
};
|
|
16721
17091
|
}
|
|
16722
|
-
const enable =
|
|
17092
|
+
const enable = spawnSync14("systemctl", ["--user", "enable", "--now", KYNVER_CRON_DAEMON_UNIT], {
|
|
16723
17093
|
encoding: "utf8"
|
|
16724
17094
|
});
|
|
16725
17095
|
return {
|
|
@@ -16733,7 +17103,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
16733
17103
|
}
|
|
16734
17104
|
|
|
16735
17105
|
// src/cron/cron-install-verify.ts
|
|
16736
|
-
import { existsSync as
|
|
17106
|
+
import { existsSync as existsSync49 } from "node:fs";
|
|
16737
17107
|
async function verifyCronInstall(input) {
|
|
16738
17108
|
const envFilePath = input.envFilePath ?? DEFAULT_KYNVER_ENV_FILE;
|
|
16739
17109
|
const checks = [];
|
|
@@ -16766,14 +17136,14 @@ async function verifyCronInstall(input) {
|
|
|
16766
17136
|
});
|
|
16767
17137
|
checks.push({
|
|
16768
17138
|
id: "env_file",
|
|
16769
|
-
ok:
|
|
16770
|
-
summary:
|
|
17139
|
+
ok: existsSync49(envFilePath) && Boolean(fileEnv.get("KYNVER_CRON_SECRET")),
|
|
17140
|
+
summary: existsSync49(envFilePath) ? `~/.kynver/.env present (${fileEnv.size} keys)` : "~/.kynver/.env missing",
|
|
16771
17141
|
remediation: "Run `kynver cron install` to write ~/.kynver/.env."
|
|
16772
17142
|
});
|
|
16773
17143
|
checks.push({
|
|
16774
17144
|
id: "cron_store",
|
|
16775
|
-
ok:
|
|
16776
|
-
summary:
|
|
17145
|
+
ok: existsSync49(env.storePath),
|
|
17146
|
+
summary: existsSync49(env.storePath) ? `cron store present (${env.storePath})` : `cron store missing (${env.storePath})`,
|
|
16777
17147
|
remediation: "Run `kynver cron install` to initialize the local store."
|
|
16778
17148
|
});
|
|
16779
17149
|
const jobs = await loadCronJobs(env.storePath).catch(() => []);
|
|
@@ -16807,7 +17177,7 @@ function resolveDaemonRunId(config, explicit) {
|
|
|
16807
17177
|
if (explicit?.trim()) return explicit.trim();
|
|
16808
17178
|
if (config.defaultDaemonRunId?.trim()) return config.defaultDaemonRunId.trim();
|
|
16809
17179
|
const runsDir = getPaths().runsDir;
|
|
16810
|
-
if (!
|
|
17180
|
+
if (!existsSync50(runsDir)) return null;
|
|
16811
17181
|
const runs = listRunRecords().sort(
|
|
16812
17182
|
(a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt)
|
|
16813
17183
|
);
|
|
@@ -16859,7 +17229,7 @@ async function runCronInstall(opts = {}) {
|
|
|
16859
17229
|
process.env.KYNVER_CRON_STORE_PATH = plan.storePath;
|
|
16860
17230
|
process.env.KYNVER_CRON_TICK_ENABLED = "1";
|
|
16861
17231
|
const storeInit = await ensureCronStoreInitialized(plan.storePath);
|
|
16862
|
-
result.storeInitialized = storeInit.created ||
|
|
17232
|
+
result.storeInitialized = storeInit.created || existsSync50(plan.storePath);
|
|
16863
17233
|
const apiKey = loadApiKey();
|
|
16864
17234
|
if (apiKey) {
|
|
16865
17235
|
try {
|
|
@@ -17078,10 +17448,10 @@ var LANDING_MAINTAINER_LANE_SPEC = {
|
|
|
17078
17448
|
};
|
|
17079
17449
|
|
|
17080
17450
|
// src/lane/landing-maintainer-local.ts
|
|
17081
|
-
import { spawnSync as
|
|
17082
|
-
import
|
|
17451
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
17452
|
+
import path78 from "node:path";
|
|
17083
17453
|
function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
17084
|
-
const script =
|
|
17454
|
+
const script = path78.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
|
|
17085
17455
|
const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
|
|
17086
17456
|
if (!execute) {
|
|
17087
17457
|
return {
|
|
@@ -17092,7 +17462,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
|
17092
17462
|
stderr: ""
|
|
17093
17463
|
};
|
|
17094
17464
|
}
|
|
17095
|
-
const result =
|
|
17465
|
+
const result = spawnSync15("node", args, {
|
|
17096
17466
|
cwd: repoRoot,
|
|
17097
17467
|
encoding: "utf8",
|
|
17098
17468
|
timeout: 10 * 60 * 1e3
|
|
@@ -17107,7 +17477,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
|
17107
17477
|
}
|
|
17108
17478
|
function resolveLandingMaintainerRepoRoot(args) {
|
|
17109
17479
|
const explicit = args.repoPath ? String(args.repoPath).trim() : "";
|
|
17110
|
-
if (explicit) return
|
|
17480
|
+
if (explicit) return path78.resolve(explicit);
|
|
17111
17481
|
const resolved = resolveDefaultRepo();
|
|
17112
17482
|
return resolved?.repo ?? process.cwd();
|
|
17113
17483
|
}
|
|
@@ -17232,9 +17602,9 @@ function usage(code = 0) {
|
|
|
17232
17602
|
"Usage:",
|
|
17233
17603
|
" kynver login [--api-key KEY] [--api-base-url URL] (omit --api-key to authorize in the browser)",
|
|
17234
17604
|
" kynver bootstrap [--api-base-url URL] [--api-key KEY] [--repo PATH] (login + setup + runner credential in one shot)",
|
|
17235
|
-
" kynver start [--repo PATH] [--api-base-url URL] [--run RUN_ID] [--interval-ms MS] (bring your agent online: bootstrap if needed + run + daemon)",
|
|
17605
|
+
" kynver start [--repo PATH] [--api-base-url URL] [--run RUN_ID] [--interval-ms MS] [--chat-oauth] (bring your agent online: bootstrap if needed + run + daemon; --chat-oauth lets delegated chat turns use your local Claude subscription)",
|
|
17236
17606
|
" kynver runner credential [--agent-os-id ID] [--base-url URL]",
|
|
17237
|
-
" kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--box-kind forge|ghost] [--repo PATH] [--discover-repo] [--max-workers N] [--provider claude|cursor]",
|
|
17607
|
+
" kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--box-kind forge|ghost] [--repo PATH] [--discover-repo] [--max-workers N] [--provider claude|cursor] [--chat-oauth]",
|
|
17238
17608
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS] [--stall-ms MS] [--no-supervise]",
|
|
17239
17609
|
" kynver status --run RUN_ID [--blocked] [--running] [--task TASK_ID] [--worker WORKER] [--full] # top-level compact run status",
|
|
17240
17610
|
" kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
|
|
@@ -17298,8 +17668,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
17298
17668
|
if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
|
|
17299
17669
|
const args = parseArgs(rest);
|
|
17300
17670
|
const { runsDir, worktreesDir } = getPaths();
|
|
17301
|
-
|
|
17302
|
-
|
|
17671
|
+
mkdirSync15(runsDir, { recursive: true });
|
|
17672
|
+
mkdirSync15(worktreesDir, { recursive: true });
|
|
17303
17673
|
if (scope === "daemon") {
|
|
17304
17674
|
assertNativeDaemonAllowed();
|
|
17305
17675
|
}
|