claudish 8.1.0 → 9.0.1

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.
Files changed (2) hide show
  1. package/dist/index.js +583 -410
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -731,7 +731,7 @@ var init_onepassword_config = __esm(() => {
731
731
  });
732
732
 
733
733
  // src/version.ts
734
- var VERSION = "8.1.0";
734
+ var VERSION = "9.0.1";
735
735
 
736
736
  // src/logger.ts
737
737
  var exports_logger = {};
@@ -27477,11 +27477,13 @@ function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
27477
27477
  const lastUpdated = typeof data.lastUpdated === "string" ? data.lastUpdated : new Date(0).toISOString();
27478
27478
  const models = Array.isArray(data.models) ? data.models : [];
27479
27479
  const entries = Array.isArray(data.entries) ? data.entries : [];
27480
+ const plans = Array.isArray(data.plans) ? data.plans : undefined;
27480
27481
  return {
27481
27482
  version: 2,
27482
27483
  lastUpdated,
27483
27484
  entries,
27484
- models
27485
+ models,
27486
+ ...plans !== undefined ? { plans } : {}
27485
27487
  };
27486
27488
  }
27487
27489
  function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
@@ -27490,7 +27492,8 @@ function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
27490
27492
  version: 2,
27491
27493
  lastUpdated: data.lastUpdated ?? new Date().toISOString(),
27492
27494
  entries: data.entries ?? existing?.entries ?? [],
27493
- models: data.models ?? existing?.models ?? []
27495
+ models: data.models ?? existing?.models ?? [],
27496
+ ...data.plans !== undefined || existing?.plans !== undefined ? { plans: data.plans ?? existing?.plans ?? [] } : {}
27494
27497
  };
27495
27498
  mkdirSync5(dirname4(path), { recursive: true });
27496
27499
  writeFileSync5(path, JSON.stringify(merged), "utf-8");
