@kynver-app/runtime 0.1.77 → 0.1.80
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/cleanup-types.d.ts +2 -0
- package/dist/cli.js +1082 -302
- package/dist/cli.js.map +4 -4
- package/dist/cron/cron-env.d.ts +21 -0
- package/dist/cron/cron-fire.d.ts +8 -0
- package/dist/cron/cron-lock.d.ts +9 -0
- package/dist/cron/cron-match.d.ts +8 -0
- package/dist/cron/cron-status.d.ts +10 -0
- package/dist/cron/cron-store.d.ts +3 -0
- package/dist/cron/cron-tick-cli.d.ts +2 -0
- package/dist/cron/cron-tick-state.d.ts +5 -0
- package/dist/cron/cron-tick.d.ts +17 -0
- package/dist/cron/cron-types.d.ts +44 -0
- package/dist/dispatch.d.ts +1 -0
- package/dist/doctor/runtime-takeover-scheduler.d.ts +3 -1
- package/dist/doctor/runtime-takeover.probes.d.ts +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1119 -318
- package/dist/index.js.map +4 -4
- package/dist/pipeline-max-starts.d.ts +8 -1
- package/dist/pipeline-tick.d.ts +2 -0
- package/dist/prompt.d.ts +2 -0
- package/dist/run-metadata-retention.d.ts +23 -0
- package/dist/scheduler-cutover.d.ts +4 -1
- package/dist/status.d.ts +2 -0
- package/dist/supervisor.d.ts +1 -0
- package/dist/worker-metadata-reconcile.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -793,23 +793,23 @@ function isWslHost() {
|
|
|
793
793
|
function observeWslHostDisk(options = {}) {
|
|
794
794
|
const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
|
|
795
795
|
if (!wsl) return null;
|
|
796
|
-
const
|
|
796
|
+
const path63 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
|
|
797
797
|
const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
|
|
798
798
|
const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
|
|
799
799
|
const statfs = options.statfs ?? statfsSync;
|
|
800
800
|
let stats;
|
|
801
801
|
try {
|
|
802
|
-
stats = statfs(
|
|
802
|
+
stats = statfs(path63);
|
|
803
803
|
} catch (error) {
|
|
804
804
|
return {
|
|
805
805
|
ok: false,
|
|
806
|
-
path:
|
|
806
|
+
path: path63,
|
|
807
807
|
freeBytes: 0,
|
|
808
808
|
totalBytes: 0,
|
|
809
809
|
usedPercent: 100,
|
|
810
810
|
warnBelowBytes,
|
|
811
811
|
criticalBelowBytes,
|
|
812
|
-
reason: `Windows host disk probe failed at ${
|
|
812
|
+
reason: `Windows host disk probe failed at ${path63}: ${error.message}`,
|
|
813
813
|
probeError: error.message
|
|
814
814
|
};
|
|
815
815
|
}
|
|
@@ -823,11 +823,11 @@ function observeWslHostDisk(options = {}) {
|
|
|
823
823
|
let reason = null;
|
|
824
824
|
if (!ok) {
|
|
825
825
|
const tag = criticalFree ? "critical" : "warning";
|
|
826
|
-
reason = `Windows host disk ${
|
|
826
|
+
reason = `Windows host disk ${path63} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
|
|
827
827
|
}
|
|
828
828
|
return {
|
|
829
829
|
ok,
|
|
830
|
-
path:
|
|
830
|
+
path: path63,
|
|
831
831
|
freeBytes,
|
|
832
832
|
totalBytes,
|
|
833
833
|
usedPercent,
|
|
@@ -847,12 +847,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
847
847
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
848
848
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
849
849
|
function observeRunnerDiskGate(input = {}) {
|
|
850
|
-
const
|
|
850
|
+
const path63 = input.diskPath?.trim() || "/";
|
|
851
851
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
852
852
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
853
853
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
854
854
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
855
|
-
const stats = statfsSync2(
|
|
855
|
+
const stats = statfsSync2(path63);
|
|
856
856
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
857
857
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
858
858
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -875,7 +875,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
875
875
|
}
|
|
876
876
|
return {
|
|
877
877
|
ok,
|
|
878
|
-
path:
|
|
878
|
+
path: path63,
|
|
879
879
|
freeBytes,
|
|
880
880
|
totalBytes,
|
|
881
881
|
usedPercent,
|
|
@@ -2650,7 +2650,9 @@ function resolveOrchestrationRouting(input) {
|
|
|
2650
2650
|
const risk = classifyOrchestrationRisk(task);
|
|
2651
2651
|
const inventory = resolveInventory({
|
|
2652
2652
|
inventory: input.inventory,
|
|
2653
|
-
|
|
2653
|
+
// When callers pass a pre-built inventory (tests, CC snapshots), do not probe live
|
|
2654
|
+
// Hermes/Codex bindings — that would overwrite mocked oauth_local with subscription_hermes.
|
|
2655
|
+
codexBinding: input.codexBinding ?? (input.inventory ? null : resolveCodexOrchestrationAdapter())
|
|
2654
2656
|
});
|
|
2655
2657
|
const explicit = input.preferLowCost === false ? null : input.explicitProvider?.trim().toLowerCase();
|
|
2656
2658
|
const explicitModel = input.explicitModel?.trim() || void 0;
|
|
@@ -3464,6 +3466,7 @@ function buildPrompt(input) {
|
|
|
3464
3466
|
"",
|
|
3465
3467
|
...input.personaMarkdown?.trim() ? [input.personaMarkdown.trim(), ""] : [],
|
|
3466
3468
|
...input.instructionPolicyMarkdown?.trim() ? ["Operating rules (Lane A \u2014 from AgentOS memory policy):", input.instructionPolicyMarkdown.trim(), ""] : [],
|
|
3469
|
+
...input.memoryQualityMarkdown?.trim() ? [input.memoryQualityMarkdown.trim(), ""] : [],
|
|
3467
3470
|
...input.repairTargetPrUrl ? [
|
|
3468
3471
|
...repairTargetPromptLines({
|
|
3469
3472
|
targetPrUrl: input.repairTargetPrUrl,
|
|
@@ -4258,8 +4261,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
|
|
|
4258
4261
|
if (removed.length === 0) return false;
|
|
4259
4262
|
const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
|
|
4260
4263
|
return material.every((line) => {
|
|
4261
|
-
const
|
|
4262
|
-
return removedSet.has(
|
|
4264
|
+
const path63 = normalizeRelativePath(pathFromGitStatusLine(line));
|
|
4265
|
+
return removedSet.has(path63);
|
|
4263
4266
|
});
|
|
4264
4267
|
}
|
|
4265
4268
|
|
|
@@ -4681,7 +4684,9 @@ async function completeWorker(args) {
|
|
|
4681
4684
|
}
|
|
4682
4685
|
}
|
|
4683
4686
|
function workerStatus(args) {
|
|
4684
|
-
const
|
|
4687
|
+
const runId = required(typeof args.run === "string" ? args.run : "", "--run");
|
|
4688
|
+
const name = required(typeof args.name === "string" ? args.name : "", "--name");
|
|
4689
|
+
const worker = loadWorker(runId, name);
|
|
4685
4690
|
const run = loadRun(worker.runId);
|
|
4686
4691
|
const status = computeWorkerStatus(worker, workerStatusOptions(run));
|
|
4687
4692
|
writeJson(path17.join(worker.workerDir, "last-status.json"), status);
|
|
@@ -5116,6 +5121,7 @@ function spawnWorkerProcess(run, opts) {
|
|
|
5116
5121
|
planId: opts.planId,
|
|
5117
5122
|
taskId: opts.taskId,
|
|
5118
5123
|
instructionPolicyMarkdown: opts.instructionPolicyMarkdown,
|
|
5124
|
+
memoryQualityMarkdown: opts.memoryQualityPromptMarkdown ?? opts.memoryQualityCapture?.promptMarkdown ?? null,
|
|
5119
5125
|
personaMarkdown: opts.personaMarkdown,
|
|
5120
5126
|
model: launchModel,
|
|
5121
5127
|
repairTargetPrUrl: opts.repairTargetPrUrl,
|
|
@@ -5894,11 +5900,13 @@ function readHarnessWorkerContext(decision) {
|
|
|
5894
5900
|
const personaEvidence = ctx.personaEvidence && typeof ctx.personaEvidence === "object" ? ctx.personaEvidence : null;
|
|
5895
5901
|
const personaInjectionReady = ctx.personaInjectionReady === true;
|
|
5896
5902
|
const memoryQualityCapture = ctx.memoryQualityCapture && typeof ctx.memoryQualityCapture === "object" ? ctx.memoryQualityCapture : null;
|
|
5903
|
+
const memoryQualityPromptMarkdown = typeof ctx.memoryQualityPromptMarkdown === "string" ? ctx.memoryQualityPromptMarkdown : typeof memoryQualityCapture?.promptMarkdown === "string" ? memoryQualityCapture.promptMarkdown : null;
|
|
5897
5904
|
return {
|
|
5898
5905
|
instructionPolicyMarkdown: markdown,
|
|
5899
5906
|
instructionPolicyFingerprint: fingerprint,
|
|
5900
5907
|
instructionPolicyEvidence: evidence,
|
|
5901
5908
|
memoryQualityCapture,
|
|
5909
|
+
memoryQualityPromptMarkdown,
|
|
5902
5910
|
personaMarkdown,
|
|
5903
5911
|
personaSlug,
|
|
5904
5912
|
personaEvidence,
|
|
@@ -6155,6 +6163,7 @@ async function dispatchRun(args) {
|
|
|
6155
6163
|
instructionPolicyFingerprint: harnessContext?.instructionPolicyFingerprint ?? null,
|
|
6156
6164
|
instructionPolicyEvidence: harnessContext?.instructionPolicyEvidence ?? null,
|
|
6157
6165
|
memoryQualityCapture: harnessContext?.memoryQualityCapture ?? null,
|
|
6166
|
+
memoryQualityPromptMarkdown: harnessContext?.memoryQualityPromptMarkdown ?? null,
|
|
6158
6167
|
personaMarkdown: harnessContext?.personaMarkdown ?? null,
|
|
6159
6168
|
personaSlug: harnessContext?.personaSlug ?? expectedPersona,
|
|
6160
6169
|
personaEvidence: harnessContext?.personaEvidence ?? null,
|
|
@@ -6741,15 +6750,15 @@ function validateTailLines(lines) {
|
|
|
6741
6750
|
}
|
|
6742
6751
|
|
|
6743
6752
|
// src/worktree.ts
|
|
6744
|
-
import { existsSync as
|
|
6745
|
-
import
|
|
6753
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync5 } from "node:fs";
|
|
6754
|
+
import path33 from "node:path";
|
|
6746
6755
|
|
|
6747
6756
|
// src/run-list.ts
|
|
6748
|
-
import { existsSync as
|
|
6749
|
-
import
|
|
6757
|
+
import { existsSync as existsSync25, readFileSync as readFileSync12 } from "node:fs";
|
|
6758
|
+
import path32 from "node:path";
|
|
6750
6759
|
|
|
6751
6760
|
// src/stale-reconcile.ts
|
|
6752
|
-
import
|
|
6761
|
+
import path31 from "node:path";
|
|
6753
6762
|
|
|
6754
6763
|
// src/finalize.ts
|
|
6755
6764
|
import path26 from "node:path";
|
|
@@ -6810,8 +6819,8 @@ function finalizeStaleRuns() {
|
|
|
6810
6819
|
}
|
|
6811
6820
|
|
|
6812
6821
|
// src/worker-metadata-reconcile.ts
|
|
6813
|
-
import { existsSync as
|
|
6814
|
-
import
|
|
6822
|
+
import { existsSync as existsSync24, lstatSync, readdirSync as readdirSync6, readlinkSync, renameSync as renameSync2, rmSync } from "node:fs";
|
|
6823
|
+
import path30 from "node:path";
|
|
6815
6824
|
|
|
6816
6825
|
// src/worker-metadata-paths.ts
|
|
6817
6826
|
import path27 from "node:path";
|
|
@@ -6866,6 +6875,219 @@ function resolveWorkerJsonPath(input) {
|
|
|
6866
6875
|
return canonical;
|
|
6867
6876
|
}
|
|
6868
6877
|
|
|
6878
|
+
// src/run-metadata-retention.ts
|
|
6879
|
+
import { existsSync as existsSync23, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
|
|
6880
|
+
import path29 from "node:path";
|
|
6881
|
+
|
|
6882
|
+
// src/default-repo.ts
|
|
6883
|
+
import path28 from "node:path";
|
|
6884
|
+
function expandConfiguredRepo(value) {
|
|
6885
|
+
return path28.resolve(resolveUserPath(value.trim()));
|
|
6886
|
+
}
|
|
6887
|
+
function fromConfigured(value, source, persistedInConfig) {
|
|
6888
|
+
const trimmed = value?.trim();
|
|
6889
|
+
if (!trimmed) return null;
|
|
6890
|
+
return {
|
|
6891
|
+
repo: expandConfiguredRepo(trimmed),
|
|
6892
|
+
source,
|
|
6893
|
+
persistedInConfig
|
|
6894
|
+
};
|
|
6895
|
+
}
|
|
6896
|
+
function resolveDefaultRepo(opts = {}) {
|
|
6897
|
+
const env = opts.env ?? process.env;
|
|
6898
|
+
const config = opts.config ?? loadUserConfig();
|
|
6899
|
+
const fromConfig = fromConfigured(config.defaultRepo, "config", true);
|
|
6900
|
+
if (fromConfig) return fromConfig;
|
|
6901
|
+
const fromDefaultEnv = fromConfigured(env.KYNVER_DEFAULT_REPO, "env_default_repo", false);
|
|
6902
|
+
if (fromDefaultEnv) return fromDefaultEnv;
|
|
6903
|
+
const fromHarnessEnv = fromConfigured(env.KYNVER_HARNESS_REPO, "env_harness_repo", false);
|
|
6904
|
+
if (fromHarnessEnv) return fromHarnessEnv;
|
|
6905
|
+
const discovered = discoverDefaultRepo({
|
|
6906
|
+
cwd: opts.cwd,
|
|
6907
|
+
runtimeModuleUrl: opts.runtimeModuleUrl
|
|
6908
|
+
});
|
|
6909
|
+
if (!discovered) return null;
|
|
6910
|
+
return {
|
|
6911
|
+
repo: discovered.repo,
|
|
6912
|
+
source: discovered.source,
|
|
6913
|
+
persistedInConfig: false
|
|
6914
|
+
};
|
|
6915
|
+
}
|
|
6916
|
+
function persistDefaultRepo(repo, existing) {
|
|
6917
|
+
const config = {
|
|
6918
|
+
...existing ?? loadUserConfig(),
|
|
6919
|
+
defaultRepo: redactHomePath(path28.resolve(repo))
|
|
6920
|
+
};
|
|
6921
|
+
saveUserConfig(config);
|
|
6922
|
+
return config;
|
|
6923
|
+
}
|
|
6924
|
+
function remediateDefaultRepo(opts) {
|
|
6925
|
+
const existing = opts?.config ?? loadUserConfig();
|
|
6926
|
+
const resolved = resolveDefaultRepo({ ...opts, config: existing });
|
|
6927
|
+
if (!resolved) {
|
|
6928
|
+
return {
|
|
6929
|
+
ok: false,
|
|
6930
|
+
reason: "No Kynver git checkout found. Clone the repo, cd into it, then run `kynver setup --repo /path/to/Kynver` (or export KYNVER_DEFAULT_REPO)."
|
|
6931
|
+
};
|
|
6932
|
+
}
|
|
6933
|
+
if (resolved.persistedInConfig) {
|
|
6934
|
+
return { ok: true, resolved, config: existing };
|
|
6935
|
+
}
|
|
6936
|
+
const config = persistDefaultRepo(resolved.repo, existing);
|
|
6937
|
+
return {
|
|
6938
|
+
ok: true,
|
|
6939
|
+
resolved: { ...resolved, persistedInConfig: true, source: "config" },
|
|
6940
|
+
config
|
|
6941
|
+
};
|
|
6942
|
+
}
|
|
6943
|
+
function formatResolvedDefaultRepo(resolved) {
|
|
6944
|
+
return {
|
|
6945
|
+
defaultRepo: displayUserPath(resolved.repo),
|
|
6946
|
+
source: resolved.source,
|
|
6947
|
+
persistedInConfig: resolved.persistedInConfig
|
|
6948
|
+
};
|
|
6949
|
+
}
|
|
6950
|
+
|
|
6951
|
+
// src/run-metadata-retention.ts
|
|
6952
|
+
var RUN_METADATA_ACTIVE_SIGNAL_MS = 15 * 60 * 1e3;
|
|
6953
|
+
function isHarnessRunMetadataPath(targetPath, harnessRoot) {
|
|
6954
|
+
const resolved = path29.resolve(targetPath);
|
|
6955
|
+
const runsDir = path29.resolve(harnessRunsDir(harnessRoot));
|
|
6956
|
+
const rel = path29.relative(runsDir, resolved);
|
|
6957
|
+
return rel !== ".." && !rel.startsWith("..") && !path29.isAbsolute(rel);
|
|
6958
|
+
}
|
|
6959
|
+
function listRunDirIds(runsDir) {
|
|
6960
|
+
if (!existsSync23(runsDir)) return [];
|
|
6961
|
+
try {
|
|
6962
|
+
return readdirSync5(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== "runs").map((entry) => entry.name);
|
|
6963
|
+
} catch {
|
|
6964
|
+
return [];
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
function listWorkerNamesOnDisk(runDir2) {
|
|
6968
|
+
const workersDir = path29.join(runDir2, "workers");
|
|
6969
|
+
if (!existsSync23(workersDir)) return [];
|
|
6970
|
+
try {
|
|
6971
|
+
return readdirSync5(workersDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
6972
|
+
} catch {
|
|
6973
|
+
return [];
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
function pathRecentlyTouched(target, now, windowMs) {
|
|
6977
|
+
if (!existsSync23(target)) return false;
|
|
6978
|
+
try {
|
|
6979
|
+
const age = now - statSync4(target).mtimeMs;
|
|
6980
|
+
return Number.isFinite(age) && age >= 0 && age < windowMs;
|
|
6981
|
+
} catch {
|
|
6982
|
+
return false;
|
|
6983
|
+
}
|
|
6984
|
+
}
|
|
6985
|
+
function workerDirHasActiveRetentionSignals(workerDir, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
|
|
6986
|
+
if (!existsSync23(workerDir)) return false;
|
|
6987
|
+
const artifacts = workerArtifactPaths(workerDir);
|
|
6988
|
+
const worker = readJson(
|
|
6989
|
+
artifacts.workerJsonPath,
|
|
6990
|
+
void 0
|
|
6991
|
+
);
|
|
6992
|
+
if (worker?.status === "running" && isPidAlive(worker.pid)) return true;
|
|
6993
|
+
const heartbeat = parseHeartbeat(artifacts.heartbeatPath);
|
|
6994
|
+
if (heartbeat.lastHeartbeatAt) {
|
|
6995
|
+
const age = now - Date.parse(heartbeat.lastHeartbeatAt);
|
|
6996
|
+
if (Number.isFinite(age) && age >= 0 && age < windowMs) return true;
|
|
6997
|
+
}
|
|
6998
|
+
if (pathRecentlyTouched(artifacts.stdoutPath, now, windowMs)) return true;
|
|
6999
|
+
if (pathRecentlyTouched(artifacts.heartbeatPath, now, windowMs)) return true;
|
|
7000
|
+
return false;
|
|
7001
|
+
}
|
|
7002
|
+
function runDirHasActiveRetentionSignals(runDir2, now = Date.now(), windowMs = RUN_METADATA_ACTIVE_SIGNAL_MS) {
|
|
7003
|
+
for (const name of listWorkerNamesOnDisk(runDir2)) {
|
|
7004
|
+
if (workerDirHasActiveRetentionSignals(path29.join(runDir2, "workers", name), now, windowMs)) {
|
|
7005
|
+
return true;
|
|
7006
|
+
}
|
|
7007
|
+
}
|
|
7008
|
+
return false;
|
|
7009
|
+
}
|
|
7010
|
+
function inferRepoFields() {
|
|
7011
|
+
const resolved = resolveDefaultRepo();
|
|
7012
|
+
return {
|
|
7013
|
+
repo: resolved?.repo ?? "",
|
|
7014
|
+
base: "origin/main",
|
|
7015
|
+
baseCommit: "unknown"
|
|
7016
|
+
};
|
|
7017
|
+
}
|
|
7018
|
+
function synthesizeRunFromDisk(harnessRoot, runId) {
|
|
7019
|
+
const runDir2 = path29.join(harnessRunsDir(harnessRoot), safeSlug(runId));
|
|
7020
|
+
if (!existsSync23(runDir2)) return null;
|
|
7021
|
+
const workerNames = listWorkerNamesOnDisk(runDir2);
|
|
7022
|
+
if (workerNames.length === 0) return null;
|
|
7023
|
+
let createdAt;
|
|
7024
|
+
try {
|
|
7025
|
+
createdAt = statSync4(runDir2).birthtime.toISOString();
|
|
7026
|
+
} catch {
|
|
7027
|
+
createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7028
|
+
}
|
|
7029
|
+
const workers = {};
|
|
7030
|
+
for (const name of workerNames) {
|
|
7031
|
+
const canonicalDir = canonicalWorkerDir(harnessRoot, runId, name);
|
|
7032
|
+
workers[name] = {
|
|
7033
|
+
workerDir: canonicalDir,
|
|
7034
|
+
statusPath: path29.join(canonicalDir, "worker.json")
|
|
7035
|
+
};
|
|
7036
|
+
}
|
|
7037
|
+
const hasActive = runDirHasActiveRetentionSignals(runDir2);
|
|
7038
|
+
return {
|
|
7039
|
+
id: runId,
|
|
7040
|
+
name: runId,
|
|
7041
|
+
...inferRepoFields(),
|
|
7042
|
+
status: hasActive ? "running" : "needs_attention",
|
|
7043
|
+
createdAt,
|
|
7044
|
+
workers
|
|
7045
|
+
};
|
|
7046
|
+
}
|
|
7047
|
+
function repairMissingRunMetadata(harnessRootInput) {
|
|
7048
|
+
const harnessRoot = normalizeHarnessRoot(harnessRootInput ?? resolveHarnessRoot());
|
|
7049
|
+
const runsDir = harnessRunsDir(harnessRoot);
|
|
7050
|
+
const outcomes = [];
|
|
7051
|
+
for (const runId of listRunDirIds(runsDir)) {
|
|
7052
|
+
const runJsonPath = path29.join(runsDir, runId, "run.json");
|
|
7053
|
+
if (existsSync23(runJsonPath)) {
|
|
7054
|
+
outcomes.push({
|
|
7055
|
+
runId,
|
|
7056
|
+
action: "skipped",
|
|
7057
|
+
reason: "run.json present"
|
|
7058
|
+
});
|
|
7059
|
+
continue;
|
|
7060
|
+
}
|
|
7061
|
+
const synthesized = synthesizeRunFromDisk(harnessRoot, runId);
|
|
7062
|
+
if (!synthesized) {
|
|
7063
|
+
outcomes.push({
|
|
7064
|
+
runId,
|
|
7065
|
+
action: "skipped",
|
|
7066
|
+
reason: "no worker dirs on disk"
|
|
7067
|
+
});
|
|
7068
|
+
continue;
|
|
7069
|
+
}
|
|
7070
|
+
saveRun(synthesized);
|
|
7071
|
+
outcomes.push({
|
|
7072
|
+
runId,
|
|
7073
|
+
action: "repaired_run_json",
|
|
7074
|
+
reason: runDirHasActiveRetentionSignals(path29.join(runsDir, runId)) ? "synthesized run.json while worker artifacts still active" : "synthesized run.json from orphan worker metadata dirs"
|
|
7075
|
+
});
|
|
7076
|
+
}
|
|
7077
|
+
return { runs: outcomes };
|
|
7078
|
+
}
|
|
7079
|
+
function collectFilesystemLiveRunKeys(harnessRoot, now = Date.now()) {
|
|
7080
|
+
const keys = /* @__PURE__ */ new Set();
|
|
7081
|
+
const runsDir = harnessRunsDir(harnessRoot);
|
|
7082
|
+
for (const runId of listRunDirIds(runsDir)) {
|
|
7083
|
+
const runDir2 = path29.join(runsDir, runId);
|
|
7084
|
+
if (runDirHasActiveRetentionSignals(runDir2, now)) {
|
|
7085
|
+
keys.add(`${harnessRoot}\0${runId}`);
|
|
7086
|
+
}
|
|
7087
|
+
}
|
|
7088
|
+
return keys;
|
|
7089
|
+
}
|
|
7090
|
+
|
|
6869
7091
|
// src/worker-metadata-reconcile.ts
|
|
6870
7092
|
function materializeSymlinkedRunDir(harnessRoot, runId) {
|
|
6871
7093
|
const canonical = canonicalRunDir(harnessRoot, runId);
|
|
@@ -6876,7 +7098,10 @@ function materializeSymlinkedRunDir(harnessRoot, runId) {
|
|
|
6876
7098
|
return null;
|
|
6877
7099
|
}
|
|
6878
7100
|
if (!stat.isSymbolicLink()) return null;
|
|
6879
|
-
const linkedTarget =
|
|
7101
|
+
const linkedTarget = path30.resolve(path30.dirname(canonical), readlinkSync(canonical));
|
|
7102
|
+
if (runDirHasActiveRetentionSignals(linkedTarget) || runDirHasActiveRetentionSignals(canonical)) {
|
|
7103
|
+
return null;
|
|
7104
|
+
}
|
|
6880
7105
|
const staging = `${canonical}.materialize-${Date.now()}`;
|
|
6881
7106
|
renameSync2(linkedTarget, staging);
|
|
6882
7107
|
rmSync(canonical);
|
|
@@ -6886,9 +7111,9 @@ function materializeSymlinkedRunDir(harnessRoot, runId) {
|
|
|
6886
7111
|
function relocateArtifacts(input) {
|
|
6887
7112
|
const moved = [];
|
|
6888
7113
|
for (const fileName of workerArtifactFileNames()) {
|
|
6889
|
-
const from =
|
|
6890
|
-
const to =
|
|
6891
|
-
if (!
|
|
7114
|
+
const from = path30.join(input.fromDir, fileName);
|
|
7115
|
+
const to = path30.join(input.toDir, fileName);
|
|
7116
|
+
if (!existsSync24(from) || existsSync24(to)) continue;
|
|
6892
7117
|
renameSync2(from, to);
|
|
6893
7118
|
moved.push(fileName);
|
|
6894
7119
|
}
|
|
@@ -6927,8 +7152,8 @@ function deriveSynthesizedStatus(input) {
|
|
|
6927
7152
|
function synthesizeWorkerFromArtifacts(input) {
|
|
6928
7153
|
const artifacts = workerArtifactPaths(input.canonicalDir);
|
|
6929
7154
|
const lastStatus = readJson(artifacts.lastStatusPath, void 0);
|
|
6930
|
-
const stdoutExists =
|
|
6931
|
-
const heartbeatExists =
|
|
7155
|
+
const stdoutExists = existsSync24(artifacts.stdoutPath);
|
|
7156
|
+
const heartbeatExists = existsSync24(artifacts.heartbeatPath);
|
|
6932
7157
|
const hasArtifacts = stdoutExists || heartbeatExists || lastStatus != null;
|
|
6933
7158
|
if (!hasArtifacts) return null;
|
|
6934
7159
|
const parsedStdout = stdoutExists ? parseHarnessStream(artifacts.stdoutPath) : { finalResult: null };
|
|
@@ -6944,7 +7169,7 @@ function synthesizeWorkerFromArtifacts(input) {
|
|
|
6944
7169
|
runId: input.run.id,
|
|
6945
7170
|
status,
|
|
6946
7171
|
branch: typeof lastStatus?.branch === "string" ? lastStatus.branch : `agent/${input.run.id}/${input.workerName}`,
|
|
6947
|
-
worktreePath: typeof lastStatus?.worktreePath === "string" ? lastStatus.worktreePath :
|
|
7172
|
+
worktreePath: typeof lastStatus?.worktreePath === "string" ? lastStatus.worktreePath : path30.join(getPaths().worktreesDir, input.run.id, input.workerName),
|
|
6948
7173
|
workerDir: input.canonicalDir,
|
|
6949
7174
|
stdoutPath: artifacts.stdoutPath,
|
|
6950
7175
|
stderrPath: artifacts.stderrPath,
|
|
@@ -6971,7 +7196,7 @@ function repairRunWorkerIndex(run, harnessRoot) {
|
|
|
6971
7196
|
const nextWorkers = { ...run.workers || {} };
|
|
6972
7197
|
for (const [name, ref] of Object.entries(nextWorkers)) {
|
|
6973
7198
|
const canonicalDir = canonicalWorkerDir(harnessRoot, run.id, name);
|
|
6974
|
-
const canonicalStatus =
|
|
7199
|
+
const canonicalStatus = path30.join(canonicalDir, "worker.json");
|
|
6975
7200
|
const nested = hasNestedRunsSegment(ref.workerDir) || hasNestedRunsSegment(ref.statusPath) || ref.workerDir !== canonicalDir || ref.statusPath !== canonicalStatus;
|
|
6976
7201
|
if (!nested) continue;
|
|
6977
7202
|
nextWorkers[name] = { workerDir: canonicalDir, statusPath: canonicalStatus };
|
|
@@ -7004,7 +7229,7 @@ function reconcileOneWorker(input) {
|
|
|
7004
7229
|
statusPath: input.statusPath
|
|
7005
7230
|
});
|
|
7006
7231
|
let worker = readJson(workerJsonPath, void 0);
|
|
7007
|
-
if (!worker &&
|
|
7232
|
+
if (!worker && existsSync24(canonicalArtifacts.workerJsonPath)) {
|
|
7008
7233
|
worker = readJson(canonicalArtifacts.workerJsonPath, void 0);
|
|
7009
7234
|
}
|
|
7010
7235
|
if (!worker) {
|
|
@@ -7024,15 +7249,15 @@ function reconcileOneWorker(input) {
|
|
|
7024
7249
|
});
|
|
7025
7250
|
return outcomes;
|
|
7026
7251
|
}
|
|
7027
|
-
const dirExists =
|
|
7028
|
-
const hasAnyArtifact = workerArtifactFileNames().some((f) =>
|
|
7252
|
+
const dirExists = existsSync24(canonicalDir);
|
|
7253
|
+
const hasAnyArtifact = workerArtifactFileNames().some((f) => existsSync24(path30.join(canonicalDir, f)));
|
|
7029
7254
|
if (dirExists && !hasAnyArtifact) {
|
|
7030
7255
|
const abandoned = {
|
|
7031
7256
|
name: input.workerName,
|
|
7032
7257
|
runId: input.run.id,
|
|
7033
7258
|
status: "exited",
|
|
7034
7259
|
branch: `agent/${input.run.id}/${input.workerName}`,
|
|
7035
|
-
worktreePath:
|
|
7260
|
+
worktreePath: path30.join(getPaths().worktreesDir, input.run.id, input.workerName),
|
|
7036
7261
|
workerDir: canonicalDir,
|
|
7037
7262
|
stdoutPath: canonicalArtifacts.stdoutPath,
|
|
7038
7263
|
stderrPath: canonicalArtifacts.stderrPath,
|
|
@@ -7079,11 +7304,11 @@ function reconcileOneWorker(input) {
|
|
|
7079
7304
|
return outcomes;
|
|
7080
7305
|
}
|
|
7081
7306
|
function listOrphanWorkerNames(run, harnessRoot) {
|
|
7082
|
-
const workersDir =
|
|
7083
|
-
if (!
|
|
7307
|
+
const workersDir = path30.join(runDirectory(run.id), "workers");
|
|
7308
|
+
if (!existsSync24(workersDir)) return [];
|
|
7084
7309
|
const indexed = new Set(Object.keys(run.workers || {}));
|
|
7085
7310
|
const orphans = [];
|
|
7086
|
-
for (const entry of
|
|
7311
|
+
for (const entry of readdirSync6(workersDir, { withFileTypes: true })) {
|
|
7087
7312
|
if (!entry.isDirectory()) continue;
|
|
7088
7313
|
if (indexed.has(entry.name)) continue;
|
|
7089
7314
|
orphans.push(entry.name);
|
|
@@ -7092,6 +7317,7 @@ function listOrphanWorkerNames(run, harnessRoot) {
|
|
|
7092
7317
|
}
|
|
7093
7318
|
function reconcileWorkerMetadata() {
|
|
7094
7319
|
const { harnessRoot } = getPaths();
|
|
7320
|
+
const runMetadataRetention = repairMissingRunMetadata(harnessRoot);
|
|
7095
7321
|
const outcomes = [];
|
|
7096
7322
|
for (const run of listRunRecords()) {
|
|
7097
7323
|
const materializedFrom = materializeSymlinkedRunDir(harnessRoot, run.id);
|
|
@@ -7118,7 +7344,7 @@ function reconcileWorkerMetadata() {
|
|
|
7118
7344
|
...run.workers || {},
|
|
7119
7345
|
[orphan]: {
|
|
7120
7346
|
workerDir: canonicalWorkerDir(harnessRoot, run.id, orphan),
|
|
7121
|
-
statusPath:
|
|
7347
|
+
statusPath: path30.join(canonicalWorkerDir(harnessRoot, run.id, orphan), "worker.json")
|
|
7122
7348
|
}
|
|
7123
7349
|
};
|
|
7124
7350
|
saveRun(run);
|
|
@@ -7141,7 +7367,7 @@ function reconcileWorkerMetadata() {
|
|
|
7141
7367
|
);
|
|
7142
7368
|
}
|
|
7143
7369
|
}
|
|
7144
|
-
return { workers: outcomes };
|
|
7370
|
+
return { workers: outcomes, runMetadataRetention };
|
|
7145
7371
|
}
|
|
7146
7372
|
function reconcileWorkerMetadataCli() {
|
|
7147
7373
|
const result = reconcileWorkerMetadata();
|
|
@@ -7149,7 +7375,22 @@ function reconcileWorkerMetadataCli() {
|
|
|
7149
7375
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
7150
7376
|
return acc;
|
|
7151
7377
|
}, {});
|
|
7152
|
-
|
|
7378
|
+
const runRetentionTotals = result.runMetadataRetention.runs.reduce((acc, row) => {
|
|
7379
|
+
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
7380
|
+
return acc;
|
|
7381
|
+
}, {});
|
|
7382
|
+
console.log(
|
|
7383
|
+
JSON.stringify(
|
|
7384
|
+
{
|
|
7385
|
+
ok: true,
|
|
7386
|
+
totals: byAction,
|
|
7387
|
+
runMetadataRetention: { totals: runRetentionTotals, details: result.runMetadataRetention.runs },
|
|
7388
|
+
details: result.workers
|
|
7389
|
+
},
|
|
7390
|
+
null,
|
|
7391
|
+
2
|
|
7392
|
+
)
|
|
7393
|
+
);
|
|
7153
7394
|
}
|
|
7154
7395
|
|
|
7155
7396
|
// src/stale-reconcile.ts
|
|
@@ -7166,7 +7407,7 @@ function reconcileStaleWorkers() {
|
|
|
7166
7407
|
const now = Date.now();
|
|
7167
7408
|
for (const run of listRunRecords()) {
|
|
7168
7409
|
for (const name of Object.keys(run.workers || {})) {
|
|
7169
|
-
const workerPath =
|
|
7410
|
+
const workerPath = path31.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
7170
7411
|
const worker = readJson(workerPath, void 0);
|
|
7171
7412
|
if (!worker || worker.status !== "running") {
|
|
7172
7413
|
outcomes.push({
|
|
@@ -7249,16 +7490,28 @@ function reconcileRunsCli() {
|
|
|
7249
7490
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
7250
7491
|
return acc;
|
|
7251
7492
|
}, {});
|
|
7493
|
+
const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
|
|
7494
|
+
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
7495
|
+
return acc;
|
|
7496
|
+
}, {});
|
|
7252
7497
|
console.log(
|
|
7253
7498
|
JSON.stringify(
|
|
7254
7499
|
{
|
|
7255
7500
|
ok: true,
|
|
7256
7501
|
workers: { markedExited, killedStale, skipped, total: result.workers.length },
|
|
7257
|
-
metadataReconcile: {
|
|
7502
|
+
metadataReconcile: {
|
|
7503
|
+
totals: metadataTotals,
|
|
7504
|
+
total: result.metadataReconcile.workers.length,
|
|
7505
|
+
runMetadataRetention: {
|
|
7506
|
+
totals: runRetentionTotals,
|
|
7507
|
+
total: result.metadataReconcile.runMetadataRetention.runs.length
|
|
7508
|
+
}
|
|
7509
|
+
},
|
|
7258
7510
|
finalizedRuns: result.finalizedRuns.length,
|
|
7259
7511
|
details: {
|
|
7260
7512
|
workers: result.workers,
|
|
7261
7513
|
metadataReconcile: result.metadataReconcile.workers,
|
|
7514
|
+
runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
|
|
7262
7515
|
finalizedRuns: result.finalizedRuns
|
|
7263
7516
|
}
|
|
7264
7517
|
},
|
|
@@ -7270,7 +7523,7 @@ function reconcileRunsCli() {
|
|
|
7270
7523
|
|
|
7271
7524
|
// src/run-list.ts
|
|
7272
7525
|
function heartbeatByteLength(heartbeatPath) {
|
|
7273
|
-
if (!heartbeatPath || !
|
|
7526
|
+
if (!heartbeatPath || !existsSync25(heartbeatPath)) return 0;
|
|
7274
7527
|
try {
|
|
7275
7528
|
return readFileSync12(heartbeatPath, "utf8").trim().length;
|
|
7276
7529
|
} catch {
|
|
@@ -7278,7 +7531,7 @@ function heartbeatByteLength(heartbeatPath) {
|
|
|
7278
7531
|
}
|
|
7279
7532
|
}
|
|
7280
7533
|
function workerEvidence(run, workerName) {
|
|
7281
|
-
const workerPath =
|
|
7534
|
+
const workerPath = path32.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
|
|
7282
7535
|
const worker = readJson(workerPath, void 0);
|
|
7283
7536
|
if (!worker) {
|
|
7284
7537
|
return {
|
|
@@ -7335,7 +7588,7 @@ function aggregateRunAttention(workers) {
|
|
|
7335
7588
|
function countOpenWorkers(run) {
|
|
7336
7589
|
let open = 0;
|
|
7337
7590
|
for (const name of Object.keys(run.workers || {})) {
|
|
7338
|
-
const workerPath =
|
|
7591
|
+
const workerPath = path32.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
7339
7592
|
const worker = readJson(workerPath, void 0);
|
|
7340
7593
|
if (!worker) continue;
|
|
7341
7594
|
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
@@ -7383,75 +7636,6 @@ function listRunsCli() {
|
|
|
7383
7636
|
console.log(JSON.stringify(buildRunListRows(), null, 2));
|
|
7384
7637
|
}
|
|
7385
7638
|
|
|
7386
|
-
// src/default-repo.ts
|
|
7387
|
-
import path31 from "node:path";
|
|
7388
|
-
function expandConfiguredRepo(value) {
|
|
7389
|
-
return path31.resolve(resolveUserPath(value.trim()));
|
|
7390
|
-
}
|
|
7391
|
-
function fromConfigured(value, source, persistedInConfig) {
|
|
7392
|
-
const trimmed = value?.trim();
|
|
7393
|
-
if (!trimmed) return null;
|
|
7394
|
-
return {
|
|
7395
|
-
repo: expandConfiguredRepo(trimmed),
|
|
7396
|
-
source,
|
|
7397
|
-
persistedInConfig
|
|
7398
|
-
};
|
|
7399
|
-
}
|
|
7400
|
-
function resolveDefaultRepo(opts = {}) {
|
|
7401
|
-
const env = opts.env ?? process.env;
|
|
7402
|
-
const config = opts.config ?? loadUserConfig();
|
|
7403
|
-
const fromConfig = fromConfigured(config.defaultRepo, "config", true);
|
|
7404
|
-
if (fromConfig) return fromConfig;
|
|
7405
|
-
const fromDefaultEnv = fromConfigured(env.KYNVER_DEFAULT_REPO, "env_default_repo", false);
|
|
7406
|
-
if (fromDefaultEnv) return fromDefaultEnv;
|
|
7407
|
-
const fromHarnessEnv = fromConfigured(env.KYNVER_HARNESS_REPO, "env_harness_repo", false);
|
|
7408
|
-
if (fromHarnessEnv) return fromHarnessEnv;
|
|
7409
|
-
const discovered = discoverDefaultRepo({
|
|
7410
|
-
cwd: opts.cwd,
|
|
7411
|
-
runtimeModuleUrl: opts.runtimeModuleUrl
|
|
7412
|
-
});
|
|
7413
|
-
if (!discovered) return null;
|
|
7414
|
-
return {
|
|
7415
|
-
repo: discovered.repo,
|
|
7416
|
-
source: discovered.source,
|
|
7417
|
-
persistedInConfig: false
|
|
7418
|
-
};
|
|
7419
|
-
}
|
|
7420
|
-
function persistDefaultRepo(repo, existing) {
|
|
7421
|
-
const config = {
|
|
7422
|
-
...existing ?? loadUserConfig(),
|
|
7423
|
-
defaultRepo: redactHomePath(path31.resolve(repo))
|
|
7424
|
-
};
|
|
7425
|
-
saveUserConfig(config);
|
|
7426
|
-
return config;
|
|
7427
|
-
}
|
|
7428
|
-
function remediateDefaultRepo(opts) {
|
|
7429
|
-
const existing = opts?.config ?? loadUserConfig();
|
|
7430
|
-
const resolved = resolveDefaultRepo({ ...opts, config: existing });
|
|
7431
|
-
if (!resolved) {
|
|
7432
|
-
return {
|
|
7433
|
-
ok: false,
|
|
7434
|
-
reason: "No Kynver git checkout found. Clone the repo, cd into it, then run `kynver setup --repo /path/to/Kynver` (or export KYNVER_DEFAULT_REPO)."
|
|
7435
|
-
};
|
|
7436
|
-
}
|
|
7437
|
-
if (resolved.persistedInConfig) {
|
|
7438
|
-
return { ok: true, resolved, config: existing };
|
|
7439
|
-
}
|
|
7440
|
-
const config = persistDefaultRepo(resolved.repo, existing);
|
|
7441
|
-
return {
|
|
7442
|
-
ok: true,
|
|
7443
|
-
resolved: { ...resolved, persistedInConfig: true, source: "config" },
|
|
7444
|
-
config
|
|
7445
|
-
};
|
|
7446
|
-
}
|
|
7447
|
-
function formatResolvedDefaultRepo(resolved) {
|
|
7448
|
-
return {
|
|
7449
|
-
defaultRepo: displayUserPath(resolved.repo),
|
|
7450
|
-
source: resolved.source,
|
|
7451
|
-
persistedInConfig: resolved.persistedInConfig
|
|
7452
|
-
};
|
|
7453
|
-
}
|
|
7454
|
-
|
|
7455
7639
|
// src/worktree.ts
|
|
7456
7640
|
function resolveCreateRunRepo(args) {
|
|
7457
7641
|
const explicit = typeof args.repo === "string" ? args.repo.trim() : "";
|
|
@@ -7466,7 +7650,7 @@ function createRun(args) {
|
|
|
7466
7650
|
ensureGitRepo(repo);
|
|
7467
7651
|
const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
|
|
7468
7652
|
const dir = runDirectory(id);
|
|
7469
|
-
if (
|
|
7653
|
+
if (existsSync26(dir)) failExists(`run already exists: ${id}`);
|
|
7470
7654
|
mkdirSync5(dir, { recursive: true });
|
|
7471
7655
|
const base = String(args.base || "origin/main");
|
|
7472
7656
|
const baseCommit = git(repo, ["rev-parse", base]).trim();
|
|
@@ -7480,7 +7664,7 @@ function createRun(args) {
|
|
|
7480
7664
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7481
7665
|
workers: {}
|
|
7482
7666
|
};
|
|
7483
|
-
writeJson(
|
|
7667
|
+
writeJson(path33.join(dir, "run.json"), run);
|
|
7484
7668
|
console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
|
|
7485
7669
|
}
|
|
7486
7670
|
function listRuns() {
|
|
@@ -7492,7 +7676,7 @@ function failExists(message) {
|
|
|
7492
7676
|
}
|
|
7493
7677
|
|
|
7494
7678
|
// src/sweep.ts
|
|
7495
|
-
import
|
|
7679
|
+
import path34 from "node:path";
|
|
7496
7680
|
async function sweepRun(args) {
|
|
7497
7681
|
const pipeline = args.pipeline === true || args.pipeline === "true";
|
|
7498
7682
|
try {
|
|
@@ -7505,7 +7689,7 @@ async function sweepRun(args) {
|
|
|
7505
7689
|
const releasedLocalOrphans = [];
|
|
7506
7690
|
for (const name of Object.keys(run.workers || {})) {
|
|
7507
7691
|
const worker = readJson(
|
|
7508
|
-
|
|
7692
|
+
path34.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
7509
7693
|
void 0
|
|
7510
7694
|
);
|
|
7511
7695
|
if (!worker || !worker.dispatched || !worker.taskId) continue;
|
|
@@ -7553,14 +7737,14 @@ async function sweepRun(args) {
|
|
|
7553
7737
|
}
|
|
7554
7738
|
|
|
7555
7739
|
// src/harness-storage-snapshot.ts
|
|
7556
|
-
import { existsSync as
|
|
7557
|
-
import
|
|
7740
|
+
import { existsSync as existsSync28, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
|
|
7741
|
+
import path36 from "node:path";
|
|
7558
7742
|
|
|
7559
7743
|
// src/cleanup-dir-size.ts
|
|
7560
|
-
import { existsSync as
|
|
7561
|
-
import
|
|
7744
|
+
import { existsSync as existsSync27, readdirSync as readdirSync7, statSync as statSync5 } from "node:fs";
|
|
7745
|
+
import path35 from "node:path";
|
|
7562
7746
|
function directorySizeBytes(root, maxEntries = 5e4) {
|
|
7563
|
-
if (!
|
|
7747
|
+
if (!existsSync27(root)) return 0;
|
|
7564
7748
|
let total = 0;
|
|
7565
7749
|
let seen = 0;
|
|
7566
7750
|
const stack = [root];
|
|
@@ -7568,16 +7752,16 @@ function directorySizeBytes(root, maxEntries = 5e4) {
|
|
|
7568
7752
|
const current = stack.pop();
|
|
7569
7753
|
let entries;
|
|
7570
7754
|
try {
|
|
7571
|
-
entries =
|
|
7755
|
+
entries = readdirSync7(current);
|
|
7572
7756
|
} catch {
|
|
7573
7757
|
continue;
|
|
7574
7758
|
}
|
|
7575
7759
|
for (const name of entries) {
|
|
7576
7760
|
if (seen++ > maxEntries) return null;
|
|
7577
|
-
const full =
|
|
7761
|
+
const full = path35.join(current, name);
|
|
7578
7762
|
let st;
|
|
7579
7763
|
try {
|
|
7580
|
-
st =
|
|
7764
|
+
st = statSync5(full);
|
|
7581
7765
|
} catch {
|
|
7582
7766
|
continue;
|
|
7583
7767
|
}
|
|
@@ -7594,7 +7778,7 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
7594
7778
|
const worktreesDir = harnessWorktreesDir(harnessRoot);
|
|
7595
7779
|
const now = opts.now ?? Date.now();
|
|
7596
7780
|
const scannedAt = new Date(now).toISOString();
|
|
7597
|
-
if (!
|
|
7781
|
+
if (!existsSync28(worktreesDir)) {
|
|
7598
7782
|
return {
|
|
7599
7783
|
harnessRoot,
|
|
7600
7784
|
worktreesDir,
|
|
@@ -7611,7 +7795,7 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
7611
7795
|
let oldestMs = null;
|
|
7612
7796
|
let entries;
|
|
7613
7797
|
try {
|
|
7614
|
-
entries =
|
|
7798
|
+
entries = readdirSync8(worktreesDir, { withFileTypes: true });
|
|
7615
7799
|
} catch {
|
|
7616
7800
|
return {
|
|
7617
7801
|
harnessRoot,
|
|
@@ -7626,14 +7810,14 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
7626
7810
|
for (const runEntry of entries) {
|
|
7627
7811
|
if (!runEntry.isDirectory()) continue;
|
|
7628
7812
|
runCount += 1;
|
|
7629
|
-
const runPath =
|
|
7813
|
+
const runPath = path36.join(worktreesDir, runEntry.name);
|
|
7630
7814
|
try {
|
|
7631
|
-
const st =
|
|
7815
|
+
const st = statSync6(runPath);
|
|
7632
7816
|
oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
|
|
7633
7817
|
} catch {
|
|
7634
7818
|
}
|
|
7635
7819
|
try {
|
|
7636
|
-
for (const workerEntry of
|
|
7820
|
+
for (const workerEntry of readdirSync8(runPath, { withFileTypes: true })) {
|
|
7637
7821
|
if (workerEntry.isDirectory()) workerCount += 1;
|
|
7638
7822
|
}
|
|
7639
7823
|
} catch {
|
|
@@ -7661,10 +7845,10 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
7661
7845
|
}
|
|
7662
7846
|
|
|
7663
7847
|
// src/cleanup.ts
|
|
7664
|
-
import
|
|
7848
|
+
import path47 from "node:path";
|
|
7665
7849
|
|
|
7666
7850
|
// src/cleanup-guards.ts
|
|
7667
|
-
import
|
|
7851
|
+
import path37 from "node:path";
|
|
7668
7852
|
|
|
7669
7853
|
// src/cleanup-run-liveness.ts
|
|
7670
7854
|
function isWorkerProcessLive(indexed) {
|
|
@@ -7788,7 +7972,7 @@ function skipWorktreeRemoval(input) {
|
|
|
7788
7972
|
function skipDependencyCacheRemoval(input) {
|
|
7789
7973
|
const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
|
|
7790
7974
|
if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
7791
|
-
if (activeWorktreePaths.has(
|
|
7975
|
+
if (activeWorktreePaths.has(path37.resolve(worktreePath))) return "active_worker";
|
|
7792
7976
|
if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
|
|
7793
7977
|
if (indexed && hasUnrestorableWorktreeChanges(indexed.status)) return "dirty_worktree";
|
|
7794
7978
|
return null;
|
|
@@ -7815,11 +7999,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
|
7815
7999
|
function collectPreservedLivePaths(actions, skips) {
|
|
7816
8000
|
const out = [];
|
|
7817
8001
|
const seen = /* @__PURE__ */ new Set();
|
|
7818
|
-
const push = (
|
|
7819
|
-
const key = `${
|
|
8002
|
+
const push = (path63, reason, detail) => {
|
|
8003
|
+
const key = `${path63}\0${reason}`;
|
|
7820
8004
|
if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
|
|
7821
8005
|
seen.add(key);
|
|
7822
|
-
out.push({ path:
|
|
8006
|
+
out.push({ path: path63, reason, ...detail ? { detail } : {} });
|
|
7823
8007
|
};
|
|
7824
8008
|
for (const skip2 of skips) {
|
|
7825
8009
|
if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
|
|
@@ -7834,11 +8018,11 @@ function collectPreservedLivePaths(actions, skips) {
|
|
|
7834
8018
|
}
|
|
7835
8019
|
|
|
7836
8020
|
// src/cleanup-run-directory.ts
|
|
7837
|
-
import { existsSync as
|
|
7838
|
-
import
|
|
8021
|
+
import { existsSync as existsSync29, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
|
|
8022
|
+
import path39 from "node:path";
|
|
7839
8023
|
|
|
7840
8024
|
// src/cleanup-active-worktrees.ts
|
|
7841
|
-
import
|
|
8025
|
+
import path38 from "node:path";
|
|
7842
8026
|
function isActiveHarnessWorker2(worker, runBase, runBaseCommit) {
|
|
7843
8027
|
const status = computeWorkerStatus(worker, { base: runBase, baseCommit: runBaseCommit });
|
|
7844
8028
|
return status.alive && !status.finalResult && status.attention.state !== "done";
|
|
@@ -7851,17 +8035,20 @@ function collectActiveWorktreeGuards(harnessRoots) {
|
|
|
7851
8035
|
let runHasLive = false;
|
|
7852
8036
|
for (const name of Object.keys(run.workers || {})) {
|
|
7853
8037
|
const worker = readJson(
|
|
7854
|
-
|
|
8038
|
+
path38.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
|
|
7855
8039
|
void 0
|
|
7856
8040
|
);
|
|
7857
8041
|
if (!worker?.worktreePath) continue;
|
|
7858
|
-
const worktreePath =
|
|
8042
|
+
const worktreePath = path38.resolve(worker.worktreePath);
|
|
7859
8043
|
if (!isActiveHarnessWorker2(worker, run.base, run.baseCommit)) continue;
|
|
7860
8044
|
runHasLive = true;
|
|
7861
8045
|
activeWorktreePaths.add(worktreePath);
|
|
7862
8046
|
}
|
|
7863
8047
|
if (runHasLive) liveRunKeys.add(`${harnessRoot}\0${run.id}`);
|
|
7864
8048
|
}
|
|
8049
|
+
for (const key of collectFilesystemLiveRunKeys(harnessRoot)) {
|
|
8050
|
+
liveRunKeys.add(key);
|
|
8051
|
+
}
|
|
7865
8052
|
}
|
|
7866
8053
|
return { activeWorktreePaths, liveRunKeys };
|
|
7867
8054
|
}
|
|
@@ -7873,20 +8060,20 @@ function isWorktreeOnLiveRun(worktreePath, harnessRoot, runId, liveRunKeys) {
|
|
|
7873
8060
|
// src/cleanup-run-directory.ts
|
|
7874
8061
|
function pathAgeMs(target, now) {
|
|
7875
8062
|
try {
|
|
7876
|
-
const mtime =
|
|
8063
|
+
const mtime = statSync7(target).mtimeMs;
|
|
7877
8064
|
return Math.max(0, now - mtime);
|
|
7878
8065
|
} catch {
|
|
7879
8066
|
return 0;
|
|
7880
8067
|
}
|
|
7881
8068
|
}
|
|
7882
8069
|
function loadRunStatus(harnessRoot, runId) {
|
|
7883
|
-
const runPath =
|
|
7884
|
-
if (!
|
|
8070
|
+
const runPath = path39.join(harnessRoot, "runs", runId, "run.json");
|
|
8071
|
+
if (!existsSync29(runPath)) return null;
|
|
7885
8072
|
return readJson(runPath, null);
|
|
7886
8073
|
}
|
|
7887
8074
|
function runDirectoryIsEmpty(runPath) {
|
|
7888
8075
|
try {
|
|
7889
|
-
const entries =
|
|
8076
|
+
const entries = readdirSync9(runPath);
|
|
7890
8077
|
return entries.length === 0;
|
|
7891
8078
|
} catch {
|
|
7892
8079
|
return false;
|
|
@@ -7904,11 +8091,11 @@ function skipRunDirectoryRemoval(input) {
|
|
|
7904
8091
|
return null;
|
|
7905
8092
|
}
|
|
7906
8093
|
function scanStaleRunDirectoryCandidates(opts) {
|
|
7907
|
-
if (!
|
|
8094
|
+
if (!existsSync29(opts.worktreesDir)) return [];
|
|
7908
8095
|
const candidates = [];
|
|
7909
8096
|
let entries;
|
|
7910
8097
|
try {
|
|
7911
|
-
entries =
|
|
8098
|
+
entries = readdirSync9(opts.worktreesDir, { withFileTypes: true });
|
|
7912
8099
|
} catch {
|
|
7913
8100
|
return [];
|
|
7914
8101
|
}
|
|
@@ -7916,7 +8103,7 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
7916
8103
|
if (!runEntry.isDirectory()) continue;
|
|
7917
8104
|
const runId = runEntry.name;
|
|
7918
8105
|
if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
|
|
7919
|
-
const runPath =
|
|
8106
|
+
const runPath = path39.join(opts.worktreesDir, runId);
|
|
7920
8107
|
if (!runDirectoryIsEmpty(runPath)) continue;
|
|
7921
8108
|
candidates.push({
|
|
7922
8109
|
kind: "remove_run_directory",
|
|
@@ -7931,10 +8118,22 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
7931
8118
|
}
|
|
7932
8119
|
|
|
7933
8120
|
// src/cleanup-execute.ts
|
|
7934
|
-
import { existsSync as
|
|
7935
|
-
import
|
|
8121
|
+
import { existsSync as existsSync30, rmSync as rmSync2 } from "node:fs";
|
|
8122
|
+
import path40 from "node:path";
|
|
8123
|
+
function skipRunMetadataRemoval(candidate) {
|
|
8124
|
+
const harnessRoot = candidate.harnessRoot;
|
|
8125
|
+
if (!harnessRoot || !isHarnessRunMetadataPath(candidate.path, harnessRoot)) return null;
|
|
8126
|
+
return {
|
|
8127
|
+
...candidate,
|
|
8128
|
+
executed: false,
|
|
8129
|
+
skipped: true,
|
|
8130
|
+
skipReason: "run_metadata_protected"
|
|
8131
|
+
};
|
|
8132
|
+
}
|
|
7936
8133
|
function removeDependencyCache(candidate, execute) {
|
|
7937
|
-
|
|
8134
|
+
const metadataSkip = skipRunMetadataRemoval(candidate);
|
|
8135
|
+
if (metadataSkip) return metadataSkip;
|
|
8136
|
+
if (!existsSync30(candidate.path)) {
|
|
7938
8137
|
return {
|
|
7939
8138
|
...candidate,
|
|
7940
8139
|
executed: false,
|
|
@@ -7974,7 +8173,9 @@ function removeBuildCache(candidate, execute) {
|
|
|
7974
8173
|
return removeDependencyCache(candidate, execute);
|
|
7975
8174
|
}
|
|
7976
8175
|
function removeRunDirectory(candidate, execute) {
|
|
7977
|
-
|
|
8176
|
+
const metadataSkip = skipRunMetadataRemoval(candidate);
|
|
8177
|
+
if (metadataSkip) return metadataSkip;
|
|
8178
|
+
if (!existsSync30(candidate.path)) {
|
|
7978
8179
|
return {
|
|
7979
8180
|
...candidate,
|
|
7980
8181
|
executed: false,
|
|
@@ -8005,7 +8206,9 @@ function removeRunDirectory(candidate, execute) {
|
|
|
8005
8206
|
}
|
|
8006
8207
|
}
|
|
8007
8208
|
function removeWorktree(candidate, execute) {
|
|
8008
|
-
|
|
8209
|
+
const metadataSkip = skipRunMetadataRemoval(candidate);
|
|
8210
|
+
if (metadataSkip) return metadataSkip;
|
|
8211
|
+
if (!existsSync30(candidate.path)) {
|
|
8009
8212
|
return {
|
|
8010
8213
|
...candidate,
|
|
8011
8214
|
executed: false,
|
|
@@ -8022,7 +8225,7 @@ function removeWorktree(candidate, execute) {
|
|
|
8022
8225
|
if (repo) {
|
|
8023
8226
|
git(repo, ["worktree", "remove", "--force", candidate.path], { allowFailure: true });
|
|
8024
8227
|
}
|
|
8025
|
-
if (
|
|
8228
|
+
if (existsSync30(candidate.path)) {
|
|
8026
8229
|
rmSync2(candidate.path, { recursive: true, force: true });
|
|
8027
8230
|
}
|
|
8028
8231
|
return {
|
|
@@ -8042,15 +8245,15 @@ function removeWorktree(candidate, execute) {
|
|
|
8042
8245
|
}
|
|
8043
8246
|
}
|
|
8044
8247
|
function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
|
|
8045
|
-
const resolved =
|
|
8046
|
-
const suffix = `${
|
|
8248
|
+
const resolved = path40.resolve(targetPath);
|
|
8249
|
+
const suffix = `${path40.sep}${cacheDirName}`;
|
|
8047
8250
|
const cachePath = resolved.endsWith(suffix) ? resolved : null;
|
|
8048
8251
|
if (!cachePath) return "path_outside_harness";
|
|
8049
|
-
const rel =
|
|
8050
|
-
if (rel.startsWith("..") ||
|
|
8051
|
-
const parts = rel.split(
|
|
8252
|
+
const rel = path40.relative(worktreesDir, cachePath);
|
|
8253
|
+
if (rel.startsWith("..") || path40.isAbsolute(rel)) return "path_outside_harness";
|
|
8254
|
+
const parts = rel.split(path40.sep);
|
|
8052
8255
|
if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
|
|
8053
|
-
if (!resolved.startsWith(
|
|
8256
|
+
if (!resolved.startsWith(path40.resolve(harnessRoot))) return "path_outside_harness";
|
|
8054
8257
|
return null;
|
|
8055
8258
|
}
|
|
8056
8259
|
function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
|
|
@@ -8060,37 +8263,37 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
|
8060
8263
|
return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
|
|
8061
8264
|
}
|
|
8062
8265
|
function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
8063
|
-
const resolved =
|
|
8064
|
-
const relToWt =
|
|
8065
|
-
if (relToWt.startsWith("..") ||
|
|
8066
|
-
const parts = relToWt.split(
|
|
8266
|
+
const resolved = path40.resolve(targetPath);
|
|
8267
|
+
const relToWt = path40.relative(worktreesDir, resolved);
|
|
8268
|
+
if (relToWt.startsWith("..") || path40.isAbsolute(relToWt)) return "path_outside_harness";
|
|
8269
|
+
const parts = relToWt.split(path40.sep);
|
|
8067
8270
|
if (parts.length < 3) return "path_outside_harness";
|
|
8068
|
-
if (!resolved.startsWith(
|
|
8271
|
+
if (!resolved.startsWith(path40.resolve(harnessRoot))) return "path_outside_harness";
|
|
8069
8272
|
return null;
|
|
8070
8273
|
}
|
|
8071
8274
|
|
|
8072
8275
|
// src/cleanup-scan.ts
|
|
8073
|
-
import { existsSync as
|
|
8074
|
-
import
|
|
8276
|
+
import { existsSync as existsSync31, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
|
|
8277
|
+
import path41 from "node:path";
|
|
8075
8278
|
function pathAgeMs2(target, now) {
|
|
8076
8279
|
try {
|
|
8077
|
-
const mtime =
|
|
8280
|
+
const mtime = statSync8(target).mtimeMs;
|
|
8078
8281
|
return Math.max(0, now - mtime);
|
|
8079
8282
|
} catch {
|
|
8080
8283
|
return 0;
|
|
8081
8284
|
}
|
|
8082
8285
|
}
|
|
8083
8286
|
function isPathInside(child, parent) {
|
|
8084
|
-
const rel =
|
|
8085
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
8287
|
+
const rel = path41.relative(parent, child);
|
|
8288
|
+
return rel === "" || !rel.startsWith("..") && !path41.isAbsolute(rel);
|
|
8086
8289
|
}
|
|
8087
8290
|
function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
|
|
8088
8291
|
const out = [];
|
|
8089
8292
|
for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
|
|
8090
8293
|
if (rel === ".next") continue;
|
|
8091
|
-
const target =
|
|
8092
|
-
if (!
|
|
8093
|
-
const resolved =
|
|
8294
|
+
const target = path41.join(worktreePath, rel);
|
|
8295
|
+
if (!existsSync31(target)) continue;
|
|
8296
|
+
const resolved = path41.resolve(target);
|
|
8094
8297
|
if (seen.has(resolved)) continue;
|
|
8095
8298
|
if (!isPathInside(resolved, opts.harnessRoot)) continue;
|
|
8096
8299
|
seen.add(resolved);
|
|
@@ -8119,13 +8322,13 @@ function scanBuildCacheCandidates(opts) {
|
|
|
8119
8322
|
})
|
|
8120
8323
|
);
|
|
8121
8324
|
}
|
|
8122
|
-
if (!opts.includeOrphans || !
|
|
8123
|
-
for (const runEntry of
|
|
8325
|
+
if (!opts.includeOrphans || !existsSync31(opts.worktreesDir)) return candidates;
|
|
8326
|
+
for (const runEntry of readdirSync10(opts.worktreesDir, { withFileTypes: true })) {
|
|
8124
8327
|
if (!runEntry.isDirectory()) continue;
|
|
8125
|
-
const runPath =
|
|
8126
|
-
for (const workerEntry of
|
|
8328
|
+
const runPath = path41.join(opts.worktreesDir, runEntry.name);
|
|
8329
|
+
for (const workerEntry of readdirSync10(runPath, { withFileTypes: true })) {
|
|
8127
8330
|
if (!workerEntry.isDirectory()) continue;
|
|
8128
|
-
const worktreePath =
|
|
8331
|
+
const worktreePath = path41.join(runPath, workerEntry.name);
|
|
8129
8332
|
candidates.push(
|
|
8130
8333
|
...collectBuildCacheForWorktree(worktreePath, opts, seen, {
|
|
8131
8334
|
runId: runEntry.name,
|
|
@@ -8146,7 +8349,7 @@ function scanWorktreeCandidates(opts) {
|
|
|
8146
8349
|
for (const entry of opts.index.values()) {
|
|
8147
8350
|
if (opts.runIdFilter && entry.runId !== opts.runIdFilter) continue;
|
|
8148
8351
|
const resolved = entry.worktreePath;
|
|
8149
|
-
if (!
|
|
8352
|
+
if (!existsSync31(resolved)) continue;
|
|
8150
8353
|
if (seen.has(resolved)) continue;
|
|
8151
8354
|
seen.add(resolved);
|
|
8152
8355
|
candidates.push({
|
|
@@ -8160,24 +8363,24 @@ function scanWorktreeCandidates(opts) {
|
|
|
8160
8363
|
});
|
|
8161
8364
|
}
|
|
8162
8365
|
}
|
|
8163
|
-
if (!orphanEnabled || !
|
|
8366
|
+
if (!orphanEnabled || !existsSync31(opts.worktreesDir)) return candidates;
|
|
8164
8367
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
8165
8368
|
for (const entry of opts.index.values()) {
|
|
8166
|
-
indexedPaths.add(
|
|
8369
|
+
indexedPaths.add(path41.resolve(entry.worktreePath));
|
|
8167
8370
|
}
|
|
8168
|
-
for (const runEntry of
|
|
8371
|
+
for (const runEntry of readdirSync10(opts.worktreesDir, { withFileTypes: true })) {
|
|
8169
8372
|
if (!runEntry.isDirectory()) continue;
|
|
8170
8373
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
8171
|
-
const runPath =
|
|
8374
|
+
const runPath = path41.join(opts.worktreesDir, runEntry.name);
|
|
8172
8375
|
let workerEntries;
|
|
8173
8376
|
try {
|
|
8174
|
-
workerEntries =
|
|
8377
|
+
workerEntries = readdirSync10(runPath, { withFileTypes: true });
|
|
8175
8378
|
} catch {
|
|
8176
8379
|
continue;
|
|
8177
8380
|
}
|
|
8178
8381
|
for (const workerEntry of workerEntries) {
|
|
8179
8382
|
if (!workerEntry.isDirectory()) continue;
|
|
8180
|
-
const worktreePath =
|
|
8383
|
+
const worktreePath = path41.resolve(path41.join(runPath, workerEntry.name));
|
|
8181
8384
|
if (seen.has(worktreePath)) continue;
|
|
8182
8385
|
if (indexedPaths.has(worktreePath)) continue;
|
|
8183
8386
|
if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
|
|
@@ -8196,27 +8399,27 @@ function scanWorktreeCandidates(opts) {
|
|
|
8196
8399
|
}
|
|
8197
8400
|
|
|
8198
8401
|
// src/cleanup-dependency-scan.ts
|
|
8199
|
-
import { existsSync as
|
|
8200
|
-
import
|
|
8402
|
+
import { existsSync as existsSync32, readdirSync as readdirSync11, statSync as statSync9 } from "node:fs";
|
|
8403
|
+
import path42 from "node:path";
|
|
8201
8404
|
var DEPENDENCY_CACHE_DIRS = [
|
|
8202
8405
|
{ dirName: "node_modules", kind: "remove_node_modules" },
|
|
8203
8406
|
{ dirName: ".next", kind: "remove_next_cache" }
|
|
8204
8407
|
];
|
|
8205
8408
|
function pathAgeMs3(target, now) {
|
|
8206
8409
|
try {
|
|
8207
|
-
const mtime =
|
|
8410
|
+
const mtime = statSync9(target).mtimeMs;
|
|
8208
8411
|
return Math.max(0, now - mtime);
|
|
8209
8412
|
} catch {
|
|
8210
8413
|
return 0;
|
|
8211
8414
|
}
|
|
8212
8415
|
}
|
|
8213
8416
|
function isPathInside2(child, parent) {
|
|
8214
|
-
const rel =
|
|
8215
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
8417
|
+
const rel = path42.relative(parent, child);
|
|
8418
|
+
return rel === "" || !rel.startsWith("..") && !path42.isAbsolute(rel);
|
|
8216
8419
|
}
|
|
8217
8420
|
function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
8218
|
-
if (!
|
|
8219
|
-
const resolved =
|
|
8421
|
+
if (!existsSync32(targetPath)) return;
|
|
8422
|
+
const resolved = path42.resolve(targetPath);
|
|
8220
8423
|
if (seen.has(resolved)) return;
|
|
8221
8424
|
if (!isPathInside2(resolved, opts.harnessRoot)) return;
|
|
8222
8425
|
seen.add(resolved);
|
|
@@ -8233,7 +8436,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
|
8233
8436
|
}
|
|
8234
8437
|
function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
|
|
8235
8438
|
for (const entry of DEPENDENCY_CACHE_DIRS) {
|
|
8236
|
-
pushCandidate2(candidates, seen, opts,
|
|
8439
|
+
pushCandidate2(candidates, seen, opts, path42.join(worktreePath, entry.dirName), entry.kind, meta);
|
|
8237
8440
|
}
|
|
8238
8441
|
}
|
|
8239
8442
|
function scanDependencyCacheCandidates(opts) {
|
|
@@ -8247,20 +8450,20 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
8247
8450
|
repo: entry.run.repo
|
|
8248
8451
|
});
|
|
8249
8452
|
}
|
|
8250
|
-
if (!
|
|
8251
|
-
for (const runEntry of
|
|
8453
|
+
if (!existsSync32(opts.worktreesDir)) return candidates;
|
|
8454
|
+
for (const runEntry of readdirSync11(opts.worktreesDir, { withFileTypes: true })) {
|
|
8252
8455
|
if (!runEntry.isDirectory()) continue;
|
|
8253
8456
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
8254
|
-
const runPath =
|
|
8457
|
+
const runPath = path42.join(opts.worktreesDir, runEntry.name);
|
|
8255
8458
|
let workerEntries;
|
|
8256
8459
|
try {
|
|
8257
|
-
workerEntries =
|
|
8460
|
+
workerEntries = readdirSync11(runPath, { withFileTypes: true });
|
|
8258
8461
|
} catch {
|
|
8259
8462
|
continue;
|
|
8260
8463
|
}
|
|
8261
8464
|
for (const workerEntry of workerEntries) {
|
|
8262
8465
|
if (!workerEntry.isDirectory()) continue;
|
|
8263
|
-
const worktreePath =
|
|
8466
|
+
const worktreePath = path42.join(runPath, workerEntry.name);
|
|
8264
8467
|
scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
|
|
8265
8468
|
runId: runEntry.name,
|
|
8266
8469
|
worker: workerEntry.name
|
|
@@ -8271,11 +8474,11 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
8271
8474
|
}
|
|
8272
8475
|
|
|
8273
8476
|
// src/cleanup-duplicate-worktrees.ts
|
|
8274
|
-
import { existsSync as
|
|
8275
|
-
import
|
|
8477
|
+
import { existsSync as existsSync33, statSync as statSync10 } from "node:fs";
|
|
8478
|
+
import path43 from "node:path";
|
|
8276
8479
|
function pathAgeMs4(target, now) {
|
|
8277
8480
|
try {
|
|
8278
|
-
const mtime =
|
|
8481
|
+
const mtime = statSync10(target).mtimeMs;
|
|
8279
8482
|
return Math.max(0, now - mtime);
|
|
8280
8483
|
} catch {
|
|
8281
8484
|
return 0;
|
|
@@ -8302,8 +8505,8 @@ function parseWorktreePorcelain(output) {
|
|
|
8302
8505
|
return records;
|
|
8303
8506
|
}
|
|
8304
8507
|
function isUnderWorktreesDir(worktreePath, worktreesDir) {
|
|
8305
|
-
const rel =
|
|
8306
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
8508
|
+
const rel = path43.relative(path43.resolve(worktreesDir), path43.resolve(worktreePath));
|
|
8509
|
+
return rel !== "" && !rel.startsWith("..") && !path43.isAbsolute(rel);
|
|
8307
8510
|
}
|
|
8308
8511
|
function isCleanWorktree(worktreePath, repoRoot) {
|
|
8309
8512
|
try {
|
|
@@ -8316,14 +8519,14 @@ function isCleanWorktree(worktreePath, repoRoot) {
|
|
|
8316
8519
|
}
|
|
8317
8520
|
}
|
|
8318
8521
|
function scanDuplicateWorktreeCandidates(opts) {
|
|
8319
|
-
if (!opts.includeOrphans || !
|
|
8522
|
+
if (!opts.includeOrphans || !existsSync33(opts.worktreesDir)) return [];
|
|
8320
8523
|
const repos = /* @__PURE__ */ new Set();
|
|
8321
8524
|
for (const entry of opts.index.values()) {
|
|
8322
|
-
if (entry.run.repo) repos.add(
|
|
8525
|
+
if (entry.run.repo) repos.add(path43.resolve(entry.run.repo));
|
|
8323
8526
|
}
|
|
8324
8527
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
8325
8528
|
for (const entry of opts.index.values()) {
|
|
8326
|
-
indexedPaths.add(
|
|
8529
|
+
indexedPaths.add(path43.resolve(entry.worktreePath));
|
|
8327
8530
|
}
|
|
8328
8531
|
const candidates = [];
|
|
8329
8532
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8336,15 +8539,15 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
8336
8539
|
}
|
|
8337
8540
|
const worktrees = parseWorktreePorcelain(porcelain);
|
|
8338
8541
|
for (const wt of worktrees) {
|
|
8339
|
-
const resolved =
|
|
8340
|
-
if (resolved ===
|
|
8542
|
+
const resolved = path43.resolve(wt.path);
|
|
8543
|
+
if (resolved === path43.resolve(repoRoot)) continue;
|
|
8341
8544
|
if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
|
|
8342
8545
|
if (indexedPaths.has(resolved)) continue;
|
|
8343
8546
|
if (seen.has(resolved)) continue;
|
|
8344
|
-
if (!
|
|
8547
|
+
if (!existsSync33(resolved)) continue;
|
|
8345
8548
|
if (!isCleanWorktree(resolved, repoRoot)) continue;
|
|
8346
|
-
const rel =
|
|
8347
|
-
const parts = rel.split(
|
|
8549
|
+
const rel = path43.relative(opts.worktreesDir, resolved);
|
|
8550
|
+
const parts = rel.split(path43.sep);
|
|
8348
8551
|
const runId = parts[0];
|
|
8349
8552
|
const worker = parts[1] ?? "unknown";
|
|
8350
8553
|
seen.add(resolved);
|
|
@@ -8363,12 +8566,12 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
8363
8566
|
}
|
|
8364
8567
|
|
|
8365
8568
|
// src/cleanup-worktree-index.ts
|
|
8366
|
-
import
|
|
8569
|
+
import path44 from "node:path";
|
|
8367
8570
|
function buildWorktreeIndexAt(harnessRoot) {
|
|
8368
8571
|
const index = /* @__PURE__ */ new Map();
|
|
8369
8572
|
for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
|
|
8370
8573
|
for (const name of Object.keys(run.workers || {})) {
|
|
8371
|
-
const workerPath =
|
|
8574
|
+
const workerPath = path44.join(
|
|
8372
8575
|
runDirectoryAt(harnessRoot, run.id),
|
|
8373
8576
|
"workers",
|
|
8374
8577
|
safeSlug(name),
|
|
@@ -8377,9 +8580,9 @@ function buildWorktreeIndexAt(harnessRoot) {
|
|
|
8377
8580
|
const worker = readJson(workerPath, void 0);
|
|
8378
8581
|
if (!worker?.worktreePath) continue;
|
|
8379
8582
|
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
8380
|
-
index.set(
|
|
8583
|
+
index.set(path44.resolve(worker.worktreePath), {
|
|
8381
8584
|
harnessRoot,
|
|
8382
|
-
worktreePath:
|
|
8585
|
+
worktreePath: path44.resolve(worker.worktreePath),
|
|
8383
8586
|
runId: run.id,
|
|
8384
8587
|
workerName: name,
|
|
8385
8588
|
run,
|
|
@@ -8443,15 +8646,15 @@ function resolvePipelineHarnessRetention(runId) {
|
|
|
8443
8646
|
}
|
|
8444
8647
|
|
|
8445
8648
|
// src/cleanup-orphan-safety.ts
|
|
8446
|
-
import { existsSync as
|
|
8447
|
-
import
|
|
8649
|
+
import { existsSync as existsSync34, statSync as statSync11 } from "node:fs";
|
|
8650
|
+
import path45 from "node:path";
|
|
8448
8651
|
var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
|
|
8449
8652
|
function assessOrphanWorktreeSafety(input) {
|
|
8450
8653
|
const now = input.now ?? Date.now();
|
|
8451
8654
|
const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
|
|
8452
|
-
if (!
|
|
8655
|
+
if (!existsSync34(input.worktreePath)) return null;
|
|
8453
8656
|
if (input.runId && input.workerName) {
|
|
8454
|
-
const heartbeatPath =
|
|
8657
|
+
const heartbeatPath = path45.join(
|
|
8455
8658
|
input.harnessRoot,
|
|
8456
8659
|
"runs",
|
|
8457
8660
|
input.runId,
|
|
@@ -8460,13 +8663,13 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
8460
8663
|
"heartbeat.jsonl"
|
|
8461
8664
|
);
|
|
8462
8665
|
try {
|
|
8463
|
-
const mtime =
|
|
8666
|
+
const mtime = statSync11(heartbeatPath).mtimeMs;
|
|
8464
8667
|
if (now - mtime < heartbeatFreshMs) return "active_worker";
|
|
8465
8668
|
} catch {
|
|
8466
8669
|
}
|
|
8467
8670
|
}
|
|
8468
|
-
const gitDir =
|
|
8469
|
-
if (!
|
|
8671
|
+
const gitDir = path45.join(input.worktreePath, ".git");
|
|
8672
|
+
if (!existsSync34(gitDir)) return null;
|
|
8470
8673
|
const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
|
|
8471
8674
|
if (porcelain.status !== 0) return "pr_or_unmerged_commits";
|
|
8472
8675
|
const dirtyLines = porcelain.stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
@@ -8495,12 +8698,12 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
8495
8698
|
}
|
|
8496
8699
|
|
|
8497
8700
|
// src/cleanup-harness-roots.ts
|
|
8498
|
-
import { existsSync as
|
|
8701
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
8499
8702
|
import { homedir as homedir11 } from "node:os";
|
|
8500
|
-
import
|
|
8703
|
+
import path46 from "node:path";
|
|
8501
8704
|
var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
|
|
8502
8705
|
"/var/tmp/kynver-harness",
|
|
8503
|
-
|
|
8706
|
+
path46.join(homedir11(), ".openclaw", "harness")
|
|
8504
8707
|
];
|
|
8505
8708
|
function addRoot(seen, roots, candidate) {
|
|
8506
8709
|
if (!candidate?.trim()) return;
|
|
@@ -8522,8 +8725,8 @@ function resolveHarnessScanRoots(options = {}) {
|
|
|
8522
8725
|
for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
|
|
8523
8726
|
if (shouldScanWellKnownRoots(options)) {
|
|
8524
8727
|
for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
|
|
8525
|
-
const resolved =
|
|
8526
|
-
if (!seen.has(resolved) &&
|
|
8728
|
+
const resolved = path46.resolve(candidate);
|
|
8729
|
+
if (!seen.has(resolved) && existsSync35(resolved)) addRoot(seen, roots, resolved);
|
|
8527
8730
|
}
|
|
8528
8731
|
}
|
|
8529
8732
|
return roots;
|
|
@@ -8612,9 +8815,9 @@ function mergeWorktreeIndexes(scanRoots) {
|
|
|
8612
8815
|
}
|
|
8613
8816
|
function worktreePathForCandidate(candidate, worktreesDir) {
|
|
8614
8817
|
if (candidate.runId && candidate.worker) {
|
|
8615
|
-
return
|
|
8818
|
+
return path47.join(worktreesDir, candidate.runId, candidate.worker);
|
|
8616
8819
|
}
|
|
8617
|
-
return
|
|
8820
|
+
return path47.resolve(candidate.path, "..");
|
|
8618
8821
|
}
|
|
8619
8822
|
function runHarnessCleanup(options = {}) {
|
|
8620
8823
|
let retention = resolveHarnessRetention(options);
|
|
@@ -8631,7 +8834,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
8631
8834
|
const atSweepCap = () => actions.length >= maxActions;
|
|
8632
8835
|
for (const harnessRoot of paths.scanRoots) {
|
|
8633
8836
|
if (atSweepCap()) break;
|
|
8634
|
-
const worktreesDir =
|
|
8837
|
+
const worktreesDir = path47.join(harnessRoot, "worktrees");
|
|
8635
8838
|
const scanOpts = {
|
|
8636
8839
|
harnessRoot,
|
|
8637
8840
|
worktreesDir,
|
|
@@ -8645,7 +8848,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
8645
8848
|
for (const raw of scanDependencyCacheCandidates(scanOpts)) {
|
|
8646
8849
|
if (atSweepCap()) break;
|
|
8647
8850
|
const candidate = attachCandidateBytes(raw, retention.accountBytes);
|
|
8648
|
-
const resolved =
|
|
8851
|
+
const resolved = path47.resolve(candidate.path);
|
|
8649
8852
|
if (processedPaths.has(resolved)) continue;
|
|
8650
8853
|
processedPaths.add(resolved);
|
|
8651
8854
|
const pathSkip = pathGuardForDependencyCache(candidate, harnessRoot, worktreesDir);
|
|
@@ -8655,7 +8858,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
8655
8858
|
continue;
|
|
8656
8859
|
}
|
|
8657
8860
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
8658
|
-
const indexed = index.get(
|
|
8861
|
+
const indexed = index.get(path47.resolve(worktreePath)) ?? null;
|
|
8659
8862
|
const guardReason = skipDependencyCacheRemoval({
|
|
8660
8863
|
indexed,
|
|
8661
8864
|
includeOrphans: true,
|
|
@@ -8675,7 +8878,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
8675
8878
|
for (const raw of scanBuildCacheCandidates(scanOpts)) {
|
|
8676
8879
|
if (atSweepCap()) break;
|
|
8677
8880
|
const candidate = attachCandidateBytes(raw, retention.accountBytes);
|
|
8678
|
-
const resolved =
|
|
8881
|
+
const resolved = path47.resolve(candidate.path);
|
|
8679
8882
|
if (processedPaths.has(resolved)) continue;
|
|
8680
8883
|
processedPaths.add(resolved);
|
|
8681
8884
|
const pathSkip = isHarnessBuildCachePath(candidate.path, harnessRoot, worktreesDir);
|
|
@@ -8685,7 +8888,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
8685
8888
|
continue;
|
|
8686
8889
|
}
|
|
8687
8890
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
8688
|
-
const indexed = index.get(
|
|
8891
|
+
const indexed = index.get(path47.resolve(worktreePath)) ?? null;
|
|
8689
8892
|
const guardReason = skipBuildCacheRemoval({
|
|
8690
8893
|
indexed,
|
|
8691
8894
|
includeOrphans: true,
|
|
@@ -8709,11 +8912,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
8709
8912
|
const worktreeSeen = /* @__PURE__ */ new Set();
|
|
8710
8913
|
for (const raw of worktreeCandidates) {
|
|
8711
8914
|
if (atSweepCap()) break;
|
|
8712
|
-
const resolved =
|
|
8915
|
+
const resolved = path47.resolve(raw.path);
|
|
8713
8916
|
if (worktreeSeen.has(resolved)) continue;
|
|
8714
8917
|
worktreeSeen.add(resolved);
|
|
8715
8918
|
const candidate = attachCandidateBytes({ ...raw, path: resolved }, retention.accountBytes);
|
|
8716
|
-
const indexed = index.get(
|
|
8919
|
+
const indexed = index.get(path47.resolve(candidate.path)) ?? null;
|
|
8717
8920
|
const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
|
|
8718
8921
|
worktreePath: candidate.path,
|
|
8719
8922
|
harnessRoot,
|
|
@@ -8723,7 +8926,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
8723
8926
|
});
|
|
8724
8927
|
const guardSkip = skipWorktreeRemoval({
|
|
8725
8928
|
indexed,
|
|
8726
|
-
worktreePath:
|
|
8929
|
+
worktreePath: path47.resolve(candidate.path),
|
|
8727
8930
|
includeOrphans: retention.includeOrphans,
|
|
8728
8931
|
worktreesAgeMs: retention.worktreesAgeMs,
|
|
8729
8932
|
terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
|
|
@@ -8750,10 +8953,10 @@ function runHarnessCleanup(options = {}) {
|
|
|
8750
8953
|
})) {
|
|
8751
8954
|
if (atSweepCap()) break;
|
|
8752
8955
|
const candidate = attachCandidateBytes(raw, retention.accountBytes);
|
|
8753
|
-
const resolved =
|
|
8956
|
+
const resolved = path47.resolve(candidate.path);
|
|
8754
8957
|
if (processedPaths.has(resolved)) continue;
|
|
8755
8958
|
processedPaths.add(resolved);
|
|
8756
|
-
const runId = candidate.runId ??
|
|
8959
|
+
const runId = candidate.runId ?? path47.basename(resolved);
|
|
8757
8960
|
const dirSkip = skipRunDirectoryRemoval({
|
|
8758
8961
|
harnessRoot,
|
|
8759
8962
|
runId,
|
|
@@ -8845,8 +9048,8 @@ import { mkdirSync as mkdirSync7, realpathSync } from "node:fs";
|
|
|
8845
9048
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
8846
9049
|
|
|
8847
9050
|
// src/discard-disposable.ts
|
|
8848
|
-
import { existsSync as
|
|
8849
|
-
import
|
|
9051
|
+
import { existsSync as existsSync36, rmSync as rmSync3 } from "node:fs";
|
|
9052
|
+
import path48 from "node:path";
|
|
8850
9053
|
function normalizeRelativePath2(value) {
|
|
8851
9054
|
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
8852
9055
|
if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
|
|
@@ -8867,15 +9070,15 @@ function discardDisposableArtifacts(args) {
|
|
|
8867
9070
|
if (paths.length === 0) {
|
|
8868
9071
|
return { ok: false, removed: [], reason: "requires at least one --path" };
|
|
8869
9072
|
}
|
|
8870
|
-
const worktreeRoot =
|
|
9073
|
+
const worktreeRoot = path48.resolve(worker.worktreePath);
|
|
8871
9074
|
const removed = [];
|
|
8872
9075
|
for (const raw of paths) {
|
|
8873
9076
|
const rel = normalizeRelativePath2(raw);
|
|
8874
|
-
const abs =
|
|
8875
|
-
if (!abs.startsWith(worktreeRoot +
|
|
9077
|
+
const abs = path48.resolve(worktreeRoot, rel);
|
|
9078
|
+
if (!abs.startsWith(worktreeRoot + path48.sep) && abs !== worktreeRoot) {
|
|
8876
9079
|
return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
|
|
8877
9080
|
}
|
|
8878
|
-
if (!
|
|
9081
|
+
if (!existsSync36(abs)) {
|
|
8879
9082
|
return { ok: false, removed, reason: `path not found: ${raw}` };
|
|
8880
9083
|
}
|
|
8881
9084
|
rmSync3(abs, { recursive: true, force: true });
|
|
@@ -8897,8 +9100,472 @@ function discardDisposableCli(args) {
|
|
|
8897
9100
|
if (!result.ok) process.exit(1);
|
|
8898
9101
|
}
|
|
8899
9102
|
|
|
8900
|
-
// src/
|
|
9103
|
+
// src/cron/cron-env.ts
|
|
9104
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
9105
|
+
import { homedir as homedir12 } from "node:os";
|
|
9106
|
+
import path49 from "node:path";
|
|
9107
|
+
function envFlag3(name, defaultValue) {
|
|
9108
|
+
const raw = process.env[name]?.trim().toLowerCase();
|
|
9109
|
+
if (!raw) return defaultValue;
|
|
9110
|
+
if (raw === "0" || raw === "false" || raw === "no" || raw === "off") return false;
|
|
9111
|
+
if (raw === "1" || raw === "true" || raw === "yes" || raw === "on") return true;
|
|
9112
|
+
return defaultValue;
|
|
9113
|
+
}
|
|
9114
|
+
function envInt(name, fallback, min = 1) {
|
|
9115
|
+
const n = Number(process.env[name]);
|
|
9116
|
+
if (!Number.isFinite(n) || n < min) return fallback;
|
|
9117
|
+
return Math.floor(n);
|
|
9118
|
+
}
|
|
9119
|
+
function defaultKynverCronStorePath() {
|
|
9120
|
+
const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
|
|
9121
|
+
if (explicit) return explicit;
|
|
9122
|
+
return path49.join(homedir12(), ".kynver", "agent-os-cron.json");
|
|
9123
|
+
}
|
|
9124
|
+
function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
|
|
9125
|
+
const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
|
|
9126
|
+
if (explicit) return explicit;
|
|
9127
|
+
return `${storePath.replace(/\.json$/i, "")}.tick-state.json`;
|
|
9128
|
+
}
|
|
9129
|
+
function resolveKynverCronFireBaseUrl() {
|
|
9130
|
+
const config = loadUserConfig();
|
|
9131
|
+
return process.env.KYNVER_API_URL?.trim() || config.apiBaseUrl?.trim() || process.env.KYNVER_CRON_FIRE_BASE_URL?.trim() || process.env.OPENCLAW_CRON_FIRE_BASE_URL?.trim() || null;
|
|
9132
|
+
}
|
|
9133
|
+
function resolveKynverCronSecret() {
|
|
9134
|
+
return process.env.KYNVER_CRON_SECRET?.trim() || process.env.OPENCLAW_CRON_SECRET?.trim() || process.env.KYNVER_RUNTIME_SECRET?.trim() || null;
|
|
9135
|
+
}
|
|
9136
|
+
function resolveKynverCronEnv() {
|
|
9137
|
+
const storePath = defaultKynverCronStorePath();
|
|
9138
|
+
const statePath = defaultKynverCronStatePath(storePath);
|
|
9139
|
+
const fireBaseUrl = resolveKynverCronFireBaseUrl();
|
|
9140
|
+
const secret = resolveKynverCronSecret();
|
|
9141
|
+
const credsReady = Boolean(fireBaseUrl && secret);
|
|
9142
|
+
const storeExists = existsSync37(storePath);
|
|
9143
|
+
const defaultEnabled = credsReady && (storeExists || envFlag3("KYNVER_CRON_TICK_FORCE", false));
|
|
9144
|
+
return {
|
|
9145
|
+
storePath,
|
|
9146
|
+
statePath,
|
|
9147
|
+
lockPath: `${statePath}.lock`,
|
|
9148
|
+
fireBaseUrl,
|
|
9149
|
+
secret,
|
|
9150
|
+
tickEnabled: envFlag3("KYNVER_CRON_TICK_ENABLED", defaultEnabled),
|
|
9151
|
+
tickIntervalMs: envInt("KYNVER_CRON_TICK_INTERVAL_MS", 6e4, 5e3),
|
|
9152
|
+
missedRunPolicy: process.env.KYNVER_CRON_MISSED_RUN_POLICY?.trim().toLowerCase() === "skip" ? "skip" : "catch_up",
|
|
9153
|
+
maxCatchUpPerTick: envInt("KYNVER_CRON_MAX_CATCH_UP_PER_TICK", 3, 0),
|
|
9154
|
+
maxRetries: envInt("KYNVER_CRON_MAX_RETRIES", 3, 0),
|
|
9155
|
+
retryBackoffMs: envInt("KYNVER_CRON_RETRY_BACKOFF_MS", 6e4, 1e3),
|
|
9156
|
+
inflightLeaseMs: envInt("KYNVER_CRON_INFLIGHT_LEASE_MS", 12e4, 5e3)
|
|
9157
|
+
};
|
|
9158
|
+
}
|
|
9159
|
+
function isKynverCronDaemonPrimary(env = resolveKynverCronEnv()) {
|
|
9160
|
+
return env.tickEnabled && Boolean(env.fireBaseUrl && env.secret);
|
|
9161
|
+
}
|
|
9162
|
+
|
|
9163
|
+
// src/cron/cron-fire.ts
|
|
9164
|
+
function trimTrailingSlash2(url) {
|
|
9165
|
+
return url.replace(/\/+$/, "");
|
|
9166
|
+
}
|
|
9167
|
+
async function fireKynverCronJob(input) {
|
|
9168
|
+
const doFetch = input.fetchFn ?? fetch;
|
|
9169
|
+
const callbackPath = input.entry.spec.callbackPath.startsWith("/") ? input.entry.spec.callbackPath : `/${input.entry.spec.callbackPath}`;
|
|
9170
|
+
const url = `${trimTrailingSlash2(input.baseUrl)}${callbackPath}`;
|
|
9171
|
+
const jobId = input.jobId ?? input.entry.spec.dedupeKey ?? null;
|
|
9172
|
+
const body = {
|
|
9173
|
+
source: "kynver-cron",
|
|
9174
|
+
jobId,
|
|
9175
|
+
agentOsId: input.entry.spec.target.agentOsId,
|
|
9176
|
+
kind: input.entry.spec.kind,
|
|
9177
|
+
target: input.entry.spec.target,
|
|
9178
|
+
...input.entry.spec.payload !== void 0 && { payload: input.entry.spec.payload }
|
|
9179
|
+
};
|
|
9180
|
+
const res = await doFetch(url, {
|
|
9181
|
+
method: "POST",
|
|
9182
|
+
headers: buildHarnessCallbackHeaders(input.secret),
|
|
9183
|
+
body: JSON.stringify(body)
|
|
9184
|
+
});
|
|
9185
|
+
let parsed = null;
|
|
9186
|
+
try {
|
|
9187
|
+
parsed = await res.json();
|
|
9188
|
+
} catch {
|
|
9189
|
+
parsed = null;
|
|
9190
|
+
}
|
|
9191
|
+
return { ok: res.ok, status: res.status, body: parsed };
|
|
9192
|
+
}
|
|
9193
|
+
|
|
9194
|
+
// src/cron/cron-lock.ts
|
|
9195
|
+
import { closeSync as closeSync6, existsSync as existsSync38, openSync as openSync6, readFileSync as readFileSync13, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
9196
|
+
var STALE_LOCK_MS = 10 * 6e4;
|
|
9197
|
+
function readLockInfo(lockPath) {
|
|
9198
|
+
if (!existsSync38(lockPath)) return null;
|
|
9199
|
+
try {
|
|
9200
|
+
const parsed = JSON.parse(readFileSync13(lockPath, "utf8"));
|
|
9201
|
+
if (typeof parsed.pid === "number" && typeof parsed.at === "string") return parsed;
|
|
9202
|
+
} catch {
|
|
9203
|
+
return null;
|
|
9204
|
+
}
|
|
9205
|
+
return null;
|
|
9206
|
+
}
|
|
9207
|
+
function lockIsStale(lockPath) {
|
|
9208
|
+
const info = readLockInfo(lockPath);
|
|
9209
|
+
if (!info) return true;
|
|
9210
|
+
if (!isPidAlive(info.pid)) return true;
|
|
9211
|
+
const atMs = Date.parse(info.at);
|
|
9212
|
+
if (Number.isNaN(atMs)) return true;
|
|
9213
|
+
return Date.now() - atMs > STALE_LOCK_MS;
|
|
9214
|
+
}
|
|
9215
|
+
function tryAcquireCronTickLock(lockPath) {
|
|
9216
|
+
if (existsSync38(lockPath) && !lockIsStale(lockPath)) {
|
|
9217
|
+
const info = readLockInfo(lockPath);
|
|
9218
|
+
return {
|
|
9219
|
+
acquired: false,
|
|
9220
|
+
reason: info ? `held by pid ${info.pid}` : "held by another process"
|
|
9221
|
+
};
|
|
9222
|
+
}
|
|
9223
|
+
if (existsSync38(lockPath)) {
|
|
9224
|
+
try {
|
|
9225
|
+
unlinkSync2(lockPath);
|
|
9226
|
+
} catch {
|
|
9227
|
+
}
|
|
9228
|
+
}
|
|
9229
|
+
try {
|
|
9230
|
+
const fd = openSync6(lockPath, "wx");
|
|
9231
|
+
writeFileSync4(
|
|
9232
|
+
fd,
|
|
9233
|
+
JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
9234
|
+
"utf8"
|
|
9235
|
+
);
|
|
9236
|
+
closeSync6(fd);
|
|
9237
|
+
return { acquired: true };
|
|
9238
|
+
} catch (err) {
|
|
9239
|
+
if (err.code === "EEXIST") {
|
|
9240
|
+
return { acquired: false, reason: "concurrent acquire" };
|
|
9241
|
+
}
|
|
9242
|
+
throw err;
|
|
9243
|
+
}
|
|
9244
|
+
}
|
|
9245
|
+
function releaseCronTickLock(lockPath) {
|
|
9246
|
+
try {
|
|
9247
|
+
unlinkSync2(lockPath);
|
|
9248
|
+
} catch {
|
|
9249
|
+
}
|
|
9250
|
+
}
|
|
9251
|
+
|
|
9252
|
+
// src/cron/cron-match.ts
|
|
9253
|
+
var CRON_RE = /^[\d*/,\-?LW#]+\s+[\d*/,\-?LW#]+\s+[\d*/,\-?LW#]+\s+[\d*/,\-?LW#]+\s+[\d*/,\-?LW#]+$/;
|
|
9254
|
+
function isCronExpression(value) {
|
|
9255
|
+
return CRON_RE.test(value.trim());
|
|
9256
|
+
}
|
|
9257
|
+
function parseList(field, min, max) {
|
|
9258
|
+
const out = /* @__PURE__ */ new Set();
|
|
9259
|
+
for (const part of field.split(",")) {
|
|
9260
|
+
const token = part.trim();
|
|
9261
|
+
if (!token) continue;
|
|
9262
|
+
if (token === "*") {
|
|
9263
|
+
for (let i = min; i <= max; i++) out.add(i);
|
|
9264
|
+
continue;
|
|
9265
|
+
}
|
|
9266
|
+
const stepMatch = /^(.+)\/(\d+)$/.exec(token);
|
|
9267
|
+
const base = stepMatch ? stepMatch[1] : token;
|
|
9268
|
+
const step = stepMatch ? Math.max(1, Number(stepMatch[2])) : 1;
|
|
9269
|
+
if (base === "*") {
|
|
9270
|
+
for (let i = min; i <= max; i += step) out.add(i);
|
|
9271
|
+
continue;
|
|
9272
|
+
}
|
|
9273
|
+
const rangeMatch = /^(\d+)-(\d+)$/.exec(base);
|
|
9274
|
+
if (rangeMatch) {
|
|
9275
|
+
const start = Math.max(min, Number(rangeMatch[1]));
|
|
9276
|
+
const end = Math.min(max, Number(rangeMatch[2]));
|
|
9277
|
+
for (let i = start; i <= end; i += step) out.add(i);
|
|
9278
|
+
continue;
|
|
9279
|
+
}
|
|
9280
|
+
const n = Number(base);
|
|
9281
|
+
if (Number.isInteger(n) && n >= min && n <= max) out.add(n);
|
|
9282
|
+
}
|
|
9283
|
+
return out;
|
|
9284
|
+
}
|
|
9285
|
+
function fieldMatches(field, value, min, max) {
|
|
9286
|
+
const trimmed = field.trim();
|
|
9287
|
+
if (trimmed === "*") return true;
|
|
9288
|
+
return parseList(trimmed, min, max).has(value);
|
|
9289
|
+
}
|
|
9290
|
+
function cronMatchesUtc(expr, at) {
|
|
9291
|
+
const parts = expr.trim().split(/\s+/);
|
|
9292
|
+
if (parts.length !== 5) return false;
|
|
9293
|
+
const [minF, hourF, domF, monF, dowF] = parts;
|
|
9294
|
+
return fieldMatches(minF, at.getUTCMinutes(), 0, 59) && fieldMatches(hourF, at.getUTCHours(), 0, 23) && fieldMatches(domF, at.getUTCDate(), 1, 31) && fieldMatches(monF, at.getUTCMonth() + 1, 1, 12) && fieldMatches(dowF, at.getUTCDay(), 0, 6);
|
|
9295
|
+
}
|
|
9296
|
+
var MAX_LOOKAHEAD_MINUTES = 366 * 24 * 60;
|
|
9297
|
+
function truncateToUtcMinute(at) {
|
|
9298
|
+
return new Date(
|
|
9299
|
+
Date.UTC(
|
|
9300
|
+
at.getUTCFullYear(),
|
|
9301
|
+
at.getUTCMonth(),
|
|
9302
|
+
at.getUTCDate(),
|
|
9303
|
+
at.getUTCHours(),
|
|
9304
|
+
at.getUTCMinutes(),
|
|
9305
|
+
0,
|
|
9306
|
+
0
|
|
9307
|
+
)
|
|
9308
|
+
);
|
|
9309
|
+
}
|
|
9310
|
+
function computeNextCronFireUtc(expr, after) {
|
|
9311
|
+
if (!isCronExpression(expr)) return null;
|
|
9312
|
+
let cursor = truncateToUtcMinute(after);
|
|
9313
|
+
cursor = new Date(cursor.getTime() + 6e4);
|
|
9314
|
+
for (let i = 0; i < MAX_LOOKAHEAD_MINUTES; i++) {
|
|
9315
|
+
if (cronMatchesUtc(expr, cursor)) return cursor;
|
|
9316
|
+
cursor = new Date(cursor.getTime() + 6e4);
|
|
9317
|
+
}
|
|
9318
|
+
return null;
|
|
9319
|
+
}
|
|
9320
|
+
function computeInitialNextFire(spec, now) {
|
|
9321
|
+
if (spec.scheduleKind === "runAt" && spec.runAt) {
|
|
9322
|
+
const ms = Date.parse(spec.runAt);
|
|
9323
|
+
return Number.isNaN(ms) ? null : new Date(ms).toISOString();
|
|
9324
|
+
}
|
|
9325
|
+
if (spec.scheduleKind === "cron" && spec.cron) {
|
|
9326
|
+
const next = computeNextCronFireUtc(spec.cron.trim(), now);
|
|
9327
|
+
return next ? next.toISOString() : null;
|
|
9328
|
+
}
|
|
9329
|
+
return null;
|
|
9330
|
+
}
|
|
9331
|
+
function advanceRecurringNextFire(spec, fromInclusive) {
|
|
9332
|
+
if (spec.scheduleKind !== "cron" || !spec.cron?.trim()) return null;
|
|
9333
|
+
const next = computeNextCronFireUtc(spec.cron.trim(), fromInclusive);
|
|
9334
|
+
return next ? next.toISOString() : null;
|
|
9335
|
+
}
|
|
9336
|
+
|
|
9337
|
+
// src/cron/cron-store.ts
|
|
9338
|
+
import { promises as fs2 } from "node:fs";
|
|
9339
|
+
async function readFileIfExists(filePath) {
|
|
9340
|
+
try {
|
|
9341
|
+
return await fs2.readFile(filePath, "utf8");
|
|
9342
|
+
} catch (err) {
|
|
9343
|
+
if (err.code === "ENOENT") return null;
|
|
9344
|
+
throw err;
|
|
9345
|
+
}
|
|
9346
|
+
}
|
|
9347
|
+
function parseCronStore(raw) {
|
|
9348
|
+
if (!raw) return [];
|
|
9349
|
+
try {
|
|
9350
|
+
const parsed = JSON.parse(raw);
|
|
9351
|
+
return Array.isArray(parsed.entries) ? parsed.entries : [];
|
|
9352
|
+
} catch {
|
|
9353
|
+
return [];
|
|
9354
|
+
}
|
|
9355
|
+
}
|
|
9356
|
+
async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
|
|
9357
|
+
const raw = await readFileIfExists(storePath);
|
|
9358
|
+
return parseCronStore(raw);
|
|
9359
|
+
}
|
|
9360
|
+
|
|
9361
|
+
// src/cron/cron-tick-state.ts
|
|
9362
|
+
import { randomBytes } from "node:crypto";
|
|
9363
|
+
import { promises as fs3 } from "node:fs";
|
|
8901
9364
|
import path50 from "node:path";
|
|
9365
|
+
var EMPTY = { version: 1, jobs: {} };
|
|
9366
|
+
async function readFileIfExists2(filePath) {
|
|
9367
|
+
try {
|
|
9368
|
+
return await fs3.readFile(filePath, "utf8");
|
|
9369
|
+
} catch (err) {
|
|
9370
|
+
if (err.code === "ENOENT") return null;
|
|
9371
|
+
throw err;
|
|
9372
|
+
}
|
|
9373
|
+
}
|
|
9374
|
+
function parseCronTickState(raw) {
|
|
9375
|
+
if (!raw) return { ...EMPTY, jobs: { ...EMPTY.jobs } };
|
|
9376
|
+
try {
|
|
9377
|
+
const parsed = JSON.parse(raw);
|
|
9378
|
+
if (parsed?.version !== 1 || typeof parsed.jobs !== "object" || !parsed.jobs) {
|
|
9379
|
+
return { ...EMPTY, jobs: {} };
|
|
9380
|
+
}
|
|
9381
|
+
return parsed;
|
|
9382
|
+
} catch {
|
|
9383
|
+
return { ...EMPTY, jobs: {} };
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9386
|
+
async function loadCronTickState(statePath) {
|
|
9387
|
+
const raw = await readFileIfExists2(statePath);
|
|
9388
|
+
return parseCronTickState(raw);
|
|
9389
|
+
}
|
|
9390
|
+
async function writeStateAtomic(statePath, state) {
|
|
9391
|
+
await fs3.mkdir(path50.dirname(statePath), { recursive: true });
|
|
9392
|
+
const suffix = randomBytes(6).toString("hex");
|
|
9393
|
+
const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
|
|
9394
|
+
await fs3.writeFile(tmp, `${JSON.stringify(state, null, 2)}
|
|
9395
|
+
`, "utf8");
|
|
9396
|
+
try {
|
|
9397
|
+
await fs3.rename(tmp, statePath);
|
|
9398
|
+
} catch (err) {
|
|
9399
|
+
const code = err.code;
|
|
9400
|
+
if (code !== "EPERM" && code !== "EEXIST" && code !== "EACCES") throw err;
|
|
9401
|
+
await fs3.unlink(tmp).catch(() => {
|
|
9402
|
+
});
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9405
|
+
async function saveCronTickState(statePath, state) {
|
|
9406
|
+
await writeStateAtomic(statePath, state);
|
|
9407
|
+
}
|
|
9408
|
+
function getOrCreateJobState(state, providerScheduleId) {
|
|
9409
|
+
const existing = state.jobs[providerScheduleId];
|
|
9410
|
+
if (existing) return existing;
|
|
9411
|
+
const created = {
|
|
9412
|
+
providerScheduleId,
|
|
9413
|
+
nextFireAt: null,
|
|
9414
|
+
lastFiredAt: null,
|
|
9415
|
+
lastAttemptAt: null,
|
|
9416
|
+
consecutiveFailures: 0,
|
|
9417
|
+
completedAt: null,
|
|
9418
|
+
inflightUntil: null
|
|
9419
|
+
};
|
|
9420
|
+
state.jobs[providerScheduleId] = created;
|
|
9421
|
+
return created;
|
|
9422
|
+
}
|
|
9423
|
+
|
|
9424
|
+
// src/cron/cron-tick.ts
|
|
9425
|
+
function isInflight(job, nowMs) {
|
|
9426
|
+
if (!job.inflightUntil) return false;
|
|
9427
|
+
const until = Date.parse(job.inflightUntil);
|
|
9428
|
+
return !Number.isNaN(until) && until > nowMs;
|
|
9429
|
+
}
|
|
9430
|
+
function isCompleted(job) {
|
|
9431
|
+
return Boolean(job.completedAt);
|
|
9432
|
+
}
|
|
9433
|
+
function backoffReady(job, env, nowMs) {
|
|
9434
|
+
if (job.consecutiveFailures === 0) return true;
|
|
9435
|
+
if (job.consecutiveFailures > env.maxRetries) return false;
|
|
9436
|
+
if (!job.lastAttemptAt) return true;
|
|
9437
|
+
const last = Date.parse(job.lastAttemptAt);
|
|
9438
|
+
if (Number.isNaN(last)) return true;
|
|
9439
|
+
return nowMs - last >= env.retryBackoffMs;
|
|
9440
|
+
}
|
|
9441
|
+
function ensureNextFire(entry, job, now) {
|
|
9442
|
+
if (job.nextFireAt) return job.nextFireAt;
|
|
9443
|
+
const initial = computeInitialNextFire(entry.spec, now);
|
|
9444
|
+
job.nextFireAt = initial;
|
|
9445
|
+
return initial;
|
|
9446
|
+
}
|
|
9447
|
+
function isDue(entry, job, nowMs, env) {
|
|
9448
|
+
if (entry.paused || isCompleted(job) || isInflight(job, nowMs)) return false;
|
|
9449
|
+
if (!backoffReady(job, env, nowMs)) return false;
|
|
9450
|
+
const nextMs = job.nextFireAt ? Date.parse(job.nextFireAt) : NaN;
|
|
9451
|
+
if (Number.isNaN(nextMs)) return false;
|
|
9452
|
+
return nowMs >= nextMs;
|
|
9453
|
+
}
|
|
9454
|
+
function advanceRecurringBeforeFire(entry, job, now) {
|
|
9455
|
+
if (entry.spec.scheduleKind !== "cron") return;
|
|
9456
|
+
job.nextFireAt = advanceRecurringNextFire(entry.spec, now);
|
|
9457
|
+
}
|
|
9458
|
+
async function runKynverCronTick(opts = {}) {
|
|
9459
|
+
const env = opts.env ?? resolveKynverCronEnv();
|
|
9460
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
9461
|
+
const nowMs = now.getTime();
|
|
9462
|
+
if (!env.tickEnabled) {
|
|
9463
|
+
return { enabled: false, skipped: "tick_disabled", scanned: 0, due: 0, fired: 0, skippedJobs: 0, errors: 0 };
|
|
9464
|
+
}
|
|
9465
|
+
if (!env.fireBaseUrl || !env.secret) {
|
|
9466
|
+
return {
|
|
9467
|
+
enabled: true,
|
|
9468
|
+
skipped: "missing_fire_credentials",
|
|
9469
|
+
scanned: 0,
|
|
9470
|
+
due: 0,
|
|
9471
|
+
fired: 0,
|
|
9472
|
+
skippedJobs: 0,
|
|
9473
|
+
errors: 0
|
|
9474
|
+
};
|
|
9475
|
+
}
|
|
9476
|
+
const lock = tryAcquireCronTickLock(env.lockPath);
|
|
9477
|
+
if (!lock.acquired) {
|
|
9478
|
+
return {
|
|
9479
|
+
enabled: true,
|
|
9480
|
+
skipped: lock.reason ?? "lock_not_acquired",
|
|
9481
|
+
scanned: 0,
|
|
9482
|
+
due: 0,
|
|
9483
|
+
fired: 0,
|
|
9484
|
+
skippedJobs: 0,
|
|
9485
|
+
errors: 0,
|
|
9486
|
+
lockHeld: true
|
|
9487
|
+
};
|
|
9488
|
+
}
|
|
9489
|
+
try {
|
|
9490
|
+
const entries = await loadCronJobs(env.storePath);
|
|
9491
|
+
const filtered = opts.agentOsIdFilter ? entries.filter((e) => e.spec.target.agentOsId === opts.agentOsIdFilter) : entries;
|
|
9492
|
+
const state = await loadCronTickState(env.statePath);
|
|
9493
|
+
const dueEntries = [];
|
|
9494
|
+
for (const entry of filtered) {
|
|
9495
|
+
const job = getOrCreateJobState(state, entry.providerScheduleId);
|
|
9496
|
+
ensureNextFire(entry, job, now);
|
|
9497
|
+
if (isDue(entry, job, nowMs, env)) {
|
|
9498
|
+
dueEntries.push({ entry, job });
|
|
9499
|
+
}
|
|
9500
|
+
}
|
|
9501
|
+
dueEntries.sort((a, b) => {
|
|
9502
|
+
const aMs = Date.parse(a.job.nextFireAt ?? "") || 0;
|
|
9503
|
+
const bMs = Date.parse(b.job.nextFireAt ?? "") || 0;
|
|
9504
|
+
return aMs - bMs;
|
|
9505
|
+
});
|
|
9506
|
+
let fired = 0;
|
|
9507
|
+
let errors = 0;
|
|
9508
|
+
let skippedJobs = 0;
|
|
9509
|
+
let catchUpBudget = env.maxCatchUpPerTick;
|
|
9510
|
+
for (const { entry, job } of dueEntries) {
|
|
9511
|
+
if (env.missedRunPolicy === "skip" && entry.spec.scheduleKind === "cron") {
|
|
9512
|
+
const next = Date.parse(job.nextFireAt ?? "");
|
|
9513
|
+
if (!Number.isNaN(next) && next < nowMs - env.tickIntervalMs * 2) {
|
|
9514
|
+
job.nextFireAt = advanceRecurringNextFire(entry.spec, now);
|
|
9515
|
+
skippedJobs++;
|
|
9516
|
+
continue;
|
|
9517
|
+
}
|
|
9518
|
+
}
|
|
9519
|
+
if (catchUpBudget <= 0) {
|
|
9520
|
+
skippedJobs++;
|
|
9521
|
+
continue;
|
|
9522
|
+
}
|
|
9523
|
+
job.inflightUntil = new Date(nowMs + env.inflightLeaseMs).toISOString();
|
|
9524
|
+
job.lastAttemptAt = now.toISOString();
|
|
9525
|
+
advanceRecurringBeforeFire(entry, job, now);
|
|
9526
|
+
try {
|
|
9527
|
+
const result = await fireKynverCronJob({
|
|
9528
|
+
entry,
|
|
9529
|
+
baseUrl: env.fireBaseUrl,
|
|
9530
|
+
secret: env.secret,
|
|
9531
|
+
fetchFn: opts.fetchFn
|
|
9532
|
+
});
|
|
9533
|
+
job.inflightUntil = null;
|
|
9534
|
+
if (result.ok) {
|
|
9535
|
+
job.lastFiredAt = now.toISOString();
|
|
9536
|
+
job.consecutiveFailures = 0;
|
|
9537
|
+
if (entry.spec.scheduleKind === "runAt") {
|
|
9538
|
+
job.completedAt = now.toISOString();
|
|
9539
|
+
job.nextFireAt = null;
|
|
9540
|
+
}
|
|
9541
|
+
fired++;
|
|
9542
|
+
catchUpBudget--;
|
|
9543
|
+
} else {
|
|
9544
|
+
job.consecutiveFailures += 1;
|
|
9545
|
+
errors++;
|
|
9546
|
+
}
|
|
9547
|
+
} catch {
|
|
9548
|
+
job.inflightUntil = null;
|
|
9549
|
+
job.consecutiveFailures += 1;
|
|
9550
|
+
errors++;
|
|
9551
|
+
}
|
|
9552
|
+
}
|
|
9553
|
+
await saveCronTickState(env.statePath, state);
|
|
9554
|
+
return {
|
|
9555
|
+
enabled: true,
|
|
9556
|
+
scanned: filtered.length,
|
|
9557
|
+
due: dueEntries.length,
|
|
9558
|
+
fired,
|
|
9559
|
+
skippedJobs,
|
|
9560
|
+
errors
|
|
9561
|
+
};
|
|
9562
|
+
} finally {
|
|
9563
|
+
releaseCronTickLock(env.lockPath);
|
|
9564
|
+
}
|
|
9565
|
+
}
|
|
9566
|
+
|
|
9567
|
+
// src/pipeline-tick.ts
|
|
9568
|
+
import path53 from "node:path";
|
|
8902
9569
|
|
|
8903
9570
|
// src/pipeline-dispatch.ts
|
|
8904
9571
|
var RESERVED_REVIEW_STARTS = 1;
|
|
@@ -8988,29 +9655,48 @@ function operatorDispatchFromTick(operatorTick) {
|
|
|
8988
9655
|
const dispatch = body.response?.dispatch;
|
|
8989
9656
|
return dispatch && typeof dispatch === "object" ? dispatch : null;
|
|
8990
9657
|
}
|
|
9658
|
+
function nonNegativeInt(value) {
|
|
9659
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
9660
|
+
return Math.max(0, Math.floor(value));
|
|
9661
|
+
}
|
|
8991
9662
|
function resolvePipelineMaxStarts(resourceGate, operatorTick) {
|
|
8992
9663
|
const dispatch = operatorDispatchFromTick(operatorTick);
|
|
8993
|
-
const advised =
|
|
9664
|
+
const advised = nonNegativeInt(dispatch?.recommendedMaxStarts);
|
|
9665
|
+
const actionableReady = nonNegativeInt(dispatch?.actionableReady);
|
|
9666
|
+
const queuedTasks = nonNegativeInt(dispatch?.queuedTasks);
|
|
9667
|
+
const boardAdvancedThisTick = nonNegativeInt(dispatch?.boardAdvancedThisTick) ?? 0;
|
|
9668
|
+
const leaseReapedThisTick = nonNegativeInt(dispatch?.leaseReapedThisTick) ?? 0;
|
|
9669
|
+
const hygieneAdvanced = boardAdvancedThisTick + leaseReapedThisTick;
|
|
9670
|
+
const readyFloor = actionableReady ?? queuedTasks;
|
|
8994
9671
|
let maxStarts = resourceGate.slotsAvailable;
|
|
8995
|
-
if (
|
|
9672
|
+
if (readyFloor !== null) {
|
|
9673
|
+
maxStarts = Math.min(maxStarts, readyFloor);
|
|
9674
|
+
} else if (advised !== null) {
|
|
8996
9675
|
maxStarts = Math.min(maxStarts, advised);
|
|
8997
9676
|
}
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9677
|
+
if (readyFloor === null && advised !== null) {
|
|
9678
|
+
maxStarts = Math.max(maxStarts, Math.min(resourceGate.slotsAvailable, advised));
|
|
9679
|
+
}
|
|
9680
|
+
const underutilized = dispatch?.underutilized === true || (readyFloor ?? 0) > 0 && resourceGate.slotsAvailable > 0 && resourceGate.maxConcurrentWorkers > 0 && resourceGate.activeWorkers < resourceGate.maxConcurrentWorkers;
|
|
9681
|
+
if (resourceGate.slotsAvailable > 0 && maxStarts === 0 && (underutilized || hygieneAdvanced > 0)) {
|
|
9682
|
+
const ready = readyFloor ?? (hygieneAdvanced > 0 ? hygieneAdvanced : 1);
|
|
9002
9683
|
maxStarts = Math.min(resourceGate.slotsAvailable, Math.max(1, ready));
|
|
9003
9684
|
}
|
|
9685
|
+
const nonDispatchableReady = queuedTasks !== null && actionableReady !== null ? Math.max(0, queuedTasks - actionableReady) : null;
|
|
9004
9686
|
return {
|
|
9005
9687
|
maxStarts: Math.max(0, maxStarts),
|
|
9006
9688
|
underutilized,
|
|
9007
9689
|
advisedStarts: advised,
|
|
9008
|
-
|
|
9690
|
+
actionableReady,
|
|
9691
|
+
queuedTasks,
|
|
9692
|
+
nonDispatchableReady,
|
|
9693
|
+
boardAdvancedThisTick,
|
|
9694
|
+
leaseReapedThisTick
|
|
9009
9695
|
};
|
|
9010
9696
|
}
|
|
9011
9697
|
|
|
9012
9698
|
// src/plan-progress-daemon-sync.ts
|
|
9013
|
-
import
|
|
9699
|
+
import path51 from "node:path";
|
|
9014
9700
|
|
|
9015
9701
|
// src/plan-progress-sync.ts
|
|
9016
9702
|
async function syncPlanProgress(args) {
|
|
@@ -9034,7 +9720,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
9034
9720
|
const outcomes = [];
|
|
9035
9721
|
for (const name of Object.keys(run.workers || {})) {
|
|
9036
9722
|
const worker = readJson(
|
|
9037
|
-
|
|
9723
|
+
path51.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
9038
9724
|
void 0
|
|
9039
9725
|
);
|
|
9040
9726
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -9084,8 +9770,8 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
|
|
|
9084
9770
|
|
|
9085
9771
|
// src/installed-package-versions.ts
|
|
9086
9772
|
import { readFile } from "node:fs/promises";
|
|
9087
|
-
import { homedir as
|
|
9088
|
-
import
|
|
9773
|
+
import { homedir as homedir13 } from "node:os";
|
|
9774
|
+
import path52 from "node:path";
|
|
9089
9775
|
var MANAGED_PACKAGES = [
|
|
9090
9776
|
"@kynver-app/runtime",
|
|
9091
9777
|
"@kynver-app/openclaw-agent-os",
|
|
@@ -9099,13 +9785,13 @@ function unique(values) {
|
|
|
9099
9785
|
return [...new Set(values.filter((value) => Boolean(value)))];
|
|
9100
9786
|
}
|
|
9101
9787
|
function moduleRoots() {
|
|
9102
|
-
const home =
|
|
9103
|
-
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ??
|
|
9104
|
-
const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ?
|
|
9788
|
+
const home = homedir13();
|
|
9789
|
+
const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path52.join(home, ".openclaw", "npm");
|
|
9790
|
+
const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path52.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path52.join(home, ".npm-global", "lib", "node_modules"));
|
|
9105
9791
|
return unique([
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot :
|
|
9792
|
+
path52.join(openClawPrefix, "lib", "node_modules"),
|
|
9793
|
+
path52.join(openClawPrefix, "node_modules"),
|
|
9794
|
+
npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path52.join(npmGlobalRoot, "lib", "node_modules")
|
|
9109
9795
|
]);
|
|
9110
9796
|
}
|
|
9111
9797
|
async function readVersion(packageJsonPath) {
|
|
@@ -9121,7 +9807,7 @@ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new
|
|
|
9121
9807
|
const out = {};
|
|
9122
9808
|
for (const packageName of MANAGED_PACKAGES) {
|
|
9123
9809
|
for (const root of roots) {
|
|
9124
|
-
const packageJsonPath =
|
|
9810
|
+
const packageJsonPath = path52.join(root, packageName, "package.json");
|
|
9125
9811
|
const version = await readVersion(packageJsonPath);
|
|
9126
9812
|
if (!version) continue;
|
|
9127
9813
|
out[packageName] = { version, observedAt, path: packageJsonPath };
|
|
@@ -9137,7 +9823,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
9137
9823
|
const outcomes = [];
|
|
9138
9824
|
for (const name of Object.keys(run.workers || {})) {
|
|
9139
9825
|
const worker = readJson(
|
|
9140
|
-
|
|
9826
|
+
path53.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
9141
9827
|
void 0
|
|
9142
9828
|
);
|
|
9143
9829
|
if (!worker?.taskId || worker.localOnly) continue;
|
|
@@ -9248,6 +9934,7 @@ async function runPipelineTick(args) {
|
|
|
9248
9934
|
skipped: true,
|
|
9249
9935
|
reason: execute ? dispatchResourceGate.reason ?? "no slots or queued work" : "execute disabled",
|
|
9250
9936
|
maxStarts: 0,
|
|
9937
|
+
dispatchAdvice: maxStartsAdvice,
|
|
9251
9938
|
...exactTargetTaskIds.length ? { exactTargetTaskIds, exactOnly: true } : {}
|
|
9252
9939
|
};
|
|
9253
9940
|
}
|
|
@@ -9269,6 +9956,7 @@ async function runPipelineTick(args) {
|
|
|
9269
9956
|
completionAckSync,
|
|
9270
9957
|
operatorTick,
|
|
9271
9958
|
sweep,
|
|
9959
|
+
dispatchAdvice: maxStartsAdvice,
|
|
9272
9960
|
dispatch,
|
|
9273
9961
|
idle
|
|
9274
9962
|
};
|
|
@@ -9292,8 +9980,18 @@ async function runDaemon(args) {
|
|
|
9292
9980
|
stopping = true;
|
|
9293
9981
|
});
|
|
9294
9982
|
console.error(JSON.stringify({ event: "daemon_start", runId, agentOsId, execute, intervalMs }));
|
|
9983
|
+
const cronEnv = resolveKynverCronEnv();
|
|
9295
9984
|
while (!stopping) {
|
|
9296
9985
|
try {
|
|
9986
|
+
if (cronEnv.tickEnabled) {
|
|
9987
|
+
const cronTick = await runKynverCronTick({
|
|
9988
|
+
env: cronEnv,
|
|
9989
|
+
agentOsIdFilter: agentOsId
|
|
9990
|
+
});
|
|
9991
|
+
if (cronTick.enabled && (cronTick.fired > 0 || cronTick.errors > 0)) {
|
|
9992
|
+
console.error(JSON.stringify({ event: "daemon_cron_tick", ...cronTick }));
|
|
9993
|
+
}
|
|
9994
|
+
}
|
|
9297
9995
|
const tick = await runPipelineTick({ run: runId, agentOsId, execute, ...args });
|
|
9298
9996
|
console.error(JSON.stringify({ event: "daemon_tick", ...tick }));
|
|
9299
9997
|
if (tick.idle) {
|
|
@@ -9312,7 +10010,7 @@ async function runDaemon(args) {
|
|
|
9312
10010
|
}
|
|
9313
10011
|
|
|
9314
10012
|
// src/plan-progress.ts
|
|
9315
|
-
import
|
|
10013
|
+
import path54 from "node:path";
|
|
9316
10014
|
|
|
9317
10015
|
// src/bounded-build/constants.ts
|
|
9318
10016
|
var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
|
|
@@ -9599,7 +10297,7 @@ async function emitPlanProgress(args) {
|
|
|
9599
10297
|
}
|
|
9600
10298
|
function verifyPlanLocal(args) {
|
|
9601
10299
|
const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
|
|
9602
|
-
const cwd =
|
|
10300
|
+
const cwd = path54.resolve(worktree);
|
|
9603
10301
|
const summary = runHarnessVerifyCommands(cwd);
|
|
9604
10302
|
const emitJson = args.json === true || args.json === "true";
|
|
9605
10303
|
const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
|
|
@@ -9648,9 +10346,9 @@ async function verifyPlan(args) {
|
|
|
9648
10346
|
}
|
|
9649
10347
|
|
|
9650
10348
|
// src/harness-verify-cli.ts
|
|
9651
|
-
import
|
|
10349
|
+
import path55 from "node:path";
|
|
9652
10350
|
function runHarnessVerifyCli(args) {
|
|
9653
|
-
const cwd =
|
|
10351
|
+
const cwd = path55.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
|
|
9654
10352
|
const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
|
|
9655
10353
|
const commands = [];
|
|
9656
10354
|
const rawCmd = args.command;
|
|
@@ -9694,7 +10392,7 @@ function runHarnessVerifyCli(args) {
|
|
|
9694
10392
|
}
|
|
9695
10393
|
|
|
9696
10394
|
// src/plan-persist-cli.ts
|
|
9697
|
-
import { readFileSync as
|
|
10395
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
9698
10396
|
var OPERATIONS = ["create", "add_version", "update_metadata"];
|
|
9699
10397
|
var FAILURE_KINDS = [
|
|
9700
10398
|
"approval_guard",
|
|
@@ -9706,7 +10404,7 @@ var FAILURE_KINDS = [
|
|
|
9706
10404
|
function readBodyArg(args) {
|
|
9707
10405
|
const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
|
|
9708
10406
|
if (bodyFile) {
|
|
9709
|
-
return { body:
|
|
10407
|
+
return { body: readFileSync14(bodyFile, "utf8"), bodyPathHint: bodyFile };
|
|
9710
10408
|
}
|
|
9711
10409
|
const inline = args.body ? String(args.body) : void 0;
|
|
9712
10410
|
if (inline) return { body: inline };
|
|
@@ -10079,7 +10777,7 @@ ${text.slice(0, 800)}`,
|
|
|
10079
10777
|
}
|
|
10080
10778
|
|
|
10081
10779
|
// src/monitor/monitor.service.ts
|
|
10082
|
-
import
|
|
10780
|
+
import path57 from "node:path";
|
|
10083
10781
|
|
|
10084
10782
|
// src/monitor/monitor.classify.ts
|
|
10085
10783
|
function classifyWorkerHealth(input) {
|
|
@@ -10131,11 +10829,11 @@ function classifyWorkerHealth(input) {
|
|
|
10131
10829
|
}
|
|
10132
10830
|
|
|
10133
10831
|
// src/monitor/monitor.store.ts
|
|
10134
|
-
import { existsSync as
|
|
10135
|
-
import
|
|
10832
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync6, readdirSync as readdirSync12, unlinkSync as unlinkSync3 } from "node:fs";
|
|
10833
|
+
import path56 from "node:path";
|
|
10136
10834
|
function monitorsDir() {
|
|
10137
10835
|
const { harnessRoot } = getHarnessPaths();
|
|
10138
|
-
const dir =
|
|
10836
|
+
const dir = path56.join(harnessRoot, "monitors");
|
|
10139
10837
|
mkdirSync6(dir, { recursive: true });
|
|
10140
10838
|
return dir;
|
|
10141
10839
|
}
|
|
@@ -10143,7 +10841,7 @@ function monitorIdFor(runId, workerName) {
|
|
|
10143
10841
|
return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
|
|
10144
10842
|
}
|
|
10145
10843
|
function monitorPath(monitorId) {
|
|
10146
|
-
return
|
|
10844
|
+
return path56.join(monitorsDir(), `${monitorId}.json`);
|
|
10147
10845
|
}
|
|
10148
10846
|
function loadMonitorSession(monitorId) {
|
|
10149
10847
|
return readJson(monitorPath(monitorId), void 0);
|
|
@@ -10153,18 +10851,18 @@ function saveMonitorSession(session) {
|
|
|
10153
10851
|
}
|
|
10154
10852
|
function deleteMonitorSession(monitorId) {
|
|
10155
10853
|
const file = monitorPath(monitorId);
|
|
10156
|
-
if (!
|
|
10157
|
-
|
|
10854
|
+
if (!existsSync39(file)) return false;
|
|
10855
|
+
unlinkSync3(file);
|
|
10158
10856
|
return true;
|
|
10159
10857
|
}
|
|
10160
10858
|
function listMonitorSessions() {
|
|
10161
10859
|
const dir = monitorsDir();
|
|
10162
|
-
if (!
|
|
10860
|
+
if (!existsSync39(dir)) return [];
|
|
10163
10861
|
const entries = [];
|
|
10164
|
-
for (const name of
|
|
10862
|
+
for (const name of readdirSync12(dir)) {
|
|
10165
10863
|
if (!name.endsWith(".json")) continue;
|
|
10166
10864
|
const session = readJson(
|
|
10167
|
-
|
|
10865
|
+
path56.join(dir, name),
|
|
10168
10866
|
void 0
|
|
10169
10867
|
);
|
|
10170
10868
|
if (!session?.monitorId) continue;
|
|
@@ -10255,7 +10953,7 @@ async function fetchTaskLeasesForWorkers(input) {
|
|
|
10255
10953
|
// src/monitor/monitor.service.ts
|
|
10256
10954
|
function workerRecord2(runId, name) {
|
|
10257
10955
|
return readJson(
|
|
10258
|
-
|
|
10956
|
+
path57.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
|
|
10259
10957
|
void 0
|
|
10260
10958
|
);
|
|
10261
10959
|
}
|
|
@@ -10461,21 +11159,21 @@ async function runMonitorLoop(args) {
|
|
|
10461
11159
|
|
|
10462
11160
|
// src/monitor/monitor-spawn.ts
|
|
10463
11161
|
import { spawn as spawn6 } from "node:child_process";
|
|
10464
|
-
import { closeSync as
|
|
10465
|
-
import
|
|
11162
|
+
import { closeSync as closeSync7, existsSync as existsSync40, openSync as openSync7 } from "node:fs";
|
|
11163
|
+
import path58 from "node:path";
|
|
10466
11164
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
10467
11165
|
function resolveDefaultCliPath2() {
|
|
10468
|
-
return
|
|
11166
|
+
return path58.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
|
|
10469
11167
|
}
|
|
10470
11168
|
function spawnMonitorSidecar(opts) {
|
|
10471
11169
|
const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
|
|
10472
|
-
if (!
|
|
11170
|
+
if (!existsSync40(cliPath)) return void 0;
|
|
10473
11171
|
const monitorId = monitorIdFor(opts.runId, opts.workerName);
|
|
10474
11172
|
const { harnessRoot } = getHarnessPaths();
|
|
10475
|
-
const logPath =
|
|
11173
|
+
const logPath = path58.join(harnessRoot, "monitors", `${monitorId}.log`);
|
|
10476
11174
|
let logFd;
|
|
10477
11175
|
try {
|
|
10478
|
-
logFd =
|
|
11176
|
+
logFd = openSync7(logPath, "a");
|
|
10479
11177
|
} catch {
|
|
10480
11178
|
logFd = void 0;
|
|
10481
11179
|
}
|
|
@@ -10515,7 +11213,7 @@ function spawnMonitorSidecar(opts) {
|
|
|
10515
11213
|
env: process.env
|
|
10516
11214
|
})
|
|
10517
11215
|
);
|
|
10518
|
-
if (logFd !== void 0)
|
|
11216
|
+
if (logFd !== void 0) closeSync7(logFd);
|
|
10519
11217
|
child.unref();
|
|
10520
11218
|
const session = {
|
|
10521
11219
|
monitorId,
|
|
@@ -10532,7 +11230,7 @@ function spawnMonitorSidecar(opts) {
|
|
|
10532
11230
|
} catch {
|
|
10533
11231
|
if (logFd !== void 0) {
|
|
10534
11232
|
try {
|
|
10535
|
-
|
|
11233
|
+
closeSync7(logFd);
|
|
10536
11234
|
} catch {
|
|
10537
11235
|
}
|
|
10538
11236
|
}
|
|
@@ -10592,7 +11290,7 @@ async function monitorTickCli(args) {
|
|
|
10592
11290
|
}
|
|
10593
11291
|
|
|
10594
11292
|
// src/post-restart-unblock.ts
|
|
10595
|
-
import
|
|
11293
|
+
import path59 from "node:path";
|
|
10596
11294
|
function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
|
|
10597
11295
|
return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
|
|
10598
11296
|
}
|
|
@@ -10605,7 +11303,7 @@ async function postRestartUnblock(args) {
|
|
|
10605
11303
|
const errors = [];
|
|
10606
11304
|
for (const run of listRunRecords()) {
|
|
10607
11305
|
for (const name of Object.keys(run.workers ?? {})) {
|
|
10608
|
-
const workerPath =
|
|
11306
|
+
const workerPath = path59.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
10609
11307
|
const worker = readJson(workerPath, void 0);
|
|
10610
11308
|
if (!worker) {
|
|
10611
11309
|
skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
|
|
@@ -10717,12 +11415,12 @@ async function postRestartUnblockCli(args) {
|
|
|
10717
11415
|
}
|
|
10718
11416
|
|
|
10719
11417
|
// src/doctor/runtime-takeover.ts
|
|
10720
|
-
import
|
|
11418
|
+
import path61 from "node:path";
|
|
10721
11419
|
|
|
10722
11420
|
// src/doctor/runtime-takeover.probes.ts
|
|
10723
|
-
import { accessSync, constants, existsSync as
|
|
10724
|
-
import { homedir as
|
|
10725
|
-
import
|
|
11421
|
+
import { accessSync, constants, existsSync as existsSync41, readFileSync as readFileSync15 } from "node:fs";
|
|
11422
|
+
import { homedir as homedir14 } from "node:os";
|
|
11423
|
+
import path60 from "node:path";
|
|
10726
11424
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
10727
11425
|
function captureCommand(bin, args) {
|
|
10728
11426
|
try {
|
|
@@ -10751,7 +11449,7 @@ function tokenPrefix(token) {
|
|
|
10751
11449
|
return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
|
|
10752
11450
|
}
|
|
10753
11451
|
function isWritable(target) {
|
|
10754
|
-
if (!
|
|
11452
|
+
if (!existsSync41(target)) return false;
|
|
10755
11453
|
try {
|
|
10756
11454
|
accessSync(target, constants.W_OK);
|
|
10757
11455
|
return true;
|
|
@@ -10764,15 +11462,15 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
10764
11462
|
commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
|
|
10765
11463
|
kynverVersion: (bin) => captureCommand(bin, ["--version"]),
|
|
10766
11464
|
loadConfig: () => loadUserConfig(),
|
|
10767
|
-
configFilePath: () =>
|
|
10768
|
-
credentialsFilePath: () =>
|
|
11465
|
+
configFilePath: () => path60.join(homedir14(), ".kynver", "config.json"),
|
|
11466
|
+
credentialsFilePath: () => path60.join(homedir14(), ".kynver", "credentials"),
|
|
10769
11467
|
readCredentials: () => {
|
|
10770
|
-
const credPath =
|
|
10771
|
-
if (!
|
|
11468
|
+
const credPath = path60.join(homedir14(), ".kynver", "credentials");
|
|
11469
|
+
if (!existsSync41(credPath)) {
|
|
10772
11470
|
return { hasApiKey: false };
|
|
10773
11471
|
}
|
|
10774
11472
|
try {
|
|
10775
|
-
const parsed = JSON.parse(
|
|
11473
|
+
const parsed = JSON.parse(readFileSync15(credPath, "utf8"));
|
|
10776
11474
|
return {
|
|
10777
11475
|
hasApiKey: Boolean(parsed.apiKey?.trim()),
|
|
10778
11476
|
runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
|
|
@@ -10791,7 +11489,10 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
10791
11489
|
kynverHarnessRoot: process.env.KYNVER_HARNESS_ROOT?.trim() || void 0,
|
|
10792
11490
|
opusHarnessRoot: process.env.OPUS_HARNESS_ROOT?.trim() || void 0,
|
|
10793
11491
|
kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0,
|
|
10794
|
-
openclawCronStorePath: Boolean(
|
|
11492
|
+
openclawCronStorePath: Boolean(
|
|
11493
|
+
process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim()
|
|
11494
|
+
),
|
|
11495
|
+
kynverCronDaemonPrimary: isKynverCronDaemonPrimary(),
|
|
10795
11496
|
qstashTokenPresent: Boolean(process.env.QSTASH_TOKEN?.trim()),
|
|
10796
11497
|
kynverHostedDeployment: (() => {
|
|
10797
11498
|
const v = process.env.KYNVER_HOSTED_DEPLOYMENT?.trim().toLowerCase();
|
|
@@ -10799,8 +11500,8 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
10799
11500
|
})()
|
|
10800
11501
|
}),
|
|
10801
11502
|
harnessRoot: () => resolveHarnessRoot(),
|
|
10802
|
-
legacyOpenclawHarnessRoot: () =>
|
|
10803
|
-
pathExists: (target) =>
|
|
11503
|
+
legacyOpenclawHarnessRoot: () => path60.join(homedir14(), ".openclaw", "harness"),
|
|
11504
|
+
pathExists: (target) => existsSync41(target),
|
|
10804
11505
|
pathWritable: (target) => isWritable(target),
|
|
10805
11506
|
vercelVersion: () => captureCommand("vercel", ["--version"]),
|
|
10806
11507
|
vercelWhoami: () => captureCommand("vercel", ["whoami"])
|
|
@@ -10820,8 +11521,10 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
|
|
|
10820
11521
|
const schedulerDetails = {
|
|
10821
11522
|
schedulerProvider: env.kynverSchedulerProvider ?? null,
|
|
10822
11523
|
deploymentSchedulerProvider: ctx.deploymentSchedulerProvider ?? null,
|
|
10823
|
-
openclawCronStorePath: Boolean(env.openclawCronStorePath)
|
|
11524
|
+
openclawCronStorePath: Boolean(env.openclawCronStorePath),
|
|
11525
|
+
kynverCronDaemonPrimary: Boolean(env.kynverCronDaemonPrimary)
|
|
10824
11526
|
};
|
|
11527
|
+
const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
|
|
10825
11528
|
if (hasQstashCutover(env, ctx) && !hasLocalOpenClawDependency(env, ctx)) {
|
|
10826
11529
|
const source = env.kynverSchedulerProvider === "qstash" ? "KYNVER_SCHEDULER_PROVIDER=qstash on this host" : "deploymentSchedulerProvider=qstash in ~/.kynver/config.json";
|
|
10827
11530
|
return check({
|
|
@@ -10833,6 +11536,19 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
|
|
|
10833
11536
|
});
|
|
10834
11537
|
}
|
|
10835
11538
|
if (hasLocalOpenClawDependency(env, ctx)) {
|
|
11539
|
+
if (env.kynverCronDaemonPrimary && daemonDispatchReady) {
|
|
11540
|
+
return check({
|
|
11541
|
+
id: "hotspot_openclaw_scheduler",
|
|
11542
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
11543
|
+
status: "pass",
|
|
11544
|
+
summary: "Kynver Cron local store present; `kynver daemon` owns schedule fires (kynver-cron tick loop)",
|
|
11545
|
+
details: {
|
|
11546
|
+
...schedulerDetails,
|
|
11547
|
+
dispatchPath: "kynver-daemon-cron-tick",
|
|
11548
|
+
kynverCronDaemonPrimary: true
|
|
11549
|
+
}
|
|
11550
|
+
});
|
|
11551
|
+
}
|
|
10836
11552
|
const parts = [];
|
|
10837
11553
|
if (env.kynverSchedulerProvider === "openclaw-cron") {
|
|
10838
11554
|
parts.push("KYNVER_SCHEDULER_PROVIDER=openclaw-cron");
|
|
@@ -10841,21 +11557,20 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
|
|
|
10841
11557
|
parts.push("deploymentSchedulerProvider=openclaw-cron in config");
|
|
10842
11558
|
}
|
|
10843
11559
|
if (env.openclawCronStorePath) {
|
|
10844
|
-
parts.push("OPENCLAW_CRON_STORE_PATH set (local cron
|
|
11560
|
+
parts.push("KYNVER_CRON_STORE_PATH or OPENCLAW_CRON_STORE_PATH set (local cron store)");
|
|
10845
11561
|
}
|
|
10846
11562
|
return check({
|
|
10847
11563
|
id: "hotspot_openclaw_scheduler",
|
|
10848
11564
|
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
10849
11565
|
status: "warn",
|
|
10850
|
-
summary: `
|
|
10851
|
-
remediation: "
|
|
11566
|
+
summary: `Local cron store without daemon tick (${parts.join("; ")})`,
|
|
11567
|
+
remediation: "Run `kynver daemon` with KYNVER_CRON_SECRET + KYNVER_API_URL (or KYNVER_CRON_FIRE_BASE_URL) so the daemon-owned cron tick fires schedules. On hosted deploys use QStash (KYNVER_SCHEDULER_PROVIDER=qstash). Legacy OpenClaw cron env aliases still work during cutover.",
|
|
10852
11568
|
details: schedulerDetails
|
|
10853
11569
|
});
|
|
10854
11570
|
}
|
|
10855
11571
|
const runnerOpenclaw = env.kynverSchedulerProvider === "openclaw-cron";
|
|
10856
11572
|
const runnerQstash = env.kynverSchedulerProvider === "qstash";
|
|
10857
11573
|
const hostedDeployment = Boolean(env.qstashTokenPresent) || Boolean(env.kynverHostedDeployment);
|
|
10858
|
-
const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
|
|
10859
11574
|
const hostedSchedulerProcess = hostedDeployment && !daemonDispatchReady;
|
|
10860
11575
|
const deploymentNeedsQstash = hostedSchedulerProcess && !env.qstashTokenPresent && env.kynverSchedulerProvider !== "qstash";
|
|
10861
11576
|
const deploymentOpenclaw = hostedSchedulerProcess && env.kynverSchedulerProvider === "openclaw-cron";
|
|
@@ -11128,8 +11843,8 @@ function assessVercelCli(probes) {
|
|
|
11128
11843
|
}
|
|
11129
11844
|
function assessHarnessDirs(probes) {
|
|
11130
11845
|
const harnessRoot = probes.harnessRoot();
|
|
11131
|
-
const runsDir =
|
|
11132
|
-
const worktreesDir =
|
|
11846
|
+
const runsDir = path61.join(harnessRoot, "runs");
|
|
11847
|
+
const worktreesDir = path61.join(harnessRoot, "worktrees");
|
|
11133
11848
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
11134
11849
|
const displayRunsDir = redactHomePath(runsDir);
|
|
11135
11850
|
const displayWorktreesDir = redactHomePath(worktreesDir);
|
|
@@ -11332,6 +12047,11 @@ var RUNNER_SCHEDULER_CUTOVER_STEPS = [
|
|
|
11332
12047
|
function readSchedulerCutoverEnv(env = process.env) {
|
|
11333
12048
|
return {
|
|
11334
12049
|
kynverSchedulerProvider: env.KYNVER_SCHEDULER_PROVIDER?.trim() || null,
|
|
12050
|
+
kynverCronStorePath: env.KYNVER_CRON_STORE_PATH?.trim() || env.OPENCLAW_CRON_STORE_PATH?.trim() || null,
|
|
12051
|
+
kynverCronSecret: Boolean(
|
|
12052
|
+
env.KYNVER_CRON_SECRET?.trim() || env.OPENCLAW_CRON_SECRET?.trim()
|
|
12053
|
+
),
|
|
12054
|
+
kynverCronFireBaseUrl: env.KYNVER_CRON_FIRE_BASE_URL?.trim() || env.OPENCLAW_CRON_FIRE_BASE_URL?.trim() || null,
|
|
11335
12055
|
openclawCronStorePath: env.OPENCLAW_CRON_STORE_PATH?.trim() || null,
|
|
11336
12056
|
openclawCronSecret: Boolean(env.OPENCLAW_CRON_SECRET?.trim()),
|
|
11337
12057
|
openclawCronFireBaseUrl: env.OPENCLAW_CRON_FIRE_BASE_URL?.trim() || null
|
|
@@ -11342,8 +12062,13 @@ function assessSchedulerCutover(config, env = readSchedulerCutoverEnv()) {
|
|
|
11342
12062
|
if (env.kynverSchedulerProvider === "openclaw-cron") {
|
|
11343
12063
|
blockers.push("Runner still has KYNVER_SCHEDULER_PROVIDER=openclaw-cron");
|
|
11344
12064
|
}
|
|
11345
|
-
if (env.
|
|
11346
|
-
blockers.push(
|
|
12065
|
+
if (env.kynverCronStorePath && env.kynverSchedulerProvider !== "kynver-cron") {
|
|
12066
|
+
blockers.push(
|
|
12067
|
+
"Runner has KYNVER_CRON_STORE_PATH but KYNVER_SCHEDULER_PROVIDER is not kynver-cron \u2014 use `kynver daemon` cron tick or unset the store for QStash-only runners"
|
|
12068
|
+
);
|
|
12069
|
+
}
|
|
12070
|
+
if (env.openclawCronStorePath && !env.kynverCronStorePath) {
|
|
12071
|
+
blockers.push("Runner still has legacy OPENCLAW_CRON_STORE_PATH (prefer KYNVER_CRON_STORE_PATH)");
|
|
11347
12072
|
}
|
|
11348
12073
|
if (config.deploymentSchedulerProvider === "openclaw-cron") {
|
|
11349
12074
|
blockers.push("~/.kynver/config.json deploymentSchedulerProvider is still openclaw-cron");
|
|
@@ -11365,9 +12090,9 @@ function applySchedulerCutoverAttestation(config) {
|
|
|
11365
12090
|
}
|
|
11366
12091
|
|
|
11367
12092
|
// src/scheduler-cutover-cli.ts
|
|
11368
|
-
import
|
|
11369
|
-
import { homedir as
|
|
11370
|
-
var CONFIG_FILE2 =
|
|
12093
|
+
import path62 from "node:path";
|
|
12094
|
+
import { homedir as homedir15 } from "node:os";
|
|
12095
|
+
var CONFIG_FILE2 = path62.join(homedir15(), ".kynver", "config.json");
|
|
11371
12096
|
function runSchedulerCutoverCheckCli(json = false) {
|
|
11372
12097
|
const config = loadUserConfig();
|
|
11373
12098
|
const report = assessSchedulerCutover(config);
|
|
@@ -11395,7 +12120,10 @@ function runSchedulerCutoverCheckCli(json = false) {
|
|
|
11395
12120
|
` KYNVER_SCHEDULER_PROVIDER: ${report.runnerEnv.kynverSchedulerProvider ?? "(unset)"}`
|
|
11396
12121
|
);
|
|
11397
12122
|
console.log(
|
|
11398
|
-
`
|
|
12123
|
+
` KYNVER_CRON_STORE_PATH: ${report.runnerEnv.kynverCronStorePath ?? "(unset)"}`
|
|
12124
|
+
);
|
|
12125
|
+
console.log(
|
|
12126
|
+
` OPENCLAW_CRON_STORE_PATH (legacy): ${report.runnerEnv.openclawCronStorePath ?? "(unset)"}`
|
|
11399
12127
|
);
|
|
11400
12128
|
if (report.blockers.length) {
|
|
11401
12129
|
console.log("\nBlockers:");
|
|
@@ -11442,6 +12170,65 @@ function runSchedulerAttestCutoverCli(json = false) {
|
|
|
11442
12170
|
console.log(` config: ${payload.configPath}`);
|
|
11443
12171
|
}
|
|
11444
12172
|
|
|
12173
|
+
// src/cron/cron-status.ts
|
|
12174
|
+
async function buildKynverCronStatusReport(env = resolveKynverCronEnv()) {
|
|
12175
|
+
const jobs = await loadCronJobs(env.storePath).catch(() => []);
|
|
12176
|
+
const state = await loadCronTickState(env.statePath).catch(() => ({ version: 1, jobs: {} }));
|
|
12177
|
+
const credentialsReady = Boolean(env.fireBaseUrl && env.secret);
|
|
12178
|
+
const daemonPrimary = isKynverCronDaemonPrimary(env);
|
|
12179
|
+
let primary = "disabled";
|
|
12180
|
+
if (daemonPrimary) primary = "kynver-cron-daemon";
|
|
12181
|
+
else if (process.env.QSTASH_TOKEN?.trim()) primary = "qstash";
|
|
12182
|
+
return {
|
|
12183
|
+
primary,
|
|
12184
|
+
env: {
|
|
12185
|
+
storePath: env.storePath,
|
|
12186
|
+
statePath: env.statePath,
|
|
12187
|
+
tickEnabled: env.tickEnabled,
|
|
12188
|
+
fireBaseUrl: env.fireBaseUrl,
|
|
12189
|
+
missedRunPolicy: env.missedRunPolicy,
|
|
12190
|
+
maxCatchUpPerTick: env.maxCatchUpPerTick,
|
|
12191
|
+
maxRetries: env.maxRetries
|
|
12192
|
+
},
|
|
12193
|
+
jobCount: jobs.length,
|
|
12194
|
+
stateJobCount: Object.keys(state.jobs).length,
|
|
12195
|
+
credentialsReady,
|
|
12196
|
+
daemonPrimary
|
|
12197
|
+
};
|
|
12198
|
+
}
|
|
12199
|
+
|
|
12200
|
+
// src/cron/cron-tick-cli.ts
|
|
12201
|
+
async function runCronStatusCli(json) {
|
|
12202
|
+
const report = await buildKynverCronStatusReport();
|
|
12203
|
+
if (json) {
|
|
12204
|
+
console.log(JSON.stringify(report, null, 2));
|
|
12205
|
+
return;
|
|
12206
|
+
}
|
|
12207
|
+
console.log(`Kynver Cron primary: ${report.primary}`);
|
|
12208
|
+
console.log(` store: ${report.env.storePath} (${report.jobCount} jobs)`);
|
|
12209
|
+
console.log(` tick state: ${report.env.statePath} (${report.stateJobCount} tracked)`);
|
|
12210
|
+
console.log(` tick enabled: ${report.env.tickEnabled}`);
|
|
12211
|
+
console.log(` fire base URL: ${report.env.fireBaseUrl ?? "(unset)"}`);
|
|
12212
|
+
console.log(` credentials ready: ${report.credentialsReady}`);
|
|
12213
|
+
console.log(` daemon-owned primary: ${report.daemonPrimary}`);
|
|
12214
|
+
}
|
|
12215
|
+
async function runCronTickCli(args) {
|
|
12216
|
+
const agentOsId = typeof args.agentOsId === "string" ? args.agentOsId : void 0;
|
|
12217
|
+
const result = await runKynverCronTick({
|
|
12218
|
+
agentOsIdFilter: agentOsId ?? null
|
|
12219
|
+
});
|
|
12220
|
+
if (args.json === true) {
|
|
12221
|
+
console.log(JSON.stringify(result, null, 2));
|
|
12222
|
+
return;
|
|
12223
|
+
}
|
|
12224
|
+
console.log(
|
|
12225
|
+
JSON.stringify({
|
|
12226
|
+
event: "kynver_cron_tick",
|
|
12227
|
+
...result
|
|
12228
|
+
})
|
|
12229
|
+
);
|
|
12230
|
+
}
|
|
12231
|
+
|
|
11445
12232
|
// src/cli.ts
|
|
11446
12233
|
function isHelpFlag(arg) {
|
|
11447
12234
|
return arg === "help" || arg === "--help" || arg === "-h";
|
|
@@ -11492,6 +12279,8 @@ function usage(code = 0) {
|
|
|
11492
12279
|
" kynver doctor runtime-takeover",
|
|
11493
12280
|
" kynver scheduler cutover-check [--json]",
|
|
11494
12281
|
" kynver scheduler attest-cutover [--json]",
|
|
12282
|
+
" kynver cron status [--json]",
|
|
12283
|
+
" kynver cron tick [--agent-os-id AOS_ID] [--json]",
|
|
11495
12284
|
" kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
|
|
11496
12285
|
].join("\n")
|
|
11497
12286
|
);
|
|
@@ -11503,7 +12292,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
11503
12292
|
const scope = argv.shift();
|
|
11504
12293
|
let action;
|
|
11505
12294
|
let rest;
|
|
11506
|
-
if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "scheduler" || scope === "board") {
|
|
12295
|
+
if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "scheduler" || scope === "cron" || scope === "board") {
|
|
11507
12296
|
action = argv.shift();
|
|
11508
12297
|
rest = argv;
|
|
11509
12298
|
} else {
|
|
@@ -11536,6 +12325,12 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
11536
12325
|
if (scope === "scheduler" && action === "attest-cutover") {
|
|
11537
12326
|
return runSchedulerAttestCutoverCli(args.json === true);
|
|
11538
12327
|
}
|
|
12328
|
+
if (scope === "cron" && action === "status") {
|
|
12329
|
+
return void await runCronStatusCli(args.json === true);
|
|
12330
|
+
}
|
|
12331
|
+
if (scope === "cron" && action === "tick") {
|
|
12332
|
+
return void await runCronTickCli(args);
|
|
12333
|
+
}
|
|
11539
12334
|
if (scope === "board" && action === "contract") {
|
|
11540
12335
|
return void await runCommandCenterContractCli(args);
|
|
11541
12336
|
}
|
|
@@ -11903,6 +12698,7 @@ export {
|
|
|
11903
12698
|
HERMES_OPENAI_CODEX_DEFAULT_MODEL,
|
|
11904
12699
|
OPENAI_CODEX_PROVIDER,
|
|
11905
12700
|
PACKAGE_VERSION,
|
|
12701
|
+
RUN_METADATA_ACTIVE_SIGNAL_MS,
|
|
11906
12702
|
TRANSIENT_OPENAI_CODEX_ERROR_CLASSES,
|
|
11907
12703
|
applyProductionDatabaseToProcess,
|
|
11908
12704
|
assessAutoCompleteEligibility,
|
|
@@ -11931,6 +12727,7 @@ export {
|
|
|
11931
12727
|
classifyVercelUrl,
|
|
11932
12728
|
classifyWorkerHealth,
|
|
11933
12729
|
codexProvider,
|
|
12730
|
+
collectFilesystemLiveRunKeys,
|
|
11934
12731
|
collectVercelEvidence,
|
|
11935
12732
|
compareProviderCandidates,
|
|
11936
12733
|
completeWorker,
|
|
@@ -11974,6 +12771,7 @@ export {
|
|
|
11974
12771
|
isFinishedWorkerStatus,
|
|
11975
12772
|
isForbiddenWorkerEnvKey,
|
|
11976
12773
|
isGeneratedHarnessPath,
|
|
12774
|
+
isHarnessRunMetadataPath,
|
|
11977
12775
|
isInspectableVercelTarget,
|
|
11978
12776
|
isKynverMonorepoRoot,
|
|
11979
12777
|
isLandingBlockedWorkerStatus,
|
|
@@ -12026,6 +12824,7 @@ export {
|
|
|
12026
12824
|
redactHarness,
|
|
12027
12825
|
redactProviderErrorText,
|
|
12028
12826
|
remediateDefaultRepo,
|
|
12827
|
+
repairMissingRunMetadata,
|
|
12029
12828
|
repairNestedRunsPath,
|
|
12030
12829
|
resolveBaseUrl,
|
|
12031
12830
|
resolveBoxKindFromEnv,
|
|
@@ -12042,6 +12841,7 @@ export {
|
|
|
12042
12841
|
resolveWorkerJsonPath,
|
|
12043
12842
|
runBoundedBuildCheck,
|
|
12044
12843
|
runDaemon,
|
|
12844
|
+
runDirHasActiveRetentionSignals,
|
|
12045
12845
|
runHarnessCleanup,
|
|
12046
12846
|
runHarnessVerifyCommands,
|
|
12047
12847
|
runMonitorTick,
|
|
@@ -12072,6 +12872,7 @@ export {
|
|
|
12072
12872
|
validateRunId,
|
|
12073
12873
|
validateTailLines,
|
|
12074
12874
|
validateWorkerName,
|
|
12875
|
+
workerDirHasActiveRetentionSignals,
|
|
12075
12876
|
workerStatus
|
|
12076
12877
|
};
|
|
12077
12878
|
//# sourceMappingURL=index.js.map
|