@omnicross/daemon 0.1.6 → 0.1.8
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 +1186 -200
- package/dist/cli.js +1200 -191
- package/dist/index.cjs +1118 -147
- package/dist/index.d.cts +101 -26
- package/dist/index.d.ts +101 -26
- package/dist/index.js +1135 -139
- package/package.json +63 -63
package/dist/cli.cjs
CHANGED
|
@@ -1508,10 +1508,20 @@ function assertLoopbackGatewayUrl(value) {
|
|
|
1508
1508
|
// src/ports/JsonOutboundKeyDb.ts
|
|
1509
1509
|
var import_node_fs6 = require("fs");
|
|
1510
1510
|
var JsonOutboundKeyDb = class {
|
|
1511
|
-
|
|
1511
|
+
/**
|
|
1512
|
+
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
1513
|
+
* key's plaintext is persisted as a `keySecret` `enc:` envelope (enabling the
|
|
1514
|
+
* operator "view key" affordance via `outboundApiKeysReveal`). When absent the
|
|
1515
|
+
* store stays hash-only (byte-identical to the legacy behavior) and reveal
|
|
1516
|
+
* always returns `null`. Existing 1-arg call sites (tests, lightweight
|
|
1517
|
+
* embedders) keep working.
|
|
1518
|
+
*/
|
|
1519
|
+
constructor(keysPath, secretBox3) {
|
|
1512
1520
|
this.keysPath = keysPath;
|
|
1521
|
+
this.secretBox = secretBox3;
|
|
1513
1522
|
}
|
|
1514
1523
|
keysPath;
|
|
1524
|
+
secretBox;
|
|
1515
1525
|
async outboundApiKeysList() {
|
|
1516
1526
|
return this.readRows();
|
|
1517
1527
|
}
|
|
@@ -1537,10 +1547,27 @@ var JsonOutboundKeyDb = class {
|
|
|
1537
1547
|
allowedEndpoints: input.allowedEndpoints,
|
|
1538
1548
|
loopbackOnly: input.loopbackOnly
|
|
1539
1549
|
};
|
|
1550
|
+
if (input.plaintext && this.secretBox) {
|
|
1551
|
+
row.keySecret = this.secretBox.encrypt(input.plaintext);
|
|
1552
|
+
}
|
|
1540
1553
|
rows.push(row);
|
|
1541
1554
|
this.writeRows(rows);
|
|
1542
1555
|
return row;
|
|
1543
1556
|
}
|
|
1557
|
+
async outboundApiKeysReveal(id) {
|
|
1558
|
+
const rows = this.readRows();
|
|
1559
|
+
const row = rows.find((r) => r.id === id);
|
|
1560
|
+
if (!row || !row.keySecret || !this.secretBox) return null;
|
|
1561
|
+
return this.secretBox.decrypt(row.keySecret);
|
|
1562
|
+
}
|
|
1563
|
+
async outboundApiKeysDelete(id) {
|
|
1564
|
+
const rows = this.readRows();
|
|
1565
|
+
const idx = rows.findIndex((r) => r.id === id);
|
|
1566
|
+
if (idx < 0) return false;
|
|
1567
|
+
rows.splice(idx, 1);
|
|
1568
|
+
this.writeRows(rows);
|
|
1569
|
+
return true;
|
|
1570
|
+
}
|
|
1544
1571
|
async outboundApiKeysRevoke(id) {
|
|
1545
1572
|
return this.mutateRow(id, (row) => {
|
|
1546
1573
|
if (row.revokedAt !== null) return false;
|
|
@@ -1738,26 +1765,29 @@ async function keysRevoke(db, id) {
|
|
|
1738
1765
|
|
|
1739
1766
|
// src/commands/launch.ts
|
|
1740
1767
|
var import_node_child_process2 = require("child_process");
|
|
1741
|
-
var
|
|
1768
|
+
var import_node_crypto15 = require("crypto");
|
|
1769
|
+
var import_node_fs25 = require("fs");
|
|
1742
1770
|
var import_node_path17 = require("path");
|
|
1743
1771
|
var import_node_util4 = require("util");
|
|
1744
|
-
var
|
|
1772
|
+
var import_cli_launcher3 = require("@omnicross/cli-launcher");
|
|
1773
|
+
var import_provider_proxy5 = require("@omnicross/core/provider-proxy");
|
|
1745
1774
|
|
|
1746
1775
|
// src/bootstrap.ts
|
|
1747
|
-
var
|
|
1776
|
+
var import_node_fs24 = require("fs");
|
|
1748
1777
|
var import_audit_types = require("@omnicross/contracts/audit-types");
|
|
1749
1778
|
var import_billing_types = require("@omnicross/contracts/billing-types");
|
|
1750
1779
|
var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
|
|
1751
1780
|
var import_ApiKeyPoolService = require("@omnicross/core/completion/ApiKeyPoolService");
|
|
1752
1781
|
var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
1753
1782
|
var import_subscriptionRegistryPort = require("@omnicross/core/outbound-api/subscriptionRegistryPort");
|
|
1754
|
-
var
|
|
1783
|
+
var import_SubscriptionAccountHealth4 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
1755
1784
|
var import_AccountAllowanceStore4 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1756
|
-
var
|
|
1785
|
+
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1757
1786
|
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1758
1787
|
var import_SubscriptionIdentityStore3 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
1759
1788
|
var import_gemini_code_assist_resolver = require("@omnicross/core/ports/gemini-code-assist-resolver");
|
|
1760
|
-
var
|
|
1789
|
+
var import_provider_proxy4 = require("@omnicross/core/provider-proxy");
|
|
1790
|
+
var import_cli_launcher2 = require("@omnicross/cli-launcher");
|
|
1761
1791
|
var import_outbound_api6 = require("@omnicross/core/outbound-api");
|
|
1762
1792
|
var import_usage = require("@omnicross/core/usage");
|
|
1763
1793
|
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
@@ -1838,7 +1868,7 @@ async function runCodexLoopback(sessionId, codeVerifier, state, signal, deps) {
|
|
|
1838
1868
|
const code = await deps.codexAwaitLoopback(state, void 0, signal);
|
|
1839
1869
|
const result = await import_subscriptions.codexOAuth.exchangeCodeForTokens(
|
|
1840
1870
|
{ authorizationCode: code, codeVerifier, state },
|
|
1841
|
-
deps.oauthExchangeFetch
|
|
1871
|
+
deps.oauthExchangeFetch("codex")
|
|
1842
1872
|
);
|
|
1843
1873
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
1844
1874
|
const block = {
|
|
@@ -2340,6 +2370,17 @@ function handleAuditQuery(req, res, reader) {
|
|
|
2340
2370
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2341
2371
|
res.end(JSON.stringify({ records }));
|
|
2342
2372
|
}
|
|
2373
|
+
async function handleAuditStatsQuery(req, res, reader) {
|
|
2374
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
2375
|
+
const query2 = {};
|
|
2376
|
+
const from = intParam(url.searchParams.get("from"));
|
|
2377
|
+
if (from !== void 0) query2.from = from;
|
|
2378
|
+
const to = intParam(url.searchParams.get("to"));
|
|
2379
|
+
if (to !== void 0) query2.to = to;
|
|
2380
|
+
const stats = reader ? await reader(query2) : { requestCount: 0, errorCount: 0, complete: true };
|
|
2381
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2382
|
+
res.end(JSON.stringify(stats));
|
|
2383
|
+
}
|
|
2343
2384
|
|
|
2344
2385
|
// src/admin/billingStatusApi.ts
|
|
2345
2386
|
function handleBillingStatus(res, reader) {
|
|
@@ -2429,6 +2470,88 @@ async function handleWebhookTest(req, res) {
|
|
|
2429
2470
|
res.end(JSON.stringify({ result }));
|
|
2430
2471
|
}
|
|
2431
2472
|
|
|
2473
|
+
// src/admin/routeLeaseApi.ts
|
|
2474
|
+
var import_provider_proxy = require("@omnicross/core/provider-proxy");
|
|
2475
|
+
var MAX_BODY_BYTES = 64 * 1024;
|
|
2476
|
+
var SAFE_LEASE_ID = /^[A-Za-z0-9-]{1,128}$/u;
|
|
2477
|
+
async function readJson(req) {
|
|
2478
|
+
const chunks = [];
|
|
2479
|
+
let bytes = 0;
|
|
2480
|
+
for await (const chunk of req) {
|
|
2481
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2482
|
+
bytes += buffer.length;
|
|
2483
|
+
if (bytes > MAX_BODY_BYTES) throw new import_provider_proxy.RouteLeaseError("invalid_request", "request body is too large");
|
|
2484
|
+
chunks.push(buffer);
|
|
2485
|
+
}
|
|
2486
|
+
if (chunks.length === 0) return {};
|
|
2487
|
+
try {
|
|
2488
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
2489
|
+
} catch {
|
|
2490
|
+
throw new import_provider_proxy.RouteLeaseError("invalid_request", "request body is not valid JSON");
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
function json(res, status, body, noStore = false) {
|
|
2494
|
+
res.statusCode = status;
|
|
2495
|
+
res.setHeader("Content-Type", "application/json");
|
|
2496
|
+
if (noStore) res.setHeader("Cache-Control", "no-store");
|
|
2497
|
+
res.end(JSON.stringify(body));
|
|
2498
|
+
}
|
|
2499
|
+
function leaseId(value) {
|
|
2500
|
+
if (!value || !SAFE_LEASE_ID.test(value)) throw new import_provider_proxy.RouteLeaseError("invalid_request", "lease id is invalid");
|
|
2501
|
+
return value;
|
|
2502
|
+
}
|
|
2503
|
+
function header(req, name) {
|
|
2504
|
+
const value = req.headers[name.toLowerCase()];
|
|
2505
|
+
return Array.isArray(value) ? value[0] : value;
|
|
2506
|
+
}
|
|
2507
|
+
function writeError(res, error, noStore) {
|
|
2508
|
+
const safe = error instanceof import_provider_proxy.RouteLeaseError ? error : new import_provider_proxy.RouteLeaseError("upstream_unavailable", "route lease operation failed safely");
|
|
2509
|
+
if (safe.retryAfterSeconds !== void 0) res.setHeader("Retry-After", String(safe.retryAfterSeconds));
|
|
2510
|
+
json(res, safe.status, safe.toResponse(), noStore);
|
|
2511
|
+
}
|
|
2512
|
+
async function handleRouteLeaseApi(req, res, path2, deps) {
|
|
2513
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
2514
|
+
const noStore = method === "POST" && (path2 === "/admin/api/route-leases" || path2.endsWith("/renew"));
|
|
2515
|
+
try {
|
|
2516
|
+
if (!(0, import_provider_proxy.isLoopbackAddress)(req.socket.remoteAddress)) {
|
|
2517
|
+
throw new import_provider_proxy.RouteLeaseError("control_unauthorized", "route lease control plane is loopback only");
|
|
2518
|
+
}
|
|
2519
|
+
const manager = deps.routeLeaseManager;
|
|
2520
|
+
if (!manager) throw new import_provider_proxy.RouteLeaseError("daemon_not_ready", "route lease manager is unavailable");
|
|
2521
|
+
const base = "/admin/api/route-leases";
|
|
2522
|
+
const suffix = path2.slice(base.length).replace(/^\/+|\/+$/gu, "");
|
|
2523
|
+
const segments = suffix ? suffix.split("/") : [];
|
|
2524
|
+
if (segments.length === 1 && segments[0] === "capabilities") {
|
|
2525
|
+
if (method !== "GET" && method !== "HEAD") throw new import_provider_proxy.RouteLeaseError("invalid_request", "method is not allowed");
|
|
2526
|
+
return json(res, 200, import_provider_proxy.ROUTE_LEASE_CAPABILITIES);
|
|
2527
|
+
}
|
|
2528
|
+
if (segments.length === 0) {
|
|
2529
|
+
if (method === "GET") return json(res, 200, { leases: manager.list() });
|
|
2530
|
+
if (method === "POST") {
|
|
2531
|
+
const outcome = await manager.createFromRequest(await readJson(req), header(req, "idempotency-key"));
|
|
2532
|
+
return json(res, outcome.created ? 201 : 200, outcome.result, true);
|
|
2533
|
+
}
|
|
2534
|
+
throw new import_provider_proxy.RouteLeaseError("invalid_request", "method is not allowed");
|
|
2535
|
+
}
|
|
2536
|
+
const id = leaseId(segments[0]);
|
|
2537
|
+
if (segments.length === 1) {
|
|
2538
|
+
if (method === "GET") return json(res, 200, manager.get(id));
|
|
2539
|
+
if (method === "DELETE") return json(res, 200, manager.release(id));
|
|
2540
|
+
throw new import_provider_proxy.RouteLeaseError("invalid_request", "method is not allowed");
|
|
2541
|
+
}
|
|
2542
|
+
if (segments.length === 2 && segments[1] === "renew" && method === "POST") {
|
|
2543
|
+
const body = await readJson(req);
|
|
2544
|
+
const ttl = (0, import_provider_proxy.normalizeRouteLeaseTtl)(
|
|
2545
|
+
body && typeof body === "object" && !Array.isArray(body) ? body.ttlSeconds : void 0
|
|
2546
|
+
);
|
|
2547
|
+
return json(res, 200, manager.renew(id, ttl), true);
|
|
2548
|
+
}
|
|
2549
|
+
throw new import_provider_proxy.RouteLeaseError("lease_not_found", "route lease endpoint was not found");
|
|
2550
|
+
} catch (error) {
|
|
2551
|
+
writeError(res, error, noStore);
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2432
2555
|
// src/admin/adminApi.ts
|
|
2433
2556
|
var import_node_http = __toESM(require("http"), 1);
|
|
2434
2557
|
var import_outbound_api3 = require("@omnicross/core/outbound-api");
|
|
@@ -2522,7 +2645,13 @@ function listMappablePresets() {
|
|
|
2522
2645
|
name: preset.name,
|
|
2523
2646
|
apiFormat: resolved.format,
|
|
2524
2647
|
baseUrl: preset.api_base_url,
|
|
2525
|
-
models: Array.isArray(preset.models) ? preset.models : []
|
|
2648
|
+
models: Array.isArray(preset.models) ? preset.models : [],
|
|
2649
|
+
nameKey: preset.nameKey,
|
|
2650
|
+
icon: preset.icon,
|
|
2651
|
+
description: preset.description,
|
|
2652
|
+
features: preset.features,
|
|
2653
|
+
website: preset.website,
|
|
2654
|
+
modelsEndpoint: preset.modelsEndpoint
|
|
2526
2655
|
});
|
|
2527
2656
|
}
|
|
2528
2657
|
return { mappable, excluded };
|
|
@@ -2911,7 +3040,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
2911
3040
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
2912
3041
|
if (!sessionId) return err2(400, "oauth complete requires { sessionId }");
|
|
2913
3042
|
if (!rawCode) return err2(400, "oauth complete requires { code }");
|
|
2914
|
-
const session = deps.oauthSessions.
|
|
3043
|
+
const session = deps.oauthSessions.peek(sessionId);
|
|
2915
3044
|
if (!session) return err2(410, "oauth session is unknown, expired, or already used");
|
|
2916
3045
|
if (session.providerId !== providerId) {
|
|
2917
3046
|
return err2(400, `oauth session does not match provider '${providerId}'`);
|
|
@@ -2925,13 +3054,15 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
2925
3054
|
}
|
|
2926
3055
|
code = splitCode;
|
|
2927
3056
|
}
|
|
3057
|
+
const exchangeFetch = deps.oauthExchangeFetch(providerId);
|
|
2928
3058
|
let block;
|
|
2929
3059
|
try {
|
|
2930
|
-
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state,
|
|
3060
|
+
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
2931
3061
|
} catch (exchangeError) {
|
|
2932
3062
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
2933
3063
|
return err2(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
2934
3064
|
}
|
|
3065
|
+
deps.oauthSessions.consume(sessionId);
|
|
2935
3066
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
2936
3067
|
await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
|
|
2937
3068
|
const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
@@ -2970,8 +3101,34 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
|
2970
3101
|
var import_node_child_process = require("child_process");
|
|
2971
3102
|
var import_node_crypto6 = require("crypto");
|
|
2972
3103
|
var import_node_fs8 = require("fs");
|
|
3104
|
+
var import_node_net = require("net");
|
|
3105
|
+
var import_node_os3 = require("os");
|
|
2973
3106
|
var import_node_path7 = require("path");
|
|
2974
3107
|
var import_cli_launcher = require("@omnicross/cli-launcher");
|
|
3108
|
+
var import_provider_proxy2 = require("@omnicross/core/provider-proxy");
|
|
3109
|
+
|
|
3110
|
+
// src/routeLeaseRenewal.ts
|
|
3111
|
+
var TERMINAL_LEASE_TTL_SECONDS = 600;
|
|
3112
|
+
var TERMINAL_LEASE_RENEW_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3113
|
+
var TERMINAL_LEASE_MAX_LIFETIME_MS = 24 * 60 * 60 * 1e3;
|
|
3114
|
+
function startTerminalLeaseRenewal(manager, leaseId2) {
|
|
3115
|
+
const stopAt = Date.now() + TERMINAL_LEASE_MAX_LIFETIME_MS;
|
|
3116
|
+
const timer = setInterval(() => {
|
|
3117
|
+
if (Date.now() >= stopAt) {
|
|
3118
|
+
clearInterval(timer);
|
|
3119
|
+
return;
|
|
3120
|
+
}
|
|
3121
|
+
try {
|
|
3122
|
+
manager.renew(leaseId2, TERMINAL_LEASE_TTL_SECONDS);
|
|
3123
|
+
} catch {
|
|
3124
|
+
clearInterval(timer);
|
|
3125
|
+
}
|
|
3126
|
+
}, TERMINAL_LEASE_RENEW_INTERVAL_MS);
|
|
3127
|
+
timer.unref?.();
|
|
3128
|
+
return () => clearInterval(timer);
|
|
3129
|
+
}
|
|
3130
|
+
|
|
3131
|
+
// src/admin/cliLaunch.ts
|
|
2975
3132
|
var LAUNCHABLE_CLIS = [
|
|
2976
3133
|
{ id: "claude", displayName: "Claude Code", command: "claude" },
|
|
2977
3134
|
{ id: "codex", displayName: "Codex CLI", command: "codex" },
|
|
@@ -3052,34 +3209,183 @@ async function buildLaunchEnv(cli, llmConfig, target) {
|
|
|
3052
3209
|
function shq(s) {
|
|
3053
3210
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
3054
3211
|
}
|
|
3055
|
-
var
|
|
3212
|
+
var MAC_TERMINAL_BOOTSTRAP_SOURCE = `
|
|
3213
|
+
'use strict';
|
|
3214
|
+
const fs = require('node:fs');
|
|
3215
|
+
const net = require('node:net');
|
|
3216
|
+
const { spawn } = require('node:child_process');
|
|
3217
|
+
const [socketPath, launchDir, cwd, command, ...args] = process.argv.slice(2);
|
|
3218
|
+
let payload = '';
|
|
3219
|
+
const socket = net.createConnection(socketPath);
|
|
3220
|
+
socket.setEncoding('utf8');
|
|
3221
|
+
socket.on('data', (chunk) => { payload += chunk; });
|
|
3222
|
+
socket.on('end', () => {
|
|
3223
|
+
const descriptor = JSON.parse(payload);
|
|
3224
|
+
if (!descriptor || Array.isArray(descriptor) || Object.values(descriptor).some((value) => typeof value !== 'string')) {
|
|
3225
|
+
throw new Error('invalid terminal launch descriptor');
|
|
3226
|
+
}
|
|
3227
|
+
try { fs.rmSync(launchDir, { recursive: true, force: true }); } catch {}
|
|
3228
|
+
const child = spawn(command, args, {
|
|
3229
|
+
cwd: cwd || undefined,
|
|
3230
|
+
env: { ...process.env, ...descriptor },
|
|
3231
|
+
stdio: 'inherit',
|
|
3232
|
+
});
|
|
3233
|
+
child.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
|
|
3234
|
+
child.on('exit', (code, signal) => {
|
|
3235
|
+
if (signal) process.kill(process.pid, signal);
|
|
3236
|
+
else process.exitCode = code == null ? 1 : code;
|
|
3237
|
+
});
|
|
3238
|
+
});
|
|
3239
|
+
socket.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
|
|
3240
|
+
`;
|
|
3241
|
+
var MAC_TERMINAL_IPC_TIMEOUT_MS = 12e4;
|
|
3242
|
+
function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }, spawnProcess = import_node_child_process.spawn, macIpc = {}) {
|
|
3056
3243
|
const childEnv = { ...process.env, ...env };
|
|
3057
3244
|
if (platform === "win32") {
|
|
3058
3245
|
const args = ["/c", "start", `"omnicross ${cli}"`];
|
|
3059
3246
|
if (cwd) args.push("/D", `"${cwd}"`);
|
|
3060
3247
|
args.push("cmd", "/k", command, ...extraArgs);
|
|
3061
|
-
(
|
|
3248
|
+
spawnProcess(process.env["ComSpec"] || "cmd.exe", args, {
|
|
3062
3249
|
env: childEnv,
|
|
3063
3250
|
windowsVerbatimArguments: true,
|
|
3064
3251
|
detached: true,
|
|
3065
3252
|
stdio: "ignore"
|
|
3066
3253
|
}).unref();
|
|
3067
|
-
return
|
|
3254
|
+
return () => {
|
|
3255
|
+
};
|
|
3068
3256
|
}
|
|
3069
|
-
const exportLine = Object.entries(env).map(([k, v]) => `export ${k}=${shq(v)}`).join("; ");
|
|
3070
3257
|
const runLine = [command, ...extraArgs].map(shq).join(" ");
|
|
3071
|
-
const script = `${
|
|
3258
|
+
const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
|
|
3072
3259
|
if (platform === "darwin") {
|
|
3073
|
-
const
|
|
3074
|
-
(0,
|
|
3075
|
-
|
|
3260
|
+
const launchDir = (0, import_node_fs8.mkdtempSync)((0, import_node_path7.join)((0, import_node_os3.tmpdir)(), "omnicross-terminal-"));
|
|
3261
|
+
const commandFile = (0, import_node_path7.join)(launchDir, "launch.command");
|
|
3262
|
+
const bootstrapFile = (0, import_node_path7.join)(launchDir, "bootstrap.cjs");
|
|
3263
|
+
const socketPath = macIpc.socketPath ?? (0, import_node_path7.join)(launchDir, "descriptor.sock");
|
|
3264
|
+
const openerEnv = { ...process.env };
|
|
3265
|
+
for (const key of Object.keys(env)) delete openerEnv[key];
|
|
3266
|
+
let claimed = false;
|
|
3267
|
+
let cleaned = false;
|
|
3268
|
+
let failureNotified = false;
|
|
3269
|
+
let timer;
|
|
3270
|
+
const notifyFailure = () => {
|
|
3271
|
+
cleanup();
|
|
3272
|
+
if (failureNotified) return;
|
|
3273
|
+
failureNotified = true;
|
|
3274
|
+
try {
|
|
3275
|
+
onFailure?.();
|
|
3276
|
+
} catch {
|
|
3277
|
+
}
|
|
3278
|
+
};
|
|
3279
|
+
const handleLaunchFailure = () => {
|
|
3280
|
+
if (claimed) cleanup();
|
|
3281
|
+
else notifyFailure();
|
|
3282
|
+
};
|
|
3283
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
3284
|
+
const server = (0, import_node_net.createServer)((socket) => {
|
|
3285
|
+
socket.unref();
|
|
3286
|
+
sockets.add(socket);
|
|
3287
|
+
socket.once("close", () => sockets.delete(socket));
|
|
3288
|
+
try {
|
|
3289
|
+
macIpc.onAccepted?.(socket);
|
|
3290
|
+
} catch {
|
|
3291
|
+
cleanup();
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3294
|
+
if (claimed || cleaned) {
|
|
3295
|
+
socket.destroy();
|
|
3296
|
+
return;
|
|
3297
|
+
}
|
|
3298
|
+
claimed = true;
|
|
3299
|
+
try {
|
|
3300
|
+
macIpc.onClaimed?.();
|
|
3301
|
+
if (cleaned) return;
|
|
3302
|
+
socket.end(JSON.stringify(env), cleanup);
|
|
3303
|
+
} catch {
|
|
3304
|
+
cleanup();
|
|
3305
|
+
}
|
|
3306
|
+
});
|
|
3307
|
+
const cleanup = () => {
|
|
3308
|
+
if (!cleaned) {
|
|
3309
|
+
cleaned = true;
|
|
3310
|
+
if (timer) clearTimeout(timer);
|
|
3311
|
+
for (const socket of sockets) socket.destroy();
|
|
3312
|
+
sockets.clear();
|
|
3313
|
+
try {
|
|
3314
|
+
server.close();
|
|
3315
|
+
} catch {
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
try {
|
|
3319
|
+
if (macIpc.removeArtifacts) {
|
|
3320
|
+
macIpc.removeArtifacts(launchDir);
|
|
3321
|
+
} else {
|
|
3322
|
+
(0, import_node_fs8.rmSync)(launchDir, {
|
|
3323
|
+
recursive: true,
|
|
3324
|
+
force: true,
|
|
3325
|
+
maxRetries: 3,
|
|
3326
|
+
retryDelay: 20
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
} catch {
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
try {
|
|
3333
|
+
(0, import_node_fs8.writeFileSync)(bootstrapFile, MAC_TERMINAL_BOOTSTRAP_SOURCE, { encoding: "utf8", mode: 448 });
|
|
3334
|
+
(0, import_node_fs8.writeFileSync)(commandFile, `#!/bin/bash
|
|
3335
|
+
rm -f -- "$0"
|
|
3336
|
+
exec ${shq(process.execPath)} ${shq(bootstrapFile)} ${shq(socketPath)} ${shq(launchDir)} ${shq(cwd ?? "")} ${runLine}
|
|
3337
|
+
`, {
|
|
3338
|
+
encoding: "utf8",
|
|
3339
|
+
mode: 448
|
|
3340
|
+
});
|
|
3341
|
+
(0, import_node_fs8.chmodSync)(commandFile, 448);
|
|
3342
|
+
(0, import_node_fs8.chmodSync)(bootstrapFile, 448);
|
|
3343
|
+
server.once("error", handleLaunchFailure);
|
|
3344
|
+
server.listen(socketPath, () => {
|
|
3345
|
+
if (cleaned) return;
|
|
3346
|
+
try {
|
|
3347
|
+
macIpc.onListening?.();
|
|
3348
|
+
if (cleaned) return;
|
|
3349
|
+
if (process.platform !== "win32") (0, import_node_fs8.chmodSync)(socketPath, 384);
|
|
3350
|
+
const opener = spawnProcess("open", ["-n", "-a", "Terminal", commandFile], {
|
|
3351
|
+
env: openerEnv,
|
|
3352
|
+
detached: true,
|
|
3353
|
+
stdio: "ignore"
|
|
3354
|
+
});
|
|
3355
|
+
opener.once("error", handleLaunchFailure);
|
|
3356
|
+
opener.unref();
|
|
3357
|
+
server.unref();
|
|
3358
|
+
} catch {
|
|
3359
|
+
handleLaunchFailure();
|
|
3360
|
+
}
|
|
3361
|
+
});
|
|
3362
|
+
timer = setTimeout(handleLaunchFailure, macIpc.timeoutMs ?? MAC_TERMINAL_IPC_TIMEOUT_MS);
|
|
3363
|
+
timer.unref?.();
|
|
3364
|
+
return cleanup;
|
|
3365
|
+
} catch (error) {
|
|
3366
|
+
cleanup();
|
|
3367
|
+
throw error;
|
|
3368
|
+
}
|
|
3076
3369
|
}
|
|
3077
|
-
(
|
|
3370
|
+
spawnProcess("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
|
|
3371
|
+
env: childEnv,
|
|
3078
3372
|
detached: true,
|
|
3079
3373
|
stdio: "ignore"
|
|
3080
3374
|
}).unref();
|
|
3081
|
-
|
|
3375
|
+
return () => {
|
|
3376
|
+
};
|
|
3377
|
+
}
|
|
3378
|
+
var defaultTerminalOpener = (input) => openTerminal(input);
|
|
3082
3379
|
var sessions = /* @__PURE__ */ new Map();
|
|
3380
|
+
function resetCliSessions() {
|
|
3381
|
+
for (const s of sessions.values()) {
|
|
3382
|
+
try {
|
|
3383
|
+
s.onSessionEnd();
|
|
3384
|
+
} catch {
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
sessions.clear();
|
|
3388
|
+
}
|
|
3083
3389
|
function errBody(message) {
|
|
3084
3390
|
return { error: { type: "admin_api_error", message } };
|
|
3085
3391
|
}
|
|
@@ -3134,29 +3440,81 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
3134
3440
|
} catch (err5) {
|
|
3135
3441
|
return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "no launch target") };
|
|
3136
3442
|
}
|
|
3443
|
+
const id = (0, import_node_crypto6.randomUUID)();
|
|
3444
|
+
let leaseId2;
|
|
3137
3445
|
let launch;
|
|
3138
3446
|
try {
|
|
3139
|
-
|
|
3447
|
+
if ((cli === "claude" || cli === "codex") && ctx.routeLeaseManager) {
|
|
3448
|
+
const outcome = await ctx.routeLeaseManager.createFromRequest({
|
|
3449
|
+
schemaVersion: import_provider_proxy2.ROUTE_LEASE_REQUEST_SCHEMA,
|
|
3450
|
+
consumer: "omnicross-terminal",
|
|
3451
|
+
runtime: cli,
|
|
3452
|
+
upstream: { kind: "provider", providerId: target.providerId },
|
|
3453
|
+
model: target.model,
|
|
3454
|
+
execution: { sessionId: id }
|
|
3455
|
+
}, `omnicross-terminal:${id}`);
|
|
3456
|
+
leaseId2 = outcome.result.leaseId;
|
|
3457
|
+
const stopRenewal = startTerminalLeaseRenewal(ctx.routeLeaseManager, leaseId2);
|
|
3458
|
+
launch = {
|
|
3459
|
+
env: outcome.result.launch.env,
|
|
3460
|
+
extraArgs: outcome.result.launch.extraArgs,
|
|
3461
|
+
onSessionEnd: () => {
|
|
3462
|
+
stopRenewal();
|
|
3463
|
+
ctx.routeLeaseManager?.release(outcome.result.leaseId);
|
|
3464
|
+
}
|
|
3465
|
+
};
|
|
3466
|
+
} else {
|
|
3467
|
+
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
3468
|
+
}
|
|
3140
3469
|
} catch (err5) {
|
|
3141
|
-
|
|
3470
|
+
const status = err5 instanceof import_provider_proxy2.RouteLeaseError ? err5.status : 400;
|
|
3471
|
+
return { status, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
|
|
3142
3472
|
}
|
|
3143
3473
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
3144
3474
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
3475
|
+
let openerCleanup;
|
|
3476
|
+
let ended = false;
|
|
3477
|
+
let published = false;
|
|
3478
|
+
const onSessionEnd = () => {
|
|
3479
|
+
if (ended) return;
|
|
3480
|
+
ended = true;
|
|
3481
|
+
if (published) sessions.delete(id);
|
|
3482
|
+
try {
|
|
3483
|
+
openerCleanup?.();
|
|
3484
|
+
} finally {
|
|
3485
|
+
launch.onSessionEnd();
|
|
3486
|
+
}
|
|
3487
|
+
};
|
|
3145
3488
|
try {
|
|
3146
|
-
opener({
|
|
3489
|
+
const cleanup = opener({
|
|
3490
|
+
cli,
|
|
3491
|
+
command: meta.command,
|
|
3492
|
+
extraArgs: launch.extraArgs ?? [],
|
|
3493
|
+
env: launch.env,
|
|
3494
|
+
cwd,
|
|
3495
|
+
platform,
|
|
3496
|
+
onFailure: onSessionEnd
|
|
3497
|
+
});
|
|
3498
|
+
if (cleanup) openerCleanup = cleanup;
|
|
3147
3499
|
} catch (err5) {
|
|
3148
|
-
|
|
3500
|
+
onSessionEnd();
|
|
3149
3501
|
return { status: 500, body: errBody(err5 instanceof Error ? err5.message : "failed to open terminal") };
|
|
3150
3502
|
}
|
|
3151
|
-
|
|
3503
|
+
if (ended) {
|
|
3504
|
+
openerCleanup?.();
|
|
3505
|
+
return { status: 500, body: errBody("failed to open terminal") };
|
|
3506
|
+
}
|
|
3152
3507
|
sessions.set(id, {
|
|
3153
3508
|
id,
|
|
3154
3509
|
cli,
|
|
3155
3510
|
providerId: target.providerId,
|
|
3156
3511
|
model: target.model,
|
|
3512
|
+
...leaseId2 ? { leaseId: leaseId2 } : {},
|
|
3157
3513
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3158
|
-
onSessionEnd
|
|
3514
|
+
onSessionEnd
|
|
3159
3515
|
});
|
|
3516
|
+
published = true;
|
|
3517
|
+
if (ended) sessions.delete(id);
|
|
3160
3518
|
return { status: 200, body: { sessionId: id, providerId: target.providerId, model: target.model } };
|
|
3161
3519
|
}
|
|
3162
3520
|
|
|
@@ -3176,8 +3534,8 @@ function validateAuditSegment(patch) {
|
|
|
3176
3534
|
}
|
|
3177
3535
|
}
|
|
3178
3536
|
const maxBodyBytes = audit["maxBodyBytes"];
|
|
3179
|
-
if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes <
|
|
3180
|
-
errors.push("audit.maxBodyBytes must be a non-negative number");
|
|
3537
|
+
if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes < -1)) {
|
|
3538
|
+
errors.push("audit.maxBodyBytes must be -1 or a non-negative number");
|
|
3181
3539
|
}
|
|
3182
3540
|
const retentionDays = audit["retentionDays"];
|
|
3183
3541
|
if (retentionDays !== void 0 && (typeof retentionDays !== "number" || !Number.isFinite(retentionDays) || retentionDays < 0)) {
|
|
@@ -3612,18 +3970,16 @@ function preserveWebhookSecrets(incoming, current) {
|
|
|
3612
3970
|
}
|
|
3613
3971
|
|
|
3614
3972
|
// src/audit/auditRuntime.ts
|
|
3615
|
-
var import_node_path8 = require("path");
|
|
3616
3973
|
var import_auditSink = require("@omnicross/core/pipeline/auditSink");
|
|
3617
3974
|
var import_upstreamTrace = require("@omnicross/core/pipeline/upstreamTrace");
|
|
3618
3975
|
var writer = null;
|
|
3619
3976
|
var sweeper = null;
|
|
3620
|
-
|
|
3621
|
-
function setAuditRuntime(w, s, dir) {
|
|
3977
|
+
function setAuditRuntime(w, s) {
|
|
3622
3978
|
writer = w;
|
|
3623
3979
|
sweeper = s;
|
|
3624
|
-
auditDir = dir;
|
|
3625
3980
|
}
|
|
3626
3981
|
function applyAuditConfig(config) {
|
|
3982
|
+
(0, import_upstreamTrace.setUpstreamTracePath)(null);
|
|
3627
3983
|
const enabled = config?.enabled === true && writer !== null;
|
|
3628
3984
|
if (enabled && config) {
|
|
3629
3985
|
(0, import_auditSink.setAuditCaptureConfig)(config);
|
|
@@ -3633,11 +3989,9 @@ function applyAuditConfig(config) {
|
|
|
3633
3989
|
sweeper.configure(config);
|
|
3634
3990
|
sweeper.start();
|
|
3635
3991
|
}
|
|
3636
|
-
(0, import_upstreamTrace.setUpstreamTracePath)(config.captureBodies ? (0, import_node_path8.join)(auditDir, "upstream-trace.jsonl") : null);
|
|
3637
3992
|
} else {
|
|
3638
3993
|
(0, import_auditSink.setAuditCaptureConfig)(null);
|
|
3639
3994
|
(0, import_auditSink.setAuditSink)(null);
|
|
3640
|
-
(0, import_upstreamTrace.setUpstreamTracePath)(null);
|
|
3641
3995
|
if (sweeper) {
|
|
3642
3996
|
if (config) sweeper.configure(config);
|
|
3643
3997
|
sweeper.dispose();
|
|
@@ -4037,7 +4391,7 @@ function sealPack(bundleJson, passphrase) {
|
|
|
4037
4391
|
cipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
|
|
4038
4392
|
const ciphertext = Buffer.concat([cipher.update(bundleJson, "utf8"), cipher.final()]);
|
|
4039
4393
|
const tag = cipher.getAuthTag();
|
|
4040
|
-
const
|
|
4394
|
+
const header2 = {
|
|
4041
4395
|
magic: PACK_MAGIC,
|
|
4042
4396
|
v: PACK_VERSION,
|
|
4043
4397
|
kdf: KDF_ALGORITHM,
|
|
@@ -4048,7 +4402,7 @@ function sealPack(bundleJson, passphrase) {
|
|
|
4048
4402
|
iv: iv.toString("base64"),
|
|
4049
4403
|
tag: tag.toString("base64")
|
|
4050
4404
|
};
|
|
4051
|
-
return `${PACK_PREFIX}${toB64Url(JSON.stringify(
|
|
4405
|
+
return `${PACK_PREFIX}${toB64Url(JSON.stringify(header2))}.${ciphertext.toString("base64")}`;
|
|
4052
4406
|
}
|
|
4053
4407
|
function parsePack(packString) {
|
|
4054
4408
|
if (typeof packString !== "string" || !packString.startsWith(PACK_PREFIX)) {
|
|
@@ -4059,28 +4413,28 @@ function parsePack(packString) {
|
|
|
4059
4413
|
if (dot < 0) throw new PackAuthError("migration pack is malformed (missing body)");
|
|
4060
4414
|
const headerB64Url = rest.slice(0, dot);
|
|
4061
4415
|
const ctB64 = rest.slice(dot + 1);
|
|
4062
|
-
let
|
|
4416
|
+
let header2;
|
|
4063
4417
|
try {
|
|
4064
|
-
|
|
4418
|
+
header2 = JSON.parse(fromB64Url(headerB64Url));
|
|
4065
4419
|
} catch {
|
|
4066
4420
|
throw new PackAuthError("migration pack is malformed (unreadable header)");
|
|
4067
4421
|
}
|
|
4068
|
-
if (!
|
|
4422
|
+
if (!header2 || header2.magic !== PACK_MAGIC || header2.v !== PACK_VERSION || header2.kdf !== KDF_ALGORITHM || typeof header2.salt !== "string" || typeof header2.iv !== "string" || typeof header2.tag !== "string" || typeof header2.N !== "number" || typeof header2.r !== "number" || typeof header2.p !== "number") {
|
|
4069
4423
|
throw new PackAuthError("migration pack is malformed (unsupported header)");
|
|
4070
4424
|
}
|
|
4071
4425
|
const ciphertext = Buffer.from(ctB64, "base64");
|
|
4072
|
-
return { header, ciphertext };
|
|
4426
|
+
return { header: header2, ciphertext };
|
|
4073
4427
|
}
|
|
4074
4428
|
function openPack(packString, passphrase) {
|
|
4075
4429
|
assertPassphraseStrength(passphrase);
|
|
4076
|
-
const { header, ciphertext } = parsePack(packString);
|
|
4077
|
-
const salt = Buffer.from(
|
|
4078
|
-
const iv = Buffer.from(
|
|
4079
|
-
const tag = Buffer.from(
|
|
4430
|
+
const { header: header2, ciphertext } = parsePack(packString);
|
|
4431
|
+
const salt = Buffer.from(header2.salt, "base64");
|
|
4432
|
+
const iv = Buffer.from(header2.iv, "base64");
|
|
4433
|
+
const tag = Buffer.from(header2.tag, "base64");
|
|
4080
4434
|
if (iv.length !== IV_BYTES2 || tag.length !== TAG_BYTES2) {
|
|
4081
4435
|
throw new PackAuthError("migration pack is malformed (invalid iv/tag length)");
|
|
4082
4436
|
}
|
|
4083
|
-
const key = deriveKey(passphrase, salt,
|
|
4437
|
+
const key = deriveKey(passphrase, salt, header2.N, header2.r, header2.p);
|
|
4084
4438
|
const decipher = (0, import_node_crypto8.createDecipheriv)("aes-256-gcm", key, iv);
|
|
4085
4439
|
decipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
|
|
4086
4440
|
decipher.setAuthTag(tag);
|
|
@@ -4416,10 +4770,10 @@ function writeJson2(res, status, body) {
|
|
|
4416
4770
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
4417
4771
|
res.end(JSON.stringify(body));
|
|
4418
4772
|
}
|
|
4419
|
-
function
|
|
4773
|
+
function writeError2(res, status, message) {
|
|
4420
4774
|
writeJson2(res, status, { error: { type: "account_allowance_error", message } });
|
|
4421
4775
|
}
|
|
4422
|
-
function
|
|
4776
|
+
function readJson2(req) {
|
|
4423
4777
|
return new Promise((resolve3, reject) => {
|
|
4424
4778
|
const chunks = [];
|
|
4425
4779
|
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
@@ -4445,10 +4799,10 @@ function allowanceProvider(value) {
|
|
|
4445
4799
|
return value === "claude" || value === "codex" ? value : null;
|
|
4446
4800
|
}
|
|
4447
4801
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
4448
|
-
if (!service) return
|
|
4802
|
+
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
4449
4803
|
if (method === "GET" && rest.length === 1 && rest[0] === "scheduling") {
|
|
4450
4804
|
if (!service.getSchedulingStatus) {
|
|
4451
|
-
return
|
|
4805
|
+
return writeError2(res, 501, "allowance scheduling diagnostics are not available");
|
|
4452
4806
|
}
|
|
4453
4807
|
return writeJson2(res, 200, { scheduling: service.getSchedulingStatus() });
|
|
4454
4808
|
}
|
|
@@ -4456,30 +4810,32 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4456
4810
|
const params = query(req);
|
|
4457
4811
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
4458
4812
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
4459
|
-
if (providerId === null) return
|
|
4813
|
+
if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
|
|
4460
4814
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
4461
4815
|
const allowances = await service.list({ providerId, accountId });
|
|
4462
4816
|
return writeJson2(res, 200, { allowances });
|
|
4463
4817
|
}
|
|
4464
4818
|
if (method === "POST" && rest[0] === "refresh") {
|
|
4465
|
-
const body = await
|
|
4819
|
+
const body = await readJson2(req);
|
|
4466
4820
|
const requestedProvider = allowanceProvider(
|
|
4467
4821
|
typeof body["providerId"] === "string" ? body["providerId"] : "claude"
|
|
4468
4822
|
);
|
|
4469
4823
|
if (requestedProvider !== "claude") {
|
|
4470
|
-
return
|
|
4824
|
+
return writeError2(res, 400, "only Claude allowances support explicit refresh");
|
|
4471
4825
|
}
|
|
4472
4826
|
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
4473
4827
|
const allowances = await service.refreshClaude(accountId);
|
|
4474
4828
|
if (accountId && allowances.length === 0) {
|
|
4475
|
-
return
|
|
4829
|
+
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
4476
4830
|
}
|
|
4477
4831
|
return writeJson2(res, 200, { allowances });
|
|
4478
4832
|
}
|
|
4479
|
-
return
|
|
4833
|
+
return writeError2(res, 405, `method ${method} not allowed on account allowances`);
|
|
4480
4834
|
}
|
|
4481
4835
|
|
|
4482
4836
|
// src/admin/adminApi.ts
|
|
4837
|
+
var import_AccountRouteActivity = require("@omnicross/core/pipeline/AccountRouteActivity");
|
|
4838
|
+
var import_ServerOverloadCounter = require("@omnicross/core/pipeline/ServerOverloadCounter");
|
|
4483
4839
|
function readBody(req) {
|
|
4484
4840
|
return new Promise((resolve3, reject) => {
|
|
4485
4841
|
const chunks = [];
|
|
@@ -4516,6 +4872,9 @@ function toKeyInfo(row) {
|
|
|
4516
4872
|
id: row.id,
|
|
4517
4873
|
name: row.name,
|
|
4518
4874
|
keyPrefix: row.keyPrefix,
|
|
4875
|
+
// True only when a reversible `keySecret` envelope was persisted at creation
|
|
4876
|
+
// — gates the UI "view key" eye. Legacy hash-only rows read as absent.
|
|
4877
|
+
revealable: Boolean(row.keySecret),
|
|
4519
4878
|
enabled: row.enabled,
|
|
4520
4879
|
createdAt: row.createdAt,
|
|
4521
4880
|
lastUsedAt: row.lastUsedAt,
|
|
@@ -5171,7 +5530,13 @@ function handlePresets(res, method) {
|
|
|
5171
5530
|
name: p.name,
|
|
5172
5531
|
apiFormat: p.apiFormat,
|
|
5173
5532
|
baseUrl: p.baseUrl,
|
|
5174
|
-
models: p.models
|
|
5533
|
+
models: p.models,
|
|
5534
|
+
nameKey: p.nameKey,
|
|
5535
|
+
icon: p.icon,
|
|
5536
|
+
description: p.description,
|
|
5537
|
+
features: p.features,
|
|
5538
|
+
website: p.website,
|
|
5539
|
+
modelsEndpoint: p.modelsEndpoint
|
|
5175
5540
|
}));
|
|
5176
5541
|
return writeJson3(res, 200, { presets, excluded });
|
|
5177
5542
|
}
|
|
@@ -5205,12 +5570,27 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5205
5570
|
plaintextOnce: created.plaintextOnce
|
|
5206
5571
|
});
|
|
5207
5572
|
}
|
|
5573
|
+
if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
|
|
5574
|
+
const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
|
|
5575
|
+
if (revealed !== null) return writeJson3(res, 200, { key: revealed });
|
|
5576
|
+
const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
|
|
5577
|
+
if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
|
|
5578
|
+
return writeJsonError(
|
|
5579
|
+
res,
|
|
5580
|
+
409,
|
|
5581
|
+
`key '${rest[0]}' is not revealable (created before revealable key storage)`
|
|
5582
|
+
);
|
|
5583
|
+
}
|
|
5208
5584
|
const id = rest[0];
|
|
5209
5585
|
const action = rest[1];
|
|
5210
5586
|
if (method === "POST" && id && action === "revoke") {
|
|
5211
5587
|
const ok = await deps.keyDb.outboundApiKeysRevoke(id);
|
|
5212
5588
|
return writeJson3(res, ok ? 200 : 404, { ok });
|
|
5213
5589
|
}
|
|
5590
|
+
if (method === "DELETE" && id && !action) {
|
|
5591
|
+
const ok = await deps.keyDb.outboundApiKeysDelete(id);
|
|
5592
|
+
return writeJson3(res, ok ? 200 : 404, { ok });
|
|
5593
|
+
}
|
|
5214
5594
|
if (method === "POST" && id && action === "enabled") {
|
|
5215
5595
|
const body = await readJsonBody3(req);
|
|
5216
5596
|
const enabled = body["enabled"] === true;
|
|
@@ -5387,6 +5767,40 @@ async function handleServer(req, res, method, deps) {
|
|
|
5387
5767
|
return writeJsonError(res, 405, `method ${method} not allowed on server`);
|
|
5388
5768
|
}
|
|
5389
5769
|
async function handleAccounts(req, res, method, rest, deps) {
|
|
5770
|
+
if (rest[0] === "route-activity" && rest.length === 1) {
|
|
5771
|
+
if (method !== "GET") {
|
|
5772
|
+
return writeJsonError(res, 405, `method ${method} not allowed on account route activity`);
|
|
5773
|
+
}
|
|
5774
|
+
const query2 = requestQuery(req);
|
|
5775
|
+
const parsedLimit = Number(query2.get("limit") ?? "100");
|
|
5776
|
+
const records = (0, import_AccountRouteActivity.getSharedAccountRouteActivity)().list({
|
|
5777
|
+
providerId: query2.get("providerId") ?? void 0,
|
|
5778
|
+
accountId: query2.get("accountId") ?? void 0,
|
|
5779
|
+
sessionKey: query2.get("sessionKey") ?? void 0,
|
|
5780
|
+
limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
|
|
5781
|
+
});
|
|
5782
|
+
return writeJson3(res, 200, {
|
|
5783
|
+
available: true,
|
|
5784
|
+
records,
|
|
5785
|
+
capacity: import_AccountRouteActivity.ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
5786
|
+
collectedAt: Date.now()
|
|
5787
|
+
});
|
|
5788
|
+
}
|
|
5789
|
+
if (rest[0] === "overload-counters" && rest.length === 1) {
|
|
5790
|
+
if (method !== "GET") {
|
|
5791
|
+
return writeJsonError(res, 405, `method ${method} not allowed on overload counters`);
|
|
5792
|
+
}
|
|
5793
|
+
const query2 = requestQuery(req);
|
|
5794
|
+
const entries = (0, import_ServerOverloadCounter.getSharedOverloadCounter)().list({
|
|
5795
|
+
providerId: query2.get("providerId") ?? void 0,
|
|
5796
|
+
accountId: query2.get("accountId") ?? void 0
|
|
5797
|
+
});
|
|
5798
|
+
return writeJson3(res, 200, {
|
|
5799
|
+
available: true,
|
|
5800
|
+
entries,
|
|
5801
|
+
collectedAt: Date.now()
|
|
5802
|
+
});
|
|
5803
|
+
}
|
|
5390
5804
|
if (rest[0] === "allowances") {
|
|
5391
5805
|
return handleAccountAllowanceApi(
|
|
5392
5806
|
req,
|
|
@@ -5533,8 +5947,13 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
5533
5947
|
if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
|
|
5534
5948
|
return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
5535
5949
|
}
|
|
5536
|
-
const result = await deps.accountProbeService.
|
|
5537
|
-
return writeJson3(res, 200, {
|
|
5950
|
+
const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
|
|
5951
|
+
return writeJson3(res, 200, {
|
|
5952
|
+
ok: result.ok,
|
|
5953
|
+
marked: result.marked,
|
|
5954
|
+
tier: result.tier,
|
|
5955
|
+
model: result.model
|
|
5956
|
+
});
|
|
5538
5957
|
}
|
|
5539
5958
|
if (method === "POST" && rest[2] === "label") {
|
|
5540
5959
|
const accountId = rest[1];
|
|
@@ -5643,6 +6062,7 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
5643
6062
|
const result = await handleCliLaunch(cli, body, {
|
|
5644
6063
|
llmConfig: deps.llmConfig,
|
|
5645
6064
|
providers,
|
|
6065
|
+
routeLeaseManager: deps.routeLeaseManager,
|
|
5646
6066
|
opener: deps.cliTerminalOpener,
|
|
5647
6067
|
probe: deps.cliPathProbe
|
|
5648
6068
|
});
|
|
@@ -5813,7 +6233,7 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
5813
6233
|
var import_node_fs9 = require("fs");
|
|
5814
6234
|
var import_promises = require("fs/promises");
|
|
5815
6235
|
var import_node_module = require("module");
|
|
5816
|
-
var
|
|
6236
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
5817
6237
|
var import_meta = {};
|
|
5818
6238
|
var CONTENT_TYPES = {
|
|
5819
6239
|
".html": "text/html; charset=utf-8",
|
|
@@ -5834,13 +6254,13 @@ var CONTENT_TYPES = {
|
|
|
5834
6254
|
function resolveUiDist() {
|
|
5835
6255
|
const fromEnv = process.env["OMNICROSS_UI_DIST"];
|
|
5836
6256
|
if (fromEnv) {
|
|
5837
|
-
return (0, import_node_fs9.existsSync)(
|
|
6257
|
+
return (0, import_node_fs9.existsSync)(import_node_path8.default.join(fromEnv, "index.html")) ? import_node_path8.default.resolve(fromEnv) : null;
|
|
5838
6258
|
}
|
|
5839
6259
|
try {
|
|
5840
6260
|
const req = (0, import_node_module.createRequire)(typeof __filename !== "undefined" ? __filename : import_meta.url);
|
|
5841
6261
|
const pkgJson = req.resolve("@omnicross/ui/package.json");
|
|
5842
|
-
const dist =
|
|
5843
|
-
return (0, import_node_fs9.existsSync)(
|
|
6262
|
+
const dist = import_node_path8.default.join(import_node_path8.default.dirname(pkgJson), "dist");
|
|
6263
|
+
return (0, import_node_fs9.existsSync)(import_node_path8.default.join(dist, "index.html")) ? dist : null;
|
|
5844
6264
|
} catch {
|
|
5845
6265
|
return null;
|
|
5846
6266
|
}
|
|
@@ -5882,16 +6302,16 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
5882
6302
|
res.end(JSON.stringify({ error: { type: "bad_request", message: "invalid path" } }));
|
|
5883
6303
|
return true;
|
|
5884
6304
|
}
|
|
5885
|
-
const filePath =
|
|
5886
|
-
if (filePath !== uiDist && !filePath.startsWith(uiDist +
|
|
6305
|
+
const filePath = import_node_path8.default.resolve(uiDist, rel === "" ? "index.html" : rel);
|
|
6306
|
+
if (filePath !== uiDist && !filePath.startsWith(uiDist + import_node_path8.default.sep)) {
|
|
5887
6307
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
5888
6308
|
res.end(JSON.stringify({ error: { type: "forbidden", message: "path outside ui root" } }));
|
|
5889
6309
|
return true;
|
|
5890
6310
|
}
|
|
5891
6311
|
let target = filePath;
|
|
5892
6312
|
if (!(0, import_node_fs9.existsSync)(target) || (0, import_node_fs9.statSync)(target).isDirectory()) {
|
|
5893
|
-
if (
|
|
5894
|
-
target =
|
|
6313
|
+
if (import_node_path8.default.extname(rel) === "") {
|
|
6314
|
+
target = import_node_path8.default.join(uiDist, "index.html");
|
|
5895
6315
|
} else {
|
|
5896
6316
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
5897
6317
|
res.end(JSON.stringify({ error: { type: "not_found", message: "no such ui asset" } }));
|
|
@@ -5899,14 +6319,14 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
5899
6319
|
}
|
|
5900
6320
|
}
|
|
5901
6321
|
const body = await (0, import_promises.readFile)(target);
|
|
5902
|
-
const type = CONTENT_TYPES[
|
|
6322
|
+
const type = CONTENT_TYPES[import_node_path8.default.extname(target).toLowerCase()] ?? "application/octet-stream";
|
|
5903
6323
|
res.writeHead(200, { "Content-Type": type, "Content-Length": body.length });
|
|
5904
6324
|
res.end(req.method === "HEAD" ? void 0 : body);
|
|
5905
6325
|
return true;
|
|
5906
6326
|
}
|
|
5907
6327
|
|
|
5908
6328
|
// src/admin/version.ts
|
|
5909
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
6329
|
+
var DAEMON_VERSION = true ? "0.1.8" : "0.0.0-dev";
|
|
5910
6330
|
|
|
5911
6331
|
// src/admin/AdminServer.ts
|
|
5912
6332
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -6014,6 +6434,10 @@ var AdminServer = class {
|
|
|
6014
6434
|
handleAuditQuery(req, res, this.deps.auditReader);
|
|
6015
6435
|
return;
|
|
6016
6436
|
}
|
|
6437
|
+
if (path2 === "/admin/api/audit/stats" && (req.method === "GET" || req.method === "HEAD")) {
|
|
6438
|
+
await handleAuditStatsQuery(req, res, this.deps.auditStatsReader);
|
|
6439
|
+
return;
|
|
6440
|
+
}
|
|
6017
6441
|
if (path2 === "/admin/api/billing-status" && (req.method === "GET" || req.method === "HEAD")) {
|
|
6018
6442
|
handleBillingStatus(res, this.deps.billingStatusReader);
|
|
6019
6443
|
return;
|
|
@@ -6022,6 +6446,10 @@ var AdminServer = class {
|
|
|
6022
6446
|
await handleWebhookTest(req, res);
|
|
6023
6447
|
return;
|
|
6024
6448
|
}
|
|
6449
|
+
if (path2 === "/admin/api/route-leases" || path2.startsWith("/admin/api/route-leases/")) {
|
|
6450
|
+
await handleRouteLeaseApi(req, res, path2, this.deps);
|
|
6451
|
+
return;
|
|
6452
|
+
}
|
|
6025
6453
|
if (path2.startsWith("/admin/api/")) {
|
|
6026
6454
|
await handleAdminApi(req, res, path2, this.deps);
|
|
6027
6455
|
return;
|
|
@@ -6033,8 +6461,8 @@ var AdminServer = class {
|
|
|
6033
6461
|
}
|
|
6034
6462
|
/** Constant-time bearer/header check against the configured token. */
|
|
6035
6463
|
isAuthorized(req, token) {
|
|
6036
|
-
const
|
|
6037
|
-
const bearer = typeof
|
|
6464
|
+
const header2 = req.headers["authorization"];
|
|
6465
|
+
const bearer = typeof header2 === "string" && header2.startsWith("Bearer ") ? header2.slice("Bearer ".length).trim() : void 0;
|
|
6038
6466
|
const xToken = req.headers["x-admin-token"];
|
|
6039
6467
|
const presented = bearer ?? (typeof xToken === "string" ? xToken.trim() : void 0);
|
|
6040
6468
|
return constantTimeEquals(presented, token);
|
|
@@ -6130,20 +6558,36 @@ var OAuthSessionStore = class {
|
|
|
6130
6558
|
return sessionId;
|
|
6131
6559
|
}
|
|
6132
6560
|
/**
|
|
6133
|
-
*
|
|
6134
|
-
*
|
|
6135
|
-
* dropped). A `null` return means the completer must reject (no
|
|
6136
|
-
* write).
|
|
6561
|
+
* NON-DESTRUCTIVE lookup: return the session for `sessionId`, or `null` when
|
|
6562
|
+
* it is unknown, already consumed, or past its TTL (an expired entry is
|
|
6563
|
+
* dropped here). A `null` return means the completer must reject (no
|
|
6564
|
+
* exchange, no write).
|
|
6565
|
+
*
|
|
6566
|
+
* Deliberately NOT a consume: the completer peeks, runs the token exchange,
|
|
6567
|
+
* and only {@link consume}s once a token has actually been minted. Consuming
|
|
6568
|
+
* up-front burned the session on EVERY failed exchange (a mistyped/expired
|
|
6569
|
+
* pasted code, a proxy hiccup), so the user's natural retry hit
|
|
6570
|
+
* "session is unknown, expired, or already used" and the login became
|
|
6571
|
+
* unrecoverable without restarting the whole flow.
|
|
6137
6572
|
*/
|
|
6138
|
-
|
|
6573
|
+
peek(sessionId) {
|
|
6139
6574
|
this.sweep();
|
|
6140
6575
|
const session = this.sessions.get(sessionId);
|
|
6141
6576
|
if (!session) return null;
|
|
6142
|
-
|
|
6143
|
-
|
|
6577
|
+
if (Date.now() - session.createdAt > this.ttlMs) {
|
|
6578
|
+
this.sessions.delete(sessionId);
|
|
6579
|
+
return null;
|
|
6580
|
+
}
|
|
6144
6581
|
return session;
|
|
6145
6582
|
}
|
|
6146
|
-
/**
|
|
6583
|
+
/**
|
|
6584
|
+
* SINGLE-USE burn: drop the session so the same `sessionId` can never be
|
|
6585
|
+
* completed twice. Called ONLY after a successful token exchange.
|
|
6586
|
+
*/
|
|
6587
|
+
consume(sessionId) {
|
|
6588
|
+
this.sessions.delete(sessionId);
|
|
6589
|
+
}
|
|
6590
|
+
/** Drop every session past its TTL. Called on each put/peek. */
|
|
6147
6591
|
sweep() {
|
|
6148
6592
|
const now = Date.now();
|
|
6149
6593
|
for (const [id, session] of this.sessions) {
|
|
@@ -6158,6 +6602,10 @@ var LOOPBACK_HOST = "127.0.0.1";
|
|
|
6158
6602
|
var LOOPBACK_PORT = 1455;
|
|
6159
6603
|
var CALLBACK_PATH = "/auth/callback";
|
|
6160
6604
|
var DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
6605
|
+
var HTML_HEADERS = {
|
|
6606
|
+
"Content-Type": "text/html",
|
|
6607
|
+
Connection: "close"
|
|
6608
|
+
};
|
|
6161
6609
|
function pageHtml(message) {
|
|
6162
6610
|
return `<!doctype html><meta charset="utf-8"><title>omnicross login</title><body style="font-family:sans-serif;padding:2rem"><h2>${message}</h2><p>You can close this window and return to the terminal.</p></body>`;
|
|
6163
6611
|
}
|
|
@@ -6168,30 +6616,31 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
6168
6616
|
if (settled) return;
|
|
6169
6617
|
settled = true;
|
|
6170
6618
|
clearTimeout(timer);
|
|
6171
|
-
|
|
6619
|
+
fn();
|
|
6620
|
+
server2.close();
|
|
6172
6621
|
};
|
|
6173
6622
|
const server = (0, import_node_http3.createServer)((req, res) => {
|
|
6174
6623
|
const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
|
|
6175
6624
|
if (url.pathname !== CALLBACK_PATH) {
|
|
6176
|
-
res.writeHead(404,
|
|
6625
|
+
res.writeHead(404, HTML_HEADERS);
|
|
6177
6626
|
res.end(pageHtml("Not found"));
|
|
6178
6627
|
return;
|
|
6179
6628
|
}
|
|
6180
6629
|
const code = url.searchParams.get("code");
|
|
6181
6630
|
const state = url.searchParams.get("state");
|
|
6182
6631
|
if (!code) {
|
|
6183
|
-
res.writeHead(400,
|
|
6632
|
+
res.writeHead(400, HTML_HEADERS);
|
|
6184
6633
|
res.end(pageHtml("Login failed: missing authorization code."));
|
|
6185
6634
|
finish(server, () => reject(new Error("login: callback did not include an authorization code")));
|
|
6186
6635
|
return;
|
|
6187
6636
|
}
|
|
6188
6637
|
if (state !== expectedState) {
|
|
6189
|
-
res.writeHead(400,
|
|
6638
|
+
res.writeHead(400, HTML_HEADERS);
|
|
6190
6639
|
res.end(pageHtml("Login failed: state mismatch."));
|
|
6191
6640
|
finish(server, () => reject(new Error("login: callback state did not match (possible CSRF) \u2014 aborting")));
|
|
6192
6641
|
return;
|
|
6193
6642
|
}
|
|
6194
|
-
res.writeHead(200,
|
|
6643
|
+
res.writeHead(200, HTML_HEADERS);
|
|
6195
6644
|
res.end(pageHtml("Login complete."));
|
|
6196
6645
|
finish(server, () => resolve3(code));
|
|
6197
6646
|
});
|
|
@@ -6677,8 +7126,12 @@ var JsonlUsageEventStore = class {
|
|
|
6677
7126
|
reasoningTokens: 0,
|
|
6678
7127
|
costUsd: 0,
|
|
6679
7128
|
costSavedByCacheUsd: 0,
|
|
6680
|
-
eventCount: 0
|
|
7129
|
+
eventCount: 0,
|
|
7130
|
+
cacheEligibleEventCount: 0,
|
|
7131
|
+
coldCacheEventCount: 0,
|
|
7132
|
+
medianCacheHitRate: null
|
|
6681
7133
|
};
|
|
7134
|
+
const perEventHitRates = [];
|
|
6682
7135
|
for (const row of this.readRows(range)) {
|
|
6683
7136
|
totals.inputTokens += row.inputTokens;
|
|
6684
7137
|
totals.outputTokens += row.outputTokens;
|
|
@@ -6688,7 +7141,14 @@ var JsonlUsageEventStore = class {
|
|
|
6688
7141
|
totals.costUsd += row.costUsd;
|
|
6689
7142
|
totals.costSavedByCacheUsd += row.costSavedByCacheUsd;
|
|
6690
7143
|
totals.eventCount += 1;
|
|
7144
|
+
const promptSideTokens = row.inputTokens + row.cacheReadTokens + row.cacheCreationTokens;
|
|
7145
|
+
if (promptSideTokens > 0) {
|
|
7146
|
+
totals.cacheEligibleEventCount += 1;
|
|
7147
|
+
if (row.cacheReadTokens === 0) totals.coldCacheEventCount += 1;
|
|
7148
|
+
perEventHitRates.push(row.cacheReadTokens / promptSideTokens);
|
|
7149
|
+
}
|
|
6691
7150
|
}
|
|
7151
|
+
totals.medianCacheHitRate = median(perEventHitRates);
|
|
6692
7152
|
return totals;
|
|
6693
7153
|
}
|
|
6694
7154
|
async getByModel(range) {
|
|
@@ -6922,6 +7382,15 @@ var NUMERIC_FIELDS = [
|
|
|
6922
7382
|
];
|
|
6923
7383
|
var NULLABLE_STRING_FIELDS = ["messageId", "parentMessageId", "sessionId", "apiKeyId"];
|
|
6924
7384
|
var isStringOrNull = (v) => v === null || typeof v === "string";
|
|
7385
|
+
var CACHE_KEY_SOURCES = /* @__PURE__ */ new Set([
|
|
7386
|
+
"client",
|
|
7387
|
+
"session-header",
|
|
7388
|
+
"thread-header",
|
|
7389
|
+
"body-session-id",
|
|
7390
|
+
"body-thread-id",
|
|
7391
|
+
"content-fingerprint",
|
|
7392
|
+
"none"
|
|
7393
|
+
]);
|
|
6925
7394
|
function isUsageEventRecord(parsed) {
|
|
6926
7395
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
6927
7396
|
const r = parsed;
|
|
@@ -6929,6 +7398,10 @@ function isUsageEventRecord(parsed) {
|
|
|
6929
7398
|
if (typeof r["providerId"] !== "string") return false;
|
|
6930
7399
|
if (typeof r["model"] !== "string") return false;
|
|
6931
7400
|
if (typeof r["engineOrigin"] !== "string") return false;
|
|
7401
|
+
if (r["cacheKeySource"] !== void 0 && (typeof r["cacheKeySource"] !== "string" || !CACHE_KEY_SOURCES.has(r["cacheKeySource"]))) return false;
|
|
7402
|
+
if (r["cacheKeyInjected"] !== void 0 && typeof r["cacheKeyInjected"] !== "boolean") {
|
|
7403
|
+
return false;
|
|
7404
|
+
}
|
|
6932
7405
|
for (const f of NULLABLE_STRING_FIELDS) {
|
|
6933
7406
|
if (!isStringOrNull(r[f])) return false;
|
|
6934
7407
|
}
|
|
@@ -6938,6 +7411,12 @@ function isUsageEventRecord(parsed) {
|
|
|
6938
7411
|
}
|
|
6939
7412
|
return true;
|
|
6940
7413
|
}
|
|
7414
|
+
function median(values) {
|
|
7415
|
+
if (values.length === 0) return null;
|
|
7416
|
+
values.sort((a, b) => a - b);
|
|
7417
|
+
const middle = Math.floor(values.length / 2);
|
|
7418
|
+
return values.length % 2 === 1 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
|
|
7419
|
+
}
|
|
6941
7420
|
|
|
6942
7421
|
// src/ports/JsonPricingStore.ts
|
|
6943
7422
|
var import_node_fs13 = require("fs");
|
|
@@ -7298,7 +7777,7 @@ var JsonVoucherDb = class {
|
|
|
7298
7777
|
|
|
7299
7778
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
7300
7779
|
var import_node_fs17 = require("fs");
|
|
7301
|
-
var
|
|
7780
|
+
var import_node_path10 = require("path");
|
|
7302
7781
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
7303
7782
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
7304
7783
|
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
@@ -7348,10 +7827,10 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
7348
7827
|
|
|
7349
7828
|
// src/ports/external-cli-credentials.ts
|
|
7350
7829
|
var import_node_fs16 = require("fs");
|
|
7351
|
-
var
|
|
7352
|
-
var
|
|
7353
|
-
function externalStorePath(provider, home = (0,
|
|
7354
|
-
return provider === "claude" ? (0,
|
|
7830
|
+
var import_node_os4 = require("os");
|
|
7831
|
+
var import_node_path9 = require("path");
|
|
7832
|
+
function externalStorePath(provider, home = (0, import_node_os4.homedir)()) {
|
|
7833
|
+
return provider === "claude" ? (0, import_node_path9.join)(home, ".claude", ".credentials.json") : (0, import_node_path9.join)(home, ".codex", "auth.json");
|
|
7355
7834
|
}
|
|
7356
7835
|
function decodeJwtExpiryMs(token) {
|
|
7357
7836
|
try {
|
|
@@ -7398,7 +7877,7 @@ function parseCodexTokensEnvelope(raw) {
|
|
|
7398
7877
|
}
|
|
7399
7878
|
return parsed;
|
|
7400
7879
|
}
|
|
7401
|
-
function readExternalCliCredentials(provider, home = (0,
|
|
7880
|
+
function readExternalCliCredentials(provider, home = (0, import_node_os4.homedir)()) {
|
|
7402
7881
|
const path2 = externalStorePath(provider, home);
|
|
7403
7882
|
if (!(0, import_node_fs16.existsSync)(path2)) return null;
|
|
7404
7883
|
let raw;
|
|
@@ -7441,9 +7920,15 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7441
7920
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
7442
7921
|
* through {@link fetchUpstream} with the account's `{ providerId, accountId }`
|
|
7443
7922
|
* ctx so the per-account/provider proxy applies. `@internal` also a test seam.
|
|
7923
|
+
*
|
|
7924
|
+
* `redactBodies` is REQUIRED here: this round-trip sends the refresh_token and
|
|
7925
|
+
* receives a fresh access/refresh token pair. Carrying a `providerId` opts the
|
|
7926
|
+
* call into the upstream trace (so a failing refresh is diagnosable), and the
|
|
7927
|
+
* trace captures bodies verbatim — without this flag every refresh would write
|
|
7928
|
+
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
7444
7929
|
*/
|
|
7445
7930
|
buildRefreshFetch(providerId, accountId) {
|
|
7446
|
-
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId, accountId }));
|
|
7931
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
7447
7932
|
}
|
|
7448
7933
|
/**
|
|
7449
7934
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -7968,7 +8453,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7968
8453
|
* `enc:v1:`; already-`enc:`/`$ENV` untouched) before serializing, so any
|
|
7969
8454
|
* write incl. child 4's future refresh writes lands encrypted. */
|
|
7970
8455
|
persist(config) {
|
|
7971
|
-
(0, import_node_fs17.mkdirSync)((0,
|
|
8456
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path10.dirname)(this.tokensPath), { recursive: true });
|
|
7972
8457
|
const encrypted = encryptTokens(config, this.box);
|
|
7973
8458
|
(0, import_node_fs17.writeFileSync)(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
7974
8459
|
}
|
|
@@ -8003,6 +8488,124 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
8003
8488
|
// src/AccountHealthProbeScheduler.ts
|
|
8004
8489
|
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
8005
8490
|
|
|
8491
|
+
// src/probe/CodexGenerationProbe.ts
|
|
8492
|
+
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
8493
|
+
var CODEX_GENERATION_PROBE_MODEL = "gpt-5.6-luna";
|
|
8494
|
+
var CODEX_GENERATION_PROBE_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
8495
|
+
var MAX_STREAM_BYTES = 256 * 1024;
|
|
8496
|
+
var PROBE_INSTRUCTION = "Return exactly PONG and no other text.";
|
|
8497
|
+
function buildCodexGenerationProbeInit(token, signal) {
|
|
8498
|
+
return {
|
|
8499
|
+
method: "POST",
|
|
8500
|
+
signal,
|
|
8501
|
+
headers: {
|
|
8502
|
+
...import_codexCliHeaders.DEFAULT_CODEX_CLI_HEADERS,
|
|
8503
|
+
Authorization: `Bearer ${token}`,
|
|
8504
|
+
Accept: (0, import_codexCliHeaders.codexAcceptHeader)(true),
|
|
8505
|
+
"Content-Type": "application/json"
|
|
8506
|
+
},
|
|
8507
|
+
body: JSON.stringify({
|
|
8508
|
+
model: CODEX_GENERATION_PROBE_MODEL,
|
|
8509
|
+
input: [
|
|
8510
|
+
{
|
|
8511
|
+
role: "developer",
|
|
8512
|
+
content: [{ type: "input_text", text: PROBE_INSTRUCTION }]
|
|
8513
|
+
},
|
|
8514
|
+
{
|
|
8515
|
+
role: "user",
|
|
8516
|
+
content: [{ type: "input_text", text: "Connection probe." }]
|
|
8517
|
+
}
|
|
8518
|
+
],
|
|
8519
|
+
// GPT-5.6 otherwise defaults to medium reasoning. A connectivity probe
|
|
8520
|
+
// needs the lowest-cost path and no tool reasoning.
|
|
8521
|
+
reasoning: { effort: "none" },
|
|
8522
|
+
stream: true,
|
|
8523
|
+
store: false
|
|
8524
|
+
})
|
|
8525
|
+
};
|
|
8526
|
+
}
|
|
8527
|
+
async function readCodexGenerationProbeStream(response) {
|
|
8528
|
+
if (!response.body) return { completed: false, outputChars: 0 };
|
|
8529
|
+
const reader = response.body.getReader();
|
|
8530
|
+
const decoder = new TextDecoder();
|
|
8531
|
+
let buffer = "";
|
|
8532
|
+
let bytes = 0;
|
|
8533
|
+
let outputChars = 0;
|
|
8534
|
+
try {
|
|
8535
|
+
while (true) {
|
|
8536
|
+
const { done, value } = await reader.read();
|
|
8537
|
+
if (done) break;
|
|
8538
|
+
bytes += value.byteLength;
|
|
8539
|
+
if (bytes > MAX_STREAM_BYTES) {
|
|
8540
|
+
await reader.cancel();
|
|
8541
|
+
return { completed: false, outputChars };
|
|
8542
|
+
}
|
|
8543
|
+
buffer += decoder.decode(value, { stream: true });
|
|
8544
|
+
buffer = buffer.replace(/\r\n/g, "\n");
|
|
8545
|
+
let boundary = buffer.indexOf("\n\n");
|
|
8546
|
+
while (boundary >= 0) {
|
|
8547
|
+
const block = buffer.slice(0, boundary);
|
|
8548
|
+
buffer = buffer.slice(boundary + 2);
|
|
8549
|
+
const event = parseSseBlock(block);
|
|
8550
|
+
if (event) {
|
|
8551
|
+
const type = event["type"];
|
|
8552
|
+
if (type === "response.output_text.delta" && typeof event["delta"] === "string") {
|
|
8553
|
+
outputChars += event["delta"].length;
|
|
8554
|
+
} else if (type === "response.output_text.done" && typeof event["text"] === "string") {
|
|
8555
|
+
outputChars = Math.max(outputChars, event["text"].length);
|
|
8556
|
+
} else if (type === "response.failed" || type === "error") {
|
|
8557
|
+
await reader.cancel();
|
|
8558
|
+
return { completed: false, outputChars };
|
|
8559
|
+
} else if (type === "response.completed") {
|
|
8560
|
+
const completedResponse = asRecord(event["response"]);
|
|
8561
|
+
const status = completedResponse?.["status"];
|
|
8562
|
+
outputChars = Math.max(outputChars, countCompletedOutputChars(completedResponse));
|
|
8563
|
+
await reader.cancel();
|
|
8564
|
+
return {
|
|
8565
|
+
completed: (status === void 0 || status === "completed") && outputChars > 0,
|
|
8566
|
+
outputChars
|
|
8567
|
+
};
|
|
8568
|
+
}
|
|
8569
|
+
}
|
|
8570
|
+
boundary = buffer.indexOf("\n\n");
|
|
8571
|
+
}
|
|
8572
|
+
}
|
|
8573
|
+
} catch {
|
|
8574
|
+
return { completed: false, outputChars };
|
|
8575
|
+
} finally {
|
|
8576
|
+
reader.releaseLock();
|
|
8577
|
+
}
|
|
8578
|
+
return { completed: false, outputChars };
|
|
8579
|
+
}
|
|
8580
|
+
function parseSseBlock(block) {
|
|
8581
|
+
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
8582
|
+
if (!data || data === "[DONE]") return null;
|
|
8583
|
+
try {
|
|
8584
|
+
return JSON.parse(data);
|
|
8585
|
+
} catch {
|
|
8586
|
+
return null;
|
|
8587
|
+
}
|
|
8588
|
+
}
|
|
8589
|
+
function asRecord(value) {
|
|
8590
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
8591
|
+
}
|
|
8592
|
+
function countCompletedOutputChars(response) {
|
|
8593
|
+
const output = response?.["output"];
|
|
8594
|
+
if (!Array.isArray(output)) return 0;
|
|
8595
|
+
let chars = 0;
|
|
8596
|
+
for (const item of output) {
|
|
8597
|
+
const content = asRecord(item)?.["content"];
|
|
8598
|
+
if (!Array.isArray(content)) continue;
|
|
8599
|
+
for (const part of content) {
|
|
8600
|
+
const record = asRecord(part);
|
|
8601
|
+
if (record?.["type"] === "output_text" && typeof record["text"] === "string") {
|
|
8602
|
+
chars += record["text"].length;
|
|
8603
|
+
}
|
|
8604
|
+
}
|
|
8605
|
+
}
|
|
8606
|
+
return chars;
|
|
8607
|
+
}
|
|
8608
|
+
|
|
8006
8609
|
// src/probe/ProbeStrategy.ts
|
|
8007
8610
|
var PROVIDER_PROBE_PLANS = {
|
|
8008
8611
|
claude: {
|
|
@@ -8130,17 +8733,17 @@ var AccountHealthProbeScheduler = class {
|
|
|
8130
8733
|
}
|
|
8131
8734
|
if (readThrew) {
|
|
8132
8735
|
this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
|
|
8133
|
-
return { ok: false, marked: false };
|
|
8736
|
+
return { ok: false, marked: false, tier: "local" };
|
|
8134
8737
|
}
|
|
8135
8738
|
if (!token) {
|
|
8136
8739
|
this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
|
|
8137
8740
|
this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
|
|
8138
|
-
return { ok: false, marked: true };
|
|
8741
|
+
return { ok: false, marked: true, tier: "local" };
|
|
8139
8742
|
}
|
|
8140
8743
|
const plan = this.planFor(providerId);
|
|
8141
8744
|
if (plan.kind === "local") {
|
|
8142
8745
|
this.record(providerId, accountId, { ts: now, ok: true, tier: "local" });
|
|
8143
|
-
return { ok: true, marked: false };
|
|
8746
|
+
return { ok: true, marked: false, tier: "local" };
|
|
8144
8747
|
}
|
|
8145
8748
|
const start = this.now();
|
|
8146
8749
|
let status = null;
|
|
@@ -8165,7 +8768,60 @@ var AccountHealthProbeScheduler = class {
|
|
|
8165
8768
|
latencyMs,
|
|
8166
8769
|
tier: "upstream"
|
|
8167
8770
|
});
|
|
8168
|
-
return { ok: status !== null && status < 400, marked };
|
|
8771
|
+
return { ok: status !== null && status < 400, marked, tier: "upstream" };
|
|
8772
|
+
}
|
|
8773
|
+
/**
|
|
8774
|
+
* Manual connection test. Codex performs a real, quota-consuming generation;
|
|
8775
|
+
* every other provider keeps its existing cheap probe. Scheduled sweeps never
|
|
8776
|
+
* call this method, so they remain non-billable.
|
|
8777
|
+
*/
|
|
8778
|
+
async testAccountConnection(providerId, accountId) {
|
|
8779
|
+
if (providerId !== "codex") return this.probeAccount(providerId, accountId);
|
|
8780
|
+
const now = this.now();
|
|
8781
|
+
let token;
|
|
8782
|
+
try {
|
|
8783
|
+
token = await this.store.getAccessTokenForAccount(providerId, accountId);
|
|
8784
|
+
} catch {
|
|
8785
|
+
this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
|
|
8786
|
+
return { ok: false, marked: false, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
|
|
8787
|
+
}
|
|
8788
|
+
if (!token) {
|
|
8789
|
+
this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
|
|
8790
|
+
this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
|
|
8791
|
+
return { ok: false, marked: true, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
|
|
8792
|
+
}
|
|
8793
|
+
const startedAt = this.now();
|
|
8794
|
+
let attempt = await this.runCodexGenerationAttempt(accountId, token);
|
|
8795
|
+
if (attempt.status === 401 && this.store.refreshAccountToken) {
|
|
8796
|
+
try {
|
|
8797
|
+
if (await this.store.refreshAccountToken(providerId, accountId)) {
|
|
8798
|
+
const refreshed = await this.store.getAccessTokenForAccount(providerId, accountId);
|
|
8799
|
+
if (refreshed) attempt = await this.runCodexGenerationAttempt(accountId, refreshed);
|
|
8800
|
+
}
|
|
8801
|
+
} catch {
|
|
8802
|
+
}
|
|
8803
|
+
}
|
|
8804
|
+
const latencyMs = this.now() - startedAt;
|
|
8805
|
+
const ok = attempt.status !== null && attempt.status >= 200 && attempt.status < 300 && attempt.completed;
|
|
8806
|
+
let marked = false;
|
|
8807
|
+
if (ok) {
|
|
8808
|
+
this.health.clearTransientMark(providerId, accountId);
|
|
8809
|
+
} else if (attempt.status === 401 || attempt.status === 403) {
|
|
8810
|
+
marked = this.applyOutcome(providerId, accountId, attempt.status, attempt.bodyText, now);
|
|
8811
|
+
}
|
|
8812
|
+
this.record(providerId, accountId, {
|
|
8813
|
+
ts: now,
|
|
8814
|
+
ok,
|
|
8815
|
+
status: attempt.status,
|
|
8816
|
+
latencyMs,
|
|
8817
|
+
tier: "generation"
|
|
8818
|
+
});
|
|
8819
|
+
return {
|
|
8820
|
+
ok,
|
|
8821
|
+
marked,
|
|
8822
|
+
tier: "generation",
|
|
8823
|
+
model: CODEX_GENERATION_PROBE_MODEL
|
|
8824
|
+
};
|
|
8169
8825
|
}
|
|
8170
8826
|
/** Per-account rolling history for the authed admin surface (design D5). */
|
|
8171
8827
|
getAllHistory() {
|
|
@@ -8222,6 +8878,24 @@ var AccountHealthProbeScheduler = class {
|
|
|
8222
8878
|
return "";
|
|
8223
8879
|
}
|
|
8224
8880
|
}
|
|
8881
|
+
async runCodexGenerationAttempt(accountId, token) {
|
|
8882
|
+
try {
|
|
8883
|
+
const timeoutMs = Math.max(this.config.timeoutMs, 15e3);
|
|
8884
|
+
const response = await this.fetchImpl(
|
|
8885
|
+
CODEX_GENERATION_PROBE_URL,
|
|
8886
|
+
buildCodexGenerationProbeInit(token, AbortSignal.timeout(timeoutMs)),
|
|
8887
|
+
{ providerId: "codex", accountId, redactBodies: true }
|
|
8888
|
+
);
|
|
8889
|
+
if (response.status < 200 || response.status >= 300) {
|
|
8890
|
+
const bodyText = response.status === 403 ? await this.readBounded(response) : void 0;
|
|
8891
|
+
return { status: response.status, completed: false, bodyText };
|
|
8892
|
+
}
|
|
8893
|
+
const stream = await readCodexGenerationProbeStream(response);
|
|
8894
|
+
return { status: response.status, completed: stream.completed };
|
|
8895
|
+
} catch {
|
|
8896
|
+
return { status: null, completed: false };
|
|
8897
|
+
}
|
|
8898
|
+
}
|
|
8225
8899
|
key(providerId, accountId) {
|
|
8226
8900
|
return `${providerId}${KEY_SEP}${accountId}`;
|
|
8227
8901
|
}
|
|
@@ -8317,7 +8991,7 @@ var AccountHealthSweeper = class {
|
|
|
8317
8991
|
};
|
|
8318
8992
|
|
|
8319
8993
|
// src/audit/AuditPruneSweeper.ts
|
|
8320
|
-
var
|
|
8994
|
+
var import_node_fs19 = require("fs");
|
|
8321
8995
|
var import_node_path12 = require("path");
|
|
8322
8996
|
|
|
8323
8997
|
// src/audit/auditFiles.ts
|
|
@@ -8340,12 +9014,206 @@ function auditFileDateMs(fileName) {
|
|
|
8340
9014
|
return d.getTime();
|
|
8341
9015
|
}
|
|
8342
9016
|
|
|
9017
|
+
// src/audit/auditStats.ts
|
|
9018
|
+
var import_node_fs18 = require("fs");
|
|
9019
|
+
var import_node_path11 = require("path");
|
|
9020
|
+
var SIDECAR_VERSION = 1;
|
|
9021
|
+
var META_PREFIX_BYTES = 64 * 1024;
|
|
9022
|
+
var READ_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
9023
|
+
function auditStatsFileName(auditFile) {
|
|
9024
|
+
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
9025
|
+
}
|
|
9026
|
+
function readPersisted(path2) {
|
|
9027
|
+
if (!(0, import_node_fs18.existsSync)(path2)) return null;
|
|
9028
|
+
try {
|
|
9029
|
+
const value = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
|
|
9030
|
+
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)) {
|
|
9031
|
+
return null;
|
|
9032
|
+
}
|
|
9033
|
+
return value;
|
|
9034
|
+
} catch {
|
|
9035
|
+
return null;
|
|
9036
|
+
}
|
|
9037
|
+
}
|
|
9038
|
+
function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
|
|
9039
|
+
const statsPath = (0, import_node_path11.join)((0, import_node_path11.dirname)(auditPath), auditStatsFileName((0, import_node_path11.basename)(auditPath)));
|
|
9040
|
+
const previous = auditBytesBefore === 0 ? {
|
|
9041
|
+
version: SIDECAR_VERSION,
|
|
9042
|
+
auditBytes: 0,
|
|
9043
|
+
requestCount: 0,
|
|
9044
|
+
errorCount: 0,
|
|
9045
|
+
complete: true,
|
|
9046
|
+
minTs: null,
|
|
9047
|
+
maxTs: null
|
|
9048
|
+
} : readPersisted(statsPath);
|
|
9049
|
+
if (!previous || !previous.complete || previous.auditBytes !== auditBytesBefore) return;
|
|
9050
|
+
const next = {
|
|
9051
|
+
version: SIDECAR_VERSION,
|
|
9052
|
+
auditBytes: auditBytesAfter,
|
|
9053
|
+
requestCount: previous.requestCount + 1,
|
|
9054
|
+
errorCount: previous.errorCount + (record.status >= 400 || Boolean(record.error) ? 1 : 0),
|
|
9055
|
+
complete: true,
|
|
9056
|
+
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
9057
|
+
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
9058
|
+
};
|
|
9059
|
+
(0, import_node_fs18.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
|
|
9060
|
+
}
|
|
9061
|
+
function queryCovers(stats, from, to) {
|
|
9062
|
+
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
9063
|
+
}
|
|
9064
|
+
function fileOverlaps(file, from, to) {
|
|
9065
|
+
const start = auditFileDateMs(file);
|
|
9066
|
+
if (start === null) return false;
|
|
9067
|
+
const date = new Date(start);
|
|
9068
|
+
const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
|
|
9069
|
+
return end > from && start <= to;
|
|
9070
|
+
}
|
|
9071
|
+
function parseMetadataPrefix(prefix, prefixTruncated) {
|
|
9072
|
+
const text = prefix.toString("utf8");
|
|
9073
|
+
const tsMatch = /(?:^|,)"ts":(-?\d+)/.exec(text);
|
|
9074
|
+
const statusMatch = /(?:^|,)"status":(-?\d+)/.exec(text);
|
|
9075
|
+
const errorMatch = /(?:^|,)"error":"((?:\\.|[^"\\])*)"/.exec(text);
|
|
9076
|
+
const bodyStarted = /,(?:"requestBody"|"responseBody"):/.test(text);
|
|
9077
|
+
return {
|
|
9078
|
+
ts: tsMatch ? Number(tsMatch[1]) : void 0,
|
|
9079
|
+
status: statusMatch ? Number(statusMatch[1]) : void 0,
|
|
9080
|
+
hasError: Boolean(errorMatch?.[1]),
|
|
9081
|
+
complete: Boolean(tsMatch && statusMatch && (!prefixTruncated || bodyStarted))
|
|
9082
|
+
};
|
|
9083
|
+
}
|
|
9084
|
+
async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
|
|
9085
|
+
let requestCount = 0;
|
|
9086
|
+
let errorCount = 0;
|
|
9087
|
+
let filteredRequestCount = 0;
|
|
9088
|
+
let filteredErrorCount = 0;
|
|
9089
|
+
let minTs = null;
|
|
9090
|
+
let maxTs = null;
|
|
9091
|
+
let complete = true;
|
|
9092
|
+
let prefixParts = [];
|
|
9093
|
+
let prefixBytes = 0;
|
|
9094
|
+
let prefixTruncated = false;
|
|
9095
|
+
const consumeLine = () => {
|
|
9096
|
+
if (prefixBytes === 0 && !prefixTruncated) return;
|
|
9097
|
+
const prefix = Buffer.concat(prefixParts, prefixBytes);
|
|
9098
|
+
const metadata = parseMetadataPrefix(prefix, prefixTruncated);
|
|
9099
|
+
if (!metadata.complete || metadata.ts === void 0 || metadata.status === void 0) {
|
|
9100
|
+
complete = false;
|
|
9101
|
+
} else {
|
|
9102
|
+
requestCount += 1;
|
|
9103
|
+
const isError = metadata.status >= 400 || metadata.hasError;
|
|
9104
|
+
if (isError) errorCount += 1;
|
|
9105
|
+
minTs = minTs === null ? metadata.ts : Math.min(minTs, metadata.ts);
|
|
9106
|
+
maxTs = maxTs === null ? metadata.ts : Math.max(maxTs, metadata.ts);
|
|
9107
|
+
if (metadata.ts >= from && metadata.ts <= to) {
|
|
9108
|
+
filteredRequestCount += 1;
|
|
9109
|
+
if (isError) filteredErrorCount += 1;
|
|
9110
|
+
}
|
|
9111
|
+
}
|
|
9112
|
+
prefixParts = [];
|
|
9113
|
+
prefixBytes = 0;
|
|
9114
|
+
prefixTruncated = false;
|
|
9115
|
+
};
|
|
9116
|
+
if (auditBytes > startByte) {
|
|
9117
|
+
const stream = (0, import_node_fs18.createReadStream)(auditPath, {
|
|
9118
|
+
start: startByte,
|
|
9119
|
+
end: auditBytes - 1,
|
|
9120
|
+
highWaterMark: READ_CHUNK_BYTES
|
|
9121
|
+
});
|
|
9122
|
+
for await (const value of stream) {
|
|
9123
|
+
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
9124
|
+
let offset = 0;
|
|
9125
|
+
while (offset < chunk.length) {
|
|
9126
|
+
const newline = chunk.indexOf(10, offset);
|
|
9127
|
+
const end = newline === -1 ? chunk.length : newline;
|
|
9128
|
+
if (prefixBytes < META_PREFIX_BYTES) {
|
|
9129
|
+
const retained = Math.min(META_PREFIX_BYTES - prefixBytes, end - offset);
|
|
9130
|
+
if (retained > 0) {
|
|
9131
|
+
prefixParts.push(Buffer.from(chunk.subarray(offset, offset + retained)));
|
|
9132
|
+
prefixBytes += retained;
|
|
9133
|
+
}
|
|
9134
|
+
if (retained < end - offset) prefixTruncated = true;
|
|
9135
|
+
} else if (end > offset) {
|
|
9136
|
+
prefixTruncated = true;
|
|
9137
|
+
}
|
|
9138
|
+
if (newline === -1) break;
|
|
9139
|
+
consumeLine();
|
|
9140
|
+
offset = newline + 1;
|
|
9141
|
+
}
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
if (prefixBytes > 0 || prefixTruncated) complete = false;
|
|
9145
|
+
return {
|
|
9146
|
+
all: {
|
|
9147
|
+
version: SIDECAR_VERSION,
|
|
9148
|
+
auditBytes,
|
|
9149
|
+
requestCount,
|
|
9150
|
+
errorCount,
|
|
9151
|
+
complete,
|
|
9152
|
+
minTs,
|
|
9153
|
+
maxTs
|
|
9154
|
+
},
|
|
9155
|
+
filtered: { requestCount: filteredRequestCount, errorCount: filteredErrorCount, complete }
|
|
9156
|
+
};
|
|
9157
|
+
}
|
|
9158
|
+
function mergePersistedStats(previous, appended) {
|
|
9159
|
+
return {
|
|
9160
|
+
version: SIDECAR_VERSION,
|
|
9161
|
+
auditBytes: appended.auditBytes,
|
|
9162
|
+
requestCount: previous.requestCount + appended.requestCount,
|
|
9163
|
+
errorCount: previous.errorCount + appended.errorCount,
|
|
9164
|
+
complete: previous.complete && appended.complete,
|
|
9165
|
+
minTs: previous.minTs === null ? appended.minTs : appended.minTs === null ? previous.minTs : Math.min(previous.minTs, appended.minTs),
|
|
9166
|
+
maxTs: previous.maxTs === null ? appended.maxTs : appended.maxTs === null ? previous.maxTs : Math.max(previous.maxTs, appended.maxTs)
|
|
9167
|
+
};
|
|
9168
|
+
}
|
|
9169
|
+
async function readAuditStats(auditDir, query2 = {}) {
|
|
9170
|
+
if (!(0, import_node_fs18.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
9171
|
+
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
9172
|
+
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
9173
|
+
let files;
|
|
9174
|
+
try {
|
|
9175
|
+
files = (0, import_node_fs18.readdirSync)(auditDir).filter((file) => AUDIT_FILE_RE.test(file) && fileOverlaps(file, from, to)).sort();
|
|
9176
|
+
} catch {
|
|
9177
|
+
return { requestCount: 0, errorCount: 0, complete: false };
|
|
9178
|
+
}
|
|
9179
|
+
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
9180
|
+
for (const file of files) {
|
|
9181
|
+
const auditPath = (0, import_node_path11.join)(auditDir, file);
|
|
9182
|
+
try {
|
|
9183
|
+
const auditBytes = (0, import_node_fs18.statSync)(auditPath).size;
|
|
9184
|
+
const statsPath = (0, import_node_path11.join)(auditDir, auditStatsFileName(file));
|
|
9185
|
+
const persisted = readPersisted(statsPath);
|
|
9186
|
+
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
9187
|
+
total.requestCount += persisted.requestCount;
|
|
9188
|
+
total.errorCount += persisted.errorCount;
|
|
9189
|
+
continue;
|
|
9190
|
+
}
|
|
9191
|
+
const resumable = persisted && persisted.complete && persisted.auditBytes < auditBytes && queryCovers(persisted, from, to) ? persisted : null;
|
|
9192
|
+
const scanned = await scanAuditFile(
|
|
9193
|
+
auditPath,
|
|
9194
|
+
resumable?.auditBytes ?? 0,
|
|
9195
|
+
auditBytes,
|
|
9196
|
+
from,
|
|
9197
|
+
to
|
|
9198
|
+
);
|
|
9199
|
+
total.requestCount += scanned.filtered.requestCount + (resumable?.requestCount ?? 0);
|
|
9200
|
+
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
9201
|
+
total.complete = total.complete && scanned.filtered.complete;
|
|
9202
|
+
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
9203
|
+
if (current.complete) (0, import_node_fs18.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
|
|
9204
|
+
} catch {
|
|
9205
|
+
total.complete = false;
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
return total;
|
|
9209
|
+
}
|
|
9210
|
+
|
|
8343
9211
|
// src/audit/AuditPruneSweeper.ts
|
|
8344
9212
|
var DAY_MS = 24 * 60 * 6e4;
|
|
8345
9213
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
8346
9214
|
var AuditPruneSweeper = class {
|
|
8347
|
-
constructor(
|
|
8348
|
-
this.auditDir =
|
|
9215
|
+
constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
|
|
9216
|
+
this.auditDir = auditDir;
|
|
8349
9217
|
this.logger = logger;
|
|
8350
9218
|
this.config = config;
|
|
8351
9219
|
this.intervalMs = intervalMs;
|
|
@@ -8392,17 +9260,19 @@ var AuditPruneSweeper = class {
|
|
|
8392
9260
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
8393
9261
|
this.sweeping = true;
|
|
8394
9262
|
try {
|
|
8395
|
-
if (!(0,
|
|
9263
|
+
if (!(0, import_node_fs19.existsSync)(this.auditDir)) return 0;
|
|
8396
9264
|
const today = new Date(this.now());
|
|
8397
9265
|
const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
|
|
8398
9266
|
const cutoff = todayMidnight - (this.config.retentionDays - 1) * DAY_MS;
|
|
8399
9267
|
let removed = 0;
|
|
8400
|
-
for (const file of (0,
|
|
9268
|
+
for (const file of (0, import_node_fs19.readdirSync)(this.auditDir)) {
|
|
8401
9269
|
const dateMs = auditFileDateMs(file);
|
|
8402
9270
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
8403
9271
|
try {
|
|
8404
|
-
(0,
|
|
9272
|
+
(0, import_node_fs19.unlinkSync)((0, import_node_path12.join)(this.auditDir, file));
|
|
8405
9273
|
removed += 1;
|
|
9274
|
+
const statsPath = (0, import_node_path12.join)(this.auditDir, auditStatsFileName(file));
|
|
9275
|
+
if ((0, import_node_fs19.existsSync)(statsPath)) (0, import_node_fs19.unlinkSync)(statsPath);
|
|
8406
9276
|
} catch (error) {
|
|
8407
9277
|
this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
|
|
8408
9278
|
file,
|
|
@@ -8424,15 +9294,15 @@ var AuditPruneSweeper = class {
|
|
|
8424
9294
|
};
|
|
8425
9295
|
|
|
8426
9296
|
// src/audit/auditReader.ts
|
|
8427
|
-
var
|
|
9297
|
+
var import_node_fs20 = require("fs");
|
|
8428
9298
|
var import_node_path13 = require("path");
|
|
8429
9299
|
var DEFAULT_LIMIT = 200;
|
|
8430
9300
|
var MAX_LIMIT = 2e3;
|
|
8431
|
-
function readAuditRecords(
|
|
8432
|
-
if (!(0,
|
|
9301
|
+
function readAuditRecords(auditDir, query2 = {}) {
|
|
9302
|
+
if (!(0, import_node_fs20.existsSync)(auditDir)) return [];
|
|
8433
9303
|
let files;
|
|
8434
9304
|
try {
|
|
8435
|
-
files = (0,
|
|
9305
|
+
files = (0, import_node_fs20.readdirSync)(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
|
|
8436
9306
|
} catch {
|
|
8437
9307
|
return [];
|
|
8438
9308
|
}
|
|
@@ -8443,7 +9313,7 @@ function readAuditRecords(auditDir2, query2 = {}) {
|
|
|
8443
9313
|
for (const file of files.sort().reverse()) {
|
|
8444
9314
|
let raw;
|
|
8445
9315
|
try {
|
|
8446
|
-
raw = (0,
|
|
9316
|
+
raw = (0, import_node_fs20.readFileSync)((0, import_node_path13.join)(auditDir, file), "utf8");
|
|
8447
9317
|
} catch {
|
|
8448
9318
|
continue;
|
|
8449
9319
|
}
|
|
@@ -8472,11 +9342,11 @@ function isAuditRecord(value) {
|
|
|
8472
9342
|
}
|
|
8473
9343
|
|
|
8474
9344
|
// src/audit/AuditWriter.ts
|
|
8475
|
-
var
|
|
9345
|
+
var import_node_fs21 = require("fs");
|
|
8476
9346
|
var import_node_path14 = require("path");
|
|
8477
9347
|
var AuditWriter = class {
|
|
8478
|
-
constructor(
|
|
8479
|
-
this.auditDir =
|
|
9348
|
+
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
9349
|
+
this.auditDir = auditDir;
|
|
8480
9350
|
this.logger = logger;
|
|
8481
9351
|
this.defer = defer;
|
|
8482
9352
|
}
|
|
@@ -8506,16 +9376,30 @@ var AuditWriter = class {
|
|
|
8506
9376
|
*/
|
|
8507
9377
|
appendNow(record) {
|
|
8508
9378
|
if (!this.dirEnsured) {
|
|
8509
|
-
(0,
|
|
9379
|
+
(0, import_node_fs21.mkdirSync)(this.auditDir, { recursive: true });
|
|
8510
9380
|
this.dirEnsured = true;
|
|
8511
9381
|
}
|
|
8512
9382
|
const file = (0, import_node_path14.join)(this.auditDir, auditFileName(record.ts));
|
|
8513
|
-
|
|
9383
|
+
const line = JSON.stringify(record) + "\n";
|
|
9384
|
+
const auditBytesBefore = (0, import_node_fs21.existsSync)(file) ? (0, import_node_fs21.statSync)(file).size : 0;
|
|
9385
|
+
(0, import_node_fs21.appendFileSync)(file, line, "utf8");
|
|
9386
|
+
try {
|
|
9387
|
+
updateAuditStatsAfterAppend(
|
|
9388
|
+
file,
|
|
9389
|
+
auditBytesBefore,
|
|
9390
|
+
auditBytesBefore + Buffer.byteLength(line, "utf8"),
|
|
9391
|
+
record
|
|
9392
|
+
);
|
|
9393
|
+
} catch (error) {
|
|
9394
|
+
this.logger.warn("[AuditWriter] failed to update audit stats", {
|
|
9395
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9396
|
+
});
|
|
9397
|
+
}
|
|
8514
9398
|
}
|
|
8515
9399
|
};
|
|
8516
9400
|
|
|
8517
9401
|
// src/billing/BillingPublisher.ts
|
|
8518
|
-
var
|
|
9402
|
+
var import_node_fs22 = require("fs");
|
|
8519
9403
|
var import_node_crypto13 = require("crypto");
|
|
8520
9404
|
var import_node_path15 = require("path");
|
|
8521
9405
|
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
@@ -8589,7 +9473,7 @@ var BillingPublisher = class {
|
|
|
8589
9473
|
appendNow(event) {
|
|
8590
9474
|
this.ensureDir();
|
|
8591
9475
|
const file = (0, import_node_path15.join)(this.billingDir, billingFileName(event.ts));
|
|
8592
|
-
(0,
|
|
9476
|
+
(0, import_node_fs22.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
|
|
8593
9477
|
}
|
|
8594
9478
|
/**
|
|
8595
9479
|
* One best-effort delivery attempt for an event ALREADY in the ledger. POSTs the
|
|
@@ -8639,7 +9523,7 @@ var BillingPublisher = class {
|
|
|
8639
9523
|
try {
|
|
8640
9524
|
this.ensureDir();
|
|
8641
9525
|
const file = (0, import_node_path15.join)(this.billingDir, deliveredFileName(event.ts));
|
|
8642
|
-
(0,
|
|
9526
|
+
(0, import_node_fs22.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
|
|
8643
9527
|
} catch (error) {
|
|
8644
9528
|
this.logger.warn("[BillingPublisher] failed to append delivery marker", {
|
|
8645
9529
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -8648,20 +9532,20 @@ var BillingPublisher = class {
|
|
|
8648
9532
|
}
|
|
8649
9533
|
ensureDir() {
|
|
8650
9534
|
if (this.dirEnsured) return;
|
|
8651
|
-
(0,
|
|
9535
|
+
(0, import_node_fs22.mkdirSync)(this.billingDir, { recursive: true });
|
|
8652
9536
|
this.dirEnsured = true;
|
|
8653
9537
|
}
|
|
8654
9538
|
};
|
|
8655
9539
|
|
|
8656
9540
|
// src/billing/billingReader.ts
|
|
8657
|
-
var
|
|
9541
|
+
var import_node_fs23 = require("fs");
|
|
8658
9542
|
var import_node_path16 = require("path");
|
|
8659
9543
|
function readBillingLedger(billingDir) {
|
|
8660
9544
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
8661
|
-
if (!(0,
|
|
9545
|
+
if (!(0, import_node_fs23.existsSync)(billingDir)) return view;
|
|
8662
9546
|
let files;
|
|
8663
9547
|
try {
|
|
8664
|
-
files = (0,
|
|
9548
|
+
files = (0, import_node_fs23.readdirSync)(billingDir);
|
|
8665
9549
|
} catch {
|
|
8666
9550
|
return view;
|
|
8667
9551
|
}
|
|
@@ -8692,7 +9576,7 @@ function readBillingStatus(billingDir) {
|
|
|
8692
9576
|
function parseLines(dir, file) {
|
|
8693
9577
|
let raw;
|
|
8694
9578
|
try {
|
|
8695
|
-
raw = (0,
|
|
9579
|
+
raw = (0, import_node_fs23.readFileSync)((0, import_node_path16.join)(dir, file), "utf8");
|
|
8696
9580
|
} catch {
|
|
8697
9581
|
return [];
|
|
8698
9582
|
}
|
|
@@ -8878,6 +9762,76 @@ var TokenRefreshScheduler = class {
|
|
|
8878
9762
|
}
|
|
8879
9763
|
};
|
|
8880
9764
|
|
|
9765
|
+
// src/routeLeaseSubscriptionPreflight.ts
|
|
9766
|
+
var import_provider_proxy3 = require("@omnicross/core/provider-proxy");
|
|
9767
|
+
var import_AccountAllowanceScheduling4 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
9768
|
+
var import_SubscriptionAccountHealth3 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
9769
|
+
var import_accountModelMap = require("@omnicross/subscriptions/scheduler/accountModelMap");
|
|
9770
|
+
var PROVIDERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "opencodego"]);
|
|
9771
|
+
function accountArray(config, providerId) {
|
|
9772
|
+
const record = config;
|
|
9773
|
+
const key = `${providerId}Accounts`;
|
|
9774
|
+
const accounts = record[key];
|
|
9775
|
+
if (Array.isArray(accounts)) return accounts;
|
|
9776
|
+
const legacy = record[providerId];
|
|
9777
|
+
if (!legacy || typeof legacy !== "object") return [];
|
|
9778
|
+
const activeKey = `active${providerId[0].toUpperCase()}${providerId.slice(1)}AccountId`;
|
|
9779
|
+
return [{ id: String(record[activeKey] ?? "active"), enabled: true, tokens: legacy }];
|
|
9780
|
+
}
|
|
9781
|
+
function hasCredential(providerId, account) {
|
|
9782
|
+
const tokens = account.tokens;
|
|
9783
|
+
if (providerId === "opencodego") return typeof tokens.apiKey === "string" && tokens.apiKey.length > 0;
|
|
9784
|
+
return typeof tokens.accessToken === "string" && tokens.accessToken.length > 0;
|
|
9785
|
+
}
|
|
9786
|
+
function safeProviderId(value) {
|
|
9787
|
+
if (!PROVIDERS.has(value)) {
|
|
9788
|
+
throw new import_provider_proxy3.RouteLeaseError("upstream_not_found", "subscription provider was not found");
|
|
9789
|
+
}
|
|
9790
|
+
return value;
|
|
9791
|
+
}
|
|
9792
|
+
function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
9793
|
+
return {
|
|
9794
|
+
async assertAvailable(upstream, model) {
|
|
9795
|
+
const providerId = safeProviderId(upstream.providerId);
|
|
9796
|
+
const config = await credentials.getFullConfig();
|
|
9797
|
+
const all = accountArray(config, providerId);
|
|
9798
|
+
if (all.length === 0) {
|
|
9799
|
+
throw new import_provider_proxy3.RouteLeaseError("upstream_unavailable", "subscription provider has no configured account");
|
|
9800
|
+
}
|
|
9801
|
+
let bounded = all;
|
|
9802
|
+
if (upstream.kind === "account") {
|
|
9803
|
+
bounded = all.filter((account) => account.id === upstream.accountId);
|
|
9804
|
+
} else if (upstream.kind === "account-group") {
|
|
9805
|
+
bounded = all.filter((account) => account.group?.trim() === upstream.group);
|
|
9806
|
+
}
|
|
9807
|
+
if (bounded.length === 0) {
|
|
9808
|
+
throw new import_provider_proxy3.RouteLeaseError("upstream_not_found", "the selected subscription resource was not found");
|
|
9809
|
+
}
|
|
9810
|
+
const modelEligible = bounded.filter(
|
|
9811
|
+
(account) => (0, import_accountModelMap.accountSupportsModel)(account.supportedModels, model)
|
|
9812
|
+
);
|
|
9813
|
+
if (modelEligible.length === 0) {
|
|
9814
|
+
throw new import_provider_proxy3.RouteLeaseError("model_not_configured", "model is not supported by the selected subscription resource");
|
|
9815
|
+
}
|
|
9816
|
+
const credentialEligible = modelEligible.filter(
|
|
9817
|
+
(account) => account.enabled !== false && hasCredential(providerId, account)
|
|
9818
|
+
);
|
|
9819
|
+
const health2 = (0, import_SubscriptionAccountHealth3.getSharedAccountHealth)();
|
|
9820
|
+
const allowance = (0, import_AccountAllowanceScheduling4.getSharedAccountAllowanceScheduling)();
|
|
9821
|
+
const candidates = credentialEligible.filter(
|
|
9822
|
+
(account) => health2.isSchedulable(providerId, account.id) && allowance.preview(providerId, account.id, account.priority ?? 50).schedulable
|
|
9823
|
+
);
|
|
9824
|
+
if (candidates.length > 0) return;
|
|
9825
|
+
if (upstream.kind === "account") {
|
|
9826
|
+
throw new import_provider_proxy3.RouteLeaseError("upstream_unavailable", "the selected subscription account is unavailable");
|
|
9827
|
+
}
|
|
9828
|
+
throw new import_provider_proxy3.RouteLeaseError("upstream_exhausted", "the selected subscription pool has no eligible account", {
|
|
9829
|
+
retryAfterSeconds: 30
|
|
9830
|
+
});
|
|
9831
|
+
}
|
|
9832
|
+
};
|
|
9833
|
+
}
|
|
9834
|
+
|
|
8881
9835
|
// src/webhook/WebhookDispatcher.ts
|
|
8882
9836
|
var import_node_crypto14 = require("crypto");
|
|
8883
9837
|
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
@@ -9060,11 +10014,11 @@ function buildDaemon(config, paths) {
|
|
|
9060
10014
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
9061
10015
|
);
|
|
9062
10016
|
(0, import_AccountAllowanceStore4.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
9063
|
-
(0,
|
|
10017
|
+
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
9064
10018
|
(0, import_outbound_api5.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
9065
10019
|
);
|
|
9066
10020
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
9067
|
-
const keyDb = new JsonOutboundKeyDb(paths.keysPath);
|
|
10021
|
+
const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
|
|
9068
10022
|
const voucherDb = new JsonVoucherDb(defaultVouchersPath(paths.configPath));
|
|
9069
10023
|
const settingsStore = new JsonApiServerSettingsStore(paths.configPath, secretBox3);
|
|
9070
10024
|
const integrationStateStore = new IntegrationStateStore(
|
|
@@ -9127,11 +10081,22 @@ function buildDaemon(config, paths) {
|
|
|
9127
10081
|
const usageRecorder = new import_usage.UsageRecorder(usageEventStore, pricingEngine, logger, {
|
|
9128
10082
|
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
|
|
9129
10083
|
});
|
|
9130
|
-
const providerProxy = (0,
|
|
10084
|
+
const providerProxy = (0, import_provider_proxy4.getProviderProxy)({ llmConfig, apiKeyPool, usageRecorder });
|
|
10085
|
+
const routeLeaseManager = new import_provider_proxy4.RouteLeaseManager(
|
|
10086
|
+
providerProxy,
|
|
10087
|
+
new import_provider_proxy4.RouteLeaseTargetResolver(llmConfig, {
|
|
10088
|
+
providerKeys: apiKeyPool,
|
|
10089
|
+
subscriptions: createRouteLeaseSubscriptionPreflight(credentialStore)
|
|
10090
|
+
}),
|
|
10091
|
+
import_cli_launcher2.routeLeaseDescriptorPort,
|
|
10092
|
+
{ logger }
|
|
10093
|
+
);
|
|
10094
|
+
providerProxy.registerBeforeStop(() => routeLeaseManager.shutdown());
|
|
10095
|
+
providerProxy.registerBeforeStop(() => resetCliSessions());
|
|
9131
10096
|
llmConfig.setReloadHook(() => apiKeyPool.invalidateCache());
|
|
9132
10097
|
const accountHealthProbeScheduler = new AccountHealthProbeScheduler(
|
|
9133
10098
|
credentialStore,
|
|
9134
|
-
(0,
|
|
10099
|
+
(0, import_SubscriptionAccountHealth4.getSharedAccountHealth)(),
|
|
9135
10100
|
logger,
|
|
9136
10101
|
import_outbound_api5.DEFAULT_ACCOUNT_PROBE
|
|
9137
10102
|
);
|
|
@@ -9165,7 +10130,7 @@ function buildDaemon(config, paths) {
|
|
|
9165
10130
|
// lines through the injected logger (honors level/format/file sink).
|
|
9166
10131
|
logger
|
|
9167
10132
|
});
|
|
9168
|
-
const
|
|
10133
|
+
const auditDir = defaultAuditDir(paths.configPath);
|
|
9169
10134
|
const billingDir = defaultBillingDir(paths.configPath);
|
|
9170
10135
|
const adminServer = new AdminServer({
|
|
9171
10136
|
configPath: paths.configPath,
|
|
@@ -9178,6 +10143,7 @@ function buildDaemon(config, paths) {
|
|
|
9178
10143
|
keySpendReader: keySpendTracker,
|
|
9179
10144
|
settingsStore,
|
|
9180
10145
|
outboundApiServer,
|
|
10146
|
+
routeLeaseManager,
|
|
9181
10147
|
subscriptionAccounts,
|
|
9182
10148
|
accountAllowanceService,
|
|
9183
10149
|
allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
|
|
@@ -9199,10 +10165,16 @@ function buildDaemon(config, paths) {
|
|
|
9199
10165
|
// (NOT widening the least-authority writer — no token-returning read reachable).
|
|
9200
10166
|
oauthSessions: new OAuthSessionStore(),
|
|
9201
10167
|
// Real global fetch by default; a test seam (`paths.oauthExchangeFetch`) can
|
|
9202
|
-
// inject a mock so no real token endpoint is hit
|
|
9203
|
-
//
|
|
9204
|
-
//
|
|
9205
|
-
|
|
10168
|
+
// inject a mock so no real token endpoint is hit (one FetchLike for every
|
|
10169
|
+
// provider — the ctx below only matters on the real egress path).
|
|
10170
|
+
//
|
|
10171
|
+
// upstream-proxy: a PER-PROVIDER factory, so the exchange carries the same
|
|
10172
|
+
// `{ providerId }` ctx the CLI login and the token refresh already pass.
|
|
10173
|
+
// Without it the interactive login resolved only the global/env proxy layers
|
|
10174
|
+
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
10175
|
+
// excluded from the upstream trace, so a failing login left no evidence.
|
|
10176
|
+
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
10177
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
9206
10178
|
subscriptionAccountAppender: credentialStore,
|
|
9207
10179
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
9208
10180
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -9254,7 +10226,8 @@ function buildDaemon(config, paths) {
|
|
|
9254
10226
|
// date-rotated audit store. Bound to the store dir here so the AdminServer
|
|
9255
10227
|
// carries no path/store coupling. Records hold IP/UA/bodies → admin-only,
|
|
9256
10228
|
// NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
|
|
9257
|
-
auditReader: (query2) => readAuditRecords(
|
|
10229
|
+
auditReader: (query2) => readAuditRecords(auditDir, query2),
|
|
10230
|
+
auditStatsReader: (query2) => readAuditStats(auditDir, query2),
|
|
9258
10231
|
// billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
|
|
9259
10232
|
// secret-free total/delivered/pending counts of the durable ledger.
|
|
9260
10233
|
billingStatusReader: () => readBillingStatus(billingDir)
|
|
@@ -9263,10 +10236,10 @@ function buildDaemon(config, paths) {
|
|
|
9263
10236
|
logger,
|
|
9264
10237
|
fetchImpl: (url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init)
|
|
9265
10238
|
});
|
|
9266
|
-
setWebhookRuntime(webhookDispatcher, (0,
|
|
9267
|
-
const auditWriter = new AuditWriter(
|
|
9268
|
-
const auditPruneSweeper = new AuditPruneSweeper(
|
|
9269
|
-
setAuditRuntime(auditWriter, auditPruneSweeper
|
|
10239
|
+
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
10240
|
+
const auditWriter = new AuditWriter(auditDir, logger);
|
|
10241
|
+
const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, import_audit_types.DEFAULT_AUDIT_CONFIG);
|
|
10242
|
+
setAuditRuntime(auditWriter, auditPruneSweeper);
|
|
9270
10243
|
const billingPublisher = new BillingPublisher(billingDir, logger);
|
|
9271
10244
|
const billingRetrySweeper = new BillingRetrySweeper(
|
|
9272
10245
|
billingDir,
|
|
@@ -9278,7 +10251,7 @@ function buildDaemon(config, paths) {
|
|
|
9278
10251
|
const tokenRefreshScheduler = new TokenRefreshScheduler(credentialStore, logger);
|
|
9279
10252
|
const accountHealthSweeper = new AccountHealthSweeper(
|
|
9280
10253
|
credentialStore,
|
|
9281
|
-
(0,
|
|
10254
|
+
(0, import_SubscriptionAccountHealth4.getSharedAccountHealth)(),
|
|
9282
10255
|
logger
|
|
9283
10256
|
);
|
|
9284
10257
|
return {
|
|
@@ -9287,6 +10260,7 @@ function buildDaemon(config, paths) {
|
|
|
9287
10260
|
keyDb,
|
|
9288
10261
|
settingsStore,
|
|
9289
10262
|
providerProxy,
|
|
10263
|
+
routeLeaseManager,
|
|
9290
10264
|
outboundApiServer,
|
|
9291
10265
|
apiKeyPool,
|
|
9292
10266
|
autoDisableStore,
|
|
@@ -9312,8 +10286,8 @@ function buildDaemon(config, paths) {
|
|
|
9312
10286
|
}
|
|
9313
10287
|
function isTokensStoreReadable(tokensPath) {
|
|
9314
10288
|
try {
|
|
9315
|
-
if (!(0,
|
|
9316
|
-
(0,
|
|
10289
|
+
if (!(0, import_node_fs24.existsSync)(tokensPath)) return true;
|
|
10290
|
+
(0, import_node_fs24.accessSync)(tokensPath, import_node_fs24.constants.R_OK);
|
|
9317
10291
|
return true;
|
|
9318
10292
|
} catch {
|
|
9319
10293
|
return false;
|
|
@@ -9361,7 +10335,7 @@ function resolveInPathDefault(candidate) {
|
|
|
9361
10335
|
const segments = (process.env["PATH"] ?? "").split(import_node_path17.delimiter).filter(Boolean);
|
|
9362
10336
|
for (const seg of segments) {
|
|
9363
10337
|
const full = (0, import_node_path17.join)(seg, candidate);
|
|
9364
|
-
if ((0,
|
|
10338
|
+
if ((0, import_node_fs25.existsSync)(full)) return full;
|
|
9365
10339
|
}
|
|
9366
10340
|
return null;
|
|
9367
10341
|
}
|
|
@@ -9402,32 +10376,17 @@ async function runLaunch(argv, deps) {
|
|
|
9402
10376
|
await daemon.llmConfig.ready();
|
|
9403
10377
|
await daemon.providerProxy.start();
|
|
9404
10378
|
} catch (err5) {
|
|
9405
|
-
daemon
|
|
9406
|
-
daemon.tokenRefreshScheduler.dispose();
|
|
9407
|
-
daemon.claudeAllowanceRefreshScheduler.dispose();
|
|
9408
|
-
daemon.accountHealthSweeper.dispose();
|
|
9409
|
-
daemon.accountHealthProbeScheduler.dispose();
|
|
9410
|
-
daemon.auditPruneSweeper.dispose();
|
|
9411
|
-
daemon.billingRetrySweeper.dispose();
|
|
9412
|
-
daemon.pricingRefreshScheduler.dispose();
|
|
10379
|
+
await shutdownLaunchDaemon(daemon);
|
|
9413
10380
|
throw err5;
|
|
9414
10381
|
}
|
|
9415
10382
|
let launch;
|
|
9416
10383
|
try {
|
|
9417
|
-
launch = await buildLaunchConfig(cli, daemon
|
|
10384
|
+
launch = await buildLaunchConfig(cli, daemon, {
|
|
9418
10385
|
providerId: values.provider,
|
|
9419
10386
|
model: values.model
|
|
9420
10387
|
});
|
|
9421
10388
|
} catch (err5) {
|
|
9422
|
-
await daemon
|
|
9423
|
-
daemon.apiKeyPool.dispose();
|
|
9424
|
-
daemon.tokenRefreshScheduler.dispose();
|
|
9425
|
-
daemon.claudeAllowanceRefreshScheduler.dispose();
|
|
9426
|
-
daemon.accountHealthSweeper.dispose();
|
|
9427
|
-
daemon.accountHealthProbeScheduler.dispose();
|
|
9428
|
-
daemon.auditPruneSweeper.dispose();
|
|
9429
|
-
daemon.billingRetrySweeper.dispose();
|
|
9430
|
-
daemon.pricingRefreshScheduler.dispose();
|
|
10389
|
+
await shutdownLaunchDaemon(daemon);
|
|
9431
10390
|
throw err5;
|
|
9432
10391
|
}
|
|
9433
10392
|
try {
|
|
@@ -9446,21 +10405,40 @@ async function runLaunch(argv, deps) {
|
|
|
9446
10405
|
cwd: values.cwd
|
|
9447
10406
|
});
|
|
9448
10407
|
} finally {
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
|
|
10408
|
+
try {
|
|
10409
|
+
launch.onSessionEnd();
|
|
10410
|
+
} finally {
|
|
10411
|
+
await shutdownLaunchDaemon(daemon);
|
|
10412
|
+
}
|
|
10413
|
+
}
|
|
10414
|
+
}
|
|
10415
|
+
async function buildLaunchConfig(cli, daemon, opts) {
|
|
10416
|
+
if (cli === "claude" || cli === "codex") {
|
|
10417
|
+
const internalId = (0, import_node_crypto15.randomUUID)();
|
|
10418
|
+
const outcome = await daemon.routeLeaseManager.createFromRequest({
|
|
10419
|
+
schemaVersion: import_provider_proxy5.ROUTE_LEASE_REQUEST_SCHEMA,
|
|
10420
|
+
consumer: "omnicross-terminal",
|
|
10421
|
+
runtime: cli,
|
|
10422
|
+
upstream: { kind: "provider", providerId: opts.providerId },
|
|
10423
|
+
model: opts.model,
|
|
10424
|
+
execution: { sessionId: `launch:${cli}:${internalId}` }
|
|
10425
|
+
}, `omnicross-launch:${internalId}`);
|
|
10426
|
+
const stopRenewal = startTerminalLeaseRenewal(
|
|
10427
|
+
daemon.routeLeaseManager,
|
|
10428
|
+
outcome.result.leaseId
|
|
10429
|
+
);
|
|
10430
|
+
return {
|
|
10431
|
+
baseUrl: daemon.providerProxy.getBaseUrl(),
|
|
10432
|
+
env: outcome.result.launch.env,
|
|
10433
|
+
extraArgs: outcome.result.launch.extraArgs,
|
|
10434
|
+
onSessionEnd: () => {
|
|
10435
|
+
stopRenewal();
|
|
10436
|
+
daemon.routeLeaseManager.release(outcome.result.leaseId);
|
|
10437
|
+
}
|
|
10438
|
+
};
|
|
10439
|
+
}
|
|
9462
10440
|
const common = {
|
|
9463
|
-
llmConfig,
|
|
10441
|
+
llmConfig: daemon.llmConfig,
|
|
9464
10442
|
providerId: opts.providerId,
|
|
9465
10443
|
model: opts.model,
|
|
9466
10444
|
// Stable, bounded session id — pool failover (poolseam) fires on launch
|
|
@@ -9468,22 +10446,30 @@ async function buildLaunchConfig(cli, llmConfig, opts) {
|
|
|
9468
10446
|
sessionId: `launch:${cli}`
|
|
9469
10447
|
};
|
|
9470
10448
|
switch (cli) {
|
|
9471
|
-
case "claude":
|
|
9472
|
-
return (0, import_cli_launcher2.buildClaudeCliLaunchConfig)(common);
|
|
9473
|
-
case "codex":
|
|
9474
|
-
return (0, import_cli_launcher2.buildCodexLaunchConfig)(common);
|
|
9475
10449
|
case "gemini":
|
|
9476
|
-
return (0,
|
|
10450
|
+
return (0, import_cli_launcher3.buildGeminiCliLaunchConfig)(common);
|
|
9477
10451
|
case "qwen":
|
|
9478
10452
|
case "copilot":
|
|
9479
10453
|
case "opencode":
|
|
9480
|
-
return (0,
|
|
10454
|
+
return (0, import_cli_launcher3.buildChatCliLaunchConfig)({ backendId: cli, ...common });
|
|
9481
10455
|
default: {
|
|
9482
10456
|
const _exhaustive = cli;
|
|
9483
10457
|
throw new Error(`Unsupported launch CLI: ${String(_exhaustive)}`);
|
|
9484
10458
|
}
|
|
9485
10459
|
}
|
|
9486
10460
|
}
|
|
10461
|
+
async function shutdownLaunchDaemon(daemon) {
|
|
10462
|
+
daemon.routeLeaseManager.shutdown();
|
|
10463
|
+
await daemon.providerProxy.stop();
|
|
10464
|
+
daemon.apiKeyPool.dispose();
|
|
10465
|
+
daemon.tokenRefreshScheduler.dispose();
|
|
10466
|
+
daemon.claudeAllowanceRefreshScheduler.dispose();
|
|
10467
|
+
daemon.accountHealthSweeper.dispose();
|
|
10468
|
+
daemon.accountHealthProbeScheduler.dispose();
|
|
10469
|
+
daemon.auditPruneSweeper.dispose();
|
|
10470
|
+
daemon.billingRetrySweeper.dispose();
|
|
10471
|
+
daemon.pricingRefreshScheduler.dispose();
|
|
10472
|
+
}
|
|
9487
10473
|
function spawnCliInherit(plan) {
|
|
9488
10474
|
return new Promise((resolve3, reject) => {
|
|
9489
10475
|
const child = (0, import_node_child_process2.spawn)(plan.command, plan.args, {
|
|
@@ -9530,7 +10516,7 @@ var import_node_readline = require("readline");
|
|
|
9530
10516
|
var import_node_util5 = require("util");
|
|
9531
10517
|
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
9532
10518
|
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
9533
|
-
var
|
|
10519
|
+
var PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
9534
10520
|
async function runLogin(argv, deps) {
|
|
9535
10521
|
const { values, positionals } = (0, import_node_util5.parseArgs)({
|
|
9536
10522
|
args: argv,
|
|
@@ -9544,10 +10530,10 @@ async function runLogin(argv, deps) {
|
|
|
9544
10530
|
});
|
|
9545
10531
|
const provider = positionals[0];
|
|
9546
10532
|
if (!provider) {
|
|
9547
|
-
throw new Error(`login: a <provider> is required (one of ${
|
|
10533
|
+
throw new Error(`login: a <provider> is required (one of ${PROVIDERS2.join("|")})`);
|
|
9548
10534
|
}
|
|
9549
10535
|
if (!isLoginProvider(provider)) {
|
|
9550
|
-
throw new Error(`login: unknown provider '${provider}' (expected ${
|
|
10536
|
+
throw new Error(`login: unknown provider '${provider}' (expected ${PROVIDERS2.join("|")})`);
|
|
9551
10537
|
}
|
|
9552
10538
|
if (!values.config) {
|
|
9553
10539
|
throw new Error("login: --config <path> is required");
|
|
@@ -9563,7 +10549,7 @@ async function runLogin(argv, deps) {
|
|
|
9563
10549
|
(0, import_upstreamFetch9.setUpstreamProxyResolver)(createUpstreamProxyResolver());
|
|
9564
10550
|
try {
|
|
9565
10551
|
const tokensPath = defaultTokensPath(values.config);
|
|
9566
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init, { providerId: provider }));
|
|
10552
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init, { providerId: provider, redactBodies: true }));
|
|
9567
10553
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
9568
10554
|
const expiresAt = await runProviderLogin(
|
|
9569
10555
|
provider,
|
|
@@ -9653,7 +10639,7 @@ async function loginGemini(store, deps, exchangeFetch, label) {
|
|
|
9653
10639
|
return expiresAt;
|
|
9654
10640
|
}
|
|
9655
10641
|
function isLoginProvider(value) {
|
|
9656
|
-
return
|
|
10642
|
+
return PROVIDERS2.includes(value);
|
|
9657
10643
|
}
|
|
9658
10644
|
async function presentUrl(authUrl, deps) {
|
|
9659
10645
|
console.info("Open this URL in your browser to authorize:");
|
|
@@ -9699,7 +10685,7 @@ function promptPaste(prompt) {
|
|
|
9699
10685
|
}
|
|
9700
10686
|
|
|
9701
10687
|
// src/commands/providers.ts
|
|
9702
|
-
var
|
|
10688
|
+
var import_node_crypto16 = require("crypto");
|
|
9703
10689
|
var import_node_util6 = require("util");
|
|
9704
10690
|
async function runProviders(argv) {
|
|
9705
10691
|
const { values, positionals } = (0, import_node_util6.parseArgs)({
|
|
@@ -9821,7 +10807,7 @@ function providersAddKey(configPath, providerId, opts) {
|
|
|
9821
10807
|
const cfg = loadConfig(configPath);
|
|
9822
10808
|
const row = cfg.providers.find((p) => p.id === providerId);
|
|
9823
10809
|
if (!row) throw new Error(`providers add-key: unknown provider '${providerId}'`);
|
|
9824
|
-
const entry = { id: (0,
|
|
10810
|
+
const entry = { id: (0, import_node_crypto16.randomUUID)(), apiKey: opts.key };
|
|
9825
10811
|
if (opts.label) entry.label = opts.label;
|
|
9826
10812
|
if (opts.weight !== void 0) {
|
|
9827
10813
|
const w = Number(opts.weight);
|
|
@@ -9849,7 +10835,7 @@ function providersRmKey(configPath, providerId, keyId) {
|
|
|
9849
10835
|
}
|
|
9850
10836
|
|
|
9851
10837
|
// src/commands/secrets.ts
|
|
9852
|
-
var
|
|
10838
|
+
var import_node_fs26 = require("fs");
|
|
9853
10839
|
var import_node_util7 = require("util");
|
|
9854
10840
|
async function runSecrets(argv) {
|
|
9855
10841
|
const { values, positionals } = (0, import_node_util7.parseArgs)({
|
|
@@ -9922,12 +10908,12 @@ function secretsStatus(args) {
|
|
|
9922
10908
|
reportField("admin.token", cfg.admin.token);
|
|
9923
10909
|
}
|
|
9924
10910
|
const tokensPath = defaultTokensPath(args.config);
|
|
9925
|
-
if ((0,
|
|
10911
|
+
if ((0, import_node_fs26.existsSync)(tokensPath)) {
|
|
9926
10912
|
console.info(`Secret status for ${tokensPath}:`);
|
|
9927
10913
|
reportTokenFields(tokensPath);
|
|
9928
10914
|
}
|
|
9929
10915
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
9930
|
-
if ((0,
|
|
10916
|
+
if ((0, import_node_fs26.existsSync)(integrationsPath)) {
|
|
9931
10917
|
const state = readRawJson(integrationsPath);
|
|
9932
10918
|
const key = state.gatewayKey;
|
|
9933
10919
|
if (key && typeof key === "object" && !Array.isArray(key)) {
|
|
@@ -9981,8 +10967,8 @@ async function secretsRotate(args) {
|
|
|
9981
10967
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
9982
10968
|
try {
|
|
9983
10969
|
cfg = loadConfig(args.config);
|
|
9984
|
-
if ((0,
|
|
9985
|
-
if ((0,
|
|
10970
|
+
if ((0, import_node_fs26.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
|
|
10971
|
+
if ((0, import_node_fs26.existsSync)(integrationsPath)) {
|
|
9986
10972
|
integrationsPlain = new IntegrationStateStore(integrationsPath, oldBox).load();
|
|
9987
10973
|
}
|
|
9988
10974
|
} finally {
|
|
@@ -10017,20 +11003,20 @@ function secretsDecrypt(args) {
|
|
|
10017
11003
|
let tokensPlain = null;
|
|
10018
11004
|
try {
|
|
10019
11005
|
cfg = loadConfig(args.config);
|
|
10020
|
-
if ((0,
|
|
11006
|
+
if ((0, import_node_fs26.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
|
|
10021
11007
|
} finally {
|
|
10022
11008
|
setSecretBox(null);
|
|
10023
11009
|
}
|
|
10024
11010
|
saveConfig(args.config, cfg);
|
|
10025
11011
|
if (tokensPlain) {
|
|
10026
|
-
(0,
|
|
11012
|
+
(0, import_node_fs26.writeFileSync)(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
|
|
10027
11013
|
}
|
|
10028
11014
|
console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
|
|
10029
11015
|
}
|
|
10030
11016
|
function readRawConfig(path2) {
|
|
10031
11017
|
let parsed;
|
|
10032
11018
|
try {
|
|
10033
|
-
parsed = JSON.parse((0,
|
|
11019
|
+
parsed = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
|
|
10034
11020
|
} catch {
|
|
10035
11021
|
throw new Error(`secrets: cannot read or parse '${path2}'`);
|
|
10036
11022
|
}
|
|
@@ -10038,7 +11024,7 @@ function readRawConfig(path2) {
|
|
|
10038
11024
|
}
|
|
10039
11025
|
function readRawJson(path2) {
|
|
10040
11026
|
try {
|
|
10041
|
-
const parsed = JSON.parse((0,
|
|
11027
|
+
const parsed = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
|
|
10042
11028
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
10043
11029
|
return parsed;
|
|
10044
11030
|
}
|
|
@@ -10048,13 +11034,13 @@ function readRawJson(path2) {
|
|
|
10048
11034
|
}
|
|
10049
11035
|
function encryptTokensFileInPlace(configPath, box) {
|
|
10050
11036
|
const tokensPath = defaultTokensPath(configPath);
|
|
10051
|
-
if (!(0,
|
|
11037
|
+
if (!(0, import_node_fs26.existsSync)(tokensPath)) return;
|
|
10052
11038
|
const plain = decryptTokensFile(tokensPath, box);
|
|
10053
11039
|
writeTokensEncrypted(tokensPath, plain, box);
|
|
10054
11040
|
}
|
|
10055
11041
|
function rewriteIntegrationState(configPath, readBox, writeBox) {
|
|
10056
11042
|
const path2 = defaultIntegrationsPath(configPath);
|
|
10057
|
-
if (!(0,
|
|
11043
|
+
if (!(0, import_node_fs26.existsSync)(path2)) return;
|
|
10058
11044
|
const state = new IntegrationStateStore(path2, readBox).load();
|
|
10059
11045
|
new IntegrationStateStore(path2, writeBox).save(state);
|
|
10060
11046
|
}
|
|
@@ -10067,7 +11053,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
|
|
|
10067
11053
|
{ updatedAt: "", ...plain },
|
|
10068
11054
|
box
|
|
10069
11055
|
);
|
|
10070
|
-
(0,
|
|
11056
|
+
(0, import_node_fs26.writeFileSync)(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
10071
11057
|
}
|
|
10072
11058
|
var TOKEN_FIELDS2 = {
|
|
10073
11059
|
claude: ["accessToken", "refreshToken"],
|
|
@@ -10090,14 +11076,14 @@ function walkTokens(raw, fn) {
|
|
|
10090
11076
|
return next;
|
|
10091
11077
|
}
|
|
10092
11078
|
function tokensSuffix(configPath) {
|
|
10093
|
-
return (0,
|
|
11079
|
+
return (0, import_node_fs26.existsSync)(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
|
|
10094
11080
|
}
|
|
10095
11081
|
|
|
10096
11082
|
// src/commands/start.ts
|
|
10097
11083
|
var import_node_util8 = require("util");
|
|
10098
11084
|
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
10099
|
-
var
|
|
10100
|
-
var
|
|
11085
|
+
var import_SubscriptionAccountHealth5 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
11086
|
+
var import_AccountAllowanceScheduling6 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
10101
11087
|
|
|
10102
11088
|
// src/identity/identityRuntime.ts
|
|
10103
11089
|
var import_SubscriptionIdentityStore4 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
@@ -10157,11 +11143,11 @@ async function runStart(argv) {
|
|
|
10157
11143
|
await daemon.llmConfig.ready();
|
|
10158
11144
|
await daemon.providerProxy.start();
|
|
10159
11145
|
const serverConfig = await (0, import_outbound_api7.loadServerConfig)(daemon.settingsStore);
|
|
10160
|
-
(0,
|
|
11146
|
+
(0, import_SubscriptionAccountHealth5.getSharedAccountHealth)().configure({
|
|
10161
11147
|
overloadEnabled: serverConfig.accountHealth?.overloadCooldownEnabled,
|
|
10162
11148
|
overloadTtlMs: serverConfig.accountHealth?.overloadCooldownMs
|
|
10163
11149
|
});
|
|
10164
|
-
(0,
|
|
11150
|
+
(0, import_AccountAllowanceScheduling6.getSharedAccountAllowanceScheduling)().configure(serverConfig.allowanceScheduling);
|
|
10165
11151
|
daemon.claudeAllowanceRefreshScheduler.configure(serverConfig.allowanceScheduling);
|
|
10166
11152
|
await daemon.outboundApiServer.applyConfig({
|
|
10167
11153
|
enabled: true,
|