claudish 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +133 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34353,6 +34353,7 @@ process.stdin.on('end', () => {
|
|
|
34353
34353
|
|
|
34354
34354
|
let ctx = 100, cost = 0;
|
|
34355
34355
|
const model = process.env.CLAUDISH_ACTIVE_MODEL_NAME || 'unknown';
|
|
34356
|
+
const isLocal = process.env.CLAUDISH_IS_LOCAL === 'true';
|
|
34356
34357
|
|
|
34357
34358
|
try {
|
|
34358
34359
|
const tokens = JSON.parse(fs.readFileSync('${escapedTokenPath}', 'utf-8'));
|
|
@@ -34365,8 +34366,8 @@ process.stdin.on('end', () => {
|
|
|
34365
34366
|
} catch {}
|
|
34366
34367
|
}
|
|
34367
34368
|
|
|
34368
|
-
const
|
|
34369
|
-
console.log(\`\${CYAN}\${BOLD}\${dir}\${RESET} \${DIM}•\${RESET} \${YELLOW}\${model}\${RESET} \${DIM}•\${RESET} \${GREEN}
|
|
34369
|
+
const costDisplay = isLocal ? 'LOCAL' : ('$' + cost.toFixed(3));
|
|
34370
|
+
console.log(\`\${CYAN}\${BOLD}\${dir}\${RESET} \${DIM}•\${RESET} \${YELLOW}\${model}\${RESET} \${DIM}•\${RESET} \${GREEN}\${costDisplay}\${RESET} \${DIM}•\${RESET} \${MAGENTA}\${ctx}%\${RESET}\`);
|
|
34370
34371
|
} catch (e) {
|
|
34371
34372
|
console.log('claudish');
|
|
34372
34373
|
}
|
|
@@ -34392,7 +34393,7 @@ function createTempSettingsFile(modelDisplay, port) {
|
|
|
34392
34393
|
const DIM2 = "\\033[2m";
|
|
34393
34394
|
const RESET2 = "\\033[0m";
|
|
34394
34395
|
const BOLD2 = "\\033[1m";
|
|
34395
|
-
statusCommand = `JSON=$(cat) && DIR=$(basename "$(pwd)") && [ \${#DIR} -gt 15 ] && DIR="\${DIR:0:12}..." || true && CTX=100 && COST="0" && if [ -f "${tokenFilePath}" ]; then TOKENS=$(cat "${tokenFilePath}" 2>/dev/null) && REAL_COST=$(echo "$TOKENS" | grep -o '"total_cost":[0-9.]*' | cut -d: -f2) && REAL_CTX=$(echo "$TOKENS" | grep -o '"context_left_percent":[0-9]*' | grep -o '[0-9]*') && if [ ! -z "$REAL_COST" ]; then COST="$REAL_COST"; else COST=$(echo "$JSON" | grep -o '"total_cost_usd":[0-9.]*' | cut -d: -f2); fi && if [ ! -z "$REAL_CTX" ]; then CTX="$REAL_CTX"; fi; else COST=$(echo "$JSON" | grep -o '"total_cost_usd":[0-9.]*' | cut -d: -f2); fi && [ -z "$COST" ] && COST="0" || true && printf "${CYAN2}${BOLD2}%s${RESET2} ${DIM2}•${RESET2} ${YELLOW2}%s${RESET2} ${DIM2}•${RESET2} ${GREEN2}
|
|
34396
|
+
statusCommand = `JSON=$(cat) && DIR=$(basename "$(pwd)") && [ \${#DIR} -gt 15 ] && DIR="\${DIR:0:12}..." || true && CTX=100 && COST="0" && if [ -f "${tokenFilePath}" ]; then TOKENS=$(cat "${tokenFilePath}" 2>/dev/null) && REAL_COST=$(echo "$TOKENS" | grep -o '"total_cost":[0-9.]*' | cut -d: -f2) && REAL_CTX=$(echo "$TOKENS" | grep -o '"context_left_percent":[0-9]*' | grep -o '[0-9]*') && if [ ! -z "$REAL_COST" ]; then COST="$REAL_COST"; else COST=$(echo "$JSON" | grep -o '"total_cost_usd":[0-9.]*' | cut -d: -f2); fi && if [ ! -z "$REAL_CTX" ]; then CTX="$REAL_CTX"; fi; else COST=$(echo "$JSON" | grep -o '"total_cost_usd":[0-9.]*' | cut -d: -f2); fi && [ -z "$COST" ] && COST="0" || true && if [ "$CLAUDISH_IS_LOCAL" = "true" ]; then COST_DISPLAY="LOCAL"; else COST_DISPLAY=$(printf "\\$%.3f" "$COST"); fi && printf "${CYAN2}${BOLD2}%s${RESET2} ${DIM2}•${RESET2} ${YELLOW2}%s${RESET2} ${DIM2}•${RESET2} ${GREEN2}%s${RESET2} ${DIM2}•${RESET2} ${MAGENTA}%s%%${RESET2}\\n" "$DIR" "$CLAUDISH_ACTIVE_MODEL_NAME" "$COST_DISPLAY" "$CTX"`;
|
|
34396
34397
|
}
|
|
34397
34398
|
const settings = {
|
|
34398
34399
|
statusLine: {
|
|
@@ -34438,10 +34439,12 @@ async function runClaudeWithProxy(config3, proxyUrl) {
|
|
|
34438
34439
|
claudeArgs.push(...config3.claudeArgs);
|
|
34439
34440
|
}
|
|
34440
34441
|
}
|
|
34442
|
+
const isLocalModel = modelId.startsWith("ollama/") || modelId.startsWith("ollama:") || modelId.startsWith("lmstudio/") || modelId.startsWith("lmstudio:") || modelId.startsWith("vllm/") || modelId.startsWith("vllm:") || modelId.startsWith("http://") || modelId.startsWith("https://");
|
|
34441
34443
|
const env = {
|
|
34442
34444
|
...process.env,
|
|
34443
34445
|
ANTHROPIC_BASE_URL: proxyUrl,
|
|
34444
34446
|
[ENV.CLAUDISH_ACTIVE_MODEL_NAME]: modelId,
|
|
34447
|
+
CLAUDISH_IS_LOCAL: isLocalModel ? "true" : "false",
|
|
34445
34448
|
[ENV.ANTHROPIC_MODEL]: modelId,
|
|
34446
34449
|
[ENV.ANTHROPIC_SMALL_FAST_MODEL]: modelId
|
|
34447
34450
|
};
|
|
@@ -34926,17 +34929,40 @@ async function fetchOllamaModels() {
|
|
|
34926
34929
|
return [];
|
|
34927
34930
|
const data = await response.json();
|
|
34928
34931
|
const models = data.models || [];
|
|
34929
|
-
|
|
34930
|
-
|
|
34931
|
-
|
|
34932
|
-
|
|
34933
|
-
|
|
34934
|
-
|
|
34935
|
-
|
|
34936
|
-
|
|
34937
|
-
|
|
34938
|
-
|
|
34932
|
+
const modelsWithCapabilities = await Promise.all(models.map(async (m) => {
|
|
34933
|
+
let capabilities = [];
|
|
34934
|
+
try {
|
|
34935
|
+
const showResponse = await fetch(`${ollamaHost}/api/show`, {
|
|
34936
|
+
method: "POST",
|
|
34937
|
+
headers: { "Content-Type": "application/json" },
|
|
34938
|
+
body: JSON.stringify({ name: m.name }),
|
|
34939
|
+
signal: AbortSignal.timeout(2000)
|
|
34940
|
+
});
|
|
34941
|
+
if (showResponse.ok) {
|
|
34942
|
+
const showData = await showResponse.json();
|
|
34943
|
+
capabilities = showData.capabilities || [];
|
|
34944
|
+
}
|
|
34945
|
+
} catch {}
|
|
34946
|
+
const supportsTools = capabilities.includes("tools");
|
|
34947
|
+
const isEmbeddingModel = capabilities.includes("embedding") || m.name.toLowerCase().includes("embed");
|
|
34948
|
+
const sizeInfo = m.details?.parameter_size || "unknown size";
|
|
34949
|
+
const toolsIndicator = supportsTools ? "✓ tools" : "✗ no tools";
|
|
34950
|
+
return {
|
|
34951
|
+
id: `ollama/${m.name}`,
|
|
34952
|
+
name: m.name,
|
|
34953
|
+
description: `Local Ollama model (${sizeInfo}, ${toolsIndicator})`,
|
|
34954
|
+
provider: "ollama",
|
|
34955
|
+
context_length: null,
|
|
34956
|
+
pricing: { prompt: "0", completion: "0" },
|
|
34957
|
+
isLocal: true,
|
|
34958
|
+
supportsTools,
|
|
34959
|
+
isEmbeddingModel,
|
|
34960
|
+
capabilities,
|
|
34961
|
+
details: m.details,
|
|
34962
|
+
size: m.size
|
|
34963
|
+
};
|
|
34939
34964
|
}));
|
|
34965
|
+
return modelsWithCapabilities.filter((m) => !m.isEmbeddingModel);
|
|
34940
34966
|
} catch (e) {
|
|
34941
34967
|
return [];
|
|
34942
34968
|
}
|
|
@@ -34990,6 +35016,10 @@ async function searchAndPrintModels(query, forceUpdate) {
|
|
|
34990
35016
|
console.log(`No models found matching "${query}"`);
|
|
34991
35017
|
return;
|
|
34992
35018
|
}
|
|
35019
|
+
const RED = "\x1B[31m";
|
|
35020
|
+
const GREEN2 = "\x1B[32m";
|
|
35021
|
+
const RESET2 = "\x1B[0m";
|
|
35022
|
+
const DIM2 = "\x1B[2m";
|
|
34993
35023
|
console.log(`
|
|
34994
35024
|
Found ${results.length} matching models:
|
|
34995
35025
|
`);
|
|
@@ -35020,9 +35050,17 @@ Found ${results.length} matching models:
|
|
|
35020
35050
|
const contextLen = model.context_length || model.top_provider?.context_length || 0;
|
|
35021
35051
|
const context = contextLen > 0 ? `${Math.round(contextLen / 1000)}K` : "N/A";
|
|
35022
35052
|
const contextPadded = context.padEnd(7);
|
|
35023
|
-
|
|
35053
|
+
if (model.isLocal && model.supportsTools === false) {
|
|
35054
|
+
console.log(` ${RED}${modelIdPadded} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}% ✗ no tools${RESET2}`);
|
|
35055
|
+
} else if (model.isLocal && model.supportsTools === true) {
|
|
35056
|
+
console.log(` ${GREEN2}${modelIdPadded}${RESET2} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}%`);
|
|
35057
|
+
} else {
|
|
35058
|
+
console.log(` ${modelIdPadded} ${providerPadded} ${pricingPadded} ${contextPadded} ${(score * 100).toFixed(0)}%`);
|
|
35059
|
+
}
|
|
35024
35060
|
}
|
|
35025
35061
|
console.log("");
|
|
35062
|
+
console.log(`${DIM2}Local models: ${RED}red${RESET2}${DIM2} = no tool support (incompatible), ${GREEN2}green${RESET2}${DIM2} = compatible${RESET2}`);
|
|
35063
|
+
console.log("");
|
|
35026
35064
|
console.log("Use a model: claudish --model <model-id>");
|
|
35027
35065
|
console.log("Local models: claudish --model ollama/<model-name>");
|
|
35028
35066
|
}
|
|
@@ -35077,23 +35115,37 @@ async function printAllModels(jsonOutput, forceUpdate) {
|
|
|
35077
35115
|
}, null, 2));
|
|
35078
35116
|
return;
|
|
35079
35117
|
}
|
|
35118
|
+
const RED = "\x1B[31m";
|
|
35119
|
+
const GREEN2 = "\x1B[32m";
|
|
35120
|
+
const RESET2 = "\x1B[0m";
|
|
35121
|
+
const DIM2 = "\x1B[2m";
|
|
35080
35122
|
if (ollamaModels.length > 0) {
|
|
35123
|
+
const toolCapableCount = ollamaModels.filter((m) => m.supportsTools).length;
|
|
35081
35124
|
console.log(`
|
|
35082
|
-
\uD83C\uDFE0 LOCAL OLLAMA MODELS (${ollamaModels.length} installed):
|
|
35125
|
+
\uD83C\uDFE0 LOCAL OLLAMA MODELS (${ollamaModels.length} installed, ${toolCapableCount} with tool support):
|
|
35083
35126
|
`);
|
|
35084
|
-
console.log("
|
|
35127
|
+
console.log(" Model Size Params Tools");
|
|
35128
|
+
console.log(" " + "─".repeat(76));
|
|
35085
35129
|
for (const model of ollamaModels) {
|
|
35086
|
-
const
|
|
35087
|
-
const modelId =
|
|
35088
|
-
const modelIdPadded = modelId.padEnd(
|
|
35130
|
+
const fullId = model.id;
|
|
35131
|
+
const modelId = fullId.length > 35 ? fullId.substring(0, 32) + "..." : fullId;
|
|
35132
|
+
const modelIdPadded = modelId.padEnd(38);
|
|
35089
35133
|
const size = model.size ? `${(model.size / 1e9).toFixed(1)}GB` : "N/A";
|
|
35090
35134
|
const sizePadded = size.padEnd(12);
|
|
35091
35135
|
const params = model.details?.parameter_size || "N/A";
|
|
35092
35136
|
const paramsPadded = params.padEnd(8);
|
|
35093
|
-
|
|
35137
|
+
if (model.supportsTools) {
|
|
35138
|
+
console.log(` ${modelIdPadded} ${sizePadded} ${paramsPadded} ${GREEN2}✓${RESET2}`);
|
|
35139
|
+
} else {
|
|
35140
|
+
console.log(` ${RED}${modelIdPadded} ${sizePadded} ${paramsPadded} ✗ no tools${RESET2}`);
|
|
35141
|
+
}
|
|
35094
35142
|
}
|
|
35095
35143
|
console.log("");
|
|
35144
|
+
console.log(` ${GREEN2}✓${RESET2} = Compatible with Claude Code (supports tool calling)`);
|
|
35145
|
+
console.log(` ${RED}✗${RESET2} = Not compatible ${DIM2}(Claude Code requires tool support)${RESET2}`);
|
|
35146
|
+
console.log("");
|
|
35096
35147
|
console.log(" Use: claudish --model ollama/<model-name>");
|
|
35148
|
+
console.log(" Pull a compatible model: ollama pull llama3.2");
|
|
35097
35149
|
} else {
|
|
35098
35150
|
console.log(`
|
|
35099
35151
|
\uD83C\uDFE0 LOCAL OLLAMA: Not running or no models installed`);
|
|
@@ -39535,6 +39587,10 @@ var init_openai_compat = __esm(() => {
|
|
|
39535
39587
|
});
|
|
39536
39588
|
|
|
39537
39589
|
// src/handlers/local-provider-handler.ts
|
|
39590
|
+
import { writeFileSync as writeFileSync9 } from "node:fs";
|
|
39591
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
39592
|
+
import { join as join9 } from "node:path";
|
|
39593
|
+
|
|
39538
39594
|
class LocalProviderHandler {
|
|
39539
39595
|
provider;
|
|
39540
39596
|
modelName;
|
|
@@ -39543,6 +39599,9 @@ class LocalProviderHandler {
|
|
|
39543
39599
|
port;
|
|
39544
39600
|
healthChecked = false;
|
|
39545
39601
|
isHealthy = false;
|
|
39602
|
+
contextWindow = 8192;
|
|
39603
|
+
sessionInputTokens = 0;
|
|
39604
|
+
sessionOutputTokens = 0;
|
|
39546
39605
|
constructor(provider, modelName, port) {
|
|
39547
39606
|
this.provider = provider;
|
|
39548
39607
|
this.modelName = modelName;
|
|
@@ -39587,6 +39646,49 @@ class LocalProviderHandler {
|
|
|
39587
39646
|
this.isHealthy = false;
|
|
39588
39647
|
return false;
|
|
39589
39648
|
}
|
|
39649
|
+
async fetchContextWindow() {
|
|
39650
|
+
if (this.provider.name !== "ollama")
|
|
39651
|
+
return;
|
|
39652
|
+
try {
|
|
39653
|
+
const response = await fetch(`${this.provider.baseUrl}/api/show`, {
|
|
39654
|
+
method: "POST",
|
|
39655
|
+
headers: { "Content-Type": "application/json" },
|
|
39656
|
+
body: JSON.stringify({ name: this.modelName }),
|
|
39657
|
+
signal: AbortSignal.timeout(3000)
|
|
39658
|
+
});
|
|
39659
|
+
if (response.ok) {
|
|
39660
|
+
const data = await response.json();
|
|
39661
|
+
const ctxFromInfo = data.model_info?.["general.context_length"];
|
|
39662
|
+
const ctxFromParams = data.parameters?.match(/num_ctx\s+(\d+)/)?.[1];
|
|
39663
|
+
if (ctxFromInfo) {
|
|
39664
|
+
this.contextWindow = parseInt(ctxFromInfo, 10);
|
|
39665
|
+
} else if (ctxFromParams) {
|
|
39666
|
+
this.contextWindow = parseInt(ctxFromParams, 10);
|
|
39667
|
+
} else {
|
|
39668
|
+
this.contextWindow = 8192;
|
|
39669
|
+
}
|
|
39670
|
+
log(`[LocalProvider:${this.provider.name}] Context window: ${this.contextWindow}`);
|
|
39671
|
+
}
|
|
39672
|
+
} catch (e) {}
|
|
39673
|
+
}
|
|
39674
|
+
writeTokenFile(input, output) {
|
|
39675
|
+
try {
|
|
39676
|
+
this.sessionInputTokens += input;
|
|
39677
|
+
this.sessionOutputTokens += output;
|
|
39678
|
+
const total = this.sessionInputTokens + this.sessionOutputTokens;
|
|
39679
|
+
const leftPct = this.contextWindow > 0 ? Math.max(0, Math.min(100, Math.round((this.contextWindow - total) / this.contextWindow * 100))) : 100;
|
|
39680
|
+
const data = {
|
|
39681
|
+
input_tokens: this.sessionInputTokens,
|
|
39682
|
+
output_tokens: this.sessionOutputTokens,
|
|
39683
|
+
total_tokens: total,
|
|
39684
|
+
total_cost: 0,
|
|
39685
|
+
context_window: this.contextWindow,
|
|
39686
|
+
context_left_percent: leftPct,
|
|
39687
|
+
updated_at: Date.now()
|
|
39688
|
+
};
|
|
39689
|
+
writeFileSync9(join9(tmpdir3(), `claudish-tokens-${this.port}.json`), JSON.stringify(data), "utf-8");
|
|
39690
|
+
} catch (e) {}
|
|
39691
|
+
}
|
|
39590
39692
|
async handle(c, payload) {
|
|
39591
39693
|
const target = this.modelName;
|
|
39592
39694
|
logStructured(`LocalProvider Request`, {
|
|
@@ -39600,6 +39702,7 @@ class LocalProviderHandler {
|
|
|
39600
39702
|
if (!healthy) {
|
|
39601
39703
|
return this.errorResponse(c, "connection_error", this.getConnectionErrorMessage());
|
|
39602
39704
|
}
|
|
39705
|
+
await this.fetchContextWindow();
|
|
39603
39706
|
}
|
|
39604
39707
|
const { claudeRequest, droppedParams } = transformOpenAIToClaude(payload);
|
|
39605
39708
|
const messages = convertMessagesToOpenAI(claudeRequest, target, filterIdentity);
|
|
@@ -39652,7 +39755,7 @@ class LocalProviderHandler {
|
|
|
39652
39755
|
c.header("X-Dropped-Params", droppedParams.join(", "));
|
|
39653
39756
|
}
|
|
39654
39757
|
if (openAIPayload.stream) {
|
|
39655
|
-
return createStreamingResponseHandler(c, response, adapter, target, this.middlewareManager);
|
|
39758
|
+
return createStreamingResponseHandler(c, response, adapter, target, this.middlewareManager, (input, output) => this.writeTokenFile(input, output));
|
|
39656
39759
|
}
|
|
39657
39760
|
const data = await response.json();
|
|
39658
39761
|
return c.json(data);
|
|
@@ -39948,24 +40051,24 @@ __export(exports_update_checker, {
|
|
|
39948
40051
|
});
|
|
39949
40052
|
import { execSync } from "node:child_process";
|
|
39950
40053
|
import { createInterface as createInterface2 } from "node:readline";
|
|
39951
|
-
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as
|
|
39952
|
-
import { join as
|
|
39953
|
-
import { tmpdir as
|
|
40054
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync10, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
40055
|
+
import { join as join10 } from "node:path";
|
|
40056
|
+
import { tmpdir as tmpdir4, homedir as homedir2, platform as platform2 } from "node:os";
|
|
39954
40057
|
function getCacheFilePath() {
|
|
39955
40058
|
let cacheDir;
|
|
39956
40059
|
if (isWindows2) {
|
|
39957
|
-
const localAppData = process.env.LOCALAPPDATA ||
|
|
39958
|
-
cacheDir =
|
|
40060
|
+
const localAppData = process.env.LOCALAPPDATA || join10(homedir2(), "AppData", "Local");
|
|
40061
|
+
cacheDir = join10(localAppData, "claudish");
|
|
39959
40062
|
} else {
|
|
39960
|
-
cacheDir =
|
|
40063
|
+
cacheDir = join10(homedir2(), ".cache", "claudish");
|
|
39961
40064
|
}
|
|
39962
40065
|
try {
|
|
39963
40066
|
if (!existsSync7(cacheDir)) {
|
|
39964
40067
|
mkdirSync4(cacheDir, { recursive: true });
|
|
39965
40068
|
}
|
|
39966
|
-
return
|
|
40069
|
+
return join10(cacheDir, "update-check.json");
|
|
39967
40070
|
} catch {
|
|
39968
|
-
return
|
|
40071
|
+
return join10(tmpdir4(), "claudish-update-check.json");
|
|
39969
40072
|
}
|
|
39970
40073
|
}
|
|
39971
40074
|
function readCache() {
|
|
@@ -39987,7 +40090,7 @@ function writeCache(latestVersion) {
|
|
|
39987
40090
|
lastCheck: Date.now(),
|
|
39988
40091
|
latestVersion
|
|
39989
40092
|
};
|
|
39990
|
-
|
|
40093
|
+
writeFileSync10(cachePath, JSON.stringify(data), "utf-8");
|
|
39991
40094
|
} catch {}
|
|
39992
40095
|
}
|
|
39993
40096
|
function isCacheValid(cache) {
|