@omnicross/daemon 0.1.9 → 0.1.10
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/cli.cjs +1312 -369
- package/dist/cli.js +1309 -361
- package/dist/index.cjs +849 -107
- package/dist/index.d.cts +147 -38
- package/dist/index.d.ts +147 -38
- package/dist/index.js +847 -100
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,86 +1,633 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/commands/
|
|
4
|
-
import {
|
|
3
|
+
// src/commands/audit.ts
|
|
4
|
+
import { readdirSync as readdirSync3 } from "fs";
|
|
5
|
+
import { join as join5 } from "path";
|
|
5
6
|
import { parseArgs } from "util";
|
|
6
7
|
|
|
7
|
-
// src/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
// src/audit/auditBodyReader.ts
|
|
9
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
10
|
+
import { join as join2 } from "path";
|
|
11
|
+
import { gunzipSync } from "zlib";
|
|
12
|
+
|
|
13
|
+
// src/audit/auditBodyStore.ts
|
|
14
|
+
var ANCHOR_EVERY = 64;
|
|
15
|
+
var ANCHOR_DELTA_RATIO = 0.75;
|
|
16
|
+
var DIVERGED_PREFIX_RATIO = 0.25;
|
|
17
|
+
var MAX_BASE_CHARS = 4e6;
|
|
18
|
+
var CACHE_BUDGET_CHARS = 16e6;
|
|
19
|
+
var CACHE_MAX_SESSIONS = 32;
|
|
20
|
+
var MAX_BASES_PER_SESSION = 4;
|
|
21
|
+
var isHighSurrogate = (code) => code >= 55296 && code <= 56319;
|
|
22
|
+
var isLowSurrogate = (code) => code >= 56320 && code <= 57343;
|
|
23
|
+
function computeBodyDelta(prev, next) {
|
|
24
|
+
const shortest = Math.min(prev.length, next.length);
|
|
25
|
+
let pre = 0;
|
|
26
|
+
while (pre < shortest && prev.charCodeAt(pre) === next.charCodeAt(pre)) pre += 1;
|
|
27
|
+
if (pre > 0 && isHighSurrogate(prev.charCodeAt(pre - 1))) pre -= 1;
|
|
28
|
+
const maxSuf = shortest - pre;
|
|
29
|
+
let suf = 0;
|
|
30
|
+
while (suf < maxSuf && prev.charCodeAt(prev.length - 1 - suf) === next.charCodeAt(next.length - 1 - suf)) {
|
|
31
|
+
suf += 1;
|
|
32
|
+
}
|
|
33
|
+
if (suf > 0 && isLowSurrogate(prev.charCodeAt(prev.length - suf))) suf -= 1;
|
|
34
|
+
return { pre, suf, ins: next.slice(pre, next.length - suf) };
|
|
35
|
+
}
|
|
36
|
+
function applyBodyDelta(prev, delta) {
|
|
37
|
+
const head = delta.pre > 0 ? prev.slice(0, delta.pre) : "";
|
|
38
|
+
const tail = delta.suf > 0 ? prev.slice(prev.length - delta.suf) : "";
|
|
39
|
+
return head + delta.ins + tail;
|
|
40
|
+
}
|
|
41
|
+
var SessionBaseCache = class {
|
|
42
|
+
constructor(maxSessions = CACHE_MAX_SESSIONS, budgetChars = CACHE_BUDGET_CHARS, maxBaseChars = MAX_BASE_CHARS, maxHeads = MAX_BASES_PER_SESSION) {
|
|
43
|
+
this.maxSessions = maxSessions;
|
|
44
|
+
this.budgetChars = budgetChars;
|
|
45
|
+
this.maxBaseChars = maxBaseChars;
|
|
46
|
+
this.maxHeads = maxHeads;
|
|
47
|
+
}
|
|
48
|
+
maxSessions;
|
|
49
|
+
budgetChars;
|
|
50
|
+
maxBaseChars;
|
|
51
|
+
maxHeads;
|
|
52
|
+
/** Session key to its retained heads, most-recent first. */
|
|
53
|
+
entries = /* @__PURE__ */ new Map();
|
|
54
|
+
chars = 0;
|
|
55
|
+
/** Retained sessions (tests + diagnostics). */
|
|
56
|
+
get size() {
|
|
57
|
+
return this.entries.size;
|
|
58
|
+
}
|
|
59
|
+
/** A session's retained heads, most-recent first. Refreshes LRU recency. */
|
|
60
|
+
get(sessionKey) {
|
|
61
|
+
const found = this.entries.get(sessionKey);
|
|
62
|
+
if (!found) return [];
|
|
63
|
+
this.entries.delete(sessionKey);
|
|
64
|
+
this.entries.set(sessionKey, found);
|
|
65
|
+
return found;
|
|
11
66
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Retain `base` as a head of `sessionKey`.
|
|
69
|
+
*
|
|
70
|
+
* `replacesId` is the head this turn CONTINUES (its body was preserved whole
|
|
71
|
+
* inside the new one), which is swapped out so a linear conversation keeps
|
|
72
|
+
* exactly one head. Omit it when the turn started a distinct stream %s that
|
|
73
|
+
* head is added alongside, which is what keeps a fork's branches apart.
|
|
74
|
+
*
|
|
75
|
+
* A body larger than `maxBaseChars` is not retained: the next turn anchors
|
|
76
|
+
* rather than letting one oversized session monopolize the budget.
|
|
77
|
+
*/
|
|
78
|
+
remember(sessionKey, base, replacesId) {
|
|
79
|
+
const heads = this.entries.get(sessionKey) ?? [];
|
|
80
|
+
if (replacesId !== void 0) {
|
|
81
|
+
const at = heads.findIndex((head) => head.lastId === replacesId);
|
|
82
|
+
if (at >= 0) {
|
|
83
|
+
this.chars -= heads[at].text.length;
|
|
84
|
+
heads.splice(at, 1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (base.text.length <= this.maxBaseChars) {
|
|
88
|
+
heads.unshift(base);
|
|
89
|
+
this.chars += base.text.length;
|
|
90
|
+
}
|
|
91
|
+
while (heads.length > this.maxHeads) {
|
|
92
|
+
const dropped = heads.pop();
|
|
93
|
+
if (dropped) this.chars -= dropped.text.length;
|
|
94
|
+
}
|
|
95
|
+
this.entries.delete(sessionKey);
|
|
96
|
+
if (heads.length > 0) this.entries.set(sessionKey, heads);
|
|
97
|
+
this.evict();
|
|
98
|
+
}
|
|
99
|
+
/** Drop a session's heads (eviction, or a write failure invalidating them). */
|
|
100
|
+
forget(sessionKey) {
|
|
101
|
+
const heads = this.entries.get(sessionKey);
|
|
102
|
+
if (!heads) return;
|
|
103
|
+
for (const head of heads) this.chars -= head.text.length;
|
|
104
|
+
this.entries.delete(sessionKey);
|
|
105
|
+
}
|
|
106
|
+
/** Drop everything (writer disposal / test teardown). */
|
|
107
|
+
clear() {
|
|
108
|
+
this.entries.clear();
|
|
109
|
+
this.chars = 0;
|
|
110
|
+
}
|
|
111
|
+
/** Evict least-recently-used sessions until both bounds hold. */
|
|
112
|
+
evict() {
|
|
113
|
+
while (this.entries.size > this.maxSessions || this.chars > this.budgetChars && this.entries.size > 1) {
|
|
114
|
+
const oldest = this.entries.keys().next();
|
|
115
|
+
if (oldest.done) break;
|
|
116
|
+
this.forget(oldest.value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
function anchorReason(base, dayDir, delta, nextLength) {
|
|
121
|
+
if (!base || !delta) return "new";
|
|
122
|
+
if (base.dayDir !== dayDir) return "day";
|
|
123
|
+
if (base.chainLen >= ANCHOR_EVERY) return "chain";
|
|
124
|
+
if (delta.pre < base.text.length * DIVERGED_PREFIX_RATIO) return "diverged";
|
|
125
|
+
if (delta.ins.length > nextLength * ANCHOR_DELTA_RATIO) return "costly";
|
|
126
|
+
return null;
|
|
16
127
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
128
|
+
function pickBase(heads, next) {
|
|
129
|
+
let best = null;
|
|
130
|
+
for (const base of heads) {
|
|
131
|
+
const delta = computeBodyDelta(base.text, next);
|
|
132
|
+
if (best !== null && delta.ins.length >= best.delta.ins.length) continue;
|
|
133
|
+
best = { base, delta, continues: delta.pre + delta.suf >= base.text.length };
|
|
134
|
+
}
|
|
135
|
+
return best;
|
|
136
|
+
}
|
|
137
|
+
function encodeBodyEntry(record, sessionKey, dayDir, cache) {
|
|
138
|
+
const requestBody = record.requestBody;
|
|
139
|
+
const responseBody = record.responseBody;
|
|
140
|
+
if (requestBody === void 0 && responseBody === void 0) return null;
|
|
141
|
+
const entry = { id: record.id, ts: record.ts };
|
|
142
|
+
if (requestBody !== void 0) {
|
|
143
|
+
const heads = cache.get(sessionKey);
|
|
144
|
+
const sameDay = heads.filter((head) => head.dayDir === dayDir);
|
|
145
|
+
const chosen = pickBase(sameDay, requestBody);
|
|
146
|
+
const reason = heads.length > 0 && sameDay.length === 0 ? "day" : anchorReason(chosen?.base, dayDir, chosen?.delta ?? null, requestBody.length);
|
|
147
|
+
if (reason !== null) {
|
|
148
|
+
entry.req = { base: null, anchor: reason, pre: 0, suf: 0, ins: requestBody };
|
|
149
|
+
cache.remember(
|
|
150
|
+
sessionKey,
|
|
151
|
+
{ dayDir, lastId: record.id, text: requestBody, chainLen: 0 },
|
|
152
|
+
chosen?.continues === true ? chosen.base.lastId : void 0
|
|
153
|
+
);
|
|
154
|
+
} else {
|
|
155
|
+
const picked = chosen;
|
|
156
|
+
entry.req = {
|
|
157
|
+
base: picked.base.lastId,
|
|
158
|
+
...picked.continues ? { cont: true } : {},
|
|
159
|
+
pre: picked.delta.pre,
|
|
160
|
+
suf: picked.delta.suf,
|
|
161
|
+
ins: picked.delta.ins
|
|
162
|
+
};
|
|
163
|
+
cache.remember(
|
|
164
|
+
sessionKey,
|
|
165
|
+
{ dayDir, lastId: record.id, text: requestBody, chainLen: picked.base.chainLen + 1 },
|
|
166
|
+
picked.continues ? picked.base.lastId : void 0
|
|
167
|
+
);
|
|
168
|
+
}
|
|
21
169
|
}
|
|
22
|
-
if (
|
|
23
|
-
|
|
170
|
+
if (responseBody !== void 0) entry.res = responseBody;
|
|
171
|
+
return JSON.stringify(entry);
|
|
172
|
+
}
|
|
173
|
+
function isAuditBodyEntry(value) {
|
|
174
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
175
|
+
const entry = value;
|
|
176
|
+
if (typeof entry["id"] !== "string" || typeof entry["ts"] !== "number") return false;
|
|
177
|
+
if (entry["res"] !== void 0 && typeof entry["res"] !== "string") return false;
|
|
178
|
+
const req = entry["req"];
|
|
179
|
+
if (req === void 0) return true;
|
|
180
|
+
if (!req || typeof req !== "object" || Array.isArray(req)) return false;
|
|
181
|
+
const delta = req;
|
|
182
|
+
if (delta["anchor"] !== void 0 && typeof delta["anchor"] !== "string") return false;
|
|
183
|
+
if (delta["cont"] !== void 0 && typeof delta["cont"] !== "boolean") return false;
|
|
184
|
+
return (delta["base"] === null || typeof delta["base"] === "string") && Number.isSafeInteger(delta["pre"]) && delta["pre"] >= 0 && Number.isSafeInteger(delta["suf"]) && delta["suf"] >= 0 && typeof delta["ins"] === "string";
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/audit/auditDictionary.ts
|
|
188
|
+
import { existsSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
|
|
189
|
+
import { join } from "path";
|
|
190
|
+
|
|
191
|
+
// src/audit/auditFiles.ts
|
|
192
|
+
var AUDIT_FILE_RE = /^audit-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
193
|
+
var AUDIT_DAY_DIR_RE = /^audit-(\d{4})-(\d{2})-(\d{2})$/;
|
|
194
|
+
var AUDIT_META_FILE = "meta.jsonl";
|
|
195
|
+
var AUDIT_BODIES_DIR = "bodies";
|
|
196
|
+
var AUDIT_SESSION_KEY_RE = /^[0-9a-f]{8,64}$/;
|
|
197
|
+
var pad2 = (n) => String(n).padStart(2, "0");
|
|
198
|
+
var localDateStamp = (ts) => {
|
|
199
|
+
const d = new Date(ts);
|
|
200
|
+
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
201
|
+
};
|
|
202
|
+
function auditDayDirName(ts) {
|
|
203
|
+
return `audit-${localDateStamp(ts)}`;
|
|
204
|
+
}
|
|
205
|
+
function isAuditDayDir(name) {
|
|
206
|
+
return AUDIT_DAY_DIR_RE.test(name);
|
|
207
|
+
}
|
|
208
|
+
function isSafeSessionKey(key) {
|
|
209
|
+
return typeof key === "string" && AUDIT_SESSION_KEY_RE.test(key);
|
|
210
|
+
}
|
|
211
|
+
function auditBodyFileName(sessionKey) {
|
|
212
|
+
return `${sessionKey}.jsonl`;
|
|
213
|
+
}
|
|
214
|
+
function auditFileDateMs(name) {
|
|
215
|
+
const m = AUDIT_FILE_RE.exec(name) ?? AUDIT_DAY_DIR_RE.exec(name);
|
|
216
|
+
if (!m) return null;
|
|
217
|
+
const year = Number(m[1]);
|
|
218
|
+
const month = Number(m[2]);
|
|
219
|
+
const day = Number(m[3]);
|
|
220
|
+
const d = new Date(year, month - 1, day);
|
|
221
|
+
if (d.getFullYear() !== year || d.getMonth() !== month - 1 || d.getDate() !== day) {
|
|
222
|
+
return null;
|
|
24
223
|
}
|
|
25
|
-
|
|
26
|
-
|
|
224
|
+
return d.getTime();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/audit/auditDictionary.ts
|
|
228
|
+
var AUDIT_DICT_FILE = "_dict.jsonl";
|
|
229
|
+
var DICT_BASE_PREFIX = "dict:";
|
|
230
|
+
var DICT_CANDIDATES = 3;
|
|
231
|
+
var MIN_SAVING_RATIO = 0.2;
|
|
232
|
+
function parseEntries(raw) {
|
|
233
|
+
const entries = [];
|
|
234
|
+
for (const line of raw.split("\n")) {
|
|
235
|
+
const trimmed = line.trim();
|
|
236
|
+
if (!trimmed) continue;
|
|
237
|
+
try {
|
|
238
|
+
const parsed = JSON.parse(trimmed);
|
|
239
|
+
if (isAuditBodyEntry(parsed)) entries.push(parsed);
|
|
240
|
+
} catch {
|
|
241
|
+
}
|
|
27
242
|
}
|
|
28
|
-
|
|
29
|
-
|
|
243
|
+
return entries;
|
|
244
|
+
}
|
|
245
|
+
function plainShards(bodiesPath) {
|
|
246
|
+
try {
|
|
247
|
+
return readdirSync(bodiesPath).filter(
|
|
248
|
+
(file) => file.endsWith(".jsonl") && isSafeSessionKey(file.slice(0, -".jsonl".length))
|
|
249
|
+
);
|
|
250
|
+
} catch {
|
|
251
|
+
return [];
|
|
30
252
|
}
|
|
31
|
-
return { format: "openai", ambiguous: true };
|
|
32
253
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
254
|
+
function chooseDictionary(anchors) {
|
|
255
|
+
if (anchors.length < 2) return null;
|
|
256
|
+
const total = anchors.reduce((sum, body) => sum + body.length, 0);
|
|
257
|
+
const candidates = [...anchors].sort((a, b) => b.length - a.length).slice(0, DICT_CANDIDATES);
|
|
258
|
+
let best = null;
|
|
259
|
+
for (const candidate of candidates) {
|
|
260
|
+
let cost = candidate.length;
|
|
261
|
+
for (const body of anchors) {
|
|
262
|
+
cost += body === candidate ? 0 : computeBodyDelta(candidate, body).ins.length;
|
|
263
|
+
}
|
|
264
|
+
if (best === null || cost < best.cost) best = { body: candidate, cost };
|
|
265
|
+
}
|
|
266
|
+
if (best === null) return null;
|
|
267
|
+
return total - best.cost >= total * MIN_SAVING_RATIO ? best.body : null;
|
|
268
|
+
}
|
|
269
|
+
var EMPTY = { shards: 0, anchors: 0, savedBytes: 0 };
|
|
270
|
+
function compactAuditDay(dayPath) {
|
|
271
|
+
const bodiesPath = join(dayPath, AUDIT_BODIES_DIR);
|
|
272
|
+
if (!existsSync(bodiesPath)) return EMPTY;
|
|
273
|
+
const dictPath = join(bodiesPath, AUDIT_DICT_FILE);
|
|
274
|
+
if (existsSync(dictPath) || existsSync(`${dictPath}.gz`)) return EMPTY;
|
|
275
|
+
const shardFiles = plainShards(bodiesPath);
|
|
276
|
+
if (shardFiles.length < 2) return EMPTY;
|
|
277
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
278
|
+
const anchors = [];
|
|
279
|
+
for (const file of shardFiles) {
|
|
280
|
+
let entries;
|
|
281
|
+
try {
|
|
282
|
+
entries = parseEntries(readFileSync(join(bodiesPath, file), "utf8"));
|
|
283
|
+
} catch {
|
|
39
284
|
continue;
|
|
40
285
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`Provider '${id}': could not infer apiFormat from base URL \u2014 defaulted to 'openai'. Edit the config if this provider speaks a different wire format.`
|
|
45
|
-
);
|
|
286
|
+
loaded.set(file, entries);
|
|
287
|
+
for (const entry of entries) {
|
|
288
|
+
if (entry.req && entry.req.base === null) anchors.push(entry.req.ins);
|
|
46
289
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
290
|
+
}
|
|
291
|
+
if (anchors.length < 2) return EMPTY;
|
|
292
|
+
const dictionary = chooseDictionary(anchors);
|
|
293
|
+
if (dictionary === null) return EMPTY;
|
|
294
|
+
const dictEntry = {
|
|
295
|
+
id: `${DICT_BASE_PREFIX}0`,
|
|
296
|
+
ts: 0,
|
|
297
|
+
req: { base: null, anchor: "dict", pre: 0, suf: 0, ins: dictionary }
|
|
298
|
+
};
|
|
299
|
+
writeFileSync(dictPath, JSON.stringify(dictEntry) + "\n", "utf8");
|
|
300
|
+
const result = { shards: 0, anchors: 0, savedBytes: 0 };
|
|
301
|
+
for (const [file, entries] of loaded) {
|
|
302
|
+
let changed = false;
|
|
303
|
+
let saved = 0;
|
|
304
|
+
const rewritten = entries.map((entry) => {
|
|
305
|
+
if (!entry.req || entry.req.base !== null || entry.req.ins === dictionary) return entry;
|
|
306
|
+
const delta = computeBodyDelta(dictionary, entry.req.ins);
|
|
307
|
+
if (delta.ins.length >= entry.req.ins.length) return entry;
|
|
308
|
+
changed = true;
|
|
309
|
+
saved += entry.req.ins.length - delta.ins.length;
|
|
310
|
+
return {
|
|
311
|
+
...entry,
|
|
312
|
+
req: { base: dictEntry.id, pre: delta.pre, suf: delta.suf, ins: delta.ins }
|
|
313
|
+
};
|
|
53
314
|
});
|
|
315
|
+
if (!changed) continue;
|
|
316
|
+
const target = join(bodiesPath, file);
|
|
317
|
+
const temp = `${target}.compacting`;
|
|
318
|
+
try {
|
|
319
|
+
writeFileSync(temp, rewritten.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
|
|
320
|
+
renameSync(temp, target);
|
|
321
|
+
} catch {
|
|
322
|
+
try {
|
|
323
|
+
if (existsSync(temp)) unlinkSync(temp);
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
result.shards += 1;
|
|
329
|
+
result.anchors += rewritten.filter((e) => e.req?.base === dictEntry.id).length;
|
|
330
|
+
result.savedBytes += saved;
|
|
54
331
|
}
|
|
55
|
-
|
|
332
|
+
if (result.shards === 0) {
|
|
333
|
+
try {
|
|
334
|
+
unlinkSync(dictPath);
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
56
339
|
}
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
|
|
340
|
+
function compactAllClosedAuditDays(auditDir, now = Date.now) {
|
|
341
|
+
const run = { days: 0, shards: 0, savedBytes: 0 };
|
|
342
|
+
if (!existsSync(auditDir)) return run;
|
|
343
|
+
const today = auditDayDirName(now());
|
|
344
|
+
let names;
|
|
345
|
+
try {
|
|
346
|
+
names = readdirSync(auditDir).filter(isAuditDayDir).sort();
|
|
347
|
+
} catch {
|
|
348
|
+
return run;
|
|
60
349
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
350
|
+
for (const name of names) {
|
|
351
|
+
if (name === today) continue;
|
|
352
|
+
try {
|
|
353
|
+
const result = compactAuditDay(join(auditDir, name));
|
|
354
|
+
if (result.shards === 0) continue;
|
|
355
|
+
run.days += 1;
|
|
356
|
+
run.shards += result.shards;
|
|
357
|
+
run.savedBytes += result.savedBytes;
|
|
358
|
+
} catch {
|
|
359
|
+
}
|
|
65
360
|
}
|
|
66
|
-
|
|
67
|
-
|
|
361
|
+
return run;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/audit/auditJsonl.ts
|
|
365
|
+
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
366
|
+
var WINDOW_BYTES = 1 << 20;
|
|
367
|
+
var MAX_LINE_BYTES = 32 * 1024 * 1024;
|
|
368
|
+
var NEWLINE = 10;
|
|
369
|
+
function forEachLineFromTail(path2, onLine) {
|
|
370
|
+
let fd;
|
|
371
|
+
let end;
|
|
372
|
+
try {
|
|
373
|
+
end = statSync(path2).size;
|
|
374
|
+
if (end === 0) return;
|
|
375
|
+
fd = openSync(path2, "r");
|
|
376
|
+
} catch {
|
|
377
|
+
return;
|
|
68
378
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
379
|
+
try {
|
|
380
|
+
let carry = Buffer.alloc(0);
|
|
381
|
+
while (end > 0) {
|
|
382
|
+
const start = Math.max(0, end - WINDOW_BYTES);
|
|
383
|
+
const window = Buffer.allocUnsafe(end - start);
|
|
384
|
+
let read;
|
|
385
|
+
try {
|
|
386
|
+
read = readSync(fd, window, 0, end - start, start);
|
|
387
|
+
} catch {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const chunk = carry.length > 0 ? Buffer.concat([window.subarray(0, read), carry]) : window.subarray(0, read);
|
|
391
|
+
let lineEnd = chunk.length;
|
|
392
|
+
let nl = lineEnd > 0 ? chunk.lastIndexOf(NEWLINE, lineEnd - 1) : -1;
|
|
393
|
+
while (nl >= 0) {
|
|
394
|
+
if (nl + 1 < lineEnd) {
|
|
395
|
+
const line = chunk.subarray(nl + 1, lineEnd).toString("utf8").trim();
|
|
396
|
+
if (line && onLine(line)) return;
|
|
397
|
+
}
|
|
398
|
+
lineEnd = nl;
|
|
399
|
+
nl = lineEnd > 0 ? chunk.lastIndexOf(NEWLINE, lineEnd - 1) : -1;
|
|
400
|
+
}
|
|
401
|
+
if (start === 0) {
|
|
402
|
+
if (lineEnd > 0) {
|
|
403
|
+
const line = chunk.subarray(0, lineEnd).toString("utf8").trim();
|
|
404
|
+
if (line) onLine(line);
|
|
405
|
+
}
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (lineEnd > MAX_LINE_BYTES) return;
|
|
409
|
+
carry = Buffer.from(chunk.subarray(0, lineEnd));
|
|
410
|
+
end = start;
|
|
411
|
+
}
|
|
412
|
+
} finally {
|
|
413
|
+
try {
|
|
414
|
+
closeSync(fd);
|
|
415
|
+
} catch {
|
|
416
|
+
}
|
|
73
417
|
}
|
|
74
418
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
419
|
+
|
|
420
|
+
// src/audit/auditBodyReader.ts
|
|
421
|
+
function candidateDays(auditDir, ts) {
|
|
422
|
+
if (typeof ts === "number" && Number.isFinite(ts)) {
|
|
423
|
+
const named = auditDayDirName(ts);
|
|
424
|
+
if (existsSync2(join2(auditDir, named))) return [named];
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
return readdirSync2(auditDir).filter(isAuditDayDir).sort().reverse();
|
|
428
|
+
} catch {
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function readShard(auditDir, day, sessionKey) {
|
|
433
|
+
const base = join2(auditDir, day, AUDIT_BODIES_DIR, auditBodyFileName(sessionKey));
|
|
434
|
+
try {
|
|
435
|
+
if (existsSync2(base)) return readFileSync2(base, "utf8");
|
|
436
|
+
const gz = `${base}.gz`;
|
|
437
|
+
if (existsSync2(gz)) return gunzipSync(readFileSync2(gz)).toString("utf8");
|
|
438
|
+
} catch {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
function parseShard(raw) {
|
|
444
|
+
const entries = /* @__PURE__ */ new Map();
|
|
445
|
+
for (const line of raw.split("\n")) {
|
|
446
|
+
const trimmed = line.trim();
|
|
447
|
+
if (!trimmed) continue;
|
|
448
|
+
let parsed;
|
|
449
|
+
try {
|
|
450
|
+
parsed = JSON.parse(trimmed);
|
|
451
|
+
} catch {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (isAuditBodyEntry(parsed)) entries.set(parsed.id, parsed);
|
|
455
|
+
}
|
|
456
|
+
return entries;
|
|
457
|
+
}
|
|
458
|
+
function withDictionary(auditDir, day, entries) {
|
|
459
|
+
let needed = false;
|
|
460
|
+
for (const entry of entries.values()) {
|
|
461
|
+
if (entry.req?.base?.startsWith(DICT_BASE_PREFIX)) {
|
|
462
|
+
needed = true;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (!needed) return entries;
|
|
467
|
+
const base = join2(auditDir, day, AUDIT_BODIES_DIR, AUDIT_DICT_FILE);
|
|
468
|
+
let raw = null;
|
|
469
|
+
try {
|
|
470
|
+
if (existsSync2(base)) raw = readFileSync2(base, "utf8");
|
|
471
|
+
else if (existsSync2(`${base}.gz`)) raw = gunzipSync(readFileSync2(`${base}.gz`)).toString("utf8");
|
|
472
|
+
} catch {
|
|
473
|
+
return entries;
|
|
474
|
+
}
|
|
475
|
+
if (raw === null) return entries;
|
|
476
|
+
for (const [id, entry] of parseShard(raw)) entries.set(id, entry);
|
|
477
|
+
return entries;
|
|
478
|
+
}
|
|
479
|
+
function reconstructRequest(entries, entry) {
|
|
480
|
+
if (!entry.req) return void 0;
|
|
481
|
+
const chain = [];
|
|
482
|
+
const visited = /* @__PURE__ */ new Set();
|
|
483
|
+
let cursor = entry;
|
|
484
|
+
while (cursor?.req) {
|
|
485
|
+
if (visited.has(cursor.id)) return void 0;
|
|
486
|
+
visited.add(cursor.id);
|
|
487
|
+
chain.push(cursor);
|
|
488
|
+
if (cursor.req.base === null) break;
|
|
489
|
+
cursor = entries.get(cursor.req.base);
|
|
490
|
+
}
|
|
491
|
+
const anchor = chain[chain.length - 1];
|
|
492
|
+
if (!anchor?.req || anchor.req.base !== null) return void 0;
|
|
493
|
+
let text = anchor.req.ins;
|
|
494
|
+
for (let i = chain.length - 2; i >= 0; i -= 1) {
|
|
495
|
+
const delta = chain[i]?.req;
|
|
496
|
+
if (!delta) return void 0;
|
|
497
|
+
if (delta.pre > text.length || delta.suf > text.length - delta.pre) return void 0;
|
|
498
|
+
text = applyBodyDelta(text, delta);
|
|
499
|
+
}
|
|
500
|
+
return text;
|
|
501
|
+
}
|
|
502
|
+
function assignStreams(entries) {
|
|
503
|
+
const rootOf = /* @__PURE__ */ new Map();
|
|
504
|
+
for (const entry of entries.values()) {
|
|
505
|
+
const seen = /* @__PURE__ */ new Set();
|
|
506
|
+
let cursor = entry;
|
|
507
|
+
while (cursor?.req?.cont === true && cursor.req.base !== null && !seen.has(cursor.id)) {
|
|
508
|
+
seen.add(cursor.id);
|
|
509
|
+
const next = entries.get(cursor.req.base);
|
|
510
|
+
if (!next) break;
|
|
511
|
+
cursor = next;
|
|
512
|
+
}
|
|
513
|
+
rootOf.set(entry.id, cursor?.id ?? entry.id);
|
|
514
|
+
}
|
|
515
|
+
const firstTs = /* @__PURE__ */ new Map();
|
|
516
|
+
for (const entry of entries.values()) {
|
|
517
|
+
const root = rootOf.get(entry.id);
|
|
518
|
+
const known = firstTs.get(root);
|
|
519
|
+
if (known === void 0 || entry.ts < known) firstTs.set(root, entry.ts);
|
|
520
|
+
}
|
|
521
|
+
const order = [...firstTs.entries()].sort((a, b) => a[1] - b[1]).map(([root]) => root);
|
|
522
|
+
const index = new Map(order.map((root, i) => [root, i]));
|
|
523
|
+
const streams = /* @__PURE__ */ new Map();
|
|
524
|
+
for (const entry of entries.values()) {
|
|
525
|
+
streams.set(entry.id, index.get(rootOf.get(entry.id)) ?? 0);
|
|
526
|
+
}
|
|
527
|
+
return streams;
|
|
528
|
+
}
|
|
529
|
+
function readAuditBody(auditDir, query2) {
|
|
530
|
+
if (!isSafeSessionKey(query2.sessionKey) || !query2.id) return {};
|
|
531
|
+
if (!existsSync2(auditDir)) return {};
|
|
532
|
+
for (const day of candidateDays(auditDir, query2.ts)) {
|
|
533
|
+
const raw = readShard(auditDir, day, query2.sessionKey);
|
|
534
|
+
if (raw === null) continue;
|
|
535
|
+
const entries = withDictionary(auditDir, day, parseShard(raw));
|
|
536
|
+
const entry = entries.get(query2.id);
|
|
537
|
+
if (!entry) continue;
|
|
538
|
+
const result = {};
|
|
539
|
+
const requestBody = reconstructRequest(entries, entry);
|
|
540
|
+
if (requestBody !== void 0) result.requestBody = requestBody;
|
|
541
|
+
if (entry.res !== void 0) result.responseBody = entry.res;
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
return readLegacyInlineBody(auditDir, query2.id);
|
|
545
|
+
}
|
|
546
|
+
function readLegacyInlineBody(auditDir, id) {
|
|
547
|
+
let names;
|
|
548
|
+
try {
|
|
549
|
+
names = readdirSync2(auditDir).filter((name) => AUDIT_FILE_RE.test(name)).sort().reverse();
|
|
550
|
+
} catch {
|
|
551
|
+
return {};
|
|
552
|
+
}
|
|
553
|
+
const needle = JSON.stringify(id);
|
|
554
|
+
let found = {};
|
|
555
|
+
for (const name of names) {
|
|
556
|
+
forEachLineFromTail(join2(auditDir, name), (line) => {
|
|
557
|
+
if (!line.includes(needle)) return false;
|
|
558
|
+
let parsed;
|
|
559
|
+
try {
|
|
560
|
+
parsed = JSON.parse(line);
|
|
561
|
+
} catch {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
const record = parsed;
|
|
565
|
+
if (record.id !== id) return false;
|
|
566
|
+
const result = {};
|
|
567
|
+
if (typeof record.requestBody === "string") result.requestBody = record.requestBody;
|
|
568
|
+
if (typeof record.responseBody === "string") result.responseBody = record.responseBody;
|
|
569
|
+
found = result;
|
|
570
|
+
return true;
|
|
571
|
+
});
|
|
572
|
+
if (found.requestBody !== void 0 || found.responseBody !== void 0) break;
|
|
573
|
+
}
|
|
574
|
+
return found;
|
|
575
|
+
}
|
|
576
|
+
function readAuditSessionTurns(auditDir, sessionKey, ts) {
|
|
577
|
+
if (!isSafeSessionKey(sessionKey) || !existsSync2(auditDir)) return [];
|
|
578
|
+
for (const day of candidateDays(auditDir, ts)) {
|
|
579
|
+
const raw = readShard(auditDir, day, sessionKey);
|
|
580
|
+
if (raw === null) continue;
|
|
581
|
+
const shardEntries = parseShard(raw);
|
|
582
|
+
const streams = assignStreams(shardEntries);
|
|
583
|
+
const entries = withDictionary(auditDir, day, shardEntries);
|
|
584
|
+
const turns = [];
|
|
585
|
+
for (const entry of entries.values()) {
|
|
586
|
+
if (entry.id.startsWith(DICT_BASE_PREFIX)) continue;
|
|
587
|
+
const turn = { id: entry.id, ts: entry.ts, stream: streams.get(entry.id) ?? 0 };
|
|
588
|
+
if (entry.req?.anchor === "diverged") turn.diverged = true;
|
|
589
|
+
const requestBody = reconstructRequest(entries, entry);
|
|
590
|
+
if (requestBody !== void 0) turn.requestBody = requestBody;
|
|
591
|
+
if (entry.res !== void 0) turn.responseBody = entry.res;
|
|
592
|
+
turns.push(turn);
|
|
593
|
+
}
|
|
594
|
+
turns.sort((a, b) => a.stream === b.stream ? a.ts - b.ts : a.stream - b.stream);
|
|
595
|
+
return turns;
|
|
596
|
+
}
|
|
597
|
+
return [];
|
|
598
|
+
}
|
|
599
|
+
function listAuditSessions(auditDir, ts) {
|
|
600
|
+
if (!existsSync2(auditDir)) return [];
|
|
601
|
+
const summaries = [];
|
|
602
|
+
for (const day of candidateDays(auditDir, ts)) {
|
|
603
|
+
const bodiesPath = join2(auditDir, day, AUDIT_BODIES_DIR);
|
|
604
|
+
let files;
|
|
605
|
+
try {
|
|
606
|
+
files = readdirSync2(bodiesPath);
|
|
607
|
+
} catch {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
for (const file of files) {
|
|
611
|
+
const compressed = file.endsWith(".jsonl.gz");
|
|
612
|
+
const sessionKey = file.replace(/\.jsonl(\.gz)?$/, "");
|
|
613
|
+
if (!isSafeSessionKey(sessionKey) || !compressed && !file.endsWith(".jsonl")) continue;
|
|
614
|
+
let bytes = 0;
|
|
615
|
+
try {
|
|
616
|
+
bytes = statSync2(join2(bodiesPath, file)).size;
|
|
617
|
+
} catch {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const raw = readShard(auditDir, day, sessionKey);
|
|
621
|
+
const turns = raw === null ? 0 : parseShard(raw).size;
|
|
622
|
+
summaries.push({ sessionKey, day, turns, bytes, compressed });
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
summaries.sort((a, b) => a.day === b.day ? b.bytes - a.bytes : a.day < b.day ? 1 : -1);
|
|
626
|
+
return summaries;
|
|
80
627
|
}
|
|
81
628
|
|
|
82
|
-
// src/
|
|
83
|
-
import {
|
|
629
|
+
// src/commands/paths.ts
|
|
630
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
84
631
|
|
|
85
632
|
// src/secrets/envelope.ts
|
|
86
633
|
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
@@ -139,13 +686,13 @@ function decryptValue(envelope, key) {
|
|
|
139
686
|
|
|
140
687
|
// src/secrets/masterKey.ts
|
|
141
688
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
142
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
689
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
143
690
|
import { homedir } from "os";
|
|
144
|
-
import { dirname, join } from "path";
|
|
691
|
+
import { dirname, join as join3 } from "path";
|
|
145
692
|
var MASTER_KEY_ENV = "OMNICROSS_MASTER_KEY";
|
|
146
693
|
var KEY_BYTES2 = 32;
|
|
147
694
|
function defaultMasterKeyPath() {
|
|
148
|
-
return
|
|
695
|
+
return join3(homedir(), ".omnicross", "master.key");
|
|
149
696
|
}
|
|
150
697
|
function decodeEnvKey(raw) {
|
|
151
698
|
const trimmed = raw.trim();
|
|
@@ -161,7 +708,7 @@ function decodeEnvKey(raw) {
|
|
|
161
708
|
return buf;
|
|
162
709
|
}
|
|
163
710
|
function readKeyFile(path2) {
|
|
164
|
-
const raw =
|
|
711
|
+
const raw = readFileSync3(path2);
|
|
165
712
|
if (raw.length === KEY_BYTES2) return raw;
|
|
166
713
|
const text = raw.toString("utf8").trim();
|
|
167
714
|
if (/^[0-9a-fA-F]{64}$/.test(text)) return Buffer.from(text, "hex");
|
|
@@ -174,7 +721,7 @@ function readKeyFile(path2) {
|
|
|
174
721
|
function generateKeyFile(path2) {
|
|
175
722
|
const key = randomBytes2(KEY_BYTES2);
|
|
176
723
|
mkdirSync(dirname(path2), { recursive: true });
|
|
177
|
-
|
|
724
|
+
writeFileSync2(path2, key, { mode: 384 });
|
|
178
725
|
try {
|
|
179
726
|
chmodSync(path2, 384);
|
|
180
727
|
} catch {
|
|
@@ -187,7 +734,7 @@ function resolveMasterKey(options = {}) {
|
|
|
187
734
|
return decodeEnvKey(envRaw);
|
|
188
735
|
}
|
|
189
736
|
const keyFilePath = options.keyFilePath ?? defaultMasterKeyPath();
|
|
190
|
-
if (
|
|
737
|
+
if (existsSync3(keyFilePath)) {
|
|
191
738
|
return readKeyFile(keyFilePath);
|
|
192
739
|
}
|
|
193
740
|
return generateKeyFile(keyFilePath);
|
|
@@ -403,39 +950,266 @@ function transformTokens(tokens, fn) {
|
|
|
403
950
|
if (block && typeof block === "object" && !Array.isArray(block)) {
|
|
404
951
|
bag[provider] = transformTokenBlock(block, fields, fn);
|
|
405
952
|
}
|
|
406
|
-
const accountsKey = `${provider}Accounts`;
|
|
407
|
-
const accounts = bag[accountsKey];
|
|
408
|
-
if (Array.isArray(accounts)) {
|
|
409
|
-
bag[accountsKey] = accounts.map((entry) => {
|
|
410
|
-
if (entry && typeof entry === "object" && "tokens" in entry && entry.tokens && typeof entry.tokens === "object") {
|
|
411
|
-
const nextEntry = {
|
|
412
|
-
...entry,
|
|
413
|
-
tokens: transformTokenBlock(
|
|
414
|
-
entry.tokens,
|
|
415
|
-
fields,
|
|
416
|
-
fn
|
|
417
|
-
)
|
|
418
|
-
};
|
|
419
|
-
const proxy = entry.proxy;
|
|
420
|
-
if (proxy && typeof proxy === "object") {
|
|
421
|
-
nextEntry.proxy = transformProxyConfig(proxy, fn);
|
|
422
|
-
}
|
|
423
|
-
return nextEntry;
|
|
424
|
-
}
|
|
425
|
-
return entry;
|
|
426
|
-
});
|
|
953
|
+
const accountsKey = `${provider}Accounts`;
|
|
954
|
+
const accounts = bag[accountsKey];
|
|
955
|
+
if (Array.isArray(accounts)) {
|
|
956
|
+
bag[accountsKey] = accounts.map((entry) => {
|
|
957
|
+
if (entry && typeof entry === "object" && "tokens" in entry && entry.tokens && typeof entry.tokens === "object") {
|
|
958
|
+
const nextEntry = {
|
|
959
|
+
...entry,
|
|
960
|
+
tokens: transformTokenBlock(
|
|
961
|
+
entry.tokens,
|
|
962
|
+
fields,
|
|
963
|
+
fn
|
|
964
|
+
)
|
|
965
|
+
};
|
|
966
|
+
const proxy = entry.proxy;
|
|
967
|
+
if (proxy && typeof proxy === "object") {
|
|
968
|
+
nextEntry.proxy = transformProxyConfig(proxy, fn);
|
|
969
|
+
}
|
|
970
|
+
return nextEntry;
|
|
971
|
+
}
|
|
972
|
+
return entry;
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return next;
|
|
977
|
+
}
|
|
978
|
+
function encryptTokens(tokens, box) {
|
|
979
|
+
return transformTokens(tokens, (v) => box.encryptMaybe(v));
|
|
980
|
+
}
|
|
981
|
+
function decryptTokens(tokens, box) {
|
|
982
|
+
return transformTokens(tokens, (v) => box.decryptMaybe(v));
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/commands/paths.ts
|
|
986
|
+
function defaultKeysPath(configPath) {
|
|
987
|
+
return join4(dirname2(configPath), "keys.json");
|
|
988
|
+
}
|
|
989
|
+
function defaultVouchersPath(configPath) {
|
|
990
|
+
return join4(dirname2(configPath), "vouchers.json");
|
|
991
|
+
}
|
|
992
|
+
function defaultTokensPath(configPath) {
|
|
993
|
+
return join4(dirname2(configPath), "tokens.json");
|
|
994
|
+
}
|
|
995
|
+
function defaultIntegrationsPath(configPath) {
|
|
996
|
+
return join4(dirname2(configPath), "integrations.json");
|
|
997
|
+
}
|
|
998
|
+
function defaultPricingPath(configPath) {
|
|
999
|
+
return join4(dirname2(configPath), "pricing.json");
|
|
1000
|
+
}
|
|
1001
|
+
function defaultPricingRefreshStatePath(configPath) {
|
|
1002
|
+
return join4(dirname2(configPath), "pricing-refresh.json");
|
|
1003
|
+
}
|
|
1004
|
+
function defaultAccountAllowancePath(configPath) {
|
|
1005
|
+
return join4(dirname2(configPath), "allowance-cache.json");
|
|
1006
|
+
}
|
|
1007
|
+
function defaultUsageEventsPath(configPath) {
|
|
1008
|
+
return join4(dirname2(configPath), "usage-events.jsonl");
|
|
1009
|
+
}
|
|
1010
|
+
function defaultAuditDir(configPath) {
|
|
1011
|
+
return join4(dirname2(configPath), "audit");
|
|
1012
|
+
}
|
|
1013
|
+
function defaultBillingDir(configPath) {
|
|
1014
|
+
return join4(dirname2(configPath), "billing");
|
|
1015
|
+
}
|
|
1016
|
+
function resolveSecretBox(masterKeyFilePath) {
|
|
1017
|
+
return new SecretBox(() => resolveMasterKey({ keyFilePath: masterKeyFilePath }));
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// src/commands/audit.ts
|
|
1021
|
+
function parseDate(value) {
|
|
1022
|
+
if (!value) return void 0;
|
|
1023
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
|
|
1024
|
+
if (!m) throw new Error(`audit: --date must be YYYY-MM-DD (got ${value})`);
|
|
1025
|
+
const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
1026
|
+
return d.getTime();
|
|
1027
|
+
}
|
|
1028
|
+
function formatBytes(bytes) {
|
|
1029
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1030
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1031
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1032
|
+
}
|
|
1033
|
+
async function runAudit(argv) {
|
|
1034
|
+
const { values, positionals } = parseArgs({
|
|
1035
|
+
args: argv,
|
|
1036
|
+
options: {
|
|
1037
|
+
config: { type: "string", short: "c" },
|
|
1038
|
+
session: { type: "string", short: "s" },
|
|
1039
|
+
date: { type: "string", short: "d" },
|
|
1040
|
+
id: { type: "string" }
|
|
1041
|
+
},
|
|
1042
|
+
allowPositionals: true
|
|
1043
|
+
});
|
|
1044
|
+
const configPath = values.config;
|
|
1045
|
+
if (!configPath) throw new Error("audit: --config <path> is required");
|
|
1046
|
+
const auditDir = defaultAuditDir(configPath);
|
|
1047
|
+
const ts = parseDate(values.date);
|
|
1048
|
+
const action = positionals[0];
|
|
1049
|
+
if (action === "sessions") {
|
|
1050
|
+
const sessions2 = listAuditSessions(auditDir, ts);
|
|
1051
|
+
if (sessions2.length === 0) {
|
|
1052
|
+
console.info("No audit body shards found. Is `captureBodies` enabled?");
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
console.info(["DAY", "SESSION", "TURNS", "SIZE", "ARCHIVED"].join(" "));
|
|
1056
|
+
for (const s of sessions2) {
|
|
1057
|
+
console.info(
|
|
1058
|
+
[s.day, s.sessionKey, String(s.turns), formatBytes(s.bytes), s.compressed ? "gz" : "-"].join(" ")
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
if (action === "show") {
|
|
1064
|
+
const sessionKey = values.session;
|
|
1065
|
+
if (!sessionKey) throw new Error("audit show: --session <key> is required");
|
|
1066
|
+
if (values.id) {
|
|
1067
|
+
const body = readAuditBody(auditDir, { id: values.id, sessionKey, ...ts !== void 0 ? { ts } : {} });
|
|
1068
|
+
if (body.requestBody === void 0 && body.responseBody === void 0) {
|
|
1069
|
+
throw new Error(`audit show: no body found for record ${values.id}`);
|
|
1070
|
+
}
|
|
1071
|
+
if (body.requestBody !== void 0) console.info(`--- request ${values.id} ---
|
|
1072
|
+
${body.requestBody}`);
|
|
1073
|
+
if (body.responseBody !== void 0) console.info(`--- response ${values.id} ---
|
|
1074
|
+
${body.responseBody}`);
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
const turns = readAuditSessionTurns(auditDir, sessionKey, ts);
|
|
1078
|
+
if (turns.length === 0) throw new Error(`audit show: no shard found for session ${sessionKey}`);
|
|
1079
|
+
let stream = -1;
|
|
1080
|
+
for (const turn of turns) {
|
|
1081
|
+
const when = new Date(turn.ts).toISOString();
|
|
1082
|
+
if (turn.stream !== stream) {
|
|
1083
|
+
stream = turn.stream;
|
|
1084
|
+
console.info(`
|
|
1085
|
+
########## stream ${stream} ##########`);
|
|
1086
|
+
}
|
|
1087
|
+
if (turn.diverged) {
|
|
1088
|
+
console.info("=== prefix diverged here (system prompt changed, or a restart reused this session) ===");
|
|
1089
|
+
}
|
|
1090
|
+
if (turn.requestBody !== void 0) {
|
|
1091
|
+
console.info(`--- request ${turn.id} @ ${when} ---
|
|
1092
|
+
${turn.requestBody}`);
|
|
1093
|
+
}
|
|
1094
|
+
if (turn.responseBody !== void 0) {
|
|
1095
|
+
console.info(`--- response ${turn.id} @ ${when} ---
|
|
1096
|
+
${turn.responseBody}`);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
if (action === "compact") {
|
|
1102
|
+
const todayDir = auditDayDirName(Date.now());
|
|
1103
|
+
let names;
|
|
1104
|
+
try {
|
|
1105
|
+
names = readdirSync3(auditDir).filter(isAuditDayDir).sort();
|
|
1106
|
+
} catch {
|
|
1107
|
+
names = [];
|
|
1108
|
+
}
|
|
1109
|
+
const targets = ts !== void 0 ? names.filter((name) => name === auditDayDirName(ts)) : names.filter((name) => name !== todayDir);
|
|
1110
|
+
if (targets.length === 0) {
|
|
1111
|
+
console.info("Nothing to compact (today is skipped; a day is compacted once).");
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
let shards = 0;
|
|
1115
|
+
let saved = 0;
|
|
1116
|
+
for (const name of targets) {
|
|
1117
|
+
if (name === todayDir) {
|
|
1118
|
+
console.info(`Skipping ${name}: the current day is still being written.`);
|
|
1119
|
+
continue;
|
|
1120
|
+
}
|
|
1121
|
+
const result = compactAuditDay(join5(auditDir, name));
|
|
1122
|
+
shards += result.shards;
|
|
1123
|
+
saved += result.savedBytes;
|
|
1124
|
+
console.info(`${name}: ${result.shards} shard(s), ${result.anchors} anchor(s), ${formatBytes(result.savedBytes)} saved`);
|
|
1125
|
+
}
|
|
1126
|
+
console.info(`Done: ${shards} shard(s) rewritten, ${formatBytes(saved)} saved.`);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
throw new Error("audit: expected `sessions`, `show`, or `compact` (see `omnicross help`)");
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// src/commands/import-ccr.ts
|
|
1133
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
1134
|
+
import { parseArgs as parseArgs2 } from "util";
|
|
1135
|
+
|
|
1136
|
+
// src/ccr-import.ts
|
|
1137
|
+
function parseCcrConfig(raw) {
|
|
1138
|
+
if (!raw || typeof raw !== "object") {
|
|
1139
|
+
throw new Error("CCR config: top-level value must be an object");
|
|
1140
|
+
}
|
|
1141
|
+
const obj = raw;
|
|
1142
|
+
const Providers = Array.isArray(obj["Providers"]) ? obj["Providers"] : [];
|
|
1143
|
+
const Router = obj["Router"] && typeof obj["Router"] === "object" ? obj["Router"] : {};
|
|
1144
|
+
return { Providers, Router };
|
|
1145
|
+
}
|
|
1146
|
+
function inferApiFormat(provider) {
|
|
1147
|
+
const hay = `${provider.api_base_url ?? ""} ${provider.name ?? ""}`.toLowerCase();
|
|
1148
|
+
if (hay.includes("anthropic") || hay.includes("claude")) {
|
|
1149
|
+
return { format: "anthropic", ambiguous: false };
|
|
1150
|
+
}
|
|
1151
|
+
if (hay.includes("generativelanguage") || hay.includes("gemini") || hay.includes("google")) {
|
|
1152
|
+
return { format: "gemini", ambiguous: false };
|
|
1153
|
+
}
|
|
1154
|
+
if (hay.includes("/responses")) {
|
|
1155
|
+
return { format: "openai-response", ambiguous: false };
|
|
1156
|
+
}
|
|
1157
|
+
if (hay.includes("openai") || hay.includes("/v1") || hay.includes("chat/completions")) {
|
|
1158
|
+
return { format: "openai", ambiguous: false };
|
|
1159
|
+
}
|
|
1160
|
+
return { format: "openai", ambiguous: true };
|
|
1161
|
+
}
|
|
1162
|
+
function mapProviders(providers, notes) {
|
|
1163
|
+
const rows = [];
|
|
1164
|
+
for (const [i, p] of providers.entries()) {
|
|
1165
|
+
const id = p.name?.trim();
|
|
1166
|
+
if (!id) {
|
|
1167
|
+
notes.push(`Providers[${i}] has no name \u2014 skipped.`);
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
const { format, ambiguous } = inferApiFormat(p);
|
|
1171
|
+
if (ambiguous) {
|
|
1172
|
+
notes.push(
|
|
1173
|
+
`Provider '${id}': could not infer apiFormat from base URL \u2014 defaulted to 'openai'. Edit the config if this provider speaks a different wire format.`
|
|
1174
|
+
);
|
|
427
1175
|
}
|
|
1176
|
+
rows.push({
|
|
1177
|
+
id,
|
|
1178
|
+
apiFormat: format,
|
|
1179
|
+
baseUrl: p.api_base_url ?? "",
|
|
1180
|
+
apiKey: p.api_key ?? "",
|
|
1181
|
+
models: Array.isArray(p.models) ? p.models : void 0
|
|
1182
|
+
});
|
|
428
1183
|
}
|
|
429
|
-
return
|
|
1184
|
+
return rows;
|
|
430
1185
|
}
|
|
431
|
-
function
|
|
432
|
-
|
|
1186
|
+
function noteRouterRoles(router, notes) {
|
|
1187
|
+
if (router.think) {
|
|
1188
|
+
notes.push(`Router.think \u2192 folded into 'default' (omnicross has no think slot).`);
|
|
1189
|
+
}
|
|
1190
|
+
if (router.longContext) {
|
|
1191
|
+
notes.push(
|
|
1192
|
+
`Router.longContext \u2192 folded into 'default' (no longContext slot; longContextThreshold dropped).`
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
if (router.image) {
|
|
1196
|
+
notes.push(`Router.image \u2192 mapped to 'vision' (CCR forceUseImageAgent dropped).`);
|
|
1197
|
+
}
|
|
1198
|
+
if (router.webSearch) {
|
|
1199
|
+
notes.push(
|
|
1200
|
+
`Router.webSearch \u2192 DROPPED. omnicross injects web search via an interception port rather than routing to a natively-online model; the CCR webSearch model ('${router.webSearch}') was not carried over.`
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
433
1203
|
}
|
|
434
|
-
function
|
|
435
|
-
|
|
1204
|
+
function mapCcrToOmnicross(ccr) {
|
|
1205
|
+
const notes = [];
|
|
1206
|
+
const providers = mapProviders(ccr.Providers ?? [], notes);
|
|
1207
|
+
noteRouterRoles(ccr.Router ?? {}, notes);
|
|
1208
|
+
return { config: { providers }, notes };
|
|
436
1209
|
}
|
|
437
1210
|
|
|
438
1211
|
// src/config.ts
|
|
1212
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
439
1213
|
var DEFAULT_ADMIN_PORT = 8766;
|
|
440
1214
|
function validateAdmin(raw) {
|
|
441
1215
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -728,7 +1502,7 @@ function setSecretBox(box) {
|
|
|
728
1502
|
function loadConfig(path2) {
|
|
729
1503
|
let raw;
|
|
730
1504
|
try {
|
|
731
|
-
raw =
|
|
1505
|
+
raw = readFileSync4(path2, "utf8");
|
|
732
1506
|
} catch {
|
|
733
1507
|
throw new Error(`config: cannot read file at '${path2}'`);
|
|
734
1508
|
}
|
|
@@ -743,48 +1517,12 @@ function loadConfig(path2) {
|
|
|
743
1517
|
}
|
|
744
1518
|
function saveConfig(path2, cfg) {
|
|
745
1519
|
const toWrite = secretBox ? encryptConfigSecrets(cfg, secretBox) : cfg;
|
|
746
|
-
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// src/commands/paths.ts
|
|
750
|
-
import { dirname as dirname2, join as join2 } from "path";
|
|
751
|
-
function defaultKeysPath(configPath) {
|
|
752
|
-
return join2(dirname2(configPath), "keys.json");
|
|
753
|
-
}
|
|
754
|
-
function defaultVouchersPath(configPath) {
|
|
755
|
-
return join2(dirname2(configPath), "vouchers.json");
|
|
756
|
-
}
|
|
757
|
-
function defaultTokensPath(configPath) {
|
|
758
|
-
return join2(dirname2(configPath), "tokens.json");
|
|
759
|
-
}
|
|
760
|
-
function defaultIntegrationsPath(configPath) {
|
|
761
|
-
return join2(dirname2(configPath), "integrations.json");
|
|
762
|
-
}
|
|
763
|
-
function defaultPricingPath(configPath) {
|
|
764
|
-
return join2(dirname2(configPath), "pricing.json");
|
|
765
|
-
}
|
|
766
|
-
function defaultPricingRefreshStatePath(configPath) {
|
|
767
|
-
return join2(dirname2(configPath), "pricing-refresh.json");
|
|
768
|
-
}
|
|
769
|
-
function defaultAccountAllowancePath(configPath) {
|
|
770
|
-
return join2(dirname2(configPath), "allowance-cache.json");
|
|
771
|
-
}
|
|
772
|
-
function defaultUsageEventsPath(configPath) {
|
|
773
|
-
return join2(dirname2(configPath), "usage-events.jsonl");
|
|
774
|
-
}
|
|
775
|
-
function defaultAuditDir(configPath) {
|
|
776
|
-
return join2(dirname2(configPath), "audit");
|
|
777
|
-
}
|
|
778
|
-
function defaultBillingDir(configPath) {
|
|
779
|
-
return join2(dirname2(configPath), "billing");
|
|
780
|
-
}
|
|
781
|
-
function resolveSecretBox(masterKeyFilePath) {
|
|
782
|
-
return new SecretBox(() => resolveMasterKey({ keyFilePath: masterKeyFilePath }));
|
|
1520
|
+
writeFileSync3(path2, JSON.stringify(toWrite, null, 2) + "\n", "utf8");
|
|
783
1521
|
}
|
|
784
1522
|
|
|
785
1523
|
// src/commands/import-ccr.ts
|
|
786
1524
|
async function runImportCcr(argv) {
|
|
787
|
-
const { values, positionals } =
|
|
1525
|
+
const { values, positionals } = parseArgs2({
|
|
788
1526
|
args: argv,
|
|
789
1527
|
options: {
|
|
790
1528
|
out: { type: "string", short: "o" },
|
|
@@ -799,7 +1537,7 @@ async function runImportCcr(argv) {
|
|
|
799
1537
|
const outPath = values.out ?? "omnicross.config.json";
|
|
800
1538
|
let raw;
|
|
801
1539
|
try {
|
|
802
|
-
raw = JSON.parse(
|
|
1540
|
+
raw = JSON.parse(readFileSync5(ccrPath, "utf8"));
|
|
803
1541
|
} catch {
|
|
804
1542
|
throw new Error(`import-ccr: cannot read or parse '${ccrPath}'`);
|
|
805
1543
|
}
|
|
@@ -821,24 +1559,24 @@ async function runImportCcr(argv) {
|
|
|
821
1559
|
|
|
822
1560
|
// src/commands/integrations.ts
|
|
823
1561
|
import { resolve as resolve2 } from "path";
|
|
824
|
-
import { parseArgs as
|
|
1562
|
+
import { parseArgs as parseArgs3 } from "util";
|
|
825
1563
|
|
|
826
1564
|
// src/integrations/IntegrationManager.ts
|
|
827
1565
|
import { createHash } from "crypto";
|
|
828
|
-
import { existsSync as
|
|
1566
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7, unlinkSync as unlinkSync3 } from "fs";
|
|
829
1567
|
import { homedir as homedir2 } from "os";
|
|
830
|
-
import { dirname as dirname4, join as
|
|
1568
|
+
import { dirname as dirname4, join as join6, resolve } from "path";
|
|
831
1569
|
import { createIntegrationKey } from "@omnicross/core";
|
|
832
1570
|
|
|
833
1571
|
// src/integrations/IntegrationStateStore.ts
|
|
834
1572
|
import {
|
|
835
1573
|
chmodSync as chmodSync2,
|
|
836
|
-
existsSync as
|
|
1574
|
+
existsSync as existsSync4,
|
|
837
1575
|
mkdirSync as mkdirSync2,
|
|
838
|
-
readFileSync as
|
|
839
|
-
renameSync,
|
|
840
|
-
unlinkSync,
|
|
841
|
-
writeFileSync as
|
|
1576
|
+
readFileSync as readFileSync6,
|
|
1577
|
+
renameSync as renameSync2,
|
|
1578
|
+
unlinkSync as unlinkSync2,
|
|
1579
|
+
writeFileSync as writeFileSync4
|
|
842
1580
|
} from "fs";
|
|
843
1581
|
import { dirname as dirname3 } from "path";
|
|
844
1582
|
var EMPTY_STATE = { version: 1, clients: {} };
|
|
@@ -850,10 +1588,10 @@ var IntegrationStateStore = class {
|
|
|
850
1588
|
path;
|
|
851
1589
|
box;
|
|
852
1590
|
load() {
|
|
853
|
-
if (!
|
|
1591
|
+
if (!existsSync4(this.path)) return { ...EMPTY_STATE, clients: {} };
|
|
854
1592
|
let raw;
|
|
855
1593
|
try {
|
|
856
|
-
raw = JSON.parse(
|
|
1594
|
+
raw = JSON.parse(readFileSync6(this.path, "utf8"));
|
|
857
1595
|
} catch {
|
|
858
1596
|
throw new Error(`integration state '${this.path}' is not valid JSON`);
|
|
859
1597
|
}
|
|
@@ -923,17 +1661,17 @@ function isManagedFileRecord(value) {
|
|
|
923
1661
|
function atomicWrite(path2, content) {
|
|
924
1662
|
mkdirSync2(dirname3(path2), { recursive: true });
|
|
925
1663
|
const temp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
926
|
-
|
|
1664
|
+
writeFileSync4(temp, content, { encoding: "utf8", mode: 384 });
|
|
927
1665
|
try {
|
|
928
|
-
|
|
1666
|
+
renameSync2(temp, path2);
|
|
929
1667
|
} catch (error) {
|
|
930
1668
|
try {
|
|
931
|
-
|
|
1669
|
+
unlinkSync2(temp);
|
|
932
1670
|
} catch {
|
|
933
1671
|
}
|
|
934
1672
|
throw error;
|
|
935
1673
|
} finally {
|
|
936
|
-
if (
|
|
1674
|
+
if (existsSync4(path2)) {
|
|
937
1675
|
try {
|
|
938
1676
|
chmodSync2(path2, 384);
|
|
939
1677
|
} catch {
|
|
@@ -1427,10 +2165,10 @@ var IntegrationManager = class {
|
|
|
1427
2165
|
};
|
|
1428
2166
|
}
|
|
1429
2167
|
defaultConfigPath(client) {
|
|
1430
|
-
return client === "codex" ?
|
|
2168
|
+
return client === "codex" ? join6(this.homeDir, ".codex", "config.toml") : join6(this.homeDir, ".claude", "settings.json");
|
|
1431
2169
|
}
|
|
1432
2170
|
codexAuthPathForConfig(configPath) {
|
|
1433
|
-
return
|
|
2171
|
+
return join6(dirname4(configPath), "auth.json");
|
|
1434
2172
|
}
|
|
1435
2173
|
renderInstalled(client, base, secret) {
|
|
1436
2174
|
if (client === "claude") {
|
|
@@ -1443,7 +2181,7 @@ var IntegrationManager = class {
|
|
|
1443
2181
|
}
|
|
1444
2182
|
};
|
|
1445
2183
|
function readOptional(path2) {
|
|
1446
|
-
return
|
|
2184
|
+
return existsSync5(path2) ? readFileSync7(path2, "utf8") : null;
|
|
1447
2185
|
}
|
|
1448
2186
|
function sha256(value) {
|
|
1449
2187
|
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
@@ -1507,7 +2245,7 @@ function writeOptional(path2, content) {
|
|
|
1507
2245
|
atomicWrite(path2, content);
|
|
1508
2246
|
return;
|
|
1509
2247
|
}
|
|
1510
|
-
if (
|
|
2248
|
+
if (existsSync5(path2)) unlinkSync3(path2);
|
|
1511
2249
|
}
|
|
1512
2250
|
function assertLoopbackGatewayUrl(value) {
|
|
1513
2251
|
let url;
|
|
@@ -1524,7 +2262,7 @@ function assertLoopbackGatewayUrl(value) {
|
|
|
1524
2262
|
}
|
|
1525
2263
|
|
|
1526
2264
|
// src/ports/JsonOutboundKeyDb.ts
|
|
1527
|
-
import { existsSync as
|
|
2265
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
1528
2266
|
var JsonOutboundKeyDb = class {
|
|
1529
2267
|
/**
|
|
1530
2268
|
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
@@ -1657,16 +2395,16 @@ var JsonOutboundKeyDb = class {
|
|
|
1657
2395
|
}
|
|
1658
2396
|
/** Read the key rows, tolerating a missing/corrupt file (→ empty list). */
|
|
1659
2397
|
readRows() {
|
|
1660
|
-
if (!
|
|
2398
|
+
if (!existsSync6(this.keysPath)) return [];
|
|
1661
2399
|
try {
|
|
1662
|
-
const parsed = JSON.parse(
|
|
2400
|
+
const parsed = JSON.parse(readFileSync8(this.keysPath, "utf8"));
|
|
1663
2401
|
return Array.isArray(parsed) ? parsed : [];
|
|
1664
2402
|
} catch {
|
|
1665
2403
|
return [];
|
|
1666
2404
|
}
|
|
1667
2405
|
}
|
|
1668
2406
|
writeRows(rows) {
|
|
1669
|
-
|
|
2407
|
+
writeFileSync5(this.keysPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
1670
2408
|
}
|
|
1671
2409
|
};
|
|
1672
2410
|
function applyPolicyField(row, field, value) {
|
|
@@ -1677,7 +2415,7 @@ function applyPolicyField(row, field, value) {
|
|
|
1677
2415
|
|
|
1678
2416
|
// src/commands/integrations.ts
|
|
1679
2417
|
async function runIntegrations(argv) {
|
|
1680
|
-
const { values, positionals } =
|
|
2418
|
+
const { values, positionals } = parseArgs3({
|
|
1681
2419
|
args: argv,
|
|
1682
2420
|
options: {
|
|
1683
2421
|
config: { type: "string", short: "c" },
|
|
@@ -1727,10 +2465,10 @@ function isClient(value) {
|
|
|
1727
2465
|
}
|
|
1728
2466
|
|
|
1729
2467
|
// src/commands/keys.ts
|
|
1730
|
-
import { parseArgs as
|
|
2468
|
+
import { parseArgs as parseArgs4 } from "util";
|
|
1731
2469
|
import { createNamedKey } from "@omnicross/core/outbound-api";
|
|
1732
2470
|
async function runKeys(argv) {
|
|
1733
|
-
const { values, positionals } =
|
|
2471
|
+
const { values, positionals } = parseArgs4({
|
|
1734
2472
|
args: argv,
|
|
1735
2473
|
options: { config: { type: "string", short: "c" } },
|
|
1736
2474
|
allowPositionals: true
|
|
@@ -1784,9 +2522,9 @@ async function keysRevoke(db, id) {
|
|
|
1784
2522
|
// src/commands/launch.ts
|
|
1785
2523
|
import { spawn as spawn2 } from "child_process";
|
|
1786
2524
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
1787
|
-
import { existsSync as
|
|
1788
|
-
import { delimiter as delimiter2, join as
|
|
1789
|
-
import { parseArgs as
|
|
2525
|
+
import { existsSync as existsSync22 } from "fs";
|
|
2526
|
+
import { delimiter as delimiter2, join as join15 } from "path";
|
|
2527
|
+
import { parseArgs as parseArgs5 } from "util";
|
|
1790
2528
|
import {
|
|
1791
2529
|
buildChatCliLaunchConfig as buildChatCliLaunchConfig2,
|
|
1792
2530
|
buildGeminiCliLaunchConfig as buildGeminiCliLaunchConfig2
|
|
@@ -1794,7 +2532,7 @@ import {
|
|
|
1794
2532
|
import { ROUTE_LEASE_REQUEST_SCHEMA as ROUTE_LEASE_REQUEST_SCHEMA2 } from "@omnicross/core/provider-proxy";
|
|
1795
2533
|
|
|
1796
2534
|
// src/bootstrap.ts
|
|
1797
|
-
import { accessSync, constants as fsConstants, existsSync as
|
|
2535
|
+
import { accessSync, constants as fsConstants, existsSync as existsSync21 } from "fs";
|
|
1798
2536
|
import { DEFAULT_AUDIT_CONFIG } from "@omnicross/contracts/audit-types";
|
|
1799
2537
|
import { DEFAULT_BILLING_CONFIG } from "@omnicross/contracts/billing-types";
|
|
1800
2538
|
import { getGeminiCodeAssistProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
@@ -1828,7 +2566,12 @@ import {
|
|
|
1828
2566
|
} from "@omnicross/core/provider-proxy";
|
|
1829
2567
|
import { routeLeaseDescriptorPort } from "@omnicross/cli-launcher";
|
|
1830
2568
|
import { KeySpendTracker } from "@omnicross/core/outbound-api";
|
|
1831
|
-
import {
|
|
2569
|
+
import {
|
|
2570
|
+
__resetSharedUsageThroughputTrackerForTests,
|
|
2571
|
+
getSharedUsageThroughputTracker as getSharedUsageThroughputTracker2,
|
|
2572
|
+
PricingEngine,
|
|
2573
|
+
UsageRecorder
|
|
2574
|
+
} from "@omnicross/core/usage";
|
|
1832
2575
|
import {
|
|
1833
2576
|
setSubscriptionAccountService,
|
|
1834
2577
|
setSubscriptionProviderRegistry,
|
|
@@ -2335,13 +3078,13 @@ var ClaudeAllowanceRefreshScheduler = class {
|
|
|
2335
3078
|
// src/allowance/JsonAccountAllowancePersistence.ts
|
|
2336
3079
|
import { randomUUID } from "crypto";
|
|
2337
3080
|
import {
|
|
2338
|
-
existsSync as
|
|
3081
|
+
existsSync as existsSync7,
|
|
2339
3082
|
mkdirSync as mkdirSync3,
|
|
2340
|
-
readFileSync as
|
|
2341
|
-
renameSync as
|
|
3083
|
+
readFileSync as readFileSync9,
|
|
3084
|
+
renameSync as renameSync3,
|
|
2342
3085
|
rmSync,
|
|
2343
|
-
statSync,
|
|
2344
|
-
writeFileSync as
|
|
3086
|
+
statSync as statSync3,
|
|
3087
|
+
writeFileSync as writeFileSync6
|
|
2345
3088
|
} from "fs";
|
|
2346
3089
|
import { dirname as dirname5 } from "path";
|
|
2347
3090
|
import { normalizeAccountAllowanceSnapshot } from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
@@ -2355,10 +3098,10 @@ var JsonAccountAllowancePersistence = class {
|
|
|
2355
3098
|
cachePath;
|
|
2356
3099
|
/** Read only the `snapshots` payload; all row validation remains defensive. */
|
|
2357
3100
|
load() {
|
|
2358
|
-
if (!
|
|
3101
|
+
if (!existsSync7(this.cachePath)) return [];
|
|
2359
3102
|
try {
|
|
2360
|
-
if (
|
|
2361
|
-
const raw =
|
|
3103
|
+
if (statSync3(this.cachePath).size > MAX_ALLOWANCE_CACHE_BYTES) return [];
|
|
3104
|
+
const raw = readFileSync9(this.cachePath, "utf8");
|
|
2362
3105
|
if (!raw.trim()) return [];
|
|
2363
3106
|
const parsed = JSON.parse(raw);
|
|
2364
3107
|
if (Array.isArray(parsed)) return parsed;
|
|
@@ -2389,8 +3132,8 @@ var JsonAccountAllowancePersistence = class {
|
|
|
2389
3132
|
mkdirSync3(dirname5(this.cachePath), { recursive: true });
|
|
2390
3133
|
const temporaryPath = `${this.cachePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
2391
3134
|
try {
|
|
2392
|
-
|
|
2393
|
-
|
|
3135
|
+
writeFileSync6(temporaryPath, serialized, { encoding: "utf8", flag: "wx" });
|
|
3136
|
+
renameSync3(temporaryPath, this.cachePath);
|
|
2394
3137
|
} finally {
|
|
2395
3138
|
rmSync(temporaryPath, { force: true });
|
|
2396
3139
|
}
|
|
@@ -2432,6 +3175,37 @@ function handleAuditQuery(req, res, reader) {
|
|
|
2432
3175
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2433
3176
|
res.end(JSON.stringify({ records }));
|
|
2434
3177
|
}
|
|
3178
|
+
function handleAuditBodyQuery(req, res, reader) {
|
|
3179
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
3180
|
+
const id = url.searchParams.get("id")?.trim();
|
|
3181
|
+
const sessionKey = url.searchParams.get("session")?.trim();
|
|
3182
|
+
if (!id || !sessionKey) {
|
|
3183
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
3184
|
+
res.end(JSON.stringify({ error: "id and session are required" }));
|
|
3185
|
+
return;
|
|
3186
|
+
}
|
|
3187
|
+
const query2 = { id, sessionKey };
|
|
3188
|
+
const ts = intParam(url.searchParams.get("ts"));
|
|
3189
|
+
if (ts !== void 0) query2.ts = ts;
|
|
3190
|
+
const body = reader ? reader(query2) : {};
|
|
3191
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3192
|
+
res.end(JSON.stringify(body));
|
|
3193
|
+
}
|
|
3194
|
+
function handleAuditCompact(res, compact) {
|
|
3195
|
+
if (!compact) {
|
|
3196
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3197
|
+
res.end(JSON.stringify({ days: 0, shards: 0, savedBytes: 0 }));
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
3200
|
+
try {
|
|
3201
|
+
const result = compact();
|
|
3202
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3203
|
+
res.end(JSON.stringify(result));
|
|
3204
|
+
} catch (error) {
|
|
3205
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
3206
|
+
res.end(JSON.stringify({ error: error instanceof Error ? error.message : "compaction failed" }));
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
2435
3209
|
async function handleAuditStatsQuery(req, res, reader) {
|
|
2436
3210
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
2437
3211
|
const query2 = {};
|
|
@@ -3177,10 +3951,10 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
|
3177
3951
|
// src/admin/cliLaunch.ts
|
|
3178
3952
|
import { exec, spawn } from "child_process";
|
|
3179
3953
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3180
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
3954
|
+
import { chmodSync as chmodSync3, existsSync as existsSync8, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
3181
3955
|
import { createServer } from "net";
|
|
3182
3956
|
import { tmpdir } from "os";
|
|
3183
|
-
import { delimiter, join as
|
|
3957
|
+
import { delimiter, join as join7 } from "path";
|
|
3184
3958
|
import {
|
|
3185
3959
|
buildChatCliLaunchConfig,
|
|
3186
3960
|
buildClaudeCliLaunchConfig,
|
|
@@ -3237,8 +4011,8 @@ function isLaunchCliId(id) {
|
|
|
3237
4011
|
function probeDefault(candidate) {
|
|
3238
4012
|
const segments = (process.env["PATH"] ?? "").split(delimiter).filter(Boolean);
|
|
3239
4013
|
for (const seg of segments) {
|
|
3240
|
-
const full =
|
|
3241
|
-
if (
|
|
4014
|
+
const full = join7(seg, candidate);
|
|
4015
|
+
if (existsSync8(full)) return full;
|
|
3242
4016
|
}
|
|
3243
4017
|
return null;
|
|
3244
4018
|
}
|
|
@@ -3342,10 +4116,10 @@ function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }
|
|
|
3342
4116
|
const runLine = [command, ...extraArgs].map(shq).join(" ");
|
|
3343
4117
|
const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
|
|
3344
4118
|
if (platform === "darwin") {
|
|
3345
|
-
const launchDir = mkdtempSync(
|
|
3346
|
-
const commandFile =
|
|
3347
|
-
const bootstrapFile =
|
|
3348
|
-
const socketPath = macIpc.socketPath ??
|
|
4119
|
+
const launchDir = mkdtempSync(join7(tmpdir(), "omnicross-terminal-"));
|
|
4120
|
+
const commandFile = join7(launchDir, "launch.command");
|
|
4121
|
+
const bootstrapFile = join7(launchDir, "bootstrap.cjs");
|
|
4122
|
+
const socketPath = macIpc.socketPath ?? join7(launchDir, "descriptor.sock");
|
|
3349
4123
|
const openerEnv = { ...process.env };
|
|
3350
4124
|
for (const key of Object.keys(env)) delete openerEnv[key];
|
|
3351
4125
|
let claimed = false;
|
|
@@ -3415,8 +4189,8 @@ function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }
|
|
|
3415
4189
|
}
|
|
3416
4190
|
};
|
|
3417
4191
|
try {
|
|
3418
|
-
|
|
3419
|
-
|
|
4192
|
+
writeFileSync7(bootstrapFile, MAC_TERMINAL_BOOTSTRAP_SOURCE, { encoding: "utf8", mode: 448 });
|
|
4193
|
+
writeFileSync7(commandFile, `#!/bin/bash
|
|
3420
4194
|
rm -f -- "$0"
|
|
3421
4195
|
exec ${shq(process.execPath)} ${shq(bootstrapFile)} ${shq(socketPath)} ${shq(launchDir)} ${shq(cwd ?? "")} ${runLine}
|
|
3422
4196
|
`, {
|
|
@@ -4087,6 +4861,7 @@ function applyAuditConfig(config) {
|
|
|
4087
4861
|
} else {
|
|
4088
4862
|
setAuditCaptureConfig(null);
|
|
4089
4863
|
setAuditSink(null);
|
|
4864
|
+
writer?.reset();
|
|
4090
4865
|
if (sweeper) {
|
|
4091
4866
|
if (config) sweeper.configure(config);
|
|
4092
4867
|
sweeper.dispose();
|
|
@@ -4673,6 +5448,7 @@ async function handleImport(body, deps) {
|
|
|
4673
5448
|
}
|
|
4674
5449
|
|
|
4675
5450
|
// src/admin/usagePricing.ts
|
|
5451
|
+
import { getSharedUsageThroughputTracker } from "@omnicross/core/usage";
|
|
4676
5452
|
var err4 = (status, message) => ({
|
|
4677
5453
|
status,
|
|
4678
5454
|
body: { error: { type: "admin_api_error", message } }
|
|
@@ -4698,6 +5474,9 @@ var BUCKET_SPAN_MS = {
|
|
|
4698
5474
|
};
|
|
4699
5475
|
var MAX_TIMESERIES_BUCKETS = 2e3;
|
|
4700
5476
|
async function handleUsageGet(view, query2, deps) {
|
|
5477
|
+
if (view === "throughput") {
|
|
5478
|
+
return { status: 200, body: getSharedUsageThroughputTracker().snapshot() };
|
|
5479
|
+
}
|
|
4701
5480
|
const range = parseRange(query2);
|
|
4702
5481
|
if (!isRange(range)) return range;
|
|
4703
5482
|
switch (view) {
|
|
@@ -6334,7 +7113,7 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
6334
7113
|
}
|
|
6335
7114
|
|
|
6336
7115
|
// src/admin/uiStatic.ts
|
|
6337
|
-
import { existsSync as
|
|
7116
|
+
import { existsSync as existsSync9, statSync as statSync4 } from "fs";
|
|
6338
7117
|
import { readFile } from "fs/promises";
|
|
6339
7118
|
import { createRequire } from "module";
|
|
6340
7119
|
import path from "path";
|
|
@@ -6357,13 +7136,13 @@ var CONTENT_TYPES = {
|
|
|
6357
7136
|
function resolveUiDist() {
|
|
6358
7137
|
const fromEnv = process.env["OMNICROSS_UI_DIST"];
|
|
6359
7138
|
if (fromEnv) {
|
|
6360
|
-
return
|
|
7139
|
+
return existsSync9(path.join(fromEnv, "index.html")) ? path.resolve(fromEnv) : null;
|
|
6361
7140
|
}
|
|
6362
7141
|
try {
|
|
6363
7142
|
const req = createRequire(typeof __filename !== "undefined" ? __filename : import.meta.url);
|
|
6364
7143
|
const pkgJson = req.resolve("@omnicross/ui/package.json");
|
|
6365
7144
|
const dist = path.join(path.dirname(pkgJson), "dist");
|
|
6366
|
-
return
|
|
7145
|
+
return existsSync9(path.join(dist, "index.html")) ? dist : null;
|
|
6367
7146
|
} catch {
|
|
6368
7147
|
return null;
|
|
6369
7148
|
}
|
|
@@ -6412,7 +7191,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
6412
7191
|
return true;
|
|
6413
7192
|
}
|
|
6414
7193
|
let target = filePath;
|
|
6415
|
-
if (!
|
|
7194
|
+
if (!existsSync9(target) || statSync4(target).isDirectory()) {
|
|
6416
7195
|
if (path.extname(rel) === "") {
|
|
6417
7196
|
target = path.join(uiDist, "index.html");
|
|
6418
7197
|
} else {
|
|
@@ -6429,7 +7208,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
6429
7208
|
}
|
|
6430
7209
|
|
|
6431
7210
|
// src/admin/version.ts
|
|
6432
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
7211
|
+
var DAEMON_VERSION = true ? "0.1.10" : "0.0.0-dev";
|
|
6433
7212
|
|
|
6434
7213
|
// src/admin/AdminServer.ts
|
|
6435
7214
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -6541,6 +7320,14 @@ var AdminServer = class {
|
|
|
6541
7320
|
await handleAuditStatsQuery(req, res, this.deps.auditStatsReader);
|
|
6542
7321
|
return;
|
|
6543
7322
|
}
|
|
7323
|
+
if (path2 === "/admin/api/audit/body" && (req.method === "GET" || req.method === "HEAD")) {
|
|
7324
|
+
handleAuditBodyQuery(req, res, this.deps.auditBodyReader);
|
|
7325
|
+
return;
|
|
7326
|
+
}
|
|
7327
|
+
if (path2 === "/admin/api/audit/compact" && req.method === "POST") {
|
|
7328
|
+
handleAuditCompact(res, this.deps.auditCompactor);
|
|
7329
|
+
return;
|
|
7330
|
+
}
|
|
6544
7331
|
if (path2 === "/admin/api/billing-status" && (req.method === "GET" || req.method === "HEAD")) {
|
|
6545
7332
|
handleBillingStatus(res, this.deps.billingStatusReader);
|
|
6546
7333
|
return;
|
|
@@ -7153,7 +7940,7 @@ function safeStringify(value) {
|
|
|
7153
7940
|
}
|
|
7154
7941
|
|
|
7155
7942
|
// src/ports/JsonApiServerSettingsStore.ts
|
|
7156
|
-
import { readFileSync as
|
|
7943
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
7157
7944
|
import { OUTBOUND_API_SERVER_CONFIG_KEY } from "@omnicross/core/outbound-api";
|
|
7158
7945
|
var JsonApiServerSettingsStore = class {
|
|
7159
7946
|
/**
|
|
@@ -7180,7 +7967,7 @@ var JsonApiServerSettingsStore = class {
|
|
|
7180
7967
|
if (key !== OUTBOUND_API_SERVER_CONFIG_KEY) return;
|
|
7181
7968
|
const file = this.readFile();
|
|
7182
7969
|
file.server = this.encryptSecrets(value);
|
|
7183
|
-
|
|
7970
|
+
writeFileSync8(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
|
|
7184
7971
|
}
|
|
7185
7972
|
/** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
|
|
7186
7973
|
encryptSecrets(config) {
|
|
@@ -7203,7 +7990,7 @@ var JsonApiServerSettingsStore = class {
|
|
|
7203
7990
|
/** Read the config.json, tolerating a missing/corrupt file (→ empty shape). */
|
|
7204
7991
|
readFile() {
|
|
7205
7992
|
try {
|
|
7206
|
-
const raw =
|
|
7993
|
+
const raw = readFileSync10(this.configPath, "utf8");
|
|
7207
7994
|
const parsed = JSON.parse(raw);
|
|
7208
7995
|
if (parsed && typeof parsed === "object") return parsed;
|
|
7209
7996
|
} catch {
|
|
@@ -7214,7 +8001,7 @@ var JsonApiServerSettingsStore = class {
|
|
|
7214
8001
|
|
|
7215
8002
|
// src/ports/JsonlUsageEventStore.ts
|
|
7216
8003
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7217
|
-
import { appendFileSync, existsSync as
|
|
8004
|
+
import { appendFileSync, existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
7218
8005
|
var JsonlUsageEventStore = class {
|
|
7219
8006
|
constructor(eventsPath, isPriced) {
|
|
7220
8007
|
this.eventsPath = eventsPath;
|
|
@@ -7428,10 +8215,10 @@ var JsonlUsageEventStore = class {
|
|
|
7428
8215
|
}
|
|
7429
8216
|
/** Parse every line, skipping malformed/torn lines defensively. */
|
|
7430
8217
|
readAllRows() {
|
|
7431
|
-
if (!
|
|
8218
|
+
if (!existsSync10(this.eventsPath)) return [];
|
|
7432
8219
|
let raw;
|
|
7433
8220
|
try {
|
|
7434
|
-
raw =
|
|
8221
|
+
raw = readFileSync11(this.eventsPath, "utf8");
|
|
7435
8222
|
} catch {
|
|
7436
8223
|
return [];
|
|
7437
8224
|
}
|
|
@@ -7470,15 +8257,15 @@ function nextBoundary(ts, bucket) {
|
|
|
7470
8257
|
return new Date(d.getFullYear(), d.getMonth() + 1, 1).getTime();
|
|
7471
8258
|
}
|
|
7472
8259
|
}
|
|
7473
|
-
var
|
|
8260
|
+
var pad22 = (n) => String(n).padStart(2, "0");
|
|
7474
8261
|
function bucketLabel(bucketStartTs, bucket) {
|
|
7475
8262
|
const d = new Date(bucketStartTs);
|
|
7476
8263
|
const y = d.getFullYear();
|
|
7477
|
-
const mo =
|
|
7478
|
-
const day =
|
|
8264
|
+
const mo = pad22(d.getMonth() + 1);
|
|
8265
|
+
const day = pad22(d.getDate());
|
|
7479
8266
|
switch (bucket) {
|
|
7480
8267
|
case "hour":
|
|
7481
|
-
return `${mo}-${day} ${
|
|
8268
|
+
return `${mo}-${day} ${pad22(d.getHours())}:00`;
|
|
7482
8269
|
case "day":
|
|
7483
8270
|
return `${y}-${mo}-${day}`;
|
|
7484
8271
|
case "month":
|
|
@@ -7534,7 +8321,7 @@ function median(values) {
|
|
|
7534
8321
|
}
|
|
7535
8322
|
|
|
7536
8323
|
// src/ports/JsonPricingStore.ts
|
|
7537
|
-
import { existsSync as
|
|
8324
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
7538
8325
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
7539
8326
|
var JsonPricingStore = class {
|
|
7540
8327
|
constructor(pricingPath) {
|
|
@@ -7549,9 +8336,9 @@ var JsonPricingStore = class {
|
|
|
7549
8336
|
* otherwise unusable pricing table after a crash or manual file edit.
|
|
7550
8337
|
*/
|
|
7551
8338
|
hasUsableSnapshot() {
|
|
7552
|
-
if (!
|
|
8339
|
+
if (!existsSync11(this.pricingPath)) return false;
|
|
7553
8340
|
try {
|
|
7554
|
-
const parsed = JSON.parse(
|
|
8341
|
+
const parsed = JSON.parse(readFileSync12(this.pricingPath, "utf8"));
|
|
7555
8342
|
return Array.isArray(parsed) && parsed.some(isUsablePricingRow);
|
|
7556
8343
|
} catch {
|
|
7557
8344
|
return false;
|
|
@@ -7664,9 +8451,9 @@ var JsonPricingStore = class {
|
|
|
7664
8451
|
}
|
|
7665
8452
|
/** Read the pricing rows, tolerating a missing/corrupt file (→ empty list). */
|
|
7666
8453
|
readRows() {
|
|
7667
|
-
if (!
|
|
8454
|
+
if (!existsSync11(this.pricingPath)) return [];
|
|
7668
8455
|
try {
|
|
7669
|
-
const parsed = JSON.parse(
|
|
8456
|
+
const parsed = JSON.parse(readFileSync12(this.pricingPath, "utf8"));
|
|
7670
8457
|
return Array.isArray(parsed) ? parsed : [];
|
|
7671
8458
|
} catch {
|
|
7672
8459
|
return [];
|
|
@@ -7675,7 +8462,7 @@ var JsonPricingStore = class {
|
|
|
7675
8462
|
writeRows(rows) {
|
|
7676
8463
|
const temporaryPath = `${this.pricingPath}.${process.pid}.${randomUUID5()}.tmp`;
|
|
7677
8464
|
try {
|
|
7678
|
-
|
|
8465
|
+
writeFileSync9(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
7679
8466
|
encoding: "utf8",
|
|
7680
8467
|
flag: "wx"
|
|
7681
8468
|
});
|
|
@@ -7686,7 +8473,7 @@ var JsonPricingStore = class {
|
|
|
7686
8473
|
}
|
|
7687
8474
|
/** Isolated for deterministic failure testing; never removes the target. */
|
|
7688
8475
|
replaceFile(temporaryPath) {
|
|
7689
|
-
|
|
8476
|
+
renameSync4(temporaryPath, this.pricingPath);
|
|
7690
8477
|
}
|
|
7691
8478
|
};
|
|
7692
8479
|
function isUsablePricingRow(value) {
|
|
@@ -7696,7 +8483,7 @@ function isUsablePricingRow(value) {
|
|
|
7696
8483
|
}
|
|
7697
8484
|
|
|
7698
8485
|
// src/pricing/PricingRefreshScheduler.ts
|
|
7699
|
-
import { existsSync as
|
|
8486
|
+
import { existsSync as existsSync12, readFileSync as readFileSync13, renameSync as renameSync5, writeFileSync as writeFileSync10 } from "fs";
|
|
7700
8487
|
var EMPTY_STATE2 = {
|
|
7701
8488
|
lastAttemptAt: null,
|
|
7702
8489
|
lastSuccessAt: null,
|
|
@@ -7734,9 +8521,9 @@ var PricingRefreshScheduler = class {
|
|
|
7734
8521
|
this.timer = null;
|
|
7735
8522
|
}
|
|
7736
8523
|
getState() {
|
|
7737
|
-
if (!
|
|
8524
|
+
if (!existsSync12(this.statePath)) return { ...EMPTY_STATE2, sources: [] };
|
|
7738
8525
|
try {
|
|
7739
|
-
const value = JSON.parse(
|
|
8526
|
+
const value = JSON.parse(readFileSync13(this.statePath, "utf8"));
|
|
7740
8527
|
return {
|
|
7741
8528
|
lastAttemptAt: finiteOrNull(value.lastAttemptAt),
|
|
7742
8529
|
lastSuccessAt: finiteOrNull(value.lastSuccessAt),
|
|
@@ -7789,9 +8576,9 @@ var PricingRefreshScheduler = class {
|
|
|
7789
8576
|
}
|
|
7790
8577
|
writeState(state) {
|
|
7791
8578
|
const temporaryPath = `${this.statePath}.tmp`;
|
|
7792
|
-
|
|
8579
|
+
writeFileSync10(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
7793
8580
|
`, "utf8");
|
|
7794
|
-
|
|
8581
|
+
renameSync5(temporaryPath, this.statePath);
|
|
7795
8582
|
}
|
|
7796
8583
|
};
|
|
7797
8584
|
function finiteOrNull(value) {
|
|
@@ -7799,7 +8586,7 @@ function finiteOrNull(value) {
|
|
|
7799
8586
|
}
|
|
7800
8587
|
|
|
7801
8588
|
// src/ports/JsonVoucherDb.ts
|
|
7802
|
-
import { existsSync as
|
|
8589
|
+
import { existsSync as existsSync13, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
|
|
7803
8590
|
var JsonVoucherDb = class {
|
|
7804
8591
|
constructor(vouchersPath) {
|
|
7805
8592
|
this.vouchersPath = vouchersPath;
|
|
@@ -7877,21 +8664,21 @@ var JsonVoucherDb = class {
|
|
|
7877
8664
|
}
|
|
7878
8665
|
/** Read the voucher rows, tolerating a missing/corrupt file (→ empty list). */
|
|
7879
8666
|
readRows() {
|
|
7880
|
-
if (!
|
|
8667
|
+
if (!existsSync13(this.vouchersPath)) return [];
|
|
7881
8668
|
try {
|
|
7882
|
-
const parsed = JSON.parse(
|
|
8669
|
+
const parsed = JSON.parse(readFileSync14(this.vouchersPath, "utf8"));
|
|
7883
8670
|
return Array.isArray(parsed) ? parsed : [];
|
|
7884
8671
|
} catch {
|
|
7885
8672
|
return [];
|
|
7886
8673
|
}
|
|
7887
8674
|
}
|
|
7888
8675
|
writeRows(rows) {
|
|
7889
|
-
|
|
8676
|
+
writeFileSync11(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
7890
8677
|
}
|
|
7891
8678
|
};
|
|
7892
8679
|
|
|
7893
8680
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
7894
|
-
import { existsSync as
|
|
8681
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync4, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "fs";
|
|
7895
8682
|
import { dirname as dirname6 } from "path";
|
|
7896
8683
|
import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
7897
8684
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
@@ -7945,11 +8732,11 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
7945
8732
|
}
|
|
7946
8733
|
|
|
7947
8734
|
// src/ports/external-cli-credentials.ts
|
|
7948
|
-
import { existsSync as
|
|
8735
|
+
import { existsSync as existsSync14, readFileSync as readFileSync15 } from "fs";
|
|
7949
8736
|
import { homedir as homedir3 } from "os";
|
|
7950
|
-
import { join as
|
|
8737
|
+
import { join as join8 } from "path";
|
|
7951
8738
|
function externalStorePath(provider, home = homedir3()) {
|
|
7952
|
-
return provider === "claude" ?
|
|
8739
|
+
return provider === "claude" ? join8(home, ".claude", ".credentials.json") : join8(home, ".codex", "auth.json");
|
|
7953
8740
|
}
|
|
7954
8741
|
function decodeJwtExpiryMs(token) {
|
|
7955
8742
|
try {
|
|
@@ -7998,10 +8785,10 @@ function parseCodexTokensEnvelope(raw) {
|
|
|
7998
8785
|
}
|
|
7999
8786
|
function readExternalCliCredentials(provider, home = homedir3()) {
|
|
8000
8787
|
const path2 = externalStorePath(provider, home);
|
|
8001
|
-
if (!
|
|
8788
|
+
if (!existsSync14(path2)) return null;
|
|
8002
8789
|
let raw;
|
|
8003
8790
|
try {
|
|
8004
|
-
const parsed = JSON.parse(
|
|
8791
|
+
const parsed = JSON.parse(readFileSync15(path2, "utf8"));
|
|
8005
8792
|
raw = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
8006
8793
|
} catch {
|
|
8007
8794
|
return null;
|
|
@@ -8574,7 +9361,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
8574
9361
|
persist(config) {
|
|
8575
9362
|
mkdirSync4(dirname6(this.tokensPath), { recursive: true });
|
|
8576
9363
|
const encrypted = encryptTokens(config, this.box);
|
|
8577
|
-
|
|
9364
|
+
writeFileSync12(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
8578
9365
|
}
|
|
8579
9366
|
/**
|
|
8580
9367
|
* Read + parse `tokens.json`, tolerating a missing/corrupt file, then DECRYPT
|
|
@@ -8590,10 +9377,10 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
8590
9377
|
* `config.ts loadConfig`, which decrypts outside its parse try.
|
|
8591
9378
|
*/
|
|
8592
9379
|
readConfig() {
|
|
8593
|
-
if (!
|
|
9380
|
+
if (!existsSync15(this.tokensPath)) return { updatedAt: "" };
|
|
8594
9381
|
let parsed;
|
|
8595
9382
|
try {
|
|
8596
|
-
const raw = JSON.parse(
|
|
9383
|
+
const raw = JSON.parse(readFileSync16(this.tokensPath, "utf8"));
|
|
8597
9384
|
parsed = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
8598
9385
|
} catch {
|
|
8599
9386
|
parsed = null;
|
|
@@ -9113,39 +9900,21 @@ var AccountHealthSweeper = class {
|
|
|
9113
9900
|
};
|
|
9114
9901
|
|
|
9115
9902
|
// src/audit/AuditPruneSweeper.ts
|
|
9116
|
-
import { existsSync as
|
|
9117
|
-
import { join as
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
var AUDIT_FILE_RE = /^audit-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
9121
|
-
var pad22 = (n) => String(n).padStart(2, "0");
|
|
9122
|
-
function auditFileName(ts) {
|
|
9123
|
-
const d = new Date(ts);
|
|
9124
|
-
return `audit-${d.getFullYear()}-${pad22(d.getMonth() + 1)}-${pad22(d.getDate())}.jsonl`;
|
|
9125
|
-
}
|
|
9126
|
-
function auditFileDateMs(fileName) {
|
|
9127
|
-
const m = AUDIT_FILE_RE.exec(fileName);
|
|
9128
|
-
if (!m) return null;
|
|
9129
|
-
const year = Number(m[1]);
|
|
9130
|
-
const month = Number(m[2]);
|
|
9131
|
-
const day = Number(m[3]);
|
|
9132
|
-
const d = new Date(year, month - 1, day);
|
|
9133
|
-
if (d.getFullYear() !== year || d.getMonth() !== month - 1 || d.getDate() !== day) {
|
|
9134
|
-
return null;
|
|
9135
|
-
}
|
|
9136
|
-
return d.getTime();
|
|
9137
|
-
}
|
|
9903
|
+
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync17, readdirSync as readdirSync5, rmSync as rmSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
9904
|
+
import { join as join10 } from "path";
|
|
9905
|
+
import { pipeline } from "stream/promises";
|
|
9906
|
+
import { createGzip } from "zlib";
|
|
9138
9907
|
|
|
9139
9908
|
// src/audit/auditStats.ts
|
|
9140
9909
|
import {
|
|
9141
9910
|
createReadStream,
|
|
9142
|
-
existsSync as
|
|
9143
|
-
readFileSync as
|
|
9144
|
-
readdirSync,
|
|
9145
|
-
statSync as
|
|
9146
|
-
writeFileSync as
|
|
9911
|
+
existsSync as existsSync16,
|
|
9912
|
+
readFileSync as readFileSync17,
|
|
9913
|
+
readdirSync as readdirSync4,
|
|
9914
|
+
statSync as statSync5,
|
|
9915
|
+
writeFileSync as writeFileSync13
|
|
9147
9916
|
} from "fs";
|
|
9148
|
-
import { basename, dirname as dirname7, join as
|
|
9917
|
+
import { basename, dirname as dirname7, join as join9 } from "path";
|
|
9149
9918
|
var SIDECAR_VERSION = 1;
|
|
9150
9919
|
var META_PREFIX_BYTES = 64 * 1024;
|
|
9151
9920
|
var READ_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
@@ -9153,9 +9922,9 @@ function auditStatsFileName(auditFile) {
|
|
|
9153
9922
|
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
9154
9923
|
}
|
|
9155
9924
|
function readPersisted(path2) {
|
|
9156
|
-
if (!
|
|
9925
|
+
if (!existsSync16(path2)) return null;
|
|
9157
9926
|
try {
|
|
9158
|
-
const value = JSON.parse(
|
|
9927
|
+
const value = JSON.parse(readFileSync17(path2, "utf8"));
|
|
9159
9928
|
if (value.version !== SIDECAR_VERSION || !Number.isSafeInteger(value.auditBytes) || (value.auditBytes ?? -1) < 0 || !Number.isSafeInteger(value.requestCount) || (value.requestCount ?? -1) < 0 || !Number.isSafeInteger(value.errorCount) || (value.errorCount ?? -1) < 0 || (value.errorCount ?? 0) > (value.requestCount ?? -1) || typeof value.complete !== "boolean" || value.minTs !== null && !Number.isFinite(value.minTs) || value.maxTs !== null && !Number.isFinite(value.maxTs)) {
|
|
9160
9929
|
return null;
|
|
9161
9930
|
}
|
|
@@ -9165,7 +9934,7 @@ function readPersisted(path2) {
|
|
|
9165
9934
|
}
|
|
9166
9935
|
}
|
|
9167
9936
|
function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
|
|
9168
|
-
const statsPath =
|
|
9937
|
+
const statsPath = join9(dirname7(auditPath), auditStatsFileName(basename(auditPath)));
|
|
9169
9938
|
const previous = auditBytesBefore === 0 ? {
|
|
9170
9939
|
version: SIDECAR_VERSION,
|
|
9171
9940
|
auditBytes: 0,
|
|
@@ -9185,13 +9954,13 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
|
|
|
9185
9954
|
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
9186
9955
|
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
9187
9956
|
};
|
|
9188
|
-
|
|
9957
|
+
writeFileSync13(statsPath, JSON.stringify(next), "utf8");
|
|
9189
9958
|
}
|
|
9190
9959
|
function queryCovers(stats, from, to) {
|
|
9191
9960
|
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
9192
9961
|
}
|
|
9193
|
-
function fileOverlaps(
|
|
9194
|
-
const start = auditFileDateMs(
|
|
9962
|
+
function fileOverlaps(name, from, to) {
|
|
9963
|
+
const start = auditFileDateMs(name);
|
|
9195
9964
|
if (start === null) return false;
|
|
9196
9965
|
const date = new Date(start);
|
|
9197
9966
|
const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
|
|
@@ -9296,21 +10065,27 @@ function mergePersistedStats(previous, appended) {
|
|
|
9296
10065
|
};
|
|
9297
10066
|
}
|
|
9298
10067
|
async function readAuditStats(auditDir, query2 = {}) {
|
|
9299
|
-
if (!
|
|
10068
|
+
if (!existsSync16(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
9300
10069
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
9301
10070
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
9302
|
-
let
|
|
10071
|
+
let sources;
|
|
9303
10072
|
try {
|
|
9304
|
-
|
|
10073
|
+
sources = readdirSync4(auditDir).filter((name) => fileOverlaps(name, from, to)).sort().map(
|
|
10074
|
+
(name) => AUDIT_DAY_DIR_RE.test(name) ? {
|
|
10075
|
+
auditPath: join9(auditDir, name, AUDIT_META_FILE),
|
|
10076
|
+
statsPath: join9(auditDir, name, auditStatsFileName(AUDIT_META_FILE))
|
|
10077
|
+
} : {
|
|
10078
|
+
auditPath: join9(auditDir, name),
|
|
10079
|
+
statsPath: join9(auditDir, auditStatsFileName(name))
|
|
10080
|
+
}
|
|
10081
|
+
).filter((source) => existsSync16(source.auditPath));
|
|
9305
10082
|
} catch {
|
|
9306
10083
|
return { requestCount: 0, errorCount: 0, complete: false };
|
|
9307
10084
|
}
|
|
9308
10085
|
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
9309
|
-
for (const
|
|
9310
|
-
const auditPath = join6(auditDir, file);
|
|
10086
|
+
for (const { auditPath, statsPath } of sources) {
|
|
9311
10087
|
try {
|
|
9312
|
-
const auditBytes =
|
|
9313
|
-
const statsPath = join6(auditDir, auditStatsFileName(file));
|
|
10088
|
+
const auditBytes = statSync5(auditPath).size;
|
|
9314
10089
|
const persisted = readPersisted(statsPath);
|
|
9315
10090
|
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
9316
10091
|
total.requestCount += persisted.requestCount;
|
|
@@ -9329,7 +10104,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
9329
10104
|
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
9330
10105
|
total.complete = total.complete && scanned.filtered.complete;
|
|
9331
10106
|
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
9332
|
-
if (current.complete)
|
|
10107
|
+
if (current.complete) writeFileSync13(statsPath, JSON.stringify(current), "utf8");
|
|
9333
10108
|
} catch {
|
|
9334
10109
|
total.complete = false;
|
|
9335
10110
|
}
|
|
@@ -9340,6 +10115,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
9340
10115
|
// src/audit/AuditPruneSweeper.ts
|
|
9341
10116
|
var DAY_MS = 24 * 60 * 6e4;
|
|
9342
10117
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
10118
|
+
var ARCHIVE_BATCH = 64;
|
|
9343
10119
|
var AuditPruneSweeper = class {
|
|
9344
10120
|
constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
|
|
9345
10121
|
this.auditDir = auditDir;
|
|
@@ -9355,6 +10131,7 @@ var AuditPruneSweeper = class {
|
|
|
9355
10131
|
now;
|
|
9356
10132
|
timer = null;
|
|
9357
10133
|
sweeping = false;
|
|
10134
|
+
archiving = false;
|
|
9358
10135
|
/** Whether pruning is active (audit enabled). */
|
|
9359
10136
|
get enabled() {
|
|
9360
10137
|
return this.config.enabled;
|
|
@@ -9364,13 +10141,13 @@ var AuditPruneSweeper = class {
|
|
|
9364
10141
|
this.config = config;
|
|
9365
10142
|
}
|
|
9366
10143
|
/**
|
|
9367
|
-
* Arm the
|
|
9368
|
-
*
|
|
10144
|
+
* Arm the interval AND run one pass immediately (boot cleanup). No-op when
|
|
10145
|
+
* audit is disabled (zero regression). Idempotent.
|
|
9369
10146
|
*/
|
|
9370
10147
|
start() {
|
|
9371
10148
|
if (this.timer || !this.config.enabled) return;
|
|
9372
|
-
void this.
|
|
9373
|
-
this.timer = setInterval(() => void this.
|
|
10149
|
+
void this.runOnce();
|
|
10150
|
+
this.timer = setInterval(() => void this.runOnce(), this.intervalMs);
|
|
9374
10151
|
this.timer.unref?.();
|
|
9375
10152
|
}
|
|
9376
10153
|
/** Clear the interval (daemon shutdown / test teardown). Idempotent. */
|
|
@@ -9380,31 +10157,43 @@ var AuditPruneSweeper = class {
|
|
|
9380
10157
|
this.timer = null;
|
|
9381
10158
|
}
|
|
9382
10159
|
}
|
|
10160
|
+
/** Prune first, then archive — never spend CPU compressing a day about to go. */
|
|
10161
|
+
async runOnce() {
|
|
10162
|
+
await this.sweep();
|
|
10163
|
+
await this.archive();
|
|
10164
|
+
}
|
|
10165
|
+
/** The LOCAL-midnight epoch ms of the current day. */
|
|
10166
|
+
todayMidnight() {
|
|
10167
|
+
const today = new Date(this.now());
|
|
10168
|
+
return new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
|
|
10169
|
+
}
|
|
9383
10170
|
/**
|
|
9384
|
-
* One prune:
|
|
9385
|
-
*
|
|
9386
|
-
*
|
|
10171
|
+
* One prune: remove every audit day strictly OLDER than the retention cutoff
|
|
10172
|
+
* (`now - retentionDays` days, at local-midnight granularity). Exposed for
|
|
10173
|
+
* tests; never throws. Returns the number of days removed.
|
|
9387
10174
|
*/
|
|
9388
10175
|
async sweep() {
|
|
9389
10176
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
9390
10177
|
this.sweeping = true;
|
|
9391
10178
|
try {
|
|
9392
|
-
if (!
|
|
9393
|
-
const
|
|
9394
|
-
const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
|
|
9395
|
-
const cutoff = todayMidnight - (this.config.retentionDays - 1) * DAY_MS;
|
|
10179
|
+
if (!existsSync17(this.auditDir)) return 0;
|
|
10180
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS;
|
|
9396
10181
|
let removed = 0;
|
|
9397
|
-
for (const
|
|
9398
|
-
const dateMs = auditFileDateMs(
|
|
10182
|
+
for (const name of readdirSync5(this.auditDir)) {
|
|
10183
|
+
const dateMs = auditFileDateMs(name);
|
|
9399
10184
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
9400
10185
|
try {
|
|
9401
|
-
|
|
10186
|
+
if (isAuditDayDir(name)) {
|
|
10187
|
+
rmSync4(join10(this.auditDir, name), { recursive: true, force: true });
|
|
10188
|
+
} else {
|
|
10189
|
+
unlinkSync4(join10(this.auditDir, name));
|
|
10190
|
+
const statsPath = join10(this.auditDir, auditStatsFileName(name));
|
|
10191
|
+
if (existsSync17(statsPath)) unlinkSync4(statsPath);
|
|
10192
|
+
}
|
|
9402
10193
|
removed += 1;
|
|
9403
|
-
const statsPath = join7(this.auditDir, auditStatsFileName(file));
|
|
9404
|
-
if (existsSync15(statsPath)) unlinkSync3(statsPath);
|
|
9405
10194
|
} catch (error) {
|
|
9406
|
-
this.logger.warn("[AuditPruneSweeper] failed to
|
|
9407
|
-
|
|
10195
|
+
this.logger.warn("[AuditPruneSweeper] failed to remove expired audit day", {
|
|
10196
|
+
name,
|
|
9408
10197
|
error: error instanceof Error ? error.message : String(error)
|
|
9409
10198
|
});
|
|
9410
10199
|
}
|
|
@@ -9420,59 +10209,158 @@ var AuditPruneSweeper = class {
|
|
|
9420
10209
|
this.sweeping = false;
|
|
9421
10210
|
}
|
|
9422
10211
|
}
|
|
10212
|
+
/**
|
|
10213
|
+
* Gzip the body shards of every CLOSED day (anything before today). Today is
|
|
10214
|
+
* deliberately left as plain text so it stays greppable while it is the day you
|
|
10215
|
+
* are debugging. Exposed for tests; never throws. Returns shards compressed.
|
|
10216
|
+
*/
|
|
10217
|
+
async archive() {
|
|
10218
|
+
if (!this.config.enabled || this.archiving) return 0;
|
|
10219
|
+
this.archiving = true;
|
|
10220
|
+
try {
|
|
10221
|
+
if (!existsSync17(this.auditDir)) return 0;
|
|
10222
|
+
const today = this.todayMidnight();
|
|
10223
|
+
let compressed = 0;
|
|
10224
|
+
for (const name of readdirSync5(this.auditDir)) {
|
|
10225
|
+
if (compressed >= ARCHIVE_BATCH) break;
|
|
10226
|
+
const dateMs = auditFileDateMs(name);
|
|
10227
|
+
if (dateMs === null || dateMs >= today || !isAuditDayDir(name)) continue;
|
|
10228
|
+
const dayPath = join10(this.auditDir, name);
|
|
10229
|
+
try {
|
|
10230
|
+
const compaction = compactAuditDay(dayPath);
|
|
10231
|
+
if (compaction.shards > 0) {
|
|
10232
|
+
this.logger.debug("audit cross-session compaction complete", {
|
|
10233
|
+
day: name,
|
|
10234
|
+
shards: compaction.shards,
|
|
10235
|
+
anchors: compaction.anchors,
|
|
10236
|
+
savedBytes: compaction.savedBytes
|
|
10237
|
+
});
|
|
10238
|
+
}
|
|
10239
|
+
} catch (error) {
|
|
10240
|
+
this.logger.warn("[AuditPruneSweeper] cross-session compaction failed", {
|
|
10241
|
+
day: name,
|
|
10242
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10243
|
+
});
|
|
10244
|
+
}
|
|
10245
|
+
compressed += await this.archiveDay(
|
|
10246
|
+
join10(dayPath, AUDIT_BODIES_DIR),
|
|
10247
|
+
ARCHIVE_BATCH - compressed
|
|
10248
|
+
);
|
|
10249
|
+
}
|
|
10250
|
+
if (compressed > 0) this.logger.debug("audit archive complete", { compressed });
|
|
10251
|
+
return compressed;
|
|
10252
|
+
} catch (error) {
|
|
10253
|
+
this.logger.warn("audit archive pass failed", {
|
|
10254
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10255
|
+
});
|
|
10256
|
+
return 0;
|
|
10257
|
+
} finally {
|
|
10258
|
+
this.archiving = false;
|
|
10259
|
+
}
|
|
10260
|
+
}
|
|
10261
|
+
/** Gzip up to `budget` plain shards in one day's `bodies/` directory. */
|
|
10262
|
+
async archiveDay(bodiesPath, budget) {
|
|
10263
|
+
let shards;
|
|
10264
|
+
try {
|
|
10265
|
+
shards = readdirSync5(bodiesPath).filter((file) => file.endsWith(".jsonl"));
|
|
10266
|
+
} catch {
|
|
10267
|
+
return 0;
|
|
10268
|
+
}
|
|
10269
|
+
let compressed = 0;
|
|
10270
|
+
for (const shard of shards) {
|
|
10271
|
+
if (compressed >= budget) break;
|
|
10272
|
+
const source = join10(bodiesPath, shard);
|
|
10273
|
+
const target = `${source}.gz`;
|
|
10274
|
+
try {
|
|
10275
|
+
if (existsSync17(target)) {
|
|
10276
|
+
unlinkSync4(source);
|
|
10277
|
+
continue;
|
|
10278
|
+
}
|
|
10279
|
+
await pipeline(createReadStream2(source), createGzip(), createWriteStream2(target));
|
|
10280
|
+
unlinkSync4(source);
|
|
10281
|
+
compressed += 1;
|
|
10282
|
+
} catch (error) {
|
|
10283
|
+
try {
|
|
10284
|
+
if (existsSync17(target)) unlinkSync4(target);
|
|
10285
|
+
} catch {
|
|
10286
|
+
}
|
|
10287
|
+
this.logger.warn("[AuditPruneSweeper] failed to archive audit body shard", {
|
|
10288
|
+
shard,
|
|
10289
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10290
|
+
});
|
|
10291
|
+
}
|
|
10292
|
+
}
|
|
10293
|
+
return compressed;
|
|
10294
|
+
}
|
|
9423
10295
|
};
|
|
9424
10296
|
|
|
9425
10297
|
// src/audit/auditReader.ts
|
|
9426
|
-
import { existsSync as
|
|
9427
|
-
import { join as
|
|
10298
|
+
import { existsSync as existsSync18, readdirSync as readdirSync6 } from "fs";
|
|
10299
|
+
import { join as join11 } from "path";
|
|
9428
10300
|
var DEFAULT_LIMIT = 200;
|
|
9429
10301
|
var MAX_LIMIT = 2e3;
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
let
|
|
10302
|
+
var OVERSCAN = 256;
|
|
10303
|
+
function daySources(auditDir) {
|
|
10304
|
+
let names;
|
|
9433
10305
|
try {
|
|
9434
|
-
|
|
10306
|
+
names = readdirSync6(auditDir);
|
|
9435
10307
|
} catch {
|
|
9436
10308
|
return [];
|
|
9437
10309
|
}
|
|
10310
|
+
const sources = [];
|
|
10311
|
+
for (const name of names) {
|
|
10312
|
+
const dateMs = auditFileDateMs(name);
|
|
10313
|
+
if (dateMs === null) continue;
|
|
10314
|
+
if (AUDIT_DAY_DIR_RE.test(name)) {
|
|
10315
|
+
const path2 = join11(auditDir, name, AUDIT_META_FILE);
|
|
10316
|
+
if (existsSync18(path2)) sources.push({ path: path2, dateMs });
|
|
10317
|
+
} else if (AUDIT_FILE_RE.test(name)) {
|
|
10318
|
+
sources.push({ path: join11(auditDir, name), dateMs });
|
|
10319
|
+
}
|
|
10320
|
+
}
|
|
10321
|
+
return sources.sort((a, b) => b.dateMs - a.dateMs);
|
|
10322
|
+
}
|
|
10323
|
+
function isAuditRecord(value) {
|
|
10324
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
10325
|
+
const r = value;
|
|
10326
|
+
return typeof r["id"] === "string" && typeof r["ts"] === "number" && typeof r["method"] === "string" && typeof r["path"] === "string" && typeof r["status"] === "number";
|
|
10327
|
+
}
|
|
10328
|
+
function toMetaRecord(record) {
|
|
10329
|
+
if (record.requestBody === void 0 && record.responseBody === void 0) return record;
|
|
10330
|
+
const { requestBody: _req, responseBody: _res, ...meta } = record;
|
|
10331
|
+
return { ...meta, hasBody: true };
|
|
10332
|
+
}
|
|
10333
|
+
function readAuditRecords(auditDir, query2 = {}) {
|
|
10334
|
+
if (!existsSync18(auditDir)) return [];
|
|
9438
10335
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
9439
10336
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
9440
10337
|
const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(query2.limit ?? DEFAULT_LIMIT)));
|
|
9441
10338
|
const matched = [];
|
|
9442
|
-
for (const
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
} catch {
|
|
9447
|
-
continue;
|
|
9448
|
-
}
|
|
9449
|
-
for (const line of raw.split("\n")) {
|
|
9450
|
-
const trimmed = line.trim();
|
|
9451
|
-
if (!trimmed) continue;
|
|
9452
|
-
let rec;
|
|
10339
|
+
for (const source of daySources(auditDir)) {
|
|
10340
|
+
const before = matched.length;
|
|
10341
|
+
forEachLineFromTail(source.path, (line) => {
|
|
10342
|
+
let parsed;
|
|
9453
10343
|
try {
|
|
9454
|
-
|
|
10344
|
+
parsed = JSON.parse(line);
|
|
9455
10345
|
} catch {
|
|
9456
|
-
|
|
10346
|
+
return false;
|
|
9457
10347
|
}
|
|
9458
|
-
if (!isAuditRecord(
|
|
9459
|
-
if (query2.keyId !== void 0 &&
|
|
9460
|
-
if (
|
|
9461
|
-
|
|
9462
|
-
|
|
10348
|
+
if (!isAuditRecord(parsed)) return false;
|
|
10349
|
+
if (query2.keyId !== void 0 && parsed.keyId !== query2.keyId) return false;
|
|
10350
|
+
if (query2.sessionKey !== void 0 && parsed.sessionKey !== query2.sessionKey) return false;
|
|
10351
|
+
if (parsed.ts < from || parsed.ts > to) return false;
|
|
10352
|
+
matched.push(toMetaRecord(parsed));
|
|
10353
|
+
return matched.length - before >= limit + OVERSCAN;
|
|
10354
|
+
});
|
|
10355
|
+
if (matched.length >= limit) break;
|
|
9463
10356
|
}
|
|
9464
10357
|
matched.sort((a, b) => b.ts - a.ts);
|
|
9465
10358
|
return matched.slice(0, limit);
|
|
9466
10359
|
}
|
|
9467
|
-
function isAuditRecord(value) {
|
|
9468
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
9469
|
-
const r = value;
|
|
9470
|
-
return typeof r["id"] === "string" && typeof r["ts"] === "number" && typeof r["method"] === "string" && typeof r["path"] === "string" && typeof r["status"] === "number";
|
|
9471
|
-
}
|
|
9472
10360
|
|
|
9473
10361
|
// src/audit/AuditWriter.ts
|
|
9474
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
9475
|
-
import { join as
|
|
10362
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync19, mkdirSync as mkdirSync5, statSync as statSync6 } from "fs";
|
|
10363
|
+
import { join as join12 } from "path";
|
|
9476
10364
|
var AuditWriter = class {
|
|
9477
10365
|
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
9478
10366
|
this.auditDir = auditDir;
|
|
@@ -9482,10 +10370,13 @@ var AuditWriter = class {
|
|
|
9482
10370
|
auditDir;
|
|
9483
10371
|
logger;
|
|
9484
10372
|
defer;
|
|
9485
|
-
|
|
10373
|
+
/** Day directories already created this process (avoids an mkdir per record). */
|
|
10374
|
+
ensuredDirs = /* @__PURE__ */ new Set();
|
|
10375
|
+
/** Per-session encoding bases. Memory-only; a miss simply writes a full snapshot. */
|
|
10376
|
+
bases = new SessionBaseCache();
|
|
9486
10377
|
/**
|
|
9487
|
-
* Enqueue one record
|
|
9488
|
-
*
|
|
10378
|
+
* Enqueue one record. Returns IMMEDIATELY (fire-and-forget); every fs write and
|
|
10379
|
+
* the delta encoding happen on the deferred tick. A failure is logged, never thrown.
|
|
9489
10380
|
*/
|
|
9490
10381
|
record(record) {
|
|
9491
10382
|
this.defer(() => {
|
|
@@ -9498,25 +10389,41 @@ var AuditWriter = class {
|
|
|
9498
10389
|
}
|
|
9499
10390
|
});
|
|
9500
10391
|
}
|
|
10392
|
+
/** Drop all retained encoding bases (config reload / shutdown / test teardown). */
|
|
10393
|
+
reset() {
|
|
10394
|
+
this.bases.clear();
|
|
10395
|
+
this.ensuredDirs.clear();
|
|
10396
|
+
}
|
|
9501
10397
|
/**
|
|
9502
|
-
* Append synchronously — the awaitable form tests use to assert
|
|
9503
|
-
*
|
|
9504
|
-
* store's lazy file creation).
|
|
10398
|
+
* Append synchronously — the awaitable form tests use to assert a line landed.
|
|
10399
|
+
* Writes the metadata line first (canonical), then the body shard.
|
|
9505
10400
|
*/
|
|
9506
10401
|
appendNow(record) {
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
10402
|
+
const dayDir = auditDayDirName(record.ts);
|
|
10403
|
+
const dayPath = this.ensureDir(join12(this.auditDir, dayDir));
|
|
10404
|
+
this.appendMeta(dayPath, record);
|
|
10405
|
+
this.appendBody(dayPath, dayDir, record);
|
|
10406
|
+
}
|
|
10407
|
+
/** Create a directory once per process and remember it. */
|
|
10408
|
+
ensureDir(path2) {
|
|
10409
|
+
if (!this.ensuredDirs.has(path2)) {
|
|
10410
|
+
mkdirSync5(path2, { recursive: true });
|
|
10411
|
+
this.ensuredDirs.add(path2);
|
|
10412
|
+
}
|
|
10413
|
+
return path2;
|
|
10414
|
+
}
|
|
10415
|
+
/** Write the body-free metadata line + refresh the exact-count sidecar. */
|
|
10416
|
+
appendMeta(dayPath, record) {
|
|
10417
|
+
const { requestBody: _req, responseBody: _res, ...meta } = record;
|
|
10418
|
+
const file = join12(dayPath, AUDIT_META_FILE);
|
|
10419
|
+
const line = JSON.stringify(meta) + "\n";
|
|
10420
|
+
const bytesBefore = existsSync19(file) ? statSync6(file).size : 0;
|
|
9514
10421
|
appendFileSync2(file, line, "utf8");
|
|
9515
10422
|
try {
|
|
9516
10423
|
updateAuditStatsAfterAppend(
|
|
9517
10424
|
file,
|
|
9518
|
-
|
|
9519
|
-
|
|
10425
|
+
bytesBefore,
|
|
10426
|
+
bytesBefore + Buffer.byteLength(line, "utf8"),
|
|
9520
10427
|
record
|
|
9521
10428
|
);
|
|
9522
10429
|
} catch (error) {
|
|
@@ -9525,12 +10432,39 @@ var AuditWriter = class {
|
|
|
9525
10432
|
});
|
|
9526
10433
|
}
|
|
9527
10434
|
}
|
|
10435
|
+
/**
|
|
10436
|
+
* Write the delta-encoded body shard for one record. A no-op when nothing was
|
|
10437
|
+
* captured or when the session key is missing/unsafe — in which case the body
|
|
10438
|
+
* is dropped rather than written to an unvalidated path.
|
|
10439
|
+
*/
|
|
10440
|
+
appendBody(dayPath, dayDir, record) {
|
|
10441
|
+
if (record.requestBody === void 0 && record.responseBody === void 0) return;
|
|
10442
|
+
const sessionKey = record.sessionKey;
|
|
10443
|
+
if (!isSafeSessionKey(sessionKey)) {
|
|
10444
|
+
this.logger.warn("[AuditWriter] dropping audit body with no usable session key", {
|
|
10445
|
+
id: record.id
|
|
10446
|
+
});
|
|
10447
|
+
return;
|
|
10448
|
+
}
|
|
10449
|
+
try {
|
|
10450
|
+
const line = encodeBodyEntry(record, sessionKey, dayDir, this.bases);
|
|
10451
|
+
if (line === null) return;
|
|
10452
|
+
const bodiesPath = this.ensureDir(join12(dayPath, AUDIT_BODIES_DIR));
|
|
10453
|
+
appendFileSync2(join12(bodiesPath, auditBodyFileName(sessionKey)), line + "\n", "utf8");
|
|
10454
|
+
} catch (error) {
|
|
10455
|
+
this.bases.forget(sessionKey);
|
|
10456
|
+
this.logger.warn("[AuditWriter] failed to append audit body shard", {
|
|
10457
|
+
id: record.id,
|
|
10458
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10459
|
+
});
|
|
10460
|
+
}
|
|
10461
|
+
}
|
|
9528
10462
|
};
|
|
9529
10463
|
|
|
9530
10464
|
// src/billing/BillingPublisher.ts
|
|
9531
10465
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync6 } from "fs";
|
|
9532
10466
|
import { createHmac } from "crypto";
|
|
9533
|
-
import { join as
|
|
10467
|
+
import { join as join13 } from "path";
|
|
9534
10468
|
import { fetchUpstream as fetchUpstream5 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
9535
10469
|
|
|
9536
10470
|
// src/billing/billingFiles.ts
|
|
@@ -9601,7 +10535,7 @@ var BillingPublisher = class {
|
|
|
9601
10535
|
*/
|
|
9602
10536
|
appendNow(event) {
|
|
9603
10537
|
this.ensureDir();
|
|
9604
|
-
const file =
|
|
10538
|
+
const file = join13(this.billingDir, billingFileName(event.ts));
|
|
9605
10539
|
appendFileSync3(file, JSON.stringify(event) + "\n", "utf8");
|
|
9606
10540
|
}
|
|
9607
10541
|
/**
|
|
@@ -9651,7 +10585,7 @@ var BillingPublisher = class {
|
|
|
9651
10585
|
markDelivered(event) {
|
|
9652
10586
|
try {
|
|
9653
10587
|
this.ensureDir();
|
|
9654
|
-
const file =
|
|
10588
|
+
const file = join13(this.billingDir, deliveredFileName(event.ts));
|
|
9655
10589
|
appendFileSync3(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
|
|
9656
10590
|
} catch (error) {
|
|
9657
10591
|
this.logger.warn("[BillingPublisher] failed to append delivery marker", {
|
|
@@ -9667,14 +10601,14 @@ var BillingPublisher = class {
|
|
|
9667
10601
|
};
|
|
9668
10602
|
|
|
9669
10603
|
// src/billing/billingReader.ts
|
|
9670
|
-
import { existsSync as
|
|
9671
|
-
import { join as
|
|
10604
|
+
import { existsSync as existsSync20, readdirSync as readdirSync7, readFileSync as readFileSync18 } from "fs";
|
|
10605
|
+
import { join as join14 } from "path";
|
|
9672
10606
|
function readBillingLedger(billingDir) {
|
|
9673
10607
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
9674
|
-
if (!
|
|
10608
|
+
if (!existsSync20(billingDir)) return view;
|
|
9675
10609
|
let files;
|
|
9676
10610
|
try {
|
|
9677
|
-
files =
|
|
10611
|
+
files = readdirSync7(billingDir);
|
|
9678
10612
|
} catch {
|
|
9679
10613
|
return view;
|
|
9680
10614
|
}
|
|
@@ -9705,7 +10639,7 @@ function readBillingStatus(billingDir) {
|
|
|
9705
10639
|
function parseLines(dir, file) {
|
|
9706
10640
|
let raw;
|
|
9707
10641
|
try {
|
|
9708
|
-
raw =
|
|
10642
|
+
raw = readFileSync18(join14(dir, file), "utf8");
|
|
9709
10643
|
} catch {
|
|
9710
10644
|
return [];
|
|
9711
10645
|
}
|
|
@@ -10209,8 +11143,10 @@ function buildDaemon(config, paths) {
|
|
|
10209
11143
|
async (providerId, model) => await pricingEngine.getEntry(providerId, model) !== null
|
|
10210
11144
|
);
|
|
10211
11145
|
const keySpendTracker = new KeySpendTracker(usageEventStore);
|
|
11146
|
+
const usageThroughput = getSharedUsageThroughputTracker2();
|
|
10212
11147
|
const usageRecorder = new UsageRecorder(usageEventStore, pricingEngine, logger, {
|
|
10213
|
-
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
|
|
11148
|
+
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at),
|
|
11149
|
+
onEvent: (row, at) => usageThroughput.record(row, at)
|
|
10214
11150
|
});
|
|
10215
11151
|
const providerProxy = getProviderProxy({ llmConfig, apiKeyPool, usageRecorder });
|
|
10216
11152
|
const routeLeaseManager = new RouteLeaseManager(
|
|
@@ -10359,6 +11295,11 @@ function buildDaemon(config, paths) {
|
|
|
10359
11295
|
// NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
|
|
10360
11296
|
auditReader: (query2) => readAuditRecords(auditDir, query2),
|
|
10361
11297
|
auditStatsReader: (query2) => readAuditStats(auditDir, query2),
|
|
11298
|
+
// audit-store-sharding: bodies live in per-session shards, so opening ONE
|
|
11299
|
+
// record's payload is a separate authed call that replays its delta chain.
|
|
11300
|
+
auditBodyReader: (query2) => readAuditBody(auditDir, query2),
|
|
11301
|
+
// audit-store-sharding D8: the manual counterpart to the daily pass.
|
|
11302
|
+
auditCompactor: () => compactAllClosedAuditDays(auditDir),
|
|
10362
11303
|
// billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
|
|
10363
11304
|
// secret-free total/delivered/pending counts of the durable ledger.
|
|
10364
11305
|
billingStatusReader: () => readBillingStatus(billingDir)
|
|
@@ -10417,7 +11358,7 @@ function buildDaemon(config, paths) {
|
|
|
10417
11358
|
}
|
|
10418
11359
|
function isTokensStoreReadable(tokensPath) {
|
|
10419
11360
|
try {
|
|
10420
|
-
if (!
|
|
11361
|
+
if (!existsSync21(tokensPath)) return true;
|
|
10421
11362
|
accessSync(tokensPath, fsConstants.R_OK);
|
|
10422
11363
|
return true;
|
|
10423
11364
|
} catch {
|
|
@@ -10465,8 +11406,8 @@ function buildCliSpawnPlan(opts) {
|
|
|
10465
11406
|
function resolveInPathDefault(candidate) {
|
|
10466
11407
|
const segments = (process.env["PATH"] ?? "").split(delimiter2).filter(Boolean);
|
|
10467
11408
|
for (const seg of segments) {
|
|
10468
|
-
const full =
|
|
10469
|
-
if (
|
|
11409
|
+
const full = join15(seg, candidate);
|
|
11410
|
+
if (existsSync22(full)) return full;
|
|
10470
11411
|
}
|
|
10471
11412
|
return null;
|
|
10472
11413
|
}
|
|
@@ -10474,7 +11415,7 @@ async function runLaunch(argv, deps) {
|
|
|
10474
11415
|
const sep = argv.indexOf("--");
|
|
10475
11416
|
const own = sep === -1 ? argv : argv.slice(0, sep);
|
|
10476
11417
|
const passthrough = sep === -1 ? [] : argv.slice(sep + 1);
|
|
10477
|
-
const { values, positionals } =
|
|
11418
|
+
const { values, positionals } = parseArgs5({
|
|
10478
11419
|
args: own,
|
|
10479
11420
|
options: {
|
|
10480
11421
|
provider: { type: "string", short: "p" },
|
|
@@ -10644,12 +11585,12 @@ function spawnCliInherit(plan) {
|
|
|
10644
11585
|
// src/commands/login.ts
|
|
10645
11586
|
import { spawn as spawn3 } from "child_process";
|
|
10646
11587
|
import { createInterface } from "readline";
|
|
10647
|
-
import { parseArgs as
|
|
11588
|
+
import { parseArgs as parseArgs6 } from "util";
|
|
10648
11589
|
import { fetchUpstream as fetchUpstream8, setUpstreamProxyResolver as setUpstreamProxyResolver2 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
10649
11590
|
import { claudeOAuth as claudeOAuth3, codexOAuth as codexOAuth3, geminiOAuth as geminiOAuth3 } from "@omnicross/subscriptions";
|
|
10650
11591
|
var PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
10651
11592
|
async function runLogin(argv, deps) {
|
|
10652
|
-
const { values, positionals } =
|
|
11593
|
+
const { values, positionals } = parseArgs6({
|
|
10653
11594
|
args: argv,
|
|
10654
11595
|
options: {
|
|
10655
11596
|
config: { type: "string", short: "c" },
|
|
@@ -10817,9 +11758,9 @@ function promptPaste(prompt) {
|
|
|
10817
11758
|
|
|
10818
11759
|
// src/commands/providers.ts
|
|
10819
11760
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
10820
|
-
import { parseArgs as
|
|
11761
|
+
import { parseArgs as parseArgs7 } from "util";
|
|
10821
11762
|
async function runProviders(argv) {
|
|
10822
|
-
const { values, positionals } =
|
|
11763
|
+
const { values, positionals } = parseArgs7({
|
|
10823
11764
|
args: argv,
|
|
10824
11765
|
options: {
|
|
10825
11766
|
config: { type: "string", short: "c" },
|
|
@@ -10966,10 +11907,10 @@ function providersRmKey(configPath, providerId, keyId) {
|
|
|
10966
11907
|
}
|
|
10967
11908
|
|
|
10968
11909
|
// src/commands/secrets.ts
|
|
10969
|
-
import { existsSync as
|
|
10970
|
-
import { parseArgs as
|
|
11910
|
+
import { existsSync as existsSync23, readFileSync as readFileSync19, writeFileSync as writeFileSync14 } from "fs";
|
|
11911
|
+
import { parseArgs as parseArgs8 } from "util";
|
|
10971
11912
|
async function runSecrets(argv) {
|
|
10972
|
-
const { values, positionals } =
|
|
11913
|
+
const { values, positionals } = parseArgs8({
|
|
10973
11914
|
args: argv,
|
|
10974
11915
|
options: {
|
|
10975
11916
|
config: { type: "string", short: "c" },
|
|
@@ -11039,12 +11980,12 @@ function secretsStatus(args) {
|
|
|
11039
11980
|
reportField("admin.token", cfg.admin.token);
|
|
11040
11981
|
}
|
|
11041
11982
|
const tokensPath = defaultTokensPath(args.config);
|
|
11042
|
-
if (
|
|
11983
|
+
if (existsSync23(tokensPath)) {
|
|
11043
11984
|
console.info(`Secret status for ${tokensPath}:`);
|
|
11044
11985
|
reportTokenFields(tokensPath);
|
|
11045
11986
|
}
|
|
11046
11987
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
11047
|
-
if (
|
|
11988
|
+
if (existsSync23(integrationsPath)) {
|
|
11048
11989
|
const state = readRawJson(integrationsPath);
|
|
11049
11990
|
const key = state.gatewayKey;
|
|
11050
11991
|
if (key && typeof key === "object" && !Array.isArray(key)) {
|
|
@@ -11098,8 +12039,8 @@ async function secretsRotate(args) {
|
|
|
11098
12039
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
11099
12040
|
try {
|
|
11100
12041
|
cfg = loadConfig(args.config);
|
|
11101
|
-
if (
|
|
11102
|
-
if (
|
|
12042
|
+
if (existsSync23(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
|
|
12043
|
+
if (existsSync23(integrationsPath)) {
|
|
11103
12044
|
integrationsPlain = new IntegrationStateStore(integrationsPath, oldBox).load();
|
|
11104
12045
|
}
|
|
11105
12046
|
} finally {
|
|
@@ -11134,20 +12075,20 @@ function secretsDecrypt(args) {
|
|
|
11134
12075
|
let tokensPlain = null;
|
|
11135
12076
|
try {
|
|
11136
12077
|
cfg = loadConfig(args.config);
|
|
11137
|
-
if (
|
|
12078
|
+
if (existsSync23(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
|
|
11138
12079
|
} finally {
|
|
11139
12080
|
setSecretBox(null);
|
|
11140
12081
|
}
|
|
11141
12082
|
saveConfig(args.config, cfg);
|
|
11142
12083
|
if (tokensPlain) {
|
|
11143
|
-
|
|
12084
|
+
writeFileSync14(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
|
|
11144
12085
|
}
|
|
11145
12086
|
console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
|
|
11146
12087
|
}
|
|
11147
12088
|
function readRawConfig(path2) {
|
|
11148
12089
|
let parsed;
|
|
11149
12090
|
try {
|
|
11150
|
-
parsed = JSON.parse(
|
|
12091
|
+
parsed = JSON.parse(readFileSync19(path2, "utf8"));
|
|
11151
12092
|
} catch {
|
|
11152
12093
|
throw new Error(`secrets: cannot read or parse '${path2}'`);
|
|
11153
12094
|
}
|
|
@@ -11155,7 +12096,7 @@ function readRawConfig(path2) {
|
|
|
11155
12096
|
}
|
|
11156
12097
|
function readRawJson(path2) {
|
|
11157
12098
|
try {
|
|
11158
|
-
const parsed = JSON.parse(
|
|
12099
|
+
const parsed = JSON.parse(readFileSync19(path2, "utf8"));
|
|
11159
12100
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
11160
12101
|
return parsed;
|
|
11161
12102
|
}
|
|
@@ -11165,13 +12106,13 @@ function readRawJson(path2) {
|
|
|
11165
12106
|
}
|
|
11166
12107
|
function encryptTokensFileInPlace(configPath, box) {
|
|
11167
12108
|
const tokensPath = defaultTokensPath(configPath);
|
|
11168
|
-
if (!
|
|
12109
|
+
if (!existsSync23(tokensPath)) return;
|
|
11169
12110
|
const plain = decryptTokensFile(tokensPath, box);
|
|
11170
12111
|
writeTokensEncrypted(tokensPath, plain, box);
|
|
11171
12112
|
}
|
|
11172
12113
|
function rewriteIntegrationState(configPath, readBox, writeBox) {
|
|
11173
12114
|
const path2 = defaultIntegrationsPath(configPath);
|
|
11174
|
-
if (!
|
|
12115
|
+
if (!existsSync23(path2)) return;
|
|
11175
12116
|
const state = new IntegrationStateStore(path2, readBox).load();
|
|
11176
12117
|
new IntegrationStateStore(path2, writeBox).save(state);
|
|
11177
12118
|
}
|
|
@@ -11184,7 +12125,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
|
|
|
11184
12125
|
{ updatedAt: "", ...plain },
|
|
11185
12126
|
box
|
|
11186
12127
|
);
|
|
11187
|
-
|
|
12128
|
+
writeFileSync14(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
11188
12129
|
}
|
|
11189
12130
|
var TOKEN_FIELDS2 = {
|
|
11190
12131
|
claude: ["accessToken", "refreshToken"],
|
|
@@ -11207,11 +12148,11 @@ function walkTokens(raw, fn) {
|
|
|
11207
12148
|
return next;
|
|
11208
12149
|
}
|
|
11209
12150
|
function tokensSuffix(configPath) {
|
|
11210
|
-
return
|
|
12151
|
+
return existsSync23(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
|
|
11211
12152
|
}
|
|
11212
12153
|
|
|
11213
12154
|
// src/commands/start.ts
|
|
11214
|
-
import { parseArgs as
|
|
12155
|
+
import { parseArgs as parseArgs9 } from "util";
|
|
11215
12156
|
import { loadServerConfig as loadServerConfig3 } from "@omnicross/core/outbound-api";
|
|
11216
12157
|
import { getSharedAccountHealth as getSharedAccountHealth5 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
11217
12158
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling6 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
@@ -11250,7 +12191,7 @@ async function seedIdentities(store, credentialStore) {
|
|
|
11250
12191
|
|
|
11251
12192
|
// src/commands/start.ts
|
|
11252
12193
|
async function runStart(argv) {
|
|
11253
|
-
const { values } =
|
|
12194
|
+
const { values } = parseArgs9({
|
|
11254
12195
|
args: argv,
|
|
11255
12196
|
options: {
|
|
11256
12197
|
config: { type: "string", short: "c" },
|
|
@@ -11390,6 +12331,10 @@ Usage:
|
|
|
11390
12331
|
omnicross secrets encrypt --config <p> Encrypt all at-rest secrets in place.
|
|
11391
12332
|
omnicross secrets status --config <p> Report each secret field (no values shown).
|
|
11392
12333
|
omnicross secrets rotate --config <p> --new-master-key-file <p> Re-seal under a new master key.
|
|
12334
|
+
|
|
12335
|
+
omnicross audit sessions --config <p> [--date YYYY-MM-DD] List captured body shards per session.
|
|
12336
|
+
omnicross audit show --config <p> --session <key> [--id <recordId>] Print reconstructed request/response bodies.
|
|
12337
|
+
omnicross audit compact --config <p> [--date YYYY-MM-DD] Run cross-session body compaction now.
|
|
11393
12338
|
`;
|
|
11394
12339
|
async function main() {
|
|
11395
12340
|
const [, , subcommand, ...rest] = process.argv;
|
|
@@ -11421,6 +12366,9 @@ async function main() {
|
|
|
11421
12366
|
case "secrets":
|
|
11422
12367
|
await runSecrets(rest);
|
|
11423
12368
|
return;
|
|
12369
|
+
case "audit":
|
|
12370
|
+
await runAudit(rest);
|
|
12371
|
+
return;
|
|
11424
12372
|
case void 0:
|
|
11425
12373
|
case "-h":
|
|
11426
12374
|
case "--help":
|