@kynver-app/runtime 0.1.120 → 0.1.122
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 +881 -520
- 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 +826 -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,22 @@ async function sleep2(ms, shouldStop) {
|
|
|
13276
13580
|
}
|
|
13277
13581
|
}
|
|
13278
13582
|
async function runChatClaimLoop(opts) {
|
|
13279
|
-
|
|
13280
|
-
|
|
13583
|
+
let target = await resolveChatBridgeTarget();
|
|
13584
|
+
while (!target && !opts.shouldStop()) {
|
|
13585
|
+
if (!loadApiKey() || !loadUserConfig().agentOsId?.trim()) return;
|
|
13586
|
+
await sleep2(BRIDGE_REDISCOVER_MS, opts.shouldStop);
|
|
13587
|
+
if (opts.shouldStop()) return;
|
|
13588
|
+
target = await resolveChatBridgeTarget();
|
|
13589
|
+
}
|
|
13590
|
+
if (!target || opts.shouldStop()) return;
|
|
13281
13591
|
console.error(
|
|
13282
13592
|
JSON.stringify({
|
|
13283
13593
|
event: "chat_claim_loop_start",
|
|
13284
13594
|
bridgeUrl: target.bridgeUrl,
|
|
13285
13595
|
agentOsId: target.agentOsId,
|
|
13286
13596
|
boxId: target.boxId,
|
|
13287
|
-
model: target.model
|
|
13597
|
+
model: target.model,
|
|
13598
|
+
useClaudeOauth: target.useClaudeOauth
|
|
13288
13599
|
})
|
|
13289
13600
|
);
|
|
13290
13601
|
while (!opts.shouldStop()) {
|
|
@@ -13369,16 +13680,40 @@ async function runDaemon(args) {
|
|
|
13369
13680
|
})
|
|
13370
13681
|
);
|
|
13371
13682
|
});
|
|
13683
|
+
let credentialMissingLogged = false;
|
|
13372
13684
|
while (!stopping) {
|
|
13373
13685
|
try {
|
|
13374
13686
|
writeDaemonHeartbeat({ agentOsId, runId });
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13378
|
-
|
|
13379
|
-
|
|
13380
|
-
if (
|
|
13381
|
-
|
|
13687
|
+
const credential = await tryResolveCallbackSecretWithMint(
|
|
13688
|
+
args.secret ? String(args.secret) : void 0,
|
|
13689
|
+
agentOsId
|
|
13690
|
+
);
|
|
13691
|
+
if (!credential.ok) {
|
|
13692
|
+
if (!credentialMissingLogged) {
|
|
13693
|
+
credentialMissingLogged = true;
|
|
13694
|
+
console.error(
|
|
13695
|
+
JSON.stringify({
|
|
13696
|
+
event: "daemon_runner_credential_missing",
|
|
13697
|
+
agentOsId,
|
|
13698
|
+
reason: credential.reason,
|
|
13699
|
+
remedy: `run \`kynver runner credential --agent-os-id ${agentOsId}\` (or \`kynver bootstrap\`); ticks resume automatically once the credential exists`
|
|
13700
|
+
})
|
|
13701
|
+
);
|
|
13702
|
+
}
|
|
13703
|
+
await awaitDaemonBackoff(intervalMs, () => stopping);
|
|
13704
|
+
continue;
|
|
13705
|
+
}
|
|
13706
|
+
if (credentialMissingLogged) {
|
|
13707
|
+
credentialMissingLogged = false;
|
|
13708
|
+
console.error(JSON.stringify({ event: "daemon_runner_credential_recovered", agentOsId }));
|
|
13709
|
+
}
|
|
13710
|
+
if (cronEnv.tickEnabled) {
|
|
13711
|
+
const cronTick = await runKynverCronTick({
|
|
13712
|
+
env: cronEnv,
|
|
13713
|
+
agentOsIdFilter: agentOsId
|
|
13714
|
+
});
|
|
13715
|
+
if (cronTick.enabled && (cronTick.fired > 0 || cronTick.errors > 0)) {
|
|
13716
|
+
console.error(JSON.stringify({ event: "daemon_cron_tick", ...cronTick }));
|
|
13382
13717
|
}
|
|
13383
13718
|
}
|
|
13384
13719
|
const tick = await runPipelineTick({ run: runId, agentOsId, execute, ...args });
|
|
@@ -13399,21 +13734,152 @@ async function runDaemon(args) {
|
|
|
13399
13734
|
console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
|
|
13400
13735
|
}
|
|
13401
13736
|
|
|
13737
|
+
// src/daemon-keeper.ts
|
|
13738
|
+
init_config();
|
|
13739
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
13740
|
+
init_util();
|
|
13741
|
+
var DEFAULT_STALL_MS = 15 * 6e4;
|
|
13742
|
+
var STARTUP_GRACE_MS = 2 * 6e4;
|
|
13743
|
+
var KILL_GRACE_MS = 1e4;
|
|
13744
|
+
var BACKOFF_BASE_MS = 5e3;
|
|
13745
|
+
var BACKOFF_CAP_MS = 5 * 6e4;
|
|
13746
|
+
var HEALTHY_RESET_MS = 30 * 6e4;
|
|
13747
|
+
var POLL_MS = 5e3;
|
|
13748
|
+
function resolveKeeperStallMs(flag, env = process.env) {
|
|
13749
|
+
const fromFlag = typeof flag === "string" ? Number.parseInt(flag, 10) : NaN;
|
|
13750
|
+
if (Number.isFinite(fromFlag) && fromFlag > 0) return fromFlag;
|
|
13751
|
+
const fromEnv = Number.parseInt(env.KYNVER_DAEMON_STALL_MS ?? "", 10);
|
|
13752
|
+
if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv;
|
|
13753
|
+
return DEFAULT_STALL_MS;
|
|
13754
|
+
}
|
|
13755
|
+
function shouldRunDaemonKeeper(args, env = process.env) {
|
|
13756
|
+
if (args.keeperChild === true || args.keeperChild === "true") return false;
|
|
13757
|
+
if (args.noSupervise === true || args.noSupervise === "true") return false;
|
|
13758
|
+
if (args.supervised === "false") return false;
|
|
13759
|
+
const envFlag5 = (env.KYNVER_DAEMON_SUPERVISED ?? "").trim().toLowerCase();
|
|
13760
|
+
if (envFlag5 === "0" || envFlag5 === "false" || envFlag5 === "no" || envFlag5 === "off") {
|
|
13761
|
+
return false;
|
|
13762
|
+
}
|
|
13763
|
+
return true;
|
|
13764
|
+
}
|
|
13765
|
+
function nextKeeperBackoffMs(consecutiveFailures, base = BACKOFF_BASE_MS, cap = BACKOFF_CAP_MS) {
|
|
13766
|
+
const exp = Math.min(Math.max(consecutiveFailures, 1) - 1, 10);
|
|
13767
|
+
return Math.min(base * 2 ** exp, cap);
|
|
13768
|
+
}
|
|
13769
|
+
function keeperRunWasHealthy(startedAtMs, endedAtMs, healthyMs = HEALTHY_RESET_MS) {
|
|
13770
|
+
return endedAtMs - startedAtMs >= healthyMs;
|
|
13771
|
+
}
|
|
13772
|
+
function keeperLog(event, detail = {}) {
|
|
13773
|
+
console.error(JSON.stringify({ event: `daemon_keeper_${event}`, ...detail }));
|
|
13774
|
+
}
|
|
13775
|
+
function buildKeeperChildArgv(argv) {
|
|
13776
|
+
const out = [];
|
|
13777
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
13778
|
+
const arg = argv[i];
|
|
13779
|
+
if (arg === "--supervised" || arg === "--no-supervise" || arg === "--keeper-child") continue;
|
|
13780
|
+
if (arg === "--stall-ms") {
|
|
13781
|
+
if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) i += 1;
|
|
13782
|
+
continue;
|
|
13783
|
+
}
|
|
13784
|
+
if (arg.startsWith("--stall-ms=") || arg.startsWith("--supervised=")) continue;
|
|
13785
|
+
out.push(arg);
|
|
13786
|
+
}
|
|
13787
|
+
out.push("--keeper-child");
|
|
13788
|
+
return out;
|
|
13789
|
+
}
|
|
13790
|
+
async function runDaemonKeeper(args, rawArgv = process.argv.slice(2)) {
|
|
13791
|
+
const agentOsId = String(
|
|
13792
|
+
required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id")
|
|
13793
|
+
);
|
|
13794
|
+
const stallMs = resolveKeeperStallMs(args.stallMs);
|
|
13795
|
+
const childArgv = buildKeeperChildArgv(rawArgv);
|
|
13796
|
+
const cliEntry = process.argv[1];
|
|
13797
|
+
let stopping = false;
|
|
13798
|
+
let child = null;
|
|
13799
|
+
let consecutiveFailures = 0;
|
|
13800
|
+
const stop = (signal) => {
|
|
13801
|
+
stopping = true;
|
|
13802
|
+
keeperLog("stop", { signal });
|
|
13803
|
+
if (child?.pid) child.kill(signal);
|
|
13804
|
+
};
|
|
13805
|
+
process.on("SIGINT", () => stop("SIGINT"));
|
|
13806
|
+
process.on("SIGTERM", () => stop("SIGTERM"));
|
|
13807
|
+
keeperLog("start", { agentOsId, stallMs, childArgv });
|
|
13808
|
+
while (!stopping) {
|
|
13809
|
+
const startedAt = Date.now();
|
|
13810
|
+
let exited = false;
|
|
13811
|
+
let exitCode = null;
|
|
13812
|
+
let exitSignal = null;
|
|
13813
|
+
child = spawn6(process.execPath, [cliEntry, ...childArgv], {
|
|
13814
|
+
stdio: "inherit",
|
|
13815
|
+
env: process.env
|
|
13816
|
+
});
|
|
13817
|
+
keeperLog("child_spawned", { pid: child.pid ?? null });
|
|
13818
|
+
child.on("exit", (code, signal) => {
|
|
13819
|
+
exited = true;
|
|
13820
|
+
exitCode = code;
|
|
13821
|
+
exitSignal = signal;
|
|
13822
|
+
});
|
|
13823
|
+
while (!exited && !stopping) {
|
|
13824
|
+
await sleepMsAsync(POLL_MS);
|
|
13825
|
+
if (exited || stopping) break;
|
|
13826
|
+
if (Date.now() - startedAt < STARTUP_GRACE_MS) continue;
|
|
13827
|
+
const beat = readDaemonHeartbeat(agentOsId);
|
|
13828
|
+
const ownBeat = beat && beat.pid === child.pid ? beat : null;
|
|
13829
|
+
if (ownBeat && isDaemonHeartbeatStale(ownBeat, stallMs)) {
|
|
13830
|
+
keeperLog("stall_detected", {
|
|
13831
|
+
pid: child.pid ?? null,
|
|
13832
|
+
lastBeatAt: ownBeat.observedAt,
|
|
13833
|
+
stallMs
|
|
13834
|
+
});
|
|
13835
|
+
child.kill("SIGTERM");
|
|
13836
|
+
await sleepMsAsync(KILL_GRACE_MS);
|
|
13837
|
+
if (!exited) child.kill("SIGKILL");
|
|
13838
|
+
break;
|
|
13839
|
+
}
|
|
13840
|
+
if (!ownBeat && Date.now() - startedAt > stallMs + STARTUP_GRACE_MS) {
|
|
13841
|
+
keeperLog("no_heartbeat_detected", { pid: child.pid ?? null, stallMs });
|
|
13842
|
+
child.kill("SIGTERM");
|
|
13843
|
+
await sleepMsAsync(KILL_GRACE_MS);
|
|
13844
|
+
if (!exited) child.kill("SIGKILL");
|
|
13845
|
+
break;
|
|
13846
|
+
}
|
|
13847
|
+
}
|
|
13848
|
+
while (!exited && !stopping) {
|
|
13849
|
+
await sleepMsAsync(POLL_MS);
|
|
13850
|
+
}
|
|
13851
|
+
if (stopping) break;
|
|
13852
|
+
const endedAt = Date.now();
|
|
13853
|
+
if (keeperRunWasHealthy(startedAt, endedAt)) consecutiveFailures = 0;
|
|
13854
|
+
consecutiveFailures += 1;
|
|
13855
|
+
const backoff = nextKeeperBackoffMs(consecutiveFailures);
|
|
13856
|
+
keeperLog("child_exited", {
|
|
13857
|
+
code: exitCode,
|
|
13858
|
+
signal: exitSignal,
|
|
13859
|
+
uptimeMs: endedAt - startedAt,
|
|
13860
|
+
consecutiveFailures,
|
|
13861
|
+
respawnInMs: backoff
|
|
13862
|
+
});
|
|
13863
|
+
await sleepMsAsync(backoff);
|
|
13864
|
+
}
|
|
13865
|
+
keeperLog("stopped", { agentOsId });
|
|
13866
|
+
}
|
|
13867
|
+
|
|
13402
13868
|
// src/worktree.ts
|
|
13403
13869
|
init_git();
|
|
13404
13870
|
init_run_store();
|
|
13405
|
-
import { existsSync as
|
|
13406
|
-
import
|
|
13871
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync9 } from "node:fs";
|
|
13872
|
+
import path60 from "node:path";
|
|
13407
13873
|
|
|
13408
13874
|
// src/run-list.ts
|
|
13409
13875
|
init_run_store();
|
|
13410
13876
|
init_run_worker_index();
|
|
13411
|
-
import { existsSync as
|
|
13412
|
-
import
|
|
13877
|
+
import { existsSync as existsSync39, readFileSync as readFileSync15 } from "node:fs";
|
|
13878
|
+
import path59 from "node:path";
|
|
13413
13879
|
init_status();
|
|
13414
13880
|
init_util();
|
|
13415
13881
|
function heartbeatByteLength(heartbeatPath) {
|
|
13416
|
-
if (!heartbeatPath || !
|
|
13882
|
+
if (!heartbeatPath || !existsSync39(heartbeatPath)) return 0;
|
|
13417
13883
|
try {
|
|
13418
13884
|
return readFileSync15(heartbeatPath, "utf8").trim().length;
|
|
13419
13885
|
} catch {
|
|
@@ -13421,7 +13887,7 @@ function heartbeatByteLength(heartbeatPath) {
|
|
|
13421
13887
|
}
|
|
13422
13888
|
}
|
|
13423
13889
|
function workerEvidence(run, workerName) {
|
|
13424
|
-
const workerPath =
|
|
13890
|
+
const workerPath = path59.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
|
|
13425
13891
|
const worker = readJson(workerPath, void 0);
|
|
13426
13892
|
if (!worker) {
|
|
13427
13893
|
return {
|
|
@@ -13478,7 +13944,7 @@ function aggregateRunAttention(workers) {
|
|
|
13478
13944
|
function countOpenWorkers(run) {
|
|
13479
13945
|
let open = 0;
|
|
13480
13946
|
for (const name of listRunWorkerNames(run)) {
|
|
13481
|
-
const workerPath =
|
|
13947
|
+
const workerPath = path59.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
13482
13948
|
const worker = readJson(workerPath, void 0);
|
|
13483
13949
|
if (!worker) continue;
|
|
13484
13950
|
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
@@ -13541,8 +14007,8 @@ function createRun(args) {
|
|
|
13541
14007
|
ensureGitRepo(repo);
|
|
13542
14008
|
const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
|
|
13543
14009
|
const dir = runDirectory(id);
|
|
13544
|
-
if (
|
|
13545
|
-
|
|
14010
|
+
if (existsSync40(dir)) failExists(`run already exists: ${id}`);
|
|
14011
|
+
mkdirSync9(dir, { recursive: true });
|
|
13546
14012
|
const base = String(args.base || "origin/main");
|
|
13547
14013
|
const baseCommit = git(repo, ["rev-parse", base]).trim();
|
|
13548
14014
|
const run = {
|
|
@@ -13555,7 +14021,7 @@ function createRun(args) {
|
|
|
13555
14021
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13556
14022
|
workers: {}
|
|
13557
14023
|
};
|
|
13558
|
-
writeJson(
|
|
14024
|
+
writeJson(path60.join(dir, "run.json"), run);
|
|
13559
14025
|
const info = { runId: id, runDir: dir, repo, base, baseCommit };
|
|
13560
14026
|
console.log(JSON.stringify(info, null, 2));
|
|
13561
14027
|
return info;
|
|
@@ -13587,6 +14053,11 @@ async function runStart(args) {
|
|
|
13587
14053
|
console.error("No AgentOS workspace configured \u2014 run `kynver bootstrap` (or pass --agent-os-id).");
|
|
13588
14054
|
process.exit(1);
|
|
13589
14055
|
}
|
|
14056
|
+
if (args.chatOauth === true && config.chatUseClaudeOauth !== true) {
|
|
14057
|
+
saveUserConfig({ ...config, chatUseClaudeOauth: true });
|
|
14058
|
+
config = loadUserConfig();
|
|
14059
|
+
console.log(" Chat: Claude Code OAuth opt-in saved (delegated turns may use your local subscription).");
|
|
14060
|
+
}
|
|
13590
14061
|
const repo = (typeof args.repo === "string" ? args.repo.trim() : "") || config.defaultRepo?.trim() || resolveDefaultRepo()?.repo || "";
|
|
13591
14062
|
if (!repo) {
|
|
13592
14063
|
console.error("No repo configured \u2014 pass --repo /path/to/repo or run `kynver setup --discover-repo`.");
|
|
@@ -13608,7 +14079,25 @@ async function runStart(args) {
|
|
|
13608
14079
|
console.log(` run: ${runId}`);
|
|
13609
14080
|
console.log(" Ctrl-C stops the agent. (Advanced control: `kynver daemon --help`.)");
|
|
13610
14081
|
console.log("");
|
|
13611
|
-
|
|
14082
|
+
const daemonArgs = { ...args, run: runId, agentOsId };
|
|
14083
|
+
if (shouldRunDaemonKeeper(daemonArgs)) {
|
|
14084
|
+
await runDaemonKeeper(daemonArgs, buildStartDaemonArgv(runId, agentOsId, args));
|
|
14085
|
+
return;
|
|
14086
|
+
}
|
|
14087
|
+
await runDaemon(daemonArgs);
|
|
14088
|
+
}
|
|
14089
|
+
function buildStartDaemonArgv(runId, agentOsId, args) {
|
|
14090
|
+
const argv = ["daemon", "--run", runId, "--agent-os-id", agentOsId];
|
|
14091
|
+
if (typeof args.intervalMs === "string" && args.intervalMs.trim()) {
|
|
14092
|
+
argv.push("--interval-ms", args.intervalMs.trim());
|
|
14093
|
+
}
|
|
14094
|
+
if (args.execute === false || args.execute === "false") {
|
|
14095
|
+
argv.push("--execute", "false");
|
|
14096
|
+
}
|
|
14097
|
+
if (typeof args.stallMs === "string" && args.stallMs.trim()) {
|
|
14098
|
+
argv.push("--stall-ms", args.stallMs.trim());
|
|
14099
|
+
}
|
|
14100
|
+
return argv;
|
|
13612
14101
|
}
|
|
13613
14102
|
|
|
13614
14103
|
// src/cli.ts
|
|
@@ -13617,8 +14106,8 @@ init_run_store();
|
|
|
13617
14106
|
// src/discard-disposable.ts
|
|
13618
14107
|
init_run_store();
|
|
13619
14108
|
init_status();
|
|
13620
|
-
import { existsSync as
|
|
13621
|
-
import
|
|
14109
|
+
import { existsSync as existsSync41, rmSync as rmSync4 } from "node:fs";
|
|
14110
|
+
import path61 from "node:path";
|
|
13622
14111
|
function normalizeRelativePath2(value) {
|
|
13623
14112
|
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
13624
14113
|
if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
|
|
@@ -13640,15 +14129,15 @@ function discardDisposableArtifacts(args) {
|
|
|
13640
14129
|
if (paths.length === 0) {
|
|
13641
14130
|
return { ok: false, removed: [], reason: "requires at least one --path" };
|
|
13642
14131
|
}
|
|
13643
|
-
const worktreeRoot =
|
|
14132
|
+
const worktreeRoot = path61.resolve(worker.worktreePath);
|
|
13644
14133
|
const removed = [];
|
|
13645
14134
|
for (const raw of paths) {
|
|
13646
14135
|
const rel = normalizeRelativePath2(raw);
|
|
13647
|
-
const abs =
|
|
13648
|
-
if (!abs.startsWith(worktreeRoot +
|
|
14136
|
+
const abs = path61.resolve(worktreeRoot, rel);
|
|
14137
|
+
if (!abs.startsWith(worktreeRoot + path61.sep) && abs !== worktreeRoot) {
|
|
13649
14138
|
return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
|
|
13650
14139
|
}
|
|
13651
|
-
if (!
|
|
14140
|
+
if (!existsSync41(abs)) {
|
|
13652
14141
|
return { ok: false, removed, reason: `path not found: ${raw}` };
|
|
13653
14142
|
}
|
|
13654
14143
|
rmSync4(abs, { recursive: true, force: true });
|
|
@@ -13670,140 +14159,9 @@ function discardDisposableCli(args) {
|
|
|
13670
14159
|
if (!result.ok) process.exit(1);
|
|
13671
14160
|
}
|
|
13672
14161
|
|
|
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
14162
|
// src/plan-progress.ts
|
|
13805
14163
|
init_config();
|
|
13806
|
-
import
|
|
14164
|
+
import path65 from "node:path";
|
|
13807
14165
|
|
|
13808
14166
|
// src/bounded-build/constants.ts
|
|
13809
14167
|
var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
|
|
@@ -13844,7 +14202,7 @@ function formatNodeOptionsFlag(mb = resolveNodeOldSpaceSizeMb()) {
|
|
|
13844
14202
|
}
|
|
13845
14203
|
|
|
13846
14204
|
// src/bounded-build/systemd-wrap.ts
|
|
13847
|
-
import { spawnSync as
|
|
14205
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
13848
14206
|
var systemdAvailableCache;
|
|
13849
14207
|
function isSystemdRunAvailable() {
|
|
13850
14208
|
if (process.env.KYNVER_BUILD_SKIP_SYSTEMD === "1" || process.env.KYNVER_BUILD_SKIP_SYSTEMD === "true") {
|
|
@@ -13855,7 +14213,7 @@ function isSystemdRunAvailable() {
|
|
|
13855
14213
|
systemdAvailableCache = false;
|
|
13856
14214
|
return false;
|
|
13857
14215
|
}
|
|
13858
|
-
const res =
|
|
14216
|
+
const res = spawnSync9("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
|
|
13859
14217
|
systemdAvailableCache = res.status === 0;
|
|
13860
14218
|
return systemdAvailableCache;
|
|
13861
14219
|
}
|
|
@@ -13880,7 +14238,7 @@ function buildSystemdRunArgv(opts) {
|
|
|
13880
14238
|
|
|
13881
14239
|
// src/bounded-build/admission.ts
|
|
13882
14240
|
init_config();
|
|
13883
|
-
import { spawnSync as
|
|
14241
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
13884
14242
|
init_meminfo();
|
|
13885
14243
|
function positiveInt4(value, fallback) {
|
|
13886
14244
|
const n = Number(value);
|
|
@@ -13917,7 +14275,7 @@ function assessBuildAdmission(opts = {}) {
|
|
|
13917
14275
|
}
|
|
13918
14276
|
function sleepMs2(ms) {
|
|
13919
14277
|
if (ms <= 0) return;
|
|
13920
|
-
|
|
14278
|
+
spawnSync10(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
13921
14279
|
stdio: "ignore"
|
|
13922
14280
|
});
|
|
13923
14281
|
}
|
|
@@ -13938,34 +14296,34 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
|
|
|
13938
14296
|
}
|
|
13939
14297
|
|
|
13940
14298
|
// src/bounded-build/exec.ts
|
|
13941
|
-
import { spawnSync as
|
|
14299
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
13942
14300
|
|
|
13943
14301
|
// src/heavy-verification/slot.ts
|
|
13944
14302
|
init_util();
|
|
13945
14303
|
import {
|
|
13946
14304
|
closeSync as closeSync7,
|
|
13947
|
-
existsSync as
|
|
13948
|
-
mkdirSync as
|
|
14305
|
+
existsSync as existsSync42,
|
|
14306
|
+
mkdirSync as mkdirSync11,
|
|
13949
14307
|
openSync as openSync7,
|
|
13950
|
-
readdirSync as
|
|
14308
|
+
readdirSync as readdirSync14,
|
|
13951
14309
|
readFileSync as readFileSync16,
|
|
13952
14310
|
unlinkSync as unlinkSync4,
|
|
13953
|
-
writeFileSync as
|
|
14311
|
+
writeFileSync as writeFileSync7
|
|
13954
14312
|
} from "node:fs";
|
|
13955
|
-
import
|
|
14313
|
+
import path63 from "node:path";
|
|
13956
14314
|
|
|
13957
14315
|
// src/heavy-verification/paths.ts
|
|
13958
|
-
import { mkdirSync as
|
|
13959
|
-
import
|
|
14316
|
+
import { mkdirSync as mkdirSync10 } from "node:fs";
|
|
14317
|
+
import path62 from "node:path";
|
|
13960
14318
|
function resolveHeavyVerificationRoot() {
|
|
13961
|
-
return
|
|
14319
|
+
return path62.join(resolveKynverStateRoot(), "heavy-verification");
|
|
13962
14320
|
}
|
|
13963
14321
|
function heavyVerificationSlotsDir() {
|
|
13964
|
-
return
|
|
14322
|
+
return path62.join(resolveHeavyVerificationRoot(), "slots");
|
|
13965
14323
|
}
|
|
13966
14324
|
function ensureHeavyVerificationDirs() {
|
|
13967
14325
|
const dir = heavyVerificationSlotsDir();
|
|
13968
|
-
|
|
14326
|
+
mkdirSync10(dir, { recursive: true });
|
|
13969
14327
|
return dir;
|
|
13970
14328
|
}
|
|
13971
14329
|
|
|
@@ -13990,10 +14348,10 @@ function indexedSlotId(index) {
|
|
|
13990
14348
|
return `slot-${index}`;
|
|
13991
14349
|
}
|
|
13992
14350
|
function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
|
|
13993
|
-
return
|
|
14351
|
+
return path63.join(slotsDir, `${slotId}.json`);
|
|
13994
14352
|
}
|
|
13995
14353
|
function readSlotRecord(filePath) {
|
|
13996
|
-
if (!
|
|
14354
|
+
if (!existsSync42(filePath)) return null;
|
|
13997
14355
|
try {
|
|
13998
14356
|
const parsed = JSON.parse(readFileSync16(filePath, "utf8"));
|
|
13999
14357
|
if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
|
|
@@ -14020,19 +14378,19 @@ function reclaimStaleSlot(filePath, staleMs) {
|
|
|
14020
14378
|
}
|
|
14021
14379
|
}
|
|
14022
14380
|
function ensureSlotsDir(slotsDir) {
|
|
14023
|
-
|
|
14381
|
+
mkdirSync11(slotsDir, { recursive: true });
|
|
14024
14382
|
return slotsDir;
|
|
14025
14383
|
}
|
|
14026
14384
|
function reclaimStaleHeavyVerificationSlots(opts = {}) {
|
|
14027
14385
|
const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
|
|
14028
14386
|
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
14029
14387
|
let reclaimed = 0;
|
|
14030
|
-
for (const name of
|
|
14388
|
+
for (const name of readdirSync14(slotsDir)) {
|
|
14031
14389
|
if (!name.endsWith(".json")) continue;
|
|
14032
|
-
const filePath =
|
|
14033
|
-
const before =
|
|
14390
|
+
const filePath = path63.join(slotsDir, name);
|
|
14391
|
+
const before = existsSync42(filePath);
|
|
14034
14392
|
reclaimStaleSlot(filePath, staleMs);
|
|
14035
|
-
if (before && !
|
|
14393
|
+
if (before && !existsSync42(filePath)) reclaimed += 1;
|
|
14036
14394
|
}
|
|
14037
14395
|
return reclaimed;
|
|
14038
14396
|
}
|
|
@@ -14041,9 +14399,9 @@ function listActiveHeavyVerificationSlots(opts = {}) {
|
|
|
14041
14399
|
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
14042
14400
|
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
14043
14401
|
const active = [];
|
|
14044
|
-
for (const name of
|
|
14402
|
+
for (const name of readdirSync14(slotsDir)) {
|
|
14045
14403
|
if (!name.endsWith(".json")) continue;
|
|
14046
|
-
const record = readSlotRecord(
|
|
14404
|
+
const record = readSlotRecord(path63.join(slotsDir, name));
|
|
14047
14405
|
if (record && !slotIsStale(record, staleMs)) active.push(record);
|
|
14048
14406
|
}
|
|
14049
14407
|
return active;
|
|
@@ -14085,7 +14443,7 @@ function tryAcquireHeavyVerificationSlot(command, opts = {}) {
|
|
|
14085
14443
|
};
|
|
14086
14444
|
try {
|
|
14087
14445
|
const fd = openSync7(filePath, "wx");
|
|
14088
|
-
|
|
14446
|
+
writeFileSync7(fd, JSON.stringify(record, null, 2), "utf8");
|
|
14089
14447
|
closeSync7(fd);
|
|
14090
14448
|
const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
14091
14449
|
return {
|
|
@@ -14145,10 +14503,10 @@ function assessHeavyVerificationGate(command, opts = {}) {
|
|
|
14145
14503
|
}
|
|
14146
14504
|
|
|
14147
14505
|
// src/heavy-verification/gate.ts
|
|
14148
|
-
import { spawnSync as
|
|
14506
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
14149
14507
|
function sleepMs3(ms) {
|
|
14150
14508
|
if (ms <= 0) return;
|
|
14151
|
-
|
|
14509
|
+
spawnSync11(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
14152
14510
|
stdio: "ignore"
|
|
14153
14511
|
});
|
|
14154
14512
|
}
|
|
@@ -14164,11 +14522,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
|
|
|
14164
14522
|
|
|
14165
14523
|
// src/harness-worktree-build-guard.ts
|
|
14166
14524
|
init_paths();
|
|
14167
|
-
import
|
|
14525
|
+
import path64 from "node:path";
|
|
14168
14526
|
function isPathUnderHarnessWorktree(cwd) {
|
|
14169
14527
|
const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
|
|
14170
|
-
const rel =
|
|
14171
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
14528
|
+
const rel = path64.relative(worktreesDir, path64.resolve(cwd));
|
|
14529
|
+
return rel.length > 0 && !rel.startsWith("..") && !path64.isAbsolute(rel);
|
|
14172
14530
|
}
|
|
14173
14531
|
function assessHarnessWorktreeBuildGuard(cwd) {
|
|
14174
14532
|
if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
|
|
@@ -14192,7 +14550,7 @@ function envArgv(env) {
|
|
|
14192
14550
|
return out;
|
|
14193
14551
|
}
|
|
14194
14552
|
function runSpawn(argv, opts) {
|
|
14195
|
-
const res =
|
|
14553
|
+
const res = spawnSync12(argv[0], argv.slice(1), {
|
|
14196
14554
|
cwd: opts.cwd,
|
|
14197
14555
|
env: opts.env,
|
|
14198
14556
|
encoding: "utf8",
|
|
@@ -14381,7 +14739,7 @@ async function emitPlanProgress(args) {
|
|
|
14381
14739
|
}
|
|
14382
14740
|
function verifyPlanLocal(args) {
|
|
14383
14741
|
const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
|
|
14384
|
-
const cwd =
|
|
14742
|
+
const cwd = path65.resolve(worktree);
|
|
14385
14743
|
const summary = runHarnessVerifyCommands(cwd);
|
|
14386
14744
|
const emitJson = args.json === true || args.json === "true";
|
|
14387
14745
|
const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
|
|
@@ -14430,10 +14788,10 @@ async function verifyPlan(args) {
|
|
|
14430
14788
|
}
|
|
14431
14789
|
|
|
14432
14790
|
// src/harness-verify-cli.ts
|
|
14433
|
-
import
|
|
14791
|
+
import path66 from "node:path";
|
|
14434
14792
|
init_util();
|
|
14435
14793
|
function runHarnessVerifyCli(args) {
|
|
14436
|
-
const cwd =
|
|
14794
|
+
const cwd = path66.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
|
|
14437
14795
|
const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
|
|
14438
14796
|
const commands = [];
|
|
14439
14797
|
const rawCmd = args.command;
|
|
@@ -14565,7 +14923,9 @@ function runCleanupCli(args) {
|
|
|
14565
14923
|
const skipFinalize = args.skipFinalize === true || args.skipFinalize === "true";
|
|
14566
14924
|
const accountBytes = args.accountBytes === true || args.accountBytes === "true";
|
|
14567
14925
|
const compact = args.compact === true || args.compact === "true";
|
|
14568
|
-
const
|
|
14926
|
+
const nodeModulesAgeMsRaw = args.nodeModulesAgeMs;
|
|
14927
|
+
const scanDependencyCaches = nodeModulesAgeMsRaw != null && nodeModulesAgeMsRaw !== "";
|
|
14928
|
+
const nodeModulesAgeMs = scanDependencyCaches ? Number(nodeModulesAgeMsRaw) : void 0;
|
|
14569
14929
|
const worktreesAgeMs = args.worktreesAgeMs ? Number(args.worktreesAgeMs) : 0;
|
|
14570
14930
|
const includeOrphans = args.includeOrphans === true || args.includeOrphans === "true";
|
|
14571
14931
|
const harnessRoot = args.harnessRoot ? String(args.harnessRoot) : void 0;
|
|
@@ -14573,7 +14933,8 @@ function runCleanupCli(args) {
|
|
|
14573
14933
|
execute,
|
|
14574
14934
|
finalizeStaleRuns: !skipFinalize,
|
|
14575
14935
|
accountBytes,
|
|
14576
|
-
|
|
14936
|
+
scanDependencyCaches,
|
|
14937
|
+
nodeModulesAgeMs: nodeModulesAgeMs !== void 0 && Number.isFinite(nodeModulesAgeMs) ? nodeModulesAgeMs : scanDependencyCaches ? DEFAULT_NODE_MODULES_AGE_MS : void 0,
|
|
14577
14938
|
worktreesAgeMs: Number.isFinite(worktreesAgeMs) ? worktreesAgeMs : 0,
|
|
14578
14939
|
includeOrphans,
|
|
14579
14940
|
harnessRoot
|
|
@@ -14643,7 +15004,7 @@ function formatMonitorTickNotice(tick) {
|
|
|
14643
15004
|
}
|
|
14644
15005
|
|
|
14645
15006
|
// src/monitor/monitor.service.ts
|
|
14646
|
-
import
|
|
15007
|
+
import path68 from "node:path";
|
|
14647
15008
|
init_run_store();
|
|
14648
15009
|
init_status();
|
|
14649
15010
|
init_util();
|
|
@@ -14702,19 +15063,19 @@ function classifyWorkerHealth(input) {
|
|
|
14702
15063
|
// src/monitor/monitor.store.ts
|
|
14703
15064
|
init_paths();
|
|
14704
15065
|
init_util();
|
|
14705
|
-
import { existsSync as
|
|
14706
|
-
import
|
|
15066
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync12, readdirSync as readdirSync15, unlinkSync as unlinkSync5 } from "node:fs";
|
|
15067
|
+
import path67 from "node:path";
|
|
14707
15068
|
function monitorsDir() {
|
|
14708
15069
|
const { harnessRoot } = getHarnessPaths();
|
|
14709
|
-
const dir =
|
|
14710
|
-
|
|
15070
|
+
const dir = path67.join(harnessRoot, "monitors");
|
|
15071
|
+
mkdirSync12(dir, { recursive: true });
|
|
14711
15072
|
return dir;
|
|
14712
15073
|
}
|
|
14713
15074
|
function monitorIdFor(runId, workerName) {
|
|
14714
15075
|
return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
|
|
14715
15076
|
}
|
|
14716
15077
|
function monitorPath(monitorId) {
|
|
14717
|
-
return
|
|
15078
|
+
return path67.join(monitorsDir(), `${monitorId}.json`);
|
|
14718
15079
|
}
|
|
14719
15080
|
function loadMonitorSession(monitorId) {
|
|
14720
15081
|
return readJson(monitorPath(monitorId), void 0);
|
|
@@ -14724,18 +15085,18 @@ function saveMonitorSession(session) {
|
|
|
14724
15085
|
}
|
|
14725
15086
|
function deleteMonitorSession(monitorId) {
|
|
14726
15087
|
const file = monitorPath(monitorId);
|
|
14727
|
-
if (!
|
|
15088
|
+
if (!existsSync43(file)) return false;
|
|
14728
15089
|
unlinkSync5(file);
|
|
14729
15090
|
return true;
|
|
14730
15091
|
}
|
|
14731
15092
|
function listMonitorSessions() {
|
|
14732
15093
|
const dir = monitorsDir();
|
|
14733
|
-
if (!
|
|
15094
|
+
if (!existsSync43(dir)) return [];
|
|
14734
15095
|
const entries = [];
|
|
14735
|
-
for (const name of
|
|
15096
|
+
for (const name of readdirSync15(dir)) {
|
|
14736
15097
|
if (!name.endsWith(".json")) continue;
|
|
14737
15098
|
const session = readJson(
|
|
14738
|
-
|
|
15099
|
+
path67.join(dir, name),
|
|
14739
15100
|
void 0
|
|
14740
15101
|
);
|
|
14741
15102
|
if (!session?.monitorId) continue;
|
|
@@ -14828,7 +15189,7 @@ async function fetchTaskLeasesForWorkers(input) {
|
|
|
14828
15189
|
// src/monitor/monitor.service.ts
|
|
14829
15190
|
function workerRecord2(runId, name) {
|
|
14830
15191
|
return readJson(
|
|
14831
|
-
|
|
15192
|
+
path68.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
|
|
14832
15193
|
void 0
|
|
14833
15194
|
);
|
|
14834
15195
|
}
|
|
@@ -15037,18 +15398,18 @@ async function runMonitorLoop(args) {
|
|
|
15037
15398
|
init_util();
|
|
15038
15399
|
init_paths();
|
|
15039
15400
|
import { spawn as spawn7 } from "node:child_process";
|
|
15040
|
-
import { closeSync as closeSync8, existsSync as
|
|
15041
|
-
import
|
|
15401
|
+
import { closeSync as closeSync8, existsSync as existsSync44, openSync as openSync8 } from "node:fs";
|
|
15402
|
+
import path69 from "node:path";
|
|
15042
15403
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
15043
15404
|
function resolveDefaultCliPath2() {
|
|
15044
|
-
return
|
|
15405
|
+
return path69.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
|
|
15045
15406
|
}
|
|
15046
15407
|
function spawnMonitorSidecar(opts) {
|
|
15047
15408
|
const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
|
|
15048
|
-
if (!
|
|
15409
|
+
if (!existsSync44(cliPath)) return void 0;
|
|
15049
15410
|
const monitorId = monitorIdFor(opts.runId, opts.workerName);
|
|
15050
15411
|
const { harnessRoot } = getHarnessPaths();
|
|
15051
|
-
const logPath =
|
|
15412
|
+
const logPath = path69.join(harnessRoot, "monitors", `${monitorId}.log`);
|
|
15052
15413
|
let logFd;
|
|
15053
15414
|
try {
|
|
15054
15415
|
logFd = openSync8(logPath, "a");
|
|
@@ -15169,13 +15530,13 @@ async function monitorTickCli(args) {
|
|
|
15169
15530
|
}
|
|
15170
15531
|
|
|
15171
15532
|
// src/package-version.ts
|
|
15172
|
-
import { existsSync as
|
|
15533
|
+
import { existsSync as existsSync45, readFileSync as readFileSync18 } from "node:fs";
|
|
15173
15534
|
import { dirname, join } from "node:path";
|
|
15174
15535
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
15175
15536
|
function resolvePackageRoot(moduleUrl) {
|
|
15176
15537
|
let dir = dirname(fileURLToPath4(moduleUrl));
|
|
15177
15538
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
15178
|
-
if (
|
|
15539
|
+
if (existsSync45(join(dir, "package.json"))) return dir;
|
|
15179
15540
|
const parent = dirname(dir);
|
|
15180
15541
|
if (parent === dir) break;
|
|
15181
15542
|
dir = parent;
|
|
@@ -15281,7 +15642,7 @@ init_run_store();
|
|
|
15281
15642
|
init_status();
|
|
15282
15643
|
init_util();
|
|
15283
15644
|
init_config();
|
|
15284
|
-
import
|
|
15645
|
+
import path70 from "node:path";
|
|
15285
15646
|
function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
|
|
15286
15647
|
return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
|
|
15287
15648
|
}
|
|
@@ -15294,7 +15655,7 @@ async function postRestartUnblock(args) {
|
|
|
15294
15655
|
const errors = [];
|
|
15295
15656
|
for (const run of listRunRecords()) {
|
|
15296
15657
|
for (const name of Object.keys(run.workers ?? {})) {
|
|
15297
|
-
const workerPath =
|
|
15658
|
+
const workerPath = path70.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
15298
15659
|
const worker = readJson(workerPath, void 0);
|
|
15299
15660
|
if (!worker) {
|
|
15300
15661
|
skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
|
|
@@ -15408,9 +15769,9 @@ async function postRestartUnblockCli(args) {
|
|
|
15408
15769
|
// src/default-repo-cli.ts
|
|
15409
15770
|
init_path_values();
|
|
15410
15771
|
init_config();
|
|
15411
|
-
import
|
|
15772
|
+
import path71 from "node:path";
|
|
15412
15773
|
import { homedir as homedir16 } from "node:os";
|
|
15413
|
-
var CONFIG_FILE2 =
|
|
15774
|
+
var CONFIG_FILE2 = path71.join(homedir16(), ".kynver", "config.json");
|
|
15414
15775
|
function ensureDefaultRepo(opts) {
|
|
15415
15776
|
const existing = loadUserConfig();
|
|
15416
15777
|
const resolved = resolveDefaultRepo({ ...opts, config: existing });
|
|
@@ -15491,19 +15852,19 @@ function summarizeResolvedDefaultRepo(resolved) {
|
|
|
15491
15852
|
}
|
|
15492
15853
|
|
|
15493
15854
|
// src/doctor/runtime-takeover.ts
|
|
15494
|
-
import
|
|
15855
|
+
import path73 from "node:path";
|
|
15495
15856
|
init_path_values();
|
|
15496
15857
|
|
|
15497
15858
|
// src/doctor/runtime-takeover.probes.ts
|
|
15498
15859
|
init_config();
|
|
15499
|
-
import { accessSync, constants, existsSync as
|
|
15860
|
+
import { accessSync, constants, existsSync as existsSync46, readFileSync as readFileSync19 } from "node:fs";
|
|
15500
15861
|
import { homedir as homedir17 } from "node:os";
|
|
15501
|
-
import
|
|
15502
|
-
import { spawnSync as
|
|
15862
|
+
import path72 from "node:path";
|
|
15863
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
15503
15864
|
init_paths();
|
|
15504
15865
|
function captureCommand(bin, args) {
|
|
15505
15866
|
try {
|
|
15506
|
-
const res =
|
|
15867
|
+
const res = spawnSync13(bin, args, { encoding: "utf8" });
|
|
15507
15868
|
const stdout = (res.stdout || "").trim();
|
|
15508
15869
|
const stderr = (res.stderr || "").trim();
|
|
15509
15870
|
const ok = res.status === 0;
|
|
@@ -15528,7 +15889,7 @@ function tokenPrefix(token) {
|
|
|
15528
15889
|
return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
|
|
15529
15890
|
}
|
|
15530
15891
|
function isWritable(target) {
|
|
15531
|
-
if (!
|
|
15892
|
+
if (!existsSync46(target)) return false;
|
|
15532
15893
|
try {
|
|
15533
15894
|
accessSync(target, constants.W_OK);
|
|
15534
15895
|
return true;
|
|
@@ -15541,11 +15902,11 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
15541
15902
|
commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
|
|
15542
15903
|
kynverVersion: (bin) => captureCommand(bin, ["--version"]),
|
|
15543
15904
|
loadConfig: () => loadUserConfig(),
|
|
15544
|
-
configFilePath: () =>
|
|
15545
|
-
credentialsFilePath: () =>
|
|
15905
|
+
configFilePath: () => path72.join(homedir17(), ".kynver", "config.json"),
|
|
15906
|
+
credentialsFilePath: () => path72.join(homedir17(), ".kynver", "credentials"),
|
|
15546
15907
|
readCredentials: () => {
|
|
15547
|
-
const credPath =
|
|
15548
|
-
if (!
|
|
15908
|
+
const credPath = path72.join(homedir17(), ".kynver", "credentials");
|
|
15909
|
+
if (!existsSync46(credPath)) {
|
|
15549
15910
|
return { hasApiKey: false };
|
|
15550
15911
|
}
|
|
15551
15912
|
try {
|
|
@@ -15579,8 +15940,8 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
15579
15940
|
})()
|
|
15580
15941
|
}),
|
|
15581
15942
|
harnessRoot: () => resolveHarnessRoot(),
|
|
15582
|
-
legacyOpenclawHarnessRoot: () =>
|
|
15583
|
-
pathExists: (target) =>
|
|
15943
|
+
legacyOpenclawHarnessRoot: () => path72.join(homedir17(), ".openclaw", "harness"),
|
|
15944
|
+
pathExists: (target) => existsSync46(target),
|
|
15584
15945
|
pathWritable: (target) => isWritable(target)
|
|
15585
15946
|
};
|
|
15586
15947
|
|
|
@@ -15986,8 +16347,8 @@ function assessVercelDeployEvidence(probes) {
|
|
|
15986
16347
|
}
|
|
15987
16348
|
function assessHarnessDirs(probes) {
|
|
15988
16349
|
const harnessRoot = probes.harnessRoot();
|
|
15989
|
-
const runsDir =
|
|
15990
|
-
const worktreesDir =
|
|
16350
|
+
const runsDir = path73.join(harnessRoot, "runs");
|
|
16351
|
+
const worktreesDir = path73.join(harnessRoot, "worktrees");
|
|
15991
16352
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
15992
16353
|
const displayRunsDir = redactHomePath(runsDir);
|
|
15993
16354
|
const displayWorktreesDir = redactHomePath(worktreesDir);
|
|
@@ -16256,9 +16617,9 @@ function applySchedulerCutoverAttestation(config) {
|
|
|
16256
16617
|
|
|
16257
16618
|
// src/scheduler-cutover-cli.ts
|
|
16258
16619
|
init_config();
|
|
16259
|
-
import
|
|
16620
|
+
import path74 from "node:path";
|
|
16260
16621
|
import { homedir as homedir18 } from "node:os";
|
|
16261
|
-
var CONFIG_FILE3 =
|
|
16622
|
+
var CONFIG_FILE3 = path74.join(homedir18(), ".kynver", "config.json");
|
|
16262
16623
|
function runSchedulerCutoverCheckCli(json = false) {
|
|
16263
16624
|
const config = loadUserConfig();
|
|
16264
16625
|
const report = assessSchedulerCutover(config);
|
|
@@ -16402,7 +16763,7 @@ init_config();
|
|
|
16402
16763
|
init_config();
|
|
16403
16764
|
init_path_values();
|
|
16404
16765
|
init_util();
|
|
16405
|
-
import { existsSync as
|
|
16766
|
+
import { existsSync as existsSync50 } from "node:fs";
|
|
16406
16767
|
|
|
16407
16768
|
// src/cron/cron-id.ts
|
|
16408
16769
|
import { createHash as createHash4 } from "node:crypto";
|
|
@@ -16421,13 +16782,13 @@ function deterministicCronProviderId(spec) {
|
|
|
16421
16782
|
|
|
16422
16783
|
// src/cron/cron-install-plan.ts
|
|
16423
16784
|
import { homedir as homedir20 } from "node:os";
|
|
16424
|
-
import
|
|
16785
|
+
import path76 from "node:path";
|
|
16425
16786
|
|
|
16426
16787
|
// src/cron/cron-env-file.ts
|
|
16427
|
-
import { existsSync as
|
|
16788
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync13, readFileSync as readFileSync20, writeFileSync as writeFileSync8 } from "node:fs";
|
|
16428
16789
|
import { homedir as homedir19 } from "node:os";
|
|
16429
|
-
import
|
|
16430
|
-
var DEFAULT_KYNVER_ENV_FILE =
|
|
16790
|
+
import path75 from "node:path";
|
|
16791
|
+
var DEFAULT_KYNVER_ENV_FILE = path75.join(homedir19(), ".kynver", ".env");
|
|
16431
16792
|
function parseEnvFile(content) {
|
|
16432
16793
|
const map = /* @__PURE__ */ new Map();
|
|
16433
16794
|
for (const line of content.split(/\r?\n/)) {
|
|
@@ -16454,12 +16815,12 @@ function serializeEnvFile(values, header = "# Managed by kynver cron install \u2
|
|
|
16454
16815
|
return lines.join("\n");
|
|
16455
16816
|
}
|
|
16456
16817
|
function readEnvFile(filePath = DEFAULT_KYNVER_ENV_FILE) {
|
|
16457
|
-
if (!
|
|
16818
|
+
if (!existsSync47(filePath)) return /* @__PURE__ */ new Map();
|
|
16458
16819
|
return parseEnvFile(readFileSync20(filePath, "utf8"));
|
|
16459
16820
|
}
|
|
16460
16821
|
function mergeEnvFile(updates, options = {}) {
|
|
16461
16822
|
const filePath = options.filePath ?? DEFAULT_KYNVER_ENV_FILE;
|
|
16462
|
-
const existing =
|
|
16823
|
+
const existing = existsSync47(filePath) ? readFileSync20(filePath, "utf8") : "";
|
|
16463
16824
|
const map = parseEnvFile(existing);
|
|
16464
16825
|
const keysWritten = [];
|
|
16465
16826
|
const keysRemoved = [];
|
|
@@ -16484,8 +16845,8 @@ function mergeEnvFile(updates, options = {}) {
|
|
|
16484
16845
|
}
|
|
16485
16846
|
const nextContent = serializeEnvFile(map);
|
|
16486
16847
|
if (changed) {
|
|
16487
|
-
|
|
16488
|
-
|
|
16848
|
+
mkdirSync13(path75.dirname(filePath), { recursive: true });
|
|
16849
|
+
writeFileSync8(filePath, nextContent, { mode: 384 });
|
|
16489
16850
|
}
|
|
16490
16851
|
return { path: filePath, changed, keysWritten, keysRemoved };
|
|
16491
16852
|
}
|
|
@@ -16502,7 +16863,7 @@ var VERCEL_KYNVER_CRON_CUTOVER_STEPS = [
|
|
|
16502
16863
|
function buildCronInstallPlan(input) {
|
|
16503
16864
|
const storePath = input.storePath?.trim() || defaultKynverCronStorePath();
|
|
16504
16865
|
const envFilePath = input.envFilePath?.trim() || DEFAULT_KYNVER_ENV_FILE;
|
|
16505
|
-
const configPath =
|
|
16866
|
+
const configPath = path76.join(homedir20(), ".kynver", "config.json");
|
|
16506
16867
|
const callbackPath = `/api/agent-os/by-id/${input.agentOsId}/scheduler/fire`;
|
|
16507
16868
|
const prerequisites = [];
|
|
16508
16869
|
if (!input.apiBaseUrl?.trim()) prerequisites.push("apiBaseUrl \u2014 run `kynver setup --api-base-url \u2026`");
|
|
@@ -16654,13 +17015,13 @@ function resolveCronSecretForInstall(envFilePath = DEFAULT_KYNVER_ENV_FILE) {
|
|
|
16654
17015
|
}
|
|
16655
17016
|
|
|
16656
17017
|
// src/cron/cron-install-systemd.ts
|
|
16657
|
-
import { existsSync as
|
|
17018
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync14, writeFileSync as writeFileSync9 } from "node:fs";
|
|
16658
17019
|
import { homedir as homedir21 } from "node:os";
|
|
16659
|
-
import
|
|
16660
|
-
import { spawnSync as
|
|
17020
|
+
import path77 from "node:path";
|
|
17021
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
16661
17022
|
var KYNVER_CRON_DAEMON_UNIT = "kynver-cron-daemon.service";
|
|
16662
17023
|
function defaultSystemdUserUnitDir() {
|
|
16663
|
-
return
|
|
17024
|
+
return path77.join(homedir21(), ".config", "systemd", "user");
|
|
16664
17025
|
}
|
|
16665
17026
|
function renderKynverCronDaemonService(input) {
|
|
16666
17027
|
const kynverBin = input.kynverBin?.trim() || "kynver";
|
|
@@ -16693,7 +17054,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
16693
17054
|
};
|
|
16694
17055
|
}
|
|
16695
17056
|
const unitDir = defaultSystemdUserUnitDir();
|
|
16696
|
-
const unitPath =
|
|
17057
|
+
const unitPath = path77.join(unitDir, KYNVER_CRON_DAEMON_UNIT);
|
|
16697
17058
|
const content = renderKynverCronDaemonService(input);
|
|
16698
17059
|
if (!execute) {
|
|
16699
17060
|
return {
|
|
@@ -16705,10 +17066,10 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
16705
17066
|
note: "Dry-run \u2014 pass --execute to write and enable the user unit."
|
|
16706
17067
|
};
|
|
16707
17068
|
}
|
|
16708
|
-
|
|
16709
|
-
const existed =
|
|
16710
|
-
|
|
16711
|
-
const reload =
|
|
17069
|
+
mkdirSync14(unitDir, { recursive: true });
|
|
17070
|
+
const existed = existsSync48(unitPath);
|
|
17071
|
+
writeFileSync9(unitPath, content, "utf8");
|
|
17072
|
+
const reload = spawnSync14("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
|
|
16712
17073
|
if (reload.status !== 0) {
|
|
16713
17074
|
return {
|
|
16714
17075
|
supported: true,
|
|
@@ -16719,7 +17080,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
16719
17080
|
note: `Wrote ${unitPath} but systemctl --user daemon-reload failed: ${reload.stderr || reload.stdout}`
|
|
16720
17081
|
};
|
|
16721
17082
|
}
|
|
16722
|
-
const enable =
|
|
17083
|
+
const enable = spawnSync14("systemctl", ["--user", "enable", "--now", KYNVER_CRON_DAEMON_UNIT], {
|
|
16723
17084
|
encoding: "utf8"
|
|
16724
17085
|
});
|
|
16725
17086
|
return {
|
|
@@ -16733,7 +17094,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
16733
17094
|
}
|
|
16734
17095
|
|
|
16735
17096
|
// src/cron/cron-install-verify.ts
|
|
16736
|
-
import { existsSync as
|
|
17097
|
+
import { existsSync as existsSync49 } from "node:fs";
|
|
16737
17098
|
async function verifyCronInstall(input) {
|
|
16738
17099
|
const envFilePath = input.envFilePath ?? DEFAULT_KYNVER_ENV_FILE;
|
|
16739
17100
|
const checks = [];
|
|
@@ -16766,14 +17127,14 @@ async function verifyCronInstall(input) {
|
|
|
16766
17127
|
});
|
|
16767
17128
|
checks.push({
|
|
16768
17129
|
id: "env_file",
|
|
16769
|
-
ok:
|
|
16770
|
-
summary:
|
|
17130
|
+
ok: existsSync49(envFilePath) && Boolean(fileEnv.get("KYNVER_CRON_SECRET")),
|
|
17131
|
+
summary: existsSync49(envFilePath) ? `~/.kynver/.env present (${fileEnv.size} keys)` : "~/.kynver/.env missing",
|
|
16771
17132
|
remediation: "Run `kynver cron install` to write ~/.kynver/.env."
|
|
16772
17133
|
});
|
|
16773
17134
|
checks.push({
|
|
16774
17135
|
id: "cron_store",
|
|
16775
|
-
ok:
|
|
16776
|
-
summary:
|
|
17136
|
+
ok: existsSync49(env.storePath),
|
|
17137
|
+
summary: existsSync49(env.storePath) ? `cron store present (${env.storePath})` : `cron store missing (${env.storePath})`,
|
|
16777
17138
|
remediation: "Run `kynver cron install` to initialize the local store."
|
|
16778
17139
|
});
|
|
16779
17140
|
const jobs = await loadCronJobs(env.storePath).catch(() => []);
|
|
@@ -16807,7 +17168,7 @@ function resolveDaemonRunId(config, explicit) {
|
|
|
16807
17168
|
if (explicit?.trim()) return explicit.trim();
|
|
16808
17169
|
if (config.defaultDaemonRunId?.trim()) return config.defaultDaemonRunId.trim();
|
|
16809
17170
|
const runsDir = getPaths().runsDir;
|
|
16810
|
-
if (!
|
|
17171
|
+
if (!existsSync50(runsDir)) return null;
|
|
16811
17172
|
const runs = listRunRecords().sort(
|
|
16812
17173
|
(a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt)
|
|
16813
17174
|
);
|
|
@@ -16859,7 +17220,7 @@ async function runCronInstall(opts = {}) {
|
|
|
16859
17220
|
process.env.KYNVER_CRON_STORE_PATH = plan.storePath;
|
|
16860
17221
|
process.env.KYNVER_CRON_TICK_ENABLED = "1";
|
|
16861
17222
|
const storeInit = await ensureCronStoreInitialized(plan.storePath);
|
|
16862
|
-
result.storeInitialized = storeInit.created ||
|
|
17223
|
+
result.storeInitialized = storeInit.created || existsSync50(plan.storePath);
|
|
16863
17224
|
const apiKey = loadApiKey();
|
|
16864
17225
|
if (apiKey) {
|
|
16865
17226
|
try {
|
|
@@ -17078,10 +17439,10 @@ var LANDING_MAINTAINER_LANE_SPEC = {
|
|
|
17078
17439
|
};
|
|
17079
17440
|
|
|
17080
17441
|
// src/lane/landing-maintainer-local.ts
|
|
17081
|
-
import { spawnSync as
|
|
17082
|
-
import
|
|
17442
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
17443
|
+
import path78 from "node:path";
|
|
17083
17444
|
function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
17084
|
-
const script =
|
|
17445
|
+
const script = path78.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
|
|
17085
17446
|
const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
|
|
17086
17447
|
if (!execute) {
|
|
17087
17448
|
return {
|
|
@@ -17092,7 +17453,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
|
17092
17453
|
stderr: ""
|
|
17093
17454
|
};
|
|
17094
17455
|
}
|
|
17095
|
-
const result =
|
|
17456
|
+
const result = spawnSync15("node", args, {
|
|
17096
17457
|
cwd: repoRoot,
|
|
17097
17458
|
encoding: "utf8",
|
|
17098
17459
|
timeout: 10 * 60 * 1e3
|
|
@@ -17107,7 +17468,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
|
17107
17468
|
}
|
|
17108
17469
|
function resolveLandingMaintainerRepoRoot(args) {
|
|
17109
17470
|
const explicit = args.repoPath ? String(args.repoPath).trim() : "";
|
|
17110
|
-
if (explicit) return
|
|
17471
|
+
if (explicit) return path78.resolve(explicit);
|
|
17111
17472
|
const resolved = resolveDefaultRepo();
|
|
17112
17473
|
return resolved?.repo ?? process.cwd();
|
|
17113
17474
|
}
|
|
@@ -17232,9 +17593,9 @@ function usage(code = 0) {
|
|
|
17232
17593
|
"Usage:",
|
|
17233
17594
|
" kynver login [--api-key KEY] [--api-base-url URL] (omit --api-key to authorize in the browser)",
|
|
17234
17595
|
" 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)",
|
|
17596
|
+
" 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
17597
|
" 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]",
|
|
17598
|
+
" 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
17599
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS] [--stall-ms MS] [--no-supervise]",
|
|
17239
17600
|
" kynver status --run RUN_ID [--blocked] [--running] [--task TASK_ID] [--worker WORKER] [--full] # top-level compact run status",
|
|
17240
17601
|
" kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
|
|
@@ -17298,8 +17659,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
17298
17659
|
if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
|
|
17299
17660
|
const args = parseArgs(rest);
|
|
17300
17661
|
const { runsDir, worktreesDir } = getPaths();
|
|
17301
|
-
|
|
17302
|
-
|
|
17662
|
+
mkdirSync15(runsDir, { recursive: true });
|
|
17663
|
+
mkdirSync15(worktreesDir, { recursive: true });
|
|
17303
17664
|
if (scope === "daemon") {
|
|
17304
17665
|
assertNativeDaemonAllowed();
|
|
17305
17666
|
}
|