@omnicross/daemon 0.1.7 → 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 +511 -99
- package/dist/cli.js +536 -114
- package/dist/index.cjs +450 -52
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +475 -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 {
|
|
@@ -2613,7 +2703,9 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
|
2613
2703
|
// src/admin/cliLaunch.ts
|
|
2614
2704
|
import { exec, spawn } from "child_process";
|
|
2615
2705
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2616
|
-
import { existsSync as existsSync5 } from "fs";
|
|
2706
|
+
import { chmodSync as chmodSync3, existsSync as existsSync5, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
2707
|
+
import { createServer } from "net";
|
|
2708
|
+
import { tmpdir } from "os";
|
|
2617
2709
|
import { delimiter, join as join3 } from "path";
|
|
2618
2710
|
import {
|
|
2619
2711
|
buildChatCliLaunchConfig,
|
|
@@ -2621,6 +2713,33 @@ import {
|
|
|
2621
2713
|
buildCodexLaunchConfig,
|
|
2622
2714
|
buildGeminiCliLaunchConfig
|
|
2623
2715
|
} from "@omnicross/cli-launcher";
|
|
2716
|
+
import {
|
|
2717
|
+
ROUTE_LEASE_REQUEST_SCHEMA,
|
|
2718
|
+
RouteLeaseError as RouteLeaseError2
|
|
2719
|
+
} from "@omnicross/core/provider-proxy";
|
|
2720
|
+
|
|
2721
|
+
// src/routeLeaseRenewal.ts
|
|
2722
|
+
var TERMINAL_LEASE_TTL_SECONDS = 600;
|
|
2723
|
+
var TERMINAL_LEASE_RENEW_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2724
|
+
var TERMINAL_LEASE_MAX_LIFETIME_MS = 24 * 60 * 60 * 1e3;
|
|
2725
|
+
function startTerminalLeaseRenewal(manager, leaseId2) {
|
|
2726
|
+
const stopAt = Date.now() + TERMINAL_LEASE_MAX_LIFETIME_MS;
|
|
2727
|
+
const timer = setInterval(() => {
|
|
2728
|
+
if (Date.now() >= stopAt) {
|
|
2729
|
+
clearInterval(timer);
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
try {
|
|
2733
|
+
manager.renew(leaseId2, TERMINAL_LEASE_TTL_SECONDS);
|
|
2734
|
+
} catch {
|
|
2735
|
+
clearInterval(timer);
|
|
2736
|
+
}
|
|
2737
|
+
}, TERMINAL_LEASE_RENEW_INTERVAL_MS);
|
|
2738
|
+
timer.unref?.();
|
|
2739
|
+
return () => clearInterval(timer);
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
// src/admin/cliLaunch.ts
|
|
2624
2743
|
var LAUNCHABLE_CLIS = [
|
|
2625
2744
|
{ id: "claude", displayName: "Claude Code", command: "claude" },
|
|
2626
2745
|
{ id: "codex", displayName: "Codex CLI", command: "codex" },
|
|
@@ -2701,34 +2820,183 @@ async function buildLaunchEnv(cli, llmConfig, target) {
|
|
|
2701
2820
|
function shq(s) {
|
|
2702
2821
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
2703
2822
|
}
|
|
2704
|
-
var
|
|
2823
|
+
var MAC_TERMINAL_BOOTSTRAP_SOURCE = `
|
|
2824
|
+
'use strict';
|
|
2825
|
+
const fs = require('node:fs');
|
|
2826
|
+
const net = require('node:net');
|
|
2827
|
+
const { spawn } = require('node:child_process');
|
|
2828
|
+
const [socketPath, launchDir, cwd, command, ...args] = process.argv.slice(2);
|
|
2829
|
+
let payload = '';
|
|
2830
|
+
const socket = net.createConnection(socketPath);
|
|
2831
|
+
socket.setEncoding('utf8');
|
|
2832
|
+
socket.on('data', (chunk) => { payload += chunk; });
|
|
2833
|
+
socket.on('end', () => {
|
|
2834
|
+
const descriptor = JSON.parse(payload);
|
|
2835
|
+
if (!descriptor || Array.isArray(descriptor) || Object.values(descriptor).some((value) => typeof value !== 'string')) {
|
|
2836
|
+
throw new Error('invalid terminal launch descriptor');
|
|
2837
|
+
}
|
|
2838
|
+
try { fs.rmSync(launchDir, { recursive: true, force: true }); } catch {}
|
|
2839
|
+
const child = spawn(command, args, {
|
|
2840
|
+
cwd: cwd || undefined,
|
|
2841
|
+
env: { ...process.env, ...descriptor },
|
|
2842
|
+
stdio: 'inherit',
|
|
2843
|
+
});
|
|
2844
|
+
child.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
|
|
2845
|
+
child.on('exit', (code, signal) => {
|
|
2846
|
+
if (signal) process.kill(process.pid, signal);
|
|
2847
|
+
else process.exitCode = code == null ? 1 : code;
|
|
2848
|
+
});
|
|
2849
|
+
});
|
|
2850
|
+
socket.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
|
|
2851
|
+
`;
|
|
2852
|
+
var MAC_TERMINAL_IPC_TIMEOUT_MS = 12e4;
|
|
2853
|
+
function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }, spawnProcess = spawn, macIpc = {}) {
|
|
2705
2854
|
const childEnv = { ...process.env, ...env };
|
|
2706
2855
|
if (platform === "win32") {
|
|
2707
2856
|
const args = ["/c", "start", `"omnicross ${cli}"`];
|
|
2708
2857
|
if (cwd) args.push("/D", `"${cwd}"`);
|
|
2709
2858
|
args.push("cmd", "/k", command, ...extraArgs);
|
|
2710
|
-
|
|
2859
|
+
spawnProcess(process.env["ComSpec"] || "cmd.exe", args, {
|
|
2711
2860
|
env: childEnv,
|
|
2712
2861
|
windowsVerbatimArguments: true,
|
|
2713
2862
|
detached: true,
|
|
2714
2863
|
stdio: "ignore"
|
|
2715
2864
|
}).unref();
|
|
2716
|
-
return
|
|
2865
|
+
return () => {
|
|
2866
|
+
};
|
|
2717
2867
|
}
|
|
2718
|
-
const exportLine = Object.entries(env).map(([k, v]) => `export ${k}=${shq(v)}`).join("; ");
|
|
2719
2868
|
const runLine = [command, ...extraArgs].map(shq).join(" ");
|
|
2720
|
-
const script = `${
|
|
2869
|
+
const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
|
|
2721
2870
|
if (platform === "darwin") {
|
|
2722
|
-
const
|
|
2723
|
-
|
|
2724
|
-
|
|
2871
|
+
const launchDir = mkdtempSync(join3(tmpdir(), "omnicross-terminal-"));
|
|
2872
|
+
const commandFile = join3(launchDir, "launch.command");
|
|
2873
|
+
const bootstrapFile = join3(launchDir, "bootstrap.cjs");
|
|
2874
|
+
const socketPath = macIpc.socketPath ?? join3(launchDir, "descriptor.sock");
|
|
2875
|
+
const openerEnv = { ...process.env };
|
|
2876
|
+
for (const key of Object.keys(env)) delete openerEnv[key];
|
|
2877
|
+
let claimed = false;
|
|
2878
|
+
let cleaned = false;
|
|
2879
|
+
let failureNotified = false;
|
|
2880
|
+
let timer;
|
|
2881
|
+
const notifyFailure = () => {
|
|
2882
|
+
cleanup();
|
|
2883
|
+
if (failureNotified) return;
|
|
2884
|
+
failureNotified = true;
|
|
2885
|
+
try {
|
|
2886
|
+
onFailure?.();
|
|
2887
|
+
} catch {
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
const handleLaunchFailure = () => {
|
|
2891
|
+
if (claimed) cleanup();
|
|
2892
|
+
else notifyFailure();
|
|
2893
|
+
};
|
|
2894
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
2895
|
+
const server = createServer((socket) => {
|
|
2896
|
+
socket.unref();
|
|
2897
|
+
sockets.add(socket);
|
|
2898
|
+
socket.once("close", () => sockets.delete(socket));
|
|
2899
|
+
try {
|
|
2900
|
+
macIpc.onAccepted?.(socket);
|
|
2901
|
+
} catch {
|
|
2902
|
+
cleanup();
|
|
2903
|
+
return;
|
|
2904
|
+
}
|
|
2905
|
+
if (claimed || cleaned) {
|
|
2906
|
+
socket.destroy();
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2909
|
+
claimed = true;
|
|
2910
|
+
try {
|
|
2911
|
+
macIpc.onClaimed?.();
|
|
2912
|
+
if (cleaned) return;
|
|
2913
|
+
socket.end(JSON.stringify(env), cleanup);
|
|
2914
|
+
} catch {
|
|
2915
|
+
cleanup();
|
|
2916
|
+
}
|
|
2917
|
+
});
|
|
2918
|
+
const cleanup = () => {
|
|
2919
|
+
if (!cleaned) {
|
|
2920
|
+
cleaned = true;
|
|
2921
|
+
if (timer) clearTimeout(timer);
|
|
2922
|
+
for (const socket of sockets) socket.destroy();
|
|
2923
|
+
sockets.clear();
|
|
2924
|
+
try {
|
|
2925
|
+
server.close();
|
|
2926
|
+
} catch {
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
try {
|
|
2930
|
+
if (macIpc.removeArtifacts) {
|
|
2931
|
+
macIpc.removeArtifacts(launchDir);
|
|
2932
|
+
} else {
|
|
2933
|
+
rmSync2(launchDir, {
|
|
2934
|
+
recursive: true,
|
|
2935
|
+
force: true,
|
|
2936
|
+
maxRetries: 3,
|
|
2937
|
+
retryDelay: 20
|
|
2938
|
+
});
|
|
2939
|
+
}
|
|
2940
|
+
} catch {
|
|
2941
|
+
}
|
|
2942
|
+
};
|
|
2943
|
+
try {
|
|
2944
|
+
writeFileSync5(bootstrapFile, MAC_TERMINAL_BOOTSTRAP_SOURCE, { encoding: "utf8", mode: 448 });
|
|
2945
|
+
writeFileSync5(commandFile, `#!/bin/bash
|
|
2946
|
+
rm -f -- "$0"
|
|
2947
|
+
exec ${shq(process.execPath)} ${shq(bootstrapFile)} ${shq(socketPath)} ${shq(launchDir)} ${shq(cwd ?? "")} ${runLine}
|
|
2948
|
+
`, {
|
|
2949
|
+
encoding: "utf8",
|
|
2950
|
+
mode: 448
|
|
2951
|
+
});
|
|
2952
|
+
chmodSync3(commandFile, 448);
|
|
2953
|
+
chmodSync3(bootstrapFile, 448);
|
|
2954
|
+
server.once("error", handleLaunchFailure);
|
|
2955
|
+
server.listen(socketPath, () => {
|
|
2956
|
+
if (cleaned) return;
|
|
2957
|
+
try {
|
|
2958
|
+
macIpc.onListening?.();
|
|
2959
|
+
if (cleaned) return;
|
|
2960
|
+
if (process.platform !== "win32") chmodSync3(socketPath, 384);
|
|
2961
|
+
const opener = spawnProcess("open", ["-n", "-a", "Terminal", commandFile], {
|
|
2962
|
+
env: openerEnv,
|
|
2963
|
+
detached: true,
|
|
2964
|
+
stdio: "ignore"
|
|
2965
|
+
});
|
|
2966
|
+
opener.once("error", handleLaunchFailure);
|
|
2967
|
+
opener.unref();
|
|
2968
|
+
server.unref();
|
|
2969
|
+
} catch {
|
|
2970
|
+
handleLaunchFailure();
|
|
2971
|
+
}
|
|
2972
|
+
});
|
|
2973
|
+
timer = setTimeout(handleLaunchFailure, macIpc.timeoutMs ?? MAC_TERMINAL_IPC_TIMEOUT_MS);
|
|
2974
|
+
timer.unref?.();
|
|
2975
|
+
return cleanup;
|
|
2976
|
+
} catch (error) {
|
|
2977
|
+
cleanup();
|
|
2978
|
+
throw error;
|
|
2979
|
+
}
|
|
2725
2980
|
}
|
|
2726
|
-
|
|
2981
|
+
spawnProcess("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
|
|
2982
|
+
env: childEnv,
|
|
2727
2983
|
detached: true,
|
|
2728
2984
|
stdio: "ignore"
|
|
2729
2985
|
}).unref();
|
|
2730
|
-
|
|
2986
|
+
return () => {
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
var defaultTerminalOpener = (input) => openTerminal(input);
|
|
2731
2990
|
var sessions = /* @__PURE__ */ new Map();
|
|
2991
|
+
function resetCliSessions() {
|
|
2992
|
+
for (const s of sessions.values()) {
|
|
2993
|
+
try {
|
|
2994
|
+
s.onSessionEnd();
|
|
2995
|
+
} catch {
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
sessions.clear();
|
|
2999
|
+
}
|
|
2732
3000
|
function errBody(message) {
|
|
2733
3001
|
return { error: { type: "admin_api_error", message } };
|
|
2734
3002
|
}
|
|
@@ -2783,29 +3051,81 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
2783
3051
|
} catch (err5) {
|
|
2784
3052
|
return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "no launch target") };
|
|
2785
3053
|
}
|
|
3054
|
+
const id = randomUUID2();
|
|
3055
|
+
let leaseId2;
|
|
2786
3056
|
let launch;
|
|
2787
3057
|
try {
|
|
2788
|
-
|
|
3058
|
+
if ((cli === "claude" || cli === "codex") && ctx.routeLeaseManager) {
|
|
3059
|
+
const outcome = await ctx.routeLeaseManager.createFromRequest({
|
|
3060
|
+
schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA,
|
|
3061
|
+
consumer: "omnicross-terminal",
|
|
3062
|
+
runtime: cli,
|
|
3063
|
+
upstream: { kind: "provider", providerId: target.providerId },
|
|
3064
|
+
model: target.model,
|
|
3065
|
+
execution: { sessionId: id }
|
|
3066
|
+
}, `omnicross-terminal:${id}`);
|
|
3067
|
+
leaseId2 = outcome.result.leaseId;
|
|
3068
|
+
const stopRenewal = startTerminalLeaseRenewal(ctx.routeLeaseManager, leaseId2);
|
|
3069
|
+
launch = {
|
|
3070
|
+
env: outcome.result.launch.env,
|
|
3071
|
+
extraArgs: outcome.result.launch.extraArgs,
|
|
3072
|
+
onSessionEnd: () => {
|
|
3073
|
+
stopRenewal();
|
|
3074
|
+
ctx.routeLeaseManager?.release(outcome.result.leaseId);
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
} else {
|
|
3078
|
+
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
3079
|
+
}
|
|
2789
3080
|
} catch (err5) {
|
|
2790
|
-
|
|
3081
|
+
const status = err5 instanceof RouteLeaseError2 ? err5.status : 400;
|
|
3082
|
+
return { status, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
|
|
2791
3083
|
}
|
|
2792
3084
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
2793
3085
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
3086
|
+
let openerCleanup;
|
|
3087
|
+
let ended = false;
|
|
3088
|
+
let published = false;
|
|
3089
|
+
const onSessionEnd = () => {
|
|
3090
|
+
if (ended) return;
|
|
3091
|
+
ended = true;
|
|
3092
|
+
if (published) sessions.delete(id);
|
|
3093
|
+
try {
|
|
3094
|
+
openerCleanup?.();
|
|
3095
|
+
} finally {
|
|
3096
|
+
launch.onSessionEnd();
|
|
3097
|
+
}
|
|
3098
|
+
};
|
|
2794
3099
|
try {
|
|
2795
|
-
opener({
|
|
3100
|
+
const cleanup = opener({
|
|
3101
|
+
cli,
|
|
3102
|
+
command: meta.command,
|
|
3103
|
+
extraArgs: launch.extraArgs ?? [],
|
|
3104
|
+
env: launch.env,
|
|
3105
|
+
cwd,
|
|
3106
|
+
platform,
|
|
3107
|
+
onFailure: onSessionEnd
|
|
3108
|
+
});
|
|
3109
|
+
if (cleanup) openerCleanup = cleanup;
|
|
2796
3110
|
} catch (err5) {
|
|
2797
|
-
|
|
3111
|
+
onSessionEnd();
|
|
2798
3112
|
return { status: 500, body: errBody(err5 instanceof Error ? err5.message : "failed to open terminal") };
|
|
2799
3113
|
}
|
|
2800
|
-
|
|
3114
|
+
if (ended) {
|
|
3115
|
+
openerCleanup?.();
|
|
3116
|
+
return { status: 500, body: errBody("failed to open terminal") };
|
|
3117
|
+
}
|
|
2801
3118
|
sessions.set(id, {
|
|
2802
3119
|
id,
|
|
2803
3120
|
cli,
|
|
2804
3121
|
providerId: target.providerId,
|
|
2805
3122
|
model: target.model,
|
|
3123
|
+
...leaseId2 ? { leaseId: leaseId2 } : {},
|
|
2806
3124
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2807
|
-
onSessionEnd
|
|
3125
|
+
onSessionEnd
|
|
2808
3126
|
});
|
|
3127
|
+
published = true;
|
|
3128
|
+
if (ended) sessions.delete(id);
|
|
2809
3129
|
return { status: 200, body: { sessionId: id, providerId: target.providerId, model: target.model } };
|
|
2810
3130
|
}
|
|
2811
3131
|
|
|
@@ -3707,7 +4027,7 @@ function sealPack(bundleJson, passphrase) {
|
|
|
3707
4027
|
cipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
|
|
3708
4028
|
const ciphertext = Buffer.concat([cipher.update(bundleJson, "utf8"), cipher.final()]);
|
|
3709
4029
|
const tag = cipher.getAuthTag();
|
|
3710
|
-
const
|
|
4030
|
+
const header2 = {
|
|
3711
4031
|
magic: PACK_MAGIC,
|
|
3712
4032
|
v: PACK_VERSION,
|
|
3713
4033
|
kdf: KDF_ALGORITHM,
|
|
@@ -3718,7 +4038,7 @@ function sealPack(bundleJson, passphrase) {
|
|
|
3718
4038
|
iv: iv.toString("base64"),
|
|
3719
4039
|
tag: tag.toString("base64")
|
|
3720
4040
|
};
|
|
3721
|
-
return `${PACK_PREFIX}${toB64Url(JSON.stringify(
|
|
4041
|
+
return `${PACK_PREFIX}${toB64Url(JSON.stringify(header2))}.${ciphertext.toString("base64")}`;
|
|
3722
4042
|
}
|
|
3723
4043
|
function parsePack(packString) {
|
|
3724
4044
|
if (typeof packString !== "string" || !packString.startsWith(PACK_PREFIX)) {
|
|
@@ -3729,28 +4049,28 @@ function parsePack(packString) {
|
|
|
3729
4049
|
if (dot < 0) throw new PackAuthError("migration pack is malformed (missing body)");
|
|
3730
4050
|
const headerB64Url = rest.slice(0, dot);
|
|
3731
4051
|
const ctB64 = rest.slice(dot + 1);
|
|
3732
|
-
let
|
|
4052
|
+
let header2;
|
|
3733
4053
|
try {
|
|
3734
|
-
|
|
4054
|
+
header2 = JSON.parse(fromB64Url(headerB64Url));
|
|
3735
4055
|
} catch {
|
|
3736
4056
|
throw new PackAuthError("migration pack is malformed (unreadable header)");
|
|
3737
4057
|
}
|
|
3738
|
-
if (!
|
|
4058
|
+
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
4059
|
throw new PackAuthError("migration pack is malformed (unsupported header)");
|
|
3740
4060
|
}
|
|
3741
4061
|
const ciphertext = Buffer.from(ctB64, "base64");
|
|
3742
|
-
return { header, ciphertext };
|
|
4062
|
+
return { header: header2, ciphertext };
|
|
3743
4063
|
}
|
|
3744
4064
|
function openPack(packString, passphrase) {
|
|
3745
4065
|
assertPassphraseStrength(passphrase);
|
|
3746
|
-
const { header, ciphertext } = parsePack(packString);
|
|
3747
|
-
const salt = Buffer.from(
|
|
3748
|
-
const iv = Buffer.from(
|
|
3749
|
-
const tag = Buffer.from(
|
|
4066
|
+
const { header: header2, ciphertext } = parsePack(packString);
|
|
4067
|
+
const salt = Buffer.from(header2.salt, "base64");
|
|
4068
|
+
const iv = Buffer.from(header2.iv, "base64");
|
|
4069
|
+
const tag = Buffer.from(header2.tag, "base64");
|
|
3750
4070
|
if (iv.length !== IV_BYTES2 || tag.length !== TAG_BYTES2) {
|
|
3751
4071
|
throw new PackAuthError("migration pack is malformed (invalid iv/tag length)");
|
|
3752
4072
|
}
|
|
3753
|
-
const key = deriveKey(passphrase, salt,
|
|
4073
|
+
const key = deriveKey(passphrase, salt, header2.N, header2.r, header2.p);
|
|
3754
4074
|
const decipher = createDecipheriv2("aes-256-gcm", key, iv);
|
|
3755
4075
|
decipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
|
|
3756
4076
|
decipher.setAuthTag(tag);
|
|
@@ -4086,10 +4406,10 @@ function writeJson2(res, status, body) {
|
|
|
4086
4406
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
4087
4407
|
res.end(JSON.stringify(body));
|
|
4088
4408
|
}
|
|
4089
|
-
function
|
|
4409
|
+
function writeError2(res, status, message) {
|
|
4090
4410
|
writeJson2(res, status, { error: { type: "account_allowance_error", message } });
|
|
4091
4411
|
}
|
|
4092
|
-
function
|
|
4412
|
+
function readJson2(req) {
|
|
4093
4413
|
return new Promise((resolve2, reject) => {
|
|
4094
4414
|
const chunks = [];
|
|
4095
4415
|
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
@@ -4115,10 +4435,10 @@ function allowanceProvider(value) {
|
|
|
4115
4435
|
return value === "claude" || value === "codex" ? value : null;
|
|
4116
4436
|
}
|
|
4117
4437
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
4118
|
-
if (!service) return
|
|
4438
|
+
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
4119
4439
|
if (method === "GET" && rest.length === 1 && rest[0] === "scheduling") {
|
|
4120
4440
|
if (!service.getSchedulingStatus) {
|
|
4121
|
-
return
|
|
4441
|
+
return writeError2(res, 501, "allowance scheduling diagnostics are not available");
|
|
4122
4442
|
}
|
|
4123
4443
|
return writeJson2(res, 200, { scheduling: service.getSchedulingStatus() });
|
|
4124
4444
|
}
|
|
@@ -4126,27 +4446,27 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4126
4446
|
const params = query(req);
|
|
4127
4447
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
4128
4448
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
4129
|
-
if (providerId === null) return
|
|
4449
|
+
if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
|
|
4130
4450
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
4131
4451
|
const allowances = await service.list({ providerId, accountId });
|
|
4132
4452
|
return writeJson2(res, 200, { allowances });
|
|
4133
4453
|
}
|
|
4134
4454
|
if (method === "POST" && rest[0] === "refresh") {
|
|
4135
|
-
const body = await
|
|
4455
|
+
const body = await readJson2(req);
|
|
4136
4456
|
const requestedProvider = allowanceProvider(
|
|
4137
4457
|
typeof body["providerId"] === "string" ? body["providerId"] : "claude"
|
|
4138
4458
|
);
|
|
4139
4459
|
if (requestedProvider !== "claude") {
|
|
4140
|
-
return
|
|
4460
|
+
return writeError2(res, 400, "only Claude allowances support explicit refresh");
|
|
4141
4461
|
}
|
|
4142
4462
|
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
4143
4463
|
const allowances = await service.refreshClaude(accountId);
|
|
4144
4464
|
if (accountId && allowances.length === 0) {
|
|
4145
|
-
return
|
|
4465
|
+
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
4146
4466
|
}
|
|
4147
4467
|
return writeJson2(res, 200, { allowances });
|
|
4148
4468
|
}
|
|
4149
|
-
return
|
|
4469
|
+
return writeError2(res, 405, `method ${method} not allowed on account allowances`);
|
|
4150
4470
|
}
|
|
4151
4471
|
|
|
4152
4472
|
// src/admin/adminApi.ts
|
|
@@ -5381,6 +5701,7 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
5381
5701
|
const result = await handleCliLaunch(cli, body, {
|
|
5382
5702
|
llmConfig: deps.llmConfig,
|
|
5383
5703
|
providers,
|
|
5704
|
+
routeLeaseManager: deps.routeLeaseManager,
|
|
5384
5705
|
opener: deps.cliTerminalOpener,
|
|
5385
5706
|
probe: deps.cliPathProbe
|
|
5386
5707
|
});
|
|
@@ -5643,7 +5964,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
5643
5964
|
}
|
|
5644
5965
|
|
|
5645
5966
|
// src/admin/version.ts
|
|
5646
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
5967
|
+
var DAEMON_VERSION = true ? "0.1.8" : "0.0.0-dev";
|
|
5647
5968
|
|
|
5648
5969
|
// src/admin/AdminServer.ts
|
|
5649
5970
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -5763,6 +6084,10 @@ var AdminServer = class {
|
|
|
5763
6084
|
await handleWebhookTest(req, res);
|
|
5764
6085
|
return;
|
|
5765
6086
|
}
|
|
6087
|
+
if (path2 === "/admin/api/route-leases" || path2.startsWith("/admin/api/route-leases/")) {
|
|
6088
|
+
await handleRouteLeaseApi(req, res, path2, this.deps);
|
|
6089
|
+
return;
|
|
6090
|
+
}
|
|
5766
6091
|
if (path2.startsWith("/admin/api/")) {
|
|
5767
6092
|
await handleAdminApi(req, res, path2, this.deps);
|
|
5768
6093
|
return;
|
|
@@ -5774,8 +6099,8 @@ var AdminServer = class {
|
|
|
5774
6099
|
}
|
|
5775
6100
|
/** Constant-time bearer/header check against the configured token. */
|
|
5776
6101
|
isAuthorized(req, token) {
|
|
5777
|
-
const
|
|
5778
|
-
const bearer = typeof
|
|
6102
|
+
const header2 = req.headers["authorization"];
|
|
6103
|
+
const bearer = typeof header2 === "string" && header2.startsWith("Bearer ") ? header2.slice("Bearer ".length).trim() : void 0;
|
|
5779
6104
|
const xToken = req.headers["x-admin-token"];
|
|
5780
6105
|
const presented = bearer ?? (typeof xToken === "string" ? xToken.trim() : void 0);
|
|
5781
6106
|
return constantTimeEquals(presented, token);
|
|
@@ -5910,7 +6235,7 @@ var OAuthSessionStore = class {
|
|
|
5910
6235
|
};
|
|
5911
6236
|
|
|
5912
6237
|
// src/commands/loopbackCallback.ts
|
|
5913
|
-
import { createServer } from "http";
|
|
6238
|
+
import { createServer as createServer2 } from "http";
|
|
5914
6239
|
var LOOPBACK_HOST = "127.0.0.1";
|
|
5915
6240
|
var LOOPBACK_PORT = 1455;
|
|
5916
6241
|
var CALLBACK_PATH = "/auth/callback";
|
|
@@ -5932,7 +6257,7 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
5932
6257
|
fn();
|
|
5933
6258
|
server2.close();
|
|
5934
6259
|
};
|
|
5935
|
-
const server =
|
|
6260
|
+
const server = createServer2((req, res) => {
|
|
5936
6261
|
const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
|
|
5937
6262
|
if (url.pathname !== CALLBACK_PATH) {
|
|
5938
6263
|
res.writeHead(404, HTML_HEADERS);
|
|
@@ -6381,7 +6706,7 @@ function safeStringify(value) {
|
|
|
6381
6706
|
}
|
|
6382
6707
|
|
|
6383
6708
|
// src/ports/JsonApiServerSettingsStore.ts
|
|
6384
|
-
import { readFileSync as readFileSync6, writeFileSync as
|
|
6709
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
6385
6710
|
import { OUTBOUND_API_SERVER_CONFIG_KEY } from "@omnicross/core/outbound-api";
|
|
6386
6711
|
var JsonApiServerSettingsStore = class {
|
|
6387
6712
|
/**
|
|
@@ -6408,7 +6733,7 @@ var JsonApiServerSettingsStore = class {
|
|
|
6408
6733
|
if (key !== OUTBOUND_API_SERVER_CONFIG_KEY) return;
|
|
6409
6734
|
const file = this.readFile();
|
|
6410
6735
|
file.server = this.encryptSecrets(value);
|
|
6411
|
-
|
|
6736
|
+
writeFileSync6(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
|
|
6412
6737
|
}
|
|
6413
6738
|
/** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
|
|
6414
6739
|
encryptSecrets(config) {
|
|
@@ -6762,7 +7087,7 @@ function median(values) {
|
|
|
6762
7087
|
}
|
|
6763
7088
|
|
|
6764
7089
|
// src/ports/JsonOutboundKeyDb.ts
|
|
6765
|
-
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as
|
|
7090
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
6766
7091
|
var JsonOutboundKeyDb = class {
|
|
6767
7092
|
/**
|
|
6768
7093
|
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
@@ -6904,7 +7229,7 @@ var JsonOutboundKeyDb = class {
|
|
|
6904
7229
|
}
|
|
6905
7230
|
}
|
|
6906
7231
|
writeRows(rows) {
|
|
6907
|
-
|
|
7232
|
+
writeFileSync7(this.keysPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
6908
7233
|
}
|
|
6909
7234
|
};
|
|
6910
7235
|
function applyPolicyField(row, field, value) {
|
|
@@ -6914,7 +7239,7 @@ function applyPolicyField(row, field, value) {
|
|
|
6914
7239
|
}
|
|
6915
7240
|
|
|
6916
7241
|
// src/ports/JsonPricingStore.ts
|
|
6917
|
-
import { existsSync as existsSync9, readFileSync as readFileSync9, renameSync as renameSync3, rmSync as
|
|
7242
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
6918
7243
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
6919
7244
|
var JsonPricingStore = class {
|
|
6920
7245
|
constructor(pricingPath) {
|
|
@@ -7055,13 +7380,13 @@ var JsonPricingStore = class {
|
|
|
7055
7380
|
writeRows(rows) {
|
|
7056
7381
|
const temporaryPath = `${this.pricingPath}.${process.pid}.${randomUUID5()}.tmp`;
|
|
7057
7382
|
try {
|
|
7058
|
-
|
|
7383
|
+
writeFileSync8(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
7059
7384
|
encoding: "utf8",
|
|
7060
7385
|
flag: "wx"
|
|
7061
7386
|
});
|
|
7062
7387
|
this.replaceFile(temporaryPath);
|
|
7063
7388
|
} finally {
|
|
7064
|
-
|
|
7389
|
+
rmSync3(temporaryPath, { force: true });
|
|
7065
7390
|
}
|
|
7066
7391
|
}
|
|
7067
7392
|
/** Isolated for deterministic failure testing; never removes the target. */
|
|
@@ -7076,7 +7401,7 @@ function isUsablePricingRow(value) {
|
|
|
7076
7401
|
}
|
|
7077
7402
|
|
|
7078
7403
|
// src/pricing/PricingRefreshScheduler.ts
|
|
7079
|
-
import { existsSync as existsSync10, readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as
|
|
7404
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
7080
7405
|
var EMPTY_STATE2 = {
|
|
7081
7406
|
lastAttemptAt: null,
|
|
7082
7407
|
lastSuccessAt: null,
|
|
@@ -7169,7 +7494,7 @@ var PricingRefreshScheduler = class {
|
|
|
7169
7494
|
}
|
|
7170
7495
|
writeState(state) {
|
|
7171
7496
|
const temporaryPath = `${this.statePath}.tmp`;
|
|
7172
|
-
|
|
7497
|
+
writeFileSync9(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
7173
7498
|
`, "utf8");
|
|
7174
7499
|
renameSync4(temporaryPath, this.statePath);
|
|
7175
7500
|
}
|
|
@@ -7179,7 +7504,7 @@ function finiteOrNull(value) {
|
|
|
7179
7504
|
}
|
|
7180
7505
|
|
|
7181
7506
|
// src/ports/JsonVoucherDb.ts
|
|
7182
|
-
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as
|
|
7507
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
|
|
7183
7508
|
var JsonVoucherDb = class {
|
|
7184
7509
|
constructor(vouchersPath) {
|
|
7185
7510
|
this.vouchersPath = vouchersPath;
|
|
@@ -7266,12 +7591,12 @@ var JsonVoucherDb = class {
|
|
|
7266
7591
|
}
|
|
7267
7592
|
}
|
|
7268
7593
|
writeRows(rows) {
|
|
7269
|
-
|
|
7594
|
+
writeFileSync10(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
7270
7595
|
}
|
|
7271
7596
|
};
|
|
7272
7597
|
|
|
7273
7598
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
7274
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, writeFileSync as
|
|
7599
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
7275
7600
|
import { dirname as dirname6 } from "path";
|
|
7276
7601
|
import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
7277
7602
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
@@ -7954,7 +8279,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
7954
8279
|
persist(config) {
|
|
7955
8280
|
mkdirSync4(dirname6(this.tokensPath), { recursive: true });
|
|
7956
8281
|
const encrypted = encryptTokens(config, this.box);
|
|
7957
|
-
|
|
8282
|
+
writeFileSync11(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
|
|
7958
8283
|
}
|
|
7959
8284
|
/**
|
|
7960
8285
|
* Read + parse `tokens.json`, tolerating a missing/corrupt file, then DECRYPT
|
|
@@ -8523,7 +8848,7 @@ import {
|
|
|
8523
8848
|
readFileSync as readFileSync14,
|
|
8524
8849
|
readdirSync,
|
|
8525
8850
|
statSync as statSync3,
|
|
8526
|
-
writeFileSync as
|
|
8851
|
+
writeFileSync as writeFileSync12
|
|
8527
8852
|
} from "fs";
|
|
8528
8853
|
import { basename, dirname as dirname7, join as join6 } from "path";
|
|
8529
8854
|
var SIDECAR_VERSION = 1;
|
|
@@ -8565,7 +8890,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
|
|
|
8565
8890
|
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
8566
8891
|
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
8567
8892
|
};
|
|
8568
|
-
|
|
8893
|
+
writeFileSync12(statsPath, JSON.stringify(next), "utf8");
|
|
8569
8894
|
}
|
|
8570
8895
|
function queryCovers(stats, from, to) {
|
|
8571
8896
|
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
@@ -8709,7 +9034,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
8709
9034
|
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
8710
9035
|
total.complete = total.complete && scanned.filtered.complete;
|
|
8711
9036
|
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
8712
|
-
if (current.complete)
|
|
9037
|
+
if (current.complete) writeFileSync12(statsPath, JSON.stringify(current), "utf8");
|
|
8713
9038
|
} catch {
|
|
8714
9039
|
total.complete = false;
|
|
8715
9040
|
}
|
|
@@ -9271,6 +9596,78 @@ var TokenRefreshScheduler = class {
|
|
|
9271
9596
|
}
|
|
9272
9597
|
};
|
|
9273
9598
|
|
|
9599
|
+
// src/routeLeaseSubscriptionPreflight.ts
|
|
9600
|
+
import {
|
|
9601
|
+
RouteLeaseError as RouteLeaseError3
|
|
9602
|
+
} from "@omnicross/core/provider-proxy";
|
|
9603
|
+
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
9604
|
+
import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
9605
|
+
import { accountSupportsModel } from "@omnicross/subscriptions/scheduler/accountModelMap";
|
|
9606
|
+
var PROVIDERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "opencodego"]);
|
|
9607
|
+
function accountArray(config, providerId) {
|
|
9608
|
+
const record = config;
|
|
9609
|
+
const key = `${providerId}Accounts`;
|
|
9610
|
+
const accounts = record[key];
|
|
9611
|
+
if (Array.isArray(accounts)) return accounts;
|
|
9612
|
+
const legacy = record[providerId];
|
|
9613
|
+
if (!legacy || typeof legacy !== "object") return [];
|
|
9614
|
+
const activeKey = `active${providerId[0].toUpperCase()}${providerId.slice(1)}AccountId`;
|
|
9615
|
+
return [{ id: String(record[activeKey] ?? "active"), enabled: true, tokens: legacy }];
|
|
9616
|
+
}
|
|
9617
|
+
function hasCredential(providerId, account) {
|
|
9618
|
+
const tokens = account.tokens;
|
|
9619
|
+
if (providerId === "opencodego") return typeof tokens.apiKey === "string" && tokens.apiKey.length > 0;
|
|
9620
|
+
return typeof tokens.accessToken === "string" && tokens.accessToken.length > 0;
|
|
9621
|
+
}
|
|
9622
|
+
function safeProviderId(value) {
|
|
9623
|
+
if (!PROVIDERS.has(value)) {
|
|
9624
|
+
throw new RouteLeaseError3("upstream_not_found", "subscription provider was not found");
|
|
9625
|
+
}
|
|
9626
|
+
return value;
|
|
9627
|
+
}
|
|
9628
|
+
function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
9629
|
+
return {
|
|
9630
|
+
async assertAvailable(upstream, model) {
|
|
9631
|
+
const providerId = safeProviderId(upstream.providerId);
|
|
9632
|
+
const config = await credentials.getFullConfig();
|
|
9633
|
+
const all = accountArray(config, providerId);
|
|
9634
|
+
if (all.length === 0) {
|
|
9635
|
+
throw new RouteLeaseError3("upstream_unavailable", "subscription provider has no configured account");
|
|
9636
|
+
}
|
|
9637
|
+
let bounded = all;
|
|
9638
|
+
if (upstream.kind === "account") {
|
|
9639
|
+
bounded = all.filter((account) => account.id === upstream.accountId);
|
|
9640
|
+
} else if (upstream.kind === "account-group") {
|
|
9641
|
+
bounded = all.filter((account) => account.group?.trim() === upstream.group);
|
|
9642
|
+
}
|
|
9643
|
+
if (bounded.length === 0) {
|
|
9644
|
+
throw new RouteLeaseError3("upstream_not_found", "the selected subscription resource was not found");
|
|
9645
|
+
}
|
|
9646
|
+
const modelEligible = bounded.filter(
|
|
9647
|
+
(account) => accountSupportsModel(account.supportedModels, model)
|
|
9648
|
+
);
|
|
9649
|
+
if (modelEligible.length === 0) {
|
|
9650
|
+
throw new RouteLeaseError3("model_not_configured", "model is not supported by the selected subscription resource");
|
|
9651
|
+
}
|
|
9652
|
+
const credentialEligible = modelEligible.filter(
|
|
9653
|
+
(account) => account.enabled !== false && hasCredential(providerId, account)
|
|
9654
|
+
);
|
|
9655
|
+
const health2 = getSharedAccountHealth3();
|
|
9656
|
+
const allowance = getSharedAccountAllowanceScheduling4();
|
|
9657
|
+
const candidates = credentialEligible.filter(
|
|
9658
|
+
(account) => health2.isSchedulable(providerId, account.id) && allowance.preview(providerId, account.id, account.priority ?? 50).schedulable
|
|
9659
|
+
);
|
|
9660
|
+
if (candidates.length > 0) return;
|
|
9661
|
+
if (upstream.kind === "account") {
|
|
9662
|
+
throw new RouteLeaseError3("upstream_unavailable", "the selected subscription account is unavailable");
|
|
9663
|
+
}
|
|
9664
|
+
throw new RouteLeaseError3("upstream_exhausted", "the selected subscription pool has no eligible account", {
|
|
9665
|
+
retryAfterSeconds: 30
|
|
9666
|
+
});
|
|
9667
|
+
}
|
|
9668
|
+
};
|
|
9669
|
+
}
|
|
9670
|
+
|
|
9274
9671
|
// src/webhook/WebhookDispatcher.ts
|
|
9275
9672
|
import { createHmac as createHmac2 } from "crypto";
|
|
9276
9673
|
import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
@@ -9453,7 +9850,7 @@ function buildDaemon(config, paths) {
|
|
|
9453
9850
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
9454
9851
|
);
|
|
9455
9852
|
setSharedAccountAllowanceStore(accountAllowanceStore);
|
|
9456
|
-
|
|
9853
|
+
getSharedAccountAllowanceScheduling5().configure(
|
|
9457
9854
|
normalizeServerConfig(decryptedConfig.server).allowanceScheduling
|
|
9458
9855
|
);
|
|
9459
9856
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
@@ -9521,10 +9918,21 @@ function buildDaemon(config, paths) {
|
|
|
9521
9918
|
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
|
|
9522
9919
|
});
|
|
9523
9920
|
const providerProxy = getProviderProxy({ llmConfig, apiKeyPool, usageRecorder });
|
|
9921
|
+
const routeLeaseManager = new RouteLeaseManager(
|
|
9922
|
+
providerProxy,
|
|
9923
|
+
new RouteLeaseTargetResolver(llmConfig, {
|
|
9924
|
+
providerKeys: apiKeyPool,
|
|
9925
|
+
subscriptions: createRouteLeaseSubscriptionPreflight(credentialStore)
|
|
9926
|
+
}),
|
|
9927
|
+
routeLeaseDescriptorPort,
|
|
9928
|
+
{ logger }
|
|
9929
|
+
);
|
|
9930
|
+
providerProxy.registerBeforeStop(() => routeLeaseManager.shutdown());
|
|
9931
|
+
providerProxy.registerBeforeStop(() => resetCliSessions());
|
|
9524
9932
|
llmConfig.setReloadHook(() => apiKeyPool.invalidateCache());
|
|
9525
9933
|
const accountHealthProbeScheduler = new AccountHealthProbeScheduler(
|
|
9526
9934
|
credentialStore,
|
|
9527
|
-
|
|
9935
|
+
getSharedAccountHealth4(),
|
|
9528
9936
|
logger,
|
|
9529
9937
|
DEFAULT_ACCOUNT_PROBE
|
|
9530
9938
|
);
|
|
@@ -9571,6 +9979,7 @@ function buildDaemon(config, paths) {
|
|
|
9571
9979
|
keySpendReader: keySpendTracker,
|
|
9572
9980
|
settingsStore,
|
|
9573
9981
|
outboundApiServer,
|
|
9982
|
+
routeLeaseManager,
|
|
9574
9983
|
subscriptionAccounts,
|
|
9575
9984
|
accountAllowanceService,
|
|
9576
9985
|
allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
|
|
@@ -9663,7 +10072,7 @@ function buildDaemon(config, paths) {
|
|
|
9663
10072
|
logger,
|
|
9664
10073
|
fetchImpl: (url, init) => fetchUpstream7(url, init)
|
|
9665
10074
|
});
|
|
9666
|
-
setWebhookRuntime(webhookDispatcher,
|
|
10075
|
+
setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
|
|
9667
10076
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
9668
10077
|
const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
|
|
9669
10078
|
setAuditRuntime(auditWriter, auditPruneSweeper);
|
|
@@ -9678,7 +10087,7 @@ function buildDaemon(config, paths) {
|
|
|
9678
10087
|
const tokenRefreshScheduler = new TokenRefreshScheduler(credentialStore, logger);
|
|
9679
10088
|
const accountHealthSweeper = new AccountHealthSweeper(
|
|
9680
10089
|
credentialStore,
|
|
9681
|
-
|
|
10090
|
+
getSharedAccountHealth4(),
|
|
9682
10091
|
logger
|
|
9683
10092
|
);
|
|
9684
10093
|
return {
|
|
@@ -9687,6 +10096,7 @@ function buildDaemon(config, paths) {
|
|
|
9687
10096
|
keyDb,
|
|
9688
10097
|
settingsStore,
|
|
9689
10098
|
providerProxy,
|
|
10099
|
+
routeLeaseManager,
|
|
9690
10100
|
outboundApiServer,
|
|
9691
10101
|
apiKeyPool,
|
|
9692
10102
|
autoDisableStore,
|