@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.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/bootstrap.ts
|
|
2
|
-
import { accessSync, constants as fsConstants, existsSync as
|
|
2
|
+
import { accessSync, constants as fsConstants, existsSync as existsSync19 } from "fs";
|
|
3
3
|
import { DEFAULT_AUDIT_CONFIG } from "@omnicross/contracts/audit-types";
|
|
4
4
|
import { DEFAULT_BILLING_CONFIG } from "@omnicross/contracts/billing-types";
|
|
5
5
|
import { getGeminiCodeAssistProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
@@ -114,7 +114,7 @@ async function runCodexLoopback(sessionId, codeVerifier, state, signal, deps) {
|
|
|
114
114
|
const code = await deps.codexAwaitLoopback(state, void 0, signal);
|
|
115
115
|
const result = await codexOAuth.exchangeCodeForTokens(
|
|
116
116
|
{ authorizationCode: code, codeVerifier, state },
|
|
117
|
-
deps.oauthExchangeFetch
|
|
117
|
+
deps.oauthExchangeFetch("codex")
|
|
118
118
|
);
|
|
119
119
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
120
120
|
const block = {
|
|
@@ -634,6 +634,17 @@ function handleAuditQuery(req, res, reader) {
|
|
|
634
634
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
635
635
|
res.end(JSON.stringify({ records }));
|
|
636
636
|
}
|
|
637
|
+
async function handleAuditStatsQuery(req, res, reader) {
|
|
638
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
639
|
+
const query2 = {};
|
|
640
|
+
const from = intParam(url.searchParams.get("from"));
|
|
641
|
+
if (from !== void 0) query2.from = from;
|
|
642
|
+
const to = intParam(url.searchParams.get("to"));
|
|
643
|
+
if (to !== void 0) query2.to = to;
|
|
644
|
+
const stats = reader ? await reader(query2) : { requestCount: 0, errorCount: 0, complete: true };
|
|
645
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
646
|
+
res.end(JSON.stringify(stats));
|
|
647
|
+
}
|
|
637
648
|
|
|
638
649
|
// src/admin/billingStatusApi.ts
|
|
639
650
|
function handleBillingStatus(res, reader) {
|
|
@@ -2145,7 +2156,13 @@ function listMappablePresets() {
|
|
|
2145
2156
|
name: preset.name,
|
|
2146
2157
|
apiFormat: resolved.format,
|
|
2147
2158
|
baseUrl: preset.api_base_url,
|
|
2148
|
-
models: Array.isArray(preset.models) ? preset.models : []
|
|
2159
|
+
models: Array.isArray(preset.models) ? preset.models : [],
|
|
2160
|
+
nameKey: preset.nameKey,
|
|
2161
|
+
icon: preset.icon,
|
|
2162
|
+
description: preset.description,
|
|
2163
|
+
features: preset.features,
|
|
2164
|
+
website: preset.website,
|
|
2165
|
+
modelsEndpoint: preset.modelsEndpoint
|
|
2149
2166
|
});
|
|
2150
2167
|
}
|
|
2151
2168
|
return { mappable, excluded };
|
|
@@ -2536,7 +2553,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
2536
2553
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
2537
2554
|
if (!sessionId) return err2(400, "oauth complete requires { sessionId }");
|
|
2538
2555
|
if (!rawCode) return err2(400, "oauth complete requires { code }");
|
|
2539
|
-
const session = deps.oauthSessions.
|
|
2556
|
+
const session = deps.oauthSessions.peek(sessionId);
|
|
2540
2557
|
if (!session) return err2(410, "oauth session is unknown, expired, or already used");
|
|
2541
2558
|
if (session.providerId !== providerId) {
|
|
2542
2559
|
return err2(400, `oauth session does not match provider '${providerId}'`);
|
|
@@ -2550,13 +2567,15 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
2550
2567
|
}
|
|
2551
2568
|
code = splitCode;
|
|
2552
2569
|
}
|
|
2570
|
+
const exchangeFetch = deps.oauthExchangeFetch(providerId);
|
|
2553
2571
|
let block;
|
|
2554
2572
|
try {
|
|
2555
|
-
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state,
|
|
2573
|
+
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
2556
2574
|
} catch (exchangeError) {
|
|
2557
2575
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
2558
2576
|
return err2(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
2559
2577
|
}
|
|
2578
|
+
deps.oauthSessions.consume(sessionId);
|
|
2560
2579
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
2561
2580
|
await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
|
|
2562
2581
|
const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
@@ -2806,8 +2825,8 @@ function validateAuditSegment(patch) {
|
|
|
2806
2825
|
}
|
|
2807
2826
|
}
|
|
2808
2827
|
const maxBodyBytes = audit["maxBodyBytes"];
|
|
2809
|
-
if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes <
|
|
2810
|
-
errors.push("audit.maxBodyBytes must be a non-negative number");
|
|
2828
|
+
if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes < -1)) {
|
|
2829
|
+
errors.push("audit.maxBodyBytes must be -1 or a non-negative number");
|
|
2811
2830
|
}
|
|
2812
2831
|
const retentionDays = audit["retentionDays"];
|
|
2813
2832
|
if (retentionDays !== void 0 && (typeof retentionDays !== "number" || !Number.isFinite(retentionDays) || retentionDays < 0)) {
|
|
@@ -3252,18 +3271,16 @@ function preserveWebhookSecrets(incoming, current) {
|
|
|
3252
3271
|
}
|
|
3253
3272
|
|
|
3254
3273
|
// src/audit/auditRuntime.ts
|
|
3255
|
-
import { join as join4 } from "path";
|
|
3256
3274
|
import { setAuditCaptureConfig, setAuditSink } from "@omnicross/core/pipeline/auditSink";
|
|
3257
3275
|
import { setUpstreamTracePath } from "@omnicross/core/pipeline/upstreamTrace";
|
|
3258
3276
|
var writer = null;
|
|
3259
3277
|
var sweeper = null;
|
|
3260
|
-
|
|
3261
|
-
function setAuditRuntime(w, s, dir) {
|
|
3278
|
+
function setAuditRuntime(w, s) {
|
|
3262
3279
|
writer = w;
|
|
3263
3280
|
sweeper = s;
|
|
3264
|
-
auditDir = dir;
|
|
3265
3281
|
}
|
|
3266
3282
|
function applyAuditConfig(config) {
|
|
3283
|
+
setUpstreamTracePath(null);
|
|
3267
3284
|
const enabled = config?.enabled === true && writer !== null;
|
|
3268
3285
|
if (enabled && config) {
|
|
3269
3286
|
setAuditCaptureConfig(config);
|
|
@@ -3273,11 +3290,9 @@ function applyAuditConfig(config) {
|
|
|
3273
3290
|
sweeper.configure(config);
|
|
3274
3291
|
sweeper.start();
|
|
3275
3292
|
}
|
|
3276
|
-
setUpstreamTracePath(config.captureBodies ? join4(auditDir, "upstream-trace.jsonl") : null);
|
|
3277
3293
|
} else {
|
|
3278
3294
|
setAuditCaptureConfig(null);
|
|
3279
3295
|
setAuditSink(null);
|
|
3280
|
-
setUpstreamTracePath(null);
|
|
3281
3296
|
if (sweeper) {
|
|
3282
3297
|
if (config) sweeper.configure(config);
|
|
3283
3298
|
sweeper.dispose();
|
|
@@ -3291,7 +3306,6 @@ function resetAuditRuntimeForTests() {
|
|
|
3291
3306
|
if (sweeper) sweeper.dispose();
|
|
3292
3307
|
writer = null;
|
|
3293
3308
|
sweeper = null;
|
|
3294
|
-
auditDir = "";
|
|
3295
3309
|
}
|
|
3296
3310
|
|
|
3297
3311
|
// src/billing/billingRuntime.ts
|
|
@@ -4136,6 +4150,11 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4136
4150
|
}
|
|
4137
4151
|
|
|
4138
4152
|
// src/admin/adminApi.ts
|
|
4153
|
+
import {
|
|
4154
|
+
ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
4155
|
+
getSharedAccountRouteActivity
|
|
4156
|
+
} from "@omnicross/core/pipeline/AccountRouteActivity";
|
|
4157
|
+
import { getSharedOverloadCounter } from "@omnicross/core/pipeline/ServerOverloadCounter";
|
|
4139
4158
|
function readBody(req) {
|
|
4140
4159
|
return new Promise((resolve2, reject) => {
|
|
4141
4160
|
const chunks = [];
|
|
@@ -4172,6 +4191,9 @@ function toKeyInfo(row) {
|
|
|
4172
4191
|
id: row.id,
|
|
4173
4192
|
name: row.name,
|
|
4174
4193
|
keyPrefix: row.keyPrefix,
|
|
4194
|
+
// True only when a reversible `keySecret` envelope was persisted at creation
|
|
4195
|
+
// — gates the UI "view key" eye. Legacy hash-only rows read as absent.
|
|
4196
|
+
revealable: Boolean(row.keySecret),
|
|
4175
4197
|
enabled: row.enabled,
|
|
4176
4198
|
createdAt: row.createdAt,
|
|
4177
4199
|
lastUsedAt: row.lastUsedAt,
|
|
@@ -4827,7 +4849,13 @@ function handlePresets(res, method) {
|
|
|
4827
4849
|
name: p.name,
|
|
4828
4850
|
apiFormat: p.apiFormat,
|
|
4829
4851
|
baseUrl: p.baseUrl,
|
|
4830
|
-
models: p.models
|
|
4852
|
+
models: p.models,
|
|
4853
|
+
nameKey: p.nameKey,
|
|
4854
|
+
icon: p.icon,
|
|
4855
|
+
description: p.description,
|
|
4856
|
+
features: p.features,
|
|
4857
|
+
website: p.website,
|
|
4858
|
+
modelsEndpoint: p.modelsEndpoint
|
|
4831
4859
|
}));
|
|
4832
4860
|
return writeJson3(res, 200, { presets, excluded });
|
|
4833
4861
|
}
|
|
@@ -4861,12 +4889,27 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
4861
4889
|
plaintextOnce: created.plaintextOnce
|
|
4862
4890
|
});
|
|
4863
4891
|
}
|
|
4892
|
+
if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
|
|
4893
|
+
const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
|
|
4894
|
+
if (revealed !== null) return writeJson3(res, 200, { key: revealed });
|
|
4895
|
+
const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
|
|
4896
|
+
if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
|
|
4897
|
+
return writeJsonError(
|
|
4898
|
+
res,
|
|
4899
|
+
409,
|
|
4900
|
+
`key '${rest[0]}' is not revealable (created before revealable key storage)`
|
|
4901
|
+
);
|
|
4902
|
+
}
|
|
4864
4903
|
const id = rest[0];
|
|
4865
4904
|
const action = rest[1];
|
|
4866
4905
|
if (method === "POST" && id && action === "revoke") {
|
|
4867
4906
|
const ok = await deps.keyDb.outboundApiKeysRevoke(id);
|
|
4868
4907
|
return writeJson3(res, ok ? 200 : 404, { ok });
|
|
4869
4908
|
}
|
|
4909
|
+
if (method === "DELETE" && id && !action) {
|
|
4910
|
+
const ok = await deps.keyDb.outboundApiKeysDelete(id);
|
|
4911
|
+
return writeJson3(res, ok ? 200 : 404, { ok });
|
|
4912
|
+
}
|
|
4870
4913
|
if (method === "POST" && id && action === "enabled") {
|
|
4871
4914
|
const body = await readJsonBody3(req);
|
|
4872
4915
|
const enabled = body["enabled"] === true;
|
|
@@ -5043,6 +5086,40 @@ async function handleServer(req, res, method, deps) {
|
|
|
5043
5086
|
return writeJsonError(res, 405, `method ${method} not allowed on server`);
|
|
5044
5087
|
}
|
|
5045
5088
|
async function handleAccounts(req, res, method, rest, deps) {
|
|
5089
|
+
if (rest[0] === "route-activity" && rest.length === 1) {
|
|
5090
|
+
if (method !== "GET") {
|
|
5091
|
+
return writeJsonError(res, 405, `method ${method} not allowed on account route activity`);
|
|
5092
|
+
}
|
|
5093
|
+
const query2 = requestQuery(req);
|
|
5094
|
+
const parsedLimit = Number(query2.get("limit") ?? "100");
|
|
5095
|
+
const records = getSharedAccountRouteActivity().list({
|
|
5096
|
+
providerId: query2.get("providerId") ?? void 0,
|
|
5097
|
+
accountId: query2.get("accountId") ?? void 0,
|
|
5098
|
+
sessionKey: query2.get("sessionKey") ?? void 0,
|
|
5099
|
+
limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
|
|
5100
|
+
});
|
|
5101
|
+
return writeJson3(res, 200, {
|
|
5102
|
+
available: true,
|
|
5103
|
+
records,
|
|
5104
|
+
capacity: ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
5105
|
+
collectedAt: Date.now()
|
|
5106
|
+
});
|
|
5107
|
+
}
|
|
5108
|
+
if (rest[0] === "overload-counters" && rest.length === 1) {
|
|
5109
|
+
if (method !== "GET") {
|
|
5110
|
+
return writeJsonError(res, 405, `method ${method} not allowed on overload counters`);
|
|
5111
|
+
}
|
|
5112
|
+
const query2 = requestQuery(req);
|
|
5113
|
+
const entries = getSharedOverloadCounter().list({
|
|
5114
|
+
providerId: query2.get("providerId") ?? void 0,
|
|
5115
|
+
accountId: query2.get("accountId") ?? void 0
|
|
5116
|
+
});
|
|
5117
|
+
return writeJson3(res, 200, {
|
|
5118
|
+
available: true,
|
|
5119
|
+
entries,
|
|
5120
|
+
collectedAt: Date.now()
|
|
5121
|
+
});
|
|
5122
|
+
}
|
|
5046
5123
|
if (rest[0] === "allowances") {
|
|
5047
5124
|
return handleAccountAllowanceApi(
|
|
5048
5125
|
req,
|
|
@@ -5189,8 +5266,13 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
5189
5266
|
if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
|
|
5190
5267
|
return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
5191
5268
|
}
|
|
5192
|
-
const result = await deps.accountProbeService.
|
|
5193
|
-
return writeJson3(res, 200, {
|
|
5269
|
+
const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
|
|
5270
|
+
return writeJson3(res, 200, {
|
|
5271
|
+
ok: result.ok,
|
|
5272
|
+
marked: result.marked,
|
|
5273
|
+
tier: result.tier,
|
|
5274
|
+
model: result.model
|
|
5275
|
+
});
|
|
5194
5276
|
}
|
|
5195
5277
|
if (method === "POST" && rest[2] === "label") {
|
|
5196
5278
|
const accountId = rest[1];
|
|
@@ -5561,7 +5643,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
5561
5643
|
}
|
|
5562
5644
|
|
|
5563
5645
|
// src/admin/version.ts
|
|
5564
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
5646
|
+
var DAEMON_VERSION = true ? "0.1.7" : "0.0.0-dev";
|
|
5565
5647
|
|
|
5566
5648
|
// src/admin/AdminServer.ts
|
|
5567
5649
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -5669,6 +5751,10 @@ var AdminServer = class {
|
|
|
5669
5751
|
handleAuditQuery(req, res, this.deps.auditReader);
|
|
5670
5752
|
return;
|
|
5671
5753
|
}
|
|
5754
|
+
if (path2 === "/admin/api/audit/stats" && (req.method === "GET" || req.method === "HEAD")) {
|
|
5755
|
+
await handleAuditStatsQuery(req, res, this.deps.auditStatsReader);
|
|
5756
|
+
return;
|
|
5757
|
+
}
|
|
5672
5758
|
if (path2 === "/admin/api/billing-status" && (req.method === "GET" || req.method === "HEAD")) {
|
|
5673
5759
|
handleBillingStatus(res, this.deps.billingStatusReader);
|
|
5674
5760
|
return;
|
|
@@ -5785,20 +5871,36 @@ var OAuthSessionStore = class {
|
|
|
5785
5871
|
return sessionId;
|
|
5786
5872
|
}
|
|
5787
5873
|
/**
|
|
5788
|
-
*
|
|
5789
|
-
*
|
|
5790
|
-
* dropped). A `null` return means the completer must reject (no
|
|
5791
|
-
* write).
|
|
5874
|
+
* NON-DESTRUCTIVE lookup: return the session for `sessionId`, or `null` when
|
|
5875
|
+
* it is unknown, already consumed, or past its TTL (an expired entry is
|
|
5876
|
+
* dropped here). A `null` return means the completer must reject (no
|
|
5877
|
+
* exchange, no write).
|
|
5878
|
+
*
|
|
5879
|
+
* Deliberately NOT a consume: the completer peeks, runs the token exchange,
|
|
5880
|
+
* and only {@link consume}s once a token has actually been minted. Consuming
|
|
5881
|
+
* up-front burned the session on EVERY failed exchange (a mistyped/expired
|
|
5882
|
+
* pasted code, a proxy hiccup), so the user's natural retry hit
|
|
5883
|
+
* "session is unknown, expired, or already used" and the login became
|
|
5884
|
+
* unrecoverable without restarting the whole flow.
|
|
5792
5885
|
*/
|
|
5793
|
-
|
|
5886
|
+
peek(sessionId) {
|
|
5794
5887
|
this.sweep();
|
|
5795
5888
|
const session = this.sessions.get(sessionId);
|
|
5796
5889
|
if (!session) return null;
|
|
5797
|
-
|
|
5798
|
-
|
|
5890
|
+
if (Date.now() - session.createdAt > this.ttlMs) {
|
|
5891
|
+
this.sessions.delete(sessionId);
|
|
5892
|
+
return null;
|
|
5893
|
+
}
|
|
5799
5894
|
return session;
|
|
5800
5895
|
}
|
|
5801
|
-
/**
|
|
5896
|
+
/**
|
|
5897
|
+
* SINGLE-USE burn: drop the session so the same `sessionId` can never be
|
|
5898
|
+
* completed twice. Called ONLY after a successful token exchange.
|
|
5899
|
+
*/
|
|
5900
|
+
consume(sessionId) {
|
|
5901
|
+
this.sessions.delete(sessionId);
|
|
5902
|
+
}
|
|
5903
|
+
/** Drop every session past its TTL. Called on each put/peek. */
|
|
5802
5904
|
sweep() {
|
|
5803
5905
|
const now = Date.now();
|
|
5804
5906
|
for (const [id, session] of this.sessions) {
|
|
@@ -5813,6 +5915,10 @@ var LOOPBACK_HOST = "127.0.0.1";
|
|
|
5813
5915
|
var LOOPBACK_PORT = 1455;
|
|
5814
5916
|
var CALLBACK_PATH = "/auth/callback";
|
|
5815
5917
|
var DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
5918
|
+
var HTML_HEADERS = {
|
|
5919
|
+
"Content-Type": "text/html",
|
|
5920
|
+
Connection: "close"
|
|
5921
|
+
};
|
|
5816
5922
|
function pageHtml(message) {
|
|
5817
5923
|
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>`;
|
|
5818
5924
|
}
|
|
@@ -5823,30 +5929,31 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
5823
5929
|
if (settled) return;
|
|
5824
5930
|
settled = true;
|
|
5825
5931
|
clearTimeout(timer);
|
|
5826
|
-
|
|
5932
|
+
fn();
|
|
5933
|
+
server2.close();
|
|
5827
5934
|
};
|
|
5828
5935
|
const server = createServer((req, res) => {
|
|
5829
5936
|
const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
|
|
5830
5937
|
if (url.pathname !== CALLBACK_PATH) {
|
|
5831
|
-
res.writeHead(404,
|
|
5938
|
+
res.writeHead(404, HTML_HEADERS);
|
|
5832
5939
|
res.end(pageHtml("Not found"));
|
|
5833
5940
|
return;
|
|
5834
5941
|
}
|
|
5835
5942
|
const code = url.searchParams.get("code");
|
|
5836
5943
|
const state = url.searchParams.get("state");
|
|
5837
5944
|
if (!code) {
|
|
5838
|
-
res.writeHead(400,
|
|
5945
|
+
res.writeHead(400, HTML_HEADERS);
|
|
5839
5946
|
res.end(pageHtml("Login failed: missing authorization code."));
|
|
5840
5947
|
finish(server, () => reject(new Error("login: callback did not include an authorization code")));
|
|
5841
5948
|
return;
|
|
5842
5949
|
}
|
|
5843
5950
|
if (state !== expectedState) {
|
|
5844
|
-
res.writeHead(400,
|
|
5951
|
+
res.writeHead(400, HTML_HEADERS);
|
|
5845
5952
|
res.end(pageHtml("Login failed: state mismatch."));
|
|
5846
5953
|
finish(server, () => reject(new Error("login: callback state did not match (possible CSRF) \u2014 aborting")));
|
|
5847
5954
|
return;
|
|
5848
5955
|
}
|
|
5849
|
-
res.writeHead(200,
|
|
5956
|
+
res.writeHead(200, HTML_HEADERS);
|
|
5850
5957
|
res.end(pageHtml("Login complete."));
|
|
5851
5958
|
finish(server, () => resolve2(code));
|
|
5852
5959
|
});
|
|
@@ -5943,30 +6050,30 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
5943
6050
|
}
|
|
5944
6051
|
|
|
5945
6052
|
// src/commands/paths.ts
|
|
5946
|
-
import { dirname as dirname5, join as
|
|
6053
|
+
import { dirname as dirname5, join as join4 } from "path";
|
|
5947
6054
|
function defaultVouchersPath(configPath) {
|
|
5948
|
-
return
|
|
6055
|
+
return join4(dirname5(configPath), "vouchers.json");
|
|
5949
6056
|
}
|
|
5950
6057
|
function defaultIntegrationsPath(configPath) {
|
|
5951
|
-
return
|
|
6058
|
+
return join4(dirname5(configPath), "integrations.json");
|
|
5952
6059
|
}
|
|
5953
6060
|
function defaultPricingPath(configPath) {
|
|
5954
|
-
return
|
|
6061
|
+
return join4(dirname5(configPath), "pricing.json");
|
|
5955
6062
|
}
|
|
5956
6063
|
function defaultPricingRefreshStatePath(configPath) {
|
|
5957
|
-
return
|
|
6064
|
+
return join4(dirname5(configPath), "pricing-refresh.json");
|
|
5958
6065
|
}
|
|
5959
6066
|
function defaultAccountAllowancePath(configPath) {
|
|
5960
|
-
return
|
|
6067
|
+
return join4(dirname5(configPath), "allowance-cache.json");
|
|
5961
6068
|
}
|
|
5962
6069
|
function defaultUsageEventsPath(configPath) {
|
|
5963
|
-
return
|
|
6070
|
+
return join4(dirname5(configPath), "usage-events.jsonl");
|
|
5964
6071
|
}
|
|
5965
6072
|
function defaultAuditDir(configPath) {
|
|
5966
|
-
return
|
|
6073
|
+
return join4(dirname5(configPath), "audit");
|
|
5967
6074
|
}
|
|
5968
6075
|
function defaultBillingDir(configPath) {
|
|
5969
|
-
return
|
|
6076
|
+
return join4(dirname5(configPath), "billing");
|
|
5970
6077
|
}
|
|
5971
6078
|
|
|
5972
6079
|
// src/ports/ConfigFileProviderConfigSource.ts
|
|
@@ -6362,8 +6469,12 @@ var JsonlUsageEventStore = class {
|
|
|
6362
6469
|
reasoningTokens: 0,
|
|
6363
6470
|
costUsd: 0,
|
|
6364
6471
|
costSavedByCacheUsd: 0,
|
|
6365
|
-
eventCount: 0
|
|
6472
|
+
eventCount: 0,
|
|
6473
|
+
cacheEligibleEventCount: 0,
|
|
6474
|
+
coldCacheEventCount: 0,
|
|
6475
|
+
medianCacheHitRate: null
|
|
6366
6476
|
};
|
|
6477
|
+
const perEventHitRates = [];
|
|
6367
6478
|
for (const row of this.readRows(range)) {
|
|
6368
6479
|
totals.inputTokens += row.inputTokens;
|
|
6369
6480
|
totals.outputTokens += row.outputTokens;
|
|
@@ -6373,7 +6484,14 @@ var JsonlUsageEventStore = class {
|
|
|
6373
6484
|
totals.costUsd += row.costUsd;
|
|
6374
6485
|
totals.costSavedByCacheUsd += row.costSavedByCacheUsd;
|
|
6375
6486
|
totals.eventCount += 1;
|
|
6487
|
+
const promptSideTokens = row.inputTokens + row.cacheReadTokens + row.cacheCreationTokens;
|
|
6488
|
+
if (promptSideTokens > 0) {
|
|
6489
|
+
totals.cacheEligibleEventCount += 1;
|
|
6490
|
+
if (row.cacheReadTokens === 0) totals.coldCacheEventCount += 1;
|
|
6491
|
+
perEventHitRates.push(row.cacheReadTokens / promptSideTokens);
|
|
6492
|
+
}
|
|
6376
6493
|
}
|
|
6494
|
+
totals.medianCacheHitRate = median(perEventHitRates);
|
|
6377
6495
|
return totals;
|
|
6378
6496
|
}
|
|
6379
6497
|
async getByModel(range) {
|
|
@@ -6607,6 +6725,15 @@ var NUMERIC_FIELDS = [
|
|
|
6607
6725
|
];
|
|
6608
6726
|
var NULLABLE_STRING_FIELDS = ["messageId", "parentMessageId", "sessionId", "apiKeyId"];
|
|
6609
6727
|
var isStringOrNull = (v) => v === null || typeof v === "string";
|
|
6728
|
+
var CACHE_KEY_SOURCES = /* @__PURE__ */ new Set([
|
|
6729
|
+
"client",
|
|
6730
|
+
"session-header",
|
|
6731
|
+
"thread-header",
|
|
6732
|
+
"body-session-id",
|
|
6733
|
+
"body-thread-id",
|
|
6734
|
+
"content-fingerprint",
|
|
6735
|
+
"none"
|
|
6736
|
+
]);
|
|
6610
6737
|
function isUsageEventRecord(parsed) {
|
|
6611
6738
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
6612
6739
|
const r = parsed;
|
|
@@ -6614,6 +6741,10 @@ function isUsageEventRecord(parsed) {
|
|
|
6614
6741
|
if (typeof r["providerId"] !== "string") return false;
|
|
6615
6742
|
if (typeof r["model"] !== "string") return false;
|
|
6616
6743
|
if (typeof r["engineOrigin"] !== "string") return false;
|
|
6744
|
+
if (r["cacheKeySource"] !== void 0 && (typeof r["cacheKeySource"] !== "string" || !CACHE_KEY_SOURCES.has(r["cacheKeySource"]))) return false;
|
|
6745
|
+
if (r["cacheKeyInjected"] !== void 0 && typeof r["cacheKeyInjected"] !== "boolean") {
|
|
6746
|
+
return false;
|
|
6747
|
+
}
|
|
6617
6748
|
for (const f of NULLABLE_STRING_FIELDS) {
|
|
6618
6749
|
if (!isStringOrNull(r[f])) return false;
|
|
6619
6750
|
}
|
|
@@ -6623,14 +6754,30 @@ function isUsageEventRecord(parsed) {
|
|
|
6623
6754
|
}
|
|
6624
6755
|
return true;
|
|
6625
6756
|
}
|
|
6757
|
+
function median(values) {
|
|
6758
|
+
if (values.length === 0) return null;
|
|
6759
|
+
values.sort((a, b) => a - b);
|
|
6760
|
+
const middle = Math.floor(values.length / 2);
|
|
6761
|
+
return values.length % 2 === 1 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
|
|
6762
|
+
}
|
|
6626
6763
|
|
|
6627
6764
|
// src/ports/JsonOutboundKeyDb.ts
|
|
6628
6765
|
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
6629
6766
|
var JsonOutboundKeyDb = class {
|
|
6630
|
-
|
|
6767
|
+
/**
|
|
6768
|
+
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
6769
|
+
* key's plaintext is persisted as a `keySecret` `enc:` envelope (enabling the
|
|
6770
|
+
* operator "view key" affordance via `outboundApiKeysReveal`). When absent the
|
|
6771
|
+
* store stays hash-only (byte-identical to the legacy behavior) and reveal
|
|
6772
|
+
* always returns `null`. Existing 1-arg call sites (tests, lightweight
|
|
6773
|
+
* embedders) keep working.
|
|
6774
|
+
*/
|
|
6775
|
+
constructor(keysPath, secretBox3) {
|
|
6631
6776
|
this.keysPath = keysPath;
|
|
6777
|
+
this.secretBox = secretBox3;
|
|
6632
6778
|
}
|
|
6633
6779
|
keysPath;
|
|
6780
|
+
secretBox;
|
|
6634
6781
|
async outboundApiKeysList() {
|
|
6635
6782
|
return this.readRows();
|
|
6636
6783
|
}
|
|
@@ -6656,10 +6803,27 @@ var JsonOutboundKeyDb = class {
|
|
|
6656
6803
|
allowedEndpoints: input.allowedEndpoints,
|
|
6657
6804
|
loopbackOnly: input.loopbackOnly
|
|
6658
6805
|
};
|
|
6806
|
+
if (input.plaintext && this.secretBox) {
|
|
6807
|
+
row.keySecret = this.secretBox.encrypt(input.plaintext);
|
|
6808
|
+
}
|
|
6659
6809
|
rows.push(row);
|
|
6660
6810
|
this.writeRows(rows);
|
|
6661
6811
|
return row;
|
|
6662
6812
|
}
|
|
6813
|
+
async outboundApiKeysReveal(id) {
|
|
6814
|
+
const rows = this.readRows();
|
|
6815
|
+
const row = rows.find((r) => r.id === id);
|
|
6816
|
+
if (!row || !row.keySecret || !this.secretBox) return null;
|
|
6817
|
+
return this.secretBox.decrypt(row.keySecret);
|
|
6818
|
+
}
|
|
6819
|
+
async outboundApiKeysDelete(id) {
|
|
6820
|
+
const rows = this.readRows();
|
|
6821
|
+
const idx = rows.findIndex((r) => r.id === id);
|
|
6822
|
+
if (idx < 0) return false;
|
|
6823
|
+
rows.splice(idx, 1);
|
|
6824
|
+
this.writeRows(rows);
|
|
6825
|
+
return true;
|
|
6826
|
+
}
|
|
6663
6827
|
async outboundApiKeysRevoke(id) {
|
|
6664
6828
|
return this.mutateRow(id, (row) => {
|
|
6665
6829
|
if (row.revokedAt !== null) return false;
|
|
@@ -7163,9 +7327,9 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
7163
7327
|
// src/ports/external-cli-credentials.ts
|
|
7164
7328
|
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
7165
7329
|
import { homedir as homedir3 } from "os";
|
|
7166
|
-
import { join as
|
|
7330
|
+
import { join as join5 } from "path";
|
|
7167
7331
|
function externalStorePath(provider, home = homedir3()) {
|
|
7168
|
-
return provider === "claude" ?
|
|
7332
|
+
return provider === "claude" ? join5(home, ".claude", ".credentials.json") : join5(home, ".codex", "auth.json");
|
|
7169
7333
|
}
|
|
7170
7334
|
function decodeJwtExpiryMs(token) {
|
|
7171
7335
|
try {
|
|
@@ -7255,9 +7419,15 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7255
7419
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
7256
7420
|
* through {@link fetchUpstream} with the account's `{ providerId, accountId }`
|
|
7257
7421
|
* ctx so the per-account/provider proxy applies. `@internal` also a test seam.
|
|
7422
|
+
*
|
|
7423
|
+
* `redactBodies` is REQUIRED here: this round-trip sends the refresh_token and
|
|
7424
|
+
* receives a fresh access/refresh token pair. Carrying a `providerId` opts the
|
|
7425
|
+
* call into the upstream trace (so a failing refresh is diagnosable), and the
|
|
7426
|
+
* trace captures bodies verbatim — without this flag every refresh would write
|
|
7427
|
+
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
7258
7428
|
*/
|
|
7259
7429
|
buildRefreshFetch(providerId, accountId) {
|
|
7260
|
-
return this.fetchImpl ?? ((url, init) => fetchUpstream3(url, init, { providerId, accountId }));
|
|
7430
|
+
return this.fetchImpl ?? ((url, init) => fetchUpstream3(url, init, { providerId, accountId, redactBodies: true }));
|
|
7261
7431
|
}
|
|
7262
7432
|
/**
|
|
7263
7433
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -7817,6 +7987,127 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7817
7987
|
// src/AccountHealthProbeScheduler.ts
|
|
7818
7988
|
import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
7819
7989
|
|
|
7990
|
+
// src/probe/CodexGenerationProbe.ts
|
|
7991
|
+
import {
|
|
7992
|
+
DEFAULT_CODEX_CLI_HEADERS,
|
|
7993
|
+
codexAcceptHeader
|
|
7994
|
+
} from "@omnicross/core/provider-proxy/identity/codexCliHeaders";
|
|
7995
|
+
var CODEX_GENERATION_PROBE_MODEL = "gpt-5.6-luna";
|
|
7996
|
+
var CODEX_GENERATION_PROBE_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
7997
|
+
var MAX_STREAM_BYTES = 256 * 1024;
|
|
7998
|
+
var PROBE_INSTRUCTION = "Return exactly PONG and no other text.";
|
|
7999
|
+
function buildCodexGenerationProbeInit(token, signal) {
|
|
8000
|
+
return {
|
|
8001
|
+
method: "POST",
|
|
8002
|
+
signal,
|
|
8003
|
+
headers: {
|
|
8004
|
+
...DEFAULT_CODEX_CLI_HEADERS,
|
|
8005
|
+
Authorization: `Bearer ${token}`,
|
|
8006
|
+
Accept: codexAcceptHeader(true),
|
|
8007
|
+
"Content-Type": "application/json"
|
|
8008
|
+
},
|
|
8009
|
+
body: JSON.stringify({
|
|
8010
|
+
model: CODEX_GENERATION_PROBE_MODEL,
|
|
8011
|
+
input: [
|
|
8012
|
+
{
|
|
8013
|
+
role: "developer",
|
|
8014
|
+
content: [{ type: "input_text", text: PROBE_INSTRUCTION }]
|
|
8015
|
+
},
|
|
8016
|
+
{
|
|
8017
|
+
role: "user",
|
|
8018
|
+
content: [{ type: "input_text", text: "Connection probe." }]
|
|
8019
|
+
}
|
|
8020
|
+
],
|
|
8021
|
+
// GPT-5.6 otherwise defaults to medium reasoning. A connectivity probe
|
|
8022
|
+
// needs the lowest-cost path and no tool reasoning.
|
|
8023
|
+
reasoning: { effort: "none" },
|
|
8024
|
+
stream: true,
|
|
8025
|
+
store: false
|
|
8026
|
+
})
|
|
8027
|
+
};
|
|
8028
|
+
}
|
|
8029
|
+
async function readCodexGenerationProbeStream(response) {
|
|
8030
|
+
if (!response.body) return { completed: false, outputChars: 0 };
|
|
8031
|
+
const reader = response.body.getReader();
|
|
8032
|
+
const decoder = new TextDecoder();
|
|
8033
|
+
let buffer = "";
|
|
8034
|
+
let bytes = 0;
|
|
8035
|
+
let outputChars = 0;
|
|
8036
|
+
try {
|
|
8037
|
+
while (true) {
|
|
8038
|
+
const { done, value } = await reader.read();
|
|
8039
|
+
if (done) break;
|
|
8040
|
+
bytes += value.byteLength;
|
|
8041
|
+
if (bytes > MAX_STREAM_BYTES) {
|
|
8042
|
+
await reader.cancel();
|
|
8043
|
+
return { completed: false, outputChars };
|
|
8044
|
+
}
|
|
8045
|
+
buffer += decoder.decode(value, { stream: true });
|
|
8046
|
+
buffer = buffer.replace(/\r\n/g, "\n");
|
|
8047
|
+
let boundary = buffer.indexOf("\n\n");
|
|
8048
|
+
while (boundary >= 0) {
|
|
8049
|
+
const block = buffer.slice(0, boundary);
|
|
8050
|
+
buffer = buffer.slice(boundary + 2);
|
|
8051
|
+
const event = parseSseBlock(block);
|
|
8052
|
+
if (event) {
|
|
8053
|
+
const type = event["type"];
|
|
8054
|
+
if (type === "response.output_text.delta" && typeof event["delta"] === "string") {
|
|
8055
|
+
outputChars += event["delta"].length;
|
|
8056
|
+
} else if (type === "response.output_text.done" && typeof event["text"] === "string") {
|
|
8057
|
+
outputChars = Math.max(outputChars, event["text"].length);
|
|
8058
|
+
} else if (type === "response.failed" || type === "error") {
|
|
8059
|
+
await reader.cancel();
|
|
8060
|
+
return { completed: false, outputChars };
|
|
8061
|
+
} else if (type === "response.completed") {
|
|
8062
|
+
const completedResponse = asRecord(event["response"]);
|
|
8063
|
+
const status = completedResponse?.["status"];
|
|
8064
|
+
outputChars = Math.max(outputChars, countCompletedOutputChars(completedResponse));
|
|
8065
|
+
await reader.cancel();
|
|
8066
|
+
return {
|
|
8067
|
+
completed: (status === void 0 || status === "completed") && outputChars > 0,
|
|
8068
|
+
outputChars
|
|
8069
|
+
};
|
|
8070
|
+
}
|
|
8071
|
+
}
|
|
8072
|
+
boundary = buffer.indexOf("\n\n");
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
} catch {
|
|
8076
|
+
return { completed: false, outputChars };
|
|
8077
|
+
} finally {
|
|
8078
|
+
reader.releaseLock();
|
|
8079
|
+
}
|
|
8080
|
+
return { completed: false, outputChars };
|
|
8081
|
+
}
|
|
8082
|
+
function parseSseBlock(block) {
|
|
8083
|
+
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
8084
|
+
if (!data || data === "[DONE]") return null;
|
|
8085
|
+
try {
|
|
8086
|
+
return JSON.parse(data);
|
|
8087
|
+
} catch {
|
|
8088
|
+
return null;
|
|
8089
|
+
}
|
|
8090
|
+
}
|
|
8091
|
+
function asRecord(value) {
|
|
8092
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
8093
|
+
}
|
|
8094
|
+
function countCompletedOutputChars(response) {
|
|
8095
|
+
const output = response?.["output"];
|
|
8096
|
+
if (!Array.isArray(output)) return 0;
|
|
8097
|
+
let chars = 0;
|
|
8098
|
+
for (const item of output) {
|
|
8099
|
+
const content = asRecord(item)?.["content"];
|
|
8100
|
+
if (!Array.isArray(content)) continue;
|
|
8101
|
+
for (const part of content) {
|
|
8102
|
+
const record = asRecord(part);
|
|
8103
|
+
if (record?.["type"] === "output_text" && typeof record["text"] === "string") {
|
|
8104
|
+
chars += record["text"].length;
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
return chars;
|
|
8109
|
+
}
|
|
8110
|
+
|
|
7820
8111
|
// src/probe/ProbeStrategy.ts
|
|
7821
8112
|
var PROVIDER_PROBE_PLANS = {
|
|
7822
8113
|
claude: {
|
|
@@ -7944,17 +8235,17 @@ var AccountHealthProbeScheduler = class {
|
|
|
7944
8235
|
}
|
|
7945
8236
|
if (readThrew) {
|
|
7946
8237
|
this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
|
|
7947
|
-
return { ok: false, marked: false };
|
|
8238
|
+
return { ok: false, marked: false, tier: "local" };
|
|
7948
8239
|
}
|
|
7949
8240
|
if (!token) {
|
|
7950
8241
|
this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
|
|
7951
8242
|
this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
|
|
7952
|
-
return { ok: false, marked: true };
|
|
8243
|
+
return { ok: false, marked: true, tier: "local" };
|
|
7953
8244
|
}
|
|
7954
8245
|
const plan = this.planFor(providerId);
|
|
7955
8246
|
if (plan.kind === "local") {
|
|
7956
8247
|
this.record(providerId, accountId, { ts: now, ok: true, tier: "local" });
|
|
7957
|
-
return { ok: true, marked: false };
|
|
8248
|
+
return { ok: true, marked: false, tier: "local" };
|
|
7958
8249
|
}
|
|
7959
8250
|
const start = this.now();
|
|
7960
8251
|
let status = null;
|
|
@@ -7979,7 +8270,60 @@ var AccountHealthProbeScheduler = class {
|
|
|
7979
8270
|
latencyMs,
|
|
7980
8271
|
tier: "upstream"
|
|
7981
8272
|
});
|
|
7982
|
-
return { ok: status !== null && status < 400, marked };
|
|
8273
|
+
return { ok: status !== null && status < 400, marked, tier: "upstream" };
|
|
8274
|
+
}
|
|
8275
|
+
/**
|
|
8276
|
+
* Manual connection test. Codex performs a real, quota-consuming generation;
|
|
8277
|
+
* every other provider keeps its existing cheap probe. Scheduled sweeps never
|
|
8278
|
+
* call this method, so they remain non-billable.
|
|
8279
|
+
*/
|
|
8280
|
+
async testAccountConnection(providerId, accountId) {
|
|
8281
|
+
if (providerId !== "codex") return this.probeAccount(providerId, accountId);
|
|
8282
|
+
const now = this.now();
|
|
8283
|
+
let token;
|
|
8284
|
+
try {
|
|
8285
|
+
token = await this.store.getAccessTokenForAccount(providerId, accountId);
|
|
8286
|
+
} catch {
|
|
8287
|
+
this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
|
|
8288
|
+
return { ok: false, marked: false, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
|
|
8289
|
+
}
|
|
8290
|
+
if (!token) {
|
|
8291
|
+
this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
|
|
8292
|
+
this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
|
|
8293
|
+
return { ok: false, marked: true, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
|
|
8294
|
+
}
|
|
8295
|
+
const startedAt = this.now();
|
|
8296
|
+
let attempt = await this.runCodexGenerationAttempt(accountId, token);
|
|
8297
|
+
if (attempt.status === 401 && this.store.refreshAccountToken) {
|
|
8298
|
+
try {
|
|
8299
|
+
if (await this.store.refreshAccountToken(providerId, accountId)) {
|
|
8300
|
+
const refreshed = await this.store.getAccessTokenForAccount(providerId, accountId);
|
|
8301
|
+
if (refreshed) attempt = await this.runCodexGenerationAttempt(accountId, refreshed);
|
|
8302
|
+
}
|
|
8303
|
+
} catch {
|
|
8304
|
+
}
|
|
8305
|
+
}
|
|
8306
|
+
const latencyMs = this.now() - startedAt;
|
|
8307
|
+
const ok = attempt.status !== null && attempt.status >= 200 && attempt.status < 300 && attempt.completed;
|
|
8308
|
+
let marked = false;
|
|
8309
|
+
if (ok) {
|
|
8310
|
+
this.health.clearTransientMark(providerId, accountId);
|
|
8311
|
+
} else if (attempt.status === 401 || attempt.status === 403) {
|
|
8312
|
+
marked = this.applyOutcome(providerId, accountId, attempt.status, attempt.bodyText, now);
|
|
8313
|
+
}
|
|
8314
|
+
this.record(providerId, accountId, {
|
|
8315
|
+
ts: now,
|
|
8316
|
+
ok,
|
|
8317
|
+
status: attempt.status,
|
|
8318
|
+
latencyMs,
|
|
8319
|
+
tier: "generation"
|
|
8320
|
+
});
|
|
8321
|
+
return {
|
|
8322
|
+
ok,
|
|
8323
|
+
marked,
|
|
8324
|
+
tier: "generation",
|
|
8325
|
+
model: CODEX_GENERATION_PROBE_MODEL
|
|
8326
|
+
};
|
|
7983
8327
|
}
|
|
7984
8328
|
/** Per-account rolling history for the authed admin surface (design D5). */
|
|
7985
8329
|
getAllHistory() {
|
|
@@ -8036,6 +8380,24 @@ var AccountHealthProbeScheduler = class {
|
|
|
8036
8380
|
return "";
|
|
8037
8381
|
}
|
|
8038
8382
|
}
|
|
8383
|
+
async runCodexGenerationAttempt(accountId, token) {
|
|
8384
|
+
try {
|
|
8385
|
+
const timeoutMs = Math.max(this.config.timeoutMs, 15e3);
|
|
8386
|
+
const response = await this.fetchImpl(
|
|
8387
|
+
CODEX_GENERATION_PROBE_URL,
|
|
8388
|
+
buildCodexGenerationProbeInit(token, AbortSignal.timeout(timeoutMs)),
|
|
8389
|
+
{ providerId: "codex", accountId, redactBodies: true }
|
|
8390
|
+
);
|
|
8391
|
+
if (response.status < 200 || response.status >= 300) {
|
|
8392
|
+
const bodyText = response.status === 403 ? await this.readBounded(response) : void 0;
|
|
8393
|
+
return { status: response.status, completed: false, bodyText };
|
|
8394
|
+
}
|
|
8395
|
+
const stream = await readCodexGenerationProbeStream(response);
|
|
8396
|
+
return { status: response.status, completed: stream.completed };
|
|
8397
|
+
} catch {
|
|
8398
|
+
return { status: null, completed: false };
|
|
8399
|
+
}
|
|
8400
|
+
}
|
|
8039
8401
|
key(providerId, accountId) {
|
|
8040
8402
|
return `${providerId}${KEY_SEP}${accountId}`;
|
|
8041
8403
|
}
|
|
@@ -8131,7 +8493,7 @@ var AccountHealthSweeper = class {
|
|
|
8131
8493
|
};
|
|
8132
8494
|
|
|
8133
8495
|
// src/audit/AuditPruneSweeper.ts
|
|
8134
|
-
import { existsSync as
|
|
8496
|
+
import { existsSync as existsSync15, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
8135
8497
|
import { join as join7 } from "path";
|
|
8136
8498
|
|
|
8137
8499
|
// src/audit/auditFiles.ts
|
|
@@ -8154,12 +8516,213 @@ function auditFileDateMs(fileName) {
|
|
|
8154
8516
|
return d.getTime();
|
|
8155
8517
|
}
|
|
8156
8518
|
|
|
8519
|
+
// src/audit/auditStats.ts
|
|
8520
|
+
import {
|
|
8521
|
+
createReadStream,
|
|
8522
|
+
existsSync as existsSync14,
|
|
8523
|
+
readFileSync as readFileSync14,
|
|
8524
|
+
readdirSync,
|
|
8525
|
+
statSync as statSync3,
|
|
8526
|
+
writeFileSync as writeFileSync11
|
|
8527
|
+
} from "fs";
|
|
8528
|
+
import { basename, dirname as dirname7, join as join6 } from "path";
|
|
8529
|
+
var SIDECAR_VERSION = 1;
|
|
8530
|
+
var META_PREFIX_BYTES = 64 * 1024;
|
|
8531
|
+
var READ_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
8532
|
+
function auditStatsFileName(auditFile) {
|
|
8533
|
+
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
8534
|
+
}
|
|
8535
|
+
function readPersisted(path2) {
|
|
8536
|
+
if (!existsSync14(path2)) return null;
|
|
8537
|
+
try {
|
|
8538
|
+
const value = JSON.parse(readFileSync14(path2, "utf8"));
|
|
8539
|
+
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)) {
|
|
8540
|
+
return null;
|
|
8541
|
+
}
|
|
8542
|
+
return value;
|
|
8543
|
+
} catch {
|
|
8544
|
+
return null;
|
|
8545
|
+
}
|
|
8546
|
+
}
|
|
8547
|
+
function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
|
|
8548
|
+
const statsPath = join6(dirname7(auditPath), auditStatsFileName(basename(auditPath)));
|
|
8549
|
+
const previous = auditBytesBefore === 0 ? {
|
|
8550
|
+
version: SIDECAR_VERSION,
|
|
8551
|
+
auditBytes: 0,
|
|
8552
|
+
requestCount: 0,
|
|
8553
|
+
errorCount: 0,
|
|
8554
|
+
complete: true,
|
|
8555
|
+
minTs: null,
|
|
8556
|
+
maxTs: null
|
|
8557
|
+
} : readPersisted(statsPath);
|
|
8558
|
+
if (!previous || !previous.complete || previous.auditBytes !== auditBytesBefore) return;
|
|
8559
|
+
const next = {
|
|
8560
|
+
version: SIDECAR_VERSION,
|
|
8561
|
+
auditBytes: auditBytesAfter,
|
|
8562
|
+
requestCount: previous.requestCount + 1,
|
|
8563
|
+
errorCount: previous.errorCount + (record.status >= 400 || Boolean(record.error) ? 1 : 0),
|
|
8564
|
+
complete: true,
|
|
8565
|
+
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
8566
|
+
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
8567
|
+
};
|
|
8568
|
+
writeFileSync11(statsPath, JSON.stringify(next), "utf8");
|
|
8569
|
+
}
|
|
8570
|
+
function queryCovers(stats, from, to) {
|
|
8571
|
+
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
8572
|
+
}
|
|
8573
|
+
function fileOverlaps(file, from, to) {
|
|
8574
|
+
const start = auditFileDateMs(file);
|
|
8575
|
+
if (start === null) return false;
|
|
8576
|
+
const date = new Date(start);
|
|
8577
|
+
const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
|
|
8578
|
+
return end > from && start <= to;
|
|
8579
|
+
}
|
|
8580
|
+
function parseMetadataPrefix(prefix, prefixTruncated) {
|
|
8581
|
+
const text = prefix.toString("utf8");
|
|
8582
|
+
const tsMatch = /(?:^|,)"ts":(-?\d+)/.exec(text);
|
|
8583
|
+
const statusMatch = /(?:^|,)"status":(-?\d+)/.exec(text);
|
|
8584
|
+
const errorMatch = /(?:^|,)"error":"((?:\\.|[^"\\])*)"/.exec(text);
|
|
8585
|
+
const bodyStarted = /,(?:"requestBody"|"responseBody"):/.test(text);
|
|
8586
|
+
return {
|
|
8587
|
+
ts: tsMatch ? Number(tsMatch[1]) : void 0,
|
|
8588
|
+
status: statusMatch ? Number(statusMatch[1]) : void 0,
|
|
8589
|
+
hasError: Boolean(errorMatch?.[1]),
|
|
8590
|
+
complete: Boolean(tsMatch && statusMatch && (!prefixTruncated || bodyStarted))
|
|
8591
|
+
};
|
|
8592
|
+
}
|
|
8593
|
+
async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
|
|
8594
|
+
let requestCount = 0;
|
|
8595
|
+
let errorCount = 0;
|
|
8596
|
+
let filteredRequestCount = 0;
|
|
8597
|
+
let filteredErrorCount = 0;
|
|
8598
|
+
let minTs = null;
|
|
8599
|
+
let maxTs = null;
|
|
8600
|
+
let complete = true;
|
|
8601
|
+
let prefixParts = [];
|
|
8602
|
+
let prefixBytes = 0;
|
|
8603
|
+
let prefixTruncated = false;
|
|
8604
|
+
const consumeLine = () => {
|
|
8605
|
+
if (prefixBytes === 0 && !prefixTruncated) return;
|
|
8606
|
+
const prefix = Buffer.concat(prefixParts, prefixBytes);
|
|
8607
|
+
const metadata = parseMetadataPrefix(prefix, prefixTruncated);
|
|
8608
|
+
if (!metadata.complete || metadata.ts === void 0 || metadata.status === void 0) {
|
|
8609
|
+
complete = false;
|
|
8610
|
+
} else {
|
|
8611
|
+
requestCount += 1;
|
|
8612
|
+
const isError = metadata.status >= 400 || metadata.hasError;
|
|
8613
|
+
if (isError) errorCount += 1;
|
|
8614
|
+
minTs = minTs === null ? metadata.ts : Math.min(minTs, metadata.ts);
|
|
8615
|
+
maxTs = maxTs === null ? metadata.ts : Math.max(maxTs, metadata.ts);
|
|
8616
|
+
if (metadata.ts >= from && metadata.ts <= to) {
|
|
8617
|
+
filteredRequestCount += 1;
|
|
8618
|
+
if (isError) filteredErrorCount += 1;
|
|
8619
|
+
}
|
|
8620
|
+
}
|
|
8621
|
+
prefixParts = [];
|
|
8622
|
+
prefixBytes = 0;
|
|
8623
|
+
prefixTruncated = false;
|
|
8624
|
+
};
|
|
8625
|
+
if (auditBytes > startByte) {
|
|
8626
|
+
const stream = createReadStream(auditPath, {
|
|
8627
|
+
start: startByte,
|
|
8628
|
+
end: auditBytes - 1,
|
|
8629
|
+
highWaterMark: READ_CHUNK_BYTES
|
|
8630
|
+
});
|
|
8631
|
+
for await (const value of stream) {
|
|
8632
|
+
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
8633
|
+
let offset = 0;
|
|
8634
|
+
while (offset < chunk.length) {
|
|
8635
|
+
const newline = chunk.indexOf(10, offset);
|
|
8636
|
+
const end = newline === -1 ? chunk.length : newline;
|
|
8637
|
+
if (prefixBytes < META_PREFIX_BYTES) {
|
|
8638
|
+
const retained = Math.min(META_PREFIX_BYTES - prefixBytes, end - offset);
|
|
8639
|
+
if (retained > 0) {
|
|
8640
|
+
prefixParts.push(Buffer.from(chunk.subarray(offset, offset + retained)));
|
|
8641
|
+
prefixBytes += retained;
|
|
8642
|
+
}
|
|
8643
|
+
if (retained < end - offset) prefixTruncated = true;
|
|
8644
|
+
} else if (end > offset) {
|
|
8645
|
+
prefixTruncated = true;
|
|
8646
|
+
}
|
|
8647
|
+
if (newline === -1) break;
|
|
8648
|
+
consumeLine();
|
|
8649
|
+
offset = newline + 1;
|
|
8650
|
+
}
|
|
8651
|
+
}
|
|
8652
|
+
}
|
|
8653
|
+
if (prefixBytes > 0 || prefixTruncated) complete = false;
|
|
8654
|
+
return {
|
|
8655
|
+
all: {
|
|
8656
|
+
version: SIDECAR_VERSION,
|
|
8657
|
+
auditBytes,
|
|
8658
|
+
requestCount,
|
|
8659
|
+
errorCount,
|
|
8660
|
+
complete,
|
|
8661
|
+
minTs,
|
|
8662
|
+
maxTs
|
|
8663
|
+
},
|
|
8664
|
+
filtered: { requestCount: filteredRequestCount, errorCount: filteredErrorCount, complete }
|
|
8665
|
+
};
|
|
8666
|
+
}
|
|
8667
|
+
function mergePersistedStats(previous, appended) {
|
|
8668
|
+
return {
|
|
8669
|
+
version: SIDECAR_VERSION,
|
|
8670
|
+
auditBytes: appended.auditBytes,
|
|
8671
|
+
requestCount: previous.requestCount + appended.requestCount,
|
|
8672
|
+
errorCount: previous.errorCount + appended.errorCount,
|
|
8673
|
+
complete: previous.complete && appended.complete,
|
|
8674
|
+
minTs: previous.minTs === null ? appended.minTs : appended.minTs === null ? previous.minTs : Math.min(previous.minTs, appended.minTs),
|
|
8675
|
+
maxTs: previous.maxTs === null ? appended.maxTs : appended.maxTs === null ? previous.maxTs : Math.max(previous.maxTs, appended.maxTs)
|
|
8676
|
+
};
|
|
8677
|
+
}
|
|
8678
|
+
async function readAuditStats(auditDir, query2 = {}) {
|
|
8679
|
+
if (!existsSync14(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
8680
|
+
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
8681
|
+
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
8682
|
+
let files;
|
|
8683
|
+
try {
|
|
8684
|
+
files = readdirSync(auditDir).filter((file) => AUDIT_FILE_RE.test(file) && fileOverlaps(file, from, to)).sort();
|
|
8685
|
+
} catch {
|
|
8686
|
+
return { requestCount: 0, errorCount: 0, complete: false };
|
|
8687
|
+
}
|
|
8688
|
+
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
8689
|
+
for (const file of files) {
|
|
8690
|
+
const auditPath = join6(auditDir, file);
|
|
8691
|
+
try {
|
|
8692
|
+
const auditBytes = statSync3(auditPath).size;
|
|
8693
|
+
const statsPath = join6(auditDir, auditStatsFileName(file));
|
|
8694
|
+
const persisted = readPersisted(statsPath);
|
|
8695
|
+
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
8696
|
+
total.requestCount += persisted.requestCount;
|
|
8697
|
+
total.errorCount += persisted.errorCount;
|
|
8698
|
+
continue;
|
|
8699
|
+
}
|
|
8700
|
+
const resumable = persisted && persisted.complete && persisted.auditBytes < auditBytes && queryCovers(persisted, from, to) ? persisted : null;
|
|
8701
|
+
const scanned = await scanAuditFile(
|
|
8702
|
+
auditPath,
|
|
8703
|
+
resumable?.auditBytes ?? 0,
|
|
8704
|
+
auditBytes,
|
|
8705
|
+
from,
|
|
8706
|
+
to
|
|
8707
|
+
);
|
|
8708
|
+
total.requestCount += scanned.filtered.requestCount + (resumable?.requestCount ?? 0);
|
|
8709
|
+
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
8710
|
+
total.complete = total.complete && scanned.filtered.complete;
|
|
8711
|
+
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
8712
|
+
if (current.complete) writeFileSync11(statsPath, JSON.stringify(current), "utf8");
|
|
8713
|
+
} catch {
|
|
8714
|
+
total.complete = false;
|
|
8715
|
+
}
|
|
8716
|
+
}
|
|
8717
|
+
return total;
|
|
8718
|
+
}
|
|
8719
|
+
|
|
8157
8720
|
// src/audit/AuditPruneSweeper.ts
|
|
8158
8721
|
var DAY_MS = 24 * 60 * 6e4;
|
|
8159
8722
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
8160
8723
|
var AuditPruneSweeper = class {
|
|
8161
|
-
constructor(
|
|
8162
|
-
this.auditDir =
|
|
8724
|
+
constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
|
|
8725
|
+
this.auditDir = auditDir;
|
|
8163
8726
|
this.logger = logger;
|
|
8164
8727
|
this.config = config;
|
|
8165
8728
|
this.intervalMs = intervalMs;
|
|
@@ -8206,17 +8769,19 @@ var AuditPruneSweeper = class {
|
|
|
8206
8769
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
8207
8770
|
this.sweeping = true;
|
|
8208
8771
|
try {
|
|
8209
|
-
if (!
|
|
8772
|
+
if (!existsSync15(this.auditDir)) return 0;
|
|
8210
8773
|
const today = new Date(this.now());
|
|
8211
8774
|
const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
|
|
8212
8775
|
const cutoff = todayMidnight - (this.config.retentionDays - 1) * DAY_MS;
|
|
8213
8776
|
let removed = 0;
|
|
8214
|
-
for (const file of
|
|
8777
|
+
for (const file of readdirSync2(this.auditDir)) {
|
|
8215
8778
|
const dateMs = auditFileDateMs(file);
|
|
8216
8779
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
8217
8780
|
try {
|
|
8218
8781
|
unlinkSync3(join7(this.auditDir, file));
|
|
8219
8782
|
removed += 1;
|
|
8783
|
+
const statsPath = join7(this.auditDir, auditStatsFileName(file));
|
|
8784
|
+
if (existsSync15(statsPath)) unlinkSync3(statsPath);
|
|
8220
8785
|
} catch (error) {
|
|
8221
8786
|
this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
|
|
8222
8787
|
file,
|
|
@@ -8238,15 +8803,15 @@ var AuditPruneSweeper = class {
|
|
|
8238
8803
|
};
|
|
8239
8804
|
|
|
8240
8805
|
// src/audit/auditReader.ts
|
|
8241
|
-
import { existsSync as
|
|
8806
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync15 } from "fs";
|
|
8242
8807
|
import { join as join8 } from "path";
|
|
8243
8808
|
var DEFAULT_LIMIT = 200;
|
|
8244
8809
|
var MAX_LIMIT = 2e3;
|
|
8245
|
-
function readAuditRecords(
|
|
8246
|
-
if (!
|
|
8810
|
+
function readAuditRecords(auditDir, query2 = {}) {
|
|
8811
|
+
if (!existsSync16(auditDir)) return [];
|
|
8247
8812
|
let files;
|
|
8248
8813
|
try {
|
|
8249
|
-
files =
|
|
8814
|
+
files = readdirSync3(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
|
|
8250
8815
|
} catch {
|
|
8251
8816
|
return [];
|
|
8252
8817
|
}
|
|
@@ -8257,7 +8822,7 @@ function readAuditRecords(auditDir2, query2 = {}) {
|
|
|
8257
8822
|
for (const file of files.sort().reverse()) {
|
|
8258
8823
|
let raw;
|
|
8259
8824
|
try {
|
|
8260
|
-
raw =
|
|
8825
|
+
raw = readFileSync15(join8(auditDir, file), "utf8");
|
|
8261
8826
|
} catch {
|
|
8262
8827
|
continue;
|
|
8263
8828
|
}
|
|
@@ -8286,11 +8851,11 @@ function isAuditRecord(value) {
|
|
|
8286
8851
|
}
|
|
8287
8852
|
|
|
8288
8853
|
// src/audit/AuditWriter.ts
|
|
8289
|
-
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5 } from "fs";
|
|
8854
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync5, statSync as statSync4 } from "fs";
|
|
8290
8855
|
import { join as join9 } from "path";
|
|
8291
8856
|
var AuditWriter = class {
|
|
8292
|
-
constructor(
|
|
8293
|
-
this.auditDir =
|
|
8857
|
+
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
8858
|
+
this.auditDir = auditDir;
|
|
8294
8859
|
this.logger = logger;
|
|
8295
8860
|
this.defer = defer;
|
|
8296
8861
|
}
|
|
@@ -8324,7 +8889,21 @@ var AuditWriter = class {
|
|
|
8324
8889
|
this.dirEnsured = true;
|
|
8325
8890
|
}
|
|
8326
8891
|
const file = join9(this.auditDir, auditFileName(record.ts));
|
|
8327
|
-
|
|
8892
|
+
const line = JSON.stringify(record) + "\n";
|
|
8893
|
+
const auditBytesBefore = existsSync17(file) ? statSync4(file).size : 0;
|
|
8894
|
+
appendFileSync2(file, line, "utf8");
|
|
8895
|
+
try {
|
|
8896
|
+
updateAuditStatsAfterAppend(
|
|
8897
|
+
file,
|
|
8898
|
+
auditBytesBefore,
|
|
8899
|
+
auditBytesBefore + Buffer.byteLength(line, "utf8"),
|
|
8900
|
+
record
|
|
8901
|
+
);
|
|
8902
|
+
} catch (error) {
|
|
8903
|
+
this.logger.warn("[AuditWriter] failed to update audit stats", {
|
|
8904
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8905
|
+
});
|
|
8906
|
+
}
|
|
8328
8907
|
}
|
|
8329
8908
|
};
|
|
8330
8909
|
|
|
@@ -8468,14 +9047,14 @@ var BillingPublisher = class {
|
|
|
8468
9047
|
};
|
|
8469
9048
|
|
|
8470
9049
|
// src/billing/billingReader.ts
|
|
8471
|
-
import { existsSync as
|
|
9050
|
+
import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync16 } from "fs";
|
|
8472
9051
|
import { join as join11 } from "path";
|
|
8473
9052
|
function readBillingLedger(billingDir) {
|
|
8474
9053
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
8475
|
-
if (!
|
|
9054
|
+
if (!existsSync18(billingDir)) return view;
|
|
8476
9055
|
let files;
|
|
8477
9056
|
try {
|
|
8478
|
-
files =
|
|
9057
|
+
files = readdirSync4(billingDir);
|
|
8479
9058
|
} catch {
|
|
8480
9059
|
return view;
|
|
8481
9060
|
}
|
|
@@ -8506,7 +9085,7 @@ function readBillingStatus(billingDir) {
|
|
|
8506
9085
|
function parseLines(dir, file) {
|
|
8507
9086
|
let raw;
|
|
8508
9087
|
try {
|
|
8509
|
-
raw =
|
|
9088
|
+
raw = readFileSync16(join11(dir, file), "utf8");
|
|
8510
9089
|
} catch {
|
|
8511
9090
|
return [];
|
|
8512
9091
|
}
|
|
@@ -8878,7 +9457,7 @@ function buildDaemon(config, paths) {
|
|
|
8878
9457
|
normalizeServerConfig(decryptedConfig.server).allowanceScheduling
|
|
8879
9458
|
);
|
|
8880
9459
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
8881
|
-
const keyDb = new JsonOutboundKeyDb(paths.keysPath);
|
|
9460
|
+
const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
|
|
8882
9461
|
const voucherDb = new JsonVoucherDb(defaultVouchersPath(paths.configPath));
|
|
8883
9462
|
const settingsStore = new JsonApiServerSettingsStore(paths.configPath, secretBox3);
|
|
8884
9463
|
const integrationStateStore = new IntegrationStateStore(
|
|
@@ -8979,7 +9558,7 @@ function buildDaemon(config, paths) {
|
|
|
8979
9558
|
// lines through the injected logger (honors level/format/file sink).
|
|
8980
9559
|
logger
|
|
8981
9560
|
});
|
|
8982
|
-
const
|
|
9561
|
+
const auditDir = defaultAuditDir(paths.configPath);
|
|
8983
9562
|
const billingDir = defaultBillingDir(paths.configPath);
|
|
8984
9563
|
const adminServer = new AdminServer({
|
|
8985
9564
|
configPath: paths.configPath,
|
|
@@ -9013,10 +9592,16 @@ function buildDaemon(config, paths) {
|
|
|
9013
9592
|
// (NOT widening the least-authority writer — no token-returning read reachable).
|
|
9014
9593
|
oauthSessions: new OAuthSessionStore(),
|
|
9015
9594
|
// Real global fetch by default; a test seam (`paths.oauthExchangeFetch`) can
|
|
9016
|
-
// inject a mock so no real token endpoint is hit
|
|
9017
|
-
//
|
|
9018
|
-
//
|
|
9019
|
-
|
|
9595
|
+
// inject a mock so no real token endpoint is hit (one FetchLike for every
|
|
9596
|
+
// provider — the ctx below only matters on the real egress path).
|
|
9597
|
+
//
|
|
9598
|
+
// upstream-proxy: a PER-PROVIDER factory, so the exchange carries the same
|
|
9599
|
+
// `{ providerId }` ctx the CLI login and the token refresh already pass.
|
|
9600
|
+
// Without it the interactive login resolved only the global/env proxy layers
|
|
9601
|
+
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
9602
|
+
// excluded from the upstream trace, so a failing login left no evidence.
|
|
9603
|
+
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
9604
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream7(url, init, { providerId, redactBodies: true }),
|
|
9020
9605
|
subscriptionAccountAppender: credentialStore,
|
|
9021
9606
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
9022
9607
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -9068,7 +9653,8 @@ function buildDaemon(config, paths) {
|
|
|
9068
9653
|
// date-rotated audit store. Bound to the store dir here so the AdminServer
|
|
9069
9654
|
// carries no path/store coupling. Records hold IP/UA/bodies → admin-only,
|
|
9070
9655
|
// NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
|
|
9071
|
-
auditReader: (query2) => readAuditRecords(
|
|
9656
|
+
auditReader: (query2) => readAuditRecords(auditDir, query2),
|
|
9657
|
+
auditStatsReader: (query2) => readAuditStats(auditDir, query2),
|
|
9072
9658
|
// billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
|
|
9073
9659
|
// secret-free total/delivered/pending counts of the durable ledger.
|
|
9074
9660
|
billingStatusReader: () => readBillingStatus(billingDir)
|
|
@@ -9078,9 +9664,9 @@ function buildDaemon(config, paths) {
|
|
|
9078
9664
|
fetchImpl: (url, init) => fetchUpstream7(url, init)
|
|
9079
9665
|
});
|
|
9080
9666
|
setWebhookRuntime(webhookDispatcher, getSharedAccountHealth3());
|
|
9081
|
-
const auditWriter = new AuditWriter(
|
|
9082
|
-
const auditPruneSweeper = new AuditPruneSweeper(
|
|
9083
|
-
setAuditRuntime(auditWriter, auditPruneSweeper
|
|
9667
|
+
const auditWriter = new AuditWriter(auditDir, logger);
|
|
9668
|
+
const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
|
|
9669
|
+
setAuditRuntime(auditWriter, auditPruneSweeper);
|
|
9084
9670
|
const billingPublisher = new BillingPublisher(billingDir, logger);
|
|
9085
9671
|
const billingRetrySweeper = new BillingRetrySweeper(
|
|
9086
9672
|
billingDir,
|
|
@@ -9144,7 +9730,7 @@ function resetDaemonSingletonsForTests() {
|
|
|
9144
9730
|
}
|
|
9145
9731
|
function isTokensStoreReadable(tokensPath) {
|
|
9146
9732
|
try {
|
|
9147
|
-
if (!
|
|
9733
|
+
if (!existsSync19(tokensPath)) return true;
|
|
9148
9734
|
accessSync(tokensPath, fsConstants.R_OK);
|
|
9149
9735
|
return true;
|
|
9150
9736
|
} catch {
|