@bitkyc08/opencodex 2.55.0 → 2.56.0
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/gui/dist/assets/{index-VuoiWj9J.js → index-D4zuyIxQ.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +2 -1
- package/src/adapters/base.ts +21 -0
- package/src/adapters/cursor/transport-retry.ts +46 -1
- package/src/adapters/cursor.ts +4 -0
- package/src/adapters/kiro/adapter.ts +42 -1
- package/src/adapters/kiro-retry.ts +23 -4
- package/src/adapters/openai-chat/errors.ts +116 -0
- package/src/adapters/openai-chat/messages.ts +346 -0
- package/src/adapters/openai-chat/passthrough.ts +146 -0
- package/src/adapters/openai-chat/response-events.ts +117 -0
- package/src/adapters/openai-chat/tool-call-validation.ts +200 -0
- package/src/adapters/openai-chat/tool-schema.ts +477 -0
- package/src/adapters/openai-chat/wire.ts +50 -0
- package/src/adapters/openai-chat.ts +33 -1445
- package/src/adapters/openai-responses/canonical-forward.ts +202 -0
- package/src/adapters/openai-responses/image-gen.ts +406 -0
- package/src/adapters/openai-responses/internal.ts +3 -0
- package/src/adapters/openai-responses/passthrough.ts +611 -0
- package/src/adapters/openai-responses/prompt-cache.ts +83 -0
- package/src/adapters/openai-responses/reasoning.ts +220 -0
- package/src/adapters/openai-responses/request-strips.ts +185 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +509 -0
- package/src/adapters/openai-responses/tool-schema.ts +293 -0
- package/src/adapters/openai-responses/web-search.ts +156 -0
- package/src/adapters/openai-responses.ts +4 -2625
- package/src/bridge/errors.ts +34 -0
- package/src/bridge/internal.ts +174 -0
- package/src/bridge/response-json.ts +624 -0
- package/src/bridge/sse.ts +1444 -0
- package/src/bridge.ts +5 -2204
- package/src/chat/inbound.ts +12 -1
- package/src/codex/account-lifecycle.ts +3 -0
- package/src/codex/account-store.ts +71 -9
- package/src/codex/auth-api/account-list.ts +507 -0
- package/src/codex/auth-api/http.ts +32 -0
- package/src/codex/auth-api/login-flow.ts +554 -0
- package/src/codex/auth-api/login-state.ts +64 -0
- package/src/codex/auth-api/main-account-probe.ts +331 -0
- package/src/codex/auth-api/pool-mode-gate.ts +274 -0
- package/src/codex/auth-api/pool-quota-probe.ts +512 -0
- package/src/codex/auth-api/reset-credit-service.ts +422 -0
- package/src/codex/auth-api/routes.ts +425 -0
- package/src/codex/auth-api/runtime-config.ts +48 -0
- package/src/codex/auth-api.ts +27 -3118
- package/src/codex/auth-context.ts +95 -28
- package/src/codex/catalog/auto-review.ts +507 -0
- package/src/codex/catalog/build-entries.ts +981 -0
- package/src/codex/catalog/combo-member.ts +375 -0
- package/src/codex/catalog/derive-entry.ts +229 -0
- package/src/codex/catalog/effort.ts +0 -1
- package/src/codex/catalog/gated-native-warn.ts +63 -0
- package/src/codex/catalog/gather-capture.ts +533 -0
- package/src/codex/catalog/model-hints.ts +691 -0
- package/src/codex/catalog/model-visibility.ts +304 -0
- package/src/codex/catalog/provider-fetch.ts +52 -2942
- package/src/codex/catalog/provider-models.ts +685 -0
- package/src/codex/catalog/restore.ts +132 -0
- package/src/codex/catalog/retained-sync.ts +706 -0
- package/src/codex/catalog/routed-gather.ts +858 -0
- package/src/codex/catalog/subagent-roster.ts +176 -0
- package/src/codex/catalog/sync.ts +52 -2698
- package/src/codex/inject/config-toml.ts +563 -0
- package/src/codex/inject/remove.ts +192 -0
- package/src/codex/inject/restore.ts +540 -0
- package/src/codex/inject/routing-classify.ts +109 -0
- package/src/codex/inject/routing-target.ts +125 -0
- package/src/codex/inject.ts +81 -1436
- package/src/codex/lineage.ts +458 -0
- package/src/codex/pool-refresh-backoff.ts +152 -0
- package/src/codex/routing/active-account.ts +194 -0
- package/src/codex/routing/cooldown-math.ts +275 -0
- package/src/codex/routing/health-store.ts +402 -0
- package/src/codex/routing/probe-lease.ts +358 -0
- package/src/codex/routing/selection.ts +703 -0
- package/src/codex/routing/thread-affinity.ts +538 -0
- package/src/codex/routing.ts +353 -2234
- package/src/codex/shim-fingerprint.ts +223 -0
- package/src/codex/shim-inspect.ts +175 -0
- package/src/codex/shim-probe.ts +367 -0
- package/src/codex/shim-restore-lock.ts +169 -0
- package/src/codex/shim-state-file.ts +151 -0
- package/src/codex/shim-templates.ts +265 -0
- package/src/codex/shim.ts +48 -1268
- package/src/config/diagnostics.ts +705 -0
- package/src/config/feature-flags.ts +55 -0
- package/src/config/live-reconcile.ts +403 -0
- package/src/config/load-degrade.ts +880 -0
- package/src/config/mutation-lock.ts +244 -0
- package/src/config/openai-tier-backup.ts +268 -0
- package/src/config/persist-unlocked.ts +92 -0
- package/src/config/proxy-env.ts +188 -0
- package/src/config/salvage.ts +244 -0
- package/src/config/schema/config-schema.ts +640 -0
- package/src/config/schema/leaf-validators.ts +855 -0
- package/src/config/warn-memo.ts +28 -0
- package/src/config.ts +234 -4481
- package/src/generated/compatibility-version.json +539 -39
- package/src/lib/request-execution-budget.ts +69 -20
- package/src/lib/spend-reservation-ledger.ts +940 -0
- package/src/lib/upstream-retry.ts +55 -11
- package/src/lib/workflow-budget.ts +553 -30
- package/src/providers/quota/account-cache.ts +441 -0
- package/src/providers/quota/antigravity.ts +295 -0
- package/src/providers/quota/report-cache.ts +320 -0
- package/src/providers/quota/vendor-probes-key.ts +1243 -0
- package/src/providers/quota/vendor-probes-oauth.ts +590 -0
- package/src/providers/quota.ts +324 -3079
- package/src/providers/registry/entries-core.ts +1221 -0
- package/src/providers/registry/entries-extended.ts +1204 -0
- package/src/providers/registry/model-seeds.ts +908 -0
- package/src/providers/registry/types.ts +352 -0
- package/src/providers/registry.ts +24 -3536
- package/src/responses/continuation-ownership.ts +29 -0
- package/src/responses/state/replay-fingerprint.ts +80 -0
- package/src/responses/state/snapshot-codec.ts +104 -0
- package/src/responses/state/spill-failure.ts +118 -0
- package/src/responses/state/spill-queue.ts +665 -0
- package/src/responses/state/temp-recovery.ts +257 -0
- package/src/responses/state.ts +82 -1143
- package/src/routing/identity-domains.ts +449 -0
- package/src/routing/probe-lease.ts +511 -0
- package/src/server/index/bounded-request.ts +88 -0
- package/src/server/index/live-sideband.ts +565 -0
- package/src/server/index/serve-options.ts +1766 -0
- package/src/server/index/startup-warnings.ts +213 -0
- package/src/server/index/websocket-handler.ts +335 -0
- package/src/server/index.ts +40 -2547
- package/src/server/management/route-registry.ts +26 -23
- package/src/server/management/shared.ts +8 -5
- package/src/server/management/workflow-budget-routes.ts +133 -0
- package/src/server/management-api.ts +12 -0
- package/src/server/request-log-conversation.ts +9 -7
- package/src/server/request-log.ts +245 -1
- package/src/server/responses/account-change-state.ts +233 -0
- package/src/server/responses/adapter-continuation.ts +514 -0
- package/src/server/responses/adapter-delivery.ts +214 -0
- package/src/server/responses/adapter-dispatch.ts +971 -0
- package/src/server/responses/compact.ts +59 -4
- package/src/server/responses/completion-policy.ts +33 -0
- package/src/server/responses/core-auth.ts +527 -0
- package/src/server/responses/core-codex-account.ts +859 -0
- package/src/server/responses/core-combo-failure.ts +210 -0
- package/src/server/responses/core-combo.ts +707 -0
- package/src/server/responses/core-errors.ts +152 -0
- package/src/server/responses/core-lifetime.ts +95 -0
- package/src/server/responses/core-normalize.ts +350 -0
- package/src/server/responses/core-opaque-recovery.ts +380 -0
- package/src/server/responses/core-options.ts +159 -0
- package/src/server/responses/core-replay.ts +225 -0
- package/src/server/responses/core.ts +192 -8893
- package/src/server/responses/passthrough-delivery.ts +856 -0
- package/src/server/responses/passthrough-dispatch.ts +1476 -0
- package/src/server/responses/passthrough-execution.ts +54 -0
- package/src/server/responses/request-prepare.ts +970 -0
- package/src/server/responses/request-send-budget.ts +164 -0
- package/src/server/responses/request-sidecar-auth.ts +149 -0
- package/src/server/responses/request-transport.ts +744 -0
- package/src/server/responses/response-effects.ts +157 -0
- package/src/server/responses/run-turn-execution.ts +448 -0
- package/src/server/responses/sidecar-execution.ts +469 -0
- package/src/server/responses-image-gen-repair.ts +1 -1
- package/src/server/workflow-refusal.ts +84 -0
- package/src/types/config.ts +30 -0
- package/src/usage/log.ts +146 -0
- package/src/usage/summary.ts +171 -21
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { opendirSync, lstatSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { uptime } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { getConfigDir, resolveWriteTarget } from "../../config";
|
|
5
|
+
|
|
6
|
+
const STALE_TEMP_GRACE_MS = 15 * 60 * 1_000;
|
|
7
|
+
const STALE_TEMP_MAX_ENTRIES = 4_096;
|
|
8
|
+
const STALE_TEMP_MAX_CLEANUPS = 512;
|
|
9
|
+
/** Absorbs `os.uptime()` granularity only. It is deliberately NOT the safety margin:
|
|
10
|
+
* the unconditional 15-minute grace above is (see the boot floor in the scan loop). */
|
|
11
|
+
const BOOT_FLOOR_SKEW_MS = 60 * 1_000;
|
|
12
|
+
/** Per-tick budget for the periodic reclaim. Smaller than the startup budget because the
|
|
13
|
+
* periodic pass runs synchronously on the serving process's event loop every 60 s. */
|
|
14
|
+
const PERIODIC_TEMP_MAX_ENTRIES = 512;
|
|
15
|
+
const PERIODIC_TEMP_MAX_CLEANUPS = 64;
|
|
16
|
+
/** Wall-clock ceiling for one periodic scan. An entry cap bounds syscalls, not time: on a
|
|
17
|
+
* network-mounted config dir each `lstat` can cost 10-20 ms, which would stall in-flight
|
|
18
|
+
* streams. Reclaim is idempotent, so a truncated tick simply resumes on the next one. */
|
|
19
|
+
const PERIODIC_TEMP_SCAN_DEADLINE_MS = 25;
|
|
20
|
+
const RESPONSE_STATE_TEMP_NAME = /^responses-state\.json\.ocx\.(\d+)\.(\d+)\.tmp$/;
|
|
21
|
+
|
|
22
|
+
export interface ResponseStateTempRecoveryResult {
|
|
23
|
+
matched: number;
|
|
24
|
+
removed: number;
|
|
25
|
+
failed: number;
|
|
26
|
+
bytesRemoved: number;
|
|
27
|
+
/** Entries that passed EVERY gate and would be reclaimed. In a dry run nothing is
|
|
28
|
+
* unlinked, so this is the only honest count to show an operator: `matched` is
|
|
29
|
+
* incremented before the file-type, age, boot-floor, and liveness gates. */
|
|
30
|
+
eligible: number;
|
|
31
|
+
/** Total size of the `eligible` entries. */
|
|
32
|
+
eligibleBytes: number;
|
|
33
|
+
/** The scan stopped on a budget (entry cap, cleanup cap, or deadline) rather than reaching
|
|
34
|
+
* the end of the directory, so the counts below describe a prefix of the backlog and not
|
|
35
|
+
* the backlog. `eligible > removed + failed` cannot express this: outside a dry run every
|
|
36
|
+
* eligible entry is unlinked or failed on the same iteration, so the two are always equal
|
|
37
|
+
* and a comparison between them is dead code. */
|
|
38
|
+
truncated: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ResponseStateTempRecoveryIO {
|
|
42
|
+
now: () => number;
|
|
43
|
+
/** Approximate epoch ms of the current boot; see the boot floor in the scan loop. */
|
|
44
|
+
bootTime: () => number;
|
|
45
|
+
list: (dir: string) => Iterable<string>;
|
|
46
|
+
inspect: (path: string) => { isFile: boolean; mtimeMs: number; size: number };
|
|
47
|
+
isProcessAlive: (pid: number) => boolean;
|
|
48
|
+
unlink: (path: string) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type ResponseStateTempRecoveryOptions = Partial<ResponseStateTempRecoveryIO> & {
|
|
52
|
+
maxEntries?: number;
|
|
53
|
+
maxCleanups?: number;
|
|
54
|
+
/** Wall-clock ceiling for the scan, or null/undefined for no deadline (startup path). */
|
|
55
|
+
deadlineMs?: number | null;
|
|
56
|
+
/** Report only: apply every gate, count what would be reclaimed, unlink nothing. */
|
|
57
|
+
dryRun?: boolean;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function processIsAlive(pid: number): boolean {
|
|
61
|
+
if (pid === process.pid) return true;
|
|
62
|
+
try {
|
|
63
|
+
process.kill(pid, 0);
|
|
64
|
+
return true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
// EPERM means the process exists but cannot be signalled. Unknown platform errors
|
|
67
|
+
// are also protected; cleanup should prefer a false negative over touching a live writer.
|
|
68
|
+
return (error as NodeJS.ErrnoException).code !== "ESRCH";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const responseStateTempRecoveryIO: ResponseStateTempRecoveryIO = {
|
|
73
|
+
now: Date.now,
|
|
74
|
+
bootTime: () => Date.now() - uptime() * 1_000,
|
|
75
|
+
list: function* list(dir) {
|
|
76
|
+
const handle = opendirSync(dir);
|
|
77
|
+
try {
|
|
78
|
+
for (let entry = handle.readSync(); entry; entry = handle.readSync()) yield entry.name;
|
|
79
|
+
} finally {
|
|
80
|
+
handle.closeSync();
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
inspect: path => {
|
|
84
|
+
const stat = lstatSync(path);
|
|
85
|
+
return { isFile: stat.isFile() && !stat.isSymbolicLink(), mtimeMs: stat.mtimeMs, size: stat.size };
|
|
86
|
+
},
|
|
87
|
+
isProcessAlive: processIsAlive,
|
|
88
|
+
unlink: unlinkSync,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Recover only abandoned response-state atomic-write files. The exact basename,
|
|
93
|
+
* regular-file check, age gate, and PID liveness check protect unrelated/active files.
|
|
94
|
+
* Cleanup is capped and best-effort because continuation state is only a cache. Removal
|
|
95
|
+
* deliberately uses unlink only: path-based truncation could follow a replacement symlink.
|
|
96
|
+
*/
|
|
97
|
+
export function recoverStaleResponseStateTemps(
|
|
98
|
+
dir = getConfigDir(),
|
|
99
|
+
options: ResponseStateTempRecoveryOptions = {},
|
|
100
|
+
): ResponseStateTempRecoveryResult {
|
|
101
|
+
const {
|
|
102
|
+
maxEntries = STALE_TEMP_MAX_ENTRIES,
|
|
103
|
+
maxCleanups = STALE_TEMP_MAX_CLEANUPS,
|
|
104
|
+
deadlineMs = null,
|
|
105
|
+
dryRun = false,
|
|
106
|
+
...overrides
|
|
107
|
+
} = options;
|
|
108
|
+
const io = { ...responseStateTempRecoveryIO, ...overrides };
|
|
109
|
+
const result: ResponseStateTempRecoveryResult = {
|
|
110
|
+
matched: 0,
|
|
111
|
+
removed: 0,
|
|
112
|
+
failed: 0,
|
|
113
|
+
bytesRemoved: 0,
|
|
114
|
+
eligible: 0,
|
|
115
|
+
eligibleBytes: 0,
|
|
116
|
+
truncated: false,
|
|
117
|
+
};
|
|
118
|
+
const startedAt = io.now();
|
|
119
|
+
// One probe per scan, not one per entry. A non-finite or future-dated boot is anomalous, and
|
|
120
|
+
// clamping it to "now" would be the WORST response: the floor would then retire the liveness
|
|
121
|
+
// probe for every file older than the skew, which is every file past the grace. Disable it
|
|
122
|
+
// instead -- an absent floor only costs a missed reclaim, never a wrong one.
|
|
123
|
+
const rawBoot = io.bootTime();
|
|
124
|
+
const bootMs = Number.isFinite(rawBoot) && rawBoot <= startedAt ? rawBoot : Number.NEGATIVE_INFINITY;
|
|
125
|
+
let names: Iterable<string>;
|
|
126
|
+
try { names = io.list(dir); } catch { return result; }
|
|
127
|
+
let iterator: Iterator<string>;
|
|
128
|
+
try { iterator = names[Symbol.iterator](); } catch { return result; }
|
|
129
|
+
let scanned = 0;
|
|
130
|
+
// Every early exit runs through this. The production `list` is a generator that closes its
|
|
131
|
+
// directory handle in a `finally`, and a `finally` does NOT run when the consumer simply
|
|
132
|
+
// stops calling `next()` -- only `return()` resumes the generator to completion. Breaking
|
|
133
|
+
// out of the loop directly therefore leaked one directory handle per truncated scan, and the
|
|
134
|
+
// periodic reclaim truncates on purpose (entry cap, cleanup cap, deadline), so on a slow
|
|
135
|
+
// filesystem that is a leak per tick, forever.
|
|
136
|
+
const stopScan = (): ResponseStateTempRecoveryResult => {
|
|
137
|
+
try { iterator.return?.(); } catch { /* closing is best-effort; never fail a reclaim on it */ }
|
|
138
|
+
return result;
|
|
139
|
+
};
|
|
140
|
+
for (;;) {
|
|
141
|
+
let next: IteratorResult<string>;
|
|
142
|
+
try { next = iterator.next(); } catch { return result; }
|
|
143
|
+
if (next.done) break;
|
|
144
|
+
const name = next.value;
|
|
145
|
+
scanned += 1;
|
|
146
|
+
// A dry run performs no cleanups, so bounding it by the cleanup budget would truncate
|
|
147
|
+
// the very report an operator uses to size the problem.
|
|
148
|
+
if (scanned > maxEntries) { result.truncated = true; return stopScan(); }
|
|
149
|
+
if (!dryRun && result.removed + result.failed >= maxCleanups) { result.truncated = true; return stopScan(); }
|
|
150
|
+
if (deadlineMs !== null && io.now() - startedAt > deadlineMs) { result.truncated = true; return stopScan(); }
|
|
151
|
+
const match = RESPONSE_STATE_TEMP_NAME.exec(name);
|
|
152
|
+
if (!match) continue;
|
|
153
|
+
result.matched += 1;
|
|
154
|
+
const pid = Number(match[1]);
|
|
155
|
+
const sequence = Number(match[2]);
|
|
156
|
+
if (!Number.isSafeInteger(pid) || pid <= 0 || !Number.isSafeInteger(sequence) || sequence <= 0) continue;
|
|
157
|
+
const path = join(dir, name);
|
|
158
|
+
let file: ReturnType<ResponseStateTempRecoveryIO["inspect"]>;
|
|
159
|
+
try { file = io.inspect(path); } catch { continue; }
|
|
160
|
+
if (!file.isFile || io.now() - file.mtimeMs < STALE_TEMP_GRACE_MS) continue;
|
|
161
|
+
// Boot floor. After a reboot the original writer's pid is routinely reused, which makes
|
|
162
|
+
// the liveness skip PERMANENT: the 15-minute grace above is a lower bound and never
|
|
163
|
+
// expires it, so the file is skipped on every future pass forever. A temp older than
|
|
164
|
+
// this boot cannot be owned by the pid we would probe, so the probe is vacuous and we
|
|
165
|
+
// retire it. This does NOT claim the file is provably dead: under a shared-volume
|
|
166
|
+
// container, suspend-excluding uptime, or a network config dir the computed boot can
|
|
167
|
+
// land after the real one. The unconditional 15-minute grace above remains the safety
|
|
168
|
+
// floor, and this process's own temps are never touched.
|
|
169
|
+
const predatesBoot = file.mtimeMs < bootMs - BOOT_FLOOR_SKEW_MS;
|
|
170
|
+
if (pid === process.pid) continue;
|
|
171
|
+
if (!predatesBoot && io.isProcessAlive(pid)) continue;
|
|
172
|
+
|
|
173
|
+
result.eligible += 1;
|
|
174
|
+
result.eligibleBytes += file.size;
|
|
175
|
+
if (dryRun) continue;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
io.unlink(path);
|
|
179
|
+
result.removed += 1;
|
|
180
|
+
result.bytesRemoved += file.size;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
// Another proxy sharing this config dir may have won the race. A file that is already
|
|
183
|
+
// gone is reclaimed, not a failure -- reporting it as one would surface "in use or
|
|
184
|
+
// locked" to an operator for a file nobody holds.
|
|
185
|
+
if ((error as NodeJS.ErrnoException)?.code === "ENOENT") {
|
|
186
|
+
result.removed += 1;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
// Locked files remain for a later startup. Do not truncate by path: a same-user
|
|
190
|
+
// replacement could turn that fallback into an arbitrary symlink-target write.
|
|
191
|
+
result.failed += 1;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Literal config dir plus the snapshot's resolved dir. Atomic writes place their temp beside
|
|
199
|
+
* the RESOLVED target, so a symlinked snapshot (dotfiles-managed config dir) strands temps in
|
|
200
|
+
* the link's real directory where a scan of the literal dir would never see them. The two
|
|
201
|
+
* collapse to one when nothing is symlinked.
|
|
202
|
+
*/
|
|
203
|
+
function responseStateSweepDirectories(): Set<string> {
|
|
204
|
+
const path = join(getConfigDir(), "responses-state.json");
|
|
205
|
+
let resolvedDir = dirname(path);
|
|
206
|
+
try {
|
|
207
|
+
resolvedDir = dirname(resolveWriteTarget(path));
|
|
208
|
+
} catch {
|
|
209
|
+
/* unresolvable link: sweep the literal dir only */
|
|
210
|
+
}
|
|
211
|
+
return new Set([dirname(path), resolvedDir]);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function reclaimAbandonedResponseStateTemps(
|
|
215
|
+
options: ResponseStateTempRecoveryOptions = {},
|
|
216
|
+
): ResponseStateTempRecoveryResult {
|
|
217
|
+
const total: ResponseStateTempRecoveryResult = {
|
|
218
|
+
matched: 0, removed: 0, failed: 0, bytesRemoved: 0, eligible: 0, eligibleBytes: 0, truncated: false,
|
|
219
|
+
};
|
|
220
|
+
// The try encloses responseStateSweepDirectories() deliberately: recoverStaleResponseStateTemps
|
|
221
|
+
// already swallows its own enumeration failures, so a catch around only that call would be
|
|
222
|
+
// unreachable. snapshotPath()/getConfigDir() are the paths that can genuinely throw.
|
|
223
|
+
try {
|
|
224
|
+
for (const dir of responseStateSweepDirectories()) {
|
|
225
|
+
const result = recoverStaleResponseStateTemps(dir, options);
|
|
226
|
+
total.matched += result.matched;
|
|
227
|
+
total.removed += result.removed;
|
|
228
|
+
total.failed += result.failed;
|
|
229
|
+
total.bytesRemoved += result.bytesRemoved;
|
|
230
|
+
total.eligible += result.eligible;
|
|
231
|
+
total.eligibleBytes += result.eligibleBytes;
|
|
232
|
+
// Truncation anywhere makes the whole total a prefix.
|
|
233
|
+
total.truncated ||= result.truncated;
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
/* best-effort: disk reclaim must never destabilize the caller */
|
|
237
|
+
}
|
|
238
|
+
return total;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Report-only counterpart for `ocx doctor`: applies every selection gate and unlinks
|
|
243
|
+
* nothing. It runs the SAME predicate as the reclaim, so the report and the subsequent
|
|
244
|
+
* removal cannot disagree about which files are reclaimable.
|
|
245
|
+
*/
|
|
246
|
+
export function inspectAbandonedResponseStateTemps(): ResponseStateTempRecoveryResult {
|
|
247
|
+
return reclaimAbandonedResponseStateTemps({ dryRun: true });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Sweeper adapter: narrows the reclaim to the `() => number` the liveness tick expects. */
|
|
251
|
+
export function sweepAbandonedResponseStateTemps(): number {
|
|
252
|
+
return reclaimAbandonedResponseStateTemps({
|
|
253
|
+
maxEntries: PERIODIC_TEMP_MAX_ENTRIES,
|
|
254
|
+
maxCleanups: PERIODIC_TEMP_MAX_CLEANUPS,
|
|
255
|
+
deadlineMs: PERIODIC_TEMP_SCAN_DEADLINE_MS,
|
|
256
|
+
}).removed;
|
|
257
|
+
}
|