claudish 5.8.0 → 5.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -28728,6 +28728,9 @@ function loadConfig() {
28728
28728
  if (config3.telemetry !== undefined) {
28729
28729
  merged.telemetry = config3.telemetry;
28730
28730
  }
28731
+ if (config3.routing !== undefined) {
28732
+ merged.routing = config3.routing;
28733
+ }
28731
28734
  return merged;
28732
28735
  } catch (error46) {
28733
28736
  console.error(`Warning: Failed to load config, using defaults: ${error46}`);
@@ -28762,11 +28765,15 @@ function loadLocalConfig() {
28762
28765
  try {
28763
28766
  const content = readFileSync5(localPath, "utf-8");
28764
28767
  const config3 = JSON.parse(content);
28765
- return {
28768
+ const local = {
28766
28769
  version: config3.version || DEFAULT_CONFIG.version,
28767
28770
  defaultProfile: config3.defaultProfile || "",
28768
28771
  profiles: config3.profiles || {}
28769
28772
  };
28773
+ if (config3.routing !== undefined) {
28774
+ local.routing = config3.routing;
28775
+ }
28776
+ return local;
28770
28777
  } catch (error46) {
28771
28778
  console.error(`Warning: Failed to load local config: ${error46}`);
28772
28779
  return null;
@@ -29458,7 +29465,7 @@ var getRemoteProviders = () => [
29458
29465
  authScheme: "bearer",
29459
29466
  capabilities: {
29460
29467
  supportsTools: true,
29461
- supportsVision: true,
29468
+ supportsVision: false,
29462
29469
  supportsStreaming: true,
29463
29470
  supportsJsonMode: false,
29464
29471
  supportsReasoning: false
@@ -29941,9 +29948,7 @@ function checkApiKeyForProvider(nativeProvider, modelName) {
29941
29948
  }
29942
29949
  function getAutoRouteHint(modelName, nativeProvider) {
29943
29950
  const hint = PROVIDER_HINT_MAP[nativeProvider];
29944
- const lines = [
29945
- `No credentials found for "${modelName}". Options:`
29946
- ];
29951
+ const lines = [`No credentials found for "${modelName}". Options:`];
29947
29952
  let hasOption = false;
29948
29953
  if (hint?.loginFlag) {
29949
29954
  lines.push(` Run: claudish ${hint.loginFlag} (authenticate via OAuth)`);
@@ -30004,7 +30009,61 @@ function autoRoute(modelName, nativeProvider) {
30004
30009
  }
30005
30010
  return null;
30006
30011
  }
30007
- var API_KEY_ENV_VARS, PROVIDER_HINT_MAP;
30012
+ function hasProviderCredentials(provider) {
30013
+ const keyInfo = API_KEY_ENV_VARS[provider];
30014
+ if (keyInfo?.envVar && process.env[keyInfo.envVar])
30015
+ return true;
30016
+ if (keyInfo?.aliases?.some((a) => process.env[a]))
30017
+ return true;
30018
+ return hasOAuthCredentials(provider);
30019
+ }
30020
+ function getFallbackChain(modelName, nativeProvider) {
30021
+ const routes = [];
30022
+ const litellmBaseUrl = process.env.LITELLM_BASE_URL;
30023
+ if (litellmBaseUrl && process.env.LITELLM_API_KEY) {
30024
+ routes.push({
30025
+ provider: "litellm",
30026
+ modelSpec: `litellm@${modelName}`,
30027
+ displayName: "LiteLLM"
30028
+ });
30029
+ }
30030
+ if (process.env.OPENCODE_API_KEY) {
30031
+ routes.push({
30032
+ provider: "opencode-zen",
30033
+ modelSpec: `zen@${modelName}`,
30034
+ displayName: "OpenCode Zen"
30035
+ });
30036
+ }
30037
+ const sub = SUBSCRIPTION_ALTERNATIVES[nativeProvider];
30038
+ if (sub && hasProviderCredentials(sub.subscriptionProvider)) {
30039
+ const subModelName = sub.modelName || modelName;
30040
+ routes.push({
30041
+ provider: sub.subscriptionProvider,
30042
+ modelSpec: `${sub.prefix}@${subModelName}`,
30043
+ displayName: sub.displayName
30044
+ });
30045
+ }
30046
+ if (nativeProvider !== "unknown" && nativeProvider !== "qwen" && nativeProvider !== "native-anthropic") {
30047
+ if (hasProviderCredentials(nativeProvider)) {
30048
+ const prefix = PROVIDER_TO_PREFIX[nativeProvider] || nativeProvider;
30049
+ routes.push({
30050
+ provider: nativeProvider,
30051
+ modelSpec: `${prefix}@${modelName}`,
30052
+ displayName: DISPLAY_NAMES[nativeProvider] || nativeProvider
30053
+ });
30054
+ }
30055
+ }
30056
+ if (process.env.OPENROUTER_API_KEY) {
30057
+ const resolution = resolveModelNameSync(modelName, "openrouter");
30058
+ routes.push({
30059
+ provider: "openrouter",
30060
+ modelSpec: resolution.resolvedId,
30061
+ displayName: "OpenRouter"
30062
+ });
30063
+ }
30064
+ return routes;
30065
+ }
30066
+ var API_KEY_ENV_VARS, PROVIDER_HINT_MAP, PROVIDER_TO_PREFIX, DISPLAY_NAMES, SUBSCRIPTION_ALTERNATIVES;
30008
30067
  var init_auto_route = __esm(() => {
30009
30068
  init_oauth_registry();
30010
30069
  init_model_catalog_resolver();
@@ -30068,6 +30127,65 @@ var init_auto_route = __esm(() => {
30068
30127
  apiKeyEnvVar: "OLLAMA_API_KEY"
30069
30128
  }
30070
30129
  };
30130
+ PROVIDER_TO_PREFIX = {
30131
+ google: "g",
30132
+ openai: "oai",
30133
+ minimax: "mm",
30134
+ "minimax-coding": "mmc",
30135
+ kimi: "kimi",
30136
+ "kimi-coding": "kc",
30137
+ glm: "glm",
30138
+ "glm-coding": "gc",
30139
+ zai: "zai",
30140
+ ollamacloud: "oc",
30141
+ "opencode-zen": "zen",
30142
+ "opencode-zen-go": "zengo",
30143
+ litellm: "ll",
30144
+ vertex: "v",
30145
+ "gemini-codeassist": "go"
30146
+ };
30147
+ DISPLAY_NAMES = {
30148
+ google: "Gemini",
30149
+ openai: "OpenAI",
30150
+ minimax: "MiniMax",
30151
+ "minimax-coding": "MiniMax Coding",
30152
+ kimi: "Kimi",
30153
+ "kimi-coding": "Kimi Coding",
30154
+ glm: "GLM",
30155
+ "glm-coding": "GLM Coding",
30156
+ zai: "Z.AI",
30157
+ ollamacloud: "OllamaCloud",
30158
+ "opencode-zen": "OpenCode Zen",
30159
+ "opencode-zen-go": "OpenCode Zen Go",
30160
+ litellm: "LiteLLM",
30161
+ openrouter: "OpenRouter"
30162
+ };
30163
+ SUBSCRIPTION_ALTERNATIVES = {
30164
+ kimi: {
30165
+ subscriptionProvider: "kimi-coding",
30166
+ modelName: "kimi-for-coding",
30167
+ prefix: "kc",
30168
+ displayName: "Kimi Coding"
30169
+ },
30170
+ minimax: {
30171
+ subscriptionProvider: "minimax-coding",
30172
+ modelName: null,
30173
+ prefix: "mmc",
30174
+ displayName: "MiniMax Coding"
30175
+ },
30176
+ glm: {
30177
+ subscriptionProvider: "glm-coding",
30178
+ modelName: null,
30179
+ prefix: "gc",
30180
+ displayName: "GLM Coding"
30181
+ },
30182
+ google: {
30183
+ subscriptionProvider: "gemini-codeassist",
30184
+ modelName: null,
30185
+ prefix: "go",
30186
+ displayName: "Gemini Code Assist"
30187
+ }
30188
+ };
30071
30189
  });
30072
30190
 
30073
30191
  // src/providers/provider-resolver.ts
@@ -31844,7 +31962,7 @@ async function fetchGLMCodingModels() {
31844
31962
  return [];
31845
31963
  }
31846
31964
  }
31847
- var __filename4, __dirname4, VERSION = "5.8.0", CACHE_MAX_AGE_DAYS2 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR2, ALL_MODELS_JSON_PATH;
31965
+ var __filename4, __dirname4, VERSION = "5.10.0", CACHE_MAX_AGE_DAYS2 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR2, ALL_MODELS_JSON_PATH;
31848
31966
  var init_cli = __esm(() => {
31849
31967
  init_config();
31850
31968
  init_model_loader();
@@ -34509,7 +34627,9 @@ async function fetchZenGoModels() {
34509
34627
  return [];
34510
34628
  const ZEN_GO_BASE = process.env.OPENCODE_BASE_URL ? process.env.OPENCODE_BASE_URL.replace("/zen", "/zen/go") : "https://opencode.ai/zen/go";
34511
34629
  try {
34512
- const mdevResp = await fetch("https://models.dev/api.json", { signal: AbortSignal.timeout(5000) });
34630
+ const mdevResp = await fetch("https://models.dev/api.json", {
34631
+ signal: AbortSignal.timeout(5000)
34632
+ });
34513
34633
  if (!mdevResp.ok)
34514
34634
  return [];
34515
34635
  const mdevData = await mdevResp.json();
@@ -34521,7 +34641,11 @@ async function fetchZenGoModels() {
34521
34641
  const r = await fetch(`${ZEN_GO_BASE}/v1/chat/completions`, {
34522
34642
  method: "POST",
34523
34643
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
34524
- body: JSON.stringify({ model: modelId, messages: [{ role: "user", content: "hi" }], max_tokens: 1 }),
34644
+ body: JSON.stringify({
34645
+ model: modelId,
34646
+ messages: [{ role: "user", content: "hi" }],
34647
+ max_tokens: 1
34648
+ }),
34525
34649
  signal: AbortSignal.timeout(8000)
34526
34650
  });
34527
34651
  if (!r.ok)
@@ -34982,7 +35106,10 @@ async function getAllModelsForSearch(forceUpdate = false) {
34982
35106
  const litellmBaseUrl = process.env.LITELLM_BASE_URL;
34983
35107
  const litellmApiKey = process.env.LITELLM_API_KEY;
34984
35108
  const fetchEntries = [
34985
- { name: "OpenRouter", promise: fetchAllModels(forceUpdate).then((models) => models.map(toModelInfo)) },
35109
+ {
35110
+ name: "OpenRouter",
35111
+ promise: fetchAllModels(forceUpdate).then((models) => models.map(toModelInfo))
35112
+ },
34986
35113
  { name: "xAI", promise: fetchXAIModels() },
34987
35114
  { name: "Gemini", promise: fetchGeminiModels() },
34988
35115
  { name: "OpenAI", promise: fetchOpenAIModels() },
@@ -34993,7 +35120,10 @@ async function getAllModelsForSearch(forceUpdate = false) {
34993
35120
  { name: "Zen Go", promise: fetchZenGoModels() }
34994
35121
  ];
34995
35122
  if (litellmBaseUrl && litellmApiKey) {
34996
- fetchEntries.push({ name: "LiteLLM", promise: fetchLiteLLMModels(litellmBaseUrl, litellmApiKey, forceUpdate) });
35123
+ fetchEntries.push({
35124
+ name: "LiteLLM",
35125
+ promise: fetchLiteLLMModels(litellmBaseUrl, litellmApiKey, forceUpdate)
35126
+ });
34997
35127
  }
34998
35128
  const settled = await Promise.allSettled(fetchEntries.map((e) => e.promise));
34999
35129
  const fetchResults = {};
@@ -35205,9 +35335,24 @@ function getKnownModels(provider) {
35205
35335
  { id: "google@gemini-2.0-flash", name: "Gemini 2.0 Flash", context: "1M" }
35206
35336
  ],
35207
35337
  openai: [
35208
- { id: "oai@gpt-5.3-codex", name: "GPT-5.3 Codex", context: "400K", description: "Latest coding model" },
35209
- { id: "oai@gpt-5.2-codex", name: "GPT-5.2 Codex", context: "400K", description: "Coding model" },
35210
- { id: "oai@gpt-5.1-codex-mini", name: "GPT-5.1 Codex Mini", context: "400K", description: "Fast coding model" },
35338
+ {
35339
+ id: "oai@gpt-5.3-codex",
35340
+ name: "GPT-5.3 Codex",
35341
+ context: "400K",
35342
+ description: "Latest coding model"
35343
+ },
35344
+ {
35345
+ id: "oai@gpt-5.2-codex",
35346
+ name: "GPT-5.2 Codex",
35347
+ context: "400K",
35348
+ description: "Coding model"
35349
+ },
35350
+ {
35351
+ id: "oai@gpt-5.1-codex-mini",
35352
+ name: "GPT-5.1 Codex Mini",
35353
+ context: "400K",
35354
+ description: "Fast coding model"
35355
+ },
35211
35356
  { id: "oai@o3", name: "o3", context: "200K", description: "Reasoning model" },
35212
35357
  { id: "oai@o4-mini", name: "o4-mini", context: "200K", description: "Fast reasoning model" },
35213
35358
  { id: "oai@gpt-4.1", name: "GPT-4.1", context: "1M", description: "Large context model" }
@@ -35215,39 +35360,102 @@ function getKnownModels(provider) {
35215
35360
  xai: [
35216
35361
  { id: "xai@grok-4", name: "Grok 4", context: "256K" },
35217
35362
  { id: "xai@grok-4-fast", name: "Grok 4 Fast", context: "2M" },
35218
- { id: "xai@grok-code-fast-1", name: "Grok Code Fast 1", context: "256K", description: "Optimized for coding" }
35363
+ {
35364
+ id: "xai@grok-code-fast-1",
35365
+ name: "Grok Code Fast 1",
35366
+ context: "256K",
35367
+ description: "Optimized for coding"
35368
+ }
35219
35369
  ],
35220
35370
  minimax: [
35221
- { id: "mm@minimax-m2.1", name: "MiniMax M2.1", context: "196K", description: "Lightweight coding model" }
35371
+ {
35372
+ id: "mm@minimax-m2.1",
35373
+ name: "MiniMax M2.1",
35374
+ context: "196K",
35375
+ description: "Lightweight coding model"
35376
+ }
35222
35377
  ],
35223
35378
  "minimax-coding": [
35224
- { id: "mmc@minimax-m2.5", name: "MiniMax M2.5", context: "196K", description: "MiniMax Coding subscription model" },
35225
- { id: "mmc@minimax-m2.1", name: "MiniMax M2.1", context: "196K", description: "MiniMax Coding subscription model" }
35379
+ {
35380
+ id: "mmc@minimax-m2.5",
35381
+ name: "MiniMax M2.5",
35382
+ context: "196K",
35383
+ description: "MiniMax Coding subscription model"
35384
+ },
35385
+ {
35386
+ id: "mmc@minimax-m2.1",
35387
+ name: "MiniMax M2.1",
35388
+ context: "196K",
35389
+ description: "MiniMax Coding subscription model"
35390
+ }
35226
35391
  ],
35227
35392
  kimi: [
35228
35393
  { id: "kimi@kimi-k2-thinking-turbo", name: "Kimi K2 Thinking Turbo", context: "128K" },
35229
35394
  { id: "kimi@moonshot-v1-128k", name: "Moonshot V1 128K", context: "128K" }
35230
35395
  ],
35231
35396
  "kimi-coding": [
35232
- { id: "kc@kimi-for-coding", name: "Kimi for Coding", context: "128K", description: "Kimi Coding subscription model" }
35397
+ {
35398
+ id: "kc@kimi-for-coding",
35399
+ name: "Kimi for Coding",
35400
+ context: "128K",
35401
+ description: "Kimi Coding subscription model"
35402
+ }
35233
35403
  ],
35234
35404
  glm: [
35235
- { id: "glm@glm-5", name: "GLM-5", context: "200K", description: "Latest GLM model with reasoning" },
35236
- { id: "glm@glm-4.7", name: "GLM-4.7", context: "200K", description: "GLM 4.7 with reasoning" },
35237
- { id: "glm@glm-4.7-flash", name: "GLM-4.7 Flash", context: "200K", description: "Fast GLM 4.7" },
35405
+ {
35406
+ id: "glm@glm-5",
35407
+ name: "GLM-5",
35408
+ context: "200K",
35409
+ description: "Latest GLM model with reasoning"
35410
+ },
35411
+ {
35412
+ id: "glm@glm-4.7",
35413
+ name: "GLM-4.7",
35414
+ context: "200K",
35415
+ description: "GLM 4.7 with reasoning"
35416
+ },
35417
+ {
35418
+ id: "glm@glm-4.7-flash",
35419
+ name: "GLM-4.7 Flash",
35420
+ context: "200K",
35421
+ description: "Fast GLM 4.7"
35422
+ },
35238
35423
  { id: "glm@glm-4.6", name: "GLM-4.6", context: "200K" },
35239
35424
  { id: "glm@glm-4.5-flash", name: "GLM-4.5 Flash", context: "128K" }
35240
35425
  ],
35241
- zai: [
35242
- { id: "zai@glm-4.7", name: "GLM 4.7 (Z.AI)", context: "128K" }
35243
- ],
35426
+ zai: [{ id: "zai@glm-4.7", name: "GLM 4.7 (Z.AI)", context: "128K" }],
35244
35427
  ollamacloud: [
35245
35428
  { id: "oc@glm-5", name: "GLM-5", context: "203K", description: "GLM-5 on OllamaCloud" },
35246
- { id: "oc@deepseek-v3.2", name: "DeepSeek V3.2", context: "164K", description: "DeepSeek V3.2 on OllamaCloud" },
35247
- { id: "oc@gemini-3-pro-preview", name: "Gemini 3 Pro Preview", context: "1M", description: "Gemini 3 Pro on OllamaCloud" },
35248
- { id: "oc@kimi-k2.5", name: "Kimi K2.5", context: "262K", description: "Kimi K2.5 on OllamaCloud" },
35249
- { id: "oc@qwen3-coder-next", name: "Qwen3 Coder Next", context: "262K", description: "Qwen3 Coder on OllamaCloud" },
35250
- { id: "oc@minimax-m2.1", name: "MiniMax M2.1", context: "205K", description: "MiniMax M2.1 on OllamaCloud" }
35429
+ {
35430
+ id: "oc@deepseek-v3.2",
35431
+ name: "DeepSeek V3.2",
35432
+ context: "164K",
35433
+ description: "DeepSeek V3.2 on OllamaCloud"
35434
+ },
35435
+ {
35436
+ id: "oc@gemini-3-pro-preview",
35437
+ name: "Gemini 3 Pro Preview",
35438
+ context: "1M",
35439
+ description: "Gemini 3 Pro on OllamaCloud"
35440
+ },
35441
+ {
35442
+ id: "oc@kimi-k2.5",
35443
+ name: "Kimi K2.5",
35444
+ context: "262K",
35445
+ description: "Kimi K2.5 on OllamaCloud"
35446
+ },
35447
+ {
35448
+ id: "oc@qwen3-coder-next",
35449
+ name: "Qwen3 Coder Next",
35450
+ context: "262K",
35451
+ description: "Qwen3 Coder on OllamaCloud"
35452
+ },
35453
+ {
35454
+ id: "oc@minimax-m2.1",
35455
+ name: "MiniMax M2.1",
35456
+ context: "205K",
35457
+ description: "MiniMax M2.1 on OllamaCloud"
35458
+ }
35251
35459
  ]
35252
35460
  };
35253
35461
  const providerDisplay = provider.charAt(0).toUpperCase() + provider.slice(1);
@@ -35482,23 +35690,76 @@ var init_model_selector = __esm(() => {
35482
35690
  ll: "LiteLLM"
35483
35691
  };
35484
35692
  ALL_PROVIDER_CHOICES = [
35485
- { name: "Skip (keep Claude default)", value: "skip", description: "Use native Claude model for this tier" },
35693
+ {
35694
+ name: "Skip (keep Claude default)",
35695
+ value: "skip",
35696
+ description: "Use native Claude model for this tier"
35697
+ },
35486
35698
  { name: "OpenRouter", value: "openrouter", description: "580+ models via unified API" },
35487
35699
  { name: "OpenCode Zen", value: "zen", description: "Free models, no API key needed" },
35488
- { name: "Google Gemini", value: "google", description: "Direct API (GEMINI_API_KEY)", envVar: "GEMINI_API_KEY" },
35489
- { name: "OpenAI", value: "openai", description: "Direct API (OPENAI_API_KEY)", envVar: "OPENAI_API_KEY" },
35490
- { name: "xAI / Grok", value: "xai", description: "Direct API (XAI_API_KEY)", envVar: "XAI_API_KEY" },
35491
- { name: "MiniMax", value: "minimax", description: "Direct API (MINIMAX_API_KEY)", envVar: "MINIMAX_API_KEY" },
35492
- { name: "MiniMax Coding", value: "minimax-coding", description: "MiniMax Coding subscription (MINIMAX_CODING_API_KEY)", envVar: "MINIMAX_CODING_API_KEY" },
35493
- { name: "Kimi / Moonshot", value: "kimi", description: "Direct API (MOONSHOT_API_KEY)", envVar: "MOONSHOT_API_KEY" },
35494
- { name: "Kimi Coding", value: "kimi-coding", description: "Kimi Coding subscription (KIMI_CODING_API_KEY)", envVar: "KIMI_CODING_API_KEY" },
35495
- { name: "GLM / Zhipu", value: "glm", description: "Direct API (ZHIPU_API_KEY)", envVar: "ZHIPU_API_KEY" },
35496
- { name: "GLM Coding Plan", value: "glm-coding", description: "GLM Coding subscription (GLM_CODING_API_KEY)", envVar: "GLM_CODING_API_KEY" },
35700
+ {
35701
+ name: "Google Gemini",
35702
+ value: "google",
35703
+ description: "Direct API (GEMINI_API_KEY)",
35704
+ envVar: "GEMINI_API_KEY"
35705
+ },
35706
+ {
35707
+ name: "OpenAI",
35708
+ value: "openai",
35709
+ description: "Direct API (OPENAI_API_KEY)",
35710
+ envVar: "OPENAI_API_KEY"
35711
+ },
35712
+ {
35713
+ name: "xAI / Grok",
35714
+ value: "xai",
35715
+ description: "Direct API (XAI_API_KEY)",
35716
+ envVar: "XAI_API_KEY"
35717
+ },
35718
+ {
35719
+ name: "MiniMax",
35720
+ value: "minimax",
35721
+ description: "Direct API (MINIMAX_API_KEY)",
35722
+ envVar: "MINIMAX_API_KEY"
35723
+ },
35724
+ {
35725
+ name: "MiniMax Coding",
35726
+ value: "minimax-coding",
35727
+ description: "MiniMax Coding subscription (MINIMAX_CODING_API_KEY)",
35728
+ envVar: "MINIMAX_CODING_API_KEY"
35729
+ },
35730
+ {
35731
+ name: "Kimi / Moonshot",
35732
+ value: "kimi",
35733
+ description: "Direct API (MOONSHOT_API_KEY)",
35734
+ envVar: "MOONSHOT_API_KEY"
35735
+ },
35736
+ {
35737
+ name: "Kimi Coding",
35738
+ value: "kimi-coding",
35739
+ description: "Kimi Coding subscription (KIMI_CODING_API_KEY)",
35740
+ envVar: "KIMI_CODING_API_KEY"
35741
+ },
35742
+ {
35743
+ name: "GLM / Zhipu",
35744
+ value: "glm",
35745
+ description: "Direct API (ZHIPU_API_KEY)",
35746
+ envVar: "ZHIPU_API_KEY"
35747
+ },
35748
+ {
35749
+ name: "GLM Coding Plan",
35750
+ value: "glm-coding",
35751
+ description: "GLM Coding subscription (GLM_CODING_API_KEY)",
35752
+ envVar: "GLM_CODING_API_KEY"
35753
+ },
35497
35754
  { name: "Z.AI", value: "zai", description: "Z.AI API (ZAI_API_KEY)", envVar: "ZAI_API_KEY" },
35498
35755
  { name: "OllamaCloud", value: "ollamacloud", description: "Cloud models (OLLAMA_API_KEY)" },
35499
35756
  { name: "Ollama (local)", value: "ollama", description: "Local Ollama instance" },
35500
35757
  { name: "LM Studio (local)", value: "lmstudio", description: "Local LM Studio instance" },
35501
- { name: "Enter custom model", value: "custom", description: "Type a provider@model specification" }
35758
+ {
35759
+ name: "Enter custom model",
35760
+ value: "custom",
35761
+ description: "Type a provider@model specification"
35762
+ }
35502
35763
  ];
35503
35764
  PROVIDER_MODEL_PREFIX = {
35504
35765
  google: "google@",
@@ -41355,10 +41616,12 @@ var init_openai_adapter = __esm(() => {
41355
41616
  result.push({
41356
41617
  type: "message",
41357
41618
  role: msg.role,
41358
- content: [{
41359
- type: msg.role === "user" ? "input_text" : "output_text",
41360
- text: msg.content
41361
- }]
41619
+ content: [
41620
+ {
41621
+ type: msg.role === "user" ? "input_text" : "output_text",
41622
+ text: msg.content
41623
+ }
41624
+ ]
41362
41625
  });
41363
41626
  continue;
41364
41627
  }
@@ -62098,7 +62361,7 @@ class LocalTransport {
62098
62361
  this.config = config3;
62099
62362
  this.modelName = modelName;
62100
62363
  this.name = config3.name;
62101
- this.displayName = DISPLAY_NAMES[config3.name] || "Local";
62364
+ this.displayName = DISPLAY_NAMES2[config3.name] || "Local";
62102
62365
  this.concurrency = options?.concurrency;
62103
62366
  const envContextWindow = process.env.CLAUDISH_CONTEXT_WINDOW;
62104
62367
  if (envContextWindow) {
@@ -62279,7 +62542,7 @@ class LocalTransport {
62279
62542
  }
62280
62543
  }
62281
62544
  }
62282
- var localProviderAgent, DISPLAY_NAMES;
62545
+ var localProviderAgent, DISPLAY_NAMES2;
62283
62546
  var init_local = __esm(() => {
62284
62547
  init_local_queue();
62285
62548
  init_logger();
@@ -62290,7 +62553,7 @@ var init_local = __esm(() => {
62290
62553
  keepAliveTimeout: 30000,
62291
62554
  keepAliveMaxTimeout: 600000
62292
62555
  });
62293
- DISPLAY_NAMES = {
62556
+ DISPLAY_NAMES2 = {
62294
62557
  ollama: "Ollama",
62295
62558
  lmstudio: "LM Studio",
62296
62559
  vllm: "vLLM",
@@ -63981,7 +64244,10 @@ data: ${JSON.stringify(data)}
63981
64244
  }
63982
64245
  if (part.text) {
63983
64246
  if (thinkingStarted) {
63984
- send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
64247
+ send("content_block_stop", {
64248
+ type: "content_block_stop",
64249
+ index: thinkingIdx
64250
+ });
63985
64251
  thinkingStarted = false;
63986
64252
  }
63987
64253
  let cleanedText = part.text;
@@ -64011,7 +64277,10 @@ data: ${JSON.stringify(data)}
64011
64277
  }
64012
64278
  if (part.functionCall) {
64013
64279
  if (thinkingStarted) {
64014
- send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
64280
+ send("content_block_stop", {
64281
+ type: "content_block_stop",
64282
+ index: thinkingIdx
64283
+ });
64015
64284
  thinkingStarted = false;
64016
64285
  }
64017
64286
  if (textStarted) {
@@ -64943,7 +65212,10 @@ var init_anthropic_passthrough_adapter = __esm(() => {
64943
65212
  const filteredContent = message.content.map((block) => {
64944
65213
  if (block.type === "tool_result" && Array.isArray(block.content)) {
64945
65214
  const filtered = block.content.filter((c) => c.type !== "tool_reference");
64946
- return { ...block, content: filtered.length > 0 ? filtered : [{ type: "text", text: "" }] };
65215
+ return {
65216
+ ...block,
65217
+ content: filtered.length > 0 ? filtered : [{ type: "text", text: "" }]
65218
+ };
64947
65219
  }
64948
65220
  return block;
64949
65221
  }).filter((block) => block.type !== "tool_reference");
@@ -65250,6 +65522,196 @@ var init_pricing_cache = __esm(() => {
65250
65522
  };
65251
65523
  });
65252
65524
 
65525
+ // src/handlers/fallback-handler.ts
65526
+ class FallbackHandler {
65527
+ candidates;
65528
+ constructor(candidates) {
65529
+ this.candidates = candidates;
65530
+ }
65531
+ async handle(c, payload) {
65532
+ const errors6 = [];
65533
+ for (let i = 0;i < this.candidates.length; i++) {
65534
+ const { name, handler } = this.candidates[i];
65535
+ const isLast = i === this.candidates.length - 1;
65536
+ try {
65537
+ const response = await handler.handle(c, payload);
65538
+ if (response.ok) {
65539
+ if (errors6.length > 0) {
65540
+ logStderr(`[Fallback] ${name} succeeded after ${errors6.length} failed attempt(s)`);
65541
+ }
65542
+ return response;
65543
+ }
65544
+ const errorBody = await response.clone().text();
65545
+ if (!isRetryableError(response.status, errorBody)) {
65546
+ if (errors6.length > 0) {
65547
+ errors6.push({ provider: name, status: response.status, message: errorBody });
65548
+ return this.formatCombinedError(c, errors6, payload.model);
65549
+ }
65550
+ return response;
65551
+ }
65552
+ errors6.push({ provider: name, status: response.status, message: errorBody });
65553
+ if (!isLast) {
65554
+ logStderr(`[Fallback] ${name} failed (HTTP ${response.status}), trying next provider...`);
65555
+ }
65556
+ } catch (err) {
65557
+ errors6.push({ provider: name, status: 0, message: err.message });
65558
+ if (!isLast) {
65559
+ logStderr(`[Fallback] ${name} error: ${err.message}, trying next provider...`);
65560
+ }
65561
+ }
65562
+ }
65563
+ return this.formatCombinedError(c, errors6, payload.model);
65564
+ }
65565
+ formatCombinedError(c, errors6, modelName) {
65566
+ const summary = errors6.map((e) => ` ${e.provider}: HTTP ${e.status || "ERR"} — ${truncate(parseErrorMessage(e.message), 150)}`).join(`
65567
+ `);
65568
+ logStderr(`[Fallback] All ${errors6.length} provider(s) failed for ${modelName || "model"}:
65569
+ ${summary}`);
65570
+ return c.json({
65571
+ error: {
65572
+ type: "all_providers_failed",
65573
+ message: `All ${errors6.length} providers failed for model '${modelName || "unknown"}'`,
65574
+ attempts: errors6.map((e) => ({
65575
+ provider: e.provider,
65576
+ status: e.status,
65577
+ error: truncate(parseErrorMessage(e.message), 200)
65578
+ }))
65579
+ }
65580
+ }, 502);
65581
+ }
65582
+ async shutdown() {
65583
+ for (const { handler } of this.candidates) {
65584
+ if (typeof handler.shutdown === "function") {
65585
+ await handler.shutdown();
65586
+ }
65587
+ }
65588
+ }
65589
+ }
65590
+ function isRetryableError(status, errorBody) {
65591
+ if (status === 401 || status === 403)
65592
+ return true;
65593
+ if (status === 402)
65594
+ return true;
65595
+ if (status === 404)
65596
+ return true;
65597
+ if (status === 429)
65598
+ return true;
65599
+ const lower = errorBody.toLowerCase();
65600
+ if (status === 422) {
65601
+ if (lower.includes("not available") || lower.includes("model not found") || lower.includes("not supported")) {
65602
+ return true;
65603
+ }
65604
+ }
65605
+ if (status === 400) {
65606
+ if (lower.includes("model not found") || lower.includes("not registered") || lower.includes("does not exist") || lower.includes("unknown model") || lower.includes("unsupported model")) {
65607
+ return true;
65608
+ }
65609
+ }
65610
+ if (status === 500) {
65611
+ if (lower.includes("insufficient balance") || lower.includes("insufficient credit") || lower.includes("quota exceeded") || lower.includes("billing")) {
65612
+ return true;
65613
+ }
65614
+ }
65615
+ return false;
65616
+ }
65617
+ function parseErrorMessage(body) {
65618
+ try {
65619
+ const parsed = JSON.parse(body);
65620
+ if (typeof parsed.error === "string")
65621
+ return parsed.error;
65622
+ if (typeof parsed.error?.message === "string")
65623
+ return parsed.error.message;
65624
+ if (typeof parsed.message === "string")
65625
+ return parsed.message;
65626
+ } catch {}
65627
+ return body;
65628
+ }
65629
+ function truncate(s, max) {
65630
+ return s.length > max ? s.slice(0, max) + "..." : s;
65631
+ }
65632
+ var init_fallback_handler = __esm(() => {
65633
+ init_logger();
65634
+ });
65635
+
65636
+ // src/providers/routing-rules.ts
65637
+ function loadRoutingRules() {
65638
+ const local = loadLocalConfig();
65639
+ if (local?.routing && Object.keys(local.routing).length > 0) {
65640
+ validateRoutingRules(local.routing);
65641
+ return local.routing;
65642
+ }
65643
+ const global_ = loadConfig();
65644
+ if (global_.routing && Object.keys(global_.routing).length > 0) {
65645
+ validateRoutingRules(global_.routing);
65646
+ return global_.routing;
65647
+ }
65648
+ return null;
65649
+ }
65650
+ function validateRoutingRules(rules) {
65651
+ for (const key of Object.keys(rules)) {
65652
+ if (key !== "*" && (key.match(/\*/g) || []).length > 1) {
65653
+ console.error(`[claudish] Warning: routing pattern "${key}" has multiple wildcards — only single * is supported. This pattern may not match as expected.`);
65654
+ }
65655
+ const entries = rules[key];
65656
+ if (!Array.isArray(entries) || entries.length === 0) {
65657
+ console.error(`[claudish] Warning: routing rule "${key}" has no provider entries — models matching this pattern will have no fallback chain.`);
65658
+ }
65659
+ }
65660
+ }
65661
+ function matchRoutingRule(modelName, rules) {
65662
+ if (rules[modelName])
65663
+ return rules[modelName];
65664
+ const globKeys = Object.keys(rules).filter((k) => k !== "*" && k.includes("*")).sort((a, b) => b.length - a.length);
65665
+ for (const pattern of globKeys) {
65666
+ if (globMatch(pattern, modelName))
65667
+ return rules[pattern];
65668
+ }
65669
+ if (rules["*"])
65670
+ return rules["*"];
65671
+ return null;
65672
+ }
65673
+ function buildRoutingChain(entries, originalModelName) {
65674
+ const routes = [];
65675
+ for (const entry of entries) {
65676
+ const atIdx = entry.indexOf("@");
65677
+ let providerRaw;
65678
+ let modelName;
65679
+ if (atIdx !== -1) {
65680
+ providerRaw = entry.slice(0, atIdx);
65681
+ modelName = entry.slice(atIdx + 1);
65682
+ } else {
65683
+ providerRaw = entry;
65684
+ modelName = originalModelName;
65685
+ }
65686
+ const provider = PROVIDER_SHORTCUTS[providerRaw.toLowerCase()] ?? providerRaw.toLowerCase();
65687
+ let modelSpec;
65688
+ if (provider === "openrouter") {
65689
+ const resolution = resolveModelNameSync(modelName, "openrouter");
65690
+ modelSpec = resolution.resolvedId;
65691
+ } else {
65692
+ const prefix = PROVIDER_TO_PREFIX[provider] ?? provider;
65693
+ modelSpec = `${prefix}@${modelName}`;
65694
+ }
65695
+ const displayName = DISPLAY_NAMES[provider] ?? provider;
65696
+ routes.push({ provider, modelSpec, displayName });
65697
+ }
65698
+ return routes;
65699
+ }
65700
+ function globMatch(pattern, value) {
65701
+ const star = pattern.indexOf("*");
65702
+ if (star === -1)
65703
+ return pattern === value;
65704
+ const prefix = pattern.slice(0, star);
65705
+ const suffix = pattern.slice(star + 1);
65706
+ return value.startsWith(prefix) && value.endsWith(suffix) && value.length >= prefix.length + suffix.length;
65707
+ }
65708
+ var init_routing_rules = __esm(() => {
65709
+ init_profile_config();
65710
+ init_auto_route();
65711
+ init_model_parser();
65712
+ init_model_catalog_resolver();
65713
+ });
65714
+
65253
65715
  // src/proxy-server.ts
65254
65716
  var exports_proxy_server = {};
65255
65717
  __export(exports_proxy_server, {
@@ -65267,7 +65729,10 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
65267
65729
  if (!openRouterHandlers.has(modelId)) {
65268
65730
  const orProvider = new OpenRouterProvider(openrouterApiKey || "");
65269
65731
  const orAdapter = new OpenRouterAdapter(modelId);
65270
- openRouterHandlers.set(modelId, new ComposedHandler(orProvider, modelId, modelId, port, { adapter: orAdapter, isInteractive: options.isInteractive }));
65732
+ openRouterHandlers.set(modelId, new ComposedHandler(orProvider, modelId, modelId, port, {
65733
+ adapter: orAdapter,
65734
+ isInteractive: options.isInteractive
65735
+ }));
65271
65736
  }
65272
65737
  return openRouterHandlers.get(modelId);
65273
65738
  };
@@ -65280,7 +65745,9 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
65280
65745
  const modelId = targetModel.replace(/^poe:/, "");
65281
65746
  if (!poeHandlers.has(modelId)) {
65282
65747
  const poeTransport = new PoeProvider(poeApiKey);
65283
- poeHandlers.set(modelId, new ComposedHandler(poeTransport, modelId, modelId, port, { isInteractive: options.isInteractive }));
65748
+ poeHandlers.set(modelId, new ComposedHandler(poeTransport, modelId, modelId, port, {
65749
+ isInteractive: options.isInteractive
65750
+ }));
65284
65751
  }
65285
65752
  return poeHandlers.get(modelId);
65286
65753
  };
@@ -65495,6 +65962,8 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
65495
65962
  log("[Proxy] LiteLLM model cache pre-warmed for auto-routing");
65496
65963
  }).catch(() => {});
65497
65964
  }
65965
+ const customRoutingRules = loadRoutingRules();
65966
+ const fallbackHandlerCache = new Map;
65498
65967
  const getHandlerForRequest = (requestedModel) => {
65499
65968
  if (monitorMode)
65500
65969
  return nativeHandler;
@@ -65522,6 +65991,40 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
65522
65991
  }
65523
65992
  }
65524
65993
  }
65994
+ {
65995
+ const parsedForFallback = parseModelSpec(target);
65996
+ if (!parsedForFallback.isExplicitProvider && parsedForFallback.provider !== "native-anthropic" && !isPoeModel(target)) {
65997
+ const cacheKey2 = `fallback:${target}`;
65998
+ if (fallbackHandlerCache.has(cacheKey2)) {
65999
+ return fallbackHandlerCache.get(cacheKey2);
66000
+ }
66001
+ const matchedEntries = customRoutingRules ? matchRoutingRule(parsedForFallback.model, customRoutingRules) : null;
66002
+ const chain = matchedEntries ? buildRoutingChain(matchedEntries, parsedForFallback.model) : getFallbackChain(parsedForFallback.model, parsedForFallback.provider);
66003
+ if (chain.length > 0) {
66004
+ const candidates = [];
66005
+ for (const route of chain) {
66006
+ let handler = null;
66007
+ if (route.provider === "openrouter") {
66008
+ handler = getOpenRouterHandler(route.modelSpec);
66009
+ } else {
66010
+ handler = getRemoteProviderHandler(route.modelSpec);
66011
+ }
66012
+ if (handler) {
66013
+ candidates.push({ name: route.displayName, handler });
66014
+ }
66015
+ }
66016
+ if (candidates.length > 0) {
66017
+ const resultHandler = candidates.length > 1 ? new FallbackHandler(candidates) : candidates[0].handler;
66018
+ fallbackHandlerCache.set(cacheKey2, resultHandler);
66019
+ if (!options.quiet && candidates.length > 1) {
66020
+ const source = matchedEntries ? "[Custom]" : "[Fallback]";
66021
+ logStderr(`${source} ${candidates.length} providers for ${parsedForFallback.model}: ${candidates.map((c) => c.name).join(" → ")}`);
66022
+ }
66023
+ return resultHandler;
66024
+ }
66025
+ }
66026
+ }
66027
+ }
65525
66028
  if (isPoeModel(target)) {
65526
66029
  const poeHandler = getPoeHandler(target);
65527
66030
  if (poeHandler) {
@@ -65633,6 +66136,9 @@ var init_proxy_server = __esm(() => {
65633
66136
  init_pricing_cache();
65634
66137
  init_model_loader();
65635
66138
  init_model_catalog_resolver();
66139
+ init_fallback_handler();
66140
+ init_auto_route();
66141
+ init_routing_rules();
65636
66142
  });
65637
66143
 
65638
66144
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "5.8.0",
3
+ "version": "5.10.0",
4
4
  "description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "1.2.0",
3
- "lastUpdated": "2026-03-06",
3
+ "lastUpdated": "2026-03-15",
4
4
  "source": "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
5
5
  "models": [
6
6
  {
@@ -12,9 +12,9 @@
12
12
  "category": "programming",
13
13
  "priority": 1,
14
14
  "pricing": {
15
- "input": "$0.29/1M",
15
+ "input": "$0.25/1M",
16
16
  "output": "$1.20/1M",
17
- "average": "$0.75/1M"
17
+ "average": "$0.72/1M"
18
18
  },
19
19
  "context": "196K",
20
20
  "maxOutputTokens": 196608,
@@ -56,12 +56,12 @@
56
56
  "category": "reasoning",
57
57
  "priority": 3,
58
58
  "pricing": {
59
- "input": "$0.80/1M",
60
- "output": "$2.56/1M",
61
- "average": "$1.68/1M"
59
+ "input": "$0.72/1M",
60
+ "output": "$2.30/1M",
61
+ "average": "$1.51/1M"
62
62
  },
63
63
  "context": "202K",
64
- "maxOutputTokens": null,
64
+ "maxOutputTokens": 131072,
65
65
  "modality": "text->text",
66
66
  "supportsTools": true,
67
67
  "supportsReasoning": true,