claudish 6.13.1 → 6.14.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 +1093 -1676
- package/package.json +5 -5
- package/recommended-models.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17321,188 +17321,6 @@ var init_openrouter_queue = __esm(() => {
|
|
|
17321
17321
|
init_logger();
|
|
17322
17322
|
});
|
|
17323
17323
|
|
|
17324
|
-
// src/model-loader.ts
|
|
17325
|
-
import { readFileSync as readFileSync2, existsSync as existsSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
17326
|
-
import { join as join4, dirname } from "path";
|
|
17327
|
-
import { fileURLToPath } from "url";
|
|
17328
|
-
import { homedir as homedir3 } from "os";
|
|
17329
|
-
import { createHash } from "crypto";
|
|
17330
|
-
function getRecommendedModelsPath() {
|
|
17331
|
-
return join4(__dirname2, "../recommended-models.json");
|
|
17332
|
-
}
|
|
17333
|
-
function loadRecommendedModelsJSON() {
|
|
17334
|
-
if (_cachedRecommendedModels) {
|
|
17335
|
-
return _cachedRecommendedModels;
|
|
17336
|
-
}
|
|
17337
|
-
if (existsSync3(RECOMMENDED_CACHE_PATH)) {
|
|
17338
|
-
try {
|
|
17339
|
-
const cacheData = JSON.parse(readFileSync2(RECOMMENDED_CACHE_PATH, "utf-8"));
|
|
17340
|
-
if (cacheData.models && cacheData.models.length > 0) {
|
|
17341
|
-
const generatedAt = cacheData.generatedAt;
|
|
17342
|
-
if (generatedAt) {
|
|
17343
|
-
const ageHours = (Date.now() - new Date(generatedAt).getTime()) / (1000 * 60 * 60);
|
|
17344
|
-
if (ageHours <= RECOMMENDED_CACHE_MAX_AGE_HOURS) {
|
|
17345
|
-
_cachedRecommendedModels = cacheData;
|
|
17346
|
-
return cacheData;
|
|
17347
|
-
}
|
|
17348
|
-
} else {
|
|
17349
|
-
_cachedRecommendedModels = cacheData;
|
|
17350
|
-
return cacheData;
|
|
17351
|
-
}
|
|
17352
|
-
}
|
|
17353
|
-
} catch {}
|
|
17354
|
-
}
|
|
17355
|
-
const jsonPath = getRecommendedModelsPath();
|
|
17356
|
-
if (!existsSync3(jsonPath)) {
|
|
17357
|
-
throw new Error(`recommended-models.json not found at ${jsonPath}. ` + `Run 'claudish --update-models' to fetch the latest model list.`);
|
|
17358
|
-
}
|
|
17359
|
-
try {
|
|
17360
|
-
const jsonContent = readFileSync2(jsonPath, "utf-8");
|
|
17361
|
-
_cachedRecommendedModels = JSON.parse(jsonContent);
|
|
17362
|
-
return _cachedRecommendedModels;
|
|
17363
|
-
} catch (error2) {
|
|
17364
|
-
throw new Error(`Failed to parse recommended-models.json: ${error2}`);
|
|
17365
|
-
}
|
|
17366
|
-
}
|
|
17367
|
-
function loadModelInfo() {
|
|
17368
|
-
if (_cachedModelInfo) {
|
|
17369
|
-
return _cachedModelInfo;
|
|
17370
|
-
}
|
|
17371
|
-
const data = loadRecommendedModelsJSON();
|
|
17372
|
-
const modelInfo = {};
|
|
17373
|
-
for (const model of data.models) {
|
|
17374
|
-
modelInfo[model.id] = {
|
|
17375
|
-
name: model.name,
|
|
17376
|
-
description: model.description,
|
|
17377
|
-
priority: model.priority,
|
|
17378
|
-
provider: model.provider
|
|
17379
|
-
};
|
|
17380
|
-
}
|
|
17381
|
-
modelInfo.custom = {
|
|
17382
|
-
name: "Custom Model",
|
|
17383
|
-
description: "Enter any OpenRouter model ID manually",
|
|
17384
|
-
priority: 999,
|
|
17385
|
-
provider: "Custom"
|
|
17386
|
-
};
|
|
17387
|
-
_cachedModelInfo = modelInfo;
|
|
17388
|
-
return modelInfo;
|
|
17389
|
-
}
|
|
17390
|
-
function getAvailableModels() {
|
|
17391
|
-
if (_cachedModelIds) {
|
|
17392
|
-
return _cachedModelIds;
|
|
17393
|
-
}
|
|
17394
|
-
const data = loadRecommendedModelsJSON();
|
|
17395
|
-
const modelIds = data.models.sort((a, b) => a.priority - b.priority).map((m) => m.id);
|
|
17396
|
-
const result = [...modelIds, "custom"];
|
|
17397
|
-
_cachedModelIds = result;
|
|
17398
|
-
return result;
|
|
17399
|
-
}
|
|
17400
|
-
function getCachedOpenRouterModels() {
|
|
17401
|
-
return _cachedOpenRouterModels;
|
|
17402
|
-
}
|
|
17403
|
-
async function ensureOpenRouterModelsLoaded() {
|
|
17404
|
-
if (_cachedOpenRouterModels)
|
|
17405
|
-
return _cachedOpenRouterModels;
|
|
17406
|
-
try {
|
|
17407
|
-
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
17408
|
-
if (response.ok) {
|
|
17409
|
-
const data = await response.json();
|
|
17410
|
-
_cachedOpenRouterModels = data.data || [];
|
|
17411
|
-
return _cachedOpenRouterModels;
|
|
17412
|
-
}
|
|
17413
|
-
} catch {}
|
|
17414
|
-
return [];
|
|
17415
|
-
}
|
|
17416
|
-
async function fetchLiteLLMModels(baseUrl, apiKey, forceUpdate = false) {
|
|
17417
|
-
const hash = createHash("sha256").update(baseUrl).digest("hex").substring(0, 16);
|
|
17418
|
-
const cacheDir = join4(homedir3(), ".claudish");
|
|
17419
|
-
const cachePath = join4(cacheDir, `litellm-models-${hash}.json`);
|
|
17420
|
-
if (!forceUpdate && existsSync3(cachePath)) {
|
|
17421
|
-
try {
|
|
17422
|
-
const cacheData = JSON.parse(readFileSync2(cachePath, "utf-8"));
|
|
17423
|
-
const timestamp = new Date(cacheData.timestamp);
|
|
17424
|
-
const now = new Date;
|
|
17425
|
-
const ageInHours = (now.getTime() - timestamp.getTime()) / (1000 * 60 * 60);
|
|
17426
|
-
if (ageInHours < LITELLM_CACHE_MAX_AGE_HOURS) {
|
|
17427
|
-
return cacheData.models;
|
|
17428
|
-
}
|
|
17429
|
-
} catch {}
|
|
17430
|
-
}
|
|
17431
|
-
try {
|
|
17432
|
-
const url = `${baseUrl.replace(/\/$/, "")}/model_group/info`;
|
|
17433
|
-
const response = await fetch(url, {
|
|
17434
|
-
headers: {
|
|
17435
|
-
Authorization: `Bearer ${apiKey}`
|
|
17436
|
-
},
|
|
17437
|
-
signal: AbortSignal.timeout(1e4)
|
|
17438
|
-
});
|
|
17439
|
-
if (!response.ok) {
|
|
17440
|
-
console.error(`Failed to fetch LiteLLM models: ${response.status} ${response.statusText}`);
|
|
17441
|
-
if (existsSync3(cachePath)) {
|
|
17442
|
-
try {
|
|
17443
|
-
const cacheData2 = JSON.parse(readFileSync2(cachePath, "utf-8"));
|
|
17444
|
-
return cacheData2.models;
|
|
17445
|
-
} catch {
|
|
17446
|
-
return [];
|
|
17447
|
-
}
|
|
17448
|
-
}
|
|
17449
|
-
return [];
|
|
17450
|
-
}
|
|
17451
|
-
const responseData = await response.json();
|
|
17452
|
-
const rawModels = responseData.data || responseData;
|
|
17453
|
-
const transformedModels = rawModels.filter((m) => m.mode === "chat" && m.supports_function_calling).map((m) => {
|
|
17454
|
-
const inputCostPerM = (m.input_cost_per_token || 0) * 1e6;
|
|
17455
|
-
const outputCostPerM = (m.output_cost_per_token || 0) * 1e6;
|
|
17456
|
-
const avgCost = (inputCostPerM + outputCostPerM) / 2;
|
|
17457
|
-
const isFree = inputCostPerM === 0 && outputCostPerM === 0;
|
|
17458
|
-
const contextLength = m.max_input_tokens || 128000;
|
|
17459
|
-
const contextStr = contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`;
|
|
17460
|
-
return {
|
|
17461
|
-
id: `litellm@${m.model_group}`,
|
|
17462
|
-
name: m.model_group,
|
|
17463
|
-
description: `LiteLLM model (providers: ${m.providers.join(", ")})`,
|
|
17464
|
-
provider: "LiteLLM",
|
|
17465
|
-
pricing: {
|
|
17466
|
-
input: isFree ? "FREE" : `$${inputCostPerM.toFixed(2)}`,
|
|
17467
|
-
output: isFree ? "FREE" : `$${outputCostPerM.toFixed(2)}`,
|
|
17468
|
-
average: isFree ? "FREE" : `$${avgCost.toFixed(2)}/1M`
|
|
17469
|
-
},
|
|
17470
|
-
context: contextStr,
|
|
17471
|
-
contextLength,
|
|
17472
|
-
supportsTools: m.supports_function_calling || false,
|
|
17473
|
-
supportsReasoning: m.supports_reasoning || false,
|
|
17474
|
-
supportsVision: m.supports_vision || false,
|
|
17475
|
-
isFree,
|
|
17476
|
-
source: "LiteLLM"
|
|
17477
|
-
};
|
|
17478
|
-
});
|
|
17479
|
-
mkdirSync4(cacheDir, { recursive: true });
|
|
17480
|
-
const cacheData = {
|
|
17481
|
-
timestamp: new Date().toISOString(),
|
|
17482
|
-
models: transformedModels
|
|
17483
|
-
};
|
|
17484
|
-
writeFileSync4(cachePath, JSON.stringify(cacheData, null, 2), "utf-8");
|
|
17485
|
-
return transformedModels;
|
|
17486
|
-
} catch (error2) {
|
|
17487
|
-
console.error(`Failed to fetch LiteLLM models: ${error2}`);
|
|
17488
|
-
if (existsSync3(cachePath)) {
|
|
17489
|
-
try {
|
|
17490
|
-
const cacheData = JSON.parse(readFileSync2(cachePath, "utf-8"));
|
|
17491
|
-
return cacheData.models;
|
|
17492
|
-
} catch {
|
|
17493
|
-
return [];
|
|
17494
|
-
}
|
|
17495
|
-
}
|
|
17496
|
-
return [];
|
|
17497
|
-
}
|
|
17498
|
-
}
|
|
17499
|
-
var __filename2, __dirname2, _cachedModelInfo = null, _cachedModelIds = null, _cachedRecommendedModels = null, RECOMMENDED_CACHE_PATH, RECOMMENDED_CACHE_MAX_AGE_HOURS = 12, _cachedOpenRouterModels = null, LITELLM_CACHE_MAX_AGE_HOURS = 24;
|
|
17500
|
-
var init_model_loader = __esm(() => {
|
|
17501
|
-
__filename2 = fileURLToPath(import.meta.url);
|
|
17502
|
-
__dirname2 = dirname(__filename2);
|
|
17503
|
-
RECOMMENDED_CACHE_PATH = join4(homedir3(), ".claudish", "recommended-models-cache.json");
|
|
17504
|
-
});
|
|
17505
|
-
|
|
17506
17324
|
// src/providers/transport/openrouter.ts
|
|
17507
17325
|
class OpenRouterProviderTransport {
|
|
17508
17326
|
name = "openrouter";
|
|
@@ -17510,10 +17328,8 @@ class OpenRouterProviderTransport {
|
|
|
17510
17328
|
streamFormat = "openai-sse";
|
|
17511
17329
|
apiKey;
|
|
17512
17330
|
queue;
|
|
17513
|
-
|
|
17514
|
-
constructor(apiKey, modelId) {
|
|
17331
|
+
constructor(apiKey, _modelId) {
|
|
17515
17332
|
this.apiKey = apiKey;
|
|
17516
|
-
this.modelId = modelId || "";
|
|
17517
17333
|
this.queue = OpenRouterRequestQueue.getInstance();
|
|
17518
17334
|
}
|
|
17519
17335
|
overrideStreamFormat() {
|
|
@@ -17533,15 +17349,12 @@ class OpenRouterProviderTransport {
|
|
|
17533
17349
|
return this.queue.enqueue(fetchFn);
|
|
17534
17350
|
}
|
|
17535
17351
|
getContextWindow() {
|
|
17536
|
-
|
|
17537
|
-
const model = models?.find((m) => m.id === this.modelId);
|
|
17538
|
-
return model?.context_length || model?.top_provider?.context_length || 0;
|
|
17352
|
+
return 0;
|
|
17539
17353
|
}
|
|
17540
17354
|
}
|
|
17541
17355
|
var OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
17542
17356
|
var init_openrouter = __esm(() => {
|
|
17543
17357
|
init_openrouter_queue();
|
|
17544
|
-
init_model_loader();
|
|
17545
17358
|
});
|
|
17546
17359
|
|
|
17547
17360
|
// src/adapters/tool-name-utils.ts
|
|
@@ -17621,11 +17434,7 @@ var init_remote_provider_types = __esm(() => {
|
|
|
17621
17434
|
// src/adapters/model-catalog.ts
|
|
17622
17435
|
function lookupModel(modelId) {
|
|
17623
17436
|
const lower = modelId.toLowerCase();
|
|
17624
|
-
|
|
17625
|
-
if (lower.includes("@"))
|
|
17626
|
-
unprefixed = lower.substring(lower.indexOf("@") + 1);
|
|
17627
|
-
else if (lower.includes("/"))
|
|
17628
|
-
unprefixed = lower.substring(lower.lastIndexOf("/") + 1);
|
|
17437
|
+
const unprefixed = lower.includes("/") ? lower.substring(lower.lastIndexOf("/") + 1) : lower;
|
|
17629
17438
|
for (const entry of MODEL_CATALOG) {
|
|
17630
17439
|
if (unprefixed.includes(entry.pattern) || lower.includes(entry.pattern)) {
|
|
17631
17440
|
return entry;
|
|
@@ -17975,7 +17784,7 @@ var init_openai_tools = __esm(() => {
|
|
|
17975
17784
|
function matchesModelFamily(modelId, family) {
|
|
17976
17785
|
const lower = modelId.toLowerCase();
|
|
17977
17786
|
const fam = family.toLowerCase();
|
|
17978
|
-
return lower.startsWith(fam) || lower.includes(`/${fam}`)
|
|
17787
|
+
return lower.startsWith(fam) || lower.includes(`/${fam}`);
|
|
17979
17788
|
}
|
|
17980
17789
|
|
|
17981
17790
|
class BaseAPIFormat {
|
|
@@ -20949,9 +20758,9 @@ var init_middleware = __esm(() => {
|
|
|
20949
20758
|
});
|
|
20950
20759
|
|
|
20951
20760
|
// src/handlers/shared/token-tracker.ts
|
|
20952
|
-
import { mkdirSync as
|
|
20953
|
-
import { homedir as
|
|
20954
|
-
import { join as
|
|
20761
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
20762
|
+
import { homedir as homedir3 } from "os";
|
|
20763
|
+
import { join as join4 } from "path";
|
|
20955
20764
|
|
|
20956
20765
|
class TokenTracker {
|
|
20957
20766
|
port;
|
|
@@ -21085,9 +20894,9 @@ class TokenTracker {
|
|
|
21085
20894
|
if (this.quotaRemaining !== undefined) {
|
|
21086
20895
|
data.quota_remaining = this.quotaRemaining;
|
|
21087
20896
|
}
|
|
21088
|
-
const claudishDir =
|
|
21089
|
-
|
|
21090
|
-
|
|
20897
|
+
const claudishDir = join4(homedir3(), ".claudish");
|
|
20898
|
+
mkdirSync4(claudishDir, { recursive: true });
|
|
20899
|
+
writeFileSync4(join4(claudishDir, `tokens-${this.port}.json`), JSON.stringify(data), "utf-8");
|
|
21091
20900
|
} catch (e) {
|
|
21092
20901
|
log(`[TokenTracker] Error writing token file: ${e}`);
|
|
21093
20902
|
}
|
|
@@ -22041,21 +21850,21 @@ __export(exports_profile_config, {
|
|
|
22041
21850
|
configExistsForScope: () => configExistsForScope,
|
|
22042
21851
|
configExists: () => configExists
|
|
22043
21852
|
});
|
|
22044
|
-
import { existsSync as
|
|
22045
|
-
import { homedir as
|
|
22046
|
-
import { join as
|
|
21853
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
21854
|
+
import { homedir as homedir4 } from "os";
|
|
21855
|
+
import { join as join5 } from "path";
|
|
22047
21856
|
function ensureConfigDir() {
|
|
22048
|
-
if (!
|
|
22049
|
-
|
|
21857
|
+
if (!existsSync3(CONFIG_DIR)) {
|
|
21858
|
+
mkdirSync5(CONFIG_DIR, { recursive: true });
|
|
22050
21859
|
}
|
|
22051
21860
|
}
|
|
22052
21861
|
function loadConfig() {
|
|
22053
21862
|
ensureConfigDir();
|
|
22054
|
-
if (!
|
|
21863
|
+
if (!existsSync3(CONFIG_FILE)) {
|
|
22055
21864
|
return { ...DEFAULT_CONFIG };
|
|
22056
21865
|
}
|
|
22057
21866
|
try {
|
|
22058
|
-
const content =
|
|
21867
|
+
const content = readFileSync2(CONFIG_FILE, "utf-8");
|
|
22059
21868
|
const config2 = JSON.parse(content);
|
|
22060
21869
|
const merged = {
|
|
22061
21870
|
version: config2.version || DEFAULT_CONFIG.version,
|
|
@@ -22088,31 +21897,31 @@ function loadConfig() {
|
|
|
22088
21897
|
}
|
|
22089
21898
|
function saveConfig(config2) {
|
|
22090
21899
|
ensureConfigDir();
|
|
22091
|
-
|
|
21900
|
+
writeFileSync5(CONFIG_FILE, JSON.stringify(config2, null, 2), "utf-8");
|
|
22092
21901
|
}
|
|
22093
21902
|
function configExists() {
|
|
22094
|
-
return
|
|
21903
|
+
return existsSync3(CONFIG_FILE);
|
|
22095
21904
|
}
|
|
22096
21905
|
function getConfigPath() {
|
|
22097
21906
|
return CONFIG_FILE;
|
|
22098
21907
|
}
|
|
22099
21908
|
function getLocalConfigPath() {
|
|
22100
|
-
return
|
|
21909
|
+
return join5(process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
22101
21910
|
}
|
|
22102
21911
|
function localConfigExists() {
|
|
22103
|
-
return
|
|
21912
|
+
return existsSync3(getLocalConfigPath());
|
|
22104
21913
|
}
|
|
22105
21914
|
function isProjectDirectory() {
|
|
22106
21915
|
const cwd = process.cwd();
|
|
22107
|
-
return [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", ".claudish.json"].some((f) =>
|
|
21916
|
+
return [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", ".claudish.json"].some((f) => existsSync3(join5(cwd, f)));
|
|
22108
21917
|
}
|
|
22109
21918
|
function loadLocalConfig() {
|
|
22110
21919
|
const localPath = getLocalConfigPath();
|
|
22111
|
-
if (!
|
|
21920
|
+
if (!existsSync3(localPath)) {
|
|
22112
21921
|
return null;
|
|
22113
21922
|
}
|
|
22114
21923
|
try {
|
|
22115
|
-
const content =
|
|
21924
|
+
const content = readFileSync2(localPath, "utf-8");
|
|
22116
21925
|
const config2 = JSON.parse(content);
|
|
22117
21926
|
const local = {
|
|
22118
21927
|
version: config2.version || DEFAULT_CONFIG.version,
|
|
@@ -22129,7 +21938,7 @@ function loadLocalConfig() {
|
|
|
22129
21938
|
}
|
|
22130
21939
|
}
|
|
22131
21940
|
function saveLocalConfig(config2) {
|
|
22132
|
-
|
|
21941
|
+
writeFileSync5(getLocalConfigPath(), JSON.stringify(config2, null, 2), "utf-8");
|
|
22133
21942
|
}
|
|
22134
21943
|
function loadConfigForScope(scope) {
|
|
22135
21944
|
if (scope === "local") {
|
|
@@ -22349,8 +22158,8 @@ function removeEndpoint(name) {
|
|
|
22349
22158
|
}
|
|
22350
22159
|
var CONFIG_DIR, CONFIG_FILE, LOCAL_CONFIG_FILENAME = ".claudish.json", DEFAULT_CONFIG;
|
|
22351
22160
|
var init_profile_config = __esm(() => {
|
|
22352
|
-
CONFIG_DIR =
|
|
22353
|
-
CONFIG_FILE =
|
|
22161
|
+
CONFIG_DIR = join5(homedir4(), ".claudish");
|
|
22162
|
+
CONFIG_FILE = join5(CONFIG_DIR, "config.json");
|
|
22354
22163
|
DEFAULT_CONFIG = {
|
|
22355
22164
|
version: "1.0.0",
|
|
22356
22165
|
defaultProfile: "default",
|
|
@@ -22367,11 +22176,12 @@ var init_profile_config = __esm(() => {
|
|
|
22367
22176
|
});
|
|
22368
22177
|
|
|
22369
22178
|
// src/version.ts
|
|
22370
|
-
var VERSION = "6.
|
|
22179
|
+
var VERSION = "6.14.0";
|
|
22371
22180
|
|
|
22372
22181
|
// src/telemetry.ts
|
|
22373
22182
|
var exports_telemetry = {};
|
|
22374
22183
|
__export(exports_telemetry, {
|
|
22184
|
+
setClaudeCodeRunning: () => setClaudeCodeRunning,
|
|
22375
22185
|
sanitizeModelId: () => sanitizeModelId,
|
|
22376
22186
|
sanitizeMessage: () => sanitizeMessage,
|
|
22377
22187
|
runConsentPrompt: () => runConsentPrompt,
|
|
@@ -22581,6 +22391,8 @@ async function sendReport(report) {
|
|
|
22581
22391
|
function showConsentPromptAsync(ctx) {
|
|
22582
22392
|
if (consentPromptActive)
|
|
22583
22393
|
return;
|
|
22394
|
+
if (claudeCodeRunning)
|
|
22395
|
+
return;
|
|
22584
22396
|
try {
|
|
22585
22397
|
const profileConfig = loadConfig();
|
|
22586
22398
|
if (profileConfig.telemetry?.askedAt !== undefined)
|
|
@@ -22654,9 +22466,12 @@ function initTelemetry(config2) {
|
|
|
22654
22466
|
claudishVersion = getVersion();
|
|
22655
22467
|
installMethod = detectInstallMethod();
|
|
22656
22468
|
}
|
|
22469
|
+
function setClaudeCodeRunning(running) {
|
|
22470
|
+
claudeCodeRunning = running;
|
|
22471
|
+
}
|
|
22657
22472
|
function reportError2(ctx) {
|
|
22658
22473
|
if (!initialized || !consentEnabled) {
|
|
22659
|
-
if (initialized && !consentEnabled && ctx.isInteractive && process.stderr.isTTY) {
|
|
22474
|
+
if (initialized && !consentEnabled && ctx.isInteractive && process.stderr.isTTY && !claudeCodeRunning) {
|
|
22660
22475
|
showConsentPromptAsync(ctx);
|
|
22661
22476
|
}
|
|
22662
22477
|
return;
|
|
@@ -22759,7 +22574,7 @@ Usage: claudish telemetry on|off|status|reset
|
|
|
22759
22574
|
process.exit(1);
|
|
22760
22575
|
}
|
|
22761
22576
|
}
|
|
22762
|
-
var TELEMETRY_ENDPOINT = "https://claudish.com/v1/report", MAX_REPORT_BYTES = 4096, KNOWN_PUBLIC_HOSTS, PUBLIC_PROVIDERS, consentEnabled = false, sessionId = "", initialized = false, claudishVersion = "", installMethod = "unknown", consentPromptActive = false;
|
|
22577
|
+
var TELEMETRY_ENDPOINT = "https://claudish.com/v1/report", MAX_REPORT_BYTES = 4096, KNOWN_PUBLIC_HOSTS, PUBLIC_PROVIDERS, consentEnabled = false, sessionId = "", initialized = false, claudishVersion = "", installMethod = "unknown", consentPromptActive = false, claudeCodeRunning = false;
|
|
22763
22578
|
var init_telemetry = __esm(() => {
|
|
22764
22579
|
init_profile_config();
|
|
22765
22580
|
init_logger();
|
|
@@ -22791,9 +22606,9 @@ var init_telemetry = __esm(() => {
|
|
|
22791
22606
|
});
|
|
22792
22607
|
|
|
22793
22608
|
// src/providers/provider-definitions.ts
|
|
22794
|
-
import { existsSync as
|
|
22795
|
-
import { join as
|
|
22796
|
-
import { homedir as
|
|
22609
|
+
import { existsSync as existsSync4 } from "fs";
|
|
22610
|
+
import { join as join6 } from "path";
|
|
22611
|
+
import { homedir as homedir5 } from "os";
|
|
22797
22612
|
function ensureProviderByNameCache() {
|
|
22798
22613
|
if (!_providerByNameCache) {
|
|
22799
22614
|
_providerByNameCache = new Map;
|
|
@@ -22933,7 +22748,7 @@ function isProviderAvailable(def) {
|
|
|
22933
22748
|
}
|
|
22934
22749
|
if (def.oauthFallback) {
|
|
22935
22750
|
try {
|
|
22936
|
-
if (
|
|
22751
|
+
if (existsSync4(join6(homedir5(), ".claudish", def.oauthFallback)))
|
|
22937
22752
|
return true;
|
|
22938
22753
|
} catch {}
|
|
22939
22754
|
}
|
|
@@ -23552,25 +23367,25 @@ var init_model_parser = __esm(() => {
|
|
|
23552
23367
|
|
|
23553
23368
|
// src/stats-buffer.ts
|
|
23554
23369
|
import {
|
|
23555
|
-
existsSync as
|
|
23556
|
-
mkdirSync as
|
|
23557
|
-
readFileSync as
|
|
23370
|
+
existsSync as existsSync5,
|
|
23371
|
+
mkdirSync as mkdirSync6,
|
|
23372
|
+
readFileSync as readFileSync3,
|
|
23558
23373
|
renameSync,
|
|
23559
23374
|
unlinkSync as unlinkSync2,
|
|
23560
|
-
writeFileSync as
|
|
23375
|
+
writeFileSync as writeFileSync6
|
|
23561
23376
|
} from "fs";
|
|
23562
|
-
import { homedir as
|
|
23563
|
-
import { join as
|
|
23377
|
+
import { homedir as homedir6 } from "os";
|
|
23378
|
+
import { join as join7 } from "path";
|
|
23564
23379
|
function ensureDir() {
|
|
23565
|
-
if (!
|
|
23566
|
-
|
|
23380
|
+
if (!existsSync5(CLAUDISH_DIR)) {
|
|
23381
|
+
mkdirSync6(CLAUDISH_DIR, { recursive: true });
|
|
23567
23382
|
}
|
|
23568
23383
|
}
|
|
23569
23384
|
function readFromDisk() {
|
|
23570
23385
|
try {
|
|
23571
|
-
if (!
|
|
23386
|
+
if (!existsSync5(BUFFER_FILE))
|
|
23572
23387
|
return [];
|
|
23573
|
-
const raw2 =
|
|
23388
|
+
const raw2 = readFileSync3(BUFFER_FILE, "utf-8");
|
|
23574
23389
|
const parsed = JSON.parse(raw2);
|
|
23575
23390
|
if (!Array.isArray(parsed.events))
|
|
23576
23391
|
return [];
|
|
@@ -23594,8 +23409,8 @@ function writeToDisk(events) {
|
|
|
23594
23409
|
ensureDir();
|
|
23595
23410
|
const trimmed = enforceSizeCap([...events]);
|
|
23596
23411
|
const payload = { version: 1, events: trimmed };
|
|
23597
|
-
const tmpFile =
|
|
23598
|
-
|
|
23412
|
+
const tmpFile = join7(CLAUDISH_DIR, `stats-buffer.tmp.${process.pid}.json`);
|
|
23413
|
+
writeFileSync6(tmpFile, JSON.stringify(payload, null, 2), "utf-8");
|
|
23599
23414
|
renameSync(tmpFile, BUFFER_FILE);
|
|
23600
23415
|
memoryCache = trimmed;
|
|
23601
23416
|
} catch {}
|
|
@@ -23639,7 +23454,7 @@ function clearBuffer() {
|
|
|
23639
23454
|
try {
|
|
23640
23455
|
memoryCache = [];
|
|
23641
23456
|
eventsSinceLastFlush = 0;
|
|
23642
|
-
if (
|
|
23457
|
+
if (existsSync5(BUFFER_FILE)) {
|
|
23643
23458
|
unlinkSync2(BUFFER_FILE);
|
|
23644
23459
|
}
|
|
23645
23460
|
} catch {}
|
|
@@ -23668,8 +23483,8 @@ function syncFlushOnExit() {
|
|
|
23668
23483
|
var BUFFER_MAX_BYTES, CLAUDISH_DIR, BUFFER_FILE, memoryCache = null, eventsSinceLastFlush = 0, lastFlushTime, flushScheduled = false;
|
|
23669
23484
|
var init_stats_buffer = __esm(() => {
|
|
23670
23485
|
BUFFER_MAX_BYTES = 64 * 1024;
|
|
23671
|
-
CLAUDISH_DIR =
|
|
23672
|
-
BUFFER_FILE =
|
|
23486
|
+
CLAUDISH_DIR = join7(homedir6(), ".claudish");
|
|
23487
|
+
BUFFER_FILE = join7(CLAUDISH_DIR, "stats-buffer.json");
|
|
23673
23488
|
lastFlushTime = Date.now();
|
|
23674
23489
|
process.on("exit", syncFlushOnExit);
|
|
23675
23490
|
process.on("SIGTERM", () => {
|
|
@@ -24115,29 +23930,34 @@ class ComposedHandler {
|
|
|
24115
23930
|
middlewareManager;
|
|
24116
23931
|
tokenTracker;
|
|
24117
23932
|
targetModel;
|
|
23933
|
+
bareModelName;
|
|
24118
23934
|
options;
|
|
24119
23935
|
isInteractive;
|
|
24120
23936
|
pendingFallbackMeta;
|
|
24121
23937
|
constructor(provider, targetModel, modelName, port, options = {}) {
|
|
23938
|
+
if (modelName.includes("@")) {
|
|
23939
|
+
throw new Error(`ComposedHandler: modelName must not contain '@' (got "${modelName}"). ` + `Strip the provider routing prefix before passing modelName. ` + `If you need the full routed form, pass it as targetModel.`);
|
|
23940
|
+
}
|
|
24122
23941
|
this.provider = provider;
|
|
24123
23942
|
this.targetModel = targetModel;
|
|
23943
|
+
this.bareModelName = modelName;
|
|
24124
23944
|
this.options = options;
|
|
24125
23945
|
this.explicitAdapter = options.adapter;
|
|
24126
23946
|
this.isInteractive = options.isInteractive ?? false;
|
|
24127
|
-
this.adapterManager = new DialectManager(
|
|
23947
|
+
this.adapterManager = new DialectManager(this.bareModelName);
|
|
24128
23948
|
const resolvedModelAdapter = this.adapterManager.getAdapter();
|
|
24129
23949
|
if (resolvedModelAdapter.getName() !== "DefaultAPIFormat") {
|
|
24130
23950
|
this.modelAdapter = resolvedModelAdapter;
|
|
24131
23951
|
}
|
|
24132
23952
|
this.middlewareManager = new MiddlewareManager;
|
|
24133
|
-
if (
|
|
23953
|
+
if (this.bareModelName.includes("gemini") || this.bareModelName.includes("google/")) {
|
|
24134
23954
|
this.middlewareManager.register(new GeminiThoughtSignatureMiddleware);
|
|
24135
23955
|
}
|
|
24136
|
-
this.middlewareManager.initialize().catch((err) => log(`[ComposedHandler:${
|
|
23956
|
+
this.middlewareManager.initialize().catch((err) => log(`[ComposedHandler:${this.bareModelName}] Middleware init error: ${err}`));
|
|
24137
23957
|
this.tokenTracker = new TokenTracker(port, {
|
|
24138
23958
|
contextWindow: this.getModelContextWindow(),
|
|
24139
23959
|
providerName: provider.name,
|
|
24140
|
-
modelName,
|
|
23960
|
+
modelName: this.bareModelName,
|
|
24141
23961
|
providerDisplayName: provider.displayName
|
|
24142
23962
|
});
|
|
24143
23963
|
}
|
|
@@ -24166,9 +23986,9 @@ class ComposedHandler {
|
|
|
24166
23986
|
adapter.reset();
|
|
24167
23987
|
const messages = adapter.convertMessages(claudeRequest, filterIdentity);
|
|
24168
23988
|
let tools = adapter.convertTools(claudeRequest, this.options.summarizeTools);
|
|
24169
|
-
const maxToolCount = lookupModel(this.
|
|
23989
|
+
const maxToolCount = lookupModel(this.bareModelName)?.maxToolCount;
|
|
24170
23990
|
if (maxToolCount && tools.length > maxToolCount) {
|
|
24171
|
-
log(`[ComposedHandler] Truncating tools from ${tools.length} to ${maxToolCount} (model limit for ${this.
|
|
23991
|
+
log(`[ComposedHandler] Truncating tools from ${tools.length} to ${maxToolCount} (model limit for ${this.bareModelName})`);
|
|
24172
23992
|
tools = tools.slice(0, maxToolCount);
|
|
24173
23993
|
}
|
|
24174
23994
|
if (!this.getModelSupportsVision()) {
|
|
@@ -24293,7 +24113,7 @@ class ComposedHandler {
|
|
|
24293
24113
|
requestPayload = this.provider.transformPayload(requestPayload);
|
|
24294
24114
|
}
|
|
24295
24115
|
await this.middlewareManager.beforeRequest({
|
|
24296
|
-
modelId: this.
|
|
24116
|
+
modelId: this.bareModelName,
|
|
24297
24117
|
messages,
|
|
24298
24118
|
tools,
|
|
24299
24119
|
stream: true
|
|
@@ -24341,7 +24161,7 @@ class ComposedHandler {
|
|
|
24341
24161
|
error_code,
|
|
24342
24162
|
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
24343
24163
|
adapter_name: this.getActiveAdapterName(),
|
|
24344
|
-
middleware_names: this.middlewareManager.getActiveNames(this.
|
|
24164
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
24345
24165
|
fallback_used: fallbackMeta !== undefined,
|
|
24346
24166
|
fallback_chain: fallbackMeta?.chain,
|
|
24347
24167
|
fallback_attempts: fallbackMeta?.attempts,
|
|
@@ -24403,7 +24223,7 @@ class ComposedHandler {
|
|
|
24403
24223
|
error_code,
|
|
24404
24224
|
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
24405
24225
|
adapter_name: this.getActiveAdapterName(),
|
|
24406
|
-
middleware_names: this.middlewareManager.getActiveNames(this.
|
|
24226
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
24407
24227
|
fallback_used: fallbackMeta !== undefined,
|
|
24408
24228
|
fallback_chain: fallbackMeta?.chain,
|
|
24409
24229
|
fallback_attempts: fallbackMeta?.attempts,
|
|
@@ -24440,7 +24260,7 @@ class ComposedHandler {
|
|
|
24440
24260
|
error_code,
|
|
24441
24261
|
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
24442
24262
|
adapter_name: this.getActiveAdapterName(),
|
|
24443
|
-
middleware_names: this.middlewareManager.getActiveNames(this.
|
|
24263
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
24444
24264
|
fallback_used: fallbackMeta !== undefined,
|
|
24445
24265
|
fallback_chain: fallbackMeta?.chain,
|
|
24446
24266
|
fallback_attempts: fallbackMeta?.attempts,
|
|
@@ -24487,7 +24307,7 @@ class ComposedHandler {
|
|
|
24487
24307
|
error_code,
|
|
24488
24308
|
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
24489
24309
|
adapter_name: this.getActiveAdapterName(),
|
|
24490
|
-
middleware_names: this.middlewareManager.getActiveNames(this.
|
|
24310
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
24491
24311
|
fallback_used: fallbackMeta !== undefined,
|
|
24492
24312
|
fallback_chain: fallbackMeta?.chain,
|
|
24493
24313
|
fallback_attempts: fallbackMeta?.attempts,
|
|
@@ -24524,7 +24344,7 @@ class ComposedHandler {
|
|
|
24524
24344
|
is_free_model: isFreeModel,
|
|
24525
24345
|
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
24526
24346
|
adapter_name: this.getActiveAdapterName(),
|
|
24527
|
-
middleware_names: this.middlewareManager.getActiveNames(this.
|
|
24347
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
24528
24348
|
fallback_used: fallbackMeta !== undefined,
|
|
24529
24349
|
fallback_chain: fallbackMeta?.chain,
|
|
24530
24350
|
fallback_attempts: fallbackMeta?.attempts,
|
|
@@ -24559,19 +24379,19 @@ class ComposedHandler {
|
|
|
24559
24379
|
onComplete = undefined;
|
|
24560
24380
|
}
|
|
24561
24381
|
};
|
|
24562
|
-
const streamFormat = this.provider.overrideStreamFormat?.() ?? this.modelAdapter?.getStreamFormat() ?? this.getAdapter().getStreamFormat();
|
|
24382
|
+
const streamFormat = this.provider.overrideStreamFormat?.() ?? (this.explicitAdapter?.getStreamFormat() ?? this.modelAdapter?.getStreamFormat()) ?? this.getAdapter().getStreamFormat();
|
|
24563
24383
|
switch (streamFormat) {
|
|
24564
24384
|
case "openai-sse":
|
|
24565
|
-
return createStreamingResponseHandler(c, response, adapter, this.
|
|
24385
|
+
return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap);
|
|
24566
24386
|
case "openai-responses-sse":
|
|
24567
24387
|
return createResponsesStreamHandler(c, response, {
|
|
24568
|
-
modelName: this.
|
|
24388
|
+
modelName: this.bareModelName,
|
|
24569
24389
|
onTokenUpdate,
|
|
24570
24390
|
toolNameMap: adapter.getToolNameMap()
|
|
24571
24391
|
});
|
|
24572
24392
|
case "anthropic-sse":
|
|
24573
24393
|
return createAnthropicPassthroughStream(c, response, {
|
|
24574
|
-
modelName: this.
|
|
24394
|
+
modelName: this.bareModelName,
|
|
24575
24395
|
onTokenUpdate
|
|
24576
24396
|
});
|
|
24577
24397
|
case "gemini-sse": {
|
|
@@ -24581,7 +24401,7 @@ class ComposedHandler {
|
|
|
24581
24401
|
}
|
|
24582
24402
|
};
|
|
24583
24403
|
return createGeminiSseStream(c, response, {
|
|
24584
|
-
modelName: this.
|
|
24404
|
+
modelName: this.bareModelName,
|
|
24585
24405
|
adapter,
|
|
24586
24406
|
middlewareManager: this.middlewareManager,
|
|
24587
24407
|
onTokenUpdate,
|
|
@@ -24591,7 +24411,7 @@ class ComposedHandler {
|
|
|
24591
24411
|
}
|
|
24592
24412
|
case "ollama-jsonl":
|
|
24593
24413
|
return createOllamaJsonlStream(c, response, {
|
|
24594
|
-
modelName: this.
|
|
24414
|
+
modelName: this.bareModelName,
|
|
24595
24415
|
onTokenUpdate
|
|
24596
24416
|
});
|
|
24597
24417
|
default:
|
|
@@ -24606,8 +24426,7 @@ class ComposedHandler {
|
|
|
24606
24426
|
const fn = this.provider.getQuotaRemaining;
|
|
24607
24427
|
if (typeof fn !== "function")
|
|
24608
24428
|
return;
|
|
24609
|
-
const
|
|
24610
|
-
const remaining = await fn.call(this.provider, bareModel);
|
|
24429
|
+
const remaining = await fn.call(this.provider, this.bareModelName);
|
|
24611
24430
|
if (typeof remaining === "number") {
|
|
24612
24431
|
this.tokenTracker.setQuotaRemaining(remaining);
|
|
24613
24432
|
this.tokenTracker.rewrite();
|
|
@@ -24863,18 +24682,18 @@ var init_remote_provider_registry = __esm(() => {
|
|
|
24863
24682
|
});
|
|
24864
24683
|
|
|
24865
24684
|
// src/auth/oauth-registry.ts
|
|
24866
|
-
import { existsSync as
|
|
24867
|
-
import { join as
|
|
24868
|
-
import { homedir as
|
|
24685
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
24686
|
+
import { join as join8 } from "path";
|
|
24687
|
+
import { homedir as homedir7 } from "os";
|
|
24869
24688
|
function hasValidOAuthCredentials(descriptor) {
|
|
24870
|
-
const credPath =
|
|
24871
|
-
if (!
|
|
24689
|
+
const credPath = join8(homedir7(), ".claudish", descriptor.credentialFile);
|
|
24690
|
+
if (!existsSync6(credPath))
|
|
24872
24691
|
return false;
|
|
24873
24692
|
if (descriptor.validationMode === "file-exists") {
|
|
24874
24693
|
return true;
|
|
24875
24694
|
}
|
|
24876
24695
|
try {
|
|
24877
|
-
const data = JSON.parse(
|
|
24696
|
+
const data = JSON.parse(readFileSync4(credPath, "utf-8"));
|
|
24878
24697
|
if (!data.access_token)
|
|
24879
24698
|
return false;
|
|
24880
24699
|
if (data.refresh_token)
|
|
@@ -24961,11 +24780,49 @@ var init_static_fallback = __esm(() => {
|
|
|
24961
24780
|
};
|
|
24962
24781
|
});
|
|
24963
24782
|
|
|
24964
|
-
// src/providers/
|
|
24965
|
-
import { readFileSync as
|
|
24966
|
-
import { join as
|
|
24967
|
-
import { homedir as
|
|
24783
|
+
// src/providers/all-models-cache.ts
|
|
24784
|
+
import { readFileSync as readFileSync5, existsSync as existsSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
|
|
24785
|
+
import { join as join9, dirname } from "path";
|
|
24786
|
+
import { homedir as homedir8 } from "os";
|
|
24787
|
+
function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
|
|
24788
|
+
if (!existsSync7(path))
|
|
24789
|
+
return null;
|
|
24790
|
+
let raw2;
|
|
24791
|
+
try {
|
|
24792
|
+
raw2 = JSON.parse(readFileSync5(path, "utf-8"));
|
|
24793
|
+
} catch {
|
|
24794
|
+
return null;
|
|
24795
|
+
}
|
|
24796
|
+
if (!raw2 || typeof raw2 !== "object")
|
|
24797
|
+
return null;
|
|
24798
|
+
const data = raw2;
|
|
24799
|
+
const lastUpdated = typeof data.lastUpdated === "string" ? data.lastUpdated : new Date(0).toISOString();
|
|
24800
|
+
const models = Array.isArray(data.models) ? data.models : [];
|
|
24801
|
+
const entries = Array.isArray(data.entries) ? data.entries : [];
|
|
24802
|
+
return {
|
|
24803
|
+
version: 2,
|
|
24804
|
+
lastUpdated,
|
|
24805
|
+
entries,
|
|
24806
|
+
models
|
|
24807
|
+
};
|
|
24808
|
+
}
|
|
24809
|
+
function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
|
|
24810
|
+
const existing = readAllModelsCache(path);
|
|
24811
|
+
const merged = {
|
|
24812
|
+
version: 2,
|
|
24813
|
+
lastUpdated: data.lastUpdated ?? new Date().toISOString(),
|
|
24814
|
+
entries: data.entries ?? existing?.entries ?? [],
|
|
24815
|
+
models: data.models ?? existing?.models ?? []
|
|
24816
|
+
};
|
|
24817
|
+
mkdirSync7(dirname(path), { recursive: true });
|
|
24818
|
+
writeFileSync7(path, JSON.stringify(merged), "utf-8");
|
|
24819
|
+
}
|
|
24820
|
+
var ALL_MODELS_CACHE_PATH;
|
|
24821
|
+
var init_all_models_cache = __esm(() => {
|
|
24822
|
+
ALL_MODELS_CACHE_PATH = join9(homedir8(), ".claudish", "all-models.json");
|
|
24823
|
+
});
|
|
24968
24824
|
|
|
24825
|
+
// src/providers/catalog-resolvers/openrouter.ts
|
|
24969
24826
|
class OpenRouterCatalogResolver {
|
|
24970
24827
|
provider = "openrouter";
|
|
24971
24828
|
resolveSync(userInput) {
|
|
@@ -25051,22 +24908,20 @@ class OpenRouterCatalogResolver {
|
|
|
25051
24908
|
_getEntries() {
|
|
25052
24909
|
if (_memCache)
|
|
25053
24910
|
return _memCache;
|
|
25054
|
-
|
|
25055
|
-
|
|
25056
|
-
|
|
25057
|
-
|
|
25058
|
-
|
|
25059
|
-
|
|
25060
|
-
|
|
25061
|
-
|
|
25062
|
-
|
|
25063
|
-
|
|
25064
|
-
|
|
25065
|
-
|
|
25066
|
-
|
|
25067
|
-
|
|
25068
|
-
}
|
|
25069
|
-
} catch {}
|
|
24911
|
+
const cache = readAllModelsCache();
|
|
24912
|
+
if (!cache)
|
|
24913
|
+
return null;
|
|
24914
|
+
if (cache.entries.length > 0) {
|
|
24915
|
+
_memCache = cache.entries;
|
|
24916
|
+
return _memCache;
|
|
24917
|
+
}
|
|
24918
|
+
if (cache.models.length > 0) {
|
|
24919
|
+
_memCache = cache.models.map((m) => ({
|
|
24920
|
+
modelId: m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id,
|
|
24921
|
+
aliases: [],
|
|
24922
|
+
sources: { "openrouter-api": { externalId: m.id } }
|
|
24923
|
+
}));
|
|
24924
|
+
return _memCache;
|
|
25070
24925
|
}
|
|
25071
24926
|
return null;
|
|
25072
24927
|
}
|
|
@@ -25089,35 +24944,30 @@ class OpenRouterCatalogResolver {
|
|
|
25089
24944
|
backwardCompatModels.push({ id: orSource.externalId });
|
|
25090
24945
|
}
|
|
25091
24946
|
}
|
|
25092
|
-
|
|
25093
|
-
mkdirSync8(cacheDir, { recursive: true });
|
|
25094
|
-
const diskData = {
|
|
25095
|
-
version: 2,
|
|
25096
|
-
lastUpdated: new Date().toISOString(),
|
|
24947
|
+
writeAllModelsCache({
|
|
25097
24948
|
entries: data.models,
|
|
25098
24949
|
models: backwardCompatModels
|
|
25099
|
-
};
|
|
25100
|
-
writeFileSync8(DISK_CACHE_PATH, JSON.stringify(diskData), "utf-8");
|
|
24950
|
+
});
|
|
25101
24951
|
} catch {}
|
|
25102
24952
|
}
|
|
25103
24953
|
}
|
|
25104
|
-
var FIREBASE_CATALOG_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000",
|
|
24954
|
+
var FIREBASE_CATALOG_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000", _memCache = null, _warmPromise = null;
|
|
25105
24955
|
var init_openrouter2 = __esm(() => {
|
|
25106
24956
|
init_static_fallback();
|
|
25107
|
-
|
|
24957
|
+
init_all_models_cache();
|
|
25108
24958
|
});
|
|
25109
24959
|
|
|
25110
24960
|
// src/providers/catalog-resolvers/litellm.ts
|
|
25111
|
-
import { readFileSync as
|
|
25112
|
-
import { join as
|
|
25113
|
-
import { homedir as
|
|
25114
|
-
import { createHash
|
|
24961
|
+
import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
|
|
24962
|
+
import { join as join10 } from "path";
|
|
24963
|
+
import { homedir as homedir9 } from "os";
|
|
24964
|
+
import { createHash } from "crypto";
|
|
25115
24965
|
function getCachePath() {
|
|
25116
24966
|
const baseUrl = process.env.LITELLM_BASE_URL;
|
|
25117
24967
|
if (!baseUrl)
|
|
25118
24968
|
return null;
|
|
25119
|
-
const hash =
|
|
25120
|
-
return
|
|
24969
|
+
const hash = createHash("sha256").update(baseUrl).digest("hex").substring(0, 16);
|
|
24970
|
+
return join10(homedir9(), ".claudish", `litellm-models-${hash}.json`);
|
|
25121
24971
|
}
|
|
25122
24972
|
|
|
25123
24973
|
class LiteLLMCatalogResolver {
|
|
@@ -25145,10 +24995,10 @@ class LiteLLMCatalogResolver {
|
|
|
25145
24995
|
}
|
|
25146
24996
|
async warmCache() {
|
|
25147
24997
|
const path = getCachePath();
|
|
25148
|
-
if (!path || !
|
|
24998
|
+
if (!path || !existsSync8(path))
|
|
25149
24999
|
return;
|
|
25150
25000
|
try {
|
|
25151
|
-
const data = JSON.parse(
|
|
25001
|
+
const data = JSON.parse(readFileSync6(path, "utf-8"));
|
|
25152
25002
|
if (Array.isArray(data.models)) {
|
|
25153
25003
|
_memCache2 = data.models.map((m) => m.name ?? m.id?.replace("litellm@", "") ?? "");
|
|
25154
25004
|
}
|
|
@@ -25165,10 +25015,10 @@ class LiteLLMCatalogResolver {
|
|
|
25165
25015
|
if (_memCache2)
|
|
25166
25016
|
return _memCache2;
|
|
25167
25017
|
const path = getCachePath();
|
|
25168
|
-
if (!path || !
|
|
25018
|
+
if (!path || !existsSync8(path))
|
|
25169
25019
|
return null;
|
|
25170
25020
|
try {
|
|
25171
|
-
const data = JSON.parse(
|
|
25021
|
+
const data = JSON.parse(readFileSync6(path, "utf-8"));
|
|
25172
25022
|
if (Array.isArray(data.models)) {
|
|
25173
25023
|
_memCache2 = data.models.map((m) => m.name ?? m.id?.replace("litellm@", "") ?? "");
|
|
25174
25024
|
return _memCache2;
|
|
@@ -25233,17 +25083,17 @@ var init_model_catalog_resolver = __esm(() => {
|
|
|
25233
25083
|
});
|
|
25234
25084
|
|
|
25235
25085
|
// src/providers/auto-route.ts
|
|
25236
|
-
import { existsSync as
|
|
25237
|
-
import { join as
|
|
25238
|
-
import { homedir as
|
|
25239
|
-
import { createHash as
|
|
25086
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
25087
|
+
import { join as join11 } from "path";
|
|
25088
|
+
import { homedir as homedir10 } from "os";
|
|
25089
|
+
import { createHash as createHash2 } from "crypto";
|
|
25240
25090
|
function readLiteLLMCacheSync(baseUrl) {
|
|
25241
|
-
const hash =
|
|
25242
|
-
const cachePath =
|
|
25243
|
-
if (!
|
|
25091
|
+
const hash = createHash2("sha256").update(baseUrl).digest("hex").substring(0, 16);
|
|
25092
|
+
const cachePath = join11(homedir10(), ".claudish", `litellm-models-${hash}.json`);
|
|
25093
|
+
if (!existsSync9(cachePath))
|
|
25244
25094
|
return null;
|
|
25245
25095
|
try {
|
|
25246
|
-
const data = JSON.parse(
|
|
25096
|
+
const data = JSON.parse(readFileSync7(cachePath, "utf-8"));
|
|
25247
25097
|
if (!Array.isArray(data.models))
|
|
25248
25098
|
return null;
|
|
25249
25099
|
return data.models;
|
|
@@ -25364,17 +25214,17 @@ async function warmZenModelCache() {
|
|
|
25364
25214
|
const models = (data.data ?? []).map((m) => ({ id: m.id }));
|
|
25365
25215
|
if (models.length === 0)
|
|
25366
25216
|
return;
|
|
25367
|
-
const cacheDir =
|
|
25368
|
-
const { mkdirSync:
|
|
25369
|
-
|
|
25370
|
-
writeSync(
|
|
25217
|
+
const cacheDir = join11(homedir10(), ".claudish");
|
|
25218
|
+
const { mkdirSync: mkdirSync8, writeFileSync: writeSync } = await import("fs");
|
|
25219
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
25220
|
+
writeSync(join11(cacheDir, "zen-models.json"), JSON.stringify({ models, fetchedAt: new Date().toISOString() }));
|
|
25371
25221
|
}
|
|
25372
25222
|
function readZenGoModelCacheSync() {
|
|
25373
|
-
const cachePath =
|
|
25374
|
-
if (!
|
|
25223
|
+
const cachePath = join11(homedir10(), ".claudish", "zen-go-models.json");
|
|
25224
|
+
if (!existsSync9(cachePath))
|
|
25375
25225
|
return null;
|
|
25376
25226
|
try {
|
|
25377
|
-
const data = JSON.parse(
|
|
25227
|
+
const data = JSON.parse(readFileSync7(cachePath, "utf-8"));
|
|
25378
25228
|
if (!Array.isArray(data.models))
|
|
25379
25229
|
return null;
|
|
25380
25230
|
return new Set(data.models.map((m) => m.id));
|
|
@@ -25401,10 +25251,10 @@ async function warmZenGoModelCache() {
|
|
|
25401
25251
|
const models = (data.data ?? []).map((m) => ({ id: m.id }));
|
|
25402
25252
|
if (models.length === 0)
|
|
25403
25253
|
return;
|
|
25404
|
-
const cacheDir =
|
|
25405
|
-
const { mkdirSync:
|
|
25406
|
-
|
|
25407
|
-
writeSync(
|
|
25254
|
+
const cacheDir = join11(homedir10(), ".claudish");
|
|
25255
|
+
const { mkdirSync: mkdirSync8, writeFileSync: writeSync } = await import("fs");
|
|
25256
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
25257
|
+
writeSync(join11(cacheDir, "zen-go-models.json"), JSON.stringify({ models, fetchedAt: new Date().toISOString() }));
|
|
25408
25258
|
}
|
|
25409
25259
|
function hasProviderCredentials(provider) {
|
|
25410
25260
|
const keyInfo = getApiKeyEnvVars(provider);
|
|
@@ -25541,9 +25391,9 @@ __export(exports_provider_resolver, {
|
|
|
25541
25391
|
getMissingKeyResolutions: () => getMissingKeyResolutions,
|
|
25542
25392
|
getMissingKeyError: () => getMissingKeyError
|
|
25543
25393
|
});
|
|
25544
|
-
import { existsSync as
|
|
25545
|
-
import { join as
|
|
25546
|
-
import { homedir as
|
|
25394
|
+
import { existsSync as existsSync10 } from "fs";
|
|
25395
|
+
import { join as join12 } from "path";
|
|
25396
|
+
import { homedir as homedir11 } from "os";
|
|
25547
25397
|
function getApiKeyInfoForProvider(providerName) {
|
|
25548
25398
|
const lookupName = providerName === "gemini" ? "google" : providerName;
|
|
25549
25399
|
const info = getApiKeyInfo(lookupName);
|
|
@@ -25578,8 +25428,8 @@ function isApiKeyAvailable(info) {
|
|
|
25578
25428
|
}
|
|
25579
25429
|
if (info.oauthFallback) {
|
|
25580
25430
|
try {
|
|
25581
|
-
const credPath =
|
|
25582
|
-
if (
|
|
25431
|
+
const credPath = join12(homedir11(), ".claudish", info.oauthFallback);
|
|
25432
|
+
if (existsSync10(credPath)) {
|
|
25583
25433
|
return true;
|
|
25584
25434
|
}
|
|
25585
25435
|
} catch {}
|
|
@@ -25880,9 +25730,9 @@ var init_provider_resolver = __esm(() => {
|
|
|
25880
25730
|
});
|
|
25881
25731
|
|
|
25882
25732
|
// src/services/pricing-cache.ts
|
|
25883
|
-
import { readFileSync as
|
|
25884
|
-
import { homedir as
|
|
25885
|
-
import { join as
|
|
25733
|
+
import { readFileSync as readFileSync8, existsSync as existsSync11, statSync } from "fs";
|
|
25734
|
+
import { homedir as homedir12 } from "os";
|
|
25735
|
+
import { join as join13 } from "path";
|
|
25886
25736
|
function getDynamicPricingSync(provider, modelName) {
|
|
25887
25737
|
if (provider === "openrouter") {
|
|
25888
25738
|
const direct = pricingMap.get(modelName);
|
|
@@ -25923,36 +25773,21 @@ async function warmPricingCache() {
|
|
|
25923
25773
|
const diskFresh = loadDiskCache();
|
|
25924
25774
|
if (diskFresh) {
|
|
25925
25775
|
log("[PricingCache] Loaded pricing from disk cache");
|
|
25926
|
-
|
|
25927
|
-
|
|
25928
|
-
log("[PricingCache] Disk cache stale or missing, fetching from OpenRouter API...");
|
|
25929
|
-
const models = await ensureOpenRouterModelsLoaded();
|
|
25930
|
-
if (models.length === 0) {
|
|
25931
|
-
const cached2 = getCachedOpenRouterModels();
|
|
25932
|
-
if (cached2 && cached2.length > 0) {
|
|
25933
|
-
populateFromOpenRouterModels(cached2);
|
|
25934
|
-
saveDiskCache();
|
|
25935
|
-
log(`[PricingCache] Populated from existing model-loader cache (${pricingMap.size} models)`);
|
|
25936
|
-
return;
|
|
25937
|
-
}
|
|
25938
|
-
log("[PricingCache] No models available, will use provider defaults");
|
|
25939
|
-
return;
|
|
25776
|
+
} else {
|
|
25777
|
+
log("[PricingCache] Disk cache stale or missing, using provider defaults");
|
|
25940
25778
|
}
|
|
25941
|
-
populateFromOpenRouterModels(models);
|
|
25942
|
-
saveDiskCache();
|
|
25943
|
-
log(`[PricingCache] Fetched and cached pricing for ${pricingMap.size} models`);
|
|
25944
25779
|
} catch (error2) {
|
|
25945
25780
|
log(`[PricingCache] Error warming cache: ${error2}`);
|
|
25946
25781
|
}
|
|
25947
25782
|
}
|
|
25948
25783
|
function loadDiskCache() {
|
|
25949
25784
|
try {
|
|
25950
|
-
if (!
|
|
25785
|
+
if (!existsSync11(CACHE_FILE))
|
|
25951
25786
|
return false;
|
|
25952
25787
|
const stat = statSync(CACHE_FILE);
|
|
25953
25788
|
const age = Date.now() - stat.mtimeMs;
|
|
25954
25789
|
const isFresh = age < CACHE_TTL_MS;
|
|
25955
|
-
const raw2 =
|
|
25790
|
+
const raw2 = readFileSync8(CACHE_FILE, "utf-8");
|
|
25956
25791
|
const data = JSON.parse(raw2);
|
|
25957
25792
|
for (const [key, pricing] of Object.entries(data)) {
|
|
25958
25793
|
pricingMap.set(key, pricing);
|
|
@@ -25962,45 +25797,13 @@ function loadDiskCache() {
|
|
|
25962
25797
|
return false;
|
|
25963
25798
|
}
|
|
25964
25799
|
}
|
|
25965
|
-
function saveDiskCache() {
|
|
25966
|
-
try {
|
|
25967
|
-
mkdirSync9(CACHE_DIR, { recursive: true });
|
|
25968
|
-
const data = {};
|
|
25969
|
-
for (const [key, pricing] of pricingMap) {
|
|
25970
|
-
data[key] = pricing;
|
|
25971
|
-
}
|
|
25972
|
-
writeFileSync9(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
25973
|
-
} catch (error2) {
|
|
25974
|
-
log(`[PricingCache] Error saving disk cache: ${error2}`);
|
|
25975
|
-
}
|
|
25976
|
-
}
|
|
25977
|
-
function populateFromOpenRouterModels(models) {
|
|
25978
|
-
for (const model of models) {
|
|
25979
|
-
if (!model.id || !model.pricing)
|
|
25980
|
-
continue;
|
|
25981
|
-
const promptPrice = parseFloat(model.pricing.prompt || "0");
|
|
25982
|
-
const completionPrice = parseFloat(model.pricing.completion || "0");
|
|
25983
|
-
if (isNaN(promptPrice) || isNaN(completionPrice))
|
|
25984
|
-
continue;
|
|
25985
|
-
const inputCostPer1M = promptPrice * 1e6;
|
|
25986
|
-
const outputCostPer1M = completionPrice * 1e6;
|
|
25987
|
-
const isFree = inputCostPer1M === 0 && outputCostPer1M === 0;
|
|
25988
|
-
pricingMap.set(model.id, {
|
|
25989
|
-
inputCostPer1M,
|
|
25990
|
-
outputCostPer1M,
|
|
25991
|
-
isEstimate: true,
|
|
25992
|
-
...isFree ? { isFree: true } : {}
|
|
25993
|
-
});
|
|
25994
|
-
}
|
|
25995
|
-
}
|
|
25996
25800
|
var pricingMap, CACHE_DIR, CACHE_FILE, CACHE_TTL_MS, cacheWarmed = false, PROVIDER_TO_OR_PREFIX;
|
|
25997
25801
|
var init_pricing_cache = __esm(() => {
|
|
25998
25802
|
init_logger();
|
|
25999
|
-
init_model_loader();
|
|
26000
25803
|
init_remote_provider_types();
|
|
26001
25804
|
pricingMap = new Map;
|
|
26002
|
-
CACHE_DIR =
|
|
26003
|
-
CACHE_FILE =
|
|
25805
|
+
CACHE_DIR = join13(homedir12(), ".claudish");
|
|
25806
|
+
CACHE_FILE = join13(CACHE_DIR, "pricing-cache.json");
|
|
26004
25807
|
CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
26005
25808
|
PROVIDER_TO_OR_PREFIX = {
|
|
26006
25809
|
openai: ["openai/"],
|
|
@@ -26018,6 +25821,358 @@ var init_pricing_cache = __esm(() => {
|
|
|
26018
25821
|
};
|
|
26019
25822
|
});
|
|
26020
25823
|
|
|
25824
|
+
// src/model-loader.ts
|
|
25825
|
+
import { readFileSync as readFileSync9, existsSync as existsSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
|
|
25826
|
+
import { join as join14, dirname as dirname2 } from "path";
|
|
25827
|
+
import { fileURLToPath } from "url";
|
|
25828
|
+
import { homedir as homedir13 } from "os";
|
|
25829
|
+
import { createHash as createHash3 } from "crypto";
|
|
25830
|
+
function getBundledRecommendedModelsPath() {
|
|
25831
|
+
return join14(__dirname2, "../recommended-models.json");
|
|
25832
|
+
}
|
|
25833
|
+
function groupRecommendedModels(entries) {
|
|
25834
|
+
const byId = new Map;
|
|
25835
|
+
for (const entry of entries) {
|
|
25836
|
+
const list = byId.get(entry.id);
|
|
25837
|
+
if (list)
|
|
25838
|
+
list.push(entry);
|
|
25839
|
+
else
|
|
25840
|
+
byId.set(entry.id, [entry]);
|
|
25841
|
+
}
|
|
25842
|
+
const flagship = [];
|
|
25843
|
+
const fast = [];
|
|
25844
|
+
for (const [id, members] of byId.entries()) {
|
|
25845
|
+
const primary = members.find((m) => m.category !== "subscription") ?? members[0];
|
|
25846
|
+
const subscriptions = members.filter((m) => m.category === "subscription");
|
|
25847
|
+
const bucket = primary.category === "programming" || primary.category === "vision" || primary.category === "reasoning" ? "flagship" : "fast";
|
|
25848
|
+
const group = { id, primary, subscriptions, bucket };
|
|
25849
|
+
if (bucket === "flagship")
|
|
25850
|
+
flagship.push(group);
|
|
25851
|
+
else
|
|
25852
|
+
fast.push(group);
|
|
25853
|
+
}
|
|
25854
|
+
return { flagship, fast };
|
|
25855
|
+
}
|
|
25856
|
+
function collectRoutingPrefixes(group, getNativePrefix) {
|
|
25857
|
+
const slug = (group.primary.provider || "").toLowerCase();
|
|
25858
|
+
const native = getNativePrefix(slug);
|
|
25859
|
+
const seen = new Set;
|
|
25860
|
+
const out = [];
|
|
25861
|
+
if (native) {
|
|
25862
|
+
out.push(native);
|
|
25863
|
+
seen.add(native);
|
|
25864
|
+
}
|
|
25865
|
+
for (const sub of group.subscriptions) {
|
|
25866
|
+
const p = sub.subscription?.prefix;
|
|
25867
|
+
if (!p || seen.has(p))
|
|
25868
|
+
continue;
|
|
25869
|
+
seen.add(p);
|
|
25870
|
+
out.push(p);
|
|
25871
|
+
}
|
|
25872
|
+
return out;
|
|
25873
|
+
}
|
|
25874
|
+
function parsePriceAvg(s) {
|
|
25875
|
+
if (!s || s === "N/A")
|
|
25876
|
+
return Infinity;
|
|
25877
|
+
if (s === "FREE")
|
|
25878
|
+
return 0;
|
|
25879
|
+
const m = s.match(/\$([\d.]+)/);
|
|
25880
|
+
return m ? parseFloat(m[1]) : Infinity;
|
|
25881
|
+
}
|
|
25882
|
+
function parseCtx(s) {
|
|
25883
|
+
if (!s || s === "N/A")
|
|
25884
|
+
return 0;
|
|
25885
|
+
const upper = s.toUpperCase();
|
|
25886
|
+
if (upper.includes("M"))
|
|
25887
|
+
return parseFloat(upper) * 1e6;
|
|
25888
|
+
if (upper.includes("K"))
|
|
25889
|
+
return parseFloat(upper) * 1000;
|
|
25890
|
+
return parseInt(s, 10) || 0;
|
|
25891
|
+
}
|
|
25892
|
+
function normalizePricingDisplay(raw2) {
|
|
25893
|
+
const pricing = raw2 || "N/A";
|
|
25894
|
+
if (pricing.includes("-1000000"))
|
|
25895
|
+
return "varies";
|
|
25896
|
+
if (pricing === "$0.00/1M" || pricing === "FREE")
|
|
25897
|
+
return "FREE";
|
|
25898
|
+
return pricing;
|
|
25899
|
+
}
|
|
25900
|
+
function computeQuickPicks(primaries) {
|
|
25901
|
+
if (primaries.length === 0) {
|
|
25902
|
+
return {
|
|
25903
|
+
budget: null,
|
|
25904
|
+
largeContext: null,
|
|
25905
|
+
mostCapable: null,
|
|
25906
|
+
visionCoding: null,
|
|
25907
|
+
agentic: null
|
|
25908
|
+
};
|
|
25909
|
+
}
|
|
25910
|
+
const priced = primaries.filter((m) => {
|
|
25911
|
+
const p = parsePriceAvg(m.pricing?.average);
|
|
25912
|
+
return p > 0 && p !== Infinity;
|
|
25913
|
+
}).sort((a, b) => parsePriceAvg(a.pricing?.average) - parsePriceAvg(b.pricing?.average));
|
|
25914
|
+
const budget = priced[0] ?? null;
|
|
25915
|
+
const byCtx = [...primaries].sort((a, b) => parseCtx(b.context) - parseCtx(a.context));
|
|
25916
|
+
const largeContext = byCtx[0] ?? null;
|
|
25917
|
+
const byPrice = [...primaries].sort((a, b) => parsePriceAvg(b.pricing?.average) - parsePriceAvg(a.pricing?.average));
|
|
25918
|
+
const mostCapable = byPrice.find((m) => parsePriceAvg(m.pricing?.average) !== Infinity) ?? null;
|
|
25919
|
+
const visionCoding = primaries.find((m) => m.supportsVision === true && m.id !== budget?.id && m.id !== mostCapable?.id) ?? null;
|
|
25920
|
+
const agentic = primaries.find((m) => m.supportsReasoning === true && m.id !== mostCapable?.id) ?? null;
|
|
25921
|
+
return { budget, largeContext, mostCapable, visionCoding, agentic };
|
|
25922
|
+
}
|
|
25923
|
+
async function getRecommendedModels(opts = {}) {
|
|
25924
|
+
const { forceRefresh = false } = opts;
|
|
25925
|
+
if (!forceRefresh && _cachedRecommendedModels) {
|
|
25926
|
+
return _cachedRecommendedModels;
|
|
25927
|
+
}
|
|
25928
|
+
if (!forceRefresh && existsSync12(RECOMMENDED_MODELS_CACHE_PATH)) {
|
|
25929
|
+
try {
|
|
25930
|
+
const cacheData = JSON.parse(readFileSync9(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
|
|
25931
|
+
if (cacheData.models && cacheData.models.length > 0 && isFreshEnough(cacheData)) {
|
|
25932
|
+
_cachedRecommendedModels = cacheData;
|
|
25933
|
+
return cacheData;
|
|
25934
|
+
}
|
|
25935
|
+
} catch {}
|
|
25936
|
+
}
|
|
25937
|
+
try {
|
|
25938
|
+
const response = await fetch(FIREBASE_RECOMMENDED_URL, {
|
|
25939
|
+
signal: AbortSignal.timeout(RECOMMENDED_FETCH_TIMEOUT_MS)
|
|
25940
|
+
});
|
|
25941
|
+
if (response.ok) {
|
|
25942
|
+
const data = await response.json();
|
|
25943
|
+
if (data.models && data.models.length > 0) {
|
|
25944
|
+
_cachedRecommendedModels = data;
|
|
25945
|
+
try {
|
|
25946
|
+
const cacheDir = join14(homedir13(), ".claudish");
|
|
25947
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
25948
|
+
writeFileSync8(RECOMMENDED_MODELS_CACHE_PATH, JSON.stringify(data), "utf-8");
|
|
25949
|
+
} catch {}
|
|
25950
|
+
return data;
|
|
25951
|
+
}
|
|
25952
|
+
}
|
|
25953
|
+
} catch {}
|
|
25954
|
+
return loadBundledRecommendedModels();
|
|
25955
|
+
}
|
|
25956
|
+
function getRecommendedModelsSync() {
|
|
25957
|
+
if (_cachedRecommendedModels)
|
|
25958
|
+
return _cachedRecommendedModels;
|
|
25959
|
+
if (existsSync12(RECOMMENDED_MODELS_CACHE_PATH)) {
|
|
25960
|
+
try {
|
|
25961
|
+
const cacheData = JSON.parse(readFileSync9(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
|
|
25962
|
+
if (cacheData.models && cacheData.models.length > 0) {
|
|
25963
|
+
_cachedRecommendedModels = cacheData;
|
|
25964
|
+
return cacheData;
|
|
25965
|
+
}
|
|
25966
|
+
} catch {}
|
|
25967
|
+
}
|
|
25968
|
+
return loadBundledRecommendedModels();
|
|
25969
|
+
}
|
|
25970
|
+
async function warmRecommendedModels() {
|
|
25971
|
+
try {
|
|
25972
|
+
return await getRecommendedModels({ forceRefresh: true });
|
|
25973
|
+
} catch {
|
|
25974
|
+
return null;
|
|
25975
|
+
}
|
|
25976
|
+
}
|
|
25977
|
+
function isFreshEnough(doc2) {
|
|
25978
|
+
const generatedAt = doc2.generatedAt;
|
|
25979
|
+
if (!generatedAt)
|
|
25980
|
+
return true;
|
|
25981
|
+
const ageHours = (Date.now() - new Date(generatedAt).getTime()) / (1000 * 60 * 60);
|
|
25982
|
+
return ageHours <= RECOMMENDED_CACHE_MAX_AGE_HOURS;
|
|
25983
|
+
}
|
|
25984
|
+
function loadBundledRecommendedModels() {
|
|
25985
|
+
const jsonPath = getBundledRecommendedModelsPath();
|
|
25986
|
+
if (!existsSync12(jsonPath)) {
|
|
25987
|
+
throw new Error(`recommended-models.json not found at ${jsonPath}. ` + `Run 'claudish --top-models --force-update' to refresh from Firebase.`);
|
|
25988
|
+
}
|
|
25989
|
+
try {
|
|
25990
|
+
const doc2 = JSON.parse(readFileSync9(jsonPath, "utf-8"));
|
|
25991
|
+
_cachedRecommendedModels = doc2;
|
|
25992
|
+
return doc2;
|
|
25993
|
+
} catch (error2) {
|
|
25994
|
+
throw new Error(`Failed to parse bundled recommended-models.json: ${error2}`);
|
|
25995
|
+
}
|
|
25996
|
+
}
|
|
25997
|
+
async function searchModels(query, limit = 50) {
|
|
25998
|
+
const url = `${FIREBASE_BASE_URL}?search=${encodeURIComponent(query)}&limit=${limit}&status=active`;
|
|
25999
|
+
const response = await fetch(url, {
|
|
26000
|
+
signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
|
|
26001
|
+
});
|
|
26002
|
+
if (!response.ok) {
|
|
26003
|
+
throw new Error(`Firebase search returned ${response.status} ${response.statusText}`);
|
|
26004
|
+
}
|
|
26005
|
+
const data = await response.json();
|
|
26006
|
+
return data.models ?? [];
|
|
26007
|
+
}
|
|
26008
|
+
async function getTop100Models() {
|
|
26009
|
+
const url = `${FIREBASE_BASE_URL}?catalog=top100`;
|
|
26010
|
+
const response = await fetch(url, {
|
|
26011
|
+
signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
|
|
26012
|
+
});
|
|
26013
|
+
if (!response.ok) {
|
|
26014
|
+
throw new Error(`Firebase top100 fetch failed: ${response.status} ${response.statusText}`);
|
|
26015
|
+
}
|
|
26016
|
+
const data = await response.json();
|
|
26017
|
+
return data;
|
|
26018
|
+
}
|
|
26019
|
+
async function getProviderList() {
|
|
26020
|
+
const url = `${FIREBASE_BASE_URL}?catalog=providers`;
|
|
26021
|
+
const response = await fetch(url, {
|
|
26022
|
+
signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
|
|
26023
|
+
});
|
|
26024
|
+
if (!response.ok) {
|
|
26025
|
+
throw new Error(`Firebase providers fetch failed: ${response.status} ${response.statusText}`);
|
|
26026
|
+
}
|
|
26027
|
+
const data = await response.json();
|
|
26028
|
+
return data.providers ?? [];
|
|
26029
|
+
}
|
|
26030
|
+
async function getModelsByProvider(provider, limit = 200) {
|
|
26031
|
+
const url = `${FIREBASE_BASE_URL}?provider=${encodeURIComponent(provider)}&status=active&limit=${limit}`;
|
|
26032
|
+
const response = await fetch(url, {
|
|
26033
|
+
signal: AbortSignal.timeout(SEARCH_FETCH_TIMEOUT_MS)
|
|
26034
|
+
});
|
|
26035
|
+
if (!response.ok) {
|
|
26036
|
+
throw new Error(`Firebase provider query returned ${response.status} ${response.statusText}`);
|
|
26037
|
+
}
|
|
26038
|
+
const data = await response.json();
|
|
26039
|
+
if (Array.isArray(data))
|
|
26040
|
+
return data;
|
|
26041
|
+
return data.models ?? [];
|
|
26042
|
+
}
|
|
26043
|
+
function loadModelInfo() {
|
|
26044
|
+
if (_cachedModelInfo) {
|
|
26045
|
+
return _cachedModelInfo;
|
|
26046
|
+
}
|
|
26047
|
+
const data = getRecommendedModelsSync();
|
|
26048
|
+
const modelInfo = {};
|
|
26049
|
+
for (const model of data.models) {
|
|
26050
|
+
modelInfo[model.id] = {
|
|
26051
|
+
name: model.name,
|
|
26052
|
+
description: model.description,
|
|
26053
|
+
priority: model.priority,
|
|
26054
|
+
provider: model.provider
|
|
26055
|
+
};
|
|
26056
|
+
}
|
|
26057
|
+
modelInfo.custom = {
|
|
26058
|
+
name: "Custom Model",
|
|
26059
|
+
description: "Enter any model ID manually",
|
|
26060
|
+
priority: 999,
|
|
26061
|
+
provider: "Custom"
|
|
26062
|
+
};
|
|
26063
|
+
_cachedModelInfo = modelInfo;
|
|
26064
|
+
return modelInfo;
|
|
26065
|
+
}
|
|
26066
|
+
function getAvailableModels() {
|
|
26067
|
+
if (_cachedModelIds) {
|
|
26068
|
+
return _cachedModelIds;
|
|
26069
|
+
}
|
|
26070
|
+
const data = getRecommendedModelsSync();
|
|
26071
|
+
const modelIds = data.models.sort((a, b) => a.priority - b.priority).map((m) => m.id);
|
|
26072
|
+
const result = [...modelIds, "custom"];
|
|
26073
|
+
_cachedModelIds = result;
|
|
26074
|
+
return result;
|
|
26075
|
+
}
|
|
26076
|
+
async function fetchLiteLLMModels(baseUrl, apiKey, forceUpdate = false) {
|
|
26077
|
+
const hash = createHash3("sha256").update(baseUrl).digest("hex").substring(0, 16);
|
|
26078
|
+
const cacheDir = join14(homedir13(), ".claudish");
|
|
26079
|
+
const cachePath = join14(cacheDir, `litellm-models-${hash}.json`);
|
|
26080
|
+
if (!forceUpdate && existsSync12(cachePath)) {
|
|
26081
|
+
try {
|
|
26082
|
+
const cacheData = JSON.parse(readFileSync9(cachePath, "utf-8"));
|
|
26083
|
+
const timestamp = new Date(cacheData.timestamp);
|
|
26084
|
+
const now = new Date;
|
|
26085
|
+
const ageInHours = (now.getTime() - timestamp.getTime()) / (1000 * 60 * 60);
|
|
26086
|
+
if (ageInHours < LITELLM_CACHE_MAX_AGE_HOURS) {
|
|
26087
|
+
return cacheData.models;
|
|
26088
|
+
}
|
|
26089
|
+
} catch {}
|
|
26090
|
+
}
|
|
26091
|
+
try {
|
|
26092
|
+
const url = `${baseUrl.replace(/\/$/, "")}/model_group/info`;
|
|
26093
|
+
const response = await fetch(url, {
|
|
26094
|
+
headers: {
|
|
26095
|
+
Authorization: `Bearer ${apiKey}`
|
|
26096
|
+
},
|
|
26097
|
+
signal: AbortSignal.timeout(1e4)
|
|
26098
|
+
});
|
|
26099
|
+
if (!response.ok) {
|
|
26100
|
+
console.error(`Failed to fetch LiteLLM models: ${response.status} ${response.statusText}`);
|
|
26101
|
+
if (existsSync12(cachePath)) {
|
|
26102
|
+
try {
|
|
26103
|
+
const cacheData2 = JSON.parse(readFileSync9(cachePath, "utf-8"));
|
|
26104
|
+
return cacheData2.models;
|
|
26105
|
+
} catch {
|
|
26106
|
+
return [];
|
|
26107
|
+
}
|
|
26108
|
+
}
|
|
26109
|
+
return [];
|
|
26110
|
+
}
|
|
26111
|
+
const responseData = await response.json();
|
|
26112
|
+
const rawModels = Array.isArray(responseData) ? responseData : responseData.data || [];
|
|
26113
|
+
const transformedModels = rawModels.filter((m) => m.mode === "chat" && m.supports_function_calling).map((m) => {
|
|
26114
|
+
const inputCostPerM = (m.input_cost_per_token || 0) * 1e6;
|
|
26115
|
+
const outputCostPerM = (m.output_cost_per_token || 0) * 1e6;
|
|
26116
|
+
const avgCost = (inputCostPerM + outputCostPerM) / 2;
|
|
26117
|
+
const isFree = inputCostPerM === 0 && outputCostPerM === 0;
|
|
26118
|
+
const contextLength = m.max_input_tokens || 128000;
|
|
26119
|
+
const contextStr = contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`;
|
|
26120
|
+
return {
|
|
26121
|
+
id: `litellm@${m.model_group}`,
|
|
26122
|
+
name: m.model_group,
|
|
26123
|
+
description: `LiteLLM model (providers: ${m.providers.join(", ")})`,
|
|
26124
|
+
provider: "LiteLLM",
|
|
26125
|
+
pricing: {
|
|
26126
|
+
input: isFree ? "FREE" : `$${inputCostPerM.toFixed(2)}`,
|
|
26127
|
+
output: isFree ? "FREE" : `$${outputCostPerM.toFixed(2)}`,
|
|
26128
|
+
average: isFree ? "FREE" : `$${avgCost.toFixed(2)}/1M`
|
|
26129
|
+
},
|
|
26130
|
+
context: contextStr,
|
|
26131
|
+
contextLength,
|
|
26132
|
+
supportsTools: m.supports_function_calling || false,
|
|
26133
|
+
supportsReasoning: m.supports_reasoning || false,
|
|
26134
|
+
supportsVision: m.supports_vision || false,
|
|
26135
|
+
isFree,
|
|
26136
|
+
source: "LiteLLM"
|
|
26137
|
+
};
|
|
26138
|
+
});
|
|
26139
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
26140
|
+
const cacheData = {
|
|
26141
|
+
timestamp: new Date().toISOString(),
|
|
26142
|
+
models: transformedModels
|
|
26143
|
+
};
|
|
26144
|
+
writeFileSync8(cachePath, JSON.stringify(cacheData, null, 2), "utf-8");
|
|
26145
|
+
return transformedModels;
|
|
26146
|
+
} catch (error2) {
|
|
26147
|
+
console.error(`Failed to fetch LiteLLM models: ${error2}`);
|
|
26148
|
+
if (existsSync12(cachePath)) {
|
|
26149
|
+
try {
|
|
26150
|
+
const cacheData = JSON.parse(readFileSync9(cachePath, "utf-8"));
|
|
26151
|
+
return cacheData.models;
|
|
26152
|
+
} catch {
|
|
26153
|
+
return [];
|
|
26154
|
+
}
|
|
26155
|
+
}
|
|
26156
|
+
return [];
|
|
26157
|
+
}
|
|
26158
|
+
}
|
|
26159
|
+
var __filename2, __dirname2, _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_CACHE_MAX_AGE_HOURS = 12, RECOMMENDED_FETCH_TIMEOUT_MS = 5000, SEARCH_FETCH_TIMEOUT_MS = 1e4, FIREBASE_SLUG_TO_PROVIDER_NAME, LITELLM_CACHE_MAX_AGE_HOURS = 24;
|
|
26160
|
+
var init_model_loader = __esm(() => {
|
|
26161
|
+
__filename2 = fileURLToPath(import.meta.url);
|
|
26162
|
+
__dirname2 = dirname2(__filename2);
|
|
26163
|
+
FIREBASE_RECOMMENDED_URL = `${FIREBASE_BASE_URL}?catalog=recommended`;
|
|
26164
|
+
RECOMMENDED_MODELS_CACHE_PATH = join14(homedir13(), ".claudish", "recommended-models-cache.json");
|
|
26165
|
+
FIREBASE_SLUG_TO_PROVIDER_NAME = {
|
|
26166
|
+
openai: "openai",
|
|
26167
|
+
google: "google",
|
|
26168
|
+
"x-ai": "xai",
|
|
26169
|
+
"z-ai": "zai",
|
|
26170
|
+
moonshotai: "kimi",
|
|
26171
|
+
minimax: "minimax",
|
|
26172
|
+
qwen: "qwen"
|
|
26173
|
+
};
|
|
26174
|
+
});
|
|
26175
|
+
|
|
26021
26176
|
// src/handlers/fallback-handler.ts
|
|
26022
26177
|
class FallbackHandler {
|
|
26023
26178
|
candidates;
|
|
@@ -26536,8 +26691,8 @@ Details: ${e.message}`);
|
|
|
26536
26691
|
const credPath = this.getCredentialsPath();
|
|
26537
26692
|
const claudishDir = join15(homedir14(), ".claudish");
|
|
26538
26693
|
if (!existsSync13(claudishDir)) {
|
|
26539
|
-
const { mkdirSync:
|
|
26540
|
-
|
|
26694
|
+
const { mkdirSync: mkdirSync9 } = __require("fs");
|
|
26695
|
+
mkdirSync9(claudishDir, { recursive: true });
|
|
26541
26696
|
}
|
|
26542
26697
|
const fd = openSync(credPath, "w", 384);
|
|
26543
26698
|
try {
|
|
@@ -27383,8 +27538,8 @@ Details: ${e.message}`);
|
|
|
27383
27538
|
const credPath = this.getCredentialsPath();
|
|
27384
27539
|
const claudishDir = join16(homedir15(), ".claudish");
|
|
27385
27540
|
if (!existsSync14(claudishDir)) {
|
|
27386
|
-
const { mkdirSync:
|
|
27387
|
-
|
|
27541
|
+
const { mkdirSync: mkdirSync9 } = __require("fs");
|
|
27542
|
+
mkdirSync9(claudishDir, { recursive: true });
|
|
27388
27543
|
}
|
|
27389
27544
|
const fd = openSync2(credPath, "w", 384);
|
|
27390
27545
|
try {
|
|
@@ -27686,8 +27841,8 @@ class KimiOAuth {
|
|
|
27686
27841
|
const deviceIdPath = this.getDeviceIdPath();
|
|
27687
27842
|
const claudishDir = join18(homedir17(), ".claudish");
|
|
27688
27843
|
if (!existsSync16(claudishDir)) {
|
|
27689
|
-
const { mkdirSync:
|
|
27690
|
-
|
|
27844
|
+
const { mkdirSync: mkdirSync9 } = __require("fs");
|
|
27845
|
+
mkdirSync9(claudishDir, { recursive: true });
|
|
27691
27846
|
}
|
|
27692
27847
|
if (existsSync16(deviceIdPath)) {
|
|
27693
27848
|
try {
|
|
@@ -27965,8 +28120,8 @@ Waiting for authorization...`);
|
|
|
27965
28120
|
const credPath = this.getCredentialsPath();
|
|
27966
28121
|
const claudishDir = join18(homedir17(), ".claudish");
|
|
27967
28122
|
if (!existsSync16(claudishDir)) {
|
|
27968
|
-
const { mkdirSync:
|
|
27969
|
-
|
|
28123
|
+
const { mkdirSync: mkdirSync9 } = __require("fs");
|
|
28124
|
+
mkdirSync9(claudishDir, { recursive: true });
|
|
27970
28125
|
}
|
|
27971
28126
|
const fd = openSync3(credPath, "w", 384);
|
|
27972
28127
|
try {
|
|
@@ -29307,6 +29462,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
|
|
|
29307
29462
|
port = actualPort;
|
|
29308
29463
|
log(`[Proxy] Server started on port ${port}`);
|
|
29309
29464
|
warmPricingCache().catch(() => {});
|
|
29465
|
+
warmRecommendedModels().catch(() => {});
|
|
29310
29466
|
const catalogProvidersToWarm = ["openrouter"];
|
|
29311
29467
|
if (process.env.LITELLM_BASE_URL)
|
|
29312
29468
|
catalogProvidersToWarm.push("litellm");
|
|
@@ -29315,7 +29471,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
|
|
|
29315
29471
|
port,
|
|
29316
29472
|
url: `http://127.0.0.1:${port}`,
|
|
29317
29473
|
shutdown: async () => {
|
|
29318
|
-
return new Promise((resolve3) => server.close((
|
|
29474
|
+
return new Promise((resolve3) => server.close(() => resolve3()));
|
|
29319
29475
|
}
|
|
29320
29476
|
};
|
|
29321
29477
|
}
|
|
@@ -29380,53 +29536,18 @@ var init_port_manager = () => {};
|
|
|
29380
29536
|
// src/mcp-server.ts
|
|
29381
29537
|
var exports_mcp_server = {};
|
|
29382
29538
|
__export(exports_mcp_server, {
|
|
29383
|
-
startMcpServer: () => startMcpServer
|
|
29539
|
+
startMcpServer: () => startMcpServer,
|
|
29540
|
+
runPromptViaProxy: () => runPromptViaProxy,
|
|
29541
|
+
parseAnthropicSse: () => parseAnthropicSse
|
|
29384
29542
|
});
|
|
29385
|
-
import { readFileSync as readFileSync18, existsSync as existsSync21, writeFileSync as
|
|
29386
|
-
import { join as join23, dirname as
|
|
29543
|
+
import { readFileSync as readFileSync18, existsSync as existsSync21, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, readdirSync as readdirSync3 } from "fs";
|
|
29544
|
+
import { join as join23, dirname as dirname3 } from "path";
|
|
29387
29545
|
import { homedir as homedir22 } from "os";
|
|
29388
29546
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
29389
|
-
function loadRecommendedModels() {
|
|
29390
|
-
const cachedPath = join23(CLAUDISH_CACHE_DIR, "recommended-models-cache.json");
|
|
29391
|
-
if (existsSync21(cachedPath)) {
|
|
29392
|
-
try {
|
|
29393
|
-
const data = JSON.parse(readFileSync18(cachedPath, "utf-8"));
|
|
29394
|
-
if (data.models && data.models.length > 0)
|
|
29395
|
-
return data.models;
|
|
29396
|
-
} catch {}
|
|
29397
|
-
}
|
|
29398
|
-
if (existsSync21(RECOMMENDED_MODELS_PATH)) {
|
|
29399
|
-
try {
|
|
29400
|
-
const data = JSON.parse(readFileSync18(RECOMMENDED_MODELS_PATH, "utf-8"));
|
|
29401
|
-
return data.models || [];
|
|
29402
|
-
} catch {
|
|
29403
|
-
return [];
|
|
29404
|
-
}
|
|
29405
|
-
}
|
|
29406
|
-
return [];
|
|
29407
|
-
}
|
|
29408
|
-
function parsePriceAvg(s) {
|
|
29409
|
-
if (!s || s === "N/A")
|
|
29410
|
-
return Infinity;
|
|
29411
|
-
if (s === "FREE")
|
|
29412
|
-
return 0;
|
|
29413
|
-
const m = s.match(/\$([\d.]+)/);
|
|
29414
|
-
return m ? parseFloat(m[1]) : Infinity;
|
|
29415
|
-
}
|
|
29416
|
-
function parseCtx(s) {
|
|
29417
|
-
if (!s || s === "N/A")
|
|
29418
|
-
return 0;
|
|
29419
|
-
const upper = s.toUpperCase();
|
|
29420
|
-
if (upper.includes("M"))
|
|
29421
|
-
return parseFloat(upper) * 1e6;
|
|
29422
|
-
if (upper.includes("K"))
|
|
29423
|
-
return parseFloat(upper) * 1000;
|
|
29424
|
-
return parseInt(s) || 0;
|
|
29425
|
-
}
|
|
29426
29547
|
async function loadAllModels(forceRefresh = false) {
|
|
29427
|
-
if (!forceRefresh && existsSync21(
|
|
29548
|
+
if (!forceRefresh && existsSync21(ALL_MODELS_CACHE_PATH2)) {
|
|
29428
29549
|
try {
|
|
29429
|
-
const cacheData = JSON.parse(readFileSync18(
|
|
29550
|
+
const cacheData = JSON.parse(readFileSync18(ALL_MODELS_CACHE_PATH2, "utf-8"));
|
|
29430
29551
|
const lastUpdated = new Date(cacheData.lastUpdated);
|
|
29431
29552
|
const ageInDays = (Date.now() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
|
|
29432
29553
|
if (ageInDays <= CACHE_MAX_AGE_DAYS) {
|
|
@@ -29440,12 +29561,12 @@ async function loadAllModels(forceRefresh = false) {
|
|
|
29440
29561
|
throw new Error(`API returned ${response.status}`);
|
|
29441
29562
|
const data = await response.json();
|
|
29442
29563
|
const models = data.data || [];
|
|
29443
|
-
|
|
29444
|
-
|
|
29564
|
+
mkdirSync9(CLAUDISH_CACHE_DIR, { recursive: true });
|
|
29565
|
+
writeFileSync9(ALL_MODELS_CACHE_PATH2, JSON.stringify({ lastUpdated: new Date().toISOString(), models }), "utf-8");
|
|
29445
29566
|
return models;
|
|
29446
29567
|
} catch {
|
|
29447
|
-
if (existsSync21(
|
|
29448
|
-
const cacheData = JSON.parse(readFileSync18(
|
|
29568
|
+
if (existsSync21(ALL_MODELS_CACHE_PATH2)) {
|
|
29569
|
+
const cacheData = JSON.parse(readFileSync18(ALL_MODELS_CACHE_PATH2, "utf-8"));
|
|
29449
29570
|
return cacheData.models || [];
|
|
29450
29571
|
}
|
|
29451
29572
|
return [];
|
|
@@ -29650,8 +29771,20 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
|
|
|
29650
29771
|
inputSchema: { type: "object" },
|
|
29651
29772
|
group: "low-level",
|
|
29652
29773
|
handler: async () => {
|
|
29653
|
-
|
|
29654
|
-
|
|
29774
|
+
let doc2;
|
|
29775
|
+
try {
|
|
29776
|
+
doc2 = getRecommendedModelsSync();
|
|
29777
|
+
} catch {
|
|
29778
|
+
return {
|
|
29779
|
+
content: [
|
|
29780
|
+
{
|
|
29781
|
+
type: "text",
|
|
29782
|
+
text: "No recommended models found. Try search_models instead."
|
|
29783
|
+
}
|
|
29784
|
+
]
|
|
29785
|
+
};
|
|
29786
|
+
}
|
|
29787
|
+
if (!doc2.models || doc2.models.length === 0) {
|
|
29655
29788
|
return {
|
|
29656
29789
|
content: [
|
|
29657
29790
|
{
|
|
@@ -29661,43 +29794,81 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
|
|
|
29661
29794
|
]
|
|
29662
29795
|
};
|
|
29663
29796
|
}
|
|
29797
|
+
const { flagship, fast } = groupRecommendedModels(doc2.models);
|
|
29798
|
+
const providerByName = new Map(BUILTIN_PROVIDERS.map((p) => [p.name, p]));
|
|
29799
|
+
const getNativePrefix = (firebaseSlug) => {
|
|
29800
|
+
const canonical = FIREBASE_SLUG_TO_PROVIDER_NAME[firebaseSlug];
|
|
29801
|
+
if (!canonical)
|
|
29802
|
+
return null;
|
|
29803
|
+
const def = providerByName.get(canonical);
|
|
29804
|
+
if (!def || !def.shortcuts || def.shortcuts.length === 0)
|
|
29805
|
+
return null;
|
|
29806
|
+
return def.shortcuts[0];
|
|
29807
|
+
};
|
|
29808
|
+
const renderGroup = (group) => {
|
|
29809
|
+
const m = group.primary;
|
|
29810
|
+
const pricing = normalizePricingDisplay(m.pricing?.average);
|
|
29811
|
+
const ctx = m.context || "N/A";
|
|
29812
|
+
const caps = [];
|
|
29813
|
+
if (m.supportsTools)
|
|
29814
|
+
caps.push("tools");
|
|
29815
|
+
if (m.supportsReasoning)
|
|
29816
|
+
caps.push("reasoning");
|
|
29817
|
+
if (m.supportsVision)
|
|
29818
|
+
caps.push("vision");
|
|
29819
|
+
const capsLine = caps.length > 0 ? caps.join(", ") : "none";
|
|
29820
|
+
const prefixes = collectRoutingPrefixes(group, getNativePrefix);
|
|
29821
|
+
const accessLine = prefixes.length > 0 ? prefixes.map((p) => `\`${p}@${m.id}\``).join(" \xB7 ") : `\`${m.id}\``;
|
|
29822
|
+
return [
|
|
29823
|
+
`### ${m.id}`,
|
|
29824
|
+
`- **Pricing**: ${pricing} avg \xB7 ${ctx} context`,
|
|
29825
|
+
`- **Capabilities**: ${capsLine}`,
|
|
29826
|
+
`- **Access**: ${accessLine}`,
|
|
29827
|
+
""
|
|
29828
|
+
].join(`
|
|
29829
|
+
`);
|
|
29830
|
+
};
|
|
29664
29831
|
let output = `# Recommended Models
|
|
29665
29832
|
|
|
29666
29833
|
`;
|
|
29667
|
-
output +=
|
|
29668
|
-
|
|
29669
|
-
output += `|-------|----------|---------|---------|-------|-----------|--------|
|
|
29834
|
+
output += `_Last updated: ${doc2.lastUpdated || "unknown"}_
|
|
29835
|
+
|
|
29670
29836
|
`;
|
|
29671
|
-
|
|
29672
|
-
|
|
29673
|
-
|
|
29674
|
-
const v = model.supportsVision ? "\u2713" : "\xB7";
|
|
29675
|
-
output += `| ${model.id} | ${model.provider} | ${model.pricing?.average || "N/A"} | ${model.context || "N/A"} | ${t} | ${r} | ${v} |
|
|
29837
|
+
if (flagship.length > 0) {
|
|
29838
|
+
output += `## Flagship models
|
|
29839
|
+
|
|
29676
29840
|
`;
|
|
29841
|
+
for (const group of flagship)
|
|
29842
|
+
output += renderGroup(group);
|
|
29677
29843
|
}
|
|
29678
|
-
|
|
29679
|
-
|
|
29680
|
-
|
|
29681
|
-
const cheapest = [...models].sort((a, b) => parsePriceAvg(a.pricing?.average) - parsePriceAvg(b.pricing?.average))[0];
|
|
29682
|
-
const bigCtx = [...models].sort((a, b) => parseCtx(b.context) - parseCtx(a.context))[0];
|
|
29683
|
-
const priciest = [...models].sort((a, b) => parsePriceAvg(b.pricing?.average) - parsePriceAvg(a.pricing?.average))[0];
|
|
29684
|
-
const vision = models.find((m) => m.supportsVision);
|
|
29685
|
-
const reasoning = models.find((m) => m.supportsReasoning && m.id !== priciest?.id);
|
|
29686
|
-
if (cheapest)
|
|
29687
|
-
output += `- **Budget**: \`${cheapest.id}\` (${cheapest.pricing?.average || "N/A"})
|
|
29688
|
-
`;
|
|
29689
|
-
if (bigCtx && bigCtx.id !== cheapest?.id)
|
|
29690
|
-
output += `- **Large context**: \`${bigCtx.id}\` (${bigCtx.context || "N/A"} tokens)
|
|
29691
|
-
`;
|
|
29692
|
-
if (priciest && priciest.id !== cheapest?.id)
|
|
29693
|
-
output += `- **Most advanced**: \`${priciest.id}\` (${priciest.pricing?.average || "N/A"})
|
|
29844
|
+
if (fast.length > 0) {
|
|
29845
|
+
output += `## Fast variants
|
|
29846
|
+
|
|
29694
29847
|
`;
|
|
29695
|
-
|
|
29696
|
-
|
|
29848
|
+
for (const group of fast)
|
|
29849
|
+
output += renderGroup(group);
|
|
29850
|
+
}
|
|
29851
|
+
const primaries = [...flagship, ...fast].map((g) => g.primary);
|
|
29852
|
+
const picks = computeQuickPicks(primaries);
|
|
29853
|
+
const pickLines = [];
|
|
29854
|
+
if (picks.budget)
|
|
29855
|
+
pickLines.push(`- **Budget**: \`${picks.budget.id}\` (${normalizePricingDisplay(picks.budget.pricing?.average)})`);
|
|
29856
|
+
if (picks.largeContext)
|
|
29857
|
+
pickLines.push(`- **Large context**: \`${picks.largeContext.id}\` (${picks.largeContext.context || "N/A"})`);
|
|
29858
|
+
if (picks.mostCapable)
|
|
29859
|
+
pickLines.push(`- **Most capable**: \`${picks.mostCapable.id}\``);
|
|
29860
|
+
if (picks.visionCoding)
|
|
29861
|
+
pickLines.push(`- **Vision + coding**: \`${picks.visionCoding.id}\``);
|
|
29862
|
+
if (picks.agentic)
|
|
29863
|
+
pickLines.push(`- **Agentic**: \`${picks.agentic.id}\``);
|
|
29864
|
+
if (pickLines.length > 0) {
|
|
29865
|
+
output += `## Quick picks
|
|
29866
|
+
|
|
29697
29867
|
`;
|
|
29698
|
-
|
|
29699
|
-
|
|
29868
|
+
output += pickLines.join(`
|
|
29869
|
+
`) + `
|
|
29700
29870
|
`;
|
|
29871
|
+
}
|
|
29701
29872
|
return { content: [{ type: "text", text: output }] };
|
|
29702
29873
|
}
|
|
29703
29874
|
});
|
|
@@ -30321,7 +30492,7 @@ function startMcpServer() {
|
|
|
30321
30492
|
process.exit(1);
|
|
30322
30493
|
});
|
|
30323
30494
|
}
|
|
30324
|
-
var import_dotenv2, __filename3, __dirname3,
|
|
30495
|
+
var import_dotenv2, __filename3, __dirname3, CLAUDISH_CACHE_DIR, ALL_MODELS_CACHE_PATH2, CACHE_MAX_AGE_DAYS = 2, INSTRUCTIONS = `Claudish MCP server provides access to external AI models (OpenRouter, Ollama, LM Studio, etc.) for coding tasks.
|
|
30325
30496
|
|
|
30326
30497
|
## Channel Mode \u2014 External Model Sessions
|
|
30327
30498
|
|
|
@@ -30354,13 +30525,14 @@ var init_mcp_server = __esm(() => {
|
|
|
30354
30525
|
init_channel();
|
|
30355
30526
|
init_proxy_server();
|
|
30356
30527
|
init_port_manager();
|
|
30528
|
+
init_model_loader();
|
|
30529
|
+
init_provider_definitions();
|
|
30357
30530
|
import_dotenv2 = __toESM(require_main(), 1);
|
|
30358
30531
|
import_dotenv2.config();
|
|
30359
30532
|
__filename3 = fileURLToPath2(import.meta.url);
|
|
30360
|
-
__dirname3 =
|
|
30361
|
-
RECOMMENDED_MODELS_PATH = join23(__dirname3, "../recommended-models.json");
|
|
30533
|
+
__dirname3 = dirname3(__filename3);
|
|
30362
30534
|
CLAUDISH_CACHE_DIR = join23(homedir22(), ".claudish");
|
|
30363
|
-
|
|
30535
|
+
ALL_MODELS_CACHE_PATH2 = join23(CLAUDISH_CACHE_DIR, "all-models.json");
|
|
30364
30536
|
});
|
|
30365
30537
|
|
|
30366
30538
|
// ../../node_modules/.bun/@inquirer+core@11.0.1+04f2146be16c61ef/node_modules/@inquirer/core/dist/lib/key.js
|
|
@@ -41634,7 +41806,7 @@ var init_RemoveFileError = __esm(() => {
|
|
|
41634
41806
|
|
|
41635
41807
|
// ../../node_modules/.bun/@inquirer+external-editor@2.0.1+04f2146be16c61ef/node_modules/@inquirer/external-editor/dist/index.js
|
|
41636
41808
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
41637
|
-
import { readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as
|
|
41809
|
+
import { readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
41638
41810
|
import path from "path";
|
|
41639
41811
|
import os from "os";
|
|
41640
41812
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
@@ -41743,7 +41915,7 @@ class ExternalEditor {
|
|
|
41743
41915
|
if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
|
|
41744
41916
|
opt.mode = this.fileOptions.mode;
|
|
41745
41917
|
}
|
|
41746
|
-
|
|
41918
|
+
writeFileSync10(this.tempFile, this.text, opt);
|
|
41747
41919
|
} catch (createFileError) {
|
|
41748
41920
|
throw new CreateFileError(createFileError);
|
|
41749
41921
|
}
|
|
@@ -43157,50 +43329,6 @@ var init_config = __esm(() => {
|
|
|
43157
43329
|
};
|
|
43158
43330
|
});
|
|
43159
43331
|
|
|
43160
|
-
// src/utils.ts
|
|
43161
|
-
function fuzzyScore2(text, query) {
|
|
43162
|
-
if (!text || !query)
|
|
43163
|
-
return 0;
|
|
43164
|
-
const t = text.toLowerCase();
|
|
43165
|
-
const q = query.toLowerCase();
|
|
43166
|
-
if (t === q)
|
|
43167
|
-
return 1;
|
|
43168
|
-
if (t.startsWith(q))
|
|
43169
|
-
return 0.9;
|
|
43170
|
-
if (t.includes(` ${q}`) || t.includes(`-${q}`) || t.includes(`/${q}`))
|
|
43171
|
-
return 0.8;
|
|
43172
|
-
if (t.includes(q))
|
|
43173
|
-
return 0.6;
|
|
43174
|
-
const normSep = (s) => s.replace(/[\s\-_.]/g, "");
|
|
43175
|
-
const tn = normSep(t);
|
|
43176
|
-
const qn = normSep(q);
|
|
43177
|
-
if (tn === qn)
|
|
43178
|
-
return 0.95;
|
|
43179
|
-
if (tn.startsWith(qn))
|
|
43180
|
-
return 0.85;
|
|
43181
|
-
if (tn.includes(qn))
|
|
43182
|
-
return 0.65;
|
|
43183
|
-
let score = 0;
|
|
43184
|
-
let tIdx = 0;
|
|
43185
|
-
let qIdx = 0;
|
|
43186
|
-
let consecutive = 0;
|
|
43187
|
-
while (tIdx < t.length && qIdx < q.length) {
|
|
43188
|
-
if (t[tIdx] === q[qIdx]) {
|
|
43189
|
-
score += 1 + consecutive * 0.5;
|
|
43190
|
-
consecutive++;
|
|
43191
|
-
qIdx++;
|
|
43192
|
-
} else {
|
|
43193
|
-
consecutive = 0;
|
|
43194
|
-
}
|
|
43195
|
-
tIdx++;
|
|
43196
|
-
}
|
|
43197
|
-
if (qIdx === q.length) {
|
|
43198
|
-
const compactness = q.length / (tIdx + 1);
|
|
43199
|
-
return 0.1 + 0.4 * compactness * (score / (q.length * 2));
|
|
43200
|
-
}
|
|
43201
|
-
return 0;
|
|
43202
|
-
}
|
|
43203
|
-
|
|
43204
43332
|
// src/providers/api-key-map.ts
|
|
43205
43333
|
var API_KEY_MAP;
|
|
43206
43334
|
var init_api_key_map = __esm(() => {
|
|
@@ -43540,7 +43668,7 @@ import { EventEmitter } from "events";
|
|
|
43540
43668
|
import { Buffer as Buffer2 } from "buffer";
|
|
43541
43669
|
import { Buffer as Buffer3 } from "buffer";
|
|
43542
43670
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
43543
|
-
import { resolve as resolve3, dirname as
|
|
43671
|
+
import { resolve as resolve3, dirname as dirname4 } from "path";
|
|
43544
43672
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
43545
43673
|
import { resolve as resolve22, isAbsolute, parse as parse6 } from "path";
|
|
43546
43674
|
import { existsSync as existsSync22 } from "fs";
|
|
@@ -43549,7 +43677,7 @@ import os2 from "os";
|
|
|
43549
43677
|
import path2 from "path";
|
|
43550
43678
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
43551
43679
|
import { dlopen, toArrayBuffer as toArrayBuffer4, JSCallback, ptr as ptr4 } from "bun:ffi";
|
|
43552
|
-
import { existsSync as existsSync23, writeFileSync as
|
|
43680
|
+
import { existsSync as existsSync23, writeFileSync as writeFileSync11 } from "fs";
|
|
43553
43681
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
43554
43682
|
import { toArrayBuffer, ptr } from "bun:ffi";
|
|
43555
43683
|
import { ptr as ptr2, toArrayBuffer as toArrayBuffer2 } from "bun:ffi";
|
|
@@ -45885,24 +46013,24 @@ function getParsers() {
|
|
|
45885
46013
|
{
|
|
45886
46014
|
filetype: "javascript",
|
|
45887
46015
|
queries: {
|
|
45888
|
-
highlights: [resolve3(
|
|
46016
|
+
highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default)]
|
|
45889
46017
|
},
|
|
45890
|
-
wasm: resolve3(
|
|
46018
|
+
wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_javascript_default)
|
|
45891
46019
|
},
|
|
45892
46020
|
{
|
|
45893
46021
|
filetype: "typescript",
|
|
45894
46022
|
queries: {
|
|
45895
|
-
highlights: [resolve3(
|
|
46023
|
+
highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default2)]
|
|
45896
46024
|
},
|
|
45897
|
-
wasm: resolve3(
|
|
46025
|
+
wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_typescript_default)
|
|
45898
46026
|
},
|
|
45899
46027
|
{
|
|
45900
46028
|
filetype: "markdown",
|
|
45901
46029
|
queries: {
|
|
45902
|
-
highlights: [resolve3(
|
|
45903
|
-
injections: [resolve3(
|
|
46030
|
+
highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default3)],
|
|
46031
|
+
injections: [resolve3(dirname4(fileURLToPath3(import.meta.url)), injections_default)]
|
|
45904
46032
|
},
|
|
45905
|
-
wasm: resolve3(
|
|
46033
|
+
wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_markdown_default),
|
|
45906
46034
|
injectionMapping: {
|
|
45907
46035
|
nodeTypes: {
|
|
45908
46036
|
inline: "markdown_inline",
|
|
@@ -45921,16 +46049,16 @@ function getParsers() {
|
|
|
45921
46049
|
{
|
|
45922
46050
|
filetype: "markdown_inline",
|
|
45923
46051
|
queries: {
|
|
45924
|
-
highlights: [resolve3(
|
|
46052
|
+
highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default4)]
|
|
45925
46053
|
},
|
|
45926
|
-
wasm: resolve3(
|
|
46054
|
+
wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_markdown_inline_default)
|
|
45927
46055
|
},
|
|
45928
46056
|
{
|
|
45929
46057
|
filetype: "zig",
|
|
45930
46058
|
queries: {
|
|
45931
|
-
highlights: [resolve3(
|
|
46059
|
+
highlights: [resolve3(dirname4(fileURLToPath3(import.meta.url)), highlights_default5)]
|
|
45932
46060
|
},
|
|
45933
|
-
wasm: resolve3(
|
|
46061
|
+
wasm: resolve3(dirname4(fileURLToPath3(import.meta.url)), tree_sitter_zig_default)
|
|
45934
46062
|
}
|
|
45935
46063
|
];
|
|
45936
46064
|
}
|
|
@@ -48895,7 +49023,7 @@ function convertToDebugSymbols(symbols) {
|
|
|
48895
49023
|
if (env.OTUI_DEBUG_FFI && globalFFILogPath) {
|
|
48896
49024
|
const logPath = globalFFILogPath;
|
|
48897
49025
|
const writeSync4 = (msg) => {
|
|
48898
|
-
|
|
49026
|
+
writeFileSync11(logPath, msg + `
|
|
48899
49027
|
`, { flag: "a" });
|
|
48900
49028
|
};
|
|
48901
49029
|
Object.entries(symbols).forEach(([key, value]) => {
|
|
@@ -100229,15 +100357,14 @@ __export(exports_cli, {
|
|
|
100229
100357
|
});
|
|
100230
100358
|
import {
|
|
100231
100359
|
readFileSync as readFileSync20,
|
|
100232
|
-
writeFileSync as writeFileSync13,
|
|
100233
100360
|
existsSync as existsSync24,
|
|
100234
|
-
mkdirSync as
|
|
100361
|
+
mkdirSync as mkdirSync10,
|
|
100235
100362
|
copyFileSync,
|
|
100236
100363
|
readdirSync as readdirSync4,
|
|
100237
100364
|
unlinkSync as unlinkSync7
|
|
100238
100365
|
} from "fs";
|
|
100239
100366
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
100240
|
-
import { dirname as
|
|
100367
|
+
import { dirname as dirname5, join as join25 } from "path";
|
|
100241
100368
|
import { homedir as homedir23 } from "os";
|
|
100242
100369
|
function getVersion3() {
|
|
100243
100370
|
return VERSION;
|
|
@@ -100246,7 +100373,7 @@ function clearAllModelCaches() {
|
|
|
100246
100373
|
const cacheDir = join25(homedir23(), ".claudish");
|
|
100247
100374
|
if (!existsSync24(cacheDir))
|
|
100248
100375
|
return;
|
|
100249
|
-
const cachePatterns = ["
|
|
100376
|
+
const cachePatterns = ["pricing-cache.json", "recommended-models-cache.json"];
|
|
100250
100377
|
let cleared = 0;
|
|
100251
100378
|
try {
|
|
100252
100379
|
const files = readdirSync4(cacheDir);
|
|
@@ -100444,25 +100571,54 @@ async function parseArgs(args) {
|
|
|
100444
100571
|
const forceUpdate = args.includes("--force-update");
|
|
100445
100572
|
if (forceUpdate)
|
|
100446
100573
|
clearAllModelCaches();
|
|
100447
|
-
await
|
|
100448
|
-
if (hasJsonFlag) {
|
|
100449
|
-
printAvailableModelsJSON();
|
|
100450
|
-
} else {
|
|
100451
|
-
printAvailableModels();
|
|
100452
|
-
}
|
|
100574
|
+
await printRecommendedModels(hasJsonFlag, forceUpdate);
|
|
100453
100575
|
process.exit(0);
|
|
100576
|
+
} else if (arg === "--list-providers") {
|
|
100577
|
+
const hasJsonFlag = args.includes("--json");
|
|
100578
|
+
try {
|
|
100579
|
+
const providers = await getProviderList();
|
|
100580
|
+
if (hasJsonFlag) {
|
|
100581
|
+
console.log(JSON.stringify({ providers, total: providers.length }, null, 2));
|
|
100582
|
+
} else {
|
|
100583
|
+
console.log(`
|
|
100584
|
+
Providers in Firebase catalog:
|
|
100585
|
+
`);
|
|
100586
|
+
console.log(" Slug Active models");
|
|
100587
|
+
console.log(" " + "\u2500".repeat(40));
|
|
100588
|
+
for (const { slug, count } of providers) {
|
|
100589
|
+
console.log(` ${slug.padEnd(20)} ${String(count).padStart(5)}`);
|
|
100590
|
+
}
|
|
100591
|
+
console.log(`
|
|
100592
|
+
Usage: claudish --list-models --provider <slug>`);
|
|
100593
|
+
console.log(` claudish -s <query> (fuzzy search)
|
|
100594
|
+
`);
|
|
100595
|
+
}
|
|
100596
|
+
process.exit(0);
|
|
100597
|
+
} catch (err) {
|
|
100598
|
+
console.error(`Failed to fetch providers: ${err instanceof Error ? err.message : String(err)}`);
|
|
100599
|
+
process.exit(1);
|
|
100600
|
+
}
|
|
100454
100601
|
} else if (arg === "--models" || arg === "--list-models" || arg === "-s" || arg === "--search") {
|
|
100455
100602
|
const nextArg = args[i + 1];
|
|
100456
100603
|
const hasQuery = nextArg && !nextArg.startsWith("--");
|
|
100457
100604
|
const query = hasQuery ? args[++i] : null;
|
|
100458
100605
|
const hasJsonFlag = args.includes("--json");
|
|
100459
100606
|
const forceUpdate = args.includes("--force-update");
|
|
100607
|
+
const providerIdx = args.indexOf("--provider");
|
|
100608
|
+
const providerSlug = providerIdx !== -1 && providerIdx + 1 < args.length ? args[providerIdx + 1] : null;
|
|
100460
100609
|
if (forceUpdate)
|
|
100461
100610
|
clearAllModelCaches();
|
|
100611
|
+
if (query && providerSlug) {
|
|
100612
|
+
console.error("Use --provider together with --list-models (without a query) to filter the catalog.");
|
|
100613
|
+
console.error("For keyword search, drop --provider: claudish -s <query>");
|
|
100614
|
+
process.exit(1);
|
|
100615
|
+
}
|
|
100462
100616
|
if (query) {
|
|
100463
|
-
await searchAndPrintModels(query,
|
|
100617
|
+
await searchAndPrintModels(query, hasJsonFlag);
|
|
100618
|
+
} else if (providerSlug) {
|
|
100619
|
+
await printByProvider(providerSlug, hasJsonFlag);
|
|
100464
100620
|
} else {
|
|
100465
|
-
await
|
|
100621
|
+
await printTop100(hasJsonFlag);
|
|
100466
100622
|
}
|
|
100467
100623
|
process.exit(0);
|
|
100468
100624
|
} else if (arg === "--summarize-tools") {
|
|
@@ -100585,502 +100741,298 @@ async function fetchOllamaModels() {
|
|
|
100585
100741
|
return [];
|
|
100586
100742
|
}
|
|
100587
100743
|
}
|
|
100588
|
-
|
|
100589
|
-
|
|
100590
|
-
|
|
100591
|
-
|
|
100592
|
-
|
|
100593
|
-
|
|
100594
|
-
|
|
100595
|
-
|
|
100596
|
-
|
|
100597
|
-
|
|
100598
|
-
|
|
100599
|
-
|
|
100744
|
+
function formatModelDocPricing(pricing) {
|
|
100745
|
+
if (!pricing)
|
|
100746
|
+
return "N/A";
|
|
100747
|
+
const input = typeof pricing.input === "number" ? pricing.input : undefined;
|
|
100748
|
+
const output = typeof pricing.output === "number" ? pricing.output : undefined;
|
|
100749
|
+
if (input === undefined && output === undefined)
|
|
100750
|
+
return "N/A";
|
|
100751
|
+
if ((input ?? 0) === 0 && (output ?? 0) === 0)
|
|
100752
|
+
return "FREE";
|
|
100753
|
+
const avg = ((input ?? 0) + (output ?? 0)) / 2;
|
|
100754
|
+
return `$${avg.toFixed(2)}/1M`;
|
|
100755
|
+
}
|
|
100756
|
+
function formatModelDocContext(ctx) {
|
|
100757
|
+
if (!ctx || ctx <= 0)
|
|
100758
|
+
return "N/A";
|
|
100759
|
+
if (ctx >= 1e6)
|
|
100760
|
+
return `${Math.round(ctx / 1e6)}M`;
|
|
100761
|
+
return `${Math.round(ctx / 1000)}K`;
|
|
100762
|
+
}
|
|
100763
|
+
function formatModelDocCaps(caps) {
|
|
100764
|
+
if (!caps)
|
|
100765
|
+
return "\xB7";
|
|
100766
|
+
const parts = [];
|
|
100767
|
+
if (caps.tools)
|
|
100768
|
+
parts.push("T");
|
|
100769
|
+
if (caps.thinking)
|
|
100770
|
+
parts.push("R");
|
|
100771
|
+
if (caps.vision)
|
|
100772
|
+
parts.push("V");
|
|
100773
|
+
return parts.length > 0 ? parts.join("") : "\xB7";
|
|
100774
|
+
}
|
|
100775
|
+
async function searchAndPrintModels(query, jsonOutput) {
|
|
100776
|
+
let results;
|
|
100777
|
+
try {
|
|
100778
|
+
console.error(`\uD83D\uDD04 Searching Firebase catalog for "${query}"...`);
|
|
100779
|
+
results = await searchModels(query, 50);
|
|
100780
|
+
} catch (error2) {
|
|
100781
|
+
console.error(`\u274C Failed to reach Firebase model catalog: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
100782
|
+
console.error(" Check your network connection.");
|
|
100783
|
+
process.exit(1);
|
|
100600
100784
|
}
|
|
100601
|
-
if (
|
|
100602
|
-
|
|
100603
|
-
|
|
100604
|
-
|
|
100605
|
-
|
|
100606
|
-
throw new Error(`API returned ${response.status}`);
|
|
100607
|
-
const data = await response.json();
|
|
100608
|
-
models = data.data;
|
|
100609
|
-
mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
|
|
100610
|
-
writeFileSync13(ALL_MODELS_JSON_PATH, JSON.stringify({
|
|
100611
|
-
lastUpdated: new Date().toISOString(),
|
|
100612
|
-
models
|
|
100613
|
-
}), "utf-8");
|
|
100614
|
-
console.error(`\u2705 Cached ${models.length} models`);
|
|
100615
|
-
} catch (error2) {
|
|
100616
|
-
console.error(`\u274C Failed to fetch models: ${error2}`);
|
|
100617
|
-
process.exit(1);
|
|
100785
|
+
if (results.length === 0) {
|
|
100786
|
+
if (jsonOutput) {
|
|
100787
|
+
console.log(JSON.stringify({ query, count: 0, models: [] }, null, 2));
|
|
100788
|
+
} else {
|
|
100789
|
+
console.log(`No models found matching "${query}"`);
|
|
100618
100790
|
}
|
|
100791
|
+
return;
|
|
100619
100792
|
}
|
|
100620
|
-
|
|
100621
|
-
|
|
100622
|
-
|
|
100623
|
-
|
|
100624
|
-
|
|
100625
|
-
|
|
100626
|
-
|
|
100627
|
-
|
|
100628
|
-
|
|
100629
|
-
|
|
100630
|
-
|
|
100631
|
-
|
|
100632
|
-
|
|
100633
|
-
|
|
100634
|
-
const openaiModels = Object.entries(openaiData.models).filter(([id, _2]) => {
|
|
100635
|
-
const lowerId = id.toLowerCase();
|
|
100636
|
-
return lowerId.startsWith("gpt-") || lowerId.startsWith("o1-") || lowerId.startsWith("o3-") || lowerId.startsWith("o4-") || lowerId.startsWith("chatgpt-");
|
|
100637
|
-
}).map(([id, m2]) => {
|
|
100638
|
-
const inputCost = m2.cost?.input || 2;
|
|
100639
|
-
const outputCost = m2.cost?.output || 8;
|
|
100640
|
-
const contextLen = m2.limit?.context || 128000;
|
|
100641
|
-
const inputModalities = m2.modalities?.input || [];
|
|
100642
|
-
return {
|
|
100643
|
-
id: `oai/${id}`,
|
|
100644
|
-
name: m2.name || id,
|
|
100645
|
-
description: `OpenAI direct model`,
|
|
100646
|
-
context_length: contextLen,
|
|
100647
|
-
pricing: {
|
|
100648
|
-
prompt: String(inputCost / 1e6),
|
|
100649
|
-
completion: String(outputCost / 1e6)
|
|
100650
|
-
},
|
|
100651
|
-
isOAIDirect: true,
|
|
100652
|
-
supportsTools: m2.tool_call === true,
|
|
100653
|
-
supportsReasoning: m2.reasoning === true,
|
|
100654
|
-
supportsVision: inputModalities.includes("image") || inputModalities.includes("video")
|
|
100655
|
-
};
|
|
100656
|
-
});
|
|
100657
|
-
console.error(`\uD83D\uDD11 Found ${openaiModels.length} OpenAI direct models`);
|
|
100658
|
-
models = [...openaiModels, ...models];
|
|
100659
|
-
}
|
|
100660
|
-
}
|
|
100661
|
-
} catch {}
|
|
100662
|
-
}
|
|
100663
|
-
if (process.env.GLM_CODING_API_KEY) {
|
|
100664
|
-
try {
|
|
100665
|
-
const glmCodingModels = await fetchGLMCodingModels();
|
|
100666
|
-
if (glmCodingModels.length > 0) {
|
|
100667
|
-
console.error(`\uD83D\uDD11 Found ${glmCodingModels.length} GLM Coding Plan models`);
|
|
100668
|
-
models = [...glmCodingModels, ...models];
|
|
100669
|
-
}
|
|
100670
|
-
} catch {}
|
|
100671
|
-
}
|
|
100672
|
-
if (process.env.LITELLM_BASE_URL && process.env.LITELLM_API_KEY) {
|
|
100673
|
-
try {
|
|
100674
|
-
const litellmModels = await fetchLiteLLMModels(process.env.LITELLM_BASE_URL, process.env.LITELLM_API_KEY, forceUpdate);
|
|
100675
|
-
if (litellmModels.length > 0) {
|
|
100676
|
-
console.error(`\uD83D\uDD17 Found ${litellmModels.length} LiteLLM models`);
|
|
100677
|
-
models = [...litellmModels, ...models];
|
|
100678
|
-
}
|
|
100679
|
-
} catch {}
|
|
100680
|
-
}
|
|
100681
|
-
const results = models.map((model) => {
|
|
100682
|
-
const nameScore = fuzzyScore2(model.name || "", query);
|
|
100683
|
-
const idScore = fuzzyScore2(model.id || "", query);
|
|
100684
|
-
const descScore = fuzzyScore2(model.description || "", query) * 0.5;
|
|
100685
|
-
return {
|
|
100686
|
-
model,
|
|
100687
|
-
score: Math.max(nameScore, idScore, descScore)
|
|
100688
|
-
};
|
|
100689
|
-
}).filter((item) => item.score > 0.2).sort((a, b2) => b2.score - a.score).slice(0, 20);
|
|
100690
|
-
if (results.length === 0) {
|
|
100691
|
-
console.log(`No models found matching "${query}"`);
|
|
100793
|
+
if (jsonOutput) {
|
|
100794
|
+
console.log(JSON.stringify({
|
|
100795
|
+
query,
|
|
100796
|
+
count: results.length,
|
|
100797
|
+
models: results.map((m2) => ({
|
|
100798
|
+
id: m2.modelId,
|
|
100799
|
+
provider: m2.provider,
|
|
100800
|
+
contextWindow: m2.contextWindow,
|
|
100801
|
+
pricing: m2.pricing,
|
|
100802
|
+
capabilities: m2.capabilities,
|
|
100803
|
+
aliases: m2.aliases,
|
|
100804
|
+
status: m2.status
|
|
100805
|
+
}))
|
|
100806
|
+
}, null, 2));
|
|
100692
100807
|
return;
|
|
100693
100808
|
}
|
|
100694
|
-
const RED2 = "\x1B[31m";
|
|
100695
|
-
const GREEN = "\x1B[32m";
|
|
100696
|
-
const RESET = "\x1B[0m";
|
|
100697
|
-
const DIM = "\x1B[2m";
|
|
100698
100809
|
console.log(`
|
|
100699
100810
|
Found ${results.length} matching models:
|
|
100700
100811
|
`);
|
|
100701
|
-
console.log(" Model Provider Pricing Context
|
|
100812
|
+
console.log(" Model Provider Pricing Context Caps");
|
|
100702
100813
|
console.log(" " + "\u2500".repeat(80));
|
|
100703
|
-
for (const
|
|
100704
|
-
|
|
100705
|
-
|
|
100706
|
-
|
|
100707
|
-
|
|
100708
|
-
|
|
100709
|
-
|
|
100710
|
-
|
|
100711
|
-
} else if (model.source === "LiteLLM" || model.id.startsWith("litellm@")) {
|
|
100712
|
-
fullModelId = model.id;
|
|
100713
|
-
} else {
|
|
100714
|
-
fullModelId = `openrouter@${model.id}`;
|
|
100715
|
-
}
|
|
100716
|
-
const modelId = fullModelId.length > 30 ? fullModelId.substring(0, 27) + "..." : fullModelId;
|
|
100717
|
-
const modelIdPadded = modelId.padEnd(30);
|
|
100718
|
-
const providerName = model.id.split("/")[0];
|
|
100719
|
-
const provider = providerName.length > 10 ? providerName.substring(0, 7) + "..." : providerName;
|
|
100720
|
-
const providerPadded = provider.padEnd(10);
|
|
100721
|
-
let pricing;
|
|
100722
|
-
if (model.isLocal) {
|
|
100723
|
-
pricing = "LOCAL";
|
|
100724
|
-
} else {
|
|
100725
|
-
const promptPrice = parseFloat(model.pricing?.prompt || "0") * 1e6;
|
|
100726
|
-
const completionPrice = parseFloat(model.pricing?.completion || "0") * 1e6;
|
|
100727
|
-
const avg = (promptPrice + completionPrice) / 2;
|
|
100728
|
-
if (avg < 0) {
|
|
100729
|
-
pricing = "varies";
|
|
100730
|
-
} else if (avg === 0) {
|
|
100731
|
-
pricing = "FREE";
|
|
100732
|
-
} else {
|
|
100733
|
-
pricing = `$${avg.toFixed(2)}/1M`;
|
|
100734
|
-
}
|
|
100735
|
-
}
|
|
100736
|
-
const pricingPadded = pricing.padEnd(10);
|
|
100737
|
-
const contextLen = model.context_length || model.top_provider?.context_length || 0;
|
|
100738
|
-
const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
|
|
100739
|
-
const contextPadded = context.padEnd(7);
|
|
100740
|
-
if (model.isLocal && model.supportsTools === false) {
|
|
100741
|
-
console.log(` ${RED2}${modelIdPadded} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}% \u2717 no tools${RESET}`);
|
|
100742
|
-
} else if (model.isLocal && model.supportsTools === true) {
|
|
100743
|
-
console.log(` ${GREEN}${modelIdPadded}${RESET} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}%`);
|
|
100744
|
-
} else {
|
|
100745
|
-
console.log(` ${modelIdPadded} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}%`);
|
|
100746
|
-
}
|
|
100814
|
+
for (const m2 of results) {
|
|
100815
|
+
const id = m2.modelId.length > 30 ? m2.modelId.substring(0, 27) + "..." : m2.modelId;
|
|
100816
|
+
const idPadded = id.padEnd(30);
|
|
100817
|
+
const prov = (m2.provider || "").padEnd(10);
|
|
100818
|
+
const price = formatModelDocPricing(m2.pricing).padEnd(10);
|
|
100819
|
+
const ctx = formatModelDocContext(m2.contextWindow).padEnd(7);
|
|
100820
|
+
const caps = formatModelDocCaps(m2.capabilities);
|
|
100821
|
+
console.log(` ${idPadded} ${prov} ${price} ${ctx} ${caps}`);
|
|
100747
100822
|
}
|
|
100748
100823
|
console.log("");
|
|
100749
|
-
console.log(
|
|
100824
|
+
console.log("Caps: T = tools R = reasoning V = vision");
|
|
100750
100825
|
console.log("");
|
|
100751
|
-
console.log("Use
|
|
100752
|
-
console.log("
|
|
100753
|
-
|
|
100754
|
-
|
|
100755
|
-
|
|
100756
|
-
|
|
100757
|
-
|
|
100758
|
-
|
|
100759
|
-
|
|
100760
|
-
|
|
100826
|
+
console.log("Use any model by its ID: claudish --model <model-id>");
|
|
100827
|
+
console.log("Provider shortcuts: claudish --model or@<id> | google@<id> | oai@<id>");
|
|
100828
|
+
}
|
|
100829
|
+
function renderModelDocTable(models, showRank) {
|
|
100830
|
+
const header = showRank ? " # Model Provider Pricing Context Caps" : " Model Provider Pricing Context Caps";
|
|
100831
|
+
console.log(header);
|
|
100832
|
+
console.log(" " + "\u2500".repeat(80));
|
|
100833
|
+
for (const m2 of models) {
|
|
100834
|
+
const rankCell = showRank ? String(m2.rank ?? "").padStart(3) + " " : " ";
|
|
100835
|
+
const rawId = m2.modelId;
|
|
100836
|
+
const id = rawId.length > 30 ? rawId.substring(0, 27) + "..." : rawId;
|
|
100837
|
+
const idPadded = id.padEnd(30);
|
|
100838
|
+
const prov = (m2.provider || "").padEnd(10);
|
|
100839
|
+
const price = formatModelDocPricing(m2.pricing).padEnd(10);
|
|
100840
|
+
const ctx = formatModelDocContext(m2.contextWindow).padEnd(7);
|
|
100841
|
+
const caps = formatModelDocCaps(m2.capabilities);
|
|
100842
|
+
console.log(` ${rankCell}${idPadded} ${prov} ${price} ${ctx} ${caps}`);
|
|
100843
|
+
}
|
|
100844
|
+
}
|
|
100845
|
+
async function printLocalProvidersFooter() {
|
|
100846
|
+
console.log(`
|
|
100847
|
+
Local providers`);
|
|
100848
|
+
console.log(" " + "\u2500".repeat(70));
|
|
100849
|
+
let ollamaLine = " Ollama: not running";
|
|
100850
|
+
try {
|
|
100851
|
+
const ollamaModels = await fetchOllamaModels();
|
|
100852
|
+
if (ollamaModels.length > 0) {
|
|
100853
|
+
const toolCount = ollamaModels.filter((m2) => m2.supportsTools).length;
|
|
100854
|
+
ollamaLine = ` Ollama: ${ollamaModels.length} models installed (${toolCount} with tools) \u2014 use: claudish --model ollama@<name>`;
|
|
100855
|
+
}
|
|
100856
|
+
} catch {}
|
|
100857
|
+
console.log(ollamaLine);
|
|
100858
|
+
let litellmLine = " LiteLLM: not configured (set LITELLM_BASE_URL + LITELLM_API_KEY)";
|
|
100859
|
+
if (process.env.LITELLM_BASE_URL && process.env.LITELLM_API_KEY) {
|
|
100761
100860
|
try {
|
|
100762
|
-
const
|
|
100763
|
-
|
|
100764
|
-
|
|
100765
|
-
|
|
100766
|
-
|
|
100767
|
-
models = cacheData.models;
|
|
100768
|
-
if (!jsonOutput) {
|
|
100769
|
-
console.error(`\u2713 Using cached models (last updated: ${cacheData.lastUpdated.split("T")[0]})`);
|
|
100770
|
-
}
|
|
100861
|
+
const litellmModels = await fetchLiteLLMModels(process.env.LITELLM_BASE_URL, process.env.LITELLM_API_KEY, false);
|
|
100862
|
+
if (litellmModels.length > 0) {
|
|
100863
|
+
litellmLine = ` LiteLLM: ${litellmModels.length} model groups configured \u2014 use: claudish --model litellm@<group>`;
|
|
100864
|
+
} else {
|
|
100865
|
+
litellmLine = " LiteLLM: reachable but no model groups returned";
|
|
100771
100866
|
}
|
|
100772
|
-
} catch
|
|
100773
|
-
|
|
100774
|
-
if (models.length === 0) {
|
|
100775
|
-
console.error("\uD83D\uDD04 Fetching all models from OpenRouter...");
|
|
100776
|
-
try {
|
|
100777
|
-
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
100778
|
-
if (!response.ok)
|
|
100779
|
-
throw new Error(`API returned ${response.status}`);
|
|
100780
|
-
const data = await response.json();
|
|
100781
|
-
models = data.data;
|
|
100782
|
-
mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
|
|
100783
|
-
writeFileSync13(ALL_MODELS_JSON_PATH, JSON.stringify({
|
|
100784
|
-
lastUpdated: new Date().toISOString(),
|
|
100785
|
-
models
|
|
100786
|
-
}), "utf-8");
|
|
100787
|
-
console.error(`\u2705 Cached ${models.length} models`);
|
|
100788
|
-
} catch (error2) {
|
|
100789
|
-
console.error(`\u274C Failed to fetch models: ${error2}`);
|
|
100790
|
-
process.exit(1);
|
|
100867
|
+
} catch {
|
|
100868
|
+
litellmLine = " LiteLLM: configured but unreachable";
|
|
100791
100869
|
}
|
|
100792
100870
|
}
|
|
100871
|
+
console.log(litellmLine);
|
|
100872
|
+
}
|
|
100873
|
+
async function printTop100(jsonOutput) {
|
|
100874
|
+
let response;
|
|
100875
|
+
try {
|
|
100876
|
+
response = await getTop100Models();
|
|
100877
|
+
} catch (error2) {
|
|
100878
|
+
console.error(`\u274C Failed to load top-100 models from Firebase: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
100879
|
+
console.error(" Check your network connection.");
|
|
100880
|
+
process.exit(1);
|
|
100881
|
+
}
|
|
100793
100882
|
if (jsonOutput) {
|
|
100794
|
-
|
|
100795
|
-
console.log(JSON.stringify({
|
|
100796
|
-
count: allModels.length,
|
|
100797
|
-
localCount: ollamaModels.length,
|
|
100798
|
-
zenCount: zenModels.length,
|
|
100799
|
-
lastUpdated: new Date().toISOString().split("T")[0],
|
|
100800
|
-
models: allModels.map((m2) => {
|
|
100801
|
-
let id;
|
|
100802
|
-
if (m2.isLocal) {
|
|
100803
|
-
id = m2.id.replace("ollama/", "ollama@");
|
|
100804
|
-
} else if (m2.isZen || m2.id.startsWith("zen/")) {
|
|
100805
|
-
id = m2.id.replace("zen/", "zen@");
|
|
100806
|
-
} else if (m2.source === "LiteLLM" || m2.id.startsWith("litellm@")) {
|
|
100807
|
-
id = m2.id;
|
|
100808
|
-
} else {
|
|
100809
|
-
id = `openrouter@${m2.id}`;
|
|
100810
|
-
}
|
|
100811
|
-
return {
|
|
100812
|
-
id,
|
|
100813
|
-
name: m2.name,
|
|
100814
|
-
context: m2.context_length || m2.top_provider?.context_length,
|
|
100815
|
-
pricing: m2.pricing,
|
|
100816
|
-
isLocal: m2.isLocal || false,
|
|
100817
|
-
isZen: m2.isZen || false
|
|
100818
|
-
};
|
|
100819
|
-
})
|
|
100820
|
-
}, null, 2));
|
|
100883
|
+
console.log(JSON.stringify(response, null, 2));
|
|
100821
100884
|
return;
|
|
100822
100885
|
}
|
|
100823
|
-
|
|
100824
|
-
|
|
100825
|
-
const RESET = "\x1B[0m";
|
|
100826
|
-
const DIM = "\x1B[2m";
|
|
100827
|
-
if (ollamaModels.length > 0) {
|
|
100828
|
-
const toolCapableCount = ollamaModels.filter((m2) => m2.supportsTools).length;
|
|
100829
|
-
console.log(`
|
|
100830
|
-
\uD83C\uDFE0 LOCAL OLLAMA MODELS (${ollamaModels.length} installed, ${toolCapableCount} with tool support):
|
|
100886
|
+
console.log(`
|
|
100887
|
+
Top ${response.total} models from Firebase (pool: ${response.poolSize} eligible)
|
|
100831
100888
|
`);
|
|
100832
|
-
|
|
100833
|
-
console.log("
|
|
100834
|
-
for (const model of ollamaModels) {
|
|
100835
|
-
const fullId = model.id.replace("ollama/", "ollama@");
|
|
100836
|
-
const modelId = fullId.length > 35 ? fullId.substring(0, 32) + "..." : fullId;
|
|
100837
|
-
const modelIdPadded = modelId.padEnd(38);
|
|
100838
|
-
const size = model.size ? `${(model.size / 1e9).toFixed(1)}GB` : "N/A";
|
|
100839
|
-
const sizePadded = size.padEnd(12);
|
|
100840
|
-
const params = model.details?.parameter_size || "N/A";
|
|
100841
|
-
const paramsPadded = params.padEnd(8);
|
|
100842
|
-
if (model.supportsTools) {
|
|
100843
|
-
console.log(` ${modelIdPadded} ${sizePadded} ${paramsPadded} ${GREEN}\u2713${RESET}`);
|
|
100844
|
-
} else {
|
|
100845
|
-
console.log(` ${RED2}${modelIdPadded} ${sizePadded} ${paramsPadded} \u2717 no tools${RESET}`);
|
|
100846
|
-
}
|
|
100847
|
-
}
|
|
100848
|
-
console.log("");
|
|
100849
|
-
console.log(` ${GREEN}\u2713${RESET} = Compatible with Claude Code (supports tool calling)`);
|
|
100850
|
-
console.log(` ${RED2}\u2717${RESET} = Not compatible ${DIM}(Claude Code requires tool support)${RESET}`);
|
|
100851
|
-
console.log("");
|
|
100852
|
-
console.log(" Use: claudish --model ollama@<model-name>");
|
|
100853
|
-
console.log(" Pull a compatible model: ollama pull llama3.2");
|
|
100889
|
+
if (response.models.length === 0) {
|
|
100890
|
+
console.log(" No eligible models in the catalog.");
|
|
100854
100891
|
} else {
|
|
100855
|
-
|
|
100856
|
-
\uD83C\uDFE0 LOCAL OLLAMA: Not running or no models installed`);
|
|
100857
|
-
console.log(" Start Ollama: ollama serve");
|
|
100858
|
-
console.log(" Pull a model: ollama pull llama3.2");
|
|
100859
|
-
}
|
|
100860
|
-
if (zenModels.length > 0) {
|
|
100861
|
-
const freeCount = zenModels.filter((m2) => m2.isFree).length;
|
|
100862
|
-
console.log(`
|
|
100863
|
-
\uD83D\uDD2E OPENCODE ZEN (${zenModels.length} models, ${freeCount} FREE - no API key needed):
|
|
100864
|
-
`);
|
|
100865
|
-
console.log(" Model Context Pricing Tools");
|
|
100866
|
-
console.log(" " + "\u2500".repeat(68));
|
|
100867
|
-
const sortedModels = [...zenModels].sort((a, b2) => {
|
|
100868
|
-
if (a.isFree && !b2.isFree)
|
|
100869
|
-
return -1;
|
|
100870
|
-
if (!a.isFree && b2.isFree)
|
|
100871
|
-
return 1;
|
|
100872
|
-
return (b2.context_length || 0) - (a.context_length || 0);
|
|
100873
|
-
});
|
|
100874
|
-
for (const model of sortedModels) {
|
|
100875
|
-
const fullId = model.id.replace("zen/", "zen@");
|
|
100876
|
-
const modelId = fullId.length > 30 ? fullId.substring(0, 27) + "..." : fullId;
|
|
100877
|
-
const modelIdPadded = modelId.padEnd(32);
|
|
100878
|
-
const contextLen = model.context_length || 0;
|
|
100879
|
-
const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
|
|
100880
|
-
const contextPadded = context.padEnd(10);
|
|
100881
|
-
const pricing = model.isFree ? `${GREEN}FREE${RESET}` : `$${(parseFloat(model.pricing?.prompt || "0") + parseFloat(model.pricing?.completion || "0")).toFixed(1)}/M`;
|
|
100882
|
-
const pricingPadded = model.isFree ? "FREE " : pricing.padEnd(12);
|
|
100883
|
-
const tools = model.supportsTools ? `${GREEN}\u2713${RESET}` : `${RED2}\u2717${RESET}`;
|
|
100884
|
-
console.log(` ${modelIdPadded} ${contextPadded} ${pricingPadded} ${tools}`);
|
|
100885
|
-
}
|
|
100892
|
+
renderModelDocTable(response.models, true);
|
|
100886
100893
|
console.log("");
|
|
100887
|
-
console.log(
|
|
100888
|
-
console.log(" Use: claudish --model zen@<model-id>");
|
|
100894
|
+
console.log(" Caps: T = tools R = reasoning V = vision");
|
|
100889
100895
|
}
|
|
100890
|
-
|
|
100891
|
-
|
|
100892
|
-
|
|
100893
|
-
|
|
100894
|
-
|
|
100895
|
-
|
|
100896
|
-
|
|
100897
|
-
|
|
100898
|
-
|
|
100899
|
-
|
|
100900
|
-
|
|
100901
|
-
|
|
100902
|
-
|
|
100903
|
-
|
|
100904
|
-
|
|
100905
|
-
|
|
100906
|
-
|
|
100907
|
-
}
|
|
100908
|
-
console.log("");
|
|
100909
|
-
console.log(" Use: claudish --model litellm@<model-group>");
|
|
100910
|
-
}
|
|
100911
|
-
} catch {}
|
|
100896
|
+
await printLocalProvidersFooter();
|
|
100897
|
+
console.log("");
|
|
100898
|
+
console.log("Filter by provider: claudish --list-models --provider <slug>");
|
|
100899
|
+
console.log(" (e.g. opencode-zen, anthropic, openai, google, x-ai)");
|
|
100900
|
+
console.log("All providers: claudish --list-providers");
|
|
100901
|
+
console.log("Search by keyword: claudish -s <query>");
|
|
100902
|
+
console.log("Top recommended: claudish --top-models");
|
|
100903
|
+
console.log("");
|
|
100904
|
+
}
|
|
100905
|
+
async function printByProvider(providerSlug, jsonOutput) {
|
|
100906
|
+
let models;
|
|
100907
|
+
try {
|
|
100908
|
+
models = await getModelsByProvider(providerSlug, 200);
|
|
100909
|
+
} catch (error2) {
|
|
100910
|
+
console.error(`\u274C Failed to load provider catalog from Firebase: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
100911
|
+
console.error(" Check your network connection.");
|
|
100912
|
+
process.exit(1);
|
|
100912
100913
|
}
|
|
100913
|
-
|
|
100914
|
-
|
|
100915
|
-
|
|
100916
|
-
if (!byProvider.has(provider)) {
|
|
100917
|
-
byProvider.set(provider, []);
|
|
100918
|
-
}
|
|
100919
|
-
byProvider.get(provider).push(model);
|
|
100914
|
+
if (jsonOutput) {
|
|
100915
|
+
console.log(JSON.stringify({ provider: providerSlug, count: models.length, models }, null, 2));
|
|
100916
|
+
return;
|
|
100920
100917
|
}
|
|
100921
|
-
|
|
100922
|
-
console.log(`
|
|
100923
|
-
\u2601\uFE0F OPENROUTER MODELS (${models.length} total):
|
|
100924
|
-
`);
|
|
100925
|
-
for (const provider of sortedProviders) {
|
|
100926
|
-
const providerModels = byProvider.get(provider);
|
|
100918
|
+
if (models.length === 0) {
|
|
100927
100919
|
console.log(`
|
|
100928
|
-
|
|
100929
|
-
|
|
100930
|
-
|
|
100931
|
-
const fullId = `openrouter@${model.id}`;
|
|
100932
|
-
const modelId = fullId.length > 40 ? fullId.substring(0, 37) + "..." : fullId;
|
|
100933
|
-
const modelIdPadded = modelId.padEnd(42);
|
|
100934
|
-
const promptPrice = parseFloat(model.pricing?.prompt || "0") * 1e6;
|
|
100935
|
-
const completionPrice = parseFloat(model.pricing?.completion || "0") * 1e6;
|
|
100936
|
-
const avg = (promptPrice + completionPrice) / 2;
|
|
100937
|
-
let pricing;
|
|
100938
|
-
if (avg < 0) {
|
|
100939
|
-
pricing = "varies";
|
|
100940
|
-
} else if (avg === 0) {
|
|
100941
|
-
pricing = "FREE";
|
|
100942
|
-
} else {
|
|
100943
|
-
pricing = `$${avg.toFixed(2)}/1M`;
|
|
100944
|
-
}
|
|
100945
|
-
const pricingPadded = pricing.padEnd(12);
|
|
100946
|
-
const contextLen = model.context_length || model.top_provider?.context_length || 0;
|
|
100947
|
-
const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
|
|
100948
|
-
const contextPadded = context.padEnd(8);
|
|
100949
|
-
console.log(` ${modelIdPadded} ${pricingPadded} ${contextPadded}`);
|
|
100950
|
-
}
|
|
100920
|
+
No active models found for provider "${providerSlug}". Try \`claudish -s <query>\` to search the full catalog.
|
|
100921
|
+
`);
|
|
100922
|
+
return;
|
|
100951
100923
|
}
|
|
100952
100924
|
console.log(`
|
|
100925
|
+
Provider: ${providerSlug} (${models.length} active models)
|
|
100953
100926
|
`);
|
|
100954
|
-
|
|
100955
|
-
console.log("
|
|
100956
|
-
console.log("
|
|
100957
|
-
console.log("
|
|
100958
|
-
console.log("
|
|
100959
|
-
console.log("
|
|
100960
|
-
console.log("
|
|
100961
|
-
}
|
|
100962
|
-
function
|
|
100963
|
-
|
|
100964
|
-
if (!existsSync24(cachePath)) {
|
|
100965
|
-
return true;
|
|
100966
|
-
}
|
|
100927
|
+
renderModelDocTable(models, false);
|
|
100928
|
+
console.log("");
|
|
100929
|
+
console.log(" Caps: T = tools R = reasoning V = vision");
|
|
100930
|
+
console.log("");
|
|
100931
|
+
console.log("Use any model: claudish --model <model-id>");
|
|
100932
|
+
console.log("Provider shortcuts: claudish --model or@<id> | google@<id> | oai@<id>");
|
|
100933
|
+
console.log("");
|
|
100934
|
+
}
|
|
100935
|
+
async function printRecommendedModels(jsonOutput, forceUpdate) {
|
|
100936
|
+
let doc2;
|
|
100967
100937
|
try {
|
|
100968
|
-
|
|
100969
|
-
const data = JSON.parse(jsonContent);
|
|
100970
|
-
if (!data.lastUpdated) {
|
|
100971
|
-
return true;
|
|
100972
|
-
}
|
|
100973
|
-
const lastUpdated = new Date(data.lastUpdated);
|
|
100974
|
-
const now = new Date;
|
|
100975
|
-
const ageInDays = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
|
|
100976
|
-
return ageInDays > CACHE_MAX_AGE_DAYS2;
|
|
100938
|
+
doc2 = await getRecommendedModels({ forceRefresh: forceUpdate });
|
|
100977
100939
|
} catch (error2) {
|
|
100978
|
-
|
|
100940
|
+
console.error(`\u274C Failed to load recommended models: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
100941
|
+
process.exit(1);
|
|
100979
100942
|
}
|
|
100980
|
-
|
|
100981
|
-
|
|
100982
|
-
|
|
100983
|
-
|
|
100984
|
-
|
|
100985
|
-
|
|
100986
|
-
|
|
100987
|
-
|
|
100988
|
-
const
|
|
100989
|
-
|
|
100990
|
-
|
|
100991
|
-
const
|
|
100992
|
-
|
|
100993
|
-
|
|
100994
|
-
|
|
100995
|
-
|
|
100996
|
-
|
|
100997
|
-
|
|
100998
|
-
|
|
100999
|
-
|
|
101000
|
-
|
|
101001
|
-
|
|
101002
|
-
|
|
101003
|
-
|
|
101004
|
-
|
|
101005
|
-
|
|
101006
|
-
|
|
101007
|
-
|
|
101008
|
-
|
|
101009
|
-
|
|
101010
|
-
|
|
101011
|
-
|
|
101012
|
-
|
|
101013
|
-
|
|
101014
|
-
|
|
101015
|
-
|
|
101016
|
-
|
|
101017
|
-
|
|
101018
|
-
} else if (lowerDesc.includes("reason")) {
|
|
101019
|
-
category = "reasoning";
|
|
101020
|
-
}
|
|
101021
|
-
const bareId = modelId.split("/").pop().replace(/:free$/, "");
|
|
101022
|
-
recommendations.push({
|
|
101023
|
-
id: bareId,
|
|
101024
|
-
openrouterId: modelId,
|
|
101025
|
-
name,
|
|
101026
|
-
description,
|
|
101027
|
-
provider: provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
101028
|
-
category,
|
|
101029
|
-
priority: recommendations.length + 1,
|
|
101030
|
-
pricing: {
|
|
101031
|
-
input: inputPrice,
|
|
101032
|
-
output: outputPrice,
|
|
101033
|
-
average: avgPrice
|
|
101034
|
-
},
|
|
101035
|
-
context: topProvider.context_length ? `${Math.floor(topProvider.context_length / 1000)}K` : "N/A",
|
|
101036
|
-
maxOutputTokens: topProvider.max_completion_tokens || null,
|
|
101037
|
-
modality: architecture.modality || "text->text",
|
|
101038
|
-
supportsTools: supportedParams.includes("tools") || supportedParams.includes("tool_choice"),
|
|
101039
|
-
supportsReasoning: supportedParams.includes("reasoning") || supportedParams.includes("include_reasoning"),
|
|
101040
|
-
supportsVision: (architecture.input_modalities || []).includes("image") || (architecture.input_modalities || []).includes("video"),
|
|
101041
|
-
isModerated: topProvider.is_moderated || false,
|
|
101042
|
-
recommended: true
|
|
101043
|
-
});
|
|
101044
|
-
providers.add(provider);
|
|
100943
|
+
if (jsonOutput) {
|
|
100944
|
+
console.log(JSON.stringify(doc2, null, 2));
|
|
100945
|
+
return;
|
|
100946
|
+
}
|
|
100947
|
+
const lastUpdated = doc2.lastUpdated || "unknown";
|
|
100948
|
+
const { flagship, fast } = groupRecommendedModels(doc2.models);
|
|
100949
|
+
const providerByName = new Map(BUILTIN_PROVIDERS.map((p) => [p.name, p]));
|
|
100950
|
+
const getNativePrefix = (firebaseSlug) => {
|
|
100951
|
+
const canonical = FIREBASE_SLUG_TO_PROVIDER_NAME[firebaseSlug];
|
|
100952
|
+
if (!canonical)
|
|
100953
|
+
return null;
|
|
100954
|
+
const def = providerByName.get(canonical);
|
|
100955
|
+
if (!def || !def.shortcuts || def.shortcuts.length === 0)
|
|
100956
|
+
return null;
|
|
100957
|
+
return def.shortcuts[0];
|
|
100958
|
+
};
|
|
100959
|
+
const renderGroup = (group) => {
|
|
100960
|
+
const m2 = group.primary;
|
|
100961
|
+
const rawId = m2.id;
|
|
100962
|
+
const modelId = rawId.length > 28 ? rawId.substring(0, 25) + "..." : rawId;
|
|
100963
|
+
const modelIdPadded = modelId.padEnd(28);
|
|
100964
|
+
const pricing = normalizePricingDisplay(m2.pricing?.average);
|
|
100965
|
+
const pricingPadded = pricing.padEnd(10);
|
|
100966
|
+
const context = m2.context || "N/A";
|
|
100967
|
+
const contextPadded = context.padEnd(6);
|
|
100968
|
+
const caps = [];
|
|
100969
|
+
if (m2.supportsTools)
|
|
100970
|
+
caps.push("\uD83D\uDD27");
|
|
100971
|
+
if (m2.supportsReasoning)
|
|
100972
|
+
caps.push("\uD83E\uDDE0");
|
|
100973
|
+
if (m2.supportsVision)
|
|
100974
|
+
caps.push("\uD83D\uDC41\uFE0F");
|
|
100975
|
+
const capabilities = caps.join(" ");
|
|
100976
|
+
console.log(` ${modelIdPadded} ${pricingPadded} ${contextPadded} ${capabilities}`);
|
|
100977
|
+
const prefixes = collectRoutingPrefixes(group, getNativePrefix);
|
|
100978
|
+
if (prefixes.length > 0) {
|
|
100979
|
+
const viaLine = prefixes.map((p) => `${p}@`).join(" \xB7 ");
|
|
100980
|
+
console.log(` via: ${viaLine}`);
|
|
101045
100981
|
}
|
|
101046
|
-
|
|
101047
|
-
|
|
101048
|
-
|
|
101049
|
-
|
|
101050
|
-
|
|
101051
|
-
|
|
101052
|
-
|
|
100982
|
+
};
|
|
100983
|
+
console.log(`
|
|
100984
|
+
Recommended Models (last updated: ${lastUpdated}):
|
|
100985
|
+
`);
|
|
100986
|
+
if (flagship.length > 0) {
|
|
100987
|
+
console.log("Flagship models");
|
|
100988
|
+
console.log(" " + "\u2500".repeat(70));
|
|
100989
|
+
for (let i = 0;i < flagship.length; i++) {
|
|
100990
|
+
renderGroup(flagship[i]);
|
|
100991
|
+
if (i < flagship.length - 1)
|
|
100992
|
+
console.log("");
|
|
101053
100993
|
}
|
|
101054
|
-
const updatedData = {
|
|
101055
|
-
version: version2,
|
|
101056
|
-
lastUpdated: new Date().toISOString().split("T")[0],
|
|
101057
|
-
source: "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
|
|
101058
|
-
models: recommendations
|
|
101059
|
-
};
|
|
101060
|
-
mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
|
|
101061
|
-
writeFileSync13(CACHED_MODELS_PATH, JSON.stringify(updatedData, null, 2), "utf-8");
|
|
101062
|
-
console.error(`\u2705 Updated ${recommendations.length} models (last updated: ${updatedData.lastUpdated})`);
|
|
101063
|
-
} catch (error2) {
|
|
101064
|
-
console.error(`\u274C Failed to update models: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
101065
|
-
console.error(" Using cached models (if available)");
|
|
101066
100994
|
}
|
|
101067
|
-
|
|
101068
|
-
|
|
101069
|
-
|
|
101070
|
-
console.
|
|
101071
|
-
|
|
101072
|
-
|
|
100995
|
+
if (fast.length > 0) {
|
|
100996
|
+
if (flagship.length > 0)
|
|
100997
|
+
console.log("");
|
|
100998
|
+
console.log("Fast variants");
|
|
100999
|
+
console.log(" " + "\u2500".repeat(70));
|
|
101000
|
+
for (let i = 0;i < fast.length; i++) {
|
|
101001
|
+
renderGroup(fast[i]);
|
|
101002
|
+
if (i < fast.length - 1)
|
|
101003
|
+
console.log("");
|
|
101004
|
+
}
|
|
101073
101005
|
}
|
|
101074
|
-
|
|
101075
|
-
|
|
101076
|
-
|
|
101077
|
-
|
|
101078
|
-
|
|
101079
|
-
|
|
101080
|
-
|
|
101081
|
-
|
|
101082
|
-
|
|
101006
|
+
console.log("");
|
|
101007
|
+
console.log(" Capabilities: \uD83D\uDD27 Tools \uD83E\uDDE0 Reasoning \uD83D\uDC41\uFE0F Vision");
|
|
101008
|
+
const primaries = [...flagship, ...fast].map((g2) => g2.primary);
|
|
101009
|
+
const picks = computeQuickPicks(primaries);
|
|
101010
|
+
const pickLines = [];
|
|
101011
|
+
if (picks.budget)
|
|
101012
|
+
pickLines.push(` Budget \u2192 ${picks.budget.id} (${normalizePricingDisplay(picks.budget.pricing?.average)})`);
|
|
101013
|
+
if (picks.largeContext)
|
|
101014
|
+
pickLines.push(` Large ctx \u2192 ${picks.largeContext.id} (${picks.largeContext.context || "N/A"})`);
|
|
101015
|
+
if (picks.mostCapable)
|
|
101016
|
+
pickLines.push(` Most capable \u2192 ${picks.mostCapable.id}`);
|
|
101017
|
+
if (picks.visionCoding)
|
|
101018
|
+
pickLines.push(` Vision+code \u2192 ${picks.visionCoding.id}`);
|
|
101019
|
+
if (picks.agentic)
|
|
101020
|
+
pickLines.push(` Agentic \u2192 ${picks.agentic.id}`);
|
|
101021
|
+
if (pickLines.length > 0) {
|
|
101022
|
+
console.log("");
|
|
101023
|
+
console.log(" Quick picks:");
|
|
101024
|
+
for (const line of pickLines)
|
|
101025
|
+
console.log(line);
|
|
101083
101026
|
}
|
|
101027
|
+
console.log("");
|
|
101028
|
+
console.log(" Set default: export CLAUDISH_MODEL=<model>");
|
|
101029
|
+
console.log(" or: claudish --model <model> ...");
|
|
101030
|
+
console.log("");
|
|
101031
|
+
console.log(" For more: claudish --list-models (browse full catalog)");
|
|
101032
|
+
console.log(" claudish --list-providers (list all providers + counts)");
|
|
101033
|
+
console.log(" claudish -s <query> (search by keyword)");
|
|
101034
|
+
console.log(" claudish --top-models --force-update (refresh from Firebase)");
|
|
101035
|
+
console.log("");
|
|
101084
101036
|
}
|
|
101085
101037
|
function printVersion() {
|
|
101086
101038
|
console.log(`claudish version ${VERSION}`);
|
|
@@ -101530,9 +101482,15 @@ OPTIONS:
|
|
|
101530
101482
|
--cost-tracker Enable cost tracking for API usage (NB!)
|
|
101531
101483
|
--audit-costs Show cost analysis report
|
|
101532
101484
|
--reset-costs Reset accumulated cost statistics
|
|
101533
|
-
--models
|
|
101534
|
-
--models <
|
|
101535
|
-
|
|
101485
|
+
--list-models Top 100 ranked models from Firebase + local providers
|
|
101486
|
+
--list-models --provider <slug>
|
|
101487
|
+
Filter Firebase catalog to one provider
|
|
101488
|
+
(e.g. --provider opencode-zen, --provider anthropic)
|
|
101489
|
+
--list-providers List every provider + active-model count
|
|
101490
|
+
-s, --search <query> Search Firebase catalog by keyword \u2014 matches model ID,
|
|
101491
|
+
brand synonyms (chatgpt, claude, grok), gateway names
|
|
101492
|
+
(zen, oc, codex), or capabilities (reasoning, vision, free)
|
|
101493
|
+
--top-models List the curated recommended models (flagship + fast)
|
|
101536
101494
|
--team <models> Run multiple models in parallel (comma-separated)
|
|
101537
101495
|
Example: --team minimax-m2.5,kimi-k2.5 "prompt"
|
|
101538
101496
|
--mode <mode> Team mode: default (grid), interactive, json
|
|
@@ -101541,7 +101499,7 @@ OPTIONS:
|
|
|
101541
101499
|
1-token request (diagnostic, may incur tiny cost)
|
|
101542
101500
|
--no-probe Skip live requests, show static chain only
|
|
101543
101501
|
--probe-timeout <secs> Per-link timeout for live probes (default: 40)
|
|
101544
|
-
--json Output in JSON format (use with --models, --top-models, --probe)
|
|
101502
|
+
--json Output in JSON format (use with --list-models, --top-models, --probe)
|
|
101545
101503
|
--force-update Force refresh model cache from OpenRouter API
|
|
101546
101504
|
--version Show version information
|
|
101547
101505
|
-h, --help Show this help message
|
|
@@ -101590,7 +101548,7 @@ MODEL MAPPING (per-role override):
|
|
|
101590
101548
|
--model-subagent <model> Model for sub-agents (Task tool)
|
|
101591
101549
|
|
|
101592
101550
|
CUSTOM MODELS:
|
|
101593
|
-
Claudish accepts ANY valid
|
|
101551
|
+
Claudish accepts ANY valid model ID from the Firebase catalog, even if not in --list-models
|
|
101594
101552
|
Example: claudish --model openrouter@your_provider/custom-model-123 "task"
|
|
101595
101553
|
|
|
101596
101554
|
MODES:
|
|
@@ -101758,14 +101716,14 @@ LOCAL MODELS (Ollama, LM Studio, vLLM):
|
|
|
101758
101716
|
OLLAMA_HOST=http://192.168.1.50:11434 claudish --model ollama/llama3.2 "task"
|
|
101759
101717
|
|
|
101760
101718
|
AVAILABLE MODELS:
|
|
101761
|
-
|
|
101762
|
-
|
|
101763
|
-
|
|
101719
|
+
Top 100 ranked: claudish --list-models (Firebase-ranked list + local providers)
|
|
101720
|
+
By provider: claudish --list-models --provider <slug> (e.g. opencode-zen, anthropic, openai, google, x-ai)
|
|
101721
|
+
All providers: claudish --list-providers (every provider + active-model count)
|
|
101722
|
+
Search models: claudish -s <query> (fuzzy: id, brand synonyms, gateways, capabilities)
|
|
101723
|
+
Top recommended: claudish --top-models (curated flagship + fast)
|
|
101764
101724
|
Probe routing: claudish --probe minimax-m2.5 kimi-k2.5 gemini-3.1-pro-preview
|
|
101765
|
-
Free models only: claudish --free
|
|
101766
|
-
JSON output: claudish --models --json
|
|
101767
|
-
Force cache update: claudish --models --force-update
|
|
101768
|
-
(Cache auto-updates every 2 days)
|
|
101725
|
+
Free models only: claudish --free (interactive selector with free models)
|
|
101726
|
+
JSON output: claudish --list-models --json | claudish --top-models --json
|
|
101769
101727
|
|
|
101770
101728
|
MORE INFO:
|
|
101771
101729
|
GitHub: https://github.com/MadAppGang/claude-code
|
|
@@ -101813,15 +101771,15 @@ async function initializeClaudishSkill() {
|
|
|
101813
101771
|
}
|
|
101814
101772
|
try {
|
|
101815
101773
|
if (!existsSync24(claudeDir)) {
|
|
101816
|
-
|
|
101774
|
+
mkdirSync10(claudeDir, { recursive: true });
|
|
101817
101775
|
console.log("\uD83D\uDCC1 Created .claude/ directory");
|
|
101818
101776
|
}
|
|
101819
101777
|
if (!existsSync24(skillsDir)) {
|
|
101820
|
-
|
|
101778
|
+
mkdirSync10(skillsDir, { recursive: true });
|
|
101821
101779
|
console.log("\uD83D\uDCC1 Created .claude/skills/ directory");
|
|
101822
101780
|
}
|
|
101823
101781
|
if (!existsSync24(claudishSkillDir)) {
|
|
101824
|
-
|
|
101782
|
+
mkdirSync10(claudishSkillDir, { recursive: true });
|
|
101825
101783
|
console.log("\uD83D\uDCC1 Created .claude/skills/claudish-usage/ directory");
|
|
101826
101784
|
}
|
|
101827
101785
|
copyFileSync(sourceSkillPath, skillFile);
|
|
@@ -101860,151 +101818,27 @@ async function initializeClaudishSkill() {
|
|
|
101860
101818
|
}
|
|
101861
101819
|
}
|
|
101862
101820
|
function printAvailableModels() {
|
|
101863
|
-
let lastUpdated = "unknown";
|
|
101864
|
-
let models = [];
|
|
101865
101821
|
try {
|
|
101866
|
-
const cachePath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
|
|
101867
|
-
if (existsSync24(cachePath)) {
|
|
101868
|
-
const data = JSON.parse(readFileSync20(cachePath, "utf-8"));
|
|
101869
|
-
lastUpdated = data.lastUpdated || "unknown";
|
|
101870
|
-
models = data.models || [];
|
|
101871
|
-
}
|
|
101872
|
-
} catch {
|
|
101873
101822
|
const basicModels = getAvailableModels();
|
|
101874
101823
|
const modelInfo = loadModelInfo();
|
|
101824
|
+
console.log("\nAvailable models (type `claudish --top-models` for full table):\n");
|
|
101875
101825
|
for (const model of basicModels) {
|
|
101876
101826
|
const info = modelInfo[model];
|
|
101827
|
+
if (!info)
|
|
101828
|
+
continue;
|
|
101877
101829
|
console.log(` ${model}`);
|
|
101878
101830
|
console.log(` ${info.name} - ${info.description}`);
|
|
101879
|
-
console.log("");
|
|
101880
101831
|
}
|
|
101881
|
-
|
|
101882
|
-
}
|
|
101883
|
-
console.log(`
|
|
101884
|
-
Recommended Models (last updated: ${lastUpdated}):
|
|
101885
|
-
`);
|
|
101886
|
-
console.log(" Model Pricing Context Capabilities");
|
|
101887
|
-
console.log(" " + "\u2500".repeat(66));
|
|
101888
|
-
for (const model of models) {
|
|
101889
|
-
const modelId = model.id.length > 28 ? model.id.substring(0, 25) + "..." : model.id;
|
|
101890
|
-
const modelIdPadded = modelId.padEnd(28);
|
|
101891
|
-
let pricing = model.pricing?.average || "N/A";
|
|
101892
|
-
if (pricing.includes("-1000000")) {
|
|
101893
|
-
pricing = "varies";
|
|
101894
|
-
} else if (pricing === "$0.00/1M" || pricing === "FREE") {
|
|
101895
|
-
pricing = "FREE";
|
|
101896
|
-
}
|
|
101897
|
-
const pricingPadded = pricing.padEnd(10);
|
|
101898
|
-
const context = model.context || "N/A";
|
|
101899
|
-
const contextPadded = context.padEnd(7);
|
|
101900
|
-
const tools = model.supportsTools ? "\uD83D\uDD27" : " ";
|
|
101901
|
-
const reasoning = model.supportsReasoning ? "\uD83E\uDDE0" : " ";
|
|
101902
|
-
const vision = model.supportsVision ? "\uD83D\uDC41\uFE0F " : " ";
|
|
101903
|
-
const capabilities = `${tools} ${reasoning} ${vision}`;
|
|
101904
|
-
console.log(` ${modelIdPadded} ${pricingPadded} ${contextPadded} ${capabilities}`);
|
|
101905
|
-
}
|
|
101906
|
-
console.log("");
|
|
101907
|
-
console.log(" Capabilities: \uD83D\uDD27 Tools \uD83E\uDDE0 Reasoning \uD83D\uDC41\uFE0F Vision");
|
|
101908
|
-
console.log("");
|
|
101909
|
-
console.log("Set default with: export CLAUDISH_MODEL=<model>");
|
|
101910
|
-
console.log(" or: export ANTHROPIC_MODEL=<model>");
|
|
101911
|
-
console.log("Or use: claudish --model <model> ...");
|
|
101912
|
-
console.log(`
|
|
101913
|
-
Force update: claudish --list-models --force-update
|
|
101914
|
-
`);
|
|
101915
|
-
}
|
|
101916
|
-
function printAvailableModelsJSON() {
|
|
101917
|
-
const jsonPath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
|
|
101918
|
-
try {
|
|
101919
|
-
const jsonContent = readFileSync20(jsonPath, "utf-8");
|
|
101920
|
-
const data = JSON.parse(jsonContent);
|
|
101921
|
-
console.log(JSON.stringify(data, null, 2));
|
|
101832
|
+
console.log("");
|
|
101922
101833
|
} catch (error2) {
|
|
101923
|
-
|
|
101924
|
-
const modelInfo = loadModelInfo();
|
|
101925
|
-
const output = {
|
|
101926
|
-
version: VERSION,
|
|
101927
|
-
lastUpdated: new Date().toISOString().split("T")[0],
|
|
101928
|
-
source: "runtime",
|
|
101929
|
-
models: models.filter((m2) => m2 !== "custom").map((modelId) => {
|
|
101930
|
-
const info = modelInfo[modelId];
|
|
101931
|
-
return {
|
|
101932
|
-
id: modelId,
|
|
101933
|
-
name: info.name,
|
|
101934
|
-
description: info.description,
|
|
101935
|
-
provider: info.provider,
|
|
101936
|
-
priority: info.priority
|
|
101937
|
-
};
|
|
101938
|
-
})
|
|
101939
|
-
};
|
|
101940
|
-
console.log(JSON.stringify(output, null, 2));
|
|
101834
|
+
console.error(`Failed to load available models: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
101941
101835
|
}
|
|
101942
101836
|
}
|
|
101943
|
-
|
|
101944
|
-
try {
|
|
101945
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
101946
|
-
signal: AbortSignal.timeout(1e4)
|
|
101947
|
-
});
|
|
101948
|
-
if (!response.ok) {
|
|
101949
|
-
return [];
|
|
101950
|
-
}
|
|
101951
|
-
const data = await response.json();
|
|
101952
|
-
const opencode = data.opencode;
|
|
101953
|
-
if (!opencode?.models)
|
|
101954
|
-
return [];
|
|
101955
|
-
return Object.entries(opencode.models).map(([id, m2]) => {
|
|
101956
|
-
const isFree = m2.cost?.input === 0 && m2.cost?.output === 0;
|
|
101957
|
-
return {
|
|
101958
|
-
id: `zen/${id}`,
|
|
101959
|
-
name: m2.name || id,
|
|
101960
|
-
context_length: m2.limit?.context || 128000,
|
|
101961
|
-
max_output: m2.limit?.output || 32000,
|
|
101962
|
-
pricing: isFree ? { prompt: "0", completion: "0" } : { prompt: String(m2.cost?.input || 0), completion: String(m2.cost?.output || 0) },
|
|
101963
|
-
isZen: true,
|
|
101964
|
-
isFree,
|
|
101965
|
-
supportsTools: m2.tool_call || false,
|
|
101966
|
-
supportsReasoning: m2.reasoning || false
|
|
101967
|
-
};
|
|
101968
|
-
});
|
|
101969
|
-
} catch {
|
|
101970
|
-
return [];
|
|
101971
|
-
}
|
|
101972
|
-
}
|
|
101973
|
-
async function fetchGLMCodingModels() {
|
|
101974
|
-
try {
|
|
101975
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
101976
|
-
signal: AbortSignal.timeout(5000)
|
|
101977
|
-
});
|
|
101978
|
-
if (!response.ok) {
|
|
101979
|
-
return [];
|
|
101980
|
-
}
|
|
101981
|
-
const data = await response.json();
|
|
101982
|
-
const codingPlan = data["zai-coding-plan"];
|
|
101983
|
-
if (!codingPlan?.models)
|
|
101984
|
-
return [];
|
|
101985
|
-
return Object.entries(codingPlan.models).map(([id, m2]) => {
|
|
101986
|
-
const inputModalities = m2.modalities?.input || [];
|
|
101987
|
-
return {
|
|
101988
|
-
id: `gc/${id}`,
|
|
101989
|
-
name: m2.name || id,
|
|
101990
|
-
description: `GLM Coding Plan model (subscription)`,
|
|
101991
|
-
context_length: m2.limit?.context || 131072,
|
|
101992
|
-
pricing: { prompt: "0", completion: "0" },
|
|
101993
|
-
isGLMCoding: true,
|
|
101994
|
-
isSubscription: true,
|
|
101995
|
-
supportsTools: m2.tool_call || false,
|
|
101996
|
-
supportsReasoning: m2.reasoning || false,
|
|
101997
|
-
supportsVision: inputModalities.includes("image") || inputModalities.includes("video")
|
|
101998
|
-
};
|
|
101999
|
-
});
|
|
102000
|
-
} catch {
|
|
102001
|
-
return [];
|
|
102002
|
-
}
|
|
102003
|
-
}
|
|
102004
|
-
var __filename4, __dirname4, CACHE_MAX_AGE_DAYS2 = 2, CLAUDISH_CACHE_DIR2, BUNDLED_MODELS_PATH, CACHED_MODELS_PATH, ALL_MODELS_JSON_PATH;
|
|
101837
|
+
var __filename4, __dirname4;
|
|
102005
101838
|
var init_cli = __esm(async () => {
|
|
102006
101839
|
init_config();
|
|
102007
101840
|
init_model_loader();
|
|
101841
|
+
init_provider_definitions();
|
|
102008
101842
|
init_profile_config();
|
|
102009
101843
|
init_model_parser();
|
|
102010
101844
|
init_auto_route();
|
|
@@ -102016,11 +101850,7 @@ var init_cli = __esm(async () => {
|
|
|
102016
101850
|
init_provider_resolver();
|
|
102017
101851
|
await init_probe_tui_runtime();
|
|
102018
101852
|
__filename4 = fileURLToPath4(import.meta.url);
|
|
102019
|
-
__dirname4 =
|
|
102020
|
-
CLAUDISH_CACHE_DIR2 = join25(homedir23(), ".claudish");
|
|
102021
|
-
BUNDLED_MODELS_PATH = join25(__dirname4, "../recommended-models.json");
|
|
102022
|
-
CACHED_MODELS_PATH = join25(CLAUDISH_CACHE_DIR2, "recommended-models.json");
|
|
102023
|
-
ALL_MODELS_JSON_PATH = join25(CLAUDISH_CACHE_DIR2, "all-models.json");
|
|
101853
|
+
__dirname4 = dirname5(__filename4);
|
|
102024
101854
|
});
|
|
102025
101855
|
|
|
102026
101856
|
// src/update-checker.ts
|
|
@@ -102031,7 +101861,7 @@ __export(exports_update_checker, {
|
|
|
102031
101861
|
clearCache: () => clearCache,
|
|
102032
101862
|
checkForUpdates: () => checkForUpdates
|
|
102033
101863
|
});
|
|
102034
|
-
import { existsSync as existsSync26, mkdirSync as
|
|
101864
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync11, readFileSync as readFileSync21, unlinkSync as unlinkSync8, writeFileSync as writeFileSync12 } from "fs";
|
|
102035
101865
|
import { homedir as homedir24, platform as platform2, tmpdir } from "os";
|
|
102036
101866
|
import { join as join26 } from "path";
|
|
102037
101867
|
function getCacheFilePath() {
|
|
@@ -102044,7 +101874,7 @@ function getCacheFilePath() {
|
|
|
102044
101874
|
}
|
|
102045
101875
|
try {
|
|
102046
101876
|
if (!existsSync26(cacheDir)) {
|
|
102047
|
-
|
|
101877
|
+
mkdirSync11(cacheDir, { recursive: true });
|
|
102048
101878
|
}
|
|
102049
101879
|
return join26(cacheDir, "update-check.json");
|
|
102050
101880
|
} catch {
|
|
@@ -102070,7 +101900,7 @@ function writeCache(latestVersion) {
|
|
|
102070
101900
|
lastCheck: Date.now(),
|
|
102071
101901
|
latestVersion
|
|
102072
101902
|
};
|
|
102073
|
-
|
|
101903
|
+
writeFileSync12(cachePath, JSON.stringify(data), "utf-8");
|
|
102074
101904
|
} catch {}
|
|
102075
101905
|
}
|
|
102076
101906
|
function isCacheValid(cache) {
|
|
@@ -102381,218 +102211,36 @@ __export(exports_model_selector, {
|
|
|
102381
102211
|
promptForApiKey: () => promptForApiKey,
|
|
102382
102212
|
confirmAction: () => confirmAction
|
|
102383
102213
|
});
|
|
102384
|
-
|
|
102385
|
-
import { join as join27, dirname as dirname5 } from "path";
|
|
102386
|
-
import { homedir as homedir25 } from "os";
|
|
102387
|
-
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
102388
|
-
function loadRecommendedModels2() {
|
|
102389
|
-
const cachedPath = join27(CLAUDISH_CACHE_DIR3, "recommended-models-cache.json");
|
|
102390
|
-
if (existsSync27(cachedPath)) {
|
|
102391
|
-
try {
|
|
102392
|
-
const data = JSON.parse(readFileSync22(cachedPath, "utf-8"));
|
|
102393
|
-
if (data.models && data.models.length > 0) {
|
|
102394
|
-
return data.models.map((model) => ({
|
|
102395
|
-
...model,
|
|
102396
|
-
source: "Recommended"
|
|
102397
|
-
}));
|
|
102398
|
-
}
|
|
102399
|
-
} catch {}
|
|
102400
|
-
}
|
|
102401
|
-
if (existsSync27(RECOMMENDED_MODELS_JSON_PATH)) {
|
|
102402
|
-
try {
|
|
102403
|
-
const content = readFileSync22(RECOMMENDED_MODELS_JSON_PATH, "utf-8");
|
|
102404
|
-
const data = JSON.parse(content);
|
|
102405
|
-
return (data.models || []).map((model) => ({
|
|
102406
|
-
...model,
|
|
102407
|
-
source: "Recommended"
|
|
102408
|
-
}));
|
|
102409
|
-
} catch {
|
|
102410
|
-
return [];
|
|
102411
|
-
}
|
|
102412
|
-
}
|
|
102413
|
-
return [];
|
|
102414
|
-
}
|
|
102415
|
-
async function fetchAllModels(forceUpdate = false) {
|
|
102416
|
-
if (!forceUpdate && existsSync27(ALL_MODELS_JSON_PATH2)) {
|
|
102417
|
-
try {
|
|
102418
|
-
const cacheData = JSON.parse(readFileSync22(ALL_MODELS_JSON_PATH2, "utf-8"));
|
|
102419
|
-
const lastUpdated = new Date(cacheData.lastUpdated);
|
|
102420
|
-
const now = new Date;
|
|
102421
|
-
const ageInDays = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
|
|
102422
|
-
if (ageInDays <= CACHE_MAX_AGE_DAYS3) {
|
|
102423
|
-
return cacheData.models;
|
|
102424
|
-
}
|
|
102425
|
-
} catch {}
|
|
102426
|
-
}
|
|
102427
|
-
console.log("Fetching models from OpenRouter...");
|
|
102428
|
-
try {
|
|
102429
|
-
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
102430
|
-
if (!response.ok)
|
|
102431
|
-
throw new Error(`API returned ${response.status}`);
|
|
102432
|
-
const data = await response.json();
|
|
102433
|
-
const models = data.data;
|
|
102434
|
-
mkdirSync13(CLAUDISH_CACHE_DIR3, { recursive: true });
|
|
102435
|
-
writeFileSync15(ALL_MODELS_JSON_PATH2, JSON.stringify({
|
|
102436
|
-
lastUpdated: new Date().toISOString(),
|
|
102437
|
-
models
|
|
102438
|
-
}), "utf-8");
|
|
102439
|
-
console.log(`Cached ${models.length} models`);
|
|
102440
|
-
return models;
|
|
102441
|
-
} catch (error2) {
|
|
102442
|
-
console.error(`Failed to fetch models: ${error2}`);
|
|
102443
|
-
return [];
|
|
102444
|
-
}
|
|
102445
|
-
}
|
|
102446
|
-
function toModelInfo(model) {
|
|
102447
|
-
const provider = model.id.split("/")[0];
|
|
102448
|
-
const contextLen = model.context_length || model.top_provider?.context_length || 0;
|
|
102449
|
-
const promptPrice = parseFloat(model.pricing?.prompt || "0");
|
|
102450
|
-
const completionPrice = parseFloat(model.pricing?.completion || "0");
|
|
102451
|
-
const isFree = promptPrice === 0 && completionPrice === 0;
|
|
102452
|
-
let pricingStr = "N/A";
|
|
102453
|
-
if (isFree) {
|
|
102454
|
-
pricingStr = "FREE";
|
|
102455
|
-
} else if (model.pricing) {
|
|
102456
|
-
const avgPrice = (promptPrice + completionPrice) / 2;
|
|
102457
|
-
if (avgPrice < 0.001) {
|
|
102458
|
-
pricingStr = `$${(avgPrice * 1e6).toFixed(2)}/1M`;
|
|
102459
|
-
} else {
|
|
102460
|
-
pricingStr = `$${avgPrice.toFixed(4)}/1K`;
|
|
102461
|
-
}
|
|
102462
|
-
}
|
|
102463
|
-
return {
|
|
102464
|
-
id: `openrouter@${model.id}`,
|
|
102465
|
-
name: model.name || model.id,
|
|
102466
|
-
description: model.description || "",
|
|
102467
|
-
provider: provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
102468
|
-
pricing: {
|
|
102469
|
-
input: model.pricing?.prompt || "N/A",
|
|
102470
|
-
output: model.pricing?.completion || "N/A",
|
|
102471
|
-
average: pricingStr
|
|
102472
|
-
},
|
|
102473
|
-
context: contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A",
|
|
102474
|
-
contextLength: contextLen,
|
|
102475
|
-
supportsTools: (model.supported_parameters || []).includes("tools"),
|
|
102476
|
-
supportsReasoning: (model.supported_parameters || []).includes("reasoning"),
|
|
102477
|
-
supportsVision: (model.architecture?.input_modalities || []).includes("image"),
|
|
102478
|
-
isFree,
|
|
102479
|
-
source: "OpenRouter"
|
|
102480
|
-
};
|
|
102481
|
-
}
|
|
102482
|
-
async function fetchZenGoModels() {
|
|
102483
|
-
const apiKey = process.env.OPENCODE_API_KEY;
|
|
102484
|
-
if (!apiKey)
|
|
102485
|
-
return [];
|
|
102486
|
-
const ZEN_GO_BASE = process.env.OPENCODE_BASE_URL ? process.env.OPENCODE_BASE_URL.replace("/zen", "/zen/go") : "https://opencode.ai/zen/go";
|
|
102214
|
+
function loadRecommendedModels() {
|
|
102487
102215
|
try {
|
|
102488
|
-
const
|
|
102489
|
-
|
|
102490
|
-
|
|
102491
|
-
|
|
102492
|
-
|
|
102493
|
-
|
|
102494
|
-
|
|
102495
|
-
|
|
102496
|
-
|
|
102497
|
-
|
|
102498
|
-
|
|
102499
|
-
|
|
102500
|
-
|
|
102501
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
102502
|
-
body: JSON.stringify({
|
|
102503
|
-
model: modelId,
|
|
102504
|
-
messages: [{ role: "user", content: "hi" }],
|
|
102505
|
-
max_tokens: 1
|
|
102506
|
-
}),
|
|
102507
|
-
signal: AbortSignal.timeout(8000)
|
|
102508
|
-
});
|
|
102509
|
-
if (!r.ok)
|
|
102510
|
-
return null;
|
|
102511
|
-
const body = await r.json().catch(() => ({}));
|
|
102512
|
-
return Array.isArray(body?.choices) && body.choices.length > 0 ? modelId : null;
|
|
102513
|
-
} catch {
|
|
102514
|
-
return null;
|
|
102515
|
-
}
|
|
102216
|
+
const doc2 = getRecommendedModelsSync();
|
|
102217
|
+
return doc2.models.map((model) => ({
|
|
102218
|
+
id: model.id,
|
|
102219
|
+
name: model.name,
|
|
102220
|
+
description: model.description,
|
|
102221
|
+
provider: model.provider,
|
|
102222
|
+
pricing: model.pricing,
|
|
102223
|
+
context: model.context,
|
|
102224
|
+
contextLength: parseContextString(model.context),
|
|
102225
|
+
supportsTools: model.supportsTools,
|
|
102226
|
+
supportsReasoning: model.supportsReasoning,
|
|
102227
|
+
supportsVision: model.supportsVision,
|
|
102228
|
+
source: "Recommended"
|
|
102516
102229
|
}));
|
|
102517
|
-
const discoveredIds = probeResults.filter(Boolean);
|
|
102518
|
-
const goModelIds = discoveredIds.length > 0 ? discoveredIds : fallbackIds;
|
|
102519
|
-
return goModelIds.map((id) => {
|
|
102520
|
-
const m2 = ocModels[id];
|
|
102521
|
-
if (!m2)
|
|
102522
|
-
return null;
|
|
102523
|
-
const inputModalities = m2.modalities?.input ?? [];
|
|
102524
|
-
return {
|
|
102525
|
-
id: `zgo@${id}`,
|
|
102526
|
-
name: m2.name || id,
|
|
102527
|
-
description: `OpenCode Zen Go plan model`,
|
|
102528
|
-
provider: "Zen Go",
|
|
102529
|
-
pricing: { input: "PLAN", output: "PLAN", average: "PLAN" },
|
|
102530
|
-
context: m2.limit?.context ? `${Math.round(m2.limit.context / 1000)}K` : "128K",
|
|
102531
|
-
contextLength: m2.limit?.context || 128000,
|
|
102532
|
-
supportsTools: m2.tool_call === true,
|
|
102533
|
-
supportsReasoning: m2.reasoning || false,
|
|
102534
|
-
supportsVision: inputModalities.includes("image") || inputModalities.includes("video"),
|
|
102535
|
-
isFree: false,
|
|
102536
|
-
source: "Zen"
|
|
102537
|
-
};
|
|
102538
|
-
}).filter(Boolean);
|
|
102539
102230
|
} catch {
|
|
102540
102231
|
return [];
|
|
102541
102232
|
}
|
|
102542
102233
|
}
|
|
102543
|
-
|
|
102544
|
-
|
|
102545
|
-
|
|
102546
|
-
|
|
102547
|
-
|
|
102548
|
-
|
|
102549
|
-
|
|
102550
|
-
|
|
102551
|
-
|
|
102552
|
-
|
|
102553
|
-
]);
|
|
102554
|
-
if (!mdevResp.ok)
|
|
102555
|
-
return [];
|
|
102556
|
-
const mdevData = await mdevResp.json();
|
|
102557
|
-
const opencode = mdevData.opencode;
|
|
102558
|
-
if (!opencode?.models)
|
|
102559
|
-
return [];
|
|
102560
|
-
const liveIds = new Set;
|
|
102561
|
-
if (liveResp.ok) {
|
|
102562
|
-
const liveData = await liveResp.json();
|
|
102563
|
-
for (const m2 of liveData.data ?? [])
|
|
102564
|
-
liveIds.add(m2.id);
|
|
102565
|
-
}
|
|
102566
|
-
return Object.entries(opencode.models).filter(([id, m2]) => {
|
|
102567
|
-
const isFree = m2.cost?.input === 0 && m2.cost?.output === 0;
|
|
102568
|
-
const supportsTools = m2.tool_call === true;
|
|
102569
|
-
const isLive = liveIds.size === 0 || liveIds.has(id);
|
|
102570
|
-
return isFree && supportsTools && isLive;
|
|
102571
|
-
}).map(([id, m2]) => {
|
|
102572
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102573
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102574
|
-
return {
|
|
102575
|
-
id: `zen@${id}`,
|
|
102576
|
-
name: m2.name || id,
|
|
102577
|
-
description: `OpenCode Zen free model`,
|
|
102578
|
-
provider: "Zen",
|
|
102579
|
-
pricing: {
|
|
102580
|
-
input: "FREE",
|
|
102581
|
-
output: "FREE",
|
|
102582
|
-
average: "FREE"
|
|
102583
|
-
},
|
|
102584
|
-
context: m2.limit?.context ? `${Math.round(m2.limit.context / 1000)}K` : "128K",
|
|
102585
|
-
contextLength: m2.limit?.context || 128000,
|
|
102586
|
-
supportsTools: true,
|
|
102587
|
-
supportsReasoning: m2.reasoning || false,
|
|
102588
|
-
supportsVision,
|
|
102589
|
-
isFree: true,
|
|
102590
|
-
source: "Zen"
|
|
102591
|
-
};
|
|
102592
|
-
});
|
|
102593
|
-
} catch {
|
|
102594
|
-
return [];
|
|
102595
|
-
}
|
|
102234
|
+
function parseContextString(ctx) {
|
|
102235
|
+
if (!ctx || ctx === "N/A")
|
|
102236
|
+
return 0;
|
|
102237
|
+
const upper = ctx.toUpperCase();
|
|
102238
|
+
if (upper.endsWith("M"))
|
|
102239
|
+
return parseFloat(upper) * 1e6;
|
|
102240
|
+
if (upper.endsWith("K"))
|
|
102241
|
+
return parseFloat(upper) * 1000;
|
|
102242
|
+
const n = parseInt(upper, 10);
|
|
102243
|
+
return isNaN(n) ? 0 : n;
|
|
102596
102244
|
}
|
|
102597
102245
|
function getXAIContextWindow(modelId) {
|
|
102598
102246
|
const id = modelId.toLowerCase();
|
|
@@ -102743,239 +102391,15 @@ async function fetchGeminiModels() {
|
|
|
102743
102391
|
return [];
|
|
102744
102392
|
}
|
|
102745
102393
|
}
|
|
102746
|
-
async function fetchOpenAIModels() {
|
|
102747
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
102748
|
-
if (!apiKey) {
|
|
102749
|
-
return [];
|
|
102750
|
-
}
|
|
102751
|
-
try {
|
|
102752
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102753
|
-
signal: AbortSignal.timeout(5000)
|
|
102754
|
-
});
|
|
102755
|
-
if (!response.ok) {
|
|
102756
|
-
return [];
|
|
102757
|
-
}
|
|
102758
|
-
const data = await response.json();
|
|
102759
|
-
const openaiData = data.openai;
|
|
102760
|
-
if (!openaiData?.models)
|
|
102761
|
-
return [];
|
|
102762
|
-
return Object.entries(openaiData.models).filter(([id, _2]) => {
|
|
102763
|
-
const lowerId = id.toLowerCase();
|
|
102764
|
-
return lowerId.startsWith("gpt-") || lowerId.startsWith("o1-") || lowerId.startsWith("o3-") || lowerId.startsWith("o4-") || lowerId.startsWith("chatgpt-");
|
|
102765
|
-
}).map(([id, m2]) => {
|
|
102766
|
-
const inputCost = m2.cost?.input || 2;
|
|
102767
|
-
const outputCost = m2.cost?.output || 8;
|
|
102768
|
-
const avgCost = (inputCost + outputCost) / 2;
|
|
102769
|
-
const contextLength = m2.limit?.context || 128000;
|
|
102770
|
-
const contextStr = contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`;
|
|
102771
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102772
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102773
|
-
return {
|
|
102774
|
-
id: `oai@${id}`,
|
|
102775
|
-
name: m2.name || id,
|
|
102776
|
-
description: `OpenAI model`,
|
|
102777
|
-
provider: "OpenAI",
|
|
102778
|
-
pricing: {
|
|
102779
|
-
input: `$${inputCost.toFixed(2)}`,
|
|
102780
|
-
output: `$${outputCost.toFixed(2)}`,
|
|
102781
|
-
average: `$${avgCost.toFixed(2)}/1M`
|
|
102782
|
-
},
|
|
102783
|
-
context: contextStr,
|
|
102784
|
-
contextLength,
|
|
102785
|
-
supportsTools: m2.tool_call === true,
|
|
102786
|
-
supportsReasoning: m2.reasoning === true,
|
|
102787
|
-
supportsVision,
|
|
102788
|
-
isFree: false,
|
|
102789
|
-
source: "OpenAI"
|
|
102790
|
-
};
|
|
102791
|
-
});
|
|
102792
|
-
} catch {
|
|
102793
|
-
return [];
|
|
102794
|
-
}
|
|
102795
|
-
}
|
|
102796
|
-
async function fetchGLMCodingModels2() {
|
|
102797
|
-
const apiKey = process.env.GLM_CODING_API_KEY;
|
|
102798
|
-
if (!apiKey) {
|
|
102799
|
-
return [];
|
|
102800
|
-
}
|
|
102801
|
-
try {
|
|
102802
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102803
|
-
signal: AbortSignal.timeout(5000)
|
|
102804
|
-
});
|
|
102805
|
-
if (!response.ok) {
|
|
102806
|
-
return [];
|
|
102807
|
-
}
|
|
102808
|
-
const data = await response.json();
|
|
102809
|
-
const codingPlan = data["zai-coding-plan"];
|
|
102810
|
-
if (!codingPlan?.models)
|
|
102811
|
-
return [];
|
|
102812
|
-
return Object.entries(codingPlan.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
|
|
102813
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102814
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102815
|
-
const contextLength = m2.limit?.context || 131072;
|
|
102816
|
-
return {
|
|
102817
|
-
id: `gc@${id}`,
|
|
102818
|
-
name: m2.name || id,
|
|
102819
|
-
description: `GLM Coding Plan (subscription)`,
|
|
102820
|
-
provider: "GLM Coding",
|
|
102821
|
-
pricing: {
|
|
102822
|
-
input: "SUB",
|
|
102823
|
-
output: "SUB",
|
|
102824
|
-
average: "SUB"
|
|
102825
|
-
},
|
|
102826
|
-
context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
|
|
102827
|
-
contextLength,
|
|
102828
|
-
supportsTools: true,
|
|
102829
|
-
supportsReasoning: m2.reasoning || false,
|
|
102830
|
-
supportsVision,
|
|
102831
|
-
isFree: false,
|
|
102832
|
-
source: "GLM Coding"
|
|
102833
|
-
};
|
|
102834
|
-
});
|
|
102835
|
-
} catch {
|
|
102836
|
-
return [];
|
|
102837
|
-
}
|
|
102838
|
-
}
|
|
102839
|
-
async function fetchGLMDirectModels() {
|
|
102840
|
-
try {
|
|
102841
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102842
|
-
signal: AbortSignal.timeout(5000)
|
|
102843
|
-
});
|
|
102844
|
-
if (!response.ok) {
|
|
102845
|
-
return [];
|
|
102846
|
-
}
|
|
102847
|
-
const data = await response.json();
|
|
102848
|
-
const codingPlan = data["zai-coding-plan"];
|
|
102849
|
-
if (!codingPlan?.models)
|
|
102850
|
-
return [];
|
|
102851
|
-
return Object.entries(codingPlan.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
|
|
102852
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102853
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102854
|
-
const contextLength = m2.limit?.context || 131072;
|
|
102855
|
-
const inputCost = m2.cost?.input || 0;
|
|
102856
|
-
const outputCost = m2.cost?.output || 0;
|
|
102857
|
-
const isFree = inputCost === 0 && outputCost === 0;
|
|
102858
|
-
return {
|
|
102859
|
-
id: `glm@${id}`,
|
|
102860
|
-
name: m2.name || id,
|
|
102861
|
-
description: `GLM/Zhipu direct API`,
|
|
102862
|
-
provider: "GLM",
|
|
102863
|
-
pricing: {
|
|
102864
|
-
input: isFree ? "FREE" : `$${inputCost.toFixed(2)}`,
|
|
102865
|
-
output: isFree ? "FREE" : `$${outputCost.toFixed(2)}`,
|
|
102866
|
-
average: isFree ? "FREE" : `$${((inputCost + outputCost) / 2).toFixed(2)}/1M`
|
|
102867
|
-
},
|
|
102868
|
-
context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
|
|
102869
|
-
contextLength,
|
|
102870
|
-
supportsTools: true,
|
|
102871
|
-
supportsReasoning: m2.reasoning || false,
|
|
102872
|
-
supportsVision,
|
|
102873
|
-
isFree,
|
|
102874
|
-
source: "GLM"
|
|
102875
|
-
};
|
|
102876
|
-
});
|
|
102877
|
-
} catch {
|
|
102878
|
-
return [];
|
|
102879
|
-
}
|
|
102880
|
-
}
|
|
102881
|
-
async function fetchOllamaCloudModels() {
|
|
102882
|
-
try {
|
|
102883
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102884
|
-
signal: AbortSignal.timeout(5000)
|
|
102885
|
-
});
|
|
102886
|
-
if (!response.ok) {
|
|
102887
|
-
return [];
|
|
102888
|
-
}
|
|
102889
|
-
const data = await response.json();
|
|
102890
|
-
const ollamaCloud = data["ollama-cloud"];
|
|
102891
|
-
if (!ollamaCloud?.models)
|
|
102892
|
-
return [];
|
|
102893
|
-
return Object.entries(ollamaCloud.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
|
|
102894
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102895
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102896
|
-
const contextLength = m2.limit?.context || 131072;
|
|
102897
|
-
return {
|
|
102898
|
-
id: `oc@${id}`,
|
|
102899
|
-
name: m2.name || id,
|
|
102900
|
-
description: `OllamaCloud`,
|
|
102901
|
-
provider: "OllamaCloud",
|
|
102902
|
-
pricing: {
|
|
102903
|
-
input: "N/A",
|
|
102904
|
-
output: "N/A",
|
|
102905
|
-
average: "N/A"
|
|
102906
|
-
},
|
|
102907
|
-
context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
|
|
102908
|
-
contextLength,
|
|
102909
|
-
supportsTools: true,
|
|
102910
|
-
supportsReasoning: m2.reasoning || false,
|
|
102911
|
-
supportsVision,
|
|
102912
|
-
source: "OllamaCloud"
|
|
102913
|
-
};
|
|
102914
|
-
});
|
|
102915
|
-
} catch {
|
|
102916
|
-
return [];
|
|
102917
|
-
}
|
|
102918
|
-
}
|
|
102919
|
-
function shouldRefreshForFreeModels() {
|
|
102920
|
-
if (!existsSync27(ALL_MODELS_JSON_PATH2)) {
|
|
102921
|
-
return true;
|
|
102922
|
-
}
|
|
102923
|
-
try {
|
|
102924
|
-
const cacheData = JSON.parse(readFileSync22(ALL_MODELS_JSON_PATH2, "utf-8"));
|
|
102925
|
-
const lastUpdated = new Date(cacheData.lastUpdated);
|
|
102926
|
-
const now = new Date;
|
|
102927
|
-
const ageInHours = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60);
|
|
102928
|
-
return ageInHours > FREE_MODELS_CACHE_MAX_AGE_HOURS;
|
|
102929
|
-
} catch {
|
|
102930
|
-
return true;
|
|
102931
|
-
}
|
|
102932
|
-
}
|
|
102933
102394
|
async function getFreeModels() {
|
|
102934
|
-
|
|
102935
|
-
const [allModels, zenModels] = await Promise.all([
|
|
102936
|
-
fetchAllModels(forceUpdate),
|
|
102937
|
-
fetchZenFreeModels()
|
|
102938
|
-
]);
|
|
102939
|
-
const openRouterFreeModels = allModels.filter((model) => {
|
|
102940
|
-
if (!model.id?.endsWith(":free"))
|
|
102941
|
-
return false;
|
|
102942
|
-
const supportsTools = (model.supported_parameters || []).includes("tools");
|
|
102943
|
-
if (!supportsTools)
|
|
102944
|
-
return false;
|
|
102945
|
-
return true;
|
|
102946
|
-
});
|
|
102947
|
-
openRouterFreeModels.sort((a, b2) => {
|
|
102948
|
-
const contextA = a.context_length || a.top_provider?.context_length || 0;
|
|
102949
|
-
const contextB = b2.context_length || b2.top_provider?.context_length || 0;
|
|
102950
|
-
return contextB - contextA;
|
|
102951
|
-
});
|
|
102952
|
-
const openRouterModels = openRouterFreeModels.slice(0, 20).map(toModelInfo);
|
|
102953
|
-
const combined = [...zenModels, ...openRouterModels];
|
|
102954
|
-
combined.sort((a, b2) => {
|
|
102955
|
-
if (a.source === "Zen" && b2.source !== "Zen")
|
|
102956
|
-
return -1;
|
|
102957
|
-
if (a.source !== "Zen" && b2.source === "Zen")
|
|
102958
|
-
return 1;
|
|
102959
|
-
return (b2.contextLength || 0) - (a.contextLength || 0);
|
|
102960
|
-
});
|
|
102961
|
-
return combined;
|
|
102395
|
+
return [];
|
|
102962
102396
|
}
|
|
102963
102397
|
async function getAllModelsForSearch(forceUpdate = false) {
|
|
102964
102398
|
const litellmBaseUrl = process.env.LITELLM_BASE_URL;
|
|
102965
102399
|
const litellmApiKey = process.env.LITELLM_API_KEY;
|
|
102966
102400
|
const allEntries = [
|
|
102967
|
-
{
|
|
102968
|
-
name: "OpenRouter",
|
|
102969
|
-
promise: () => fetchAllModels(forceUpdate).then((models) => models.map(toModelInfo))
|
|
102970
|
-
},
|
|
102971
102401
|
{ name: "xAI", provider: "xai", promise: () => fetchXAIModels() },
|
|
102972
102402
|
{ name: "Gemini", provider: "google", promise: () => fetchGeminiModels() },
|
|
102973
|
-
{ name: "OpenAI", provider: "openai", promise: () => fetchOpenAIModels() },
|
|
102974
|
-
{ name: "GLM", provider: "glm", promise: () => fetchGLMDirectModels() },
|
|
102975
|
-
{ name: "GLM Coding", provider: "glm-coding", promise: () => fetchGLMCodingModels2() },
|
|
102976
|
-
{ name: "OllamaCloud", provider: "ollamacloud", promise: () => fetchOllamaCloudModels() },
|
|
102977
|
-
{ name: "Zen", provider: "opencode-zen", promise: () => fetchZenFreeModels() },
|
|
102978
|
-
{ name: "Zen Go", provider: "opencode-zen-go", promise: () => fetchZenGoModels() },
|
|
102979
102403
|
{
|
|
102980
102404
|
name: "MiniMax",
|
|
102981
102405
|
provider: "minimax",
|
|
@@ -103020,22 +102444,15 @@ async function getAllModelsForSearch(forceUpdate = false) {
|
|
|
103020
102444
|
}
|
|
103021
102445
|
const r = (name) => fetchResults[name] || [];
|
|
103022
102446
|
const allModels = [
|
|
103023
|
-
...r("Zen"),
|
|
103024
|
-
...r("Zen Go"),
|
|
103025
|
-
...r("OllamaCloud"),
|
|
103026
102447
|
...r("xAI"),
|
|
103027
102448
|
...r("Gemini"),
|
|
103028
|
-
...r("OpenAI"),
|
|
103029
102449
|
...r("OpenAI Codex"),
|
|
103030
|
-
...r("GLM"),
|
|
103031
|
-
...r("GLM Coding"),
|
|
103032
102450
|
...r("MiniMax"),
|
|
103033
102451
|
...r("MiniMax Coding"),
|
|
103034
102452
|
...r("Kimi"),
|
|
103035
102453
|
...r("Kimi Coding"),
|
|
103036
102454
|
...r("Z.AI"),
|
|
103037
|
-
...r("LiteLLM")
|
|
103038
|
-
...r("OpenRouter")
|
|
102455
|
+
...r("LiteLLM")
|
|
103039
102456
|
];
|
|
103040
102457
|
return allModels;
|
|
103041
102458
|
}
|
|
@@ -103131,23 +102548,17 @@ async function selectModel(options = {}) {
|
|
|
103131
102548
|
} else {
|
|
103132
102549
|
const [allModels, recommendedModels] = await Promise.all([
|
|
103133
102550
|
getAllModelsForSearch(forceUpdate),
|
|
103134
|
-
Promise.resolve(recommended ?
|
|
102551
|
+
Promise.resolve(recommended ? loadRecommendedModels() : [])
|
|
103135
102552
|
]);
|
|
103136
102553
|
const seenIds = new Set;
|
|
103137
102554
|
models = [];
|
|
103138
|
-
for (const m2 of allModels.filter((m3) => m3.source === "Zen")) {
|
|
103139
|
-
if (!seenIds.has(m2.id)) {
|
|
103140
|
-
seenIds.add(m2.id);
|
|
103141
|
-
models.push(m2);
|
|
103142
|
-
}
|
|
103143
|
-
}
|
|
103144
102555
|
for (const m2 of recommendedModels) {
|
|
103145
102556
|
if (!seenIds.has(m2.id)) {
|
|
103146
102557
|
seenIds.add(m2.id);
|
|
103147
102558
|
models.push(m2);
|
|
103148
102559
|
}
|
|
103149
102560
|
}
|
|
103150
|
-
for (const m2 of allModels.filter((m3) => m3.source && m3.source !== "
|
|
102561
|
+
for (const m2 of allModels.filter((m3) => m3.source && m3.source !== "OpenRouter")) {
|
|
103151
102562
|
if (!seenIds.has(m2.id)) {
|
|
103152
102563
|
seenIds.add(m2.id);
|
|
103153
102564
|
models.push(m2);
|
|
@@ -103517,7 +102928,7 @@ async function selectModelsForProfile() {
|
|
|
103517
102928
|
Loading available models...`);
|
|
103518
102929
|
const [fetchedModels, recommendedModels] = await Promise.all([
|
|
103519
102930
|
getAllModelsForSearch(),
|
|
103520
|
-
Promise.resolve(
|
|
102931
|
+
Promise.resolve(loadRecommendedModels())
|
|
103521
102932
|
]);
|
|
103522
102933
|
const tiers = [
|
|
103523
102934
|
{ key: "opus", name: "Opus", description: "Most capable, used for complex reasoning" },
|
|
@@ -103611,16 +103022,11 @@ async function selectProfile(profiles) {
|
|
|
103611
103022
|
async function confirmAction(message) {
|
|
103612
103023
|
return dist_default4({ message, default: false });
|
|
103613
103024
|
}
|
|
103614
|
-
var
|
|
103025
|
+
var PROVIDER_FILTER_ALIASES, ALL_PROVIDER_CHOICES, PROVIDER_MODEL_PREFIX, PROVIDER_SOURCE_FILTER;
|
|
103615
103026
|
var init_model_selector = __esm(() => {
|
|
103616
103027
|
init_dist17();
|
|
103617
103028
|
init_model_loader();
|
|
103618
103029
|
init_provider_definitions();
|
|
103619
|
-
__filename5 = fileURLToPath5(import.meta.url);
|
|
103620
|
-
__dirname5 = dirname5(__filename5);
|
|
103621
|
-
CLAUDISH_CACHE_DIR3 = join27(homedir25(), ".claudish");
|
|
103622
|
-
ALL_MODELS_JSON_PATH2 = join27(CLAUDISH_CACHE_DIR3, "all-models.json");
|
|
103623
|
-
RECOMMENDED_MODELS_JSON_PATH = join27(__dirname5, "../recommended-models.json");
|
|
103624
103030
|
PROVIDER_FILTER_ALIASES = {
|
|
103625
103031
|
zen: "Zen",
|
|
103626
103032
|
openrouter: "OpenRouter",
|
|
@@ -107277,9 +106683,9 @@ __export(exports_claude_runner, {
|
|
|
107277
106683
|
checkClaudeInstalled: () => checkClaudeInstalled
|
|
107278
106684
|
});
|
|
107279
106685
|
import { spawn as spawn4 } from "child_process";
|
|
107280
|
-
import { writeFileSync as
|
|
107281
|
-
import { tmpdir as tmpdir2, homedir as
|
|
107282
|
-
import { join as
|
|
106686
|
+
import { writeFileSync as writeFileSync13, unlinkSync as unlinkSync9, mkdirSync as mkdirSync12, existsSync as existsSync27, readFileSync as readFileSync22 } from "fs";
|
|
106687
|
+
import { tmpdir as tmpdir2, homedir as homedir25 } from "os";
|
|
106688
|
+
import { join as join27 } from "path";
|
|
107283
106689
|
function hasNativeAnthropicMapping(config3) {
|
|
107284
106690
|
const models = [
|
|
107285
106691
|
config3.model,
|
|
@@ -107295,9 +106701,9 @@ function isWindows2() {
|
|
|
107295
106701
|
}
|
|
107296
106702
|
function createStatusLineScript(tokenFilePath) {
|
|
107297
106703
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
|
|
107298
|
-
const claudishDir =
|
|
106704
|
+
const claudishDir = join27(homeDir, ".claudish");
|
|
107299
106705
|
const timestamp = Date.now();
|
|
107300
|
-
const scriptPath =
|
|
106706
|
+
const scriptPath = join27(claudishDir, `status-${timestamp}.js`);
|
|
107301
106707
|
const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
|
|
107302
106708
|
const script = `
|
|
107303
106709
|
const fs = require('fs');
|
|
@@ -107389,18 +106795,18 @@ process.stdin.on('end', () => {
|
|
|
107389
106795
|
}
|
|
107390
106796
|
});
|
|
107391
106797
|
`;
|
|
107392
|
-
|
|
106798
|
+
writeFileSync13(scriptPath, script, "utf-8");
|
|
107393
106799
|
return scriptPath;
|
|
107394
106800
|
}
|
|
107395
106801
|
function createTempSettingsFile(modelDisplay, port) {
|
|
107396
106802
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
|
|
107397
|
-
const claudishDir =
|
|
106803
|
+
const claudishDir = join27(homeDir, ".claudish");
|
|
107398
106804
|
try {
|
|
107399
|
-
|
|
106805
|
+
mkdirSync12(claudishDir, { recursive: true });
|
|
107400
106806
|
} catch {}
|
|
107401
106807
|
const timestamp = Date.now();
|
|
107402
|
-
const tempPath =
|
|
107403
|
-
const tokenFilePath =
|
|
106808
|
+
const tempPath = join27(claudishDir, `settings-${timestamp}.json`);
|
|
106809
|
+
const tokenFilePath = join27(claudishDir, `tokens-${port}.json`);
|
|
107404
106810
|
let statusCommand;
|
|
107405
106811
|
if (isWindows2()) {
|
|
107406
106812
|
const scriptPath = createStatusLineScript(tokenFilePath);
|
|
@@ -107422,7 +106828,7 @@ function createTempSettingsFile(modelDisplay, port) {
|
|
|
107422
106828
|
padding: 0
|
|
107423
106829
|
};
|
|
107424
106830
|
const settings = { statusLine };
|
|
107425
|
-
|
|
106831
|
+
writeFileSync13(tempPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
107426
106832
|
return { path: tempPath, statusLine };
|
|
107427
106833
|
}
|
|
107428
106834
|
function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine) {
|
|
@@ -107436,11 +106842,11 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine) {
|
|
|
107436
106842
|
if (userSettingsValue.trimStart().startsWith("{")) {
|
|
107437
106843
|
userSettings = JSON.parse(userSettingsValue);
|
|
107438
106844
|
} else {
|
|
107439
|
-
const rawUserSettings =
|
|
106845
|
+
const rawUserSettings = readFileSync22(userSettingsValue, "utf-8");
|
|
107440
106846
|
userSettings = JSON.parse(rawUserSettings);
|
|
107441
106847
|
}
|
|
107442
106848
|
userSettings.statusLine = statusLine;
|
|
107443
|
-
|
|
106849
|
+
writeFileSync13(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
|
|
107444
106850
|
} catch {
|
|
107445
106851
|
if (!config3.quiet) {
|
|
107446
106852
|
console.warn(`[claudish] Warning: could not merge user settings: ${userSettingsValue}`);
|
|
@@ -107528,13 +106934,14 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
|
|
|
107528
106934
|
console.error("Install it from: https://claude.com/claude-code");
|
|
107529
106935
|
console.error(`
|
|
107530
106936
|
Or set CLAUDE_PATH to your custom installation:`);
|
|
107531
|
-
const home =
|
|
107532
|
-
const localPath = isWindows2() ?
|
|
106937
|
+
const home = homedir25();
|
|
106938
|
+
const localPath = isWindows2() ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
|
|
107533
106939
|
console.error(` export CLAUDE_PATH=${localPath}`);
|
|
107534
106940
|
process.exit(1);
|
|
107535
106941
|
}
|
|
107536
106942
|
const needsShell = isWindows2() && claudeBinary.endsWith(".cmd");
|
|
107537
106943
|
const spawnCommand = needsShell ? `"${claudeBinary}"` : claudeBinary;
|
|
106944
|
+
setClaudeCodeRunning(true);
|
|
107538
106945
|
const proc = spawn4(spawnCommand, claudeArgs, {
|
|
107539
106946
|
env: env2,
|
|
107540
106947
|
stdio: "inherit",
|
|
@@ -107543,6 +106950,7 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
107543
106950
|
setupSignalHandlers(proc, tempSettingsPath, config3.quiet, onCleanup);
|
|
107544
106951
|
const exitCode = await new Promise((resolve4) => {
|
|
107545
106952
|
proc.on("exit", (code) => {
|
|
106953
|
+
setClaudeCodeRunning(false);
|
|
107546
106954
|
resolve4(code ?? 1);
|
|
107547
106955
|
});
|
|
107548
106956
|
});
|
|
@@ -107575,23 +106983,23 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet, onCleanup) {
|
|
|
107575
106983
|
async function findClaudeBinary() {
|
|
107576
106984
|
const isWindows3 = process.platform === "win32";
|
|
107577
106985
|
if (process.env.CLAUDE_PATH) {
|
|
107578
|
-
if (
|
|
106986
|
+
if (existsSync27(process.env.CLAUDE_PATH)) {
|
|
107579
106987
|
return process.env.CLAUDE_PATH;
|
|
107580
106988
|
}
|
|
107581
106989
|
}
|
|
107582
|
-
const home =
|
|
107583
|
-
const localPath = isWindows3 ?
|
|
107584
|
-
if (
|
|
106990
|
+
const home = homedir25();
|
|
106991
|
+
const localPath = isWindows3 ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
|
|
106992
|
+
if (existsSync27(localPath)) {
|
|
107585
106993
|
return localPath;
|
|
107586
106994
|
}
|
|
107587
106995
|
if (isWindows3) {
|
|
107588
106996
|
const windowsPaths = [
|
|
107589
|
-
|
|
107590
|
-
|
|
107591
|
-
|
|
106997
|
+
join27(home, "AppData", "Roaming", "npm", "claude.cmd"),
|
|
106998
|
+
join27(home, ".npm-global", "claude.cmd"),
|
|
106999
|
+
join27(home, "node_modules", ".bin", "claude.cmd")
|
|
107592
107000
|
];
|
|
107593
107001
|
for (const path3 of windowsPaths) {
|
|
107594
|
-
if (
|
|
107002
|
+
if (existsSync27(path3)) {
|
|
107595
107003
|
return path3;
|
|
107596
107004
|
}
|
|
107597
107005
|
}
|
|
@@ -107599,14 +107007,14 @@ async function findClaudeBinary() {
|
|
|
107599
107007
|
const commonPaths = [
|
|
107600
107008
|
"/usr/local/bin/claude",
|
|
107601
107009
|
"/opt/homebrew/bin/claude",
|
|
107602
|
-
|
|
107603
|
-
|
|
107604
|
-
|
|
107010
|
+
join27(home, ".npm-global/bin/claude"),
|
|
107011
|
+
join27(home, ".local/bin/claude"),
|
|
107012
|
+
join27(home, "node_modules/.bin/claude"),
|
|
107605
107013
|
"/data/data/com.termux/files/usr/bin/claude",
|
|
107606
|
-
|
|
107014
|
+
join27(home, "../usr/bin/claude")
|
|
107607
107015
|
];
|
|
107608
107016
|
for (const path3 of commonPaths) {
|
|
107609
|
-
if (
|
|
107017
|
+
if (existsSync27(path3)) {
|
|
107610
107018
|
return path3;
|
|
107611
107019
|
}
|
|
107612
107020
|
}
|
|
@@ -107646,6 +107054,7 @@ async function checkClaudeInstalled() {
|
|
|
107646
107054
|
var init_claude_runner = __esm(() => {
|
|
107647
107055
|
init_config();
|
|
107648
107056
|
init_model_parser();
|
|
107057
|
+
init_telemetry();
|
|
107649
107058
|
});
|
|
107650
107059
|
|
|
107651
107060
|
// src/diag-output.ts
|
|
@@ -107655,18 +107064,18 @@ __export(exports_diag_output, {
|
|
|
107655
107064
|
NullDiagOutput: () => NullDiagOutput,
|
|
107656
107065
|
LogFileDiagOutput: () => LogFileDiagOutput
|
|
107657
107066
|
});
|
|
107658
|
-
import { createWriteStream as createWriteStream3, mkdirSync as
|
|
107659
|
-
import { homedir as
|
|
107660
|
-
import { join as
|
|
107067
|
+
import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, unlinkSync as unlinkSync10 } from "fs";
|
|
107068
|
+
import { homedir as homedir26 } from "os";
|
|
107069
|
+
import { join as join28 } from "path";
|
|
107661
107070
|
function getClaudishDir() {
|
|
107662
|
-
const dir =
|
|
107071
|
+
const dir = join28(homedir26(), ".claudish");
|
|
107663
107072
|
try {
|
|
107664
|
-
|
|
107073
|
+
mkdirSync13(dir, { recursive: true });
|
|
107665
107074
|
} catch {}
|
|
107666
107075
|
return dir;
|
|
107667
107076
|
}
|
|
107668
107077
|
function getDiagLogPath() {
|
|
107669
|
-
return
|
|
107078
|
+
return join28(getClaudishDir(), `diag-${process.pid}.log`);
|
|
107670
107079
|
}
|
|
107671
107080
|
|
|
107672
107081
|
class LogFileDiagOutput {
|
|
@@ -107675,7 +107084,7 @@ class LogFileDiagOutput {
|
|
|
107675
107084
|
constructor() {
|
|
107676
107085
|
this.logPath = getDiagLogPath();
|
|
107677
107086
|
try {
|
|
107678
|
-
|
|
107087
|
+
writeFileSync14(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
|
|
107679
107088
|
`);
|
|
107680
107089
|
} catch {}
|
|
107681
107090
|
this.stream = createWriteStream3(this.logPath, { flags: "a" });
|
|
@@ -107725,12 +107134,12 @@ __export(exports_team_grid, {
|
|
|
107725
107134
|
});
|
|
107726
107135
|
import { spawn as spawn5 } from "child_process";
|
|
107727
107136
|
import {
|
|
107728
|
-
existsSync as
|
|
107729
|
-
readFileSync as
|
|
107730
|
-
writeFileSync as
|
|
107137
|
+
existsSync as existsSync28,
|
|
107138
|
+
readFileSync as readFileSync23,
|
|
107139
|
+
writeFileSync as writeFileSync15
|
|
107731
107140
|
} from "fs";
|
|
107732
|
-
import { dirname as dirname6, join as
|
|
107733
|
-
import { fileURLToPath as
|
|
107141
|
+
import { dirname as dirname6, join as join29 } from "path";
|
|
107142
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
107734
107143
|
import { execSync as execSync2 } from "child_process";
|
|
107735
107144
|
import { connect as netConnect } from "net";
|
|
107736
107145
|
import { setTimeout as wait } from "timers/promises";
|
|
@@ -107815,20 +107224,20 @@ function buildPaneHeader(model, prompt, bg2) {
|
|
|
107815
107224
|
return lines.join(" ");
|
|
107816
107225
|
}
|
|
107817
107226
|
function findMagmuxBinary() {
|
|
107818
|
-
const thisFile =
|
|
107227
|
+
const thisFile = fileURLToPath5(import.meta.url);
|
|
107819
107228
|
const thisDir = dirname6(thisFile);
|
|
107820
|
-
const pkgRoot =
|
|
107229
|
+
const pkgRoot = join29(thisDir, "..");
|
|
107821
107230
|
const platform3 = process.platform;
|
|
107822
107231
|
const arch = process.arch;
|
|
107823
|
-
const bundledMagmux =
|
|
107824
|
-
if (
|
|
107232
|
+
const bundledMagmux = join29(pkgRoot, "native", `magmux-${platform3}-${arch}`);
|
|
107233
|
+
if (existsSync28(bundledMagmux))
|
|
107825
107234
|
return bundledMagmux;
|
|
107826
107235
|
try {
|
|
107827
107236
|
const pkgName = `@claudish/magmux-${platform3}-${arch}`;
|
|
107828
107237
|
let searchDir = pkgRoot;
|
|
107829
107238
|
for (let i = 0;i < 5; i++) {
|
|
107830
|
-
const candidate =
|
|
107831
|
-
if (
|
|
107239
|
+
const candidate = join29(searchDir, "node_modules", pkgName, "bin", "magmux");
|
|
107240
|
+
if (existsSync28(candidate))
|
|
107832
107241
|
return candidate;
|
|
107833
107242
|
const parent = dirname6(searchDir);
|
|
107834
107243
|
if (parent === searchDir)
|
|
@@ -107847,7 +107256,7 @@ function findMagmuxBinary() {
|
|
|
107847
107256
|
async function subscribeToMagmux(sockPath, onEvent) {
|
|
107848
107257
|
let client = null;
|
|
107849
107258
|
for (let attempt = 0;attempt < 40; attempt++) {
|
|
107850
|
-
if (
|
|
107259
|
+
if (existsSync28(sockPath)) {
|
|
107851
107260
|
try {
|
|
107852
107261
|
client = await new Promise((resolve4, reject) => {
|
|
107853
107262
|
const s = netConnect(sockPath);
|
|
@@ -107934,9 +107343,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
107934
107343
|
const keep = opts?.keep ?? false;
|
|
107935
107344
|
const manifest = setupSession(sessionPath, models, input);
|
|
107936
107345
|
const startedAt = new Date().toISOString();
|
|
107937
|
-
const gridfilePath =
|
|
107938
|
-
const prompt =
|
|
107939
|
-
const rawPrompt =
|
|
107346
|
+
const gridfilePath = join29(sessionPath, "gridfile.txt");
|
|
107347
|
+
const prompt = readFileSync23(join29(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
|
|
107348
|
+
const rawPrompt = readFileSync23(join29(sessionPath, "input.md"), "utf-8");
|
|
107940
107349
|
const usedBannerColors = new Set;
|
|
107941
107350
|
const gridLines = Object.entries(manifest.models).map(([anonId]) => {
|
|
107942
107351
|
const model = manifest.models[anonId].model;
|
|
@@ -107947,7 +107356,7 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
107947
107356
|
const header = buildPaneHeader(model, rawPrompt, bg2);
|
|
107948
107357
|
return `${header} claudish --model ${model} -y --quiet '${prompt}'`;
|
|
107949
107358
|
});
|
|
107950
|
-
|
|
107359
|
+
writeFileSync15(gridfilePath, gridLines.join(`
|
|
107951
107360
|
`) + `
|
|
107952
107361
|
`, "utf-8");
|
|
107953
107362
|
const magmuxPath = findMagmuxBinary();
|
|
@@ -107967,8 +107376,8 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
107967
107376
|
});
|
|
107968
107377
|
const [{ results }] = await Promise.all([subscription, procExit]);
|
|
107969
107378
|
const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
|
|
107970
|
-
const statusPath =
|
|
107971
|
-
|
|
107379
|
+
const statusPath = join29(sessionPath, "status.json");
|
|
107380
|
+
writeFileSync15(statusPath, JSON.stringify(status, null, 2), "utf-8");
|
|
107972
107381
|
return status;
|
|
107973
107382
|
}
|
|
107974
107383
|
var BANNER_BG_COLORS;
|
|
@@ -107990,16 +107399,16 @@ var init_team_grid = __esm(() => {
|
|
|
107990
107399
|
|
|
107991
107400
|
// src/index.ts
|
|
107992
107401
|
var import_dotenv3 = __toESM(require_main(), 1);
|
|
107993
|
-
import { existsSync as
|
|
107994
|
-
import { homedir as
|
|
107995
|
-
import { join as
|
|
107402
|
+
import { existsSync as existsSync29, readFileSync as readFileSync24 } from "fs";
|
|
107403
|
+
import { homedir as homedir27 } from "os";
|
|
107404
|
+
import { join as join30 } from "path";
|
|
107996
107405
|
import_dotenv3.config({ quiet: true });
|
|
107997
107406
|
function loadStoredApiKeys() {
|
|
107998
107407
|
try {
|
|
107999
|
-
const configPath =
|
|
108000
|
-
if (!
|
|
107408
|
+
const configPath = join30(homedir27(), ".claudish", "config.json");
|
|
107409
|
+
if (!existsSync29(configPath))
|
|
108001
107410
|
return;
|
|
108002
|
-
const raw2 =
|
|
107411
|
+
const raw2 = readFileSync24(configPath, "utf-8");
|
|
108003
107412
|
const cfg = JSON.parse(raw2);
|
|
108004
107413
|
if (cfg.apiKeys) {
|
|
108005
107414
|
for (const [envVar, value] of Object.entries(cfg.apiKeys)) {
|
|
@@ -108113,14 +107522,14 @@ async function runCli() {
|
|
|
108113
107522
|
if (cliConfig.team && cliConfig.team.length > 0) {
|
|
108114
107523
|
let prompt = cliConfig.claudeArgs.join(" ");
|
|
108115
107524
|
if (cliConfig.inputFile) {
|
|
108116
|
-
prompt =
|
|
107525
|
+
prompt = readFileSync24(cliConfig.inputFile, "utf-8");
|
|
108117
107526
|
}
|
|
108118
107527
|
if (!prompt.trim()) {
|
|
108119
107528
|
console.error("Error: --team requires a prompt (positional args or -f <file>)");
|
|
108120
107529
|
process.exit(1);
|
|
108121
107530
|
}
|
|
108122
107531
|
const mode = cliConfig.teamMode ?? "default";
|
|
108123
|
-
const sessionPath =
|
|
107532
|
+
const sessionPath = join30(process.cwd(), `.claudish-team-${Date.now()}`);
|
|
108124
107533
|
if (mode === "json") {
|
|
108125
107534
|
const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
|
|
108126
107535
|
setupSession2(sessionPath, cliConfig.team, prompt);
|
|
@@ -108130,9 +107539,9 @@ async function runCli() {
|
|
|
108130
107539
|
});
|
|
108131
107540
|
const result = { ...status2, responses: {} };
|
|
108132
107541
|
for (const anonId of Object.keys(status2.models)) {
|
|
108133
|
-
const responsePath =
|
|
107542
|
+
const responsePath = join30(sessionPath, `response-${anonId}.md`);
|
|
108134
107543
|
try {
|
|
108135
|
-
const raw2 =
|
|
107544
|
+
const raw2 = readFileSync24(responsePath, "utf-8").trim();
|
|
108136
107545
|
try {
|
|
108137
107546
|
result.responses[anonId] = JSON.parse(raw2);
|
|
108138
107547
|
} catch {
|
|
@@ -108264,6 +107673,14 @@ Team Status`);
|
|
|
108264
107673
|
}
|
|
108265
107674
|
}
|
|
108266
107675
|
}
|
|
107676
|
+
if (cliConfig.interactive && !cliConfig.monitor && process.stdin.isTTY) {
|
|
107677
|
+
if (typeof process.stdin.setRawMode === "function") {
|
|
107678
|
+
process.stdin.setRawMode(false);
|
|
107679
|
+
}
|
|
107680
|
+
process.stdin.pause();
|
|
107681
|
+
process.stdin.removeAllListeners("data");
|
|
107682
|
+
process.stdin.removeAllListeners("keypress");
|
|
107683
|
+
}
|
|
108267
107684
|
if (!cliConfig.quiet) {
|
|
108268
107685
|
const modelsToCheck = [
|
|
108269
107686
|
cliConfig.model,
|