@@ -27632,13 +27635,32 @@ function resolveSubscriptionRouting(modelId, provider, cachePath) {
27632
27635
  const entry = findCacheEntry(modelId, cachePath);
27633
27636
  if (!entry)
27634
27637
  return { kind: "unknown" };
27635
- if (entry.subscriptionPlans?.includes(provider)) {
27638
+ const cache = readAllModelsCache(cachePath);
27639
+ const providerPlans = cache?.plans?.filter((plan) => plan.routing?.providerUid === provider) ?? [];
27640
+ if (cache?.plans === undefined) {
27641
+ if (entry.subscriptionPlans?.includes(provider)) {
27642
+ const agg = entry.aggregators?.find((a) => a.provider === provider);
27643
+ return agg?.externalId ? { kind: "serves", externalId: agg.externalId } : { kind: "unknown" };
27644
+ }
27645
+ return isLegacySubscriptionPlan(provider, cachePath) ? { kind: "not-served" } : { kind: "unknown" };
27646
+ }
27647
+ if (providerPlans.length === 0)
27648
+ return { kind: "unknown" };
27649
+ const providerPlanIds = new Set(providerPlans.map((plan) => plan.id));
27650
+ const hasMembership = entry.subscriptionPlans?.some((planId) => providerPlanIds.has(planId));
27651
+ if (hasMembership) {
27636
27652
  const agg = entry.aggregators?.find((a) => a.provider === provider);
27637
27653
  return agg?.externalId ? { kind: "serves", externalId: agg.externalId } : { kind: "unknown" };
27638
27654
  }
27639
- return isSubscriptionPlan(provider, cachePath) ? { kind: "not-served" } : { kind: "unknown" };
27655
+ const hasPublishedProviderRoster = cache.entries.some((candidate) => candidate.subscriptionPlans?.some((planId) => providerPlanIds.has(planId)));
27656
+ if (!hasPublishedProviderRoster)
27657
+ return { kind: "unknown" };
27658
+ return providerPlans.every(isCatalogDiscoveredPlan) ? { kind: "not-served" } : { kind: "unknown" };
27640
27659
  }
27641
- function isSubscriptionPlan(provider, cachePath) {
27660
+ function isCatalogDiscoveredPlan(plan) {
27661
+ return plan.modelDiscovery === "catalog";
27662
+ }
27663
+ function isLegacySubscriptionPlan(provider, cachePath) {
27642
27664
  const cache = readAllModelsCache(cachePath);
27643
27665
  if (!cache)
27644
27666
  return false;
@@ -28305,17 +28327,24 @@ var init_runtime_providers = __esm(() => {
28305
28327
  });
28306
28328
 
28307
28329
  // src/handlers/shared/remote-provider-types.ts
28330
+ function registerSubscriptionCredentialProbe(fn) {
28331
+ const previous = _subscriptionCredentialProbe;
28332
+ _subscriptionCredentialProbe = fn;
28333
+ return previous;
28334
+ }
28308
28335
  function isSubscriptionProvider(provider) {
28309
- return SUBSCRIPTION_PROVIDERS.has(provider.toLowerCase());
28336
+ const p = provider.toLowerCase();
28337
+ if (SUBSCRIPTION_PROVIDERS.has(p))
28338
+ return true;
28339
+ if (!CREDENTIAL_DECIDED_PROVIDERS.has(p))
28340
+ return false;
28341
+ return _subscriptionCredentialProbe?.(p) === true;
28310
28342
  }
28311
28343
  function registerDynamicPricingLookup(fn) {
28312
28344
  _dynamicLookup = fn;
28313
28345
  }
28314
28346
  function getModelPricing(provider, modelName) {
28315
28347
  const p = provider.toLowerCase();
28316
- if (FREE_PROVIDERS.has(p)) {
28317
- return { inputCostPer1M: 0, outputCostPer1M: 0, isFree: true };
28318
- }
28319
28348
  if (isSubscriptionProvider(p)) {
28320
28349
  return { inputCostPer1M: 0, outputCostPer1M: 0, isSubscription: true };
28321
28350
  }
@@ -28327,7 +28356,7 @@ function getModelPricing(provider, modelName) {
28327
28356
  const canonical = PROVIDER_ALIAS[p] || p;
28328
28357
  return PROVIDER_DEFAULTS[canonical] || { inputCostPer1M: 1, outputCostPer1M: 4, isEstimate: true };
28329
28358
  }
28330
- var PROVIDER_DEFAULTS, FREE_PROVIDERS, SUBSCRIPTION_PROVIDERS, PROVIDER_ALIAS, _dynamicLookup = null;
28359
+ var PROVIDER_DEFAULTS, SUBSCRIPTION_PROVIDERS, CREDENTIAL_DECIDED_PROVIDERS, _subscriptionCredentialProbe = null, PROVIDER_ALIAS, _dynamicLookup = null;
28331
28360
  var init_remote_provider_types = __esm(() => {
28332
28361
  PROVIDER_DEFAULTS = {
28333
28362
  gemini: { inputCostPer1M: 0.5, outputCostPer1M: 2, isEstimate: true },
@@ -28337,7 +28366,6 @@ var init_remote_provider_types = __esm(() => {
28337
28366
  glm: { inputCostPer1M: 0.16, outputCostPer1M: 0.8, isEstimate: true },
28338
28367
  ollamacloud: { inputCostPer1M: 1, outputCostPer1M: 4, isEstimate: true }
28339
28368
  };
28340
- FREE_PROVIDERS = new Set(["opencode-zen", "zen"]);
28341
28369
  SUBSCRIPTION_PROVIDERS = new Set([
28342
28370
  "minimax-coding",
28343
28371
  "kimi-coding",
@@ -28346,8 +28374,10 @@ var init_remote_provider_types = __esm(() => {
28346
28374
  "devin",
28347
28375
  "antigravity",
28348
28376
  "sakana-subscription",
28349
- "grok-subscription"
28377
+ "grok-subscription",
28378
+ "opencode-zen-go"
28350
28379
  ]);
28380
+ CREDENTIAL_DECIDED_PROVIDERS = new Set(["openai-codex"]);
28351
28381
  PROVIDER_ALIAS = {
28352
28382
  google: "gemini",
28353
28383
  oai: "openai",
@@ -36548,7 +36578,7 @@ class ApiKeyCredentialProvider {
36548
36578
  } else {
36549
36579
  headers = { ...this.staticHeaders };
36550
36580
  }
36551
- return { headers };
36581
+ return { arm: "api-key", headers };
36552
36582
  }
36553
36583
  }
36554
36584
  var init_api_key_credential = __esm(() => {
@@ -36927,6 +36957,34 @@ var init_codex_oauth = __esm(() => {
36927
36957
  };
36928
36958
  });
36929
36959
 
36960
+ // src/auth/credentials/billing-probe.ts
36961
+ function recordSignedArm(provider, arm) {
36962
+ signedArm.set(provider.toLowerCase(), arm);
36963
+ }
36964
+ function clearSignedArm(provider) {
36965
+ if (provider)
36966
+ signedArm.delete(provider.toLowerCase());
36967
+ else
36968
+ signedArm.clear();
36969
+ }
36970
+ function installBillingProbes() {
36971
+ return registerSubscriptionCredentialProbe((p) => {
36972
+ const recorded = signedArm.get(p);
36973
+ if (recorded)
36974
+ return recorded === "subscription";
36975
+ return PROBES[p]?.() === true;
36976
+ });
36977
+ }
36978
+ var signedArm, PROBES;
36979
+ var init_billing_probe = __esm(() => {
36980
+ init_remote_provider_types();
36981
+ init_codex_oauth();
36982
+ signedArm = new Map;
36983
+ PROBES = {
36984
+ "openai-codex": () => CodexOAuth.getInstance().hasCredentials()
36985
+ };
36986
+ });
36987
+
36930
36988
  // src/auth/credentials/composite-credential.ts
36931
36989
  class CompositeCredentialProvider {
36932
36990
  catalogName;
@@ -36994,6 +37052,7 @@ class CodexOAuthHalf {
36994
37052
  const token = await this.oauth.getAccessToken();
36995
37053
  const accountId = this.oauth.getAccountId();
36996
37054
  return {
37055
+ arm: "oauth",
36997
37056
  headers: buildOAuthHeaders(token, accountId),
36998
37057
  endpoint: CODEX_RESPONSES_ENDPOINT,
36999
37058
  transformPayload: (p) => ({
@@ -37483,6 +37542,17 @@ var init_kimi_oauth = __esm(() => {
37483
37542
  import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
37484
37543
  import { homedir as homedir22 } from "os";
37485
37544
  import { join as join22 } from "path";
37545
+ function credentialSatisfies(descriptor, data) {
37546
+ if (!data?.access_token)
37547
+ return false;
37548
+ if (data.refresh_token)
37549
+ return true;
37550
+ if (descriptor.expiresAtField && data[descriptor.expiresAtField]) {
37551
+ const buffer = descriptor.expiryBufferMs ?? 0;
37552
+ return data[descriptor.expiresAtField] > Date.now() + buffer;
37553
+ }
37554
+ return true;
37555
+ }
37486
37556
  function hasValidOAuthCredentials(descriptor) {
37487
37557
  const credPath = join22(homedir22(), ".claudish", descriptor.credentialFile);
37488
37558
  if (!existsSync15(credPath))
@@ -37491,16 +37561,7 @@ function hasValidOAuthCredentials(descriptor) {
37491
37561
  return true;
37492
37562
  }
37493
37563
  try {
37494
- const data = JSON.parse(readFileSync14(credPath, "utf-8"));
37495
- if (!data.access_token)
37496
- return false;
37497
- if (data.refresh_token)
37498
- return true;
37499
- if (descriptor.expiresAtField && data[descriptor.expiresAtField]) {
37500
- const buffer = descriptor.expiryBufferMs ?? 0;
37501
- return data[descriptor.expiresAtField] > Date.now() + buffer;
37502
- }
37503
- return true;
37564
+ return credentialSatisfies(descriptor, JSON.parse(readFileSync14(credPath, "utf-8")));
37504
37565
  } catch {
37505
37566
  return false;
37506
37567
  }
@@ -37551,6 +37612,7 @@ class KimiOAuthHalf {
37551
37612
  async getRequestAuth(_ctx) {
37552
37613
  const token = await this.oauth.getAccessToken();
37553
37614
  return {
37615
+ arm: "oauth",
37554
37616
  headers: {
37555
37617
  "anthropic-version": "2023-06-01",
37556
37618
  Authorization: `Bearer ${token}`,
@@ -37750,6 +37812,7 @@ class CredentialAuthority {
37750
37812
  return p.getRequestAuth(ctx);
37751
37813
  }
37752
37814
  invalidate(name) {
37815
+ clearSignedArm(name);
37753
37816
  if (name) {
37754
37817
  this.registry.get(name)?.invalidate?.();
37755
37818
  return;
@@ -37764,9 +37827,11 @@ class CredentialAuthority {
37764
37827
  }
37765
37828
  async login(name) {
37766
37829
  await this.registry.get(name)?.login?.();
37830
+ clearSignedArm(name);
37767
37831
  }
37768
37832
  async logout(name) {
37769
37833
  await this.registry.get(name)?.logout?.();
37834
+ clearSignedArm(name);
37770
37835
  }
37771
37836
  get(name) {
37772
37837
  return this.registry.get(name);
@@ -37817,6 +37882,7 @@ var init_authority = __esm(() => {
37817
37882
  init_provider_definitions();
37818
37883
  init_antigravity_credential();
37819
37884
  init_api_key_credential();
37885
+ init_billing_probe();
37820
37886
  init_codex_credential();
37821
37887
  init_devin_credential();
37822
37888
  init_grok_credential();
@@ -37829,6 +37895,7 @@ var init_authority = __esm(() => {
37829
37895
  google: ["gemini"]
37830
37896
  };
37831
37897
  credentials = CredentialAuthority.buildDefault();
37898
+ installBillingProbes();
37832
37899
  });
37833
37900
 
37834
37901
  // src/providers/devin/proto-codec.ts
@@ -44976,6 +45043,7 @@ var init_openai_codex = __esm(() => {
44976
45043
  init_codex_api_format();
44977
45044
  init_model_catalog();
44978
45045
  init_authority();
45046
+ init_billing_probe();
44979
45047
  init_harness();
44980
45048
  init_openai();
44981
45049
  FALLBACK_CONVERSATION_KEY = randomBytes6(16).toString("hex");
@@ -44989,6 +45057,8 @@ var init_openai_codex = __esm(() => {
44989
45057
  } catch {
44990
45058
  this.cachedAuth = null;
44991
45059
  }
45060
+ const signedWithOAuth = this.cachedAuth?.arm === "oauth";
45061
+ recordSignedArm("openai-codex", signedWithOAuth ? "subscription" : "metered");
44992
45062
  }
44993
45063
  getEndpoint(_targetModel) {
44994
45064
  return this.cachedAuth?.endpoint ?? super.getEndpoint();
@@ -45477,16 +45547,28 @@ function describeMissingCredential(providerName) {
45477
45547
  const keyNames = info?.envVar ? [info.envVar, ...info.aliases ?? []].join(" or ") : undefined;
45478
45548
  const signup = info?.url ? ` Get one at ${info.url}.` : "";
45479
45549
  const def = getProviderByName(providerName);
45550
+ const sibling = describeSiblingKeys(def);
45480
45551
  if (isLocalTransport(providerName)) {
45481
45552
  const where = def ? ` Claudish will use ${getEffectiveBaseUrl(def)}.` : "";
45482
45553
  const keyClause = keyNames ? ` (Only set ${keyNames} if your local server requires a bearer token.)` : "";
45483
- return `Provider "${providerName}" is a LOCAL server and is not enabled. Enable it in \`claudish config\` (Providers tab), or add "localProviders": ["${providerName}"] to ~/.claudish/config.json.${where}${keyClause}`;
45554
+ return `Provider "${providerName}" is a LOCAL server and is not enabled. Enable it in \`claudish config\` (Providers tab), or add "localProviders": ["${providerName}"] to ~/.claudish/config.json.${where}${keyClause}${sibling}`;
45484
45555
  }
45485
45556
  if (def?.oauthFallback) {
45486
45557
  const keyClause = keyNames ? ` Or set ${keyNames} (env, config, or 1Password import) to use a metered API key instead.${signup}` : "";
45487
- return `No credential for provider "${providerName}". Sign in with \`claudish login ${providerName}\` to use your existing subscription.${keyClause}`;
45558
+ return `No credential for provider "${providerName}". Sign in with \`claudish login ${providerName}\` to use your existing subscription.${keyClause}${sibling}`;
45488
45559
  }
45489
- return keyNames ? `No API key for provider "${providerName}". Set ${keyNames} (env, config, or 1Password import).${signup}` : `No API key for provider "${providerName}".`;
45560
+ return keyNames ? `No API key for provider "${providerName}". Set ${keyNames} (env, config, or 1Password import).${signup}${sibling}` : `No API key for provider "${providerName}".${sibling}`;
45561
+ }
45562
+ function describeSiblingKeys(def) {
45563
+ const vars = def?.siblingKeyEnvVars ?? [];
45564
+ if (vars.length === 0)
45565
+ return "";
45566
+ const all = getAllProviders();
45567
+ const named = vars.map((v) => {
45568
+ const owner = all.find((p) => p.name !== def?.name && p.apiKeyEnvVar === v);
45569
+ return owner ? `${v} (${owner.name})` : v;
45570
+ });
45571
+ return ` Note: ${named.join(" or ")} is a DIFFERENT plan's key and is not accepted here.`;
45490
45572
  }
45491
45573
  function getDisplayName(providerName) {
45492
45574
  const def = getProviderByName(providerName);
@@ -45952,7 +46034,7 @@ var init_provider_definitions = __esm(() => {
45952
46034
  apiPath: "/v1/chat/completions",
45953
46035
  modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
45954
46036
  apiKeyEnvVar: "OPENCODE_GO_API_KEY",
45955
- apiKeyAliases: ["OPENCODE_API_KEY"],
46037
+ siblingKeyEnvVars: ["OPENCODE_API_KEY"],
45956
46038
  apiKeyDescription: "OpenCode Zen Go (Lite Plan) API Key",
45957
46039
  apiKeyUrl: "https://opencode.ai/",
45958
46040
  shortcuts: ["zengo", "zgo"],
@@ -46288,6 +46370,16 @@ var init_auto_route = __esm(() => {
46288
46370
  });
46289
46371
 
46290
46372
  // src/providers/catalog-client.ts
46373
+ function derivePlansUrl(catalogUrl) {
46374
+ try {
46375
+ const url2 = new URL(catalogUrl);
46376
+ url2.pathname = url2.pathname.replace(/\/queryModels$/, "/queryPlans");
46377
+ url2.search = "";
46378
+ return url2.toString();
46379
+ } catch {
46380
+ return "https://us-central1-claudish-6da10.cloudfunctions.net/queryPlans";
46381
+ }
46382
+ }
46291
46383
  function getCatalogEntries() {
46292
46384
  if (_catalogEntriesForTest !== undefined)
46293
46385
  return _catalogEntriesForTest;
@@ -46421,6 +46513,7 @@ function logResolution(userInput, result, quiet = false) {
46421
46513
  }
46422
46514
  }
46423
46515
  async function refreshCatalog(timeoutMs) {
46516
+ const plansPromise = fetchSubscriptionPlans(timeoutMs);
46424
46517
  let response;
46425
46518
  try {
46426
46519
  response = await fetch(FIREBASE_CATALOG_URL, { signal: AbortSignal.timeout(timeoutMs) });
@@ -46447,10 +46540,28 @@ async function refreshCatalog(timeoutMs) {
46447
46540
  backwardCompatModels.push({ id });
46448
46541
  }
46449
46542
  _memCache = data.models;
46450
- writeAllModelsCache({ entries: data.models, models: backwardCompatModels });
46543
+ const plans = await plansPromise;
46544
+ writeAllModelsCache({
46545
+ entries: data.models,
46546
+ models: backwardCompatModels,
46547
+ ...plans !== undefined ? { plans } : {}
46548
+ });
46451
46549
  _warmPromise = Promise.resolve();
46452
46550
  return { kind: "refreshed", modelCount: data.models.length };
46453
46551
  }
46552
+ async function fetchSubscriptionPlans(timeoutMs) {
46553
+ try {
46554
+ const response = await fetch(FIREBASE_PLANS_URL, {
46555
+ signal: AbortSignal.timeout(timeoutMs)
46556
+ });
46557
+ if (!response.ok)
46558
+ return;
46559
+ const data = await response.json();
46560
+ return Array.isArray(data.plans) ? data.plans : undefined;
46561
+ } catch {
46562
+ return;
46563
+ }
46564
+ }
46454
46565
  async function warmCatalog() {
46455
46566
  if (!_warmPromise) {
46456
46567
  _warmPromise = refreshCatalog(8000).then(() => {
@@ -46472,10 +46583,11 @@ async function ensureCatalogReady(timeoutMs = 5000) {
46472
46583
  new Promise((resolve3) => setTimeout(resolve3, timeoutMs))
46473
46584
  ]);
46474
46585
  }
46475
- var FIREBASE_CATALOG_URL, _memCache = null, _catalogEntriesForTest, _warmPromise = null;
46586
+ var FIREBASE_CATALOG_URL, FIREBASE_PLANS_URL, _memCache = null, _catalogEntriesForTest, _warmPromise = null;
46476
46587
  var init_catalog_client = __esm(() => {
46477
46588
  init_all_models_cache();
46478
46589
  FIREBASE_CATALOG_URL = process.env.CLAUDISH_CATALOG_URL ?? process.env.FIREBASE_CATALOG_URL ?? "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000";
46590
+ FIREBASE_PLANS_URL = process.env.CLAUDISH_PLANS_URL ?? derivePlansUrl(FIREBASE_CATALOG_URL);
46479
46591
  });
46480
46592
 
46481
46593
  // src/config-schema.ts
@@ -47568,7 +47680,351 @@ var init_routing_hints = __esm(() => {
47568
47680
  openrouter: { apiKeyEnvVar: "OPENROUTER_API_KEY" },
47569
47681
  "x-ai": { apiKeyEnvVar: "XAI_API_KEY" },
47570
47682
  "z-ai": { apiKeyEnvVar: "ZAI_API_KEY" },
47571
- "opencode-zen": { apiKeyEnvVar: "OPENCODE_API_KEY" }
47683
+ "opencode-zen": { apiKeyEnvVar: "OPENCODE_API_KEY" },
47684
+ "opencode-zen-go": { apiKeyEnvVar: "OPENCODE_GO_API_KEY" }
47685
+ };
47686
+ });
47687
+
47688
+ // src/providers/cache-ttl.ts
47689
+ var FIREBASE_CACHE_TTL_HOURS = 24, FIREBASE_CACHE_TTL_MS;
47690
+ var init_cache_ttl = __esm(() => {
47691
+ FIREBASE_CACHE_TTL_MS = FIREBASE_CACHE_TTL_HOURS * 60 * 60 * 1000;
47692
+ });
47693
+
47694
+ // src/model-loader.ts
47695
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
47696
+ import { homedir as homedir27 } from "os";
47697
+ import { join as join27 } from "path";
47698
+ function groupRecommendedModels(entries) {
47699
+ const byId = new Map;
47700
+ const categoryOrder = new Map;
47701
+ for (const entry of entries) {
47702
+ const list = byId.get(entry.id);
47703
+ if (list)
47704
+ list.push(entry);
47705
+ else
47706
+ byId.set(entry.id, [entry]);
47707
+ if (!categoryOrder.has(entry.category))
47708
+ categoryOrder.set(entry.category, categoryOrder.size);
47709
+ }
47710
+ const flagship = [];
47711
+ const fast = [];
47712
+ for (const [id, members] of byId.entries()) {
47713
+ const primary = members.find((m) => m.category !== "subscription") ?? members[0];
47714
+ const subscriptions = members.filter((m) => m.category === "subscription");
47715
+ const bucket = primary.category === "programming" || primary.category === "vision" || primary.category === "reasoning" ? "flagship" : "fast";
47716
+ const group = { id, primary, subscriptions, bucket };
47717
+ if (bucket === "flagship")
47718
+ flagship.push(group);
47719
+ else
47720
+ fast.push(group);
47721
+ }
47722
+ const byCuratedPriorityThenFreshness = (a, b) => {
47723
+ const aCat = categoryOrder.get(a.primary.category) ?? Number.MAX_SAFE_INTEGER;
47724
+ const bCat = categoryOrder.get(b.primary.category) ?? Number.MAX_SAFE_INTEGER;
47725
+ if (aCat !== bCat)
47726
+ return aCat - bCat;
47727
+ if (a.primary.priority !== b.primary.priority)
47728
+ return a.primary.priority - b.primary.priority;
47729
+ return compareByReleaseDateDesc(a.primary, b.primary);
47730
+ };
47731
+ flagship.sort(byCuratedPriorityThenFreshness);
47732
+ fast.sort(byCuratedPriorityThenFreshness);
47733
+ return { flagship, fast };
47734
+ }
47735
+ function collectRoutingPrefixes(group, getNativePrefix) {
47736
+ const slug = (group.primary.provider || "").toLowerCase();
47737
+ const native = getNativePrefix(slug);
47738
+ const seen = new Set;
47739
+ const out = [];
47740
+ if (native) {
47741
+ out.push(native);
47742
+ seen.add(native);
47743
+ }
47744
+ for (const subscriptionRow of group.subscriptions) {
47745
+ const routes = subscriptionRow.subscriptions && subscriptionRow.subscriptions.length > 0 ? subscriptionRow.subscriptions : subscriptionRow.subscription ? [subscriptionRow.subscription] : [];
47746
+ const orderedRoutes = [...routes].sort(compareRecommendedRoutes);
47747
+ for (const route of orderedRoutes) {
47748
+ const p = route?.prefix;
47749
+ if (!p || seen.has(p))
47750
+ continue;
47751
+ seen.add(p);
47752
+ out.push(p);
47753
+ }
47754
+ }
47755
+ return out;
47756
+ }
47757
+ function compareRecommendedRoutes(left, right) {
47758
+ const leftRank = left?.tier && Object.hasOwn(RECOMMENDED_ROUTE_TIER_ORDER, left.tier) ? RECOMMENDED_ROUTE_TIER_ORDER[left.tier] : Number.MAX_SAFE_INTEGER;
47759
+ const rightRank = right?.tier && Object.hasOwn(RECOMMENDED_ROUTE_TIER_ORDER, right.tier) ? RECOMMENDED_ROUTE_TIER_ORDER[right.tier] : Number.MAX_SAFE_INTEGER;
47760
+ return leftRank - rightRank;
47761
+ }
47762
+ function buildCatalogRoutingRules(doc2) {
47763
+ const routesByModel = new Map;
47764
+ let sourceIndex = 0;
47765
+ for (const entry of doc2.models) {
47766
+ const routes = entry.subscriptions && entry.subscriptions.length > 0 ? entry.subscriptions : entry.subscription ? [entry.subscription] : [];
47767
+ for (const route of routes) {
47768
+ routesByModel.set(entry.id, [...routesByModel.get(entry.id) ?? [], { route, sourceIndex }]);
47769
+ sourceIndex += 1;
47770
+ }
47771
+ }
47772
+ const rules = {};
47773
+ for (const [modelId, candidates] of routesByModel) {
47774
+ const seen = new Set;
47775
+ const entries = candidates.filter(({ route }) => typeof route?.routingProvider === "string" && route.routingProvider.length > 0 && typeof route.command === "string" && route.command.length > 0).sort((left, right) => compareRecommendedRoutes(left.route, right.route) || left.sourceIndex - right.sourceIndex).map(({ route }) => {
47776
+ const at = route.command.indexOf("@");
47777
+ const wireId = at >= 0 ? route.command.slice(at + 1) : route.command;
47778
+ return `${route.routingProvider}@${wireId}`;
47779
+ }).filter((entry) => {
47780
+ if (seen.has(entry))
47781
+ return false;
47782
+ seen.add(entry);
47783
+ return true;
47784
+ });
47785
+ if (entries.length > 0)
47786
+ rules[modelId] = entries;
47787
+ }
47788
+ return rules;
47789
+ }
47790
+ function parsePriceAvg(s) {
47791
+ if (!s || s === "N/A")
47792
+ return Number.POSITIVE_INFINITY;
47793
+ if (s === "FREE")
47794
+ return 0;
47795
+ const m = s.match(/\$([\d.]+)/);
47796
+ return m ? Number.parseFloat(m[1]) : Number.POSITIVE_INFINITY;
47797
+ }
47798
+ function parseCtx(s) {
47799
+ if (!s || s === "N/A")
47800
+ return 0;
47801
+ const upper = s.toUpperCase();
47802
+ if (upper.includes("M"))
47803
+ return Number.parseFloat(upper) * 1e6;
47804
+ if (upper.includes("K"))
47805
+ return Number.parseFloat(upper) * 1000;
47806
+ return Number.parseInt(s, 10) || 0;
47807
+ }
47808
+ function normalizePricingDisplay(raw) {
47809
+ const pricing = raw || "N/A";
47810
+ if (pricing.includes("-1000000"))
47811
+ return "varies";
47812
+ if (pricing === "$0.00/1M" || pricing === "FREE")
47813
+ return "FREE";
47814
+ return pricing;
47815
+ }
47816
+ function formatListingPrice(entry, opts) {
47817
+ const rate = normalizePricingDisplay(entry.pricing?.average);
47818
+ if (rate !== "N/A")
47819
+ return rate;
47820
+ const plan = entry.subscription?.plan;
47821
+ if (!plan)
47822
+ return "N/A";
47823
+ return opts?.compact ? "SUB" : `SUB (${plan})`;
47824
+ }
47825
+ function computeQuickPicks(primaries) {
47826
+ if (primaries.length === 0) {
47827
+ return {
47828
+ budget: null,
47829
+ largeContext: null,
47830
+ mostCapable: null,
47831
+ visionCoding: null,
47832
+ agentic: null
47833
+ };
47834
+ }
47835
+ const priced = primaries.filter((m) => {
47836
+ const p = parsePriceAvg(m.pricing?.average);
47837
+ return p > 0 && p !== Number.POSITIVE_INFINITY;
47838
+ }).sort((a, b) => parsePriceAvg(a.pricing?.average) - parsePriceAvg(b.pricing?.average));
47839
+ const budget = priced[0] ?? null;
47840
+ const byCtx = [...primaries].sort((a, b) => parseCtx(b.context) - parseCtx(a.context));
47841
+ const largeContext = byCtx[0] ?? null;
47842
+ const byPrice = [...primaries].sort((a, b) => parsePriceAvg(b.pricing?.average) - parsePriceAvg(a.pricing?.average));
47843
+ const mostCapable = byPrice.find((m) => parsePriceAvg(m.pricing?.average) !== Number.POSITIVE_INFINITY) ?? null;
47844
+ const visionCoding = primaries.find((m) => m.supportsVision === true && m.id !== budget?.id && m.id !== mostCapable?.id) ?? null;
47845
+ const agentic = primaries.find((m) => m.supportsReasoning === true && m.id !== mostCapable?.id) ?? null;
47846
+ return { budget, largeContext, mostCapable, visionCoding, agentic };
47847
+ }
47848
+ async function getRecommendedModels(opts = {}) {
47849
+ const { forceRefresh = false } = opts;
47850
+ if (!forceRefresh && _cachedRecommendedModels) {
47851
+ return _cachedRecommendedModels;
47852
+ }
47853
+ if (!forceRefresh && existsSync19(RECOMMENDED_MODELS_CACHE_PATH)) {
47854
+ try {
47855
+ const cacheData = JSON.parse(readFileSync18(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
47856
+ if (cacheData.models && cacheData.models.length > 0 && isFreshEnough(cacheData)) {
47857
+ _cachedRecommendedModels = cacheData;
47858
+ return cacheData;
47859
+ }
47860
+ } catch {}
47861
+ }
47862
+ try {
47863
+ const response = await fetch(FIREBASE_RECOMMENDED_URL, {
47864
+ signal: AbortSignal.timeout(RECOMMENDED_FETCH_TIMEOUT_MS)
47865
+ });
47866
+ if (response.ok) {
47867
+ const data = await response.json();
47868
+ if (data.models && data.models.length > 0) {
47869
+ _cachedRecommendedModels = data;
47870
+ try {
47871
+ const cacheDir = join27(homedir27(), ".claudish");
47872
+ mkdirSync11(cacheDir, { recursive: true });
47873
+ writeFileSync10(RECOMMENDED_MODELS_CACHE_PATH, JSON.stringify(data), "utf-8");
47874
+ } catch {}
47875
+ return data;
47876
+ }
47877
+ }
47878
+ } catch {}
47879
+ throw new Error("Unable to load recommended models: Firebase unreachable and no local cache. " + "Check connectivity.");
47880
+ }
47881
+ function getRecommendedModelsSync() {
47882
+ if (_cachedRecommendedModels)
47883
+ return _cachedRecommendedModels;
47884
+ if (existsSync19(RECOMMENDED_MODELS_CACHE_PATH)) {
47885
+ try {
47886
+ const cacheData = JSON.parse(readFileSync18(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
47887
+ if (cacheData.models && cacheData.models.length > 0 && isFreshEnough(cacheData)) {
47888
+ _cachedRecommendedModels = cacheData;
47889
+ return cacheData;
47890
+ }
47891
+ } catch {}
47892
+ }
47893
+ return { version: "0", lastUpdated: "", models: [] };
47894
+ }
47895
+ async function warmRecommendedModels() {
47896
+ try {
47897
+ return await getRecommendedModels({ forceRefresh: true });
47898
+ } catch {
47899
+ return null;
47900
+ }
47901
+ }
47902
+ function isFreshEnough(doc2) {
47903
+ const generatedAt = doc2.generatedAt;
47904
+ if (!generatedAt)
47905
+ return true;
47906
+ const ageHours = (Date.now() - new Date(generatedAt).getTime()) / (1000 * 60 * 60);
47907
+ return ageHours <= FIREBASE_CACHE_TTL_HOURS;
47908
+ }
47909
+ async function searchModels(query, limit = 50) {
47910
+ const url2 = `${FIREBASE_BASE_URL}?search=${encodeURIComponent(query)}&limit=${limit}&status=active`;
47911
+ const response = await fetch(url2, {
47912
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
47913
+ });
47914
+ if (!response.ok) {
47915
+ throw new Error(`Firebase search returned ${response.status} ${response.statusText}`);
47916
+ }
47917
+ const data = await response.json();
47918
+ return data.models ?? [];
47919
+ }
47920
+ async function getModelByIdFromFirebase(modelId) {
47921
+ const url2 = `${FIREBASE_BASE_URL}?search=${encodeURIComponent(modelId)}&limit=5`;
47922
+ const response = await fetch(url2, {
47923
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
47924
+ });
47925
+ if (!response.ok) {
47926
+ throw new Error(`Firebase lookup returned ${response.status} ${response.statusText}`);
47927
+ }
47928
+ const data = await response.json();
47929
+ const models = data.models ?? [];
47930
+ for (const m of models) {
47931
+ if (m.modelId === modelId)
47932
+ return m;
47933
+ if (m.aliases?.includes(modelId))
47934
+ return m;
47935
+ }
47936
+ return null;
47937
+ }
47938
+ async function getTop100Models() {
47939
+ const url2 = `${FIREBASE_BASE_URL}?catalog=top100`;
47940
+ const response = await fetch(url2, {
47941
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
47942
+ });
47943
+ if (!response.ok) {
47944
+ throw new Error(`Firebase top100 fetch failed: ${response.status} ${response.statusText}`);
47945
+ }
47946
+ const data = await response.json();
47947
+ return data;
47948
+ }
47949
+ async function getProviderList() {
47950
+ const url2 = `${FIREBASE_BASE_URL}?catalog=providers`;
47951
+ const response = await fetch(url2, {
47952
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
47953
+ });
47954
+ if (!response.ok) {
47955
+ throw new Error(`Firebase providers fetch failed: ${response.status} ${response.statusText}`);
47956
+ }
47957
+ const data = await response.json();
47958
+ return data.providers ?? [];
47959
+ }
47960
+ async function getModelsByProvider(provider, limit = 200) {
47961
+ const url2 = `${FIREBASE_BASE_URL}?provider=${encodeURIComponent(provider)}&status=active&limit=${limit}`;
47962
+ const response = await fetch(url2, {
47963
+ signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
47964
+ });
47965
+ if (!response.ok) {
47966
+ throw new Error(`Firebase provider query returned ${response.status} ${response.statusText}`);
47967
+ }
47968
+ const data = await response.json();
47969
+ if (Array.isArray(data))
47970
+ return data;
47971
+ return data.models ?? [];
47972
+ }
47973
+ function loadModelInfo() {
47974
+ if (_cachedModelInfo) {
47975
+ return _cachedModelInfo;
47976
+ }
47977
+ const data = getRecommendedModelsSync();
47978
+ const modelInfo = {};
47979
+ for (const model of data.models) {
47980
+ modelInfo[model.id] = {
47981
+ name: model.name,
47982
+ description: model.description,
47983
+ priority: model.priority,
47984
+ provider: model.provider
47985
+ };
47986
+ }
47987
+ modelInfo.custom = {
47988
+ name: "Custom Model",
47989
+ description: "Enter any model ID manually",
47990
+ priority: 999,
47991
+ provider: "Custom"
47992
+ };
47993
+ _cachedModelInfo = modelInfo;
47994
+ return modelInfo;
47995
+ }
47996
+ function getAvailableModels() {
47997
+ if (_cachedModelIds) {
47998
+ return _cachedModelIds;
47999
+ }
48000
+ const data = getRecommendedModelsSync();
48001
+ const modelIds = data.models.sort((a, b) => a.priority - b.priority).map((m) => m.id);
48002
+ const result = [...modelIds, "custom"];
48003
+ _cachedModelIds = result;
48004
+ return result;
48005
+ }
48006
+ var _cachedModelInfo = null, _cachedModelIds = null, _cachedRecommendedModels = null, FIREBASE_BASE_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels", FIREBASE_RECOMMENDED_URL, RECOMMENDED_MODELS_CACHE_PATH, RECOMMENDED_FETCH_TIMEOUT_MS = 5000, SEARCH_FETCH_TIMEOUT_MS = 1e4, FIREBASE_SLUG_TO_PROVIDER_NAME, RECOMMENDED_ROUTE_TIER_ORDER;
48007
+ var init_model_loader = __esm(() => {
48008
+ init_cache_ttl();
48009
+ FIREBASE_RECOMMENDED_URL = `${FIREBASE_BASE_URL}?catalog=recommended`;
48010
+ RECOMMENDED_MODELS_CACHE_PATH = join27(homedir27(), ".claudish", "recommended-models-cache.json");
48011
+ FIREBASE_SLUG_TO_PROVIDER_NAME = {
48012
+ openai: "openai",
48013
+ google: "google",
48014
+ "x-ai": "x-ai",
48015
+ "z-ai": "z-ai",
48016
+ moonshotai: "kimi",
48017
+ minimax: "minimax",
48018
+ qwen: "qwen",
48019
+ deepseek: "deepseek",
48020
+ mistralai: "mistralai",
48021
+ sakana: "sakana"
48022
+ };
48023
+ RECOMMENDED_ROUTE_TIER_ORDER = {
48024
+ native: 0,
48025
+ general: 1,
48026
+ metered: 2,
48027
+ aggregator: 3
47572
48028
  };
47573
48029
  });
47574
48030
 
@@ -47668,7 +48124,21 @@ function loadRoutingRules() {
47668
48124
  const global_ = loadConfig().routing ?? {};
47669
48125
  validateRoutingRules(local);
47670
48126
  validateRoutingRules(global_);
47671
- return mergeRoutingRules(DEFAULT_ROUTING_RULES, global_, local);
48127
+ const catalogRules = retainKnownCatalogRoutingRules(buildCatalogRoutingRules(getRecommendedModelsSync()));
48128
+ return mergeRoutingRules({ ...DEFAULT_ROUTING_RULES, ...catalogRules }, global_, local);
48129
+ }
48130
+ function retainKnownCatalogRoutingRules(rules) {
48131
+ const retained = {};
48132
+ for (const [modelId, entries] of Object.entries(rules)) {
48133
+ const knownEntries = entries.filter((entry) => {
48134
+ const providerRaw = entry.split("@", 1)[0]?.toLowerCase() ?? "";
48135
+ const provider = PROVIDER_SHORTCUTS[providerRaw] ?? providerRaw;
48136
+ return getProviderByName(provider) !== undefined;
48137
+ });
48138
+ if (knownEntries.length > 0)
48139
+ retained[modelId] = knownEntries;
48140
+ }
48141
+ return retained;
47672
48142
  }
47673
48143
  function validateRoutingRules(rules) {
47674
48144
  const seenLower = new Map;
@@ -47863,6 +48333,7 @@ var init_routing_rules = __esm(() => {
47863
48333
  init_authority();
47864
48334
  init_remote_provider_types();
47865
48335
  init_logger();
48336
+ init_model_loader();
47866
48337
  init_profile_config();
47867
48338
  init_auto_route();
47868
48339
  init_catalog_client();
@@ -47870,6 +48341,7 @@ var init_routing_rules = __esm(() => {
47870
48341
  init_model_availability();
47871
48342
  init_model_parser();
47872
48343
  init_model_parser();
48344
+ init_provider_definitions();
47873
48345
  init_routing_hints();
47874
48346
  });
47875
48347
 
@@ -49005,8 +49477,8 @@ __export(exports_session_discovery, {
49005
49477
  });
49006
49478
  import { execFile, execFileSync as execFileSync2 } from "child_process";
49007
49479
  import { closeSync as closeSync6, openSync as openSync6, readSync as readSync2, readdirSync as readdirSync4, realpathSync, statSync as statSync5 } from "fs";
49008
- import { homedir as homedir27 } from "os";
49009
- import { basename, join as join27 } from "path";
49480
+ import { homedir as homedir28 } from "os";
49481
+ import { basename, join as join28 } from "path";
49010
49482
  function slugForPath(absPath) {
49011
49483
  return absPath.replace(/[/.]/g, "-");
49012
49484
  }
@@ -49015,7 +49487,7 @@ function transcriptPathFor(cwd, sessionUuid) {
49015
49487
  try {
49016
49488
  real = realpathSync(cwd);
49017
49489
  } catch {}
49018
- return join27(PROJECTS_DIR, slugForPath(real), `${sessionUuid}.jsonl`);
49490
+ return join28(PROJECTS_DIR, slugForPath(real), `${sessionUuid}.jsonl`);
49019
49491
  }
49020
49492
  function isAgentSession(row) {
49021
49493
  return row.entrypoint !== undefined && row.entrypoint !== "cli";
@@ -49062,7 +49534,7 @@ function projectDirs() {
49062
49534
  }
49063
49535
  }
49064
49536
  function sessionsIn(dirName) {
49065
- const dir = join27(PROJECTS_DIR, dirName);
49537
+ const dir = join28(PROJECTS_DIR, dirName);
49066
49538
  let names;
49067
49539
  try {
49068
49540
  names = readdirSync4(dir).filter((n) => n.endsWith(".jsonl"));
@@ -49071,7 +49543,7 @@ function sessionsIn(dirName) {
49071
49543
  }
49072
49544
  const rows = [];
49073
49545
  for (const n of names) {
49074
- const file2 = join27(dir, n);
49546
+ const file2 = join28(dir, n);
49075
49547
  try {
49076
49548
  const st = statSync5(file2);
49077
49549
  if (st.size === 0)
@@ -49430,7 +49902,7 @@ function findLatestSessionId(cwd = process.cwd(), sinceMs = 0) {
49430
49902
  }
49431
49903
  var ENTRYPOINT_BYTES = 8192, PROJECTS_DIR, ACTIVE_WINDOW_MS = 120000, HEAD_BYTES, TAIL_BYTES, HARNESS_ENVELOPES, DEEP_TAIL_BYTES, RECENT_AI_TURNS = 5, RECENT_USER_TURNS = 1;
49432
49904
  var init_session_discovery = __esm(() => {
49433
- PROJECTS_DIR = join27(homedir27(), ".claude", "projects");
49905
+ PROJECTS_DIR = join28(homedir28(), ".claude", "projects");
49434
49906
  HEAD_BYTES = 64 * 1024;
49435
49907
  TAIL_BYTES = 128 * 1024;
49436
49908
  HARNESS_ENVELOPES = [
@@ -49465,19 +49937,19 @@ function newStdioDecoder() {
49465
49937
  var init_stdio_decode = () => {};
49466
49938
 
49467
49939
  // src/team-stats.ts
49468
- import { existsSync as existsSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
49469
- import { join as join28 } from "path";
49940
+ import { existsSync as existsSync20, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "fs";
49941
+ import { join as join29 } from "path";
49470
49942
  function statsDir(sessionPath) {
49471
- return join28(sessionPath, "stats");
49943
+ return join29(sessionPath, "stats");
49472
49944
  }
49473
49945
  function tokenFileFor(sessionPath, anonId) {
49474
- return join28(statsDir(sessionPath), `${anonId}.json`);
49946
+ return join29(statsDir(sessionPath), `${anonId}.json`);
49475
49947
  }
49476
49948
  function readTokenStatsAt(path) {
49477
- if (!existsSync19(path))
49949
+ if (!existsSync20(path))
49478
49950
  return null;
49479
49951
  try {
49480
- return JSON.parse(readFileSync18(path, "utf-8"));
49952
+ return JSON.parse(readFileSync19(path, "utf-8"));
49481
49953
  } catch {
49482
49954
  return null;
49483
49955
  }
@@ -49628,7 +50100,7 @@ ${segs.join(" \xB7 ")}`;
49628
50100
  }
49629
50101
  function writeStatusFile(sessionPath, manifest, status, opts) {
49630
50102
  try {
49631
- writeFileSync10(join28(sessionPath, "status.txt"), `${renderTeamStats(sessionPath, manifest, status, opts)}
50103
+ writeFileSync11(join29(sessionPath, "status.txt"), `${renderTeamStats(sessionPath, manifest, status, opts)}
49632
50104
  `, "utf-8");
49633
50105
  } catch {}
49634
50106
  }
@@ -49663,13 +50135,13 @@ __export(exports_team_orchestrator, {
49663
50135
  import { spawn as spawn2 } from "child_process";
49664
50136
  import {
49665
50137
  createWriteStream,
49666
- existsSync as existsSync20,
49667
- mkdirSync as mkdirSync11,
49668
- readFileSync as readFileSync19,
50138
+ existsSync as existsSync21,
50139
+ mkdirSync as mkdirSync12,
50140
+ readFileSync as readFileSync20,
49669
50141
  readdirSync as readdirSync5,
49670
- writeFileSync as writeFileSync11
50142
+ writeFileSync as writeFileSync12
49671
50143
  } from "fs";
49672
- import { basename as basename2, join as join29, resolve as resolve3 } from "path";
50144
+ import { basename as basename2, join as join30, resolve as resolve3 } from "path";
49673
50145
  function resolveCaptureMode(explicit, env = process.env) {
49674
50146
  if (explicit)
49675
50147
  return explicit;
@@ -49787,7 +50259,7 @@ function persistErrorLog(errorLogPath, header, stderr, stdoutTail) {
49787
50259
  parts.push("--- stderr ---", stderr.trim() ? redactSecrets(stderr) : "(empty)", "");
49788
50260
  parts.push("--- stdout (tail) ---", stdoutTail.trim() ? redactSecrets(stdoutTail) : "(empty)", "");
49789
50261
  try {
49790
- writeFileSync11(errorLogPath, parts.join(`
50262
+ writeFileSync12(errorLogPath, parts.join(`
49791
50263
  `), "utf-8");
49792
50264
  } catch {}
49793
50265
  }
@@ -49805,10 +50277,10 @@ function readTeamInputFile(inputPath) {
49805
50277
  if (!resolved.startsWith(`${cwd}/`) && resolved !== cwd) {
49806
50278
  throw new Error(`Input file must be within current directory: ${inputPath}`);
49807
50279
  }
49808
- if (!existsSync20(resolved)) {
50280
+ if (!existsSync21(resolved)) {
49809
50281
  throw new Error(`Input file not found: ${resolved}`);
49810
50282
  }
49811
- const text = readFileSync19(resolved, "utf-8");
50283
+ const text = readFileSync20(resolved, "utf-8");
49812
50284
  if (text.trim().length === 0) {
49813
50285
  throw new Error(`Input file is empty: ${resolved}`);
49814
50286
  }
@@ -49818,14 +50290,14 @@ function setupSession(sessionPath, models, input) {
49818
50290
  if (models.length === 0) {
49819
50291
  throw new Error("At least one model is required");
49820
50292
  }
49821
- if (existsSync20(join29(sessionPath, "manifest.json"))) {
50293
+ if (existsSync21(join30(sessionPath, "manifest.json"))) {
49822
50294
  throw new Error(`Session already exists at ${sessionPath}. Use a new directory path or delete the existing session first.`);
49823
50295
  }
49824
- mkdirSync11(join29(sessionPath, "work"), { recursive: true });
49825
- mkdirSync11(join29(sessionPath, "errors"), { recursive: true });
50296
+ mkdirSync12(join30(sessionPath, "work"), { recursive: true });
50297
+ mkdirSync12(join30(sessionPath, "errors"), { recursive: true });
49826
50298
  if (input !== undefined) {
49827
- writeFileSync11(join29(sessionPath, "input.md"), input, "utf-8");
49828
- } else if (!existsSync20(join29(sessionPath, "input.md"))) {
50299
+ writeFileSync12(join30(sessionPath, "input.md"), input, "utf-8");
50300
+ } else if (!existsSync21(join30(sessionPath, "input.md"))) {
49829
50301
  throw new Error(`No input.md found at ${sessionPath} and no input provided`);
49830
50302
  }
49831
50303
  const ids = models.map((_, i) => String(i + 1).padStart(2, "0"));
@@ -49842,9 +50314,9 @@ function setupSession(sessionPath, models, input) {
49842
50314
  model: models[i],
49843
50315
  assignedAt: now2
49844
50316
  };
49845
- mkdirSync11(join29(sessionPath, "work", anonId), { recursive: true });
50317
+ mkdirSync12(join30(sessionPath, "work", anonId), { recursive: true });
49846
50318
  }
49847
- writeFileSync11(join29(sessionPath, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
50319
+ writeFileSync12(join30(sessionPath, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
49848
50320
  const status = {
49849
50321
  startedAt: now2,
49850
50322
  models: Object.fromEntries(Object.keys(manifest.models).map((id) => [
@@ -49858,7 +50330,7 @@ function setupSession(sessionPath, models, input) {
49858
50330
  }
49859
50331
  ]))
49860
50332
  };
49861
- writeFileSync11(join29(sessionPath, "status.json"), JSON.stringify(status, null, 2), "utf-8");
50333
+ writeFileSync12(join30(sessionPath, "status.json"), JSON.stringify(status, null, 2), "utf-8");
49862
50334
  return manifest;
49863
50335
  }
49864
50336
  function assertValidRequirePattern(pattern) {
@@ -49875,22 +50347,22 @@ function readFullOutputIfNeeded(opts) {
49875
50347
  if (crashed || !requirePattern || outputSize <= STDOUT_TAIL_LIMIT)
49876
50348
  return;
49877
50349
  try {
49878
- return readFileSync19(outputPath, "utf-8");
50350
+ return readFileSync20(outputPath, "utf-8");
49879
50351
  } catch {
49880
50352
  return;
49881
50353
  }
49882
50354
  }
49883
50355
  async function startModels(sessionPath, opts = {}) {
49884
50356
  assertValidRequirePattern(opts.requirePattern);
49885
- const manifest = JSON.parse(readFileSync19(join29(sessionPath, "manifest.json"), "utf-8"));
49886
- const statusPath = join29(sessionPath, "status.json");
49887
- const inputPath = join29(sessionPath, "input.md");
49888
- const inputContent = readFileSync19(inputPath, "utf-8");
50357
+ const manifest = JSON.parse(readFileSync20(join30(sessionPath, "manifest.json"), "utf-8"));
50358
+ const statusPath = join30(sessionPath, "status.json");
50359
+ const inputPath = join30(sessionPath, "input.md");
50360
+ const inputContent = readFileSync20(inputPath, "utf-8");
49889
50361
  const spawnPlan = await (opts.spawnPlanner ?? prehydrateCredentialsForSpawn)(Object.values(manifest.models).map((m) => m.model));
49890
- const statusCache = JSON.parse(readFileSync19(statusPath, "utf-8"));
50362
+ const statusCache = JSON.parse(readFileSync20(statusPath, "utf-8"));
49891
50363
  function updateModelStatus(id, update) {
49892
50364
  statusCache.models[id] = { ...statusCache.models[id], ...update };
49893
- writeFileSync11(statusPath, JSON.stringify(statusCache, null, 2), "utf-8");
50365
+ writeFileSync12(statusPath, JSON.stringify(statusCache, null, 2), "utf-8");
49894
50366
  }
49895
50367
  const minOutputBytes = opts.minOutputBytes ?? DEFAULT_MIN_OUTPUT_BYTES;
49896
50368
  const requirePattern = opts.requirePattern;
@@ -49923,7 +50395,7 @@ async function startModels(sessionPath, opts = {}) {
49923
50395
  persistErrorLog(rt.errorLogPath, `RECOVERED: ${note}`, stderr, stdoutTail);
49924
50396
  opts.onStatusChange?.(id, statusCache.models[id]);
49925
50397
  }
49926
- mkdirSync11(statsDir(sessionPath), { recursive: true });
50398
+ mkdirSync12(statsDir(sessionPath), { recursive: true });
49927
50399
  const processes = new Map;
49928
50400
  const runtimes = new Map;
49929
50401
  const cancelledSlots = new Set;
@@ -49936,9 +50408,9 @@ async function startModels(sessionPath, opts = {}) {
49936
50408
  process.on("SIGINT", sigintHandler);
49937
50409
  const completionPromises = [];
49938
50410
  for (const [anonId, entry] of Object.entries(manifest.models)) {
49939
- const outputPath = join29(sessionPath, `response-${anonId}.md`);
49940
- const errorLogPath = join29(sessionPath, "errors", `${anonId}.log`);
49941
- const upstreamErrorLogPath = join29(sessionPath, "errors", `${anonId}-upstream.jsonl`);
50411
+ const outputPath = join30(sessionPath, `response-${anonId}.md`);
50412
+ const errorLogPath = join30(sessionPath, "errors", `${anonId}.log`);
50413
+ const upstreamErrorLogPath = join30(sessionPath, "errors", `${anonId}-upstream.jsonl`);
49942
50414
  const spawnModel = spawnPlan.pinned.get(entry.model) ?? entry.model;
49943
50415
  const args = [
49944
50416
  "--model",
@@ -50078,7 +50550,7 @@ async function startModels(sessionPath, opts = {}) {
50078
50550
  stderrSnippet: stderr ? redactSecrets(stderr).slice(-2000) : undefined,
50079
50551
  stdoutSnippet: stdoutTail ? redactSecrets(stdoutTail).slice(-2000) : undefined,
50080
50552
  errorLogPath,
50081
- upstreamErrorLogPath: existsSync20(upstreamErrorLogPath) ? upstreamErrorLogPath : undefined,
50553
+ upstreamErrorLogPath: existsSync21(upstreamErrorLogPath) ? upstreamErrorLogPath : undefined,
50082
50554
  workDir: sessionPath
50083
50555
  }
50084
50556
  });
@@ -50098,7 +50570,7 @@ async function startModels(sessionPath, opts = {}) {
50098
50570
  proc.on("exit", (code) => {
50099
50571
  const timedOut = statusCache.models[anonId]?.state === "TIMEOUT";
50100
50572
  if (!timedOut && meaningfulStderr(stderr)) {
50101
- writeFileSync11(errorLogPath, redactSecrets(stderr), "utf-8");
50573
+ writeFileSync12(errorLogPath, redactSecrets(stderr), "utf-8");
50102
50574
  }
50103
50575
  exitCode = code;
50104
50576
  if (outputStream.destroyed) {
@@ -50178,23 +50650,23 @@ async function judgeResponses(sessionPath, opts = {}) {
50178
50650
  const responses = {};
50179
50651
  for (const file2 of responseFiles) {
50180
50652
  const id = file2.replace(/^response-/, "").replace(/\.md$/, "");
50181
- responses[id] = readFileSync19(join29(sessionPath, file2), "utf-8");
50653
+ responses[id] = readFileSync20(join30(sessionPath, file2), "utf-8");
50182
50654
  }
50183
- const input = readFileSync19(join29(sessionPath, "input.md"), "utf-8");
50655
+ const input = readFileSync20(join30(sessionPath, "input.md"), "utf-8");
50184
50656
  const judgePrompt = buildJudgePrompt(input, responses);
50185
- writeFileSync11(join29(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
50657
+ writeFileSync12(join30(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
50186
50658
  const judgeModels = opts.judges ?? getDefaultJudgeModels(sessionPath);
50187
- const judgePath = join29(sessionPath, "judging");
50188
- mkdirSync11(judgePath, { recursive: true });
50659
+ const judgePath = join30(sessionPath, "judging");
50660
+ mkdirSync12(judgePath, { recursive: true });
50189
50661
  setupSession(judgePath, judgeModels, judgePrompt);
50190
50662
  await runModels(judgePath, { claudeFlags: opts.claudeFlags });
50191
50663
  const votes = parseJudgeVotes(judgePath, Object.keys(responses));
50192
50664
  const verdict = aggregateVerdict(votes, Object.keys(responses));
50193
- writeFileSync11(join29(sessionPath, "verdict.md"), formatVerdict(verdict, sessionPath), "utf-8");
50665
+ writeFileSync12(join30(sessionPath, "verdict.md"), formatVerdict(verdict, sessionPath), "utf-8");
50194
50666
  return verdict;
50195
50667
  }
50196
50668
  function getStatus(sessionPath) {
50197
- return JSON.parse(readFileSync19(join29(sessionPath, "status.json"), "utf-8"));
50669
+ return JSON.parse(readFileSync20(join30(sessionPath, "status.json"), "utf-8"));
50198
50670
  }
50199
50671
  function fisherYatesShuffle(arr) {
50200
50672
  for (let i = arr.length - 1;i > 0; i--) {
@@ -50204,7 +50676,7 @@ function fisherYatesShuffle(arr) {
50204
50676
  return arr;
50205
50677
  }
50206
50678
  function getDefaultJudgeModels(sessionPath) {
50207
- const manifest = JSON.parse(readFileSync19(join29(sessionPath, "manifest.json"), "utf-8"));
50679
+ const manifest = JSON.parse(readFileSync20(join30(sessionPath, "manifest.json"), "utf-8"));
50208
50680
  return Object.values(manifest.models).map((e) => e.model);
50209
50681
  }
50210
50682
  function buildJudgePrompt(input, responses) {
@@ -50267,7 +50739,7 @@ function parseJudgeVotes(judgePath, responseIds) {
50267
50739
  const judgeId = file2.replace(/^response-/, "").replace(/\.md$/, "");
50268
50740
  let content;
50269
50741
  try {
50270
- content = readFileSync19(join29(judgePath, file2), "utf-8");
50742
+ content = readFileSync20(join30(judgePath, file2), "utf-8");
50271
50743
  } catch {
50272
50744
  continue;
50273
50745
  }
@@ -50319,7 +50791,7 @@ function aggregateVerdict(votes, responseIds) {
50319
50791
  function formatVerdict(verdict, sessionPath) {
50320
50792
  let manifest = null;
50321
50793
  try {
50322
- manifest = JSON.parse(readFileSync19(join29(sessionPath, "manifest.json"), "utf-8"));
50794
+ manifest = JSON.parse(readFileSync20(join30(sessionPath, "manifest.json"), "utf-8"));
50323
50795
  } catch {}
50324
50796
  let output = `# Team Verdict
50325
50797
 
@@ -50373,15 +50845,15 @@ import {
50373
50845
  appendFileSync as appendFileSync6,
50374
50846
  closeSync as closeSync7,
50375
50847
  createWriteStream as createWriteStream2,
50376
- mkdirSync as mkdirSync12,
50848
+ mkdirSync as mkdirSync13,
50377
50849
  openSync as openSync7,
50378
- readFileSync as readFileSync20,
50850
+ readFileSync as readFileSync21,
50379
50851
  readSync as readSync3,
50380
50852
  statSync as statSync6,
50381
- writeFileSync as writeFileSync12
50853
+ writeFileSync as writeFileSync13
50382
50854
  } from "fs";
50383
- import { homedir as homedir28 } from "os";
50384
- import { join as join30, resolve as resolve4, sep } from "path";
50855
+ import { homedir as homedir29 } from "os";
50856
+ import { join as join31, resolve as resolve4, sep } from "path";
50385
50857
  import { StringDecoder as StringDecoder2 } from "string_decoder";
50386
50858
  function buildChannelSpawnArgs(opts) {
50387
50859
  return [
@@ -50456,7 +50928,7 @@ function readJsonObject(path, maxBytes) {
50456
50928
  try {
50457
50929
  if (fileSize(path) > maxBytes)
50458
50930
  return null;
50459
- const parsed = JSON.parse(readFileSync20(path, "utf-8"));
50931
+ const parsed = JSON.parse(readFileSync21(path, "utf-8"));
50460
50932
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
50461
50933
  return null;
50462
50934
  return parsed;
@@ -50472,7 +50944,7 @@ function dropLeadingFragment(tail) {
50472
50944
  return firstBreak === -1 ? tail.text : tail.text.slice(firstBreak + 1);
50473
50945
  }
50474
50946
  function diskAccounting(sessionDir) {
50475
- const stats = readTokenStatsAt(join30(sessionDir, "tokens.json"));
50947
+ const stats = readTokenStatsAt(join31(sessionDir, "tokens.json"));
50476
50948
  return {
50477
50949
  tokensUsed: (stats?.total_tokens ?? 0) || (stats?.input_tokens ?? 0) + (stats?.output_tokens ?? 0),
50478
50950
  costUsd: stats?.total_cost ?? 0,
@@ -50512,7 +50984,7 @@ class SessionManager {
50512
50984
  this.maxSessions = options?.maxSessions ?? DEFAULT_MAX_SESSIONS;
50513
50985
  this.scrollbackCapacity = options?.scrollbackCapacity ?? DEFAULT_SCROLLBACK;
50514
50986
  this.terminalRetentionMs = options?.terminalRetentionMs ?? TERMINAL_RETENTION_MS;
50515
- this.sessionsDir = options?.sessionsDir ?? process.env.CLAUDISH_SESSIONS_DIR ?? join30(homedir28(), ".claudish", "sessions");
50987
+ this.sessionsDir = options?.sessionsDir ?? process.env.CLAUDISH_SESSIONS_DIR ?? join31(homedir29(), ".claudish", "sessions");
50516
50988
  this.stallSeconds = options?.stallSeconds;
50517
50989
  this.onStateChange = options?.onStateChange;
50518
50990
  }
@@ -50528,19 +51000,19 @@ class SessionManager {
50528
51000
  const claudeSessionId = randomUUID4();
50529
51001
  const timeout = Math.min(opts.timeoutSeconds ?? DEFAULT_TIMEOUT, MAX_TIMEOUT);
50530
51002
  const startedAt = new Date().toISOString();
50531
- const sessionDir = opts.sessionDir ?? join30(this.sessionsDir, sessionId2);
50532
- mkdirSync12(sessionDir, { recursive: true });
51003
+ const sessionDir = opts.sessionDir ?? join31(this.sessionsDir, sessionId2);
51004
+ mkdirSync13(sessionDir, { recursive: true });
50533
51005
  if (opts.prompt) {
50534
- writeFileSync12(join30(sessionDir, "prompt.md"), opts.prompt, "utf-8");
51006
+ writeFileSync13(join31(sessionDir, "prompt.md"), opts.prompt, "utf-8");
50535
51007
  }
50536
51008
  const args = buildChannelSpawnArgs({
50537
51009
  model: opts.spawnModel ?? opts.model,
50538
51010
  claudeSessionId,
50539
51011
  claudishFlags: opts.claudishFlags
50540
51012
  });
50541
- const tokenFile = opts.tokenFile ?? join30(sessionDir, "tokens.json");
50542
- const eventLogPath = join30(sessionDir, "events.jsonl");
50543
- const upstreamErrorLogPath = join30(sessionDir, "upstream-errors.jsonl");
51013
+ const tokenFile = opts.tokenFile ?? join31(sessionDir, "tokens.json");
51014
+ const eventLogPath = join31(sessionDir, "events.jsonl");
51015
+ const upstreamErrorLogPath = join31(sessionDir, "upstream-errors.jsonl");
50544
51016
  const cwd = opts.cwd ?? process.cwd();
50545
51017
  const spawnTarget = resolveClaudishSpawn();
50546
51018
  const proc = spawn3(spawnTarget.command, [...spawnTarget.prefixArgs, ...args], {
@@ -50555,7 +51027,7 @@ class SessionManager {
50555
51027
  }
50556
51028
  });
50557
51029
  const scrollback = new ScrollbackBuffer(this.scrollbackCapacity);
50558
- const outputLogStream = createWriteStream2(join30(sessionDir, "output.log"));
51030
+ const outputLogStream = createWriteStream2(join31(sessionDir, "output.log"));
50559
51031
  const entry = {
50560
51032
  info: {
50561
51033
  sessionId: sessionId2,
@@ -50847,7 +51319,7 @@ class SessionManager {
50847
51319
  return null;
50848
51320
  const root = resolve4(this.sessionsDir);
50849
51321
  const dir = resolve4(root, sessionId2);
50850
- if (dir !== join30(root, sessionId2))
51322
+ if (dir !== join31(root, sessionId2))
50851
51323
  return null;
50852
51324
  if (!dir.startsWith(root + sep))
50853
51325
  return null;
@@ -50866,7 +51338,7 @@ class SessionManager {
50866
51338
  } catch {
50867
51339
  return null;
50868
51340
  }
50869
- const meta3 = readJsonObject(join30(sessionDir, "meta.json"), META_READ_LIMIT);
51341
+ const meta3 = readJsonObject(join31(sessionDir, "meta.json"), META_READ_LIMIT);
50870
51342
  const partial2 = meta3 === null;
50871
51343
  const measured = diskAccounting(sessionDir);
50872
51344
  const startedAt = metaString(meta3?.startedAt) ?? new Date(dirMtimeMs).toISOString();
@@ -50896,7 +51368,7 @@ class SessionManager {
50896
51368
  };
50897
51369
  }
50898
51370
  diskOutput(record4, tailLines) {
50899
- const tail = readTailText(join30(record4.sessionDir, "output.log"), OUTPUT_TAIL_BYTES);
51371
+ const tail = readTailText(join31(record4.sessionDir, "output.log"), OUTPUT_TAIL_BYTES);
50900
51372
  const buffer = new ScrollbackBuffer(this.scrollbackCapacity);
50901
51373
  if (tail?.text)
50902
51374
  buffer.append(dropLeadingFragment(tail));
@@ -50915,9 +51387,9 @@ class SessionManager {
50915
51387
  }
50916
51388
  diskDiagnostics(record4, limit) {
50917
51389
  const { sessionDir, info } = record4;
50918
- const eventLogPath = join30(sessionDir, "events.jsonl");
50919
- const upstreamErrorLogPath = join30(sessionDir, "upstream-errors.jsonl");
50920
- const outputLogPath = join30(sessionDir, "output.log");
51390
+ const eventLogPath = join31(sessionDir, "events.jsonl");
51391
+ const upstreamErrorLogPath = join31(sessionDir, "upstream-errors.jsonl");
51392
+ const outputLogPath = join31(sessionDir, "output.log");
50921
51393
  const events = readTailLines(eventLogPath, EVENT_TAIL_BYTES);
50922
51394
  const outputTail = readTailText(outputLogPath, OUTPUT_TAIL_BYTES);
50923
51395
  return {
@@ -50959,7 +51431,7 @@ class SessionManager {
50959
51431
  };
50960
51432
  }
50961
51433
  diskStderrForDiagnostics(record4) {
50962
- const tail = readTailText(join30(record4.sessionDir, "stderr.log"), STDERR_READ_BYTES);
51434
+ const tail = readTailText(join31(record4.sessionDir, "stderr.log"), STDERR_READ_BYTES);
50963
51435
  const raw = tail?.text ?? "";
50964
51436
  const filtered = record4.info.status === "completed";
50965
51437
  const source = filtered ? meaningfulStderr(raw) : raw;
@@ -51135,11 +51607,11 @@ ${STDERR_TRUNCATION_MARKER} ${STDERR_SIDE_LIMIT} bytes per end \u2026
51135
51607
  entry.outputLogStream?.end();
51136
51608
  entry.outputLogStream = null;
51137
51609
  if (entry.stderr) {
51138
- writeFileSync12(join30(entry.sessionDir, "stderr.log"), redactSecrets(entry.stderr), "utf-8");
51610
+ writeFileSync13(join31(entry.sessionDir, "stderr.log"), redactSecrets(entry.stderr), "utf-8");
51139
51611
  }
51140
51612
  this.refreshAccounting(entry);
51141
51613
  entry.info.claudeSessionId = entry.reducer.claudeSessionId ?? entry.info.claudeSessionId;
51142
- writeFileSync12(join30(entry.sessionDir, "meta.json"), JSON.stringify(entry.info, null, 2), "utf-8");
51614
+ writeFileSync13(join31(entry.sessionDir, "meta.json"), JSON.stringify(entry.info, null, 2), "utf-8");
51143
51615
  }
51144
51616
  scheduleEviction(entry) {
51145
51617
  if (entry.evictHandle)
@@ -51199,7 +51671,7 @@ ${STDERR_TRUNCATION_MARKER} ${STDERR_SIDE_LIMIT} bytes per end \u2026
51199
51671
  return { state: "completed", content: "" };
51200
51672
  }
51201
51673
  refreshAccounting(entry) {
51202
- const stats = readTokenStatsAt(join30(entry.sessionDir, "tokens.json"));
51674
+ const stats = readTokenStatsAt(join31(entry.sessionDir, "tokens.json"));
51203
51675
  const fileTokens = (stats?.total_tokens ?? 0) || (stats?.input_tokens ?? 0) + (stats?.output_tokens ?? 0);
51204
51676
  entry.info.tokensUsed = fileTokens || entry.reducer.tokens;
51205
51677
  entry.info.costUsd = stats?.total_cost ?? 0;
@@ -51390,306 +51862,6 @@ var init_progress_heartbeat = __esm(() => {
51390
51862
  });
51391
51863
  });
51392
51864
 
51393
- // src/providers/cache-ttl.ts
51394
- var FIREBASE_CACHE_TTL_HOURS = 24, FIREBASE_CACHE_TTL_MS;
51395
- var init_cache_ttl = __esm(() => {
51396
- FIREBASE_CACHE_TTL_MS = FIREBASE_CACHE_TTL_HOURS * 60 * 60 * 1000;
51397
- });
51398
-
51399
- // src/model-loader.ts
51400
- import { existsSync as existsSync21, mkdirSync as mkdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "fs";
51401
- import { homedir as homedir29 } from "os";
51402
- import { join as join31 } from "path";
51403
- function groupRecommendedModels(entries) {
51404
- const byId = new Map;
51405
- const categoryOrder = new Map;
51406
- for (const entry of entries) {
51407
- const list = byId.get(entry.id);
51408
- if (list)
51409
- list.push(entry);
51410
- else
51411
- byId.set(entry.id, [entry]);
51412
- if (!categoryOrder.has(entry.category))
51413
- categoryOrder.set(entry.category, categoryOrder.size);
51414
- }
51415
- const flagship = [];
51416
- const fast = [];
51417
- for (const [id, members] of byId.entries()) {
51418
- const primary = members.find((m) => m.category !== "subscription") ?? members[0];
51419
- const subscriptions = members.filter((m) => m.category === "subscription");
51420
- const bucket = primary.category === "programming" || primary.category === "vision" || primary.category === "reasoning" ? "flagship" : "fast";
51421
- const group = { id, primary, subscriptions, bucket };
51422
- if (bucket === "flagship")
51423
- flagship.push(group);
51424
- else
51425
- fast.push(group);
51426
- }
51427
- const byCuratedPriorityThenFreshness = (a, b) => {
51428
- const aCat = categoryOrder.get(a.primary.category) ?? Number.MAX_SAFE_INTEGER;
51429
- const bCat = categoryOrder.get(b.primary.category) ?? Number.MAX_SAFE_INTEGER;
51430
- if (aCat !== bCat)
51431
- return aCat - bCat;
51432
- if (a.primary.priority !== b.primary.priority)
51433
- return a.primary.priority - b.primary.priority;
51434
- return compareByReleaseDateDesc(a.primary, b.primary);
51435
- };
51436
- flagship.sort(byCuratedPriorityThenFreshness);
51437
- fast.sort(byCuratedPriorityThenFreshness);
51438
- return { flagship, fast };
51439
- }
51440
- function collectRoutingPrefixes(group, getNativePrefix) {
51441
- const slug = (group.primary.provider || "").toLowerCase();
51442
- const native = getNativePrefix(slug);
51443
- const seen = new Set;
51444
- const out = [];
51445
- if (native) {
51446
- out.push(native);
51447
- seen.add(native);
51448
- }
51449
- for (const sub of group.subscriptions) {
51450
- const p = sub.subscription?.prefix;
51451
- if (!p || seen.has(p))
51452
- continue;
51453
- seen.add(p);
51454
- out.push(p);
51455
- }
51456
- return out;
51457
- }
51458
- function parsePriceAvg(s) {
51459
- if (!s || s === "N/A")
51460
- return Number.POSITIVE_INFINITY;
51461
- if (s === "FREE")
51462
- return 0;
51463
- const m = s.match(/\$([\d.]+)/);
51464
- return m ? Number.parseFloat(m[1]) : Number.POSITIVE_INFINITY;
51465
- }
51466
- function parseCtx(s) {
51467
- if (!s || s === "N/A")
51468
- return 0;
51469
- const upper = s.toUpperCase();
51470
- if (upper.includes("M"))
51471
- return Number.parseFloat(upper) * 1e6;
51472
- if (upper.includes("K"))
51473
- return Number.parseFloat(upper) * 1000;
51474
- return Number.parseInt(s, 10) || 0;
51475
- }
51476
- function normalizePricingDisplay(raw) {
51477
- const pricing = raw || "N/A";
51478
- if (pricing.includes("-1000000"))
51479
- return "varies";
51480
- if (pricing === "$0.00/1M" || pricing === "FREE")
51481
- return "FREE";
51482
- return pricing;
51483
- }
51484
- function formatListingPrice(entry, opts) {
51485
- const rate = normalizePricingDisplay(entry.pricing?.average);
51486
- if (rate !== "N/A")
51487
- return rate;
51488
- const plan = entry.subscription?.plan;
51489
- if (!plan)
51490
- return "N/A";
51491
- return opts?.compact ? "SUB" : `SUB (${plan})`;
51492
- }
51493
- function computeQuickPicks(primaries) {
51494
- if (primaries.length === 0) {
51495
- return {
51496
- budget: null,
51497
- largeContext: null,
51498
- mostCapable: null,
51499
- visionCoding: null,
51500
- agentic: null
51501
- };
51502
- }
51503
- const priced = primaries.filter((m) => {
51504
- const p = parsePriceAvg(m.pricing?.average);
51505
- return p > 0 && p !== Number.POSITIVE_INFINITY;
51506
- }).sort((a, b) => parsePriceAvg(a.pricing?.average) - parsePriceAvg(b.pricing?.average));
51507
- const budget = priced[0] ?? null;
51508
- const byCtx = [...primaries].sort((a, b) => parseCtx(b.context) - parseCtx(a.context));
51509
- const largeContext = byCtx[0] ?? null;
51510
- const byPrice = [...primaries].sort((a, b) => parsePriceAvg(b.pricing?.average) - parsePriceAvg(a.pricing?.average));
51511
- const mostCapable = byPrice.find((m) => parsePriceAvg(m.pricing?.average) !== Number.POSITIVE_INFINITY) ?? null;
51512
- const visionCoding = primaries.find((m) => m.supportsVision === true && m.id !== budget?.id && m.id !== mostCapable?.id) ?? null;
51513
- const agentic = primaries.find((m) => m.supportsReasoning === true && m.id !== mostCapable?.id) ?? null;
51514
- return { budget, largeContext, mostCapable, visionCoding, agentic };
51515
- }
51516
- async function getRecommendedModels(opts = {}) {
51517
- const { forceRefresh = false } = opts;
51518
- if (!forceRefresh && _cachedRecommendedModels) {
51519
- return _cachedRecommendedModels;
51520
- }
51521
- if (!forceRefresh && existsSync21(RECOMMENDED_MODELS_CACHE_PATH)) {
51522
- try {
51523
- const cacheData = JSON.parse(readFileSync21(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
51524
- if (cacheData.models && cacheData.models.length > 0 && isFreshEnough(cacheData)) {
51525
- _cachedRecommendedModels = cacheData;
51526
- return cacheData;
51527
- }
51528
- } catch {}
51529
- }
51530
- try {
51531
- const response = await fetch(FIREBASE_RECOMMENDED_URL, {
51532
- signal: AbortSignal.timeout(RECOMMENDED_FETCH_TIMEOUT_MS)
51533
- });
51534
- if (response.ok) {
51535
- const data = await response.json();
51536
- if (data.models && data.models.length > 0) {
51537
- _cachedRecommendedModels = data;
51538
- try {
51539
- const cacheDir = join31(homedir29(), ".claudish");
51540
- mkdirSync13(cacheDir, { recursive: true });
51541
- writeFileSync13(RECOMMENDED_MODELS_CACHE_PATH, JSON.stringify(data), "utf-8");
51542
- } catch {}
51543
- return data;
51544
- }
51545
- }
51546
- } catch {}
51547
- throw new Error("Unable to load recommended models: Firebase unreachable and no local cache. " + "Check connectivity.");
51548
- }
51549
- function getRecommendedModelsSync() {
51550
- if (_cachedRecommendedModels)
51551
- return _cachedRecommendedModels;
51552
- if (existsSync21(RECOMMENDED_MODELS_CACHE_PATH)) {
51553
- try {
51554
- const cacheData = JSON.parse(readFileSync21(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
51555
- if (cacheData.models && cacheData.models.length > 0 && isFreshEnough(cacheData)) {
51556
- _cachedRecommendedModels = cacheData;
51557
- return cacheData;
51558
- }
51559
- } catch {}
51560
- }
51561
- return { version: "0", lastUpdated: "", models: [] };
51562
- }
51563
- async function warmRecommendedModels() {
51564
- try {
51565
- return await getRecommendedModels({ forceRefresh: true });
51566
- } catch {
51567
- return null;
51568
- }
51569
- }
51570
- function isFreshEnough(doc2) {
51571
- const generatedAt = doc2.generatedAt;
51572
- if (!generatedAt)
51573
- return true;
51574
- const ageHours = (Date.now() - new Date(generatedAt).getTime()) / (1000 * 60 * 60);
51575
- return ageHours <= FIREBASE_CACHE_TTL_HOURS;
51576
- }
51577
- async function searchModels(query, limit = 50) {
51578
- const url2 = `${FIREBASE_BASE_URL}?search=${encodeURIComponent(query)}&limit=${limit}&status=active`;
51579
- const response = await fetch(url2, {
51580
- signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
51581
- });
51582
- if (!response.ok) {
51583
- throw new Error(`Firebase search returned ${response.status} ${response.statusText}`);
51584
- }
51585
- const data = await response.json();
51586
- return data.models ?? [];
51587
- }
51588
- async function getModelByIdFromFirebase(modelId) {
51589
- const url2 = `${FIREBASE_BASE_URL}?search=${encodeURIComponent(modelId)}&limit=5`;
51590
- const response = await fetch(url2, {
51591
- signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
51592
- });
51593
- if (!response.ok) {
51594
- throw new Error(`Firebase lookup returned ${response.status} ${response.statusText}`);
51595
- }
51596
- const data = await response.json();
51597
- const models = data.models ?? [];
51598
- for (const m of models) {
51599
- if (m.modelId === modelId)
51600
- return m;
51601
- if (m.aliases?.includes(modelId))
51602
- return m;
51603
- }
51604
- return null;
51605
- }
51606
- async function getTop100Models() {
51607
- const url2 = `${FIREBASE_BASE_URL}?catalog=top100`;
51608
- const response = await fetch(url2, {
51609
- signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
51610
- });
51611
- if (!response.ok) {
51612
- throw new Error(`Firebase top100 fetch failed: ${response.status} ${response.statusText}`);
51613
- }
51614
- const data = await response.json();
51615
- return data;
51616
- }
51617
- async function getProviderList() {
51618
- const url2 = `${FIREBASE_BASE_URL}?catalog=providers`;
51619
- const response = await fetch(url2, {
51620
- signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
51621
- });
51622
- if (!response.ok) {
51623
- throw new Error(`Firebase providers fetch failed: ${response.status} ${response.statusText}`);
51624
- }
51625
- const data = await response.json();
51626
- return data.providers ?? [];
51627
- }
51628
- async function getModelsByProvider(provider, limit = 200) {
51629
- const url2 = `${FIREBASE_BASE_URL}?provider=${encodeURIComponent(provider)}&status=active&limit=${limit}`;
51630
- const response = await fetch(url2, {
51631
- signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
51632
- });
51633
- if (!response.ok) {
51634
- throw new Error(`Firebase provider query returned ${response.status} ${response.statusText}`);
51635
- }
51636
- const data = await response.json();
51637
- if (Array.isArray(data))
51638
- return data;
51639
- return data.models ?? [];
51640
- }
51641
- function loadModelInfo() {
51642
- if (_cachedModelInfo) {
51643
- return _cachedModelInfo;
51644
- }
51645
- const data = getRecommendedModelsSync();
51646
- const modelInfo = {};
51647
- for (const model of data.models) {
51648
- modelInfo[model.id] = {
51649
- name: model.name,
51650
- description: model.description,
51651
- priority: model.priority,
51652
- provider: model.provider
51653
- };
51654
- }
51655
- modelInfo.custom = {
51656
- name: "Custom Model",
51657
- description: "Enter any model ID manually",
51658
- priority: 999,
51659
- provider: "Custom"
51660
- };
51661
- _cachedModelInfo = modelInfo;
51662
- return modelInfo;
51663
- }
51664
- function getAvailableModels() {
51665
- if (_cachedModelIds) {
51666
- return _cachedModelIds;
51667
- }
51668
- const data = getRecommendedModelsSync();
51669
- const modelIds = data.models.sort((a, b) => a.priority - b.priority).map((m) => m.id);
51670
- const result = [...modelIds, "custom"];
51671
- _cachedModelIds = result;
51672
- return result;
51673
- }
51674
- var _cachedModelInfo = null, _cachedModelIds = null, _cachedRecommendedModels = null, FIREBASE_BASE_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels", FIREBASE_RECOMMENDED_URL, RECOMMENDED_MODELS_CACHE_PATH, RECOMMENDED_FETCH_TIMEOUT_MS = 5000, SEARCH_FETCH_TIMEOUT_MS = 1e4, FIREBASE_SLUG_TO_PROVIDER_NAME;
51675
- var init_model_loader = __esm(() => {
51676
- init_cache_ttl();
51677
- FIREBASE_RECOMMENDED_URL = `${FIREBASE_BASE_URL}?catalog=recommended`;
51678
- RECOMMENDED_MODELS_CACHE_PATH = join31(homedir29(), ".claudish", "recommended-models-cache.json");
51679
- FIREBASE_SLUG_TO_PROVIDER_NAME = {
51680
- openai: "openai",
51681
- google: "google",
51682
- "x-ai": "x-ai",
51683
- "z-ai": "z-ai",
51684
- moonshotai: "kimi",
51685
- minimax: "minimax",
51686
- qwen: "qwen",
51687
- deepseek: "deepseek",
51688
- mistralai: "mistralai",
51689
- sakana: "sakana"
51690
- };
51691
- });
51692
-
51693
51865
  // src/port-manager.ts
51694
51866
  var exports_port_manager = {};
51695
51867
  __export(exports_port_manager, {
@@ -75450,7 +75622,7 @@ var init_api_key_map = __esm(() => {
75450
75622
  "qwen-payg": { envVar: "DASHSCOPE_API_KEY", aliases: ["QWEN_API_KEY"] },
75451
75623
  ollamacloud: { envVar: "OLLAMA_API_KEY" },
75452
75624
  "opencode-zen": { envVar: "OPENCODE_API_KEY" },
75453
- "opencode-zen-go": { envVar: "OPENCODE_GO_API_KEY", aliases: ["OPENCODE_API_KEY"] },
75625
+ "opencode-zen-go": { envVar: "OPENCODE_GO_API_KEY" },
75454
75626
  vertex: { envVar: "VERTEX_API_KEY", aliases: ["VERTEX_PROJECT"] },
75455
75627
  poe: { envVar: "POE_API_KEY" }
75456
75628
  };
@@ -77023,6 +77195,7 @@ ${h("ENVIRONMENT VARIABLES")}
77023
77195
  ${blue("SAKANA_SUBSCRIPTION_API_KEY")} Sakana Fugu Subscription ${dim4("(sc@; separate subscription key)")}
77024
77196
  ${blue("OLLAMA_API_KEY")} OllamaCloud ${dim4("(oc@, llama@)")}
77025
77197
  ${blue("OPENCODE_API_KEY")} OpenCode Zen ${dim4("(zen@)")}
77198
+ ${blue("OPENCODE_GO_API_KEY")} OpenCode Zen Go plan ${dim4("(zgo@, zengo@; separate plan key)")}
77026
77199
  ${blue("POE_API_KEY")} Poe ${dim4("(poe@)")}
77027
77200
  ${blue("LITELLM_API_KEY")} LiteLLM ${dim4("(litellm@, ll@; needs LITELLM_BASE_URL)")}
77028
77201
  ${blue("VERTEX_API_KEY")} Vertex AI Express ${dim4("(v@)")}