@omnicross/daemon 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -497,6 +497,35 @@ function validateApiKeys(raw) {
497
497
  }
498
498
  return out.length > 0 ? out : void 0;
499
499
  }
500
+ var THINK_LEVELS = /* @__PURE__ */ new Set([
501
+ "none",
502
+ "minimal",
503
+ "low",
504
+ "medium",
505
+ "high",
506
+ "xhigh",
507
+ "max"
508
+ ]);
509
+ function validateThinkingLevels(raw) {
510
+ if (!Array.isArray(raw)) return void 0;
511
+ if (!raw.every((level) => typeof level === "string" && THINK_LEVELS.has(level))) {
512
+ return void 0;
513
+ }
514
+ return [...raw];
515
+ }
516
+ function validateThinkingTokenLimit(raw) {
517
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
518
+ const bounds = raw;
519
+ const min = bounds["min"];
520
+ const max = bounds["max"];
521
+ if (typeof min !== "number" || !Number.isFinite(min) || !Number.isInteger(min) || min < 0) {
522
+ return void 0;
523
+ }
524
+ if (typeof max !== "number" || !Number.isFinite(max) || !Number.isInteger(max) || max < min) {
525
+ return void 0;
526
+ }
527
+ return { min, max };
528
+ }
500
529
  function validateModelConfigs(raw) {
501
530
  if (!Array.isArray(raw)) return void 0;
502
531
  const out = [];
@@ -511,6 +540,10 @@ function validateModelConfigs(raw) {
511
540
  if (typeof m["enabled"] === "boolean") entry.enabled = m["enabled"];
512
541
  if (typeof m["vision"] === "boolean") entry.vision = m["vision"];
513
542
  if (typeof m["reasoning"] === "boolean") entry.reasoning = m["reasoning"];
543
+ const thinkingLevels = validateThinkingLevels(m["thinkingLevels"]);
544
+ if (thinkingLevels) entry.thinkingLevels = thinkingLevels;
545
+ const thinkingTokenLimit = validateThinkingTokenLimit(m["thinkingTokenLimit"]);
546
+ if (thinkingTokenLimit) entry.thinkingTokenLimit = thinkingTokenLimit;
514
547
  out.push(entry);
515
548
  }
516
549
  return out.length > 0 ? out : void 0;
@@ -1750,15 +1783,15 @@ async function keysRevoke(db, id) {
1750
1783
 
1751
1784
  // src/commands/launch.ts
1752
1785
  import { spawn as spawn2 } from "child_process";
1786
+ import { randomUUID as randomUUID6 } from "crypto";
1753
1787
  import { existsSync as existsSync20 } from "fs";
1754
1788
  import { delimiter as delimiter2, join as join12 } from "path";
1755
1789
  import { parseArgs as parseArgs4 } from "util";
1756
1790
  import {
1757
1791
  buildChatCliLaunchConfig as buildChatCliLaunchConfig2,
1758
- buildClaudeCliLaunchConfig as buildClaudeCliLaunchConfig2,
1759
- buildCodexLaunchConfig as buildCodexLaunchConfig2,
1760
1792
  buildGeminiCliLaunchConfig as buildGeminiCliLaunchConfig2
1761
1793
  } from "@omnicross/cli-launcher";
1794
+ import { ROUTE_LEASE_REQUEST_SCHEMA as ROUTE_LEASE_REQUEST_SCHEMA2 } from "@omnicross/core/provider-proxy";
1762
1795
 
1763
1796
  // src/bootstrap.ts
1764
1797
  import { accessSync, constants as fsConstants, existsSync as existsSync19 } from "fs";
@@ -1774,7 +1807,7 @@ import {
1774
1807
  normalizeServerConfig
1775
1808
  } from "@omnicross/core/outbound-api";
1776
1809
  import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api/subscriptionRegistryPort";
1777
- import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
1810
+ import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
1778
1811
  import {
1779
1812
  __resetSharedAccountAllowanceStoreForTests,
1780
1813
  AccountAllowanceStore as AccountAllowanceStore3,
@@ -1782,15 +1815,18 @@ import {
1782
1815
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
1783
1816
  import {
1784
1817
  __resetSharedAccountAllowanceSchedulingForTests,
1785
- getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4
1818
+ getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
1786
1819
  } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
1787
1820
  import { fetchUpstream as fetchUpstream7, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
1788
1821
  import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
1789
1822
  import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
1790
1823
  import {
1791
1824
  __resetProviderProxyForTests,
1792
- getProviderProxy
1825
+ getProviderProxy,
1826
+ RouteLeaseManager,
1827
+ RouteLeaseTargetResolver
1793
1828
  } from "@omnicross/core/provider-proxy";
1829
+ import { routeLeaseDescriptorPort } from "@omnicross/cli-launcher";
1794
1830
  import { KeySpendTracker } from "@omnicross/core/outbound-api";
1795
1831
  import { PricingEngine, UsageRecorder } from "@omnicross/core/usage";
1796
1832
  import {
@@ -2496,6 +2532,93 @@ async function handleWebhookTest(req, res) {
2496
2532
  res.end(JSON.stringify({ result }));
2497
2533
  }
2498
2534
 
2535
+ // src/admin/routeLeaseApi.ts
2536
+ import {
2537
+ isLoopbackAddress,
2538
+ normalizeRouteLeaseTtl,
2539
+ ROUTE_LEASE_CAPABILITIES,
2540
+ RouteLeaseError
2541
+ } from "@omnicross/core/provider-proxy";
2542
+ var MAX_BODY_BYTES = 64 * 1024;
2543
+ var SAFE_LEASE_ID = /^[A-Za-z0-9-]{1,128}$/u;
2544
+ async function readJson(req) {
2545
+ const chunks = [];
2546
+ let bytes = 0;
2547
+ for await (const chunk of req) {
2548
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2549
+ bytes += buffer.length;
2550
+ if (bytes > MAX_BODY_BYTES) throw new RouteLeaseError("invalid_request", "request body is too large");
2551
+ chunks.push(buffer);
2552
+ }
2553
+ if (chunks.length === 0) return {};
2554
+ try {
2555
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
2556
+ } catch {
2557
+ throw new RouteLeaseError("invalid_request", "request body is not valid JSON");
2558
+ }
2559
+ }
2560
+ function json(res, status, body, noStore = false) {
2561
+ res.statusCode = status;
2562
+ res.setHeader("Content-Type", "application/json");
2563
+ if (noStore) res.setHeader("Cache-Control", "no-store");
2564
+ res.end(JSON.stringify(body));
2565
+ }
2566
+ function leaseId(value) {
2567
+ if (!value || !SAFE_LEASE_ID.test(value)) throw new RouteLeaseError("invalid_request", "lease id is invalid");
2568
+ return value;
2569
+ }
2570
+ function header(req, name) {
2571
+ const value = req.headers[name.toLowerCase()];
2572
+ return Array.isArray(value) ? value[0] : value;
2573
+ }
2574
+ function writeError(res, error, noStore) {
2575
+ const safe = error instanceof RouteLeaseError ? error : new RouteLeaseError("upstream_unavailable", "route lease operation failed safely");
2576
+ if (safe.retryAfterSeconds !== void 0) res.setHeader("Retry-After", String(safe.retryAfterSeconds));
2577
+ json(res, safe.status, safe.toResponse(), noStore);
2578
+ }
2579
+ async function handleRouteLeaseApi(req, res, path2, deps) {
2580
+ const method = (req.method ?? "GET").toUpperCase();
2581
+ const noStore = method === "POST" && (path2 === "/admin/api/route-leases" || path2.endsWith("/renew"));
2582
+ try {
2583
+ if (!isLoopbackAddress(req.socket.remoteAddress)) {
2584
+ throw new RouteLeaseError("control_unauthorized", "route lease control plane is loopback only");
2585
+ }
2586
+ const manager = deps.routeLeaseManager;
2587
+ if (!manager) throw new RouteLeaseError("daemon_not_ready", "route lease manager is unavailable");
2588
+ const base = "/admin/api/route-leases";
2589
+ const suffix = path2.slice(base.length).replace(/^\/+|\/+$/gu, "");
2590
+ const segments = suffix ? suffix.split("/") : [];
2591
+ if (segments.length === 1 && segments[0] === "capabilities") {
2592
+ if (method !== "GET" && method !== "HEAD") throw new RouteLeaseError("invalid_request", "method is not allowed");
2593
+ return json(res, 200, ROUTE_LEASE_CAPABILITIES);
2594
+ }
2595
+ if (segments.length === 0) {
2596
+ if (method === "GET") return json(res, 200, { leases: manager.list() });
2597
+ if (method === "POST") {
2598
+ const outcome = await manager.createFromRequest(await readJson(req), header(req, "idempotency-key"));
2599
+ return json(res, outcome.created ? 201 : 200, outcome.result, true);
2600
+ }
2601
+ throw new RouteLeaseError("invalid_request", "method is not allowed");
2602
+ }
2603
+ const id = leaseId(segments[0]);
2604
+ if (segments.length === 1) {
2605
+ if (method === "GET") return json(res, 200, manager.get(id));
2606
+ if (method === "DELETE") return json(res, 200, manager.release(id));
2607
+ throw new RouteLeaseError("invalid_request", "method is not allowed");
2608
+ }
2609
+ if (segments.length === 2 && segments[1] === "renew" && method === "POST") {
2610
+ const body = await readJson(req);
2611
+ const ttl = normalizeRouteLeaseTtl(
2612
+ body && typeof body === "object" && !Array.isArray(body) ? body.ttlSeconds : void 0
2613
+ );
2614
+ return json(res, 200, manager.renew(id, ttl), true);
2615
+ }
2616
+ throw new RouteLeaseError("lease_not_found", "route lease endpoint was not found");
2617
+ } catch (error) {
2618
+ writeError(res, error, noStore);
2619
+ }
2620
+ }
2621
+
2499
2622
  // src/admin/adminApi.ts
2500
2623
  import http from "http";
2501
2624
  import {
@@ -3054,7 +3177,9 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
3054
3177
  // src/admin/cliLaunch.ts
3055
3178
  import { exec, spawn } from "child_process";
3056
3179
  import { randomUUID as randomUUID2 } from "crypto";
3057
- import { existsSync as existsSync6 } from "fs";
3180
+ import { chmodSync as chmodSync3, existsSync as existsSync6, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
3181
+ import { createServer } from "net";
3182
+ import { tmpdir } from "os";
3058
3183
  import { delimiter, join as join4 } from "path";
3059
3184
  import {
3060
3185
  buildChatCliLaunchConfig,
@@ -3062,6 +3187,33 @@ import {
3062
3187
  buildCodexLaunchConfig,
3063
3188
  buildGeminiCliLaunchConfig
3064
3189
  } from "@omnicross/cli-launcher";
3190
+ import {
3191
+ ROUTE_LEASE_REQUEST_SCHEMA,
3192
+ RouteLeaseError as RouteLeaseError2
3193
+ } from "@omnicross/core/provider-proxy";
3194
+
3195
+ // src/routeLeaseRenewal.ts
3196
+ var TERMINAL_LEASE_TTL_SECONDS = 600;
3197
+ var TERMINAL_LEASE_RENEW_INTERVAL_MS = 5 * 60 * 1e3;
3198
+ var TERMINAL_LEASE_MAX_LIFETIME_MS = 24 * 60 * 60 * 1e3;
3199
+ function startTerminalLeaseRenewal(manager, leaseId2) {
3200
+ const stopAt = Date.now() + TERMINAL_LEASE_MAX_LIFETIME_MS;
3201
+ const timer = setInterval(() => {
3202
+ if (Date.now() >= stopAt) {
3203
+ clearInterval(timer);
3204
+ return;
3205
+ }
3206
+ try {
3207
+ manager.renew(leaseId2, TERMINAL_LEASE_TTL_SECONDS);
3208
+ } catch {
3209
+ clearInterval(timer);
3210
+ }
3211
+ }, TERMINAL_LEASE_RENEW_INTERVAL_MS);
3212
+ timer.unref?.();
3213
+ return () => clearInterval(timer);
3214
+ }
3215
+
3216
+ // src/admin/cliLaunch.ts
3065
3217
  var LAUNCHABLE_CLIS = [
3066
3218
  { id: "claude", displayName: "Claude Code", command: "claude" },
3067
3219
  { id: "codex", displayName: "Codex CLI", command: "codex" },
@@ -3142,34 +3294,183 @@ async function buildLaunchEnv(cli, llmConfig, target) {
3142
3294
  function shq(s) {
3143
3295
  return `'${s.replace(/'/g, `'\\''`)}'`;
3144
3296
  }
3145
- var defaultTerminalOpener = ({ cli, command, extraArgs, env, cwd, platform }) => {
3297
+ var MAC_TERMINAL_BOOTSTRAP_SOURCE = `
3298
+ 'use strict';
3299
+ const fs = require('node:fs');
3300
+ const net = require('node:net');
3301
+ const { spawn } = require('node:child_process');
3302
+ const [socketPath, launchDir, cwd, command, ...args] = process.argv.slice(2);
3303
+ let payload = '';
3304
+ const socket = net.createConnection(socketPath);
3305
+ socket.setEncoding('utf8');
3306
+ socket.on('data', (chunk) => { payload += chunk; });
3307
+ socket.on('end', () => {
3308
+ const descriptor = JSON.parse(payload);
3309
+ if (!descriptor || Array.isArray(descriptor) || Object.values(descriptor).some((value) => typeof value !== 'string')) {
3310
+ throw new Error('invalid terminal launch descriptor');
3311
+ }
3312
+ try { fs.rmSync(launchDir, { recursive: true, force: true }); } catch {}
3313
+ const child = spawn(command, args, {
3314
+ cwd: cwd || undefined,
3315
+ env: { ...process.env, ...descriptor },
3316
+ stdio: 'inherit',
3317
+ });
3318
+ child.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
3319
+ child.on('exit', (code, signal) => {
3320
+ if (signal) process.kill(process.pid, signal);
3321
+ else process.exitCode = code == null ? 1 : code;
3322
+ });
3323
+ });
3324
+ socket.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
3325
+ `;
3326
+ var MAC_TERMINAL_IPC_TIMEOUT_MS = 12e4;
3327
+ function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }, spawnProcess = spawn, macIpc = {}) {
3146
3328
  const childEnv = { ...process.env, ...env };
3147
3329
  if (platform === "win32") {
3148
3330
  const args = ["/c", "start", `"omnicross ${cli}"`];
3149
3331
  if (cwd) args.push("/D", `"${cwd}"`);
3150
3332
  args.push("cmd", "/k", command, ...extraArgs);
3151
- spawn(process.env["ComSpec"] || "cmd.exe", args, {
3333
+ spawnProcess(process.env["ComSpec"] || "cmd.exe", args, {
3152
3334
  env: childEnv,
3153
3335
  windowsVerbatimArguments: true,
3154
3336
  detached: true,
3155
3337
  stdio: "ignore"
3156
3338
  }).unref();
3157
- return;
3339
+ return () => {
3340
+ };
3158
3341
  }
3159
- const exportLine = Object.entries(env).map(([k, v]) => `export ${k}=${shq(v)}`).join("; ");
3160
3342
  const runLine = [command, ...extraArgs].map(shq).join(" ");
3161
- const script = `${exportLine}; ${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
3343
+ const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
3162
3344
  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;
3345
+ const launchDir = mkdtempSync(join4(tmpdir(), "omnicross-terminal-"));
3346
+ const commandFile = join4(launchDir, "launch.command");
3347
+ const bootstrapFile = join4(launchDir, "bootstrap.cjs");
3348
+ const socketPath = macIpc.socketPath ?? join4(launchDir, "descriptor.sock");
3349
+ const openerEnv = { ...process.env };
3350
+ for (const key of Object.keys(env)) delete openerEnv[key];
3351
+ let claimed = false;
3352
+ let cleaned = false;
3353
+ let failureNotified = false;
3354
+ let timer;
3355
+ const notifyFailure = () => {
3356
+ cleanup();
3357
+ if (failureNotified) return;
3358
+ failureNotified = true;
3359
+ try {
3360
+ onFailure?.();
3361
+ } catch {
3362
+ }
3363
+ };
3364
+ const handleLaunchFailure = () => {
3365
+ if (claimed) cleanup();
3366
+ else notifyFailure();
3367
+ };
3368
+ const sockets = /* @__PURE__ */ new Set();
3369
+ const server = createServer((socket) => {
3370
+ socket.unref();
3371
+ sockets.add(socket);
3372
+ socket.once("close", () => sockets.delete(socket));
3373
+ try {
3374
+ macIpc.onAccepted?.(socket);
3375
+ } catch {
3376
+ cleanup();
3377
+ return;
3378
+ }
3379
+ if (claimed || cleaned) {
3380
+ socket.destroy();
3381
+ return;
3382
+ }
3383
+ claimed = true;
3384
+ try {
3385
+ macIpc.onClaimed?.();
3386
+ if (cleaned) return;
3387
+ socket.end(JSON.stringify(env), cleanup);
3388
+ } catch {
3389
+ cleanup();
3390
+ }
3391
+ });
3392
+ const cleanup = () => {
3393
+ if (!cleaned) {
3394
+ cleaned = true;
3395
+ if (timer) clearTimeout(timer);
3396
+ for (const socket of sockets) socket.destroy();
3397
+ sockets.clear();
3398
+ try {
3399
+ server.close();
3400
+ } catch {
3401
+ }
3402
+ }
3403
+ try {
3404
+ if (macIpc.removeArtifacts) {
3405
+ macIpc.removeArtifacts(launchDir);
3406
+ } else {
3407
+ rmSync2(launchDir, {
3408
+ recursive: true,
3409
+ force: true,
3410
+ maxRetries: 3,
3411
+ retryDelay: 20
3412
+ });
3413
+ }
3414
+ } catch {
3415
+ }
3416
+ };
3417
+ try {
3418
+ writeFileSync6(bootstrapFile, MAC_TERMINAL_BOOTSTRAP_SOURCE, { encoding: "utf8", mode: 448 });
3419
+ writeFileSync6(commandFile, `#!/bin/bash
3420
+ rm -f -- "$0"
3421
+ exec ${shq(process.execPath)} ${shq(bootstrapFile)} ${shq(socketPath)} ${shq(launchDir)} ${shq(cwd ?? "")} ${runLine}
3422
+ `, {
3423
+ encoding: "utf8",
3424
+ mode: 448
3425
+ });
3426
+ chmodSync3(commandFile, 448);
3427
+ chmodSync3(bootstrapFile, 448);
3428
+ server.once("error", handleLaunchFailure);
3429
+ server.listen(socketPath, () => {
3430
+ if (cleaned) return;
3431
+ try {
3432
+ macIpc.onListening?.();
3433
+ if (cleaned) return;
3434
+ if (process.platform !== "win32") chmodSync3(socketPath, 384);
3435
+ const opener = spawnProcess("open", ["-n", "-a", "Terminal", commandFile], {
3436
+ env: openerEnv,
3437
+ detached: true,
3438
+ stdio: "ignore"
3439
+ });
3440
+ opener.once("error", handleLaunchFailure);
3441
+ opener.unref();
3442
+ server.unref();
3443
+ } catch {
3444
+ handleLaunchFailure();
3445
+ }
3446
+ });
3447
+ timer = setTimeout(handleLaunchFailure, macIpc.timeoutMs ?? MAC_TERMINAL_IPC_TIMEOUT_MS);
3448
+ timer.unref?.();
3449
+ return cleanup;
3450
+ } catch (error) {
3451
+ cleanup();
3452
+ throw error;
3453
+ }
3166
3454
  }
3167
- spawn("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
3455
+ spawnProcess("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
3456
+ env: childEnv,
3168
3457
  detached: true,
3169
3458
  stdio: "ignore"
3170
3459
  }).unref();
3171
- };
3460
+ return () => {
3461
+ };
3462
+ }
3463
+ var defaultTerminalOpener = (input) => openTerminal(input);
3172
3464
  var sessions = /* @__PURE__ */ new Map();
3465
+ function resetCliSessions() {
3466
+ for (const s of sessions.values()) {
3467
+ try {
3468
+ s.onSessionEnd();
3469
+ } catch {
3470
+ }
3471
+ }
3472
+ sessions.clear();
3473
+ }
3173
3474
  function errBody(message) {
3174
3475
  return { error: { type: "admin_api_error", message } };
3175
3476
  }
@@ -3224,29 +3525,81 @@ async function handleCliLaunch(cli, body, ctx) {
3224
3525
  } catch (err5) {
3225
3526
  return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "no launch target") };
3226
3527
  }
3528
+ const id = randomUUID2();
3529
+ let leaseId2;
3227
3530
  let launch;
3228
3531
  try {
3229
- launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
3532
+ if ((cli === "claude" || cli === "codex") && ctx.routeLeaseManager) {
3533
+ const outcome = await ctx.routeLeaseManager.createFromRequest({
3534
+ schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA,
3535
+ consumer: "omnicross-terminal",
3536
+ runtime: cli,
3537
+ upstream: { kind: "provider", providerId: target.providerId },
3538
+ model: target.model,
3539
+ execution: { sessionId: id }
3540
+ }, `omnicross-terminal:${id}`);
3541
+ leaseId2 = outcome.result.leaseId;
3542
+ const stopRenewal = startTerminalLeaseRenewal(ctx.routeLeaseManager, leaseId2);
3543
+ launch = {
3544
+ env: outcome.result.launch.env,
3545
+ extraArgs: outcome.result.launch.extraArgs,
3546
+ onSessionEnd: () => {
3547
+ stopRenewal();
3548
+ ctx.routeLeaseManager?.release(outcome.result.leaseId);
3549
+ }
3550
+ };
3551
+ } else {
3552
+ launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
3553
+ }
3230
3554
  } catch (err5) {
3231
- return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
3555
+ const status = err5 instanceof RouteLeaseError2 ? err5.status : 400;
3556
+ return { status, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
3232
3557
  }
3233
3558
  const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
3234
3559
  const opener = ctx.opener ?? defaultTerminalOpener;
3560
+ let openerCleanup;
3561
+ let ended = false;
3562
+ let published = false;
3563
+ const onSessionEnd = () => {
3564
+ if (ended) return;
3565
+ ended = true;
3566
+ if (published) sessions.delete(id);
3567
+ try {
3568
+ openerCleanup?.();
3569
+ } finally {
3570
+ launch.onSessionEnd();
3571
+ }
3572
+ };
3235
3573
  try {
3236
- opener({ cli, command: meta.command, extraArgs: launch.extraArgs ?? [], env: launch.env, cwd, platform });
3574
+ const cleanup = opener({
3575
+ cli,
3576
+ command: meta.command,
3577
+ extraArgs: launch.extraArgs ?? [],
3578
+ env: launch.env,
3579
+ cwd,
3580
+ platform,
3581
+ onFailure: onSessionEnd
3582
+ });
3583
+ if (cleanup) openerCleanup = cleanup;
3237
3584
  } catch (err5) {
3238
- launch.onSessionEnd();
3585
+ onSessionEnd();
3239
3586
  return { status: 500, body: errBody(err5 instanceof Error ? err5.message : "failed to open terminal") };
3240
3587
  }
3241
- const id = randomUUID2();
3588
+ if (ended) {
3589
+ openerCleanup?.();
3590
+ return { status: 500, body: errBody("failed to open terminal") };
3591
+ }
3242
3592
  sessions.set(id, {
3243
3593
  id,
3244
3594
  cli,
3245
3595
  providerId: target.providerId,
3246
3596
  model: target.model,
3597
+ ...leaseId2 ? { leaseId: leaseId2 } : {},
3247
3598
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
3248
- onSessionEnd: launch.onSessionEnd
3599
+ onSessionEnd
3249
3600
  });
3601
+ published = true;
3602
+ if (ended) sessions.delete(id);
3250
3603
  return { status: 200, body: { sessionId: id, providerId: target.providerId, model: target.model } };
3251
3604
  }
3252
3605
 
@@ -4133,7 +4486,7 @@ function sealPack(bundleJson, passphrase) {
4133
4486
  cipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
4134
4487
  const ciphertext = Buffer.concat([cipher.update(bundleJson, "utf8"), cipher.final()]);
4135
4488
  const tag = cipher.getAuthTag();
4136
- const header = {
4489
+ const header2 = {
4137
4490
  magic: PACK_MAGIC,
4138
4491
  v: PACK_VERSION,
4139
4492
  kdf: KDF_ALGORITHM,
@@ -4144,7 +4497,7 @@ function sealPack(bundleJson, passphrase) {
4144
4497
  iv: iv.toString("base64"),
4145
4498
  tag: tag.toString("base64")
4146
4499
  };
4147
- return `${PACK_PREFIX}${toB64Url(JSON.stringify(header))}.${ciphertext.toString("base64")}`;
4500
+ return `${PACK_PREFIX}${toB64Url(JSON.stringify(header2))}.${ciphertext.toString("base64")}`;
4148
4501
  }
4149
4502
  function parsePack(packString) {
4150
4503
  if (typeof packString !== "string" || !packString.startsWith(PACK_PREFIX)) {
@@ -4155,28 +4508,28 @@ function parsePack(packString) {
4155
4508
  if (dot < 0) throw new PackAuthError("migration pack is malformed (missing body)");
4156
4509
  const headerB64Url = rest.slice(0, dot);
4157
4510
  const ctB64 = rest.slice(dot + 1);
4158
- let header;
4511
+ let header2;
4159
4512
  try {
4160
- header = JSON.parse(fromB64Url(headerB64Url));
4513
+ header2 = JSON.parse(fromB64Url(headerB64Url));
4161
4514
  } catch {
4162
4515
  throw new PackAuthError("migration pack is malformed (unreadable header)");
4163
4516
  }
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") {
4517
+ 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
4518
  throw new PackAuthError("migration pack is malformed (unsupported header)");
4166
4519
  }
4167
4520
  const ciphertext = Buffer.from(ctB64, "base64");
4168
- return { header, ciphertext };
4521
+ return { header: header2, ciphertext };
4169
4522
  }
4170
4523
  function openPack(packString, passphrase) {
4171
4524
  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");
4525
+ const { header: header2, ciphertext } = parsePack(packString);
4526
+ const salt = Buffer.from(header2.salt, "base64");
4527
+ const iv = Buffer.from(header2.iv, "base64");
4528
+ const tag = Buffer.from(header2.tag, "base64");
4176
4529
  if (iv.length !== IV_BYTES2 || tag.length !== TAG_BYTES2) {
4177
4530
  throw new PackAuthError("migration pack is malformed (invalid iv/tag length)");
4178
4531
  }
4179
- const key = deriveKey(passphrase, salt, header.N, header.r, header.p);
4532
+ const key = deriveKey(passphrase, salt, header2.N, header2.r, header2.p);
4180
4533
  const decipher = createDecipheriv2("aes-256-gcm", key, iv);
4181
4534
  decipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
4182
4535
  decipher.setAuthTag(tag);
@@ -4512,10 +4865,10 @@ function writeJson2(res, status, body) {
4512
4865
  res.writeHead(status, { "Content-Type": "application/json" });
4513
4866
  res.end(JSON.stringify(body));
4514
4867
  }
4515
- function writeError(res, status, message) {
4868
+ function writeError2(res, status, message) {
4516
4869
  writeJson2(res, status, { error: { type: "account_allowance_error", message } });
4517
4870
  }
4518
- function readJson(req) {
4871
+ function readJson2(req) {
4519
4872
  return new Promise((resolve3, reject) => {
4520
4873
  const chunks = [];
4521
4874
  req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
@@ -4541,10 +4894,10 @@ function allowanceProvider(value) {
4541
4894
  return value === "claude" || value === "codex" ? value : null;
4542
4895
  }
4543
4896
  async function handleAccountAllowanceApi(req, res, method, rest, service) {
4544
- if (!service) return writeError(res, 501, "account allowance service is not available");
4897
+ if (!service) return writeError2(res, 501, "account allowance service is not available");
4545
4898
  if (method === "GET" && rest.length === 1 && rest[0] === "scheduling") {
4546
4899
  if (!service.getSchedulingStatus) {
4547
- return writeError(res, 501, "allowance scheduling diagnostics are not available");
4900
+ return writeError2(res, 501, "allowance scheduling diagnostics are not available");
4548
4901
  }
4549
4902
  return writeJson2(res, 200, { scheduling: service.getSchedulingStatus() });
4550
4903
  }
@@ -4552,27 +4905,27 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
4552
4905
  const params = query(req);
4553
4906
  const pathProvider = rest.length >= 2 ? rest[0] : null;
4554
4907
  const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
4555
- if (providerId === null) return writeError(res, 400, "providerId must be claude or codex");
4908
+ if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
4556
4909
  const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
4557
4910
  const allowances = await service.list({ providerId, accountId });
4558
4911
  return writeJson2(res, 200, { allowances });
4559
4912
  }
4560
4913
  if (method === "POST" && rest[0] === "refresh") {
4561
- const body = await readJson(req);
4914
+ const body = await readJson2(req);
4562
4915
  const requestedProvider = allowanceProvider(
4563
4916
  typeof body["providerId"] === "string" ? body["providerId"] : "claude"
4564
4917
  );
4565
4918
  if (requestedProvider !== "claude") {
4566
- return writeError(res, 400, "only Claude allowances support explicit refresh");
4919
+ return writeError2(res, 400, "only Claude allowances support explicit refresh");
4567
4920
  }
4568
4921
  const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
4569
4922
  const allowances = await service.refreshClaude(accountId);
4570
4923
  if (accountId && allowances.length === 0) {
4571
- return writeError(res, 404, `Claude account '${accountId}' not found`);
4924
+ return writeError2(res, 404, `Claude account '${accountId}' not found`);
4572
4925
  }
4573
4926
  return writeJson2(res, 200, { allowances });
4574
4927
  }
4575
- return writeError(res, 405, `method ${method} not allowed on account allowances`);
4928
+ return writeError2(res, 405, `method ${method} not allowed on account allowances`);
4576
4929
  }
4577
4930
 
4578
4931
  // src/admin/adminApi.ts
@@ -5150,6 +5503,12 @@ function parseModelConfigsInput(raw, existing) {
5150
5503
  else if (typeof prior?.vision === "boolean") entry.vision = prior.vision;
5151
5504
  if (typeof m["reasoning"] === "boolean") entry.reasoning = m["reasoning"];
5152
5505
  else if (typeof prior?.reasoning === "boolean") entry.reasoning = prior.reasoning;
5506
+ const thinkingLevels = validateThinkingLevels(m["thinkingLevels"]);
5507
+ if (thinkingLevels) entry.thinkingLevels = thinkingLevels;
5508
+ else if (prior?.thinkingLevels) entry.thinkingLevels = prior.thinkingLevels;
5509
+ const thinkingTokenLimit = validateThinkingTokenLimit(m["thinkingTokenLimit"]);
5510
+ if (thinkingTokenLimit) entry.thinkingTokenLimit = thinkingTokenLimit;
5511
+ else if (prior?.thinkingTokenLimit) entry.thinkingTokenLimit = prior.thinkingTokenLimit;
5153
5512
  out.push(entry);
5154
5513
  }
5155
5514
  return out.length > 0 ? out : void 0;
@@ -5807,6 +6166,7 @@ async function handleCli(req, res, method, rest, deps) {
5807
6166
  const result = await handleCliLaunch(cli, body, {
5808
6167
  llmConfig: deps.llmConfig,
5809
6168
  providers,
6169
+ routeLeaseManager: deps.routeLeaseManager,
5810
6170
  opener: deps.cliTerminalOpener,
5811
6171
  probe: deps.cliPathProbe
5812
6172
  });
@@ -6069,7 +6429,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
6069
6429
  }
6070
6430
 
6071
6431
  // src/admin/version.ts
6072
- var DAEMON_VERSION = true ? "0.1.7" : "0.0.0-dev";
6432
+ var DAEMON_VERSION = true ? "0.1.9" : "0.0.0-dev";
6073
6433
 
6074
6434
  // src/admin/AdminServer.ts
6075
6435
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -6189,6 +6549,10 @@ var AdminServer = class {
6189
6549
  await handleWebhookTest(req, res);
6190
6550
  return;
6191
6551
  }
6552
+ if (path2 === "/admin/api/route-leases" || path2.startsWith("/admin/api/route-leases/")) {
6553
+ await handleRouteLeaseApi(req, res, path2, this.deps);
6554
+ return;
6555
+ }
6192
6556
  if (path2.startsWith("/admin/api/")) {
6193
6557
  await handleAdminApi(req, res, path2, this.deps);
6194
6558
  return;
@@ -6200,8 +6564,8 @@ var AdminServer = class {
6200
6564
  }
6201
6565
  /** Constant-time bearer/header check against the configured token. */
6202
6566
  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;
6567
+ const header2 = req.headers["authorization"];
6568
+ const bearer = typeof header2 === "string" && header2.startsWith("Bearer ") ? header2.slice("Bearer ".length).trim() : void 0;
6205
6569
  const xToken = req.headers["x-admin-token"];
6206
6570
  const presented = bearer ?? (typeof xToken === "string" ? xToken.trim() : void 0);
6207
6571
  return constantTimeEquals(presented, token);
@@ -6336,7 +6700,7 @@ var OAuthSessionStore = class {
6336
6700
  };
6337
6701
 
6338
6702
  // src/commands/loopbackCallback.ts
6339
- import { createServer } from "http";
6703
+ import { createServer as createServer2 } from "http";
6340
6704
  var LOOPBACK_HOST = "127.0.0.1";
6341
6705
  var LOOPBACK_PORT = 1455;
6342
6706
  var CALLBACK_PATH = "/auth/callback";
@@ -6358,7 +6722,7 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
6358
6722
  fn();
6359
6723
  server2.close();
6360
6724
  };
6361
- const server = createServer((req, res) => {
6725
+ const server = createServer2((req, res) => {
6362
6726
  const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
6363
6727
  if (url.pathname !== CALLBACK_PATH) {
6364
6728
  res.writeHead(404, HTML_HEADERS);
@@ -6609,6 +6973,15 @@ function toLLMProvider(row) {
6609
6973
  api_base_url: row.baseUrl,
6610
6974
  api_key: resolvePreferredApiKey(row),
6611
6975
  models,
6976
+ modelConfigs: row.modelConfigs?.map((config) => ({
6977
+ id: config.id,
6978
+ name: config.name ?? config.id,
6979
+ enabled: config.enabled ?? true,
6980
+ vision: config.vision,
6981
+ reasoning: config.reasoning,
6982
+ thinkingLevels: config.thinkingLevels,
6983
+ thinkingTokenLimit: config.thinkingTokenLimit
6984
+ })),
6612
6985
  enabled: true,
6613
6986
  transformer,
6614
6987
  // app-parity-2 child 3: POPULATE the coding-plan endpoint onto the core
@@ -6780,7 +7153,7 @@ function safeStringify(value) {
6780
7153
  }
6781
7154
 
6782
7155
  // src/ports/JsonApiServerSettingsStore.ts
6783
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
7156
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
6784
7157
  import { OUTBOUND_API_SERVER_CONFIG_KEY } from "@omnicross/core/outbound-api";
6785
7158
  var JsonApiServerSettingsStore = class {
6786
7159
  /**
@@ -6807,7 +7180,7 @@ var JsonApiServerSettingsStore = class {
6807
7180
  if (key !== OUTBOUND_API_SERVER_CONFIG_KEY) return;
6808
7181
  const file = this.readFile();
6809
7182
  file.server = this.encryptSecrets(value);
6810
- writeFileSync6(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
7183
+ writeFileSync7(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
6811
7184
  }
6812
7185
  /** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
6813
7186
  encryptSecrets(config) {
@@ -7161,7 +7534,7 @@ function median(values) {
7161
7534
  }
7162
7535
 
7163
7536
  // src/ports/JsonPricingStore.ts
7164
- import { existsSync as existsSync9, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
7537
+ import { existsSync as existsSync9, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
7165
7538
  import { randomUUID as randomUUID5 } from "crypto";
7166
7539
  var JsonPricingStore = class {
7167
7540
  constructor(pricingPath) {
@@ -7302,13 +7675,13 @@ var JsonPricingStore = class {
7302
7675
  writeRows(rows) {
7303
7676
  const temporaryPath = `${this.pricingPath}.${process.pid}.${randomUUID5()}.tmp`;
7304
7677
  try {
7305
- writeFileSync7(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
7678
+ writeFileSync8(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
7306
7679
  encoding: "utf8",
7307
7680
  flag: "wx"
7308
7681
  });
7309
7682
  this.replaceFile(temporaryPath);
7310
7683
  } finally {
7311
- rmSync2(temporaryPath, { force: true });
7684
+ rmSync3(temporaryPath, { force: true });
7312
7685
  }
7313
7686
  }
7314
7687
  /** Isolated for deterministic failure testing; never removes the target. */
@@ -7323,7 +7696,7 @@ function isUsablePricingRow(value) {
7323
7696
  }
7324
7697
 
7325
7698
  // src/pricing/PricingRefreshScheduler.ts
7326
- import { existsSync as existsSync10, readFileSync as readFileSync11, renameSync as renameSync4, writeFileSync as writeFileSync8 } from "fs";
7699
+ import { existsSync as existsSync10, readFileSync as readFileSync11, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
7327
7700
  var EMPTY_STATE2 = {
7328
7701
  lastAttemptAt: null,
7329
7702
  lastSuccessAt: null,
@@ -7416,7 +7789,7 @@ var PricingRefreshScheduler = class {
7416
7789
  }
7417
7790
  writeState(state) {
7418
7791
  const temporaryPath = `${this.statePath}.tmp`;
7419
- writeFileSync8(temporaryPath, `${JSON.stringify(state, null, 2)}
7792
+ writeFileSync9(temporaryPath, `${JSON.stringify(state, null, 2)}
7420
7793
  `, "utf8");
7421
7794
  renameSync4(temporaryPath, this.statePath);
7422
7795
  }
@@ -7426,7 +7799,7 @@ function finiteOrNull(value) {
7426
7799
  }
7427
7800
 
7428
7801
  // src/ports/JsonVoucherDb.ts
7429
- import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
7802
+ import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
7430
7803
  var JsonVoucherDb = class {
7431
7804
  constructor(vouchersPath) {
7432
7805
  this.vouchersPath = vouchersPath;
@@ -7513,12 +7886,12 @@ var JsonVoucherDb = class {
7513
7886
  }
7514
7887
  }
7515
7888
  writeRows(rows) {
7516
- writeFileSync9(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
7889
+ writeFileSync10(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
7517
7890
  }
7518
7891
  };
7519
7892
 
7520
7893
  // src/ports/JsonSubscriptionCredentialStore.ts
7521
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
7894
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
7522
7895
  import { dirname as dirname6 } from "path";
7523
7896
  import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
7524
7897
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
@@ -8201,7 +8574,7 @@ var JsonSubscriptionCredentialStore = class {
8201
8574
  persist(config) {
8202
8575
  mkdirSync4(dirname6(this.tokensPath), { recursive: true });
8203
8576
  const encrypted = encryptTokens(config, this.box);
8204
- writeFileSync10(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
8577
+ writeFileSync11(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
8205
8578
  }
8206
8579
  /**
8207
8580
  * Read + parse `tokens.json`, tolerating a missing/corrupt file, then DECRYPT
@@ -8770,7 +9143,7 @@ import {
8770
9143
  readFileSync as readFileSync15,
8771
9144
  readdirSync,
8772
9145
  statSync as statSync3,
8773
- writeFileSync as writeFileSync11
9146
+ writeFileSync as writeFileSync12
8774
9147
  } from "fs";
8775
9148
  import { basename, dirname as dirname7, join as join6 } from "path";
8776
9149
  var SIDECAR_VERSION = 1;
@@ -8812,7 +9185,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
8812
9185
  minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
8813
9186
  maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
8814
9187
  };
8815
- writeFileSync11(statsPath, JSON.stringify(next), "utf8");
9188
+ writeFileSync12(statsPath, JSON.stringify(next), "utf8");
8816
9189
  }
8817
9190
  function queryCovers(stats, from, to) {
8818
9191
  return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
@@ -8956,7 +9329,7 @@ async function readAuditStats(auditDir, query2 = {}) {
8956
9329
  total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
8957
9330
  total.complete = total.complete && scanned.filtered.complete;
8958
9331
  const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
8959
- if (current.complete) writeFileSync11(statsPath, JSON.stringify(current), "utf8");
9332
+ if (current.complete) writeFileSync12(statsPath, JSON.stringify(current), "utf8");
8960
9333
  } catch {
8961
9334
  total.complete = false;
8962
9335
  }
@@ -9518,6 +9891,78 @@ var TokenRefreshScheduler = class {
9518
9891
  }
9519
9892
  };
9520
9893
 
9894
+ // src/routeLeaseSubscriptionPreflight.ts
9895
+ import {
9896
+ RouteLeaseError as RouteLeaseError3
9897
+ } from "@omnicross/core/provider-proxy";
9898
+ import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
9899
+ import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
9900
+ import { accountSupportsModel } from "@omnicross/subscriptions/scheduler/accountModelMap";
9901
+ var PROVIDERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "opencodego"]);
9902
+ function accountArray(config, providerId) {
9903
+ const record = config;
9904
+ const key = `${providerId}Accounts`;
9905
+ const accounts = record[key];
9906
+ if (Array.isArray(accounts)) return accounts;
9907
+ const legacy = record[providerId];
9908
+ if (!legacy || typeof legacy !== "object") return [];
9909
+ const activeKey = `active${providerId[0].toUpperCase()}${providerId.slice(1)}AccountId`;
9910
+ return [{ id: String(record[activeKey] ?? "active"), enabled: true, tokens: legacy }];
9911
+ }
9912
+ function hasCredential(providerId, account) {
9913
+ const tokens = account.tokens;
9914
+ if (providerId === "opencodego") return typeof tokens.apiKey === "string" && tokens.apiKey.length > 0;
9915
+ return typeof tokens.accessToken === "string" && tokens.accessToken.length > 0;
9916
+ }
9917
+ function safeProviderId(value) {
9918
+ if (!PROVIDERS.has(value)) {
9919
+ throw new RouteLeaseError3("upstream_not_found", "subscription provider was not found");
9920
+ }
9921
+ return value;
9922
+ }
9923
+ function createRouteLeaseSubscriptionPreflight(credentials) {
9924
+ return {
9925
+ async assertAvailable(upstream, model) {
9926
+ const providerId = safeProviderId(upstream.providerId);
9927
+ const config = await credentials.getFullConfig();
9928
+ const all = accountArray(config, providerId);
9929
+ if (all.length === 0) {
9930
+ throw new RouteLeaseError3("upstream_unavailable", "subscription provider has no configured account");
9931
+ }
9932
+ let bounded = all;
9933
+ if (upstream.kind === "account") {
9934
+ bounded = all.filter((account) => account.id === upstream.accountId);
9935
+ } else if (upstream.kind === "account-group") {
9936
+ bounded = all.filter((account) => account.group?.trim() === upstream.group);
9937
+ }
9938
+ if (bounded.length === 0) {
9939
+ throw new RouteLeaseError3("upstream_not_found", "the selected subscription resource was not found");
9940
+ }
9941
+ const modelEligible = bounded.filter(
9942
+ (account) => accountSupportsModel(account.supportedModels, model)
9943
+ );
9944
+ if (modelEligible.length === 0) {
9945
+ throw new RouteLeaseError3("model_not_configured", "model is not supported by the selected subscription resource");
9946
+ }
9947
+ const credentialEligible = modelEligible.filter(
9948
+ (account) => account.enabled !== false && hasCredential(providerId, account)
9949
+ );
9950
+ const health2 = getSharedAccountHealth3();
9951
+ const allowance = getSharedAccountAllowanceScheduling4();
9952
+ const candidates = credentialEligible.filter(
9953
+ (account) => health2.isSchedulable(providerId, account.id) && allowance.preview(providerId, account.id, account.priority ?? 50).schedulable
9954
+ );
9955
+ if (candidates.length > 0) return;
9956
+ if (upstream.kind === "account") {
9957
+ throw new RouteLeaseError3("upstream_unavailable", "the selected subscription account is unavailable");
9958
+ }
9959
+ throw new RouteLeaseError3("upstream_exhausted", "the selected subscription pool has no eligible account", {
9960
+ retryAfterSeconds: 30
9961
+ });
9962
+ }
9963
+ };
9964
+ }
9965
+
9521
9966
  // src/webhook/WebhookDispatcher.ts
9522
9967
  import { createHmac as createHmac2 } from "crypto";
9523
9968
  import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
@@ -9700,7 +10145,7 @@ function buildDaemon(config, paths) {
9700
10145
  new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
9701
10146
  );
9702
10147
  setSharedAccountAllowanceStore(accountAllowanceStore);
9703
- getSharedAccountAllowanceScheduling4().configure(
10148
+ getSharedAccountAllowanceScheduling5().configure(
9704
10149
  normalizeServerConfig(decryptedConfig.server).allowanceScheduling
9705
10150
  );
9706
10151
  const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
@@ -9768,10 +10213,21 @@ function buildDaemon(config, paths) {
9768
10213
  onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
9769
10214
  });
9770
10215
  const providerProxy = getProviderProxy({ llmConfig, apiKeyPool, usageRecorder });
10216
+ const routeLeaseManager = new RouteLeaseManager(
10217
+ providerProxy,
10218
+ new RouteLeaseTargetResolver(llmConfig, {
10219
+ providerKeys: apiKeyPool,
10220
+ subscriptions: createRouteLeaseSubscriptionPreflight(credentialStore)
10221
+ }),
10222
+ routeLeaseDescriptorPort,
10223
+ { logger }
10224
+ );
10225
+ providerProxy.registerBeforeStop(() => routeLeaseManager.shutdown());
10226
+ providerProxy.registerBeforeStop(() => resetCliSessions());
9771
10227
  llmConfig.setReloadHook(() => apiKeyPool.invalidateCache());
9772
10228
  const accountHealthProbeScheduler = new AccountHealthProbeScheduler(
9773
10229
  credentialStore,
9774
- getSharedAccountHealth3(),
10230
+ getSharedAccountHealth4(),
9775
10231
  logger,
9776
10232
  DEFAULT_ACCOUNT_PROBE
9777
10233
  );
@@ -9818,6 +10274,7 @@ function buildDaemon(config, paths) {
9818
10274
  keySpendReader: keySpendTracker,
9819
10275
  settingsStore,
9820
10276
  outboundApiServer,
10277
+ routeLeaseManager,
9821
10278
  subscriptionAccounts,
9822
10279
  accountAllowanceService,
9823
10280
  allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
@@ -9910,7 +10367,7 @@ function buildDaemon(config, paths) {
9910
10367
  logger,
9911
10368
  fetchImpl: (url, init) => fetchUpstream7(url, init)
9912
10369
  });
9913
- setWebhookRuntime(webhookDispatcher, getSharedAccountHealth3());
10370
+ setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
9914
10371
  const auditWriter = new AuditWriter(auditDir, logger);
9915
10372
  const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
9916
10373
  setAuditRuntime(auditWriter, auditPruneSweeper);
@@ -9925,7 +10382,7 @@ function buildDaemon(config, paths) {
9925
10382
  const tokenRefreshScheduler = new TokenRefreshScheduler(credentialStore, logger);
9926
10383
  const accountHealthSweeper = new AccountHealthSweeper(
9927
10384
  credentialStore,
9928
- getSharedAccountHealth3(),
10385
+ getSharedAccountHealth4(),
9929
10386
  logger
9930
10387
  );
9931
10388
  return {
@@ -9934,6 +10391,7 @@ function buildDaemon(config, paths) {
9934
10391
  keyDb,
9935
10392
  settingsStore,
9936
10393
  providerProxy,
10394
+ routeLeaseManager,
9937
10395
  outboundApiServer,
9938
10396
  apiKeyPool,
9939
10397
  autoDisableStore,
@@ -10049,32 +10507,17 @@ async function runLaunch(argv, deps) {
10049
10507
  await daemon.llmConfig.ready();
10050
10508
  await daemon.providerProxy.start();
10051
10509
  } 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();
10510
+ await shutdownLaunchDaemon(daemon);
10060
10511
  throw err5;
10061
10512
  }
10062
10513
  let launch;
10063
10514
  try {
10064
- launch = await buildLaunchConfig(cli, daemon.llmConfig, {
10515
+ launch = await buildLaunchConfig(cli, daemon, {
10065
10516
  providerId: values.provider,
10066
10517
  model: values.model
10067
10518
  });
10068
10519
  } 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();
10520
+ await shutdownLaunchDaemon(daemon);
10078
10521
  throw err5;
10079
10522
  }
10080
10523
  try {
@@ -10093,21 +10536,40 @@ async function runLaunch(argv, deps) {
10093
10536
  cwd: values.cwd
10094
10537
  });
10095
10538
  } 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) {
10539
+ try {
10540
+ launch.onSessionEnd();
10541
+ } finally {
10542
+ await shutdownLaunchDaemon(daemon);
10543
+ }
10544
+ }
10545
+ }
10546
+ async function buildLaunchConfig(cli, daemon, opts) {
10547
+ if (cli === "claude" || cli === "codex") {
10548
+ const internalId = randomUUID6();
10549
+ const outcome = await daemon.routeLeaseManager.createFromRequest({
10550
+ schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA2,
10551
+ consumer: "omnicross-terminal",
10552
+ runtime: cli,
10553
+ upstream: { kind: "provider", providerId: opts.providerId },
10554
+ model: opts.model,
10555
+ execution: { sessionId: `launch:${cli}:${internalId}` }
10556
+ }, `omnicross-launch:${internalId}`);
10557
+ const stopRenewal = startTerminalLeaseRenewal(
10558
+ daemon.routeLeaseManager,
10559
+ outcome.result.leaseId
10560
+ );
10561
+ return {
10562
+ baseUrl: daemon.providerProxy.getBaseUrl(),
10563
+ env: outcome.result.launch.env,
10564
+ extraArgs: outcome.result.launch.extraArgs,
10565
+ onSessionEnd: () => {
10566
+ stopRenewal();
10567
+ daemon.routeLeaseManager.release(outcome.result.leaseId);
10568
+ }
10569
+ };
10570
+ }
10109
10571
  const common = {
10110
- llmConfig,
10572
+ llmConfig: daemon.llmConfig,
10111
10573
  providerId: opts.providerId,
10112
10574
  model: opts.model,
10113
10575
  // Stable, bounded session id — pool failover (poolseam) fires on launch
@@ -10115,10 +10577,6 @@ async function buildLaunchConfig(cli, llmConfig, opts) {
10115
10577
  sessionId: `launch:${cli}`
10116
10578
  };
10117
10579
  switch (cli) {
10118
- case "claude":
10119
- return buildClaudeCliLaunchConfig2(common);
10120
- case "codex":
10121
- return buildCodexLaunchConfig2(common);
10122
10580
  case "gemini":
10123
10581
  return buildGeminiCliLaunchConfig2(common);
10124
10582
  case "qwen":
@@ -10131,6 +10589,18 @@ async function buildLaunchConfig(cli, llmConfig, opts) {
10131
10589
  }
10132
10590
  }
10133
10591
  }
10592
+ async function shutdownLaunchDaemon(daemon) {
10593
+ daemon.routeLeaseManager.shutdown();
10594
+ await daemon.providerProxy.stop();
10595
+ daemon.apiKeyPool.dispose();
10596
+ daemon.tokenRefreshScheduler.dispose();
10597
+ daemon.claudeAllowanceRefreshScheduler.dispose();
10598
+ daemon.accountHealthSweeper.dispose();
10599
+ daemon.accountHealthProbeScheduler.dispose();
10600
+ daemon.auditPruneSweeper.dispose();
10601
+ daemon.billingRetrySweeper.dispose();
10602
+ daemon.pricingRefreshScheduler.dispose();
10603
+ }
10134
10604
  function spawnCliInherit(plan) {
10135
10605
  return new Promise((resolve3, reject) => {
10136
10606
  const child = spawn2(plan.command, plan.args, {
@@ -10177,7 +10647,7 @@ import { createInterface } from "readline";
10177
10647
  import { parseArgs as parseArgs5 } from "util";
10178
10648
  import { fetchUpstream as fetchUpstream8, setUpstreamProxyResolver as setUpstreamProxyResolver2 } from "@omnicross/core/pipeline/upstreamFetch";
10179
10649
  import { claudeOAuth as claudeOAuth3, codexOAuth as codexOAuth3, geminiOAuth as geminiOAuth3 } from "@omnicross/subscriptions";
10180
- var PROVIDERS = ["claude", "codex", "gemini"];
10650
+ var PROVIDERS2 = ["claude", "codex", "gemini"];
10181
10651
  async function runLogin(argv, deps) {
10182
10652
  const { values, positionals } = parseArgs5({
10183
10653
  args: argv,
@@ -10191,10 +10661,10 @@ async function runLogin(argv, deps) {
10191
10661
  });
10192
10662
  const provider = positionals[0];
10193
10663
  if (!provider) {
10194
- throw new Error(`login: a <provider> is required (one of ${PROVIDERS.join("|")})`);
10664
+ throw new Error(`login: a <provider> is required (one of ${PROVIDERS2.join("|")})`);
10195
10665
  }
10196
10666
  if (!isLoginProvider(provider)) {
10197
- throw new Error(`login: unknown provider '${provider}' (expected ${PROVIDERS.join("|")})`);
10667
+ throw new Error(`login: unknown provider '${provider}' (expected ${PROVIDERS2.join("|")})`);
10198
10668
  }
10199
10669
  if (!values.config) {
10200
10670
  throw new Error("login: --config <path> is required");
@@ -10300,7 +10770,7 @@ async function loginGemini(store, deps, exchangeFetch, label) {
10300
10770
  return expiresAt;
10301
10771
  }
10302
10772
  function isLoginProvider(value) {
10303
- return PROVIDERS.includes(value);
10773
+ return PROVIDERS2.includes(value);
10304
10774
  }
10305
10775
  async function presentUrl(authUrl, deps) {
10306
10776
  console.info("Open this URL in your browser to authorize:");
@@ -10346,7 +10816,7 @@ function promptPaste(prompt) {
10346
10816
  }
10347
10817
 
10348
10818
  // src/commands/providers.ts
10349
- import { randomUUID as randomUUID6 } from "crypto";
10819
+ import { randomUUID as randomUUID7 } from "crypto";
10350
10820
  import { parseArgs as parseArgs6 } from "util";
10351
10821
  async function runProviders(argv) {
10352
10822
  const { values, positionals } = parseArgs6({
@@ -10468,7 +10938,7 @@ function providersAddKey(configPath, providerId, opts) {
10468
10938
  const cfg = loadConfig(configPath);
10469
10939
  const row = cfg.providers.find((p) => p.id === providerId);
10470
10940
  if (!row) throw new Error(`providers add-key: unknown provider '${providerId}'`);
10471
- const entry = { id: randomUUID6(), apiKey: opts.key };
10941
+ const entry = { id: randomUUID7(), apiKey: opts.key };
10472
10942
  if (opts.label) entry.label = opts.label;
10473
10943
  if (opts.weight !== void 0) {
10474
10944
  const w = Number(opts.weight);
@@ -10496,7 +10966,7 @@ function providersRmKey(configPath, providerId, keyId) {
10496
10966
  }
10497
10967
 
10498
10968
  // src/commands/secrets.ts
10499
- import { existsSync as existsSync21, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
10969
+ import { existsSync as existsSync21, readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
10500
10970
  import { parseArgs as parseArgs7 } from "util";
10501
10971
  async function runSecrets(argv) {
10502
10972
  const { values, positionals } = parseArgs7({
@@ -10670,7 +11140,7 @@ function secretsDecrypt(args) {
10670
11140
  }
10671
11141
  saveConfig(args.config, cfg);
10672
11142
  if (tokensPlain) {
10673
- writeFileSync12(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
11143
+ writeFileSync13(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n", "utf8");
10674
11144
  }
10675
11145
  console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
10676
11146
  }
@@ -10714,7 +11184,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
10714
11184
  { updatedAt: "", ...plain },
10715
11185
  box
10716
11186
  );
10717
- writeFileSync12(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
11187
+ writeFileSync13(tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
10718
11188
  }
10719
11189
  var TOKEN_FIELDS2 = {
10720
11190
  claude: ["accessToken", "refreshToken"],
@@ -10743,8 +11213,8 @@ function tokensSuffix(configPath) {
10743
11213
  // src/commands/start.ts
10744
11214
  import { parseArgs as parseArgs8 } from "util";
10745
11215
  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";
11216
+ import { getSharedAccountHealth as getSharedAccountHealth5 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
11217
+ import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling6 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
10748
11218
 
10749
11219
  // src/identity/identityRuntime.ts
10750
11220
  import { getSharedIdentityStore as getSharedIdentityStore3 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
@@ -10804,11 +11274,11 @@ async function runStart(argv) {
10804
11274
  await daemon.llmConfig.ready();
10805
11275
  await daemon.providerProxy.start();
10806
11276
  const serverConfig = await loadServerConfig3(daemon.settingsStore);
10807
- getSharedAccountHealth4().configure({
11277
+ getSharedAccountHealth5().configure({
10808
11278
  overloadEnabled: serverConfig.accountHealth?.overloadCooldownEnabled,
10809
11279
  overloadTtlMs: serverConfig.accountHealth?.overloadCooldownMs
10810
11280
  });
10811
- getSharedAccountAllowanceScheduling5().configure(serverConfig.allowanceScheduling);
11281
+ getSharedAccountAllowanceScheduling6().configure(serverConfig.allowanceScheduling);
10812
11282
  daemon.claudeAllowanceRefreshScheduler.configure(serverConfig.allowanceScheduling);
10813
11283
  await daemon.outboundApiServer.applyConfig({
10814
11284
  enabled: true,