@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.js
CHANGED
|
@@ -1493,10 +1493,20 @@ function assertLoopbackGatewayUrl(value) {
|
|
|
1493
1493
|
// src/ports/JsonOutboundKeyDb.ts
|
|
1494
1494
|
import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
1495
1495
|
var JsonOutboundKeyDb = class {
|
|
1496
|
-
|
|
1496
|
+
/**
|
|
1497
|
+
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
1498
|
+
* key's plaintext is persisted as a `keySecret` `enc:` envelope (enabling the
|
|
1499
|
+
* operator "view key" affordance via `outboundApiKeysReveal`). When absent the
|
|
1500
|
+
* store stays hash-only (byte-identical to the legacy behavior) and reveal
|
|
1501
|
+
* always returns `null`. Existing 1-arg call sites (tests, lightweight
|
|
1502
|
+
* embedders) keep working.
|
|
1503
|
+
*/
|
|
1504
|
+
constructor(keysPath, secretBox3) {
|
|
1497
1505
|
this.keysPath = keysPath;
|
|
1506
|
+
this.secretBox = secretBox3;
|
|
1498
1507
|
}
|
|
1499
1508
|
keysPath;
|
|
1509
|
+
secretBox;
|
|
1500
1510
|
async outboundApiKeysList() {
|
|
1501
1511
|
return this.readRows();
|
|
1502
1512
|
}
|
|
@@ -1522,10 +1532,27 @@ var JsonOutboundKeyDb = class {
|
|
|
1522
1532
|
allowedEndpoints: input.allowedEndpoints,
|
|
1523
1533
|
loopbackOnly: input.loopbackOnly
|
|
1524
1534
|
};
|
|
1535
|
+
if (input.plaintext && this.secretBox) {
|
|
1536
|
+
row.keySecret = this.secretBox.encrypt(input.plaintext);
|
|
1537
|
+
}
|
|
1525
1538
|
rows.push(row);
|
|
1526
1539
|
this.writeRows(rows);
|
|
1527
1540
|
return row;
|
|
1528
1541
|
}
|
|
1542
|
+
async outboundApiKeysReveal(id) {
|
|
1543
|
+
const rows = this.readRows();
|
|
1544
|
+
const row = rows.find((r) => r.id === id);
|
|
1545
|
+
if (!row || !row.keySecret || !this.secretBox) return null;
|
|
1546
|
+
return this.secretBox.decrypt(row.keySecret);
|
|
1547
|
+
}
|
|
1548
|
+
async outboundApiKeysDelete(id) {
|
|
1549
|
+
const rows = this.readRows();
|
|
1550
|
+
const idx = rows.findIndex((r) => r.id === id);
|
|
1551
|
+
if (idx < 0) return false;
|
|
1552
|
+
rows.splice(idx, 1);
|
|
1553
|
+
this.writeRows(rows);
|
|
1554
|
+
return true;
|
|
1555
|
+
}
|
|
1529
1556
|
async outboundApiKeysRevoke(id) {
|
|
1530
1557
|
return this.mutateRow(id, (row) => {
|
|
1531
1558
|
if (row.revokedAt !== null) return false;
|
|
@@ -1723,18 +1750,18 @@ async function keysRevoke(db, id) {
|
|
|
1723
1750
|
|
|
1724
1751
|
// src/commands/launch.ts
|
|
1725
1752
|
import { spawn as spawn2 } from "child_process";
|
|
1726
|
-
import {
|
|
1753
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
1754
|
+
import { existsSync as existsSync20 } from "fs";
|
|
1727
1755
|
import { delimiter as delimiter2, join as join12 } from "path";
|
|
1728
1756
|
import { parseArgs as parseArgs4 } from "util";
|
|
1729
1757
|
import {
|
|
1730
1758
|
buildChatCliLaunchConfig as buildChatCliLaunchConfig2,
|
|
1731
|
-
buildClaudeCliLaunchConfig as buildClaudeCliLaunchConfig2,
|
|
1732
|
-
buildCodexLaunchConfig as buildCodexLaunchConfig2,
|
|
1733
1759
|
buildGeminiCliLaunchConfig as buildGeminiCliLaunchConfig2
|
|
1734
1760
|
} from "@omnicross/cli-launcher";
|
|
1761
|
+
import { ROUTE_LEASE_REQUEST_SCHEMA as ROUTE_LEASE_REQUEST_SCHEMA2 } from "@omnicross/core/provider-proxy";
|
|
1735
1762
|
|
|
1736
1763
|
// src/bootstrap.ts
|
|
1737
|
-
import { accessSync, constants as fsConstants, existsSync as
|
|
1764
|
+
import { accessSync, constants as fsConstants, existsSync as existsSync19 } from "fs";
|
|
1738
1765
|
import { DEFAULT_AUDIT_CONFIG } from "@omnicross/contracts/audit-types";
|
|
1739
1766
|
import { DEFAULT_BILLING_CONFIG } from "@omnicross/contracts/billing-types";
|
|
1740
1767
|
import { getGeminiCodeAssistProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
@@ -1747,7 +1774,7 @@ import {
|
|
|
1747
1774
|
normalizeServerConfig
|
|
1748
1775
|
} from "@omnicross/core/outbound-api";
|
|
1749
1776
|
import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api/subscriptionRegistryPort";
|
|
1750
|
-
import { getSharedAccountHealth as
|
|
1777
|
+
import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
1751
1778
|
import {
|
|
1752
1779
|
__resetSharedAccountAllowanceStoreForTests,
|
|
1753
1780
|
AccountAllowanceStore as AccountAllowanceStore3,
|
|
@@ -1755,15 +1782,18 @@ import {
|
|
|
1755
1782
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
1756
1783
|
import {
|
|
1757
1784
|
__resetSharedAccountAllowanceSchedulingForTests,
|
|
1758
|
-
getSharedAccountAllowanceScheduling as
|
|
1785
|
+
getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
|
|
1759
1786
|
} from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
1760
1787
|
import { fetchUpstream as fetchUpstream7, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
|
|
1761
1788
|
import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
|
|
1762
1789
|
import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
|
|
1763
1790
|
import {
|
|
1764
1791
|
__resetProviderProxyForTests,
|
|
1765
|
-
getProviderProxy
|
|
1792
|
+
getProviderProxy,
|
|
1793
|
+
RouteLeaseManager,
|
|
1794
|
+
RouteLeaseTargetResolver
|
|
1766
1795
|
} from "@omnicross/core/provider-proxy";
|
|
1796
|
+
import { routeLeaseDescriptorPort } from "@omnicross/cli-launcher";
|
|
1767
1797
|
import { KeySpendTracker } from "@omnicross/core/outbound-api";
|
|
1768
1798
|
import { PricingEngine, UsageRecorder } from "@omnicross/core/usage";
|
|
1769
1799
|
import {
|
|
@@ -1849,7 +1879,7 @@ async function runCodexLoopback(sessionId, codeVerifier, state, signal, deps) {
|
|
|
1849
1879
|
const code = await deps.codexAwaitLoopback(state, void 0, signal);
|
|
1850
1880
|
const result = await codexOAuth.exchangeCodeForTokens(
|
|
1851
1881
|
{ authorizationCode: code, codeVerifier, state },
|
|
1852
|
-
deps.oauthExchangeFetch
|
|
1882
|
+
deps.oauthExchangeFetch("codex")
|
|
1853
1883
|
);
|
|
1854
1884
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
1855
1885
|
const block = {
|
|
@@ -2369,6 +2399,17 @@ function handleAuditQuery(req, res, reader) {
|
|
|
2369
2399
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2370
2400
|
res.end(JSON.stringify({ records }));
|
|
2371
2401
|
}
|
|
2402
|
+
async function handleAuditStatsQuery(req, res, reader) {
|
|
2403
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
2404
|
+
const query2 = {};
|
|
2405
|
+
const from = intParam(url.searchParams.get("from"));
|
|
2406
|
+
if (from !== void 0) query2.from = from;
|
|
2407
|
+
const to = intParam(url.searchParams.get("to"));
|
|
2408
|
+
if (to !== void 0) query2.to = to;
|
|
2409
|
+
const stats = reader ? await reader(query2) : { requestCount: 0, errorCount: 0, complete: true };
|
|
2410
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2411
|
+
res.end(JSON.stringify(stats));
|
|
2412
|
+
}
|
|
2372
2413
|
|
|
2373
2414
|
// src/admin/billingStatusApi.ts
|
|
2374
2415
|
function handleBillingStatus(res, reader) {
|
|
@@ -2458,6 +2499,93 @@ async function handleWebhookTest(req, res) {
|
|
|
2458
2499
|
res.end(JSON.stringify({ result }));
|
|
2459
2500
|
}
|
|
2460
2501
|
|
|
2502
|
+
// src/admin/routeLeaseApi.ts
|
|
2503
|
+
import {
|
|
2504
|
+
isLoopbackAddress,
|
|
2505
|
+
normalizeRouteLeaseTtl,
|
|
2506
|
+
ROUTE_LEASE_CAPABILITIES,
|
|
2507
|
+
RouteLeaseError
|
|
2508
|
+
} from "@omnicross/core/provider-proxy";
|
|
2509
|
+
var MAX_BODY_BYTES = 64 * 1024;
|
|
2510
|
+
var SAFE_LEASE_ID = /^[A-Za-z0-9-]{1,128}$/u;
|
|
2511
|
+
async function readJson(req) {
|
|
2512
|
+
const chunks = [];
|
|
2513
|
+
let bytes = 0;
|
|
2514
|
+
for await (const chunk of req) {
|
|
2515
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2516
|
+
bytes += buffer.length;
|
|
2517
|
+
if (bytes > MAX_BODY_BYTES) throw new RouteLeaseError("invalid_request", "request body is too large");
|
|
2518
|
+
chunks.push(buffer);
|
|
2519
|
+
}
|
|
2520
|
+
if (chunks.length === 0) return {};
|
|
2521
|
+
try {
|
|
2522
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
2523
|
+
} catch {
|
|
2524
|
+
throw new RouteLeaseError("invalid_request", "request body is not valid JSON");
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
function json(res, status, body, noStore = false) {
|
|
2528
|
+
res.statusCode = status;
|
|
2529
|
+
res.setHeader("Content-Type", "application/json");
|
|
2530
|
+
if (noStore) res.setHeader("Cache-Control", "no-store");
|
|
2531
|
+
res.end(JSON.stringify(body));
|
|
2532
|
+
}
|
|
2533
|
+
function leaseId(value) {
|
|
2534
|
+
if (!value || !SAFE_LEASE_ID.test(value)) throw new RouteLeaseError("invalid_request", "lease id is invalid");
|
|
2535
|
+
return value;
|
|
2536
|
+
}
|
|
2537
|
+
function header(req, name) {
|
|
2538
|
+
const value = req.headers[name.toLowerCase()];
|
|
2539
|
+
return Array.isArray(value) ? value[0] : value;
|
|
2540
|
+
}
|
|
2541
|
+
function writeError(res, error, noStore) {
|
|
2542
|
+
const safe = error instanceof RouteLeaseError ? error : new RouteLeaseError("upstream_unavailable", "route lease operation failed safely");
|
|
2543
|
+
if (safe.retryAfterSeconds !== void 0) res.setHeader("Retry-After", String(safe.retryAfterSeconds));
|
|
2544
|
+
json(res, safe.status, safe.toResponse(), noStore);
|
|
2545
|
+
}
|
|
2546
|
+
async function handleRouteLeaseApi(req, res, path2, deps) {
|
|
2547
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
2548
|
+
const noStore = method === "POST" && (path2 === "/admin/api/route-leases" || path2.endsWith("/renew"));
|
|
2549
|
+
try {
|
|
2550
|
+
if (!isLoopbackAddress(req.socket.remoteAddress)) {
|
|
2551
|
+
throw new RouteLeaseError("control_unauthorized", "route lease control plane is loopback only");
|
|
2552
|
+
}
|
|
2553
|
+
const manager = deps.routeLeaseManager;
|
|
2554
|
+
if (!manager) throw new RouteLeaseError("daemon_not_ready", "route lease manager is unavailable");
|
|
2555
|
+
const base = "/admin/api/route-leases";
|
|
2556
|
+
const suffix = path2.slice(base.length).replace(/^\/+|\/+$/gu, "");
|
|
2557
|
+
const segments = suffix ? suffix.split("/") : [];
|
|
2558
|
+
if (segments.length === 1 && segments[0] === "capabilities") {
|
|
2559
|
+
if (method !== "GET" && method !== "HEAD") throw new RouteLeaseError("invalid_request", "method is not allowed");
|
|
2560
|
+
return json(res, 200, ROUTE_LEASE_CAPABILITIES);
|
|
2561
|
+
}
|
|
2562
|
+
if (segments.length === 0) {
|
|
2563
|
+
if (method === "GET") return json(res, 200, { leases: manager.list() });
|
|
2564
|
+
if (method === "POST") {
|
|
2565
|
+
const outcome = await manager.createFromRequest(await readJson(req), header(req, "idempotency-key"));
|
|
2566
|
+
return json(res, outcome.created ? 201 : 200, outcome.result, true);
|
|
2567
|
+
}
|
|
2568
|
+
throw new RouteLeaseError("invalid_request", "method is not allowed");
|
|
2569
|
+
}
|
|
2570
|
+
const id = leaseId(segments[0]);
|
|
2571
|
+
if (segments.length === 1) {
|
|
2572
|
+
if (method === "GET") return json(res, 200, manager.get(id));
|
|
2573
|
+
if (method === "DELETE") return json(res, 200, manager.release(id));
|
|
2574
|
+
throw new RouteLeaseError("invalid_request", "method is not allowed");
|
|
2575
|
+
}
|
|
2576
|
+
if (segments.length === 2 && segments[1] === "renew" && method === "POST") {
|
|
2577
|
+
const body = await readJson(req);
|
|
2578
|
+
const ttl = normalizeRouteLeaseTtl(
|
|
2579
|
+
body && typeof body === "object" && !Array.isArray(body) ? body.ttlSeconds : void 0
|
|
2580
|
+
);
|
|
2581
|
+
return json(res, 200, manager.renew(id, ttl), true);
|
|
2582
|
+
}
|
|
2583
|
+
throw new RouteLeaseError("lease_not_found", "route lease endpoint was not found");
|
|
2584
|
+
} catch (error) {
|
|
2585
|
+
writeError(res, error, noStore);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2461
2589
|
// src/admin/adminApi.ts
|
|
2462
2590
|
import http from "http";
|
|
2463
2591
|
import {
|
|
@@ -2559,7 +2687,13 @@ function listMappablePresets() {
|
|
|
2559
2687
|
name: preset.name,
|
|
2560
2688
|
apiFormat: resolved.format,
|
|
2561
2689
|
baseUrl: preset.api_base_url,
|
|
2562
|
-
models: Array.isArray(preset.models) ? preset.models : []
|
|
2690
|
+
models: Array.isArray(preset.models) ? preset.models : [],
|
|
2691
|
+
nameKey: preset.nameKey,
|
|
2692
|
+
icon: preset.icon,
|
|
2693
|
+
description: preset.description,
|
|
2694
|
+
features: preset.features,
|
|
2695
|
+
website: preset.website,
|
|
2696
|
+
modelsEndpoint: preset.modelsEndpoint
|
|
2563
2697
|
});
|
|
2564
2698
|
}
|
|
2565
2699
|
return { mappable, excluded };
|
|
@@ -2950,7 +3084,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
2950
3084
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
2951
3085
|
if (!sessionId) return err2(400, "oauth complete requires { sessionId }");
|
|
2952
3086
|
if (!rawCode) return err2(400, "oauth complete requires { code }");
|
|
2953
|
-
const session = deps.oauthSessions.
|
|
3087
|
+
const session = deps.oauthSessions.peek(sessionId);
|
|
2954
3088
|
if (!session) return err2(410, "oauth session is unknown, expired, or already used");
|
|
2955
3089
|
if (session.providerId !== providerId) {
|
|
2956
3090
|
return err2(400, `oauth session does not match provider '${providerId}'`);
|
|
@@ -2964,13 +3098,15 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
2964
3098
|
}
|
|
2965
3099
|
code = splitCode;
|
|
2966
3100
|
}
|
|
3101
|
+
const exchangeFetch = deps.oauthExchangeFetch(providerId);
|
|
2967
3102
|
let block;
|
|
2968
3103
|
try {
|
|
2969
|
-
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state,
|
|
3104
|
+
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
2970
3105
|
} catch (exchangeError) {
|
|
2971
3106
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
2972
3107
|
return err2(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
2973
3108
|
}
|
|
3109
|
+
deps.oauthSessions.consume(sessionId);
|
|
2974
3110
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
2975
3111
|
await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
|
|
2976
3112
|
const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
@@ -3008,7 +3144,9 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
|
3008
3144
|
// src/admin/cliLaunch.ts
|
|
3009
3145
|
import { exec, spawn } from "child_process";
|
|
3010
3146
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3011
|
-
import { existsSync as existsSync6 } from "fs";
|
|
3147
|
+
import { chmodSync as chmodSync3, existsSync as existsSync6, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
3148
|
+
import { createServer } from "net";
|
|
3149
|
+
import { tmpdir } from "os";
|
|
3012
3150
|
import { delimiter, join as join4 } from "path";
|
|
3013
3151
|
import {
|
|
3014
3152
|
buildChatCliLaunchConfig,
|
|
@@ -3016,6 +3154,33 @@ import {
|
|
|
3016
3154
|
buildCodexLaunchConfig,
|
|
3017
3155
|
buildGeminiCliLaunchConfig
|
|
3018
3156
|
} from "@omnicross/cli-launcher";
|
|
3157
|
+
import {
|
|
3158
|
+
ROUTE_LEASE_REQUEST_SCHEMA,
|
|
3159
|
+
RouteLeaseError as RouteLeaseError2
|
|
3160
|
+
} from "@omnicross/core/provider-proxy";
|
|
3161
|
+
|
|
3162
|
+
// src/routeLeaseRenewal.ts
|
|
3163
|
+
var TERMINAL_LEASE_TTL_SECONDS = 600;
|
|
3164
|
+
var TERMINAL_LEASE_RENEW_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3165
|
+
var TERMINAL_LEASE_MAX_LIFETIME_MS = 24 * 60 * 60 * 1e3;
|
|
3166
|
+
function startTerminalLeaseRenewal(manager, leaseId2) {
|
|
3167
|
+
const stopAt = Date.now() + TERMINAL_LEASE_MAX_LIFETIME_MS;
|
|
3168
|
+
const timer = setInterval(() => {
|
|
3169
|
+
if (Date.now() >= stopAt) {
|
|
3170
|
+
clearInterval(timer);
|
|
3171
|
+
return;
|
|
3172
|
+
}
|
|
3173
|
+
try {
|
|
3174
|
+
manager.renew(leaseId2, TERMINAL_LEASE_TTL_SECONDS);
|
|
3175
|
+
} catch {
|
|
3176
|
+
clearInterval(timer);
|
|
3177
|
+
}
|
|
3178
|
+
}, TERMINAL_LEASE_RENEW_INTERVAL_MS);
|
|
3179
|
+
timer.unref?.();
|
|
3180
|
+
return () => clearInterval(timer);
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3183
|
+
// src/admin/cliLaunch.ts
|
|
3019
3184
|
var LAUNCHABLE_CLIS = [
|
|
3020
3185
|
{ id: "claude", displayName: "Claude Code", command: "claude" },
|
|
3021
3186
|
{ id: "codex", displayName: "Codex CLI", command: "codex" },
|
|
@@ -3096,34 +3261,183 @@ async function buildLaunchEnv(cli, llmConfig, target) {
|
|
|
3096
3261
|
function shq(s) {
|
|
3097
3262
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
3098
3263
|
}
|
|
3099
|
-
var
|
|
3264
|
+
var MAC_TERMINAL_BOOTSTRAP_SOURCE = `
|
|
3265
|
+
'use strict';
|
|
3266
|
+
const fs = require('node:fs');
|
|
3267
|
+
const net = require('node:net');
|
|
3268
|
+
const { spawn } = require('node:child_process');
|
|
3269
|
+
const [socketPath, launchDir, cwd, command, ...args] = process.argv.slice(2);
|
|
3270
|
+
let payload = '';
|
|
3271
|
+
const socket = net.createConnection(socketPath);
|
|
3272
|
+
socket.setEncoding('utf8');
|
|
3273
|
+
socket.on('data', (chunk) => { payload += chunk; });
|
|
3274
|
+
socket.on('end', () => {
|
|
3275
|
+
const descriptor = JSON.parse(payload);
|
|
3276
|
+
if (!descriptor || Array.isArray(descriptor) || Object.values(descriptor).some((value) => typeof value !== 'string')) {
|
|
3277
|
+
throw new Error('invalid terminal launch descriptor');
|
|
3278
|
+
}
|
|
3279
|
+
try { fs.rmSync(launchDir, { recursive: true, force: true }); } catch {}
|
|
3280
|
+
const child = spawn(command, args, {
|
|
3281
|
+
cwd: cwd || undefined,
|
|
3282
|
+
env: { ...process.env, ...descriptor },
|
|
3283
|
+
stdio: 'inherit',
|
|
3284
|
+
});
|
|
3285
|
+
child.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
|
|
3286
|
+
child.on('exit', (code, signal) => {
|
|
3287
|
+
if (signal) process.kill(process.pid, signal);
|
|
3288
|
+
else process.exitCode = code == null ? 1 : code;
|
|
3289
|
+
});
|
|
3290
|
+
});
|
|
3291
|
+
socket.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
|
|
3292
|
+
`;
|
|
3293
|
+
var MAC_TERMINAL_IPC_TIMEOUT_MS = 12e4;
|
|
3294
|
+
function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }, spawnProcess = spawn, macIpc = {}) {
|
|
3100
3295
|
const childEnv = { ...process.env, ...env };
|
|
3101
3296
|
if (platform === "win32") {
|
|
3102
3297
|
const args = ["/c", "start", `"omnicross ${cli}"`];
|
|
3103
3298
|
if (cwd) args.push("/D", `"${cwd}"`);
|
|
3104
3299
|
args.push("cmd", "/k", command, ...extraArgs);
|
|
3105
|
-
|
|
3300
|
+
spawnProcess(process.env["ComSpec"] || "cmd.exe", args, {
|
|
3106
3301
|
env: childEnv,
|
|
3107
3302
|
windowsVerbatimArguments: true,
|
|
3108
3303
|
detached: true,
|
|
3109
3304
|
stdio: "ignore"
|
|
3110
3305
|
}).unref();
|
|
3111
|
-
return
|
|
3306
|
+
return () => {
|
|
3307
|
+
};
|
|
3112
3308
|
}
|
|
3113
|
-
const exportLine = Object.entries(env).map(([k, v]) => `export ${k}=${shq(v)}`).join("; ");
|
|
3114
3309
|
const runLine = [command, ...extraArgs].map(shq).join(" ");
|
|
3115
|
-
const script = `${
|
|
3310
|
+
const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
|
|
3116
3311
|
if (platform === "darwin") {
|
|
3117
|
-
const
|
|
3118
|
-
|
|
3119
|
-
|
|
3312
|
+
const launchDir = mkdtempSync(join4(tmpdir(), "omnicross-terminal-"));
|
|
3313
|
+
const commandFile = join4(launchDir, "launch.command");
|
|
3314
|
+
const bootstrapFile = join4(launchDir, "bootstrap.cjs");
|
|
3315
|
+
const socketPath = macIpc.socketPath ?? join4(launchDir, "descriptor.sock");
|
|
3316
|
+
const openerEnv = { ...process.env };
|
|
3317
|
+
for (const key of Object.keys(env)) delete openerEnv[key];
|
|
3318
|
+
let claimed = false;
|
|
3319
|
+
let cleaned = false;
|
|
3320
|
+
let failureNotified = false;
|
|
3321
|
+
let timer;
|
|
3322
|
+
const notifyFailure = () => {
|
|
3323
|
+
cleanup();
|
|
3324
|
+
if (failureNotified) return;
|
|
3325
|
+
failureNotified = true;
|
|
3326
|
+
try {
|
|
3327
|
+
onFailure?.();
|
|
3328
|
+
} catch {
|
|
3329
|
+
}
|
|
3330
|
+
};
|
|
3331
|
+
const handleLaunchFailure = () => {
|
|
3332
|
+
if (claimed) cleanup();
|
|
3333
|
+
else notifyFailure();
|
|
3334
|
+
};
|
|
3335
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
3336
|
+
const server = createServer((socket) => {
|
|
3337
|
+
socket.unref();
|
|
3338
|
+
sockets.add(socket);
|
|
3339
|
+
socket.once("close", () => sockets.delete(socket));
|
|
3340
|
+
try {
|
|
3341
|
+
macIpc.onAccepted?.(socket);
|
|
3342
|
+
} catch {
|
|
3343
|
+
cleanup();
|
|
3344
|
+
return;
|
|
3345
|
+
}
|
|
3346
|
+
if (claimed || cleaned) {
|
|
3347
|
+
socket.destroy();
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
claimed = true;
|
|
3351
|
+
try {
|
|
3352
|
+
macIpc.onClaimed?.();
|
|
3353
|
+
if (cleaned) return;
|
|
3354
|
+
socket.end(JSON.stringify(env), cleanup);
|
|
3355
|
+
} catch {
|
|
3356
|
+
cleanup();
|
|
3357
|
+
}
|
|
3358
|
+
});
|
|
3359
|
+
const cleanup = () => {
|
|
3360
|
+
if (!cleaned) {
|
|
3361
|
+
cleaned = true;
|
|
3362
|
+
if (timer) clearTimeout(timer);
|
|
3363
|
+
for (const socket of sockets) socket.destroy();
|
|
3364
|
+
sockets.clear();
|
|
3365
|
+
try {
|
|
3366
|
+
server.close();
|
|
3367
|
+
} catch {
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
try {
|
|
3371
|
+
if (macIpc.removeArtifacts) {
|
|
3372
|
+
macIpc.removeArtifacts(launchDir);
|
|
3373
|
+
} else {
|
|
3374
|
+
rmSync2(launchDir, {
|
|
3375
|
+
recursive: true,
|
|
3376
|
+
force: true,
|
|
3377
|
+
maxRetries: 3,
|
|
3378
|
+
retryDelay: 20
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
} catch {
|
|
3382
|
+
}
|
|
3383
|
+
};
|
|
3384
|
+
try {
|
|
3385
|
+
writeFileSync6(bootstrapFile, MAC_TERMINAL_BOOTSTRAP_SOURCE, { encoding: "utf8", mode: 448 });
|
|
3386
|
+
writeFileSync6(commandFile, `#!/bin/bash
|
|
3387
|
+
rm -f -- "$0"
|
|
3388
|
+
exec ${shq(process.execPath)} ${shq(bootstrapFile)} ${shq(socketPath)} ${shq(launchDir)} ${shq(cwd ?? "")} ${runLine}
|
|
3389
|
+
`, {
|
|
3390
|
+
encoding: "utf8",
|
|
3391
|
+
mode: 448
|
|
3392
|
+
});
|
|
3393
|
+
chmodSync3(commandFile, 448);
|
|
3394
|
+
chmodSync3(bootstrapFile, 448);
|
|
3395
|
+
server.once("error", handleLaunchFailure);
|
|
3396
|
+
server.listen(socketPath, () => {
|
|
3397
|
+
if (cleaned) return;
|
|
3398
|
+
try {
|
|
3399
|
+
macIpc.onListening?.();
|
|
3400
|
+
if (cleaned) return;
|
|
3401
|
+
if (process.platform !== "win32") chmodSync3(socketPath, 384);
|
|
3402
|
+
const opener = spawnProcess("open", ["-n", "-a", "Terminal", commandFile], {
|
|
3403
|
+
env: openerEnv,
|
|
3404
|
+
detached: true,
|
|
3405
|
+
stdio: "ignore"
|
|
3406
|
+
});
|
|
3407
|
+
opener.once("error", handleLaunchFailure);
|
|
3408
|
+
opener.unref();
|
|
3409
|
+
server.unref();
|
|
3410
|
+
} catch {
|
|
3411
|
+
handleLaunchFailure();
|
|
3412
|
+
}
|
|
3413
|
+
});
|
|
3414
|
+
timer = setTimeout(handleLaunchFailure, macIpc.timeoutMs ?? MAC_TERMINAL_IPC_TIMEOUT_MS);
|
|
3415
|
+
timer.unref?.();
|
|
3416
|
+
return cleanup;
|
|
3417
|
+
} catch (error) {
|
|
3418
|
+
cleanup();
|
|
3419
|
+
throw error;
|
|
3420
|
+
}
|
|
3120
3421
|
}
|
|
3121
|
-
|
|
3422
|
+
spawnProcess("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
|
|
3423
|
+
env: childEnv,
|
|
3122
3424
|
detached: true,
|
|
3123
3425
|
stdio: "ignore"
|
|
3124
3426
|
}).unref();
|
|
3125
|
-
|
|
3427
|
+
return () => {
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
var defaultTerminalOpener = (input) => openTerminal(input);
|
|
3126
3431
|
var sessions = /* @__PURE__ */ new Map();
|
|
3432
|
+
function resetCliSessions() {
|
|
3433
|
+
for (const s of sessions.values()) {
|
|
3434
|
+
try {
|
|
3435
|
+
s.onSessionEnd();
|
|
3436
|
+
} catch {
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
sessions.clear();
|
|
3440
|
+
}
|
|
3127
3441
|
function errBody(message) {
|
|
3128
3442
|
return { error: { type: "admin_api_error", message } };
|
|
3129
3443
|
}
|
|
@@ -3178,29 +3492,81 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
3178
3492
|
} catch (err5) {
|
|
3179
3493
|
return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "no launch target") };
|
|
3180
3494
|
}
|
|
3495
|
+
const id = randomUUID2();
|
|
3496
|
+
let leaseId2;
|
|
3181
3497
|
let launch;
|
|
3182
3498
|
try {
|
|
3183
|
-
|
|
3499
|
+
if ((cli === "claude" || cli === "codex") && ctx.routeLeaseManager) {
|
|
3500
|
+
const outcome = await ctx.routeLeaseManager.createFromRequest({
|
|
3501
|
+
schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA,
|
|
3502
|
+
consumer: "omnicross-terminal",
|
|
3503
|
+
runtime: cli,
|
|
3504
|
+
upstream: { kind: "provider", providerId: target.providerId },
|
|
3505
|
+
model: target.model,
|
|
3506
|
+
execution: { sessionId: id }
|
|
3507
|
+
}, `omnicross-terminal:${id}`);
|
|
3508
|
+
leaseId2 = outcome.result.leaseId;
|
|
3509
|
+
const stopRenewal = startTerminalLeaseRenewal(ctx.routeLeaseManager, leaseId2);
|
|
3510
|
+
launch = {
|
|
3511
|
+
env: outcome.result.launch.env,
|
|
3512
|
+
extraArgs: outcome.result.launch.extraArgs,
|
|
3513
|
+
onSessionEnd: () => {
|
|
3514
|
+
stopRenewal();
|
|
3515
|
+
ctx.routeLeaseManager?.release(outcome.result.leaseId);
|
|
3516
|
+
}
|
|
3517
|
+
};
|
|
3518
|
+
} else {
|
|
3519
|
+
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
3520
|
+
}
|
|
3184
3521
|
} catch (err5) {
|
|
3185
|
-
|
|
3522
|
+
const status = err5 instanceof RouteLeaseError2 ? err5.status : 400;
|
|
3523
|
+
return { status, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
|
|
3186
3524
|
}
|
|
3187
3525
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
3188
3526
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
3527
|
+
let openerCleanup;
|
|
3528
|
+
let ended = false;
|
|
3529
|
+
let published = false;
|
|
3530
|
+
const onSessionEnd = () => {
|
|
3531
|
+
if (ended) return;
|
|
3532
|
+
ended = true;
|
|
3533
|
+
if (published) sessions.delete(id);
|
|
3534
|
+
try {
|
|
3535
|
+
openerCleanup?.();
|
|
3536
|
+
} finally {
|
|
3537
|
+
launch.onSessionEnd();
|
|
3538
|
+
}
|
|
3539
|
+
};
|
|
3189
3540
|
try {
|
|
3190
|
-
opener({
|
|
3541
|
+
const cleanup = opener({
|
|
3542
|
+
cli,
|
|
3543
|
+
command: meta.command,
|
|
3544
|
+
extraArgs: launch.extraArgs ?? [],
|
|
3545
|
+
env: launch.env,
|
|
3546
|
+
cwd,
|
|
3547
|
+
platform,
|
|
3548
|
+
onFailure: onSessionEnd
|
|
3549
|
+
});
|
|
3550
|
+
if (cleanup) openerCleanup = cleanup;
|
|
3191
3551
|
} catch (err5) {
|
|
3192
|
-
|
|
3552
|
+
onSessionEnd();
|
|
3193
3553
|
return { status: 500, body: errBody(err5 instanceof Error ? err5.message : "failed to open terminal") };
|
|
3194
3554
|
}
|
|
3195
|
-
|
|
3555
|
+
if (ended) {
|
|
3556
|
+
openerCleanup?.();
|
|
3557
|
+
return { status: 500, body: errBody("failed to open terminal") };
|
|
3558
|
+
}
|
|
3196
3559
|
sessions.set(id, {
|
|
3197
3560
|
id,
|
|
3198
3561
|
cli,
|
|
3199
3562
|
providerId: target.providerId,
|
|
3200
3563
|
model: target.model,
|
|
3564
|
+
...leaseId2 ? { leaseId: leaseId2 } : {},
|
|
3201
3565
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3202
|
-
onSessionEnd
|
|
3566
|
+
onSessionEnd
|
|
3203
3567
|
});
|
|
3568
|
+
published = true;
|
|
3569
|
+
if (ended) sessions.delete(id);
|
|
3204
3570
|
return { status: 200, body: { sessionId: id, providerId: target.providerId, model: target.model } };
|
|
3205
3571
|
}
|
|
3206
3572
|
|
|
@@ -3220,8 +3586,8 @@ function validateAuditSegment(patch) {
|
|
|
3220
3586
|
}
|
|
3221
3587
|
}
|
|
3222
3588
|
const maxBodyBytes = audit["maxBodyBytes"];
|
|
3223
|
-
if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes <
|
|
3224
|
-
errors.push("audit.maxBodyBytes must be a non-negative number");
|
|
3589
|
+
if (maxBodyBytes !== void 0 && (typeof maxBodyBytes !== "number" || !Number.isFinite(maxBodyBytes) || maxBodyBytes < -1)) {
|
|
3590
|
+
errors.push("audit.maxBodyBytes must be -1 or a non-negative number");
|
|
3225
3591
|
}
|
|
3226
3592
|
const retentionDays = audit["retentionDays"];
|
|
3227
3593
|
if (retentionDays !== void 0 && (typeof retentionDays !== "number" || !Number.isFinite(retentionDays) || retentionDays < 0)) {
|
|
@@ -3666,18 +4032,16 @@ function preserveWebhookSecrets(incoming, current) {
|
|
|
3666
4032
|
}
|
|
3667
4033
|
|
|
3668
4034
|
// src/audit/auditRuntime.ts
|
|
3669
|
-
import { join as join5 } from "path";
|
|
3670
4035
|
import { setAuditCaptureConfig, setAuditSink } from "@omnicross/core/pipeline/auditSink";
|
|
3671
4036
|
import { setUpstreamTracePath } from "@omnicross/core/pipeline/upstreamTrace";
|
|
3672
4037
|
var writer = null;
|
|
3673
4038
|
var sweeper = null;
|
|
3674
|
-
|
|
3675
|
-
function setAuditRuntime(w, s, dir) {
|
|
4039
|
+
function setAuditRuntime(w, s) {
|
|
3676
4040
|
writer = w;
|
|
3677
4041
|
sweeper = s;
|
|
3678
|
-
auditDir = dir;
|
|
3679
4042
|
}
|
|
3680
4043
|
function applyAuditConfig(config) {
|
|
4044
|
+
setUpstreamTracePath(null);
|
|
3681
4045
|
const enabled = config?.enabled === true && writer !== null;
|
|
3682
4046
|
if (enabled && config) {
|
|
3683
4047
|
setAuditCaptureConfig(config);
|
|
@@ -3687,11 +4051,9 @@ function applyAuditConfig(config) {
|
|
|
3687
4051
|
sweeper.configure(config);
|
|
3688
4052
|
sweeper.start();
|
|
3689
4053
|
}
|
|
3690
|
-
setUpstreamTracePath(config.captureBodies ? join5(auditDir, "upstream-trace.jsonl") : null);
|
|
3691
4054
|
} else {
|
|
3692
4055
|
setAuditCaptureConfig(null);
|
|
3693
4056
|
setAuditSink(null);
|
|
3694
|
-
setUpstreamTracePath(null);
|
|
3695
4057
|
if (sweeper) {
|
|
3696
4058
|
if (config) sweeper.configure(config);
|
|
3697
4059
|
sweeper.dispose();
|
|
@@ -4091,7 +4453,7 @@ function sealPack(bundleJson, passphrase) {
|
|
|
4091
4453
|
cipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
|
|
4092
4454
|
const ciphertext = Buffer.concat([cipher.update(bundleJson, "utf8"), cipher.final()]);
|
|
4093
4455
|
const tag = cipher.getAuthTag();
|
|
4094
|
-
const
|
|
4456
|
+
const header2 = {
|
|
4095
4457
|
magic: PACK_MAGIC,
|
|
4096
4458
|
v: PACK_VERSION,
|
|
4097
4459
|
kdf: KDF_ALGORITHM,
|
|
@@ -4102,7 +4464,7 @@ function sealPack(bundleJson, passphrase) {
|
|
|
4102
4464
|
iv: iv.toString("base64"),
|
|
4103
4465
|
tag: tag.toString("base64")
|
|
4104
4466
|
};
|
|
4105
|
-
return `${PACK_PREFIX}${toB64Url(JSON.stringify(
|
|
4467
|
+
return `${PACK_PREFIX}${toB64Url(JSON.stringify(header2))}.${ciphertext.toString("base64")}`;
|
|
4106
4468
|
}
|
|
4107
4469
|
function parsePack(packString) {
|
|
4108
4470
|
if (typeof packString !== "string" || !packString.startsWith(PACK_PREFIX)) {
|
|
@@ -4113,28 +4475,28 @@ function parsePack(packString) {
|
|
|
4113
4475
|
if (dot < 0) throw new PackAuthError("migration pack is malformed (missing body)");
|
|
4114
4476
|
const headerB64Url = rest.slice(0, dot);
|
|
4115
4477
|
const ctB64 = rest.slice(dot + 1);
|
|
4116
|
-
let
|
|
4478
|
+
let header2;
|
|
4117
4479
|
try {
|
|
4118
|
-
|
|
4480
|
+
header2 = JSON.parse(fromB64Url(headerB64Url));
|
|
4119
4481
|
} catch {
|
|
4120
4482
|
throw new PackAuthError("migration pack is malformed (unreadable header)");
|
|
4121
4483
|
}
|
|
4122
|
-
if (!
|
|
4484
|
+
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") {
|
|
4123
4485
|
throw new PackAuthError("migration pack is malformed (unsupported header)");
|
|
4124
4486
|
}
|
|
4125
4487
|
const ciphertext = Buffer.from(ctB64, "base64");
|
|
4126
|
-
return { header, ciphertext };
|
|
4488
|
+
return { header: header2, ciphertext };
|
|
4127
4489
|
}
|
|
4128
4490
|
function openPack(packString, passphrase) {
|
|
4129
4491
|
assertPassphraseStrength(passphrase);
|
|
4130
|
-
const { header, ciphertext } = parsePack(packString);
|
|
4131
|
-
const salt = Buffer.from(
|
|
4132
|
-
const iv = Buffer.from(
|
|
4133
|
-
const tag = Buffer.from(
|
|
4492
|
+
const { header: header2, ciphertext } = parsePack(packString);
|
|
4493
|
+
const salt = Buffer.from(header2.salt, "base64");
|
|
4494
|
+
const iv = Buffer.from(header2.iv, "base64");
|
|
4495
|
+
const tag = Buffer.from(header2.tag, "base64");
|
|
4134
4496
|
if (iv.length !== IV_BYTES2 || tag.length !== TAG_BYTES2) {
|
|
4135
4497
|
throw new PackAuthError("migration pack is malformed (invalid iv/tag length)");
|
|
4136
4498
|
}
|
|
4137
|
-
const key = deriveKey(passphrase, salt,
|
|
4499
|
+
const key = deriveKey(passphrase, salt, header2.N, header2.r, header2.p);
|
|
4138
4500
|
const decipher = createDecipheriv2("aes-256-gcm", key, iv);
|
|
4139
4501
|
decipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
|
|
4140
4502
|
decipher.setAuthTag(tag);
|
|
@@ -4470,10 +4832,10 @@ function writeJson2(res, status, body) {
|
|
|
4470
4832
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
4471
4833
|
res.end(JSON.stringify(body));
|
|
4472
4834
|
}
|
|
4473
|
-
function
|
|
4835
|
+
function writeError2(res, status, message) {
|
|
4474
4836
|
writeJson2(res, status, { error: { type: "account_allowance_error", message } });
|
|
4475
4837
|
}
|
|
4476
|
-
function
|
|
4838
|
+
function readJson2(req) {
|
|
4477
4839
|
return new Promise((resolve3, reject) => {
|
|
4478
4840
|
const chunks = [];
|
|
4479
4841
|
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
@@ -4499,10 +4861,10 @@ function allowanceProvider(value) {
|
|
|
4499
4861
|
return value === "claude" || value === "codex" ? value : null;
|
|
4500
4862
|
}
|
|
4501
4863
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
4502
|
-
if (!service) return
|
|
4864
|
+
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
4503
4865
|
if (method === "GET" && rest.length === 1 && rest[0] === "scheduling") {
|
|
4504
4866
|
if (!service.getSchedulingStatus) {
|
|
4505
|
-
return
|
|
4867
|
+
return writeError2(res, 501, "allowance scheduling diagnostics are not available");
|
|
4506
4868
|
}
|
|
4507
4869
|
return writeJson2(res, 200, { scheduling: service.getSchedulingStatus() });
|
|
4508
4870
|
}
|
|
@@ -4510,30 +4872,35 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4510
4872
|
const params = query(req);
|
|
4511
4873
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
4512
4874
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
4513
|
-
if (providerId === null) return
|
|
4875
|
+
if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
|
|
4514
4876
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
4515
4877
|
const allowances = await service.list({ providerId, accountId });
|
|
4516
4878
|
return writeJson2(res, 200, { allowances });
|
|
4517
4879
|
}
|
|
4518
4880
|
if (method === "POST" && rest[0] === "refresh") {
|
|
4519
|
-
const body = await
|
|
4881
|
+
const body = await readJson2(req);
|
|
4520
4882
|
const requestedProvider = allowanceProvider(
|
|
4521
4883
|
typeof body["providerId"] === "string" ? body["providerId"] : "claude"
|
|
4522
4884
|
);
|
|
4523
4885
|
if (requestedProvider !== "claude") {
|
|
4524
|
-
return
|
|
4886
|
+
return writeError2(res, 400, "only Claude allowances support explicit refresh");
|
|
4525
4887
|
}
|
|
4526
4888
|
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
4527
4889
|
const allowances = await service.refreshClaude(accountId);
|
|
4528
4890
|
if (accountId && allowances.length === 0) {
|
|
4529
|
-
return
|
|
4891
|
+
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
4530
4892
|
}
|
|
4531
4893
|
return writeJson2(res, 200, { allowances });
|
|
4532
4894
|
}
|
|
4533
|
-
return
|
|
4895
|
+
return writeError2(res, 405, `method ${method} not allowed on account allowances`);
|
|
4534
4896
|
}
|
|
4535
4897
|
|
|
4536
4898
|
// src/admin/adminApi.ts
|
|
4899
|
+
import {
|
|
4900
|
+
ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
4901
|
+
getSharedAccountRouteActivity
|
|
4902
|
+
} from "@omnicross/core/pipeline/AccountRouteActivity";
|
|
4903
|
+
import { getSharedOverloadCounter } from "@omnicross/core/pipeline/ServerOverloadCounter";
|
|
4537
4904
|
function readBody(req) {
|
|
4538
4905
|
return new Promise((resolve3, reject) => {
|
|
4539
4906
|
const chunks = [];
|
|
@@ -4570,6 +4937,9 @@ function toKeyInfo(row) {
|
|
|
4570
4937
|
id: row.id,
|
|
4571
4938
|
name: row.name,
|
|
4572
4939
|
keyPrefix: row.keyPrefix,
|
|
4940
|
+
// True only when a reversible `keySecret` envelope was persisted at creation
|
|
4941
|
+
// — gates the UI "view key" eye. Legacy hash-only rows read as absent.
|
|
4942
|
+
revealable: Boolean(row.keySecret),
|
|
4573
4943
|
enabled: row.enabled,
|
|
4574
4944
|
createdAt: row.createdAt,
|
|
4575
4945
|
lastUsedAt: row.lastUsedAt,
|
|
@@ -5225,7 +5595,13 @@ function handlePresets(res, method) {
|
|
|
5225
5595
|
name: p.name,
|
|
5226
5596
|
apiFormat: p.apiFormat,
|
|
5227
5597
|
baseUrl: p.baseUrl,
|
|
5228
|
-
models: p.models
|
|
5598
|
+
models: p.models,
|
|
5599
|
+
nameKey: p.nameKey,
|
|
5600
|
+
icon: p.icon,
|
|
5601
|
+
description: p.description,
|
|
5602
|
+
features: p.features,
|
|
5603
|
+
website: p.website,
|
|
5604
|
+
modelsEndpoint: p.modelsEndpoint
|
|
5229
5605
|
}));
|
|
5230
5606
|
return writeJson3(res, 200, { presets, excluded });
|
|
5231
5607
|
}
|
|
@@ -5259,12 +5635,27 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5259
5635
|
plaintextOnce: created.plaintextOnce
|
|
5260
5636
|
});
|
|
5261
5637
|
}
|
|
5638
|
+
if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
|
|
5639
|
+
const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
|
|
5640
|
+
if (revealed !== null) return writeJson3(res, 200, { key: revealed });
|
|
5641
|
+
const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
|
|
5642
|
+
if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
|
|
5643
|
+
return writeJsonError(
|
|
5644
|
+
res,
|
|
5645
|
+
409,
|
|
5646
|
+
`key '${rest[0]}' is not revealable (created before revealable key storage)`
|
|
5647
|
+
);
|
|
5648
|
+
}
|
|
5262
5649
|
const id = rest[0];
|
|
5263
5650
|
const action = rest[1];
|
|
5264
5651
|
if (method === "POST" && id && action === "revoke") {
|
|
5265
5652
|
const ok = await deps.keyDb.outboundApiKeysRevoke(id);
|
|
5266
5653
|
return writeJson3(res, ok ? 200 : 404, { ok });
|
|
5267
5654
|
}
|
|
5655
|
+
if (method === "DELETE" && id && !action) {
|
|
5656
|
+
const ok = await deps.keyDb.outboundApiKeysDelete(id);
|
|
5657
|
+
return writeJson3(res, ok ? 200 : 404, { ok });
|
|
5658
|
+
}
|
|
5268
5659
|
if (method === "POST" && id && action === "enabled") {
|
|
5269
5660
|
const body = await readJsonBody3(req);
|
|
5270
5661
|
const enabled = body["enabled"] === true;
|
|
@@ -5441,6 +5832,40 @@ async function handleServer(req, res, method, deps) {
|
|
|
5441
5832
|
return writeJsonError(res, 405, `method ${method} not allowed on server`);
|
|
5442
5833
|
}
|
|
5443
5834
|
async function handleAccounts(req, res, method, rest, deps) {
|
|
5835
|
+
if (rest[0] === "route-activity" && rest.length === 1) {
|
|
5836
|
+
if (method !== "GET") {
|
|
5837
|
+
return writeJsonError(res, 405, `method ${method} not allowed on account route activity`);
|
|
5838
|
+
}
|
|
5839
|
+
const query2 = requestQuery(req);
|
|
5840
|
+
const parsedLimit = Number(query2.get("limit") ?? "100");
|
|
5841
|
+
const records = getSharedAccountRouteActivity().list({
|
|
5842
|
+
providerId: query2.get("providerId") ?? void 0,
|
|
5843
|
+
accountId: query2.get("accountId") ?? void 0,
|
|
5844
|
+
sessionKey: query2.get("sessionKey") ?? void 0,
|
|
5845
|
+
limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
|
|
5846
|
+
});
|
|
5847
|
+
return writeJson3(res, 200, {
|
|
5848
|
+
available: true,
|
|
5849
|
+
records,
|
|
5850
|
+
capacity: ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
5851
|
+
collectedAt: Date.now()
|
|
5852
|
+
});
|
|
5853
|
+
}
|
|
5854
|
+
if (rest[0] === "overload-counters" && rest.length === 1) {
|
|
5855
|
+
if (method !== "GET") {
|
|
5856
|
+
return writeJsonError(res, 405, `method ${method} not allowed on overload counters`);
|
|
5857
|
+
}
|
|
5858
|
+
const query2 = requestQuery(req);
|
|
5859
|
+
const entries = getSharedOverloadCounter().list({
|
|
5860
|
+
providerId: query2.get("providerId") ?? void 0,
|
|
5861
|
+
accountId: query2.get("accountId") ?? void 0
|
|
5862
|
+
});
|
|
5863
|
+
return writeJson3(res, 200, {
|
|
5864
|
+
available: true,
|
|
5865
|
+
entries,
|
|
5866
|
+
collectedAt: Date.now()
|
|
5867
|
+
});
|
|
5868
|
+
}
|
|
5444
5869
|
if (rest[0] === "allowances") {
|
|
5445
5870
|
return handleAccountAllowanceApi(
|
|
5446
5871
|
req,
|
|
@@ -5587,8 +6012,13 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
5587
6012
|
if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
|
|
5588
6013
|
return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
5589
6014
|
}
|
|
5590
|
-
const result = await deps.accountProbeService.
|
|
5591
|
-
return writeJson3(res, 200, {
|
|
6015
|
+
const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
|
|
6016
|
+
return writeJson3(res, 200, {
|
|
6017
|
+
ok: result.ok,
|
|
6018
|
+
marked: result.marked,
|
|
6019
|
+
tier: result.tier,
|
|
6020
|
+
model: result.model
|
|
6021
|
+
});
|
|
5592
6022
|
}
|
|
5593
6023
|
if (method === "POST" && rest[2] === "label") {
|
|
5594
6024
|
const accountId = rest[1];
|
|
@@ -5697,6 +6127,7 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
5697
6127
|
const result = await handleCliLaunch(cli, body, {
|
|
5698
6128
|
llmConfig: deps.llmConfig,
|
|
5699
6129
|
providers,
|
|
6130
|
+
routeLeaseManager: deps.routeLeaseManager,
|
|
5700
6131
|
opener: deps.cliTerminalOpener,
|
|
5701
6132
|
probe: deps.cliPathProbe
|
|
5702
6133
|
});
|
|
@@ -5959,7 +6390,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
5959
6390
|
}
|
|
5960
6391
|
|
|
5961
6392
|
// src/admin/version.ts
|
|
5962
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
6393
|
+
var DAEMON_VERSION = true ? "0.1.8" : "0.0.0-dev";
|
|
5963
6394
|
|
|
5964
6395
|
// src/admin/AdminServer.ts
|
|
5965
6396
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -6067,6 +6498,10 @@ var AdminServer = class {
|
|
|
6067
6498
|
handleAuditQuery(req, res, this.deps.auditReader);
|
|
6068
6499
|
return;
|
|
6069
6500
|
}
|
|
6501
|
+
if (path2 === "/admin/api/audit/stats" && (req.method === "GET" || req.method === "HEAD")) {
|
|
6502
|
+
await handleAuditStatsQuery(req, res, this.deps.auditStatsReader);
|
|
6503
|
+
return;
|
|
6504
|
+
}
|
|
6070
6505
|
if (path2 === "/admin/api/billing-status" && (req.method === "GET" || req.method === "HEAD")) {
|
|
6071
6506
|
handleBillingStatus(res, this.deps.billingStatusReader);
|
|
6072
6507
|
return;
|
|
@@ -6075,6 +6510,10 @@ var AdminServer = class {
|
|
|
6075
6510
|
await handleWebhookTest(req, res);
|
|
6076
6511
|
return;
|
|
6077
6512
|
}
|
|
6513
|
+
if (path2 === "/admin/api/route-leases" || path2.startsWith("/admin/api/route-leases/")) {
|
|
6514
|
+
await handleRouteLeaseApi(req, res, path2, this.deps);
|
|
6515
|
+
return;
|
|
6516
|
+
}
|
|
6078
6517
|
if (path2.startsWith("/admin/api/")) {
|
|
6079
6518
|
await handleAdminApi(req, res, path2, this.deps);
|
|
6080
6519
|
return;
|
|
@@ -6086,8 +6525,8 @@ var AdminServer = class {
|
|
|
6086
6525
|
}
|
|
6087
6526
|
/** Constant-time bearer/header check against the configured token. */
|
|
6088
6527
|
isAuthorized(req, token) {
|
|
6089
|
-
const
|
|
6090
|
-
const bearer = typeof
|
|
6528
|
+
const header2 = req.headers["authorization"];
|
|
6529
|
+
const bearer = typeof header2 === "string" && header2.startsWith("Bearer ") ? header2.slice("Bearer ".length).trim() : void 0;
|
|
6091
6530
|
const xToken = req.headers["x-admin-token"];
|
|
6092
6531
|
const presented = bearer ?? (typeof xToken === "string" ? xToken.trim() : void 0);
|
|
6093
6532
|
return constantTimeEquals(presented, token);
|
|
@@ -6183,20 +6622,36 @@ var OAuthSessionStore = class {
|
|
|
6183
6622
|
return sessionId;
|
|
6184
6623
|
}
|
|
6185
6624
|
/**
|
|
6186
|
-
*
|
|
6187
|
-
*
|
|
6188
|
-
* dropped). A `null` return means the completer must reject (no
|
|
6189
|
-
* write).
|
|
6625
|
+
* NON-DESTRUCTIVE lookup: return the session for `sessionId`, or `null` when
|
|
6626
|
+
* it is unknown, already consumed, or past its TTL (an expired entry is
|
|
6627
|
+
* dropped here). A `null` return means the completer must reject (no
|
|
6628
|
+
* exchange, no write).
|
|
6629
|
+
*
|
|
6630
|
+
* Deliberately NOT a consume: the completer peeks, runs the token exchange,
|
|
6631
|
+
* and only {@link consume}s once a token has actually been minted. Consuming
|
|
6632
|
+
* up-front burned the session on EVERY failed exchange (a mistyped/expired
|
|
6633
|
+
* pasted code, a proxy hiccup), so the user's natural retry hit
|
|
6634
|
+
* "session is unknown, expired, or already used" and the login became
|
|
6635
|
+
* unrecoverable without restarting the whole flow.
|
|
6190
6636
|
*/
|
|
6191
|
-
|
|
6637
|
+
peek(sessionId) {
|
|
6192
6638
|
this.sweep();
|
|
6193
6639
|
const session = this.sessions.get(sessionId);
|
|
6194
6640
|
if (!session) return null;
|
|
6195
|
-
|
|
6196
|
-
|
|
6641
|
+
if (Date.now() - session.createdAt > this.ttlMs) {
|
|
6642
|
+
this.sessions.delete(sessionId);
|
|
6643
|
+
return null;
|
|
6644
|
+
}
|
|
6197
6645
|
return session;
|
|
6198
6646
|
}
|
|
6199
|
-
/**
|
|
6647
|
+
/**
|
|
6648
|
+
* SINGLE-USE burn: drop the session so the same `sessionId` can never be
|
|
6649
|
+
* completed twice. Called ONLY after a successful token exchange.
|
|
6650
|
+
*/
|
|
6651
|
+
consume(sessionId) {
|
|
6652
|
+
this.sessions.delete(sessionId);
|
|
6653
|
+
}
|
|
6654
|
+
/** Drop every session past its TTL. Called on each put/peek. */
|
|
6200
6655
|
sweep() {
|
|
6201
6656
|
const now = Date.now();
|
|
6202
6657
|
for (const [id, session] of this.sessions) {
|
|
@@ -6206,11 +6661,15 @@ var OAuthSessionStore = class {
|
|
|
6206
6661
|
};
|
|
6207
6662
|
|
|
6208
6663
|
// src/commands/loopbackCallback.ts
|
|
6209
|
-
import { createServer } from "http";
|
|
6664
|
+
import { createServer as createServer2 } from "http";
|
|
6210
6665
|
var LOOPBACK_HOST = "127.0.0.1";
|
|
6211
6666
|
var LOOPBACK_PORT = 1455;
|
|
6212
6667
|
var CALLBACK_PATH = "/auth/callback";
|
|
6213
6668
|
var DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
6669
|
+
var HTML_HEADERS = {
|
|
6670
|
+
"Content-Type": "text/html",
|
|
6671
|
+
Connection: "close"
|
|
6672
|
+
};
|
|
6214
6673
|
function pageHtml(message) {
|
|
6215
6674
|
return `<!doctype html><meta charset="utf-8"><title>omnicross login</title><body style="font-family:sans-serif;padding:2rem"><h2>${message}</h2><p>You can close this window and return to the terminal.</p></body>`;
|
|
6216
6675
|
}
|
|
@@ -6221,30 +6680,31 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
6221
6680
|
if (settled) return;
|
|
6222
6681
|
settled = true;
|
|
6223
6682
|
clearTimeout(timer);
|
|
6224
|
-
|
|
6683
|
+
fn();
|
|
6684
|
+
server2.close();
|
|
6225
6685
|
};
|
|
6226
|
-
const server =
|
|
6686
|
+
const server = createServer2((req, res) => {
|
|
6227
6687
|
const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
|
|
6228
6688
|
if (url.pathname !== CALLBACK_PATH) {
|
|
6229
|
-
res.writeHead(404,
|
|
6689
|
+
res.writeHead(404, HTML_HEADERS);
|
|
6230
6690
|
res.end(pageHtml("Not found"));
|
|
6231
6691
|
return;
|
|
6232
6692
|
}
|
|
6233
6693
|
const code = url.searchParams.get("code");
|
|
6234
6694
|
const state = url.searchParams.get("state");
|
|
6235
6695
|
if (!code) {
|
|
6236
|
-
res.writeHead(400,
|
|
6696
|
+
res.writeHead(400, HTML_HEADERS);
|
|
6237
6697
|
res.end(pageHtml("Login failed: missing authorization code."));
|
|
6238
6698
|
finish(server, () => reject(new Error("login: callback did not include an authorization code")));
|
|
6239
6699
|
return;
|
|
6240
6700
|
}
|
|
6241
6701
|
if (state !== expectedState) {
|
|
6242
|
-
res.writeHead(400,
|
|
6702
|
+
res.writeHead(400, HTML_HEADERS);
|
|
6243
6703
|
res.end(pageHtml("Login failed: state mismatch."));
|
|
6244
6704
|
finish(server, () => reject(new Error("login: callback state did not match (possible CSRF) \u2014 aborting")));
|
|
6245
6705
|
return;
|
|
6246
6706
|
}
|
|
6247
|
-
res.writeHead(200,
|
|
6707
|
+
res.writeHead(200, HTML_HEADERS);
|
|
6248
6708
|
res.end(pageHtml("Login complete."));
|
|
6249
6709
|
finish(server, () => resolve3(code));
|
|
6250
6710
|
});
|
|
@@ -6645,7 +7105,7 @@ function safeStringify(value) {
|
|
|
6645
7105
|
}
|
|
6646
7106
|
|
|
6647
7107
|
// src/ports/JsonApiServerSettingsStore.ts
|
|
6648
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
7108
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
6649
7109
|
import { OUTBOUND_API_SERVER_CONFIG_KEY } from "@omnicross/core/outbound-api";
|
|
6650
7110
|
var JsonApiServerSettingsStore = class {
|
|
6651
7111
|
/**
|
|
@@ -6672,7 +7132,7 @@ var JsonApiServerSettingsStore = class {
|
|
|
6672
7132
|
if (key !== OUTBOUND_API_SERVER_CONFIG_KEY) return;
|
|
6673
7133
|
const file = this.readFile();
|
|
6674
7134
|
file.server = this.encryptSecrets(value);
|
|
6675
|
-
|
|
7135
|
+
writeFileSync7(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
|
|
6676
7136
|
}
|
|
6677
7137
|
/** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
|
|
6678
7138
|
encryptSecrets(config) {
|
|
@@ -6733,8 +7193,12 @@ var JsonlUsageEventStore = class {
|
|
|
6733
7193
|
reasoningTokens: 0,
|
|
6734
7194
|
costUsd: 0,
|
|
6735
7195
|
costSavedByCacheUsd: 0,
|
|
6736
|
-
eventCount: 0
|
|
7196
|
+
eventCount: 0,
|
|
7197
|
+
cacheEligibleEventCount: 0,
|
|
7198
|
+
coldCacheEventCount: 0,
|
|
7199
|
+
medianCacheHitRate: null
|
|
6737
7200
|
};
|
|
7201
|
+
const perEventHitRates = [];
|
|
6738
7202
|
for (const row of this.readRows(range)) {
|
|
6739
7203
|
totals.inputTokens += row.inputTokens;
|
|
6740
7204
|
totals.outputTokens += row.outputTokens;
|
|
@@ -6744,7 +7208,14 @@ var JsonlUsageEventStore = class {
|
|
|
6744
7208
|
totals.costUsd += row.costUsd;
|
|
6745
7209
|
totals.costSavedByCacheUsd += row.costSavedByCacheUsd;
|
|
6746
7210
|
totals.eventCount += 1;
|
|
7211
|
+
const promptSideTokens = row.inputTokens + row.cacheReadTokens + row.cacheCreationTokens;
|
|
7212
|
+
if (promptSideTokens > 0) {
|
|
7213
|
+
totals.cacheEligibleEventCount += 1;
|
|
7214
|
+
if (row.cacheReadTokens === 0) totals.coldCacheEventCount += 1;
|
|
7215
|
+
perEventHitRates.push(row.cacheReadTokens / promptSideTokens);
|
|
7216
|
+
}
|
|
6747
7217
|
}
|
|
7218
|
+
totals.medianCacheHitRate = median(perEventHitRates);
|
|
6748
7219
|
return totals;
|
|
6749
7220
|
}
|
|
6750
7221
|
async getByModel(range) {
|
|
@@ -6978,6 +7449,15 @@ var NUMERIC_FIELDS = [
|
|
|
6978
7449
|
];
|
|
6979
7450
|
var NULLABLE_STRING_FIELDS = ["messageId", "parentMessageId", "sessionId", "apiKeyId"];
|
|
6980
7451
|
var isStringOrNull = (v) => v === null || typeof v === "string";
|
|
7452
|
+
var CACHE_KEY_SOURCES = /* @__PURE__ */ new Set([
|
|
7453
|
+
"client",
|
|
7454
|
+
"session-header",
|
|
7455
|
+
"thread-header",
|
|
7456
|
+
"body-session-id",
|
|
7457
|
+
"body-thread-id",
|
|
7458
|
+
"content-fingerprint",
|
|
7459
|
+
"none"
|
|
7460
|
+
]);
|
|
6981
7461
|
function isUsageEventRecord(parsed) {
|
|
6982
7462
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
6983
7463
|
const r = parsed;
|
|
@@ -6985,6 +7465,10 @@ function isUsageEventRecord(parsed) {
|
|
|
6985
7465
|
if (typeof r["providerId"] !== "string") return false;
|
|
6986
7466
|
if (typeof r["model"] !== "string") return false;
|
|
6987
7467
|
if (typeof r["engineOrigin"] !== "string") return false;
|
|
7468
|
+
if (r["cacheKeySource"] !== void 0 && (typeof r["cacheKeySource"] !== "string" || !CACHE_KEY_SOURCES.has(r["cacheKeySource"]))) return false;
|
|
7469
|
+
if (r["cacheKeyInjected"] !== void 0 && typeof r["cacheKeyInjected"] !== "boolean") {
|
|
7470
|
+
return false;
|
|
7471
|
+
}
|
|
6988
7472
|
for (const f of NULLABLE_STRING_FIELDS) {
|
|
6989
7473
|
if (!isStringOrNull(r[f])) return false;
|
|
6990
7474
|
}
|
|
@@ -6994,9 +7478,15 @@ function isUsageEventRecord(parsed) {
|
|
|
6994
7478
|
}
|
|
6995
7479
|
return true;
|
|
6996
7480
|
}
|
|
7481
|
+
function median(values) {
|
|
7482
|
+
if (values.length === 0) return null;
|
|
7483
|
+
values.sort((a, b) => a - b);
|
|
7484
|
+
const middle = Math.floor(values.length / 2);
|
|
7485
|
+
return values.length % 2 === 1 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
|
|
7486
|
+
}
|
|
6997
7487
|
|
|
6998
7488
|
// src/ports/JsonPricingStore.ts
|
|
6999
|
-
import { existsSync as existsSync9, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as
|
|
7489
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
7000
7490
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
7001
7491
|
var JsonPricingStore = class {
|
|
7002
7492
|
constructor(pricingPath) {
|
|
@@ -7137,13 +7627,13 @@ var JsonPricingStore = class {
|
|
|
7137
7627
|
writeRows(rows) {
|
|
7138
7628
|
const temporaryPath = `${this.pricingPath}.${process.pid}.${randomUUID5()}.tmp`;
|
|
7139
7629
|
try {
|
|
7140
|
-
|
|
7630
|
+
writeFileSync8(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
7141
7631
|
encoding: "utf8",
|
|
7142
7632
|
flag: "wx"
|
|
7143
7633
|
});
|
|
7144
7634
|
this.replaceFile(temporaryPath);
|
|
7145
7635
|
} finally {
|
|
7146
|
-
|
|
7636
|
+
rmSync3(temporaryPath, { force: true });
|
|
7147
7637
|
}
|
|
7148
7638
|
}
|
|
7149
7639
|
/** Isolated for deterministic failure testing; never removes the target. */
|
|
@@ -7158,7 +7648,7 @@ function isUsablePricingRow(value) {
|
|
|
7158
7648
|
}
|
|
7159
7649
|
|
|
7160
7650
|
// src/pricing/PricingRefreshScheduler.ts
|
|
7161
|
-
import { existsSync as existsSync10, readFileSync as readFileSync11, renameSync as renameSync4, writeFileSync as
|
|
7651
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
7162
7652
|
var EMPTY_STATE2 = {
|
|
7163
7653
|
lastAttemptAt: null,
|
|
7164
7654
|
lastSuccessAt: null,
|
|
@@ -7251,7 +7741,7 @@ var PricingRefreshScheduler = class {
|
|
|
7251
7741
|
}
|
|
7252
7742
|
writeState(state) {
|
|
7253
7743
|
const temporaryPath = `${this.statePath}.tmp`;
|
|
7254
|
-
|
|
7744
|
+
writeFileSync9(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
7255
7745
|
`, "utf8");
|
|
7256
7746
|
renameSync4(temporaryPath, this.statePath);
|
|
7257
7747
|
}
|
|
@@ -7261,7 +7751,7 @@ function finiteOrNull(value) {
|
|
|
7261
7751
|
}
|
|
7262
7752
|
|
|
7263
7753
|
// src/ports/JsonVoucherDb.ts
|
|
7264
|
-
import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as
|
|
7754
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
7265
7755
|
var JsonVoucherDb = class {
|
|
7266
7756
|
constructor(vouchersPath) {
|
|
7267
7757
|
this.vouchersPath = vouchersPath;
|
|
@@ -7348,12 +7838,12 @@ var JsonVoucherDb = class {
|
|
|
7348
7838
|
}
|
|
7349
7839
|
}
|
|
7350
7840
|
writeRows(rows) {
|
|
7351
|
-
|
|
7841
|
+
writeFileSync10(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
7352
7842
|
}
|
|
7353
7843
|
};
|
|
7354
7844
|
|
|
7355
7845
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
7356
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as
|
|
7846
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
|
|
7357
7847
|
import { dirname as dirname6 } from "path";
|
|
7358
7848
|
import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
7359
7849
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
@@ -7409,9 +7899,9 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
7409
7899
|
// src/ports/external-cli-credentials.ts
|
|
7410
7900
|
import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
|
|
7411
7901
|
import { homedir as homedir3 } from "os";
|
|
7412
|
-
import { join as
|
|
7902
|
+
import { join as join5 } from "path";
|
|
7413
7903
|
function externalStorePath(provider, home = homedir3()) {
|
|
7414
|
-
return provider === "claude" ?
|
|
7904
|
+
return provider === "claude" ? join5(home, ".claude", ".credentials.json") : join5(home, ".codex", "auth.json");
|
|
7415
7905
|
}
|
|
7416
7906
|
function decodeJwtExpiryMs(token) {
|
|
7417
7907
|
try {
|
|
@@ -7501,9 +7991,15 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7501
7991
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
7502
7992
|
* through {@link fetchUpstream} with the account's `{ providerId, accountId }`
|
|
7503
7993
|
* ctx so the per-account/provider proxy applies. `@internal` also a test seam.
|
|
7994
|
+
*
|
|
7995
|
+
* `redactBodies` is REQUIRED here: this round-trip sends the refresh_token and
|
|
7996
|
+
* receives a fresh access/refresh token pair. Carrying a `providerId` opts the
|
|
7997
|
+
* call into the upstream trace (so a failing refresh is diagnosable), and the
|
|
7998
|
+
* trace captures bodies verbatim — without this flag every refresh would write
|
|
7999
|
+
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
7504
8000
|
*/
|
|
7505
8001
|
buildRefreshFetch(providerId, accountId) {
|
|
7506
|
-
return this.fetchImpl ?? ((url, init) => fetchUpstream3(url, init, { providerId, accountId }));
|
|
8002
|
+
return this.fetchImpl ?? ((url, init) => fetchUpstream3(url, init, { providerId, accountId, redactBodies: true }));
|
|
7507
8003
|
}
|
|
7508
8004
|
/**
|
|
7509
8005
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -8030,7 +8526,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
8030
8526
|
persist(config) {
|
|
8031
8527
|
mkdirSync4(dirname6(this.tokensPath), { recursive: true });
|
|
8032
8528
|
const encrypted = encryptTokens(config, this.box);
|
|
8033
|
-
|
|
8529
|
+
writeFileSync11(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
8034
8530
|
}
|
|
8035
8531
|
/**
|
|
8036
8532
|
* Read + parse `tokens.json`, tolerating a missing/corrupt file, then DECRYPT
|
|
@@ -8063,6 +8559,127 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
8063
8559
|
// src/AccountHealthProbeScheduler.ts
|
|
8064
8560
|
import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
8065
8561
|
|
|
8562
|
+
// src/probe/CodexGenerationProbe.ts
|
|
8563
|
+
import {
|
|
8564
|
+
DEFAULT_CODEX_CLI_HEADERS,
|
|
8565
|
+
codexAcceptHeader
|
|
8566
|
+
} from "@omnicross/core/provider-proxy/identity/codexCliHeaders";
|
|
8567
|
+
var CODEX_GENERATION_PROBE_MODEL = "gpt-5.6-luna";
|
|
8568
|
+
var CODEX_GENERATION_PROBE_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
8569
|
+
var MAX_STREAM_BYTES = 256 * 1024;
|
|
8570
|
+
var PROBE_INSTRUCTION = "Return exactly PONG and no other text.";
|
|
8571
|
+
function buildCodexGenerationProbeInit(token, signal) {
|
|
8572
|
+
return {
|
|
8573
|
+
method: "POST",
|
|
8574
|
+
signal,
|
|
8575
|
+
headers: {
|
|
8576
|
+
...DEFAULT_CODEX_CLI_HEADERS,
|
|
8577
|
+
Authorization: `Bearer ${token}`,
|
|
8578
|
+
Accept: codexAcceptHeader(true),
|
|
8579
|
+
"Content-Type": "application/json"
|
|
8580
|
+
},
|
|
8581
|
+
body: JSON.stringify({
|
|
8582
|
+
model: CODEX_GENERATION_PROBE_MODEL,
|
|
8583
|
+
input: [
|
|
8584
|
+
{
|
|
8585
|
+
role: "developer",
|
|
8586
|
+
content: [{ type: "input_text", text: PROBE_INSTRUCTION }]
|
|
8587
|
+
},
|
|
8588
|
+
{
|
|
8589
|
+
role: "user",
|
|
8590
|
+
content: [{ type: "input_text", text: "Connection probe." }]
|
|
8591
|
+
}
|
|
8592
|
+
],
|
|
8593
|
+
// GPT-5.6 otherwise defaults to medium reasoning. A connectivity probe
|
|
8594
|
+
// needs the lowest-cost path and no tool reasoning.
|
|
8595
|
+
reasoning: { effort: "none" },
|
|
8596
|
+
stream: true,
|
|
8597
|
+
store: false
|
|
8598
|
+
})
|
|
8599
|
+
};
|
|
8600
|
+
}
|
|
8601
|
+
async function readCodexGenerationProbeStream(response) {
|
|
8602
|
+
if (!response.body) return { completed: false, outputChars: 0 };
|
|
8603
|
+
const reader = response.body.getReader();
|
|
8604
|
+
const decoder = new TextDecoder();
|
|
8605
|
+
let buffer = "";
|
|
8606
|
+
let bytes = 0;
|
|
8607
|
+
let outputChars = 0;
|
|
8608
|
+
try {
|
|
8609
|
+
while (true) {
|
|
8610
|
+
const { done, value } = await reader.read();
|
|
8611
|
+
if (done) break;
|
|
8612
|
+
bytes += value.byteLength;
|
|
8613
|
+
if (bytes > MAX_STREAM_BYTES) {
|
|
8614
|
+
await reader.cancel();
|
|
8615
|
+
return { completed: false, outputChars };
|
|
8616
|
+
}
|
|
8617
|
+
buffer += decoder.decode(value, { stream: true });
|
|
8618
|
+
buffer = buffer.replace(/\r\n/g, "\n");
|
|
8619
|
+
let boundary = buffer.indexOf("\n\n");
|
|
8620
|
+
while (boundary >= 0) {
|
|
8621
|
+
const block = buffer.slice(0, boundary);
|
|
8622
|
+
buffer = buffer.slice(boundary + 2);
|
|
8623
|
+
const event = parseSseBlock(block);
|
|
8624
|
+
if (event) {
|
|
8625
|
+
const type = event["type"];
|
|
8626
|
+
if (type === "response.output_text.delta" && typeof event["delta"] === "string") {
|
|
8627
|
+
outputChars += event["delta"].length;
|
|
8628
|
+
} else if (type === "response.output_text.done" && typeof event["text"] === "string") {
|
|
8629
|
+
outputChars = Math.max(outputChars, event["text"].length);
|
|
8630
|
+
} else if (type === "response.failed" || type === "error") {
|
|
8631
|
+
await reader.cancel();
|
|
8632
|
+
return { completed: false, outputChars };
|
|
8633
|
+
} else if (type === "response.completed") {
|
|
8634
|
+
const completedResponse = asRecord(event["response"]);
|
|
8635
|
+
const status = completedResponse?.["status"];
|
|
8636
|
+
outputChars = Math.max(outputChars, countCompletedOutputChars(completedResponse));
|
|
8637
|
+
await reader.cancel();
|
|
8638
|
+
return {
|
|
8639
|
+
completed: (status === void 0 || status === "completed") && outputChars > 0,
|
|
8640
|
+
outputChars
|
|
8641
|
+
};
|
|
8642
|
+
}
|
|
8643
|
+
}
|
|
8644
|
+
boundary = buffer.indexOf("\n\n");
|
|
8645
|
+
}
|
|
8646
|
+
}
|
|
8647
|
+
} catch {
|
|
8648
|
+
return { completed: false, outputChars };
|
|
8649
|
+
} finally {
|
|
8650
|
+
reader.releaseLock();
|
|
8651
|
+
}
|
|
8652
|
+
return { completed: false, outputChars };
|
|
8653
|
+
}
|
|
8654
|
+
function parseSseBlock(block) {
|
|
8655
|
+
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
8656
|
+
if (!data || data === "[DONE]") return null;
|
|
8657
|
+
try {
|
|
8658
|
+
return JSON.parse(data);
|
|
8659
|
+
} catch {
|
|
8660
|
+
return null;
|
|
8661
|
+
}
|
|
8662
|
+
}
|
|
8663
|
+
function asRecord(value) {
|
|
8664
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
8665
|
+
}
|
|
8666
|
+
function countCompletedOutputChars(response) {
|
|
8667
|
+
const output = response?.["output"];
|
|
8668
|
+
if (!Array.isArray(output)) return 0;
|
|
8669
|
+
let chars = 0;
|
|
8670
|
+
for (const item of output) {
|
|
8671
|
+
const content = asRecord(item)?.["content"];
|
|
8672
|
+
if (!Array.isArray(content)) continue;
|
|
8673
|
+
for (const part of content) {
|
|
8674
|
+
const record = asRecord(part);
|
|
8675
|
+
if (record?.["type"] === "output_text" && typeof record["text"] === "string") {
|
|
8676
|
+
chars += record["text"].length;
|
|
8677
|
+
}
|
|
8678
|
+
}
|
|
8679
|
+
}
|
|
8680
|
+
return chars;
|
|
8681
|
+
}
|
|
8682
|
+
|
|
8066
8683
|
// src/probe/ProbeStrategy.ts
|
|
8067
8684
|
var PROVIDER_PROBE_PLANS = {
|
|
8068
8685
|
claude: {
|
|
@@ -8190,17 +8807,17 @@ var AccountHealthProbeScheduler = class {
|
|
|
8190
8807
|
}
|
|
8191
8808
|
if (readThrew) {
|
|
8192
8809
|
this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
|
|
8193
|
-
return { ok: false, marked: false };
|
|
8810
|
+
return { ok: false, marked: false, tier: "local" };
|
|
8194
8811
|
}
|
|
8195
8812
|
if (!token) {
|
|
8196
8813
|
this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
|
|
8197
8814
|
this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
|
|
8198
|
-
return { ok: false, marked: true };
|
|
8815
|
+
return { ok: false, marked: true, tier: "local" };
|
|
8199
8816
|
}
|
|
8200
8817
|
const plan = this.planFor(providerId);
|
|
8201
8818
|
if (plan.kind === "local") {
|
|
8202
8819
|
this.record(providerId, accountId, { ts: now, ok: true, tier: "local" });
|
|
8203
|
-
return { ok: true, marked: false };
|
|
8820
|
+
return { ok: true, marked: false, tier: "local" };
|
|
8204
8821
|
}
|
|
8205
8822
|
const start = this.now();
|
|
8206
8823
|
let status = null;
|
|
@@ -8225,7 +8842,60 @@ var AccountHealthProbeScheduler = class {
|
|
|
8225
8842
|
latencyMs,
|
|
8226
8843
|
tier: "upstream"
|
|
8227
8844
|
});
|
|
8228
|
-
return { ok: status !== null && status < 400, marked };
|
|
8845
|
+
return { ok: status !== null && status < 400, marked, tier: "upstream" };
|
|
8846
|
+
}
|
|
8847
|
+
/**
|
|
8848
|
+
* Manual connection test. Codex performs a real, quota-consuming generation;
|
|
8849
|
+
* every other provider keeps its existing cheap probe. Scheduled sweeps never
|
|
8850
|
+
* call this method, so they remain non-billable.
|
|
8851
|
+
*/
|
|
8852
|
+
async testAccountConnection(providerId, accountId) {
|
|
8853
|
+
if (providerId !== "codex") return this.probeAccount(providerId, accountId);
|
|
8854
|
+
const now = this.now();
|
|
8855
|
+
let token;
|
|
8856
|
+
try {
|
|
8857
|
+
token = await this.store.getAccessTokenForAccount(providerId, accountId);
|
|
8858
|
+
} catch {
|
|
8859
|
+
this.record(providerId, accountId, { ts: now, ok: false, status: null, tier: "local" });
|
|
8860
|
+
return { ok: false, marked: false, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
|
|
8861
|
+
}
|
|
8862
|
+
if (!token) {
|
|
8863
|
+
this.health.recordUpstreamOutcome(providerId, accountId, { status: 401, now });
|
|
8864
|
+
this.record(providerId, accountId, { ts: now, ok: false, status: 401, tier: "local" });
|
|
8865
|
+
return { ok: false, marked: true, tier: "local", model: CODEX_GENERATION_PROBE_MODEL };
|
|
8866
|
+
}
|
|
8867
|
+
const startedAt = this.now();
|
|
8868
|
+
let attempt = await this.runCodexGenerationAttempt(accountId, token);
|
|
8869
|
+
if (attempt.status === 401 && this.store.refreshAccountToken) {
|
|
8870
|
+
try {
|
|
8871
|
+
if (await this.store.refreshAccountToken(providerId, accountId)) {
|
|
8872
|
+
const refreshed = await this.store.getAccessTokenForAccount(providerId, accountId);
|
|
8873
|
+
if (refreshed) attempt = await this.runCodexGenerationAttempt(accountId, refreshed);
|
|
8874
|
+
}
|
|
8875
|
+
} catch {
|
|
8876
|
+
}
|
|
8877
|
+
}
|
|
8878
|
+
const latencyMs = this.now() - startedAt;
|
|
8879
|
+
const ok = attempt.status !== null && attempt.status >= 200 && attempt.status < 300 && attempt.completed;
|
|
8880
|
+
let marked = false;
|
|
8881
|
+
if (ok) {
|
|
8882
|
+
this.health.clearTransientMark(providerId, accountId);
|
|
8883
|
+
} else if (attempt.status === 401 || attempt.status === 403) {
|
|
8884
|
+
marked = this.applyOutcome(providerId, accountId, attempt.status, attempt.bodyText, now);
|
|
8885
|
+
}
|
|
8886
|
+
this.record(providerId, accountId, {
|
|
8887
|
+
ts: now,
|
|
8888
|
+
ok,
|
|
8889
|
+
status: attempt.status,
|
|
8890
|
+
latencyMs,
|
|
8891
|
+
tier: "generation"
|
|
8892
|
+
});
|
|
8893
|
+
return {
|
|
8894
|
+
ok,
|
|
8895
|
+
marked,
|
|
8896
|
+
tier: "generation",
|
|
8897
|
+
model: CODEX_GENERATION_PROBE_MODEL
|
|
8898
|
+
};
|
|
8229
8899
|
}
|
|
8230
8900
|
/** Per-account rolling history for the authed admin surface (design D5). */
|
|
8231
8901
|
getAllHistory() {
|
|
@@ -8282,6 +8952,24 @@ var AccountHealthProbeScheduler = class {
|
|
|
8282
8952
|
return "";
|
|
8283
8953
|
}
|
|
8284
8954
|
}
|
|
8955
|
+
async runCodexGenerationAttempt(accountId, token) {
|
|
8956
|
+
try {
|
|
8957
|
+
const timeoutMs = Math.max(this.config.timeoutMs, 15e3);
|
|
8958
|
+
const response = await this.fetchImpl(
|
|
8959
|
+
CODEX_GENERATION_PROBE_URL,
|
|
8960
|
+
buildCodexGenerationProbeInit(token, AbortSignal.timeout(timeoutMs)),
|
|
8961
|
+
{ providerId: "codex", accountId, redactBodies: true }
|
|
8962
|
+
);
|
|
8963
|
+
if (response.status < 200 || response.status >= 300) {
|
|
8964
|
+
const bodyText = response.status === 403 ? await this.readBounded(response) : void 0;
|
|
8965
|
+
return { status: response.status, completed: false, bodyText };
|
|
8966
|
+
}
|
|
8967
|
+
const stream = await readCodexGenerationProbeStream(response);
|
|
8968
|
+
return { status: response.status, completed: stream.completed };
|
|
8969
|
+
} catch {
|
|
8970
|
+
return { status: null, completed: false };
|
|
8971
|
+
}
|
|
8972
|
+
}
|
|
8285
8973
|
key(providerId, accountId) {
|
|
8286
8974
|
return `${providerId}${KEY_SEP}${accountId}`;
|
|
8287
8975
|
}
|
|
@@ -8377,7 +9065,7 @@ var AccountHealthSweeper = class {
|
|
|
8377
9065
|
};
|
|
8378
9066
|
|
|
8379
9067
|
// src/audit/AuditPruneSweeper.ts
|
|
8380
|
-
import { existsSync as
|
|
9068
|
+
import { existsSync as existsSync15, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
8381
9069
|
import { join as join7 } from "path";
|
|
8382
9070
|
|
|
8383
9071
|
// src/audit/auditFiles.ts
|
|
@@ -8400,12 +9088,213 @@ function auditFileDateMs(fileName) {
|
|
|
8400
9088
|
return d.getTime();
|
|
8401
9089
|
}
|
|
8402
9090
|
|
|
9091
|
+
// src/audit/auditStats.ts
|
|
9092
|
+
import {
|
|
9093
|
+
createReadStream,
|
|
9094
|
+
existsSync as existsSync14,
|
|
9095
|
+
readFileSync as readFileSync15,
|
|
9096
|
+
readdirSync,
|
|
9097
|
+
statSync as statSync3,
|
|
9098
|
+
writeFileSync as writeFileSync12
|
|
9099
|
+
} from "fs";
|
|
9100
|
+
import { basename, dirname as dirname7, join as join6 } from "path";
|
|
9101
|
+
var SIDECAR_VERSION = 1;
|
|
9102
|
+
var META_PREFIX_BYTES = 64 * 1024;
|
|
9103
|
+
var READ_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
9104
|
+
function auditStatsFileName(auditFile) {
|
|
9105
|
+
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
9106
|
+
}
|
|
9107
|
+
function readPersisted(path2) {
|
|
9108
|
+
if (!existsSync14(path2)) return null;
|
|
9109
|
+
try {
|
|
9110
|
+
const value = JSON.parse(readFileSync15(path2, "utf8"));
|
|
9111
|
+
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)) {
|
|
9112
|
+
return null;
|
|
9113
|
+
}
|
|
9114
|
+
return value;
|
|
9115
|
+
} catch {
|
|
9116
|
+
return null;
|
|
9117
|
+
}
|
|
9118
|
+
}
|
|
9119
|
+
function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
|
|
9120
|
+
const statsPath = join6(dirname7(auditPath), auditStatsFileName(basename(auditPath)));
|
|
9121
|
+
const previous = auditBytesBefore === 0 ? {
|
|
9122
|
+
version: SIDECAR_VERSION,
|
|
9123
|
+
auditBytes: 0,
|
|
9124
|
+
requestCount: 0,
|
|
9125
|
+
errorCount: 0,
|
|
9126
|
+
complete: true,
|
|
9127
|
+
minTs: null,
|
|
9128
|
+
maxTs: null
|
|
9129
|
+
} : readPersisted(statsPath);
|
|
9130
|
+
if (!previous || !previous.complete || previous.auditBytes !== auditBytesBefore) return;
|
|
9131
|
+
const next = {
|
|
9132
|
+
version: SIDECAR_VERSION,
|
|
9133
|
+
auditBytes: auditBytesAfter,
|
|
9134
|
+
requestCount: previous.requestCount + 1,
|
|
9135
|
+
errorCount: previous.errorCount + (record.status >= 400 || Boolean(record.error) ? 1 : 0),
|
|
9136
|
+
complete: true,
|
|
9137
|
+
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
9138
|
+
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
9139
|
+
};
|
|
9140
|
+
writeFileSync12(statsPath, JSON.stringify(next), "utf8");
|
|
9141
|
+
}
|
|
9142
|
+
function queryCovers(stats, from, to) {
|
|
9143
|
+
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
9144
|
+
}
|
|
9145
|
+
function fileOverlaps(file, from, to) {
|
|
9146
|
+
const start = auditFileDateMs(file);
|
|
9147
|
+
if (start === null) return false;
|
|
9148
|
+
const date = new Date(start);
|
|
9149
|
+
const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
|
|
9150
|
+
return end > from && start <= to;
|
|
9151
|
+
}
|
|
9152
|
+
function parseMetadataPrefix(prefix, prefixTruncated) {
|
|
9153
|
+
const text = prefix.toString("utf8");
|
|
9154
|
+
const tsMatch = /(?:^|,)"ts":(-?\d+)/.exec(text);
|
|
9155
|
+
const statusMatch = /(?:^|,)"status":(-?\d+)/.exec(text);
|
|
9156
|
+
const errorMatch = /(?:^|,)"error":"((?:\\.|[^"\\])*)"/.exec(text);
|
|
9157
|
+
const bodyStarted = /,(?:"requestBody"|"responseBody"):/.test(text);
|
|
9158
|
+
return {
|
|
9159
|
+
ts: tsMatch ? Number(tsMatch[1]) : void 0,
|
|
9160
|
+
status: statusMatch ? Number(statusMatch[1]) : void 0,
|
|
9161
|
+
hasError: Boolean(errorMatch?.[1]),
|
|
9162
|
+
complete: Boolean(tsMatch && statusMatch && (!prefixTruncated || bodyStarted))
|
|
9163
|
+
};
|
|
9164
|
+
}
|
|
9165
|
+
async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
|
|
9166
|
+
let requestCount = 0;
|
|
9167
|
+
let errorCount = 0;
|
|
9168
|
+
let filteredRequestCount = 0;
|
|
9169
|
+
let filteredErrorCount = 0;
|
|
9170
|
+
let minTs = null;
|
|
9171
|
+
let maxTs = null;
|
|
9172
|
+
let complete = true;
|
|
9173
|
+
let prefixParts = [];
|
|
9174
|
+
let prefixBytes = 0;
|
|
9175
|
+
let prefixTruncated = false;
|
|
9176
|
+
const consumeLine = () => {
|
|
9177
|
+
if (prefixBytes === 0 && !prefixTruncated) return;
|
|
9178
|
+
const prefix = Buffer.concat(prefixParts, prefixBytes);
|
|
9179
|
+
const metadata = parseMetadataPrefix(prefix, prefixTruncated);
|
|
9180
|
+
if (!metadata.complete || metadata.ts === void 0 || metadata.status === void 0) {
|
|
9181
|
+
complete = false;
|
|
9182
|
+
} else {
|
|
9183
|
+
requestCount += 1;
|
|
9184
|
+
const isError = metadata.status >= 400 || metadata.hasError;
|
|
9185
|
+
if (isError) errorCount += 1;
|
|
9186
|
+
minTs = minTs === null ? metadata.ts : Math.min(minTs, metadata.ts);
|
|
9187
|
+
maxTs = maxTs === null ? metadata.ts : Math.max(maxTs, metadata.ts);
|
|
9188
|
+
if (metadata.ts >= from && metadata.ts <= to) {
|
|
9189
|
+
filteredRequestCount += 1;
|
|
9190
|
+
if (isError) filteredErrorCount += 1;
|
|
9191
|
+
}
|
|
9192
|
+
}
|
|
9193
|
+
prefixParts = [];
|
|
9194
|
+
prefixBytes = 0;
|
|
9195
|
+
prefixTruncated = false;
|
|
9196
|
+
};
|
|
9197
|
+
if (auditBytes > startByte) {
|
|
9198
|
+
const stream = createReadStream(auditPath, {
|
|
9199
|
+
start: startByte,
|
|
9200
|
+
end: auditBytes - 1,
|
|
9201
|
+
highWaterMark: READ_CHUNK_BYTES
|
|
9202
|
+
});
|
|
9203
|
+
for await (const value of stream) {
|
|
9204
|
+
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
9205
|
+
let offset = 0;
|
|
9206
|
+
while (offset < chunk.length) {
|
|
9207
|
+
const newline = chunk.indexOf(10, offset);
|
|
9208
|
+
const end = newline === -1 ? chunk.length : newline;
|
|
9209
|
+
if (prefixBytes < META_PREFIX_BYTES) {
|
|
9210
|
+
const retained = Math.min(META_PREFIX_BYTES - prefixBytes, end - offset);
|
|
9211
|
+
if (retained > 0) {
|
|
9212
|
+
prefixParts.push(Buffer.from(chunk.subarray(offset, offset + retained)));
|
|
9213
|
+
prefixBytes += retained;
|
|
9214
|
+
}
|
|
9215
|
+
if (retained < end - offset) prefixTruncated = true;
|
|
9216
|
+
} else if (end > offset) {
|
|
9217
|
+
prefixTruncated = true;
|
|
9218
|
+
}
|
|
9219
|
+
if (newline === -1) break;
|
|
9220
|
+
consumeLine();
|
|
9221
|
+
offset = newline + 1;
|
|
9222
|
+
}
|
|
9223
|
+
}
|
|
9224
|
+
}
|
|
9225
|
+
if (prefixBytes > 0 || prefixTruncated) complete = false;
|
|
9226
|
+
return {
|
|
9227
|
+
all: {
|
|
9228
|
+
version: SIDECAR_VERSION,
|
|
9229
|
+
auditBytes,
|
|
9230
|
+
requestCount,
|
|
9231
|
+
errorCount,
|
|
9232
|
+
complete,
|
|
9233
|
+
minTs,
|
|
9234
|
+
maxTs
|
|
9235
|
+
},
|
|
9236
|
+
filtered: { requestCount: filteredRequestCount, errorCount: filteredErrorCount, complete }
|
|
9237
|
+
};
|
|
9238
|
+
}
|
|
9239
|
+
function mergePersistedStats(previous, appended) {
|
|
9240
|
+
return {
|
|
9241
|
+
version: SIDECAR_VERSION,
|
|
9242
|
+
auditBytes: appended.auditBytes,
|
|
9243
|
+
requestCount: previous.requestCount + appended.requestCount,
|
|
9244
|
+
errorCount: previous.errorCount + appended.errorCount,
|
|
9245
|
+
complete: previous.complete && appended.complete,
|
|
9246
|
+
minTs: previous.minTs === null ? appended.minTs : appended.minTs === null ? previous.minTs : Math.min(previous.minTs, appended.minTs),
|
|
9247
|
+
maxTs: previous.maxTs === null ? appended.maxTs : appended.maxTs === null ? previous.maxTs : Math.max(previous.maxTs, appended.maxTs)
|
|
9248
|
+
};
|
|
9249
|
+
}
|
|
9250
|
+
async function readAuditStats(auditDir, query2 = {}) {
|
|
9251
|
+
if (!existsSync14(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
9252
|
+
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
9253
|
+
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
9254
|
+
let files;
|
|
9255
|
+
try {
|
|
9256
|
+
files = readdirSync(auditDir).filter((file) => AUDIT_FILE_RE.test(file) && fileOverlaps(file, from, to)).sort();
|
|
9257
|
+
} catch {
|
|
9258
|
+
return { requestCount: 0, errorCount: 0, complete: false };
|
|
9259
|
+
}
|
|
9260
|
+
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
9261
|
+
for (const file of files) {
|
|
9262
|
+
const auditPath = join6(auditDir, file);
|
|
9263
|
+
try {
|
|
9264
|
+
const auditBytes = statSync3(auditPath).size;
|
|
9265
|
+
const statsPath = join6(auditDir, auditStatsFileName(file));
|
|
9266
|
+
const persisted = readPersisted(statsPath);
|
|
9267
|
+
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
9268
|
+
total.requestCount += persisted.requestCount;
|
|
9269
|
+
total.errorCount += persisted.errorCount;
|
|
9270
|
+
continue;
|
|
9271
|
+
}
|
|
9272
|
+
const resumable = persisted && persisted.complete && persisted.auditBytes < auditBytes && queryCovers(persisted, from, to) ? persisted : null;
|
|
9273
|
+
const scanned = await scanAuditFile(
|
|
9274
|
+
auditPath,
|
|
9275
|
+
resumable?.auditBytes ?? 0,
|
|
9276
|
+
auditBytes,
|
|
9277
|
+
from,
|
|
9278
|
+
to
|
|
9279
|
+
);
|
|
9280
|
+
total.requestCount += scanned.filtered.requestCount + (resumable?.requestCount ?? 0);
|
|
9281
|
+
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
9282
|
+
total.complete = total.complete && scanned.filtered.complete;
|
|
9283
|
+
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
9284
|
+
if (current.complete) writeFileSync12(statsPath, JSON.stringify(current), "utf8");
|
|
9285
|
+
} catch {
|
|
9286
|
+
total.complete = false;
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
return total;
|
|
9290
|
+
}
|
|
9291
|
+
|
|
8403
9292
|
// src/audit/AuditPruneSweeper.ts
|
|
8404
9293
|
var DAY_MS = 24 * 60 * 6e4;
|
|
8405
9294
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
8406
9295
|
var AuditPruneSweeper = class {
|
|
8407
|
-
constructor(
|
|
8408
|
-
this.auditDir =
|
|
9296
|
+
constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
|
|
9297
|
+
this.auditDir = auditDir;
|
|
8409
9298
|
this.logger = logger;
|
|
8410
9299
|
this.config = config;
|
|
8411
9300
|
this.intervalMs = intervalMs;
|
|
@@ -8452,17 +9341,19 @@ var AuditPruneSweeper = class {
|
|
|
8452
9341
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
8453
9342
|
this.sweeping = true;
|
|
8454
9343
|
try {
|
|
8455
|
-
if (!
|
|
9344
|
+
if (!existsSync15(this.auditDir)) return 0;
|
|
8456
9345
|
const today = new Date(this.now());
|
|
8457
9346
|
const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
|
|
8458
9347
|
const cutoff = todayMidnight - (this.config.retentionDays - 1) * DAY_MS;
|
|
8459
9348
|
let removed = 0;
|
|
8460
|
-
for (const file of
|
|
9349
|
+
for (const file of readdirSync2(this.auditDir)) {
|
|
8461
9350
|
const dateMs = auditFileDateMs(file);
|
|
8462
9351
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
8463
9352
|
try {
|
|
8464
9353
|
unlinkSync3(join7(this.auditDir, file));
|
|
8465
9354
|
removed += 1;
|
|
9355
|
+
const statsPath = join7(this.auditDir, auditStatsFileName(file));
|
|
9356
|
+
if (existsSync15(statsPath)) unlinkSync3(statsPath);
|
|
8466
9357
|
} catch (error) {
|
|
8467
9358
|
this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
|
|
8468
9359
|
file,
|
|
@@ -8484,15 +9375,15 @@ var AuditPruneSweeper = class {
|
|
|
8484
9375
|
};
|
|
8485
9376
|
|
|
8486
9377
|
// src/audit/auditReader.ts
|
|
8487
|
-
import { existsSync as
|
|
9378
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync16 } from "fs";
|
|
8488
9379
|
import { join as join8 } from "path";
|
|
8489
9380
|
var DEFAULT_LIMIT = 200;
|
|
8490
9381
|
var MAX_LIMIT = 2e3;
|
|
8491
|
-
function readAuditRecords(
|
|
8492
|
-
if (!
|
|
9382
|
+
function readAuditRecords(auditDir, query2 = {}) {
|
|
9383
|
+
if (!existsSync16(auditDir)) return [];
|
|
8493
9384
|
let files;
|
|
8494
9385
|
try {
|
|
8495
|
-
files =
|
|
9386
|
+
files = readdirSync3(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
|
|
8496
9387
|
} catch {
|
|
8497
9388
|
return [];
|
|
8498
9389
|
}
|
|
@@ -8503,7 +9394,7 @@ function readAuditRecords(auditDir2, query2 = {}) {
|
|
|
8503
9394
|
for (const file of files.sort().reverse()) {
|
|
8504
9395
|
let raw;
|
|
8505
9396
|
try {
|
|
8506
|
-
raw =
|
|
9397
|
+
raw = readFileSync16(join8(auditDir, file), "utf8");
|
|
8507
9398
|
} catch {
|
|
8508
9399
|
continue;
|
|
8509
9400
|
}
|
|
@@ -8532,11 +9423,11 @@ function isAuditRecord(value) {
|
|
|
8532
9423
|
}
|
|
8533
9424
|
|
|
8534
9425
|
// src/audit/AuditWriter.ts
|
|
8535
|
-
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5 } from "fs";
|
|
9426
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync5, statSync as statSync4 } from "fs";
|
|
8536
9427
|
import { join as join9 } from "path";
|
|
8537
9428
|
var AuditWriter = class {
|
|
8538
|
-
constructor(
|
|
8539
|
-
this.auditDir =
|
|
9429
|
+
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
9430
|
+
this.auditDir = auditDir;
|
|
8540
9431
|
this.logger = logger;
|
|
8541
9432
|
this.defer = defer;
|
|
8542
9433
|
}
|
|
@@ -8570,7 +9461,21 @@ var AuditWriter = class {
|
|
|
8570
9461
|
this.dirEnsured = true;
|
|
8571
9462
|
}
|
|
8572
9463
|
const file = join9(this.auditDir, auditFileName(record.ts));
|
|
8573
|
-
|
|
9464
|
+
const line = JSON.stringify(record) + "\n";
|
|
9465
|
+
const auditBytesBefore = existsSync17(file) ? statSync4(file).size : 0;
|
|
9466
|
+
appendFileSync2(file, line, "utf8");
|
|
9467
|
+
try {
|
|
9468
|
+
updateAuditStatsAfterAppend(
|
|
9469
|
+
file,
|
|
9470
|
+
auditBytesBefore,
|
|
9471
|
+
auditBytesBefore + Buffer.byteLength(line, "utf8"),
|
|
9472
|
+
record
|
|
9473
|
+
);
|
|
9474
|
+
} catch (error) {
|
|
9475
|
+
this.logger.warn("[AuditWriter] failed to update audit stats", {
|
|
9476
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9477
|
+
});
|
|
9478
|
+
}
|
|
8574
9479
|
}
|
|
8575
9480
|
};
|
|
8576
9481
|
|
|
@@ -8714,14 +9619,14 @@ var BillingPublisher = class {
|
|
|
8714
9619
|
};
|
|
8715
9620
|
|
|
8716
9621
|
// src/billing/billingReader.ts
|
|
8717
|
-
import { existsSync as
|
|
9622
|
+
import { existsSync as existsSync18, readdirSync as readdirSync4, readFileSync as readFileSync17 } from "fs";
|
|
8718
9623
|
import { join as join11 } from "path";
|
|
8719
9624
|
function readBillingLedger(billingDir) {
|
|
8720
9625
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
8721
|
-
if (!
|
|
9626
|
+
if (!existsSync18(billingDir)) return view;
|
|
8722
9627
|
let files;
|
|
8723
9628
|
try {
|
|
8724
|
-
files =
|
|
9629
|
+
files = readdirSync4(billingDir);
|
|
8725
9630
|
} catch {
|
|
8726
9631
|
return view;
|
|
8727
9632
|
}
|
|
@@ -8752,7 +9657,7 @@ function readBillingStatus(billingDir) {
|
|
|
8752
9657
|
function parseLines(dir, file) {
|
|
8753
9658
|
let raw;
|
|
8754
9659
|
try {
|
|
8755
|
-
raw =
|
|
9660
|
+
raw = readFileSync17(join11(dir, file), "utf8");
|
|
8756
9661
|
} catch {
|
|
8757
9662
|
return [];
|
|
8758
9663
|
}
|
|
@@ -8938,6 +9843,78 @@ var TokenRefreshScheduler = class {
|
|
|
8938
9843
|
}
|
|
8939
9844
|
};
|
|
8940
9845
|
|
|
9846
|
+
// src/routeLeaseSubscriptionPreflight.ts
|
|
9847
|
+
import {
|
|
9848
|
+
RouteLeaseError as RouteLeaseError3
|
|
9849
|
+
} from "@omnicross/core/provider-proxy";
|
|
9850
|
+
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
9851
|
+
import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
9852
|
+
import { accountSupportsModel } from "@omnicross/subscriptions/scheduler/accountModelMap";
|
|
9853
|
+
var PROVIDERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "opencodego"]);
|
|
9854
|
+
function accountArray(config, providerId) {
|
|
9855
|
+
const record = config;
|
|
9856
|
+
const key = `${providerId}Accounts`;
|
|
9857
|
+
const accounts = record[key];
|
|
9858
|
+
if (Array.isArray(accounts)) return accounts;
|
|
9859
|
+
const legacy = record[providerId];
|
|
9860
|
+
if (!legacy || typeof legacy !== "object") return [];
|
|
9861
|
+
const activeKey = `active${providerId[0].toUpperCase()}${providerId.slice(1)}AccountId`;
|
|
9862
|
+
return [{ id: String(record[activeKey] ?? "active"), enabled: true, tokens: legacy }];
|
|
9863
|
+
}
|
|
9864
|
+
function hasCredential(providerId, account) {
|
|
9865
|
+
const tokens = account.tokens;
|
|
9866
|
+
if (providerId === "opencodego") return typeof tokens.apiKey === "string" && tokens.apiKey.length > 0;
|
|
9867
|
+
return typeof tokens.accessToken === "string" && tokens.accessToken.length > 0;
|
|
9868
|
+
}
|
|
9869
|
+
function safeProviderId(value) {
|
|
9870
|
+
if (!PROVIDERS.has(value)) {
|
|
9871
|
+
throw new RouteLeaseError3("upstream_not_found", "subscription provider was not found");
|
|
9872
|
+
}
|
|
9873
|
+
return value;
|
|
9874
|
+
}
|
|
9875
|
+
function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
9876
|
+
return {
|
|
9877
|
+
async assertAvailable(upstream, model) {
|
|
9878
|
+
const providerId = safeProviderId(upstream.providerId);
|
|
9879
|
+
const config = await credentials.getFullConfig();
|
|
9880
|
+
const all = accountArray(config, providerId);
|
|
9881
|
+
if (all.length === 0) {
|
|
9882
|
+
throw new RouteLeaseError3("upstream_unavailable", "subscription provider has no configured account");
|
|
9883
|
+
}
|
|
9884
|
+
let bounded = all;
|
|
9885
|
+
if (upstream.kind === "account") {
|
|
9886
|
+
bounded = all.filter((account) => account.id === upstream.accountId);
|
|
9887
|
+
} else if (upstream.kind === "account-group") {
|
|
9888
|
+
bounded = all.filter((account) => account.group?.trim() === upstream.group);
|
|
9889
|
+
}
|
|
9890
|
+
if (bounded.length === 0) {
|
|
9891
|
+
throw new RouteLeaseError3("upstream_not_found", "the selected subscription resource was not found");
|
|
9892
|
+
}
|
|
9893
|
+
const modelEligible = bounded.filter(
|
|
9894
|
+
(account) => accountSupportsModel(account.supportedModels, model)
|
|
9895
|
+
);
|
|
9896
|
+
if (modelEligible.length === 0) {
|
|
9897
|
+
throw new RouteLeaseError3("model_not_configured", "model is not supported by the selected subscription resource");
|
|
9898
|
+
}
|
|
9899
|
+
const credentialEligible = modelEligible.filter(
|
|
9900
|
+
(account) => account.enabled !== false && hasCredential(providerId, account)
|
|
9901
|
+
);
|
|
9902
|
+
const health2 = getSharedAccountHealth3();
|
|
9903
|
+
const allowance = getSharedAccountAllowanceScheduling4();
|
|
9904
|
+
const candidates = credentialEligible.filter(
|
|
9905
|
+
(account) => health2.isSchedulable(providerId, account.id) && allowance.preview(providerId, account.id, account.priority ?? 50).schedulable
|
|
9906
|
+
);
|
|
9907
|
+
if (candidates.length > 0) return;
|
|
9908
|
+
if (upstream.kind === "account") {
|
|
9909
|
+
throw new RouteLeaseError3("upstream_unavailable", "the selected subscription account is unavailable");
|
|
9910
|
+
}
|
|
9911
|
+
throw new RouteLeaseError3("upstream_exhausted", "the selected subscription pool has no eligible account", {
|
|
9912
|
+
retryAfterSeconds: 30
|
|
9913
|
+
});
|
|
9914
|
+
}
|
|
9915
|
+
};
|
|
9916
|
+
}
|
|
9917
|
+
|
|
8941
9918
|
// src/webhook/WebhookDispatcher.ts
|
|
8942
9919
|
import { createHmac as createHmac2 } from "crypto";
|
|
8943
9920
|
import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
@@ -9120,11 +10097,11 @@ function buildDaemon(config, paths) {
|
|
|
9120
10097
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
9121
10098
|
);
|
|
9122
10099
|
setSharedAccountAllowanceStore(accountAllowanceStore);
|
|
9123
|
-
|
|
10100
|
+
getSharedAccountAllowanceScheduling5().configure(
|
|
9124
10101
|
normalizeServerConfig(decryptedConfig.server).allowanceScheduling
|
|
9125
10102
|
);
|
|
9126
10103
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
9127
|
-
const keyDb = new JsonOutboundKeyDb(paths.keysPath);
|
|
10104
|
+
const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
|
|
9128
10105
|
const voucherDb = new JsonVoucherDb(defaultVouchersPath(paths.configPath));
|
|
9129
10106
|
const settingsStore = new JsonApiServerSettingsStore(paths.configPath, secretBox3);
|
|
9130
10107
|
const integrationStateStore = new IntegrationStateStore(
|
|
@@ -9188,10 +10165,21 @@ function buildDaemon(config, paths) {
|
|
|
9188
10165
|
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
|
|
9189
10166
|
});
|
|
9190
10167
|
const providerProxy = getProviderProxy({ llmConfig, apiKeyPool, usageRecorder });
|
|
10168
|
+
const routeLeaseManager = new RouteLeaseManager(
|
|
10169
|
+
providerProxy,
|
|
10170
|
+
new RouteLeaseTargetResolver(llmConfig, {
|
|
10171
|
+
providerKeys: apiKeyPool,
|
|
10172
|
+
subscriptions: createRouteLeaseSubscriptionPreflight(credentialStore)
|
|
10173
|
+
}),
|
|
10174
|
+
routeLeaseDescriptorPort,
|
|
10175
|
+
{ logger }
|
|
10176
|
+
);
|
|
10177
|
+
providerProxy.registerBeforeStop(() => routeLeaseManager.shutdown());
|
|
10178
|
+
providerProxy.registerBeforeStop(() => resetCliSessions());
|
|
9191
10179
|
llmConfig.setReloadHook(() => apiKeyPool.invalidateCache());
|
|
9192
10180
|
const accountHealthProbeScheduler = new AccountHealthProbeScheduler(
|
|
9193
10181
|
credentialStore,
|
|
9194
|
-
|
|
10182
|
+
getSharedAccountHealth4(),
|
|
9195
10183
|
logger,
|
|
9196
10184
|
DEFAULT_ACCOUNT_PROBE
|
|
9197
10185
|
);
|
|
@@ -9225,7 +10213,7 @@ function buildDaemon(config, paths) {
|
|
|
9225
10213
|
// lines through the injected logger (honors level/format/file sink).
|
|
9226
10214
|
logger
|
|
9227
10215
|
});
|
|
9228
|
-
const
|
|
10216
|
+
const auditDir = defaultAuditDir(paths.configPath);
|
|
9229
10217
|
const billingDir = defaultBillingDir(paths.configPath);
|
|
9230
10218
|
const adminServer = new AdminServer({
|
|
9231
10219
|
configPath: paths.configPath,
|
|
@@ -9238,6 +10226,7 @@ function buildDaemon(config, paths) {
|
|
|
9238
10226
|
keySpendReader: keySpendTracker,
|
|
9239
10227
|
settingsStore,
|
|
9240
10228
|
outboundApiServer,
|
|
10229
|
+
routeLeaseManager,
|
|
9241
10230
|
subscriptionAccounts,
|
|
9242
10231
|
accountAllowanceService,
|
|
9243
10232
|
allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
|
|
@@ -9259,10 +10248,16 @@ function buildDaemon(config, paths) {
|
|
|
9259
10248
|
// (NOT widening the least-authority writer — no token-returning read reachable).
|
|
9260
10249
|
oauthSessions: new OAuthSessionStore(),
|
|
9261
10250
|
// Real global fetch by default; a test seam (`paths.oauthExchangeFetch`) can
|
|
9262
|
-
// inject a mock so no real token endpoint is hit
|
|
9263
|
-
//
|
|
9264
|
-
//
|
|
9265
|
-
|
|
10251
|
+
// inject a mock so no real token endpoint is hit (one FetchLike for every
|
|
10252
|
+
// provider — the ctx below only matters on the real egress path).
|
|
10253
|
+
//
|
|
10254
|
+
// upstream-proxy: a PER-PROVIDER factory, so the exchange carries the same
|
|
10255
|
+
// `{ providerId }` ctx the CLI login and the token refresh already pass.
|
|
10256
|
+
// Without it the interactive login resolved only the global/env proxy layers
|
|
10257
|
+
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
10258
|
+
// excluded from the upstream trace, so a failing login left no evidence.
|
|
10259
|
+
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
10260
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream7(url, init, { providerId, redactBodies: true }),
|
|
9266
10261
|
subscriptionAccountAppender: credentialStore,
|
|
9267
10262
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
9268
10263
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -9314,7 +10309,8 @@ function buildDaemon(config, paths) {
|
|
|
9314
10309
|
// date-rotated audit store. Bound to the store dir here so the AdminServer
|
|
9315
10310
|
// carries no path/store coupling. Records hold IP/UA/bodies → admin-only,
|
|
9316
10311
|
// NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
|
|
9317
|
-
auditReader: (query2) => readAuditRecords(
|
|
10312
|
+
auditReader: (query2) => readAuditRecords(auditDir, query2),
|
|
10313
|
+
auditStatsReader: (query2) => readAuditStats(auditDir, query2),
|
|
9318
10314
|
// billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
|
|
9319
10315
|
// secret-free total/delivered/pending counts of the durable ledger.
|
|
9320
10316
|
billingStatusReader: () => readBillingStatus(billingDir)
|
|
@@ -9323,10 +10319,10 @@ function buildDaemon(config, paths) {
|
|
|
9323
10319
|
logger,
|
|
9324
10320
|
fetchImpl: (url, init) => fetchUpstream7(url, init)
|
|
9325
10321
|
});
|
|
9326
|
-
setWebhookRuntime(webhookDispatcher,
|
|
9327
|
-
const auditWriter = new AuditWriter(
|
|
9328
|
-
const auditPruneSweeper = new AuditPruneSweeper(
|
|
9329
|
-
setAuditRuntime(auditWriter, auditPruneSweeper
|
|
10322
|
+
setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
|
|
10323
|
+
const auditWriter = new AuditWriter(auditDir, logger);
|
|
10324
|
+
const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
|
|
10325
|
+
setAuditRuntime(auditWriter, auditPruneSweeper);
|
|
9330
10326
|
const billingPublisher = new BillingPublisher(billingDir, logger);
|
|
9331
10327
|
const billingRetrySweeper = new BillingRetrySweeper(
|
|
9332
10328
|
billingDir,
|
|
@@ -9338,7 +10334,7 @@ function buildDaemon(config, paths) {
|
|
|
9338
10334
|
const tokenRefreshScheduler = new TokenRefreshScheduler(credentialStore, logger);
|
|
9339
10335
|
const accountHealthSweeper = new AccountHealthSweeper(
|
|
9340
10336
|
credentialStore,
|
|
9341
|
-
|
|
10337
|
+
getSharedAccountHealth4(),
|
|
9342
10338
|
logger
|
|
9343
10339
|
);
|
|
9344
10340
|
return {
|
|
@@ -9347,6 +10343,7 @@ function buildDaemon(config, paths) {
|
|
|
9347
10343
|
keyDb,
|
|
9348
10344
|
settingsStore,
|
|
9349
10345
|
providerProxy,
|
|
10346
|
+
routeLeaseManager,
|
|
9350
10347
|
outboundApiServer,
|
|
9351
10348
|
apiKeyPool,
|
|
9352
10349
|
autoDisableStore,
|
|
@@ -9372,7 +10369,7 @@ function buildDaemon(config, paths) {
|
|
|
9372
10369
|
}
|
|
9373
10370
|
function isTokensStoreReadable(tokensPath) {
|
|
9374
10371
|
try {
|
|
9375
|
-
if (!
|
|
10372
|
+
if (!existsSync19(tokensPath)) return true;
|
|
9376
10373
|
accessSync(tokensPath, fsConstants.R_OK);
|
|
9377
10374
|
return true;
|
|
9378
10375
|
} catch {
|
|
@@ -9421,7 +10418,7 @@ function resolveInPathDefault(candidate) {
|
|
|
9421
10418
|
const segments = (process.env["PATH"] ?? "").split(delimiter2).filter(Boolean);
|
|
9422
10419
|
for (const seg of segments) {
|
|
9423
10420
|
const full = join12(seg, candidate);
|
|
9424
|
-
if (
|
|
10421
|
+
if (existsSync20(full)) return full;
|
|
9425
10422
|
}
|
|
9426
10423
|
return null;
|
|
9427
10424
|
}
|
|
@@ -9462,32 +10459,17 @@ async function runLaunch(argv, deps) {
|
|
|
9462
10459
|
await daemon.llmConfig.ready();
|
|
9463
10460
|
await daemon.providerProxy.start();
|
|
9464
10461
|
} catch (err5) {
|
|
9465
|
-
daemon
|
|
9466
|
-
daemon.tokenRefreshScheduler.dispose();
|
|
9467
|
-
daemon.claudeAllowanceRefreshScheduler.dispose();
|
|
9468
|
-
daemon.accountHealthSweeper.dispose();
|
|
9469
|
-
daemon.accountHealthProbeScheduler.dispose();
|
|
9470
|
-
daemon.auditPruneSweeper.dispose();
|
|
9471
|
-
daemon.billingRetrySweeper.dispose();
|
|
9472
|
-
daemon.pricingRefreshScheduler.dispose();
|
|
10462
|
+
await shutdownLaunchDaemon(daemon);
|
|
9473
10463
|
throw err5;
|
|
9474
10464
|
}
|
|
9475
10465
|
let launch;
|
|
9476
10466
|
try {
|
|
9477
|
-
launch = await buildLaunchConfig(cli, daemon
|
|
10467
|
+
launch = await buildLaunchConfig(cli, daemon, {
|
|
9478
10468
|
providerId: values.provider,
|
|
9479
10469
|
model: values.model
|
|
9480
10470
|
});
|
|
9481
10471
|
} catch (err5) {
|
|
9482
|
-
await daemon
|
|
9483
|
-
daemon.apiKeyPool.dispose();
|
|
9484
|
-
daemon.tokenRefreshScheduler.dispose();
|
|
9485
|
-
daemon.claudeAllowanceRefreshScheduler.dispose();
|
|
9486
|
-
daemon.accountHealthSweeper.dispose();
|
|
9487
|
-
daemon.accountHealthProbeScheduler.dispose();
|
|
9488
|
-
daemon.auditPruneSweeper.dispose();
|
|
9489
|
-
daemon.billingRetrySweeper.dispose();
|
|
9490
|
-
daemon.pricingRefreshScheduler.dispose();
|
|
10472
|
+
await shutdownLaunchDaemon(daemon);
|
|
9491
10473
|
throw err5;
|
|
9492
10474
|
}
|
|
9493
10475
|
try {
|
|
@@ -9506,21 +10488,40 @@ async function runLaunch(argv, deps) {
|
|
|
9506
10488
|
cwd: values.cwd
|
|
9507
10489
|
});
|
|
9508
10490
|
} finally {
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
10491
|
+
try {
|
|
10492
|
+
launch.onSessionEnd();
|
|
10493
|
+
} finally {
|
|
10494
|
+
await shutdownLaunchDaemon(daemon);
|
|
10495
|
+
}
|
|
10496
|
+
}
|
|
10497
|
+
}
|
|
10498
|
+
async function buildLaunchConfig(cli, daemon, opts) {
|
|
10499
|
+
if (cli === "claude" || cli === "codex") {
|
|
10500
|
+
const internalId = randomUUID6();
|
|
10501
|
+
const outcome = await daemon.routeLeaseManager.createFromRequest({
|
|
10502
|
+
schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA2,
|
|
10503
|
+
consumer: "omnicross-terminal",
|
|
10504
|
+
runtime: cli,
|
|
10505
|
+
upstream: { kind: "provider", providerId: opts.providerId },
|
|
10506
|
+
model: opts.model,
|
|
10507
|
+
execution: { sessionId: `launch:${cli}:${internalId}` }
|
|
10508
|
+
}, `omnicross-launch:${internalId}`);
|
|
10509
|
+
const stopRenewal = startTerminalLeaseRenewal(
|
|
10510
|
+
daemon.routeLeaseManager,
|
|
10511
|
+
outcome.result.leaseId
|
|
10512
|
+
);
|
|
10513
|
+
return {
|
|
10514
|
+
baseUrl: daemon.providerProxy.getBaseUrl(),
|
|
10515
|
+
env: outcome.result.launch.env,
|
|
10516
|
+
extraArgs: outcome.result.launch.extraArgs,
|
|
10517
|
+
onSessionEnd: () => {
|
|
10518
|
+
stopRenewal();
|
|
10519
|
+
daemon.routeLeaseManager.release(outcome.result.leaseId);
|
|
10520
|
+
}
|
|
10521
|
+
};
|
|
10522
|
+
}
|
|
9522
10523
|
const common = {
|
|
9523
|
-
llmConfig,
|
|
10524
|
+
llmConfig: daemon.llmConfig,
|
|
9524
10525
|
providerId: opts.providerId,
|
|
9525
10526
|
model: opts.model,
|
|
9526
10527
|
// Stable, bounded session id — pool failover (poolseam) fires on launch
|
|
@@ -9528,10 +10529,6 @@ async function buildLaunchConfig(cli, llmConfig, opts) {
|
|
|
9528
10529
|
sessionId: `launch:${cli}`
|
|
9529
10530
|
};
|
|
9530
10531
|
switch (cli) {
|
|
9531
|
-
case "claude":
|
|
9532
|
-
return buildClaudeCliLaunchConfig2(common);
|
|
9533
|
-
case "codex":
|
|
9534
|
-
return buildCodexLaunchConfig2(common);
|
|
9535
10532
|
case "gemini":
|
|
9536
10533
|
return buildGeminiCliLaunchConfig2(common);
|
|
9537
10534
|
case "qwen":
|
|
@@ -9544,6 +10541,18 @@ async function buildLaunchConfig(cli, llmConfig, opts) {
|
|
|
9544
10541
|
}
|
|
9545
10542
|
}
|
|
9546
10543
|
}
|
|
10544
|
+
async function shutdownLaunchDaemon(daemon) {
|
|
10545
|
+
daemon.routeLeaseManager.shutdown();
|
|
10546
|
+
await daemon.providerProxy.stop();
|
|
10547
|
+
daemon.apiKeyPool.dispose();
|
|
10548
|
+
daemon.tokenRefreshScheduler.dispose();
|
|
10549
|
+
daemon.claudeAllowanceRefreshScheduler.dispose();
|
|
10550
|
+
daemon.accountHealthSweeper.dispose();
|
|
10551
|
+
daemon.accountHealthProbeScheduler.dispose();
|
|
10552
|
+
daemon.auditPruneSweeper.dispose();
|
|
10553
|
+
daemon.billingRetrySweeper.dispose();
|
|
10554
|
+
daemon.pricingRefreshScheduler.dispose();
|
|
10555
|
+
}
|
|
9547
10556
|
function spawnCliInherit(plan) {
|
|
9548
10557
|
return new Promise((resolve3, reject) => {
|
|
9549
10558
|
const child = spawn2(plan.command, plan.args, {
|
|
@@ -9590,7 +10599,7 @@ import { createInterface } from "readline";
|
|
|
9590
10599
|
import { parseArgs as parseArgs5 } from "util";
|
|
9591
10600
|
import { fetchUpstream as fetchUpstream8, setUpstreamProxyResolver as setUpstreamProxyResolver2 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
9592
10601
|
import { claudeOAuth as claudeOAuth3, codexOAuth as codexOAuth3, geminiOAuth as geminiOAuth3 } from "@omnicross/subscriptions";
|
|
9593
|
-
var
|
|
10602
|
+
var PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
9594
10603
|
async function runLogin(argv, deps) {
|
|
9595
10604
|
const { values, positionals } = parseArgs5({
|
|
9596
10605
|
args: argv,
|
|
@@ -9604,10 +10613,10 @@ async function runLogin(argv, deps) {
|
|
|
9604
10613
|
});
|
|
9605
10614
|
const provider = positionals[0];
|
|
9606
10615
|
if (!provider) {
|
|
9607
|
-
throw new Error(`login: a <provider> is required (one of ${
|
|
10616
|
+
throw new Error(`login: a <provider> is required (one of ${PROVIDERS2.join("|")})`);
|
|
9608
10617
|
}
|
|
9609
10618
|
if (!isLoginProvider(provider)) {
|
|
9610
|
-
throw new Error(`login: unknown provider '${provider}' (expected ${
|
|
10619
|
+
throw new Error(`login: unknown provider '${provider}' (expected ${PROVIDERS2.join("|")})`);
|
|
9611
10620
|
}
|
|
9612
10621
|
if (!values.config) {
|
|
9613
10622
|
throw new Error("login: --config <path> is required");
|
|
@@ -9623,7 +10632,7 @@ async function runLogin(argv, deps) {
|
|
|
9623
10632
|
setUpstreamProxyResolver2(createUpstreamProxyResolver());
|
|
9624
10633
|
try {
|
|
9625
10634
|
const tokensPath = defaultTokensPath(values.config);
|
|
9626
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => fetchUpstream8(url, init, { providerId: provider }));
|
|
10635
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => fetchUpstream8(url, init, { providerId: provider, redactBodies: true }));
|
|
9627
10636
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
9628
10637
|
const expiresAt = await runProviderLogin(
|
|
9629
10638
|
provider,
|
|
@@ -9713,7 +10722,7 @@ async function loginGemini(store, deps, exchangeFetch, label) {
|
|
|
9713
10722
|
return expiresAt;
|
|
9714
10723
|
}
|
|
9715
10724
|
function isLoginProvider(value) {
|
|
9716
|
-
return
|
|
10725
|
+
return PROVIDERS2.includes(value);
|
|
9717
10726
|
}
|
|
9718
10727
|
async function presentUrl(authUrl, deps) {
|
|
9719
10728
|
console.info("Open this URL in your browser to authorize:");
|
|
@@ -9759,7 +10768,7 @@ function promptPaste(prompt) {
|
|
|
9759
10768
|
}
|
|
9760
10769
|
|
|
9761
10770
|
// src/commands/providers.ts
|
|
9762
|
-
import { randomUUID as
|
|
10771
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
9763
10772
|
import { parseArgs as parseArgs6 } from "util";
|
|
9764
10773
|
async function runProviders(argv) {
|
|
9765
10774
|
const { values, positionals } = parseArgs6({
|
|
@@ -9881,7 +10890,7 @@ function providersAddKey(configPath, providerId, opts) {
|
|
|
9881
10890
|
const cfg = loadConfig(configPath);
|
|
9882
10891
|
const row = cfg.providers.find((p) => p.id === providerId);
|
|
9883
10892
|
if (!row) throw new Error(`providers add-key: unknown provider '${providerId}'`);
|
|
9884
|
-
const entry = { id:
|
|
10893
|
+
const entry = { id: randomUUID7(), apiKey: opts.key };
|
|
9885
10894
|
if (opts.label) entry.label = opts.label;
|
|
9886
10895
|
if (opts.weight !== void 0) {
|
|
9887
10896
|
const w = Number(opts.weight);
|
|
@@ -9909,7 +10918,7 @@ function providersRmKey(configPath, providerId, keyId) {
|
|
|
9909
10918
|
}
|
|
9910
10919
|
|
|
9911
10920
|
// src/commands/secrets.ts
|
|
9912
|
-
import { existsSync as
|
|
10921
|
+
import { existsSync as existsSync21, readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
|
|
9913
10922
|
import { parseArgs as parseArgs7 } from "util";
|
|
9914
10923
|
async function runSecrets(argv) {
|
|
9915
10924
|
const { values, positionals } = parseArgs7({
|
|
@@ -9982,12 +10991,12 @@ function secretsStatus(args) {
|
|
|
9982
10991
|
reportField("admin.token", cfg.admin.token);
|
|
9983
10992
|
}
|
|
9984
10993
|
const tokensPath = defaultTokensPath(args.config);
|
|
9985
|
-
if (
|
|
10994
|
+
if (existsSync21(tokensPath)) {
|
|
9986
10995
|
console.info(`Secret status for ${tokensPath}:`);
|
|
9987
10996
|
reportTokenFields(tokensPath);
|
|
9988
10997
|
}
|
|
9989
10998
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
9990
|
-
if (
|
|
10999
|
+
if (existsSync21(integrationsPath)) {
|
|
9991
11000
|
const state = readRawJson(integrationsPath);
|
|
9992
11001
|
const key = state.gatewayKey;
|
|
9993
11002
|
if (key && typeof key === "object" && !Array.isArray(key)) {
|
|
@@ -10041,8 +11050,8 @@ async function secretsRotate(args) {
|
|
|
10041
11050
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
10042
11051
|
try {
|
|
10043
11052
|
cfg = loadConfig(args.config);
|
|
10044
|
-
if (
|
|
10045
|
-
if (
|
|
11053
|
+
if (existsSync21(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
|
|
11054
|
+
if (existsSync21(integrationsPath)) {
|
|
10046
11055
|
integrationsPlain = new IntegrationStateStore(integrationsPath, oldBox).load();
|
|
10047
11056
|
}
|
|
10048
11057
|
} finally {
|
|
@@ -10077,20 +11086,20 @@ function secretsDecrypt(args) {
|
|
|
10077
11086
|
let tokensPlain = null;
|
|
10078
11087
|
try {
|
|
10079
11088
|
cfg = loadConfig(args.config);
|
|
10080
|
-
if (
|
|
11089
|
+
if (existsSync21(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
|
|
10081
11090
|
} finally {
|
|
10082
11091
|
setSecretBox(null);
|
|
10083
11092
|
}
|
|
10084
11093
|
saveConfig(args.config, cfg);
|
|
10085
11094
|
if (tokensPlain) {
|
|
10086
|
-
|
|
11095
|
+
writeFileSync13(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
|
|
10087
11096
|
}
|
|
10088
11097
|
console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
|
|
10089
11098
|
}
|
|
10090
11099
|
function readRawConfig(path2) {
|
|
10091
11100
|
let parsed;
|
|
10092
11101
|
try {
|
|
10093
|
-
parsed = JSON.parse(
|
|
11102
|
+
parsed = JSON.parse(readFileSync18(path2, "utf8"));
|
|
10094
11103
|
} catch {
|
|
10095
11104
|
throw new Error(`secrets: cannot read or parse '${path2}'`);
|
|
10096
11105
|
}
|
|
@@ -10098,7 +11107,7 @@ function readRawConfig(path2) {
|
|
|
10098
11107
|
}
|
|
10099
11108
|
function readRawJson(path2) {
|
|
10100
11109
|
try {
|
|
10101
|
-
const parsed = JSON.parse(
|
|
11110
|
+
const parsed = JSON.parse(readFileSync18(path2, "utf8"));
|
|
10102
11111
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
10103
11112
|
return parsed;
|
|
10104
11113
|
}
|
|
@@ -10108,13 +11117,13 @@ function readRawJson(path2) {
|
|
|
10108
11117
|
}
|
|
10109
11118
|
function encryptTokensFileInPlace(configPath, box) {
|
|
10110
11119
|
const tokensPath = defaultTokensPath(configPath);
|
|
10111
|
-
if (!
|
|
11120
|
+
if (!existsSync21(tokensPath)) return;
|
|
10112
11121
|
const plain = decryptTokensFile(tokensPath, box);
|
|
10113
11122
|
writeTokensEncrypted(tokensPath, plain, box);
|
|
10114
11123
|
}
|
|
10115
11124
|
function rewriteIntegrationState(configPath, readBox, writeBox) {
|
|
10116
11125
|
const path2 = defaultIntegrationsPath(configPath);
|
|
10117
|
-
if (!
|
|
11126
|
+
if (!existsSync21(path2)) return;
|
|
10118
11127
|
const state = new IntegrationStateStore(path2, readBox).load();
|
|
10119
11128
|
new IntegrationStateStore(path2, writeBox).save(state);
|
|
10120
11129
|
}
|
|
@@ -10127,7 +11136,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
|
|
|
10127
11136
|
{ updatedAt: "", ...plain },
|
|
10128
11137
|
box
|
|
10129
11138
|
);
|
|
10130
|
-
|
|
11139
|
+
writeFileSync13(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
10131
11140
|
}
|
|
10132
11141
|
var TOKEN_FIELDS2 = {
|
|
10133
11142
|
claude: ["accessToken", "refreshToken"],
|
|
@@ -10150,14 +11159,14 @@ function walkTokens(raw, fn) {
|
|
|
10150
11159
|
return next;
|
|
10151
11160
|
}
|
|
10152
11161
|
function tokensSuffix(configPath) {
|
|
10153
|
-
return
|
|
11162
|
+
return existsSync21(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
|
|
10154
11163
|
}
|
|
10155
11164
|
|
|
10156
11165
|
// src/commands/start.ts
|
|
10157
11166
|
import { parseArgs as parseArgs8 } from "util";
|
|
10158
11167
|
import { loadServerConfig as loadServerConfig3 } from "@omnicross/core/outbound-api";
|
|
10159
|
-
import { getSharedAccountHealth as
|
|
10160
|
-
import { getSharedAccountAllowanceScheduling as
|
|
11168
|
+
import { getSharedAccountHealth as getSharedAccountHealth5 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
11169
|
+
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling6 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
10161
11170
|
|
|
10162
11171
|
// src/identity/identityRuntime.ts
|
|
10163
11172
|
import { getSharedIdentityStore as getSharedIdentityStore3 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
|
|
@@ -10217,11 +11226,11 @@ async function runStart(argv) {
|
|
|
10217
11226
|
await daemon.llmConfig.ready();
|
|
10218
11227
|
await daemon.providerProxy.start();
|
|
10219
11228
|
const serverConfig = await loadServerConfig3(daemon.settingsStore);
|
|
10220
|
-
|
|
11229
|
+
getSharedAccountHealth5().configure({
|
|
10221
11230
|
overloadEnabled: serverConfig.accountHealth?.overloadCooldownEnabled,
|
|
10222
11231
|
overloadTtlMs: serverConfig.accountHealth?.overloadCooldownMs
|
|
10223
11232
|
});
|
|
10224
|
-
|
|
11233
|
+
getSharedAccountAllowanceScheduling6().configure(serverConfig.allowanceScheduling);
|
|
10225
11234
|
daemon.claudeAllowanceRefreshScheduler.configure(serverConfig.allowanceScheduling);
|
|
10226
11235
|
await daemon.outboundApiServer.applyConfig({
|
|
10227
11236
|
enabled: true,
|