claudish 5.9.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;
@@ -31955,7 +31962,7 @@ async function fetchGLMCodingModels() {
31955
31962
  return [];
31956
31963
  }
31957
31964
  }
31958
- var __filename4, __dirname4, VERSION = "5.9.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;
31959
31966
  var init_cli = __esm(() => {
31960
31967
  init_config();
31961
31968
  init_model_loader();
@@ -65626,6 +65633,85 @@ var init_fallback_handler = __esm(() => {
65626
65633
  init_logger();
65627
65634
  });
65628
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
+
65629
65715
  // src/proxy-server.ts
65630
65716
  var exports_proxy_server = {};
65631
65717
  __export(exports_proxy_server, {
@@ -65876,6 +65962,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
65876
65962
  log("[Proxy] LiteLLM model cache pre-warmed for auto-routing");
65877
65963
  }).catch(() => {});
65878
65964
  }
65965
+ const customRoutingRules = loadRoutingRules();
65879
65966
  const fallbackHandlerCache = new Map;
65880
65967
  const getHandlerForRequest = (requestedModel) => {
65881
65968
  if (monitorMode)
@@ -65911,7 +65998,8 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
65911
65998
  if (fallbackHandlerCache.has(cacheKey2)) {
65912
65999
  return fallbackHandlerCache.get(cacheKey2);
65913
66000
  }
65914
- const chain = getFallbackChain(parsedForFallback.model, parsedForFallback.provider);
66001
+ const matchedEntries = customRoutingRules ? matchRoutingRule(parsedForFallback.model, customRoutingRules) : null;
66002
+ const chain = matchedEntries ? buildRoutingChain(matchedEntries, parsedForFallback.model) : getFallbackChain(parsedForFallback.model, parsedForFallback.provider);
65915
66003
  if (chain.length > 0) {
65916
66004
  const candidates = [];
65917
66005
  for (const route of chain) {
@@ -65929,7 +66017,8 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
65929
66017
  const resultHandler = candidates.length > 1 ? new FallbackHandler(candidates) : candidates[0].handler;
65930
66018
  fallbackHandlerCache.set(cacheKey2, resultHandler);
65931
66019
  if (!options.quiet && candidates.length > 1) {
65932
- logStderr(`[Fallback] ${candidates.length} providers for ${parsedForFallback.model}: ${candidates.map((c) => c.name).join(" ")}`);
66020
+ const source = matchedEntries ? "[Custom]" : "[Fallback]";
66021
+ logStderr(`${source} ${candidates.length} providers for ${parsedForFallback.model}: ${candidates.map((c) => c.name).join(" → ")}`);
65933
66022
  }
65934
66023
  return resultHandler;
65935
66024
  }
@@ -66049,6 +66138,7 @@ var init_proxy_server = __esm(() => {
66049
66138
  init_model_catalog_resolver();
66050
66139
  init_fallback_handler();
66051
66140
  init_auto_route();
66141
+ init_routing_rules();
66052
66142
  });
66053
66143
 
66054
66144
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "5.9.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-14",
3
+ "lastUpdated": "2026-03-15",
4
4
  "source": "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
5
5
  "models": [
6
6
  {