@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 +676 -102
- package/dist/cli.js +671 -84
- package/dist/index.cjs +669 -96
- package/dist/index.d.cts +94 -24
- package/dist/index.d.ts +94 -24
- package/dist/index.js +664 -78
- package/package.json +63 -63
package/dist/index.cjs
CHANGED
|
@@ -53,7 +53,7 @@ __export(src_exports, {
|
|
|
53
53
|
module.exports = __toCommonJS(src_exports);
|
|
54
54
|
|
|
55
55
|
// src/bootstrap.ts
|
|
56
|
-
var
|
|
56
|
+
var import_node_fs23 = require("fs");
|
|
57
57
|
var import_audit_types = require("@omnicross/contracts/audit-types");
|
|
58
58
|
var import_billing_types = require("@omnicross/contracts/billing-types");
|
|
59
59
|
var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
|
|
@@ -147,7 +147,7 @@ async function runCodexLoopback(sessionId, codeVerifier, state, signal, deps) {
|
|
|
147
147
|
const code = await deps.codexAwaitLoopback(state, void 0, signal);
|
|
148
148
|
const result = await import_subscriptions.codexOAuth.exchangeCodeForTokens(
|
|
149
149
|
{ authorizationCode: code, codeVerifier, state },
|
|
150
|
-
deps.oauthExchangeFetch
|
|
150
|
+
deps.oauthExchangeFetch("codex")
|
|
151
151
|
);
|
|
152
152
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
153
153
|
const block = {
|
|
@@ -649,6 +649,17 @@ function handleAuditQuery(req, res, reader) {
|
|
|
649
649
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
650
650
|
res.end(JSON.stringify({ records }));
|
|
651
651
|
}
|
|
652
|
+
async function handleAuditStatsQuery(req, res, reader) {
|
|
653
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
654
|
+
const query2 = {};
|
|
655
|
+
const from = intParam(url.searchParams.get("from"));
|
|
656
|
+
if (from !== void 0) query2.from = from;
|
|
657
|
+
const to = intParam(url.searchParams.get("to"));
|
|
658
|
+
if (to !== void 0) query2.to = to;
|
|
659
|
+
const stats = reader ? await reader(query2) : { requestCount: 0, errorCount: 0, complete: true };
|
|
660
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
661
|
+
res.end(JSON.stringify(stats));
|
|
662
|
+
}
|
|
652
663
|
|
|
653
664
|
// src/admin/billingStatusApi.ts
|
|
654
665
|
function handleBillingStatus(res, reader) {
|
|
@@ -2144,7 +2155,13 @@ function listMappablePresets() {
|
|
|
2144
2155
|
name: preset.name,
|
|
2145
2156
|
apiFormat: resolved.format,
|
|
2146
2157
|
baseUrl: preset.api_base_url,
|
|
2147
|
-
models: Array.isArray(preset.models) ? preset.models : []
|
|
2158
|
+
models: Array.isArray(preset.models) ? preset.models : [],
|
|
2159
|
+
nameKey: preset.nameKey,
|
|
2160
|
+
icon: preset.icon,
|
|
2161
|
+
description: preset.description,
|
|
2162
|
+
features: preset.features,
|
|
2163
|
+
website: preset.website,
|
|
2164
|
+
modelsEndpoint: preset.modelsEndpoint
|
|
2148
2165
|
});
|
|
2149
2166
|
}
|
|
2150
2167
|
return { mappable, excluded };
|
|
@@ -2533,7 +2550,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
2533
2550
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
2534
2551
|
if (!sessionId) return err2(400, "oauth complete requires { sessionId }");
|
|
2535
2552
|
if (!rawCode) return err2(400, "oauth complete requires { code }");
|
|
2536
|
-
const session = deps.oauthSessions.
|
|
2553
|
+
const session = deps.oauthSessions.peek(sessionId);
|
|
2537
2554
|
if (!session) return err2(410, "oauth session is unknown, expired, or already used");
|
|
2538
2555
|
if (session.providerId !== providerId) {
|
|
2539
2556
|
return err2(400, `oauth session does not match provider '${providerId}'`);
|
|
@@ -2547,13 +2564,15 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
2547
2564
|
}
|
|
2548
2565
|
code = splitCode;
|
|
2549
2566
|
}
|
|
2567
|
+
const exchangeFetch = deps.oauthExchangeFetch(providerId);
|
|
2550
2568
|
let block;
|
|
2551
2569
|
try {
|
|
2552
|
-
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state,
|
|
2570
|
+
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
2553
2571
|
} catch (exchangeError) {
|
|
2554
2572
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
2555
2573
|
return err2(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
2556
2574
|
}
|
|
2575
|
+
deps.oauthSessions.consume(sessionId);
|
|
2557
2576
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
2558
2577
|
await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
|
|
2559
2578
|
const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
@@ -2798,8 +2817,8 @@ function validateAuditSegment(patch) {
|
|
|
2798
2817
|
}
|
|
2799
2818
|
}
|
|
2800
2819
|
const maxBodyBytes = audit["maxBodyBytes"];
|
|
2801
|
-
if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes <
|
|
2802
|
-
errors.push("audit.maxBodyBytes must be a non-negative number");
|
|
2820
|
+
if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes < -1)) {
|
|
2821
|
+
errors.push("audit.maxBodyBytes must be -1 or a non-negative number");
|
|
2803
2822
|
}
|
|
2804
2823
|
const retentionDays = audit["retentionDays"];
|
|
2805
2824
|
if (retentionDays !== void 0 && (typeof retentionDays !== "number" || !Number.isFinite(retentionDays) || retentionDays < 0)) {
|
|
@@ -3234,18 +3253,16 @@ function preserveWebhookSecrets(incoming, current) {
|
|
|
3234
3253
|
}
|
|
3235
3254
|
|
|
3236
3255
|
// src/audit/auditRuntime.ts
|
|
3237
|
-
var import_node_path6 = require("path");
|
|
3238
3256
|
var import_auditSink = require("@omnicross/core/pipeline/auditSink");
|
|
3239
3257
|
var import_upstreamTrace = require("@omnicross/core/pipeline/upstreamTrace");
|
|
3240
3258
|
var writer = null;
|
|
3241
3259
|
var sweeper = null;
|
|
3242
|
-
|
|
3243
|
-
function setAuditRuntime(w, s, dir) {
|
|
3260
|
+
function setAuditRuntime(w, s) {
|
|
3244
3261
|
writer = w;
|
|
3245
3262
|
sweeper = s;
|
|
3246
|
-
auditDir = dir;
|
|
3247
3263
|
}
|
|
3248
3264
|
function applyAuditConfig(config) {
|
|
3265
|
+
(0, import_upstreamTrace.setUpstreamTracePath)(null);
|
|
3249
3266
|
const enabled = config?.enabled === true && writer !== null;
|
|
3250
3267
|
if (enabled && config) {
|
|
3251
3268
|
(0, import_auditSink.setAuditCaptureConfig)(config);
|
|
@@ -3255,11 +3272,9 @@ function applyAuditConfig(config) {
|
|
|
3255
3272
|
sweeper.configure(config);
|
|
3256
3273
|
sweeper.start();
|
|
3257
3274
|
}
|
|
3258
|
-
(0, import_upstreamTrace.setUpstreamTracePath)(config.captureBodies ? (0, import_node_path6.join)(auditDir, "upstream-trace.jsonl") : null);
|
|
3259
3275
|
} else {
|
|
3260
3276
|
(0, import_auditSink.setAuditCaptureConfig)(null);
|
|
3261
3277
|
(0, import_auditSink.setAuditSink)(null);
|
|
3262
|
-
(0, import_upstreamTrace.setUpstreamTracePath)(null);
|
|
3263
3278
|
if (sweeper) {
|
|
3264
3279
|
if (config) sweeper.configure(config);
|
|
3265
3280
|
sweeper.dispose();
|
|
@@ -3273,7 +3288,6 @@ function resetAuditRuntimeForTests() {
|
|
|
3273
3288
|
if (sweeper) sweeper.dispose();
|
|
3274
3289
|
writer = null;
|
|
3275
3290
|
sweeper = null;
|
|
3276
|
-
auditDir = "";
|
|
3277
3291
|
}
|
|
3278
3292
|
|
|
3279
3293
|
// src/billing/billingRuntime.ts
|
|
@@ -4118,6 +4132,8 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4118
4132
|
}
|
|
4119
4133
|
|
|
4120
4134
|
// src/admin/adminApi.ts
|
|
4135
|
+
var import_AccountRouteActivity = require("@omnicross/core/pipeline/AccountRouteActivity");
|
|
4136
|
+
var import_ServerOverloadCounter = require("@omnicross/core/pipeline/ServerOverloadCounter");
|
|
4121
4137
|
function readBody(req) {
|
|
4122
4138
|
return new Promise((resolve2, reject) => {
|
|
4123
4139
|
const chunks = [];
|
|
@@ -4154,6 +4170,9 @@ function toKeyInfo(row) {
|
|
|
4154
4170
|
id: row.id,
|
|
4155
4171
|
name: row.name,
|
|
4156
4172
|
keyPrefix: row.keyPrefix,
|
|
4173
|
+
// True only when a reversible `keySecret` envelope was persisted at creation
|
|
4174
|
+
// — gates the UI "view key" eye. Legacy hash-only rows read as absent.
|
|
4175
|
+
revealable: Boolean(row.keySecret),
|
|
4157
4176
|
enabled: row.enabled,
|
|
4158
4177
|
createdAt: row.createdAt,
|
|
4159
4178
|
lastUsedAt: row.lastUsedAt,
|
|
@@ -4809,7 +4828,13 @@ function handlePresets(res, method) {
|
|
|
4809
4828
|
name: p.name,
|
|
4810
4829
|
apiFormat: p.apiFormat,
|
|
4811
4830
|
baseUrl: p.baseUrl,
|
|
4812
|
-
models: p.models
|
|
4831
|
+
models: p.models,
|
|
4832
|
+
nameKey: p.nameKey,
|
|
4833
|
+
icon: p.icon,
|
|
4834
|
+
description: p.description,
|
|
4835
|
+
features: p.features,
|
|
4836
|
+
website: p.website,
|
|
4837
|
+
modelsEndpoint: p.modelsEndpoint
|
|
4813
4838
|
}));
|
|
4814
4839
|
return writeJson3(res, 200, { presets, excluded });
|
|
4815
4840
|
}
|
|
@@ -4843,12 +4868,27 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
4843
4868
|
plaintextOnce: created.plaintextOnce
|
|
4844
4869
|
});
|
|
4845
4870
|
}
|
|
4871
|
+
if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
|
|
4872
|
+
const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
|
|
4873
|
+
if (revealed !== null) return writeJson3(res, 200, { key: revealed });
|
|
4874
|
+
const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
|
|
4875
|
+
if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
|
|
4876
|
+
return writeJsonError(
|
|
4877
|
+
res,
|
|
4878
|
+
409,
|
|
4879
|
+
`key '${rest[0]}' is not revealable (created before revealable key storage)`
|
|
4880
|
+
);
|
|
4881
|
+
}
|
|
4846
4882
|
const id = rest[0];
|
|
4847
4883
|
const action = rest[1];
|
|
4848
4884
|
if (method === "POST" && id && action === "revoke") {
|
|
4849
4885
|
const ok = await deps.keyDb.outboundApiKeysRevoke(id);
|
|
4850
4886
|
return writeJson3(res, ok ? 200 : 404, { ok });
|
|
4851
4887
|
}
|
|
4888
|
+
if (method === "DELETE" && id && !action) {
|
|
4889
|
+
const ok = await deps.keyDb.outboundApiKeysDelete(id);
|
|
4890
|
+
return writeJson3(res, ok ? 200 : 404, { ok });
|
|
4891
|
+
}
|
|
4852
4892
|
if (method === "POST" && id && action === "enabled") {
|
|
4853
4893
|
const body = await readJsonBody3(req);
|
|
4854
4894
|
const enabled = body["enabled"] === true;
|
|
@@ -5025,6 +5065,40 @@ async function handleServer(req, res, method, deps) {
|
|
|
5025
5065
|
return writeJsonError(res, 405, `method ${method} not allowed on server`);
|
|
5026
5066
|
}
|
|
5027
5067
|
async function handleAccounts(req, res, method, rest, deps) {
|
|
5068
|
+
if (rest[0] === "route-activity" && rest.length === 1) {
|
|
5069
|
+
if (method !== "GET") {
|
|
5070
|
+
return writeJsonError(res, 405, `method ${method} not allowed on account route activity`);
|
|
5071
|
+
}
|
|
5072
|
+
const query2 = requestQuery(req);
|
|
5073
|
+
const parsedLimit = Number(query2.get("limit") ?? "100");
|
|
5074
|
+
const records = (0, import_AccountRouteActivity.getSharedAccountRouteActivity)().list({
|
|
5075
|
+
providerId: query2.get("providerId") ?? void 0,
|
|
5076
|
+
accountId: query2.get("accountId") ?? void 0,
|
|
5077
|
+
sessionKey: query2.get("sessionKey") ?? void 0,
|
|
5078
|
+
limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
|
|
5079
|
+
});
|
|
5080
|
+
return writeJson3(res, 200, {
|
|
5081
|
+
available: true,
|
|
5082
|
+
records,
|
|
5083
|
+
capacity: import_AccountRouteActivity.ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
5084
|
+
collectedAt: Date.now()
|
|
5085
|
+
});
|
|
5086
|
+
}
|
|
5087
|
+
if (rest[0] === "overload-counters" && rest.length === 1) {
|
|
5088
|
+
if (method !== "GET") {
|
|
5089
|
+
return writeJsonError(res, 405, `method ${method} not allowed on overload counters`);
|
|
5090
|
+
}
|
|
5091
|
+
const query2 = requestQuery(req);
|
|
5092
|
+
const entries = (0, import_ServerOverloadCounter.getSharedOverloadCounter)().list({
|
|
5093
|
+
providerId: query2.get("providerId") ?? void 0,
|
|
5094
|
+
accountId: query2.get("accountId") ?? void 0
|
|
5095
|
+
});
|
|
5096
|
+
return writeJson3(res, 200, {
|
|
5097
|
+
available: true,
|
|
5098
|
+
entries,
|
|
5099
|
+
collectedAt: Date.now()
|
|
5100
|
+
});
|
|
5101
|
+
}
|
|
5028
5102
|
if (rest[0] === "allowances") {
|
|
5029
5103
|
return handleAccountAllowanceApi(
|
|
5030
5104
|
req,
|
|
@@ -5171,8 +5245,13 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
5171
5245
|
if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
|
|
5172
5246
|
return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
5173
5247
|
}
|
|
5174
|
-
const result = await deps.accountProbeService.
|
|
5175
|
-
return writeJson3(res, 200, {
|
|
5248
|
+
const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
|
|
5249
|
+
return writeJson3(res, 200, {
|
|
5250
|
+
ok: result.ok,
|
|
5251
|
+
marked: result.marked,
|
|
5252
|
+
tier: result.tier,
|
|
5253
|
+
model: result.model
|
|
5254
|
+
});
|
|
5176
5255
|
}
|
|
5177
5256
|
if (method === "POST" && rest[2] === "label") {
|
|
5178
5257
|
const accountId = rest[1];
|
|
@@ -5451,7 +5530,7 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
5451
5530
|
var import_node_fs7 = require("fs");
|
|
5452
5531
|
var import_promises = require("fs/promises");
|
|
5453
5532
|
var import_node_module = require("module");
|
|
5454
|
-
var
|
|
5533
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
5455
5534
|
var import_meta = {};
|
|
5456
5535
|
var CONTENT_TYPES = {
|
|
5457
5536
|
".html": "text/html; charset=utf-8",
|
|
@@ -5472,13 +5551,13 @@ var CONTENT_TYPES = {
|
|
|
5472
5551
|
function resolveUiDist() {
|
|
5473
5552
|
const fromEnv = process.env["OMNICROSS_UI_DIST"];
|
|
5474
5553
|
if (fromEnv) {
|
|
5475
|
-
return (0, import_node_fs7.existsSync)(
|
|
5554
|
+
return (0, import_node_fs7.existsSync)(import_node_path6.default.join(fromEnv, "index.html")) ? import_node_path6.default.resolve(fromEnv) : null;
|
|
5476
5555
|
}
|
|
5477
5556
|
try {
|
|
5478
5557
|
const req = (0, import_node_module.createRequire)(typeof __filename !== "undefined" ? __filename : import_meta.url);
|
|
5479
5558
|
const pkgJson = req.resolve("@omnicross/ui/package.json");
|
|
5480
|
-
const dist =
|
|
5481
|
-
return (0, import_node_fs7.existsSync)(
|
|
5559
|
+
const dist = import_node_path6.default.join(import_node_path6.default.dirname(pkgJson), "dist");
|
|
5560
|
+
return (0, import_node_fs7.existsSync)(import_node_path6.default.join(dist, "index.html")) ? dist : null;
|
|
5482
5561
|
} catch {
|
|
5483
5562
|
return null;
|
|
5484
5563
|
}
|
|
@@ -5520,16 +5599,16 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
5520
5599
|
res.end(JSON.stringify({ error: { type: "bad_request", message: "invalid path" } }));
|
|
5521
5600
|
return true;
|
|
5522
5601
|
}
|
|
5523
|
-
const filePath =
|
|
5524
|
-
if (filePath !== uiDist && !filePath.startsWith(uiDist +
|
|
5602
|
+
const filePath = import_node_path6.default.resolve(uiDist, rel === "" ? "index.html" : rel);
|
|
5603
|
+
if (filePath !== uiDist && !filePath.startsWith(uiDist + import_node_path6.default.sep)) {
|
|
5525
5604
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
5526
5605
|
res.end(JSON.stringify({ error: { type: "forbidden", message: "path outside ui root" } }));
|
|
5527
5606
|
return true;
|
|
5528
5607
|
}
|
|
5529
5608
|
let target = filePath;
|
|
5530
5609
|
if (!(0, import_node_fs7.existsSync)(target) || (0, import_node_fs7.statSync)(target).isDirectory()) {
|
|
5531
|
-
if (
|
|
5532
|
-
target =
|
|
5610
|
+
if (import_node_path6.default.extname(rel) === "") {
|
|
5611
|
+
target = import_node_path6.default.join(uiDist, "index.html");
|
|
5533
5612
|
} else {
|
|
5534
5613
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
5535
5614
|
res.end(JSON.stringify({ error: { type: "not_found", message: "no such ui asset" } }));
|
|
@@ -5537,14 +5616,14 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
5537
5616
|
}
|
|
5538
5617
|
}
|
|
5539
5618
|
const body = await (0, import_promises.readFile)(target);
|
|
5540
|
-
const type = CONTENT_TYPES[
|
|
5619
|
+
const type = CONTENT_TYPES[import_node_path6.default.extname(target).toLowerCase()] ?? "application/octet-stream";
|
|
5541
5620
|
res.writeHead(200, { "Content-Type": type, "Content-Length": body.length });
|
|
5542
5621
|
res.end(req.method === "HEAD" ? void 0 : body);
|
|
5543
5622
|
return true;
|
|
5544
5623
|
}
|
|
5545
5624
|
|
|
5546
5625
|
// src/admin/version.ts
|
|
5547
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
5626
|
+
var DAEMON_VERSION = true ? "0.1.7" : "0.0.0-dev";
|
|
5548
5627
|
|
|
5549
5628
|
// src/admin/AdminServer.ts
|
|
5550
5629
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -5652,6 +5731,10 @@ var AdminServer = class {
|
|
|
5652
5731
|
handleAuditQuery(req, res, this.deps.auditReader);
|
|
5653
5732
|
return;
|
|
5654
5733
|
}
|
|
5734
|
+
if (path2 === "/admin/api/audit/stats" && (req.method === "GET" || req.method === "HEAD")) {
|
|
5735
|
+
await handleAuditStatsQuery(req, res, this.deps.auditStatsReader);
|
|
5736
|
+
return;
|
|
5737
|
+
}
|
|
5655
5738
|
if (path2 === "/admin/api/billing-status" && (req.method === "GET" || req.method === "HEAD")) {
|
|
5656
5739
|
handleBillingStatus(res, this.deps.billingStatusReader);
|
|
5657
5740
|
return;
|
|
@@ -5768,20 +5851,36 @@ var OAuthSessionStore = class {
|
|
|
5768
5851
|
return sessionId;
|
|
5769
5852
|
}
|
|
5770
5853
|
/**
|
|
5771
|
-
*
|
|
5772
|
-
*
|
|
5773
|
-
* dropped). A `null` return means the completer must reject (no
|
|
5774
|
-
* write).
|
|
5854
|
+
* NON-DESTRUCTIVE lookup: return the session for `sessionId`, or `null` when
|
|
5855
|
+
* it is unknown, already consumed, or past its TTL (an expired entry is
|
|
5856
|
+
* dropped here). A `null` return means the completer must reject (no
|
|
5857
|
+
* exchange, no write).
|
|
5858
|
+
*
|
|
5859
|
+
* Deliberately NOT a consume: the completer peeks, runs the token exchange,
|
|
5860
|
+
* and only {@link consume}s once a token has actually been minted. Consuming
|
|
5861
|
+
* up-front burned the session on EVERY failed exchange (a mistyped/expired
|
|
5862
|
+
* pasted code, a proxy hiccup), so the user's natural retry hit
|
|
5863
|
+
* "session is unknown, expired, or already used" and the login became
|
|
5864
|
+
* unrecoverable without restarting the whole flow.
|
|
5775
5865
|
*/
|
|
5776
|
-
|
|
5866
|
+
peek(sessionId) {
|
|
5777
5867
|
this.sweep();
|
|
5778
5868
|
const session = this.sessions.get(sessionId);
|
|
5779
5869
|
if (!session) return null;
|
|
5780
|
-
|
|
5781
|
-
|
|
5870
|
+
if (Date.now() - session.createdAt > this.ttlMs) {
|
|
5871
|
+
this.sessions.delete(sessionId);
|
|
5872
|
+
return null;
|
|
5873
|
+
}
|
|
5782
5874
|
return session;
|
|
5783
5875
|
}
|
|
5784
|
-
/**
|
|
5876
|
+
/**
|
|
5877
|
+
* SINGLE-USE burn: drop the session so the same `sessionId` can never be
|
|
5878
|
+
* completed twice. Called ONLY after a successful token exchange.
|
|
5879
|
+
*/
|
|
5880
|
+
consume(sessionId) {
|
|
5881
|
+
this.sessions.delete(sessionId);
|
|
5882
|
+
}
|
|
5883
|
+
/** Drop every session past its TTL. Called on each put/peek. */
|
|
5785
5884
|
sweep() {
|
|
5786
5885
|
const now = Date.now();
|
|
5787
5886
|
for (const [id, session] of this.sessions) {
|
|
@@ -5796,6 +5895,10 @@ var LOOPBACK_HOST = "127.0.0.1";
|
|
|
5796
5895
|
var LOOPBACK_PORT = 1455;
|
|
5797
5896
|
var CALLBACK_PATH = "/auth/callback";
|
|
5798
5897
|
var DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
5898
|
+
var HTML_HEADERS = {
|
|
5899
|
+
"Content-Type": "text/html",
|
|
5900
|
+
Connection: "close"
|
|
5901
|
+
};
|
|
5799
5902
|
function pageHtml(message) {
|
|
5800
5903
|
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>`;
|
|
5801
5904
|
}
|
|
@@ -5806,30 +5909,31 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
5806
5909
|
if (settled) return;
|
|
5807
5910
|
settled = true;
|
|
5808
5911
|
clearTimeout(timer);
|
|
5809
|
-
|
|
5912
|
+
fn();
|
|
5913
|
+
server2.close();
|
|
5810
5914
|
};
|
|
5811
5915
|
const server = (0, import_node_http3.createServer)((req, res) => {
|
|
5812
5916
|
const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
|
|
5813
5917
|
if (url.pathname !== CALLBACK_PATH) {
|
|
5814
|
-
res.writeHead(404,
|
|
5918
|
+
res.writeHead(404, HTML_HEADERS);
|
|
5815
5919
|
res.end(pageHtml("Not found"));
|
|
5816
5920
|
return;
|
|
5817
5921
|
}
|
|
5818
5922
|
const code = url.searchParams.get("code");
|
|
5819
5923
|
const state = url.searchParams.get("state");
|
|
5820
5924
|
if (!code) {
|
|
5821
|
-
res.writeHead(400,
|
|
5925
|
+
res.writeHead(400, HTML_HEADERS);
|
|
5822
5926
|
res.end(pageHtml("Login failed: missing authorization code."));
|
|
5823
5927
|
finish(server, () => reject(new Error("login: callback did not include an authorization code")));
|
|
5824
5928
|
return;
|
|
5825
5929
|
}
|
|
5826
5930
|
if (state !== expectedState) {
|
|
5827
|
-
res.writeHead(400,
|
|
5931
|
+
res.writeHead(400, HTML_HEADERS);
|
|
5828
5932
|
res.end(pageHtml("Login failed: state mismatch."));
|
|
5829
5933
|
finish(server, () => reject(new Error("login: callback state did not match (possible CSRF) \u2014 aborting")));
|
|
5830
5934
|
return;
|
|
5831
5935
|
}
|
|
5832
|
-
res.writeHead(200,
|
|
5936
|
+
res.writeHead(200, HTML_HEADERS);
|
|
5833
5937
|
res.end(pageHtml("Login complete."));
|
|
5834
5938
|
finish(server, () => resolve2(code));
|
|
5835
5939
|
});
|
|
@@ -5926,30 +6030,30 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
5926
6030
|
}
|
|
5927
6031
|
|
|
5928
6032
|
// src/commands/paths.ts
|
|
5929
|
-
var
|
|
6033
|
+
var import_node_path7 = require("path");
|
|
5930
6034
|
function defaultVouchersPath(configPath) {
|
|
5931
|
-
return (0,
|
|
6035
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(configPath), "vouchers.json");
|
|
5932
6036
|
}
|
|
5933
6037
|
function defaultIntegrationsPath(configPath) {
|
|
5934
|
-
return (0,
|
|
6038
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(configPath), "integrations.json");
|
|
5935
6039
|
}
|
|
5936
6040
|
function defaultPricingPath(configPath) {
|
|
5937
|
-
return (0,
|
|
6041
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(configPath), "pricing.json");
|
|
5938
6042
|
}
|
|
5939
6043
|
function defaultPricingRefreshStatePath(configPath) {
|
|
5940
|
-
return (0,
|
|
6044
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(configPath), "pricing-refresh.json");
|
|
5941
6045
|
}
|
|
5942
6046
|
function defaultAccountAllowancePath(configPath) {
|
|
5943
|
-
return (0,
|
|
6047
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(configPath), "allowance-cache.json");
|
|
5944
6048
|
}
|
|
5945
6049
|
function defaultUsageEventsPath(configPath) {
|
|
5946
|
-
return (0,
|
|
6050
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(configPath), "usage-events.jsonl");
|
|
5947
6051
|
}
|
|
5948
6052
|
function defaultAuditDir(configPath) {
|
|
5949
|
-
return (0,
|
|
6053
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(configPath), "audit");
|
|
5950
6054
|
}
|
|
5951
6055
|
function defaultBillingDir(configPath) {
|
|
5952
|
-
return (0,
|
|
6056
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(configPath), "billing");
|
|
5953
6057
|
}
|
|
5954
6058
|
|
|
5955
6059
|
// src/ports/ConfigFileProviderConfigSource.ts
|
|
@@ -6342,8 +6446,12 @@ var JsonlUsageEventStore = class {
|
|
|
6342
6446
|
reasoningTokens: 0,
|
|
6343
6447
|
costUsd: 0,
|
|
6344
6448
|
costSavedByCacheUsd: 0,
|
|
6345
|
-
eventCount: 0
|
|
6449
|
+
eventCount: 0,
|
|
6450
|
+
cacheEligibleEventCount: 0,
|
|
6451
|
+
coldCacheEventCount: 0,
|
|
6452
|
+
medianCacheHitRate: null
|
|
6346
6453
|
};
|
|
6454
|
+
const perEventHitRates = [];
|
|
6347
6455
|
for (const row of this.readRows(range)) {
|
|
6348
6456
|
totals.inputTokens += row.inputTokens;
|
|
6349
6457
|
totals.outputTokens += row.outputTokens;
|
|
@@ -6353,7 +6461,14 @@ var JsonlUsageEventStore = class {
|
|
|
6353
6461
|
totals.costUsd += row.costUsd;
|
|
6354
6462
|
totals.costSavedByCacheUsd += row.costSavedByCacheUsd;
|
|
6355
6463
|
totals.eventCount += 1;
|
|
6464
|
+
const promptSideTokens = row.inputTokens + row.cacheReadTokens + row.cacheCreationTokens;
|
|
6465
|
+
if (promptSideTokens > 0) {
|
|
6466
|
+
totals.cacheEligibleEventCount += 1;
|
|
6467
|
+
if (row.cacheReadTokens === 0) totals.coldCacheEventCount += 1;
|
|
6468
|
+
perEventHitRates.push(row.cacheReadTokens / promptSideTokens);
|
|
6469
|
+
}
|
|
6356
6470
|
}
|
|
6471
|
+
totals.medianCacheHitRate = median(perEventHitRates);
|
|
6357
6472
|
return totals;
|
|
6358
6473
|
}
|
|
6359
6474
|
async getByModel(range) {
|
|
@@ -6587,6 +6702,15 @@ var NUMERIC_FIELDS = [
|
|
|
6587
6702
|
];
|
|
6588
6703
|
var NULLABLE_STRING_FIELDS = ["messageId", "parentMessageId", "sessionId", "apiKeyId"];
|
|
6589
6704
|
var isStringOrNull = (v) => v === null || typeof v === "string";
|
|
6705
|
+
var CACHE_KEY_SOURCES = /* @__PURE__ */ new Set([
|
|
6706
|
+
"client",
|
|
6707
|
+
"session-header",
|
|
6708
|
+
"thread-header",
|
|
6709
|
+
"body-session-id",
|
|
6710
|
+
"body-thread-id",
|
|
6711
|
+
"content-fingerprint",
|
|
6712
|
+
"none"
|
|
6713
|
+
]);
|
|
6590
6714
|
function isUsageEventRecord(parsed) {
|
|
6591
6715
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
6592
6716
|
const r = parsed;
|
|
@@ -6594,6 +6718,10 @@ function isUsageEventRecord(parsed) {
|
|
|
6594
6718
|
if (typeof r["providerId"] !== "string") return false;
|
|
6595
6719
|
if (typeof r["model"] !== "string") return false;
|
|
6596
6720
|
if (typeof r["engineOrigin"] !== "string") return false;
|
|
6721
|
+
if (r["cacheKeySource"] !== void 0 && (typeof r["cacheKeySource"] !== "string" || !CACHE_KEY_SOURCES.has(r["cacheKeySource"]))) return false;
|
|
6722
|
+
if (r["cacheKeyInjected"] !== void 0 && typeof r["cacheKeyInjected"] !== "boolean") {
|
|
6723
|
+
return false;
|
|
6724
|
+
}
|
|
6597
6725
|
for (const f of NULLABLE_STRING_FIELDS) {
|
|
6598
6726
|
if (!isStringOrNull(r[f])) return false;
|
|
6599
6727
|
}
|
|
@@ -6603,14 +6731,30 @@ function isUsageEventRecord(parsed) {
|
|
|
6603
6731
|
}
|
|
6604
6732
|
return true;
|
|
6605
6733
|
}
|
|
6734
|
+
function median(values) {
|
|
6735
|
+
if (values.length === 0) return null;
|
|
6736
|
+
values.sort((a, b) => a - b);
|
|
6737
|
+
const middle = Math.floor(values.length / 2);
|
|
6738
|
+
return values.length % 2 === 1 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
|
|
6739
|
+
}
|
|
6606
6740
|
|
|
6607
6741
|
// src/ports/JsonOutboundKeyDb.ts
|
|
6608
6742
|
var import_node_fs11 = require("fs");
|
|
6609
6743
|
var JsonOutboundKeyDb = class {
|
|
6610
|
-
|
|
6744
|
+
/**
|
|
6745
|
+
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
6746
|
+
* key's plaintext is persisted as a `keySecret` `enc:` envelope (enabling the
|
|
6747
|
+
* operator "view key" affordance via `outboundApiKeysReveal`). When absent the
|
|
6748
|
+
* store stays hash-only (byte-identical to the legacy behavior) and reveal
|
|
6749
|
+
* always returns `null`. Existing 1-arg call sites (tests, lightweight
|
|
6750
|
+
* embedders) keep working.
|
|
6751
|
+
*/
|
|
6752
|
+
constructor(keysPath, secretBox3) {
|
|
6611
6753
|
this.keysPath = keysPath;
|
|
6754
|
+
this.secretBox = secretBox3;
|
|
6612
6755
|
}
|
|
6613
6756
|
keysPath;
|
|
6757
|
+
secretBox;
|
|
6614
6758
|
async outboundApiKeysList() {
|
|
6615
6759
|
return this.readRows();
|
|
6616
6760
|
}
|
|
@@ -6636,10 +6780,27 @@ var JsonOutboundKeyDb = class {
|
|
|
6636
6780
|
allowedEndpoints: input.allowedEndpoints,
|
|
6637
6781
|
loopbackOnly: input.loopbackOnly
|
|
6638
6782
|
};
|
|
6783
|
+
if (input.plaintext && this.secretBox) {
|
|
6784
|
+
row.keySecret = this.secretBox.encrypt(input.plaintext);
|
|
6785
|
+
}
|
|
6639
6786
|
rows.push(row);
|
|
6640
6787
|
this.writeRows(rows);
|
|
6641
6788
|
return row;
|
|
6642
6789
|
}
|
|
6790
|
+
async outboundApiKeysReveal(id) {
|
|
6791
|
+
const rows = this.readRows();
|
|
6792
|
+
const row = rows.find((r) => r.id === id);
|
|
6793
|
+
if (!row || !row.keySecret || !this.secretBox) return null;
|
|
6794
|
+
return this.secretBox.decrypt(row.keySecret);
|
|
6795
|
+
}
|
|
6796
|
+
async outboundApiKeysDelete(id) {
|
|
6797
|
+
const rows = this.readRows();
|
|
6798
|
+
const idx = rows.findIndex((r) => r.id === id);
|
|
6799
|
+
if (idx < 0) return false;
|
|
6800
|
+
rows.splice(idx, 1);
|
|
6801
|
+
this.writeRows(rows);
|
|
6802
|
+
return true;
|
|
6803
|
+
}
|
|
6643
6804
|
async outboundApiKeysRevoke(id) {
|
|
6644
6805
|
return this.mutateRow(id, (row) => {
|
|
6645
6806
|
if (row.revokedAt !== null) return false;
|
|
@@ -7088,7 +7249,7 @@ var JsonVoucherDb = class {
|
|
|
7088
7249
|
|
|
7089
7250
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
7090
7251
|
var import_node_fs16 = require("fs");
|
|
7091
|
-
var
|
|
7252
|
+
var import_node_path9 = require("path");
|
|
7092
7253
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
7093
7254
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
7094
7255
|
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
@@ -7139,9 +7300,9 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
7139
7300
|
// src/ports/external-cli-credentials.ts
|
|
7140
7301
|
var import_node_fs15 = require("fs");
|
|
7141
7302
|
var import_node_os3 = require("os");
|
|
7142
|
-
var
|
|
7303
|
+
var import_node_path8 = require("path");
|
|
7143
7304
|
function externalStorePath(provider, home = (0, import_node_os3.homedir)()) {
|
|
7144
|
-
return provider === "claude" ? (0,
|
|
7305
|
+
return provider === "claude" ? (0, import_node_path8.join)(home, ".claude", ".credentials.json") : (0, import_node_path8.join)(home, ".codex", "auth.json");
|
|
7145
7306
|
}
|
|
7146
7307
|
function decodeJwtExpiryMs(token) {
|
|
7147
7308
|
try {
|
|
@@ -7231,9 +7392,15 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7231
7392
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
7232
7393
|
* through {@link fetchUpstream} with the account's `{ providerId, accountId }`
|
|
7233
7394
|
* ctx so the per-account/provider proxy applies. `@internal` also a test seam.
|
|
7395
|
+
*
|
|
7396
|
+
* `redactBodies` is REQUIRED here: this round-trip sends the refresh_token and
|
|
7397
|
+
* receives a fresh access/refresh token pair. Carrying a `providerId` opts the
|
|
7398
|
+
* call into the upstream trace (so a failing refresh is diagnosable), and the
|
|
7399
|
+
* trace captures bodies verbatim — without this flag every refresh would write
|
|
7400
|
+
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
7234
7401
|
*/
|
|
7235
7402
|
buildRefreshFetch(providerId, accountId) {
|
|
7236
|
-
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId, accountId }));
|
|
7403
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
7237
7404
|
}
|
|
7238
7405
|
/**
|
|
7239
7406
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -7758,7 +7925,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7758
7925
|
* `enc:v1:`; already-`enc:`/`$ENV` untouched) before serializing, so any
|
|
7759
7926
|
* write incl. child 4's future refresh writes lands encrypted. */
|
|
7760
7927
|
persist(config) {
|
|
7761
|
-
(0, import_node_fs16.mkdirSync)((0,
|
|
7928
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path9.dirname)(this.tokensPath), { recursive: true });
|
|
7762
7929
|
const encrypted = encryptTokens(config, this.box);
|
|
7763
7930
|
(0, import_node_fs16.writeFileSync)(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
7764
7931
|
}
|
|
@@ -7793,6 +7960,124 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7793
7960
|
// src/AccountHealthProbeScheduler.ts
|
|
7794
7961
|
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
7795
7962
|
|
|
7963
|
+
// src/probe/CodexGenerationProbe.ts
|
|
7964
|
+
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
7965
|
+
var CODEX_GENERATION_PROBE_MODEL = "gpt-5.6-luna";
|
|
7966
|
+
var CODEX_GENERATION_PROBE_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
7967
|
+
var MAX_STREAM_BYTES = 256 * 1024;
|
|
7968
|
+
var PROBE_INSTRUCTION = "Return exactly PONG and no other text.";
|
|
7969
|
+
function buildCodexGenerationProbeInit(token, signal) {
|
|
7970
|
+
return {
|
|
7971
|
+
method: "POST",
|
|
7972
|
+
signal,
|
|
7973
|
+
headers: {
|
|
7974
|
+
...import_codexCliHeaders.DEFAULT_CODEX_CLI_HEADERS,
|
|
7975
|
+
Authorization: `Bearer ${token}`,
|
|
7976
|
+
Accept: (0, import_codexCliHeaders.codexAcceptHeader)(true),
|
|
7977
|
+
"Content-Type": "application/json"
|
|
7978
|
+
},
|
|
7979
|
+
body: JSON.stringify({
|
|
7980
|
+
model: CODEX_GENERATION_PROBE_MODEL,
|
|
7981
|
+
input: [
|
|
7982
|
+
{
|
|
7983
|
+
role: "developer",
|
|
7984
|
+
content: [{ type: "input_text", text: PROBE_INSTRUCTION }]
|
|
7985
|
+
},
|
|
7986
|
+
{
|
|
7987
|
+
role: "user",
|
|
7988
|
+
content: [{ type: "input_text", text: "Connection probe." }]
|
|
7989
|
+
}
|
|
7990
|
+
],
|
|
7991
|
+
// GPT-5.6 otherwise defaults to medium reasoning. A connectivity probe
|
|
7992
|
+
// needs the lowest-cost path and no tool reasoning.
|
|
7993
|
+
reasoning: { effort: "none" },
|
|
7994
|
+
stream: true,
|
|
7995
|
+
store: false
|
|
7996
|
+
})
|
|
7997
|
+
};
|
|
7998
|
+
}
|
|
7999
|
+
async function readCodexGenerationProbeStream(response) {
|
|
8000
|
+
if (!response.body) return { completed: false, outputChars: 0 };
|
|
8001
|
+
const reader = response.body.getReader();
|
|
8002
|
+
const decoder = new TextDecoder();
|
|
8003
|
+
let buffer = "";
|
|
8004
|
+
let bytes = 0;
|
|
8005
|
+
let outputChars = 0;
|
|
8006
|
+
try {
|
|
8007
|
+
while (true) {
|
|
8008
|
+
const { done, value } = await reader.read();
|
|
8009
|
+
if (done) break;
|
|
8010
|
+
bytes += value.byteLength;
|
|
8011
|
+
if (bytes > MAX_STREAM_BYTES) {
|
|
8012
|
+
await reader.cancel();
|
|
8013
|
+
return { completed: false, outputChars };
|
|
8014
|
+
}
|
|
8015
|
+
buffer += decoder.decode(value, { stream: true });
|
|
8016
|
+
buffer = buffer.replace(/\r\n/g, "\n");
|
|
8017
|
+
let boundary = buffer.indexOf("\n\n");
|
|
8018
|
+
while (boundary >= 0) {
|
|
8019
|
+
const block = buffer.slice(0, boundary);
|
|
8020
|
+
buffer = buffer.slice(boundary + 2);
|
|
8021
|
+
const event = parseSseBlock(block);
|
|
8022
|
+
if (event) {
|
|
8023
|
+
const type = event["type"];
|
|
8024
|
+
if (type === "response.output_text.delta" && typeof event["delta"] === "string") {
|
|
8025
|
+
outputChars += event["delta"].length;
|
|
8026
|
+
} else if (type === "response.output_text.done" && typeof event["text"] === "string") {
|
|
8027
|
+
outputChars = Math.max(outputChars, event["text"].length);
|
|
8028
|
+
} else if (type === "response.failed" || type === "error") {
|
|
8029
|
+
await reader.cancel();
|
|
8030
|
+
return { completed: false, outputChars };
|
|
8031
|
+
} else if (type === "response.completed") {
|
|
8032
|
+
const completedResponse = asRecord(event["response"]);
|
|
8033
|
+
const status = completedResponse?.["status"];
|
|
8034
|
+
outputChars = Math.max(outputChars, countCompletedOutputChars(completedResponse));
|
|
8035
|
+
await reader.cancel();
|
|
8036
|
+
return {
|
|
8037
|
+
completed: (status === void 0 || status === "completed") && outputChars > 0,
|
|
8038
|
+
outputChars
|
|
8039
|
+
};
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
8042
|
+
boundary = buffer.indexOf("\n\n");
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
8045
|
+
} catch {
|
|
8046
|
+
return { completed: false, outputChars };
|
|
8047
|
+
} finally {
|
|
8048
|
+
reader.releaseLock();
|
|
8049
|
+
}
|
|
8050
|
+
return { completed: false, outputChars };
|
|
8051
|
+
}
|
|
8052
|
+
function parseSseBlock(block) {
|
|
8053
|
+
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
8054
|
+
if (!data || data === "[DONE]") return null;
|
|
8055
|
+
try {
|
|
8056
|
+
return JSON.parse(data);
|
|
8057
|
+
} catch {
|
|
8058
|
+
return null;
|
|
8059
|
+
}
|
|
8060
|
+
}
|
|
8061
|
+
function asRecord(value) {
|
|
8062
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
8063
|
+
}
|
|
8064
|
+
function countCompletedOutputChars(response) {
|
|
8065
|
+
const output = response?.["output"];
|
|
8066
|
+
if (!Array.isArray(output)) return 0;
|
|
8067
|
+
let chars = 0;
|
|
8068
|
+
for (const item of output) {
|
|
8069
|
+
const content = asRecord(item)?.["content"];
|
|
8070
|
+
if (!Array.isArray(content)) continue;
|
|
8071
|
+
for (const part of content) {
|
|
8072
|
+
const record = asRecord(part);
|
|
8073
|
+
if (record?.["type"] === "output_text" && typeof record["text"] === "string") {
|
|
8074
|
+
chars += record["text"].length;
|
|
8075
|
+
}
|
|
8076
|
+
}
|
|
8077
|
+
}
|
|
8078
|
+
return chars;
|
|
8079
|
+
}
|
|
8080
|
+
|
|
7796
8081
|
// src/probe/ProbeStrategy.ts
|
|
7797
8082
|
var PROVIDER_PROBE_PLANS = {
|
|
7798
8083
|
claude: {
|
|
@@ -7920,17 +8205,17 @@ var AccountHealthProbeScheduler = class {
|
|
|
7920
8205
|
}
|
|
7921
8206
|
if (readThrew) {
|
|
7922
8207
|
this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
|
|
7923
|
-
return { ok: false, marked: false };
|
|
8208
|
+
return { ok: false, marked: false, tier: "local" };
|
|
7924
8209
|
}
|
|
7925
8210
|
if (!token) {
|
|
7926
8211
|
this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
|
|
7927
8212
|
this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
|
|
7928
|
-
return { ok: false, marked: true };
|
|
8213
|
+
return { ok: false, marked: true, tier: "local" };
|
|
7929
8214
|
}
|
|
7930
8215
|
const plan = this.planFor(providerId);
|
|
7931
8216
|
if (plan.kind === "local") {
|
|
7932
8217
|
this.record(providerId, accountId, { ts: now, ok: true, tier: "local" });
|
|
7933
|
-
return { ok: true, marked: false };
|
|
8218
|
+
return { ok: true, marked: false, tier: "local" };
|
|
7934
8219
|
}
|
|
7935
8220
|
const start = this.now();
|
|
7936
8221
|
let status = null;
|
|
@@ -7955,7 +8240,60 @@ var AccountHealthProbeScheduler = class {
|
|
|
7955
8240
|
latencyMs,
|
|
7956
8241
|
tier: "upstream"
|
|
7957
8242
|
});
|
|
7958
|
-
return { ok: status !== null && status < 400, marked };
|
|
8243
|
+
return { ok: status !== null && status < 400, marked, tier: "upstream" };
|
|
8244
|
+
}
|
|
8245
|
+
/**
|
|
8246
|
+
* Manual connection test. Codex performs a real, quota-consuming generation;
|
|
8247
|
+
* every other provider keeps its existing cheap probe. Scheduled sweeps never
|
|
8248
|
+
* call this method, so they remain non-billable.
|
|
8249
|
+
*/
|
|
8250
|
+
async testAccountConnection(providerId, accountId) {
|
|
8251
|
+
if (providerId !== "codex") return this.probeAccount(providerId, accountId);
|
|
8252
|
+
const now = this.now();
|
|
8253
|
+
let token;
|
|
8254
|
+
try {
|
|
8255
|
+
token = await this.store.getAccessTokenForAccount(providerId, accountId);
|
|
8256
|
+
} catch {
|
|
8257
|
+
this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
|
|
8258
|
+
return { ok: false, marked: false, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
|
|
8259
|
+
}
|
|
8260
|
+
if (!token) {
|
|
8261
|
+
this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
|
|
8262
|
+
this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
|
|
8263
|
+
return { ok: false, marked: true, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
|
|
8264
|
+
}
|
|
8265
|
+
const startedAt = this.now();
|
|
8266
|
+
let attempt = await this.runCodexGenerationAttempt(accountId, token);
|
|
8267
|
+
if (attempt.status === 401 && this.store.refreshAccountToken) {
|
|
8268
|
+
try {
|
|
8269
|
+
if (await this.store.refreshAccountToken(providerId, accountId)) {
|
|
8270
|
+
const refreshed = await this.store.getAccessTokenForAccount(providerId, accountId);
|
|
8271
|
+
if (refreshed) attempt = await this.runCodexGenerationAttempt(accountId, refreshed);
|
|
8272
|
+
}
|
|
8273
|
+
} catch {
|
|
8274
|
+
}
|
|
8275
|
+
}
|
|
8276
|
+
const latencyMs = this.now() - startedAt;
|
|
8277
|
+
const ok = attempt.status !== null && attempt.status >= 200 && attempt.status < 300 && attempt.completed;
|
|
8278
|
+
let marked = false;
|
|
8279
|
+
if (ok) {
|
|
8280
|
+
this.health.clearTransientMark(providerId, accountId);
|
|
8281
|
+
} else if (attempt.status === 401 || attempt.status === 403) {
|
|
8282
|
+
marked = this.applyOutcome(providerId, accountId, attempt.status, attempt.bodyText, now);
|
|
8283
|
+
}
|
|
8284
|
+
this.record(providerId, accountId, {
|
|
8285
|
+
ts: now,
|
|
8286
|
+
ok,
|
|
8287
|
+
status: attempt.status,
|
|
8288
|
+
latencyMs,
|
|
8289
|
+
tier: "generation"
|
|
8290
|
+
});
|
|
8291
|
+
return {
|
|
8292
|
+
ok,
|
|
8293
|
+
marked,
|
|
8294
|
+
tier: "generation",
|
|
8295
|
+
model: CODEX_GENERATION_PROBE_MODEL
|
|
8296
|
+
};
|
|
7959
8297
|
}
|
|
7960
8298
|
/** Per-account rolling history for the authed admin surface (design D5). */
|
|
7961
8299
|
getAllHistory() {
|
|
@@ -8012,6 +8350,24 @@ var AccountHealthProbeScheduler = class {
|
|
|
8012
8350
|
return "";
|
|
8013
8351
|
}
|
|
8014
8352
|
}
|
|
8353
|
+
async runCodexGenerationAttempt(accountId, token) {
|
|
8354
|
+
try {
|
|
8355
|
+
const timeoutMs = Math.max(this.config.timeoutMs, 15e3);
|
|
8356
|
+
const response = await this.fetchImpl(
|
|
8357
|
+
CODEX_GENERATION_PROBE_URL,
|
|
8358
|
+
buildCodexGenerationProbeInit(token, AbortSignal.timeout(timeoutMs)),
|
|
8359
|
+
{ providerId: "codex", accountId, redactBodies: true }
|
|
8360
|
+
);
|
|
8361
|
+
if (response.status < 200 || response.status >= 300) {
|
|
8362
|
+
const bodyText = response.status === 403 ? await this.readBounded(response) : void 0;
|
|
8363
|
+
return { status: response.status, completed: false, bodyText };
|
|
8364
|
+
}
|
|
8365
|
+
const stream = await readCodexGenerationProbeStream(response);
|
|
8366
|
+
return { status: response.status, completed: stream.completed };
|
|
8367
|
+
} catch {
|
|
8368
|
+
return { status: null, completed: false };
|
|
8369
|
+
}
|
|
8370
|
+
}
|
|
8015
8371
|
key(providerId, accountId) {
|
|
8016
8372
|
return `${providerId}${KEY_SEP}${accountId}`;
|
|
8017
8373
|
}
|
|
@@ -8107,7 +8463,7 @@ var AccountHealthSweeper = class {
|
|
|
8107
8463
|
};
|
|
8108
8464
|
|
|
8109
8465
|
// src/audit/AuditPruneSweeper.ts
|
|
8110
|
-
var
|
|
8466
|
+
var import_node_fs18 = require("fs");
|
|
8111
8467
|
var import_node_path11 = require("path");
|
|
8112
8468
|
|
|
8113
8469
|
// src/audit/auditFiles.ts
|
|
@@ -8130,12 +8486,206 @@ function auditFileDateMs(fileName) {
|
|
|
8130
8486
|
return d.getTime();
|
|
8131
8487
|
}
|
|
8132
8488
|
|
|
8489
|
+
// src/audit/auditStats.ts
|
|
8490
|
+
var import_node_fs17 = require("fs");
|
|
8491
|
+
var import_node_path10 = require("path");
|
|
8492
|
+
var SIDECAR_VERSION = 1;
|
|
8493
|
+
var META_PREFIX_BYTES = 64 * 1024;
|
|
8494
|
+
var READ_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
8495
|
+
function auditStatsFileName(auditFile) {
|
|
8496
|
+
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
8497
|
+
}
|
|
8498
|
+
function readPersisted(path2) {
|
|
8499
|
+
if (!(0, import_node_fs17.existsSync)(path2)) return null;
|
|
8500
|
+
try {
|
|
8501
|
+
const value = JSON.parse((0, import_node_fs17.readFileSync)(path2, "utf8"));
|
|
8502
|
+
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)) {
|
|
8503
|
+
return null;
|
|
8504
|
+
}
|
|
8505
|
+
return value;
|
|
8506
|
+
} catch {
|
|
8507
|
+
return null;
|
|
8508
|
+
}
|
|
8509
|
+
}
|
|
8510
|
+
function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
|
|
8511
|
+
const statsPath = (0, import_node_path10.join)((0, import_node_path10.dirname)(auditPath), auditStatsFileName((0, import_node_path10.basename)(auditPath)));
|
|
8512
|
+
const previous = auditBytesBefore === 0 ? {
|
|
8513
|
+
version: SIDECAR_VERSION,
|
|
8514
|
+
auditBytes: 0,
|
|
8515
|
+
requestCount: 0,
|
|
8516
|
+
errorCount: 0,
|
|
8517
|
+
complete: true,
|
|
8518
|
+
minTs: null,
|
|
8519
|
+
maxTs: null
|
|
8520
|
+
} : readPersisted(statsPath);
|
|
8521
|
+
if (!previous || !previous.complete || previous.auditBytes !== auditBytesBefore) return;
|
|
8522
|
+
const next = {
|
|
8523
|
+
version: SIDECAR_VERSION,
|
|
8524
|
+
auditBytes: auditBytesAfter,
|
|
8525
|
+
requestCount: previous.requestCount + 1,
|
|
8526
|
+
errorCount: previous.errorCount + (record.status >= 400 || Boolean(record.error) ? 1 : 0),
|
|
8527
|
+
complete: true,
|
|
8528
|
+
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
8529
|
+
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
8530
|
+
};
|
|
8531
|
+
(0, import_node_fs17.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
|
|
8532
|
+
}
|
|
8533
|
+
function queryCovers(stats, from, to) {
|
|
8534
|
+
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
8535
|
+
}
|
|
8536
|
+
function fileOverlaps(file, from, to) {
|
|
8537
|
+
const start = auditFileDateMs(file);
|
|
8538
|
+
if (start === null) return false;
|
|
8539
|
+
const date = new Date(start);
|
|
8540
|
+
const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
|
|
8541
|
+
return end > from && start <= to;
|
|
8542
|
+
}
|
|
8543
|
+
function parseMetadataPrefix(prefix, prefixTruncated) {
|
|
8544
|
+
const text = prefix.toString("utf8");
|
|
8545
|
+
const tsMatch = /(?:^|,)"ts":(-?\d+)/.exec(text);
|
|
8546
|
+
const statusMatch = /(?:^|,)"status":(-?\d+)/.exec(text);
|
|
8547
|
+
const errorMatch = /(?:^|,)"error":"((?:\\.|[^"\\])*)"/.exec(text);
|
|
8548
|
+
const bodyStarted = /,(?:"requestBody"|"responseBody"):/.test(text);
|
|
8549
|
+
return {
|
|
8550
|
+
ts: tsMatch ? Number(tsMatch[1]) : void 0,
|
|
8551
|
+
status: statusMatch ? Number(statusMatch[1]) : void 0,
|
|
8552
|
+
hasError: Boolean(errorMatch?.[1]),
|
|
8553
|
+
complete: Boolean(tsMatch && statusMatch && (!prefixTruncated || bodyStarted))
|
|
8554
|
+
};
|
|
8555
|
+
}
|
|
8556
|
+
async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
|
|
8557
|
+
let requestCount = 0;
|
|
8558
|
+
let errorCount = 0;
|
|
8559
|
+
let filteredRequestCount = 0;
|
|
8560
|
+
let filteredErrorCount = 0;
|
|
8561
|
+
let minTs = null;
|
|
8562
|
+
let maxTs = null;
|
|
8563
|
+
let complete = true;
|
|
8564
|
+
let prefixParts = [];
|
|
8565
|
+
let prefixBytes = 0;
|
|
8566
|
+
let prefixTruncated = false;
|
|
8567
|
+
const consumeLine = () => {
|
|
8568
|
+
if (prefixBytes === 0 && !prefixTruncated) return;
|
|
8569
|
+
const prefix = Buffer.concat(prefixParts, prefixBytes);
|
|
8570
|
+
const metadata = parseMetadataPrefix(prefix, prefixTruncated);
|
|
8571
|
+
if (!metadata.complete || metadata.ts === void 0 || metadata.status === void 0) {
|
|
8572
|
+
complete = false;
|
|
8573
|
+
} else {
|
|
8574
|
+
requestCount += 1;
|
|
8575
|
+
const isError = metadata.status >= 400 || metadata.hasError;
|
|
8576
|
+
if (isError) errorCount += 1;
|
|
8577
|
+
minTs = minTs === null ? metadata.ts : Math.min(minTs, metadata.ts);
|
|
8578
|
+
maxTs = maxTs === null ? metadata.ts : Math.max(maxTs, metadata.ts);
|
|
8579
|
+
if (metadata.ts >= from && metadata.ts <= to) {
|
|
8580
|
+
filteredRequestCount += 1;
|
|
8581
|
+
if (isError) filteredErrorCount += 1;
|
|
8582
|
+
}
|
|
8583
|
+
}
|
|
8584
|
+
prefixParts = [];
|
|
8585
|
+
prefixBytes = 0;
|
|
8586
|
+
prefixTruncated = false;
|
|
8587
|
+
};
|
|
8588
|
+
if (auditBytes > startByte) {
|
|
8589
|
+
const stream = (0, import_node_fs17.createReadStream)(auditPath, {
|
|
8590
|
+
start: startByte,
|
|
8591
|
+
end: auditBytes - 1,
|
|
8592
|
+
highWaterMark: READ_CHUNK_BYTES
|
|
8593
|
+
});
|
|
8594
|
+
for await (const value of stream) {
|
|
8595
|
+
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
8596
|
+
let offset = 0;
|
|
8597
|
+
while (offset < chunk.length) {
|
|
8598
|
+
const newline = chunk.indexOf(10, offset);
|
|
8599
|
+
const end = newline === -1 ? chunk.length : newline;
|
|
8600
|
+
if (prefixBytes < META_PREFIX_BYTES) {
|
|
8601
|
+
const retained = Math.min(META_PREFIX_BYTES - prefixBytes, end - offset);
|
|
8602
|
+
if (retained > 0) {
|
|
8603
|
+
prefixParts.push(Buffer.from(chunk.subarray(offset, offset + retained)));
|
|
8604
|
+
prefixBytes += retained;
|
|
8605
|
+
}
|
|
8606
|
+
if (retained < end - offset) prefixTruncated = true;
|
|
8607
|
+
} else if (end > offset) {
|
|
8608
|
+
prefixTruncated = true;
|
|
8609
|
+
}
|
|
8610
|
+
if (newline === -1) break;
|
|
8611
|
+
consumeLine();
|
|
8612
|
+
offset = newline + 1;
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
}
|
|
8616
|
+
if (prefixBytes > 0 || prefixTruncated) complete = false;
|
|
8617
|
+
return {
|
|
8618
|
+
all: {
|
|
8619
|
+
version: SIDECAR_VERSION,
|
|
8620
|
+
auditBytes,
|
|
8621
|
+
requestCount,
|
|
8622
|
+
errorCount,
|
|
8623
|
+
complete,
|
|
8624
|
+
minTs,
|
|
8625
|
+
maxTs
|
|
8626
|
+
},
|
|
8627
|
+
filtered: { requestCount: filteredRequestCount, errorCount: filteredErrorCount, complete }
|
|
8628
|
+
};
|
|
8629
|
+
}
|
|
8630
|
+
function mergePersistedStats(previous, appended) {
|
|
8631
|
+
return {
|
|
8632
|
+
version: SIDECAR_VERSION,
|
|
8633
|
+
auditBytes: appended.auditBytes,
|
|
8634
|
+
requestCount: previous.requestCount + appended.requestCount,
|
|
8635
|
+
errorCount: previous.errorCount + appended.errorCount,
|
|
8636
|
+
complete: previous.complete && appended.complete,
|
|
8637
|
+
minTs: previous.minTs === null ? appended.minTs : appended.minTs === null ? previous.minTs : Math.min(previous.minTs, appended.minTs),
|
|
8638
|
+
maxTs: previous.maxTs === null ? appended.maxTs : appended.maxTs === null ? previous.maxTs : Math.max(previous.maxTs, appended.maxTs)
|
|
8639
|
+
};
|
|
8640
|
+
}
|
|
8641
|
+
async function readAuditStats(auditDir, query2 = {}) {
|
|
8642
|
+
if (!(0, import_node_fs17.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
8643
|
+
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
8644
|
+
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
8645
|
+
let files;
|
|
8646
|
+
try {
|
|
8647
|
+
files = (0, import_node_fs17.readdirSync)(auditDir).filter((file) => AUDIT_FILE_RE.test(file) && fileOverlaps(file, from, to)).sort();
|
|
8648
|
+
} catch {
|
|
8649
|
+
return { requestCount: 0, errorCount: 0, complete: false };
|
|
8650
|
+
}
|
|
8651
|
+
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
8652
|
+
for (const file of files) {
|
|
8653
|
+
const auditPath = (0, import_node_path10.join)(auditDir, file);
|
|
8654
|
+
try {
|
|
8655
|
+
const auditBytes = (0, import_node_fs17.statSync)(auditPath).size;
|
|
8656
|
+
const statsPath = (0, import_node_path10.join)(auditDir, auditStatsFileName(file));
|
|
8657
|
+
const persisted = readPersisted(statsPath);
|
|
8658
|
+
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
8659
|
+
total.requestCount += persisted.requestCount;
|
|
8660
|
+
total.errorCount += persisted.errorCount;
|
|
8661
|
+
continue;
|
|
8662
|
+
}
|
|
8663
|
+
const resumable = persisted && persisted.complete && persisted.auditBytes < auditBytes && queryCovers(persisted, from, to) ? persisted : null;
|
|
8664
|
+
const scanned = await scanAuditFile(
|
|
8665
|
+
auditPath,
|
|
8666
|
+
resumable?.auditBytes ?? 0,
|
|
8667
|
+
auditBytes,
|
|
8668
|
+
from,
|
|
8669
|
+
to
|
|
8670
|
+
);
|
|
8671
|
+
total.requestCount += scanned.filtered.requestCount + (resumable?.requestCount ?? 0);
|
|
8672
|
+
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
8673
|
+
total.complete = total.complete && scanned.filtered.complete;
|
|
8674
|
+
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
8675
|
+
if (current.complete) (0, import_node_fs17.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
|
|
8676
|
+
} catch {
|
|
8677
|
+
total.complete = false;
|
|
8678
|
+
}
|
|
8679
|
+
}
|
|
8680
|
+
return total;
|
|
8681
|
+
}
|
|
8682
|
+
|
|
8133
8683
|
// src/audit/AuditPruneSweeper.ts
|
|
8134
8684
|
var DAY_MS = 24 * 60 * 6e4;
|
|
8135
8685
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
8136
8686
|
var AuditPruneSweeper = class {
|
|
8137
|
-
constructor(
|
|
8138
|
-
this.auditDir =
|
|
8687
|
+
constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
|
|
8688
|
+
this.auditDir = auditDir;
|
|
8139
8689
|
this.logger = logger;
|
|
8140
8690
|
this.config = config;
|
|
8141
8691
|
this.intervalMs = intervalMs;
|
|
@@ -8182,17 +8732,19 @@ var AuditPruneSweeper = class {
|
|
|
8182
8732
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
8183
8733
|
this.sweeping = true;
|
|
8184
8734
|
try {
|
|
8185
|
-
if (!(0,
|
|
8735
|
+
if (!(0, import_node_fs18.existsSync)(this.auditDir)) return 0;
|
|
8186
8736
|
const today = new Date(this.now());
|
|
8187
8737
|
const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
|
|
8188
8738
|
const cutoff = todayMidnight - (this.config.retentionDays - 1) * DAY_MS;
|
|
8189
8739
|
let removed = 0;
|
|
8190
|
-
for (const file of (0,
|
|
8740
|
+
for (const file of (0, import_node_fs18.readdirSync)(this.auditDir)) {
|
|
8191
8741
|
const dateMs = auditFileDateMs(file);
|
|
8192
8742
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
8193
8743
|
try {
|
|
8194
|
-
(0,
|
|
8744
|
+
(0, import_node_fs18.unlinkSync)((0, import_node_path11.join)(this.auditDir, file));
|
|
8195
8745
|
removed += 1;
|
|
8746
|
+
const statsPath = (0, import_node_path11.join)(this.auditDir, auditStatsFileName(file));
|
|
8747
|
+
if ((0, import_node_fs18.existsSync)(statsPath)) (0, import_node_fs18.unlinkSync)(statsPath);
|
|
8196
8748
|
} catch (error) {
|
|
8197
8749
|
this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
|
|
8198
8750
|
file,
|
|
@@ -8214,15 +8766,15 @@ var AuditPruneSweeper = class {
|
|
|
8214
8766
|
};
|
|
8215
8767
|
|
|
8216
8768
|
// src/audit/auditReader.ts
|
|
8217
|
-
var
|
|
8769
|
+
var import_node_fs19 = require("fs");
|
|
8218
8770
|
var import_node_path12 = require("path");
|
|
8219
8771
|
var DEFAULT_LIMIT = 200;
|
|
8220
8772
|
var MAX_LIMIT = 2e3;
|
|
8221
|
-
function readAuditRecords(
|
|
8222
|
-
if (!(0,
|
|
8773
|
+
function readAuditRecords(auditDir, query2 = {}) {
|
|
8774
|
+
if (!(0, import_node_fs19.existsSync)(auditDir)) return [];
|
|
8223
8775
|
let files;
|
|
8224
8776
|
try {
|
|
8225
|
-
files = (0,
|
|
8777
|
+
files = (0, import_node_fs19.readdirSync)(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
|
|
8226
8778
|
} catch {
|
|
8227
8779
|
return [];
|
|
8228
8780
|
}
|
|
@@ -8233,7 +8785,7 @@ function readAuditRecords(auditDir2, query2 = {}) {
|
|
|
8233
8785
|
for (const file of files.sort().reverse()) {
|
|
8234
8786
|
let raw;
|
|
8235
8787
|
try {
|
|
8236
|
-
raw = (0,
|
|
8788
|
+
raw = (0, import_node_fs19.readFileSync)((0, import_node_path12.join)(auditDir, file), "utf8");
|
|
8237
8789
|
} catch {
|
|
8238
8790
|
continue;
|
|
8239
8791
|
}
|
|
@@ -8262,11 +8814,11 @@ function isAuditRecord(value) {
|
|
|
8262
8814
|
}
|
|
8263
8815
|
|
|
8264
8816
|
// src/audit/AuditWriter.ts
|
|
8265
|
-
var
|
|
8817
|
+
var import_node_fs20 = require("fs");
|
|
8266
8818
|
var import_node_path13 = require("path");
|
|
8267
8819
|
var AuditWriter = class {
|
|
8268
|
-
constructor(
|
|
8269
|
-
this.auditDir =
|
|
8820
|
+
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
8821
|
+
this.auditDir = auditDir;
|
|
8270
8822
|
this.logger = logger;
|
|
8271
8823
|
this.defer = defer;
|
|
8272
8824
|
}
|
|
@@ -8296,16 +8848,30 @@ var AuditWriter = class {
|
|
|
8296
8848
|
*/
|
|
8297
8849
|
appendNow(record) {
|
|
8298
8850
|
if (!this.dirEnsured) {
|
|
8299
|
-
(0,
|
|
8851
|
+
(0, import_node_fs20.mkdirSync)(this.auditDir, { recursive: true });
|
|
8300
8852
|
this.dirEnsured = true;
|
|
8301
8853
|
}
|
|
8302
8854
|
const file = (0, import_node_path13.join)(this.auditDir, auditFileName(record.ts));
|
|
8303
|
-
|
|
8855
|
+
const line = JSON.stringify(record) + "\n";
|
|
8856
|
+
const auditBytesBefore = (0, import_node_fs20.existsSync)(file) ? (0, import_node_fs20.statSync)(file).size : 0;
|
|
8857
|
+
(0, import_node_fs20.appendFileSync)(file, line, "utf8");
|
|
8858
|
+
try {
|
|
8859
|
+
updateAuditStatsAfterAppend(
|
|
8860
|
+
file,
|
|
8861
|
+
auditBytesBefore,
|
|
8862
|
+
auditBytesBefore + Buffer.byteLength(line, "utf8"),
|
|
8863
|
+
record
|
|
8864
|
+
);
|
|
8865
|
+
} catch (error) {
|
|
8866
|
+
this.logger.warn("[AuditWriter] failed to update audit stats", {
|
|
8867
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8868
|
+
});
|
|
8869
|
+
}
|
|
8304
8870
|
}
|
|
8305
8871
|
};
|
|
8306
8872
|
|
|
8307
8873
|
// src/billing/BillingPublisher.ts
|
|
8308
|
-
var
|
|
8874
|
+
var import_node_fs21 = require("fs");
|
|
8309
8875
|
var import_node_crypto13 = require("crypto");
|
|
8310
8876
|
var import_node_path14 = require("path");
|
|
8311
8877
|
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
@@ -8379,7 +8945,7 @@ var BillingPublisher = class {
|
|
|
8379
8945
|
appendNow(event) {
|
|
8380
8946
|
this.ensureDir();
|
|
8381
8947
|
const file = (0, import_node_path14.join)(this.billingDir, billingFileName(event.ts));
|
|
8382
|
-
(0,
|
|
8948
|
+
(0, import_node_fs21.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
|
|
8383
8949
|
}
|
|
8384
8950
|
/**
|
|
8385
8951
|
* One best-effort delivery attempt for an event ALREADY in the ledger. POSTs the
|
|
@@ -8429,7 +8995,7 @@ var BillingPublisher = class {
|
|
|
8429
8995
|
try {
|
|
8430
8996
|
this.ensureDir();
|
|
8431
8997
|
const file = (0, import_node_path14.join)(this.billingDir, deliveredFileName(event.ts));
|
|
8432
|
-
(0,
|
|
8998
|
+
(0, import_node_fs21.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
|
|
8433
8999
|
} catch (error) {
|
|
8434
9000
|
this.logger.warn("[BillingPublisher] failed to append delivery marker", {
|
|
8435
9001
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -8438,20 +9004,20 @@ var BillingPublisher = class {
|
|
|
8438
9004
|
}
|
|
8439
9005
|
ensureDir() {
|
|
8440
9006
|
if (this.dirEnsured) return;
|
|
8441
|
-
(0,
|
|
9007
|
+
(0, import_node_fs21.mkdirSync)(this.billingDir, { recursive: true });
|
|
8442
9008
|
this.dirEnsured = true;
|
|
8443
9009
|
}
|
|
8444
9010
|
};
|
|
8445
9011
|
|
|
8446
9012
|
// src/billing/billingReader.ts
|
|
8447
|
-
var
|
|
9013
|
+
var import_node_fs22 = require("fs");
|
|
8448
9014
|
var import_node_path15 = require("path");
|
|
8449
9015
|
function readBillingLedger(billingDir) {
|
|
8450
9016
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
8451
|
-
if (!(0,
|
|
9017
|
+
if (!(0, import_node_fs22.existsSync)(billingDir)) return view;
|
|
8452
9018
|
let files;
|
|
8453
9019
|
try {
|
|
8454
|
-
files = (0,
|
|
9020
|
+
files = (0, import_node_fs22.readdirSync)(billingDir);
|
|
8455
9021
|
} catch {
|
|
8456
9022
|
return view;
|
|
8457
9023
|
}
|
|
@@ -8482,7 +9048,7 @@ function readBillingStatus(billingDir) {
|
|
|
8482
9048
|
function parseLines(dir, file) {
|
|
8483
9049
|
let raw;
|
|
8484
9050
|
try {
|
|
8485
|
-
raw = (0,
|
|
9051
|
+
raw = (0, import_node_fs22.readFileSync)((0, import_node_path15.join)(dir, file), "utf8");
|
|
8486
9052
|
} catch {
|
|
8487
9053
|
return [];
|
|
8488
9054
|
}
|
|
@@ -8854,7 +9420,7 @@ function buildDaemon(config, paths) {
|
|
|
8854
9420
|
(0, import_outbound_api4.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
8855
9421
|
);
|
|
8856
9422
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
8857
|
-
const keyDb = new JsonOutboundKeyDb(paths.keysPath);
|
|
9423
|
+
const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
|
|
8858
9424
|
const voucherDb = new JsonVoucherDb(defaultVouchersPath(paths.configPath));
|
|
8859
9425
|
const settingsStore = new JsonApiServerSettingsStore(paths.configPath, secretBox3);
|
|
8860
9426
|
const integrationStateStore = new IntegrationStateStore(
|
|
@@ -8955,7 +9521,7 @@ function buildDaemon(config, paths) {
|
|
|
8955
9521
|
// lines through the injected logger (honors level/format/file sink).
|
|
8956
9522
|
logger
|
|
8957
9523
|
});
|
|
8958
|
-
const
|
|
9524
|
+
const auditDir = defaultAuditDir(paths.configPath);
|
|
8959
9525
|
const billingDir = defaultBillingDir(paths.configPath);
|
|
8960
9526
|
const adminServer = new AdminServer({
|
|
8961
9527
|
configPath: paths.configPath,
|
|
@@ -8989,10 +9555,16 @@ function buildDaemon(config, paths) {
|
|
|
8989
9555
|
// (NOT widening the least-authority writer — no token-returning read reachable).
|
|
8990
9556
|
oauthSessions: new OAuthSessionStore(),
|
|
8991
9557
|
// Real global fetch by default; a test seam (`paths.oauthExchangeFetch`) can
|
|
8992
|
-
// inject a mock so no real token endpoint is hit
|
|
8993
|
-
//
|
|
8994
|
-
//
|
|
8995
|
-
|
|
9558
|
+
// inject a mock so no real token endpoint is hit (one FetchLike for every
|
|
9559
|
+
// provider — the ctx below only matters on the real egress path).
|
|
9560
|
+
//
|
|
9561
|
+
// upstream-proxy: a PER-PROVIDER factory, so the exchange carries the same
|
|
9562
|
+
// `{ providerId }` ctx the CLI login and the token refresh already pass.
|
|
9563
|
+
// Without it the interactive login resolved only the global/env proxy layers
|
|
9564
|
+
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
9565
|
+
// excluded from the upstream trace, so a failing login left no evidence.
|
|
9566
|
+
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
9567
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
8996
9568
|
subscriptionAccountAppender: credentialStore,
|
|
8997
9569
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
8998
9570
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -9044,7 +9616,8 @@ function buildDaemon(config, paths) {
|
|
|
9044
9616
|
// date-rotated audit store. Bound to the store dir here so the AdminServer
|
|
9045
9617
|
// carries no path/store coupling. Records hold IP/UA/bodies → admin-only,
|
|
9046
9618
|
// NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
|
|
9047
|
-
auditReader: (query2) => readAuditRecords(
|
|
9619
|
+
auditReader: (query2) => readAuditRecords(auditDir, query2),
|
|
9620
|
+
auditStatsReader: (query2) => readAuditStats(auditDir, query2),
|
|
9048
9621
|
// billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
|
|
9049
9622
|
// secret-free total/delivered/pending counts of the durable ledger.
|
|
9050
9623
|
billingStatusReader: () => readBillingStatus(billingDir)
|
|
@@ -9054,9 +9627,9 @@ function buildDaemon(config, paths) {
|
|
|
9054
9627
|
fetchImpl: (url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init)
|
|
9055
9628
|
});
|
|
9056
9629
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth3.getSharedAccountHealth)());
|
|
9057
|
-
const auditWriter = new AuditWriter(
|
|
9058
|
-
const auditPruneSweeper = new AuditPruneSweeper(
|
|
9059
|
-
setAuditRuntime(auditWriter, auditPruneSweeper
|
|
9630
|
+
const auditWriter = new AuditWriter(auditDir, logger);
|
|
9631
|
+
const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, import_audit_types.DEFAULT_AUDIT_CONFIG);
|
|
9632
|
+
setAuditRuntime(auditWriter, auditPruneSweeper);
|
|
9060
9633
|
const billingPublisher = new BillingPublisher(billingDir, logger);
|
|
9061
9634
|
const billingRetrySweeper = new BillingRetrySweeper(
|
|
9062
9635
|
billingDir,
|
|
@@ -9120,8 +9693,8 @@ function resetDaemonSingletonsForTests() {
|
|
|
9120
9693
|
}
|
|
9121
9694
|
function isTokensStoreReadable(tokensPath) {
|
|
9122
9695
|
try {
|
|
9123
|
-
if (!(0,
|
|
9124
|
-
(0,
|
|
9696
|
+
if (!(0, import_node_fs23.existsSync)(tokensPath)) return true;
|
|
9697
|
+
(0, import_node_fs23.accessSync)(tokensPath, import_node_fs23.constants.R_OK);
|
|
9125
9698
|
return true;
|
|
9126
9699
|
} catch {
|
|
9127
9700
|
return false;
|