claudish 4.6.8 → 4.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -31010,11 +31010,11 @@ async function doesModelSupportReasoning(modelId) {
31010
31010
  }
31011
31011
  return false;
31012
31012
  }
31013
- async function fetchLiteLLMModels(baseUrl, apiKey) {
31013
+ async function fetchLiteLLMModels(baseUrl, apiKey, forceUpdate = false) {
31014
31014
  const hash2 = createHash2("sha256").update(baseUrl).digest("hex").substring(0, 16);
31015
31015
  const cacheDir = join6(homedir5(), ".claudish");
31016
31016
  const cachePath = join6(cacheDir, `litellm-models-${hash2}.json`);
31017
- if (existsSync6(cachePath)) {
31017
+ if (!forceUpdate && existsSync6(cachePath)) {
31018
31018
  try {
31019
31019
  const cacheData = JSON.parse(readFileSync5(cachePath, "utf-8"));
31020
31020
  const timestamp = new Date(cacheData.timestamp);
@@ -31572,11 +31572,11 @@ async function getFreeModels() {
31572
31572
  });
31573
31573
  return combined;
31574
31574
  }
31575
- async function getAllModelsForSearch() {
31575
+ async function getAllModelsForSearch(forceUpdate = false) {
31576
31576
  const litellmBaseUrl = process.env.LITELLM_BASE_URL;
31577
31577
  const litellmApiKey = process.env.LITELLM_API_KEY;
31578
31578
  const fetchEntries = [
31579
- { name: "OpenRouter", promise: fetchAllModels().then((models) => models.map(toModelInfo)) },
31579
+ { name: "OpenRouter", promise: fetchAllModels(forceUpdate).then((models) => models.map(toModelInfo)) },
31580
31580
  { name: "xAI", promise: fetchXAIModels() },
31581
31581
  { name: "Gemini", promise: fetchGeminiModels() },
31582
31582
  { name: "OpenAI", promise: fetchOpenAIModels() },
@@ -31586,7 +31586,7 @@ async function getAllModelsForSearch() {
31586
31586
  { name: "Zen", promise: fetchZenFreeModels() }
31587
31587
  ];
31588
31588
  if (litellmBaseUrl && litellmApiKey) {
31589
- fetchEntries.push({ name: "LiteLLM", promise: fetchLiteLLMModels(litellmBaseUrl, litellmApiKey) });
31589
+ fetchEntries.push({ name: "LiteLLM", promise: fetchLiteLLMModels(litellmBaseUrl, litellmApiKey, forceUpdate) });
31590
31590
  }
31591
31591
  const settled = await Promise.allSettled(fetchEntries.map((e) => e.promise));
31592
31592
  const fetchResults = {};
@@ -31686,7 +31686,7 @@ function fuzzyMatch(text, query) {
31686
31686
  return queryIdx === lowerQuery.length ? score / lowerQuery.length * 0.6 : 0;
31687
31687
  }
31688
31688
  async function selectModel(options = {}) {
31689
- const { freeOnly = false, recommended = true, message } = options;
31689
+ const { freeOnly = false, recommended = true, message, forceUpdate = false } = options;
31690
31690
  let models;
31691
31691
  if (freeOnly) {
31692
31692
  models = await getFreeModels();
@@ -31695,7 +31695,7 @@ async function selectModel(options = {}) {
31695
31695
  }
31696
31696
  } else {
31697
31697
  const [allModels, recommendedModels] = await Promise.all([
31698
- getAllModelsForSearch(),
31698
+ getAllModelsForSearch(forceUpdate),
31699
31699
  Promise.resolve(recommended ? loadRecommendedModels2() : [])
31700
31700
  ]);
31701
31701
  const seenIds = new Set;
@@ -33648,13 +33648,34 @@ __export(exports_cli, {
33648
33648
  getMissingKeyResolutions: () => getMissingKeyResolutions,
33649
33649
  getMissingKeyError: () => getMissingKeyError
33650
33650
  });
33651
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync6, copyFileSync } from "node:fs";
33651
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync6, copyFileSync, readdirSync, unlinkSync as unlinkSync3 } from "node:fs";
33652
33652
  import { fileURLToPath as fileURLToPath4 } from "node:url";
33653
33653
  import { dirname as dirname4, join as join9 } from "node:path";
33654
33654
  import { homedir as homedir8 } from "node:os";
33655
33655
  function getVersion() {
33656
33656
  return VERSION;
33657
33657
  }
33658
+ function clearAllModelCaches() {
33659
+ const cacheDir = join9(homedir8(), ".claudish");
33660
+ if (!existsSync9(cacheDir))
33661
+ return;
33662
+ const cachePatterns = ["all-models.json", "pricing-cache.json"];
33663
+ let cleared = 0;
33664
+ try {
33665
+ const files = readdirSync(cacheDir);
33666
+ for (const file2 of files) {
33667
+ if (cachePatterns.includes(file2) || file2.startsWith("litellm-models-")) {
33668
+ unlinkSync3(join9(cacheDir, file2));
33669
+ cleared++;
33670
+ }
33671
+ }
33672
+ if (cleared > 0) {
33673
+ console.error(`\uD83D\uDDD1️ Cleared ${cleared} cache file(s)`);
33674
+ }
33675
+ } catch (error46) {
33676
+ console.error(`Warning: Could not clear caches: ${error46}`);
33677
+ }
33678
+ }
33658
33679
  async function parseArgs(args) {
33659
33680
  const config3 = {
33660
33681
  model: undefined,
@@ -33794,6 +33815,8 @@ async function parseArgs(args) {
33794
33815
  } else if (arg === "--top-models") {
33795
33816
  const hasJsonFlag = args.includes("--json");
33796
33817
  const forceUpdate = args.includes("--force-update");
33818
+ if (forceUpdate)
33819
+ clearAllModelCaches();
33797
33820
  await checkAndUpdateModelsCache(forceUpdate);
33798
33821
  if (hasJsonFlag) {
33799
33822
  printAvailableModelsJSON();
@@ -33801,12 +33824,14 @@ async function parseArgs(args) {
33801
33824
  printAvailableModels();
33802
33825
  }
33803
33826
  process.exit(0);
33804
- } else if (arg === "--models" || arg === "-s" || arg === "--search") {
33827
+ } else if (arg === "--models" || arg === "--list-models" || arg === "-s" || arg === "--search") {
33805
33828
  const nextArg = args[i + 1];
33806
33829
  const hasQuery = nextArg && !nextArg.startsWith("--");
33807
33830
  const query = hasQuery ? args[++i] : null;
33808
33831
  const hasJsonFlag = args.includes("--json");
33809
33832
  const forceUpdate = args.includes("--force-update");
33833
+ if (forceUpdate)
33834
+ clearAllModelCaches();
33810
33835
  if (query) {
33811
33836
  await searchAndPrintModels(query, forceUpdate);
33812
33837
  } else {
@@ -34914,7 +34939,7 @@ async function fetchGLMCodingModels2() {
34914
34939
  return [];
34915
34940
  }
34916
34941
  }
34917
- var __filename5, __dirname5, VERSION = "4.6.8", CACHE_MAX_AGE_DAYS3 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR3, ALL_MODELS_JSON_PATH2;
34942
+ var __filename5, __dirname5, VERSION = "4.6.9", CACHE_MAX_AGE_DAYS3 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR3, ALL_MODELS_JSON_PATH2;
34918
34943
  var init_cli = __esm(() => {
34919
34944
  init_config();
34920
34945
  init_model_loader();
@@ -34938,7 +34963,7 @@ __export(exports_claude_runner, {
34938
34963
  checkClaudeInstalled: () => checkClaudeInstalled
34939
34964
  });
34940
34965
  import { spawn } from "node:child_process";
34941
- import { writeFileSync as writeFileSync7, unlinkSync as unlinkSync3, mkdirSync as mkdirSync7, existsSync as existsSync10 } from "node:fs";
34966
+ import { writeFileSync as writeFileSync7, unlinkSync as unlinkSync4, mkdirSync as mkdirSync7, existsSync as existsSync10 } from "node:fs";
34942
34967
  import { tmpdir, homedir as homedir9 } from "node:os";
34943
34968
  import { join as join10 } from "node:path";
34944
34969
  function isWindows() {
@@ -35161,7 +35186,7 @@ Or set CLAUDE_PATH to your custom installation:`);
35161
35186
  });
35162
35187
  });
35163
35188
  try {
35164
- unlinkSync3(tempSettingsPath);
35189
+ unlinkSync4(tempSettingsPath);
35165
35190
  } catch (error46) {}
35166
35191
  return exitCode;
35167
35192
  }
@@ -35175,7 +35200,7 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet) {
35175
35200
  }
35176
35201
  proc.kill();
35177
35202
  try {
35178
- unlinkSync3(tempSettingsPath);
35203
+ unlinkSync4(tempSettingsPath);
35179
35204
  } catch {}
35180
35205
  process.exit(0);
35181
35206
  });
@@ -65511,8 +65536,24 @@ class RemoteProviderHandler {
65511
65536
  this.sessionTotalCost += cost;
65512
65537
  this.writeTokenFile(inputTokens, this.sessionOutputTokens);
65513
65538
  }
65539
+ supportsVision() {
65540
+ return true;
65541
+ }
65514
65542
  convertMessages(claudeRequest) {
65515
- return convertMessagesToOpenAI(claudeRequest, this.targetModel, filterIdentity);
65543
+ const messages = convertMessagesToOpenAI(claudeRequest, this.targetModel, filterIdentity);
65544
+ if (!this.supportsVision()) {
65545
+ for (const msg of messages) {
65546
+ if (Array.isArray(msg.content)) {
65547
+ msg.content = msg.content.filter((part) => part.type !== "image_url");
65548
+ if (msg.content.length === 1 && msg.content[0].type === "text") {
65549
+ msg.content = msg.content[0].text;
65550
+ } else if (msg.content.length === 0) {
65551
+ msg.content = "";
65552
+ }
65553
+ }
65554
+ }
65555
+ }
65556
+ return messages;
65516
65557
  }
65517
65558
  convertTools(claudeRequest) {
65518
65559
  return convertToolsToOpenAI(claudeRequest);
@@ -65591,14 +65632,90 @@ var init_remote_provider_handler = __esm(() => {
65591
65632
  });
65592
65633
 
65593
65634
  // src/handlers/litellm-handler.ts
65594
- var LiteLLMHandler;
65635
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
65636
+ import { createHash as createHash4 } from "node:crypto";
65637
+ import { homedir as homedir20 } from "node:os";
65638
+ import { join as join21 } from "node:path";
65639
+ var INLINE_IMAGE_MODEL_PATTERNS, MODEL_EXTRA_HEADERS, LiteLLMHandler;
65595
65640
  var init_litellm_handler = __esm(() => {
65596
65641
  init_remote_provider_handler();
65642
+ init_logger();
65643
+ INLINE_IMAGE_MODEL_PATTERNS = ["minimax"];
65644
+ MODEL_EXTRA_HEADERS = [
65645
+ { pattern: "kimi", headers: { "User-Agent": "claude-code/1.0" } }
65646
+ ];
65597
65647
  LiteLLMHandler = class LiteLLMHandler extends RemoteProviderHandler {
65598
65648
  baseUrl;
65649
+ modelVisionSupported;
65650
+ needsInlineImages;
65599
65651
  constructor(targetModel, modelName, apiKey, port, baseUrl) {
65600
65652
  super(targetModel, modelName, apiKey, port);
65601
65653
  this.baseUrl = baseUrl;
65654
+ this.modelVisionSupported = this.checkVisionSupport();
65655
+ this.needsInlineImages = INLINE_IMAGE_MODEL_PATTERNS.some((p) => this.modelName.toLowerCase().includes(p));
65656
+ }
65657
+ supportsVision() {
65658
+ return this.modelVisionSupported;
65659
+ }
65660
+ convertMessages(claudeRequest) {
65661
+ const messages = super.convertMessages(claudeRequest);
65662
+ if (!this.needsInlineImages)
65663
+ return messages;
65664
+ for (const msg of messages) {
65665
+ if (!Array.isArray(msg.content))
65666
+ continue;
65667
+ const newContent = [];
65668
+ let inlineImages = "";
65669
+ for (const part of msg.content) {
65670
+ if (part.type === "image_url") {
65671
+ const url2 = typeof part.image_url === "string" ? part.image_url : part.image_url?.url;
65672
+ if (url2?.startsWith("data:")) {
65673
+ const base64Match = url2.match(/^data:[^;]+;base64,(.+)$/);
65674
+ if (base64Match) {
65675
+ inlineImages += `
65676
+ [Image base64:${base64Match[1]}]`;
65677
+ log(`[LiteLLM] Converted image_url to inline base64 for ${this.modelName}`);
65678
+ }
65679
+ } else if (url2) {
65680
+ inlineImages += `
65681
+ [Image URL: ${url2}]`;
65682
+ }
65683
+ } else {
65684
+ newContent.push(part);
65685
+ }
65686
+ }
65687
+ if (inlineImages) {
65688
+ const lastText = newContent.findLast((p) => p.type === "text");
65689
+ if (lastText) {
65690
+ lastText.text += inlineImages;
65691
+ } else {
65692
+ newContent.push({ type: "text", text: inlineImages.trim() });
65693
+ }
65694
+ }
65695
+ if (newContent.length === 1 && newContent[0].type === "text") {
65696
+ msg.content = newContent[0].text;
65697
+ } else if (newContent.length > 0) {
65698
+ msg.content = newContent;
65699
+ }
65700
+ }
65701
+ return messages;
65702
+ }
65703
+ checkVisionSupport() {
65704
+ try {
65705
+ const hash2 = createHash4("sha256").update(this.baseUrl).digest("hex").substring(0, 16);
65706
+ const cachePath = join21(homedir20(), ".claudish", `litellm-models-${hash2}.json`);
65707
+ if (!existsSync12(cachePath))
65708
+ return true;
65709
+ const cacheData = JSON.parse(readFileSync9(cachePath, "utf-8"));
65710
+ const model = cacheData.models?.find((m) => m.name === this.modelName);
65711
+ if (model && model.supportsVision === false) {
65712
+ log(`[LiteLLM] Model ${this.modelName} does not support vision, images will be stripped`);
65713
+ return false;
65714
+ }
65715
+ return true;
65716
+ } catch {
65717
+ return true;
65718
+ }
65602
65719
  }
65603
65720
  getProviderConfig() {
65604
65721
  return {
@@ -65638,15 +65755,31 @@ var init_litellm_handler = __esm(() => {
65638
65755
  payload.tool_choice = type;
65639
65756
  }
65640
65757
  }
65758
+ const extraHeaders = this.getExtraHeaders();
65759
+ if (extraHeaders) {
65760
+ payload.extra_headers = extraHeaders;
65761
+ }
65641
65762
  return payload;
65642
65763
  }
65764
+ getExtraHeaders() {
65765
+ const model = this.modelName.toLowerCase();
65766
+ const merged = {};
65767
+ let found = false;
65768
+ for (const { pattern, headers } of MODEL_EXTRA_HEADERS) {
65769
+ if (model.includes(pattern)) {
65770
+ Object.assign(merged, headers);
65771
+ found = true;
65772
+ }
65773
+ }
65774
+ return found ? merged : null;
65775
+ }
65643
65776
  };
65644
65777
  });
65645
65778
 
65646
65779
  // src/services/pricing-cache.ts
65647
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync17, existsSync as existsSync12, mkdirSync as mkdirSync17, statSync } from "node:fs";
65648
- import { homedir as homedir20 } from "node:os";
65649
- import { join as join21 } from "node:path";
65780
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync17, existsSync as existsSync13, mkdirSync as mkdirSync17, statSync } from "node:fs";
65781
+ import { homedir as homedir21 } from "node:os";
65782
+ import { join as join22 } from "node:path";
65650
65783
  function getDynamicPricingSync(provider, modelName) {
65651
65784
  if (provider === "openrouter") {
65652
65785
  const direct = pricingMap.get(modelName);
@@ -65711,12 +65844,12 @@ async function warmPricingCache() {
65711
65844
  }
65712
65845
  function loadDiskCache() {
65713
65846
  try {
65714
- if (!existsSync12(CACHE_FILE))
65847
+ if (!existsSync13(CACHE_FILE))
65715
65848
  return false;
65716
65849
  const stat = statSync(CACHE_FILE);
65717
65850
  const age = Date.now() - stat.mtimeMs;
65718
65851
  const isFresh = age < CACHE_TTL_MS;
65719
- const raw2 = readFileSync9(CACHE_FILE, "utf-8");
65852
+ const raw2 = readFileSync10(CACHE_FILE, "utf-8");
65720
65853
  const data = JSON.parse(raw2);
65721
65854
  for (const [key, pricing] of Object.entries(data)) {
65722
65855
  pricingMap.set(key, pricing);
@@ -65763,8 +65896,8 @@ var init_pricing_cache = __esm(() => {
65763
65896
  init_model_loader();
65764
65897
  init_remote_provider_types();
65765
65898
  pricingMap = new Map;
65766
- CACHE_DIR = join21(homedir20(), ".claudish");
65767
- CACHE_FILE = join21(CACHE_DIR, "pricing-cache.json");
65899
+ CACHE_DIR = join22(homedir21(), ".claudish");
65900
+ CACHE_FILE = join22(CACHE_DIR, "pricing-cache.json");
65768
65901
  CACHE_TTL_MS = 24 * 60 * 60 * 1000;
65769
65902
  PROVIDER_TO_OR_PREFIX = {
65770
65903
  openai: ["openai/"],
@@ -66047,9 +66180,9 @@ __export(exports_update_checker, {
66047
66180
  checkForUpdates: () => checkForUpdates
66048
66181
  });
66049
66182
  import { execSync } from "node:child_process";
66050
- import { existsSync as existsSync13, mkdirSync as mkdirSync18, readFileSync as readFileSync10, unlinkSync as unlinkSync4, writeFileSync as writeFileSync18 } from "node:fs";
66051
- import { homedir as homedir21, platform as platform2, tmpdir as tmpdir2 } from "node:os";
66052
- import { join as join22 } from "node:path";
66183
+ import { existsSync as existsSync14, mkdirSync as mkdirSync18, readFileSync as readFileSync11, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
66184
+ import { homedir as homedir22, platform as platform2, tmpdir as tmpdir2 } from "node:os";
66185
+ import { join as join23 } from "node:path";
66053
66186
  import { createInterface as createInterface2 } from "node:readline";
66054
66187
  function getUpdateCommand() {
66055
66188
  const scriptPath = process.argv[1] || "";
@@ -66061,27 +66194,27 @@ function getUpdateCommand() {
66061
66194
  function getCacheFilePath() {
66062
66195
  let cacheDir;
66063
66196
  if (isWindows2) {
66064
- const localAppData = process.env.LOCALAPPDATA || join22(homedir21(), "AppData", "Local");
66065
- cacheDir = join22(localAppData, "claudish");
66197
+ const localAppData = process.env.LOCALAPPDATA || join23(homedir22(), "AppData", "Local");
66198
+ cacheDir = join23(localAppData, "claudish");
66066
66199
  } else {
66067
- cacheDir = join22(homedir21(), ".cache", "claudish");
66200
+ cacheDir = join23(homedir22(), ".cache", "claudish");
66068
66201
  }
66069
66202
  try {
66070
- if (!existsSync13(cacheDir)) {
66203
+ if (!existsSync14(cacheDir)) {
66071
66204
  mkdirSync18(cacheDir, { recursive: true });
66072
66205
  }
66073
- return join22(cacheDir, "update-check.json");
66206
+ return join23(cacheDir, "update-check.json");
66074
66207
  } catch {
66075
- return join22(tmpdir2(), "claudish-update-check.json");
66208
+ return join23(tmpdir2(), "claudish-update-check.json");
66076
66209
  }
66077
66210
  }
66078
66211
  function readCache() {
66079
66212
  try {
66080
66213
  const cachePath = getCacheFilePath();
66081
- if (!existsSync13(cachePath)) {
66214
+ if (!existsSync14(cachePath)) {
66082
66215
  return null;
66083
66216
  }
66084
- const data = JSON.parse(readFileSync10(cachePath, "utf-8"));
66217
+ const data = JSON.parse(readFileSync11(cachePath, "utf-8"));
66085
66218
  return data;
66086
66219
  } catch {
66087
66220
  return null;
@@ -66104,8 +66237,8 @@ function isCacheValid(cache) {
66104
66237
  function clearCache() {
66105
66238
  try {
66106
66239
  const cachePath = getCacheFilePath();
66107
- if (existsSync13(cachePath)) {
66108
- unlinkSync4(cachePath);
66240
+ if (existsSync14(cachePath)) {
66241
+ unlinkSync5(cachePath);
66109
66242
  }
66110
66243
  } catch {}
66111
66244
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "4.6.8",
3
+ "version": "4.6.9",
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-02-14",
3
+ "lastUpdated": "2026-02-15",
4
4
  "source": "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
5
5
  "models": [
6
6
  {