@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/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 getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
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 getSharedAccountAllowanceScheduling4
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 defaultTerminalOpener = ({ cli, command, extraArgs, env, cwd, platform }) => {
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
- spawn(process.env["ComSpec"] || "cmd.exe", args, {
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 = `${exportLine}; ${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
2869
+ const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
2721
2870
  if (platform === "darwin") {
2722
- const osa = `tell application "Terminal" to do script ${JSON.stringify(script)}`;
2723
- spawn("osascript", ["-e", osa], { detached: true, stdio: "ignore" }).unref();
2724
- return;
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
- spawn("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
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
- launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
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
- return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
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({ cli, command: meta.command, extraArgs: launch.extraArgs ?? [], env: launch.env, cwd, platform });
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
- launch.onSessionEnd();
3111
+ onSessionEnd();
2798
3112
  return { status: 500, body: errBody(err5 instanceof Error ? err5.message : "failed to open terminal") };
2799
3113
  }
2800
- const id = randomUUID2();
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: launch.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 header = {
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(header))}.${ciphertext.toString("base64")}`;
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 header;
4052
+ let header2;
3733
4053
  try {
3734
- header = JSON.parse(fromB64Url(headerB64Url));
4054
+ header2 = JSON.parse(fromB64Url(headerB64Url));
3735
4055
  } catch {
3736
4056
  throw new PackAuthError("migration pack is malformed (unreadable header)");
3737
4057
  }
3738
- if (!header || header.magic !== PACK_MAGIC || header.v !== PACK_VERSION || header.kdf !== KDF_ALGORITHM || typeof header.salt !== "string" || typeof header.iv !== "string" || typeof header.tag !== "string" || typeof header.N !== "number" || typeof header.r !== "number" || typeof header.p !== "number") {
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(header.salt, "base64");
3748
- const iv = Buffer.from(header.iv, "base64");
3749
- const tag = Buffer.from(header.tag, "base64");
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, header.N, header.r, header.p);
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 writeError(res, status, message) {
4409
+ function writeError2(res, status, message) {
4090
4410
  writeJson2(res, status, { error: { type: "account_allowance_error", message } });
4091
4411
  }
4092
- function readJson(req) {
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 writeError(res, 501, "account allowance service is not available");
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 writeError(res, 501, "allowance scheduling diagnostics are not available");
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 writeError(res, 400, "providerId must be claude or codex");
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 readJson(req);
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 writeError(res, 400, "only Claude allowances support explicit refresh");
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 writeError(res, 404, `Claude account '${accountId}' not found`);
4465
+ return writeError2(res, 404, `Claude account '${accountId}' not found`);
4146
4466
  }
4147
4467
  return writeJson2(res, 200, { allowances });
4148
4468
  }
4149
- return writeError(res, 405, `method ${method} not allowed on account allowances`);
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.7" : "0.0.0-dev";
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 header = req.headers["authorization"];
5778
- const bearer = typeof header === "string" && header.startsWith("Bearer ") ? header.slice("Bearer ".length).trim() : void 0;
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 = createServer((req, res) => {
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 writeFileSync5 } from "fs";
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
- writeFileSync5(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
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 writeFileSync6 } from "fs";
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
- writeFileSync6(this.keysPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
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 rmSync2, writeFileSync as writeFileSync7 } from "fs";
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
- writeFileSync7(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
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
- rmSync2(temporaryPath, { force: true });
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 writeFileSync8 } from "fs";
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
- writeFileSync8(temporaryPath, `${JSON.stringify(state, null, 2)}
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 writeFileSync9 } from "fs";
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
- writeFileSync9(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
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 writeFileSync10 } from "fs";
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
- writeFileSync10(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
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 writeFileSync11
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
- writeFileSync11(statsPath, JSON.stringify(next), "utf8");
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) writeFileSync11(statsPath, JSON.stringify(current), "utf8");
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
- getSharedAccountAllowanceScheduling4().configure(
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
- getSharedAccountHealth3(),
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, getSharedAccountHealth3());
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
- getSharedAccountHealth3(),
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,