crewly 1.20.198 → 1.20.199
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/backend/backend/src/constants.d.ts +16 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +16 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/system/usage.controller.d.ts +14 -0
- package/dist/backend/backend/src/controllers/system/usage.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/system/usage.controller.js +35 -1
- package/dist/backend/backend/src/controllers/system/usage.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +11 -2
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/experiments/experiment.service.d.ts +15 -1
- package/dist/backend/backend/src/services/experiments/experiment.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/experiments/experiment.service.js +41 -12
- package/dist/backend/backend/src/services/experiments/experiment.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/message-queue.service.d.ts +21 -0
- package/dist/backend/backend/src/services/messaging/message-queue.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/message-queue.service.js +86 -11
- package/dist/backend/backend/src/services/messaging/message-queue.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +4 -0
- package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.js +58 -15
- package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/antigravity-usage-sync.service.d.ts +7 -0
- package/dist/backend/backend/src/services/monitoring/antigravity-usage-sync.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/antigravity-usage-sync.service.js +21 -6
- package/dist/backend/backend/src/services/monitoring/antigravity-usage-sync.service.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/claude-transcript-sync.service.d.ts +49 -2
- package/dist/backend/backend/src/services/monitoring/claude-transcript-sync.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/claude-transcript-sync.service.js +160 -55
- package/dist/backend/backend/src/services/monitoring/claude-transcript-sync.service.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/codex-rollout-sync.service.d.ts +7 -0
- package/dist/backend/backend/src/services/monitoring/codex-rollout-sync.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/codex-rollout-sync.service.js +21 -7
- package/dist/backend/backend/src/services/monitoring/codex-rollout-sync.service.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts +71 -1
- package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/token-usage.service.js +193 -20
- package/dist/backend/backend/src/services/monitoring/token-usage.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-identity.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-identity.service.js +5 -0
- package/dist/backend/backend/src/services/slack/slack-agent-identity.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-cloud-config.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-cloud-config.service.js +5 -0
- package/dist/backend/backend/src/services/slack/slack-cloud-config.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +5 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/spend/spend-cap.service.d.ts +24 -1
- package/dist/backend/backend/src/services/spend/spend-cap.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/spend/spend-cap.service.js +57 -0
- package/dist/backend/backend/src/services/spend/spend-cap.service.js.map +1 -1
- package/dist/backend/backend/src/services/spend/spend-cap.store.d.ts +37 -1
- package/dist/backend/backend/src/services/spend/spend-cap.store.d.ts.map +1 -1
- package/dist/backend/backend/src/services/spend/spend-cap.store.js +85 -14
- package/dist/backend/backend/src/services/spend/spend-cap.store.js.map +1 -1
- package/dist/backend/backend/src/services/spend/spend-cap.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/spend/spend-cap.wiring.js +1 -1
- package/dist/backend/backend/src/services/spend/spend-cap.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-store.d.ts +3 -1
- package/dist/backend/backend/src/services/trace/trace-store.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-store.js +27 -15
- package/dist/backend/backend/src/services/trace/trace-store.js.map +1 -1
- package/dist/backend/backend/src/services/usage/usage-backfill.service.d.ts +96 -0
- package/dist/backend/backend/src/services/usage/usage-backfill.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage/usage-backfill.service.js +257 -0
- package/dist/backend/backend/src/services/usage/usage-backfill.service.js.map +1 -0
- package/dist/backend/backend/src/services/v3/kr-tracking.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/kr-tracking.service.js +2 -1
- package/dist/backend/backend/src/services/v3/kr-tracking.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/okr-cascade.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/okr-cascade.service.js +2 -1
- package/dist/backend/backend/src/services/v3/okr-cascade.service.js.map +1 -1
- package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts +28 -0
- package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/workflow/cron-task.service.js +121 -68
- package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
- package/dist/backend/backend/src/utils/file-io.utils.d.ts +114 -1
- package/dist/backend/backend/src/utils/file-io.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/utils/file-io.utils.js +236 -6
- package/dist/backend/backend/src/utils/file-io.utils.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +16 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +16 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/messaging/message-queue.service.d.ts +21 -0
- package/dist/cli/backend/src/services/messaging/message-queue.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/messaging/message-queue.service.js +86 -11
- package/dist/cli/backend/src/services/messaging/message-queue.service.js.map +1 -1
- package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +4 -0
- package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js +58 -15
- package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -1
- package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts +71 -1
- package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/monitoring/token-usage.service.js +193 -20
- package/dist/cli/backend/src/services/monitoring/token-usage.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-agent-identity.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-agent-identity.service.js +5 -0
- package/dist/cli/backend/src/services/slack/slack-agent-identity.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +5 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-store.d.ts +3 -1
- package/dist/cli/backend/src/services/trace/trace-store.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-store.js +27 -15
- package/dist/cli/backend/src/services/trace/trace-store.js.map +1 -1
- package/dist/cli/backend/src/services/v3/kr-tracking.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/kr-tracking.service.js +2 -1
- package/dist/cli/backend/src/services/v3/kr-tracking.service.js.map +1 -1
- package/dist/cli/backend/src/services/workflow/cron-task.service.d.ts +28 -0
- package/dist/cli/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/workflow/cron-task.service.js +121 -68
- package/dist/cli/backend/src/services/workflow/cron-task.service.js.map +1 -1
- package/dist/cli/backend/src/utils/file-io.utils.d.ts +114 -1
- package/dist/cli/backend/src/utils/file-io.utils.d.ts.map +1 -1
- package/dist/cli/backend/src/utils/file-io.utils.js +236 -6
- package/dist/cli/backend/src/utils/file-io.utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,8 +13,37 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export interface FileIOLogger {
|
|
15
15
|
warn(message: string, meta?: Record<string, unknown>): void;
|
|
16
|
+
error?(message: string, meta?: Record<string, unknown>): void;
|
|
16
17
|
debug?(message: string, meta?: Record<string, unknown>): void;
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* A JSON store file exists but cannot be read or parsed, AND it could not be
|
|
21
|
+
* copied aside. The caller must not overwrite it: that would destroy the only
|
|
22
|
+
* copy of whatever it still holds (the 2026-10-03 token-ledger loss).
|
|
23
|
+
*/
|
|
24
|
+
export declare class CorruptJsonFileError extends Error {
|
|
25
|
+
/** The store file that is bad */
|
|
26
|
+
readonly filePath: string;
|
|
27
|
+
/** Why it could not be used (parse or read error) */
|
|
28
|
+
readonly reason: string;
|
|
29
|
+
/**
|
|
30
|
+
* @param filePath - The bad store file
|
|
31
|
+
* @param reason - Parse / read error text
|
|
32
|
+
* @param copyError - Why copying it aside failed
|
|
33
|
+
*/
|
|
34
|
+
constructor(filePath: string, reason: string, copyError: string);
|
|
35
|
+
}
|
|
36
|
+
/** Result of {@link readJsonStore} / {@link readJsonStoreSync}. */
|
|
37
|
+
export type JsonStoreRead<T> = {
|
|
38
|
+
status: 'missing';
|
|
39
|
+
} | {
|
|
40
|
+
status: 'ok';
|
|
41
|
+
data: T;
|
|
42
|
+
} | {
|
|
43
|
+
status: 'quarantined';
|
|
44
|
+
quarantinedTo: string;
|
|
45
|
+
reason: string;
|
|
46
|
+
};
|
|
18
47
|
/**
|
|
19
48
|
* Ensures a directory exists, creating it recursively if necessary.
|
|
20
49
|
*
|
|
@@ -62,12 +91,96 @@ export declare function atomicWriteFile(filePath: string, content: string): Prom
|
|
|
62
91
|
* @param data - Value to serialize (via `JSON.stringify`)
|
|
63
92
|
*/
|
|
64
93
|
export declare function atomicWriteJson<T>(filePath: string, data: T): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Synchronous {@link atomicWriteFile} for stores that persist synchronously
|
|
96
|
+
* (temp file in the same directory → fsync → rename).
|
|
97
|
+
*
|
|
98
|
+
* A failure (ENOSPC, EACCES) leaves the destination untouched, removes the
|
|
99
|
+
* temp file and rethrows. No in-process lock: a synchronous call cannot
|
|
100
|
+
* interleave with another one in the same process.
|
|
101
|
+
*
|
|
102
|
+
* **Precondition:** the parent directory must already exist.
|
|
103
|
+
*
|
|
104
|
+
* @param filePath - Destination file path
|
|
105
|
+
* @param content - String content to write
|
|
106
|
+
*/
|
|
107
|
+
export declare function atomicWriteFileSync(filePath: string, content: string): void;
|
|
108
|
+
/**
|
|
109
|
+
* Where a bad store file is copied: `<file>.corrupt-<ISO timestamp>`
|
|
110
|
+
* (colons and dots replaced so the name is valid everywhere).
|
|
111
|
+
*
|
|
112
|
+
* @param filePath - Store file
|
|
113
|
+
* @param now - Clock (tests)
|
|
114
|
+
* @returns Quarantine path
|
|
115
|
+
*/
|
|
116
|
+
export declare function quarantinePathFor(filePath: string, now?: Date): string;
|
|
117
|
+
/**
|
|
118
|
+
* Copy a store file that cannot be used to `<file>.corrupt-<ts>` and log an
|
|
119
|
+
* error. After this returns, the caller may start empty and overwrite the
|
|
120
|
+
* original.
|
|
121
|
+
*
|
|
122
|
+
* @param filePath - The bad store file
|
|
123
|
+
* @param reason - Why it cannot be used
|
|
124
|
+
* @param logger - Optional logger
|
|
125
|
+
* @returns The quarantine path
|
|
126
|
+
* @throws CorruptJsonFileError when the copy fails — the caller must then NOT
|
|
127
|
+
* overwrite the original
|
|
128
|
+
*/
|
|
129
|
+
export declare function quarantineCorruptFile(filePath: string, reason: string, logger?: FileIOLogger): Promise<string>;
|
|
130
|
+
/**
|
|
131
|
+
* Synchronous {@link quarantineCorruptFile}.
|
|
132
|
+
*
|
|
133
|
+
* @param filePath - The bad store file
|
|
134
|
+
* @param reason - Why it cannot be used
|
|
135
|
+
* @param logger - Optional logger
|
|
136
|
+
* @returns The quarantine path
|
|
137
|
+
* @throws CorruptJsonFileError when the copy fails
|
|
138
|
+
*/
|
|
139
|
+
export declare function quarantineCorruptFileSync(filePath: string, reason: string, logger?: FileIOLogger): string;
|
|
140
|
+
/**
|
|
141
|
+
* Read a JSON store, telling "missing" apart from "bad".
|
|
142
|
+
*
|
|
143
|
+
* - missing (`ENOENT`) → `{ status: 'missing' }` — start fresh;
|
|
144
|
+
* - parses (and passes `validate`) → `{ status: 'ok', data }`;
|
|
145
|
+
* - invalid JSON, or rejected by `validate` → copied to
|
|
146
|
+
* `<file>.corrupt-<ts>`, error logged, `{ status: 'quarantined' }`;
|
|
147
|
+
* - any other read error (EMFILE, EIO, EACCES…) → rethrown. The file may be
|
|
148
|
+
* good; the caller must not start empty and write over it.
|
|
149
|
+
*
|
|
150
|
+
* @param filePath - Store file
|
|
151
|
+
* @param options.validate - Optional shape check; return an error text to reject
|
|
152
|
+
* @param options.logger - Optional logger
|
|
153
|
+
* @returns What was found
|
|
154
|
+
* @throws CorruptJsonFileError when the file is bad and could not be copied aside
|
|
155
|
+
* @throws The read error itself for anything but ENOENT
|
|
156
|
+
*/
|
|
157
|
+
export declare function readJsonStore<T>(filePath: string, options?: {
|
|
158
|
+
validate?: (data: unknown) => string | null;
|
|
159
|
+
logger?: FileIOLogger;
|
|
160
|
+
}): Promise<JsonStoreRead<T>>;
|
|
161
|
+
/**
|
|
162
|
+
* Synchronous {@link readJsonStore}.
|
|
163
|
+
*
|
|
164
|
+
* @param filePath - Store file
|
|
165
|
+
* @param options.validate - Optional shape check; return an error text to reject
|
|
166
|
+
* @param options.logger - Optional logger
|
|
167
|
+
* @returns What was found
|
|
168
|
+
* @throws CorruptJsonFileError when the file is bad and could not be copied aside
|
|
169
|
+
* @throws The read error itself for anything but ENOENT
|
|
170
|
+
*/
|
|
171
|
+
export declare function readJsonStoreSync<T>(filePath: string, options?: {
|
|
172
|
+
validate?: (data: unknown) => string | null;
|
|
173
|
+
logger?: FileIOLogger;
|
|
174
|
+
}): JsonStoreRead<T>;
|
|
65
175
|
/**
|
|
66
176
|
* Read and parse a JSON file safely.
|
|
67
177
|
*
|
|
68
178
|
* - On `ENOENT` → returns `defaultValue` silently.
|
|
69
|
-
* - On parse error → backs up the corrupt file as `<path>.corrupt
|
|
179
|
+
* - On parse error → backs up the corrupt file as `<path>.corrupt-<ts>`
|
|
70
180
|
* and returns `defaultValue`.
|
|
181
|
+
* - If that backup fails (a full disk, permissions) → throws
|
|
182
|
+
* {@link CorruptJsonFileError}. Returning the default would let the caller
|
|
183
|
+
* write it over the only copy of the data.
|
|
71
184
|
*
|
|
72
185
|
* @param filePath - Path to the JSON file
|
|
73
186
|
* @param defaultValue - Value to return when the file is missing or corrupt
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-io.utils.d.ts","sourceRoot":"","sources":["../../../../../backend/src/utils/file-io.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"file-io.utils.d.ts","sourceRoot":"","sources":["../../../../../backend/src/utils/file-io.utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,53 @@
|
|
|
8
8
|
* @module utils/file-io.utils
|
|
9
9
|
*/
|
|
10
10
|
import * as fs from 'fs/promises';
|
|
11
|
+
import * as fsSync from 'fs';
|
|
12
|
+
/**
|
|
13
|
+
* A JSON store file exists but cannot be read or parsed, AND it could not be
|
|
14
|
+
* copied aside. The caller must not overwrite it: that would destroy the only
|
|
15
|
+
* copy of whatever it still holds (the 2026-10-03 token-ledger loss).
|
|
16
|
+
*/
|
|
17
|
+
export class CorruptJsonFileError extends Error {
|
|
18
|
+
/** The store file that is bad */
|
|
19
|
+
filePath;
|
|
20
|
+
/** Why it could not be used (parse or read error) */
|
|
21
|
+
reason;
|
|
22
|
+
/**
|
|
23
|
+
* @param filePath - The bad store file
|
|
24
|
+
* @param reason - Parse / read error text
|
|
25
|
+
* @param copyError - Why copying it aside failed
|
|
26
|
+
*/
|
|
27
|
+
constructor(filePath, reason, copyError) {
|
|
28
|
+
super(`Store file ${filePath} is unreadable (${reason}) and could not be set aside (${copyError}); refusing to overwrite it`);
|
|
29
|
+
this.name = 'CorruptJsonFileError';
|
|
30
|
+
this.filePath = filePath;
|
|
31
|
+
this.reason = reason;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Error text of an unknown thrown value.
|
|
36
|
+
*
|
|
37
|
+
* @param err - Thrown value
|
|
38
|
+
* @returns Message
|
|
39
|
+
*/
|
|
40
|
+
function errText(err) {
|
|
41
|
+
return err instanceof Error ? err.message : String(err);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Log at error level when the logger has one, else warn.
|
|
45
|
+
*
|
|
46
|
+
* @param logger - Optional logger
|
|
47
|
+
* @param message - Message
|
|
48
|
+
* @param meta - Metadata
|
|
49
|
+
*/
|
|
50
|
+
function logError(logger, message, meta) {
|
|
51
|
+
if (!logger)
|
|
52
|
+
return;
|
|
53
|
+
if (logger.error)
|
|
54
|
+
logger.error(message, meta);
|
|
55
|
+
else
|
|
56
|
+
logger.warn(message, meta);
|
|
57
|
+
}
|
|
11
58
|
// ──────────────────────────────────────────────────────────────────────
|
|
12
59
|
// Module-level lock maps (singleton per process — no class needed)
|
|
13
60
|
// ──────────────────────────────────────────────────────────────────────
|
|
@@ -125,6 +172,185 @@ export async function atomicWriteFile(filePath, content) {
|
|
|
125
172
|
export async function atomicWriteJson(filePath, data) {
|
|
126
173
|
await atomicWriteFile(filePath, JSON.stringify(data, null, 2));
|
|
127
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Synchronous {@link atomicWriteFile} for stores that persist synchronously
|
|
177
|
+
* (temp file in the same directory → fsync → rename).
|
|
178
|
+
*
|
|
179
|
+
* A failure (ENOSPC, EACCES) leaves the destination untouched, removes the
|
|
180
|
+
* temp file and rethrows. No in-process lock: a synchronous call cannot
|
|
181
|
+
* interleave with another one in the same process.
|
|
182
|
+
*
|
|
183
|
+
* **Precondition:** the parent directory must already exist.
|
|
184
|
+
*
|
|
185
|
+
* @param filePath - Destination file path
|
|
186
|
+
* @param content - String content to write
|
|
187
|
+
*/
|
|
188
|
+
export function atomicWriteFileSync(filePath, content) {
|
|
189
|
+
const tempPath = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).substring(2)}`;
|
|
190
|
+
let fd = null;
|
|
191
|
+
try {
|
|
192
|
+
fd = fsSync.openSync(tempPath, 'w');
|
|
193
|
+
fsSync.writeFileSync(fd, content, 'utf8');
|
|
194
|
+
fsSync.fsyncSync(fd);
|
|
195
|
+
fsSync.closeSync(fd);
|
|
196
|
+
fd = null;
|
|
197
|
+
fsSync.renameSync(tempPath, filePath);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
if (fd !== null) {
|
|
201
|
+
try {
|
|
202
|
+
fsSync.closeSync(fd);
|
|
203
|
+
}
|
|
204
|
+
catch { /* ignore */ }
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
fsSync.unlinkSync(tempPath);
|
|
208
|
+
}
|
|
209
|
+
catch { /* ignore */ }
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
214
|
+
// Corrupt-store quarantine
|
|
215
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
216
|
+
/**
|
|
217
|
+
* Where a bad store file is copied: `<file>.corrupt-<ISO timestamp>`
|
|
218
|
+
* (colons and dots replaced so the name is valid everywhere).
|
|
219
|
+
*
|
|
220
|
+
* @param filePath - Store file
|
|
221
|
+
* @param now - Clock (tests)
|
|
222
|
+
* @returns Quarantine path
|
|
223
|
+
*/
|
|
224
|
+
export function quarantinePathFor(filePath, now = new Date()) {
|
|
225
|
+
return `${filePath}.corrupt-${now.toISOString().replace(/[:.]/g, '-')}`;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Copy a store file that cannot be used to `<file>.corrupt-<ts>` and log an
|
|
229
|
+
* error. After this returns, the caller may start empty and overwrite the
|
|
230
|
+
* original.
|
|
231
|
+
*
|
|
232
|
+
* @param filePath - The bad store file
|
|
233
|
+
* @param reason - Why it cannot be used
|
|
234
|
+
* @param logger - Optional logger
|
|
235
|
+
* @returns The quarantine path
|
|
236
|
+
* @throws CorruptJsonFileError when the copy fails — the caller must then NOT
|
|
237
|
+
* overwrite the original
|
|
238
|
+
*/
|
|
239
|
+
export async function quarantineCorruptFile(filePath, reason, logger) {
|
|
240
|
+
const target = quarantinePathFor(filePath);
|
|
241
|
+
try {
|
|
242
|
+
await fs.copyFile(filePath, target);
|
|
243
|
+
}
|
|
244
|
+
catch (copyErr) {
|
|
245
|
+
logError(logger, 'Store file is unreadable and could not be set aside; it will not be overwritten', {
|
|
246
|
+
filePath, reason, copyError: errText(copyErr),
|
|
247
|
+
});
|
|
248
|
+
throw new CorruptJsonFileError(filePath, reason, errText(copyErr));
|
|
249
|
+
}
|
|
250
|
+
logError(logger, 'Store file was unreadable; copied aside and starting empty', { filePath, quarantinedTo: target, reason });
|
|
251
|
+
return target;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Synchronous {@link quarantineCorruptFile}.
|
|
255
|
+
*
|
|
256
|
+
* @param filePath - The bad store file
|
|
257
|
+
* @param reason - Why it cannot be used
|
|
258
|
+
* @param logger - Optional logger
|
|
259
|
+
* @returns The quarantine path
|
|
260
|
+
* @throws CorruptJsonFileError when the copy fails
|
|
261
|
+
*/
|
|
262
|
+
export function quarantineCorruptFileSync(filePath, reason, logger) {
|
|
263
|
+
const target = quarantinePathFor(filePath);
|
|
264
|
+
try {
|
|
265
|
+
fsSync.copyFileSync(filePath, target);
|
|
266
|
+
}
|
|
267
|
+
catch (copyErr) {
|
|
268
|
+
logError(logger, 'Store file is unreadable and could not be set aside; it will not be overwritten', {
|
|
269
|
+
filePath, reason, copyError: errText(copyErr),
|
|
270
|
+
});
|
|
271
|
+
throw new CorruptJsonFileError(filePath, reason, errText(copyErr));
|
|
272
|
+
}
|
|
273
|
+
logError(logger, 'Store file was unreadable; copied aside and starting empty', { filePath, quarantinedTo: target, reason });
|
|
274
|
+
return target;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Read a JSON store, telling "missing" apart from "bad".
|
|
278
|
+
*
|
|
279
|
+
* - missing (`ENOENT`) → `{ status: 'missing' }` — start fresh;
|
|
280
|
+
* - parses (and passes `validate`) → `{ status: 'ok', data }`;
|
|
281
|
+
* - invalid JSON, or rejected by `validate` → copied to
|
|
282
|
+
* `<file>.corrupt-<ts>`, error logged, `{ status: 'quarantined' }`;
|
|
283
|
+
* - any other read error (EMFILE, EIO, EACCES…) → rethrown. The file may be
|
|
284
|
+
* good; the caller must not start empty and write over it.
|
|
285
|
+
*
|
|
286
|
+
* @param filePath - Store file
|
|
287
|
+
* @param options.validate - Optional shape check; return an error text to reject
|
|
288
|
+
* @param options.logger - Optional logger
|
|
289
|
+
* @returns What was found
|
|
290
|
+
* @throws CorruptJsonFileError when the file is bad and could not be copied aside
|
|
291
|
+
* @throws The read error itself for anything but ENOENT
|
|
292
|
+
*/
|
|
293
|
+
export async function readJsonStore(filePath, options = {}) {
|
|
294
|
+
let raw;
|
|
295
|
+
try {
|
|
296
|
+
raw = await fs.readFile(filePath, 'utf-8');
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
if (err.code === 'ENOENT')
|
|
300
|
+
return { status: 'missing' };
|
|
301
|
+
// EMFILE, EIO, EACCES…: the file may be perfectly good. Never set it
|
|
302
|
+
// aside (the store would start empty and overwrite it); let the caller
|
|
303
|
+
// refuse to write until a read succeeds.
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
const parsed = parseStore(raw, options.validate);
|
|
307
|
+
if (parsed.ok)
|
|
308
|
+
return { status: 'ok', data: parsed.data };
|
|
309
|
+
return { status: 'quarantined', quarantinedTo: await quarantineCorruptFile(filePath, parsed.reason, options.logger), reason: parsed.reason };
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Synchronous {@link readJsonStore}.
|
|
313
|
+
*
|
|
314
|
+
* @param filePath - Store file
|
|
315
|
+
* @param options.validate - Optional shape check; return an error text to reject
|
|
316
|
+
* @param options.logger - Optional logger
|
|
317
|
+
* @returns What was found
|
|
318
|
+
* @throws CorruptJsonFileError when the file is bad and could not be copied aside
|
|
319
|
+
* @throws The read error itself for anything but ENOENT
|
|
320
|
+
*/
|
|
321
|
+
export function readJsonStoreSync(filePath, options = {}) {
|
|
322
|
+
let raw;
|
|
323
|
+
try {
|
|
324
|
+
raw = fsSync.readFileSync(filePath, 'utf-8');
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
if (err.code === 'ENOENT')
|
|
328
|
+
return { status: 'missing' };
|
|
329
|
+
throw err; // see readJsonStore: a read error is not corruption
|
|
330
|
+
}
|
|
331
|
+
const parsed = parseStore(raw, options.validate);
|
|
332
|
+
if (parsed.ok)
|
|
333
|
+
return { status: 'ok', data: parsed.data };
|
|
334
|
+
return { status: 'quarantined', quarantinedTo: quarantineCorruptFileSync(filePath, parsed.reason, options.logger), reason: parsed.reason };
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Parse store text and run the optional shape check.
|
|
338
|
+
*
|
|
339
|
+
* @param raw - File text
|
|
340
|
+
* @param validate - Optional shape check
|
|
341
|
+
* @returns Parsed data, or why it was rejected
|
|
342
|
+
*/
|
|
343
|
+
function parseStore(raw, validate) {
|
|
344
|
+
let data;
|
|
345
|
+
try {
|
|
346
|
+
data = JSON.parse(raw);
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
return { ok: false, reason: `invalid JSON (${raw.length} bytes): ${errText(err)}` };
|
|
350
|
+
}
|
|
351
|
+
const rejected = validate?.(data) ?? null;
|
|
352
|
+
return rejected ? { ok: false, reason: rejected } : { ok: true, data };
|
|
353
|
+
}
|
|
128
354
|
// ──────────────────────────────────────────────────────────────────────
|
|
129
355
|
// Safe read
|
|
130
356
|
// ──────────────────────────────────────────────────────────────────────
|
|
@@ -132,8 +358,11 @@ export async function atomicWriteJson(filePath, data) {
|
|
|
132
358
|
* Read and parse a JSON file safely.
|
|
133
359
|
*
|
|
134
360
|
* - On `ENOENT` → returns `defaultValue` silently.
|
|
135
|
-
* - On parse error → backs up the corrupt file as `<path>.corrupt
|
|
361
|
+
* - On parse error → backs up the corrupt file as `<path>.corrupt-<ts>`
|
|
136
362
|
* and returns `defaultValue`.
|
|
363
|
+
* - If that backup fails (a full disk, permissions) → throws
|
|
364
|
+
* {@link CorruptJsonFileError}. Returning the default would let the caller
|
|
365
|
+
* write it over the only copy of the data.
|
|
137
366
|
*
|
|
138
367
|
* @param filePath - Path to the JSON file
|
|
139
368
|
* @param defaultValue - Value to return when the file is missing or corrupt
|
|
@@ -155,15 +384,16 @@ export async function safeReadJson(filePath, defaultValue, logger) {
|
|
|
155
384
|
try {
|
|
156
385
|
return JSON.parse(raw);
|
|
157
386
|
}
|
|
158
|
-
catch {
|
|
159
|
-
// Corrupt JSON — back up the file
|
|
160
|
-
const backupPath =
|
|
387
|
+
catch (parseErr) {
|
|
388
|
+
// Corrupt JSON — back up the file before anyone can write over it
|
|
389
|
+
const backupPath = quarantinePathFor(filePath);
|
|
161
390
|
try {
|
|
162
391
|
await fs.copyFile(filePath, backupPath);
|
|
163
392
|
logger?.warn('Backed up corrupt JSON file', { filePath, backupPath });
|
|
164
393
|
}
|
|
165
|
-
catch {
|
|
166
|
-
logger
|
|
394
|
+
catch (copyErr) {
|
|
395
|
+
logError(logger, 'Failed to back up corrupt JSON file; refusing to fall back to the default', { filePath });
|
|
396
|
+
throw new CorruptJsonFileError(filePath, `invalid JSON: ${errText(parseErr)}`, errText(copyErr));
|
|
167
397
|
}
|
|
168
398
|
return defaultValue;
|
|
169
399
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-io.utils.js","sourceRoot":"","sources":["../../../../../backend/src/utils/file-io.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"file-io.utils.js","sourceRoot":"","sources":["../../../../../backend/src/utils/file-io.utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -5696,6 +5696,22 @@ export declare const REPLY_ROUTING_CONSTANTS: {
|
|
|
5696
5696
|
/** Decision ids (`D-12`) */
|
|
5697
5697
|
readonly DECISION_PATTERN: RegExp;
|
|
5698
5698
|
};
|
|
5699
|
+
/**
|
|
5700
|
+
* Usage ledger backfill (`POST /api/system/usage/backfill`, owner only).
|
|
5701
|
+
* specs/2026-10-03-usage-ledger-durability.md §Backfill
|
|
5702
|
+
*/
|
|
5703
|
+
export declare const USAGE_BACKFILL_CONSTANTS: {
|
|
5704
|
+
/** Longest range one request may rebuild, in days */
|
|
5705
|
+
readonly MAX_DAYS: 120;
|
|
5706
|
+
/** Most ledger backup files one request may read */
|
|
5707
|
+
readonly MAX_LEDGER_FILES: 10;
|
|
5708
|
+
/** Largest ledger backup file read (the live ledger was 2.8 MB) */
|
|
5709
|
+
readonly MAX_LEDGER_FILE_BYTES: number;
|
|
5710
|
+
/** Names a ledger backup may have: `*.json`, `*.corrupt-*`, `*.corrupt.*`, `*.bak-*` */
|
|
5711
|
+
readonly LEDGER_FILE_NAME_PATTERN: RegExp;
|
|
5712
|
+
/** The one error a rejected ledger file gets: never file contents, never "missing" vs "unreadable" */
|
|
5713
|
+
readonly LEDGER_FILE_ERROR: "not a usable ledger file";
|
|
5714
|
+
};
|
|
5699
5715
|
/**
|
|
5700
5716
|
* Daily token caps with a hard stop, team caps and temporary boosts
|
|
5701
5717
|
* (specs/2026-10-02-spend-cap.md). The unit is TOKENS, not dollars (owner,
|