@kynver-app/runtime 0.1.120 → 0.1.123
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/anthropic-credentials.d.ts +3 -1
- package/dist/chat/chat-claim-loop.d.ts +3 -1
- package/dist/cleanup-duplicate-worktrees.d.ts +3 -2
- package/dist/cleanup-git-probe.d.ts +5 -0
- package/dist/cleanup-guards.d.ts +4 -0
- package/dist/cleanup-retention-config.d.ts +2 -0
- package/dist/cleanup-run-liveness.d.ts +3 -6
- package/dist/cleanup-salvage-evidence.d.ts +33 -0
- package/dist/cleanup-types.d.ts +2 -0
- package/dist/cleanup-worker-harness-live.d.ts +9 -0
- package/dist/cleanup-worktree-index.d.ts +2 -0
- package/dist/cli.js +884 -514
- package/dist/cli.js.map +4 -4
- package/dist/config.d.ts +22 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +835 -463
- package/dist/index.js.map +4 -4
- package/dist/run-metadata-retention.d.ts +15 -0
- package/dist/server/cleanup.js +452 -308
- package/dist/server/cleanup.js.map +4 -4
- package/dist/server/default-repo.js.map +2 -2
- package/dist/server/monitor.js.map +2 -2
- package/dist/server/worker-policy.js.map +2 -2
- package/dist/stale-reconcile.d.ts +2 -0
- package/dist/start.d.ts +6 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -532,23 +532,23 @@ function isWslHost() {
|
|
|
532
532
|
function observeWslHostDisk(options = {}) {
|
|
533
533
|
const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
|
|
534
534
|
if (!wsl) return null;
|
|
535
|
-
const
|
|
535
|
+
const path81 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
|
|
536
536
|
const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
|
|
537
537
|
const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
|
|
538
538
|
const statfs = options.statfs ?? statfsSync;
|
|
539
539
|
let stats;
|
|
540
540
|
try {
|
|
541
|
-
stats = statfs(
|
|
541
|
+
stats = statfs(path81);
|
|
542
542
|
} catch (error) {
|
|
543
543
|
return {
|
|
544
544
|
ok: false,
|
|
545
|
-
path:
|
|
545
|
+
path: path81,
|
|
546
546
|
freeBytes: 0,
|
|
547
547
|
totalBytes: 0,
|
|
548
548
|
usedPercent: 100,
|
|
549
549
|
warnBelowBytes,
|
|
550
550
|
criticalBelowBytes,
|
|
551
|
-
reason: `Windows host disk probe failed at ${
|
|
551
|
+
reason: `Windows host disk probe failed at ${path81}: ${error.message}`,
|
|
552
552
|
probeError: error.message
|
|
553
553
|
};
|
|
554
554
|
}
|
|
@@ -562,11 +562,11 @@ function observeWslHostDisk(options = {}) {
|
|
|
562
562
|
let reason = null;
|
|
563
563
|
if (!ok) {
|
|
564
564
|
const tag = criticalFree ? "critical" : "warning";
|
|
565
|
-
reason = `Windows host disk ${
|
|
565
|
+
reason = `Windows host disk ${path81} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
|
|
566
566
|
}
|
|
567
567
|
return {
|
|
568
568
|
ok,
|
|
569
|
-
path:
|
|
569
|
+
path: path81,
|
|
570
570
|
freeBytes,
|
|
571
571
|
totalBytes,
|
|
572
572
|
usedPercent,
|
|
@@ -592,12 +592,12 @@ var init_wsl_host = __esm({
|
|
|
592
592
|
// src/disk-gate.ts
|
|
593
593
|
import { statfsSync as statfsSync2 } from "node:fs";
|
|
594
594
|
function observeRunnerDiskGate(input = {}) {
|
|
595
|
-
const
|
|
595
|
+
const path81 = input.diskPath?.trim() || "/";
|
|
596
596
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
597
597
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
598
598
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
599
599
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
600
|
-
const stats = statfsSync2(
|
|
600
|
+
const stats = statfsSync2(path81);
|
|
601
601
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
602
602
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
603
603
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -620,7 +620,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
620
620
|
}
|
|
621
621
|
return {
|
|
622
622
|
ok,
|
|
623
|
-
path:
|
|
623
|
+
path: path81,
|
|
624
624
|
freeBytes,
|
|
625
625
|
totalBytes,
|
|
626
626
|
usedPercent,
|
|
@@ -2635,23 +2635,29 @@ function resolveCallbackSecret(argsSecret, agentOsId) {
|
|
|
2635
2635
|
"requires --secret, KYNVER_RUNNER_TOKEN, a scoped runner token (`kynver runner credential`), ~/.kynver/credentials runnerToken, KYNVER_API_KEY with an API base URL to mint one, or (legacy) KYNVER_RUNTIME_SECRET / KYNVER_CRON_SECRET / OPENCLAW_CRON_SECRET"
|
|
2636
2636
|
);
|
|
2637
2637
|
}
|
|
2638
|
-
async function
|
|
2638
|
+
async function tryResolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
|
|
2639
2639
|
const configured = resolveConfiguredCallbackSecret(argsSecret, agentOsId);
|
|
2640
|
-
if (configured) return configured;
|
|
2640
|
+
if (configured) return { ok: true, secret: configured };
|
|
2641
2641
|
const apiKey = loadApiKey();
|
|
2642
2642
|
const baseUrl = resolveConfiguredBaseUrl(opts?.baseUrl);
|
|
2643
2643
|
if (apiKey && agentOsId && baseUrl) {
|
|
2644
2644
|
try {
|
|
2645
2645
|
const token = await fetchRunnerCredential(agentOsId, { baseUrl, apiKey });
|
|
2646
2646
|
saveRunnerToken(agentOsId, token);
|
|
2647
|
-
return token;
|
|
2647
|
+
return { ok: true, secret: token };
|
|
2648
2648
|
} catch (error) {
|
|
2649
|
-
|
|
2649
|
+
return { ok: false, reason: `runner credential mint failed: ${error.message}` };
|
|
2650
2650
|
}
|
|
2651
2651
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2652
|
+
return {
|
|
2653
|
+
ok: false,
|
|
2654
|
+
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"
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
async function resolveCallbackSecretWithMint(argsSecret, agentOsId, opts) {
|
|
2658
|
+
const result = await tryResolveCallbackSecretWithMint(argsSecret, agentOsId, opts);
|
|
2659
|
+
if (result.ok) return result.secret;
|
|
2660
|
+
failConfig(result.reason);
|
|
2655
2661
|
}
|
|
2656
2662
|
async function refreshRunnerToken(agentOsId, opts) {
|
|
2657
2663
|
const apiKey = loadApiKey();
|
|
@@ -2771,7 +2777,10 @@ async function runSetup(args) {
|
|
|
2771
2777
|
...existing,
|
|
2772
2778
|
...inferSetupFields(existing, args),
|
|
2773
2779
|
...workerConfig,
|
|
2774
|
-
workerProvider: typeof args.provider === "string" ? args.provider : existing.workerProvider || "cursor"
|
|
2780
|
+
workerProvider: typeof args.provider === "string" ? args.provider : existing.workerProvider || "cursor",
|
|
2781
|
+
// M5.4 chat-turn delegation: `--chat-oauth` persists consent to use the
|
|
2782
|
+
// Claude Code CLI's local OAuth token for delegated chat turns.
|
|
2783
|
+
...args.chatOauth === true ? { chatUseClaudeOauth: true } : {}
|
|
2775
2784
|
});
|
|
2776
2785
|
saveUserConfig(config);
|
|
2777
2786
|
const boxIdentity = resolveBoxIdentity(process.env, config);
|
|
@@ -5858,8 +5867,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
|
|
|
5858
5867
|
if (removed.length === 0) return false;
|
|
5859
5868
|
const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
|
|
5860
5869
|
return material.every((line) => {
|
|
5861
|
-
const
|
|
5862
|
-
return removedSet.has(
|
|
5870
|
+
const path81 = normalizeRelativePath(pathFromGitStatusLine(line));
|
|
5871
|
+
return removedSet.has(path81);
|
|
5863
5872
|
});
|
|
5864
5873
|
}
|
|
5865
5874
|
|
|
@@ -6709,6 +6718,9 @@ function buildBoard(run, workers, compact) {
|
|
|
6709
6718
|
}
|
|
6710
6719
|
return board;
|
|
6711
6720
|
}
|
|
6721
|
+
function isTerminalDoneBoardWorker(worker) {
|
|
6722
|
+
return worker.status === "done" && worker.attention === "done";
|
|
6723
|
+
}
|
|
6712
6724
|
function buildRunBoard(runId) {
|
|
6713
6725
|
const run = loadRun(runId);
|
|
6714
6726
|
const names = Object.keys(run.workers || {});
|
|
@@ -6729,7 +6741,7 @@ function buildRunBoard(runId) {
|
|
|
6729
6741
|
function buildCompactRunBoard(runId) {
|
|
6730
6742
|
const run = loadRun(runId);
|
|
6731
6743
|
const names = Object.keys(run.workers || {});
|
|
6732
|
-
const
|
|
6744
|
+
const allWorkers = names.map((name) => {
|
|
6733
6745
|
const worker = readJson(
|
|
6734
6746
|
path21.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
6735
6747
|
void 0
|
|
@@ -6740,7 +6752,17 @@ function buildCompactRunBoard(runId) {
|
|
|
6740
6752
|
if (isMetadataTerminalDone(worker)) return buildCompactDoneEntry(name, worker);
|
|
6741
6753
|
return buildWorkerBoardEntry({ run, workerName: name, worker });
|
|
6742
6754
|
});
|
|
6755
|
+
const workers = allWorkers.filter((worker) => !isTerminalDoneBoardWorker(worker));
|
|
6743
6756
|
const board = buildBoard(run, workers, true);
|
|
6757
|
+
board.status = deriveRunStatus(run.status, allWorkers);
|
|
6758
|
+
board.summary = {
|
|
6759
|
+
...board.summary ?? {},
|
|
6760
|
+
totalWorkerCount: allWorkers.length,
|
|
6761
|
+
shownWorkerCount: workers.length,
|
|
6762
|
+
omittedTerminalDoneWorkerCount: allWorkers.length - workers.length,
|
|
6763
|
+
allStatusCounts: countBy(allWorkers, "status"),
|
|
6764
|
+
allAttentionCounts: countBy(allWorkers, "attention")
|
|
6765
|
+
};
|
|
6744
6766
|
writeJson(path21.join(runDirectory(run.id), "last-board-compact.json"), board);
|
|
6745
6767
|
return board;
|
|
6746
6768
|
}
|
|
@@ -9355,7 +9377,7 @@ function applyProductionDatabaseToProcess(options = {}) {
|
|
|
9355
9377
|
// src/worktree.ts
|
|
9356
9378
|
init_git();
|
|
9357
9379
|
init_run_store();
|
|
9358
|
-
import { existsSync as existsSync29, mkdirSync as
|
|
9380
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync7 } from "node:fs";
|
|
9359
9381
|
import path38 from "node:path";
|
|
9360
9382
|
|
|
9361
9383
|
// src/run-list.ts
|
|
@@ -9438,7 +9460,7 @@ init_util();
|
|
|
9438
9460
|
// src/worker-metadata-reconcile.ts
|
|
9439
9461
|
init_heartbeat();
|
|
9440
9462
|
init_stream();
|
|
9441
|
-
import { existsSync as existsSync27, lstatSync, readdirSync as readdirSync7, readlinkSync, renameSync as
|
|
9463
|
+
import { existsSync as existsSync27, lstatSync, readdirSync as readdirSync7, readlinkSync, renameSync as renameSync3, rmSync } from "node:fs";
|
|
9442
9464
|
import path34 from "node:path";
|
|
9443
9465
|
|
|
9444
9466
|
// src/worker-metadata-paths.ts
|
|
@@ -9497,7 +9519,7 @@ function resolveWorkerJsonPath(input) {
|
|
|
9497
9519
|
}
|
|
9498
9520
|
|
|
9499
9521
|
// src/run-metadata-retention.ts
|
|
9500
|
-
import { existsSync as existsSync26, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
|
|
9522
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync6, readdirSync as readdirSync6, renameSync as renameSync2, statSync as statSync5 } from "node:fs";
|
|
9501
9523
|
import path33 from "node:path";
|
|
9502
9524
|
|
|
9503
9525
|
// src/default-repo.ts
|
|
@@ -9578,6 +9600,7 @@ init_paths();
|
|
|
9578
9600
|
init_run_store();
|
|
9579
9601
|
init_util();
|
|
9580
9602
|
var RUN_METADATA_ACTIVE_SIGNAL_MS = 15 * 60 * 1e3;
|
|
9603
|
+
var TERMINAL_WORKER_ARCHIVE_AGE_MS = 60 * 60 * 1e3;
|
|
9581
9604
|
function isHarnessRunMetadataPath(targetPath, harnessRoot) {
|
|
9582
9605
|
const resolved = path33.resolve(targetPath);
|
|
9583
9606
|
const runsDir = path33.resolve(harnessRunsDir(harnessRoot));
|
|
@@ -9601,6 +9624,14 @@ function listWorkerNamesOnDisk(runDir2) {
|
|
|
9601
9624
|
return [];
|
|
9602
9625
|
}
|
|
9603
9626
|
}
|
|
9627
|
+
function workerArchiveDir(runDir2) {
|
|
9628
|
+
return path33.join(runDir2, "archived-workers");
|
|
9629
|
+
}
|
|
9630
|
+
function uniqueArchivePath(runDir2, workerName) {
|
|
9631
|
+
const base = path33.join(workerArchiveDir(runDir2), safeSlug(workerName));
|
|
9632
|
+
if (!existsSync26(base)) return base;
|
|
9633
|
+
return `${base}-${Date.now()}`;
|
|
9634
|
+
}
|
|
9604
9635
|
function pathRecentlyTouched(target, now, windowMs) {
|
|
9605
9636
|
if (!existsSync26(target)) return false;
|
|
9606
9637
|
try {
|
|
@@ -9627,6 +9658,41 @@ function workerDirHasActiveRetentionSignals(workerDir, now = Date.now(), windowM
|
|
|
9627
9658
|
if (pathRecentlyTouched(artifacts.heartbeatPath, now, windowMs)) return true;
|
|
9628
9659
|
return false;
|
|
9629
9660
|
}
|
|
9661
|
+
function workerTerminalArchiveAgeMs(workerDir, worker, now) {
|
|
9662
|
+
const completionReportedAt = typeof worker.completionReportedAt === "string" ? Date.parse(worker.completionReportedAt) : NaN;
|
|
9663
|
+
if (Number.isFinite(completionReportedAt)) return now - completionReportedAt;
|
|
9664
|
+
const completionSnapshotAt = (() => {
|
|
9665
|
+
const raw = worker.completionResponse;
|
|
9666
|
+
if (!raw || typeof raw !== "object") return NaN;
|
|
9667
|
+
const task = raw.task;
|
|
9668
|
+
if (!task || typeof task !== "object") return NaN;
|
|
9669
|
+
const updatedAt = task.updatedAt;
|
|
9670
|
+
return typeof updatedAt === "string" ? Date.parse(updatedAt) : NaN;
|
|
9671
|
+
})();
|
|
9672
|
+
if (Number.isFinite(completionSnapshotAt)) return now - completionSnapshotAt;
|
|
9673
|
+
try {
|
|
9674
|
+
return now - statSync5(path33.join(workerDir, "worker.json")).mtimeMs;
|
|
9675
|
+
} catch {
|
|
9676
|
+
return 0;
|
|
9677
|
+
}
|
|
9678
|
+
}
|
|
9679
|
+
function isArchiveEligibleTerminalWorker(workerDir, worker, now, archiveAgeMs) {
|
|
9680
|
+
if (workerDirHasActiveRetentionSignals(workerDir, now)) {
|
|
9681
|
+
return { eligible: false, reason: "worker has active/recent retention signals" };
|
|
9682
|
+
}
|
|
9683
|
+
const status = typeof worker.status === "string" ? worker.status : "";
|
|
9684
|
+
const completionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim().length > 0;
|
|
9685
|
+
if (completionBlocker) return { eligible: false, reason: "worker has completion blocker" };
|
|
9686
|
+
const completionAcknowledged = typeof worker.completionReportedAt === "string" || worker.completionOutcome === "acknowledged";
|
|
9687
|
+
if (status !== "done" && !completionAcknowledged) {
|
|
9688
|
+
return { eligible: false, reason: `worker status is ${status || "unknown"}` };
|
|
9689
|
+
}
|
|
9690
|
+
const age = workerTerminalArchiveAgeMs(workerDir, worker, now);
|
|
9691
|
+
if (!Number.isFinite(age) || age < archiveAgeMs) {
|
|
9692
|
+
return { eligible: false, reason: "terminal worker is still within archive grace window" };
|
|
9693
|
+
}
|
|
9694
|
+
return { eligible: true, reason: "terminal worker metadata archived" };
|
|
9695
|
+
}
|
|
9630
9696
|
function runDirHasActiveRetentionSignals(runDir2, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
|
|
9631
9697
|
for (const name of listWorkerNamesOnDisk(runDir2)) {
|
|
9632
9698
|
if (workerDirHasActiveRetentionSignals(path33.join(runDir2, "workers", name), now, windowMs)) {
|
|
@@ -9704,6 +9770,59 @@ function repairMissingRunMetadata(harnessRootInput) {
|
|
|
9704
9770
|
}
|
|
9705
9771
|
return { runs: outcomes };
|
|
9706
9772
|
}
|
|
9773
|
+
function archiveTerminalWorkerMetadata(harnessRootInput, options = {}) {
|
|
9774
|
+
const harnessRoot = normalizeHarnessRoot(harnessRootInput ?? resolveHarnessRoot());
|
|
9775
|
+
const runsDir = harnessRunsDir(harnessRoot);
|
|
9776
|
+
const now = options.now ?? Date.now();
|
|
9777
|
+
const archiveAgeMs = options.archiveAgeMs ?? TERMINAL_WORKER_ARCHIVE_AGE_MS;
|
|
9778
|
+
const outcomes = [];
|
|
9779
|
+
repairMissingRunMetadata(harnessRoot);
|
|
9780
|
+
for (const runId of listRunDirIds(runsDir)) {
|
|
9781
|
+
const runDir2 = path33.join(runsDir, runId);
|
|
9782
|
+
const runJsonPath = path33.join(runDir2, "run.json");
|
|
9783
|
+
const run = readJson(runJsonPath, void 0);
|
|
9784
|
+
if (!run?.id) continue;
|
|
9785
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(run.workers || {}), ...listWorkerNamesOnDisk(runDir2)]);
|
|
9786
|
+
let changed = false;
|
|
9787
|
+
for (const name of names) {
|
|
9788
|
+
const workerDir = path33.join(runDir2, "workers", safeSlug(name));
|
|
9789
|
+
const workerJsonPath = path33.join(workerDir, "worker.json");
|
|
9790
|
+
const worker = readJson(workerJsonPath, void 0);
|
|
9791
|
+
if (!worker) {
|
|
9792
|
+
const archivedWorkerDir = path33.join(workerArchiveDir(runDir2), safeSlug(name));
|
|
9793
|
+
if (existsSync26(archivedWorkerDir) && (run.workers[safeSlug(name)] || run.workers[name])) {
|
|
9794
|
+
delete run.workers[safeSlug(name)];
|
|
9795
|
+
delete run.workers[name];
|
|
9796
|
+
changed = true;
|
|
9797
|
+
outcomes.push({
|
|
9798
|
+
runId: run.id,
|
|
9799
|
+
worker: name,
|
|
9800
|
+
action: "archived",
|
|
9801
|
+
reason: "removed stale live index entry for archived worker metadata",
|
|
9802
|
+
archivePath: archivedWorkerDir
|
|
9803
|
+
});
|
|
9804
|
+
continue;
|
|
9805
|
+
}
|
|
9806
|
+
outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "worker.json missing" });
|
|
9807
|
+
continue;
|
|
9808
|
+
}
|
|
9809
|
+
const eligibility = isArchiveEligibleTerminalWorker(workerDir, worker, now, archiveAgeMs);
|
|
9810
|
+
if (!eligibility.eligible) {
|
|
9811
|
+
outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: eligibility.reason });
|
|
9812
|
+
continue;
|
|
9813
|
+
}
|
|
9814
|
+
const archivePath = uniqueArchivePath(runDir2, name);
|
|
9815
|
+
mkdirSync6(path33.dirname(archivePath), { recursive: true });
|
|
9816
|
+
renameSync2(workerDir, archivePath);
|
|
9817
|
+
delete run.workers[safeSlug(name)];
|
|
9818
|
+
delete run.workers[name];
|
|
9819
|
+
changed = true;
|
|
9820
|
+
outcomes.push({ runId: run.id, worker: name, action: "archived", reason: eligibility.reason, archivePath });
|
|
9821
|
+
}
|
|
9822
|
+
if (changed) saveRun(run);
|
|
9823
|
+
}
|
|
9824
|
+
return { workers: outcomes };
|
|
9825
|
+
}
|
|
9707
9826
|
function collectFilesystemLiveRunKeys(harnessRoot, now = Date.now()) {
|
|
9708
9827
|
const keys = /* @__PURE__ */ new Set();
|
|
9709
9828
|
const runsDir = harnessRunsDir(harnessRoot);
|
|
@@ -9733,9 +9852,9 @@ function materializeSymlinkedRunDir(harnessRoot, runId) {
|
|
|
9733
9852
|
return null;
|
|
9734
9853
|
}
|
|
9735
9854
|
const staging = `${canonical}.materialize-${Date.now()}`;
|
|
9736
|
-
|
|
9855
|
+
renameSync3(linkedTarget, staging);
|
|
9737
9856
|
rmSync(canonical);
|
|
9738
|
-
|
|
9857
|
+
renameSync3(staging, canonical);
|
|
9739
9858
|
return linkedTarget;
|
|
9740
9859
|
}
|
|
9741
9860
|
function relocateArtifacts(input) {
|
|
@@ -9744,7 +9863,7 @@ function relocateArtifacts(input) {
|
|
|
9744
9863
|
const from = path34.join(input.fromDir, fileName);
|
|
9745
9864
|
const to = path34.join(input.toDir, fileName);
|
|
9746
9865
|
if (!existsSync27(from) || existsSync27(to)) continue;
|
|
9747
|
-
|
|
9866
|
+
renameSync3(from, to);
|
|
9748
9867
|
moved.push(fileName);
|
|
9749
9868
|
}
|
|
9750
9869
|
return moved;
|
|
@@ -10225,7 +10344,14 @@ function reconcileStaleWorkers() {
|
|
|
10225
10344
|
const metadataReconcile = reconcileWorkerMetadata();
|
|
10226
10345
|
if (staleReconcileDisabled()) {
|
|
10227
10346
|
const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
|
|
10228
|
-
|
|
10347
|
+
const terminalWorkerArchive2 = archiveTerminalWorkerMetadata();
|
|
10348
|
+
return {
|
|
10349
|
+
workers: [],
|
|
10350
|
+
finalizedRuns: finalizeStaleRuns(),
|
|
10351
|
+
metadataReconcile,
|
|
10352
|
+
localPrAttentionReconcile: localPrAttentionReconcile2,
|
|
10353
|
+
terminalWorkerArchive: terminalWorkerArchive2
|
|
10354
|
+
};
|
|
10229
10355
|
}
|
|
10230
10356
|
const outcomes = [];
|
|
10231
10357
|
const now = Date.now();
|
|
@@ -10304,7 +10430,8 @@ function reconcileStaleWorkers() {
|
|
|
10304
10430
|
}
|
|
10305
10431
|
}
|
|
10306
10432
|
const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
|
|
10307
|
-
|
|
10433
|
+
const terminalWorkerArchive = archiveTerminalWorkerMetadata();
|
|
10434
|
+
return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile, terminalWorkerArchive };
|
|
10308
10435
|
}
|
|
10309
10436
|
function reconcileRunsCli() {
|
|
10310
10437
|
const result = reconcileStaleWorkers();
|
|
@@ -10323,6 +10450,10 @@ function reconcileRunsCli() {
|
|
|
10323
10450
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
10324
10451
|
return acc;
|
|
10325
10452
|
}, {});
|
|
10453
|
+
const archiveTotals = result.terminalWorkerArchive.workers.reduce((acc, row) => {
|
|
10454
|
+
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
10455
|
+
return acc;
|
|
10456
|
+
}, {});
|
|
10326
10457
|
console.log(
|
|
10327
10458
|
JSON.stringify(
|
|
10328
10459
|
{
|
|
@@ -10340,11 +10471,16 @@ function reconcileRunsCli() {
|
|
|
10340
10471
|
totals: localPrAttentionTotals,
|
|
10341
10472
|
total: result.localPrAttentionReconcile.workers.length
|
|
10342
10473
|
},
|
|
10474
|
+
terminalWorkerArchive: {
|
|
10475
|
+
totals: archiveTotals,
|
|
10476
|
+
total: result.terminalWorkerArchive.workers.length
|
|
10477
|
+
},
|
|
10343
10478
|
finalizedRuns: result.finalizedRuns.length,
|
|
10344
10479
|
details: {
|
|
10345
10480
|
workers: result.workers,
|
|
10346
10481
|
metadataReconcile: result.metadataReconcile.workers,
|
|
10347
10482
|
localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
|
|
10483
|
+
terminalWorkerArchive: result.terminalWorkerArchive.workers,
|
|
10348
10484
|
runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
|
|
10349
10485
|
finalizedRuns: result.finalizedRuns
|
|
10350
10486
|
}
|
|
@@ -10488,7 +10624,7 @@ function createRun(args) {
|
|
|
10488
10624
|
const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
|
|
10489
10625
|
const dir = runDirectory(id);
|
|
10490
10626
|
if (existsSync29(dir)) failExists(`run already exists: ${id}`);
|
|
10491
|
-
|
|
10627
|
+
mkdirSync7(dir, { recursive: true });
|
|
10492
10628
|
const base = String(args.base || "origin/main");
|
|
10493
10629
|
const baseCommit = git(repo, ["rev-parse", base]).trim();
|
|
10494
10630
|
const run = {
|
|
@@ -10742,11 +10878,11 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
10742
10878
|
|
|
10743
10879
|
// src/cleanup.ts
|
|
10744
10880
|
init_paths();
|
|
10745
|
-
import
|
|
10881
|
+
import path55 from "node:path";
|
|
10746
10882
|
|
|
10747
10883
|
// src/cleanup-guards.ts
|
|
10748
10884
|
init_landing_gate();
|
|
10749
|
-
import
|
|
10885
|
+
import path43 from "node:path";
|
|
10750
10886
|
|
|
10751
10887
|
// src/cleanup-index-status.ts
|
|
10752
10888
|
init_git();
|
|
@@ -10889,6 +11025,44 @@ init_status();
|
|
|
10889
11025
|
// src/cleanup-run-liveness.ts
|
|
10890
11026
|
init_status();
|
|
10891
11027
|
|
|
11028
|
+
// src/cleanup-worker-harness-live.ts
|
|
11029
|
+
init_heartbeat();
|
|
11030
|
+
init_util();
|
|
11031
|
+
function heartbeatContentIsFresh(worker, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
|
|
11032
|
+
const heartbeat = parseHeartbeat(worker.heartbeatPath);
|
|
11033
|
+
if (!heartbeat.lastHeartbeatAt) return false;
|
|
11034
|
+
const age = now - Date.parse(heartbeat.lastHeartbeatAt);
|
|
11035
|
+
return Number.isFinite(age) && age >= 0 && age < windowMs;
|
|
11036
|
+
}
|
|
11037
|
+
function isHarnessWorkerHarnessLive(worker, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
|
|
11038
|
+
if (isPidAlive(worker.pid)) return true;
|
|
11039
|
+
return heartbeatContentIsFresh(worker, now, windowMs);
|
|
11040
|
+
}
|
|
11041
|
+
|
|
11042
|
+
// src/cleanup-run-liveness.ts
|
|
11043
|
+
function deriveRunTerminal(indexed, ctx) {
|
|
11044
|
+
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
11045
|
+
return deriveTerminalRunStatus(indexed.run);
|
|
11046
|
+
}
|
|
11047
|
+
function isWorkerProcessLive(indexed, now = Date.now()) {
|
|
11048
|
+
return isHarnessWorkerHarnessLive(indexed.worker, now);
|
|
11049
|
+
}
|
|
11050
|
+
function isRunStaleActive(indexed, ctx) {
|
|
11051
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
11052
|
+
return deriveRunTerminal(indexed, ctx) !== null;
|
|
11053
|
+
}
|
|
11054
|
+
function runBlocksWorktreeRemoval(indexed, ctx, now = Date.now()) {
|
|
11055
|
+
if (isWorkerProcessLive(indexed, now)) return true;
|
|
11056
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
11057
|
+
if (isRunStaleActive(indexed, ctx)) return false;
|
|
11058
|
+
const status = indexedWorktreeStatus(indexed);
|
|
11059
|
+
if (!isFinishedWorkerStatus(status)) {
|
|
11060
|
+
if (!isWorkerProcessLive(indexed, now)) return false;
|
|
11061
|
+
return true;
|
|
11062
|
+
}
|
|
11063
|
+
return false;
|
|
11064
|
+
}
|
|
11065
|
+
|
|
10892
11066
|
// src/cleanup-completion-blocker.ts
|
|
10893
11067
|
init_landing_gate();
|
|
10894
11068
|
init_status();
|
|
@@ -10910,51 +11084,80 @@ function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
|
10910
11084
|
return false;
|
|
10911
11085
|
}
|
|
10912
11086
|
|
|
10913
|
-
// src/cleanup-
|
|
11087
|
+
// src/cleanup-salvage-evidence.ts
|
|
11088
|
+
init_git();
|
|
10914
11089
|
init_util();
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
"
|
|
10919
|
-
"failed",
|
|
10920
|
-
"abandoned"
|
|
10921
|
-
]);
|
|
10922
|
-
function deriveRunTerminal(indexed, ctx) {
|
|
10923
|
-
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
10924
|
-
return deriveTerminalRunStatus(indexed.run);
|
|
10925
|
-
}
|
|
10926
|
-
function isWorkerProcessLive(indexed) {
|
|
10927
|
-
if (isPidAlive(indexed.worker.pid)) return true;
|
|
10928
|
-
if (typeof indexed.worker.completionReportedAt === "string" && indexed.worker.completionReportedAt.trim().length > 0) {
|
|
10929
|
-
return false;
|
|
10930
|
-
}
|
|
10931
|
-
const workerStatus2 = indexed.worker.status;
|
|
10932
|
-
if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2)) return false;
|
|
10933
|
-
if (!indexed.worker.pid) {
|
|
10934
|
-
if (workerStatus2 !== "running") return false;
|
|
10935
|
-
return indexedWorktreeStatus(indexed).alive;
|
|
10936
|
-
}
|
|
10937
|
-
return false;
|
|
11090
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync8, writeFileSync as writeFileSync4 } from "node:fs";
|
|
11091
|
+
import path42 from "node:path";
|
|
11092
|
+
function salvageEvidenceDir(harnessRoot, runId, workerName) {
|
|
11093
|
+
return path42.join(harnessRoot, "salvage", safeSlug(runId), safeSlug(workerName));
|
|
10938
11094
|
}
|
|
10939
|
-
function
|
|
10940
|
-
|
|
10941
|
-
return deriveRunTerminal(indexed, ctx) !== null;
|
|
11095
|
+
function hasSalvageEvidence(input) {
|
|
11096
|
+
return existsSync32(path42.join(salvageEvidenceDir(input.harnessRoot, input.runId, input.workerName), "evidence.json"));
|
|
10942
11097
|
}
|
|
10943
|
-
function
|
|
10944
|
-
|
|
10945
|
-
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
|
|
11098
|
+
function writeWorktreeSalvageEvidence(input) {
|
|
11099
|
+
const dir = salvageEvidenceDir(input.harnessRoot, input.indexed.runId, input.indexed.workerName);
|
|
11100
|
+
mkdirSync8(dir, { recursive: true });
|
|
11101
|
+
const patchPath = path42.join(dir, "salvage.patch");
|
|
11102
|
+
let wrotePatch = false;
|
|
11103
|
+
if (existsSync32(input.indexed.worktreePath)) {
|
|
11104
|
+
const diff = gitCapture(input.indexed.worktreePath, ["diff", "HEAD"]);
|
|
11105
|
+
const staged = gitCapture(input.indexed.worktreePath, ["diff", "--cached"]);
|
|
11106
|
+
const patch = [diff.stdout, staged.stdout].filter((chunk) => chunk.trim()).join("\n");
|
|
11107
|
+
if (patch.trim()) {
|
|
11108
|
+
writeFileSync4(patchPath, patch.endsWith("\n") ? patch : `${patch}
|
|
11109
|
+
`);
|
|
11110
|
+
wrotePatch = true;
|
|
11111
|
+
}
|
|
11112
|
+
}
|
|
11113
|
+
const record3 = {
|
|
11114
|
+
capturedAt: new Date(input.now ?? Date.now()).toISOString(),
|
|
11115
|
+
skipReason: input.skipReason,
|
|
11116
|
+
runId: input.indexed.runId,
|
|
11117
|
+
workerName: input.indexed.workerName,
|
|
11118
|
+
worktreePath: input.indexed.worktreePath,
|
|
11119
|
+
taskId: input.indexed.worker.taskId,
|
|
11120
|
+
agentOsId: input.indexed.worker.agentOsId,
|
|
11121
|
+
branch: input.indexed.worker.branch,
|
|
11122
|
+
completionBlocker: input.indexed.worker.completionBlocker,
|
|
11123
|
+
finalResult: input.status.finalResult,
|
|
11124
|
+
changedFiles: input.status.changedFiles,
|
|
11125
|
+
gitAncestry: input.status.gitAncestry,
|
|
11126
|
+
prUrl: input.status.prUrl ?? null,
|
|
11127
|
+
...wrotePatch ? { patchPath } : {}
|
|
11128
|
+
};
|
|
11129
|
+
writeFileSync4(path42.join(dir, "evidence.json"), `${JSON.stringify(record3, null, 2)}
|
|
11130
|
+
`);
|
|
11131
|
+
return record3;
|
|
10955
11132
|
}
|
|
10956
11133
|
|
|
10957
11134
|
// src/cleanup-guards.ts
|
|
11135
|
+
var SALVAGE_ELIGIBLE_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
11136
|
+
"completion_blocked",
|
|
11137
|
+
"dirty_worktree",
|
|
11138
|
+
"pr_or_unmerged_commits",
|
|
11139
|
+
"landing_blocked"
|
|
11140
|
+
]);
|
|
11141
|
+
function maybeSalvageBlockedWorktree(input) {
|
|
11142
|
+
if (!SALVAGE_ELIGIBLE_SKIP_REASONS.has(input.skipReason)) return false;
|
|
11143
|
+
if (isWorkerProcessLive(input.indexed, input.now)) return false;
|
|
11144
|
+
if (hasSalvageEvidence({
|
|
11145
|
+
harnessRoot: input.harnessRoot,
|
|
11146
|
+
runId: input.indexed.runId,
|
|
11147
|
+
workerName: input.indexed.workerName
|
|
11148
|
+
})) {
|
|
11149
|
+
return true;
|
|
11150
|
+
}
|
|
11151
|
+
if (!input.writeSalvageEvidence) return false;
|
|
11152
|
+
writeWorktreeSalvageEvidence({
|
|
11153
|
+
harnessRoot: input.harnessRoot,
|
|
11154
|
+
indexed: input.indexed,
|
|
11155
|
+
skipReason: input.skipReason,
|
|
11156
|
+
status: input.status,
|
|
11157
|
+
now: input.now
|
|
11158
|
+
});
|
|
11159
|
+
return true;
|
|
11160
|
+
}
|
|
10958
11161
|
function effectiveWorktreeAgeMs(input) {
|
|
10959
11162
|
const { indexed, includeOrphans, worktreesAgeMs, terminalWorktreesAgeMs } = input;
|
|
10960
11163
|
if (!indexed) return includeOrphans ? terminalWorktreesAgeMs : worktreesAgeMs;
|
|
@@ -10969,8 +11172,18 @@ function effectiveWorktreeAgeMs(input) {
|
|
|
10969
11172
|
}
|
|
10970
11173
|
return worktreesAgeMs;
|
|
10971
11174
|
}
|
|
11175
|
+
function resolveHarnessRoot2(input, indexed) {
|
|
11176
|
+
if (input.harnessRoot?.trim()) return path43.resolve(input.harnessRoot);
|
|
11177
|
+
const workerDir = indexed.worker.workerDir;
|
|
11178
|
+
if (!workerDir) return null;
|
|
11179
|
+
const runsMarker = `${path43.sep}runs${path43.sep}`;
|
|
11180
|
+
const idx = workerDir.indexOf(runsMarker);
|
|
11181
|
+
if (idx < 0) return null;
|
|
11182
|
+
return workerDir.slice(0, idx + runsMarker.length - 1);
|
|
11183
|
+
}
|
|
10972
11184
|
function skipWorktreeRemoval(input) {
|
|
10973
11185
|
const { indexed, includeOrphans, worktreesAgeMs, ageMs, orphanSafety, worktreeRemovalGuard } = input;
|
|
11186
|
+
const now = input.now ?? Date.now();
|
|
10974
11187
|
if (!indexed) {
|
|
10975
11188
|
if (!includeOrphans) return "orphan_without_flag";
|
|
10976
11189
|
return orphanSafety ?? null;
|
|
@@ -10978,25 +11191,55 @@ function skipWorktreeRemoval(input) {
|
|
|
10978
11191
|
const ageThresholdMs = effectiveWorktreeAgeMs(input);
|
|
10979
11192
|
if (worktreesAgeMs <= 0 && !includeOrphans && ageThresholdMs <= 0) return "worktrees_disabled";
|
|
10980
11193
|
if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
|
|
10981
|
-
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
11194
|
+
if (isWorkerProcessLive(indexed, now)) return "active_worker";
|
|
11195
|
+
const status = resolveWorktreeGuardStatus(indexed, input.liveness);
|
|
11196
|
+
const salvageHarnessRoot = resolveHarnessRoot2(input, indexed);
|
|
11197
|
+
const salvageOrBlock = (skipReason) => {
|
|
11198
|
+
if (salvageHarnessRoot && maybeSalvageBlockedWorktree({
|
|
11199
|
+
indexed,
|
|
11200
|
+
harnessRoot: salvageHarnessRoot,
|
|
11201
|
+
skipReason,
|
|
11202
|
+
status,
|
|
11203
|
+
now,
|
|
11204
|
+
writeSalvageEvidence: input.writeSalvageEvidence === true
|
|
11205
|
+
})) {
|
|
11206
|
+
return null;
|
|
11207
|
+
}
|
|
11208
|
+
return skipReason;
|
|
11209
|
+
};
|
|
11210
|
+
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) {
|
|
11211
|
+
const blocked = salvageOrBlock("completion_blocked");
|
|
11212
|
+
if (blocked) return blocked;
|
|
11213
|
+
}
|
|
10982
11214
|
if (indexedWorktreeHasMaterialChanges(indexed, input.liveness?.gitStatusCache)) {
|
|
10983
|
-
|
|
11215
|
+
const blocked = salvageOrBlock("dirty_worktree");
|
|
11216
|
+
if (blocked) return blocked;
|
|
10984
11217
|
}
|
|
10985
11218
|
const ahead = input.liveness?.gitRevCache?.countAheadOfMain(input.worktreePath);
|
|
10986
|
-
if (ahead !== null && ahead !== void 0 && ahead > 0)
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
11219
|
+
if (ahead !== null && ahead !== void 0 && ahead > 0) {
|
|
11220
|
+
const blocked = salvageOrBlock("pr_or_unmerged_commits");
|
|
11221
|
+
if (blocked) return blocked;
|
|
11222
|
+
}
|
|
11223
|
+
if (runBlocksWorktreeRemoval(indexed, input.liveness, now)) return "run_still_active";
|
|
10990
11224
|
if (!isFinishedWorkerStatus(status)) return "run_still_active";
|
|
10991
|
-
if (isPrOrUnmergedWork(status))
|
|
10992
|
-
|
|
11225
|
+
if (isPrOrUnmergedWork(status)) {
|
|
11226
|
+
const blocked = salvageOrBlock("pr_or_unmerged_commits");
|
|
11227
|
+
if (blocked) return blocked;
|
|
11228
|
+
}
|
|
11229
|
+
if (materialWorktreeChanges2(status.changedFiles).length > 0) {
|
|
11230
|
+
const blocked = salvageOrBlock("dirty_worktree");
|
|
11231
|
+
if (blocked) return blocked;
|
|
11232
|
+
}
|
|
10993
11233
|
const landing = assessWorkerLanding({
|
|
10994
11234
|
finalResult: status.finalResult,
|
|
10995
11235
|
changedFiles: status.changedFiles,
|
|
10996
11236
|
gitAncestry: status.gitAncestry,
|
|
10997
11237
|
prUrl: prUrlFromFinalResult(status.finalResult)
|
|
10998
11238
|
});
|
|
10999
|
-
if (landing.blocked)
|
|
11239
|
+
if (landing.blocked) {
|
|
11240
|
+
const blocked = salvageOrBlock("landing_blocked");
|
|
11241
|
+
if (blocked) return blocked;
|
|
11242
|
+
}
|
|
11000
11243
|
if (worktreeRemovalGuard && input.worktreePath) {
|
|
11001
11244
|
const overlay = worktreeRemovalGuard({
|
|
11002
11245
|
worktreePath: input.worktreePath,
|
|
@@ -11013,7 +11256,7 @@ function skipWorktreeRemoval(input) {
|
|
|
11013
11256
|
function skipDependencyCacheRemoval(input) {
|
|
11014
11257
|
const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
|
|
11015
11258
|
if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
11016
|
-
if (activeWorktreePaths.has(
|
|
11259
|
+
if (activeWorktreePaths.has(path43.resolve(worktreePath))) return "active_worker";
|
|
11017
11260
|
if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
|
|
11018
11261
|
if (indexed && indexedWorktreeHasMaterialChanges(indexed, input.gitStatusCache)) {
|
|
11019
11262
|
return "dirty_worktree";
|
|
@@ -11042,11 +11285,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
|
11042
11285
|
function collectPreservedLivePaths(actions, skips) {
|
|
11043
11286
|
const out = [];
|
|
11044
11287
|
const seen = /* @__PURE__ */ new Set();
|
|
11045
|
-
const push = (
|
|
11046
|
-
const key = `${
|
|
11288
|
+
const push = (path81, reason, detail) => {
|
|
11289
|
+
const key = `${path81}\0${reason}`;
|
|
11047
11290
|
if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
|
|
11048
11291
|
seen.add(key);
|
|
11049
|
-
out.push({ path:
|
|
11292
|
+
out.push({ path: path81, reason, ...detail ? { detail } : {} });
|
|
11050
11293
|
};
|
|
11051
11294
|
for (const skip2 of skips) {
|
|
11052
11295
|
if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
|
|
@@ -11061,31 +11304,16 @@ function collectPreservedLivePaths(actions, skips) {
|
|
|
11061
11304
|
}
|
|
11062
11305
|
|
|
11063
11306
|
// src/cleanup-run-directory.ts
|
|
11064
|
-
import { existsSync as existsSync33, readdirSync as
|
|
11065
|
-
import
|
|
11307
|
+
import { existsSync as existsSync33, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
|
|
11308
|
+
import path45 from "node:path";
|
|
11066
11309
|
|
|
11067
11310
|
// src/cleanup-active-worktrees.ts
|
|
11068
11311
|
init_run_store();
|
|
11069
11312
|
init_paths();
|
|
11070
|
-
import
|
|
11071
|
-
import path43 from "node:path";
|
|
11313
|
+
import path44 from "node:path";
|
|
11072
11314
|
init_util();
|
|
11073
|
-
function workerHasRecentHarnessActivity(worker, now) {
|
|
11074
|
-
const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
|
|
11075
|
-
for (const target of paths) {
|
|
11076
|
-
if (!existsSync32(target)) continue;
|
|
11077
|
-
try {
|
|
11078
|
-
const age = now - statSync8(target).mtimeMs;
|
|
11079
|
-
if (Number.isFinite(age) && age >= 0 && age < RUN_METADATA_ACTIVE_SIGNAL_MS) return true;
|
|
11080
|
-
} catch {
|
|
11081
|
-
}
|
|
11082
|
-
}
|
|
11083
|
-
return false;
|
|
11084
|
-
}
|
|
11085
11315
|
function isActiveHarnessWorker2(worker, now) {
|
|
11086
|
-
|
|
11087
|
-
if (worker.status === "running" && workerHasRecentHarnessActivity(worker, now)) return true;
|
|
11088
|
-
return false;
|
|
11316
|
+
return isHarnessWorkerHarnessLive(worker, now);
|
|
11089
11317
|
}
|
|
11090
11318
|
function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
|
|
11091
11319
|
const activeWorktreePaths = /* @__PURE__ */ new Set();
|
|
@@ -11095,11 +11323,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
|
|
|
11095
11323
|
let runHasLive = false;
|
|
11096
11324
|
for (const name of Object.keys(run.workers || {})) {
|
|
11097
11325
|
const worker = readJson(
|
|
11098
|
-
|
|
11326
|
+
path44.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
|
|
11099
11327
|
void 0
|
|
11100
11328
|
);
|
|
11101
11329
|
if (!worker?.worktreePath) continue;
|
|
11102
|
-
const worktreePath =
|
|
11330
|
+
const worktreePath = path44.resolve(worker.worktreePath);
|
|
11103
11331
|
if (!isActiveHarnessWorker2(worker, now)) continue;
|
|
11104
11332
|
runHasLive = true;
|
|
11105
11333
|
activeWorktreePaths.add(worktreePath);
|
|
@@ -11121,20 +11349,20 @@ function isWorktreeOnLiveRun(worktreePath, harnessRoot, runId, liveRunKeys) {
|
|
|
11121
11349
|
init_util();
|
|
11122
11350
|
function pathAgeMs(target, now) {
|
|
11123
11351
|
try {
|
|
11124
|
-
const mtime =
|
|
11352
|
+
const mtime = statSync8(target).mtimeMs;
|
|
11125
11353
|
return Math.max(0, now - mtime);
|
|
11126
11354
|
} catch {
|
|
11127
11355
|
return 0;
|
|
11128
11356
|
}
|
|
11129
11357
|
}
|
|
11130
11358
|
function loadRunStatus(harnessRoot, runId) {
|
|
11131
|
-
const runPath =
|
|
11359
|
+
const runPath = path45.join(harnessRoot, "runs", runId, "run.json");
|
|
11132
11360
|
if (!existsSync33(runPath)) return null;
|
|
11133
11361
|
return readJson(runPath, null);
|
|
11134
11362
|
}
|
|
11135
11363
|
function runDirectoryIsEmpty(runPath) {
|
|
11136
11364
|
try {
|
|
11137
|
-
const entries =
|
|
11365
|
+
const entries = readdirSync10(runPath);
|
|
11138
11366
|
return entries.length === 0;
|
|
11139
11367
|
} catch {
|
|
11140
11368
|
return false;
|
|
@@ -11158,7 +11386,7 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
11158
11386
|
const candidates = [];
|
|
11159
11387
|
let entries;
|
|
11160
11388
|
try {
|
|
11161
|
-
entries =
|
|
11389
|
+
entries = readdirSync10(opts.worktreesDir, { withFileTypes: true });
|
|
11162
11390
|
} catch {
|
|
11163
11391
|
return [];
|
|
11164
11392
|
}
|
|
@@ -11166,7 +11394,7 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
11166
11394
|
if (!runEntry.isDirectory()) continue;
|
|
11167
11395
|
const runId = runEntry.name;
|
|
11168
11396
|
if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
|
|
11169
|
-
const runPath =
|
|
11397
|
+
const runPath = path45.join(opts.worktreesDir, runId);
|
|
11170
11398
|
if (!runDirectoryIsEmpty(runPath)) continue;
|
|
11171
11399
|
candidates.push({
|
|
11172
11400
|
kind: "remove_run_directory",
|
|
@@ -11189,7 +11417,7 @@ import { existsSync as existsSync34, rmSync as rmSync2 } from "node:fs";
|
|
|
11189
11417
|
init_paths();
|
|
11190
11418
|
|
|
11191
11419
|
// src/cleanup-path-ownership.ts
|
|
11192
|
-
import { lstatSync as lstatSync2, readdirSync as
|
|
11420
|
+
import { lstatSync as lstatSync2, readdirSync as readdirSync11 } from "node:fs";
|
|
11193
11421
|
function readPathOwnership(targetPath) {
|
|
11194
11422
|
try {
|
|
11195
11423
|
const st = lstatSync2(targetPath);
|
|
@@ -11206,7 +11434,7 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
11206
11434
|
if (!root) return false;
|
|
11207
11435
|
if (root.foreign) return true;
|
|
11208
11436
|
try {
|
|
11209
|
-
const names =
|
|
11437
|
+
const names = readdirSync11(targetPath);
|
|
11210
11438
|
let checked = 0;
|
|
11211
11439
|
for (const name of names) {
|
|
11212
11440
|
if (checked >= maxEntries) break;
|
|
@@ -11222,20 +11450,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
11222
11450
|
|
|
11223
11451
|
// src/cleanup-privileged-remove.ts
|
|
11224
11452
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
11225
|
-
import
|
|
11453
|
+
import path47 from "node:path";
|
|
11226
11454
|
|
|
11227
11455
|
// src/cleanup-harness-path-validate.ts
|
|
11228
|
-
import
|
|
11456
|
+
import path46 from "node:path";
|
|
11229
11457
|
function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
|
|
11230
|
-
const resolved =
|
|
11231
|
-
const suffix = `${
|
|
11458
|
+
const resolved = path46.resolve(targetPath);
|
|
11459
|
+
const suffix = `${path46.sep}${cacheDirName}`;
|
|
11232
11460
|
const cachePath = resolved.endsWith(suffix) ? resolved : null;
|
|
11233
11461
|
if (!cachePath) return "path_outside_harness";
|
|
11234
|
-
const rel =
|
|
11235
|
-
if (rel.startsWith("..") ||
|
|
11236
|
-
const parts = rel.split(
|
|
11462
|
+
const rel = path46.relative(worktreesDir, cachePath);
|
|
11463
|
+
if (rel.startsWith("..") || path46.isAbsolute(rel)) return "path_outside_harness";
|
|
11464
|
+
const parts = rel.split(path46.sep);
|
|
11237
11465
|
if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
|
|
11238
|
-
if (!resolved.startsWith(
|
|
11466
|
+
if (!resolved.startsWith(path46.resolve(harnessRoot))) return "path_outside_harness";
|
|
11239
11467
|
return null;
|
|
11240
11468
|
}
|
|
11241
11469
|
function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
|
|
@@ -11245,16 +11473,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
|
11245
11473
|
return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
|
|
11246
11474
|
}
|
|
11247
11475
|
function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
11248
|
-
const resolved =
|
|
11249
|
-
const relToWt =
|
|
11250
|
-
if (relToWt.startsWith("..") ||
|
|
11251
|
-
const parts = relToWt.split(
|
|
11476
|
+
const resolved = path46.resolve(targetPath);
|
|
11477
|
+
const relToWt = path46.relative(worktreesDir, resolved);
|
|
11478
|
+
if (relToWt.startsWith("..") || path46.isAbsolute(relToWt)) return "path_outside_harness";
|
|
11479
|
+
const parts = relToWt.split(path46.sep);
|
|
11252
11480
|
if (parts.length < 3) return "path_outside_harness";
|
|
11253
|
-
if (!resolved.startsWith(
|
|
11481
|
+
if (!resolved.startsWith(path46.resolve(harnessRoot))) return "path_outside_harness";
|
|
11254
11482
|
return null;
|
|
11255
11483
|
}
|
|
11256
11484
|
function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
11257
|
-
const resolved =
|
|
11485
|
+
const resolved = path46.resolve(targetPath);
|
|
11258
11486
|
return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
|
|
11259
11487
|
}
|
|
11260
11488
|
|
|
@@ -11288,12 +11516,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
|
|
|
11288
11516
|
"chown",
|
|
11289
11517
|
"-R",
|
|
11290
11518
|
`${effectiveUid}:${effectiveGid}`,
|
|
11291
|
-
|
|
11519
|
+
path47.resolve(targetPath)
|
|
11292
11520
|
]);
|
|
11293
11521
|
if (chown.ok) {
|
|
11294
11522
|
return { ok: true, method: "chown_then_rm" };
|
|
11295
11523
|
}
|
|
11296
|
-
const rm = runSudoNonInteractive(["rm", "-rf",
|
|
11524
|
+
const rm = runSudoNonInteractive(["rm", "-rf", path47.resolve(targetPath)]);
|
|
11297
11525
|
if (rm.ok) {
|
|
11298
11526
|
return { ok: true, method: "sudo_rm" };
|
|
11299
11527
|
}
|
|
@@ -11494,27 +11722,27 @@ function removeWorktree(candidate, execute) {
|
|
|
11494
11722
|
}
|
|
11495
11723
|
|
|
11496
11724
|
// src/cleanup-scan.ts
|
|
11497
|
-
import { existsSync as existsSync36, readdirSync as
|
|
11498
|
-
import
|
|
11725
|
+
import { existsSync as existsSync36, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
|
|
11726
|
+
import path48 from "node:path";
|
|
11499
11727
|
function pathAgeMs2(target, now) {
|
|
11500
11728
|
try {
|
|
11501
|
-
const mtime =
|
|
11729
|
+
const mtime = statSync9(target).mtimeMs;
|
|
11502
11730
|
return Math.max(0, now - mtime);
|
|
11503
11731
|
} catch {
|
|
11504
11732
|
return 0;
|
|
11505
11733
|
}
|
|
11506
11734
|
}
|
|
11507
11735
|
function isPathInside(child, parent) {
|
|
11508
|
-
const rel =
|
|
11509
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
11736
|
+
const rel = path48.relative(parent, child);
|
|
11737
|
+
return rel === "" || !rel.startsWith("..") && !path48.isAbsolute(rel);
|
|
11510
11738
|
}
|
|
11511
11739
|
function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
|
|
11512
11740
|
const out = [];
|
|
11513
11741
|
for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
|
|
11514
11742
|
if (rel === ".next") continue;
|
|
11515
|
-
const target =
|
|
11743
|
+
const target = path48.join(worktreePath, rel);
|
|
11516
11744
|
if (!existsSync36(target)) continue;
|
|
11517
|
-
const resolved =
|
|
11745
|
+
const resolved = path48.resolve(target);
|
|
11518
11746
|
if (seen.has(resolved)) continue;
|
|
11519
11747
|
if (!isPathInside(resolved, opts.harnessRoot)) continue;
|
|
11520
11748
|
seen.add(resolved);
|
|
@@ -11544,12 +11772,12 @@ function scanBuildCacheCandidates(opts) {
|
|
|
11544
11772
|
);
|
|
11545
11773
|
}
|
|
11546
11774
|
if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return candidates;
|
|
11547
|
-
for (const runEntry of
|
|
11775
|
+
for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
|
|
11548
11776
|
if (!runEntry.isDirectory()) continue;
|
|
11549
|
-
const runPath =
|
|
11550
|
-
for (const workerEntry of
|
|
11777
|
+
const runPath = path48.join(opts.worktreesDir, runEntry.name);
|
|
11778
|
+
for (const workerEntry of readdirSync12(runPath, { withFileTypes: true })) {
|
|
11551
11779
|
if (!workerEntry.isDirectory()) continue;
|
|
11552
|
-
const worktreePath =
|
|
11780
|
+
const worktreePath = path48.join(runPath, workerEntry.name);
|
|
11553
11781
|
candidates.push(
|
|
11554
11782
|
...collectBuildCacheForWorktree(worktreePath, opts, seen, {
|
|
11555
11783
|
runId: runEntry.name,
|
|
@@ -11587,21 +11815,21 @@ function scanWorktreeCandidates(opts) {
|
|
|
11587
11815
|
if (!orphanEnabled || !existsSync36(opts.worktreesDir)) return candidates;
|
|
11588
11816
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
11589
11817
|
for (const entry of opts.index.values()) {
|
|
11590
|
-
indexedPaths.add(
|
|
11818
|
+
indexedPaths.add(path48.resolve(entry.worktreePath));
|
|
11591
11819
|
}
|
|
11592
|
-
for (const runEntry of
|
|
11820
|
+
for (const runEntry of readdirSync12(opts.worktreesDir, { withFileTypes: true })) {
|
|
11593
11821
|
if (!runEntry.isDirectory()) continue;
|
|
11594
11822
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
11595
|
-
const runPath =
|
|
11823
|
+
const runPath = path48.join(opts.worktreesDir, runEntry.name);
|
|
11596
11824
|
let workerEntries;
|
|
11597
11825
|
try {
|
|
11598
|
-
workerEntries =
|
|
11826
|
+
workerEntries = readdirSync12(runPath, { withFileTypes: true });
|
|
11599
11827
|
} catch {
|
|
11600
11828
|
continue;
|
|
11601
11829
|
}
|
|
11602
11830
|
for (const workerEntry of workerEntries) {
|
|
11603
11831
|
if (!workerEntry.isDirectory()) continue;
|
|
11604
|
-
const worktreePath =
|
|
11832
|
+
const worktreePath = path48.resolve(path48.join(runPath, workerEntry.name));
|
|
11605
11833
|
if (seen.has(worktreePath)) continue;
|
|
11606
11834
|
if (indexedPaths.has(worktreePath)) continue;
|
|
11607
11835
|
if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
|
|
@@ -11620,27 +11848,27 @@ function scanWorktreeCandidates(opts) {
|
|
|
11620
11848
|
}
|
|
11621
11849
|
|
|
11622
11850
|
// src/cleanup-dependency-scan.ts
|
|
11623
|
-
import { existsSync as existsSync37, readdirSync as
|
|
11624
|
-
import
|
|
11851
|
+
import { existsSync as existsSync37, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
|
|
11852
|
+
import path49 from "node:path";
|
|
11625
11853
|
var DEPENDENCY_CACHE_DIRS = [
|
|
11626
11854
|
{ dirName: "node_modules", kind: "remove_node_modules" },
|
|
11627
11855
|
{ dirName: ".next", kind: "remove_next_cache" }
|
|
11628
11856
|
];
|
|
11629
11857
|
function pathAgeMs3(target, now) {
|
|
11630
11858
|
try {
|
|
11631
|
-
const mtime =
|
|
11859
|
+
const mtime = statSync10(target).mtimeMs;
|
|
11632
11860
|
return Math.max(0, now - mtime);
|
|
11633
11861
|
} catch {
|
|
11634
11862
|
return 0;
|
|
11635
11863
|
}
|
|
11636
11864
|
}
|
|
11637
11865
|
function isPathInside2(child, parent) {
|
|
11638
|
-
const rel =
|
|
11639
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
11866
|
+
const rel = path49.relative(parent, child);
|
|
11867
|
+
return rel === "" || !rel.startsWith("..") && !path49.isAbsolute(rel);
|
|
11640
11868
|
}
|
|
11641
11869
|
function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
11642
11870
|
if (!existsSync37(targetPath)) return;
|
|
11643
|
-
const resolved =
|
|
11871
|
+
const resolved = path49.resolve(targetPath);
|
|
11644
11872
|
if (seen.has(resolved)) return;
|
|
11645
11873
|
if (!isPathInside2(resolved, opts.harnessRoot)) return;
|
|
11646
11874
|
seen.add(resolved);
|
|
@@ -11657,7 +11885,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
|
11657
11885
|
}
|
|
11658
11886
|
function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
|
|
11659
11887
|
for (const entry of DEPENDENCY_CACHE_DIRS) {
|
|
11660
|
-
pushCandidate2(candidates, seen, opts,
|
|
11888
|
+
pushCandidate2(candidates, seen, opts, path49.join(worktreePath, entry.dirName), entry.kind, meta);
|
|
11661
11889
|
}
|
|
11662
11890
|
}
|
|
11663
11891
|
function scanDependencyCacheCandidates(opts) {
|
|
@@ -11672,19 +11900,19 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
11672
11900
|
});
|
|
11673
11901
|
}
|
|
11674
11902
|
if (!opts.includeOrphans || !existsSync37(opts.worktreesDir)) return candidates;
|
|
11675
|
-
for (const runEntry of
|
|
11903
|
+
for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
|
|
11676
11904
|
if (!runEntry.isDirectory()) continue;
|
|
11677
11905
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
11678
|
-
const runPath =
|
|
11906
|
+
const runPath = path49.join(opts.worktreesDir, runEntry.name);
|
|
11679
11907
|
let workerEntries;
|
|
11680
11908
|
try {
|
|
11681
|
-
workerEntries =
|
|
11909
|
+
workerEntries = readdirSync13(runPath, { withFileTypes: true });
|
|
11682
11910
|
} catch {
|
|
11683
11911
|
continue;
|
|
11684
11912
|
}
|
|
11685
11913
|
for (const workerEntry of workerEntries) {
|
|
11686
11914
|
if (!workerEntry.isDirectory()) continue;
|
|
11687
|
-
const worktreePath =
|
|
11915
|
+
const worktreePath = path49.join(runPath, workerEntry.name);
|
|
11688
11916
|
scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
|
|
11689
11917
|
runId: runEntry.name,
|
|
11690
11918
|
worker: workerEntry.name
|
|
@@ -11696,11 +11924,11 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
11696
11924
|
|
|
11697
11925
|
// src/cleanup-duplicate-worktrees.ts
|
|
11698
11926
|
init_git();
|
|
11699
|
-
import { existsSync as existsSync38, statSync as
|
|
11700
|
-
import
|
|
11927
|
+
import { existsSync as existsSync38, statSync as statSync11 } from "node:fs";
|
|
11928
|
+
import path50 from "node:path";
|
|
11701
11929
|
function pathAgeMs4(target, now) {
|
|
11702
11930
|
try {
|
|
11703
|
-
const mtime =
|
|
11931
|
+
const mtime = statSync11(target).mtimeMs;
|
|
11704
11932
|
return Math.max(0, now - mtime);
|
|
11705
11933
|
} catch {
|
|
11706
11934
|
return 0;
|
|
@@ -11727,32 +11955,24 @@ function parseWorktreePorcelain(output) {
|
|
|
11727
11955
|
return records;
|
|
11728
11956
|
}
|
|
11729
11957
|
function isUnderWorktreesDir(worktreePath, worktreesDir) {
|
|
11730
|
-
const rel =
|
|
11731
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
11732
|
-
}
|
|
11733
|
-
function isCleanWorktree(worktreePath, repoRoot) {
|
|
11734
|
-
try {
|
|
11735
|
-
const porcelain = git(repoRoot, ["-C", worktreePath, "status", "--porcelain"], {
|
|
11736
|
-
allowFailure: true
|
|
11737
|
-
});
|
|
11738
|
-
return !String(porcelain || "").trim();
|
|
11739
|
-
} catch {
|
|
11740
|
-
return false;
|
|
11741
|
-
}
|
|
11958
|
+
const rel = path50.relative(path50.resolve(worktreesDir), path50.resolve(worktreePath));
|
|
11959
|
+
return rel !== "" && !rel.startsWith("..") && !path50.isAbsolute(rel);
|
|
11742
11960
|
}
|
|
11961
|
+
var MAX_DUPLICATE_WORKTREE_CANDIDATES_PER_REPO = 200;
|
|
11743
11962
|
function scanDuplicateWorktreeCandidates(opts) {
|
|
11744
11963
|
if (!opts.includeOrphans || !existsSync38(opts.worktreesDir)) return [];
|
|
11745
11964
|
const repos = /* @__PURE__ */ new Set();
|
|
11746
11965
|
for (const entry of opts.index.values()) {
|
|
11747
|
-
if (entry.run.repo) repos.add(
|
|
11966
|
+
if (entry.run.repo) repos.add(path50.resolve(entry.run.repo));
|
|
11748
11967
|
}
|
|
11749
11968
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
11750
11969
|
for (const entry of opts.index.values()) {
|
|
11751
|
-
indexedPaths.add(
|
|
11970
|
+
indexedPaths.add(path50.resolve(entry.worktreePath));
|
|
11752
11971
|
}
|
|
11753
11972
|
const candidates = [];
|
|
11754
11973
|
const seen = /* @__PURE__ */ new Set();
|
|
11755
11974
|
for (const repoRoot of repos) {
|
|
11975
|
+
let repoCandidateCount = 0;
|
|
11756
11976
|
let porcelain;
|
|
11757
11977
|
try {
|
|
11758
11978
|
porcelain = git(repoRoot, ["worktree", "list", "--porcelain"], { allowFailure: true });
|
|
@@ -11761,18 +11981,19 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
11761
11981
|
}
|
|
11762
11982
|
const worktrees = parseWorktreePorcelain(porcelain);
|
|
11763
11983
|
for (const wt of worktrees) {
|
|
11764
|
-
|
|
11765
|
-
|
|
11984
|
+
if (repoCandidateCount >= MAX_DUPLICATE_WORKTREE_CANDIDATES_PER_REPO) break;
|
|
11985
|
+
const resolved = path50.resolve(wt.path);
|
|
11986
|
+
if (resolved === path50.resolve(repoRoot)) continue;
|
|
11766
11987
|
if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
|
|
11767
11988
|
if (indexedPaths.has(resolved)) continue;
|
|
11768
11989
|
if (seen.has(resolved)) continue;
|
|
11769
11990
|
if (!existsSync38(resolved)) continue;
|
|
11770
|
-
|
|
11771
|
-
const
|
|
11772
|
-
const parts = rel.split(path49.sep);
|
|
11991
|
+
const rel = path50.relative(opts.worktreesDir, resolved);
|
|
11992
|
+
const parts = rel.split(path50.sep);
|
|
11773
11993
|
const runId = parts[0];
|
|
11774
11994
|
const worker = parts[1] ?? "unknown";
|
|
11775
11995
|
seen.add(resolved);
|
|
11996
|
+
repoCandidateCount += 1;
|
|
11776
11997
|
candidates.push({
|
|
11777
11998
|
kind: "remove_worktree",
|
|
11778
11999
|
path: resolved,
|
|
@@ -11790,12 +12011,22 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
11790
12011
|
// src/cleanup-worktree-index.ts
|
|
11791
12012
|
init_run_store();
|
|
11792
12013
|
init_util();
|
|
11793
|
-
import
|
|
12014
|
+
import path51 from "node:path";
|
|
12015
|
+
function filterWorktreeIndexForRoot(index, harnessRoot) {
|
|
12016
|
+
const resolvedRoot = path51.resolve(harnessRoot);
|
|
12017
|
+
const scoped = /* @__PURE__ */ new Map();
|
|
12018
|
+
for (const [key, entry] of index) {
|
|
12019
|
+
const entryRoot = entry.harnessRoot ? path51.resolve(entry.harnessRoot) : null;
|
|
12020
|
+
if (entryRoot && entryRoot !== resolvedRoot) continue;
|
|
12021
|
+
scoped.set(key, entry);
|
|
12022
|
+
}
|
|
12023
|
+
return scoped;
|
|
12024
|
+
}
|
|
11794
12025
|
function buildWorktreeIndexAt(harnessRoot) {
|
|
11795
12026
|
const index = /* @__PURE__ */ new Map();
|
|
11796
12027
|
for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
|
|
11797
12028
|
for (const name of Object.keys(run.workers || {})) {
|
|
11798
|
-
const workerPath =
|
|
12029
|
+
const workerPath = path51.join(
|
|
11799
12030
|
runDirectoryAt(harnessRoot, run.id),
|
|
11800
12031
|
"workers",
|
|
11801
12032
|
safeSlug(name),
|
|
@@ -11803,9 +12034,9 @@ function buildWorktreeIndexAt(harnessRoot) {
|
|
|
11803
12034
|
);
|
|
11804
12035
|
const worker = readJson(workerPath, void 0);
|
|
11805
12036
|
if (!worker?.worktreePath) continue;
|
|
11806
|
-
index.set(
|
|
12037
|
+
index.set(path51.resolve(worker.worktreePath), {
|
|
11807
12038
|
harnessRoot,
|
|
11808
|
-
worktreePath:
|
|
12039
|
+
worktreePath: path51.resolve(worker.worktreePath),
|
|
11809
12040
|
runId: run.id,
|
|
11810
12041
|
workerName: name,
|
|
11811
12042
|
run,
|
|
@@ -11831,6 +12062,7 @@ function resolveHarnessRetention(options = {}) {
|
|
|
11831
12062
|
const execute = options.execute === true || options.execute !== false && envFlag("KYNVER_CLEANUP_EXECUTE");
|
|
11832
12063
|
const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag("KYNVER_CLEANUP_SKIP_FINALIZE");
|
|
11833
12064
|
const nodeModulesAgeMs = options.nodeModulesAgeMs ?? envMs("KYNVER_CLEANUP_NODE_MODULES_AGE_MS", DEFAULT_NODE_MODULES_AGE_MS);
|
|
12065
|
+
const scanDependencyCaches = options.scanDependencyCaches ?? (options.nodeModulesAgeMs !== void 0 || process.env.KYNVER_CLEANUP_NODE_MODULES_AGE_MS != null);
|
|
11834
12066
|
const worktreesAgeMs = options.worktreesAgeMs ?? envMs("KYNVER_CLEANUP_WORKTREES_AGE_MS", 0);
|
|
11835
12067
|
const terminalWorktreesAgeMs = options.terminalWorktreesAgeMs ?? envMs("KYNVER_CLEANUP_TERMINAL_WORKTREES_AGE_MS", DEFAULT_TERMINAL_WORKTREES_AGE_MS);
|
|
11836
12068
|
const runDirectoriesAgeMs = options.runDirectoriesAgeMs ?? envMs("KYNVER_CLEANUP_RUN_DIRECTORIES_AGE_MS", DEFAULT_RUN_DIRECTORIES_AGE_MS);
|
|
@@ -11846,6 +12078,7 @@ function resolveHarnessRetention(options = {}) {
|
|
|
11846
12078
|
return {
|
|
11847
12079
|
execute,
|
|
11848
12080
|
finalizeStaleRuns: finalizeStaleRuns2,
|
|
12081
|
+
scanDependencyCaches,
|
|
11849
12082
|
nodeModulesAgeMs,
|
|
11850
12083
|
worktreesAgeMs: worktreesAgeMs > 0 ? worktreesAgeMs : 0,
|
|
11851
12084
|
terminalWorktreesAgeMs: terminalWorktreesAgeMs >= 0 ? terminalWorktreesAgeMs : 0,
|
|
@@ -11869,35 +12102,38 @@ function resolvePipelineHarnessRetention(runId) {
|
|
|
11869
12102
|
runDirectoriesAgeMs: scopeAll ? DEFAULT_RUN_DIRECTORIES_AGE_MS : void 0,
|
|
11870
12103
|
includeOrphans: scopeAll ? true : void 0,
|
|
11871
12104
|
finalizeStaleRuns: true,
|
|
11872
|
-
accountBytes: true
|
|
12105
|
+
accountBytes: true,
|
|
12106
|
+
scanDependencyCaches: true
|
|
11873
12107
|
});
|
|
11874
12108
|
}
|
|
11875
12109
|
|
|
11876
12110
|
// src/cleanup-orphan-safety.ts
|
|
11877
12111
|
init_git();
|
|
11878
|
-
import { existsSync as existsSync39
|
|
11879
|
-
import
|
|
12112
|
+
import { existsSync as existsSync39 } from "node:fs";
|
|
12113
|
+
import path52 from "node:path";
|
|
12114
|
+
init_util();
|
|
11880
12115
|
var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
|
|
11881
12116
|
function assessOrphanWorktreeSafety(input) {
|
|
11882
12117
|
const now = input.now ?? Date.now();
|
|
11883
12118
|
const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
|
|
11884
12119
|
if (!existsSync39(input.worktreePath)) return null;
|
|
11885
12120
|
if (input.runId && input.workerName) {
|
|
11886
|
-
const
|
|
12121
|
+
const workerDir = path52.join(
|
|
11887
12122
|
input.harnessRoot,
|
|
11888
12123
|
"runs",
|
|
11889
12124
|
input.runId,
|
|
11890
12125
|
"workers",
|
|
11891
|
-
input.workerName
|
|
11892
|
-
"heartbeat.jsonl"
|
|
12126
|
+
input.workerName
|
|
11893
12127
|
);
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
12128
|
+
const worker = readJson(
|
|
12129
|
+
path52.join(workerDir, "worker.json"),
|
|
12130
|
+
void 0
|
|
12131
|
+
);
|
|
12132
|
+
if (worker && heartbeatContentIsFresh(worker, now, heartbeatFreshMs)) {
|
|
12133
|
+
return "active_worker";
|
|
11898
12134
|
}
|
|
11899
12135
|
}
|
|
11900
|
-
const gitDir =
|
|
12136
|
+
const gitDir = path52.join(input.worktreePath, ".git");
|
|
11901
12137
|
if (!existsSync39(gitDir)) return null;
|
|
11902
12138
|
const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
|
|
11903
12139
|
if (porcelain.status !== 0) return "pr_or_unmerged_commits";
|
|
@@ -11930,10 +12166,10 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
11930
12166
|
init_paths();
|
|
11931
12167
|
import { existsSync as existsSync40 } from "node:fs";
|
|
11932
12168
|
import { homedir as homedir13 } from "node:os";
|
|
11933
|
-
import
|
|
12169
|
+
import path53 from "node:path";
|
|
11934
12170
|
var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
|
|
11935
12171
|
"/var/tmp/kynver-harness",
|
|
11936
|
-
|
|
12172
|
+
path53.join(homedir13(), ".openclaw", "harness")
|
|
11937
12173
|
];
|
|
11938
12174
|
function addRoot(seen, roots, candidate) {
|
|
11939
12175
|
if (!candidate?.trim()) return;
|
|
@@ -11955,7 +12191,7 @@ function resolveHarnessScanRoots(options = {}) {
|
|
|
11955
12191
|
for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
|
|
11956
12192
|
if (shouldScanWellKnownRoots(options)) {
|
|
11957
12193
|
for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
|
|
11958
|
-
const resolved =
|
|
12194
|
+
const resolved = path53.resolve(candidate);
|
|
11959
12195
|
if (!seen.has(resolved) && existsSync40(resolved)) addRoot(seen, roots, resolved);
|
|
11960
12196
|
}
|
|
11961
12197
|
}
|
|
@@ -12034,15 +12270,52 @@ var CleanupGitRevCache = class {
|
|
|
12034
12270
|
}
|
|
12035
12271
|
};
|
|
12036
12272
|
|
|
12273
|
+
// src/cleanup-git-probe.ts
|
|
12274
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12275
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
12276
|
+
import path54 from "node:path";
|
|
12277
|
+
var CLEANUP_GIT_PROBE_TIMEOUT_MS = 5e3;
|
|
12278
|
+
function cleanupGitCapture(cwd, args) {
|
|
12279
|
+
if (!existsSync41(path54.join(cwd, ".git"))) {
|
|
12280
|
+
return { status: null, stdout: "", stderr: "", error: "not_a_git_repo" };
|
|
12281
|
+
}
|
|
12282
|
+
try {
|
|
12283
|
+
const res = spawnSync8("git", args, {
|
|
12284
|
+
cwd,
|
|
12285
|
+
encoding: "utf8",
|
|
12286
|
+
timeout: CLEANUP_GIT_PROBE_TIMEOUT_MS
|
|
12287
|
+
});
|
|
12288
|
+
const timedOut = res.error?.message?.includes("ETIMEDOUT") === true;
|
|
12289
|
+
return {
|
|
12290
|
+
status: timedOut ? null : res.status,
|
|
12291
|
+
stdout: res.stdout || "",
|
|
12292
|
+
stderr: res.stderr || "",
|
|
12293
|
+
error: timedOut ? "git_timeout" : res.error ? res.error.message : null
|
|
12294
|
+
};
|
|
12295
|
+
} catch (error) {
|
|
12296
|
+
return {
|
|
12297
|
+
status: null,
|
|
12298
|
+
stdout: "",
|
|
12299
|
+
stderr: "",
|
|
12300
|
+
error: error.message
|
|
12301
|
+
};
|
|
12302
|
+
}
|
|
12303
|
+
}
|
|
12304
|
+
function gitStatusShortForCleanup(worktreePath) {
|
|
12305
|
+
const captured = cleanupGitCapture(worktreePath, ["status", "--short"]);
|
|
12306
|
+
if (captured.error === "not_a_git_repo") return [];
|
|
12307
|
+
if (captured.error === "git_timeout" || captured.status !== 0) return null;
|
|
12308
|
+
return captured.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
12309
|
+
}
|
|
12310
|
+
|
|
12037
12311
|
// src/cleanup-git-status-cache.ts
|
|
12038
|
-
init_git();
|
|
12039
12312
|
var CleanupGitStatusCache = class {
|
|
12040
12313
|
cache = /* @__PURE__ */ new Map();
|
|
12041
12314
|
porcelain(worktreePath) {
|
|
12042
12315
|
const resolved = worktreePath;
|
|
12043
12316
|
const cached = this.cache.get(resolved);
|
|
12044
12317
|
if (cached !== void 0) return cached;
|
|
12045
|
-
const lines =
|
|
12318
|
+
const lines = gitStatusShortForCleanup(resolved) ?? [];
|
|
12046
12319
|
this.cache.set(resolved, lines);
|
|
12047
12320
|
return lines;
|
|
12048
12321
|
}
|
|
@@ -12147,9 +12420,9 @@ function mergeWorktreeIndexes(scanRoots) {
|
|
|
12147
12420
|
}
|
|
12148
12421
|
function worktreePathForCandidate(candidate, worktreesDir) {
|
|
12149
12422
|
if (candidate.runId && candidate.worker) {
|
|
12150
|
-
return
|
|
12423
|
+
return path55.join(worktreesDir, candidate.runId, candidate.worker);
|
|
12151
12424
|
}
|
|
12152
|
-
return
|
|
12425
|
+
return path55.resolve(candidate.path, "..");
|
|
12153
12426
|
}
|
|
12154
12427
|
function runHarnessCleanup(options = {}) {
|
|
12155
12428
|
let retention = resolveHarnessRetention(options);
|
|
@@ -12178,7 +12451,8 @@ function runHarnessCleanup(options = {}) {
|
|
|
12178
12451
|
for (const harnessRoot of paths.scanRoots) {
|
|
12179
12452
|
if (atSweepCap()) break;
|
|
12180
12453
|
emitCleanupProgress("root", harnessRoot);
|
|
12181
|
-
const worktreesDir =
|
|
12454
|
+
const worktreesDir = path55.join(harnessRoot, "worktrees");
|
|
12455
|
+
const rootIndex = filterWorktreeIndexForRoot(index, harnessRoot);
|
|
12182
12456
|
const scanOpts = {
|
|
12183
12457
|
harnessRoot,
|
|
12184
12458
|
worktreesDir,
|
|
@@ -12186,11 +12460,14 @@ function runHarnessCleanup(options = {}) {
|
|
|
12186
12460
|
worktreesAgeMs: retention.worktreesAgeMs,
|
|
12187
12461
|
includeOrphans: retention.includeOrphans,
|
|
12188
12462
|
runIdFilter: retention.runIdFilter,
|
|
12189
|
-
index,
|
|
12463
|
+
index: rootIndex,
|
|
12190
12464
|
now: paths.now
|
|
12191
12465
|
};
|
|
12192
|
-
const dependencyCandidates = scanDependencyCacheCandidates(scanOpts);
|
|
12193
|
-
emitCleanupProgress(
|
|
12466
|
+
const dependencyCandidates = retention.scanDependencyCaches ? scanDependencyCacheCandidates(scanOpts) : [];
|
|
12467
|
+
emitCleanupProgress(
|
|
12468
|
+
"dependency",
|
|
12469
|
+
retention.scanDependencyCaches ? `${dependencyCandidates.length} cache candidate(s) at ${harnessRoot}` : "skipped (worktree-only sweep)"
|
|
12470
|
+
);
|
|
12194
12471
|
let dependencyProcessed = 0;
|
|
12195
12472
|
for (const raw of dependencyCandidates) {
|
|
12196
12473
|
if (atSweepCap()) break;
|
|
@@ -12198,7 +12475,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
12198
12475
|
if (dependencyProcessed % 50 === 0) {
|
|
12199
12476
|
emitCleanupProgress("dependency", `${dependencyProcessed}/${dependencyCandidates.length} evaluated`);
|
|
12200
12477
|
}
|
|
12201
|
-
const resolved =
|
|
12478
|
+
const resolved = path55.resolve(raw.path);
|
|
12202
12479
|
if (processedPaths.has(resolved)) continue;
|
|
12203
12480
|
processedPaths.add(resolved);
|
|
12204
12481
|
const candidate = { ...raw, path: resolved };
|
|
@@ -12209,7 +12486,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
12209
12486
|
continue;
|
|
12210
12487
|
}
|
|
12211
12488
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
12212
|
-
const indexed =
|
|
12489
|
+
const indexed = rootIndex.get(path55.resolve(worktreePath)) ?? null;
|
|
12213
12490
|
const guardReason = skipDependencyCacheRemoval({
|
|
12214
12491
|
indexed,
|
|
12215
12492
|
includeOrphans: true,
|
|
@@ -12232,9 +12509,9 @@ function runHarnessCleanup(options = {}) {
|
|
|
12232
12509
|
)
|
|
12233
12510
|
);
|
|
12234
12511
|
}
|
|
12235
|
-
for (const raw of scanBuildCacheCandidates(scanOpts)) {
|
|
12512
|
+
if (retention.scanDependencyCaches) for (const raw of scanBuildCacheCandidates(scanOpts)) {
|
|
12236
12513
|
if (atSweepCap()) break;
|
|
12237
|
-
const resolved =
|
|
12514
|
+
const resolved = path55.resolve(raw.path);
|
|
12238
12515
|
if (processedPaths.has(resolved)) continue;
|
|
12239
12516
|
processedPaths.add(resolved);
|
|
12240
12517
|
const candidate = { ...raw, path: resolved };
|
|
@@ -12245,7 +12522,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
12245
12522
|
continue;
|
|
12246
12523
|
}
|
|
12247
12524
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
12248
|
-
const indexed =
|
|
12525
|
+
const indexed = rootIndex.get(path55.resolve(worktreePath)) ?? null;
|
|
12249
12526
|
const guardReason = skipBuildCacheRemoval({
|
|
12250
12527
|
indexed,
|
|
12251
12528
|
includeOrphans: true,
|
|
@@ -12281,11 +12558,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
12281
12558
|
if (worktreeProcessed % 50 === 0) {
|
|
12282
12559
|
emitCleanupProgress("worktrees", `${worktreeProcessed}/${worktreeCandidates.length} evaluated`);
|
|
12283
12560
|
}
|
|
12284
|
-
const resolved =
|
|
12561
|
+
const resolved = path55.resolve(raw.path);
|
|
12285
12562
|
if (worktreeSeen.has(resolved)) continue;
|
|
12286
12563
|
worktreeSeen.add(resolved);
|
|
12287
12564
|
const candidate = { ...raw, path: resolved };
|
|
12288
|
-
const indexed =
|
|
12565
|
+
const indexed = rootIndex.get(path55.resolve(candidate.path)) ?? null;
|
|
12289
12566
|
const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
|
|
12290
12567
|
worktreePath: candidate.path,
|
|
12291
12568
|
harnessRoot,
|
|
@@ -12295,14 +12572,17 @@ function runHarnessCleanup(options = {}) {
|
|
|
12295
12572
|
});
|
|
12296
12573
|
const guardSkip = skipWorktreeRemoval({
|
|
12297
12574
|
indexed,
|
|
12298
|
-
worktreePath:
|
|
12575
|
+
worktreePath: path55.resolve(candidate.path),
|
|
12299
12576
|
includeOrphans: retention.includeOrphans,
|
|
12300
12577
|
worktreesAgeMs: retention.worktreesAgeMs,
|
|
12301
12578
|
terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
|
|
12302
12579
|
ageMs: candidate.ageMs,
|
|
12303
12580
|
orphanSafety,
|
|
12304
12581
|
worktreeRemovalGuard: options.worktreeRemovalGuard,
|
|
12305
|
-
liveness
|
|
12582
|
+
liveness,
|
|
12583
|
+
now: paths.now,
|
|
12584
|
+
harnessRoot,
|
|
12585
|
+
writeSalvageEvidence: retention.execute
|
|
12306
12586
|
});
|
|
12307
12587
|
if (guardSkip) {
|
|
12308
12588
|
const { reason: guardReason, detail: guardDetail } = normalizeGuardSkip(guardSkip);
|
|
@@ -12327,11 +12607,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
12327
12607
|
now: paths.now
|
|
12328
12608
|
})) {
|
|
12329
12609
|
if (atSweepCap()) break;
|
|
12330
|
-
const resolved =
|
|
12610
|
+
const resolved = path55.resolve(raw.path);
|
|
12331
12611
|
if (processedPaths.has(resolved)) continue;
|
|
12332
12612
|
processedPaths.add(resolved);
|
|
12333
12613
|
const candidate = { ...raw, path: resolved };
|
|
12334
|
-
const runId = candidate.runId ??
|
|
12614
|
+
const runId = candidate.runId ?? path55.basename(resolved);
|
|
12335
12615
|
const dirSkip = skipRunDirectoryRemoval({
|
|
12336
12616
|
harnessRoot,
|
|
12337
12617
|
runId,
|
|
@@ -12466,7 +12746,7 @@ function isPipelineCleanupEnabled() {
|
|
|
12466
12746
|
|
|
12467
12747
|
// src/cli.ts
|
|
12468
12748
|
init_config();
|
|
12469
|
-
import { mkdirSync as
|
|
12749
|
+
import { mkdirSync as mkdirSync15, realpathSync } from "node:fs";
|
|
12470
12750
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
12471
12751
|
|
|
12472
12752
|
// src/bootstrap.ts
|
|
@@ -12582,17 +12862,17 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
|
|
|
12582
12862
|
}
|
|
12583
12863
|
|
|
12584
12864
|
// src/daemon-heartbeat.ts
|
|
12585
|
-
import { mkdirSync as
|
|
12865
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync16, renameSync as renameSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
12586
12866
|
import { homedir as homedir14 } from "node:os";
|
|
12587
|
-
import
|
|
12867
|
+
import path56 from "node:path";
|
|
12588
12868
|
function daemonHeartbeatPath(agentOsId) {
|
|
12589
12869
|
const safe = agentOsId.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
12590
|
-
return
|
|
12870
|
+
return path56.join(homedir14(), ".kynver", `daemon-heartbeat-${safe}.json`);
|
|
12591
12871
|
}
|
|
12592
12872
|
function writeDaemonHeartbeat(input) {
|
|
12593
12873
|
try {
|
|
12594
12874
|
const file = daemonHeartbeatPath(input.agentOsId);
|
|
12595
|
-
|
|
12875
|
+
mkdirSync9(path56.dirname(file), { recursive: true });
|
|
12596
12876
|
const beat = {
|
|
12597
12877
|
observedAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
12598
12878
|
pid: process.pid,
|
|
@@ -12600,8 +12880,8 @@ function writeDaemonHeartbeat(input) {
|
|
|
12600
12880
|
agentOsId: input.agentOsId
|
|
12601
12881
|
};
|
|
12602
12882
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
12603
|
-
|
|
12604
|
-
|
|
12883
|
+
writeFileSync5(tmp, JSON.stringify(beat), "utf8");
|
|
12884
|
+
renameSync4(tmp, file);
|
|
12605
12885
|
} catch {
|
|
12606
12886
|
}
|
|
12607
12887
|
}
|
|
@@ -12642,9 +12922,9 @@ function assertNativeDaemonAllowed() {
|
|
|
12642
12922
|
|
|
12643
12923
|
// src/cron/cron-env.ts
|
|
12644
12924
|
init_config();
|
|
12645
|
-
import { existsSync as
|
|
12925
|
+
import { existsSync as existsSync42 } from "node:fs";
|
|
12646
12926
|
import { homedir as homedir15 } from "node:os";
|
|
12647
|
-
import
|
|
12927
|
+
import path57 from "node:path";
|
|
12648
12928
|
function envFlag4(name, defaultValue) {
|
|
12649
12929
|
const raw = process.env[name]?.trim().toLowerCase();
|
|
12650
12930
|
if (!raw) return defaultValue;
|
|
@@ -12660,7 +12940,7 @@ function envInt(name, fallback, min = 1) {
|
|
|
12660
12940
|
function defaultKynverCronStorePath() {
|
|
12661
12941
|
const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
|
|
12662
12942
|
if (explicit) return explicit;
|
|
12663
|
-
return
|
|
12943
|
+
return path57.join(homedir15(), ".kynver", "agent-os-cron.json");
|
|
12664
12944
|
}
|
|
12665
12945
|
function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
|
|
12666
12946
|
const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
|
|
@@ -12680,7 +12960,7 @@ function resolveKynverCronEnv() {
|
|
|
12680
12960
|
const fireBaseUrl = resolveKynverCronFireBaseUrl();
|
|
12681
12961
|
const secret = resolveKynverCronSecret();
|
|
12682
12962
|
const credsReady = Boolean(fireBaseUrl && secret);
|
|
12683
|
-
const storeExists =
|
|
12963
|
+
const storeExists = existsSync42(storePath);
|
|
12684
12964
|
const defaultEnabled = credsReady && (storeExists || envFlag4("KYNVER_CRON_TICK_FORCE", false));
|
|
12685
12965
|
return {
|
|
12686
12966
|
storePath,
|
|
@@ -12734,10 +13014,10 @@ async function fireKynverCronJob(input) {
|
|
|
12734
13014
|
|
|
12735
13015
|
// src/cron/cron-lock.ts
|
|
12736
13016
|
init_util();
|
|
12737
|
-
import { closeSync as closeSync6, existsSync as
|
|
13017
|
+
import { closeSync as closeSync6, existsSync as existsSync43, openSync as openSync6, readFileSync as readFileSync17, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
12738
13018
|
var STALE_LOCK_MS = 10 * 6e4;
|
|
12739
13019
|
function readLockInfo(lockPath) {
|
|
12740
|
-
if (!
|
|
13020
|
+
if (!existsSync43(lockPath)) return null;
|
|
12741
13021
|
try {
|
|
12742
13022
|
const parsed = JSON.parse(readFileSync17(lockPath, "utf8"));
|
|
12743
13023
|
if (typeof parsed.pid === "number" && typeof parsed.at === "string") return parsed;
|
|
@@ -12755,14 +13035,14 @@ function lockIsStale(lockPath) {
|
|
|
12755
13035
|
return Date.now() - atMs > STALE_LOCK_MS;
|
|
12756
13036
|
}
|
|
12757
13037
|
function tryAcquireCronTickLock(lockPath) {
|
|
12758
|
-
if (
|
|
13038
|
+
if (existsSync43(lockPath) && !lockIsStale(lockPath)) {
|
|
12759
13039
|
const info = readLockInfo(lockPath);
|
|
12760
13040
|
return {
|
|
12761
13041
|
acquired: false,
|
|
12762
13042
|
reason: info ? `held by pid ${info.pid}` : "held by another process"
|
|
12763
13043
|
};
|
|
12764
13044
|
}
|
|
12765
|
-
if (
|
|
13045
|
+
if (existsSync43(lockPath)) {
|
|
12766
13046
|
try {
|
|
12767
13047
|
unlinkSync3(lockPath);
|
|
12768
13048
|
} catch {
|
|
@@ -12770,7 +13050,7 @@ function tryAcquireCronTickLock(lockPath) {
|
|
|
12770
13050
|
}
|
|
12771
13051
|
try {
|
|
12772
13052
|
const fd = openSync6(lockPath, "wx");
|
|
12773
|
-
|
|
13053
|
+
writeFileSync6(
|
|
12774
13054
|
fd,
|
|
12775
13055
|
JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
12776
13056
|
"utf8"
|
|
@@ -12926,7 +13206,7 @@ async function ensureCronStoreInitialized(storePath = defaultKynverCronStorePath
|
|
|
12926
13206
|
// src/cron/cron-tick-state.ts
|
|
12927
13207
|
import { randomBytes } from "node:crypto";
|
|
12928
13208
|
import { promises as fs4 } from "node:fs";
|
|
12929
|
-
import
|
|
13209
|
+
import path58 from "node:path";
|
|
12930
13210
|
var EMPTY = { version: 1, jobs: {} };
|
|
12931
13211
|
async function readFileIfExists2(filePath) {
|
|
12932
13212
|
try {
|
|
@@ -12953,7 +13233,7 @@ async function loadCronTickState(statePath) {
|
|
|
12953
13233
|
return parseCronTickState(raw);
|
|
12954
13234
|
}
|
|
12955
13235
|
async function writeStateAtomic(statePath, state) {
|
|
12956
|
-
await fs4.mkdir(
|
|
13236
|
+
await fs4.mkdir(path58.dirname(statePath), { recursive: true });
|
|
12957
13237
|
const suffix = randomBytes(6).toString("hex");
|
|
12958
13238
|
const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
|
|
12959
13239
|
await fs4.writeFile(tmp, `${JSON.stringify(state, null, 2)}
|
|
@@ -13133,7 +13413,7 @@ async function runKynverCronTick(opts = {}) {
|
|
|
13133
13413
|
init_util();
|
|
13134
13414
|
|
|
13135
13415
|
// src/pipeline-tick.ts
|
|
13136
|
-
import
|
|
13416
|
+
import path61 from "node:path";
|
|
13137
13417
|
init_config();
|
|
13138
13418
|
|
|
13139
13419
|
// src/pipeline-dispatch.ts
|
|
@@ -13275,7 +13555,7 @@ init_util();
|
|
|
13275
13555
|
init_status();
|
|
13276
13556
|
init_run_store();
|
|
13277
13557
|
init_util();
|
|
13278
|
-
import
|
|
13558
|
+
import path59 from "node:path";
|
|
13279
13559
|
|
|
13280
13560
|
// src/plan-progress-sync.ts
|
|
13281
13561
|
init_config();
|
|
@@ -13300,7 +13580,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
13300
13580
|
const outcomes = [];
|
|
13301
13581
|
for (const name of Object.keys(run.workers || {})) {
|
|
13302
13582
|
const worker = readJson(
|
|
13303
|
-
|
|
13583
|
+
path59.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
13304
13584
|
void 0
|
|
13305
13585
|
);
|
|
13306
13586
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -13363,12 +13643,12 @@ init_config();
|
|
|
13363
13643
|
init_box_identity();
|
|
13364
13644
|
|
|
13365
13645
|
// src/provider-evidence/exec.ts
|
|
13366
|
-
import { spawnSync as
|
|
13646
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
13367
13647
|
var DEFAULT_CLI_TIMEOUT_MS = 1e4;
|
|
13368
13648
|
var MAX_CLI_BUFFER_BYTES = 4 * 1024 * 1024;
|
|
13369
13649
|
var defaultCliRunner = (cmd, args, opts) => {
|
|
13370
13650
|
try {
|
|
13371
|
-
const result =
|
|
13651
|
+
const result = spawnSync9(cmd, args, {
|
|
13372
13652
|
encoding: "utf8",
|
|
13373
13653
|
stdio: ["ignore", "pipe", "pipe"],
|
|
13374
13654
|
timeout: opts?.timeoutMs ?? DEFAULT_CLI_TIMEOUT_MS,
|
|
@@ -13668,10 +13948,10 @@ function collectProviderEvidence(wanted, opts = {}) {
|
|
|
13668
13948
|
// src/provider-evidence/wanted-store.ts
|
|
13669
13949
|
init_run_store();
|
|
13670
13950
|
init_util();
|
|
13671
|
-
import
|
|
13951
|
+
import path60 from "node:path";
|
|
13672
13952
|
var WANTED_FILE = "provider-evidence-wanted.json";
|
|
13673
13953
|
function wantedFilePath(runId) {
|
|
13674
|
-
return
|
|
13954
|
+
return path60.join(runDirectory(runId), WANTED_FILE);
|
|
13675
13955
|
}
|
|
13676
13956
|
function parseWantedItems(value) {
|
|
13677
13957
|
if (!Array.isArray(value)) return [];
|
|
@@ -13710,7 +13990,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
13710
13990
|
const outcomes = [];
|
|
13711
13991
|
for (const name of Object.keys(run.workers || {})) {
|
|
13712
13992
|
const worker = readJson(
|
|
13713
|
-
|
|
13993
|
+
path61.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
13714
13994
|
void 0
|
|
13715
13995
|
);
|
|
13716
13996
|
if (!worker?.taskId || worker.localOnly) continue;
|
|
@@ -13885,7 +14165,7 @@ import os11 from "node:os";
|
|
|
13885
14165
|
// src/chat/anthropic-credentials.ts
|
|
13886
14166
|
import { readFileSync as readFileSync18 } from "node:fs";
|
|
13887
14167
|
import { homedir as homedir16, platform } from "node:os";
|
|
13888
|
-
import
|
|
14168
|
+
import path62 from "node:path";
|
|
13889
14169
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
13890
14170
|
function parseClaudeCredentials(raw, now = Date.now()) {
|
|
13891
14171
|
try {
|
|
@@ -13913,17 +14193,18 @@ function readClaudeCliToken() {
|
|
|
13913
14193
|
}
|
|
13914
14194
|
}
|
|
13915
14195
|
try {
|
|
13916
|
-
const raw = readFileSync18(
|
|
14196
|
+
const raw = readFileSync18(path62.join(homedir16(), ".claude", ".credentials.json"), "utf8");
|
|
13917
14197
|
return parseClaudeCredentials(raw);
|
|
13918
14198
|
} catch {
|
|
13919
14199
|
return null;
|
|
13920
14200
|
}
|
|
13921
14201
|
}
|
|
13922
|
-
function resolveLocalAnthropicCredentials(env = process.env) {
|
|
14202
|
+
function resolveLocalAnthropicCredentials(env = process.env, opts = {}) {
|
|
13923
14203
|
const apiKey = env.ANTHROPIC_API_KEY?.trim();
|
|
13924
14204
|
if (apiKey) return { kind: "api_key", key: apiKey };
|
|
13925
|
-
const
|
|
13926
|
-
|
|
14205
|
+
const envOptIn = env.KYNVER_CHAT_USE_CLAUDE_OAUTH;
|
|
14206
|
+
const optedIn = opts.oauthOptIn === true || envOptIn === "1" || envOptIn === "true" || envOptIn === "yes";
|
|
14207
|
+
if (optedIn) {
|
|
13927
14208
|
const token = readClaudeCliToken();
|
|
13928
14209
|
if (token) return { kind: "oauth", token };
|
|
13929
14210
|
}
|
|
@@ -14109,20 +14390,43 @@ var CLAIM_WAIT_MS = 25e3;
|
|
|
14109
14390
|
var CLAIM_FETCH_TIMEOUT_MS = 32e3;
|
|
14110
14391
|
var ERROR_BACKOFF_MS = 5e3;
|
|
14111
14392
|
var AUTH_BACKOFF_MS = 6e4;
|
|
14393
|
+
var BRIDGE_REDISCOVER_MS = 5 * 6e4;
|
|
14394
|
+
var DISCOVERY_TIMEOUT_MS = 5e3;
|
|
14112
14395
|
var DEFAULT_CHAT_MODEL = "claude-sonnet-4-6";
|
|
14113
|
-
function
|
|
14114
|
-
const
|
|
14115
|
-
if (
|
|
14396
|
+
async function discoverBridgeUrl(apiBaseUrl, apiKey, env) {
|
|
14397
|
+
const override = env.KYNVER_RUNTIME_CHAT_BRIDGE_URL?.trim();
|
|
14398
|
+
if (override) return override;
|
|
14399
|
+
if (!apiBaseUrl) return null;
|
|
14400
|
+
const controller = new AbortController();
|
|
14401
|
+
const timer = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT_MS);
|
|
14402
|
+
try {
|
|
14403
|
+
const res = await fetch(`${apiBaseUrl.replace(/\/$/, "")}/api/runtime/chat-bridge`, {
|
|
14404
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
14405
|
+
signal: controller.signal
|
|
14406
|
+
});
|
|
14407
|
+
if (!res.ok) return null;
|
|
14408
|
+
const body = await res.json().catch(() => null);
|
|
14409
|
+
return typeof body?.bridgeUrl === "string" && body.bridgeUrl.trim() ? body.bridgeUrl.trim() : null;
|
|
14410
|
+
} catch {
|
|
14411
|
+
return null;
|
|
14412
|
+
} finally {
|
|
14413
|
+
clearTimeout(timer);
|
|
14414
|
+
}
|
|
14415
|
+
}
|
|
14416
|
+
async function resolveChatBridgeTarget(env = process.env) {
|
|
14116
14417
|
const config = loadUserConfig();
|
|
14117
14418
|
const agentOsId = config.agentOsId?.trim();
|
|
14118
14419
|
const apiKey = loadApiKey();
|
|
14119
14420
|
if (!agentOsId || !apiKey) return null;
|
|
14421
|
+
const bridgeUrl = await discoverBridgeUrl(config.apiBaseUrl, apiKey, env);
|
|
14422
|
+
if (!bridgeUrl) return null;
|
|
14120
14423
|
return {
|
|
14121
14424
|
bridgeUrl: bridgeUrl.replace(/\/$/, ""),
|
|
14122
14425
|
agentOsId,
|
|
14123
14426
|
apiKey,
|
|
14124
14427
|
boxId: os11.hostname(),
|
|
14125
|
-
model: env.KYNVER_CHAT_MODEL?.trim() || config.defaultModel?.trim() || DEFAULT_CHAT_MODEL
|
|
14428
|
+
model: env.KYNVER_CHAT_MODEL?.trim() || config.defaultModel?.trim() || DEFAULT_CHAT_MODEL,
|
|
14429
|
+
useClaudeOauth: config.chatUseClaudeOauth === true
|
|
14126
14430
|
};
|
|
14127
14431
|
}
|
|
14128
14432
|
async function claimOnce(target, shouldStop) {
|
|
@@ -14183,7 +14487,7 @@ async function executeChatTurn(target, turn) {
|
|
|
14183
14487
|
});
|
|
14184
14488
|
};
|
|
14185
14489
|
const batcher = new DeltaBatcher(sendBatch);
|
|
14186
|
-
const creds = resolveLocalAnthropicCredentials();
|
|
14490
|
+
const creds = resolveLocalAnthropicCredentials(process.env, { oauthOptIn: target.useClaudeOauth });
|
|
14187
14491
|
if (!creds) {
|
|
14188
14492
|
await pushEvents(target, turn.turnId, [
|
|
14189
14493
|
{ seq: 0, kind: "error", error: "no_local_credentials" }
|
|
@@ -14228,15 +14532,31 @@ async function sleep2(ms, shouldStop) {
|
|
|
14228
14532
|
}
|
|
14229
14533
|
}
|
|
14230
14534
|
async function runChatClaimLoop(opts) {
|
|
14231
|
-
|
|
14232
|
-
if (!target)
|
|
14535
|
+
let target = await resolveChatBridgeTarget();
|
|
14536
|
+
if (!target) {
|
|
14537
|
+
console.error(
|
|
14538
|
+
JSON.stringify({
|
|
14539
|
+
event: "chat_claim_loop_waiting",
|
|
14540
|
+
reason: !loadApiKey() || !loadUserConfig().agentOsId?.trim() ? "machine_not_linked" : "bridge_not_discovered (rollout off, discovery route missing/404, or API unreachable)",
|
|
14541
|
+
rediscoverMs: BRIDGE_REDISCOVER_MS
|
|
14542
|
+
})
|
|
14543
|
+
);
|
|
14544
|
+
}
|
|
14545
|
+
while (!target && !opts.shouldStop()) {
|
|
14546
|
+
if (!loadApiKey() || !loadUserConfig().agentOsId?.trim()) return;
|
|
14547
|
+
await sleep2(BRIDGE_REDISCOVER_MS, opts.shouldStop);
|
|
14548
|
+
if (opts.shouldStop()) return;
|
|
14549
|
+
target = await resolveChatBridgeTarget();
|
|
14550
|
+
}
|
|
14551
|
+
if (!target || opts.shouldStop()) return;
|
|
14233
14552
|
console.error(
|
|
14234
14553
|
JSON.stringify({
|
|
14235
14554
|
event: "chat_claim_loop_start",
|
|
14236
14555
|
bridgeUrl: target.bridgeUrl,
|
|
14237
14556
|
agentOsId: target.agentOsId,
|
|
14238
14557
|
boxId: target.boxId,
|
|
14239
|
-
model: target.model
|
|
14558
|
+
model: target.model,
|
|
14559
|
+
useClaudeOauth: target.useClaudeOauth
|
|
14240
14560
|
})
|
|
14241
14561
|
);
|
|
14242
14562
|
while (!opts.shouldStop()) {
|
|
@@ -14321,9 +14641,33 @@ async function runDaemon(args) {
|
|
|
14321
14641
|
})
|
|
14322
14642
|
);
|
|
14323
14643
|
});
|
|
14644
|
+
let credentialMissingLogged = false;
|
|
14324
14645
|
while (!stopping) {
|
|
14325
14646
|
try {
|
|
14326
14647
|
writeDaemonHeartbeat({ agentOsId, runId });
|
|
14648
|
+
const credential = await tryResolveCallbackSecretWithMint(
|
|
14649
|
+
args.secret ? String(args.secret) : void 0,
|
|
14650
|
+
agentOsId
|
|
14651
|
+
);
|
|
14652
|
+
if (!credential.ok) {
|
|
14653
|
+
if (!credentialMissingLogged) {
|
|
14654
|
+
credentialMissingLogged = true;
|
|
14655
|
+
console.error(
|
|
14656
|
+
JSON.stringify({
|
|
14657
|
+
event: "daemon_runner_credential_missing",
|
|
14658
|
+
agentOsId,
|
|
14659
|
+
reason: credential.reason,
|
|
14660
|
+
remedy: `run \`kynver runner credential --agent-os-id ${agentOsId}\` (or \`kynver bootstrap\`); ticks resume automatically once the credential exists`
|
|
14661
|
+
})
|
|
14662
|
+
);
|
|
14663
|
+
}
|
|
14664
|
+
await awaitDaemonBackoff(intervalMs, () => stopping);
|
|
14665
|
+
continue;
|
|
14666
|
+
}
|
|
14667
|
+
if (credentialMissingLogged) {
|
|
14668
|
+
credentialMissingLogged = false;
|
|
14669
|
+
console.error(JSON.stringify({ event: "daemon_runner_credential_recovered", agentOsId }));
|
|
14670
|
+
}
|
|
14327
14671
|
if (cronEnv.tickEnabled) {
|
|
14328
14672
|
const cronTick = await runKynverCronTick({
|
|
14329
14673
|
env: cronEnv,
|
|
@@ -14351,108 +14695,6 @@ async function runDaemon(args) {
|
|
|
14351
14695
|
console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
|
|
14352
14696
|
}
|
|
14353
14697
|
|
|
14354
|
-
// src/start.ts
|
|
14355
|
-
init_run_store();
|
|
14356
|
-
function resolveStartRunId(runs, repo) {
|
|
14357
|
-
const candidates = runs.filter((r) => r.repo === repo && !TERMINAL_RUN_STATUSES.has(r.status)).sort((a, b) => (b.createdAt ?? "").localeCompare(a.createdAt ?? ""));
|
|
14358
|
-
return candidates[0]?.id ?? null;
|
|
14359
|
-
}
|
|
14360
|
-
async function runStart(args) {
|
|
14361
|
-
assertNativeDaemonAllowed();
|
|
14362
|
-
let config = loadUserConfig();
|
|
14363
|
-
if (!loadApiKey() || !config.agentOsId?.trim()) {
|
|
14364
|
-
console.log(" This machine isn't linked yet \u2014 running bootstrap first.");
|
|
14365
|
-
await runBootstrap(args);
|
|
14366
|
-
config = loadUserConfig();
|
|
14367
|
-
}
|
|
14368
|
-
const agentOsId = (typeof args.agentOsId === "string" ? args.agentOsId.trim() : "") || config.agentOsId?.trim() || "";
|
|
14369
|
-
if (!agentOsId) {
|
|
14370
|
-
console.error("No AgentOS workspace configured \u2014 run `kynver bootstrap` (or pass --agent-os-id).");
|
|
14371
|
-
process.exit(1);
|
|
14372
|
-
}
|
|
14373
|
-
const repo = (typeof args.repo === "string" ? args.repo.trim() : "") || config.defaultRepo?.trim() || resolveDefaultRepo()?.repo || "";
|
|
14374
|
-
if (!repo) {
|
|
14375
|
-
console.error("No repo configured \u2014 pass --repo /path/to/repo or run `kynver setup --discover-repo`.");
|
|
14376
|
-
process.exit(1);
|
|
14377
|
-
}
|
|
14378
|
-
let runId = typeof args.run === "string" && args.run.trim() ? args.run.trim() : "";
|
|
14379
|
-
if (!runId) {
|
|
14380
|
-
runId = resolveStartRunId(listRunRecords(), repo) ?? "";
|
|
14381
|
-
if (runId) {
|
|
14382
|
-
console.log(` Reusing run ${runId} for ${repo}.`);
|
|
14383
|
-
} else {
|
|
14384
|
-
runId = createRun({ ...args, repo, name: "agent" }).runId;
|
|
14385
|
-
}
|
|
14386
|
-
}
|
|
14387
|
-
console.log("");
|
|
14388
|
-
console.log(` ${os12.hostname()} \u2014 agent coming online`);
|
|
14389
|
-
console.log(` workspace: ${agentOsId}`);
|
|
14390
|
-
console.log(` repo: ${repo}`);
|
|
14391
|
-
console.log(` run: ${runId}`);
|
|
14392
|
-
console.log(" Ctrl-C stops the agent. (Advanced control: `kynver daemon --help`.)");
|
|
14393
|
-
console.log("");
|
|
14394
|
-
await runDaemon({ ...args, run: runId, agentOsId });
|
|
14395
|
-
}
|
|
14396
|
-
|
|
14397
|
-
// src/cli.ts
|
|
14398
|
-
init_run_store();
|
|
14399
|
-
|
|
14400
|
-
// src/discard-disposable.ts
|
|
14401
|
-
init_run_store();
|
|
14402
|
-
init_status();
|
|
14403
|
-
import { existsSync as existsSync43, rmSync as rmSync4 } from "node:fs";
|
|
14404
|
-
import path61 from "node:path";
|
|
14405
|
-
function normalizeRelativePath2(value) {
|
|
14406
|
-
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
14407
|
-
if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
|
|
14408
|
-
throw new Error(`unsafe path: ${value}`);
|
|
14409
|
-
}
|
|
14410
|
-
return normalized;
|
|
14411
|
-
}
|
|
14412
|
-
function parsePathsArg(raw) {
|
|
14413
|
-
if (typeof raw !== "string" || !raw.trim()) return [];
|
|
14414
|
-
return raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
14415
|
-
}
|
|
14416
|
-
function discardDisposableArtifacts(args) {
|
|
14417
|
-
const { runId, workerName } = resolveWorkerTargetArgs(args);
|
|
14418
|
-
const worker = loadWorker(runId, workerName);
|
|
14419
|
-
const paths = [
|
|
14420
|
-
...parsePathsArg(args.path),
|
|
14421
|
-
...Array.isArray(args.paths) ? args.paths : []
|
|
14422
|
-
];
|
|
14423
|
-
if (paths.length === 0) {
|
|
14424
|
-
return { ok: false, removed: [], reason: "requires at least one --path" };
|
|
14425
|
-
}
|
|
14426
|
-
const worktreeRoot = path61.resolve(worker.worktreePath);
|
|
14427
|
-
const removed = [];
|
|
14428
|
-
for (const raw of paths) {
|
|
14429
|
-
const rel = normalizeRelativePath2(raw);
|
|
14430
|
-
const abs = path61.resolve(worktreeRoot, rel);
|
|
14431
|
-
if (!abs.startsWith(worktreeRoot + path61.sep) && abs !== worktreeRoot) {
|
|
14432
|
-
return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
|
|
14433
|
-
}
|
|
14434
|
-
if (!existsSync43(abs)) {
|
|
14435
|
-
return { ok: false, removed, reason: `path not found: ${raw}` };
|
|
14436
|
-
}
|
|
14437
|
-
rmSync4(abs, { recursive: true, force: true });
|
|
14438
|
-
removed.push(rel);
|
|
14439
|
-
}
|
|
14440
|
-
const prior = Array.isArray(worker.disposableArtifactsRemoved) ? worker.disposableArtifactsRemoved.filter((p) => typeof p === "string") : [];
|
|
14441
|
-
worker.disposableArtifactsRemoved = [.../* @__PURE__ */ new Set([...prior, ...removed])];
|
|
14442
|
-
saveWorker(worker.runId, worker);
|
|
14443
|
-
const status = computeWorkerStatus(worker);
|
|
14444
|
-
return {
|
|
14445
|
-
ok: true,
|
|
14446
|
-
removed,
|
|
14447
|
-
...status.changedFiles.length ? { reason: "worktree still has other changes" } : {}
|
|
14448
|
-
};
|
|
14449
|
-
}
|
|
14450
|
-
function discardDisposableCli(args) {
|
|
14451
|
-
const result = discardDisposableArtifacts(args);
|
|
14452
|
-
console.log(JSON.stringify(result, null, 2));
|
|
14453
|
-
if (!result.ok) process.exit(1);
|
|
14454
|
-
}
|
|
14455
|
-
|
|
14456
14698
|
// src/daemon-keeper.ts
|
|
14457
14699
|
init_config();
|
|
14458
14700
|
import { spawn as spawn6 } from "node:child_process";
|
|
@@ -14584,9 +14826,134 @@ async function runDaemonKeeper(args, rawArgv = process.argv.slice(2)) {
|
|
|
14584
14826
|
keeperLog("stopped", { agentOsId });
|
|
14585
14827
|
}
|
|
14586
14828
|
|
|
14829
|
+
// src/start.ts
|
|
14830
|
+
init_run_store();
|
|
14831
|
+
function resolveStartRunId(runs, repo) {
|
|
14832
|
+
const candidates = runs.filter((r) => r.repo === repo && !TERMINAL_RUN_STATUSES.has(r.status)).sort((a, b) => (b.createdAt ?? "").localeCompare(a.createdAt ?? ""));
|
|
14833
|
+
return candidates[0]?.id ?? null;
|
|
14834
|
+
}
|
|
14835
|
+
async function runStart(args) {
|
|
14836
|
+
assertNativeDaemonAllowed();
|
|
14837
|
+
let config = loadUserConfig();
|
|
14838
|
+
if (!loadApiKey() || !config.agentOsId?.trim()) {
|
|
14839
|
+
console.log(" This machine isn't linked yet \u2014 running bootstrap first.");
|
|
14840
|
+
await runBootstrap(args);
|
|
14841
|
+
config = loadUserConfig();
|
|
14842
|
+
}
|
|
14843
|
+
const agentOsId = (typeof args.agentOsId === "string" ? args.agentOsId.trim() : "") || config.agentOsId?.trim() || "";
|
|
14844
|
+
if (!agentOsId) {
|
|
14845
|
+
console.error("No AgentOS workspace configured \u2014 run `kynver bootstrap` (or pass --agent-os-id).");
|
|
14846
|
+
process.exit(1);
|
|
14847
|
+
}
|
|
14848
|
+
if (args.chatOauth === true && config.chatUseClaudeOauth !== true) {
|
|
14849
|
+
saveUserConfig({ ...config, chatUseClaudeOauth: true });
|
|
14850
|
+
config = loadUserConfig();
|
|
14851
|
+
console.log(" Chat: Claude Code OAuth opt-in saved (delegated turns may use your local subscription).");
|
|
14852
|
+
}
|
|
14853
|
+
const repo = (typeof args.repo === "string" ? args.repo.trim() : "") || config.defaultRepo?.trim() || resolveDefaultRepo()?.repo || "";
|
|
14854
|
+
if (!repo) {
|
|
14855
|
+
console.error("No repo configured \u2014 pass --repo /path/to/repo or run `kynver setup --discover-repo`.");
|
|
14856
|
+
process.exit(1);
|
|
14857
|
+
}
|
|
14858
|
+
let runId = typeof args.run === "string" && args.run.trim() ? args.run.trim() : "";
|
|
14859
|
+
if (!runId) {
|
|
14860
|
+
runId = resolveStartRunId(listRunRecords(), repo) ?? "";
|
|
14861
|
+
if (runId) {
|
|
14862
|
+
console.log(` Reusing run ${runId} for ${repo}.`);
|
|
14863
|
+
} else {
|
|
14864
|
+
runId = createRun({ ...args, repo, name: "agent" }).runId;
|
|
14865
|
+
}
|
|
14866
|
+
}
|
|
14867
|
+
console.log("");
|
|
14868
|
+
console.log(` ${os12.hostname()} \u2014 agent coming online`);
|
|
14869
|
+
console.log(` workspace: ${agentOsId}`);
|
|
14870
|
+
console.log(` repo: ${repo}`);
|
|
14871
|
+
console.log(` run: ${runId}`);
|
|
14872
|
+
console.log(" Ctrl-C stops the agent. (Advanced control: `kynver daemon --help`.)");
|
|
14873
|
+
console.log("");
|
|
14874
|
+
const daemonArgs = { ...args, run: runId, agentOsId };
|
|
14875
|
+
if (shouldRunDaemonKeeper(daemonArgs)) {
|
|
14876
|
+
await runDaemonKeeper(daemonArgs, buildStartDaemonArgv(runId, agentOsId, args));
|
|
14877
|
+
return;
|
|
14878
|
+
}
|
|
14879
|
+
await runDaemon(daemonArgs);
|
|
14880
|
+
}
|
|
14881
|
+
function buildStartDaemonArgv(runId, agentOsId, args) {
|
|
14882
|
+
const argv = ["daemon", "--run", runId, "--agent-os-id", agentOsId];
|
|
14883
|
+
if (typeof args.intervalMs === "string" && args.intervalMs.trim()) {
|
|
14884
|
+
argv.push("--interval-ms", args.intervalMs.trim());
|
|
14885
|
+
}
|
|
14886
|
+
if (args.execute === false || args.execute === "false") {
|
|
14887
|
+
argv.push("--execute", "false");
|
|
14888
|
+
}
|
|
14889
|
+
if (typeof args.stallMs === "string" && args.stallMs.trim()) {
|
|
14890
|
+
argv.push("--stall-ms", args.stallMs.trim());
|
|
14891
|
+
}
|
|
14892
|
+
return argv;
|
|
14893
|
+
}
|
|
14894
|
+
|
|
14895
|
+
// src/cli.ts
|
|
14896
|
+
init_run_store();
|
|
14897
|
+
|
|
14898
|
+
// src/discard-disposable.ts
|
|
14899
|
+
init_run_store();
|
|
14900
|
+
init_status();
|
|
14901
|
+
import { existsSync as existsSync44, rmSync as rmSync4 } from "node:fs";
|
|
14902
|
+
import path63 from "node:path";
|
|
14903
|
+
function normalizeRelativePath2(value) {
|
|
14904
|
+
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
14905
|
+
if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
|
|
14906
|
+
throw new Error(`unsafe path: ${value}`);
|
|
14907
|
+
}
|
|
14908
|
+
return normalized;
|
|
14909
|
+
}
|
|
14910
|
+
function parsePathsArg(raw) {
|
|
14911
|
+
if (typeof raw !== "string" || !raw.trim()) return [];
|
|
14912
|
+
return raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
14913
|
+
}
|
|
14914
|
+
function discardDisposableArtifacts(args) {
|
|
14915
|
+
const { runId, workerName } = resolveWorkerTargetArgs(args);
|
|
14916
|
+
const worker = loadWorker(runId, workerName);
|
|
14917
|
+
const paths = [
|
|
14918
|
+
...parsePathsArg(args.path),
|
|
14919
|
+
...Array.isArray(args.paths) ? args.paths : []
|
|
14920
|
+
];
|
|
14921
|
+
if (paths.length === 0) {
|
|
14922
|
+
return { ok: false, removed: [], reason: "requires at least one --path" };
|
|
14923
|
+
}
|
|
14924
|
+
const worktreeRoot = path63.resolve(worker.worktreePath);
|
|
14925
|
+
const removed = [];
|
|
14926
|
+
for (const raw of paths) {
|
|
14927
|
+
const rel = normalizeRelativePath2(raw);
|
|
14928
|
+
const abs = path63.resolve(worktreeRoot, rel);
|
|
14929
|
+
if (!abs.startsWith(worktreeRoot + path63.sep) && abs !== worktreeRoot) {
|
|
14930
|
+
return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
|
|
14931
|
+
}
|
|
14932
|
+
if (!existsSync44(abs)) {
|
|
14933
|
+
return { ok: false, removed, reason: `path not found: ${raw}` };
|
|
14934
|
+
}
|
|
14935
|
+
rmSync4(abs, { recursive: true, force: true });
|
|
14936
|
+
removed.push(rel);
|
|
14937
|
+
}
|
|
14938
|
+
const prior = Array.isArray(worker.disposableArtifactsRemoved) ? worker.disposableArtifactsRemoved.filter((p) => typeof p === "string") : [];
|
|
14939
|
+
worker.disposableArtifactsRemoved = [.../* @__PURE__ */ new Set([...prior, ...removed])];
|
|
14940
|
+
saveWorker(worker.runId, worker);
|
|
14941
|
+
const status = computeWorkerStatus(worker);
|
|
14942
|
+
return {
|
|
14943
|
+
ok: true,
|
|
14944
|
+
removed,
|
|
14945
|
+
...status.changedFiles.length ? { reason: "worktree still has other changes" } : {}
|
|
14946
|
+
};
|
|
14947
|
+
}
|
|
14948
|
+
function discardDisposableCli(args) {
|
|
14949
|
+
const result = discardDisposableArtifacts(args);
|
|
14950
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14951
|
+
if (!result.ok) process.exit(1);
|
|
14952
|
+
}
|
|
14953
|
+
|
|
14587
14954
|
// src/plan-progress.ts
|
|
14588
14955
|
init_config();
|
|
14589
|
-
import
|
|
14956
|
+
import path67 from "node:path";
|
|
14590
14957
|
|
|
14591
14958
|
// src/bounded-build/constants.ts
|
|
14592
14959
|
var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
|
|
@@ -14627,7 +14994,7 @@ function formatNodeOptionsFlag(mb = resolveNodeOldSpaceSizeMb()) {
|
|
|
14627
14994
|
}
|
|
14628
14995
|
|
|
14629
14996
|
// src/bounded-build/systemd-wrap.ts
|
|
14630
|
-
import { spawnSync as
|
|
14997
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
14631
14998
|
var systemdAvailableCache;
|
|
14632
14999
|
function isSystemdRunAvailable() {
|
|
14633
15000
|
if (process.env.KYNVER_BUILD_SKIP_SYSTEMD === "1" || process.env.KYNVER_BUILD_SKIP_SYSTEMD === "true") {
|
|
@@ -14638,7 +15005,7 @@ function isSystemdRunAvailable() {
|
|
|
14638
15005
|
systemdAvailableCache = false;
|
|
14639
15006
|
return false;
|
|
14640
15007
|
}
|
|
14641
|
-
const res =
|
|
15008
|
+
const res = spawnSync10("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
|
|
14642
15009
|
systemdAvailableCache = res.status === 0;
|
|
14643
15010
|
return systemdAvailableCache;
|
|
14644
15011
|
}
|
|
@@ -14663,7 +15030,7 @@ function buildSystemdRunArgv(opts) {
|
|
|
14663
15030
|
|
|
14664
15031
|
// src/bounded-build/admission.ts
|
|
14665
15032
|
init_config();
|
|
14666
|
-
import { spawnSync as
|
|
15033
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
14667
15034
|
init_meminfo();
|
|
14668
15035
|
function positiveInt4(value, fallback) {
|
|
14669
15036
|
const n = Number(value);
|
|
@@ -14700,7 +15067,7 @@ function assessBuildAdmission(opts = {}) {
|
|
|
14700
15067
|
}
|
|
14701
15068
|
function sleepMs2(ms) {
|
|
14702
15069
|
if (ms <= 0) return;
|
|
14703
|
-
|
|
15070
|
+
spawnSync11(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
14704
15071
|
stdio: "ignore"
|
|
14705
15072
|
});
|
|
14706
15073
|
}
|
|
@@ -14721,34 +15088,34 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
|
|
|
14721
15088
|
}
|
|
14722
15089
|
|
|
14723
15090
|
// src/bounded-build/exec.ts
|
|
14724
|
-
import { spawnSync as
|
|
15091
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
14725
15092
|
|
|
14726
15093
|
// src/heavy-verification/slot.ts
|
|
14727
15094
|
init_util();
|
|
14728
15095
|
import {
|
|
14729
15096
|
closeSync as closeSync7,
|
|
14730
|
-
existsSync as
|
|
14731
|
-
mkdirSync as
|
|
15097
|
+
existsSync as existsSync45,
|
|
15098
|
+
mkdirSync as mkdirSync11,
|
|
14732
15099
|
openSync as openSync7,
|
|
14733
|
-
readdirSync as
|
|
15100
|
+
readdirSync as readdirSync14,
|
|
14734
15101
|
readFileSync as readFileSync19,
|
|
14735
15102
|
unlinkSync as unlinkSync4,
|
|
14736
|
-
writeFileSync as
|
|
15103
|
+
writeFileSync as writeFileSync7
|
|
14737
15104
|
} from "node:fs";
|
|
14738
|
-
import
|
|
15105
|
+
import path65 from "node:path";
|
|
14739
15106
|
|
|
14740
15107
|
// src/heavy-verification/paths.ts
|
|
14741
|
-
import { mkdirSync as
|
|
14742
|
-
import
|
|
15108
|
+
import { mkdirSync as mkdirSync10 } from "node:fs";
|
|
15109
|
+
import path64 from "node:path";
|
|
14743
15110
|
function resolveHeavyVerificationRoot() {
|
|
14744
|
-
return
|
|
15111
|
+
return path64.join(resolveKynverStateRoot(), "heavy-verification");
|
|
14745
15112
|
}
|
|
14746
15113
|
function heavyVerificationSlotsDir() {
|
|
14747
|
-
return
|
|
15114
|
+
return path64.join(resolveHeavyVerificationRoot(), "slots");
|
|
14748
15115
|
}
|
|
14749
15116
|
function ensureHeavyVerificationDirs() {
|
|
14750
15117
|
const dir = heavyVerificationSlotsDir();
|
|
14751
|
-
|
|
15118
|
+
mkdirSync10(dir, { recursive: true });
|
|
14752
15119
|
return dir;
|
|
14753
15120
|
}
|
|
14754
15121
|
|
|
@@ -14773,10 +15140,10 @@ function indexedSlotId(index) {
|
|
|
14773
15140
|
return `slot-${index}`;
|
|
14774
15141
|
}
|
|
14775
15142
|
function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
|
|
14776
|
-
return
|
|
15143
|
+
return path65.join(slotsDir, `${slotId}.json`);
|
|
14777
15144
|
}
|
|
14778
15145
|
function readSlotRecord(filePath) {
|
|
14779
|
-
if (!
|
|
15146
|
+
if (!existsSync45(filePath)) return null;
|
|
14780
15147
|
try {
|
|
14781
15148
|
const parsed = JSON.parse(readFileSync19(filePath, "utf8"));
|
|
14782
15149
|
if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
|
|
@@ -14803,19 +15170,19 @@ function reclaimStaleSlot(filePath, staleMs) {
|
|
|
14803
15170
|
}
|
|
14804
15171
|
}
|
|
14805
15172
|
function ensureSlotsDir(slotsDir) {
|
|
14806
|
-
|
|
15173
|
+
mkdirSync11(slotsDir, { recursive: true });
|
|
14807
15174
|
return slotsDir;
|
|
14808
15175
|
}
|
|
14809
15176
|
function reclaimStaleHeavyVerificationSlots(opts = {}) {
|
|
14810
15177
|
const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
|
|
14811
15178
|
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
14812
15179
|
let reclaimed = 0;
|
|
14813
|
-
for (const name of
|
|
15180
|
+
for (const name of readdirSync14(slotsDir)) {
|
|
14814
15181
|
if (!name.endsWith(".json")) continue;
|
|
14815
|
-
const filePath =
|
|
14816
|
-
const before =
|
|
15182
|
+
const filePath = path65.join(slotsDir, name);
|
|
15183
|
+
const before = existsSync45(filePath);
|
|
14817
15184
|
reclaimStaleSlot(filePath, staleMs);
|
|
14818
|
-
if (before && !
|
|
15185
|
+
if (before && !existsSync45(filePath)) reclaimed += 1;
|
|
14819
15186
|
}
|
|
14820
15187
|
return reclaimed;
|
|
14821
15188
|
}
|
|
@@ -14824,9 +15191,9 @@ function listActiveHeavyVerificationSlots(opts = {}) {
|
|
|
14824
15191
|
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
14825
15192
|
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
14826
15193
|
const active = [];
|
|
14827
|
-
for (const name of
|
|
15194
|
+
for (const name of readdirSync14(slotsDir)) {
|
|
14828
15195
|
if (!name.endsWith(".json")) continue;
|
|
14829
|
-
const record3 = readSlotRecord(
|
|
15196
|
+
const record3 = readSlotRecord(path65.join(slotsDir, name));
|
|
14830
15197
|
if (record3 && !slotIsStale(record3, staleMs)) active.push(record3);
|
|
14831
15198
|
}
|
|
14832
15199
|
return active;
|
|
@@ -14868,7 +15235,7 @@ function tryAcquireHeavyVerificationSlot(command, opts = {}) {
|
|
|
14868
15235
|
};
|
|
14869
15236
|
try {
|
|
14870
15237
|
const fd = openSync7(filePath, "wx");
|
|
14871
|
-
|
|
15238
|
+
writeFileSync7(fd, JSON.stringify(record3, null, 2), "utf8");
|
|
14872
15239
|
closeSync7(fd);
|
|
14873
15240
|
const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
14874
15241
|
return {
|
|
@@ -14928,10 +15295,10 @@ function assessHeavyVerificationGate(command, opts = {}) {
|
|
|
14928
15295
|
}
|
|
14929
15296
|
|
|
14930
15297
|
// src/heavy-verification/gate.ts
|
|
14931
|
-
import { spawnSync as
|
|
15298
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
14932
15299
|
function sleepMs3(ms) {
|
|
14933
15300
|
if (ms <= 0) return;
|
|
14934
|
-
|
|
15301
|
+
spawnSync12(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
14935
15302
|
stdio: "ignore"
|
|
14936
15303
|
});
|
|
14937
15304
|
}
|
|
@@ -14947,11 +15314,11 @@ function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {
|
|
|
14947
15314
|
|
|
14948
15315
|
// src/harness-worktree-build-guard.ts
|
|
14949
15316
|
init_paths();
|
|
14950
|
-
import
|
|
15317
|
+
import path66 from "node:path";
|
|
14951
15318
|
function isPathUnderHarnessWorktree(cwd) {
|
|
14952
15319
|
const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
|
|
14953
|
-
const rel =
|
|
14954
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
15320
|
+
const rel = path66.relative(worktreesDir, path66.resolve(cwd));
|
|
15321
|
+
return rel.length > 0 && !rel.startsWith("..") && !path66.isAbsolute(rel);
|
|
14955
15322
|
}
|
|
14956
15323
|
function assessHarnessWorktreeBuildGuard(cwd) {
|
|
14957
15324
|
if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
|
|
@@ -14975,7 +15342,7 @@ function envArgv(env) {
|
|
|
14975
15342
|
return out;
|
|
14976
15343
|
}
|
|
14977
15344
|
function runSpawn(argv, opts) {
|
|
14978
|
-
const res =
|
|
15345
|
+
const res = spawnSync13(argv[0], argv.slice(1), {
|
|
14979
15346
|
cwd: opts.cwd,
|
|
14980
15347
|
env: opts.env,
|
|
14981
15348
|
encoding: "utf8",
|
|
@@ -15164,7 +15531,7 @@ async function emitPlanProgress(args) {
|
|
|
15164
15531
|
}
|
|
15165
15532
|
function verifyPlanLocal(args) {
|
|
15166
15533
|
const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
|
|
15167
|
-
const cwd =
|
|
15534
|
+
const cwd = path67.resolve(worktree);
|
|
15168
15535
|
const summary = runHarnessVerifyCommands(cwd);
|
|
15169
15536
|
const emitJson = args.json === true || args.json === "true";
|
|
15170
15537
|
const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
|
|
@@ -15213,10 +15580,10 @@ async function verifyPlan(args) {
|
|
|
15213
15580
|
}
|
|
15214
15581
|
|
|
15215
15582
|
// src/harness-verify-cli.ts
|
|
15216
|
-
import
|
|
15583
|
+
import path68 from "node:path";
|
|
15217
15584
|
init_util();
|
|
15218
15585
|
function runHarnessVerifyCli(args) {
|
|
15219
|
-
const cwd =
|
|
15586
|
+
const cwd = path68.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
|
|
15220
15587
|
const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
|
|
15221
15588
|
const commands = [];
|
|
15222
15589
|
const rawCmd = args.command;
|
|
@@ -15348,7 +15715,9 @@ function runCleanupCli(args) {
|
|
|
15348
15715
|
const skipFinalize = args.skipFinalize === true || args.skipFinalize === "true";
|
|
15349
15716
|
const accountBytes = args.accountBytes === true || args.accountBytes === "true";
|
|
15350
15717
|
const compact = args.compact === true || args.compact === "true";
|
|
15351
|
-
const
|
|
15718
|
+
const nodeModulesAgeMsRaw = args.nodeModulesAgeMs;
|
|
15719
|
+
const scanDependencyCaches = nodeModulesAgeMsRaw != null && nodeModulesAgeMsRaw !== "";
|
|
15720
|
+
const nodeModulesAgeMs = scanDependencyCaches ? Number(nodeModulesAgeMsRaw) : void 0;
|
|
15352
15721
|
const worktreesAgeMs = args.worktreesAgeMs ? Number(args.worktreesAgeMs) : 0;
|
|
15353
15722
|
const includeOrphans = args.includeOrphans === true || args.includeOrphans === "true";
|
|
15354
15723
|
const harnessRoot = args.harnessRoot ? String(args.harnessRoot) : void 0;
|
|
@@ -15356,7 +15725,8 @@ function runCleanupCli(args) {
|
|
|
15356
15725
|
execute,
|
|
15357
15726
|
finalizeStaleRuns: !skipFinalize,
|
|
15358
15727
|
accountBytes,
|
|
15359
|
-
|
|
15728
|
+
scanDependencyCaches,
|
|
15729
|
+
nodeModulesAgeMs: nodeModulesAgeMs !== void 0 && Number.isFinite(nodeModulesAgeMs) ? nodeModulesAgeMs : scanDependencyCaches ? DEFAULT_NODE_MODULES_AGE_MS : void 0,
|
|
15360
15730
|
worktreesAgeMs: Number.isFinite(worktreesAgeMs) ? worktreesAgeMs : 0,
|
|
15361
15731
|
includeOrphans,
|
|
15362
15732
|
harnessRoot
|
|
@@ -15655,7 +16025,7 @@ ${text.slice(0, 800)}`,
|
|
|
15655
16025
|
}
|
|
15656
16026
|
|
|
15657
16027
|
// src/monitor/monitor.service.ts
|
|
15658
|
-
import
|
|
16028
|
+
import path70 from "node:path";
|
|
15659
16029
|
init_run_store();
|
|
15660
16030
|
init_status();
|
|
15661
16031
|
init_util();
|
|
@@ -15714,19 +16084,19 @@ function classifyWorkerHealth(input) {
|
|
|
15714
16084
|
// src/monitor/monitor.store.ts
|
|
15715
16085
|
init_paths();
|
|
15716
16086
|
init_util();
|
|
15717
|
-
import { existsSync as
|
|
15718
|
-
import
|
|
16087
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync12, readdirSync as readdirSync15, unlinkSync as unlinkSync5 } from "node:fs";
|
|
16088
|
+
import path69 from "node:path";
|
|
15719
16089
|
function monitorsDir() {
|
|
15720
16090
|
const { harnessRoot } = getHarnessPaths();
|
|
15721
|
-
const dir =
|
|
15722
|
-
|
|
16091
|
+
const dir = path69.join(harnessRoot, "monitors");
|
|
16092
|
+
mkdirSync12(dir, { recursive: true });
|
|
15723
16093
|
return dir;
|
|
15724
16094
|
}
|
|
15725
16095
|
function monitorIdFor(runId, workerName) {
|
|
15726
16096
|
return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
|
|
15727
16097
|
}
|
|
15728
16098
|
function monitorPath(monitorId) {
|
|
15729
|
-
return
|
|
16099
|
+
return path69.join(monitorsDir(), `${monitorId}.json`);
|
|
15730
16100
|
}
|
|
15731
16101
|
function loadMonitorSession(monitorId) {
|
|
15732
16102
|
return readJson(monitorPath(monitorId), void 0);
|
|
@@ -15736,18 +16106,18 @@ function saveMonitorSession(session) {
|
|
|
15736
16106
|
}
|
|
15737
16107
|
function deleteMonitorSession(monitorId) {
|
|
15738
16108
|
const file = monitorPath(monitorId);
|
|
15739
|
-
if (!
|
|
16109
|
+
if (!existsSync46(file)) return false;
|
|
15740
16110
|
unlinkSync5(file);
|
|
15741
16111
|
return true;
|
|
15742
16112
|
}
|
|
15743
16113
|
function listMonitorSessions() {
|
|
15744
16114
|
const dir = monitorsDir();
|
|
15745
|
-
if (!
|
|
16115
|
+
if (!existsSync46(dir)) return [];
|
|
15746
16116
|
const entries = [];
|
|
15747
|
-
for (const name of
|
|
16117
|
+
for (const name of readdirSync15(dir)) {
|
|
15748
16118
|
if (!name.endsWith(".json")) continue;
|
|
15749
16119
|
const session = readJson(
|
|
15750
|
-
|
|
16120
|
+
path69.join(dir, name),
|
|
15751
16121
|
void 0
|
|
15752
16122
|
);
|
|
15753
16123
|
if (!session?.monitorId) continue;
|
|
@@ -15840,7 +16210,7 @@ async function fetchTaskLeasesForWorkers(input) {
|
|
|
15840
16210
|
// src/monitor/monitor.service.ts
|
|
15841
16211
|
function workerRecord2(runId, name) {
|
|
15842
16212
|
return readJson(
|
|
15843
|
-
|
|
16213
|
+
path70.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
|
|
15844
16214
|
void 0
|
|
15845
16215
|
);
|
|
15846
16216
|
}
|
|
@@ -16049,18 +16419,18 @@ async function runMonitorLoop(args) {
|
|
|
16049
16419
|
init_util();
|
|
16050
16420
|
init_paths();
|
|
16051
16421
|
import { spawn as spawn7 } from "node:child_process";
|
|
16052
|
-
import { closeSync as closeSync8, existsSync as
|
|
16053
|
-
import
|
|
16422
|
+
import { closeSync as closeSync8, existsSync as existsSync47, openSync as openSync8 } from "node:fs";
|
|
16423
|
+
import path71 from "node:path";
|
|
16054
16424
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
16055
16425
|
function resolveDefaultCliPath2() {
|
|
16056
|
-
return
|
|
16426
|
+
return path71.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
|
|
16057
16427
|
}
|
|
16058
16428
|
function spawnMonitorSidecar(opts) {
|
|
16059
16429
|
const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
|
|
16060
|
-
if (!
|
|
16430
|
+
if (!existsSync47(cliPath)) return void 0;
|
|
16061
16431
|
const monitorId = monitorIdFor(opts.runId, opts.workerName);
|
|
16062
16432
|
const { harnessRoot } = getHarnessPaths();
|
|
16063
|
-
const logPath =
|
|
16433
|
+
const logPath = path71.join(harnessRoot, "monitors", `${monitorId}.log`);
|
|
16064
16434
|
let logFd;
|
|
16065
16435
|
try {
|
|
16066
16436
|
logFd = openSync8(logPath, "a");
|
|
@@ -16185,7 +16555,7 @@ init_run_store();
|
|
|
16185
16555
|
init_status();
|
|
16186
16556
|
init_util();
|
|
16187
16557
|
init_config();
|
|
16188
|
-
import
|
|
16558
|
+
import path72 from "node:path";
|
|
16189
16559
|
function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
|
|
16190
16560
|
return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
|
|
16191
16561
|
}
|
|
@@ -16198,7 +16568,7 @@ async function postRestartUnblock(args) {
|
|
|
16198
16568
|
const errors = [];
|
|
16199
16569
|
for (const run of listRunRecords()) {
|
|
16200
16570
|
for (const name of Object.keys(run.workers ?? {})) {
|
|
16201
|
-
const workerPath =
|
|
16571
|
+
const workerPath = path72.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
16202
16572
|
const worker = readJson(workerPath, void 0);
|
|
16203
16573
|
if (!worker) {
|
|
16204
16574
|
skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
|
|
@@ -16312,9 +16682,9 @@ async function postRestartUnblockCli(args) {
|
|
|
16312
16682
|
// src/default-repo-cli.ts
|
|
16313
16683
|
init_path_values();
|
|
16314
16684
|
init_config();
|
|
16315
|
-
import
|
|
16685
|
+
import path73 from "node:path";
|
|
16316
16686
|
import { homedir as homedir17 } from "node:os";
|
|
16317
|
-
var CONFIG_FILE2 =
|
|
16687
|
+
var CONFIG_FILE2 = path73.join(homedir17(), ".kynver", "config.json");
|
|
16318
16688
|
function ensureDefaultRepo(opts) {
|
|
16319
16689
|
const existing = loadUserConfig();
|
|
16320
16690
|
const resolved = resolveDefaultRepo({ ...opts, config: existing });
|
|
@@ -16395,19 +16765,19 @@ function summarizeResolvedDefaultRepo(resolved) {
|
|
|
16395
16765
|
}
|
|
16396
16766
|
|
|
16397
16767
|
// src/doctor/runtime-takeover.ts
|
|
16398
|
-
import
|
|
16768
|
+
import path75 from "node:path";
|
|
16399
16769
|
init_path_values();
|
|
16400
16770
|
|
|
16401
16771
|
// src/doctor/runtime-takeover.probes.ts
|
|
16402
16772
|
init_config();
|
|
16403
|
-
import { accessSync, constants, existsSync as
|
|
16773
|
+
import { accessSync, constants, existsSync as existsSync48, readFileSync as readFileSync21 } from "node:fs";
|
|
16404
16774
|
import { homedir as homedir18 } from "node:os";
|
|
16405
|
-
import
|
|
16406
|
-
import { spawnSync as
|
|
16775
|
+
import path74 from "node:path";
|
|
16776
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
16407
16777
|
init_paths();
|
|
16408
16778
|
function captureCommand(bin, args) {
|
|
16409
16779
|
try {
|
|
16410
|
-
const res =
|
|
16780
|
+
const res = spawnSync14(bin, args, { encoding: "utf8" });
|
|
16411
16781
|
const stdout = (res.stdout || "").trim();
|
|
16412
16782
|
const stderr = (res.stderr || "").trim();
|
|
16413
16783
|
const ok = res.status === 0;
|
|
@@ -16432,7 +16802,7 @@ function tokenPrefix(token) {
|
|
|
16432
16802
|
return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
|
|
16433
16803
|
}
|
|
16434
16804
|
function isWritable(target) {
|
|
16435
|
-
if (!
|
|
16805
|
+
if (!existsSync48(target)) return false;
|
|
16436
16806
|
try {
|
|
16437
16807
|
accessSync(target, constants.W_OK);
|
|
16438
16808
|
return true;
|
|
@@ -16445,11 +16815,11 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
16445
16815
|
commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
|
|
16446
16816
|
kynverVersion: (bin) => captureCommand(bin, ["--version"]),
|
|
16447
16817
|
loadConfig: () => loadUserConfig(),
|
|
16448
|
-
configFilePath: () =>
|
|
16449
|
-
credentialsFilePath: () =>
|
|
16818
|
+
configFilePath: () => path74.join(homedir18(), ".kynver", "config.json"),
|
|
16819
|
+
credentialsFilePath: () => path74.join(homedir18(), ".kynver", "credentials"),
|
|
16450
16820
|
readCredentials: () => {
|
|
16451
|
-
const credPath =
|
|
16452
|
-
if (!
|
|
16821
|
+
const credPath = path74.join(homedir18(), ".kynver", "credentials");
|
|
16822
|
+
if (!existsSync48(credPath)) {
|
|
16453
16823
|
return { hasApiKey: false };
|
|
16454
16824
|
}
|
|
16455
16825
|
try {
|
|
@@ -16483,8 +16853,8 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
16483
16853
|
})()
|
|
16484
16854
|
}),
|
|
16485
16855
|
harnessRoot: () => resolveHarnessRoot(),
|
|
16486
|
-
legacyOpenclawHarnessRoot: () =>
|
|
16487
|
-
pathExists: (target) =>
|
|
16856
|
+
legacyOpenclawHarnessRoot: () => path74.join(homedir18(), ".openclaw", "harness"),
|
|
16857
|
+
pathExists: (target) => existsSync48(target),
|
|
16488
16858
|
pathWritable: (target) => isWritable(target)
|
|
16489
16859
|
};
|
|
16490
16860
|
|
|
@@ -16890,8 +17260,8 @@ function assessVercelDeployEvidence(probes) {
|
|
|
16890
17260
|
}
|
|
16891
17261
|
function assessHarnessDirs(probes) {
|
|
16892
17262
|
const harnessRoot = probes.harnessRoot();
|
|
16893
|
-
const runsDir =
|
|
16894
|
-
const worktreesDir =
|
|
17263
|
+
const runsDir = path75.join(harnessRoot, "runs");
|
|
17264
|
+
const worktreesDir = path75.join(harnessRoot, "worktrees");
|
|
16895
17265
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
16896
17266
|
const displayRunsDir = redactHomePath(runsDir);
|
|
16897
17267
|
const displayWorktreesDir = redactHomePath(worktreesDir);
|
|
@@ -17160,9 +17530,9 @@ function applySchedulerCutoverAttestation(config) {
|
|
|
17160
17530
|
|
|
17161
17531
|
// src/scheduler-cutover-cli.ts
|
|
17162
17532
|
init_config();
|
|
17163
|
-
import
|
|
17533
|
+
import path76 from "node:path";
|
|
17164
17534
|
import { homedir as homedir19 } from "node:os";
|
|
17165
|
-
var CONFIG_FILE3 =
|
|
17535
|
+
var CONFIG_FILE3 = path76.join(homedir19(), ".kynver", "config.json");
|
|
17166
17536
|
function runSchedulerCutoverCheckCli(json = false) {
|
|
17167
17537
|
const config = loadUserConfig();
|
|
17168
17538
|
const report = assessSchedulerCutover(config);
|
|
@@ -17306,7 +17676,7 @@ init_config();
|
|
|
17306
17676
|
init_config();
|
|
17307
17677
|
init_path_values();
|
|
17308
17678
|
init_util();
|
|
17309
|
-
import { existsSync as
|
|
17679
|
+
import { existsSync as existsSync52 } from "node:fs";
|
|
17310
17680
|
|
|
17311
17681
|
// src/cron/cron-id.ts
|
|
17312
17682
|
import { createHash as createHash5 } from "node:crypto";
|
|
@@ -17325,13 +17695,13 @@ function deterministicCronProviderId(spec) {
|
|
|
17325
17695
|
|
|
17326
17696
|
// src/cron/cron-install-plan.ts
|
|
17327
17697
|
import { homedir as homedir21 } from "node:os";
|
|
17328
|
-
import
|
|
17698
|
+
import path78 from "node:path";
|
|
17329
17699
|
|
|
17330
17700
|
// src/cron/cron-env-file.ts
|
|
17331
|
-
import { existsSync as
|
|
17701
|
+
import { existsSync as existsSync49, mkdirSync as mkdirSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync8 } from "node:fs";
|
|
17332
17702
|
import { homedir as homedir20 } from "node:os";
|
|
17333
|
-
import
|
|
17334
|
-
var DEFAULT_KYNVER_ENV_FILE =
|
|
17703
|
+
import path77 from "node:path";
|
|
17704
|
+
var DEFAULT_KYNVER_ENV_FILE = path77.join(homedir20(), ".kynver", ".env");
|
|
17335
17705
|
function parseEnvFile(content) {
|
|
17336
17706
|
const map = /* @__PURE__ */ new Map();
|
|
17337
17707
|
for (const line of content.split(/\r?\n/)) {
|
|
@@ -17358,12 +17728,12 @@ function serializeEnvFile(values, header = "# Managed by kynver cron install \u2
|
|
|
17358
17728
|
return lines.join("\n");
|
|
17359
17729
|
}
|
|
17360
17730
|
function readEnvFile(filePath = DEFAULT_KYNVER_ENV_FILE) {
|
|
17361
|
-
if (!
|
|
17731
|
+
if (!existsSync49(filePath)) return /* @__PURE__ */ new Map();
|
|
17362
17732
|
return parseEnvFile(readFileSync22(filePath, "utf8"));
|
|
17363
17733
|
}
|
|
17364
17734
|
function mergeEnvFile(updates, options = {}) {
|
|
17365
17735
|
const filePath = options.filePath ?? DEFAULT_KYNVER_ENV_FILE;
|
|
17366
|
-
const existing =
|
|
17736
|
+
const existing = existsSync49(filePath) ? readFileSync22(filePath, "utf8") : "";
|
|
17367
17737
|
const map = parseEnvFile(existing);
|
|
17368
17738
|
const keysWritten = [];
|
|
17369
17739
|
const keysRemoved = [];
|
|
@@ -17388,8 +17758,8 @@ function mergeEnvFile(updates, options = {}) {
|
|
|
17388
17758
|
}
|
|
17389
17759
|
const nextContent = serializeEnvFile(map);
|
|
17390
17760
|
if (changed) {
|
|
17391
|
-
|
|
17392
|
-
|
|
17761
|
+
mkdirSync13(path77.dirname(filePath), { recursive: true });
|
|
17762
|
+
writeFileSync8(filePath, nextContent, { mode: 384 });
|
|
17393
17763
|
}
|
|
17394
17764
|
return { path: filePath, changed, keysWritten, keysRemoved };
|
|
17395
17765
|
}
|
|
@@ -17406,7 +17776,7 @@ var VERCEL_KYNVER_CRON_CUTOVER_STEPS = [
|
|
|
17406
17776
|
function buildCronInstallPlan(input) {
|
|
17407
17777
|
const storePath = input.storePath?.trim() || defaultKynverCronStorePath();
|
|
17408
17778
|
const envFilePath = input.envFilePath?.trim() || DEFAULT_KYNVER_ENV_FILE;
|
|
17409
|
-
const configPath =
|
|
17779
|
+
const configPath = path78.join(homedir21(), ".kynver", "config.json");
|
|
17410
17780
|
const callbackPath = `/api/agent-os/by-id/${input.agentOsId}/scheduler/fire`;
|
|
17411
17781
|
const prerequisites = [];
|
|
17412
17782
|
if (!input.apiBaseUrl?.trim()) prerequisites.push("apiBaseUrl \u2014 run `kynver setup --api-base-url \u2026`");
|
|
@@ -17558,13 +17928,13 @@ function resolveCronSecretForInstall(envFilePath = DEFAULT_KYNVER_ENV_FILE) {
|
|
|
17558
17928
|
}
|
|
17559
17929
|
|
|
17560
17930
|
// src/cron/cron-install-systemd.ts
|
|
17561
|
-
import { existsSync as
|
|
17931
|
+
import { existsSync as existsSync50, mkdirSync as mkdirSync14, writeFileSync as writeFileSync9 } from "node:fs";
|
|
17562
17932
|
import { homedir as homedir22 } from "node:os";
|
|
17563
|
-
import
|
|
17564
|
-
import { spawnSync as
|
|
17933
|
+
import path79 from "node:path";
|
|
17934
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
17565
17935
|
var KYNVER_CRON_DAEMON_UNIT = "kynver-cron-daemon.service";
|
|
17566
17936
|
function defaultSystemdUserUnitDir() {
|
|
17567
|
-
return
|
|
17937
|
+
return path79.join(homedir22(), ".config", "systemd", "user");
|
|
17568
17938
|
}
|
|
17569
17939
|
function renderKynverCronDaemonService(input) {
|
|
17570
17940
|
const kynverBin = input.kynverBin?.trim() || "kynver";
|
|
@@ -17597,7 +17967,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
17597
17967
|
};
|
|
17598
17968
|
}
|
|
17599
17969
|
const unitDir = defaultSystemdUserUnitDir();
|
|
17600
|
-
const unitPath =
|
|
17970
|
+
const unitPath = path79.join(unitDir, KYNVER_CRON_DAEMON_UNIT);
|
|
17601
17971
|
const content = renderKynverCronDaemonService(input);
|
|
17602
17972
|
if (!execute) {
|
|
17603
17973
|
return {
|
|
@@ -17609,10 +17979,10 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
17609
17979
|
note: "Dry-run \u2014 pass --execute to write and enable the user unit."
|
|
17610
17980
|
};
|
|
17611
17981
|
}
|
|
17612
|
-
|
|
17613
|
-
const existed =
|
|
17614
|
-
|
|
17615
|
-
const reload =
|
|
17982
|
+
mkdirSync14(unitDir, { recursive: true });
|
|
17983
|
+
const existed = existsSync50(unitPath);
|
|
17984
|
+
writeFileSync9(unitPath, content, "utf8");
|
|
17985
|
+
const reload = spawnSync15("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
|
|
17616
17986
|
if (reload.status !== 0) {
|
|
17617
17987
|
return {
|
|
17618
17988
|
supported: true,
|
|
@@ -17623,7 +17993,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
17623
17993
|
note: `Wrote ${unitPath} but systemctl --user daemon-reload failed: ${reload.stderr || reload.stdout}`
|
|
17624
17994
|
};
|
|
17625
17995
|
}
|
|
17626
|
-
const enable =
|
|
17996
|
+
const enable = spawnSync15("systemctl", ["--user", "enable", "--now", KYNVER_CRON_DAEMON_UNIT], {
|
|
17627
17997
|
encoding: "utf8"
|
|
17628
17998
|
});
|
|
17629
17999
|
return {
|
|
@@ -17637,7 +18007,7 @@ function installSystemdUserDaemon(input, execute) {
|
|
|
17637
18007
|
}
|
|
17638
18008
|
|
|
17639
18009
|
// src/cron/cron-install-verify.ts
|
|
17640
|
-
import { existsSync as
|
|
18010
|
+
import { existsSync as existsSync51 } from "node:fs";
|
|
17641
18011
|
async function verifyCronInstall(input) {
|
|
17642
18012
|
const envFilePath = input.envFilePath ?? DEFAULT_KYNVER_ENV_FILE;
|
|
17643
18013
|
const checks = [];
|
|
@@ -17670,14 +18040,14 @@ async function verifyCronInstall(input) {
|
|
|
17670
18040
|
});
|
|
17671
18041
|
checks.push({
|
|
17672
18042
|
id: "env_file",
|
|
17673
|
-
ok:
|
|
17674
|
-
summary:
|
|
18043
|
+
ok: existsSync51(envFilePath) && Boolean(fileEnv.get("KYNVER_CRON_SECRET")),
|
|
18044
|
+
summary: existsSync51(envFilePath) ? `~/.kynver/.env present (${fileEnv.size} keys)` : "~/.kynver/.env missing",
|
|
17675
18045
|
remediation: "Run `kynver cron install` to write ~/.kynver/.env."
|
|
17676
18046
|
});
|
|
17677
18047
|
checks.push({
|
|
17678
18048
|
id: "cron_store",
|
|
17679
|
-
ok:
|
|
17680
|
-
summary:
|
|
18049
|
+
ok: existsSync51(env.storePath),
|
|
18050
|
+
summary: existsSync51(env.storePath) ? `cron store present (${env.storePath})` : `cron store missing (${env.storePath})`,
|
|
17681
18051
|
remediation: "Run `kynver cron install` to initialize the local store."
|
|
17682
18052
|
});
|
|
17683
18053
|
const jobs = await loadCronJobs(env.storePath).catch(() => []);
|
|
@@ -17711,7 +18081,7 @@ function resolveDaemonRunId(config, explicit) {
|
|
|
17711
18081
|
if (explicit?.trim()) return explicit.trim();
|
|
17712
18082
|
if (config.defaultDaemonRunId?.trim()) return config.defaultDaemonRunId.trim();
|
|
17713
18083
|
const runsDir = getPaths().runsDir;
|
|
17714
|
-
if (!
|
|
18084
|
+
if (!existsSync52(runsDir)) return null;
|
|
17715
18085
|
const runs = listRunRecords().sort(
|
|
17716
18086
|
(a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt)
|
|
17717
18087
|
);
|
|
@@ -17763,7 +18133,7 @@ async function runCronInstall(opts = {}) {
|
|
|
17763
18133
|
process.env.KYNVER_CRON_STORE_PATH = plan.storePath;
|
|
17764
18134
|
process.env.KYNVER_CRON_TICK_ENABLED = "1";
|
|
17765
18135
|
const storeInit = await ensureCronStoreInitialized(plan.storePath);
|
|
17766
|
-
result.storeInitialized = storeInit.created ||
|
|
18136
|
+
result.storeInitialized = storeInit.created || existsSync52(plan.storePath);
|
|
17767
18137
|
const apiKey = loadApiKey();
|
|
17768
18138
|
if (apiKey) {
|
|
17769
18139
|
try {
|
|
@@ -17982,10 +18352,10 @@ var LANDING_MAINTAINER_LANE_SPEC = {
|
|
|
17982
18352
|
};
|
|
17983
18353
|
|
|
17984
18354
|
// src/lane/landing-maintainer-local.ts
|
|
17985
|
-
import { spawnSync as
|
|
17986
|
-
import
|
|
18355
|
+
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
18356
|
+
import path80 from "node:path";
|
|
17987
18357
|
function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
17988
|
-
const script =
|
|
18358
|
+
const script = path80.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
|
|
17989
18359
|
const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
|
|
17990
18360
|
if (!execute) {
|
|
17991
18361
|
return {
|
|
@@ -17996,7 +18366,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
|
17996
18366
|
stderr: ""
|
|
17997
18367
|
};
|
|
17998
18368
|
}
|
|
17999
|
-
const result =
|
|
18369
|
+
const result = spawnSync16("node", args, {
|
|
18000
18370
|
cwd: repoRoot,
|
|
18001
18371
|
encoding: "utf8",
|
|
18002
18372
|
timeout: 10 * 60 * 1e3
|
|
@@ -18011,7 +18381,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
|
18011
18381
|
}
|
|
18012
18382
|
function resolveLandingMaintainerRepoRoot(args) {
|
|
18013
18383
|
const explicit = args.repoPath ? String(args.repoPath).trim() : "";
|
|
18014
|
-
if (explicit) return
|
|
18384
|
+
if (explicit) return path80.resolve(explicit);
|
|
18015
18385
|
const resolved = resolveDefaultRepo();
|
|
18016
18386
|
return resolved?.repo ?? process.cwd();
|
|
18017
18387
|
}
|
|
@@ -18136,9 +18506,9 @@ function usage(code = 0) {
|
|
|
18136
18506
|
"Usage:",
|
|
18137
18507
|
" kynver login [--api-key KEY] [--api-base-url URL] (omit --api-key to authorize in the browser)",
|
|
18138
18508
|
" kynver bootstrap [--api-base-url URL] [--api-key KEY] [--repo PATH] (login + setup + runner credential in one shot)",
|
|
18139
|
-
" kynver start [--repo PATH] [--api-base-url URL] [--run RUN_ID] [--interval-ms MS] (bring your agent online: bootstrap if needed + run + daemon)",
|
|
18509
|
+
" 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)",
|
|
18140
18510
|
" kynver runner credential [--agent-os-id ID] [--base-url URL]",
|
|
18141
|
-
" 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]",
|
|
18511
|
+
" 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]",
|
|
18142
18512
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS] [--stall-ms MS] [--no-supervise]",
|
|
18143
18513
|
" kynver status --run RUN_ID [--blocked] [--running] [--task TASK_ID] [--worker WORKER] [--full] # top-level compact run status",
|
|
18144
18514
|
" kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
|
|
@@ -18202,8 +18572,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
18202
18572
|
if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
|
|
18203
18573
|
const args = parseArgs(rest);
|
|
18204
18574
|
const { runsDir, worktreesDir } = getPaths();
|
|
18205
|
-
|
|
18206
|
-
|
|
18575
|
+
mkdirSync15(runsDir, { recursive: true });
|
|
18576
|
+
mkdirSync15(worktreesDir, { recursive: true });
|
|
18207
18577
|
if (scope === "daemon") {
|
|
18208
18578
|
assertNativeDaemonAllowed();
|
|
18209
18579
|
}
|
|
@@ -18682,9 +19052,11 @@ export {
|
|
|
18682
19052
|
ORCHESTRATION_POLICY_MODES,
|
|
18683
19053
|
PACKAGE_VERSION,
|
|
18684
19054
|
RUN_METADATA_ACTIVE_SIGNAL_MS,
|
|
19055
|
+
TERMINAL_WORKER_ARCHIVE_AGE_MS,
|
|
18685
19056
|
TRANSIENT_OPENAI_CODEX_ERROR_CLASSES,
|
|
18686
19057
|
WORKER_PERSONA_CATALOG,
|
|
18687
19058
|
applyProductionDatabaseToProcess,
|
|
19059
|
+
archiveTerminalWorkerMetadata,
|
|
18688
19060
|
assertMemoryCostPackageVersionGuard,
|
|
18689
19061
|
assessAutoCompleteEligibility,
|
|
18690
19062
|
assessBuildAdmission,
|