@omnicross/daemon 0.1.6 → 0.1.7

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.js CHANGED
@@ -1493,10 +1493,20 @@ function assertLoopbackGatewayUrl(value) {
1493
1493
  // src/ports/JsonOutboundKeyDb.ts
1494
1494
  import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
1495
1495
  var JsonOutboundKeyDb = class {
1496
- constructor(keysPath) {
1496
+ /**
1497
+ * @param secretBox OPTIONAL reversible-secret codec. When present, a created
1498
+ * key's plaintext is persisted as a `keySecret` `enc:` envelope (enabling the
1499
+ * operator "view key" affordance via `outboundApiKeysReveal`). When absent the
1500
+ * store stays hash-only (byte-identical to the legacy behavior) and reveal
1501
+ * always returns `null`. Existing 1-arg call sites (tests, lightweight
1502
+ * embedders) keep working.
1503
+ */
1504
+ constructor(keysPath, secretBox3) {
1497
1505
  this.keysPath = keysPath;
1506
+ this.secretBox = secretBox3;
1498
1507
  }
1499
1508
  keysPath;
1509
+ secretBox;
1500
1510
  async outboundApiKeysList() {
1501
1511
  return this.readRows();
1502
1512
  }
@@ -1522,10 +1532,27 @@ var JsonOutboundKeyDb = class {
1522
1532
  allowedEndpoints: input.allowedEndpoints,
1523
1533
  loopbackOnly: input.loopbackOnly
1524
1534
  };
1535
+ if (input.plaintext && this.secretBox) {
1536
+ row.keySecret = this.secretBox.encrypt(input.plaintext);
1537
+ }
1525
1538
  rows.push(row);
1526
1539
  this.writeRows(rows);
1527
1540
  return row;
1528
1541
  }
1542
+ async outboundApiKeysReveal(id) {
1543
+ const rows = this.readRows();
1544
+ const row = rows.find((r) => r.id === id);
1545
+ if (!row || !row.keySecret || !this.secretBox) return null;
1546
+ return this.secretBox.decrypt(row.keySecret);
1547
+ }
1548
+ async outboundApiKeysDelete(id) {
1549
+ const rows = this.readRows();
1550
+ const idx = rows.findIndex((r) => r.id === id);
1551
+ if (idx < 0) return false;
1552
+ rows.splice(idx, 1);
1553
+ this.writeRows(rows);
1554
+ return true;
1555
+ }
1529
1556
  async outboundApiKeysRevoke(id) {
1530
1557
  return this.mutateRow(id, (row) => {
1531
1558
  if (row.revokedAt !== null) return false;
@@ -1723,7 +1750,7 @@ async function keysRevoke(db, id) {
1723
1750
 
1724
1751
  // src/commands/launch.ts
1725
1752
  import { spawn as spawn2 } from "child_process";
1726
- import { existsSync as existsSync18 } from "fs";
1753
+ import { existsSync as existsSync20 } from "fs";
1727
1754
  import { delimiter as delimiter2, join as join12 } from "path";
1728
1755
  import { parseArgs as parseArgs4 } from "util";
1729
1756
  import {
@@ -1734,7 +1761,7 @@ import {
1734
1761
  } from "@omnicross/cli-launcher";
1735
1762
 
1736
1763
  // src/bootstrap.ts
1737
- import { accessSync, constants as fsConstants, existsSync as existsSync17 } from "fs";
1764
+ import { accessSync, constants as fsConstants, existsSync as existsSync19 } from "fs";
1738
1765
  import { DEFAULT_AUDIT_CONFIG } from "@omnicross/contracts/audit-types";
1739
1766
  import { DEFAULT_BILLING_CONFIG } from "@omnicross/contracts/billing-types";
1740
1767
  import { getGeminiCodeAssistProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
@@ -1849,7 +1876,7 @@ async function runCodexLoopback(sessionId, codeVerifier, state, signal, deps) {
1849
1876
  const code = await deps.codexAwaitLoopback(state, void 0, signal);
1850
1877
  const result = await codexOAuth.exchangeCodeForTokens(
1851
1878
  { authorizationCode: code, codeVerifier, state },
1852
- deps.oauthExchangeFetch
1879
+ deps.oauthExchangeFetch("codex")
1853
1880
  );
1854
1881
  const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
1855
1882
  const block = {
@@ -2369,6 +2396,17 @@ function handleAuditQuery(req, res, reader) {
2369
2396
  res.writeHead(200, { "Content-Type": "application/json" });
2370
2397
  res.end(JSON.stringify({ records }));
2371
2398
  }
2399
+ async function handleAuditStatsQuery(req, res, reader) {
2400
+ const url = new URL(req.url ?? "/", "http://localhost");
2401
+ const query2 = {};
2402
+ const from = intParam(url.searchParams.get("from"));
2403
+ if (from !== void 0) query2.from = from;
2404
+ const to = intParam(url.searchParams.get("to"));
2405
+ if (to !== void 0) query2.to = to;
2406
+ const stats = reader ? await reader(query2) : { requestCount: 0, errorCount: 0, complete: true };
2407
+ res.writeHead(200, { "Content-Type": "application/json" });
2408
+ res.end(JSON.stringify(stats));
2409
+ }
2372
2410
 
2373
2411
  // src/admin/billingStatusApi.ts
2374
2412
  function handleBillingStatus(res, reader) {
@@ -2559,7 +2597,13 @@ function listMappablePresets() {
2559
2597
  name: preset.name,
2560
2598
  apiFormat: resolved.format,
2561
2599
  baseUrl: preset.api_base_url,
2562
- models: Array.isArray(preset.models) ? preset.models : []
2600
+ models: Array.isArray(preset.models) ? preset.models : [],
2601
+ nameKey: preset.nameKey,
2602
+ icon: preset.icon,
2603
+ description: preset.description,
2604
+ features: preset.features,
2605
+ website: preset.website,
2606
+ modelsEndpoint: preset.modelsEndpoint
2563
2607
  });
2564
2608
  }
2565
2609
  return { mappable, excluded };
@@ -2950,7 +2994,7 @@ async function handleOAuthComplete(providerId, body, deps) {
2950
2994
  const rawCode = typeof body["code"] === "string" ? body["code"] : "";
2951
2995
  if (!sessionId) return err2(400, "oauth complete requires { sessionId }");
2952
2996
  if (!rawCode) return err2(400, "oauth complete requires { code }");
2953
- const session = deps.oauthSessions.take(sessionId);
2997
+ const session = deps.oauthSessions.peek(sessionId);
2954
2998
  if (!session) return err2(410, "oauth session is unknown, expired, or already used");
2955
2999
  if (session.providerId !== providerId) {
2956
3000
  return err2(400, `oauth session does not match provider '${providerId}'`);
@@ -2964,13 +3008,15 @@ async function handleOAuthComplete(providerId, body, deps) {
2964
3008
  }
2965
3009
  code = splitCode;
2966
3010
  }
3011
+ const exchangeFetch = deps.oauthExchangeFetch(providerId);
2967
3012
  let block;
2968
3013
  try {
2969
- block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, deps.oauthExchangeFetch) : await exchangeGemini(code, session.codeVerifier, deps.oauthExchangeFetch);
3014
+ block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
2970
3015
  } catch (exchangeError) {
2971
3016
  const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
2972
3017
  return err2(502, `oauth token exchange failed for '${providerId}': ${reason}`);
2973
3018
  }
3019
+ deps.oauthSessions.consume(sessionId);
2974
3020
  const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
2975
3021
  await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
2976
3022
  const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
@@ -3220,8 +3266,8 @@ function validateAuditSegment(patch) {
3220
3266
  }
3221
3267
  }
3222
3268
  const maxBodyBytes = audit["maxBodyBytes"];
3223
- if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes < 0)) {
3224
- errors.push("audit.maxBodyBytes must be a non-negative number");
3269
+ if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes < -1)) {
3270
+ errors.push("audit.maxBodyBytes must be -1 or a non-negative number");
3225
3271
  }
3226
3272
  const retentionDays = audit["retentionDays"];
3227
3273
  if (retentionDays !== void 0 && (typeof retentionDays !== "number" || !Number.isFinite(retentionDays) || retentionDays < 0)) {
@@ -3666,18 +3712,16 @@ function preserveWebhookSecrets(incoming, current) {
3666
3712
  }
3667
3713
 
3668
3714
  // src/audit/auditRuntime.ts
3669
- import { join as join5 } from "path";
3670
3715
  import { setAuditCaptureConfig, setAuditSink } from "@omnicross/core/pipeline/auditSink";
3671
3716
  import { setUpstreamTracePath } from "@omnicross/core/pipeline/upstreamTrace";
3672
3717
  var writer = null;
3673
3718
  var sweeper = null;
3674
- var auditDir = "";
3675
- function setAuditRuntime(w, s, dir) {
3719
+ function setAuditRuntime(w, s) {
3676
3720
  writer = w;
3677
3721
  sweeper = s;
3678
- auditDir = dir;
3679
3722
  }
3680
3723
  function applyAuditConfig(config) {
3724
+ setUpstreamTracePath(null);
3681
3725
  const enabled = config?.enabled === true && writer !== null;
3682
3726
  if (enabled && config) {
3683
3727
  setAuditCaptureConfig(config);
@@ -3687,11 +3731,9 @@ function applyAuditConfig(config) {
3687
3731
  sweeper.configure(config);
3688
3732
  sweeper.start();
3689
3733
  }
3690
- setUpstreamTracePath(config.captureBodies ? join5(auditDir, "upstream-trace.jsonl") : null);
3691
3734
  } else {
3692
3735
  setAuditCaptureConfig(null);
3693
3736
  setAuditSink(null);
3694
- setUpstreamTracePath(null);
3695
3737
  if (sweeper) {
3696
3738
  if (config) sweeper.configure(config);
3697
3739
  sweeper.dispose();
@@ -4534,6 +4576,11 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
4534
4576
  }
4535
4577
 
4536
4578
  // src/admin/adminApi.ts
4579
+ import {
4580
+ ACCOUNT_ROUTE_ACTIVITY_LIMIT,
4581
+ getSharedAccountRouteActivity
4582
+ } from "@omnicross/core/pipeline/AccountRouteActivity";
4583
+ import { getSharedOverloadCounter } from "@omnicross/core/pipeline/ServerOverloadCounter";
4537
4584
  function readBody(req) {
4538
4585
  return new Promise((resolve3, reject) => {
4539
4586
  const chunks = [];
@@ -4570,6 +4617,9 @@ function toKeyInfo(row) {
4570
4617
  id: row.id,
4571
4618
  name: row.name,
4572
4619
  keyPrefix: row.keyPrefix,
4620
+ // True only when a reversible `keySecret` envelope was persisted at creation
4621
+ // — gates the UI "view key" eye. Legacy hash-only rows read as absent.
4622
+ revealable: Boolean(row.keySecret),
4573
4623
  enabled: row.enabled,
4574
4624
  createdAt: row.createdAt,
4575
4625
  lastUsedAt: row.lastUsedAt,
@@ -5225,7 +5275,13 @@ function handlePresets(res, method) {
5225
5275
  name: p.name,
5226
5276
  apiFormat: p.apiFormat,
5227
5277
  baseUrl: p.baseUrl,
5228
- models: p.models
5278
+ models: p.models,
5279
+ nameKey: p.nameKey,
5280
+ icon: p.icon,
5281
+ description: p.description,
5282
+ features: p.features,
5283
+ website: p.website,
5284
+ modelsEndpoint: p.modelsEndpoint
5229
5285
  }));
5230
5286
  return writeJson3(res, 200, { presets, excluded });
5231
5287
  }
@@ -5259,12 +5315,27 @@ async function handleKeys(req, res, method, rest, deps) {
5259
5315
  plaintextOnce: created.plaintextOnce
5260
5316
  });
5261
5317
  }
5318
+ if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
5319
+ const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
5320
+ if (revealed !== null) return writeJson3(res, 200, { key: revealed });
5321
+ const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
5322
+ if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
5323
+ return writeJsonError(
5324
+ res,
5325
+ 409,
5326
+ `key '${rest[0]}' is not revealable (created before revealable key storage)`
5327
+ );
5328
+ }
5262
5329
  const id = rest[0];
5263
5330
  const action = rest[1];
5264
5331
  if (method === "POST" && id && action === "revoke") {
5265
5332
  const ok = await deps.keyDb.outboundApiKeysRevoke(id);
5266
5333
  return writeJson3(res, ok ? 200 : 404, { ok });
5267
5334
  }
5335
+ if (method === "DELETE" && id && !action) {
5336
+ const ok = await deps.keyDb.outboundApiKeysDelete(id);
5337
+ return writeJson3(res, ok ? 200 : 404, { ok });
5338
+ }
5268
5339
  if (method === "POST" && id && action === "enabled") {
5269
5340
  const body = await readJsonBody3(req);
5270
5341
  const enabled = body["enabled"] === true;
@@ -5441,6 +5512,40 @@ async function handleServer(req, res, method, deps) {
5441
5512
  return writeJsonError(res, 405, `method ${method} not allowed on server`);
5442
5513
  }
5443
5514
  async function handleAccounts(req, res, method, rest, deps) {
5515
+ if (rest[0] === "route-activity" && rest.length === 1) {
5516
+ if (method !== "GET") {
5517
+ return writeJsonError(res, 405, `method ${method} not allowed on account route activity`);
5518
+ }
5519
+ const query2 = requestQuery(req);
5520
+ const parsedLimit = Number(query2.get("limit") ?? "100");
5521
+ const records = getSharedAccountRouteActivity().list({
5522
+ providerId: query2.get("providerId") ?? void 0,
5523
+ accountId: query2.get("accountId") ?? void 0,
5524
+ sessionKey: query2.get("sessionKey") ?? void 0,
5525
+ limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
5526
+ });
5527
+ return writeJson3(res, 200, {
5528
+ available: true,
5529
+ records,
5530
+ capacity: ACCOUNT_ROUTE_ACTIVITY_LIMIT,
5531
+ collectedAt: Date.now()
5532
+ });
5533
+ }
5534
+ if (rest[0] === "overload-counters" && rest.length === 1) {
5535
+ if (method !== "GET") {
5536
+ return writeJsonError(res, 405, `method ${method} not allowed on overload counters`);
5537
+ }
5538
+ const query2 = requestQuery(req);
5539
+ const entries = getSharedOverloadCounter().list({
5540
+ providerId: query2.get("providerId") ?? void 0,
5541
+ accountId: query2.get("accountId") ?? void 0
5542
+ });
5543
+ return writeJson3(res, 200, {
5544
+ available: true,
5545
+ entries,
5546
+ collectedAt: Date.now()
5547
+ });
5548
+ }
5444
5549
  if (rest[0] === "allowances") {
5445
5550
  return handleAccountAllowanceApi(
5446
5551
  req,
@@ -5587,8 +5692,13 @@ async function handleAccounts(req, res, method, rest, deps) {
5587
5692
  if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
5588
5693
  return writeJsonError(res, 404, `account '${accountId}' not found`);
5589
5694
  }
5590
- const result = await deps.accountProbeService.probeAccount(providerId, accountId);
5591
- return writeJson3(res, 200, { ok: result.ok, marked: result.marked });
5695
+ const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
5696
+ return writeJson3(res, 200, {
5697
+ ok: result.ok,
5698
+ marked: result.marked,
5699
+ tier: result.tier,
5700
+ model: result.model
5701
+ });
5592
5702
  }
5593
5703
  if (method === "POST" && rest[2] === "label") {
5594
5704
  const accountId = rest[1];
@@ -5959,7 +6069,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
5959
6069
  }
5960
6070
 
5961
6071
  // src/admin/version.ts
5962
- var DAEMON_VERSION = true ? "0.1.6" : "0.0.0-dev";
6072
+ var DAEMON_VERSION = true ? "0.1.7" : "0.0.0-dev";
5963
6073
 
5964
6074
  // src/admin/AdminServer.ts
5965
6075
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -6067,6 +6177,10 @@ var AdminServer = class {
6067
6177
  handleAuditQuery(req, res, this.deps.auditReader);
6068
6178
  return;
6069
6179
  }
6180
+ if (path2 === "/admin/api/audit/stats" && (req.method === "GET" || req.method === "HEAD")) {
6181
+ await handleAuditStatsQuery(req, res, this.deps.auditStatsReader);
6182
+ return;
6183
+ }
6070
6184
  if (path2 === "/admin/api/billing-status" && (req.method === "GET" || req.method === "HEAD")) {
6071
6185
  handleBillingStatus(res, this.deps.billingStatusReader);
6072
6186
  return;
@@ -6183,20 +6297,36 @@ var OAuthSessionStore = class {
6183
6297
  return sessionId;
6184
6298
  }
6185
6299
  /**
6186
- * SINGLE-USE consume: return + delete the session for `sessionId`, or `null`
6187
- * when it is unknown, already used, or past its TTL (in which case it is
6188
- * dropped). A `null` return means the completer must reject (no exchange, no
6189
- * write).
6300
+ * NON-DESTRUCTIVE lookup: return the session for `sessionId`, or `null` when
6301
+ * it is unknown, already consumed, or past its TTL (an expired entry is
6302
+ * dropped here). A `null` return means the completer must reject (no
6303
+ * exchange, no write).
6304
+ *
6305
+ * Deliberately NOT a consume: the completer peeks, runs the token exchange,
6306
+ * and only {@link consume}s once a token has actually been minted. Consuming
6307
+ * up-front burned the session on EVERY failed exchange (a mistyped/expired
6308
+ * pasted code, a proxy hiccup), so the user's natural retry hit
6309
+ * "session is unknown, expired, or already used" and the login became
6310
+ * unrecoverable without restarting the whole flow.
6190
6311
  */
6191
- take(sessionId) {
6312
+ peek(sessionId) {
6192
6313
  this.sweep();
6193
6314
  const session = this.sessions.get(sessionId);
6194
6315
  if (!session) return null;
6195
- this.sessions.delete(sessionId);
6196
- if (Date.now() - session.createdAt > this.ttlMs) return null;
6316
+ if (Date.now() - session.createdAt > this.ttlMs) {
6317
+ this.sessions.delete(sessionId);
6318
+ return null;
6319
+ }
6197
6320
  return session;
6198
6321
  }
6199
- /** Drop every session past its TTL. Called on each put/take. */
6322
+ /**
6323
+ * SINGLE-USE burn: drop the session so the same `sessionId` can never be
6324
+ * completed twice. Called ONLY after a successful token exchange.
6325
+ */
6326
+ consume(sessionId) {
6327
+ this.sessions.delete(sessionId);
6328
+ }
6329
+ /** Drop every session past its TTL. Called on each put/peek. */
6200
6330
  sweep() {
6201
6331
  const now = Date.now();
6202
6332
  for (const [id, session] of this.sessions) {
@@ -6211,6 +6341,10 @@ var LOOPBACK_HOST = "127.0.0.1";
6211
6341
  var LOOPBACK_PORT = 1455;
6212
6342
  var CALLBACK_PATH = "/auth/callback";
6213
6343
  var DEFAULT_TIMEOUT_MS = 5 * 6e4;
6344
+ var HTML_HEADERS = {
6345
+ "Content-Type": "text/html",
6346
+ Connection: "close"
6347
+ };
6214
6348
  function pageHtml(message) {
6215
6349
  return `<!doctype html><meta charset="utf-8"><title>omnicross login</title><body style="font-family:sans-serif;padding:2rem"><h2>${message}</h2><p>You can close this window and return to the terminal.</p></body>`;
6216
6350
  }
@@ -6221,30 +6355,31 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
6221
6355
  if (settled) return;
6222
6356
  settled = true;
6223
6357
  clearTimeout(timer);
6224
- server2.close(() => fn());
6358
+ fn();
6359
+ server2.close();
6225
6360
  };
6226
6361
  const server = createServer((req, res) => {
6227
6362
  const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
6228
6363
  if (url.pathname !== CALLBACK_PATH) {
6229
- res.writeHead(404, { "Content-Type": "text/html" });
6364
+ res.writeHead(404, HTML_HEADERS);
6230
6365
  res.end(pageHtml("Not found"));
6231
6366
  return;
6232
6367
  }
6233
6368
  const code = url.searchParams.get("code");
6234
6369
  const state = url.searchParams.get("state");
6235
6370
  if (!code) {
6236
- res.writeHead(400, { "Content-Type": "text/html" });
6371
+ res.writeHead(400, HTML_HEADERS);
6237
6372
  res.end(pageHtml("Login failed: missing authorization code."));
6238
6373
  finish(server, () => reject(new Error("login: callback did not include an authorization code")));
6239
6374
  return;
6240
6375
  }
6241
6376
  if (state !== expectedState) {
6242
- res.writeHead(400, { "Content-Type": "text/html" });
6377
+ res.writeHead(400, HTML_HEADERS);
6243
6378
  res.end(pageHtml("Login failed: state mismatch."));
6244
6379
  finish(server, () => reject(new Error("login: callback state did not match (possible CSRF) \u2014 aborting")));
6245
6380
  return;
6246
6381
  }
6247
- res.writeHead(200, { "Content-Type": "text/html" });
6382
+ res.writeHead(200, HTML_HEADERS);
6248
6383
  res.end(pageHtml("Login complete."));
6249
6384
  finish(server, () => resolve3(code));
6250
6385
  });
@@ -6733,8 +6868,12 @@ var JsonlUsageEventStore = class {
6733
6868
  reasoningTokens: 0,
6734
6869
  costUsd: 0,
6735
6870
  costSavedByCacheUsd: 0,
6736
- eventCount: 0
6871
+ eventCount: 0,
6872
+ cacheEligibleEventCount: 0,
6873
+ coldCacheEventCount: 0,
6874
+ medianCacheHitRate: null
6737
6875
  };
6876
+ const perEventHitRates = [];
6738
6877
  for (const row of this.readRows(range)) {
6739
6878
  totals.inputTokens += row.inputTokens;
6740
6879
  totals.outputTokens += row.outputTokens;
@@ -6744,7 +6883,14 @@ var JsonlUsageEventStore = class {
6744
6883
  totals.costUsd += row.costUsd;
6745
6884
  totals.costSavedByCacheUsd += row.costSavedByCacheUsd;
6746
6885
  totals.eventCount += 1;
6886
+ const promptSideTokens = row.inputTokens + row.cacheReadTokens + row.cacheCreationTokens;
6887
+ if (promptSideTokens > 0) {
6888
+ totals.cacheEligibleEventCount += 1;
6889
+ if (row.cacheReadTokens === 0) totals.coldCacheEventCount += 1;
6890
+ perEventHitRates.push(row.cacheReadTokens / promptSideTokens);
6891
+ }
6747
6892
  }
6893
+ totals.medianCacheHitRate = median(perEventHitRates);
6748
6894
  return totals;
6749
6895
  }
6750
6896
  async getByModel(range) {
@@ -6978,6 +7124,15 @@ var NUMERIC_FIELDS = [
6978
7124
  ];
6979
7125
  var NULLABLE_STRING_FIELDS = ["messageId", "parentMessageId", "sessionId", "apiKeyId"];
6980
7126
  var isStringOrNull = (v) => v === null || typeof v === "string";
7127
+ var CACHE_KEY_SOURCES = /* @__PURE__ */ new Set([
7128
+ "client",
7129
+ "session-header",
7130
+ "thread-header",
7131
+ "body-session-id",
7132
+ "body-thread-id",
7133
+ "content-fingerprint",
7134
+ "none"
7135
+ ]);
6981
7136
  function isUsageEventRecord(parsed) {
6982
7137
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
6983
7138
  const r = parsed;
@@ -6985,6 +7140,10 @@ function isUsageEventRecord(parsed) {
6985
7140
  if (typeof r["providerId"] !== "string") return false;
6986
7141
  if (typeof r["model"] !== "string") return false;
6987
7142
  if (typeof r["engineOrigin"] !== "string") return false;
7143
+ if (r["cacheKeySource"] !== void 0 && (typeof r["cacheKeySource"] !== "string" || !CACHE_KEY_SOURCES.has(r["cacheKeySource"]))) return false;
7144
+ if (r["cacheKeyInjected"] !== void 0 && typeof r["cacheKeyInjected"] !== "boolean") {
7145
+ return false;
7146
+ }
6988
7147
  for (const f of NULLABLE_STRING_FIELDS) {
6989
7148
  if (!isStringOrNull(r[f])) return false;
6990
7149
  }
@@ -6994,6 +7153,12 @@ function isUsageEventRecord(parsed) {
6994
7153
  }
6995
7154
  return true;
6996
7155
  }
7156
+ function median(values) {
7157
+ if (values.length === 0) return null;
7158
+ values.sort((a, b) => a - b);
7159
+ const middle = Math.floor(values.length / 2);
7160
+ return values.length % 2 === 1 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
7161
+ }
6997
7162
 
6998
7163
  // src/ports/JsonPricingStore.ts
6999
7164
  import { existsSync as existsSync9, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
@@ -7409,9 +7574,9 @@ function findDuplicateCredentialIds(accounts) {
7409
7574
  // src/ports/external-cli-credentials.ts
7410
7575
  import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
7411
7576
  import { homedir as homedir3 } from "os";
7412
- import { join as join6 } from "path";
7577
+ import { join as join5 } from "path";
7413
7578
  function externalStorePath(provider, home = homedir3()) {
7414
- return provider === "claude" ? join6(home, ".claude", ".credentials.json") : join6(home, ".codex", "auth.json");
7579
+ return provider === "claude" ? join5(home, ".claude", ".credentials.json") : join5(home, ".codex", "auth.json");
7415
7580
  }
7416
7581
  function decodeJwtExpiryMs(token) {
7417
7582
  try {
@@ -7501,9 +7666,15 @@ var JsonSubscriptionCredentialStore = class {
7501
7666
  * TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
7502
7667
  * through {@link fetchUpstream} with the account's `{ providerId, accountId }`
7503
7668
  * ctx so the per-account/provider proxy applies. `@internal` also a test seam.
7669
+ *
7670
+ * `redactBodies` is REQUIRED here: this round-trip sends the refresh_token and
7671
+ * receives a fresh access/refresh token pair. Carrying a `providerId` opts the
7672
+ * call into the upstream trace (so a failing refresh is diagnosable), and the
7673
+ * trace captures bodies verbatim — without this flag every refresh would write
7674
+ * a plaintext token pair into `upstream-trace.jsonl`.
7504
7675
  */
7505
7676
  buildRefreshFetch(providerId, accountId) {
7506
- return this.fetchImpl ?? ((url, init) => fetchUpstream3(url, init, { providerId, accountId }));
7677
+ return this.fetchImpl ?? ((url, init) => fetchUpstream3(url, init, { providerId, accountId, redactBodies: true }));
7507
7678
  }
7508
7679
  /**
7509
7680
  * In-flight refresh coalescing. OAuth refresh tokens are
@@ -8063,6 +8234,127 @@ var JsonSubscriptionCredentialStore = class {
8063
8234
  // src/AccountHealthProbeScheduler.ts
8064
8235
  import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
8065
8236
 
8237
+ // src/probe/CodexGenerationProbe.ts
8238
+ import {
8239
+ DEFAULT_CODEX_CLI_HEADERS,
8240
+ codexAcceptHeader
8241
+ } from "@omnicross/core/provider-proxy/identity/codexCliHeaders";
8242
+ var CODEX_GENERATION_PROBE_MODEL = "gpt-5.6-luna";
8243
+ var CODEX_GENERATION_PROBE_URL = "https://chatgpt.com/backend-api/codex/responses";
8244
+ var MAX_STREAM_BYTES = 256 * 1024;
8245
+ var PROBE_INSTRUCTION = "Return exactly PONG and no other text.";
8246
+ function buildCodexGenerationProbeInit(token, signal) {
8247
+ return {
8248
+ method: "POST",
8249
+ signal,
8250
+ headers: {
8251
+ ...DEFAULT_CODEX_CLI_HEADERS,
8252
+ Authorization: `Bearer ${token}`,
8253
+ Accept: codexAcceptHeader(true),
8254
+ "Content-Type": "application/json"
8255
+ },
8256
+ body: JSON.stringify({
8257
+ model: CODEX_GENERATION_PROBE_MODEL,
8258
+ input: [
8259
+ {
8260
+ role: "developer",
8261
+ content: [{ type: "input_text", text: PROBE_INSTRUCTION }]
8262
+ },
8263
+ {
8264
+ role: "user",
8265
+ content: [{ type: "input_text", text: "Connection probe." }]
8266
+ }
8267
+ ],
8268
+ // GPT-5.6 otherwise defaults to medium reasoning. A connectivity probe
8269
+ // needs the lowest-cost path and no tool reasoning.
8270
+ reasoning: { effort: "none" },
8271
+ stream: true,
8272
+ store: false
8273
+ })
8274
+ };
8275
+ }
8276
+ async function readCodexGenerationProbeStream(response) {
8277
+ if (!response.body) return { completed: false, outputChars: 0 };
8278
+ const reader = response.body.getReader();
8279
+ const decoder = new TextDecoder();
8280
+ let buffer = "";
8281
+ let bytes = 0;
8282
+ let outputChars = 0;
8283
+ try {
8284
+ while (true) {
8285
+ const { done, value } = await reader.read();
8286
+ if (done) break;
8287
+ bytes += value.byteLength;
8288
+ if (bytes > MAX_STREAM_BYTES) {
8289
+ await reader.cancel();
8290
+ return { completed: false, outputChars };
8291
+ }
8292
+ buffer += decoder.decode(value, { stream: true });
8293
+ buffer = buffer.replace(/\r\n/g, "\n");
8294
+ let boundary = buffer.indexOf("\n\n");
8295
+ while (boundary >= 0) {
8296
+ const block = buffer.slice(0, boundary);
8297
+ buffer = buffer.slice(boundary + 2);
8298
+ const event = parseSseBlock(block);
8299
+ if (event) {
8300
+ const type = event["type"];
8301
+ if (type === "response.output_text.delta" && typeof event["delta"] === "string") {
8302
+ outputChars += event["delta"].length;
8303
+ } else if (type === "response.output_text.done" && typeof event["text"] === "string") {
8304
+ outputChars = Math.max(outputChars, event["text"].length);
8305
+ } else if (type === "response.failed" || type === "error") {
8306
+ await reader.cancel();
8307
+ return { completed: false, outputChars };
8308
+ } else if (type === "response.completed") {
8309
+ const completedResponse = asRecord(event["response"]);
8310
+ const status = completedResponse?.["status"];
8311
+ outputChars = Math.max(outputChars, countCompletedOutputChars(completedResponse));
8312
+ await reader.cancel();
8313
+ return {
8314
+ completed: (status === void 0 || status === "completed") && outputChars > 0,
8315
+ outputChars
8316
+ };
8317
+ }
8318
+ }
8319
+ boundary = buffer.indexOf("\n\n");
8320
+ }
8321
+ }
8322
+ } catch {
8323
+ return { completed: false, outputChars };
8324
+ } finally {
8325
+ reader.releaseLock();
8326
+ }
8327
+ return { completed: false, outputChars };
8328
+ }
8329
+ function parseSseBlock(block) {
8330
+ const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
8331
+ if (!data || data === "[DONE]") return null;
8332
+ try {
8333
+ return JSON.parse(data);
8334
+ } catch {
8335
+ return null;
8336
+ }
8337
+ }
8338
+ function asRecord(value) {
8339
+ return value !== null && typeof value === "object" ? value : void 0;
8340
+ }
8341
+ function countCompletedOutputChars(response) {
8342
+ const output = response?.["output"];
8343
+ if (!Array.isArray(output)) return 0;
8344
+ let chars = 0;
8345
+ for (const item of output) {
8346
+ const content = asRecord(item)?.["content"];
8347
+ if (!Array.isArray(content)) continue;
8348
+ for (const part of content) {
8349
+ const record = asRecord(part);
8350
+ if (record?.["type"] === "output_text" && typeof record["text"] === "string") {
8351
+ chars += record["text"].length;
8352
+ }
8353
+ }
8354
+ }
8355
+ return chars;
8356
+ }
8357
+
8066
8358
  // src/probe/ProbeStrategy.ts
8067
8359
  var PROVIDER_PROBE_PLANS = {
8068
8360
  claude: {
@@ -8190,17 +8482,17 @@ var AccountHealthProbeScheduler = class {
8190
8482
  }
8191
8483
  if (readThrew) {
8192
8484
  this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
8193
- return { ok: false, marked: false };
8485
+ return { ok: false, marked: false, tier: "local" };
8194
8486
  }
8195
8487
  if (!token) {
8196
8488
  this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
8197
8489
  this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
8198
- return { ok: false, marked: true };
8490
+ return { ok: false, marked: true, tier: "local" };
8199
8491
  }
8200
8492
  const plan = this.planFor(providerId);
8201
8493
  if (plan.kind === "local") {
8202
8494
  this.record(providerId, accountId, { ts: now, ok: true, tier: "local" });
8203
- return { ok: true, marked: false };
8495
+ return { ok: true, marked: false, tier: "local" };
8204
8496
  }
8205
8497
  const start = this.now();
8206
8498
  let status = null;
@@ -8225,7 +8517,60 @@ var AccountHealthProbeScheduler = class {
8225
8517
  latencyMs,
8226
8518
  tier: "upstream"
8227
8519
  });
8228
- return { ok: status !== null && status < 400, marked };
8520
+ return { ok: status !== null && status < 400, marked, tier: "upstream" };
8521
+ }
8522
+ /**
8523
+ * Manual connection test. Codex performs a real, quota-consuming generation;
8524
+ * every other provider keeps its existing cheap probe. Scheduled sweeps never
8525
+ * call this method, so they remain non-billable.
8526
+ */
8527
+ async testAccountConnection(providerId, accountId) {
8528
+ if (providerId !== "codex") return this.probeAccount(providerId, accountId);
8529
+ const now = this.now();
8530
+ let token;
8531
+ try {
8532
+ token = await this.store.getAccessTokenForAccount(providerId, accountId);
8533
+ } catch {
8534
+ this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
8535
+ return { ok: false, marked: false, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
8536
+ }
8537
+ if (!token) {
8538
+ this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
8539
+ this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
8540
+ return { ok: false, marked: true, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
8541
+ }
8542
+ const startedAt = this.now();
8543
+ let attempt = await this.runCodexGenerationAttempt(accountId, token);
8544
+ if (attempt.status === 401 && this.store.refreshAccountToken) {
8545
+ try {
8546
+ if (await this.store.refreshAccountToken(providerId, accountId)) {
8547
+ const refreshed = await this.store.getAccessTokenForAccount(providerId, accountId);
8548
+ if (refreshed) attempt = await this.runCodexGenerationAttempt(accountId, refreshed);
8549
+ }
8550
+ } catch {
8551
+ }
8552
+ }
8553
+ const latencyMs = this.now() - startedAt;
8554
+ const ok = attempt.status !== null && attempt.status >= 200 && attempt.status < 300 && attempt.completed;
8555
+ let marked = false;
8556
+ if (ok) {
8557
+ this.health.clearTransientMark(providerId, accountId);
8558
+ } else if (attempt.status === 401 || attempt.status === 403) {
8559
+ marked = this.applyOutcome(providerId, accountId, attempt.status, attempt.bodyText, now);
8560
+ }
8561
+ this.record(providerId, accountId, {
8562
+ ts: now,
8563
+ ok,
8564
+ status: attempt.status,
8565
+ latencyMs,
8566
+ tier: "generation"
8567
+ });
8568
+ return {
8569
+ ok,
8570
+ marked,
8571
+ tier: "generation",
8572
+ model: CODEX_GENERATION_PROBE_MODEL
8573
+ };
8229
8574
  }
8230
8575
  /** Per-account rolling history for the authed admin surface (design D5). */
8231
8576
  getAllHistory() {
@@ -8282,6 +8627,24 @@ var AccountHealthProbeScheduler = class {
8282
8627
  return "";
8283
8628
  }
8284
8629
  }
8630
+ async runCodexGenerationAttempt(accountId, token) {
8631
+ try {
8632
+ const timeoutMs = Math.max(this.config.timeoutMs, 15e3);
8633
+ const response = await this.fetchImpl(
8634
+ CODEX_GENERATION_PROBE_URL,
8635
+ buildCodexGenerationProbeInit(token, AbortSignal.timeout(timeoutMs)),
8636
+ { providerId: "codex", accountId, redactBodies: true }
8637
+ );
8638
+ if (response.status < 200 || response.status >= 300) {
8639
+ const bodyText = response.status === 403 ? await this.readBounded(response) : void 0;
8640
+ return { status: response.status, completed: false, bodyText };
8641
+ }
8642
+ const stream = await readCodexGenerationProbeStream(response);
8643
+ return { status: response.status, completed: stream.completed };
8644
+ } catch {
8645
+ return { status: null, completed: false };
8646
+ }
8647
+ }
8285
8648
  key(providerId, accountId) {
8286
8649
  return `${providerId}${KEY_SEP}${accountId}`;
8287
8650
  }
@@ -8377,7 +8740,7 @@ var AccountHealthSweeper = class {
8377
8740
  };
8378
8741
 
8379
8742
  // src/audit/AuditPruneSweeper.ts
8380
- import { existsSync as existsSync14, readdirSync, unlinkSync as unlinkSync3 } from "fs";
8743
+ import { existsSync as existsSync15, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
8381
8744
  import { join as join7 } from "path";
8382
8745
 
8383
8746
  // src/audit/auditFiles.ts
@@ -8400,12 +8763,213 @@ function auditFileDateMs(fileName) {
8400
8763
  return d.getTime();
8401
8764
  }
8402
8765
 
8766
+ // src/audit/auditStats.ts
8767
+ import {
8768
+ createReadStream,
8769
+ existsSync as existsSync14,
8770
+ readFileSync as readFileSync15,
8771
+ readdirSync,
8772
+ statSync as statSync3,
8773
+ writeFileSync as writeFileSync11
8774
+ } from "fs";
8775
+ import { basename, dirname as dirname7, join as join6 } from "path";
8776
+ var SIDECAR_VERSION = 1;
8777
+ var META_PREFIX_BYTES = 64 * 1024;
8778
+ var READ_CHUNK_BYTES = 4 * 1024 * 1024;
8779
+ function auditStatsFileName(auditFile) {
8780
+ return auditFile.replace(/\.jsonl$/, ".stats.json");
8781
+ }
8782
+ function readPersisted(path2) {
8783
+ if (!existsSync14(path2)) return null;
8784
+ try {
8785
+ const value = JSON.parse(readFileSync15(path2, "utf8"));
8786
+ 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)) {
8787
+ return null;
8788
+ }
8789
+ return value;
8790
+ } catch {
8791
+ return null;
8792
+ }
8793
+ }
8794
+ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
8795
+ const statsPath = join6(dirname7(auditPath), auditStatsFileName(basename(auditPath)));
8796
+ const previous = auditBytesBefore === 0 ? {
8797
+ version: SIDECAR_VERSION,
8798
+ auditBytes: 0,
8799
+ requestCount: 0,
8800
+ errorCount: 0,
8801
+ complete: true,
8802
+ minTs: null,
8803
+ maxTs: null
8804
+ } : readPersisted(statsPath);
8805
+ if (!previous || !previous.complete || previous.auditBytes !== auditBytesBefore) return;
8806
+ const next = {
8807
+ version: SIDECAR_VERSION,
8808
+ auditBytes: auditBytesAfter,
8809
+ requestCount: previous.requestCount + 1,
8810
+ errorCount: previous.errorCount + (record.status >= 400 || Boolean(record.error) ? 1 : 0),
8811
+ complete: true,
8812
+ minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
8813
+ maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
8814
+ };
8815
+ writeFileSync11(statsPath, JSON.stringify(next), "utf8");
8816
+ }
8817
+ function queryCovers(stats, from, to) {
8818
+ return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
8819
+ }
8820
+ function fileOverlaps(file, from, to) {
8821
+ const start = auditFileDateMs(file);
8822
+ if (start === null) return false;
8823
+ const date = new Date(start);
8824
+ const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
8825
+ return end > from && start <= to;
8826
+ }
8827
+ function parseMetadataPrefix(prefix, prefixTruncated) {
8828
+ const text = prefix.toString("utf8");
8829
+ const tsMatch = /(?:^|,)"ts":(-?\d+)/.exec(text);
8830
+ const statusMatch = /(?:^|,)"status":(-?\d+)/.exec(text);
8831
+ const errorMatch = /(?:^|,)"error":"((?:\\.|[^"\\])*)"/.exec(text);
8832
+ const bodyStarted = /,(?:"requestBody"|"responseBody"):/.test(text);
8833
+ return {
8834
+ ts: tsMatch ? Number(tsMatch[1]) : void 0,
8835
+ status: statusMatch ? Number(statusMatch[1]) : void 0,
8836
+ hasError: Boolean(errorMatch?.[1]),
8837
+ complete: Boolean(tsMatch && statusMatch && (!prefixTruncated || bodyStarted))
8838
+ };
8839
+ }
8840
+ async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
8841
+ let requestCount = 0;
8842
+ let errorCount = 0;
8843
+ let filteredRequestCount = 0;
8844
+ let filteredErrorCount = 0;
8845
+ let minTs = null;
8846
+ let maxTs = null;
8847
+ let complete = true;
8848
+ let prefixParts = [];
8849
+ let prefixBytes = 0;
8850
+ let prefixTruncated = false;
8851
+ const consumeLine = () => {
8852
+ if (prefixBytes === 0 && !prefixTruncated) return;
8853
+ const prefix = Buffer.concat(prefixParts, prefixBytes);
8854
+ const metadata = parseMetadataPrefix(prefix, prefixTruncated);
8855
+ if (!metadata.complete || metadata.ts === void 0 || metadata.status === void 0) {
8856
+ complete = false;
8857
+ } else {
8858
+ requestCount += 1;
8859
+ const isError = metadata.status >= 400 || metadata.hasError;
8860
+ if (isError) errorCount += 1;
8861
+ minTs = minTs === null ? metadata.ts : Math.min(minTs, metadata.ts);
8862
+ maxTs = maxTs === null ? metadata.ts : Math.max(maxTs, metadata.ts);
8863
+ if (metadata.ts >= from && metadata.ts <= to) {
8864
+ filteredRequestCount += 1;
8865
+ if (isError) filteredErrorCount += 1;
8866
+ }
8867
+ }
8868
+ prefixParts = [];
8869
+ prefixBytes = 0;
8870
+ prefixTruncated = false;
8871
+ };
8872
+ if (auditBytes > startByte) {
8873
+ const stream = createReadStream(auditPath, {
8874
+ start: startByte,
8875
+ end: auditBytes - 1,
8876
+ highWaterMark: READ_CHUNK_BYTES
8877
+ });
8878
+ for await (const value of stream) {
8879
+ const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
8880
+ let offset = 0;
8881
+ while (offset < chunk.length) {
8882
+ const newline = chunk.indexOf(10, offset);
8883
+ const end = newline === -1 ? chunk.length : newline;
8884
+ if (prefixBytes < META_PREFIX_BYTES) {
8885
+ const retained = Math.min(META_PREFIX_BYTES - prefixBytes, end - offset);
8886
+ if (retained > 0) {
8887
+ prefixParts.push(Buffer.from(chunk.subarray(offset, offset + retained)));
8888
+ prefixBytes += retained;
8889
+ }
8890
+ if (retained < end - offset) prefixTruncated = true;
8891
+ } else if (end > offset) {
8892
+ prefixTruncated = true;
8893
+ }
8894
+ if (newline === -1) break;
8895
+ consumeLine();
8896
+ offset = newline + 1;
8897
+ }
8898
+ }
8899
+ }
8900
+ if (prefixBytes > 0 || prefixTruncated) complete = false;
8901
+ return {
8902
+ all: {
8903
+ version: SIDECAR_VERSION,
8904
+ auditBytes,
8905
+ requestCount,
8906
+ errorCount,
8907
+ complete,
8908
+ minTs,
8909
+ maxTs
8910
+ },
8911
+ filtered: { requestCount: filteredRequestCount, errorCount: filteredErrorCount, complete }
8912
+ };
8913
+ }
8914
+ function mergePersistedStats(previous, appended) {
8915
+ return {
8916
+ version: SIDECAR_VERSION,
8917
+ auditBytes: appended.auditBytes,
8918
+ requestCount: previous.requestCount + appended.requestCount,
8919
+ errorCount: previous.errorCount + appended.errorCount,
8920
+ complete: previous.complete && appended.complete,
8921
+ minTs: previous.minTs === null ? appended.minTs : appended.minTs === null ? previous.minTs : Math.min(previous.minTs, appended.minTs),
8922
+ maxTs: previous.maxTs === null ? appended.maxTs : appended.maxTs === null ? previous.maxTs : Math.max(previous.maxTs, appended.maxTs)
8923
+ };
8924
+ }
8925
+ async function readAuditStats(auditDir, query2 = {}) {
8926
+ if (!existsSync14(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
8927
+ const from = typeof query2.from === "number" ? query2.from : -Infinity;
8928
+ const to = typeof query2.to === "number" ? query2.to : Infinity;
8929
+ let files;
8930
+ try {
8931
+ files = readdirSync(auditDir).filter((file) => AUDIT_FILE_RE.test(file) && fileOverlaps(file, from, to)).sort();
8932
+ } catch {
8933
+ return { requestCount: 0, errorCount: 0, complete: false };
8934
+ }
8935
+ const total = { requestCount: 0, errorCount: 0, complete: true };
8936
+ for (const file of files) {
8937
+ const auditPath = join6(auditDir, file);
8938
+ try {
8939
+ const auditBytes = statSync3(auditPath).size;
8940
+ const statsPath = join6(auditDir, auditStatsFileName(file));
8941
+ const persisted = readPersisted(statsPath);
8942
+ if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
8943
+ total.requestCount += persisted.requestCount;
8944
+ total.errorCount += persisted.errorCount;
8945
+ continue;
8946
+ }
8947
+ const resumable = persisted && persisted.complete && persisted.auditBytes < auditBytes && queryCovers(persisted, from, to) ? persisted : null;
8948
+ const scanned = await scanAuditFile(
8949
+ auditPath,
8950
+ resumable?.auditBytes ?? 0,
8951
+ auditBytes,
8952
+ from,
8953
+ to
8954
+ );
8955
+ total.requestCount += scanned.filtered.requestCount + (resumable?.requestCount ?? 0);
8956
+ total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
8957
+ total.complete = total.complete && scanned.filtered.complete;
8958
+ const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
8959
+ if (current.complete) writeFileSync11(statsPath, JSON.stringify(current), "utf8");
8960
+ } catch {
8961
+ total.complete = false;
8962
+ }
8963
+ }
8964
+ return total;
8965
+ }
8966
+
8403
8967
  // src/audit/AuditPruneSweeper.ts
8404
8968
  var DAY_MS = 24 * 60 * 6e4;
8405
8969
  var SWEEP_INTERVAL_MS2 = 60 * 6e4;
8406
8970
  var AuditPruneSweeper = class {
8407
- constructor(auditDir2, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
8408
- this.auditDir = auditDir2;
8971
+ constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
8972
+ this.auditDir = auditDir;
8409
8973
  this.logger = logger;
8410
8974
  this.config = config;
8411
8975
  this.intervalMs = intervalMs;
@@ -8452,17 +9016,19 @@ var AuditPruneSweeper = class {
8452
9016
  if (!this.config.enabled || this.sweeping) return 0;
8453
9017
  this.sweeping = true;
8454
9018
  try {
8455
- if (!existsSync14(this.auditDir)) return 0;
9019
+ if (!existsSync15(this.auditDir)) return 0;
8456
9020
  const today = new Date(this.now());
8457
9021
  const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
8458
9022
  const cutoff = todayMidnight - (this.config.retentionDays - 1) * DAY_MS;
8459
9023
  let removed = 0;
8460
- for (const file of readdirSync(this.auditDir)) {
9024
+ for (const file of readdirSync2(this.auditDir)) {
8461
9025
  const dateMs = auditFileDateMs(file);
8462
9026
  if (dateMs === null || dateMs >= cutoff) continue;
8463
9027
  try {
8464
9028
  unlinkSync3(join7(this.auditDir, file));
8465
9029
  removed += 1;
9030
+ const statsPath = join7(this.auditDir, auditStatsFileName(file));
9031
+ if (existsSync15(statsPath)) unlinkSync3(statsPath);
8466
9032
  } catch (error) {
8467
9033
  this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
8468
9034
  file,
@@ -8484,15 +9050,15 @@ var AuditPruneSweeper = class {
8484
9050
  };
8485
9051
 
8486
9052
  // src/audit/auditReader.ts
8487
- import { existsSync as existsSync15, readdirSync as readdirSync2, readFileSync as readFileSync15 } from "fs";
9053
+ import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync16 } from "fs";
8488
9054
  import { join as join8 } from "path";
8489
9055
  var DEFAULT_LIMIT = 200;
8490
9056
  var MAX_LIMIT = 2e3;
8491
- function readAuditRecords(auditDir2, query2 = {}) {
8492
- if (!existsSync15(auditDir2)) return [];
9057
+ function readAuditRecords(auditDir, query2 = {}) {
9058
+ if (!existsSync16(auditDir)) return [];
8493
9059
  let files;
8494
9060
  try {
8495
- files = readdirSync2(auditDir2).filter((f) => AUDIT_FILE_RE.test(f));
9061
+ files = readdirSync3(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
8496
9062
  } catch {
8497
9063
  return [];
8498
9064
  }
@@ -8503,7 +9069,7 @@ function readAuditRecords(auditDir2, query2 = {}) {
8503
9069
  for (const file of files.sort().reverse()) {
8504
9070
  let raw;
8505
9071
  try {
8506
- raw = readFileSync15(join8(auditDir2, file), "utf8");
9072
+ raw = readFileSync16(join8(auditDir, file), "utf8");
8507
9073
  } catch {
8508
9074
  continue;
8509
9075
  }
@@ -8532,11 +9098,11 @@ function isAuditRecord(value) {
8532
9098
  }
8533
9099
 
8534
9100
  // src/audit/AuditWriter.ts
8535
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5 } from "fs";
9101
+ import { appendFileSync as appendFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync5, statSync as statSync4 } from "fs";
8536
9102
  import { join as join9 } from "path";
8537
9103
  var AuditWriter = class {
8538
- constructor(auditDir2, logger, defer = (fn) => setTimeout(fn, 0)) {
8539
- this.auditDir = auditDir2;
9104
+ constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
9105
+ this.auditDir = auditDir;
8540
9106
  this.logger = logger;
8541
9107
  this.defer = defer;
8542
9108
  }
@@ -8570,7 +9136,21 @@ var AuditWriter = class {
8570
9136
  this.dirEnsured = true;
8571
9137
  }
8572
9138
  const file = join9(this.auditDir, auditFileName(record.ts));
8573
- appendFileSync2(file, JSON.stringify(record) + "\n", "utf8");
9139
+ const line = JSON.stringify(record) + "\n";
9140
+ const auditBytesBefore = existsSync17(file) ? statSync4(file).size : 0;
9141
+ appendFileSync2(file, line, "utf8");
9142
+ try {
9143
+ updateAuditStatsAfterAppend(
9144
+ file,
9145
+ auditBytesBefore,
9146
+ auditBytesBefore + Buffer.byteLength(line, "utf8"),
9147
+ record
9148
+ );
9149
+ } catch (error) {
9150
+ this.logger.warn("[AuditWriter] failed to update audit stats", {
9151
+ error: error instanceof Error ? error.message : String(error)
9152
+ });
9153
+ }
8574
9154
  }
8575
9155
  };
8576
9156
 
@@ -8714,14 +9294,14 @@ var BillingPublisher = class {
8714
9294
  };
8715
9295
 
8716
9296
  // src/billing/billingReader.ts
8717
- import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync16 } from "fs";
9297
+ import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync17 } from "fs";
8718
9298
  import { join as join11 } from "path";
8719
9299
  function readBillingLedger(billingDir) {
8720
9300
  const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
8721
- if (!existsSync16(billingDir)) return view;
9301
+ if (!existsSync18(billingDir)) return view;
8722
9302
  let files;
8723
9303
  try {
8724
- files = readdirSync3(billingDir);
9304
+ files = readdirSync4(billingDir);
8725
9305
  } catch {
8726
9306
  return view;
8727
9307
  }
@@ -8752,7 +9332,7 @@ function readBillingStatus(billingDir) {
8752
9332
  function parseLines(dir, file) {
8753
9333
  let raw;
8754
9334
  try {
8755
- raw = readFileSync16(join11(dir, file), "utf8");
9335
+ raw = readFileSync17(join11(dir, file), "utf8");
8756
9336
  } catch {
8757
9337
  return [];
8758
9338
  }
@@ -9124,7 +9704,7 @@ function buildDaemon(config, paths) {
9124
9704
  normalizeServerConfig(decryptedConfig.server).allowanceScheduling
9125
9705
  );
9126
9706
  const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
9127
- const keyDb = new JsonOutboundKeyDb(paths.keysPath);
9707
+ const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
9128
9708
  const voucherDb = new JsonVoucherDb(defaultVouchersPath(paths.configPath));
9129
9709
  const settingsStore = new JsonApiServerSettingsStore(paths.configPath, secretBox3);
9130
9710
  const integrationStateStore = new IntegrationStateStore(
@@ -9225,7 +9805,7 @@ function buildDaemon(config, paths) {
9225
9805
  // lines through the injected logger (honors level/format/file sink).
9226
9806
  logger
9227
9807
  });
9228
- const auditDir2 = defaultAuditDir(paths.configPath);
9808
+ const auditDir = defaultAuditDir(paths.configPath);
9229
9809
  const billingDir = defaultBillingDir(paths.configPath);
9230
9810
  const adminServer = new AdminServer({
9231
9811
  configPath: paths.configPath,
@@ -9259,10 +9839,16 @@ function buildDaemon(config, paths) {
9259
9839
  // (NOT widening the least-authority writer — no token-returning read reachable).
9260
9840
  oauthSessions: new OAuthSessionStore(),
9261
9841
  // Real global fetch by default; a test seam (`paths.oauthExchangeFetch`) can
9262
- // inject a mock so no real token endpoint is hit.
9263
- // upstream-proxy: default the OAuth token-exchange fetch to the proxy-aware
9264
- // helper so interactive login honors a configured proxy (global/env layers).
9265
- oauthExchangeFetch: paths.oauthExchangeFetch ?? ((url, init) => fetchUpstream7(url, init)),
9842
+ // inject a mock so no real token endpoint is hit (one FetchLike for every
9843
+ // provider the ctx below only matters on the real egress path).
9844
+ //
9845
+ // upstream-proxy: a PER-PROVIDER factory, so the exchange carries the same
9846
+ // `{ providerId }` ctx the CLI login and the token refresh already pass.
9847
+ // Without it the interactive login resolved only the global/env proxy layers
9848
+ // — `server.proxy.byProvider[...]` was silently skipped — and the call was
9849
+ // excluded from the upstream trace, so a failing login left no evidence.
9850
+ // `redactBodies` keeps the code/verifier + minted token out of that trace.
9851
+ oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream7(url, init, { providerId, redactBodies: true }),
9266
9852
  subscriptionAccountAppender: credentialStore,
9267
9853
  // Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
9268
9854
  // + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
@@ -9314,7 +9900,8 @@ function buildDaemon(config, paths) {
9314
9900
  // date-rotated audit store. Bound to the store dir here so the AdminServer
9315
9901
  // carries no path/store coupling. Records hold IP/UA/bodies → admin-only,
9316
9902
  // NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
9317
- auditReader: (query2) => readAuditRecords(auditDir2, query2),
9903
+ auditReader: (query2) => readAuditRecords(auditDir, query2),
9904
+ auditStatsReader: (query2) => readAuditStats(auditDir, query2),
9318
9905
  // billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
9319
9906
  // secret-free total/delivered/pending counts of the durable ledger.
9320
9907
  billingStatusReader: () => readBillingStatus(billingDir)
@@ -9324,9 +9911,9 @@ function buildDaemon(config, paths) {
9324
9911
  fetchImpl: (url, init) => fetchUpstream7(url, init)
9325
9912
  });
9326
9913
  setWebhookRuntime(webhookDispatcher, getSharedAccountHealth3());
9327
- const auditWriter = new AuditWriter(auditDir2, logger);
9328
- const auditPruneSweeper = new AuditPruneSweeper(auditDir2, logger, DEFAULT_AUDIT_CONFIG);
9329
- setAuditRuntime(auditWriter, auditPruneSweeper, auditDir2);
9914
+ const auditWriter = new AuditWriter(auditDir, logger);
9915
+ const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
9916
+ setAuditRuntime(auditWriter, auditPruneSweeper);
9330
9917
  const billingPublisher = new BillingPublisher(billingDir, logger);
9331
9918
  const billingRetrySweeper = new BillingRetrySweeper(
9332
9919
  billingDir,
@@ -9372,7 +9959,7 @@ function buildDaemon(config, paths) {
9372
9959
  }
9373
9960
  function isTokensStoreReadable(tokensPath) {
9374
9961
  try {
9375
- if (!existsSync17(tokensPath)) return true;
9962
+ if (!existsSync19(tokensPath)) return true;
9376
9963
  accessSync(tokensPath, fsConstants.R_OK);
9377
9964
  return true;
9378
9965
  } catch {
@@ -9421,7 +10008,7 @@ function resolveInPathDefault(candidate) {
9421
10008
  const segments = (process.env["PATH"] ?? "").split(delimiter2).filter(Boolean);
9422
10009
  for (const seg of segments) {
9423
10010
  const full = join12(seg, candidate);
9424
- if (existsSync18(full)) return full;
10011
+ if (existsSync20(full)) return full;
9425
10012
  }
9426
10013
  return null;
9427
10014
  }
@@ -9623,7 +10210,7 @@ async function runLogin(argv, deps) {
9623
10210
  setUpstreamProxyResolver2(createUpstreamProxyResolver());
9624
10211
  try {
9625
10212
  const tokensPath = defaultTokensPath(values.config);
9626
- const exchangeFetch = resolved.tokensFetch ?? ((url, init) => fetchUpstream8(url, init, { providerId: provider }));
10213
+ const exchangeFetch = resolved.tokensFetch ?? ((url, init) => fetchUpstream8(url, init, { providerId: provider, redactBodies: true }));
9627
10214
  const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
9628
10215
  const expiresAt = await runProviderLogin(
9629
10216
  provider,
@@ -9909,7 +10496,7 @@ function providersRmKey(configPath, providerId, keyId) {
9909
10496
  }
9910
10497
 
9911
10498
  // src/commands/secrets.ts
9912
- import { existsSync as existsSync19, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
10499
+ import { existsSync as existsSync21, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
9913
10500
  import { parseArgs as parseArgs7 } from "util";
9914
10501
  async function runSecrets(argv) {
9915
10502
  const { values, positionals } = parseArgs7({
@@ -9982,12 +10569,12 @@ function secretsStatus(args) {
9982
10569
  reportField("admin.token", cfg.admin.token);
9983
10570
  }
9984
10571
  const tokensPath = defaultTokensPath(args.config);
9985
- if (existsSync19(tokensPath)) {
10572
+ if (existsSync21(tokensPath)) {
9986
10573
  console.info(`Secret status for ${tokensPath}:`);
9987
10574
  reportTokenFields(tokensPath);
9988
10575
  }
9989
10576
  const integrationsPath = defaultIntegrationsPath(args.config);
9990
- if (existsSync19(integrationsPath)) {
10577
+ if (existsSync21(integrationsPath)) {
9991
10578
  const state = readRawJson(integrationsPath);
9992
10579
  const key = state.gatewayKey;
9993
10580
  if (key && typeof key === "object" && !Array.isArray(key)) {
@@ -10041,8 +10628,8 @@ async function secretsRotate(args) {
10041
10628
  const integrationsPath = defaultIntegrationsPath(args.config);
10042
10629
  try {
10043
10630
  cfg = loadConfig(args.config);
10044
- if (existsSync19(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
10045
- if (existsSync19(integrationsPath)) {
10631
+ if (existsSync21(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
10632
+ if (existsSync21(integrationsPath)) {
10046
10633
  integrationsPlain = new IntegrationStateStore(integrationsPath, oldBox).load();
10047
10634
  }
10048
10635
  } finally {
@@ -10077,20 +10664,20 @@ function secretsDecrypt(args) {
10077
10664
  let tokensPlain = null;
10078
10665
  try {
10079
10666
  cfg = loadConfig(args.config);
10080
- if (existsSync19(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
10667
+ if (existsSync21(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
10081
10668
  } finally {
10082
10669
  setSecretBox(null);
10083
10670
  }
10084
10671
  saveConfig(args.config, cfg);
10085
10672
  if (tokensPlain) {
10086
- writeFileSync11(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
10673
+ writeFileSync12(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
10087
10674
  }
10088
10675
  console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
10089
10676
  }
10090
10677
  function readRawConfig(path2) {
10091
10678
  let parsed;
10092
10679
  try {
10093
- parsed = JSON.parse(readFileSync17(path2, "utf8"));
10680
+ parsed = JSON.parse(readFileSync18(path2, "utf8"));
10094
10681
  } catch {
10095
10682
  throw new Error(`secrets: cannot read or parse '${path2}'`);
10096
10683
  }
@@ -10098,7 +10685,7 @@ function readRawConfig(path2) {
10098
10685
  }
10099
10686
  function readRawJson(path2) {
10100
10687
  try {
10101
- const parsed = JSON.parse(readFileSync17(path2, "utf8"));
10688
+ const parsed = JSON.parse(readFileSync18(path2, "utf8"));
10102
10689
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
10103
10690
  return parsed;
10104
10691
  }
@@ -10108,13 +10695,13 @@ function readRawJson(path2) {
10108
10695
  }
10109
10696
  function encryptTokensFileInPlace(configPath, box) {
10110
10697
  const tokensPath = defaultTokensPath(configPath);
10111
- if (!existsSync19(tokensPath)) return;
10698
+ if (!existsSync21(tokensPath)) return;
10112
10699
  const plain = decryptTokensFile(tokensPath, box);
10113
10700
  writeTokensEncrypted(tokensPath, plain, box);
10114
10701
  }
10115
10702
  function rewriteIntegrationState(configPath, readBox, writeBox) {
10116
10703
  const path2 = defaultIntegrationsPath(configPath);
10117
- if (!existsSync19(path2)) return;
10704
+ if (!existsSync21(path2)) return;
10118
10705
  const state = new IntegrationStateStore(path2, readBox).load();
10119
10706
  new IntegrationStateStore(path2, writeBox).save(state);
10120
10707
  }
@@ -10127,7 +10714,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
10127
10714
  { updatedAt: "", ...plain },
10128
10715
  box
10129
10716
  );
10130
- writeFileSync11(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
10717
+ writeFileSync12(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
10131
10718
  }
10132
10719
  var TOKEN_FIELDS2 = {
10133
10720
  claude: ["accessToken", "refreshToken"],
@@ -10150,7 +10737,7 @@ function walkTokens(raw, fn) {
10150
10737
  return next;
10151
10738
  }
10152
10739
  function tokensSuffix(configPath) {
10153
- return existsSync19(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
10740
+ return existsSync21(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
10154
10741
  }
10155
10742
 
10156
10743
  // src/commands/start.ts