claudish 6.13.2 → 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 +985 -1609
- package/package.json +5 -5
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
|
|
@@ -20945,9 +20758,9 @@ var init_middleware = __esm(() => {
|
|
|
20945
20758
|
});
|
|
20946
20759
|
|
|
20947
20760
|
// src/handlers/shared/token-tracker.ts
|
|
20948
|
-
import { mkdirSync as
|
|
20949
|
-
import { homedir as
|
|
20950
|
-
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";
|
|
20951
20764
|
|
|
20952
20765
|
class TokenTracker {
|
|
20953
20766
|
port;
|
|
@@ -21081,9 +20894,9 @@ class TokenTracker {
|
|
|
21081
20894
|
if (this.quotaRemaining !== undefined) {
|
|
21082
20895
|
data.quota_remaining = this.quotaRemaining;
|
|
21083
20896
|
}
|
|
21084
|
-
const claudishDir =
|
|
21085
|
-
|
|
21086
|
-
|
|
20897
|
+
const claudishDir = join4(homedir3(), ".claudish");
|
|
20898
|
+
mkdirSync4(claudishDir, { recursive: true });
|
|
20899
|
+
writeFileSync4(join4(claudishDir, `tokens-${this.port}.json`), JSON.stringify(data), "utf-8");
|
|
21087
20900
|
} catch (e) {
|
|
21088
20901
|
log(`[TokenTracker] Error writing token file: ${e}`);
|
|
21089
20902
|
}
|
|
@@ -22037,21 +21850,21 @@ __export(exports_profile_config, {
|
|
|
22037
21850
|
configExistsForScope: () => configExistsForScope,
|
|
22038
21851
|
configExists: () => configExists
|
|
22039
21852
|
});
|
|
22040
|
-
import { existsSync as
|
|
22041
|
-
import { homedir as
|
|
22042
|
-
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";
|
|
22043
21856
|
function ensureConfigDir() {
|
|
22044
|
-
if (!
|
|
22045
|
-
|
|
21857
|
+
if (!existsSync3(CONFIG_DIR)) {
|
|
21858
|
+
mkdirSync5(CONFIG_DIR, { recursive: true });
|
|
22046
21859
|
}
|
|
22047
21860
|
}
|
|
22048
21861
|
function loadConfig() {
|
|
22049
21862
|
ensureConfigDir();
|
|
22050
|
-
if (!
|
|
21863
|
+
if (!existsSync3(CONFIG_FILE)) {
|
|
22051
21864
|
return { ...DEFAULT_CONFIG };
|
|
22052
21865
|
}
|
|
22053
21866
|
try {
|
|
22054
|
-
const content =
|
|
21867
|
+
const content = readFileSync2(CONFIG_FILE, "utf-8");
|
|
22055
21868
|
const config2 = JSON.parse(content);
|
|
22056
21869
|
const merged = {
|
|
22057
21870
|
version: config2.version || DEFAULT_CONFIG.version,
|
|
@@ -22084,31 +21897,31 @@ function loadConfig() {
|
|
|
22084
21897
|
}
|
|
22085
21898
|
function saveConfig(config2) {
|
|
22086
21899
|
ensureConfigDir();
|
|
22087
|
-
|
|
21900
|
+
writeFileSync5(CONFIG_FILE, JSON.stringify(config2, null, 2), "utf-8");
|
|
22088
21901
|
}
|
|
22089
21902
|
function configExists() {
|
|
22090
|
-
return
|
|
21903
|
+
return existsSync3(CONFIG_FILE);
|
|
22091
21904
|
}
|
|
22092
21905
|
function getConfigPath() {
|
|
22093
21906
|
return CONFIG_FILE;
|
|
22094
21907
|
}
|
|
22095
21908
|
function getLocalConfigPath() {
|
|
22096
|
-
return
|
|
21909
|
+
return join5(process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
22097
21910
|
}
|
|
22098
21911
|
function localConfigExists() {
|
|
22099
|
-
return
|
|
21912
|
+
return existsSync3(getLocalConfigPath());
|
|
22100
21913
|
}
|
|
22101
21914
|
function isProjectDirectory() {
|
|
22102
21915
|
const cwd = process.cwd();
|
|
22103
|
-
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)));
|
|
22104
21917
|
}
|
|
22105
21918
|
function loadLocalConfig() {
|
|
22106
21919
|
const localPath = getLocalConfigPath();
|
|
22107
|
-
if (!
|
|
21920
|
+
if (!existsSync3(localPath)) {
|
|
22108
21921
|
return null;
|
|
22109
21922
|
}
|
|
22110
21923
|
try {
|
|
22111
|
-
const content =
|
|
21924
|
+
const content = readFileSync2(localPath, "utf-8");
|
|
22112
21925
|
const config2 = JSON.parse(content);
|
|
22113
21926
|
const local = {
|
|
22114
21927
|
version: config2.version || DEFAULT_CONFIG.version,
|
|
@@ -22125,7 +21938,7 @@ function loadLocalConfig() {
|
|
|
22125
21938
|
}
|
|
22126
21939
|
}
|
|
22127
21940
|
function saveLocalConfig(config2) {
|
|
22128
|
-
|
|
21941
|
+
writeFileSync5(getLocalConfigPath(), JSON.stringify(config2, null, 2), "utf-8");
|
|
22129
21942
|
}
|
|
22130
21943
|
function loadConfigForScope(scope) {
|
|
22131
21944
|
if (scope === "local") {
|
|
@@ -22345,8 +22158,8 @@ function removeEndpoint(name) {
|
|
|
22345
22158
|
}
|
|
22346
22159
|
var CONFIG_DIR, CONFIG_FILE, LOCAL_CONFIG_FILENAME = ".claudish.json", DEFAULT_CONFIG;
|
|
22347
22160
|
var init_profile_config = __esm(() => {
|
|
22348
|
-
CONFIG_DIR =
|
|
22349
|
-
CONFIG_FILE =
|
|
22161
|
+
CONFIG_DIR = join5(homedir4(), ".claudish");
|
|
22162
|
+
CONFIG_FILE = join5(CONFIG_DIR, "config.json");
|
|
22350
22163
|
DEFAULT_CONFIG = {
|
|
22351
22164
|
version: "1.0.0",
|
|
22352
22165
|
defaultProfile: "default",
|
|
@@ -22363,11 +22176,12 @@ var init_profile_config = __esm(() => {
|
|
|
22363
22176
|
});
|
|
22364
22177
|
|
|
22365
22178
|
// src/version.ts
|
|
22366
|
-
var VERSION = "6.
|
|
22179
|
+
var VERSION = "6.14.0";
|
|
22367
22180
|
|
|
22368
22181
|
// src/telemetry.ts
|
|
22369
22182
|
var exports_telemetry = {};
|
|
22370
22183
|
__export(exports_telemetry, {
|
|
22184
|
+
setClaudeCodeRunning: () => setClaudeCodeRunning,
|
|
22371
22185
|
sanitizeModelId: () => sanitizeModelId,
|
|
22372
22186
|
sanitizeMessage: () => sanitizeMessage,
|
|
22373
22187
|
runConsentPrompt: () => runConsentPrompt,
|
|
@@ -22577,6 +22391,8 @@ async function sendReport(report) {
|
|
|
22577
22391
|
function showConsentPromptAsync(ctx) {
|
|
22578
22392
|
if (consentPromptActive)
|
|
22579
22393
|
return;
|
|
22394
|
+
if (claudeCodeRunning)
|
|
22395
|
+
return;
|
|
22580
22396
|
try {
|
|
22581
22397
|
const profileConfig = loadConfig();
|
|
22582
22398
|
if (profileConfig.telemetry?.askedAt !== undefined)
|
|
@@ -22650,9 +22466,12 @@ function initTelemetry(config2) {
|
|
|
22650
22466
|
claudishVersion = getVersion();
|
|
22651
22467
|
installMethod = detectInstallMethod();
|
|
22652
22468
|
}
|
|
22469
|
+
function setClaudeCodeRunning(running) {
|
|
22470
|
+
claudeCodeRunning = running;
|
|
22471
|
+
}
|
|
22653
22472
|
function reportError2(ctx) {
|
|
22654
22473
|
if (!initialized || !consentEnabled) {
|
|
22655
|
-
if (initialized && !consentEnabled && ctx.isInteractive && process.stderr.isTTY) {
|
|
22474
|
+
if (initialized && !consentEnabled && ctx.isInteractive && process.stderr.isTTY && !claudeCodeRunning) {
|
|
22656
22475
|
showConsentPromptAsync(ctx);
|
|
22657
22476
|
}
|
|
22658
22477
|
return;
|
|
@@ -22755,7 +22574,7 @@ Usage: claudish telemetry on|off|status|reset
|
|
|
22755
22574
|
process.exit(1);
|
|
22756
22575
|
}
|
|
22757
22576
|
}
|
|
22758
|
-
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;
|
|
22759
22578
|
var init_telemetry = __esm(() => {
|
|
22760
22579
|
init_profile_config();
|
|
22761
22580
|
init_logger();
|
|
@@ -22787,9 +22606,9 @@ var init_telemetry = __esm(() => {
|
|
|
22787
22606
|
});
|
|
22788
22607
|
|
|
22789
22608
|
// src/providers/provider-definitions.ts
|
|
22790
|
-
import { existsSync as
|
|
22791
|
-
import { join as
|
|
22792
|
-
import { homedir as
|
|
22609
|
+
import { existsSync as existsSync4 } from "fs";
|
|
22610
|
+
import { join as join6 } from "path";
|
|
22611
|
+
import { homedir as homedir5 } from "os";
|
|
22793
22612
|
function ensureProviderByNameCache() {
|
|
22794
22613
|
if (!_providerByNameCache) {
|
|
22795
22614
|
_providerByNameCache = new Map;
|
|
@@ -22929,7 +22748,7 @@ function isProviderAvailable(def) {
|
|
|
22929
22748
|
}
|
|
22930
22749
|
if (def.oauthFallback) {
|
|
22931
22750
|
try {
|
|
22932
|
-
if (
|
|
22751
|
+
if (existsSync4(join6(homedir5(), ".claudish", def.oauthFallback)))
|
|
22933
22752
|
return true;
|
|
22934
22753
|
} catch {}
|
|
22935
22754
|
}
|
|
@@ -23548,25 +23367,25 @@ var init_model_parser = __esm(() => {
|
|
|
23548
23367
|
|
|
23549
23368
|
// src/stats-buffer.ts
|
|
23550
23369
|
import {
|
|
23551
|
-
existsSync as
|
|
23552
|
-
mkdirSync as
|
|
23553
|
-
readFileSync as
|
|
23370
|
+
existsSync as existsSync5,
|
|
23371
|
+
mkdirSync as mkdirSync6,
|
|
23372
|
+
readFileSync as readFileSync3,
|
|
23554
23373
|
renameSync,
|
|
23555
23374
|
unlinkSync as unlinkSync2,
|
|
23556
|
-
writeFileSync as
|
|
23375
|
+
writeFileSync as writeFileSync6
|
|
23557
23376
|
} from "fs";
|
|
23558
|
-
import { homedir as
|
|
23559
|
-
import { join as
|
|
23377
|
+
import { homedir as homedir6 } from "os";
|
|
23378
|
+
import { join as join7 } from "path";
|
|
23560
23379
|
function ensureDir() {
|
|
23561
|
-
if (!
|
|
23562
|
-
|
|
23380
|
+
if (!existsSync5(CLAUDISH_DIR)) {
|
|
23381
|
+
mkdirSync6(CLAUDISH_DIR, { recursive: true });
|
|
23563
23382
|
}
|
|
23564
23383
|
}
|
|
23565
23384
|
function readFromDisk() {
|
|
23566
23385
|
try {
|
|
23567
|
-
if (!
|
|
23386
|
+
if (!existsSync5(BUFFER_FILE))
|
|
23568
23387
|
return [];
|
|
23569
|
-
const raw2 =
|
|
23388
|
+
const raw2 = readFileSync3(BUFFER_FILE, "utf-8");
|
|
23570
23389
|
const parsed = JSON.parse(raw2);
|
|
23571
23390
|
if (!Array.isArray(parsed.events))
|
|
23572
23391
|
return [];
|
|
@@ -23590,8 +23409,8 @@ function writeToDisk(events) {
|
|
|
23590
23409
|
ensureDir();
|
|
23591
23410
|
const trimmed = enforceSizeCap([...events]);
|
|
23592
23411
|
const payload = { version: 1, events: trimmed };
|
|
23593
|
-
const tmpFile =
|
|
23594
|
-
|
|
23412
|
+
const tmpFile = join7(CLAUDISH_DIR, `stats-buffer.tmp.${process.pid}.json`);
|
|
23413
|
+
writeFileSync6(tmpFile, JSON.stringify(payload, null, 2), "utf-8");
|
|
23595
23414
|
renameSync(tmpFile, BUFFER_FILE);
|
|
23596
23415
|
memoryCache = trimmed;
|
|
23597
23416
|
} catch {}
|
|
@@ -23635,7 +23454,7 @@ function clearBuffer() {
|
|
|
23635
23454
|
try {
|
|
23636
23455
|
memoryCache = [];
|
|
23637
23456
|
eventsSinceLastFlush = 0;
|
|
23638
|
-
if (
|
|
23457
|
+
if (existsSync5(BUFFER_FILE)) {
|
|
23639
23458
|
unlinkSync2(BUFFER_FILE);
|
|
23640
23459
|
}
|
|
23641
23460
|
} catch {}
|
|
@@ -23664,8 +23483,8 @@ function syncFlushOnExit() {
|
|
|
23664
23483
|
var BUFFER_MAX_BYTES, CLAUDISH_DIR, BUFFER_FILE, memoryCache = null, eventsSinceLastFlush = 0, lastFlushTime, flushScheduled = false;
|
|
23665
23484
|
var init_stats_buffer = __esm(() => {
|
|
23666
23485
|
BUFFER_MAX_BYTES = 64 * 1024;
|
|
23667
|
-
CLAUDISH_DIR =
|
|
23668
|
-
BUFFER_FILE =
|
|
23486
|
+
CLAUDISH_DIR = join7(homedir6(), ".claudish");
|
|
23487
|
+
BUFFER_FILE = join7(CLAUDISH_DIR, "stats-buffer.json");
|
|
23669
23488
|
lastFlushTime = Date.now();
|
|
23670
23489
|
process.on("exit", syncFlushOnExit);
|
|
23671
23490
|
process.on("SIGTERM", () => {
|
|
@@ -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)
|
|
@@ -24962,15 +24781,15 @@ var init_static_fallback = __esm(() => {
|
|
|
24962
24781
|
});
|
|
24963
24782
|
|
|
24964
24783
|
// src/providers/all-models-cache.ts
|
|
24965
|
-
import { readFileSync as
|
|
24966
|
-
import { join as
|
|
24967
|
-
import { homedir as
|
|
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";
|
|
24968
24787
|
function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
|
|
24969
|
-
if (!
|
|
24788
|
+
if (!existsSync7(path))
|
|
24970
24789
|
return null;
|
|
24971
24790
|
let raw2;
|
|
24972
24791
|
try {
|
|
24973
|
-
raw2 = JSON.parse(
|
|
24792
|
+
raw2 = JSON.parse(readFileSync5(path, "utf-8"));
|
|
24974
24793
|
} catch {
|
|
24975
24794
|
return null;
|
|
24976
24795
|
}
|
|
@@ -24995,12 +24814,12 @@ function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
|
|
|
24995
24814
|
entries: data.entries ?? existing?.entries ?? [],
|
|
24996
24815
|
models: data.models ?? existing?.models ?? []
|
|
24997
24816
|
};
|
|
24998
|
-
|
|
24999
|
-
|
|
24817
|
+
mkdirSync7(dirname(path), { recursive: true });
|
|
24818
|
+
writeFileSync7(path, JSON.stringify(merged), "utf-8");
|
|
25000
24819
|
}
|
|
25001
24820
|
var ALL_MODELS_CACHE_PATH;
|
|
25002
24821
|
var init_all_models_cache = __esm(() => {
|
|
25003
|
-
ALL_MODELS_CACHE_PATH =
|
|
24822
|
+
ALL_MODELS_CACHE_PATH = join9(homedir8(), ".claudish", "all-models.json");
|
|
25004
24823
|
});
|
|
25005
24824
|
|
|
25006
24825
|
// src/providers/catalog-resolvers/openrouter.ts
|
|
@@ -25139,16 +24958,16 @@ var init_openrouter2 = __esm(() => {
|
|
|
25139
24958
|
});
|
|
25140
24959
|
|
|
25141
24960
|
// src/providers/catalog-resolvers/litellm.ts
|
|
25142
|
-
import { readFileSync as
|
|
25143
|
-
import { join as
|
|
25144
|
-
import { homedir as
|
|
25145
|
-
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";
|
|
25146
24965
|
function getCachePath() {
|
|
25147
24966
|
const baseUrl = process.env.LITELLM_BASE_URL;
|
|
25148
24967
|
if (!baseUrl)
|
|
25149
24968
|
return null;
|
|
25150
|
-
const hash =
|
|
25151
|
-
return
|
|
24969
|
+
const hash = createHash("sha256").update(baseUrl).digest("hex").substring(0, 16);
|
|
24970
|
+
return join10(homedir9(), ".claudish", `litellm-models-${hash}.json`);
|
|
25152
24971
|
}
|
|
25153
24972
|
|
|
25154
24973
|
class LiteLLMCatalogResolver {
|
|
@@ -25176,10 +24995,10 @@ class LiteLLMCatalogResolver {
|
|
|
25176
24995
|
}
|
|
25177
24996
|
async warmCache() {
|
|
25178
24997
|
const path = getCachePath();
|
|
25179
|
-
if (!path || !
|
|
24998
|
+
if (!path || !existsSync8(path))
|
|
25180
24999
|
return;
|
|
25181
25000
|
try {
|
|
25182
|
-
const data = JSON.parse(
|
|
25001
|
+
const data = JSON.parse(readFileSync6(path, "utf-8"));
|
|
25183
25002
|
if (Array.isArray(data.models)) {
|
|
25184
25003
|
_memCache2 = data.models.map((m) => m.name ?? m.id?.replace("litellm@", "") ?? "");
|
|
25185
25004
|
}
|
|
@@ -25196,10 +25015,10 @@ class LiteLLMCatalogResolver {
|
|
|
25196
25015
|
if (_memCache2)
|
|
25197
25016
|
return _memCache2;
|
|
25198
25017
|
const path = getCachePath();
|
|
25199
|
-
if (!path || !
|
|
25018
|
+
if (!path || !existsSync8(path))
|
|
25200
25019
|
return null;
|
|
25201
25020
|
try {
|
|
25202
|
-
const data = JSON.parse(
|
|
25021
|
+
const data = JSON.parse(readFileSync6(path, "utf-8"));
|
|
25203
25022
|
if (Array.isArray(data.models)) {
|
|
25204
25023
|
_memCache2 = data.models.map((m) => m.name ?? m.id?.replace("litellm@", "") ?? "");
|
|
25205
25024
|
return _memCache2;
|
|
@@ -25264,17 +25083,17 @@ var init_model_catalog_resolver = __esm(() => {
|
|
|
25264
25083
|
});
|
|
25265
25084
|
|
|
25266
25085
|
// src/providers/auto-route.ts
|
|
25267
|
-
import { existsSync as
|
|
25268
|
-
import { join as
|
|
25269
|
-
import { homedir as
|
|
25270
|
-
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";
|
|
25271
25090
|
function readLiteLLMCacheSync(baseUrl) {
|
|
25272
|
-
const hash =
|
|
25273
|
-
const cachePath =
|
|
25274
|
-
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))
|
|
25275
25094
|
return null;
|
|
25276
25095
|
try {
|
|
25277
|
-
const data = JSON.parse(
|
|
25096
|
+
const data = JSON.parse(readFileSync7(cachePath, "utf-8"));
|
|
25278
25097
|
if (!Array.isArray(data.models))
|
|
25279
25098
|
return null;
|
|
25280
25099
|
return data.models;
|
|
@@ -25395,17 +25214,17 @@ async function warmZenModelCache() {
|
|
|
25395
25214
|
const models = (data.data ?? []).map((m) => ({ id: m.id }));
|
|
25396
25215
|
if (models.length === 0)
|
|
25397
25216
|
return;
|
|
25398
|
-
const cacheDir =
|
|
25399
|
-
const { mkdirSync:
|
|
25400
|
-
|
|
25401
|
-
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() }));
|
|
25402
25221
|
}
|
|
25403
25222
|
function readZenGoModelCacheSync() {
|
|
25404
|
-
const cachePath =
|
|
25405
|
-
if (!
|
|
25223
|
+
const cachePath = join11(homedir10(), ".claudish", "zen-go-models.json");
|
|
25224
|
+
if (!existsSync9(cachePath))
|
|
25406
25225
|
return null;
|
|
25407
25226
|
try {
|
|
25408
|
-
const data = JSON.parse(
|
|
25227
|
+
const data = JSON.parse(readFileSync7(cachePath, "utf-8"));
|
|
25409
25228
|
if (!Array.isArray(data.models))
|
|
25410
25229
|
return null;
|
|
25411
25230
|
return new Set(data.models.map((m) => m.id));
|
|
@@ -25432,10 +25251,10 @@ async function warmZenGoModelCache() {
|
|
|
25432
25251
|
const models = (data.data ?? []).map((m) => ({ id: m.id }));
|
|
25433
25252
|
if (models.length === 0)
|
|
25434
25253
|
return;
|
|
25435
|
-
const cacheDir =
|
|
25436
|
-
const { mkdirSync:
|
|
25437
|
-
|
|
25438
|
-
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() }));
|
|
25439
25258
|
}
|
|
25440
25259
|
function hasProviderCredentials(provider) {
|
|
25441
25260
|
const keyInfo = getApiKeyEnvVars(provider);
|
|
@@ -25572,9 +25391,9 @@ __export(exports_provider_resolver, {
|
|
|
25572
25391
|
getMissingKeyResolutions: () => getMissingKeyResolutions,
|
|
25573
25392
|
getMissingKeyError: () => getMissingKeyError
|
|
25574
25393
|
});
|
|
25575
|
-
import { existsSync as
|
|
25576
|
-
import { join as
|
|
25577
|
-
import { homedir as
|
|
25394
|
+
import { existsSync as existsSync10 } from "fs";
|
|
25395
|
+
import { join as join12 } from "path";
|
|
25396
|
+
import { homedir as homedir11 } from "os";
|
|
25578
25397
|
function getApiKeyInfoForProvider(providerName) {
|
|
25579
25398
|
const lookupName = providerName === "gemini" ? "google" : providerName;
|
|
25580
25399
|
const info = getApiKeyInfo(lookupName);
|
|
@@ -25609,8 +25428,8 @@ function isApiKeyAvailable(info) {
|
|
|
25609
25428
|
}
|
|
25610
25429
|
if (info.oauthFallback) {
|
|
25611
25430
|
try {
|
|
25612
|
-
const credPath =
|
|
25613
|
-
if (
|
|
25431
|
+
const credPath = join12(homedir11(), ".claudish", info.oauthFallback);
|
|
25432
|
+
if (existsSync10(credPath)) {
|
|
25614
25433
|
return true;
|
|
25615
25434
|
}
|
|
25616
25435
|
} catch {}
|
|
@@ -25911,9 +25730,9 @@ var init_provider_resolver = __esm(() => {
|
|
|
25911
25730
|
});
|
|
25912
25731
|
|
|
25913
25732
|
// src/services/pricing-cache.ts
|
|
25914
|
-
import { readFileSync as
|
|
25915
|
-
import { homedir as
|
|
25916
|
-
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";
|
|
25917
25736
|
function getDynamicPricingSync(provider, modelName) {
|
|
25918
25737
|
if (provider === "openrouter") {
|
|
25919
25738
|
const direct = pricingMap.get(modelName);
|
|
@@ -25954,36 +25773,21 @@ async function warmPricingCache() {
|
|
|
25954
25773
|
const diskFresh = loadDiskCache();
|
|
25955
25774
|
if (diskFresh) {
|
|
25956
25775
|
log("[PricingCache] Loaded pricing from disk cache");
|
|
25957
|
-
|
|
25958
|
-
|
|
25959
|
-
log("[PricingCache] Disk cache stale or missing, fetching from OpenRouter API...");
|
|
25960
|
-
const models = await ensureOpenRouterModelsLoaded();
|
|
25961
|
-
if (models.length === 0) {
|
|
25962
|
-
const cached2 = getCachedOpenRouterModels();
|
|
25963
|
-
if (cached2 && cached2.length > 0) {
|
|
25964
|
-
populateFromOpenRouterModels(cached2);
|
|
25965
|
-
saveDiskCache();
|
|
25966
|
-
log(`[PricingCache] Populated from existing model-loader cache (${pricingMap.size} models)`);
|
|
25967
|
-
return;
|
|
25968
|
-
}
|
|
25969
|
-
log("[PricingCache] No models available, will use provider defaults");
|
|
25970
|
-
return;
|
|
25776
|
+
} else {
|
|
25777
|
+
log("[PricingCache] Disk cache stale or missing, using provider defaults");
|
|
25971
25778
|
}
|
|
25972
|
-
populateFromOpenRouterModels(models);
|
|
25973
|
-
saveDiskCache();
|
|
25974
|
-
log(`[PricingCache] Fetched and cached pricing for ${pricingMap.size} models`);
|
|
25975
25779
|
} catch (error2) {
|
|
25976
25780
|
log(`[PricingCache] Error warming cache: ${error2}`);
|
|
25977
25781
|
}
|
|
25978
25782
|
}
|
|
25979
25783
|
function loadDiskCache() {
|
|
25980
25784
|
try {
|
|
25981
|
-
if (!
|
|
25785
|
+
if (!existsSync11(CACHE_FILE))
|
|
25982
25786
|
return false;
|
|
25983
25787
|
const stat = statSync(CACHE_FILE);
|
|
25984
25788
|
const age = Date.now() - stat.mtimeMs;
|
|
25985
25789
|
const isFresh = age < CACHE_TTL_MS;
|
|
25986
|
-
const raw2 =
|
|
25790
|
+
const raw2 = readFileSync8(CACHE_FILE, "utf-8");
|
|
25987
25791
|
const data = JSON.parse(raw2);
|
|
25988
25792
|
for (const [key, pricing] of Object.entries(data)) {
|
|
25989
25793
|
pricingMap.set(key, pricing);
|
|
@@ -25993,45 +25797,13 @@ function loadDiskCache() {
|
|
|
25993
25797
|
return false;
|
|
25994
25798
|
}
|
|
25995
25799
|
}
|
|
25996
|
-
function saveDiskCache() {
|
|
25997
|
-
try {
|
|
25998
|
-
mkdirSync9(CACHE_DIR, { recursive: true });
|
|
25999
|
-
const data = {};
|
|
26000
|
-
for (const [key, pricing] of pricingMap) {
|
|
26001
|
-
data[key] = pricing;
|
|
26002
|
-
}
|
|
26003
|
-
writeFileSync9(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
26004
|
-
} catch (error2) {
|
|
26005
|
-
log(`[PricingCache] Error saving disk cache: ${error2}`);
|
|
26006
|
-
}
|
|
26007
|
-
}
|
|
26008
|
-
function populateFromOpenRouterModels(models) {
|
|
26009
|
-
for (const model of models) {
|
|
26010
|
-
if (!model.id || !model.pricing)
|
|
26011
|
-
continue;
|
|
26012
|
-
const promptPrice = parseFloat(model.pricing.prompt || "0");
|
|
26013
|
-
const completionPrice = parseFloat(model.pricing.completion || "0");
|
|
26014
|
-
if (isNaN(promptPrice) || isNaN(completionPrice))
|
|
26015
|
-
continue;
|
|
26016
|
-
const inputCostPer1M = promptPrice * 1e6;
|
|
26017
|
-
const outputCostPer1M = completionPrice * 1e6;
|
|
26018
|
-
const isFree = inputCostPer1M === 0 && outputCostPer1M === 0;
|
|
26019
|
-
pricingMap.set(model.id, {
|
|
26020
|
-
inputCostPer1M,
|
|
26021
|
-
outputCostPer1M,
|
|
26022
|
-
isEstimate: true,
|
|
26023
|
-
...isFree ? { isFree: true } : {}
|
|
26024
|
-
});
|
|
26025
|
-
}
|
|
26026
|
-
}
|
|
26027
25800
|
var pricingMap, CACHE_DIR, CACHE_FILE, CACHE_TTL_MS, cacheWarmed = false, PROVIDER_TO_OR_PREFIX;
|
|
26028
25801
|
var init_pricing_cache = __esm(() => {
|
|
26029
25802
|
init_logger();
|
|
26030
|
-
init_model_loader();
|
|
26031
25803
|
init_remote_provider_types();
|
|
26032
25804
|
pricingMap = new Map;
|
|
26033
|
-
CACHE_DIR =
|
|
26034
|
-
CACHE_FILE =
|
|
25805
|
+
CACHE_DIR = join13(homedir12(), ".claudish");
|
|
25806
|
+
CACHE_FILE = join13(CACHE_DIR, "pricing-cache.json");
|
|
26035
25807
|
CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
26036
25808
|
PROVIDER_TO_OR_PREFIX = {
|
|
26037
25809
|
openai: ["openai/"],
|
|
@@ -26049,6 +25821,358 @@ var init_pricing_cache = __esm(() => {
|
|
|
26049
25821
|
};
|
|
26050
25822
|
});
|
|
26051
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
|
+
|
|
26052
26176
|
// src/handlers/fallback-handler.ts
|
|
26053
26177
|
class FallbackHandler {
|
|
26054
26178
|
candidates;
|
|
@@ -26567,8 +26691,8 @@ Details: ${e.message}`);
|
|
|
26567
26691
|
const credPath = this.getCredentialsPath();
|
|
26568
26692
|
const claudishDir = join15(homedir14(), ".claudish");
|
|
26569
26693
|
if (!existsSync13(claudishDir)) {
|
|
26570
|
-
const { mkdirSync:
|
|
26571
|
-
|
|
26694
|
+
const { mkdirSync: mkdirSync9 } = __require("fs");
|
|
26695
|
+
mkdirSync9(claudishDir, { recursive: true });
|
|
26572
26696
|
}
|
|
26573
26697
|
const fd = openSync(credPath, "w", 384);
|
|
26574
26698
|
try {
|
|
@@ -27414,8 +27538,8 @@ Details: ${e.message}`);
|
|
|
27414
27538
|
const credPath = this.getCredentialsPath();
|
|
27415
27539
|
const claudishDir = join16(homedir15(), ".claudish");
|
|
27416
27540
|
if (!existsSync14(claudishDir)) {
|
|
27417
|
-
const { mkdirSync:
|
|
27418
|
-
|
|
27541
|
+
const { mkdirSync: mkdirSync9 } = __require("fs");
|
|
27542
|
+
mkdirSync9(claudishDir, { recursive: true });
|
|
27419
27543
|
}
|
|
27420
27544
|
const fd = openSync2(credPath, "w", 384);
|
|
27421
27545
|
try {
|
|
@@ -27717,8 +27841,8 @@ class KimiOAuth {
|
|
|
27717
27841
|
const deviceIdPath = this.getDeviceIdPath();
|
|
27718
27842
|
const claudishDir = join18(homedir17(), ".claudish");
|
|
27719
27843
|
if (!existsSync16(claudishDir)) {
|
|
27720
|
-
const { mkdirSync:
|
|
27721
|
-
|
|
27844
|
+
const { mkdirSync: mkdirSync9 } = __require("fs");
|
|
27845
|
+
mkdirSync9(claudishDir, { recursive: true });
|
|
27722
27846
|
}
|
|
27723
27847
|
if (existsSync16(deviceIdPath)) {
|
|
27724
27848
|
try {
|
|
@@ -27996,8 +28120,8 @@ Waiting for authorization...`);
|
|
|
27996
28120
|
const credPath = this.getCredentialsPath();
|
|
27997
28121
|
const claudishDir = join18(homedir17(), ".claudish");
|
|
27998
28122
|
if (!existsSync16(claudishDir)) {
|
|
27999
|
-
const { mkdirSync:
|
|
28000
|
-
|
|
28123
|
+
const { mkdirSync: mkdirSync9 } = __require("fs");
|
|
28124
|
+
mkdirSync9(claudishDir, { recursive: true });
|
|
28001
28125
|
}
|
|
28002
28126
|
const fd = openSync3(credPath, "w", 384);
|
|
28003
28127
|
try {
|
|
@@ -29338,6 +29462,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
|
|
|
29338
29462
|
port = actualPort;
|
|
29339
29463
|
log(`[Proxy] Server started on port ${port}`);
|
|
29340
29464
|
warmPricingCache().catch(() => {});
|
|
29465
|
+
warmRecommendedModels().catch(() => {});
|
|
29341
29466
|
const catalogProvidersToWarm = ["openrouter"];
|
|
29342
29467
|
if (process.env.LITELLM_BASE_URL)
|
|
29343
29468
|
catalogProvidersToWarm.push("litellm");
|
|
@@ -29346,7 +29471,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
|
|
|
29346
29471
|
port,
|
|
29347
29472
|
url: `http://127.0.0.1:${port}`,
|
|
29348
29473
|
shutdown: async () => {
|
|
29349
|
-
return new Promise((resolve3) => server.close((
|
|
29474
|
+
return new Promise((resolve3) => server.close(() => resolve3()));
|
|
29350
29475
|
}
|
|
29351
29476
|
};
|
|
29352
29477
|
}
|
|
@@ -29415,47 +29540,10 @@ __export(exports_mcp_server, {
|
|
|
29415
29540
|
runPromptViaProxy: () => runPromptViaProxy,
|
|
29416
29541
|
parseAnthropicSse: () => parseAnthropicSse
|
|
29417
29542
|
});
|
|
29418
|
-
import { readFileSync as readFileSync18, existsSync as existsSync21, writeFileSync as
|
|
29543
|
+
import { readFileSync as readFileSync18, existsSync as existsSync21, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, readdirSync as readdirSync3 } from "fs";
|
|
29419
29544
|
import { join as join23, dirname as dirname3 } from "path";
|
|
29420
29545
|
import { homedir as homedir22 } from "os";
|
|
29421
29546
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
29422
|
-
function loadRecommendedModels() {
|
|
29423
|
-
const cachedPath = join23(CLAUDISH_CACHE_DIR, "recommended-models-cache.json");
|
|
29424
|
-
if (existsSync21(cachedPath)) {
|
|
29425
|
-
try {
|
|
29426
|
-
const data = JSON.parse(readFileSync18(cachedPath, "utf-8"));
|
|
29427
|
-
if (data.models && data.models.length > 0)
|
|
29428
|
-
return data.models;
|
|
29429
|
-
} catch {}
|
|
29430
|
-
}
|
|
29431
|
-
if (existsSync21(RECOMMENDED_MODELS_PATH)) {
|
|
29432
|
-
try {
|
|
29433
|
-
const data = JSON.parse(readFileSync18(RECOMMENDED_MODELS_PATH, "utf-8"));
|
|
29434
|
-
return data.models || [];
|
|
29435
|
-
} catch {
|
|
29436
|
-
return [];
|
|
29437
|
-
}
|
|
29438
|
-
}
|
|
29439
|
-
return [];
|
|
29440
|
-
}
|
|
29441
|
-
function parsePriceAvg(s) {
|
|
29442
|
-
if (!s || s === "N/A")
|
|
29443
|
-
return Infinity;
|
|
29444
|
-
if (s === "FREE")
|
|
29445
|
-
return 0;
|
|
29446
|
-
const m = s.match(/\$([\d.]+)/);
|
|
29447
|
-
return m ? parseFloat(m[1]) : Infinity;
|
|
29448
|
-
}
|
|
29449
|
-
function parseCtx(s) {
|
|
29450
|
-
if (!s || s === "N/A")
|
|
29451
|
-
return 0;
|
|
29452
|
-
const upper = s.toUpperCase();
|
|
29453
|
-
if (upper.includes("M"))
|
|
29454
|
-
return parseFloat(upper) * 1e6;
|
|
29455
|
-
if (upper.includes("K"))
|
|
29456
|
-
return parseFloat(upper) * 1000;
|
|
29457
|
-
return parseInt(s) || 0;
|
|
29458
|
-
}
|
|
29459
29547
|
async function loadAllModels(forceRefresh = false) {
|
|
29460
29548
|
if (!forceRefresh && existsSync21(ALL_MODELS_CACHE_PATH2)) {
|
|
29461
29549
|
try {
|
|
@@ -29473,8 +29561,8 @@ async function loadAllModels(forceRefresh = false) {
|
|
|
29473
29561
|
throw new Error(`API returned ${response.status}`);
|
|
29474
29562
|
const data = await response.json();
|
|
29475
29563
|
const models = data.data || [];
|
|
29476
|
-
|
|
29477
|
-
|
|
29564
|
+
mkdirSync9(CLAUDISH_CACHE_DIR, { recursive: true });
|
|
29565
|
+
writeFileSync9(ALL_MODELS_CACHE_PATH2, JSON.stringify({ lastUpdated: new Date().toISOString(), models }), "utf-8");
|
|
29478
29566
|
return models;
|
|
29479
29567
|
} catch {
|
|
29480
29568
|
if (existsSync21(ALL_MODELS_CACHE_PATH2)) {
|
|
@@ -29683,8 +29771,20 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
|
|
|
29683
29771
|
inputSchema: { type: "object" },
|
|
29684
29772
|
group: "low-level",
|
|
29685
29773
|
handler: async () => {
|
|
29686
|
-
|
|
29687
|
-
|
|
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) {
|
|
29688
29788
|
return {
|
|
29689
29789
|
content: [
|
|
29690
29790
|
{
|
|
@@ -29694,43 +29794,81 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
|
|
|
29694
29794
|
]
|
|
29695
29795
|
};
|
|
29696
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
|
+
};
|
|
29697
29831
|
let output = `# Recommended Models
|
|
29698
29832
|
|
|
29699
29833
|
`;
|
|
29700
|
-
output +=
|
|
29701
|
-
|
|
29702
|
-
output += `|-------|----------|---------|---------|-------|-----------|--------|
|
|
29834
|
+
output += `_Last updated: ${doc2.lastUpdated || "unknown"}_
|
|
29835
|
+
|
|
29703
29836
|
`;
|
|
29704
|
-
|
|
29705
|
-
|
|
29706
|
-
|
|
29707
|
-
const v = model.supportsVision ? "\u2713" : "\xB7";
|
|
29708
|
-
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
|
+
|
|
29709
29840
|
`;
|
|
29841
|
+
for (const group of flagship)
|
|
29842
|
+
output += renderGroup(group);
|
|
29710
29843
|
}
|
|
29711
|
-
|
|
29712
|
-
|
|
29713
|
-
|
|
29714
|
-
const cheapest = [...models].sort((a, b) => parsePriceAvg(a.pricing?.average) - parsePriceAvg(b.pricing?.average))[0];
|
|
29715
|
-
const bigCtx = [...models].sort((a, b) => parseCtx(b.context) - parseCtx(a.context))[0];
|
|
29716
|
-
const priciest = [...models].sort((a, b) => parsePriceAvg(b.pricing?.average) - parsePriceAvg(a.pricing?.average))[0];
|
|
29717
|
-
const vision = models.find((m) => m.supportsVision);
|
|
29718
|
-
const reasoning = models.find((m) => m.supportsReasoning && m.id !== priciest?.id);
|
|
29719
|
-
if (cheapest)
|
|
29720
|
-
output += `- **Budget**: \`${cheapest.id}\` (${cheapest.pricing?.average || "N/A"})
|
|
29721
|
-
`;
|
|
29722
|
-
if (bigCtx && bigCtx.id !== cheapest?.id)
|
|
29723
|
-
output += `- **Large context**: \`${bigCtx.id}\` (${bigCtx.context || "N/A"} tokens)
|
|
29724
|
-
`;
|
|
29725
|
-
if (priciest && priciest.id !== cheapest?.id)
|
|
29726
|
-
output += `- **Most advanced**: \`${priciest.id}\` (${priciest.pricing?.average || "N/A"})
|
|
29844
|
+
if (fast.length > 0) {
|
|
29845
|
+
output += `## Fast variants
|
|
29846
|
+
|
|
29727
29847
|
`;
|
|
29728
|
-
|
|
29729
|
-
|
|
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
|
+
|
|
29730
29867
|
`;
|
|
29731
|
-
|
|
29732
|
-
|
|
29868
|
+
output += pickLines.join(`
|
|
29869
|
+
`) + `
|
|
29733
29870
|
`;
|
|
29871
|
+
}
|
|
29734
29872
|
return { content: [{ type: "text", text: output }] };
|
|
29735
29873
|
}
|
|
29736
29874
|
});
|
|
@@ -30354,7 +30492,7 @@ function startMcpServer() {
|
|
|
30354
30492
|
process.exit(1);
|
|
30355
30493
|
});
|
|
30356
30494
|
}
|
|
30357
|
-
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.
|
|
30358
30496
|
|
|
30359
30497
|
## Channel Mode \u2014 External Model Sessions
|
|
30360
30498
|
|
|
@@ -30387,11 +30525,12 @@ var init_mcp_server = __esm(() => {
|
|
|
30387
30525
|
init_channel();
|
|
30388
30526
|
init_proxy_server();
|
|
30389
30527
|
init_port_manager();
|
|
30528
|
+
init_model_loader();
|
|
30529
|
+
init_provider_definitions();
|
|
30390
30530
|
import_dotenv2 = __toESM(require_main(), 1);
|
|
30391
30531
|
import_dotenv2.config();
|
|
30392
30532
|
__filename3 = fileURLToPath2(import.meta.url);
|
|
30393
30533
|
__dirname3 = dirname3(__filename3);
|
|
30394
|
-
RECOMMENDED_MODELS_PATH = join23(__dirname3, "../recommended-models.json");
|
|
30395
30534
|
CLAUDISH_CACHE_DIR = join23(homedir22(), ".claudish");
|
|
30396
30535
|
ALL_MODELS_CACHE_PATH2 = join23(CLAUDISH_CACHE_DIR, "all-models.json");
|
|
30397
30536
|
});
|
|
@@ -41667,7 +41806,7 @@ var init_RemoveFileError = __esm(() => {
|
|
|
41667
41806
|
|
|
41668
41807
|
// ../../node_modules/.bun/@inquirer+external-editor@2.0.1+04f2146be16c61ef/node_modules/@inquirer/external-editor/dist/index.js
|
|
41669
41808
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
41670
|
-
import { readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as
|
|
41809
|
+
import { readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
41671
41810
|
import path from "path";
|
|
41672
41811
|
import os from "os";
|
|
41673
41812
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
@@ -41776,7 +41915,7 @@ class ExternalEditor {
|
|
|
41776
41915
|
if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
|
|
41777
41916
|
opt.mode = this.fileOptions.mode;
|
|
41778
41917
|
}
|
|
41779
|
-
|
|
41918
|
+
writeFileSync10(this.tempFile, this.text, opt);
|
|
41780
41919
|
} catch (createFileError) {
|
|
41781
41920
|
throw new CreateFileError(createFileError);
|
|
41782
41921
|
}
|
|
@@ -43190,50 +43329,6 @@ var init_config = __esm(() => {
|
|
|
43190
43329
|
};
|
|
43191
43330
|
});
|
|
43192
43331
|
|
|
43193
|
-
// src/utils.ts
|
|
43194
|
-
function fuzzyScore2(text, query) {
|
|
43195
|
-
if (!text || !query)
|
|
43196
|
-
return 0;
|
|
43197
|
-
const t = text.toLowerCase();
|
|
43198
|
-
const q = query.toLowerCase();
|
|
43199
|
-
if (t === q)
|
|
43200
|
-
return 1;
|
|
43201
|
-
if (t.startsWith(q))
|
|
43202
|
-
return 0.9;
|
|
43203
|
-
if (t.includes(` ${q}`) || t.includes(`-${q}`) || t.includes(`/${q}`))
|
|
43204
|
-
return 0.8;
|
|
43205
|
-
if (t.includes(q))
|
|
43206
|
-
return 0.6;
|
|
43207
|
-
const normSep = (s) => s.replace(/[\s\-_.]/g, "");
|
|
43208
|
-
const tn = normSep(t);
|
|
43209
|
-
const qn = normSep(q);
|
|
43210
|
-
if (tn === qn)
|
|
43211
|
-
return 0.95;
|
|
43212
|
-
if (tn.startsWith(qn))
|
|
43213
|
-
return 0.85;
|
|
43214
|
-
if (tn.includes(qn))
|
|
43215
|
-
return 0.65;
|
|
43216
|
-
let score = 0;
|
|
43217
|
-
let tIdx = 0;
|
|
43218
|
-
let qIdx = 0;
|
|
43219
|
-
let consecutive = 0;
|
|
43220
|
-
while (tIdx < t.length && qIdx < q.length) {
|
|
43221
|
-
if (t[tIdx] === q[qIdx]) {
|
|
43222
|
-
score += 1 + consecutive * 0.5;
|
|
43223
|
-
consecutive++;
|
|
43224
|
-
qIdx++;
|
|
43225
|
-
} else {
|
|
43226
|
-
consecutive = 0;
|
|
43227
|
-
}
|
|
43228
|
-
tIdx++;
|
|
43229
|
-
}
|
|
43230
|
-
if (qIdx === q.length) {
|
|
43231
|
-
const compactness = q.length / (tIdx + 1);
|
|
43232
|
-
return 0.1 + 0.4 * compactness * (score / (q.length * 2));
|
|
43233
|
-
}
|
|
43234
|
-
return 0;
|
|
43235
|
-
}
|
|
43236
|
-
|
|
43237
43332
|
// src/providers/api-key-map.ts
|
|
43238
43333
|
var API_KEY_MAP;
|
|
43239
43334
|
var init_api_key_map = __esm(() => {
|
|
@@ -43582,7 +43677,7 @@ import os2 from "os";
|
|
|
43582
43677
|
import path2 from "path";
|
|
43583
43678
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
43584
43679
|
import { dlopen, toArrayBuffer as toArrayBuffer4, JSCallback, ptr as ptr4 } from "bun:ffi";
|
|
43585
|
-
import { existsSync as existsSync23, writeFileSync as
|
|
43680
|
+
import { existsSync as existsSync23, writeFileSync as writeFileSync11 } from "fs";
|
|
43586
43681
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
43587
43682
|
import { toArrayBuffer, ptr } from "bun:ffi";
|
|
43588
43683
|
import { ptr as ptr2, toArrayBuffer as toArrayBuffer2 } from "bun:ffi";
|
|
@@ -48928,7 +49023,7 @@ function convertToDebugSymbols(symbols) {
|
|
|
48928
49023
|
if (env.OTUI_DEBUG_FFI && globalFFILogPath) {
|
|
48929
49024
|
const logPath = globalFFILogPath;
|
|
48930
49025
|
const writeSync4 = (msg) => {
|
|
48931
|
-
|
|
49026
|
+
writeFileSync11(logPath, msg + `
|
|
48932
49027
|
`, { flag: "a" });
|
|
48933
49028
|
};
|
|
48934
49029
|
Object.entries(symbols).forEach(([key, value]) => {
|
|
@@ -100262,9 +100357,8 @@ __export(exports_cli, {
|
|
|
100262
100357
|
});
|
|
100263
100358
|
import {
|
|
100264
100359
|
readFileSync as readFileSync20,
|
|
100265
|
-
writeFileSync as writeFileSync13,
|
|
100266
100360
|
existsSync as existsSync24,
|
|
100267
|
-
mkdirSync as
|
|
100361
|
+
mkdirSync as mkdirSync10,
|
|
100268
100362
|
copyFileSync,
|
|
100269
100363
|
readdirSync as readdirSync4,
|
|
100270
100364
|
unlinkSync as unlinkSync7
|
|
@@ -100279,7 +100373,7 @@ function clearAllModelCaches() {
|
|
|
100279
100373
|
const cacheDir = join25(homedir23(), ".claudish");
|
|
100280
100374
|
if (!existsSync24(cacheDir))
|
|
100281
100375
|
return;
|
|
100282
|
-
const cachePatterns = ["
|
|
100376
|
+
const cachePatterns = ["pricing-cache.json", "recommended-models-cache.json"];
|
|
100283
100377
|
let cleared = 0;
|
|
100284
100378
|
try {
|
|
100285
100379
|
const files = readdirSync4(cacheDir);
|
|
@@ -100477,25 +100571,54 @@ async function parseArgs(args) {
|
|
|
100477
100571
|
const forceUpdate = args.includes("--force-update");
|
|
100478
100572
|
if (forceUpdate)
|
|
100479
100573
|
clearAllModelCaches();
|
|
100480
|
-
await
|
|
100481
|
-
if (hasJsonFlag) {
|
|
100482
|
-
printAvailableModelsJSON();
|
|
100483
|
-
} else {
|
|
100484
|
-
printAvailableModels();
|
|
100485
|
-
}
|
|
100574
|
+
await printRecommendedModels(hasJsonFlag, forceUpdate);
|
|
100486
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
|
+
}
|
|
100487
100601
|
} else if (arg === "--models" || arg === "--list-models" || arg === "-s" || arg === "--search") {
|
|
100488
100602
|
const nextArg = args[i + 1];
|
|
100489
100603
|
const hasQuery = nextArg && !nextArg.startsWith("--");
|
|
100490
100604
|
const query = hasQuery ? args[++i] : null;
|
|
100491
100605
|
const hasJsonFlag = args.includes("--json");
|
|
100492
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;
|
|
100493
100609
|
if (forceUpdate)
|
|
100494
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
|
+
}
|
|
100495
100616
|
if (query) {
|
|
100496
|
-
await searchAndPrintModels(query,
|
|
100617
|
+
await searchAndPrintModels(query, hasJsonFlag);
|
|
100618
|
+
} else if (providerSlug) {
|
|
100619
|
+
await printByProvider(providerSlug, hasJsonFlag);
|
|
100497
100620
|
} else {
|
|
100498
|
-
await
|
|
100621
|
+
await printTop100(hasJsonFlag);
|
|
100499
100622
|
}
|
|
100500
100623
|
process.exit(0);
|
|
100501
100624
|
} else if (arg === "--summarize-tools") {
|
|
@@ -100618,502 +100741,298 @@ async function fetchOllamaModels() {
|
|
|
100618
100741
|
return [];
|
|
100619
100742
|
}
|
|
100620
100743
|
}
|
|
100621
|
-
|
|
100622
|
-
|
|
100623
|
-
|
|
100624
|
-
|
|
100625
|
-
|
|
100626
|
-
|
|
100627
|
-
|
|
100628
|
-
|
|
100629
|
-
|
|
100630
|
-
|
|
100631
|
-
|
|
100632
|
-
|
|
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);
|
|
100633
100784
|
}
|
|
100634
|
-
if (
|
|
100635
|
-
|
|
100636
|
-
|
|
100637
|
-
|
|
100638
|
-
|
|
100639
|
-
throw new Error(`API returned ${response.status}`);
|
|
100640
|
-
const data = await response.json();
|
|
100641
|
-
models = data.data;
|
|
100642
|
-
mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
|
|
100643
|
-
writeFileSync13(ALL_MODELS_JSON_PATH, JSON.stringify({
|
|
100644
|
-
lastUpdated: new Date().toISOString(),
|
|
100645
|
-
models
|
|
100646
|
-
}), "utf-8");
|
|
100647
|
-
console.error(`\u2705 Cached ${models.length} models`);
|
|
100648
|
-
} catch (error2) {
|
|
100649
|
-
console.error(`\u274C Failed to fetch models: ${error2}`);
|
|
100650
|
-
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}"`);
|
|
100651
100790
|
}
|
|
100791
|
+
return;
|
|
100652
100792
|
}
|
|
100653
|
-
|
|
100654
|
-
|
|
100655
|
-
|
|
100656
|
-
|
|
100657
|
-
|
|
100658
|
-
|
|
100659
|
-
|
|
100660
|
-
|
|
100661
|
-
|
|
100662
|
-
|
|
100663
|
-
|
|
100664
|
-
|
|
100665
|
-
|
|
100666
|
-
|
|
100667
|
-
const openaiModels = Object.entries(openaiData.models).filter(([id, _2]) => {
|
|
100668
|
-
const lowerId = id.toLowerCase();
|
|
100669
|
-
return lowerId.startsWith("gpt-") || lowerId.startsWith("o1-") || lowerId.startsWith("o3-") || lowerId.startsWith("o4-") || lowerId.startsWith("chatgpt-");
|
|
100670
|
-
}).map(([id, m2]) => {
|
|
100671
|
-
const inputCost = m2.cost?.input || 2;
|
|
100672
|
-
const outputCost = m2.cost?.output || 8;
|
|
100673
|
-
const contextLen = m2.limit?.context || 128000;
|
|
100674
|
-
const inputModalities = m2.modalities?.input || [];
|
|
100675
|
-
return {
|
|
100676
|
-
id: `oai/${id}`,
|
|
100677
|
-
name: m2.name || id,
|
|
100678
|
-
description: `OpenAI direct model`,
|
|
100679
|
-
context_length: contextLen,
|
|
100680
|
-
pricing: {
|
|
100681
|
-
prompt: String(inputCost / 1e6),
|
|
100682
|
-
completion: String(outputCost / 1e6)
|
|
100683
|
-
},
|
|
100684
|
-
isOAIDirect: true,
|
|
100685
|
-
supportsTools: m2.tool_call === true,
|
|
100686
|
-
supportsReasoning: m2.reasoning === true,
|
|
100687
|
-
supportsVision: inputModalities.includes("image") || inputModalities.includes("video")
|
|
100688
|
-
};
|
|
100689
|
-
});
|
|
100690
|
-
console.error(`\uD83D\uDD11 Found ${openaiModels.length} OpenAI direct models`);
|
|
100691
|
-
models = [...openaiModels, ...models];
|
|
100692
|
-
}
|
|
100693
|
-
}
|
|
100694
|
-
} catch {}
|
|
100695
|
-
}
|
|
100696
|
-
if (process.env.GLM_CODING_API_KEY) {
|
|
100697
|
-
try {
|
|
100698
|
-
const glmCodingModels = await fetchGLMCodingModels();
|
|
100699
|
-
if (glmCodingModels.length > 0) {
|
|
100700
|
-
console.error(`\uD83D\uDD11 Found ${glmCodingModels.length} GLM Coding Plan models`);
|
|
100701
|
-
models = [...glmCodingModels, ...models];
|
|
100702
|
-
}
|
|
100703
|
-
} catch {}
|
|
100704
|
-
}
|
|
100705
|
-
if (process.env.LITELLM_BASE_URL && process.env.LITELLM_API_KEY) {
|
|
100706
|
-
try {
|
|
100707
|
-
const litellmModels = await fetchLiteLLMModels(process.env.LITELLM_BASE_URL, process.env.LITELLM_API_KEY, forceUpdate);
|
|
100708
|
-
if (litellmModels.length > 0) {
|
|
100709
|
-
console.error(`\uD83D\uDD17 Found ${litellmModels.length} LiteLLM models`);
|
|
100710
|
-
models = [...litellmModels, ...models];
|
|
100711
|
-
}
|
|
100712
|
-
} catch {}
|
|
100713
|
-
}
|
|
100714
|
-
const results = models.map((model) => {
|
|
100715
|
-
const nameScore = fuzzyScore2(model.name || "", query);
|
|
100716
|
-
const idScore = fuzzyScore2(model.id || "", query);
|
|
100717
|
-
const descScore = fuzzyScore2(model.description || "", query) * 0.5;
|
|
100718
|
-
return {
|
|
100719
|
-
model,
|
|
100720
|
-
score: Math.max(nameScore, idScore, descScore)
|
|
100721
|
-
};
|
|
100722
|
-
}).filter((item) => item.score > 0.2).sort((a, b2) => b2.score - a.score).slice(0, 20);
|
|
100723
|
-
if (results.length === 0) {
|
|
100724
|
-
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));
|
|
100725
100807
|
return;
|
|
100726
100808
|
}
|
|
100727
|
-
const RED2 = "\x1B[31m";
|
|
100728
|
-
const GREEN = "\x1B[32m";
|
|
100729
|
-
const RESET = "\x1B[0m";
|
|
100730
|
-
const DIM = "\x1B[2m";
|
|
100731
100809
|
console.log(`
|
|
100732
100810
|
Found ${results.length} matching models:
|
|
100733
100811
|
`);
|
|
100734
|
-
console.log(" Model Provider Pricing Context
|
|
100812
|
+
console.log(" Model Provider Pricing Context Caps");
|
|
100735
100813
|
console.log(" " + "\u2500".repeat(80));
|
|
100736
|
-
for (const
|
|
100737
|
-
|
|
100738
|
-
|
|
100739
|
-
|
|
100740
|
-
|
|
100741
|
-
|
|
100742
|
-
|
|
100743
|
-
|
|
100744
|
-
} else if (model.source === "LiteLLM" || model.id.startsWith("litellm@")) {
|
|
100745
|
-
fullModelId = model.id;
|
|
100746
|
-
} else {
|
|
100747
|
-
fullModelId = `openrouter@${model.id}`;
|
|
100748
|
-
}
|
|
100749
|
-
const modelId = fullModelId.length > 30 ? fullModelId.substring(0, 27) + "..." : fullModelId;
|
|
100750
|
-
const modelIdPadded = modelId.padEnd(30);
|
|
100751
|
-
const providerName = model.id.split("/")[0];
|
|
100752
|
-
const provider = providerName.length > 10 ? providerName.substring(0, 7) + "..." : providerName;
|
|
100753
|
-
const providerPadded = provider.padEnd(10);
|
|
100754
|
-
let pricing;
|
|
100755
|
-
if (model.isLocal) {
|
|
100756
|
-
pricing = "LOCAL";
|
|
100757
|
-
} else {
|
|
100758
|
-
const promptPrice = parseFloat(model.pricing?.prompt || "0") * 1e6;
|
|
100759
|
-
const completionPrice = parseFloat(model.pricing?.completion || "0") * 1e6;
|
|
100760
|
-
const avg = (promptPrice + completionPrice) / 2;
|
|
100761
|
-
if (avg < 0) {
|
|
100762
|
-
pricing = "varies";
|
|
100763
|
-
} else if (avg === 0) {
|
|
100764
|
-
pricing = "FREE";
|
|
100765
|
-
} else {
|
|
100766
|
-
pricing = `$${avg.toFixed(2)}/1M`;
|
|
100767
|
-
}
|
|
100768
|
-
}
|
|
100769
|
-
const pricingPadded = pricing.padEnd(10);
|
|
100770
|
-
const contextLen = model.context_length || model.top_provider?.context_length || 0;
|
|
100771
|
-
const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
|
|
100772
|
-
const contextPadded = context.padEnd(7);
|
|
100773
|
-
if (model.isLocal && model.supportsTools === false) {
|
|
100774
|
-
console.log(` ${RED2}${modelIdPadded} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}% \u2717 no tools${RESET}`);
|
|
100775
|
-
} else if (model.isLocal && model.supportsTools === true) {
|
|
100776
|
-
console.log(` ${GREEN}${modelIdPadded}${RESET} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}%`);
|
|
100777
|
-
} else {
|
|
100778
|
-
console.log(` ${modelIdPadded} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}%`);
|
|
100779
|
-
}
|
|
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}`);
|
|
100780
100822
|
}
|
|
100781
100823
|
console.log("");
|
|
100782
|
-
console.log(
|
|
100824
|
+
console.log("Caps: T = tools R = reasoning V = vision");
|
|
100783
100825
|
console.log("");
|
|
100784
|
-
console.log("Use
|
|
100785
|
-
console.log("
|
|
100786
|
-
|
|
100787
|
-
|
|
100788
|
-
|
|
100789
|
-
|
|
100790
|
-
|
|
100791
|
-
|
|
100792
|
-
|
|
100793
|
-
|
|
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) {
|
|
100794
100860
|
try {
|
|
100795
|
-
const
|
|
100796
|
-
|
|
100797
|
-
|
|
100798
|
-
|
|
100799
|
-
|
|
100800
|
-
models = cacheData.models;
|
|
100801
|
-
if (!jsonOutput) {
|
|
100802
|
-
console.error(`\u2713 Using cached models (last updated: ${cacheData.lastUpdated.split("T")[0]})`);
|
|
100803
|
-
}
|
|
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";
|
|
100804
100866
|
}
|
|
100805
|
-
} catch
|
|
100806
|
-
|
|
100807
|
-
if (models.length === 0) {
|
|
100808
|
-
console.error("\uD83D\uDD04 Fetching all models from OpenRouter...");
|
|
100809
|
-
try {
|
|
100810
|
-
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
100811
|
-
if (!response.ok)
|
|
100812
|
-
throw new Error(`API returned ${response.status}`);
|
|
100813
|
-
const data = await response.json();
|
|
100814
|
-
models = data.data;
|
|
100815
|
-
mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
|
|
100816
|
-
writeFileSync13(ALL_MODELS_JSON_PATH, JSON.stringify({
|
|
100817
|
-
lastUpdated: new Date().toISOString(),
|
|
100818
|
-
models
|
|
100819
|
-
}), "utf-8");
|
|
100820
|
-
console.error(`\u2705 Cached ${models.length} models`);
|
|
100821
|
-
} catch (error2) {
|
|
100822
|
-
console.error(`\u274C Failed to fetch models: ${error2}`);
|
|
100823
|
-
process.exit(1);
|
|
100867
|
+
} catch {
|
|
100868
|
+
litellmLine = " LiteLLM: configured but unreachable";
|
|
100824
100869
|
}
|
|
100825
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
|
+
}
|
|
100826
100882
|
if (jsonOutput) {
|
|
100827
|
-
|
|
100828
|
-
console.log(JSON.stringify({
|
|
100829
|
-
count: allModels.length,
|
|
100830
|
-
localCount: ollamaModels.length,
|
|
100831
|
-
zenCount: zenModels.length,
|
|
100832
|
-
lastUpdated: new Date().toISOString().split("T")[0],
|
|
100833
|
-
models: allModels.map((m2) => {
|
|
100834
|
-
let id;
|
|
100835
|
-
if (m2.isLocal) {
|
|
100836
|
-
id = m2.id.replace("ollama/", "ollama@");
|
|
100837
|
-
} else if (m2.isZen || m2.id.startsWith("zen/")) {
|
|
100838
|
-
id = m2.id.replace("zen/", "zen@");
|
|
100839
|
-
} else if (m2.source === "LiteLLM" || m2.id.startsWith("litellm@")) {
|
|
100840
|
-
id = m2.id;
|
|
100841
|
-
} else {
|
|
100842
|
-
id = `openrouter@${m2.id}`;
|
|
100843
|
-
}
|
|
100844
|
-
return {
|
|
100845
|
-
id,
|
|
100846
|
-
name: m2.name,
|
|
100847
|
-
context: m2.context_length || m2.top_provider?.context_length,
|
|
100848
|
-
pricing: m2.pricing,
|
|
100849
|
-
isLocal: m2.isLocal || false,
|
|
100850
|
-
isZen: m2.isZen || false
|
|
100851
|
-
};
|
|
100852
|
-
})
|
|
100853
|
-
}, null, 2));
|
|
100883
|
+
console.log(JSON.stringify(response, null, 2));
|
|
100854
100884
|
return;
|
|
100855
100885
|
}
|
|
100856
|
-
|
|
100857
|
-
|
|
100858
|
-
const RESET = "\x1B[0m";
|
|
100859
|
-
const DIM = "\x1B[2m";
|
|
100860
|
-
if (ollamaModels.length > 0) {
|
|
100861
|
-
const toolCapableCount = ollamaModels.filter((m2) => m2.supportsTools).length;
|
|
100862
|
-
console.log(`
|
|
100863
|
-
\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)
|
|
100864
100888
|
`);
|
|
100865
|
-
|
|
100866
|
-
console.log("
|
|
100867
|
-
for (const model of ollamaModels) {
|
|
100868
|
-
const fullId = model.id.replace("ollama/", "ollama@");
|
|
100869
|
-
const modelId = fullId.length > 35 ? fullId.substring(0, 32) + "..." : fullId;
|
|
100870
|
-
const modelIdPadded = modelId.padEnd(38);
|
|
100871
|
-
const size = model.size ? `${(model.size / 1e9).toFixed(1)}GB` : "N/A";
|
|
100872
|
-
const sizePadded = size.padEnd(12);
|
|
100873
|
-
const params = model.details?.parameter_size || "N/A";
|
|
100874
|
-
const paramsPadded = params.padEnd(8);
|
|
100875
|
-
if (model.supportsTools) {
|
|
100876
|
-
console.log(` ${modelIdPadded} ${sizePadded} ${paramsPadded} ${GREEN}\u2713${RESET}`);
|
|
100877
|
-
} else {
|
|
100878
|
-
console.log(` ${RED2}${modelIdPadded} ${sizePadded} ${paramsPadded} \u2717 no tools${RESET}`);
|
|
100879
|
-
}
|
|
100880
|
-
}
|
|
100881
|
-
console.log("");
|
|
100882
|
-
console.log(` ${GREEN}\u2713${RESET} = Compatible with Claude Code (supports tool calling)`);
|
|
100883
|
-
console.log(` ${RED2}\u2717${RESET} = Not compatible ${DIM}(Claude Code requires tool support)${RESET}`);
|
|
100884
|
-
console.log("");
|
|
100885
|
-
console.log(" Use: claudish --model ollama@<model-name>");
|
|
100886
|
-
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.");
|
|
100887
100891
|
} else {
|
|
100888
|
-
|
|
100889
|
-
\uD83C\uDFE0 LOCAL OLLAMA: Not running or no models installed`);
|
|
100890
|
-
console.log(" Start Ollama: ollama serve");
|
|
100891
|
-
console.log(" Pull a model: ollama pull llama3.2");
|
|
100892
|
-
}
|
|
100893
|
-
if (zenModels.length > 0) {
|
|
100894
|
-
const freeCount = zenModels.filter((m2) => m2.isFree).length;
|
|
100895
|
-
console.log(`
|
|
100896
|
-
\uD83D\uDD2E OPENCODE ZEN (${zenModels.length} models, ${freeCount} FREE - no API key needed):
|
|
100897
|
-
`);
|
|
100898
|
-
console.log(" Model Context Pricing Tools");
|
|
100899
|
-
console.log(" " + "\u2500".repeat(68));
|
|
100900
|
-
const sortedModels = [...zenModels].sort((a, b2) => {
|
|
100901
|
-
if (a.isFree && !b2.isFree)
|
|
100902
|
-
return -1;
|
|
100903
|
-
if (!a.isFree && b2.isFree)
|
|
100904
|
-
return 1;
|
|
100905
|
-
return (b2.context_length || 0) - (a.context_length || 0);
|
|
100906
|
-
});
|
|
100907
|
-
for (const model of sortedModels) {
|
|
100908
|
-
const fullId = model.id.replace("zen/", "zen@");
|
|
100909
|
-
const modelId = fullId.length > 30 ? fullId.substring(0, 27) + "..." : fullId;
|
|
100910
|
-
const modelIdPadded = modelId.padEnd(32);
|
|
100911
|
-
const contextLen = model.context_length || 0;
|
|
100912
|
-
const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
|
|
100913
|
-
const contextPadded = context.padEnd(10);
|
|
100914
|
-
const pricing = model.isFree ? `${GREEN}FREE${RESET}` : `$${(parseFloat(model.pricing?.prompt || "0") + parseFloat(model.pricing?.completion || "0")).toFixed(1)}/M`;
|
|
100915
|
-
const pricingPadded = model.isFree ? "FREE " : pricing.padEnd(12);
|
|
100916
|
-
const tools = model.supportsTools ? `${GREEN}\u2713${RESET}` : `${RED2}\u2717${RESET}`;
|
|
100917
|
-
console.log(` ${modelIdPadded} ${contextPadded} ${pricingPadded} ${tools}`);
|
|
100918
|
-
}
|
|
100892
|
+
renderModelDocTable(response.models, true);
|
|
100919
100893
|
console.log("");
|
|
100920
|
-
console.log(
|
|
100921
|
-
console.log(" Use: claudish --model zen@<model-id>");
|
|
100894
|
+
console.log(" Caps: T = tools R = reasoning V = vision");
|
|
100922
100895
|
}
|
|
100923
|
-
|
|
100924
|
-
|
|
100925
|
-
|
|
100926
|
-
|
|
100927
|
-
|
|
100928
|
-
|
|
100929
|
-
|
|
100930
|
-
|
|
100931
|
-
|
|
100932
|
-
|
|
100933
|
-
|
|
100934
|
-
|
|
100935
|
-
|
|
100936
|
-
|
|
100937
|
-
|
|
100938
|
-
|
|
100939
|
-
|
|
100940
|
-
}
|
|
100941
|
-
console.log("");
|
|
100942
|
-
console.log(" Use: claudish --model litellm@<model-group>");
|
|
100943
|
-
}
|
|
100944
|
-
} 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);
|
|
100945
100913
|
}
|
|
100946
|
-
|
|
100947
|
-
|
|
100948
|
-
|
|
100949
|
-
if (!byProvider.has(provider)) {
|
|
100950
|
-
byProvider.set(provider, []);
|
|
100951
|
-
}
|
|
100952
|
-
byProvider.get(provider).push(model);
|
|
100914
|
+
if (jsonOutput) {
|
|
100915
|
+
console.log(JSON.stringify({ provider: providerSlug, count: models.length, models }, null, 2));
|
|
100916
|
+
return;
|
|
100953
100917
|
}
|
|
100954
|
-
|
|
100955
|
-
console.log(`
|
|
100956
|
-
\u2601\uFE0F OPENROUTER MODELS (${models.length} total):
|
|
100957
|
-
`);
|
|
100958
|
-
for (const provider of sortedProviders) {
|
|
100959
|
-
const providerModels = byProvider.get(provider);
|
|
100918
|
+
if (models.length === 0) {
|
|
100960
100919
|
console.log(`
|
|
100961
|
-
|
|
100962
|
-
|
|
100963
|
-
|
|
100964
|
-
const fullId = `openrouter@${model.id}`;
|
|
100965
|
-
const modelId = fullId.length > 40 ? fullId.substring(0, 37) + "..." : fullId;
|
|
100966
|
-
const modelIdPadded = modelId.padEnd(42);
|
|
100967
|
-
const promptPrice = parseFloat(model.pricing?.prompt || "0") * 1e6;
|
|
100968
|
-
const completionPrice = parseFloat(model.pricing?.completion || "0") * 1e6;
|
|
100969
|
-
const avg = (promptPrice + completionPrice) / 2;
|
|
100970
|
-
let pricing;
|
|
100971
|
-
if (avg < 0) {
|
|
100972
|
-
pricing = "varies";
|
|
100973
|
-
} else if (avg === 0) {
|
|
100974
|
-
pricing = "FREE";
|
|
100975
|
-
} else {
|
|
100976
|
-
pricing = `$${avg.toFixed(2)}/1M`;
|
|
100977
|
-
}
|
|
100978
|
-
const pricingPadded = pricing.padEnd(12);
|
|
100979
|
-
const contextLen = model.context_length || model.top_provider?.context_length || 0;
|
|
100980
|
-
const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
|
|
100981
|
-
const contextPadded = context.padEnd(8);
|
|
100982
|
-
console.log(` ${modelIdPadded} ${pricingPadded} ${contextPadded}`);
|
|
100983
|
-
}
|
|
100920
|
+
No active models found for provider "${providerSlug}". Try \`claudish -s <query>\` to search the full catalog.
|
|
100921
|
+
`);
|
|
100922
|
+
return;
|
|
100984
100923
|
}
|
|
100985
100924
|
console.log(`
|
|
100925
|
+
Provider: ${providerSlug} (${models.length} active models)
|
|
100986
100926
|
`);
|
|
100987
|
-
|
|
100988
|
-
console.log("
|
|
100989
|
-
console.log("
|
|
100990
|
-
console.log("
|
|
100991
|
-
console.log("
|
|
100992
|
-
console.log("
|
|
100993
|
-
console.log("
|
|
100994
|
-
}
|
|
100995
|
-
function
|
|
100996
|
-
|
|
100997
|
-
if (!existsSync24(cachePath)) {
|
|
100998
|
-
return true;
|
|
100999
|
-
}
|
|
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;
|
|
101000
100937
|
try {
|
|
101001
|
-
|
|
101002
|
-
const data = JSON.parse(jsonContent);
|
|
101003
|
-
if (!data.lastUpdated) {
|
|
101004
|
-
return true;
|
|
101005
|
-
}
|
|
101006
|
-
const lastUpdated = new Date(data.lastUpdated);
|
|
101007
|
-
const now = new Date;
|
|
101008
|
-
const ageInDays = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
|
|
101009
|
-
return ageInDays > CACHE_MAX_AGE_DAYS2;
|
|
100938
|
+
doc2 = await getRecommendedModels({ forceRefresh: forceUpdate });
|
|
101010
100939
|
} catch (error2) {
|
|
101011
|
-
|
|
100940
|
+
console.error(`\u274C Failed to load recommended models: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
100941
|
+
process.exit(1);
|
|
101012
100942
|
}
|
|
101013
|
-
|
|
101014
|
-
|
|
101015
|
-
|
|
101016
|
-
|
|
101017
|
-
|
|
101018
|
-
|
|
101019
|
-
|
|
101020
|
-
|
|
101021
|
-
const
|
|
101022
|
-
|
|
101023
|
-
|
|
101024
|
-
const
|
|
101025
|
-
|
|
101026
|
-
|
|
101027
|
-
|
|
101028
|
-
|
|
101029
|
-
|
|
101030
|
-
|
|
101031
|
-
|
|
101032
|
-
|
|
101033
|
-
|
|
101034
|
-
|
|
101035
|
-
|
|
101036
|
-
|
|
101037
|
-
|
|
101038
|
-
|
|
101039
|
-
|
|
101040
|
-
|
|
101041
|
-
|
|
101042
|
-
|
|
101043
|
-
|
|
101044
|
-
|
|
101045
|
-
|
|
101046
|
-
|
|
101047
|
-
|
|
101048
|
-
|
|
101049
|
-
|
|
101050
|
-
|
|
101051
|
-
} else if (lowerDesc.includes("reason")) {
|
|
101052
|
-
category = "reasoning";
|
|
101053
|
-
}
|
|
101054
|
-
const bareId = modelId.split("/").pop().replace(/:free$/, "");
|
|
101055
|
-
recommendations.push({
|
|
101056
|
-
id: bareId,
|
|
101057
|
-
openrouterId: modelId,
|
|
101058
|
-
name,
|
|
101059
|
-
description,
|
|
101060
|
-
provider: provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
101061
|
-
category,
|
|
101062
|
-
priority: recommendations.length + 1,
|
|
101063
|
-
pricing: {
|
|
101064
|
-
input: inputPrice,
|
|
101065
|
-
output: outputPrice,
|
|
101066
|
-
average: avgPrice
|
|
101067
|
-
},
|
|
101068
|
-
context: topProvider.context_length ? `${Math.floor(topProvider.context_length / 1000)}K` : "N/A",
|
|
101069
|
-
maxOutputTokens: topProvider.max_completion_tokens || null,
|
|
101070
|
-
modality: architecture.modality || "text->text",
|
|
101071
|
-
supportsTools: supportedParams.includes("tools") || supportedParams.includes("tool_choice"),
|
|
101072
|
-
supportsReasoning: supportedParams.includes("reasoning") || supportedParams.includes("include_reasoning"),
|
|
101073
|
-
supportsVision: (architecture.input_modalities || []).includes("image") || (architecture.input_modalities || []).includes("video"),
|
|
101074
|
-
isModerated: topProvider.is_moderated || false,
|
|
101075
|
-
recommended: true
|
|
101076
|
-
});
|
|
101077
|
-
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}`);
|
|
101078
100981
|
}
|
|
101079
|
-
|
|
101080
|
-
|
|
101081
|
-
|
|
101082
|
-
|
|
101083
|
-
|
|
101084
|
-
|
|
101085
|
-
|
|
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("");
|
|
101086
100993
|
}
|
|
101087
|
-
const updatedData = {
|
|
101088
|
-
version: version2,
|
|
101089
|
-
lastUpdated: new Date().toISOString().split("T")[0],
|
|
101090
|
-
source: "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
|
|
101091
|
-
models: recommendations
|
|
101092
|
-
};
|
|
101093
|
-
mkdirSync11(CLAUDISH_CACHE_DIR2, { recursive: true });
|
|
101094
|
-
writeFileSync13(CACHED_MODELS_PATH, JSON.stringify(updatedData, null, 2), "utf-8");
|
|
101095
|
-
console.error(`\u2705 Updated ${recommendations.length} models (last updated: ${updatedData.lastUpdated})`);
|
|
101096
|
-
} catch (error2) {
|
|
101097
|
-
console.error(`\u274C Failed to update models: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
101098
|
-
console.error(" Using cached models (if available)");
|
|
101099
100994
|
}
|
|
101100
|
-
|
|
101101
|
-
|
|
101102
|
-
|
|
101103
|
-
console.
|
|
101104
|
-
|
|
101105
|
-
|
|
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
|
+
}
|
|
101106
101005
|
}
|
|
101107
|
-
|
|
101108
|
-
|
|
101109
|
-
|
|
101110
|
-
|
|
101111
|
-
|
|
101112
|
-
|
|
101113
|
-
|
|
101114
|
-
|
|
101115
|
-
|
|
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);
|
|
101116
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("");
|
|
101117
101036
|
}
|
|
101118
101037
|
function printVersion() {
|
|
101119
101038
|
console.log(`claudish version ${VERSION}`);
|
|
@@ -101563,9 +101482,15 @@ OPTIONS:
|
|
|
101563
101482
|
--cost-tracker Enable cost tracking for API usage (NB!)
|
|
101564
101483
|
--audit-costs Show cost analysis report
|
|
101565
101484
|
--reset-costs Reset accumulated cost statistics
|
|
101566
|
-
--models
|
|
101567
|
-
--models <
|
|
101568
|
-
|
|
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)
|
|
101569
101494
|
--team <models> Run multiple models in parallel (comma-separated)
|
|
101570
101495
|
Example: --team minimax-m2.5,kimi-k2.5 "prompt"
|
|
101571
101496
|
--mode <mode> Team mode: default (grid), interactive, json
|
|
@@ -101574,7 +101499,7 @@ OPTIONS:
|
|
|
101574
101499
|
1-token request (diagnostic, may incur tiny cost)
|
|
101575
101500
|
--no-probe Skip live requests, show static chain only
|
|
101576
101501
|
--probe-timeout <secs> Per-link timeout for live probes (default: 40)
|
|
101577
|
-
--json Output in JSON format (use with --models, --top-models, --probe)
|
|
101502
|
+
--json Output in JSON format (use with --list-models, --top-models, --probe)
|
|
101578
101503
|
--force-update Force refresh model cache from OpenRouter API
|
|
101579
101504
|
--version Show version information
|
|
101580
101505
|
-h, --help Show this help message
|
|
@@ -101623,7 +101548,7 @@ MODEL MAPPING (per-role override):
|
|
|
101623
101548
|
--model-subagent <model> Model for sub-agents (Task tool)
|
|
101624
101549
|
|
|
101625
101550
|
CUSTOM MODELS:
|
|
101626
|
-
Claudish accepts ANY valid
|
|
101551
|
+
Claudish accepts ANY valid model ID from the Firebase catalog, even if not in --list-models
|
|
101627
101552
|
Example: claudish --model openrouter@your_provider/custom-model-123 "task"
|
|
101628
101553
|
|
|
101629
101554
|
MODES:
|
|
@@ -101791,14 +101716,14 @@ LOCAL MODELS (Ollama, LM Studio, vLLM):
|
|
|
101791
101716
|
OLLAMA_HOST=http://192.168.1.50:11434 claudish --model ollama/llama3.2 "task"
|
|
101792
101717
|
|
|
101793
101718
|
AVAILABLE MODELS:
|
|
101794
|
-
|
|
101795
|
-
|
|
101796
|
-
|
|
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)
|
|
101797
101724
|
Probe routing: claudish --probe minimax-m2.5 kimi-k2.5 gemini-3.1-pro-preview
|
|
101798
|
-
Free models only: claudish --free
|
|
101799
|
-
JSON output: claudish --models --json
|
|
101800
|
-
Force cache update: claudish --models --force-update
|
|
101801
|
-
(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
|
|
101802
101727
|
|
|
101803
101728
|
MORE INFO:
|
|
101804
101729
|
GitHub: https://github.com/MadAppGang/claude-code
|
|
@@ -101846,15 +101771,15 @@ async function initializeClaudishSkill() {
|
|
|
101846
101771
|
}
|
|
101847
101772
|
try {
|
|
101848
101773
|
if (!existsSync24(claudeDir)) {
|
|
101849
|
-
|
|
101774
|
+
mkdirSync10(claudeDir, { recursive: true });
|
|
101850
101775
|
console.log("\uD83D\uDCC1 Created .claude/ directory");
|
|
101851
101776
|
}
|
|
101852
101777
|
if (!existsSync24(skillsDir)) {
|
|
101853
|
-
|
|
101778
|
+
mkdirSync10(skillsDir, { recursive: true });
|
|
101854
101779
|
console.log("\uD83D\uDCC1 Created .claude/skills/ directory");
|
|
101855
101780
|
}
|
|
101856
101781
|
if (!existsSync24(claudishSkillDir)) {
|
|
101857
|
-
|
|
101782
|
+
mkdirSync10(claudishSkillDir, { recursive: true });
|
|
101858
101783
|
console.log("\uD83D\uDCC1 Created .claude/skills/claudish-usage/ directory");
|
|
101859
101784
|
}
|
|
101860
101785
|
copyFileSync(sourceSkillPath, skillFile);
|
|
@@ -101893,151 +101818,27 @@ async function initializeClaudishSkill() {
|
|
|
101893
101818
|
}
|
|
101894
101819
|
}
|
|
101895
101820
|
function printAvailableModels() {
|
|
101896
|
-
let lastUpdated = "unknown";
|
|
101897
|
-
let models = [];
|
|
101898
101821
|
try {
|
|
101899
|
-
const cachePath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
|
|
101900
|
-
if (existsSync24(cachePath)) {
|
|
101901
|
-
const data = JSON.parse(readFileSync20(cachePath, "utf-8"));
|
|
101902
|
-
lastUpdated = data.lastUpdated || "unknown";
|
|
101903
|
-
models = data.models || [];
|
|
101904
|
-
}
|
|
101905
|
-
} catch {
|
|
101906
101822
|
const basicModels = getAvailableModels();
|
|
101907
101823
|
const modelInfo = loadModelInfo();
|
|
101824
|
+
console.log("\nAvailable models (type `claudish --top-models` for full table):\n");
|
|
101908
101825
|
for (const model of basicModels) {
|
|
101909
101826
|
const info = modelInfo[model];
|
|
101827
|
+
if (!info)
|
|
101828
|
+
continue;
|
|
101910
101829
|
console.log(` ${model}`);
|
|
101911
101830
|
console.log(` ${info.name} - ${info.description}`);
|
|
101912
|
-
console.log("");
|
|
101913
101831
|
}
|
|
101914
|
-
|
|
101915
|
-
}
|
|
101916
|
-
console.log(`
|
|
101917
|
-
Recommended Models (last updated: ${lastUpdated}):
|
|
101918
|
-
`);
|
|
101919
|
-
console.log(" Model Pricing Context Capabilities");
|
|
101920
|
-
console.log(" " + "\u2500".repeat(66));
|
|
101921
|
-
for (const model of models) {
|
|
101922
|
-
const modelId = model.id.length > 28 ? model.id.substring(0, 25) + "..." : model.id;
|
|
101923
|
-
const modelIdPadded = modelId.padEnd(28);
|
|
101924
|
-
let pricing = model.pricing?.average || "N/A";
|
|
101925
|
-
if (pricing.includes("-1000000")) {
|
|
101926
|
-
pricing = "varies";
|
|
101927
|
-
} else if (pricing === "$0.00/1M" || pricing === "FREE") {
|
|
101928
|
-
pricing = "FREE";
|
|
101929
|
-
}
|
|
101930
|
-
const pricingPadded = pricing.padEnd(10);
|
|
101931
|
-
const context = model.context || "N/A";
|
|
101932
|
-
const contextPadded = context.padEnd(7);
|
|
101933
|
-
const tools = model.supportsTools ? "\uD83D\uDD27" : " ";
|
|
101934
|
-
const reasoning = model.supportsReasoning ? "\uD83E\uDDE0" : " ";
|
|
101935
|
-
const vision = model.supportsVision ? "\uD83D\uDC41\uFE0F " : " ";
|
|
101936
|
-
const capabilities = `${tools} ${reasoning} ${vision}`;
|
|
101937
|
-
console.log(` ${modelIdPadded} ${pricingPadded} ${contextPadded} ${capabilities}`);
|
|
101938
|
-
}
|
|
101939
|
-
console.log("");
|
|
101940
|
-
console.log(" Capabilities: \uD83D\uDD27 Tools \uD83E\uDDE0 Reasoning \uD83D\uDC41\uFE0F Vision");
|
|
101941
|
-
console.log("");
|
|
101942
|
-
console.log("Set default with: export CLAUDISH_MODEL=<model>");
|
|
101943
|
-
console.log(" or: export ANTHROPIC_MODEL=<model>");
|
|
101944
|
-
console.log("Or use: claudish --model <model> ...");
|
|
101945
|
-
console.log(`
|
|
101946
|
-
Force update: claudish --list-models --force-update
|
|
101947
|
-
`);
|
|
101948
|
-
}
|
|
101949
|
-
function printAvailableModelsJSON() {
|
|
101950
|
-
const jsonPath = existsSync24(CACHED_MODELS_PATH) ? CACHED_MODELS_PATH : BUNDLED_MODELS_PATH;
|
|
101951
|
-
try {
|
|
101952
|
-
const jsonContent = readFileSync20(jsonPath, "utf-8");
|
|
101953
|
-
const data = JSON.parse(jsonContent);
|
|
101954
|
-
console.log(JSON.stringify(data, null, 2));
|
|
101832
|
+
console.log("");
|
|
101955
101833
|
} catch (error2) {
|
|
101956
|
-
|
|
101957
|
-
const modelInfo = loadModelInfo();
|
|
101958
|
-
const output = {
|
|
101959
|
-
version: VERSION,
|
|
101960
|
-
lastUpdated: new Date().toISOString().split("T")[0],
|
|
101961
|
-
source: "runtime",
|
|
101962
|
-
models: models.filter((m2) => m2 !== "custom").map((modelId) => {
|
|
101963
|
-
const info = modelInfo[modelId];
|
|
101964
|
-
return {
|
|
101965
|
-
id: modelId,
|
|
101966
|
-
name: info.name,
|
|
101967
|
-
description: info.description,
|
|
101968
|
-
provider: info.provider,
|
|
101969
|
-
priority: info.priority
|
|
101970
|
-
};
|
|
101971
|
-
})
|
|
101972
|
-
};
|
|
101973
|
-
console.log(JSON.stringify(output, null, 2));
|
|
101834
|
+
console.error(`Failed to load available models: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
101974
101835
|
}
|
|
101975
101836
|
}
|
|
101976
|
-
|
|
101977
|
-
try {
|
|
101978
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
101979
|
-
signal: AbortSignal.timeout(1e4)
|
|
101980
|
-
});
|
|
101981
|
-
if (!response.ok) {
|
|
101982
|
-
return [];
|
|
101983
|
-
}
|
|
101984
|
-
const data = await response.json();
|
|
101985
|
-
const opencode = data.opencode;
|
|
101986
|
-
if (!opencode?.models)
|
|
101987
|
-
return [];
|
|
101988
|
-
return Object.entries(opencode.models).map(([id, m2]) => {
|
|
101989
|
-
const isFree = m2.cost?.input === 0 && m2.cost?.output === 0;
|
|
101990
|
-
return {
|
|
101991
|
-
id: `zen/${id}`,
|
|
101992
|
-
name: m2.name || id,
|
|
101993
|
-
context_length: m2.limit?.context || 128000,
|
|
101994
|
-
max_output: m2.limit?.output || 32000,
|
|
101995
|
-
pricing: isFree ? { prompt: "0", completion: "0" } : { prompt: String(m2.cost?.input || 0), completion: String(m2.cost?.output || 0) },
|
|
101996
|
-
isZen: true,
|
|
101997
|
-
isFree,
|
|
101998
|
-
supportsTools: m2.tool_call || false,
|
|
101999
|
-
supportsReasoning: m2.reasoning || false
|
|
102000
|
-
};
|
|
102001
|
-
});
|
|
102002
|
-
} catch {
|
|
102003
|
-
return [];
|
|
102004
|
-
}
|
|
102005
|
-
}
|
|
102006
|
-
async function fetchGLMCodingModels() {
|
|
102007
|
-
try {
|
|
102008
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102009
|
-
signal: AbortSignal.timeout(5000)
|
|
102010
|
-
});
|
|
102011
|
-
if (!response.ok) {
|
|
102012
|
-
return [];
|
|
102013
|
-
}
|
|
102014
|
-
const data = await response.json();
|
|
102015
|
-
const codingPlan = data["zai-coding-plan"];
|
|
102016
|
-
if (!codingPlan?.models)
|
|
102017
|
-
return [];
|
|
102018
|
-
return Object.entries(codingPlan.models).map(([id, m2]) => {
|
|
102019
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102020
|
-
return {
|
|
102021
|
-
id: `gc/${id}`,
|
|
102022
|
-
name: m2.name || id,
|
|
102023
|
-
description: `GLM Coding Plan model (subscription)`,
|
|
102024
|
-
context_length: m2.limit?.context || 131072,
|
|
102025
|
-
pricing: { prompt: "0", completion: "0" },
|
|
102026
|
-
isGLMCoding: true,
|
|
102027
|
-
isSubscription: true,
|
|
102028
|
-
supportsTools: m2.tool_call || false,
|
|
102029
|
-
supportsReasoning: m2.reasoning || false,
|
|
102030
|
-
supportsVision: inputModalities.includes("image") || inputModalities.includes("video")
|
|
102031
|
-
};
|
|
102032
|
-
});
|
|
102033
|
-
} catch {
|
|
102034
|
-
return [];
|
|
102035
|
-
}
|
|
102036
|
-
}
|
|
102037
|
-
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;
|
|
102038
101838
|
var init_cli = __esm(async () => {
|
|
102039
101839
|
init_config();
|
|
102040
101840
|
init_model_loader();
|
|
101841
|
+
init_provider_definitions();
|
|
102041
101842
|
init_profile_config();
|
|
102042
101843
|
init_model_parser();
|
|
102043
101844
|
init_auto_route();
|
|
@@ -102050,10 +101851,6 @@ var init_cli = __esm(async () => {
|
|
|
102050
101851
|
await init_probe_tui_runtime();
|
|
102051
101852
|
__filename4 = fileURLToPath4(import.meta.url);
|
|
102052
101853
|
__dirname4 = dirname5(__filename4);
|
|
102053
|
-
CLAUDISH_CACHE_DIR2 = join25(homedir23(), ".claudish");
|
|
102054
|
-
BUNDLED_MODELS_PATH = join25(__dirname4, "../recommended-models.json");
|
|
102055
|
-
CACHED_MODELS_PATH = join25(CLAUDISH_CACHE_DIR2, "recommended-models.json");
|
|
102056
|
-
ALL_MODELS_JSON_PATH = join25(CLAUDISH_CACHE_DIR2, "all-models.json");
|
|
102057
101854
|
});
|
|
102058
101855
|
|
|
102059
101856
|
// src/update-checker.ts
|
|
@@ -102064,7 +101861,7 @@ __export(exports_update_checker, {
|
|
|
102064
101861
|
clearCache: () => clearCache,
|
|
102065
101862
|
checkForUpdates: () => checkForUpdates
|
|
102066
101863
|
});
|
|
102067
|
-
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";
|
|
102068
101865
|
import { homedir as homedir24, platform as platform2, tmpdir } from "os";
|
|
102069
101866
|
import { join as join26 } from "path";
|
|
102070
101867
|
function getCacheFilePath() {
|
|
@@ -102077,7 +101874,7 @@ function getCacheFilePath() {
|
|
|
102077
101874
|
}
|
|
102078
101875
|
try {
|
|
102079
101876
|
if (!existsSync26(cacheDir)) {
|
|
102080
|
-
|
|
101877
|
+
mkdirSync11(cacheDir, { recursive: true });
|
|
102081
101878
|
}
|
|
102082
101879
|
return join26(cacheDir, "update-check.json");
|
|
102083
101880
|
} catch {
|
|
@@ -102103,7 +101900,7 @@ function writeCache(latestVersion) {
|
|
|
102103
101900
|
lastCheck: Date.now(),
|
|
102104
101901
|
latestVersion
|
|
102105
101902
|
};
|
|
102106
|
-
|
|
101903
|
+
writeFileSync12(cachePath, JSON.stringify(data), "utf-8");
|
|
102107
101904
|
} catch {}
|
|
102108
101905
|
}
|
|
102109
101906
|
function isCacheValid(cache) {
|
|
@@ -102414,218 +102211,36 @@ __export(exports_model_selector, {
|
|
|
102414
102211
|
promptForApiKey: () => promptForApiKey,
|
|
102415
102212
|
confirmAction: () => confirmAction
|
|
102416
102213
|
});
|
|
102417
|
-
|
|
102418
|
-
import { join as join27, dirname as dirname6 } from "path";
|
|
102419
|
-
import { homedir as homedir25 } from "os";
|
|
102420
|
-
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
102421
|
-
function loadRecommendedModels2() {
|
|
102422
|
-
const cachedPath = join27(CLAUDISH_CACHE_DIR3, "recommended-models-cache.json");
|
|
102423
|
-
if (existsSync27(cachedPath)) {
|
|
102424
|
-
try {
|
|
102425
|
-
const data = JSON.parse(readFileSync22(cachedPath, "utf-8"));
|
|
102426
|
-
if (data.models && data.models.length > 0) {
|
|
102427
|
-
return data.models.map((model) => ({
|
|
102428
|
-
...model,
|
|
102429
|
-
source: "Recommended"
|
|
102430
|
-
}));
|
|
102431
|
-
}
|
|
102432
|
-
} catch {}
|
|
102433
|
-
}
|
|
102434
|
-
if (existsSync27(RECOMMENDED_MODELS_JSON_PATH)) {
|
|
102435
|
-
try {
|
|
102436
|
-
const content = readFileSync22(RECOMMENDED_MODELS_JSON_PATH, "utf-8");
|
|
102437
|
-
const data = JSON.parse(content);
|
|
102438
|
-
return (data.models || []).map((model) => ({
|
|
102439
|
-
...model,
|
|
102440
|
-
source: "Recommended"
|
|
102441
|
-
}));
|
|
102442
|
-
} catch {
|
|
102443
|
-
return [];
|
|
102444
|
-
}
|
|
102445
|
-
}
|
|
102446
|
-
return [];
|
|
102447
|
-
}
|
|
102448
|
-
async function fetchAllModels(forceUpdate = false) {
|
|
102449
|
-
if (!forceUpdate && existsSync27(ALL_MODELS_JSON_PATH2)) {
|
|
102450
|
-
try {
|
|
102451
|
-
const cacheData = JSON.parse(readFileSync22(ALL_MODELS_JSON_PATH2, "utf-8"));
|
|
102452
|
-
const lastUpdated = new Date(cacheData.lastUpdated);
|
|
102453
|
-
const now = new Date;
|
|
102454
|
-
const ageInDays = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24);
|
|
102455
|
-
if (ageInDays <= CACHE_MAX_AGE_DAYS3) {
|
|
102456
|
-
return cacheData.models;
|
|
102457
|
-
}
|
|
102458
|
-
} catch {}
|
|
102459
|
-
}
|
|
102460
|
-
console.log("Fetching models from OpenRouter...");
|
|
102461
|
-
try {
|
|
102462
|
-
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
102463
|
-
if (!response.ok)
|
|
102464
|
-
throw new Error(`API returned ${response.status}`);
|
|
102465
|
-
const data = await response.json();
|
|
102466
|
-
const models = data.data;
|
|
102467
|
-
mkdirSync13(CLAUDISH_CACHE_DIR3, { recursive: true });
|
|
102468
|
-
writeFileSync15(ALL_MODELS_JSON_PATH2, JSON.stringify({
|
|
102469
|
-
lastUpdated: new Date().toISOString(),
|
|
102470
|
-
models
|
|
102471
|
-
}), "utf-8");
|
|
102472
|
-
console.log(`Cached ${models.length} models`);
|
|
102473
|
-
return models;
|
|
102474
|
-
} catch (error2) {
|
|
102475
|
-
console.error(`Failed to fetch models: ${error2}`);
|
|
102476
|
-
return [];
|
|
102477
|
-
}
|
|
102478
|
-
}
|
|
102479
|
-
function toModelInfo(model) {
|
|
102480
|
-
const provider = model.id.split("/")[0];
|
|
102481
|
-
const contextLen = model.context_length || model.top_provider?.context_length || 0;
|
|
102482
|
-
const promptPrice = parseFloat(model.pricing?.prompt || "0");
|
|
102483
|
-
const completionPrice = parseFloat(model.pricing?.completion || "0");
|
|
102484
|
-
const isFree = promptPrice === 0 && completionPrice === 0;
|
|
102485
|
-
let pricingStr = "N/A";
|
|
102486
|
-
if (isFree) {
|
|
102487
|
-
pricingStr = "FREE";
|
|
102488
|
-
} else if (model.pricing) {
|
|
102489
|
-
const avgPrice = (promptPrice + completionPrice) / 2;
|
|
102490
|
-
if (avgPrice < 0.001) {
|
|
102491
|
-
pricingStr = `$${(avgPrice * 1e6).toFixed(2)}/1M`;
|
|
102492
|
-
} else {
|
|
102493
|
-
pricingStr = `$${avgPrice.toFixed(4)}/1K`;
|
|
102494
|
-
}
|
|
102495
|
-
}
|
|
102496
|
-
return {
|
|
102497
|
-
id: `openrouter@${model.id}`,
|
|
102498
|
-
name: model.name || model.id,
|
|
102499
|
-
description: model.description || "",
|
|
102500
|
-
provider: provider.charAt(0).toUpperCase() + provider.slice(1),
|
|
102501
|
-
pricing: {
|
|
102502
|
-
input: model.pricing?.prompt || "N/A",
|
|
102503
|
-
output: model.pricing?.completion || "N/A",
|
|
102504
|
-
average: pricingStr
|
|
102505
|
-
},
|
|
102506
|
-
context: contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A",
|
|
102507
|
-
contextLength: contextLen,
|
|
102508
|
-
supportsTools: (model.supported_parameters || []).includes("tools"),
|
|
102509
|
-
supportsReasoning: (model.supported_parameters || []).includes("reasoning"),
|
|
102510
|
-
supportsVision: (model.architecture?.input_modalities || []).includes("image"),
|
|
102511
|
-
isFree,
|
|
102512
|
-
source: "OpenRouter"
|
|
102513
|
-
};
|
|
102514
|
-
}
|
|
102515
|
-
async function fetchZenGoModels() {
|
|
102516
|
-
const apiKey = process.env.OPENCODE_API_KEY;
|
|
102517
|
-
if (!apiKey)
|
|
102518
|
-
return [];
|
|
102519
|
-
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() {
|
|
102520
102215
|
try {
|
|
102521
|
-
const
|
|
102522
|
-
|
|
102523
|
-
|
|
102524
|
-
|
|
102525
|
-
|
|
102526
|
-
|
|
102527
|
-
|
|
102528
|
-
|
|
102529
|
-
|
|
102530
|
-
|
|
102531
|
-
|
|
102532
|
-
|
|
102533
|
-
|
|
102534
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
102535
|
-
body: JSON.stringify({
|
|
102536
|
-
model: modelId,
|
|
102537
|
-
messages: [{ role: "user", content: "hi" }],
|
|
102538
|
-
max_tokens: 1
|
|
102539
|
-
}),
|
|
102540
|
-
signal: AbortSignal.timeout(8000)
|
|
102541
|
-
});
|
|
102542
|
-
if (!r.ok)
|
|
102543
|
-
return null;
|
|
102544
|
-
const body = await r.json().catch(() => ({}));
|
|
102545
|
-
return Array.isArray(body?.choices) && body.choices.length > 0 ? modelId : null;
|
|
102546
|
-
} catch {
|
|
102547
|
-
return null;
|
|
102548
|
-
}
|
|
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"
|
|
102549
102229
|
}));
|
|
102550
|
-
const discoveredIds = probeResults.filter(Boolean);
|
|
102551
|
-
const goModelIds = discoveredIds.length > 0 ? discoveredIds : fallbackIds;
|
|
102552
|
-
return goModelIds.map((id) => {
|
|
102553
|
-
const m2 = ocModels[id];
|
|
102554
|
-
if (!m2)
|
|
102555
|
-
return null;
|
|
102556
|
-
const inputModalities = m2.modalities?.input ?? [];
|
|
102557
|
-
return {
|
|
102558
|
-
id: `zgo@${id}`,
|
|
102559
|
-
name: m2.name || id,
|
|
102560
|
-
description: `OpenCode Zen Go plan model`,
|
|
102561
|
-
provider: "Zen Go",
|
|
102562
|
-
pricing: { input: "PLAN", output: "PLAN", average: "PLAN" },
|
|
102563
|
-
context: m2.limit?.context ? `${Math.round(m2.limit.context / 1000)}K` : "128K",
|
|
102564
|
-
contextLength: m2.limit?.context || 128000,
|
|
102565
|
-
supportsTools: m2.tool_call === true,
|
|
102566
|
-
supportsReasoning: m2.reasoning || false,
|
|
102567
|
-
supportsVision: inputModalities.includes("image") || inputModalities.includes("video"),
|
|
102568
|
-
isFree: false,
|
|
102569
|
-
source: "Zen"
|
|
102570
|
-
};
|
|
102571
|
-
}).filter(Boolean);
|
|
102572
102230
|
} catch {
|
|
102573
102231
|
return [];
|
|
102574
102232
|
}
|
|
102575
102233
|
}
|
|
102576
|
-
|
|
102577
|
-
|
|
102578
|
-
|
|
102579
|
-
|
|
102580
|
-
|
|
102581
|
-
|
|
102582
|
-
|
|
102583
|
-
|
|
102584
|
-
|
|
102585
|
-
|
|
102586
|
-
]);
|
|
102587
|
-
if (!mdevResp.ok)
|
|
102588
|
-
return [];
|
|
102589
|
-
const mdevData = await mdevResp.json();
|
|
102590
|
-
const opencode = mdevData.opencode;
|
|
102591
|
-
if (!opencode?.models)
|
|
102592
|
-
return [];
|
|
102593
|
-
const liveIds = new Set;
|
|
102594
|
-
if (liveResp.ok) {
|
|
102595
|
-
const liveData = await liveResp.json();
|
|
102596
|
-
for (const m2 of liveData.data ?? [])
|
|
102597
|
-
liveIds.add(m2.id);
|
|
102598
|
-
}
|
|
102599
|
-
return Object.entries(opencode.models).filter(([id, m2]) => {
|
|
102600
|
-
const isFree = m2.cost?.input === 0 && m2.cost?.output === 0;
|
|
102601
|
-
const supportsTools = m2.tool_call === true;
|
|
102602
|
-
const isLive = liveIds.size === 0 || liveIds.has(id);
|
|
102603
|
-
return isFree && supportsTools && isLive;
|
|
102604
|
-
}).map(([id, m2]) => {
|
|
102605
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102606
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102607
|
-
return {
|
|
102608
|
-
id: `zen@${id}`,
|
|
102609
|
-
name: m2.name || id,
|
|
102610
|
-
description: `OpenCode Zen free model`,
|
|
102611
|
-
provider: "Zen",
|
|
102612
|
-
pricing: {
|
|
102613
|
-
input: "FREE",
|
|
102614
|
-
output: "FREE",
|
|
102615
|
-
average: "FREE"
|
|
102616
|
-
},
|
|
102617
|
-
context: m2.limit?.context ? `${Math.round(m2.limit.context / 1000)}K` : "128K",
|
|
102618
|
-
contextLength: m2.limit?.context || 128000,
|
|
102619
|
-
supportsTools: true,
|
|
102620
|
-
supportsReasoning: m2.reasoning || false,
|
|
102621
|
-
supportsVision,
|
|
102622
|
-
isFree: true,
|
|
102623
|
-
source: "Zen"
|
|
102624
|
-
};
|
|
102625
|
-
});
|
|
102626
|
-
} catch {
|
|
102627
|
-
return [];
|
|
102628
|
-
}
|
|
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;
|
|
102629
102244
|
}
|
|
102630
102245
|
function getXAIContextWindow(modelId) {
|
|
102631
102246
|
const id = modelId.toLowerCase();
|
|
@@ -102776,239 +102391,15 @@ async function fetchGeminiModels() {
|
|
|
102776
102391
|
return [];
|
|
102777
102392
|
}
|
|
102778
102393
|
}
|
|
102779
|
-
async function fetchOpenAIModels() {
|
|
102780
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
102781
|
-
if (!apiKey) {
|
|
102782
|
-
return [];
|
|
102783
|
-
}
|
|
102784
|
-
try {
|
|
102785
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102786
|
-
signal: AbortSignal.timeout(5000)
|
|
102787
|
-
});
|
|
102788
|
-
if (!response.ok) {
|
|
102789
|
-
return [];
|
|
102790
|
-
}
|
|
102791
|
-
const data = await response.json();
|
|
102792
|
-
const openaiData = data.openai;
|
|
102793
|
-
if (!openaiData?.models)
|
|
102794
|
-
return [];
|
|
102795
|
-
return Object.entries(openaiData.models).filter(([id, _2]) => {
|
|
102796
|
-
const lowerId = id.toLowerCase();
|
|
102797
|
-
return lowerId.startsWith("gpt-") || lowerId.startsWith("o1-") || lowerId.startsWith("o3-") || lowerId.startsWith("o4-") || lowerId.startsWith("chatgpt-");
|
|
102798
|
-
}).map(([id, m2]) => {
|
|
102799
|
-
const inputCost = m2.cost?.input || 2;
|
|
102800
|
-
const outputCost = m2.cost?.output || 8;
|
|
102801
|
-
const avgCost = (inputCost + outputCost) / 2;
|
|
102802
|
-
const contextLength = m2.limit?.context || 128000;
|
|
102803
|
-
const contextStr = contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`;
|
|
102804
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102805
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102806
|
-
return {
|
|
102807
|
-
id: `oai@${id}`,
|
|
102808
|
-
name: m2.name || id,
|
|
102809
|
-
description: `OpenAI model`,
|
|
102810
|
-
provider: "OpenAI",
|
|
102811
|
-
pricing: {
|
|
102812
|
-
input: `$${inputCost.toFixed(2)}`,
|
|
102813
|
-
output: `$${outputCost.toFixed(2)}`,
|
|
102814
|
-
average: `$${avgCost.toFixed(2)}/1M`
|
|
102815
|
-
},
|
|
102816
|
-
context: contextStr,
|
|
102817
|
-
contextLength,
|
|
102818
|
-
supportsTools: m2.tool_call === true,
|
|
102819
|
-
supportsReasoning: m2.reasoning === true,
|
|
102820
|
-
supportsVision,
|
|
102821
|
-
isFree: false,
|
|
102822
|
-
source: "OpenAI"
|
|
102823
|
-
};
|
|
102824
|
-
});
|
|
102825
|
-
} catch {
|
|
102826
|
-
return [];
|
|
102827
|
-
}
|
|
102828
|
-
}
|
|
102829
|
-
async function fetchGLMCodingModels2() {
|
|
102830
|
-
const apiKey = process.env.GLM_CODING_API_KEY;
|
|
102831
|
-
if (!apiKey) {
|
|
102832
|
-
return [];
|
|
102833
|
-
}
|
|
102834
|
-
try {
|
|
102835
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102836
|
-
signal: AbortSignal.timeout(5000)
|
|
102837
|
-
});
|
|
102838
|
-
if (!response.ok) {
|
|
102839
|
-
return [];
|
|
102840
|
-
}
|
|
102841
|
-
const data = await response.json();
|
|
102842
|
-
const codingPlan = data["zai-coding-plan"];
|
|
102843
|
-
if (!codingPlan?.models)
|
|
102844
|
-
return [];
|
|
102845
|
-
return Object.entries(codingPlan.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
|
|
102846
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102847
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102848
|
-
const contextLength = m2.limit?.context || 131072;
|
|
102849
|
-
return {
|
|
102850
|
-
id: `gc@${id}`,
|
|
102851
|
-
name: m2.name || id,
|
|
102852
|
-
description: `GLM Coding Plan (subscription)`,
|
|
102853
|
-
provider: "GLM Coding",
|
|
102854
|
-
pricing: {
|
|
102855
|
-
input: "SUB",
|
|
102856
|
-
output: "SUB",
|
|
102857
|
-
average: "SUB"
|
|
102858
|
-
},
|
|
102859
|
-
context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
|
|
102860
|
-
contextLength,
|
|
102861
|
-
supportsTools: true,
|
|
102862
|
-
supportsReasoning: m2.reasoning || false,
|
|
102863
|
-
supportsVision,
|
|
102864
|
-
isFree: false,
|
|
102865
|
-
source: "GLM Coding"
|
|
102866
|
-
};
|
|
102867
|
-
});
|
|
102868
|
-
} catch {
|
|
102869
|
-
return [];
|
|
102870
|
-
}
|
|
102871
|
-
}
|
|
102872
|
-
async function fetchGLMDirectModels() {
|
|
102873
|
-
try {
|
|
102874
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102875
|
-
signal: AbortSignal.timeout(5000)
|
|
102876
|
-
});
|
|
102877
|
-
if (!response.ok) {
|
|
102878
|
-
return [];
|
|
102879
|
-
}
|
|
102880
|
-
const data = await response.json();
|
|
102881
|
-
const codingPlan = data["zai-coding-plan"];
|
|
102882
|
-
if (!codingPlan?.models)
|
|
102883
|
-
return [];
|
|
102884
|
-
return Object.entries(codingPlan.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
|
|
102885
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102886
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102887
|
-
const contextLength = m2.limit?.context || 131072;
|
|
102888
|
-
const inputCost = m2.cost?.input || 0;
|
|
102889
|
-
const outputCost = m2.cost?.output || 0;
|
|
102890
|
-
const isFree = inputCost === 0 && outputCost === 0;
|
|
102891
|
-
return {
|
|
102892
|
-
id: `glm@${id}`,
|
|
102893
|
-
name: m2.name || id,
|
|
102894
|
-
description: `GLM/Zhipu direct API`,
|
|
102895
|
-
provider: "GLM",
|
|
102896
|
-
pricing: {
|
|
102897
|
-
input: isFree ? "FREE" : `$${inputCost.toFixed(2)}`,
|
|
102898
|
-
output: isFree ? "FREE" : `$${outputCost.toFixed(2)}`,
|
|
102899
|
-
average: isFree ? "FREE" : `$${((inputCost + outputCost) / 2).toFixed(2)}/1M`
|
|
102900
|
-
},
|
|
102901
|
-
context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
|
|
102902
|
-
contextLength,
|
|
102903
|
-
supportsTools: true,
|
|
102904
|
-
supportsReasoning: m2.reasoning || false,
|
|
102905
|
-
supportsVision,
|
|
102906
|
-
isFree,
|
|
102907
|
-
source: "GLM"
|
|
102908
|
-
};
|
|
102909
|
-
});
|
|
102910
|
-
} catch {
|
|
102911
|
-
return [];
|
|
102912
|
-
}
|
|
102913
|
-
}
|
|
102914
|
-
async function fetchOllamaCloudModels() {
|
|
102915
|
-
try {
|
|
102916
|
-
const response = await fetch("https://models.dev/api.json", {
|
|
102917
|
-
signal: AbortSignal.timeout(5000)
|
|
102918
|
-
});
|
|
102919
|
-
if (!response.ok) {
|
|
102920
|
-
return [];
|
|
102921
|
-
}
|
|
102922
|
-
const data = await response.json();
|
|
102923
|
-
const ollamaCloud = data["ollama-cloud"];
|
|
102924
|
-
if (!ollamaCloud?.models)
|
|
102925
|
-
return [];
|
|
102926
|
-
return Object.entries(ollamaCloud.models).filter(([_2, m2]) => m2.tool_call === true).map(([id, m2]) => {
|
|
102927
|
-
const inputModalities = m2.modalities?.input || [];
|
|
102928
|
-
const supportsVision = inputModalities.includes("image") || inputModalities.includes("video");
|
|
102929
|
-
const contextLength = m2.limit?.context || 131072;
|
|
102930
|
-
return {
|
|
102931
|
-
id: `oc@${id}`,
|
|
102932
|
-
name: m2.name || id,
|
|
102933
|
-
description: `OllamaCloud`,
|
|
102934
|
-
provider: "OllamaCloud",
|
|
102935
|
-
pricing: {
|
|
102936
|
-
input: "N/A",
|
|
102937
|
-
output: "N/A",
|
|
102938
|
-
average: "N/A"
|
|
102939
|
-
},
|
|
102940
|
-
context: contextLength >= 1e6 ? `${Math.round(contextLength / 1e6)}M` : `${Math.round(contextLength / 1000)}K`,
|
|
102941
|
-
contextLength,
|
|
102942
|
-
supportsTools: true,
|
|
102943
|
-
supportsReasoning: m2.reasoning || false,
|
|
102944
|
-
supportsVision,
|
|
102945
|
-
source: "OllamaCloud"
|
|
102946
|
-
};
|
|
102947
|
-
});
|
|
102948
|
-
} catch {
|
|
102949
|
-
return [];
|
|
102950
|
-
}
|
|
102951
|
-
}
|
|
102952
|
-
function shouldRefreshForFreeModels() {
|
|
102953
|
-
if (!existsSync27(ALL_MODELS_JSON_PATH2)) {
|
|
102954
|
-
return true;
|
|
102955
|
-
}
|
|
102956
|
-
try {
|
|
102957
|
-
const cacheData = JSON.parse(readFileSync22(ALL_MODELS_JSON_PATH2, "utf-8"));
|
|
102958
|
-
const lastUpdated = new Date(cacheData.lastUpdated);
|
|
102959
|
-
const now = new Date;
|
|
102960
|
-
const ageInHours = (now.getTime() - lastUpdated.getTime()) / (1000 * 60 * 60);
|
|
102961
|
-
return ageInHours > FREE_MODELS_CACHE_MAX_AGE_HOURS;
|
|
102962
|
-
} catch {
|
|
102963
|
-
return true;
|
|
102964
|
-
}
|
|
102965
|
-
}
|
|
102966
102394
|
async function getFreeModels() {
|
|
102967
|
-
|
|
102968
|
-
const [allModels, zenModels] = await Promise.all([
|
|
102969
|
-
fetchAllModels(forceUpdate),
|
|
102970
|
-
fetchZenFreeModels()
|
|
102971
|
-
]);
|
|
102972
|
-
const openRouterFreeModels = allModels.filter((model) => {
|
|
102973
|
-
if (!model.id?.endsWith(":free"))
|
|
102974
|
-
return false;
|
|
102975
|
-
const supportsTools = (model.supported_parameters || []).includes("tools");
|
|
102976
|
-
if (!supportsTools)
|
|
102977
|
-
return false;
|
|
102978
|
-
return true;
|
|
102979
|
-
});
|
|
102980
|
-
openRouterFreeModels.sort((a, b2) => {
|
|
102981
|
-
const contextA = a.context_length || a.top_provider?.context_length || 0;
|
|
102982
|
-
const contextB = b2.context_length || b2.top_provider?.context_length || 0;
|
|
102983
|
-
return contextB - contextA;
|
|
102984
|
-
});
|
|
102985
|
-
const openRouterModels = openRouterFreeModels.slice(0, 20).map(toModelInfo);
|
|
102986
|
-
const combined = [...zenModels, ...openRouterModels];
|
|
102987
|
-
combined.sort((a, b2) => {
|
|
102988
|
-
if (a.source === "Zen" && b2.source !== "Zen")
|
|
102989
|
-
return -1;
|
|
102990
|
-
if (a.source !== "Zen" && b2.source === "Zen")
|
|
102991
|
-
return 1;
|
|
102992
|
-
return (b2.contextLength || 0) - (a.contextLength || 0);
|
|
102993
|
-
});
|
|
102994
|
-
return combined;
|
|
102395
|
+
return [];
|
|
102995
102396
|
}
|
|
102996
102397
|
async function getAllModelsForSearch(forceUpdate = false) {
|
|
102997
102398
|
const litellmBaseUrl = process.env.LITELLM_BASE_URL;
|
|
102998
102399
|
const litellmApiKey = process.env.LITELLM_API_KEY;
|
|
102999
102400
|
const allEntries = [
|
|
103000
|
-
{
|
|
103001
|
-
name: "OpenRouter",
|
|
103002
|
-
promise: () => fetchAllModels(forceUpdate).then((models) => models.map(toModelInfo))
|
|
103003
|
-
},
|
|
103004
102401
|
{ name: "xAI", provider: "xai", promise: () => fetchXAIModels() },
|
|
103005
102402
|
{ name: "Gemini", provider: "google", promise: () => fetchGeminiModels() },
|
|
103006
|
-
{ name: "OpenAI", provider: "openai", promise: () => fetchOpenAIModels() },
|
|
103007
|
-
{ name: "GLM", provider: "glm", promise: () => fetchGLMDirectModels() },
|
|
103008
|
-
{ name: "GLM Coding", provider: "glm-coding", promise: () => fetchGLMCodingModels2() },
|
|
103009
|
-
{ name: "OllamaCloud", provider: "ollamacloud", promise: () => fetchOllamaCloudModels() },
|
|
103010
|
-
{ name: "Zen", provider: "opencode-zen", promise: () => fetchZenFreeModels() },
|
|
103011
|
-
{ name: "Zen Go", provider: "opencode-zen-go", promise: () => fetchZenGoModels() },
|
|
103012
102403
|
{
|
|
103013
102404
|
name: "MiniMax",
|
|
103014
102405
|
provider: "minimax",
|
|
@@ -103053,22 +102444,15 @@ async function getAllModelsForSearch(forceUpdate = false) {
|
|
|
103053
102444
|
}
|
|
103054
102445
|
const r = (name) => fetchResults[name] || [];
|
|
103055
102446
|
const allModels = [
|
|
103056
|
-
...r("Zen"),
|
|
103057
|
-
...r("Zen Go"),
|
|
103058
|
-
...r("OllamaCloud"),
|
|
103059
102447
|
...r("xAI"),
|
|
103060
102448
|
...r("Gemini"),
|
|
103061
|
-
...r("OpenAI"),
|
|
103062
102449
|
...r("OpenAI Codex"),
|
|
103063
|
-
...r("GLM"),
|
|
103064
|
-
...r("GLM Coding"),
|
|
103065
102450
|
...r("MiniMax"),
|
|
103066
102451
|
...r("MiniMax Coding"),
|
|
103067
102452
|
...r("Kimi"),
|
|
103068
102453
|
...r("Kimi Coding"),
|
|
103069
102454
|
...r("Z.AI"),
|
|
103070
|
-
...r("LiteLLM")
|
|
103071
|
-
...r("OpenRouter")
|
|
102455
|
+
...r("LiteLLM")
|
|
103072
102456
|
];
|
|
103073
102457
|
return allModels;
|
|
103074
102458
|
}
|
|
@@ -103164,23 +102548,17 @@ async function selectModel(options = {}) {
|
|
|
103164
102548
|
} else {
|
|
103165
102549
|
const [allModels, recommendedModels] = await Promise.all([
|
|
103166
102550
|
getAllModelsForSearch(forceUpdate),
|
|
103167
|
-
Promise.resolve(recommended ?
|
|
102551
|
+
Promise.resolve(recommended ? loadRecommendedModels() : [])
|
|
103168
102552
|
]);
|
|
103169
102553
|
const seenIds = new Set;
|
|
103170
102554
|
models = [];
|
|
103171
|
-
for (const m2 of allModels.filter((m3) => m3.source === "Zen")) {
|
|
103172
|
-
if (!seenIds.has(m2.id)) {
|
|
103173
|
-
seenIds.add(m2.id);
|
|
103174
|
-
models.push(m2);
|
|
103175
|
-
}
|
|
103176
|
-
}
|
|
103177
102555
|
for (const m2 of recommendedModels) {
|
|
103178
102556
|
if (!seenIds.has(m2.id)) {
|
|
103179
102557
|
seenIds.add(m2.id);
|
|
103180
102558
|
models.push(m2);
|
|
103181
102559
|
}
|
|
103182
102560
|
}
|
|
103183
|
-
for (const m2 of allModels.filter((m3) => m3.source && m3.source !== "
|
|
102561
|
+
for (const m2 of allModels.filter((m3) => m3.source && m3.source !== "OpenRouter")) {
|
|
103184
102562
|
if (!seenIds.has(m2.id)) {
|
|
103185
102563
|
seenIds.add(m2.id);
|
|
103186
102564
|
models.push(m2);
|
|
@@ -103550,7 +102928,7 @@ async function selectModelsForProfile() {
|
|
|
103550
102928
|
Loading available models...`);
|
|
103551
102929
|
const [fetchedModels, recommendedModels] = await Promise.all([
|
|
103552
102930
|
getAllModelsForSearch(),
|
|
103553
|
-
Promise.resolve(
|
|
102931
|
+
Promise.resolve(loadRecommendedModels())
|
|
103554
102932
|
]);
|
|
103555
102933
|
const tiers = [
|
|
103556
102934
|
{ key: "opus", name: "Opus", description: "Most capable, used for complex reasoning" },
|
|
@@ -103644,16 +103022,11 @@ async function selectProfile(profiles) {
|
|
|
103644
103022
|
async function confirmAction(message) {
|
|
103645
103023
|
return dist_default4({ message, default: false });
|
|
103646
103024
|
}
|
|
103647
|
-
var
|
|
103025
|
+
var PROVIDER_FILTER_ALIASES, ALL_PROVIDER_CHOICES, PROVIDER_MODEL_PREFIX, PROVIDER_SOURCE_FILTER;
|
|
103648
103026
|
var init_model_selector = __esm(() => {
|
|
103649
103027
|
init_dist17();
|
|
103650
103028
|
init_model_loader();
|
|
103651
103029
|
init_provider_definitions();
|
|
103652
|
-
__filename5 = fileURLToPath5(import.meta.url);
|
|
103653
|
-
__dirname5 = dirname6(__filename5);
|
|
103654
|
-
CLAUDISH_CACHE_DIR3 = join27(homedir25(), ".claudish");
|
|
103655
|
-
ALL_MODELS_JSON_PATH2 = join27(CLAUDISH_CACHE_DIR3, "all-models.json");
|
|
103656
|
-
RECOMMENDED_MODELS_JSON_PATH = join27(__dirname5, "../recommended-models.json");
|
|
103657
103030
|
PROVIDER_FILTER_ALIASES = {
|
|
103658
103031
|
zen: "Zen",
|
|
103659
103032
|
openrouter: "OpenRouter",
|
|
@@ -107310,9 +106683,9 @@ __export(exports_claude_runner, {
|
|
|
107310
106683
|
checkClaudeInstalled: () => checkClaudeInstalled
|
|
107311
106684
|
});
|
|
107312
106685
|
import { spawn as spawn4 } from "child_process";
|
|
107313
|
-
import { writeFileSync as
|
|
107314
|
-
import { tmpdir as tmpdir2, homedir as
|
|
107315
|
-
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";
|
|
107316
106689
|
function hasNativeAnthropicMapping(config3) {
|
|
107317
106690
|
const models = [
|
|
107318
106691
|
config3.model,
|
|
@@ -107328,9 +106701,9 @@ function isWindows2() {
|
|
|
107328
106701
|
}
|
|
107329
106702
|
function createStatusLineScript(tokenFilePath) {
|
|
107330
106703
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
|
|
107331
|
-
const claudishDir =
|
|
106704
|
+
const claudishDir = join27(homeDir, ".claudish");
|
|
107332
106705
|
const timestamp = Date.now();
|
|
107333
|
-
const scriptPath =
|
|
106706
|
+
const scriptPath = join27(claudishDir, `status-${timestamp}.js`);
|
|
107334
106707
|
const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
|
|
107335
106708
|
const script = `
|
|
107336
106709
|
const fs = require('fs');
|
|
@@ -107422,18 +106795,18 @@ process.stdin.on('end', () => {
|
|
|
107422
106795
|
}
|
|
107423
106796
|
});
|
|
107424
106797
|
`;
|
|
107425
|
-
|
|
106798
|
+
writeFileSync13(scriptPath, script, "utf-8");
|
|
107426
106799
|
return scriptPath;
|
|
107427
106800
|
}
|
|
107428
106801
|
function createTempSettingsFile(modelDisplay, port) {
|
|
107429
106802
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
|
|
107430
|
-
const claudishDir =
|
|
106803
|
+
const claudishDir = join27(homeDir, ".claudish");
|
|
107431
106804
|
try {
|
|
107432
|
-
|
|
106805
|
+
mkdirSync12(claudishDir, { recursive: true });
|
|
107433
106806
|
} catch {}
|
|
107434
106807
|
const timestamp = Date.now();
|
|
107435
|
-
const tempPath =
|
|
107436
|
-
const tokenFilePath =
|
|
106808
|
+
const tempPath = join27(claudishDir, `settings-${timestamp}.json`);
|
|
106809
|
+
const tokenFilePath = join27(claudishDir, `tokens-${port}.json`);
|
|
107437
106810
|
let statusCommand;
|
|
107438
106811
|
if (isWindows2()) {
|
|
107439
106812
|
const scriptPath = createStatusLineScript(tokenFilePath);
|
|
@@ -107455,7 +106828,7 @@ function createTempSettingsFile(modelDisplay, port) {
|
|
|
107455
106828
|
padding: 0
|
|
107456
106829
|
};
|
|
107457
106830
|
const settings = { statusLine };
|
|
107458
|
-
|
|
106831
|
+
writeFileSync13(tempPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
107459
106832
|
return { path: tempPath, statusLine };
|
|
107460
106833
|
}
|
|
107461
106834
|
function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine) {
|
|
@@ -107469,11 +106842,11 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine) {
|
|
|
107469
106842
|
if (userSettingsValue.trimStart().startsWith("{")) {
|
|
107470
106843
|
userSettings = JSON.parse(userSettingsValue);
|
|
107471
106844
|
} else {
|
|
107472
|
-
const rawUserSettings =
|
|
106845
|
+
const rawUserSettings = readFileSync22(userSettingsValue, "utf-8");
|
|
107473
106846
|
userSettings = JSON.parse(rawUserSettings);
|
|
107474
106847
|
}
|
|
107475
106848
|
userSettings.statusLine = statusLine;
|
|
107476
|
-
|
|
106849
|
+
writeFileSync13(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
|
|
107477
106850
|
} catch {
|
|
107478
106851
|
if (!config3.quiet) {
|
|
107479
106852
|
console.warn(`[claudish] Warning: could not merge user settings: ${userSettingsValue}`);
|
|
@@ -107561,13 +106934,14 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
|
|
|
107561
106934
|
console.error("Install it from: https://claude.com/claude-code");
|
|
107562
106935
|
console.error(`
|
|
107563
106936
|
Or set CLAUDE_PATH to your custom installation:`);
|
|
107564
|
-
const home =
|
|
107565
|
-
const localPath = isWindows2() ?
|
|
106937
|
+
const home = homedir25();
|
|
106938
|
+
const localPath = isWindows2() ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
|
|
107566
106939
|
console.error(` export CLAUDE_PATH=${localPath}`);
|
|
107567
106940
|
process.exit(1);
|
|
107568
106941
|
}
|
|
107569
106942
|
const needsShell = isWindows2() && claudeBinary.endsWith(".cmd");
|
|
107570
106943
|
const spawnCommand = needsShell ? `"${claudeBinary}"` : claudeBinary;
|
|
106944
|
+
setClaudeCodeRunning(true);
|
|
107571
106945
|
const proc = spawn4(spawnCommand, claudeArgs, {
|
|
107572
106946
|
env: env2,
|
|
107573
106947
|
stdio: "inherit",
|
|
@@ -107576,6 +106950,7 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
107576
106950
|
setupSignalHandlers(proc, tempSettingsPath, config3.quiet, onCleanup);
|
|
107577
106951
|
const exitCode = await new Promise((resolve4) => {
|
|
107578
106952
|
proc.on("exit", (code) => {
|
|
106953
|
+
setClaudeCodeRunning(false);
|
|
107579
106954
|
resolve4(code ?? 1);
|
|
107580
106955
|
});
|
|
107581
106956
|
});
|
|
@@ -107608,23 +106983,23 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet, onCleanup) {
|
|
|
107608
106983
|
async function findClaudeBinary() {
|
|
107609
106984
|
const isWindows3 = process.platform === "win32";
|
|
107610
106985
|
if (process.env.CLAUDE_PATH) {
|
|
107611
|
-
if (
|
|
106986
|
+
if (existsSync27(process.env.CLAUDE_PATH)) {
|
|
107612
106987
|
return process.env.CLAUDE_PATH;
|
|
107613
106988
|
}
|
|
107614
106989
|
}
|
|
107615
|
-
const home =
|
|
107616
|
-
const localPath = isWindows3 ?
|
|
107617
|
-
if (
|
|
106990
|
+
const home = homedir25();
|
|
106991
|
+
const localPath = isWindows3 ? join27(home, ".claude", "local", "claude.exe") : join27(home, ".claude", "local", "claude");
|
|
106992
|
+
if (existsSync27(localPath)) {
|
|
107618
106993
|
return localPath;
|
|
107619
106994
|
}
|
|
107620
106995
|
if (isWindows3) {
|
|
107621
106996
|
const windowsPaths = [
|
|
107622
|
-
|
|
107623
|
-
|
|
107624
|
-
|
|
106997
|
+
join27(home, "AppData", "Roaming", "npm", "claude.cmd"),
|
|
106998
|
+
join27(home, ".npm-global", "claude.cmd"),
|
|
106999
|
+
join27(home, "node_modules", ".bin", "claude.cmd")
|
|
107625
107000
|
];
|
|
107626
107001
|
for (const path3 of windowsPaths) {
|
|
107627
|
-
if (
|
|
107002
|
+
if (existsSync27(path3)) {
|
|
107628
107003
|
return path3;
|
|
107629
107004
|
}
|
|
107630
107005
|
}
|
|
@@ -107632,14 +107007,14 @@ async function findClaudeBinary() {
|
|
|
107632
107007
|
const commonPaths = [
|
|
107633
107008
|
"/usr/local/bin/claude",
|
|
107634
107009
|
"/opt/homebrew/bin/claude",
|
|
107635
|
-
|
|
107636
|
-
|
|
107637
|
-
|
|
107010
|
+
join27(home, ".npm-global/bin/claude"),
|
|
107011
|
+
join27(home, ".local/bin/claude"),
|
|
107012
|
+
join27(home, "node_modules/.bin/claude"),
|
|
107638
107013
|
"/data/data/com.termux/files/usr/bin/claude",
|
|
107639
|
-
|
|
107014
|
+
join27(home, "../usr/bin/claude")
|
|
107640
107015
|
];
|
|
107641
107016
|
for (const path3 of commonPaths) {
|
|
107642
|
-
if (
|
|
107017
|
+
if (existsSync27(path3)) {
|
|
107643
107018
|
return path3;
|
|
107644
107019
|
}
|
|
107645
107020
|
}
|
|
@@ -107679,6 +107054,7 @@ async function checkClaudeInstalled() {
|
|
|
107679
107054
|
var init_claude_runner = __esm(() => {
|
|
107680
107055
|
init_config();
|
|
107681
107056
|
init_model_parser();
|
|
107057
|
+
init_telemetry();
|
|
107682
107058
|
});
|
|
107683
107059
|
|
|
107684
107060
|
// src/diag-output.ts
|
|
@@ -107688,18 +107064,18 @@ __export(exports_diag_output, {
|
|
|
107688
107064
|
NullDiagOutput: () => NullDiagOutput,
|
|
107689
107065
|
LogFileDiagOutput: () => LogFileDiagOutput
|
|
107690
107066
|
});
|
|
107691
|
-
import { createWriteStream as createWriteStream3, mkdirSync as
|
|
107692
|
-
import { homedir as
|
|
107693
|
-
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";
|
|
107694
107070
|
function getClaudishDir() {
|
|
107695
|
-
const dir =
|
|
107071
|
+
const dir = join28(homedir26(), ".claudish");
|
|
107696
107072
|
try {
|
|
107697
|
-
|
|
107073
|
+
mkdirSync13(dir, { recursive: true });
|
|
107698
107074
|
} catch {}
|
|
107699
107075
|
return dir;
|
|
107700
107076
|
}
|
|
107701
107077
|
function getDiagLogPath() {
|
|
107702
|
-
return
|
|
107078
|
+
return join28(getClaudishDir(), `diag-${process.pid}.log`);
|
|
107703
107079
|
}
|
|
107704
107080
|
|
|
107705
107081
|
class LogFileDiagOutput {
|
|
@@ -107708,7 +107084,7 @@ class LogFileDiagOutput {
|
|
|
107708
107084
|
constructor() {
|
|
107709
107085
|
this.logPath = getDiagLogPath();
|
|
107710
107086
|
try {
|
|
107711
|
-
|
|
107087
|
+
writeFileSync14(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
|
|
107712
107088
|
`);
|
|
107713
107089
|
} catch {}
|
|
107714
107090
|
this.stream = createWriteStream3(this.logPath, { flags: "a" });
|
|
@@ -107758,12 +107134,12 @@ __export(exports_team_grid, {
|
|
|
107758
107134
|
});
|
|
107759
107135
|
import { spawn as spawn5 } from "child_process";
|
|
107760
107136
|
import {
|
|
107761
|
-
existsSync as
|
|
107762
|
-
readFileSync as
|
|
107763
|
-
writeFileSync as
|
|
107137
|
+
existsSync as existsSync28,
|
|
107138
|
+
readFileSync as readFileSync23,
|
|
107139
|
+
writeFileSync as writeFileSync15
|
|
107764
107140
|
} from "fs";
|
|
107765
|
-
import { dirname as
|
|
107766
|
-
import { fileURLToPath as
|
|
107141
|
+
import { dirname as dirname6, join as join29 } from "path";
|
|
107142
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
107767
107143
|
import { execSync as execSync2 } from "child_process";
|
|
107768
107144
|
import { connect as netConnect } from "net";
|
|
107769
107145
|
import { setTimeout as wait } from "timers/promises";
|
|
@@ -107848,22 +107224,22 @@ function buildPaneHeader(model, prompt, bg2) {
|
|
|
107848
107224
|
return lines.join(" ");
|
|
107849
107225
|
}
|
|
107850
107226
|
function findMagmuxBinary() {
|
|
107851
|
-
const thisFile =
|
|
107852
|
-
const thisDir =
|
|
107853
|
-
const pkgRoot =
|
|
107227
|
+
const thisFile = fileURLToPath5(import.meta.url);
|
|
107228
|
+
const thisDir = dirname6(thisFile);
|
|
107229
|
+
const pkgRoot = join29(thisDir, "..");
|
|
107854
107230
|
const platform3 = process.platform;
|
|
107855
107231
|
const arch = process.arch;
|
|
107856
|
-
const bundledMagmux =
|
|
107857
|
-
if (
|
|
107232
|
+
const bundledMagmux = join29(pkgRoot, "native", `magmux-${platform3}-${arch}`);
|
|
107233
|
+
if (existsSync28(bundledMagmux))
|
|
107858
107234
|
return bundledMagmux;
|
|
107859
107235
|
try {
|
|
107860
107236
|
const pkgName = `@claudish/magmux-${platform3}-${arch}`;
|
|
107861
107237
|
let searchDir = pkgRoot;
|
|
107862
107238
|
for (let i = 0;i < 5; i++) {
|
|
107863
|
-
const candidate =
|
|
107864
|
-
if (
|
|
107239
|
+
const candidate = join29(searchDir, "node_modules", pkgName, "bin", "magmux");
|
|
107240
|
+
if (existsSync28(candidate))
|
|
107865
107241
|
return candidate;
|
|
107866
|
-
const parent =
|
|
107242
|
+
const parent = dirname6(searchDir);
|
|
107867
107243
|
if (parent === searchDir)
|
|
107868
107244
|
break;
|
|
107869
107245
|
searchDir = parent;
|
|
@@ -107880,7 +107256,7 @@ function findMagmuxBinary() {
|
|
|
107880
107256
|
async function subscribeToMagmux(sockPath, onEvent) {
|
|
107881
107257
|
let client = null;
|
|
107882
107258
|
for (let attempt = 0;attempt < 40; attempt++) {
|
|
107883
|
-
if (
|
|
107259
|
+
if (existsSync28(sockPath)) {
|
|
107884
107260
|
try {
|
|
107885
107261
|
client = await new Promise((resolve4, reject) => {
|
|
107886
107262
|
const s = netConnect(sockPath);
|
|
@@ -107967,9 +107343,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
107967
107343
|
const keep = opts?.keep ?? false;
|
|
107968
107344
|
const manifest = setupSession(sessionPath, models, input);
|
|
107969
107345
|
const startedAt = new Date().toISOString();
|
|
107970
|
-
const gridfilePath =
|
|
107971
|
-
const prompt =
|
|
107972
|
-
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");
|
|
107973
107349
|
const usedBannerColors = new Set;
|
|
107974
107350
|
const gridLines = Object.entries(manifest.models).map(([anonId]) => {
|
|
107975
107351
|
const model = manifest.models[anonId].model;
|
|
@@ -107980,7 +107356,7 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
107980
107356
|
const header = buildPaneHeader(model, rawPrompt, bg2);
|
|
107981
107357
|
return `${header} claudish --model ${model} -y --quiet '${prompt}'`;
|
|
107982
107358
|
});
|
|
107983
|
-
|
|
107359
|
+
writeFileSync15(gridfilePath, gridLines.join(`
|
|
107984
107360
|
`) + `
|
|
107985
107361
|
`, "utf-8");
|
|
107986
107362
|
const magmuxPath = findMagmuxBinary();
|
|
@@ -108000,8 +107376,8 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
108000
107376
|
});
|
|
108001
107377
|
const [{ results }] = await Promise.all([subscription, procExit]);
|
|
108002
107378
|
const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
|
|
108003
|
-
const statusPath =
|
|
108004
|
-
|
|
107379
|
+
const statusPath = join29(sessionPath, "status.json");
|
|
107380
|
+
writeFileSync15(statusPath, JSON.stringify(status, null, 2), "utf-8");
|
|
108005
107381
|
return status;
|
|
108006
107382
|
}
|
|
108007
107383
|
var BANNER_BG_COLORS;
|
|
@@ -108023,16 +107399,16 @@ var init_team_grid = __esm(() => {
|
|
|
108023
107399
|
|
|
108024
107400
|
// src/index.ts
|
|
108025
107401
|
var import_dotenv3 = __toESM(require_main(), 1);
|
|
108026
|
-
import { existsSync as
|
|
108027
|
-
import { homedir as
|
|
108028
|
-
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";
|
|
108029
107405
|
import_dotenv3.config({ quiet: true });
|
|
108030
107406
|
function loadStoredApiKeys() {
|
|
108031
107407
|
try {
|
|
108032
|
-
const configPath =
|
|
108033
|
-
if (!
|
|
107408
|
+
const configPath = join30(homedir27(), ".claudish", "config.json");
|
|
107409
|
+
if (!existsSync29(configPath))
|
|
108034
107410
|
return;
|
|
108035
|
-
const raw2 =
|
|
107411
|
+
const raw2 = readFileSync24(configPath, "utf-8");
|
|
108036
107412
|
const cfg = JSON.parse(raw2);
|
|
108037
107413
|
if (cfg.apiKeys) {
|
|
108038
107414
|
for (const [envVar, value] of Object.entries(cfg.apiKeys)) {
|
|
@@ -108146,14 +107522,14 @@ async function runCli() {
|
|
|
108146
107522
|
if (cliConfig.team && cliConfig.team.length > 0) {
|
|
108147
107523
|
let prompt = cliConfig.claudeArgs.join(" ");
|
|
108148
107524
|
if (cliConfig.inputFile) {
|
|
108149
|
-
prompt =
|
|
107525
|
+
prompt = readFileSync24(cliConfig.inputFile, "utf-8");
|
|
108150
107526
|
}
|
|
108151
107527
|
if (!prompt.trim()) {
|
|
108152
107528
|
console.error("Error: --team requires a prompt (positional args or -f <file>)");
|
|
108153
107529
|
process.exit(1);
|
|
108154
107530
|
}
|
|
108155
107531
|
const mode = cliConfig.teamMode ?? "default";
|
|
108156
|
-
const sessionPath =
|
|
107532
|
+
const sessionPath = join30(process.cwd(), `.claudish-team-${Date.now()}`);
|
|
108157
107533
|
if (mode === "json") {
|
|
108158
107534
|
const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
|
|
108159
107535
|
setupSession2(sessionPath, cliConfig.team, prompt);
|
|
@@ -108163,9 +107539,9 @@ async function runCli() {
|
|
|
108163
107539
|
});
|
|
108164
107540
|
const result = { ...status2, responses: {} };
|
|
108165
107541
|
for (const anonId of Object.keys(status2.models)) {
|
|
108166
|
-
const responsePath =
|
|
107542
|
+
const responsePath = join30(sessionPath, `response-${anonId}.md`);
|
|
108167
107543
|
try {
|
|
108168
|
-
const raw2 =
|
|
107544
|
+
const raw2 = readFileSync24(responsePath, "utf-8").trim();
|
|
108169
107545
|
try {
|
|
108170
107546
|
result.responses[anonId] = JSON.parse(raw2);
|
|
108171
107547
|
} catch {
|