claudish 4.6.7 → 4.6.8

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 +256 -20
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -32800,6 +32800,8 @@ var init_model_parser = __esm(() => {
32800
32800
  lc: "ollamacloud",
32801
32801
  meta: "ollamacloud",
32802
32802
  poe: "poe",
32803
+ litellm: "litellm",
32804
+ ll: "litellm",
32803
32805
  ollama: "ollama",
32804
32806
  lms: "lmstudio",
32805
32807
  lmstudio: "lmstudio",
@@ -32820,7 +32822,8 @@ var init_model_parser = __esm(() => {
32820
32822
  "opencode-zen",
32821
32823
  "vertex",
32822
32824
  "gemini-codeassist",
32823
- "poe"
32825
+ "poe",
32826
+ "litellm"
32824
32827
  ]);
32825
32828
  LOCAL_PROVIDERS = new Set(["ollama", "lmstudio", "vllm", "mlx"]);
32826
32829
  NATIVE_MODEL_PATTERNS = [
@@ -33041,7 +33044,8 @@ function resolveRemoteProvider(modelId) {
33041
33044
  ollamacloud: "ollamacloud",
33042
33045
  "opencode-zen": "opencode-zen",
33043
33046
  vertex: "vertex",
33044
- "gemini-codeassist": "gemini-codeassist"
33047
+ "gemini-codeassist": "gemini-codeassist",
33048
+ litellm: "litellm"
33045
33049
  };
33046
33050
  const mappedProviderName = providerNameMap[parsed.provider];
33047
33051
  if (mappedProviderName) {
@@ -33253,6 +33257,20 @@ var getRemoteProviders = () => [
33253
33257
  supportsJsonMode: false,
33254
33258
  supportsReasoning: true
33255
33259
  }
33260
+ },
33261
+ {
33262
+ name: "litellm",
33263
+ baseUrl: process.env.LITELLM_BASE_URL || "",
33264
+ apiPath: "/v1/chat/completions",
33265
+ apiKeyEnvVar: "LITELLM_API_KEY",
33266
+ prefixes: ["litellm/", "ll/"],
33267
+ capabilities: {
33268
+ supportsTools: true,
33269
+ supportsVision: true,
33270
+ supportsStreaming: true,
33271
+ supportsJsonMode: true,
33272
+ supportsReasoning: true
33273
+ }
33256
33274
  }
33257
33275
  ];
33258
33276
  var init_remote_provider_registry = __esm(() => {
@@ -33592,6 +33610,11 @@ var init_provider_resolver = __esm(() => {
33592
33610
  envVar: "ZAI_API_KEY",
33593
33611
  description: "Z.AI API Key",
33594
33612
  url: "https://z.ai/"
33613
+ },
33614
+ litellm: {
33615
+ envVar: "LITELLM_API_KEY",
33616
+ description: "LiteLLM API Key",
33617
+ url: "https://docs.litellm.ai/"
33595
33618
  }
33596
33619
  };
33597
33620
  PROVIDER_DISPLAY_NAMES = {
@@ -33607,7 +33630,8 @@ var init_provider_resolver = __esm(() => {
33607
33630
  "glm-coding": "GLM Coding",
33608
33631
  zai: "Z.AI",
33609
33632
  ollamacloud: "OllamaCloud",
33610
- "opencode-zen": "OpenCode Zen"
33633
+ "opencode-zen": "OpenCode Zen",
33634
+ litellm: "LiteLLM"
33611
33635
  };
33612
33636
  });
33613
33637
 
@@ -34890,7 +34914,7 @@ async function fetchGLMCodingModels2() {
34890
34914
  return [];
34891
34915
  }
34892
34916
  }
34893
- var __filename5, __dirname5, VERSION = "4.6.7", CACHE_MAX_AGE_DAYS3 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR3, ALL_MODELS_JSON_PATH2;
34917
+ var __filename5, __dirname5, VERSION = "4.6.8", CACHE_MAX_AGE_DAYS3 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR3, ALL_MODELS_JSON_PATH2;
34894
34918
  var init_cli = __esm(() => {
34895
34919
  init_config();
34896
34920
  init_model_loader();
@@ -65417,10 +65441,212 @@ var init_ollamacloud_handler = __esm(() => {
65417
65441
  init_remote_provider_types();
65418
65442
  });
65419
65443
 
65420
- // src/services/pricing-cache.ts
65421
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync16, existsSync as existsSync12, mkdirSync as mkdirSync16, statSync } from "node:fs";
65444
+ // src/handlers/shared/remote-provider-handler.ts
65445
+ import { writeFileSync as writeFileSync16, mkdirSync as mkdirSync16 } from "node:fs";
65422
65446
  import { homedir as homedir19 } from "node:os";
65423
65447
  import { join as join20 } from "node:path";
65448
+
65449
+ class RemoteProviderHandler {
65450
+ targetModel;
65451
+ modelName;
65452
+ apiKey;
65453
+ adapterManager;
65454
+ middlewareManager;
65455
+ port;
65456
+ sessionTotalCost = 0;
65457
+ sessionInputTokens = 0;
65458
+ sessionOutputTokens = 0;
65459
+ contextWindow = 200000;
65460
+ CLAUDE_INTERNAL_CONTEXT_MAX = 200000;
65461
+ constructor(targetModel, modelName, apiKey, port) {
65462
+ this.targetModel = targetModel;
65463
+ this.modelName = modelName;
65464
+ this.apiKey = apiKey;
65465
+ this.port = port;
65466
+ this.adapterManager = new AdapterManager(targetModel);
65467
+ this.middlewareManager = new MiddlewareManager;
65468
+ this.middlewareManager.register(new GeminiThoughtSignatureMiddleware);
65469
+ this.middlewareManager.initialize().catch((err) => log(`[Handler:${targetModel}] Middleware init error: ${err}`));
65470
+ }
65471
+ getAdditionalHeaders() {
65472
+ return {};
65473
+ }
65474
+ getApiEndpoint() {
65475
+ const config3 = this.getProviderConfig();
65476
+ return `${config3.baseUrl}${config3.apiPath}`;
65477
+ }
65478
+ supportsReasoning() {
65479
+ return false;
65480
+ }
65481
+ writeTokenFile(input, output) {
65482
+ try {
65483
+ const total = input + output;
65484
+ const leftPct = this.contextWindow > 0 ? Math.max(0, Math.min(100, Math.round((this.contextWindow - total) / this.contextWindow * 100))) : 100;
65485
+ const pricing = this.getPricing();
65486
+ const data = {
65487
+ input_tokens: input,
65488
+ output_tokens: output,
65489
+ total_tokens: total,
65490
+ total_cost: this.sessionTotalCost,
65491
+ context_window: this.contextWindow,
65492
+ context_left_percent: leftPct,
65493
+ is_free: pricing.isFree || false,
65494
+ is_estimated: pricing.isEstimate || false,
65495
+ provider_name: this.getProviderName(),
65496
+ model_name: this.modelName,
65497
+ updated_at: Date.now()
65498
+ };
65499
+ const claudishDir = join20(homedir19(), ".claudish");
65500
+ mkdirSync16(claudishDir, { recursive: true });
65501
+ writeFileSync16(join20(claudishDir, `tokens-${this.port}.json`), JSON.stringify(data), "utf-8");
65502
+ } catch (e) {
65503
+ log(`[Handler] Error writing token file: ${e}`);
65504
+ }
65505
+ }
65506
+ updateTokenTracking(inputTokens, outputTokens) {
65507
+ this.sessionInputTokens = inputTokens;
65508
+ this.sessionOutputTokens += outputTokens;
65509
+ const pricing = this.getPricing();
65510
+ const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
65511
+ this.sessionTotalCost += cost;
65512
+ this.writeTokenFile(inputTokens, this.sessionOutputTokens);
65513
+ }
65514
+ convertMessages(claudeRequest) {
65515
+ return convertMessagesToOpenAI(claudeRequest, this.targetModel, filterIdentity);
65516
+ }
65517
+ convertTools(claudeRequest) {
65518
+ return convertToolsToOpenAI(claudeRequest);
65519
+ }
65520
+ handleStreamingResponse(c, response, adapter, claudeRequest, toolNameMap) {
65521
+ return createStreamingResponseHandler(c, response, adapter, this.targetModel, this.middlewareManager, (input, output) => this.updateTokenTracking(input, output), claudeRequest.tools, toolNameMap);
65522
+ }
65523
+ async handle(c, payload) {
65524
+ const config3 = this.getProviderConfig();
65525
+ const { claudeRequest, droppedParams } = transformOpenAIToClaude(payload);
65526
+ const messages = this.convertMessages(claudeRequest);
65527
+ const tools = this.convertTools(claudeRequest);
65528
+ const systemPromptLength = typeof claudeRequest.system === "string" ? claudeRequest.system.length : 0;
65529
+ logStructured(`${config3.name} Request`, {
65530
+ targetModel: this.targetModel,
65531
+ originalModel: payload.model,
65532
+ messageCount: messages.length,
65533
+ toolCount: tools.length,
65534
+ systemPromptLength,
65535
+ maxTokens: claudeRequest.max_tokens
65536
+ });
65537
+ if (getLogLevel() === "debug") {
65538
+ const lastUserMsg = messages.filter((m) => m.role === "user").pop();
65539
+ if (lastUserMsg) {
65540
+ const content = typeof lastUserMsg.content === "string" ? lastUserMsg.content : JSON.stringify(lastUserMsg.content);
65541
+ log(`[${config3.name}] Last user message: ${truncateContent(content, 500)}`);
65542
+ }
65543
+ if (tools.length > 0) {
65544
+ const toolNames = tools.map((t) => t.function?.name || t.name).join(", ");
65545
+ log(`[${config3.name}] Tools: ${toolNames}`);
65546
+ }
65547
+ }
65548
+ const requestPayload = this.buildRequestPayload(claudeRequest, messages, tools);
65549
+ const adapter = this.adapterManager.getAdapter();
65550
+ if (typeof adapter.reset === "function")
65551
+ adapter.reset();
65552
+ adapter.prepareRequest(requestPayload, claudeRequest);
65553
+ const toolNameMap = adapter.getToolNameMap();
65554
+ await this.middlewareManager.beforeRequest({
65555
+ modelId: this.targetModel,
65556
+ messages,
65557
+ tools,
65558
+ stream: true
65559
+ });
65560
+ const endpoint = this.getApiEndpoint();
65561
+ const headers = {
65562
+ "Content-Type": "application/json",
65563
+ Authorization: `Bearer ${this.apiKey}`,
65564
+ ...this.getAdditionalHeaders()
65565
+ };
65566
+ log(`[${config3.name}] Calling API: ${endpoint}`);
65567
+ const response = await fetch(endpoint, {
65568
+ method: "POST",
65569
+ headers,
65570
+ body: JSON.stringify(requestPayload)
65571
+ });
65572
+ log(`[${config3.name}] Response status: ${response.status}`);
65573
+ if (!response.ok) {
65574
+ const errorText = await response.text();
65575
+ log(`[${config3.name}] Error: ${errorText}`);
65576
+ return c.json({ error: errorText }, response.status);
65577
+ }
65578
+ if (droppedParams.length > 0) {
65579
+ c.header("X-Dropped-Params", droppedParams.join(", "));
65580
+ }
65581
+ return this.handleStreamingResponse(c, response, adapter, claudeRequest, toolNameMap);
65582
+ }
65583
+ async shutdown() {}
65584
+ }
65585
+ var init_remote_provider_handler = __esm(() => {
65586
+ init_adapter_manager();
65587
+ init_middleware();
65588
+ init_transform();
65589
+ init_logger();
65590
+ init_openai_compat();
65591
+ });
65592
+
65593
+ // src/handlers/litellm-handler.ts
65594
+ var LiteLLMHandler;
65595
+ var init_litellm_handler = __esm(() => {
65596
+ init_remote_provider_handler();
65597
+ LiteLLMHandler = class LiteLLMHandler extends RemoteProviderHandler {
65598
+ baseUrl;
65599
+ constructor(targetModel, modelName, apiKey, port, baseUrl) {
65600
+ super(targetModel, modelName, apiKey, port);
65601
+ this.baseUrl = baseUrl;
65602
+ }
65603
+ getProviderConfig() {
65604
+ return {
65605
+ name: "litellm",
65606
+ baseUrl: this.baseUrl,
65607
+ apiPath: "/v1/chat/completions",
65608
+ apiKeyEnvVar: "LITELLM_API_KEY"
65609
+ };
65610
+ }
65611
+ getPricing() {
65612
+ return {
65613
+ inputCostPer1M: 1,
65614
+ outputCostPer1M: 4,
65615
+ isEstimate: true
65616
+ };
65617
+ }
65618
+ getProviderName() {
65619
+ return "LiteLLM";
65620
+ }
65621
+ buildRequestPayload(claudeRequest, messages, tools) {
65622
+ const payload = {
65623
+ model: this.modelName,
65624
+ messages,
65625
+ temperature: claudeRequest.temperature ?? 1,
65626
+ stream: true,
65627
+ stream_options: { include_usage: true },
65628
+ max_tokens: claudeRequest.max_tokens
65629
+ };
65630
+ if (tools.length > 0) {
65631
+ payload.tools = tools;
65632
+ }
65633
+ if (claudeRequest.tool_choice) {
65634
+ const { type, name } = claudeRequest.tool_choice;
65635
+ if (type === "tool" && name) {
65636
+ payload.tool_choice = { type: "function", function: { name } };
65637
+ } else if (type === "auto" || type === "none") {
65638
+ payload.tool_choice = type;
65639
+ }
65640
+ }
65641
+ return payload;
65642
+ }
65643
+ };
65644
+ });
65645
+
65646
+ // 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";
65424
65650
  function getDynamicPricingSync(provider, modelName) {
65425
65651
  if (provider === "openrouter") {
65426
65652
  const direct = pricingMap.get(modelName);
@@ -65502,12 +65728,12 @@ function loadDiskCache() {
65502
65728
  }
65503
65729
  function saveDiskCache() {
65504
65730
  try {
65505
- mkdirSync16(CACHE_DIR, { recursive: true });
65731
+ mkdirSync17(CACHE_DIR, { recursive: true });
65506
65732
  const data = {};
65507
65733
  for (const [key, pricing] of pricingMap) {
65508
65734
  data[key] = pricing;
65509
65735
  }
65510
- writeFileSync16(CACHE_FILE, JSON.stringify(data), "utf-8");
65736
+ writeFileSync17(CACHE_FILE, JSON.stringify(data), "utf-8");
65511
65737
  } catch (error46) {
65512
65738
  log(`[PricingCache] Error saving disk cache: ${error46}`);
65513
65739
  }
@@ -65537,8 +65763,8 @@ var init_pricing_cache = __esm(() => {
65537
65763
  init_model_loader();
65538
65764
  init_remote_provider_types();
65539
65765
  pricingMap = new Map;
65540
- CACHE_DIR = join20(homedir19(), ".claudish");
65541
- CACHE_FILE = join20(CACHE_DIR, "pricing-cache.json");
65766
+ CACHE_DIR = join21(homedir20(), ".claudish");
65767
+ CACHE_FILE = join21(CACHE_DIR, "pricing-cache.json");
65542
65768
  CACHE_TTL_MS = 24 * 60 * 60 * 1000;
65543
65769
  PROVIDER_TO_OR_PREFIX = {
65544
65770
  openai: ["openai/"],
@@ -65660,6 +65886,15 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
65660
65886
  } else if (resolved.provider.name === "ollamacloud") {
65661
65887
  handler = new OllamaCloudHandler(resolved.provider, resolved.modelName, apiKey, port);
65662
65888
  log(`[Proxy] Created OllamaCloud handler: ${resolved.modelName}`);
65889
+ } else if (resolved.provider.name === "litellm") {
65890
+ if (!resolved.provider.baseUrl) {
65891
+ console.error("Error: LITELLM_BASE_URL or --litellm-url is required for LiteLLM provider.");
65892
+ console.error("Set it with: export LITELLM_BASE_URL='https://your-litellm-instance.com'");
65893
+ console.error("Or use: claudish --litellm-url https://your-instance.com --model litellm@model 'task'");
65894
+ return null;
65895
+ }
65896
+ handler = new LiteLLMHandler(targetModel, resolved.modelName, apiKey, port, resolved.provider.baseUrl);
65897
+ log(`[Proxy] Created LiteLLM handler: ${resolved.modelName} (${resolved.provider.baseUrl})`);
65663
65898
  } else if (resolved.provider.name === "vertex") {
65664
65899
  const hasApiKey = !!process.env.VERTEX_API_KEY;
65665
65900
  const vertexConfig = getVertexConfig();
@@ -65794,6 +66029,7 @@ var init_proxy_server = __esm(() => {
65794
66029
  init_vertex_oauth_handler();
65795
66030
  init_poe_handler();
65796
66031
  init_ollamacloud_handler();
66032
+ init_litellm_handler();
65797
66033
  init_provider_registry();
65798
66034
  init_model_parser();
65799
66035
  init_remote_provider_registry();
@@ -65811,9 +66047,9 @@ __export(exports_update_checker, {
65811
66047
  checkForUpdates: () => checkForUpdates
65812
66048
  });
65813
66049
  import { execSync } from "node:child_process";
65814
- import { existsSync as existsSync13, mkdirSync as mkdirSync17, readFileSync as readFileSync10, unlinkSync as unlinkSync4, writeFileSync as writeFileSync17 } from "node:fs";
65815
- import { homedir as homedir20, platform as platform2, tmpdir as tmpdir2 } from "node:os";
65816
- import { join as join21 } from "node:path";
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";
65817
66053
  import { createInterface as createInterface2 } from "node:readline";
65818
66054
  function getUpdateCommand() {
65819
66055
  const scriptPath = process.argv[1] || "";
@@ -65825,18 +66061,18 @@ function getUpdateCommand() {
65825
66061
  function getCacheFilePath() {
65826
66062
  let cacheDir;
65827
66063
  if (isWindows2) {
65828
- const localAppData = process.env.LOCALAPPDATA || join21(homedir20(), "AppData", "Local");
65829
- cacheDir = join21(localAppData, "claudish");
66064
+ const localAppData = process.env.LOCALAPPDATA || join22(homedir21(), "AppData", "Local");
66065
+ cacheDir = join22(localAppData, "claudish");
65830
66066
  } else {
65831
- cacheDir = join21(homedir20(), ".cache", "claudish");
66067
+ cacheDir = join22(homedir21(), ".cache", "claudish");
65832
66068
  }
65833
66069
  try {
65834
66070
  if (!existsSync13(cacheDir)) {
65835
- mkdirSync17(cacheDir, { recursive: true });
66071
+ mkdirSync18(cacheDir, { recursive: true });
65836
66072
  }
65837
- return join21(cacheDir, "update-check.json");
66073
+ return join22(cacheDir, "update-check.json");
65838
66074
  } catch {
65839
- return join21(tmpdir2(), "claudish-update-check.json");
66075
+ return join22(tmpdir2(), "claudish-update-check.json");
65840
66076
  }
65841
66077
  }
65842
66078
  function readCache() {
@@ -65858,7 +66094,7 @@ function writeCache(latestVersion) {
65858
66094
  lastCheck: Date.now(),
65859
66095
  latestVersion
65860
66096
  };
65861
- writeFileSync17(cachePath, JSON.stringify(data), "utf-8");
66097
+ writeFileSync18(cachePath, JSON.stringify(data), "utf-8");
65862
66098
  } catch {}
65863
66099
  }
65864
66100
  function isCacheValid(cache) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "4.6.7",
3
+ "version": "4.6.8",
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",