@kynver-app/runtime 0.1.50 → 0.1.56
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-guards.d.ts +3 -0
- package/dist/cleanup-retention-config.d.ts +14 -0
- package/dist/cleanup-run-liveness.d.ts +13 -0
- package/dist/cleanup-types.d.ts +12 -0
- package/dist/cleanup.d.ts +1 -1
- package/dist/cli.js +159 -70
- package/dist/cli.js.map +4 -4
- package/dist/finalize.d.ts +9 -0
- package/dist/index.js +159 -70
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cleanup-guards.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { CleanupSkipReason } from "./cleanup-types.js";
|
|
2
2
|
import type { IndexedWorktree } from "./cleanup-worktree-index.js";
|
|
3
3
|
import type { RawHarnessWorkerStatus } from "./status.js";
|
|
4
|
+
/** Blocks whole-worktree removal when commits are not landed or tree is dirty. */
|
|
4
5
|
export declare function isPrOrUnmergedWork(status: RawHarnessWorkerStatus): boolean;
|
|
6
|
+
/** Strip generated install trees from porcelain — they are what cleanup removes. */
|
|
7
|
+
export declare function materialWorktreeChanges(changedFiles: string[]): string[];
|
|
5
8
|
export interface WorktreeGuardInput {
|
|
6
9
|
indexed: IndexedWorktree | null;
|
|
7
10
|
includeOrphans: boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type HarnessCleanupOptions } from "./cleanup-types.js";
|
|
2
|
+
export interface ResolvedHarnessRetention {
|
|
3
|
+
execute: boolean;
|
|
4
|
+
finalizeStaleRuns: boolean;
|
|
5
|
+
nodeModulesAgeMs: number;
|
|
6
|
+
worktreesAgeMs: number;
|
|
7
|
+
includeOrphans: boolean;
|
|
8
|
+
runIdFilter?: string;
|
|
9
|
+
accountBytes: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Merge CLI options with operator env defaults (pipeline + `kynver cleanup`). */
|
|
12
|
+
export declare function resolveHarnessRetention(options?: HarnessCleanupOptions): ResolvedHarnessRetention;
|
|
13
|
+
/** Pipeline tick: dry-run by default; global scope when `KYNVER_CLEANUP_SCOPE=all`. */
|
|
14
|
+
export declare function resolvePipelineHarnessRetention(runId?: string): ResolvedHarnessRetention;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IndexedWorktree } from "./cleanup-worktree-index.js";
|
|
2
|
+
/** True when the worker process is still live or marked running in worker.json. */
|
|
3
|
+
export declare function isWorkerProcessLive(indexed: IndexedWorktree): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Run record still marked active but every worker is finished — safe to treat as
|
|
6
|
+
* terminal for GC after `finalizeStaleRuns()` (or when deriveTerminalRunStatus is set).
|
|
7
|
+
*/
|
|
8
|
+
export declare function isRunStaleActive(indexed: IndexedWorktree): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Whether the harness run still has unfinished work that should block whole-worktree removal.
|
|
11
|
+
* Does not block per-worker `node_modules` when only this worker is finished.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runBlocksWorktreeRemoval(indexed: IndexedWorktree): boolean;
|
package/dist/cleanup-types.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface CleanupAction extends CleanupCandidate {
|
|
|
15
15
|
skipReason?: CleanupSkipReason;
|
|
16
16
|
error?: string;
|
|
17
17
|
}
|
|
18
|
+
export interface RunFinalizeSummary {
|
|
19
|
+
runId: string;
|
|
20
|
+
from: string;
|
|
21
|
+
to: string;
|
|
22
|
+
}
|
|
18
23
|
export interface HarnessCleanupSummary {
|
|
19
24
|
harnessRoot: string;
|
|
20
25
|
dryRun: boolean;
|
|
@@ -23,6 +28,7 @@ export interface HarnessCleanupSummary {
|
|
|
23
28
|
worktreesAgeMs: number;
|
|
24
29
|
includeOrphans: boolean;
|
|
25
30
|
scannedAt: string;
|
|
31
|
+
finalizedRuns: RunFinalizeSummary[];
|
|
26
32
|
actions: CleanupAction[];
|
|
27
33
|
skips: Array<{
|
|
28
34
|
path: string;
|
|
@@ -31,15 +37,21 @@ export interface HarnessCleanupSummary {
|
|
|
31
37
|
}>;
|
|
32
38
|
totals: {
|
|
33
39
|
candidateBytes: number;
|
|
40
|
+
reclaimableBytes: number;
|
|
34
41
|
removedBytes: number;
|
|
35
42
|
removedPaths: number;
|
|
36
43
|
skippedPaths: number;
|
|
44
|
+
skipReasons: Partial<Record<CleanupSkipReason, number>>;
|
|
37
45
|
};
|
|
38
46
|
}
|
|
39
47
|
export interface HarnessCleanupOptions {
|
|
40
48
|
harnessRoot?: string;
|
|
41
49
|
/** When false (default), only report candidates. */
|
|
42
50
|
execute?: boolean;
|
|
51
|
+
/** When true (default), call `finalizeStaleRuns()` before scanning. */
|
|
52
|
+
finalizeStaleRuns?: boolean;
|
|
53
|
+
/** When true (default), estimate candidate bytes in dry-run summaries. */
|
|
54
|
+
accountBytes?: boolean;
|
|
43
55
|
/** Minimum age before removing generated `node_modules` (default 6h). */
|
|
44
56
|
nodeModulesAgeMs?: number;
|
|
45
57
|
/** When 0 or unset, worktree removal is disabled. */
|
package/dist/cleanup.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type HarnessCleanupOptions, type HarnessCleanupSummary } from "./cleanup-types.js";
|
|
2
2
|
export declare function runHarnessCleanup(options?: HarnessCleanupOptions): HarnessCleanupSummary;
|
|
3
|
-
/** Pipeline-safe defaults:
|
|
3
|
+
/** Pipeline-safe defaults: finalize stale runs, dry-run unless execute env is set. */
|
|
4
4
|
export declare function runPipelineHarnessCleanup(runId?: string): HarnessCleanupSummary;
|
|
5
5
|
export declare function isPipelineCleanupEnabled(): boolean;
|
package/dist/cli.js
CHANGED
|
@@ -4813,8 +4813,15 @@ import path24 from "node:path";
|
|
|
4813
4813
|
|
|
4814
4814
|
// src/finalize.ts
|
|
4815
4815
|
import path23 from "node:path";
|
|
4816
|
-
var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set([
|
|
4817
|
-
|
|
4816
|
+
var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set([
|
|
4817
|
+
"running",
|
|
4818
|
+
"dispatching",
|
|
4819
|
+
"pending",
|
|
4820
|
+
"queued",
|
|
4821
|
+
"needs_attention"
|
|
4822
|
+
]);
|
|
4823
|
+
var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "done"]);
|
|
4824
|
+
function deriveTerminalRunStatus(run) {
|
|
4818
4825
|
const names = Object.keys(run.workers || {});
|
|
4819
4826
|
if (names.length === 0) return "failed";
|
|
4820
4827
|
let anyAlive = false;
|
|
@@ -4852,7 +4859,7 @@ function finalizeStaleRuns() {
|
|
|
4852
4859
|
const finalized = [];
|
|
4853
4860
|
for (const run of listRunRecords()) {
|
|
4854
4861
|
if (!ACTIVE_RUN_STATUSES.has(run.status)) continue;
|
|
4855
|
-
const next =
|
|
4862
|
+
const next = deriveTerminalRunStatus(run);
|
|
4856
4863
|
if (!next || next === run.status) continue;
|
|
4857
4864
|
const from = run.status;
|
|
4858
4865
|
run.status = next;
|
|
@@ -5044,13 +5051,26 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
|
|
|
5044
5051
|
// src/cleanup.ts
|
|
5045
5052
|
import path30 from "node:path";
|
|
5046
5053
|
|
|
5047
|
-
// src/cleanup-
|
|
5048
|
-
|
|
5049
|
-
|
|
5054
|
+
// src/cleanup-run-liveness.ts
|
|
5055
|
+
function isWorkerProcessLive(indexed) {
|
|
5056
|
+
if (indexed.status.alive) return true;
|
|
5057
|
+
if (indexed.worker.status === "running") return true;
|
|
5058
|
+
return false;
|
|
5059
|
+
}
|
|
5060
|
+
function isRunStaleActive(indexed) {
|
|
5061
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
5062
|
+
return deriveTerminalRunStatus(indexed.run) !== null;
|
|
5063
|
+
}
|
|
5064
|
+
function runBlocksWorktreeRemoval(indexed) {
|
|
5065
|
+
if (isWorkerProcessLive(indexed)) return true;
|
|
5066
|
+
if (indexed.worker.completionBlocker) return true;
|
|
5067
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
5068
|
+
if (isRunStaleActive(indexed)) return false;
|
|
5069
|
+
if (!isFinishedWorkerStatus(indexed.status)) return true;
|
|
5070
|
+
return deriveTerminalRunStatus(indexed.run) === null;
|
|
5071
|
+
}
|
|
5050
5072
|
|
|
5051
5073
|
// src/cleanup-guards.ts
|
|
5052
|
-
var ACTIVE_RUN_STATUSES2 = /* @__PURE__ */ new Set(["running", "dispatching", "pending", "queued", "needs_attention"]);
|
|
5053
|
-
var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
5054
5074
|
function prUrlFromFinalResult(finalResult) {
|
|
5055
5075
|
if (typeof finalResult === "string") {
|
|
5056
5076
|
const match = finalResult.match(/https:\/\/github\.com\/[^\s]+\/pull\/\d+/i);
|
|
@@ -5072,18 +5092,29 @@ function isPrOrUnmergedWork(status) {
|
|
|
5072
5092
|
if (status.changedFiles.length > 0 && status.finalResult) return true;
|
|
5073
5093
|
return false;
|
|
5074
5094
|
}
|
|
5095
|
+
function materialWorktreeChanges(changedFiles) {
|
|
5096
|
+
return changedFiles.filter((line) => {
|
|
5097
|
+
const trimmed = line.trim();
|
|
5098
|
+
const pathPart = trimmed.startsWith("??") ? trimmed.slice(2).trim() : trimmed.length > 3 ? trimmed.slice(3).trim() : trimmed;
|
|
5099
|
+
return pathPart !== "node_modules" && !pathPart.startsWith("node_modules/");
|
|
5100
|
+
});
|
|
5101
|
+
}
|
|
5102
|
+
function hasUnrestorableWorktreeChanges(status) {
|
|
5103
|
+
if (materialWorktreeChanges(status.changedFiles).length > 0) return true;
|
|
5104
|
+
if (status.gitAncestry?.relation === "diverged") return true;
|
|
5105
|
+
return false;
|
|
5106
|
+
}
|
|
5075
5107
|
function skipWorktreeRemoval(input) {
|
|
5076
5108
|
const { indexed, includeOrphans, worktreesAgeMs, ageMs } = input;
|
|
5077
5109
|
if (worktreesAgeMs <= 0) return "worktrees_disabled";
|
|
5078
5110
|
if (ageMs < worktreesAgeMs) return "below_age_threshold";
|
|
5079
5111
|
if (!indexed) return includeOrphans ? null : "orphan_without_flag";
|
|
5080
|
-
if (
|
|
5081
|
-
if (!TERMINAL_RUN_STATUSES.has(indexed.run.status)) return "run_still_active";
|
|
5082
|
-
if (indexed.status.alive) return "active_worker";
|
|
5083
|
-
if (indexed.worker.status === "running") return "active_worker";
|
|
5112
|
+
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
5084
5113
|
if (indexed.worker.completionBlocker) return "completion_blocked";
|
|
5114
|
+
if (runBlocksWorktreeRemoval(indexed)) return "run_still_active";
|
|
5115
|
+
if (!isFinishedWorkerStatus(indexed.status)) return "run_still_active";
|
|
5085
5116
|
if (isPrOrUnmergedWork(indexed.status)) return "pr_or_unmerged_commits";
|
|
5086
|
-
if (indexed.status.changedFiles.length > 0) return "dirty_worktree";
|
|
5117
|
+
if (materialWorktreeChanges(indexed.status.changedFiles).length > 0) return "dirty_worktree";
|
|
5087
5118
|
const landing = assessWorkerLanding({
|
|
5088
5119
|
finalResult: indexed.status.finalResult,
|
|
5089
5120
|
changedFiles: indexed.status.changedFiles,
|
|
@@ -5097,18 +5128,19 @@ function skipNodeModulesRemoval(input) {
|
|
|
5097
5128
|
const { indexed, includeOrphans, nodeModulesAgeMs, ageMs } = input;
|
|
5098
5129
|
if (ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
5099
5130
|
if (!indexed) return includeOrphans ? null : "orphan_without_flag";
|
|
5100
|
-
if (indexed
|
|
5101
|
-
if (indexed.worker.status === "running") return "active_worker";
|
|
5131
|
+
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
5102
5132
|
if (indexed.worker.completionBlocker) return "completion_blocked";
|
|
5103
|
-
if (
|
|
5104
|
-
if (indexed.status
|
|
5133
|
+
if (!isFinishedWorkerStatus(indexed.status)) return "run_still_active";
|
|
5134
|
+
if (hasUnrestorableWorktreeChanges(indexed.status)) return "dirty_worktree";
|
|
5105
5135
|
const landing = assessWorkerLanding({
|
|
5106
5136
|
finalResult: indexed.status.finalResult,
|
|
5107
5137
|
changedFiles: indexed.status.changedFiles,
|
|
5108
5138
|
gitAncestry: indexed.status.gitAncestry,
|
|
5109
5139
|
prUrl: prUrlFromFinalResult(indexed.status.finalResult)
|
|
5110
5140
|
});
|
|
5111
|
-
if (landing.blocked)
|
|
5141
|
+
if (landing.blocked && materialWorktreeChanges(indexed.status.changedFiles).length > 0) {
|
|
5142
|
+
return "landing_blocked";
|
|
5143
|
+
}
|
|
5112
5144
|
return null;
|
|
5113
5145
|
}
|
|
5114
5146
|
|
|
@@ -5335,48 +5367,98 @@ function buildWorktreeIndex() {
|
|
|
5335
5367
|
return index;
|
|
5336
5368
|
}
|
|
5337
5369
|
|
|
5338
|
-
// src/cleanup.ts
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
const
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5370
|
+
// src/cleanup-types.ts
|
|
5371
|
+
var DEFAULT_NODE_MODULES_AGE_MS = 6 * 60 * 60 * 1e3;
|
|
5372
|
+
var DEFAULT_WORKTREES_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
5373
|
+
|
|
5374
|
+
// src/cleanup-retention-config.ts
|
|
5375
|
+
function envFlag(name) {
|
|
5376
|
+
const v = process.env[name];
|
|
5377
|
+
return v === "1" || v === "true" || v === "yes";
|
|
5378
|
+
}
|
|
5379
|
+
function envMs(name, fallback) {
|
|
5380
|
+
const raw = process.env[name];
|
|
5381
|
+
if (!raw) return fallback;
|
|
5382
|
+
const n = Number(raw);
|
|
5383
|
+
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
5384
|
+
}
|
|
5385
|
+
function resolveHarnessRetention(options = {}) {
|
|
5386
|
+
const execute = options.execute === true || options.execute !== false && envFlag("KYNVER_CLEANUP_EXECUTE");
|
|
5387
|
+
const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag("KYNVER_CLEANUP_SKIP_FINALIZE");
|
|
5388
|
+
const nodeModulesAgeMs = options.nodeModulesAgeMs ?? envMs("KYNVER_CLEANUP_NODE_MODULES_AGE_MS", DEFAULT_NODE_MODULES_AGE_MS);
|
|
5389
|
+
const worktreesAgeMs = options.worktreesAgeMs ?? envMs("KYNVER_CLEANUP_WORKTREES_AGE_MS", 0);
|
|
5390
|
+
const includeOrphans = options.includeOrphans === true || envFlag("KYNVER_CLEANUP_INCLUDE_ORPHANS");
|
|
5391
|
+
const scopeAll = envFlag("KYNVER_CLEANUP_SCOPE_ALL") || process.env.KYNVER_CLEANUP_SCOPE === "all";
|
|
5392
|
+
const runIdFilter = scopeAll ? options.runIdFilter : options.runIdFilter ?? (process.env.KYNVER_CLEANUP_RUN_ID || void 0);
|
|
5393
|
+
const accountBytes = options.accountBytes !== false && !envFlag("KYNVER_CLEANUP_SKIP_BYTE_ACCOUNTING");
|
|
5348
5394
|
return {
|
|
5349
|
-
harnessRoot,
|
|
5350
|
-
worktreesDir,
|
|
5351
5395
|
execute,
|
|
5352
|
-
|
|
5396
|
+
finalizeStaleRuns: finalizeStaleRuns2,
|
|
5353
5397
|
nodeModulesAgeMs,
|
|
5354
|
-
worktreesAgeMs,
|
|
5398
|
+
worktreesAgeMs: worktreesAgeMs > 0 ? worktreesAgeMs : 0,
|
|
5355
5399
|
includeOrphans,
|
|
5356
|
-
runIdFilter,
|
|
5357
|
-
|
|
5400
|
+
runIdFilter: runIdFilter ? String(runIdFilter) : void 0,
|
|
5401
|
+
accountBytes
|
|
5358
5402
|
};
|
|
5359
5403
|
}
|
|
5404
|
+
function resolvePipelineHarnessRetention(runId) {
|
|
5405
|
+
const scopeAll = process.env.KYNVER_CLEANUP_SCOPE === "all";
|
|
5406
|
+
const envWorktrees = Number(process.env.KYNVER_CLEANUP_WORKTREES_AGE_MS);
|
|
5407
|
+
const worktreesAgeMs = scopeAll ? Number.isFinite(envWorktrees) && envWorktrees > 0 ? envWorktrees : DEFAULT_WORKTREES_AGE_MS : Number.isFinite(envWorktrees) && envWorktrees > 0 ? envWorktrees : 0;
|
|
5408
|
+
return resolveHarnessRetention({
|
|
5409
|
+
runIdFilter: scopeAll ? void 0 : runId,
|
|
5410
|
+
worktreesAgeMs,
|
|
5411
|
+
finalizeStaleRuns: true,
|
|
5412
|
+
accountBytes: true
|
|
5413
|
+
});
|
|
5414
|
+
}
|
|
5415
|
+
|
|
5416
|
+
// src/cleanup.ts
|
|
5417
|
+
function resolvePaths(options = {}) {
|
|
5418
|
+
const harnessRoot = options.harnessRoot ? resolveUserPath(options.harnessRoot) : resolveHarnessRoot();
|
|
5419
|
+
const { worktreesDir } = options.harnessRoot ? { worktreesDir: path30.join(harnessRoot, "worktrees") } : getHarnessPaths();
|
|
5420
|
+
const now = options.now ?? Date.now();
|
|
5421
|
+
return { harnessRoot, worktreesDir, now };
|
|
5422
|
+
}
|
|
5360
5423
|
function recordSkip(skips, pathValue, reason, detail) {
|
|
5361
5424
|
skips.push({ path: pathValue, reason, ...detail ? { detail } : {} });
|
|
5362
5425
|
}
|
|
5426
|
+
function attachCandidateBytes(candidate, accountBytes) {
|
|
5427
|
+
if (!accountBytes || candidate.bytes != null) return candidate;
|
|
5428
|
+
return { ...candidate, bytes: directorySizeBytes(candidate.path) };
|
|
5429
|
+
}
|
|
5430
|
+
function tallySkipReasons(actions, skips) {
|
|
5431
|
+
const counts = {};
|
|
5432
|
+
for (const skip of skips) {
|
|
5433
|
+
counts[skip.reason] = (counts[skip.reason] ?? 0) + 1;
|
|
5434
|
+
}
|
|
5435
|
+
for (const action of actions) {
|
|
5436
|
+
if (action.skipReason) {
|
|
5437
|
+
counts[action.skipReason] = (counts[action.skipReason] ?? 0) + 1;
|
|
5438
|
+
}
|
|
5439
|
+
}
|
|
5440
|
+
return counts;
|
|
5441
|
+
}
|
|
5363
5442
|
function runHarnessCleanup(options = {}) {
|
|
5364
|
-
const
|
|
5443
|
+
const retention = resolveHarnessRetention(options);
|
|
5444
|
+
const paths = resolvePaths(options);
|
|
5445
|
+
const finalizedRuns = retention.finalizeStaleRuns ? finalizeStaleRuns().map((f) => ({ runId: f.runId, from: f.from, to: f.to })) : [];
|
|
5365
5446
|
const index = buildWorktreeIndex();
|
|
5366
5447
|
const scanOpts = {
|
|
5367
|
-
harnessRoot:
|
|
5368
|
-
worktreesDir:
|
|
5369
|
-
nodeModulesAgeMs:
|
|
5370
|
-
worktreesAgeMs:
|
|
5371
|
-
includeOrphans:
|
|
5372
|
-
runIdFilter:
|
|
5448
|
+
harnessRoot: paths.harnessRoot,
|
|
5449
|
+
worktreesDir: paths.worktreesDir,
|
|
5450
|
+
nodeModulesAgeMs: retention.nodeModulesAgeMs,
|
|
5451
|
+
worktreesAgeMs: retention.worktreesAgeMs,
|
|
5452
|
+
includeOrphans: retention.includeOrphans,
|
|
5453
|
+
runIdFilter: retention.runIdFilter,
|
|
5373
5454
|
index,
|
|
5374
|
-
now:
|
|
5455
|
+
now: paths.now
|
|
5375
5456
|
};
|
|
5376
5457
|
const skips = [];
|
|
5377
5458
|
const actions = [];
|
|
5378
|
-
for (const
|
|
5379
|
-
const
|
|
5459
|
+
for (const raw of scanNodeModulesCandidates(scanOpts)) {
|
|
5460
|
+
const candidate = attachCandidateBytes(raw, retention.accountBytes);
|
|
5461
|
+
const pathSkip = isHarnessNodeModulesPath(candidate.path, paths.harnessRoot, paths.worktreesDir);
|
|
5380
5462
|
if (pathSkip) {
|
|
5381
5463
|
recordSkip(skips, candidate.path, pathSkip);
|
|
5382
5464
|
actions.push({ ...candidate, executed: false, skipped: true, skipReason: pathSkip });
|
|
@@ -5386,8 +5468,8 @@ function runHarnessCleanup(options = {}) {
|
|
|
5386
5468
|
const indexed = index.get(worktreePath) ?? null;
|
|
5387
5469
|
const guardReason = skipNodeModulesRemoval({
|
|
5388
5470
|
indexed,
|
|
5389
|
-
includeOrphans:
|
|
5390
|
-
nodeModulesAgeMs:
|
|
5471
|
+
includeOrphans: retention.includeOrphans,
|
|
5472
|
+
nodeModulesAgeMs: retention.nodeModulesAgeMs,
|
|
5391
5473
|
ageMs: candidate.ageMs
|
|
5392
5474
|
});
|
|
5393
5475
|
if (guardReason) {
|
|
@@ -5395,14 +5477,15 @@ function runHarnessCleanup(options = {}) {
|
|
|
5395
5477
|
actions.push({ ...candidate, executed: false, skipped: true, skipReason: guardReason });
|
|
5396
5478
|
continue;
|
|
5397
5479
|
}
|
|
5398
|
-
actions.push(removeNodeModules(candidate,
|
|
5480
|
+
actions.push(removeNodeModules(candidate, retention.execute));
|
|
5399
5481
|
}
|
|
5400
|
-
for (const
|
|
5482
|
+
for (const raw of scanWorktreeCandidates(scanOpts)) {
|
|
5483
|
+
const candidate = attachCandidateBytes(raw, retention.accountBytes);
|
|
5401
5484
|
const indexed = index.get(path30.resolve(candidate.path)) ?? null;
|
|
5402
5485
|
const guardReason = skipWorktreeRemoval({
|
|
5403
5486
|
indexed,
|
|
5404
|
-
includeOrphans:
|
|
5405
|
-
worktreesAgeMs:
|
|
5487
|
+
includeOrphans: retention.includeOrphans,
|
|
5488
|
+
worktreesAgeMs: retention.worktreesAgeMs,
|
|
5406
5489
|
ageMs: candidate.ageMs
|
|
5407
5490
|
});
|
|
5408
5491
|
if (guardReason) {
|
|
@@ -5410,51 +5493,55 @@ function runHarnessCleanup(options = {}) {
|
|
|
5410
5493
|
actions.push({ ...candidate, executed: false, skipped: true, skipReason: guardReason });
|
|
5411
5494
|
continue;
|
|
5412
5495
|
}
|
|
5413
|
-
actions.push(removeWorktree(candidate,
|
|
5496
|
+
actions.push(removeWorktree(candidate, retention.execute));
|
|
5414
5497
|
}
|
|
5415
5498
|
let candidateBytes = 0;
|
|
5499
|
+
let reclaimableBytes = 0;
|
|
5416
5500
|
let removedBytes = 0;
|
|
5417
5501
|
let removedPaths = 0;
|
|
5418
5502
|
let skippedPaths = 0;
|
|
5419
5503
|
for (const action of actions) {
|
|
5420
5504
|
if (action.bytes) candidateBytes += action.bytes;
|
|
5505
|
+
if (!action.skipped && !action.executed && action.bytes) reclaimableBytes += action.bytes;
|
|
5421
5506
|
if (action.executed) {
|
|
5422
5507
|
removedPaths += 1;
|
|
5423
5508
|
removedBytes += action.bytes ?? 0;
|
|
5424
5509
|
} else if (action.skipped) {
|
|
5425
5510
|
skippedPaths += 1;
|
|
5511
|
+
if (action.skipReason === "dry_run" && action.bytes) reclaimableBytes += action.bytes;
|
|
5426
5512
|
}
|
|
5427
5513
|
}
|
|
5428
5514
|
return {
|
|
5429
|
-
harnessRoot:
|
|
5430
|
-
dryRun:
|
|
5431
|
-
execute:
|
|
5432
|
-
nodeModulesAgeMs:
|
|
5433
|
-
worktreesAgeMs:
|
|
5434
|
-
includeOrphans:
|
|
5435
|
-
scannedAt: new Date(
|
|
5515
|
+
harnessRoot: paths.harnessRoot,
|
|
5516
|
+
dryRun: !retention.execute,
|
|
5517
|
+
execute: retention.execute,
|
|
5518
|
+
nodeModulesAgeMs: retention.nodeModulesAgeMs,
|
|
5519
|
+
worktreesAgeMs: retention.worktreesAgeMs,
|
|
5520
|
+
includeOrphans: retention.includeOrphans,
|
|
5521
|
+
scannedAt: new Date(paths.now).toISOString(),
|
|
5522
|
+
finalizedRuns,
|
|
5436
5523
|
actions,
|
|
5437
5524
|
skips,
|
|
5438
5525
|
totals: {
|
|
5439
5526
|
candidateBytes,
|
|
5527
|
+
reclaimableBytes,
|
|
5440
5528
|
removedBytes,
|
|
5441
5529
|
removedPaths,
|
|
5442
|
-
skippedPaths
|
|
5530
|
+
skippedPaths,
|
|
5531
|
+
skipReasons: tallySkipReasons(actions, skips)
|
|
5443
5532
|
}
|
|
5444
5533
|
};
|
|
5445
5534
|
}
|
|
5446
5535
|
function runPipelineHarnessCleanup(runId) {
|
|
5447
|
-
const
|
|
5448
|
-
const worktreesAgeMs = Number(process.env.KYNVER_CLEANUP_WORKTREES_AGE_MS) || 0;
|
|
5449
|
-
const execute = process.env.KYNVER_CLEANUP_EXECUTE === "1";
|
|
5450
|
-
const includeOrphans = process.env.KYNVER_CLEANUP_INCLUDE_ORPHANS === "1";
|
|
5451
|
-
const scopeAll = process.env.KYNVER_CLEANUP_SCOPE === "all";
|
|
5536
|
+
const retention = resolvePipelineHarnessRetention(runId);
|
|
5452
5537
|
return runHarnessCleanup({
|
|
5453
|
-
execute,
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5538
|
+
execute: retention.execute,
|
|
5539
|
+
finalizeStaleRuns: retention.finalizeStaleRuns,
|
|
5540
|
+
accountBytes: retention.accountBytes,
|
|
5541
|
+
nodeModulesAgeMs: retention.nodeModulesAgeMs,
|
|
5542
|
+
worktreesAgeMs: retention.worktreesAgeMs,
|
|
5543
|
+
includeOrphans: retention.includeOrphans,
|
|
5544
|
+
runIdFilter: retention.runIdFilter
|
|
5458
5545
|
});
|
|
5459
5546
|
}
|
|
5460
5547
|
function isPipelineCleanupEnabled() {
|
|
@@ -6121,12 +6208,14 @@ async function runPlanOutboxDrain(args) {
|
|
|
6121
6208
|
// src/cleanup-cli.ts
|
|
6122
6209
|
function runCleanupCli(args) {
|
|
6123
6210
|
const execute = args.execute === true || args.execute === "true";
|
|
6211
|
+
const skipFinalize = args.skipFinalize === true || args.skipFinalize === "true";
|
|
6124
6212
|
const nodeModulesAgeMs = args.nodeModulesAgeMs ? Number(args.nodeModulesAgeMs) : DEFAULT_NODE_MODULES_AGE_MS;
|
|
6125
6213
|
const worktreesAgeMs = args.worktreesAgeMs ? Number(args.worktreesAgeMs) : 0;
|
|
6126
6214
|
const includeOrphans = args.includeOrphans === true || args.includeOrphans === "true";
|
|
6127
6215
|
const harnessRoot = args.harnessRoot ? String(args.harnessRoot) : void 0;
|
|
6128
6216
|
const summary = runHarnessCleanup({
|
|
6129
6217
|
execute,
|
|
6218
|
+
finalizeStaleRuns: !skipFinalize,
|
|
6130
6219
|
nodeModulesAgeMs: Number.isFinite(nodeModulesAgeMs) ? nodeModulesAgeMs : DEFAULT_NODE_MODULES_AGE_MS,
|
|
6131
6220
|
worktreesAgeMs: Number.isFinite(worktreesAgeMs) ? worktreesAgeMs : 0,
|
|
6132
6221
|
includeOrphans,
|
|
@@ -7281,7 +7370,7 @@ function usage(code = 0) {
|
|
|
7281
7370
|
" kynver plan persist --operation create|add_version|update_metadata --title TITLE (--body-file PATH | --body TEXT) [--slug SLUG] [--plan PLAN_ID] [--summary TEXT] [--failure-kind approval_guard|auth|network|server|tool_interruption]",
|
|
7282
7371
|
" kynver plan outbox list",
|
|
7283
7372
|
" kynver plan outbox drain [--max N] [--id OUTBOX_ID]",
|
|
7284
|
-
" kynver cleanup [--execute] [--node-modules-age-ms MS] [--worktrees-age-ms MS] [--harness-root PATH] [--include-orphans]",
|
|
7373
|
+
" kynver cleanup [--execute] [--node-modules-age-ms MS] [--worktrees-age-ms MS] [--harness-root PATH] [--include-orphans] [--skip-finalize]",
|
|
7285
7374
|
" kynver monitor start --run RUN_ID [--name worker] [--agent-os-id AOS_ID] [--poll-ms MS]",
|
|
7286
7375
|
" kynver monitor status [--run RUN_ID] [--name worker] [--tick]",
|
|
7287
7376
|
" kynver monitor stop --run RUN_ID [--name worker]",
|