@omnicross/daemon 0.1.7 → 0.1.9
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 +559 -99
- package/dist/cli.js +584 -114
- package/dist/index.cjs +498 -52
- package/dist/index.d.cts +20 -7
- package/dist/index.d.ts +20 -7
- package/dist/index.js +523 -65
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
normalizeServerConfig
|
|
13
13
|
} from "@omnicross/core/outbound-api";
|
|
14
14
|
import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api/subscriptionRegistryPort";
|
|
15
|
-
import { getSharedAccountHealth as
|
|
15
|
+
import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
16
16
|
import {
|
|
17
17
|
__resetSharedAccountAllowanceStoreForTests,
|
|
18
18
|
AccountAllowanceStore as AccountAllowanceStore3,
|
|
@@ -20,15 +20,18 @@ import {
|
|
|
20
20
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
21
21
|
import {
|
|
22
22
|
__resetSharedAccountAllowanceSchedulingForTests,
|
|
23
|
-
getSharedAccountAllowanceScheduling as
|
|
23
|
+
getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
|
|
24
24
|
} from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
25
25
|
import { fetchUpstream as fetchUpstream7, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
|
|
26
26
|
import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
|
|
27
27
|
import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
|
|
28
28
|
import {
|
|
29
29
|
__resetProviderProxyForTests,
|
|
30
|
-
getProviderProxy
|
|
30
|
+
getProviderProxy,
|
|
31
|
+
RouteLeaseManager,
|
|
32
|
+
RouteLeaseTargetResolver
|
|
31
33
|
} from "@omnicross/core/provider-proxy";
|
|
34
|
+
import { routeLeaseDescriptorPort } from "@omnicross/cli-launcher";
|
|
32
35
|
import { KeySpendTracker } from "@omnicross/core/outbound-api";
|
|
33
36
|
import { PricingEngine, UsageRecorder } from "@omnicross/core/usage";
|
|
34
37
|
import {
|
|
@@ -741,6 +744,93 @@ async function handleWebhookTest(req, res) {
|
|
|
741
744
|
res.end(JSON.stringify({ result }));
|
|
742
745
|
}
|
|
743
746
|
|
|
747
|
+
// src/admin/routeLeaseApi.ts
|
|
748
|
+
import {
|
|
749
|
+
isLoopbackAddress,
|
|
750
|
+
normalizeRouteLeaseTtl,
|
|
751
|
+
ROUTE_LEASE_CAPABILITIES,
|
|
752
|
+
RouteLeaseError
|
|
753
|
+
} from "@omnicross/core/provider-proxy";
|
|
754
|
+
var MAX_BODY_BYTES = 64 * 1024;
|
|
755
|
+
var SAFE_LEASE_ID = /^[A-Za-z0-9-]{1,128}$/u;
|
|
756
|
+
async function readJson(req) {
|
|
757
|
+
const chunks = [];
|
|
758
|
+
let bytes = 0;
|
|
759
|
+
for await (const chunk of req) {
|
|
760
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
761
|
+
bytes += buffer.length;
|
|
762
|
+
if (bytes > MAX_BODY_BYTES) throw new RouteLeaseError("invalid_request", "request body is too large");
|
|
763
|
+
chunks.push(buffer);
|
|
764
|
+
}
|
|
765
|
+
if (chunks.length === 0) return {};
|
|
766
|
+
try {
|
|
767
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
768
|
+
} catch {
|
|
769
|
+
throw new RouteLeaseError("invalid_request", "request body is not valid JSON");
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
function json(res, status, body, noStore = false) {
|
|
773
|
+
res.statusCode = status;
|
|
774
|
+
res.setHeader("Content-Type", "application/json");
|
|
775
|
+
if (noStore) res.setHeader("Cache-Control", "no-store");
|
|
776
|
+
res.end(JSON.stringify(body));
|
|
777
|
+
}
|
|
778
|
+
function leaseId(value) {
|
|
779
|
+
if (!value || !SAFE_LEASE_ID.test(value)) throw new RouteLeaseError("invalid_request", "lease id is invalid");
|
|
780
|
+
return value;
|
|
781
|
+
}
|
|
782
|
+
function header(req, name) {
|
|
783
|
+
const value = req.headers[name.toLowerCase()];
|
|
784
|
+
return Array.isArray(value) ? value[0] : value;
|
|
785
|
+
}
|
|
786
|
+
function writeError(res, error, noStore) {
|
|
787
|
+
const safe = error instanceof RouteLeaseError ? error : new RouteLeaseError("upstream_unavailable", "route lease operation failed safely");
|
|
788
|
+
if (safe.retryAfterSeconds !== void 0) res.setHeader("Retry-After", String(safe.retryAfterSeconds));
|
|
789
|
+
json(res, safe.status, safe.toResponse(), noStore);
|
|
790
|
+
}
|
|
791
|
+
async function handleRouteLeaseApi(req, res, path2, deps) {
|
|
792
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
793
|
+
const noStore = method === "POST" && (path2 === "/admin/api/route-leases" || path2.endsWith("/renew"));
|
|
794
|
+
try {
|
|
795
|
+
if (!isLoopbackAddress(req.socket.remoteAddress)) {
|
|
796
|
+
throw new RouteLeaseError("control_unauthorized", "route lease control plane is loopback only");
|
|
797
|
+
}
|
|
798
|
+
const manager = deps.routeLeaseManager;
|
|
799
|
+
if (!manager) throw new RouteLeaseError("daemon_not_ready", "route lease manager is unavailable");
|
|
800
|
+
const base = "/admin/api/route-leases";
|
|
801
|
+
const suffix = path2.slice(base.length).replace(/^\/+|\/+$/gu, "");
|
|
802
|
+
const segments = suffix ? suffix.split("/") : [];
|
|
803
|
+
if (segments.length === 1 && segments[0] === "capabilities") {
|
|
804
|
+
if (method !== "GET" && method !== "HEAD") throw new RouteLeaseError("invalid_request", "method is not allowed");
|
|
805
|
+
return json(res, 200, ROUTE_LEASE_CAPABILITIES);
|
|
806
|
+
}
|
|
807
|
+
if (segments.length === 0) {
|
|
808
|
+
if (method === "GET") return json(res, 200, { leases: manager.list() });
|
|
809
|
+
if (method === "POST") {
|
|
810
|
+
const outcome = await manager.createFromRequest(await readJson(req), header(req, "idempotency-key"));
|
|
811
|
+
return json(res, outcome.created ? 201 : 200, outcome.result, true);
|
|
812
|
+
}
|
|
813
|
+
throw new RouteLeaseError("invalid_request", "method is not allowed");
|
|
814
|
+
}
|
|
815
|
+
const id = leaseId(segments[0]);
|
|
816
|
+
if (segments.length === 1) {
|
|
817
|
+
if (method === "GET") return json(res, 200, manager.get(id));
|
|
818
|
+
if (method === "DELETE") return json(res, 200, manager.release(id));
|
|
819
|
+
throw new RouteLeaseError("invalid_request", "method is not allowed");
|
|
820
|
+
}
|
|
821
|
+
if (segments.length === 2 && segments[1] === "renew" && method === "POST") {
|
|
822
|
+
const body = await readJson(req);
|
|
823
|
+
const ttl = normalizeRouteLeaseTtl(
|
|
824
|
+
body && typeof body === "object" && !Array.isArray(body) ? body.ttlSeconds : void 0
|
|
825
|
+
);
|
|
826
|
+
return json(res, 200, manager.renew(id, ttl), true);
|
|
827
|
+
}
|
|
828
|
+
throw new RouteLeaseError("lease_not_found", "route lease endpoint was not found");
|
|
829
|
+
} catch (error) {
|
|
830
|
+
writeError(res, error, noStore);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
744
834
|
// src/admin/adminApi.ts
|
|
745
835
|
import http from "http";
|
|
746
836
|
import {
|
|
@@ -1174,6 +1264,35 @@ function validateApiKeys(raw) {
|
|
|
1174
1264
|
}
|
|
1175
1265
|
return out.length > 0 ? out : void 0;
|
|
1176
1266
|
}
|
|
1267
|
+
var THINK_LEVELS = /* @__PURE__ */ new Set([
|
|
1268
|
+
"none",
|
|
1269
|
+
"minimal",
|
|
1270
|
+
"low",
|
|
1271
|
+
"medium",
|
|
1272
|
+
"high",
|
|
1273
|
+
"xhigh",
|
|
1274
|
+
"max"
|
|
1275
|
+
]);
|
|
1276
|
+
function validateThinkingLevels(raw) {
|
|
1277
|
+
if (!Array.isArray(raw)) return void 0;
|
|
1278
|
+
if (!raw.every((level) => typeof level === "string" && THINK_LEVELS.has(level))) {
|
|
1279
|
+
return void 0;
|
|
1280
|
+
}
|
|
1281
|
+
return [...raw];
|
|
1282
|
+
}
|
|
1283
|
+
function validateThinkingTokenLimit(raw) {
|
|
1284
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
1285
|
+
const bounds = raw;
|
|
1286
|
+
const min = bounds["min"];
|
|
1287
|
+
const max = bounds["max"];
|
|
1288
|
+
if (typeof min !== "number" || !Number.isFinite(min) || !Number.isInteger(min) || min < 0) {
|
|
1289
|
+
return void 0;
|
|
1290
|
+
}
|
|
1291
|
+
if (typeof max !== "number" || !Number.isFinite(max) || !Number.isInteger(max) || max < min) {
|
|
1292
|
+
return void 0;
|
|
1293
|
+
}
|
|
1294
|
+
return { min, max };
|
|
1295
|
+
}
|
|
1177
1296
|
function validateModelConfigs(raw) {
|
|
1178
1297
|
if (!Array.isArray(raw)) return void 0;
|
|
1179
1298
|
const out = [];
|
|
@@ -1188,6 +1307,10 @@ function validateModelConfigs(raw) {
|
|
|
1188
1307
|
if (typeof m["enabled"] === "boolean") entry.enabled = m["enabled"];
|
|
1189
1308
|
if (typeof m["vision"] === "boolean") entry.vision = m["vision"];
|
|
1190
1309
|
if (typeof m["reasoning"] === "boolean") entry.reasoning = m["reasoning"];
|
|
1310
|
+
const thinkingLevels = validateThinkingLevels(m["thinkingLevels"]);
|
|
1311
|
+
if (thinkingLevels) entry.thinkingLevels = thinkingLevels;
|
|
1312
|
+
const thinkingTokenLimit = validateThinkingTokenLimit(m["thinkingTokenLimit"]);
|
|
1313
|
+
if (thinkingTokenLimit) entry.thinkingTokenLimit = thinkingTokenLimit;
|
|
1191
1314
|
out.push(entry);
|
|
1192
1315
|
}
|
|
1193
1316
|
return out.length > 0 ? out : void 0;
|
|
@@ -2613,7 +2736,9 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
|
2613
2736
|
// src/admin/cliLaunch.ts
|
|
2614
2737
|
import { exec, spawn } from "child_process";
|
|
2615
2738
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2616
|
-
import { existsSync as existsSync5 } from "fs";
|
|
2739
|
+
import { chmodSync as chmodSync3, existsSync as existsSync5, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
2740
|
+
import { createServer } from "net";
|
|
2741
|
+
import { tmpdir } from "os";
|
|
2617
2742
|
import { delimiter, join as join3 } from "path";
|
|
2618
2743
|
import {
|
|
2619
2744
|
buildChatCliLaunchConfig,
|
|
@@ -2621,6 +2746,33 @@ import {
|
|
|
2621
2746
|
buildCodexLaunchConfig,
|
|
2622
2747
|
buildGeminiCliLaunchConfig
|
|
2623
2748
|
} from "@omnicross/cli-launcher";
|
|
2749
|
+
import {
|
|
2750
|
+
ROUTE_LEASE_REQUEST_SCHEMA,
|
|
2751
|
+
RouteLeaseError as RouteLeaseError2
|
|
2752
|
+
} from "@omnicross/core/provider-proxy";
|
|
2753
|
+
|
|
2754
|
+
// src/routeLeaseRenewal.ts
|
|
2755
|
+
var TERMINAL_LEASE_TTL_SECONDS = 600;
|
|
2756
|
+
var TERMINAL_LEASE_RENEW_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2757
|
+
var TERMINAL_LEASE_MAX_LIFETIME_MS = 24 * 60 * 60 * 1e3;
|
|
2758
|
+
function startTerminalLeaseRenewal(manager, leaseId2) {
|
|
2759
|
+
const stopAt = Date.now() + TERMINAL_LEASE_MAX_LIFETIME_MS;
|
|
2760
|
+
const timer = setInterval(() => {
|
|
2761
|
+
if (Date.now() >= stopAt) {
|
|
2762
|
+
clearInterval(timer);
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
try {
|
|
2766
|
+
manager.renew(leaseId2, TERMINAL_LEASE_TTL_SECONDS);
|
|
2767
|
+
} catch {
|
|
2768
|
+
clearInterval(timer);
|
|
2769
|
+
}
|
|
2770
|
+
}, TERMINAL_LEASE_RENEW_INTERVAL_MS);
|
|
2771
|
+
timer.unref?.();
|
|
2772
|
+
return () => clearInterval(timer);
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
// src/admin/cliLaunch.ts
|
|
2624
2776
|
var LAUNCHABLE_CLIS = [
|
|
2625
2777
|
{ id: "claude", displayName: "Claude Code", command: "claude" },
|
|
2626
2778
|
{ id: "codex", displayName: "Codex CLI", command: "codex" },
|
|
@@ -2701,34 +2853,183 @@ async function buildLaunchEnv(cli, llmConfig, target) {
|
|
|
2701
2853
|
function shq(s) {
|
|
2702
2854
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
2703
2855
|
}
|
|
2704
|
-
var
|
|
2856
|
+
var MAC_TERMINAL_BOOTSTRAP_SOURCE = `
|
|
2857
|
+
'use strict';
|
|
2858
|
+
const fs = require('node:fs');
|
|
2859
|
+
const net = require('node:net');
|
|
2860
|
+
const { spawn } = require('node:child_process');
|
|
2861
|
+
const [socketPath, launchDir, cwd, command, ...args] = process.argv.slice(2);
|
|
2862
|
+
let payload = '';
|
|
2863
|
+
const socket = net.createConnection(socketPath);
|
|
2864
|
+
socket.setEncoding('utf8');
|
|
2865
|
+
socket.on('data', (chunk) => { payload += chunk; });
|
|
2866
|
+
socket.on('end', () => {
|
|
2867
|
+
const descriptor = JSON.parse(payload);
|
|
2868
|
+
if (!descriptor || Array.isArray(descriptor) || Object.values(descriptor).some((value) => typeof value !== 'string')) {
|
|
2869
|
+
throw new Error('invalid terminal launch descriptor');
|
|
2870
|
+
}
|
|
2871
|
+
try { fs.rmSync(launchDir, { recursive: true, force: true }); } catch {}
|
|
2872
|
+
const child = spawn(command, args, {
|
|
2873
|
+
cwd: cwd || undefined,
|
|
2874
|
+
env: { ...process.env, ...descriptor },
|
|
2875
|
+
stdio: 'inherit',
|
|
2876
|
+
});
|
|
2877
|
+
child.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
|
|
2878
|
+
child.on('exit', (code, signal) => {
|
|
2879
|
+
if (signal) process.kill(process.pid, signal);
|
|
2880
|
+
else process.exitCode = code == null ? 1 : code;
|
|
2881
|
+
});
|
|
2882
|
+
});
|
|
2883
|
+
socket.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
|
|
2884
|
+
`;
|
|
2885
|
+
var MAC_TERMINAL_IPC_TIMEOUT_MS = 12e4;
|
|
2886
|
+
function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }, spawnProcess = spawn, macIpc = {}) {
|
|
2705
2887
|
const childEnv = { ...process.env, ...env };
|
|
2706
2888
|
if (platform === "win32") {
|
|
2707
2889
|
const args = ["/c", "start", `"omnicross ${cli}"`];
|
|
2708
2890
|
if (cwd) args.push("/D", `"${cwd}"`);
|
|
2709
2891
|
args.push("cmd", "/k", command, ...extraArgs);
|
|
2710
|
-
|
|
2892
|
+
spawnProcess(process.env["ComSpec"] || "cmd.exe", args, {
|
|
2711
2893
|
env: childEnv,
|
|
2712
2894
|
windowsVerbatimArguments: true,
|
|
2713
2895
|
detached: true,
|
|
2714
2896
|
stdio: "ignore"
|
|
2715
2897
|
}).unref();
|
|
2716
|
-
return
|
|
2898
|
+
return () => {
|
|
2899
|
+
};
|
|
2717
2900
|
}
|
|
2718
|
-
const exportLine = Object.entries(env).map(([k, v]) => `export ${k}=${shq(v)}`).join("; ");
|
|
2719
2901
|
const runLine = [command, ...extraArgs].map(shq).join(" ");
|
|
2720
|
-
const script = `${
|
|
2902
|
+
const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
|
|
2721
2903
|
if (platform === "darwin") {
|
|
2722
|
-
const
|
|
2723
|
-
|
|
2724
|
-
|
|
2904
|
+
const launchDir = mkdtempSync(join3(tmpdir(), "omnicross-terminal-"));
|
|
2905
|
+
const commandFile = join3(launchDir, "launch.command");
|
|
2906
|
+
const bootstrapFile = join3(launchDir, "bootstrap.cjs");
|
|
2907
|
+
const socketPath = macIpc.socketPath ?? join3(launchDir, "descriptor.sock");
|
|
2908
|
+
const openerEnv = { ...process.env };
|
|
2909
|
+
for (const key of Object.keys(env)) delete openerEnv[key];
|
|
2910
|
+
let claimed = false;
|
|
2911
|
+
let cleaned = false;
|
|
2912
|
+
let failureNotified = false;
|
|
2913
|
+
let timer;
|
|
2914
|
+
const notifyFailure = () => {
|
|
2915
|
+
cleanup();
|
|
2916
|
+
if (failureNotified) return;
|
|
2917
|
+
failureNotified = true;
|
|
2918
|
+
try {
|
|
2919
|
+
onFailure?.();
|
|
2920
|
+
} catch {
|
|
2921
|
+
}
|
|
2922
|
+
};
|
|
2923
|
+
const handleLaunchFailure = () => {
|
|
2924
|
+
if (claimed) cleanup();
|
|
2925
|
+
else notifyFailure();
|
|
2926
|
+
};
|
|
2927
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
2928
|
+
const server = createServer((socket) => {
|
|
2929
|
+
socket.unref();
|
|
2930
|
+
sockets.add(socket);
|
|
2931
|
+
socket.once("close", () => sockets.delete(socket));
|
|
2932
|
+
try {
|
|
2933
|
+
macIpc.onAccepted?.(socket);
|
|
2934
|
+
} catch {
|
|
2935
|
+
cleanup();
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2938
|
+
if (claimed || cleaned) {
|
|
2939
|
+
socket.destroy();
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
claimed = true;
|
|
2943
|
+
try {
|
|
2944
|
+
macIpc.onClaimed?.();
|
|
2945
|
+
if (cleaned) return;
|
|
2946
|
+
socket.end(JSON.stringify(env), cleanup);
|
|
2947
|
+
} catch {
|
|
2948
|
+
cleanup();
|
|
2949
|
+
}
|
|
2950
|
+
});
|
|
2951
|
+
const cleanup = () => {
|
|
2952
|
+
if (!cleaned) {
|
|
2953
|
+
cleaned = true;
|
|
2954
|
+
if (timer) clearTimeout(timer);
|
|
2955
|
+
for (const socket of sockets) socket.destroy();
|
|
2956
|
+
sockets.clear();
|
|
2957
|
+
try {
|
|
2958
|
+
server.close();
|
|
2959
|
+
} catch {
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
try {
|
|
2963
|
+
if (macIpc.removeArtifacts) {
|
|
2964
|
+
macIpc.removeArtifacts(launchDir);
|
|
2965
|
+
} else {
|
|
2966
|
+
rmSync2(launchDir, {
|
|
2967
|
+
recursive: true,
|
|
2968
|
+
force: true,
|
|
2969
|
+
maxRetries: 3,
|
|
2970
|
+
retryDelay: 20
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
} catch {
|
|
2974
|
+
}
|
|
2975
|
+
};
|
|
2976
|
+
try {
|
|
2977
|
+
writeFileSync5(bootstrapFile, MAC_TERMINAL_BOOTSTRAP_SOURCE, { encoding: "utf8", mode: 448 });
|
|
2978
|
+
writeFileSync5(commandFile, `#!/bin/bash
|
|
2979
|
+
rm -f -- "$0"
|
|
2980
|
+
exec ${shq(process.execPath)} ${shq(bootstrapFile)} ${shq(socketPath)} ${shq(launchDir)} ${shq(cwd ?? "")} ${runLine}
|
|
2981
|
+
`, {
|
|
2982
|
+
encoding: "utf8",
|
|
2983
|
+
mode: 448
|
|
2984
|
+
});
|
|
2985
|
+
chmodSync3(commandFile, 448);
|
|
2986
|
+
chmodSync3(bootstrapFile, 448);
|
|
2987
|
+
server.once("error", handleLaunchFailure);
|
|
2988
|
+
server.listen(socketPath, () => {
|
|
2989
|
+
if (cleaned) return;
|
|
2990
|
+
try {
|
|
2991
|
+
macIpc.onListening?.();
|
|
2992
|
+
if (cleaned) return;
|
|
2993
|
+
if (process.platform !== "win32") chmodSync3(socketPath, 384);
|
|
2994
|
+
const opener = spawnProcess("open", ["-n", "-a", "Terminal", commandFile], {
|
|
2995
|
+
env: openerEnv,
|
|
2996
|
+
detached: true,
|
|
2997
|
+
stdio: "ignore"
|
|
2998
|
+
});
|
|
2999
|
+
opener.once("error", handleLaunchFailure);
|
|
3000
|
+
opener.unref();
|
|
3001
|
+
server.unref();
|
|
3002
|
+
} catch {
|
|
3003
|
+
handleLaunchFailure();
|
|
3004
|
+
}
|
|
3005
|
+
});
|
|
3006
|
+
timer = setTimeout(handleLaunchFailure, macIpc.timeoutMs ?? MAC_TERMINAL_IPC_TIMEOUT_MS);
|
|
3007
|
+
timer.unref?.();
|
|
3008
|
+
return cleanup;
|
|
3009
|
+
} catch (error) {
|
|
3010
|
+
cleanup();
|
|
3011
|
+
throw error;
|
|
3012
|
+
}
|
|
2725
3013
|
}
|
|
2726
|
-
|
|
3014
|
+
spawnProcess("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
|
|
3015
|
+
env: childEnv,
|
|
2727
3016
|
detached: true,
|
|
2728
3017
|
stdio: "ignore"
|
|
2729
3018
|
}).unref();
|
|
2730
|
-
|
|
3019
|
+
return () => {
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
var defaultTerminalOpener = (input) => openTerminal(input);
|
|
2731
3023
|
var sessions = /* @__PURE__ */ new Map();
|
|
3024
|
+
function resetCliSessions() {
|
|
3025
|
+
for (const s of sessions.values()) {
|
|
3026
|
+
try {
|
|
3027
|
+
s.onSessionEnd();
|
|
3028
|
+
} catch {
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
sessions.clear();
|
|
3032
|
+
}
|
|
2732
3033
|
function errBody(message) {
|
|
2733
3034
|
return { error: { type: "admin_api_error", message } };
|
|
2734
3035
|
}
|
|
@@ -2783,29 +3084,81 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
2783
3084
|
} catch (err5) {
|
|
2784
3085
|
return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "no launch target") };
|
|
2785
3086
|
}
|
|
3087
|
+
const id = randomUUID2();
|
|
3088
|
+
let leaseId2;
|
|
2786
3089
|
let launch;
|
|
2787
3090
|
try {
|
|
2788
|
-
|
|
3091
|
+
if ((cli === "claude" || cli === "codex") && ctx.routeLeaseManager) {
|
|
3092
|
+
const outcome = await ctx.routeLeaseManager.createFromRequest({
|
|
3093
|
+
schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA,
|
|
3094
|
+
consumer: "omnicross-terminal",
|
|
3095
|
+
runtime: cli,
|
|
3096
|
+
upstream: { kind: "provider", providerId: target.providerId },
|
|
3097
|
+
model: target.model,
|
|
3098
|
+
execution: { sessionId: id }
|
|
3099
|
+
}, `omnicross-terminal:${id}`);
|
|
3100
|
+
leaseId2 = outcome.result.leaseId;
|
|
3101
|
+
const stopRenewal = startTerminalLeaseRenewal(ctx.routeLeaseManager, leaseId2);
|
|
3102
|
+
launch = {
|
|
3103
|
+
env: outcome.result.launch.env,
|
|
3104
|
+
extraArgs: outcome.result.launch.extraArgs,
|
|
3105
|
+
onSessionEnd: () => {
|
|
3106
|
+
stopRenewal();
|
|
3107
|
+
ctx.routeLeaseManager?.release(outcome.result.leaseId);
|
|
3108
|
+
}
|
|
3109
|
+
};
|
|
3110
|
+
} else {
|
|
3111
|
+
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
3112
|
+
}
|
|
2789
3113
|
} catch (err5) {
|
|
2790
|
-
|
|
3114
|
+
const status = err5 instanceof RouteLeaseError2 ? err5.status : 400;
|
|
3115
|
+
return { status, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
|
|
2791
3116
|
}
|
|
2792
3117
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
2793
3118
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
3119
|
+
let openerCleanup;
|
|
3120
|
+
let ended = false;
|
|
3121
|
+
let published = false;
|
|
3122
|
+
const onSessionEnd = () => {
|
|
3123
|
+
if (ended) return;
|
|
3124
|
+
ended = true;
|
|
3125
|
+
if (published) sessions.delete(id);
|
|
3126
|
+
try {
|
|
3127
|
+
openerCleanup?.();
|
|
3128
|
+
} finally {
|
|
3129
|
+
launch.onSessionEnd();
|
|
3130
|
+
}
|
|
3131
|
+
};
|
|
2794
3132
|
try {
|
|
2795
|
-
opener({
|
|
3133
|
+
const cleanup = opener({
|
|
3134
|
+
cli,
|
|
3135
|
+
command: meta.command,
|
|
3136
|
+
extraArgs: launch.extraArgs ?? [],
|
|
3137
|
+
env: launch.env,
|
|
3138
|
+
cwd,
|
|
3139
|
+
platform,
|
|
3140
|
+
onFailure: onSessionEnd
|
|
3141
|
+
});
|
|
3142
|
+
if (cleanup) openerCleanup = cleanup;
|
|
2796
3143
|
} catch (err5) {
|
|
2797
|
-
|
|
3144
|
+
onSessionEnd();
|
|
2798
3145
|
return { status: 500, body: errBody(err5 instanceof Error ? err5.message : "failed to open terminal") };
|
|
2799
3146
|
}
|
|
2800
|
-
|
|
3147
|
+
if (ended) {
|
|
3148
|
+
openerCleanup?.();
|
|
3149
|
+
return { status: 500, body: errBody("failed to open terminal") };
|
|
3150
|
+
}
|
|
2801
3151
|
sessions.set(id, {
|
|
2802
3152
|
id,
|
|
2803
3153
|
cli,
|
|
2804
3154
|
providerId: target.providerId,
|
|
2805
3155
|
model: target.model,
|
|
3156
|
+
...leaseId2 ? { leaseId: leaseId2 } : {},
|
|
2806
3157
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2807
|
-
onSessionEnd
|
|
3158
|
+
onSessionEnd
|
|
2808
3159
|
});
|
|
3160
|
+
published = true;
|
|
3161
|
+
if (ended) sessions.delete(id);
|
|
2809
3162
|
return { status: 200, body: { sessionId: id, providerId: target.providerId, model: target.model } };
|
|
2810
3163
|
}
|
|
2811
3164
|
|
|
@@ -3707,7 +4060,7 @@ function sealPack(bundleJson, passphrase) {
|
|
|
3707
4060
|
cipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
|
|
3708
4061
|
const ciphertext = Buffer.concat([cipher.update(bundleJson, "utf8"), cipher.final()]);
|
|
3709
4062
|
const tag = cipher.getAuthTag();
|
|
3710
|
-
const
|
|
4063
|
+
const header2 = {
|
|
3711
4064
|
magic: PACK_MAGIC,
|
|
3712
4065
|
v: PACK_VERSION,
|
|
3713
4066
|
kdf: KDF_ALGORITHM,
|
|
@@ -3718,7 +4071,7 @@ function sealPack(bundleJson, passphrase) {
|
|
|
3718
4071
|
iv: iv.toString("base64"),
|
|
3719
4072
|
tag: tag.toString("base64")
|
|
3720
4073
|
};
|
|
3721
|
-
return `${PACK_PREFIX}${toB64Url(JSON.stringify(
|
|
4074
|
+
return `${PACK_PREFIX}${toB64Url(JSON.stringify(header2))}.${ciphertext.toString("base64")}`;
|
|
3722
4075
|
}
|
|
3723
4076
|
function parsePack(packString) {
|
|
3724
4077
|
if (typeof packString !== "string" || !packString.startsWith(PACK_PREFIX)) {
|
|
@@ -3729,28 +4082,28 @@ function parsePack(packString) {
|
|
|
3729
4082
|
if (dot < 0) throw new PackAuthError("migration pack is malformed (missing body)");
|
|
3730
4083
|
const headerB64Url = rest.slice(0, dot);
|
|
3731
4084
|
const ctB64 = rest.slice(dot + 1);
|
|
3732
|
-
let
|
|
4085
|
+
let header2;
|
|
3733
4086
|
try {
|
|
3734
|
-
|
|
4087
|
+
header2 = JSON.parse(fromB64Url(headerB64Url));
|
|
3735
4088
|
} catch {
|
|
3736
4089
|
throw new PackAuthError("migration pack is malformed (unreadable header)");
|
|
3737
4090
|
}
|
|
3738
|
-
if (!
|
|
4091
|
+
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") {
|
|
3739
4092
|
throw new PackAuthError("migration pack is malformed (unsupported header)");
|
|
3740
4093
|
}
|
|
3741
4094
|
const ciphertext = Buffer.from(ctB64, "base64");
|
|
3742
|
-
return { header, ciphertext };
|
|
4095
|
+
return { header: header2, ciphertext };
|
|
3743
4096
|
}
|
|
3744
4097
|
function openPack(packString, passphrase) {
|
|
3745
4098
|
assertPassphraseStrength(passphrase);
|
|
3746
|
-
const { header, ciphertext } = parsePack(packString);
|
|
3747
|
-
const salt = Buffer.from(
|
|
3748
|
-
const iv = Buffer.from(
|
|
3749
|
-
const tag = Buffer.from(
|
|
4099
|
+
const { header: header2, ciphertext } = parsePack(packString);
|
|
4100
|
+
const salt = Buffer.from(header2.salt, "base64");
|
|
4101
|
+
const iv = Buffer.from(header2.iv, "base64");
|
|
4102
|
+
const tag = Buffer.from(header2.tag, "base64");
|
|
3750
4103
|
if (iv.length !== IV_BYTES2 || tag.length !== TAG_BYTES2) {
|
|
3751
4104
|
throw new PackAuthError("migration pack is malformed (invalid iv/tag length)");
|
|
3752
4105
|
}
|
|
3753
|
-
const key = deriveKey(passphrase, salt,
|
|
4106
|
+
const key = deriveKey(passphrase, salt, header2.N, header2.r, header2.p);
|
|
3754
4107
|
const decipher = createDecipheriv2("aes-256-gcm", key, iv);
|
|
3755
4108
|
decipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
|
|
3756
4109
|
decipher.setAuthTag(tag);
|
|
@@ -4086,10 +4439,10 @@ function writeJson2(res, status, body) {
|
|
|
4086
4439
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
4087
4440
|
res.end(JSON.stringify(body));
|
|
4088
4441
|
}
|
|
4089
|
-
function
|
|
4442
|
+
function writeError2(res, status, message) {
|
|
4090
4443
|
writeJson2(res, status, { error: { type: "account_allowance_error", message } });
|
|
4091
4444
|
}
|
|
4092
|
-
function
|
|
4445
|
+
function readJson2(req) {
|
|
4093
4446
|
return new Promise((resolve2, reject) => {
|
|
4094
4447
|
const chunks = [];
|
|
4095
4448
|
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
@@ -4115,10 +4468,10 @@ function allowanceProvider(value) {
|
|
|
4115
4468
|
return value === "claude" || value === "codex" ? value : null;
|
|
4116
4469
|
}
|
|
4117
4470
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
4118
|
-
if (!service) return
|
|
4471
|
+
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
4119
4472
|
if (method === "GET" && rest.length === 1 && rest[0] === "scheduling") {
|
|
4120
4473
|
if (!service.getSchedulingStatus) {
|
|
4121
|
-
return
|
|
4474
|
+
return writeError2(res, 501, "allowance scheduling diagnostics are not available");
|
|
4122
4475
|
}
|
|
4123
4476
|
return writeJson2(res, 200, { scheduling: service.getSchedulingStatus() });
|
|
4124
4477
|
}
|
|
@@ -4126,27 +4479,27 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4126
4479
|
const params = query(req);
|
|
4127
4480
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
4128
4481
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
4129
|
-
if (providerId === null) return
|
|
4482
|
+
if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
|
|
4130
4483
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
4131
4484
|
const allowances = await service.list({ providerId, accountId });
|
|
4132
4485
|
return writeJson2(res, 200, { allowances });
|
|
4133
4486
|
}
|
|
4134
4487
|
if (method === "POST" && rest[0] === "refresh") {
|
|
4135
|
-
const body = await
|
|
4488
|
+
const body = await readJson2(req);
|
|
4136
4489
|
const requestedProvider = allowanceProvider(
|
|
4137
4490
|
typeof body["providerId"] === "string" ? body["providerId"] : "claude"
|
|
4138
4491
|
);
|
|
4139
4492
|
if (requestedProvider !== "claude") {
|
|
4140
|
-
return
|
|
4493
|
+
return writeError2(res, 400, "only Claude allowances support explicit refresh");
|
|
4141
4494
|
}
|
|
4142
4495
|
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
4143
4496
|
const allowances = await service.refreshClaude(accountId);
|
|
4144
4497
|
if (accountId && allowances.length === 0) {
|
|
4145
|
-
return
|
|
4498
|
+
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
4146
4499
|
}
|
|
4147
4500
|
return writeJson2(res, 200, { allowances });
|
|
4148
4501
|
}
|
|
4149
|
-
return
|
|
4502
|
+
return writeError2(res, 405, `method ${method} not allowed on account allowances`);
|
|
4150
4503
|
}
|
|
4151
4504
|
|
|
4152
4505
|
// src/admin/adminApi.ts
|
|
@@ -4724,6 +5077,12 @@ function parseModelConfigsInput(raw, existing) {
|
|
|
4724
5077
|
else if (typeof prior?.vision === "boolean") entry.vision = prior.vision;
|
|
4725
5078
|
if (typeof m["reasoning"] === "boolean") entry.reasoning = m["reasoning"];
|
|
4726
5079
|
else if (typeof prior?.reasoning === "boolean") entry.reasoning = prior.reasoning;
|
|
5080
|
+
const thinkingLevels = validateThinkingLevels(m["thinkingLevels"]);
|
|
5081
|
+
if (thinkingLevels) entry.thinkingLevels = thinkingLevels;
|
|
5082
|
+
else if (prior?.thinkingLevels) entry.thinkingLevels = prior.thinkingLevels;
|
|
5083
|
+
const thinkingTokenLimit = validateThinkingTokenLimit(m["thinkingTokenLimit"]);
|
|
5084
|
+
if (thinkingTokenLimit) entry.thinkingTokenLimit = thinkingTokenLimit;
|
|
5085
|
+
else if (prior?.thinkingTokenLimit) entry.thinkingTokenLimit = prior.thinkingTokenLimit;
|
|
4727
5086
|
out.push(entry);
|
|
4728
5087
|
}
|
|
4729
5088
|
return out.length > 0 ? out : void 0;
|
|
@@ -5381,6 +5740,7 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
5381
5740
|
const result = await handleCliLaunch(cli, body, {
|
|
5382
5741
|
llmConfig: deps.llmConfig,
|
|
5383
5742
|
providers,
|
|
5743
|
+
routeLeaseManager: deps.routeLeaseManager,
|
|
5384
5744
|
opener: deps.cliTerminalOpener,
|
|
5385
5745
|
probe: deps.cliPathProbe
|
|
5386
5746
|
});
|
|
@@ -5643,7 +6003,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
5643
6003
|
}
|
|
5644
6004
|
|
|
5645
6005
|
// src/admin/version.ts
|
|
5646
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
6006
|
+
var DAEMON_VERSION = true ? "0.1.9" : "0.0.0-dev";
|
|
5647
6007
|
|
|
5648
6008
|
// src/admin/AdminServer.ts
|
|
5649
6009
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -5763,6 +6123,10 @@ var AdminServer = class {
|
|
|
5763
6123
|
await handleWebhookTest(req, res);
|
|
5764
6124
|
return;
|
|
5765
6125
|
}
|
|
6126
|
+
if (path2 === "/admin/api/route-leases" || path2.startsWith("/admin/api/route-leases/")) {
|
|
6127
|
+
await handleRouteLeaseApi(req, res, path2, this.deps);
|
|
6128
|
+
return;
|
|
6129
|
+
}
|
|
5766
6130
|
if (path2.startsWith("/admin/api/")) {
|
|
5767
6131
|
await handleAdminApi(req, res, path2, this.deps);
|
|
5768
6132
|
return;
|
|
@@ -5774,8 +6138,8 @@ var AdminServer = class {
|
|
|
5774
6138
|
}
|
|
5775
6139
|
/** Constant-time bearer/header check against the configured token. */
|
|
5776
6140
|
isAuthorized(req, token) {
|
|
5777
|
-
const
|
|
5778
|
-
const bearer = typeof
|
|
6141
|
+
const header2 = req.headers["authorization"];
|
|
6142
|
+
const bearer = typeof header2 === "string" && header2.startsWith("Bearer ") ? header2.slice("Bearer ".length).trim() : void 0;
|
|
5779
6143
|
const xToken = req.headers["x-admin-token"];
|
|
5780
6144
|
const presented = bearer ?? (typeof xToken === "string" ? xToken.trim() : void 0);
|
|
5781
6145
|
return constantTimeEquals(presented, token);
|
|
@@ -5910,7 +6274,7 @@ var OAuthSessionStore = class {
|
|
|
5910
6274
|
};
|
|
5911
6275
|
|
|
5912
6276
|
// src/commands/loopbackCallback.ts
|
|
5913
|
-
import { createServer } from "http";
|
|
6277
|
+
import { createServer as createServer2 } from "http";
|
|
5914
6278
|
var LOOPBACK_HOST = "127.0.0.1";
|
|
5915
6279
|
var LOOPBACK_PORT = 1455;
|
|
5916
6280
|
var CALLBACK_PATH = "/auth/callback";
|
|
@@ -5932,7 +6296,7 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
5932
6296
|
fn();
|
|
5933
6297
|
server2.close();
|
|
5934
6298
|
};
|
|
5935
|
-
const server =
|
|
6299
|
+
const server = createServer2((req, res) => {
|
|
5936
6300
|
const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
|
|
5937
6301
|
if (url.pathname !== CALLBACK_PATH) {
|
|
5938
6302
|
res.writeHead(404, HTML_HEADERS);
|
|
@@ -6210,6 +6574,15 @@ function toLLMProvider(row) {
|
|
|
6210
6574
|
api_base_url: row.baseUrl,
|
|
6211
6575
|
api_key: resolvePreferredApiKey(row),
|
|
6212
6576
|
models,
|
|
6577
|
+
modelConfigs: row.modelConfigs?.map((config) => ({
|
|
6578
|
+
id: config.id,
|
|
6579
|
+
name: config.name ?? config.id,
|
|
6580
|
+
enabled: config.enabled ?? true,
|
|
6581
|
+
vision: config.vision,
|
|
6582
|
+
reasoning: config.reasoning,
|
|
6583
|
+
thinkingLevels: config.thinkingLevels,
|
|
6584
|
+
thinkingTokenLimit: config.thinkingTokenLimit
|
|
6585
|
+
})),
|
|
6213
6586
|
enabled: true,
|
|
6214
6587
|
transformer,
|
|
6215
6588
|
// app-parity-2 child 3: POPULATE the coding-plan endpoint onto the core
|
|
@@ -6381,7 +6754,7 @@ function safeStringify(value) {
|
|
|
6381
6754
|
}
|
|
6382
6755
|
|
|
6383
6756
|
// src/ports/JsonApiServerSettingsStore.ts
|
|
6384
|
-
import { readFileSync as readFileSync6, writeFileSync as
|
|
6757
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
6385
6758
|
import { OUTBOUND_API_SERVER_CONFIG_KEY } from "@omnicross/core/outbound-api";
|
|
6386
6759
|
var JsonApiServerSettingsStore = class {
|
|
6387
6760
|
/**
|
|
@@ -6408,7 +6781,7 @@ var JsonApiServerSettingsStore = class {
|
|
|
6408
6781
|
if (key !== OUTBOUND_API_SERVER_CONFIG_KEY) return;
|
|
6409
6782
|
const file = this.readFile();
|
|
6410
6783
|
file.server = this.encryptSecrets(value);
|
|
6411
|
-
|
|
6784
|
+
writeFileSync6(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
|
|
6412
6785
|
}
|
|
6413
6786
|
/** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
|
|
6414
6787
|
encryptSecrets(config) {
|
|
@@ -6762,7 +7135,7 @@ function median(values) {
|
|
|
6762
7135
|
}
|
|
6763
7136
|
|
|
6764
7137
|
// src/ports/JsonOutboundKeyDb.ts
|
|
6765
|
-
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as
|
|
7138
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
6766
7139
|
var JsonOutboundKeyDb = class {
|
|
6767
7140
|
/**
|
|
6768
7141
|
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
@@ -6904,7 +7277,7 @@ var JsonOutboundKeyDb = class {
|
|
|
6904
7277
|
}
|
|
6905
7278
|
}
|
|
6906
7279
|
writeRows(rows) {
|
|
6907
|
-
|
|
7280
|
+
writeFileSync7(this.keysPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
6908
7281
|
}
|
|
6909
7282
|
};
|
|
6910
7283
|
function applyPolicyField(row, field, value) {
|
|
@@ -6914,7 +7287,7 @@ function applyPolicyField(row, field, value) {
|
|
|
6914
7287
|
}
|
|
6915
7288
|
|
|
6916
7289
|
// src/ports/JsonPricingStore.ts
|
|
6917
|
-
import { existsSync as existsSync9, readFileSync as readFileSync9, renameSync as renameSync3, rmSync as
|
|
7290
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
6918
7291
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
6919
7292
|
var JsonPricingStore = class {
|
|
6920
7293
|
constructor(pricingPath) {
|
|
@@ -7055,13 +7428,13 @@ var JsonPricingStore = class {
|
|
|
7055
7428
|
writeRows(rows) {
|
|
7056
7429
|
const temporaryPath = `${this.pricingPath}.${process.pid}.${randomUUID5()}.tmp`;
|
|
7057
7430
|
try {
|
|
7058
|
-
|
|
7431
|
+
writeFileSync8(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
7059
7432
|
encoding: "utf8",
|
|
7060
7433
|
flag: "wx"
|
|
7061
7434
|
});
|
|
7062
7435
|
this.replaceFile(temporaryPath);
|
|
7063
7436
|
} finally {
|
|
7064
|
-
|
|
7437
|
+
rmSync3(temporaryPath, { force: true });
|
|
7065
7438
|
}
|
|
7066
7439
|
}
|
|
7067
7440
|
/** Isolated for deterministic failure testing; never removes the target. */
|
|
@@ -7076,7 +7449,7 @@ function isUsablePricingRow(value) {
|
|
|
7076
7449
|
}
|
|
7077
7450
|
|
|
7078
7451
|
// src/pricing/PricingRefreshScheduler.ts
|
|
7079
|
-
import { existsSync as existsSync10, readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as
|
|
7452
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
7080
7453
|
var EMPTY_STATE2 = {
|
|
7081
7454
|
lastAttemptAt: null,
|
|
7082
7455
|
lastSuccessAt: null,
|
|
@@ -7169,7 +7542,7 @@ var PricingRefreshScheduler = class {
|
|
|
7169
7542
|
}
|
|
7170
7543
|
writeState(state) {
|
|
7171
7544
|
const temporaryPath = `${this.statePath}.tmp`;
|
|
7172
|
-
|
|
7545
|
+
writeFileSync9(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
7173
7546
|
`, "utf8");
|
|
7174
7547
|
renameSync4(temporaryPath, this.statePath);
|
|
7175
7548
|
}
|
|
@@ -7179,7 +7552,7 @@ function finiteOrNull(value) {
|
|
|
7179
7552
|
}
|
|
7180
7553
|
|
|
7181
7554
|
// src/ports/JsonVoucherDb.ts
|
|
7182
|
-
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as
|
|
7555
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
|
|
7183
7556
|
var JsonVoucherDb = class {
|
|
7184
7557
|
constructor(vouchersPath) {
|
|
7185
7558
|
this.vouchersPath = vouchersPath;
|
|
@@ -7266,12 +7639,12 @@ var JsonVoucherDb = class {
|
|
|
7266
7639
|
}
|
|
7267
7640
|
}
|
|
7268
7641
|
writeRows(rows) {
|
|
7269
|
-
|
|
7642
|
+
writeFileSync10(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
7270
7643
|
}
|
|
7271
7644
|
};
|
|
7272
7645
|
|
|
7273
7646
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
7274
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, writeFileSync as
|
|
7647
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
7275
7648
|
import { dirname as dirname6 } from "path";
|
|
7276
7649
|
import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
7277
7650
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
@@ -7954,7 +8327,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7954
8327
|
persist(config) {
|
|
7955
8328
|
mkdirSync4(dirname6(this.tokensPath), { recursive: true });
|
|
7956
8329
|
const encrypted = encryptTokens(config, this.box);
|
|
7957
|
-
|
|
8330
|
+
writeFileSync11(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
7958
8331
|
}
|
|
7959
8332
|
/**
|
|
7960
8333
|
* Read + parse `tokens.json`, tolerating a missing/corrupt file, then DECRYPT
|
|
@@ -8523,7 +8896,7 @@ import {
|
|
|
8523
8896
|
readFileSync as readFileSync14,
|
|
8524
8897
|
readdirSync,
|
|
8525
8898
|
statSync as statSync3,
|
|
8526
|
-
writeFileSync as
|
|
8899
|
+
writeFileSync as writeFileSync12
|
|
8527
8900
|
} from "fs";
|
|
8528
8901
|
import { basename, dirname as dirname7, join as join6 } from "path";
|
|
8529
8902
|
var SIDECAR_VERSION = 1;
|
|
@@ -8565,7 +8938,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
|
|
|
8565
8938
|
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
8566
8939
|
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
8567
8940
|
};
|
|
8568
|
-
|
|
8941
|
+
writeFileSync12(statsPath, JSON.stringify(next), "utf8");
|
|
8569
8942
|
}
|
|
8570
8943
|
function queryCovers(stats, from, to) {
|
|
8571
8944
|
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
@@ -8709,7 +9082,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
8709
9082
|
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
8710
9083
|
total.complete = total.complete && scanned.filtered.complete;
|
|
8711
9084
|
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
8712
|
-
if (current.complete)
|
|
9085
|
+
if (current.complete) writeFileSync12(statsPath, JSON.stringify(current), "utf8");
|
|
8713
9086
|
} catch {
|
|
8714
9087
|
total.complete = false;
|
|
8715
9088
|
}
|
|
@@ -9271,6 +9644,78 @@ var TokenRefreshScheduler = class {
|
|
|
9271
9644
|
}
|
|
9272
9645
|
};
|
|
9273
9646
|
|
|
9647
|
+
// src/routeLeaseSubscriptionPreflight.ts
|
|
9648
|
+
import {
|
|
9649
|
+
RouteLeaseError as RouteLeaseError3
|
|
9650
|
+
} from "@omnicross/core/provider-proxy";
|
|
9651
|
+
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
9652
|
+
import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
9653
|
+
import { accountSupportsModel } from "@omnicross/subscriptions/scheduler/accountModelMap";
|
|
9654
|
+
var PROVIDERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "opencodego"]);
|
|
9655
|
+
function accountArray(config, providerId) {
|
|
9656
|
+
const record = config;
|
|
9657
|
+
const key = `${providerId}Accounts`;
|
|
9658
|
+
const accounts = record[key];
|
|
9659
|
+
if (Array.isArray(accounts)) return accounts;
|
|
9660
|
+
const legacy = record[providerId];
|
|
9661
|
+
if (!legacy || typeof legacy !== "object") return [];
|
|
9662
|
+
const activeKey = `active${providerId[0].toUpperCase()}${providerId.slice(1)}AccountId`;
|
|
9663
|
+
return [{ id: String(record[activeKey] ?? "active"), enabled: true, tokens: legacy }];
|
|
9664
|
+
}
|
|
9665
|
+
function hasCredential(providerId, account) {
|
|
9666
|
+
const tokens = account.tokens;
|
|
9667
|
+
if (providerId === "opencodego") return typeof tokens.apiKey === "string" && tokens.apiKey.length > 0;
|
|
9668
|
+
return typeof tokens.accessToken === "string" && tokens.accessToken.length > 0;
|
|
9669
|
+
}
|
|
9670
|
+
function safeProviderId(value) {
|
|
9671
|
+
if (!PROVIDERS.has(value)) {
|
|
9672
|
+
throw new RouteLeaseError3("upstream_not_found", "subscription provider was not found");
|
|
9673
|
+
}
|
|
9674
|
+
return value;
|
|
9675
|
+
}
|
|
9676
|
+
function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
9677
|
+
return {
|
|
9678
|
+
async assertAvailable(upstream, model) {
|
|
9679
|
+
const providerId = safeProviderId(upstream.providerId);
|
|
9680
|
+
const config = await credentials.getFullConfig();
|
|
9681
|
+
const all = accountArray(config, providerId);
|
|
9682
|
+
if (all.length === 0) {
|
|
9683
|
+
throw new RouteLeaseError3("upstream_unavailable", "subscription provider has no configured account");
|
|
9684
|
+
}
|
|
9685
|
+
let bounded = all;
|
|
9686
|
+
if (upstream.kind === "account") {
|
|
9687
|
+
bounded = all.filter((account) => account.id === upstream.accountId);
|
|
9688
|
+
} else if (upstream.kind === "account-group") {
|
|
9689
|
+
bounded = all.filter((account) => account.group?.trim() === upstream.group);
|
|
9690
|
+
}
|
|
9691
|
+
if (bounded.length === 0) {
|
|
9692
|
+
throw new RouteLeaseError3("upstream_not_found", "the selected subscription resource was not found");
|
|
9693
|
+
}
|
|
9694
|
+
const modelEligible = bounded.filter(
|
|
9695
|
+
(account) => accountSupportsModel(account.supportedModels, model)
|
|
9696
|
+
);
|
|
9697
|
+
if (modelEligible.length === 0) {
|
|
9698
|
+
throw new RouteLeaseError3("model_not_configured", "model is not supported by the selected subscription resource");
|
|
9699
|
+
}
|
|
9700
|
+
const credentialEligible = modelEligible.filter(
|
|
9701
|
+
(account) => account.enabled !== false && hasCredential(providerId, account)
|
|
9702
|
+
);
|
|
9703
|
+
const health2 = getSharedAccountHealth3();
|
|
9704
|
+
const allowance = getSharedAccountAllowanceScheduling4();
|
|
9705
|
+
const candidates = credentialEligible.filter(
|
|
9706
|
+
(account) => health2.isSchedulable(providerId, account.id) && allowance.preview(providerId, account.id, account.priority ?? 50).schedulable
|
|
9707
|
+
);
|
|
9708
|
+
if (candidates.length > 0) return;
|
|
9709
|
+
if (upstream.kind === "account") {
|
|
9710
|
+
throw new RouteLeaseError3("upstream_unavailable", "the selected subscription account is unavailable");
|
|
9711
|
+
}
|
|
9712
|
+
throw new RouteLeaseError3("upstream_exhausted", "the selected subscription pool has no eligible account", {
|
|
9713
|
+
retryAfterSeconds: 30
|
|
9714
|
+
});
|
|
9715
|
+
}
|
|
9716
|
+
};
|
|
9717
|
+
}
|
|
9718
|
+
|
|
9274
9719
|
// src/webhook/WebhookDispatcher.ts
|
|
9275
9720
|
import { createHmac as createHmac2 } from "crypto";
|
|
9276
9721
|
import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
@@ -9453,7 +9898,7 @@ function buildDaemon(config, paths) {
|
|
|
9453
9898
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
9454
9899
|
);
|
|
9455
9900
|
setSharedAccountAllowanceStore(accountAllowanceStore);
|
|
9456
|
-
|
|
9901
|
+
getSharedAccountAllowanceScheduling5().configure(
|
|
9457
9902
|
normalizeServerConfig(decryptedConfig.server).allowanceScheduling
|
|
9458
9903
|
);
|
|
9459
9904
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
@@ -9521,10 +9966,21 @@ function buildDaemon(config, paths) {
|
|
|
9521
9966
|
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
|
|
9522
9967
|
});
|
|
9523
9968
|
const providerProxy = getProviderProxy({ llmConfig, apiKeyPool, usageRecorder });
|
|
9969
|
+
const routeLeaseManager = new RouteLeaseManager(
|
|
9970
|
+
providerProxy,
|
|
9971
|
+
new RouteLeaseTargetResolver(llmConfig, {
|
|
9972
|
+
providerKeys: apiKeyPool,
|
|
9973
|
+
subscriptions: createRouteLeaseSubscriptionPreflight(credentialStore)
|
|
9974
|
+
}),
|
|
9975
|
+
routeLeaseDescriptorPort,
|
|
9976
|
+
{ logger }
|
|
9977
|
+
);
|
|
9978
|
+
providerProxy.registerBeforeStop(() => routeLeaseManager.shutdown());
|
|
9979
|
+
providerProxy.registerBeforeStop(() => resetCliSessions());
|
|
9524
9980
|
llmConfig.setReloadHook(() => apiKeyPool.invalidateCache());
|
|
9525
9981
|
const accountHealthProbeScheduler = new AccountHealthProbeScheduler(
|
|
9526
9982
|
credentialStore,
|
|
9527
|
-
|
|
9983
|
+
getSharedAccountHealth4(),
|
|
9528
9984
|
logger,
|
|
9529
9985
|
DEFAULT_ACCOUNT_PROBE
|
|
9530
9986
|
);
|
|
@@ -9571,6 +10027,7 @@ function buildDaemon(config, paths) {
|
|
|
9571
10027
|
keySpendReader: keySpendTracker,
|
|
9572
10028
|
settingsStore,
|
|
9573
10029
|
outboundApiServer,
|
|
10030
|
+
routeLeaseManager,
|
|
9574
10031
|
subscriptionAccounts,
|
|
9575
10032
|
accountAllowanceService,
|
|
9576
10033
|
allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
|
|
@@ -9663,7 +10120,7 @@ function buildDaemon(config, paths) {
|
|
|
9663
10120
|
logger,
|
|
9664
10121
|
fetchImpl: (url, init) => fetchUpstream7(url, init)
|
|
9665
10122
|
});
|
|
9666
|
-
setWebhookRuntime(webhookDispatcher,
|
|
10123
|
+
setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
|
|
9667
10124
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
9668
10125
|
const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
|
|
9669
10126
|
setAuditRuntime(auditWriter, auditPruneSweeper);
|
|
@@ -9678,7 +10135,7 @@ function buildDaemon(config, paths) {
|
|
|
9678
10135
|
const tokenRefreshScheduler = new TokenRefreshScheduler(credentialStore, logger);
|
|
9679
10136
|
const accountHealthSweeper = new AccountHealthSweeper(
|
|
9680
10137
|
credentialStore,
|
|
9681
|
-
|
|
10138
|
+
getSharedAccountHealth4(),
|
|
9682
10139
|
logger
|
|
9683
10140
|
);
|
|
9684
10141
|
return {
|
|
@@ -9687,6 +10144,7 @@ function buildDaemon(config, paths) {
|
|
|
9687
10144
|
keyDb,
|
|
9688
10145
|
settingsStore,
|
|
9689
10146
|
providerProxy,
|
|
10147
|
+
routeLeaseManager,
|
|
9690
10148
|
outboundApiServer,
|
|
9691
10149
|
apiKeyPool,
|
|
9692
10150
|
autoDisableStore,
|