@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.cjs CHANGED
@@ -1508,10 +1508,20 @@ function assertLoopbackGatewayUrl(value) {
1508
1508
  // src/ports/JsonOutboundKeyDb.ts
1509
1509
  var import_node_fs6 = require("fs");
1510
1510
  var JsonOutboundKeyDb = class {
1511
- constructor(keysPath) {
1511
+ /**
1512
+ * @param secretBox OPTIONAL reversible-secret codec. When present, a created
1513
+ * key's plaintext is persisted as a `keySecret` `enc:` envelope (enabling the
1514
+ * operator "view key" affordance via `outboundApiKeysReveal`). When absent the
1515
+ * store stays hash-only (byte-identical to the legacy behavior) and reveal
1516
+ * always returns `null`. Existing 1-arg call sites (tests, lightweight
1517
+ * embedders) keep working.
1518
+ */
1519
+ constructor(keysPath, secretBox3) {
1512
1520
  this.keysPath = keysPath;
1521
+ this.secretBox = secretBox3;
1513
1522
  }
1514
1523
  keysPath;
1524
+ secretBox;
1515
1525
  async outboundApiKeysList() {
1516
1526
  return this.readRows();
1517
1527
  }
@@ -1537,10 +1547,27 @@ var JsonOutboundKeyDb = class {
1537
1547
  allowedEndpoints: input.allowedEndpoints,
1538
1548
  loopbackOnly: input.loopbackOnly
1539
1549
  };
1550
+ if (input.plaintext && this.secretBox) {
1551
+ row.keySecret = this.secretBox.encrypt(input.plaintext);
1552
+ }
1540
1553
  rows.push(row);
1541
1554
  this.writeRows(rows);
1542
1555
  return row;
1543
1556
  }
1557
+ async outboundApiKeysReveal(id) {
1558
+ const rows = this.readRows();
1559
+ const row = rows.find((r) => r.id === id);
1560
+ if (!row || !row.keySecret || !this.secretBox) return null;
1561
+ return this.secretBox.decrypt(row.keySecret);
1562
+ }
1563
+ async outboundApiKeysDelete(id) {
1564
+ const rows = this.readRows();
1565
+ const idx = rows.findIndex((r) => r.id === id);
1566
+ if (idx < 0) return false;
1567
+ rows.splice(idx, 1);
1568
+ this.writeRows(rows);
1569
+ return true;
1570
+ }
1544
1571
  async outboundApiKeysRevoke(id) {
1545
1572
  return this.mutateRow(id, (row) => {
1546
1573
  if (row.revokedAt !== null) return false;
@@ -1738,13 +1765,13 @@ async function keysRevoke(db, id) {
1738
1765
 
1739
1766
  // src/commands/launch.ts
1740
1767
  var import_node_child_process2 = require("child_process");
1741
- var import_node_fs24 = require("fs");
1768
+ var import_node_fs25 = require("fs");
1742
1769
  var import_node_path17 = require("path");
1743
1770
  var import_node_util4 = require("util");
1744
1771
  var import_cli_launcher2 = require("@omnicross/cli-launcher");
1745
1772
 
1746
1773
  // src/bootstrap.ts
1747
- var import_node_fs23 = require("fs");
1774
+ var import_node_fs24 = require("fs");
1748
1775
  var import_audit_types = require("@omnicross/contracts/audit-types");
1749
1776
  var import_billing_types = require("@omnicross/contracts/billing-types");
1750
1777
  var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
@@ -1838,7 +1865,7 @@ async function runCodexLoopback(sessionId, codeVerifier, state, signal, deps) {
1838
1865
  const code = await deps.codexAwaitLoopback(state, void 0, signal);
1839
1866
  const result = await import_subscriptions.codexOAuth.exchangeCodeForTokens(
1840
1867
  { authorizationCode: code, codeVerifier, state },
1841
- deps.oauthExchangeFetch
1868
+ deps.oauthExchangeFetch("codex")
1842
1869
  );
1843
1870
  const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
1844
1871
  const block = {
@@ -2340,6 +2367,17 @@ function handleAuditQuery(req, res, reader) {
2340
2367
  res.writeHead(200, { "Content-Type": "application/json" });
2341
2368
  res.end(JSON.stringify({ records }));
2342
2369
  }
2370
+ async function handleAuditStatsQuery(req, res, reader) {
2371
+ const url = new URL(req.url ?? "/", "http://localhost");
2372
+ const query2 = {};
2373
+ const from = intParam(url.searchParams.get("from"));
2374
+ if (from !== void 0) query2.from = from;
2375
+ const to = intParam(url.searchParams.get("to"));
2376
+ if (to !== void 0) query2.to = to;
2377
+ const stats = reader ? await reader(query2) : { requestCount: 0, errorCount: 0, complete: true };
2378
+ res.writeHead(200, { "Content-Type": "application/json" });
2379
+ res.end(JSON.stringify(stats));
2380
+ }
2343
2381
 
2344
2382
  // src/admin/billingStatusApi.ts
2345
2383
  function handleBillingStatus(res, reader) {
@@ -2522,7 +2560,13 @@ function listMappablePresets() {
2522
2560
  name: preset.name,
2523
2561
  apiFormat: resolved.format,
2524
2562
  baseUrl: preset.api_base_url,
2525
- models: Array.isArray(preset.models) ? preset.models : []
2563
+ models: Array.isArray(preset.models) ? preset.models : [],
2564
+ nameKey: preset.nameKey,
2565
+ icon: preset.icon,
2566
+ description: preset.description,
2567
+ features: preset.features,
2568
+ website: preset.website,
2569
+ modelsEndpoint: preset.modelsEndpoint
2526
2570
  });
2527
2571
  }
2528
2572
  return { mappable, excluded };
@@ -2911,7 +2955,7 @@ async function handleOAuthComplete(providerId, body, deps) {
2911
2955
  const rawCode = typeof body["code"] === "string" ? body["code"] : "";
2912
2956
  if (!sessionId) return err2(400, "oauth complete requires { sessionId }");
2913
2957
  if (!rawCode) return err2(400, "oauth complete requires { code }");
2914
- const session = deps.oauthSessions.take(sessionId);
2958
+ const session = deps.oauthSessions.peek(sessionId);
2915
2959
  if (!session) return err2(410, "oauth session is unknown, expired, or already used");
2916
2960
  if (session.providerId !== providerId) {
2917
2961
  return err2(400, `oauth session does not match provider '${providerId}'`);
@@ -2925,13 +2969,15 @@ async function handleOAuthComplete(providerId, body, deps) {
2925
2969
  }
2926
2970
  code = splitCode;
2927
2971
  }
2972
+ const exchangeFetch = deps.oauthExchangeFetch(providerId);
2928
2973
  let block;
2929
2974
  try {
2930
- block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, deps.oauthExchangeFetch) : await exchangeGemini(code, session.codeVerifier, deps.oauthExchangeFetch);
2975
+ block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
2931
2976
  } catch (exchangeError) {
2932
2977
  const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
2933
2978
  return err2(502, `oauth token exchange failed for '${providerId}': ${reason}`);
2934
2979
  }
2980
+ deps.oauthSessions.consume(sessionId);
2935
2981
  const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
2936
2982
  await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
2937
2983
  const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
@@ -3176,8 +3222,8 @@ function validateAuditSegment(patch) {
3176
3222
  }
3177
3223
  }
3178
3224
  const maxBodyBytes = audit["maxBodyBytes"];
3179
- if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes < 0)) {
3180
- errors.push("audit.maxBodyBytes must be a non-negative number");
3225
+ if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes < -1)) {
3226
+ errors.push("audit.maxBodyBytes must be -1 or a non-negative number");
3181
3227
  }
3182
3228
  const retentionDays = audit["retentionDays"];
3183
3229
  if (retentionDays !== void 0 && (typeof retentionDays !== "number" || !Number.isFinite(retentionDays) || retentionDays < 0)) {
@@ -3612,18 +3658,16 @@ function preserveWebhookSecrets(incoming, current) {
3612
3658
  }
3613
3659
 
3614
3660
  // src/audit/auditRuntime.ts
3615
- var import_node_path8 = require("path");
3616
3661
  var import_auditSink = require("@omnicross/core/pipeline/auditSink");
3617
3662
  var import_upstreamTrace = require("@omnicross/core/pipeline/upstreamTrace");
3618
3663
  var writer = null;
3619
3664
  var sweeper = null;
3620
- var auditDir = "";
3621
- function setAuditRuntime(w, s, dir) {
3665
+ function setAuditRuntime(w, s) {
3622
3666
  writer = w;
3623
3667
  sweeper = s;
3624
- auditDir = dir;
3625
3668
  }
3626
3669
  function applyAuditConfig(config) {
3670
+ (0, import_upstreamTrace.setUpstreamTracePath)(null);
3627
3671
  const enabled = config?.enabled === true && writer !== null;
3628
3672
  if (enabled && config) {
3629
3673
  (0, import_auditSink.setAuditCaptureConfig)(config);
@@ -3633,11 +3677,9 @@ function applyAuditConfig(config) {
3633
3677
  sweeper.configure(config);
3634
3678
  sweeper.start();
3635
3679
  }
3636
- (0, import_upstreamTrace.setUpstreamTracePath)(config.captureBodies ? (0, import_node_path8.join)(auditDir, "upstream-trace.jsonl") : null);
3637
3680
  } else {
3638
3681
  (0, import_auditSink.setAuditCaptureConfig)(null);
3639
3682
  (0, import_auditSink.setAuditSink)(null);
3640
- (0, import_upstreamTrace.setUpstreamTracePath)(null);
3641
3683
  if (sweeper) {
3642
3684
  if (config) sweeper.configure(config);
3643
3685
  sweeper.dispose();
@@ -4480,6 +4522,8 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
4480
4522
  }
4481
4523
 
4482
4524
  // src/admin/adminApi.ts
4525
+ var import_AccountRouteActivity = require("@omnicross/core/pipeline/AccountRouteActivity");
4526
+ var import_ServerOverloadCounter = require("@omnicross/core/pipeline/ServerOverloadCounter");
4483
4527
  function readBody(req) {
4484
4528
  return new Promise((resolve3, reject) => {
4485
4529
  const chunks = [];
@@ -4516,6 +4560,9 @@ function toKeyInfo(row) {
4516
4560
  id: row.id,
4517
4561
  name: row.name,
4518
4562
  keyPrefix: row.keyPrefix,
4563
+ // True only when a reversible `keySecret` envelope was persisted at creation
4564
+ // — gates the UI "view key" eye. Legacy hash-only rows read as absent.
4565
+ revealable: Boolean(row.keySecret),
4519
4566
  enabled: row.enabled,
4520
4567
  createdAt: row.createdAt,
4521
4568
  lastUsedAt: row.lastUsedAt,
@@ -5171,7 +5218,13 @@ function handlePresets(res, method) {
5171
5218
  name: p.name,
5172
5219
  apiFormat: p.apiFormat,
5173
5220
  baseUrl: p.baseUrl,
5174
- models: p.models
5221
+ models: p.models,
5222
+ nameKey: p.nameKey,
5223
+ icon: p.icon,
5224
+ description: p.description,
5225
+ features: p.features,
5226
+ website: p.website,
5227
+ modelsEndpoint: p.modelsEndpoint
5175
5228
  }));
5176
5229
  return writeJson3(res, 200, { presets, excluded });
5177
5230
  }
@@ -5205,12 +5258,27 @@ async function handleKeys(req, res, method, rest, deps) {
5205
5258
  plaintextOnce: created.plaintextOnce
5206
5259
  });
5207
5260
  }
5261
+ if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
5262
+ const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
5263
+ if (revealed !== null) return writeJson3(res, 200, { key: revealed });
5264
+ const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
5265
+ if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
5266
+ return writeJsonError(
5267
+ res,
5268
+ 409,
5269
+ `key '${rest[0]}' is not revealable (created before revealable key storage)`
5270
+ );
5271
+ }
5208
5272
  const id = rest[0];
5209
5273
  const action = rest[1];
5210
5274
  if (method === "POST" && id && action === "revoke") {
5211
5275
  const ok = await deps.keyDb.outboundApiKeysRevoke(id);
5212
5276
  return writeJson3(res, ok ? 200 : 404, { ok });
5213
5277
  }
5278
+ if (method === "DELETE" && id && !action) {
5279
+ const ok = await deps.keyDb.outboundApiKeysDelete(id);
5280
+ return writeJson3(res, ok ? 200 : 404, { ok });
5281
+ }
5214
5282
  if (method === "POST" && id && action === "enabled") {
5215
5283
  const body = await readJsonBody3(req);
5216
5284
  const enabled = body["enabled"] === true;
@@ -5387,6 +5455,40 @@ async function handleServer(req, res, method, deps) {
5387
5455
  return writeJsonError(res, 405, `method ${method} not allowed on server`);
5388
5456
  }
5389
5457
  async function handleAccounts(req, res, method, rest, deps) {
5458
+ if (rest[0] === "route-activity" && rest.length === 1) {
5459
+ if (method !== "GET") {
5460
+ return writeJsonError(res, 405, `method ${method} not allowed on account route activity`);
5461
+ }
5462
+ const query2 = requestQuery(req);
5463
+ const parsedLimit = Number(query2.get("limit") ?? "100");
5464
+ const records = (0, import_AccountRouteActivity.getSharedAccountRouteActivity)().list({
5465
+ providerId: query2.get("providerId") ?? void 0,
5466
+ accountId: query2.get("accountId") ?? void 0,
5467
+ sessionKey: query2.get("sessionKey") ?? void 0,
5468
+ limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
5469
+ });
5470
+ return writeJson3(res, 200, {
5471
+ available: true,
5472
+ records,
5473
+ capacity: import_AccountRouteActivity.ACCOUNT_ROUTE_ACTIVITY_LIMIT,
5474
+ collectedAt: Date.now()
5475
+ });
5476
+ }
5477
+ if (rest[0] === "overload-counters" && rest.length === 1) {
5478
+ if (method !== "GET") {
5479
+ return writeJsonError(res, 405, `method ${method} not allowed on overload counters`);
5480
+ }
5481
+ const query2 = requestQuery(req);
5482
+ const entries = (0, import_ServerOverloadCounter.getSharedOverloadCounter)().list({
5483
+ providerId: query2.get("providerId") ?? void 0,
5484
+ accountId: query2.get("accountId") ?? void 0
5485
+ });
5486
+ return writeJson3(res, 200, {
5487
+ available: true,
5488
+ entries,
5489
+ collectedAt: Date.now()
5490
+ });
5491
+ }
5390
5492
  if (rest[0] === "allowances") {
5391
5493
  return handleAccountAllowanceApi(
5392
5494
  req,
@@ -5533,8 +5635,13 @@ async function handleAccounts(req, res, method, rest, deps) {
5533
5635
  if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
5534
5636
  return writeJsonError(res, 404, `account '${accountId}' not found`);
5535
5637
  }
5536
- const result = await deps.accountProbeService.probeAccount(providerId, accountId);
5537
- return writeJson3(res, 200, { ok: result.ok, marked: result.marked });
5638
+ const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
5639
+ return writeJson3(res, 200, {
5640
+ ok: result.ok,
5641
+ marked: result.marked,
5642
+ tier: result.tier,
5643
+ model: result.model
5644
+ });
5538
5645
  }
5539
5646
  if (method === "POST" && rest[2] === "label") {
5540
5647
  const accountId = rest[1];
@@ -5813,7 +5920,7 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
5813
5920
  var import_node_fs9 = require("fs");
5814
5921
  var import_promises = require("fs/promises");
5815
5922
  var import_node_module = require("module");
5816
- var import_node_path9 = __toESM(require("path"), 1);
5923
+ var import_node_path8 = __toESM(require("path"), 1);
5817
5924
  var import_meta = {};
5818
5925
  var CONTENT_TYPES = {
5819
5926
  ".html": "text/html; charset=utf-8",
@@ -5834,13 +5941,13 @@ var CONTENT_TYPES = {
5834
5941
  function resolveUiDist() {
5835
5942
  const fromEnv = process.env["OMNICROSS_UI_DIST"];
5836
5943
  if (fromEnv) {
5837
- return (0, import_node_fs9.existsSync)(import_node_path9.default.join(fromEnv, "index.html")) ? import_node_path9.default.resolve(fromEnv) : null;
5944
+ return (0, import_node_fs9.existsSync)(import_node_path8.default.join(fromEnv, "index.html")) ? import_node_path8.default.resolve(fromEnv) : null;
5838
5945
  }
5839
5946
  try {
5840
5947
  const req = (0, import_node_module.createRequire)(typeof __filename !== "undefined" ? __filename : import_meta.url);
5841
5948
  const pkgJson = req.resolve("@omnicross/ui/package.json");
5842
- const dist = import_node_path9.default.join(import_node_path9.default.dirname(pkgJson), "dist");
5843
- return (0, import_node_fs9.existsSync)(import_node_path9.default.join(dist, "index.html")) ? dist : null;
5949
+ const dist = import_node_path8.default.join(import_node_path8.default.dirname(pkgJson), "dist");
5950
+ return (0, import_node_fs9.existsSync)(import_node_path8.default.join(dist, "index.html")) ? dist : null;
5844
5951
  } catch {
5845
5952
  return null;
5846
5953
  }
@@ -5882,16 +5989,16 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
5882
5989
  res.end(JSON.stringify({ error: { type: "bad_request", message: "invalid path" } }));
5883
5990
  return true;
5884
5991
  }
5885
- const filePath = import_node_path9.default.resolve(uiDist, rel === "" ? "index.html" : rel);
5886
- if (filePath !== uiDist && !filePath.startsWith(uiDist + import_node_path9.default.sep)) {
5992
+ const filePath = import_node_path8.default.resolve(uiDist, rel === "" ? "index.html" : rel);
5993
+ if (filePath !== uiDist && !filePath.startsWith(uiDist + import_node_path8.default.sep)) {
5887
5994
  res.writeHead(403, { "Content-Type": "application/json" });
5888
5995
  res.end(JSON.stringify({ error: { type: "forbidden", message: "path outside ui root" } }));
5889
5996
  return true;
5890
5997
  }
5891
5998
  let target = filePath;
5892
5999
  if (!(0, import_node_fs9.existsSync)(target) || (0, import_node_fs9.statSync)(target).isDirectory()) {
5893
- if (import_node_path9.default.extname(rel) === "") {
5894
- target = import_node_path9.default.join(uiDist, "index.html");
6000
+ if (import_node_path8.default.extname(rel) === "") {
6001
+ target = import_node_path8.default.join(uiDist, "index.html");
5895
6002
  } else {
5896
6003
  res.writeHead(404, { "Content-Type": "application/json" });
5897
6004
  res.end(JSON.stringify({ error: { type: "not_found", message: "no such ui asset" } }));
@@ -5899,14 +6006,14 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
5899
6006
  }
5900
6007
  }
5901
6008
  const body = await (0, import_promises.readFile)(target);
5902
- const type = CONTENT_TYPES[import_node_path9.default.extname(target).toLowerCase()] ?? "application/octet-stream";
6009
+ const type = CONTENT_TYPES[import_node_path8.default.extname(target).toLowerCase()] ?? "application/octet-stream";
5903
6010
  res.writeHead(200, { "Content-Type": type, "Content-Length": body.length });
5904
6011
  res.end(req.method === "HEAD" ? void 0 : body);
5905
6012
  return true;
5906
6013
  }
5907
6014
 
5908
6015
  // src/admin/version.ts
5909
- var DAEMON_VERSION = true ? "0.1.6" : "0.0.0-dev";
6016
+ var DAEMON_VERSION = true ? "0.1.7" : "0.0.0-dev";
5910
6017
 
5911
6018
  // src/admin/AdminServer.ts
5912
6019
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -6014,6 +6121,10 @@ var AdminServer = class {
6014
6121
  handleAuditQuery(req, res, this.deps.auditReader);
6015
6122
  return;
6016
6123
  }
6124
+ if (path2 === "/admin/api/audit/stats" && (req.method === "GET" || req.method === "HEAD")) {
6125
+ await handleAuditStatsQuery(req, res, this.deps.auditStatsReader);
6126
+ return;
6127
+ }
6017
6128
  if (path2 === "/admin/api/billing-status" && (req.method === "GET" || req.method === "HEAD")) {
6018
6129
  handleBillingStatus(res, this.deps.billingStatusReader);
6019
6130
  return;
@@ -6130,20 +6241,36 @@ var OAuthSessionStore = class {
6130
6241
  return sessionId;
6131
6242
  }
6132
6243
  /**
6133
- * SINGLE-USE consume: return + delete the session for `sessionId`, or `null`
6134
- * when it is unknown, already used, or past its TTL (in which case it is
6135
- * dropped). A `null` return means the completer must reject (no exchange, no
6136
- * write).
6244
+ * NON-DESTRUCTIVE lookup: return the session for `sessionId`, or `null` when
6245
+ * it is unknown, already consumed, or past its TTL (an expired entry is
6246
+ * dropped here). A `null` return means the completer must reject (no
6247
+ * exchange, no write).
6248
+ *
6249
+ * Deliberately NOT a consume: the completer peeks, runs the token exchange,
6250
+ * and only {@link consume}s once a token has actually been minted. Consuming
6251
+ * up-front burned the session on EVERY failed exchange (a mistyped/expired
6252
+ * pasted code, a proxy hiccup), so the user's natural retry hit
6253
+ * "session is unknown, expired, or already used" and the login became
6254
+ * unrecoverable without restarting the whole flow.
6137
6255
  */
6138
- take(sessionId) {
6256
+ peek(sessionId) {
6139
6257
  this.sweep();
6140
6258
  const session = this.sessions.get(sessionId);
6141
6259
  if (!session) return null;
6142
- this.sessions.delete(sessionId);
6143
- if (Date.now() - session.createdAt > this.ttlMs) return null;
6260
+ if (Date.now() - session.createdAt > this.ttlMs) {
6261
+ this.sessions.delete(sessionId);
6262
+ return null;
6263
+ }
6144
6264
  return session;
6145
6265
  }
6146
- /** Drop every session past its TTL. Called on each put/take. */
6266
+ /**
6267
+ * SINGLE-USE burn: drop the session so the same `sessionId` can never be
6268
+ * completed twice. Called ONLY after a successful token exchange.
6269
+ */
6270
+ consume(sessionId) {
6271
+ this.sessions.delete(sessionId);
6272
+ }
6273
+ /** Drop every session past its TTL. Called on each put/peek. */
6147
6274
  sweep() {
6148
6275
  const now = Date.now();
6149
6276
  for (const [id, session] of this.sessions) {
@@ -6158,6 +6285,10 @@ var LOOPBACK_HOST = "127.0.0.1";
6158
6285
  var LOOPBACK_PORT = 1455;
6159
6286
  var CALLBACK_PATH = "/auth/callback";
6160
6287
  var DEFAULT_TIMEOUT_MS = 5 * 6e4;
6288
+ var HTML_HEADERS = {
6289
+ "Content-Type": "text/html",
6290
+ Connection: "close"
6291
+ };
6161
6292
  function pageHtml(message) {
6162
6293
  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>`;
6163
6294
  }
@@ -6168,30 +6299,31 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
6168
6299
  if (settled) return;
6169
6300
  settled = true;
6170
6301
  clearTimeout(timer);
6171
- server2.close(() => fn());
6302
+ fn();
6303
+ server2.close();
6172
6304
  };
6173
6305
  const server = (0, import_node_http3.createServer)((req, res) => {
6174
6306
  const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
6175
6307
  if (url.pathname !== CALLBACK_PATH) {
6176
- res.writeHead(404, { "Content-Type": "text/html" });
6308
+ res.writeHead(404, HTML_HEADERS);
6177
6309
  res.end(pageHtml("Not found"));
6178
6310
  return;
6179
6311
  }
6180
6312
  const code = url.searchParams.get("code");
6181
6313
  const state = url.searchParams.get("state");
6182
6314
  if (!code) {
6183
- res.writeHead(400, { "Content-Type": "text/html" });
6315
+ res.writeHead(400, HTML_HEADERS);
6184
6316
  res.end(pageHtml("Login failed: missing authorization code."));
6185
6317
  finish(server, () => reject(new Error("login: callback did not include an authorization code")));
6186
6318
  return;
6187
6319
  }
6188
6320
  if (state !== expectedState) {
6189
- res.writeHead(400, { "Content-Type": "text/html" });
6321
+ res.writeHead(400, HTML_HEADERS);
6190
6322
  res.end(pageHtml("Login failed: state mismatch."));
6191
6323
  finish(server, () => reject(new Error("login: callback state did not match (possible CSRF) \u2014 aborting")));
6192
6324
  return;
6193
6325
  }
6194
- res.writeHead(200, { "Content-Type": "text/html" });
6326
+ res.writeHead(200, HTML_HEADERS);
6195
6327
  res.end(pageHtml("Login complete."));
6196
6328
  finish(server, () => resolve3(code));
6197
6329
  });
@@ -6677,8 +6809,12 @@ var JsonlUsageEventStore = class {
6677
6809
  reasoningTokens: 0,
6678
6810
  costUsd: 0,
6679
6811
  costSavedByCacheUsd: 0,
6680
- eventCount: 0
6812
+ eventCount: 0,
6813
+ cacheEligibleEventCount: 0,
6814
+ coldCacheEventCount: 0,
6815
+ medianCacheHitRate: null
6681
6816
  };
6817
+ const perEventHitRates = [];
6682
6818
  for (const row of this.readRows(range)) {
6683
6819
  totals.inputTokens += row.inputTokens;
6684
6820
  totals.outputTokens += row.outputTokens;
@@ -6688,7 +6824,14 @@ var JsonlUsageEventStore = class {
6688
6824
  totals.costUsd += row.costUsd;
6689
6825
  totals.costSavedByCacheUsd += row.costSavedByCacheUsd;
6690
6826
  totals.eventCount += 1;
6827
+ const promptSideTokens = row.inputTokens + row.cacheReadTokens + row.cacheCreationTokens;
6828
+ if (promptSideTokens > 0) {
6829
+ totals.cacheEligibleEventCount += 1;
6830
+ if (row.cacheReadTokens === 0) totals.coldCacheEventCount += 1;
6831
+ perEventHitRates.push(row.cacheReadTokens / promptSideTokens);
6832
+ }
6691
6833
  }
6834
+ totals.medianCacheHitRate = median(perEventHitRates);
6692
6835
  return totals;
6693
6836
  }
6694
6837
  async getByModel(range) {
@@ -6922,6 +7065,15 @@ var NUMERIC_FIELDS = [
6922
7065
  ];
6923
7066
  var NULLABLE_STRING_FIELDS = ["messageId", "parentMessageId", "sessionId", "apiKeyId"];
6924
7067
  var isStringOrNull = (v) => v === null || typeof v === "string";
7068
+ var CACHE_KEY_SOURCES = /* @__PURE__ */ new Set([
7069
+ "client",
7070
+ "session-header",
7071
+ "thread-header",
7072
+ "body-session-id",
7073
+ "body-thread-id",
7074
+ "content-fingerprint",
7075
+ "none"
7076
+ ]);
6925
7077
  function isUsageEventRecord(parsed) {
6926
7078
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
6927
7079
  const r = parsed;
@@ -6929,6 +7081,10 @@ function isUsageEventRecord(parsed) {
6929
7081
  if (typeof r["providerId"] !== "string") return false;
6930
7082
  if (typeof r["model"] !== "string") return false;
6931
7083
  if (typeof r["engineOrigin"] !== "string") return false;
7084
+ if (r["cacheKeySource"] !== void 0 && (typeof r["cacheKeySource"] !== "string" || !CACHE_KEY_SOURCES.has(r["cacheKeySource"]))) return false;
7085
+ if (r["cacheKeyInjected"] !== void 0 && typeof r["cacheKeyInjected"] !== "boolean") {
7086
+ return false;
7087
+ }
6932
7088
  for (const f of NULLABLE_STRING_FIELDS) {
6933
7089
  if (!isStringOrNull(r[f])) return false;
6934
7090
  }
@@ -6938,6 +7094,12 @@ function isUsageEventRecord(parsed) {
6938
7094
  }
6939
7095
  return true;
6940
7096
  }
7097
+ function median(values) {
7098
+ if (values.length === 0) return null;
7099
+ values.sort((a, b) => a - b);
7100
+ const middle = Math.floor(values.length / 2);
7101
+ return values.length % 2 === 1 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
7102
+ }
6941
7103
 
6942
7104
  // src/ports/JsonPricingStore.ts
6943
7105
  var import_node_fs13 = require("fs");
@@ -7298,7 +7460,7 @@ var JsonVoucherDb = class {
7298
7460
 
7299
7461
  // src/ports/JsonSubscriptionCredentialStore.ts
7300
7462
  var import_node_fs17 = require("fs");
7301
- var import_node_path11 = require("path");
7463
+ var import_node_path10 = require("path");
7302
7464
  var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
7303
7465
  var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
7304
7466
  var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
@@ -7349,9 +7511,9 @@ function findDuplicateCredentialIds(accounts) {
7349
7511
  // src/ports/external-cli-credentials.ts
7350
7512
  var import_node_fs16 = require("fs");
7351
7513
  var import_node_os3 = require("os");
7352
- var import_node_path10 = require("path");
7514
+ var import_node_path9 = require("path");
7353
7515
  function externalStorePath(provider, home = (0, import_node_os3.homedir)()) {
7354
- return provider === "claude" ? (0, import_node_path10.join)(home, ".claude", ".credentials.json") : (0, import_node_path10.join)(home, ".codex", "auth.json");
7516
+ return provider === "claude" ? (0, import_node_path9.join)(home, ".claude", ".credentials.json") : (0, import_node_path9.join)(home, ".codex", "auth.json");
7355
7517
  }
7356
7518
  function decodeJwtExpiryMs(token) {
7357
7519
  try {
@@ -7441,9 +7603,15 @@ var JsonSubscriptionCredentialStore = class {
7441
7603
  * TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
7442
7604
  * through {@link fetchUpstream} with the account's `{ providerId, accountId }`
7443
7605
  * ctx so the per-account/provider proxy applies. `@internal` also a test seam.
7606
+ *
7607
+ * `redactBodies` is REQUIRED here: this round-trip sends the refresh_token and
7608
+ * receives a fresh access/refresh token pair. Carrying a `providerId` opts the
7609
+ * call into the upstream trace (so a failing refresh is diagnosable), and the
7610
+ * trace captures bodies verbatim — without this flag every refresh would write
7611
+ * a plaintext token pair into `upstream-trace.jsonl`.
7444
7612
  */
7445
7613
  buildRefreshFetch(providerId, accountId) {
7446
- return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId, accountId }));
7614
+ return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
7447
7615
  }
7448
7616
  /**
7449
7617
  * In-flight refresh coalescing. OAuth refresh tokens are
@@ -7968,7 +8136,7 @@ var JsonSubscriptionCredentialStore = class {
7968
8136
  * `enc:v1:`; already-`enc:`/`$ENV` untouched) before serializing, so any
7969
8137
  * write incl. child 4's future refresh writes lands encrypted. */
7970
8138
  persist(config) {
7971
- (0, import_node_fs17.mkdirSync)((0, import_node_path11.dirname)(this.tokensPath), { recursive: true });
8139
+ (0, import_node_fs17.mkdirSync)((0, import_node_path10.dirname)(this.tokensPath), { recursive: true });
7972
8140
  const encrypted = encryptTokens(config, this.box);
7973
8141
  (0, import_node_fs17.writeFileSync)(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
7974
8142
  }
@@ -8003,6 +8171,124 @@ var JsonSubscriptionCredentialStore = class {
8003
8171
  // src/AccountHealthProbeScheduler.ts
8004
8172
  var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
8005
8173
 
8174
+ // src/probe/CodexGenerationProbe.ts
8175
+ var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
8176
+ var CODEX_GENERATION_PROBE_MODEL = "gpt-5.6-luna";
8177
+ var CODEX_GENERATION_PROBE_URL = "https://chatgpt.com/backend-api/codex/responses";
8178
+ var MAX_STREAM_BYTES = 256 * 1024;
8179
+ var PROBE_INSTRUCTION = "Return exactly PONG and no other text.";
8180
+ function buildCodexGenerationProbeInit(token, signal) {
8181
+ return {
8182
+ method: "POST",
8183
+ signal,
8184
+ headers: {
8185
+ ...import_codexCliHeaders.DEFAULT_CODEX_CLI_HEADERS,
8186
+ Authorization: `Bearer ${token}`,
8187
+ Accept: (0, import_codexCliHeaders.codexAcceptHeader)(true),
8188
+ "Content-Type": "application/json"
8189
+ },
8190
+ body: JSON.stringify({
8191
+ model: CODEX_GENERATION_PROBE_MODEL,
8192
+ input: [
8193
+ {
8194
+ role: "developer",
8195
+ content: [{ type: "input_text", text: PROBE_INSTRUCTION }]
8196
+ },
8197
+ {
8198
+ role: "user",
8199
+ content: [{ type: "input_text", text: "Connection probe." }]
8200
+ }
8201
+ ],
8202
+ // GPT-5.6 otherwise defaults to medium reasoning. A connectivity probe
8203
+ // needs the lowest-cost path and no tool reasoning.
8204
+ reasoning: { effort: "none" },
8205
+ stream: true,
8206
+ store: false
8207
+ })
8208
+ };
8209
+ }
8210
+ async function readCodexGenerationProbeStream(response) {
8211
+ if (!response.body) return { completed: false, outputChars: 0 };
8212
+ const reader = response.body.getReader();
8213
+ const decoder = new TextDecoder();
8214
+ let buffer = "";
8215
+ let bytes = 0;
8216
+ let outputChars = 0;
8217
+ try {
8218
+ while (true) {
8219
+ const { done, value } = await reader.read();
8220
+ if (done) break;
8221
+ bytes += value.byteLength;
8222
+ if (bytes > MAX_STREAM_BYTES) {
8223
+ await reader.cancel();
8224
+ return { completed: false, outputChars };
8225
+ }
8226
+ buffer += decoder.decode(value, { stream: true });
8227
+ buffer = buffer.replace(/\r\n/g, "\n");
8228
+ let boundary = buffer.indexOf("\n\n");
8229
+ while (boundary >= 0) {
8230
+ const block = buffer.slice(0, boundary);
8231
+ buffer = buffer.slice(boundary + 2);
8232
+ const event = parseSseBlock(block);
8233
+ if (event) {
8234
+ const type = event["type"];
8235
+ if (type === "response.output_text.delta" && typeof event["delta"] === "string") {
8236
+ outputChars += event["delta"].length;
8237
+ } else if (type === "response.output_text.done" && typeof event["text"] === "string") {
8238
+ outputChars = Math.max(outputChars, event["text"].length);
8239
+ } else if (type === "response.failed" || type === "error") {
8240
+ await reader.cancel();
8241
+ return { completed: false, outputChars };
8242
+ } else if (type === "response.completed") {
8243
+ const completedResponse = asRecord(event["response"]);
8244
+ const status = completedResponse?.["status"];
8245
+ outputChars = Math.max(outputChars, countCompletedOutputChars(completedResponse));
8246
+ await reader.cancel();
8247
+ return {
8248
+ completed: (status === void 0 || status === "completed") && outputChars > 0,
8249
+ outputChars
8250
+ };
8251
+ }
8252
+ }
8253
+ boundary = buffer.indexOf("\n\n");
8254
+ }
8255
+ }
8256
+ } catch {
8257
+ return { completed: false, outputChars };
8258
+ } finally {
8259
+ reader.releaseLock();
8260
+ }
8261
+ return { completed: false, outputChars };
8262
+ }
8263
+ function parseSseBlock(block) {
8264
+ const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
8265
+ if (!data || data === "[DONE]") return null;
8266
+ try {
8267
+ return JSON.parse(data);
8268
+ } catch {
8269
+ return null;
8270
+ }
8271
+ }
8272
+ function asRecord(value) {
8273
+ return value !== null && typeof value === "object" ? value : void 0;
8274
+ }
8275
+ function countCompletedOutputChars(response) {
8276
+ const output = response?.["output"];
8277
+ if (!Array.isArray(output)) return 0;
8278
+ let chars = 0;
8279
+ for (const item of output) {
8280
+ const content = asRecord(item)?.["content"];
8281
+ if (!Array.isArray(content)) continue;
8282
+ for (const part of content) {
8283
+ const record = asRecord(part);
8284
+ if (record?.["type"] === "output_text" && typeof record["text"] === "string") {
8285
+ chars += record["text"].length;
8286
+ }
8287
+ }
8288
+ }
8289
+ return chars;
8290
+ }
8291
+
8006
8292
  // src/probe/ProbeStrategy.ts
8007
8293
  var PROVIDER_PROBE_PLANS = {
8008
8294
  claude: {
@@ -8130,17 +8416,17 @@ var AccountHealthProbeScheduler = class {
8130
8416
  }
8131
8417
  if (readThrew) {
8132
8418
  this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
8133
- return { ok: false, marked: false };
8419
+ return { ok: false, marked: false, tier: "local" };
8134
8420
  }
8135
8421
  if (!token) {
8136
8422
  this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
8137
8423
  this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
8138
- return { ok: false, marked: true };
8424
+ return { ok: false, marked: true, tier: "local" };
8139
8425
  }
8140
8426
  const plan = this.planFor(providerId);
8141
8427
  if (plan.kind === "local") {
8142
8428
  this.record(providerId, accountId, { ts: now, ok: true, tier: "local" });
8143
- return { ok: true, marked: false };
8429
+ return { ok: true, marked: false, tier: "local" };
8144
8430
  }
8145
8431
  const start = this.now();
8146
8432
  let status = null;
@@ -8165,7 +8451,60 @@ var AccountHealthProbeScheduler = class {
8165
8451
  latencyMs,
8166
8452
  tier: "upstream"
8167
8453
  });
8168
- return { ok: status !== null && status < 400, marked };
8454
+ return { ok: status !== null && status < 400, marked, tier: "upstream" };
8455
+ }
8456
+ /**
8457
+ * Manual connection test. Codex performs a real, quota-consuming generation;
8458
+ * every other provider keeps its existing cheap probe. Scheduled sweeps never
8459
+ * call this method, so they remain non-billable.
8460
+ */
8461
+ async testAccountConnection(providerId, accountId) {
8462
+ if (providerId !== "codex") return this.probeAccount(providerId, accountId);
8463
+ const now = this.now();
8464
+ let token;
8465
+ try {
8466
+ token = await this.store.getAccessTokenForAccount(providerId, accountId);
8467
+ } catch {
8468
+ this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
8469
+ return { ok: false, marked: false, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
8470
+ }
8471
+ if (!token) {
8472
+ this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
8473
+ this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
8474
+ return { ok: false, marked: true, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
8475
+ }
8476
+ const startedAt = this.now();
8477
+ let attempt = await this.runCodexGenerationAttempt(accountId, token);
8478
+ if (attempt.status === 401 && this.store.refreshAccountToken) {
8479
+ try {
8480
+ if (await this.store.refreshAccountToken(providerId, accountId)) {
8481
+ const refreshed = await this.store.getAccessTokenForAccount(providerId, accountId);
8482
+ if (refreshed) attempt = await this.runCodexGenerationAttempt(accountId, refreshed);
8483
+ }
8484
+ } catch {
8485
+ }
8486
+ }
8487
+ const latencyMs = this.now() - startedAt;
8488
+ const ok = attempt.status !== null && attempt.status >= 200 && attempt.status < 300 && attempt.completed;
8489
+ let marked = false;
8490
+ if (ok) {
8491
+ this.health.clearTransientMark(providerId, accountId);
8492
+ } else if (attempt.status === 401 || attempt.status === 403) {
8493
+ marked = this.applyOutcome(providerId, accountId, attempt.status, attempt.bodyText, now);
8494
+ }
8495
+ this.record(providerId, accountId, {
8496
+ ts: now,
8497
+ ok,
8498
+ status: attempt.status,
8499
+ latencyMs,
8500
+ tier: "generation"
8501
+ });
8502
+ return {
8503
+ ok,
8504
+ marked,
8505
+ tier: "generation",
8506
+ model: CODEX_GENERATION_PROBE_MODEL
8507
+ };
8169
8508
  }
8170
8509
  /** Per-account rolling history for the authed admin surface (design D5). */
8171
8510
  getAllHistory() {
@@ -8222,6 +8561,24 @@ var AccountHealthProbeScheduler = class {
8222
8561
  return "";
8223
8562
  }
8224
8563
  }
8564
+ async runCodexGenerationAttempt(accountId, token) {
8565
+ try {
8566
+ const timeoutMs = Math.max(this.config.timeoutMs, 15e3);
8567
+ const response = await this.fetchImpl(
8568
+ CODEX_GENERATION_PROBE_URL,
8569
+ buildCodexGenerationProbeInit(token, AbortSignal.timeout(timeoutMs)),
8570
+ { providerId: "codex", accountId, redactBodies: true }
8571
+ );
8572
+ if (response.status < 200 || response.status >= 300) {
8573
+ const bodyText = response.status === 403 ? await this.readBounded(response) : void 0;
8574
+ return { status: response.status, completed: false, bodyText };
8575
+ }
8576
+ const stream = await readCodexGenerationProbeStream(response);
8577
+ return { status: response.status, completed: stream.completed };
8578
+ } catch {
8579
+ return { status: null, completed: false };
8580
+ }
8581
+ }
8225
8582
  key(providerId, accountId) {
8226
8583
  return `${providerId}${KEY_SEP}${accountId}`;
8227
8584
  }
@@ -8317,7 +8674,7 @@ var AccountHealthSweeper = class {
8317
8674
  };
8318
8675
 
8319
8676
  // src/audit/AuditPruneSweeper.ts
8320
- var import_node_fs18 = require("fs");
8677
+ var import_node_fs19 = require("fs");
8321
8678
  var import_node_path12 = require("path");
8322
8679
 
8323
8680
  // src/audit/auditFiles.ts
@@ -8340,12 +8697,206 @@ function auditFileDateMs(fileName) {
8340
8697
  return d.getTime();
8341
8698
  }
8342
8699
 
8700
+ // src/audit/auditStats.ts
8701
+ var import_node_fs18 = require("fs");
8702
+ var import_node_path11 = require("path");
8703
+ var SIDECAR_VERSION = 1;
8704
+ var META_PREFIX_BYTES = 64 * 1024;
8705
+ var READ_CHUNK_BYTES = 4 * 1024 * 1024;
8706
+ function auditStatsFileName(auditFile) {
8707
+ return auditFile.replace(/\.jsonl$/, ".stats.json");
8708
+ }
8709
+ function readPersisted(path2) {
8710
+ if (!(0, import_node_fs18.existsSync)(path2)) return null;
8711
+ try {
8712
+ const value = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
8713
+ 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)) {
8714
+ return null;
8715
+ }
8716
+ return value;
8717
+ } catch {
8718
+ return null;
8719
+ }
8720
+ }
8721
+ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
8722
+ const statsPath = (0, import_node_path11.join)((0, import_node_path11.dirname)(auditPath), auditStatsFileName((0, import_node_path11.basename)(auditPath)));
8723
+ const previous = auditBytesBefore === 0 ? {
8724
+ version: SIDECAR_VERSION,
8725
+ auditBytes: 0,
8726
+ requestCount: 0,
8727
+ errorCount: 0,
8728
+ complete: true,
8729
+ minTs: null,
8730
+ maxTs: null
8731
+ } : readPersisted(statsPath);
8732
+ if (!previous || !previous.complete || previous.auditBytes !== auditBytesBefore) return;
8733
+ const next = {
8734
+ version: SIDECAR_VERSION,
8735
+ auditBytes: auditBytesAfter,
8736
+ requestCount: previous.requestCount + 1,
8737
+ errorCount: previous.errorCount + (record.status >= 400 || Boolean(record.error) ? 1 : 0),
8738
+ complete: true,
8739
+ minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
8740
+ maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
8741
+ };
8742
+ (0, import_node_fs18.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
8743
+ }
8744
+ function queryCovers(stats, from, to) {
8745
+ return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
8746
+ }
8747
+ function fileOverlaps(file, from, to) {
8748
+ const start = auditFileDateMs(file);
8749
+ if (start === null) return false;
8750
+ const date = new Date(start);
8751
+ const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
8752
+ return end > from && start <= to;
8753
+ }
8754
+ function parseMetadataPrefix(prefix, prefixTruncated) {
8755
+ const text = prefix.toString("utf8");
8756
+ const tsMatch = /(?:^|,)"ts":(-?\d+)/.exec(text);
8757
+ const statusMatch = /(?:^|,)"status":(-?\d+)/.exec(text);
8758
+ const errorMatch = /(?:^|,)"error":"((?:\\.|[^"\\])*)"/.exec(text);
8759
+ const bodyStarted = /,(?:"requestBody"|"responseBody"):/.test(text);
8760
+ return {
8761
+ ts: tsMatch ? Number(tsMatch[1]) : void 0,
8762
+ status: statusMatch ? Number(statusMatch[1]) : void 0,
8763
+ hasError: Boolean(errorMatch?.[1]),
8764
+ complete: Boolean(tsMatch && statusMatch && (!prefixTruncated || bodyStarted))
8765
+ };
8766
+ }
8767
+ async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
8768
+ let requestCount = 0;
8769
+ let errorCount = 0;
8770
+ let filteredRequestCount = 0;
8771
+ let filteredErrorCount = 0;
8772
+ let minTs = null;
8773
+ let maxTs = null;
8774
+ let complete = true;
8775
+ let prefixParts = [];
8776
+ let prefixBytes = 0;
8777
+ let prefixTruncated = false;
8778
+ const consumeLine = () => {
8779
+ if (prefixBytes === 0 && !prefixTruncated) return;
8780
+ const prefix = Buffer.concat(prefixParts, prefixBytes);
8781
+ const metadata = parseMetadataPrefix(prefix, prefixTruncated);
8782
+ if (!metadata.complete || metadata.ts === void 0 || metadata.status === void 0) {
8783
+ complete = false;
8784
+ } else {
8785
+ requestCount += 1;
8786
+ const isError = metadata.status >= 400 || metadata.hasError;
8787
+ if (isError) errorCount += 1;
8788
+ minTs = minTs === null ? metadata.ts : Math.min(minTs, metadata.ts);
8789
+ maxTs = maxTs === null ? metadata.ts : Math.max(maxTs, metadata.ts);
8790
+ if (metadata.ts >= from && metadata.ts <= to) {
8791
+ filteredRequestCount += 1;
8792
+ if (isError) filteredErrorCount += 1;
8793
+ }
8794
+ }
8795
+ prefixParts = [];
8796
+ prefixBytes = 0;
8797
+ prefixTruncated = false;
8798
+ };
8799
+ if (auditBytes > startByte) {
8800
+ const stream = (0, import_node_fs18.createReadStream)(auditPath, {
8801
+ start: startByte,
8802
+ end: auditBytes - 1,
8803
+ highWaterMark: READ_CHUNK_BYTES
8804
+ });
8805
+ for await (const value of stream) {
8806
+ const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
8807
+ let offset = 0;
8808
+ while (offset < chunk.length) {
8809
+ const newline = chunk.indexOf(10, offset);
8810
+ const end = newline === -1 ? chunk.length : newline;
8811
+ if (prefixBytes < META_PREFIX_BYTES) {
8812
+ const retained = Math.min(META_PREFIX_BYTES - prefixBytes, end - offset);
8813
+ if (retained > 0) {
8814
+ prefixParts.push(Buffer.from(chunk.subarray(offset, offset + retained)));
8815
+ prefixBytes += retained;
8816
+ }
8817
+ if (retained < end - offset) prefixTruncated = true;
8818
+ } else if (end > offset) {
8819
+ prefixTruncated = true;
8820
+ }
8821
+ if (newline === -1) break;
8822
+ consumeLine();
8823
+ offset = newline + 1;
8824
+ }
8825
+ }
8826
+ }
8827
+ if (prefixBytes > 0 || prefixTruncated) complete = false;
8828
+ return {
8829
+ all: {
8830
+ version: SIDECAR_VERSION,
8831
+ auditBytes,
8832
+ requestCount,
8833
+ errorCount,
8834
+ complete,
8835
+ minTs,
8836
+ maxTs
8837
+ },
8838
+ filtered: { requestCount: filteredRequestCount, errorCount: filteredErrorCount, complete }
8839
+ };
8840
+ }
8841
+ function mergePersistedStats(previous, appended) {
8842
+ return {
8843
+ version: SIDECAR_VERSION,
8844
+ auditBytes: appended.auditBytes,
8845
+ requestCount: previous.requestCount + appended.requestCount,
8846
+ errorCount: previous.errorCount + appended.errorCount,
8847
+ complete: previous.complete && appended.complete,
8848
+ minTs: previous.minTs === null ? appended.minTs : appended.minTs === null ? previous.minTs : Math.min(previous.minTs, appended.minTs),
8849
+ maxTs: previous.maxTs === null ? appended.maxTs : appended.maxTs === null ? previous.maxTs : Math.max(previous.maxTs, appended.maxTs)
8850
+ };
8851
+ }
8852
+ async function readAuditStats(auditDir, query2 = {}) {
8853
+ if (!(0, import_node_fs18.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
8854
+ const from = typeof query2.from === "number" ? query2.from : -Infinity;
8855
+ const to = typeof query2.to === "number" ? query2.to : Infinity;
8856
+ let files;
8857
+ try {
8858
+ files = (0, import_node_fs18.readdirSync)(auditDir).filter((file) => AUDIT_FILE_RE.test(file) && fileOverlaps(file, from, to)).sort();
8859
+ } catch {
8860
+ return { requestCount: 0, errorCount: 0, complete: false };
8861
+ }
8862
+ const total = { requestCount: 0, errorCount: 0, complete: true };
8863
+ for (const file of files) {
8864
+ const auditPath = (0, import_node_path11.join)(auditDir, file);
8865
+ try {
8866
+ const auditBytes = (0, import_node_fs18.statSync)(auditPath).size;
8867
+ const statsPath = (0, import_node_path11.join)(auditDir, auditStatsFileName(file));
8868
+ const persisted = readPersisted(statsPath);
8869
+ if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
8870
+ total.requestCount += persisted.requestCount;
8871
+ total.errorCount += persisted.errorCount;
8872
+ continue;
8873
+ }
8874
+ const resumable = persisted && persisted.complete && persisted.auditBytes < auditBytes && queryCovers(persisted, from, to) ? persisted : null;
8875
+ const scanned = await scanAuditFile(
8876
+ auditPath,
8877
+ resumable?.auditBytes ?? 0,
8878
+ auditBytes,
8879
+ from,
8880
+ to
8881
+ );
8882
+ total.requestCount += scanned.filtered.requestCount + (resumable?.requestCount ?? 0);
8883
+ total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
8884
+ total.complete = total.complete && scanned.filtered.complete;
8885
+ const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
8886
+ if (current.complete) (0, import_node_fs18.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
8887
+ } catch {
8888
+ total.complete = false;
8889
+ }
8890
+ }
8891
+ return total;
8892
+ }
8893
+
8343
8894
  // src/audit/AuditPruneSweeper.ts
8344
8895
  var DAY_MS = 24 * 60 * 6e4;
8345
8896
  var SWEEP_INTERVAL_MS2 = 60 * 6e4;
8346
8897
  var AuditPruneSweeper = class {
8347
- constructor(auditDir2, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
8348
- this.auditDir = auditDir2;
8898
+ constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
8899
+ this.auditDir = auditDir;
8349
8900
  this.logger = logger;
8350
8901
  this.config = config;
8351
8902
  this.intervalMs = intervalMs;
@@ -8392,17 +8943,19 @@ var AuditPruneSweeper = class {
8392
8943
  if (!this.config.enabled || this.sweeping) return 0;
8393
8944
  this.sweeping = true;
8394
8945
  try {
8395
- if (!(0, import_node_fs18.existsSync)(this.auditDir)) return 0;
8946
+ if (!(0, import_node_fs19.existsSync)(this.auditDir)) return 0;
8396
8947
  const today = new Date(this.now());
8397
8948
  const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
8398
8949
  const cutoff = todayMidnight - (this.config.retentionDays - 1) * DAY_MS;
8399
8950
  let removed = 0;
8400
- for (const file of (0, import_node_fs18.readdirSync)(this.auditDir)) {
8951
+ for (const file of (0, import_node_fs19.readdirSync)(this.auditDir)) {
8401
8952
  const dateMs = auditFileDateMs(file);
8402
8953
  if (dateMs === null || dateMs >= cutoff) continue;
8403
8954
  try {
8404
- (0, import_node_fs18.unlinkSync)((0, import_node_path12.join)(this.auditDir, file));
8955
+ (0, import_node_fs19.unlinkSync)((0, import_node_path12.join)(this.auditDir, file));
8405
8956
  removed += 1;
8957
+ const statsPath = (0, import_node_path12.join)(this.auditDir, auditStatsFileName(file));
8958
+ if ((0, import_node_fs19.existsSync)(statsPath)) (0, import_node_fs19.unlinkSync)(statsPath);
8406
8959
  } catch (error) {
8407
8960
  this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
8408
8961
  file,
@@ -8424,15 +8977,15 @@ var AuditPruneSweeper = class {
8424
8977
  };
8425
8978
 
8426
8979
  // src/audit/auditReader.ts
8427
- var import_node_fs19 = require("fs");
8980
+ var import_node_fs20 = require("fs");
8428
8981
  var import_node_path13 = require("path");
8429
8982
  var DEFAULT_LIMIT = 200;
8430
8983
  var MAX_LIMIT = 2e3;
8431
- function readAuditRecords(auditDir2, query2 = {}) {
8432
- if (!(0, import_node_fs19.existsSync)(auditDir2)) return [];
8984
+ function readAuditRecords(auditDir, query2 = {}) {
8985
+ if (!(0, import_node_fs20.existsSync)(auditDir)) return [];
8433
8986
  let files;
8434
8987
  try {
8435
- files = (0, import_node_fs19.readdirSync)(auditDir2).filter((f) => AUDIT_FILE_RE.test(f));
8988
+ files = (0, import_node_fs20.readdirSync)(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
8436
8989
  } catch {
8437
8990
  return [];
8438
8991
  }
@@ -8443,7 +8996,7 @@ function readAuditRecords(auditDir2, query2 = {}) {
8443
8996
  for (const file of files.sort().reverse()) {
8444
8997
  let raw;
8445
8998
  try {
8446
- raw = (0, import_node_fs19.readFileSync)((0, import_node_path13.join)(auditDir2, file), "utf8");
8999
+ raw = (0, import_node_fs20.readFileSync)((0, import_node_path13.join)(auditDir, file), "utf8");
8447
9000
  } catch {
8448
9001
  continue;
8449
9002
  }
@@ -8472,11 +9025,11 @@ function isAuditRecord(value) {
8472
9025
  }
8473
9026
 
8474
9027
  // src/audit/AuditWriter.ts
8475
- var import_node_fs20 = require("fs");
9028
+ var import_node_fs21 = require("fs");
8476
9029
  var import_node_path14 = require("path");
8477
9030
  var AuditWriter = class {
8478
- constructor(auditDir2, logger, defer = (fn) => setTimeout(fn, 0)) {
8479
- this.auditDir = auditDir2;
9031
+ constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
9032
+ this.auditDir = auditDir;
8480
9033
  this.logger = logger;
8481
9034
  this.defer = defer;
8482
9035
  }
@@ -8506,16 +9059,30 @@ var AuditWriter = class {
8506
9059
  */
8507
9060
  appendNow(record) {
8508
9061
  if (!this.dirEnsured) {
8509
- (0, import_node_fs20.mkdirSync)(this.auditDir, { recursive: true });
9062
+ (0, import_node_fs21.mkdirSync)(this.auditDir, { recursive: true });
8510
9063
  this.dirEnsured = true;
8511
9064
  }
8512
9065
  const file = (0, import_node_path14.join)(this.auditDir, auditFileName(record.ts));
8513
- (0, import_node_fs20.appendFileSync)(file, JSON.stringify(record) + "\n", "utf8");
9066
+ const line = JSON.stringify(record) + "\n";
9067
+ const auditBytesBefore = (0, import_node_fs21.existsSync)(file) ? (0, import_node_fs21.statSync)(file).size : 0;
9068
+ (0, import_node_fs21.appendFileSync)(file, line, "utf8");
9069
+ try {
9070
+ updateAuditStatsAfterAppend(
9071
+ file,
9072
+ auditBytesBefore,
9073
+ auditBytesBefore + Buffer.byteLength(line, "utf8"),
9074
+ record
9075
+ );
9076
+ } catch (error) {
9077
+ this.logger.warn("[AuditWriter] failed to update audit stats", {
9078
+ error: error instanceof Error ? error.message : String(error)
9079
+ });
9080
+ }
8514
9081
  }
8515
9082
  };
8516
9083
 
8517
9084
  // src/billing/BillingPublisher.ts
8518
- var import_node_fs21 = require("fs");
9085
+ var import_node_fs22 = require("fs");
8519
9086
  var import_node_crypto13 = require("crypto");
8520
9087
  var import_node_path15 = require("path");
8521
9088
  var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
@@ -8589,7 +9156,7 @@ var BillingPublisher = class {
8589
9156
  appendNow(event) {
8590
9157
  this.ensureDir();
8591
9158
  const file = (0, import_node_path15.join)(this.billingDir, billingFileName(event.ts));
8592
- (0, import_node_fs21.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
9159
+ (0, import_node_fs22.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
8593
9160
  }
8594
9161
  /**
8595
9162
  * One best-effort delivery attempt for an event ALREADY in the ledger. POSTs the
@@ -8639,7 +9206,7 @@ var BillingPublisher = class {
8639
9206
  try {
8640
9207
  this.ensureDir();
8641
9208
  const file = (0, import_node_path15.join)(this.billingDir, deliveredFileName(event.ts));
8642
- (0, import_node_fs21.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
9209
+ (0, import_node_fs22.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
8643
9210
  } catch (error) {
8644
9211
  this.logger.warn("[BillingPublisher] failed to append delivery marker", {
8645
9212
  error: error instanceof Error ? error.message : String(error)
@@ -8648,20 +9215,20 @@ var BillingPublisher = class {
8648
9215
  }
8649
9216
  ensureDir() {
8650
9217
  if (this.dirEnsured) return;
8651
- (0, import_node_fs21.mkdirSync)(this.billingDir, { recursive: true });
9218
+ (0, import_node_fs22.mkdirSync)(this.billingDir, { recursive: true });
8652
9219
  this.dirEnsured = true;
8653
9220
  }
8654
9221
  };
8655
9222
 
8656
9223
  // src/billing/billingReader.ts
8657
- var import_node_fs22 = require("fs");
9224
+ var import_node_fs23 = require("fs");
8658
9225
  var import_node_path16 = require("path");
8659
9226
  function readBillingLedger(billingDir) {
8660
9227
  const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
8661
- if (!(0, import_node_fs22.existsSync)(billingDir)) return view;
9228
+ if (!(0, import_node_fs23.existsSync)(billingDir)) return view;
8662
9229
  let files;
8663
9230
  try {
8664
- files = (0, import_node_fs22.readdirSync)(billingDir);
9231
+ files = (0, import_node_fs23.readdirSync)(billingDir);
8665
9232
  } catch {
8666
9233
  return view;
8667
9234
  }
@@ -8692,7 +9259,7 @@ function readBillingStatus(billingDir) {
8692
9259
  function parseLines(dir, file) {
8693
9260
  let raw;
8694
9261
  try {
8695
- raw = (0, import_node_fs22.readFileSync)((0, import_node_path16.join)(dir, file), "utf8");
9262
+ raw = (0, import_node_fs23.readFileSync)((0, import_node_path16.join)(dir, file), "utf8");
8696
9263
  } catch {
8697
9264
  return [];
8698
9265
  }
@@ -9064,7 +9631,7 @@ function buildDaemon(config, paths) {
9064
9631
  (0, import_outbound_api5.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
9065
9632
  );
9066
9633
  const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
9067
- const keyDb = new JsonOutboundKeyDb(paths.keysPath);
9634
+ const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
9068
9635
  const voucherDb = new JsonVoucherDb(defaultVouchersPath(paths.configPath));
9069
9636
  const settingsStore = new JsonApiServerSettingsStore(paths.configPath, secretBox3);
9070
9637
  const integrationStateStore = new IntegrationStateStore(
@@ -9165,7 +9732,7 @@ function buildDaemon(config, paths) {
9165
9732
  // lines through the injected logger (honors level/format/file sink).
9166
9733
  logger
9167
9734
  });
9168
- const auditDir2 = defaultAuditDir(paths.configPath);
9735
+ const auditDir = defaultAuditDir(paths.configPath);
9169
9736
  const billingDir = defaultBillingDir(paths.configPath);
9170
9737
  const adminServer = new AdminServer({
9171
9738
  configPath: paths.configPath,
@@ -9199,10 +9766,16 @@ function buildDaemon(config, paths) {
9199
9766
  // (NOT widening the least-authority writer — no token-returning read reachable).
9200
9767
  oauthSessions: new OAuthSessionStore(),
9201
9768
  // Real global fetch by default; a test seam (`paths.oauthExchangeFetch`) can
9202
- // inject a mock so no real token endpoint is hit.
9203
- // upstream-proxy: default the OAuth token-exchange fetch to the proxy-aware
9204
- // helper so interactive login honors a configured proxy (global/env layers).
9205
- oauthExchangeFetch: paths.oauthExchangeFetch ?? ((url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init)),
9769
+ // inject a mock so no real token endpoint is hit (one FetchLike for every
9770
+ // provider the ctx below only matters on the real egress path).
9771
+ //
9772
+ // upstream-proxy: a PER-PROVIDER factory, so the exchange carries the same
9773
+ // `{ providerId }` ctx the CLI login and the token refresh already pass.
9774
+ // Without it the interactive login resolved only the global/env proxy layers
9775
+ // — `server.proxy.byProvider[...]` was silently skipped — and the call was
9776
+ // excluded from the upstream trace, so a failing login left no evidence.
9777
+ // `redactBodies` keeps the code/verifier + minted token out of that trace.
9778
+ oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init, { providerId, redactBodies: true }),
9206
9779
  subscriptionAccountAppender: credentialStore,
9207
9780
  // Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
9208
9781
  // + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
@@ -9254,7 +9827,8 @@ function buildDaemon(config, paths) {
9254
9827
  // date-rotated audit store. Bound to the store dir here so the AdminServer
9255
9828
  // carries no path/store coupling. Records hold IP/UA/bodies → admin-only,
9256
9829
  // NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
9257
- auditReader: (query2) => readAuditRecords(auditDir2, query2),
9830
+ auditReader: (query2) => readAuditRecords(auditDir, query2),
9831
+ auditStatsReader: (query2) => readAuditStats(auditDir, query2),
9258
9832
  // billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
9259
9833
  // secret-free total/delivered/pending counts of the durable ledger.
9260
9834
  billingStatusReader: () => readBillingStatus(billingDir)
@@ -9264,9 +9838,9 @@ function buildDaemon(config, paths) {
9264
9838
  fetchImpl: (url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init)
9265
9839
  });
9266
9840
  setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth3.getSharedAccountHealth)());
9267
- const auditWriter = new AuditWriter(auditDir2, logger);
9268
- const auditPruneSweeper = new AuditPruneSweeper(auditDir2, logger, import_audit_types.DEFAULT_AUDIT_CONFIG);
9269
- setAuditRuntime(auditWriter, auditPruneSweeper, auditDir2);
9841
+ const auditWriter = new AuditWriter(auditDir, logger);
9842
+ const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, import_audit_types.DEFAULT_AUDIT_CONFIG);
9843
+ setAuditRuntime(auditWriter, auditPruneSweeper);
9270
9844
  const billingPublisher = new BillingPublisher(billingDir, logger);
9271
9845
  const billingRetrySweeper = new BillingRetrySweeper(
9272
9846
  billingDir,
@@ -9312,8 +9886,8 @@ function buildDaemon(config, paths) {
9312
9886
  }
9313
9887
  function isTokensStoreReadable(tokensPath) {
9314
9888
  try {
9315
- if (!(0, import_node_fs23.existsSync)(tokensPath)) return true;
9316
- (0, import_node_fs23.accessSync)(tokensPath, import_node_fs23.constants.R_OK);
9889
+ if (!(0, import_node_fs24.existsSync)(tokensPath)) return true;
9890
+ (0, import_node_fs24.accessSync)(tokensPath, import_node_fs24.constants.R_OK);
9317
9891
  return true;
9318
9892
  } catch {
9319
9893
  return false;
@@ -9361,7 +9935,7 @@ function resolveInPathDefault(candidate) {
9361
9935
  const segments = (process.env["PATH"] ?? "").split(import_node_path17.delimiter).filter(Boolean);
9362
9936
  for (const seg of segments) {
9363
9937
  const full = (0, import_node_path17.join)(seg, candidate);
9364
- if ((0, import_node_fs24.existsSync)(full)) return full;
9938
+ if ((0, import_node_fs25.existsSync)(full)) return full;
9365
9939
  }
9366
9940
  return null;
9367
9941
  }
@@ -9563,7 +10137,7 @@ async function runLogin(argv, deps) {
9563
10137
  (0, import_upstreamFetch9.setUpstreamProxyResolver)(createUpstreamProxyResolver());
9564
10138
  try {
9565
10139
  const tokensPath = defaultTokensPath(values.config);
9566
- const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init, { providerId: provider }));
10140
+ const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init, { providerId: provider, redactBodies: true }));
9567
10141
  const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
9568
10142
  const expiresAt = await runProviderLogin(
9569
10143
  provider,
@@ -9849,7 +10423,7 @@ function providersRmKey(configPath, providerId, keyId) {
9849
10423
  }
9850
10424
 
9851
10425
  // src/commands/secrets.ts
9852
- var import_node_fs25 = require("fs");
10426
+ var import_node_fs26 = require("fs");
9853
10427
  var import_node_util7 = require("util");
9854
10428
  async function runSecrets(argv) {
9855
10429
  const { values, positionals } = (0, import_node_util7.parseArgs)({
@@ -9922,12 +10496,12 @@ function secretsStatus(args) {
9922
10496
  reportField("admin.token", cfg.admin.token);
9923
10497
  }
9924
10498
  const tokensPath = defaultTokensPath(args.config);
9925
- if ((0, import_node_fs25.existsSync)(tokensPath)) {
10499
+ if ((0, import_node_fs26.existsSync)(tokensPath)) {
9926
10500
  console.info(`Secret status for ${tokensPath}:`);
9927
10501
  reportTokenFields(tokensPath);
9928
10502
  }
9929
10503
  const integrationsPath = defaultIntegrationsPath(args.config);
9930
- if ((0, import_node_fs25.existsSync)(integrationsPath)) {
10504
+ if ((0, import_node_fs26.existsSync)(integrationsPath)) {
9931
10505
  const state = readRawJson(integrationsPath);
9932
10506
  const key = state.gatewayKey;
9933
10507
  if (key && typeof key === "object" && !Array.isArray(key)) {
@@ -9981,8 +10555,8 @@ async function secretsRotate(args) {
9981
10555
  const integrationsPath = defaultIntegrationsPath(args.config);
9982
10556
  try {
9983
10557
  cfg = loadConfig(args.config);
9984
- if ((0, import_node_fs25.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
9985
- if ((0, import_node_fs25.existsSync)(integrationsPath)) {
10558
+ if ((0, import_node_fs26.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
10559
+ if ((0, import_node_fs26.existsSync)(integrationsPath)) {
9986
10560
  integrationsPlain = new IntegrationStateStore(integrationsPath, oldBox).load();
9987
10561
  }
9988
10562
  } finally {
@@ -10017,20 +10591,20 @@ function secretsDecrypt(args) {
10017
10591
  let tokensPlain = null;
10018
10592
  try {
10019
10593
  cfg = loadConfig(args.config);
10020
- if ((0, import_node_fs25.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
10594
+ if ((0, import_node_fs26.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
10021
10595
  } finally {
10022
10596
  setSecretBox(null);
10023
10597
  }
10024
10598
  saveConfig(args.config, cfg);
10025
10599
  if (tokensPlain) {
10026
- (0, import_node_fs25.writeFileSync)(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
10600
+ (0, import_node_fs26.writeFileSync)(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
10027
10601
  }
10028
10602
  console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
10029
10603
  }
10030
10604
  function readRawConfig(path2) {
10031
10605
  let parsed;
10032
10606
  try {
10033
- parsed = JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8"));
10607
+ parsed = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
10034
10608
  } catch {
10035
10609
  throw new Error(`secrets: cannot read or parse '${path2}'`);
10036
10610
  }
@@ -10038,7 +10612,7 @@ function readRawConfig(path2) {
10038
10612
  }
10039
10613
  function readRawJson(path2) {
10040
10614
  try {
10041
- const parsed = JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8"));
10615
+ const parsed = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
10042
10616
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
10043
10617
  return parsed;
10044
10618
  }
@@ -10048,13 +10622,13 @@ function readRawJson(path2) {
10048
10622
  }
10049
10623
  function encryptTokensFileInPlace(configPath, box) {
10050
10624
  const tokensPath = defaultTokensPath(configPath);
10051
- if (!(0, import_node_fs25.existsSync)(tokensPath)) return;
10625
+ if (!(0, import_node_fs26.existsSync)(tokensPath)) return;
10052
10626
  const plain = decryptTokensFile(tokensPath, box);
10053
10627
  writeTokensEncrypted(tokensPath, plain, box);
10054
10628
  }
10055
10629
  function rewriteIntegrationState(configPath, readBox, writeBox) {
10056
10630
  const path2 = defaultIntegrationsPath(configPath);
10057
- if (!(0, import_node_fs25.existsSync)(path2)) return;
10631
+ if (!(0, import_node_fs26.existsSync)(path2)) return;
10058
10632
  const state = new IntegrationStateStore(path2, readBox).load();
10059
10633
  new IntegrationStateStore(path2, writeBox).save(state);
10060
10634
  }
@@ -10067,7 +10641,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
10067
10641
  { updatedAt: "", ...plain },
10068
10642
  box
10069
10643
  );
10070
- (0, import_node_fs25.writeFileSync)(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
10644
+ (0, import_node_fs26.writeFileSync)(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
10071
10645
  }
10072
10646
  var TOKEN_FIELDS2 = {
10073
10647
  claude: ["accessToken", "refreshToken"],
@@ -10090,7 +10664,7 @@ function walkTokens(raw, fn) {
10090
10664
  return next;
10091
10665
  }
10092
10666
  function tokensSuffix(configPath) {
10093
- return (0, import_node_fs25.existsSync)(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
10667
+ return (0, import_node_fs26.existsSync)(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
10094
10668
  }
10095
10669
 
10096
10670
  // src/commands/start.ts