@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.js CHANGED
@@ -1750,15 +1750,15 @@ async function keysRevoke(db, id) {
1750
1750
 
1751
1751
  // src/commands/launch.ts
1752
1752
  import { spawn as spawn2 } from "child_process";
1753
+ import { randomUUID as randomUUID6 } from "crypto";
1753
1754
  import { existsSync as existsSync20 } from "fs";
1754
1755
  import { delimiter as delimiter2, join as join12 } from "path";
1755
1756
  import { parseArgs as parseArgs4 } from "util";
1756
1757
  import {
1757
1758
  buildChatCliLaunchConfig as buildChatCliLaunchConfig2,
1758
- buildClaudeCliLaunchConfig as buildClaudeCliLaunchConfig2,
1759
- buildCodexLaunchConfig as buildCodexLaunchConfig2,
1760
1759
  buildGeminiCliLaunchConfig as buildGeminiCliLaunchConfig2
1761
1760
  } from "@omnicross/cli-launcher";
1761
+ import { ROUTE_LEASE_REQUEST_SCHEMA as ROUTE_LEASE_REQUEST_SCHEMA2 } from "@omnicross/core/provider-proxy";
1762
1762
 
1763
1763
  // src/bootstrap.ts
1764
1764
  import { accessSync, constants as fsConstants, existsSync as existsSync19 } from "fs";
@@ -1774,7 +1774,7 @@ import {
1774
1774
  normalizeServerConfig
1775
1775
  } from "@omnicross/core/outbound-api";
1776
1776
  import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api/subscriptionRegistryPort";
1777
- import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
1777
+ import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
1778
1778
  import {
1779
1779
  __resetSharedAccountAllowanceStoreForTests,
1780
1780
  AccountAllowanceStore as AccountAllowanceStore3,
@@ -1782,15 +1782,18 @@ import {
1782
1782
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
1783
1783
  import {
1784
1784
  __resetSharedAccountAllowanceSchedulingForTests,
1785
- getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4
1785
+ getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
1786
1786
  } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
1787
1787
  import { fetchUpstream as fetchUpstream7, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
1788
1788
  import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
1789
1789
  import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
1790
1790
  import {
1791
1791
  __resetProviderProxyForTests,
1792
- getProviderProxy
1792
+ getProviderProxy,
1793
+ RouteLeaseManager,
1794
+ RouteLeaseTargetResolver
1793
1795
  } from "@omnicross/core/provider-proxy";
1796
+ import { routeLeaseDescriptorPort } from "@omnicross/cli-launcher";
1794
1797
  import { KeySpendTracker } from "@omnicross/core/outbound-api";
1795
1798
  import { PricingEngine, UsageRecorder } from "@omnicross/core/usage";
1796
1799
  import {
@@ -2496,6 +2499,93 @@ async function handleWebhookTest(req, res) {
2496
2499
  res.end(JSON.stringify({ result }));
2497
2500
  }
2498
2501
 
2502
+ // src/admin/routeLeaseApi.ts
2503
+ import {
2504
+ isLoopbackAddress,
2505
+ normalizeRouteLeaseTtl,
2506
+ ROUTE_LEASE_CAPABILITIES,
2507
+ RouteLeaseError
2508
+ } from "@omnicross/core/provider-proxy";
2509
+ var MAX_BODY_BYTES = 64 * 1024;
2510
+ var SAFE_LEASE_ID = /^[A-Za-z0-9-]{1,128}$/u;
2511
+ async function readJson(req) {
2512
+ const chunks = [];
2513
+ let bytes = 0;
2514
+ for await (const chunk of req) {
2515
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2516
+ bytes += buffer.length;
2517
+ if (bytes > MAX_BODY_BYTES) throw new RouteLeaseError("invalid_request", "request body is too large");
2518
+ chunks.push(buffer);
2519
+ }
2520
+ if (chunks.length === 0) return {};
2521
+ try {
2522
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
2523
+ } catch {
2524
+ throw new RouteLeaseError("invalid_request", "request body is not valid JSON");
2525
+ }
2526
+ }
2527
+ function json(res, status, body, noStore = false) {
2528
+ res.statusCode = status;
2529
+ res.setHeader("Content-Type", "application/json");
2530
+ if (noStore) res.setHeader("Cache-Control", "no-store");
2531
+ res.end(JSON.stringify(body));
2532
+ }
2533
+ function leaseId(value) {
2534
+ if (!value || !SAFE_LEASE_ID.test(value)) throw new RouteLeaseError("invalid_request", "lease id is invalid");
2535
+ return value;
2536
+ }
2537
+ function header(req, name) {
2538
+ const value = req.headers[name.toLowerCase()];
2539
+ return Array.isArray(value) ? value[0] : value;
2540
+ }
2541
+ function writeError(res, error, noStore) {
2542
+ const safe = error instanceof RouteLeaseError ? error : new RouteLeaseError("upstream_unavailable", "route lease operation failed safely");
2543
+ if (safe.retryAfterSeconds !== void 0) res.setHeader("Retry-After", String(safe.retryAfterSeconds));
2544
+ json(res, safe.status, safe.toResponse(), noStore);
2545
+ }
2546
+ async function handleRouteLeaseApi(req, res, path2, deps) {
2547
+ const method = (req.method ?? "GET").toUpperCase();
2548
+ const noStore = method === "POST" && (path2 === "/admin/api/route-leases" || path2.endsWith("/renew"));
2549
+ try {
2550
+ if (!isLoopbackAddress(req.socket.remoteAddress)) {
2551
+ throw new RouteLeaseError("control_unauthorized", "route lease control plane is loopback only");
2552
+ }
2553
+ const manager = deps.routeLeaseManager;
2554
+ if (!manager) throw new RouteLeaseError("daemon_not_ready", "route lease manager is unavailable");
2555
+ const base = "/admin/api/route-leases";
2556
+ const suffix = path2.slice(base.length).replace(/^\/+|\/+$/gu, "");
2557
+ const segments = suffix ? suffix.split("/") : [];
2558
+ if (segments.length === 1 && segments[0] === "capabilities") {
2559
+ if (method !== "GET" && method !== "HEAD") throw new RouteLeaseError("invalid_request", "method is not allowed");
2560
+ return json(res, 200, ROUTE_LEASE_CAPABILITIES);
2561
+ }
2562
+ if (segments.length === 0) {
2563
+ if (method === "GET") return json(res, 200, { leases: manager.list() });
2564
+ if (method === "POST") {
2565
+ const outcome = await manager.createFromRequest(await readJson(req), header(req, "idempotency-key"));
2566
+ return json(res, outcome.created ? 201 : 200, outcome.result, true);
2567
+ }
2568
+ throw new RouteLeaseError("invalid_request", "method is not allowed");
2569
+ }
2570
+ const id = leaseId(segments[0]);
2571
+ if (segments.length === 1) {
2572
+ if (method === "GET") return json(res, 200, manager.get(id));
2573
+ if (method === "DELETE") return json(res, 200, manager.release(id));
2574
+ throw new RouteLeaseError("invalid_request", "method is not allowed");
2575
+ }
2576
+ if (segments.length === 2 && segments[1] === "renew" && method === "POST") {
2577
+ const body = await readJson(req);
2578
+ const ttl = normalizeRouteLeaseTtl(
2579
+ body && typeof body === "object" && !Array.isArray(body) ? body.ttlSeconds : void 0
2580
+ );
2581
+ return json(res, 200, manager.renew(id, ttl), true);
2582
+ }
2583
+ throw new RouteLeaseError("lease_not_found", "route lease endpoint was not found");
2584
+ } catch (error) {
2585
+ writeError(res, error, noStore);
2586
+ }
2587
+ }
2588
+
2499
2589
  // src/admin/adminApi.ts
2500
2590
  import http from "http";
2501
2591
  import {
@@ -3054,7 +3144,9 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
3054
3144
  // src/admin/cliLaunch.ts
3055
3145
  import { exec, spawn } from "child_process";
3056
3146
  import { randomUUID as randomUUID2 } from "crypto";
3057
- import { existsSync as existsSync6 } from "fs";
3147
+ import { chmodSync as chmodSync3, existsSync as existsSync6, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
3148
+ import { createServer } from "net";
3149
+ import { tmpdir } from "os";
3058
3150
  import { delimiter, join as join4 } from "path";
3059
3151
  import {
3060
3152
  buildChatCliLaunchConfig,
@@ -3062,6 +3154,33 @@ import {
3062
3154
  buildCodexLaunchConfig,
3063
3155
  buildGeminiCliLaunchConfig
3064
3156
  } from "@omnicross/cli-launcher";
3157
+ import {
3158
+ ROUTE_LEASE_REQUEST_SCHEMA,
3159
+ RouteLeaseError as RouteLeaseError2
3160
+ } from "@omnicross/core/provider-proxy";
3161
+
3162
+ // src/routeLeaseRenewal.ts
3163
+ var TERMINAL_LEASE_TTL_SECONDS = 600;
3164
+ var TERMINAL_LEASE_RENEW_INTERVAL_MS = 5 * 60 * 1e3;
3165
+ var TERMINAL_LEASE_MAX_LIFETIME_MS = 24 * 60 * 60 * 1e3;
3166
+ function startTerminalLeaseRenewal(manager, leaseId2) {
3167
+ const stopAt = Date.now() + TERMINAL_LEASE_MAX_LIFETIME_MS;
3168
+ const timer = setInterval(() => {
3169
+ if (Date.now() >= stopAt) {
3170
+ clearInterval(timer);
3171
+ return;
3172
+ }
3173
+ try {
3174
+ manager.renew(leaseId2, TERMINAL_LEASE_TTL_SECONDS);
3175
+ } catch {
3176
+ clearInterval(timer);
3177
+ }
3178
+ }, TERMINAL_LEASE_RENEW_INTERVAL_MS);
3179
+ timer.unref?.();
3180
+ return () => clearInterval(timer);
3181
+ }
3182
+
3183
+ // src/admin/cliLaunch.ts
3065
3184
  var LAUNCHABLE_CLIS = [
3066
3185
  { id: "claude", displayName: "Claude Code", command: "claude" },
3067
3186
  { id: "codex", displayName: "Codex CLI", command: "codex" },
@@ -3142,34 +3261,183 @@ async function buildLaunchEnv(cli, llmConfig, target) {
3142
3261
  function shq(s) {
3143
3262
  return `'${s.replace(/'/g, `'\\''`)}'`;
3144
3263
  }
3145
- var defaultTerminalOpener = ({ cli, command, extraArgs, env, cwd, platform }) => {
3264
+ var MAC_TERMINAL_BOOTSTRAP_SOURCE = `
3265
+ 'use strict';
3266
+ const fs = require('node:fs');
3267
+ const net = require('node:net');
3268
+ const { spawn } = require('node:child_process');
3269
+ const [socketPath, launchDir, cwd, command, ...args] = process.argv.slice(2);
3270
+ let payload = '';
3271
+ const socket = net.createConnection(socketPath);
3272
+ socket.setEncoding('utf8');
3273
+ socket.on('data', (chunk) => { payload += chunk; });
3274
+ socket.on('end', () => {
3275
+ const descriptor = JSON.parse(payload);
3276
+ if (!descriptor || Array.isArray(descriptor) || Object.values(descriptor).some((value) => typeof value !== 'string')) {
3277
+ throw new Error('invalid terminal launch descriptor');
3278
+ }
3279
+ try { fs.rmSync(launchDir, { recursive: true, force: true }); } catch {}
3280
+ const child = spawn(command, args, {
3281
+ cwd: cwd || undefined,
3282
+ env: { ...process.env, ...descriptor },
3283
+ stdio: 'inherit',
3284
+ });
3285
+ child.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
3286
+ child.on('exit', (code, signal) => {
3287
+ if (signal) process.kill(process.pid, signal);
3288
+ else process.exitCode = code == null ? 1 : code;
3289
+ });
3290
+ });
3291
+ socket.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
3292
+ `;
3293
+ var MAC_TERMINAL_IPC_TIMEOUT_MS = 12e4;
3294
+ function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }, spawnProcess = spawn, macIpc = {}) {
3146
3295
  const childEnv = { ...process.env, ...env };
3147
3296
  if (platform === "win32") {
3148
3297
  const args = ["/c", "start", `"omnicross ${cli}"`];
3149
3298
  if (cwd) args.push("/D", `"${cwd}"`);
3150
3299
  args.push("cmd", "/k", command, ...extraArgs);
3151
- spawn(process.env["ComSpec"] || "cmd.exe", args, {
3300
+ spawnProcess(process.env["ComSpec"] || "cmd.exe", args, {
3152
3301
  env: childEnv,
3153
3302
  windowsVerbatimArguments: true,
3154
3303
  detached: true,
3155
3304
  stdio: "ignore"
3156
3305
  }).unref();
3157
- return;
3306
+ return () => {
3307
+ };
3158
3308
  }
3159
- const exportLine = Object.entries(env).map(([k, v]) => `export ${k}=${shq(v)}`).join("; ");
3160
3309
  const runLine = [command, ...extraArgs].map(shq).join(" ");
3161
- const script = `${exportLine}; ${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
3310
+ const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
3162
3311
  if (platform === "darwin") {
3163
- const osa = `tell application "Terminal" to do script ${JSON.stringify(script)}`;
3164
- spawn("osascript", ["-e", osa], { detached: true, stdio: "ignore" }).unref();
3165
- return;
3312
+ const launchDir = mkdtempSync(join4(tmpdir(), "omnicross-terminal-"));
3313
+ const commandFile = join4(launchDir, "launch.command");
3314
+ const bootstrapFile = join4(launchDir, "bootstrap.cjs");
3315
+ const socketPath = macIpc.socketPath ?? join4(launchDir, "descriptor.sock");
3316
+ const openerEnv = { ...process.env };
3317
+ for (const key of Object.keys(env)) delete openerEnv[key];
3318
+ let claimed = false;
3319
+ let cleaned = false;
3320
+ let failureNotified = false;
3321
+ let timer;
3322
+ const notifyFailure = () => {
3323
+ cleanup();
3324
+ if (failureNotified) return;
3325
+ failureNotified = true;
3326
+ try {
3327
+ onFailure?.();
3328
+ } catch {
3329
+ }
3330
+ };
3331
+ const handleLaunchFailure = () => {
3332
+ if (claimed) cleanup();
3333
+ else notifyFailure();
3334
+ };
3335
+ const sockets = /* @__PURE__ */ new Set();
3336
+ const server = createServer((socket) => {
3337
+ socket.unref();
3338
+ sockets.add(socket);
3339
+ socket.once("close", () => sockets.delete(socket));
3340
+ try {
3341
+ macIpc.onAccepted?.(socket);
3342
+ } catch {
3343
+ cleanup();
3344
+ return;
3345
+ }
3346
+ if (claimed || cleaned) {
3347
+ socket.destroy();
3348
+ return;
3349
+ }
3350
+ claimed = true;
3351
+ try {
3352
+ macIpc.onClaimed?.();
3353
+ if (cleaned) return;
3354
+ socket.end(JSON.stringify(env), cleanup);
3355
+ } catch {
3356
+ cleanup();
3357
+ }
3358
+ });
3359
+ const cleanup = () => {
3360
+ if (!cleaned) {
3361
+ cleaned = true;
3362
+ if (timer) clearTimeout(timer);
3363
+ for (const socket of sockets) socket.destroy();
3364
+ sockets.clear();
3365
+ try {
3366
+ server.close();
3367
+ } catch {
3368
+ }
3369
+ }
3370
+ try {
3371
+ if (macIpc.removeArtifacts) {
3372
+ macIpc.removeArtifacts(launchDir);
3373
+ } else {
3374
+ rmSync2(launchDir, {
3375
+ recursive: true,
3376
+ force: true,
3377
+ maxRetries: 3,
3378
+ retryDelay: 20
3379
+ });
3380
+ }
3381
+ } catch {
3382
+ }
3383
+ };
3384
+ try {
3385
+ writeFileSync6(bootstrapFile, MAC_TERMINAL_BOOTSTRAP_SOURCE, { encoding: "utf8", mode: 448 });
3386
+ writeFileSync6(commandFile, `#!/bin/bash
3387
+ rm -f -- "$0"
3388
+ exec ${shq(process.execPath)} ${shq(bootstrapFile)} ${shq(socketPath)} ${shq(launchDir)} ${shq(cwd ?? "")} ${runLine}
3389
+ `, {
3390
+ encoding: "utf8",
3391
+ mode: 448
3392
+ });
3393
+ chmodSync3(commandFile, 448);
3394
+ chmodSync3(bootstrapFile, 448);
3395
+ server.once("error", handleLaunchFailure);
3396
+ server.listen(socketPath, () => {
3397
+ if (cleaned) return;
3398
+ try {
3399
+ macIpc.onListening?.();
3400
+ if (cleaned) return;
3401
+ if (process.platform !== "win32") chmodSync3(socketPath, 384);
3402
+ const opener = spawnProcess("open", ["-n", "-a", "Terminal", commandFile], {
3403
+ env: openerEnv,
3404
+ detached: true,
3405
+ stdio: "ignore"
3406
+ });
3407
+ opener.once("error", handleLaunchFailure);
3408
+ opener.unref();
3409
+ server.unref();
3410
+ } catch {
3411
+ handleLaunchFailure();
3412
+ }
3413
+ });
3414
+ timer = setTimeout(handleLaunchFailure, macIpc.timeoutMs ?? MAC_TERMINAL_IPC_TIMEOUT_MS);
3415
+ timer.unref?.();
3416
+ return cleanup;
3417
+ } catch (error) {
3418
+ cleanup();
3419
+ throw error;
3420
+ }
3166
3421
  }
3167
- spawn("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
3422
+ spawnProcess("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
3423
+ env: childEnv,
3168
3424
  detached: true,
3169
3425
  stdio: "ignore"
3170
3426
  }).unref();
3171
- };
3427
+ return () => {
3428
+ };
3429
+ }
3430
+ var defaultTerminalOpener = (input) => openTerminal(input);
3172
3431
  var sessions = /* @__PURE__ */ new Map();
3432
+ function resetCliSessions() {
3433
+ for (const s of sessions.values()) {
3434
+ try {
3435
+ s.onSessionEnd();
3436
+ } catch {
3437
+ }
3438
+ }
3439
+ sessions.clear();
3440
+ }
3173
3441
  function errBody(message) {
3174
3442
  return { error: { type: "admin_api_error", message } };
3175
3443
  }
@@ -3224,29 +3492,81 @@ async function handleCliLaunch(cli, body, ctx) {
3224
3492
  } catch (err5) {
3225
3493
  return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "no launch target") };
3226
3494
  }
3495
+ const id = randomUUID2();
3496
+ let leaseId2;
3227
3497
  let launch;
3228
3498
  try {
3229
- launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
3499
+ if ((cli === "claude" || cli === "codex") && ctx.routeLeaseManager) {
3500
+ const outcome = await ctx.routeLeaseManager.createFromRequest({
3501
+ schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA,
3502
+ consumer: "omnicross-terminal",
3503
+ runtime: cli,
3504
+ upstream: { kind: "provider", providerId: target.providerId },
3505
+ model: target.model,
3506
+ execution: { sessionId: id }
3507
+ }, `omnicross-terminal:${id}`);
3508
+ leaseId2 = outcome.result.leaseId;
3509
+ const stopRenewal = startTerminalLeaseRenewal(ctx.routeLeaseManager, leaseId2);
3510
+ launch = {
3511
+ env: outcome.result.launch.env,
3512
+ extraArgs: outcome.result.launch.extraArgs,
3513
+ onSessionEnd: () => {
3514
+ stopRenewal();
3515
+ ctx.routeLeaseManager?.release(outcome.result.leaseId);
3516
+ }
3517
+ };
3518
+ } else {
3519
+ launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
3520
+ }
3230
3521
  } catch (err5) {
3231
- return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
3522
+ const status = err5 instanceof RouteLeaseError2 ? err5.status : 400;
3523
+ return { status, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
3232
3524
  }
3233
3525
  const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
3234
3526
  const opener = ctx.opener ?? defaultTerminalOpener;
3527
+ let openerCleanup;
3528
+ let ended = false;
3529
+ let published = false;
3530
+ const onSessionEnd = () => {
3531
+ if (ended) return;
3532
+ ended = true;
3533
+ if (published) sessions.delete(id);
3534
+ try {
3535
+ openerCleanup?.();
3536
+ } finally {
3537
+ launch.onSessionEnd();
3538
+ }
3539
+ };
3235
3540
  try {
3236
- opener({ cli, command: meta.command, extraArgs: launch.extraArgs ?? [], env: launch.env, cwd, platform });
3541
+ const cleanup = opener({
3542
+ cli,
3543
+ command: meta.command,
3544
+ extraArgs: launch.extraArgs ?? [],
3545
+ env: launch.env,
3546
+ cwd,
3547
+ platform,
3548
+ onFailure: onSessionEnd
3549
+ });
3550
+ if (cleanup) openerCleanup = cleanup;
3237
3551
  } catch (err5) {
3238
- launch.onSessionEnd();
3552
+ onSessionEnd();
3239
3553
  return { status: 500, body: errBody(err5 instanceof Error ? err5.message : "failed to open terminal") };
3240
3554
  }
3241
- const id = randomUUID2();
3555
+ if (ended) {
3556
+ openerCleanup?.();
3557
+ return { status: 500, body: errBody("failed to open terminal") };
3558
+ }
3242
3559
  sessions.set(id, {
3243
3560
  id,
3244
3561
  cli,
3245
3562
  providerId: target.providerId,
3246
3563
  model: target.model,
3564
+ ...leaseId2 ? { leaseId: leaseId2 } : {},
3247
3565
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
3248
- onSessionEnd: launch.onSessionEnd
3566
+ onSessionEnd
3249
3567
  });
3568
+ published = true;
3569
+ if (ended) sessions.delete(id);
3250
3570
  return { status: 200, body: { sessionId: id, providerId: target.providerId, model: target.model } };
3251
3571
  }
3252
3572
 
@@ -4133,7 +4453,7 @@ function sealPack(bundleJson, passphrase) {
4133
4453
  cipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
4134
4454
  const ciphertext = Buffer.concat([cipher.update(bundleJson, "utf8"), cipher.final()]);
4135
4455
  const tag = cipher.getAuthTag();
4136
- const header = {
4456
+ const header2 = {
4137
4457
  magic: PACK_MAGIC,
4138
4458
  v: PACK_VERSION,
4139
4459
  kdf: KDF_ALGORITHM,
@@ -4144,7 +4464,7 @@ function sealPack(bundleJson, passphrase) {
4144
4464
  iv: iv.toString("base64"),
4145
4465
  tag: tag.toString("base64")
4146
4466
  };
4147
- return `${PACK_PREFIX}${toB64Url(JSON.stringify(header))}.${ciphertext.toString("base64")}`;
4467
+ return `${PACK_PREFIX}${toB64Url(JSON.stringify(header2))}.${ciphertext.toString("base64")}`;
4148
4468
  }
4149
4469
  function parsePack(packString) {
4150
4470
  if (typeof packString !== "string" || !packString.startsWith(PACK_PREFIX)) {
@@ -4155,28 +4475,28 @@ function parsePack(packString) {
4155
4475
  if (dot < 0) throw new PackAuthError("migration pack is malformed (missing body)");
4156
4476
  const headerB64Url = rest.slice(0, dot);
4157
4477
  const ctB64 = rest.slice(dot + 1);
4158
- let header;
4478
+ let header2;
4159
4479
  try {
4160
- header = JSON.parse(fromB64Url(headerB64Url));
4480
+ header2 = JSON.parse(fromB64Url(headerB64Url));
4161
4481
  } catch {
4162
4482
  throw new PackAuthError("migration pack is malformed (unreadable header)");
4163
4483
  }
4164
- 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") {
4484
+ if (!header2 || header2.magic !== PACK_MAGIC || header2.v !== PACK_VERSION || header2.kdf !== KDF_ALGORITHM || typeof header2.salt !== "string" || typeof header2.iv !== "string" || typeof header2.tag !== "string" || typeof header2.N !== "number" || typeof header2.r !== "number" || typeof header2.p !== "number") {
4165
4485
  throw new PackAuthError("migration pack is malformed (unsupported header)");
4166
4486
  }
4167
4487
  const ciphertext = Buffer.from(ctB64, "base64");
4168
- return { header, ciphertext };
4488
+ return { header: header2, ciphertext };
4169
4489
  }
4170
4490
  function openPack(packString, passphrase) {
4171
4491
  assertPassphraseStrength(passphrase);
4172
- const { header, ciphertext } = parsePack(packString);
4173
- const salt = Buffer.from(header.salt, "base64");
4174
- const iv = Buffer.from(header.iv, "base64");
4175
- const tag = Buffer.from(header.tag, "base64");
4492
+ const { header: header2, ciphertext } = parsePack(packString);
4493
+ const salt = Buffer.from(header2.salt, "base64");
4494
+ const iv = Buffer.from(header2.iv, "base64");
4495
+ const tag = Buffer.from(header2.tag, "base64");
4176
4496
  if (iv.length !== IV_BYTES2 || tag.length !== TAG_BYTES2) {
4177
4497
  throw new PackAuthError("migration pack is malformed (invalid iv/tag length)");
4178
4498
  }
4179
- const key = deriveKey(passphrase, salt, header.N, header.r, header.p);
4499
+ const key = deriveKey(passphrase, salt, header2.N, header2.r, header2.p);
4180
4500
  const decipher = createDecipheriv2("aes-256-gcm", key, iv);
4181
4501
  decipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
4182
4502
  decipher.setAuthTag(tag);
@@ -4512,10 +4832,10 @@ function writeJson2(res, status, body) {
4512
4832
  res.writeHead(status, { "Content-Type": "application/json" });
4513
4833
  res.end(JSON.stringify(body));
4514
4834
  }
4515
- function writeError(res, status, message) {
4835
+ function writeError2(res, status, message) {
4516
4836
  writeJson2(res, status, { error: { type: "account_allowance_error", message } });
4517
4837
  }
4518
- function readJson(req) {
4838
+ function readJson2(req) {
4519
4839
  return new Promise((resolve3, reject) => {
4520
4840
  const chunks = [];
4521
4841
  req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
@@ -4541,10 +4861,10 @@ function allowanceProvider(value) {
4541
4861
  return value === "claude" || value === "codex" ? value : null;
4542
4862
  }
4543
4863
  async function handleAccountAllowanceApi(req, res, method, rest, service) {
4544
- if (!service) return writeError(res, 501, "account allowance service is not available");
4864
+ if (!service) return writeError2(res, 501, "account allowance service is not available");
4545
4865
  if (method === "GET" && rest.length === 1 && rest[0] === "scheduling") {
4546
4866
  if (!service.getSchedulingStatus) {
4547
- return writeError(res, 501, "allowance scheduling diagnostics are not available");
4867
+ return writeError2(res, 501, "allowance scheduling diagnostics are not available");
4548
4868
  }
4549
4869
  return writeJson2(res, 200, { scheduling: service.getSchedulingStatus() });
4550
4870
  }
@@ -4552,27 +4872,27 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
4552
4872
  const params = query(req);
4553
4873
  const pathProvider = rest.length >= 2 ? rest[0] : null;
4554
4874
  const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
4555
- if (providerId === null) return writeError(res, 400, "providerId must be claude or codex");
4875
+ if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
4556
4876
  const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
4557
4877
  const allowances = await service.list({ providerId, accountId });
4558
4878
  return writeJson2(res, 200, { allowances });
4559
4879
  }
4560
4880
  if (method === "POST" && rest[0] === "refresh") {
4561
- const body = await readJson(req);
4881
+ const body = await readJson2(req);
4562
4882
  const requestedProvider = allowanceProvider(
4563
4883
  typeof body["providerId"] === "string" ? body["providerId"] : "claude"
4564
4884
  );
4565
4885
  if (requestedProvider !== "claude") {
4566
- return writeError(res, 400, "only Claude allowances support explicit refresh");
4886
+ return writeError2(res, 400, "only Claude allowances support explicit refresh");
4567
4887
  }
4568
4888
  const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
4569
4889
  const allowances = await service.refreshClaude(accountId);
4570
4890
  if (accountId && allowances.length === 0) {
4571
- return writeError(res, 404, `Claude account '${accountId}' not found`);
4891
+ return writeError2(res, 404, `Claude account '${accountId}' not found`);
4572
4892
  }
4573
4893
  return writeJson2(res, 200, { allowances });
4574
4894
  }
4575
- return writeError(res, 405, `method ${method} not allowed on account allowances`);
4895
+ return writeError2(res, 405, `method ${method} not allowed on account allowances`);
4576
4896
  }
4577
4897
 
4578
4898
  // src/admin/adminApi.ts
@@ -5807,6 +6127,7 @@ async function handleCli(req, res, method, rest, deps) {
5807
6127
  const result = await handleCliLaunch(cli, body, {
5808
6128
  llmConfig: deps.llmConfig,
5809
6129
  providers,
6130
+ routeLeaseManager: deps.routeLeaseManager,
5810
6131
  opener: deps.cliTerminalOpener,
5811
6132
  probe: deps.cliPathProbe
5812
6133
  });
@@ -6069,7 +6390,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
6069
6390
  }
6070
6391
 
6071
6392
  // src/admin/version.ts
6072
- var DAEMON_VERSION = true ? "0.1.7" : "0.0.0-dev";
6393
+ var DAEMON_VERSION = true ? "0.1.8" : "0.0.0-dev";
6073
6394
 
6074
6395
  // src/admin/AdminServer.ts
6075
6396
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -6189,6 +6510,10 @@ var AdminServer = class {
6189
6510
  await handleWebhookTest(req, res);
6190
6511
  return;
6191
6512
  }
6513
+ if (path2 === "/admin/api/route-leases" || path2.startsWith("/admin/api/route-leases/")) {
6514
+ await handleRouteLeaseApi(req, res, path2, this.deps);
6515
+ return;
6516
+ }
6192
6517
  if (path2.startsWith("/admin/api/")) {
6193
6518
  await handleAdminApi(req, res, path2, this.deps);
6194
6519
  return;
@@ -6200,8 +6525,8 @@ var AdminServer = class {
6200
6525
  }
6201
6526
  /** Constant-time bearer/header check against the configured token. */
6202
6527
  isAuthorized(req, token) {
6203
- const header = req.headers["authorization"];
6204
- const bearer = typeof header === "string" && header.startsWith("Bearer ") ? header.slice("Bearer ".length).trim() : void 0;
6528
+ const header2 = req.headers["authorization"];
6529
+ const bearer = typeof header2 === "string" && header2.startsWith("Bearer ") ? header2.slice("Bearer ".length).trim() : void 0;
6205
6530
  const xToken = req.headers["x-admin-token"];
6206
6531
  const presented = bearer ?? (typeof xToken === "string" ? xToken.trim() : void 0);
6207
6532
  return constantTimeEquals(presented, token);
@@ -6336,7 +6661,7 @@ var OAuthSessionStore = class {
6336
6661
  };
6337
6662
 
6338
6663
  // src/commands/loopbackCallback.ts
6339
- import { createServer } from "http";
6664
+ import { createServer as createServer2 } from "http";
6340
6665
  var LOOPBACK_HOST = "127.0.0.1";
6341
6666
  var LOOPBACK_PORT = 1455;
6342
6667
  var CALLBACK_PATH = "/auth/callback";
@@ -6358,7 +6683,7 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
6358
6683
  fn();
6359
6684
  server2.close();
6360
6685
  };
6361
- const server = createServer((req, res) => {
6686
+ const server = createServer2((req, res) => {
6362
6687
  const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
6363
6688
  if (url.pathname !== CALLBACK_PATH) {
6364
6689
  res.writeHead(404, HTML_HEADERS);
@@ -6780,7 +7105,7 @@ function safeStringify(value) {
6780
7105
  }
6781
7106
 
6782
7107
  // src/ports/JsonApiServerSettingsStore.ts
6783
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
7108
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
6784
7109
  import { OUTBOUND_API_SERVER_CONFIG_KEY } from "@omnicross/core/outbound-api";
6785
7110
  var JsonApiServerSettingsStore = class {
6786
7111
  /**
@@ -6807,7 +7132,7 @@ var JsonApiServerSettingsStore = class {
6807
7132
  if (key !== OUTBOUND_API_SERVER_CONFIG_KEY) return;
6808
7133
  const file = this.readFile();
6809
7134
  file.server = this.encryptSecrets(value);
6810
- writeFileSync6(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
7135
+ writeFileSync7(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
6811
7136
  }
6812
7137
  /** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
6813
7138
  encryptSecrets(config) {
@@ -7161,7 +7486,7 @@ function median(values) {
7161
7486
  }
7162
7487
 
7163
7488
  // src/ports/JsonPricingStore.ts
7164
- import { existsSync as existsSync9, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
7489
+ import { existsSync as existsSync9, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
7165
7490
  import { randomUUID as randomUUID5 } from "crypto";
7166
7491
  var JsonPricingStore = class {
7167
7492
  constructor(pricingPath) {
@@ -7302,13 +7627,13 @@ var JsonPricingStore = class {
7302
7627
  writeRows(rows) {
7303
7628
  const temporaryPath = `${this.pricingPath}.${process.pid}.${randomUUID5()}.tmp`;
7304
7629
  try {
7305
- writeFileSync7(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
7630
+ writeFileSync8(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
7306
7631
  encoding: "utf8",
7307
7632
  flag: "wx"
7308
7633
  });
7309
7634
  this.replaceFile(temporaryPath);
7310
7635
  } finally {
7311
- rmSync2(temporaryPath, { force: true });
7636
+ rmSync3(temporaryPath, { force: true });
7312
7637
  }
7313
7638
  }
7314
7639
  /** Isolated for deterministic failure testing; never removes the target. */
@@ -7323,7 +7648,7 @@ function isUsablePricingRow(value) {
7323
7648
  }
7324
7649
 
7325
7650
  // src/pricing/PricingRefreshScheduler.ts
7326
- import { existsSync as existsSync10, readFileSync as readFileSync11, renameSync as renameSync4, writeFileSync as writeFileSync8 } from "fs";
7651
+ import { existsSync as existsSync10, readFileSync as readFileSync11, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
7327
7652
  var EMPTY_STATE2 = {
7328
7653
  lastAttemptAt: null,
7329
7654
  lastSuccessAt: null,
@@ -7416,7 +7741,7 @@ var PricingRefreshScheduler = class {
7416
7741
  }
7417
7742
  writeState(state) {
7418
7743
  const temporaryPath = `${this.statePath}.tmp`;
7419
- writeFileSync8(temporaryPath, `${JSON.stringify(state, null, 2)}
7744
+ writeFileSync9(temporaryPath, `${JSON.stringify(state, null, 2)}
7420
7745
  `, "utf8");
7421
7746
  renameSync4(temporaryPath, this.statePath);
7422
7747
  }
@@ -7426,7 +7751,7 @@ function finiteOrNull(value) {
7426
7751
  }
7427
7752
 
7428
7753
  // src/ports/JsonVoucherDb.ts
7429
- import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
7754
+ import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
7430
7755
  var JsonVoucherDb = class {
7431
7756
  constructor(vouchersPath) {
7432
7757
  this.vouchersPath = vouchersPath;
@@ -7513,12 +7838,12 @@ var JsonVoucherDb = class {
7513
7838
  }
7514
7839
  }
7515
7840
  writeRows(rows) {
7516
- writeFileSync9(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
7841
+ writeFileSync10(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
7517
7842
  }
7518
7843
  };
7519
7844
 
7520
7845
  // src/ports/JsonSubscriptionCredentialStore.ts
7521
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
7846
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
7522
7847
  import { dirname as dirname6 } from "path";
7523
7848
  import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
7524
7849
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
@@ -8201,7 +8526,7 @@ var JsonSubscriptionCredentialStore = class {
8201
8526
  persist(config) {
8202
8527
  mkdirSync4(dirname6(this.tokensPath), { recursive: true });
8203
8528
  const encrypted = encryptTokens(config, this.box);
8204
- writeFileSync10(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
8529
+ writeFileSync11(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
8205
8530
  }
8206
8531
  /**
8207
8532
  * Read + parse `tokens.json`, tolerating a missing/corrupt file, then DECRYPT
@@ -8770,7 +9095,7 @@ import {
8770
9095
  readFileSync as readFileSync15,
8771
9096
  readdirSync,
8772
9097
  statSync as statSync3,
8773
- writeFileSync as writeFileSync11
9098
+ writeFileSync as writeFileSync12
8774
9099
  } from "fs";
8775
9100
  import { basename, dirname as dirname7, join as join6 } from "path";
8776
9101
  var SIDECAR_VERSION = 1;
@@ -8812,7 +9137,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
8812
9137
  minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
8813
9138
  maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
8814
9139
  };
8815
- writeFileSync11(statsPath, JSON.stringify(next), "utf8");
9140
+ writeFileSync12(statsPath, JSON.stringify(next), "utf8");
8816
9141
  }
8817
9142
  function queryCovers(stats, from, to) {
8818
9143
  return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
@@ -8956,7 +9281,7 @@ async function readAuditStats(auditDir, query2 = {}) {
8956
9281
  total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
8957
9282
  total.complete = total.complete && scanned.filtered.complete;
8958
9283
  const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
8959
- if (current.complete) writeFileSync11(statsPath, JSON.stringify(current), "utf8");
9284
+ if (current.complete) writeFileSync12(statsPath, JSON.stringify(current), "utf8");
8960
9285
  } catch {
8961
9286
  total.complete = false;
8962
9287
  }
@@ -9518,6 +9843,78 @@ var TokenRefreshScheduler = class {
9518
9843
  }
9519
9844
  };
9520
9845
 
9846
+ // src/routeLeaseSubscriptionPreflight.ts
9847
+ import {
9848
+ RouteLeaseError as RouteLeaseError3
9849
+ } from "@omnicross/core/provider-proxy";
9850
+ import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
9851
+ import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
9852
+ import { accountSupportsModel } from "@omnicross/subscriptions/scheduler/accountModelMap";
9853
+ var PROVIDERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "opencodego"]);
9854
+ function accountArray(config, providerId) {
9855
+ const record = config;
9856
+ const key = `${providerId}Accounts`;
9857
+ const accounts = record[key];
9858
+ if (Array.isArray(accounts)) return accounts;
9859
+ const legacy = record[providerId];
9860
+ if (!legacy || typeof legacy !== "object") return [];
9861
+ const activeKey = `active${providerId[0].toUpperCase()}${providerId.slice(1)}AccountId`;
9862
+ return [{ id: String(record[activeKey] ?? "active"), enabled: true, tokens: legacy }];
9863
+ }
9864
+ function hasCredential(providerId, account) {
9865
+ const tokens = account.tokens;
9866
+ if (providerId === "opencodego") return typeof tokens.apiKey === "string" && tokens.apiKey.length > 0;
9867
+ return typeof tokens.accessToken === "string" && tokens.accessToken.length > 0;
9868
+ }
9869
+ function safeProviderId(value) {
9870
+ if (!PROVIDERS.has(value)) {
9871
+ throw new RouteLeaseError3("upstream_not_found", "subscription provider was not found");
9872
+ }
9873
+ return value;
9874
+ }
9875
+ function createRouteLeaseSubscriptionPreflight(credentials) {
9876
+ return {
9877
+ async assertAvailable(upstream, model) {
9878
+ const providerId = safeProviderId(upstream.providerId);
9879
+ const config = await credentials.getFullConfig();
9880
+ const all = accountArray(config, providerId);
9881
+ if (all.length === 0) {
9882
+ throw new RouteLeaseError3("upstream_unavailable", "subscription provider has no configured account");
9883
+ }
9884
+ let bounded = all;
9885
+ if (upstream.kind === "account") {
9886
+ bounded = all.filter((account) => account.id === upstream.accountId);
9887
+ } else if (upstream.kind === "account-group") {
9888
+ bounded = all.filter((account) => account.group?.trim() === upstream.group);
9889
+ }
9890
+ if (bounded.length === 0) {
9891
+ throw new RouteLeaseError3("upstream_not_found", "the selected subscription resource was not found");
9892
+ }
9893
+ const modelEligible = bounded.filter(
9894
+ (account) => accountSupportsModel(account.supportedModels, model)
9895
+ );
9896
+ if (modelEligible.length === 0) {
9897
+ throw new RouteLeaseError3("model_not_configured", "model is not supported by the selected subscription resource");
9898
+ }
9899
+ const credentialEligible = modelEligible.filter(
9900
+ (account) => account.enabled !== false && hasCredential(providerId, account)
9901
+ );
9902
+ const health2 = getSharedAccountHealth3();
9903
+ const allowance = getSharedAccountAllowanceScheduling4();
9904
+ const candidates = credentialEligible.filter(
9905
+ (account) => health2.isSchedulable(providerId, account.id) && allowance.preview(providerId, account.id, account.priority ?? 50).schedulable
9906
+ );
9907
+ if (candidates.length > 0) return;
9908
+ if (upstream.kind === "account") {
9909
+ throw new RouteLeaseError3("upstream_unavailable", "the selected subscription account is unavailable");
9910
+ }
9911
+ throw new RouteLeaseError3("upstream_exhausted", "the selected subscription pool has no eligible account", {
9912
+ retryAfterSeconds: 30
9913
+ });
9914
+ }
9915
+ };
9916
+ }
9917
+
9521
9918
  // src/webhook/WebhookDispatcher.ts
9522
9919
  import { createHmac as createHmac2 } from "crypto";
9523
9920
  import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
@@ -9700,7 +10097,7 @@ function buildDaemon(config, paths) {
9700
10097
  new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
9701
10098
  );
9702
10099
  setSharedAccountAllowanceStore(accountAllowanceStore);
9703
- getSharedAccountAllowanceScheduling4().configure(
10100
+ getSharedAccountAllowanceScheduling5().configure(
9704
10101
  normalizeServerConfig(decryptedConfig.server).allowanceScheduling
9705
10102
  );
9706
10103
  const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
@@ -9768,10 +10165,21 @@ function buildDaemon(config, paths) {
9768
10165
  onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
9769
10166
  });
9770
10167
  const providerProxy = getProviderProxy({ llmConfig, apiKeyPool, usageRecorder });
10168
+ const routeLeaseManager = new RouteLeaseManager(
10169
+ providerProxy,
10170
+ new RouteLeaseTargetResolver(llmConfig, {
10171
+ providerKeys: apiKeyPool,
10172
+ subscriptions: createRouteLeaseSubscriptionPreflight(credentialStore)
10173
+ }),
10174
+ routeLeaseDescriptorPort,
10175
+ { logger }
10176
+ );
10177
+ providerProxy.registerBeforeStop(() => routeLeaseManager.shutdown());
10178
+ providerProxy.registerBeforeStop(() => resetCliSessions());
9771
10179
  llmConfig.setReloadHook(() => apiKeyPool.invalidateCache());
9772
10180
  const accountHealthProbeScheduler = new AccountHealthProbeScheduler(
9773
10181
  credentialStore,
9774
- getSharedAccountHealth3(),
10182
+ getSharedAccountHealth4(),
9775
10183
  logger,
9776
10184
  DEFAULT_ACCOUNT_PROBE
9777
10185
  );
@@ -9818,6 +10226,7 @@ function buildDaemon(config, paths) {
9818
10226
  keySpendReader: keySpendTracker,
9819
10227
  settingsStore,
9820
10228
  outboundApiServer,
10229
+ routeLeaseManager,
9821
10230
  subscriptionAccounts,
9822
10231
  accountAllowanceService,
9823
10232
  allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
@@ -9910,7 +10319,7 @@ function buildDaemon(config, paths) {
9910
10319
  logger,
9911
10320
  fetchImpl: (url, init) => fetchUpstream7(url, init)
9912
10321
  });
9913
- setWebhookRuntime(webhookDispatcher, getSharedAccountHealth3());
10322
+ setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
9914
10323
  const auditWriter = new AuditWriter(auditDir, logger);
9915
10324
  const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
9916
10325
  setAuditRuntime(auditWriter, auditPruneSweeper);
@@ -9925,7 +10334,7 @@ function buildDaemon(config, paths) {
9925
10334
  const tokenRefreshScheduler = new TokenRefreshScheduler(credentialStore, logger);
9926
10335
  const accountHealthSweeper = new AccountHealthSweeper(
9927
10336
  credentialStore,
9928
- getSharedAccountHealth3(),
10337
+ getSharedAccountHealth4(),
9929
10338
  logger
9930
10339
  );
9931
10340
  return {
@@ -9934,6 +10343,7 @@ function buildDaemon(config, paths) {
9934
10343
  keyDb,
9935
10344
  settingsStore,
9936
10345
  providerProxy,
10346
+ routeLeaseManager,
9937
10347
  outboundApiServer,
9938
10348
  apiKeyPool,
9939
10349
  autoDisableStore,
@@ -10049,32 +10459,17 @@ async function runLaunch(argv, deps) {
10049
10459
  await daemon.llmConfig.ready();
10050
10460
  await daemon.providerProxy.start();
10051
10461
  } catch (err5) {
10052
- daemon.apiKeyPool.dispose();
10053
- daemon.tokenRefreshScheduler.dispose();
10054
- daemon.claudeAllowanceRefreshScheduler.dispose();
10055
- daemon.accountHealthSweeper.dispose();
10056
- daemon.accountHealthProbeScheduler.dispose();
10057
- daemon.auditPruneSweeper.dispose();
10058
- daemon.billingRetrySweeper.dispose();
10059
- daemon.pricingRefreshScheduler.dispose();
10462
+ await shutdownLaunchDaemon(daemon);
10060
10463
  throw err5;
10061
10464
  }
10062
10465
  let launch;
10063
10466
  try {
10064
- launch = await buildLaunchConfig(cli, daemon.llmConfig, {
10467
+ launch = await buildLaunchConfig(cli, daemon, {
10065
10468
  providerId: values.provider,
10066
10469
  model: values.model
10067
10470
  });
10068
10471
  } catch (err5) {
10069
- await daemon.providerProxy.stop();
10070
- daemon.apiKeyPool.dispose();
10071
- daemon.tokenRefreshScheduler.dispose();
10072
- daemon.claudeAllowanceRefreshScheduler.dispose();
10073
- daemon.accountHealthSweeper.dispose();
10074
- daemon.accountHealthProbeScheduler.dispose();
10075
- daemon.auditPruneSweeper.dispose();
10076
- daemon.billingRetrySweeper.dispose();
10077
- daemon.pricingRefreshScheduler.dispose();
10472
+ await shutdownLaunchDaemon(daemon);
10078
10473
  throw err5;
10079
10474
  }
10080
10475
  try {
@@ -10093,21 +10488,40 @@ async function runLaunch(argv, deps) {
10093
10488
  cwd: values.cwd
10094
10489
  });
10095
10490
  } finally {
10096
- launch.onSessionEnd();
10097
- await daemon.providerProxy.stop();
10098
- daemon.apiKeyPool.dispose();
10099
- daemon.tokenRefreshScheduler.dispose();
10100
- daemon.claudeAllowanceRefreshScheduler.dispose();
10101
- daemon.accountHealthSweeper.dispose();
10102
- daemon.accountHealthProbeScheduler.dispose();
10103
- daemon.auditPruneSweeper.dispose();
10104
- daemon.billingRetrySweeper.dispose();
10105
- daemon.pricingRefreshScheduler.dispose();
10106
- }
10107
- }
10108
- async function buildLaunchConfig(cli, llmConfig, opts) {
10491
+ try {
10492
+ launch.onSessionEnd();
10493
+ } finally {
10494
+ await shutdownLaunchDaemon(daemon);
10495
+ }
10496
+ }
10497
+ }
10498
+ async function buildLaunchConfig(cli, daemon, opts) {
10499
+ if (cli === "claude" || cli === "codex") {
10500
+ const internalId = randomUUID6();
10501
+ const outcome = await daemon.routeLeaseManager.createFromRequest({
10502
+ schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA2,
10503
+ consumer: "omnicross-terminal",
10504
+ runtime: cli,
10505
+ upstream: { kind: "provider", providerId: opts.providerId },
10506
+ model: opts.model,
10507
+ execution: { sessionId: `launch:${cli}:${internalId}` }
10508
+ }, `omnicross-launch:${internalId}`);
10509
+ const stopRenewal = startTerminalLeaseRenewal(
10510
+ daemon.routeLeaseManager,
10511
+ outcome.result.leaseId
10512
+ );
10513
+ return {
10514
+ baseUrl: daemon.providerProxy.getBaseUrl(),
10515
+ env: outcome.result.launch.env,
10516
+ extraArgs: outcome.result.launch.extraArgs,
10517
+ onSessionEnd: () => {
10518
+ stopRenewal();
10519
+ daemon.routeLeaseManager.release(outcome.result.leaseId);
10520
+ }
10521
+ };
10522
+ }
10109
10523
  const common = {
10110
- llmConfig,
10524
+ llmConfig: daemon.llmConfig,
10111
10525
  providerId: opts.providerId,
10112
10526
  model: opts.model,
10113
10527
  // Stable, bounded session id — pool failover (poolseam) fires on launch
@@ -10115,10 +10529,6 @@ async function buildLaunchConfig(cli, llmConfig, opts) {
10115
10529
  sessionId: `launch:${cli}`
10116
10530
  };
10117
10531
  switch (cli) {
10118
- case "claude":
10119
- return buildClaudeCliLaunchConfig2(common);
10120
- case "codex":
10121
- return buildCodexLaunchConfig2(common);
10122
10532
  case "gemini":
10123
10533
  return buildGeminiCliLaunchConfig2(common);
10124
10534
  case "qwen":
@@ -10131,6 +10541,18 @@ async function buildLaunchConfig(cli, llmConfig, opts) {
10131
10541
  }
10132
10542
  }
10133
10543
  }
10544
+ async function shutdownLaunchDaemon(daemon) {
10545
+ daemon.routeLeaseManager.shutdown();
10546
+ await daemon.providerProxy.stop();
10547
+ daemon.apiKeyPool.dispose();
10548
+ daemon.tokenRefreshScheduler.dispose();
10549
+ daemon.claudeAllowanceRefreshScheduler.dispose();
10550
+ daemon.accountHealthSweeper.dispose();
10551
+ daemon.accountHealthProbeScheduler.dispose();
10552
+ daemon.auditPruneSweeper.dispose();
10553
+ daemon.billingRetrySweeper.dispose();
10554
+ daemon.pricingRefreshScheduler.dispose();
10555
+ }
10134
10556
  function spawnCliInherit(plan) {
10135
10557
  return new Promise((resolve3, reject) => {
10136
10558
  const child = spawn2(plan.command, plan.args, {
@@ -10177,7 +10599,7 @@ import { createInterface } from "readline";
10177
10599
  import { parseArgs as parseArgs5 } from "util";
10178
10600
  import { fetchUpstream as fetchUpstream8, setUpstreamProxyResolver as setUpstreamProxyResolver2 } from "@omnicross/core/pipeline/upstreamFetch";
10179
10601
  import { claudeOAuth as claudeOAuth3, codexOAuth as codexOAuth3, geminiOAuth as geminiOAuth3 } from "@omnicross/subscriptions";
10180
- var PROVIDERS = ["claude", "codex", "gemini"];
10602
+ var PROVIDERS2 = ["claude", "codex", "gemini"];
10181
10603
  async function runLogin(argv, deps) {
10182
10604
  const { values, positionals } = parseArgs5({
10183
10605
  args: argv,
@@ -10191,10 +10613,10 @@ async function runLogin(argv, deps) {
10191
10613
  });
10192
10614
  const provider = positionals[0];
10193
10615
  if (!provider) {
10194
- throw new Error(`login: a <provider> is required (one of ${PROVIDERS.join("|")})`);
10616
+ throw new Error(`login: a <provider> is required (one of ${PROVIDERS2.join("|")})`);
10195
10617
  }
10196
10618
  if (!isLoginProvider(provider)) {
10197
- throw new Error(`login: unknown provider '${provider}' (expected ${PROVIDERS.join("|")})`);
10619
+ throw new Error(`login: unknown provider '${provider}' (expected ${PROVIDERS2.join("|")})`);
10198
10620
  }
10199
10621
  if (!values.config) {
10200
10622
  throw new Error("login: --config <path> is required");
@@ -10300,7 +10722,7 @@ async function loginGemini(store, deps, exchangeFetch, label) {
10300
10722
  return expiresAt;
10301
10723
  }
10302
10724
  function isLoginProvider(value) {
10303
- return PROVIDERS.includes(value);
10725
+ return PROVIDERS2.includes(value);
10304
10726
  }
10305
10727
  async function presentUrl(authUrl, deps) {
10306
10728
  console.info("Open this URL in your browser to authorize:");
@@ -10346,7 +10768,7 @@ function promptPaste(prompt) {
10346
10768
  }
10347
10769
 
10348
10770
  // src/commands/providers.ts
10349
- import { randomUUID as randomUUID6 } from "crypto";
10771
+ import { randomUUID as randomUUID7 } from "crypto";
10350
10772
  import { parseArgs as parseArgs6 } from "util";
10351
10773
  async function runProviders(argv) {
10352
10774
  const { values, positionals } = parseArgs6({
@@ -10468,7 +10890,7 @@ function providersAddKey(configPath, providerId, opts) {
10468
10890
  const cfg = loadConfig(configPath);
10469
10891
  const row = cfg.providers.find((p) => p.id === providerId);
10470
10892
  if (!row) throw new Error(`providers add-key: unknown provider '${providerId}'`);
10471
- const entry = { id: randomUUID6(), apiKey: opts.key };
10893
+ const entry = { id: randomUUID7(), apiKey: opts.key };
10472
10894
  if (opts.label) entry.label = opts.label;
10473
10895
  if (opts.weight !== void 0) {
10474
10896
  const w = Number(opts.weight);
@@ -10496,7 +10918,7 @@ function providersRmKey(configPath, providerId, keyId) {
10496
10918
  }
10497
10919
 
10498
10920
  // src/commands/secrets.ts
10499
- import { existsSync as existsSync21, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
10921
+ import { existsSync as existsSync21, readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
10500
10922
  import { parseArgs as parseArgs7 } from "util";
10501
10923
  async function runSecrets(argv) {
10502
10924
  const { values, positionals } = parseArgs7({
@@ -10670,7 +11092,7 @@ function secretsDecrypt(args) {
10670
11092
  }
10671
11093
  saveConfig(args.config, cfg);
10672
11094
  if (tokensPlain) {
10673
- writeFileSync12(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
11095
+ writeFileSync13(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
10674
11096
  }
10675
11097
  console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
10676
11098
  }
@@ -10714,7 +11136,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
10714
11136
  { updatedAt: "", ...plain },
10715
11137
  box
10716
11138
  );
10717
- writeFileSync12(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
11139
+ writeFileSync13(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
10718
11140
  }
10719
11141
  var TOKEN_FIELDS2 = {
10720
11142
  claude: ["accessToken", "refreshToken"],
@@ -10743,8 +11165,8 @@ function tokensSuffix(configPath) {
10743
11165
  // src/commands/start.ts
10744
11166
  import { parseArgs as parseArgs8 } from "util";
10745
11167
  import { loadServerConfig as loadServerConfig3 } from "@omnicross/core/outbound-api";
10746
- import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
10747
- import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
11168
+ import { getSharedAccountHealth as getSharedAccountHealth5 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
11169
+ import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling6 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
10748
11170
 
10749
11171
  // src/identity/identityRuntime.ts
10750
11172
  import { getSharedIdentityStore as getSharedIdentityStore3 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
@@ -10804,11 +11226,11 @@ async function runStart(argv) {
10804
11226
  await daemon.llmConfig.ready();
10805
11227
  await daemon.providerProxy.start();
10806
11228
  const serverConfig = await loadServerConfig3(daemon.settingsStore);
10807
- getSharedAccountHealth4().configure({
11229
+ getSharedAccountHealth5().configure({
10808
11230
  overloadEnabled: serverConfig.accountHealth?.overloadCooldownEnabled,
10809
11231
  overloadTtlMs: serverConfig.accountHealth?.overloadCooldownMs
10810
11232
  });
10811
- getSharedAccountAllowanceScheduling5().configure(serverConfig.allowanceScheduling);
11233
+ getSharedAccountAllowanceScheduling6().configure(serverConfig.allowanceScheduling);
10812
11234
  daemon.claudeAllowanceRefreshScheduler.configure(serverConfig.allowanceScheduling);
10813
11235
  await daemon.outboundApiServer.applyConfig({
10814
11236
  enabled: true,