claudish 7.56.0 → 7.58.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 +694 -406
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
729
729
|
});
|
|
730
730
|
|
|
731
731
|
// src/version.ts
|
|
732
|
-
var VERSION = "7.
|
|
732
|
+
var VERSION = "7.58.0";
|
|
733
733
|
|
|
734
734
|
// src/logger.ts
|
|
735
735
|
var exports_logger = {};
|
|
@@ -28077,6 +28077,7 @@ var init_provider_definitions = __esm(() => {
|
|
|
28077
28077
|
{ prefix: "dv/", stripPrefix: true },
|
|
28078
28078
|
{ prefix: "devin/", stripPrefix: true }
|
|
28079
28079
|
],
|
|
28080
|
+
nativeModelPatterns: [{ pattern: /^swe-/i }],
|
|
28080
28081
|
modelDiscovery: { path: "", format: "devin-connect" },
|
|
28081
28082
|
isDirectApi: true,
|
|
28082
28083
|
description: "Devin subscription (dv@, devin@)"
|
|
@@ -28214,6 +28215,7 @@ var init_provider_definitions = __esm(() => {
|
|
|
28214
28215
|
baseUrl: "https://api.minimax.io",
|
|
28215
28216
|
baseUrlEnvVars: ["MINIMAX_CODING_BASE_URL"],
|
|
28216
28217
|
apiPath: "/anthropic/v1/messages",
|
|
28218
|
+
modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
|
|
28217
28219
|
apiKeyEnvVar: "MINIMAX_CODING_API_KEY",
|
|
28218
28220
|
apiKeyDescription: "MiniMax Coding Plan API Key",
|
|
28219
28221
|
apiKeyUrl: "https://platform.minimax.io/user-center/basic-information/interface-key",
|
|
@@ -28250,6 +28252,7 @@ var init_provider_definitions = __esm(() => {
|
|
|
28250
28252
|
baseUrl: "https://api.moonshot.ai",
|
|
28251
28253
|
baseUrlEnvVars: ["MOONSHOT_BASE_URL", "KIMI_BASE_URL"],
|
|
28252
28254
|
apiPath: "/anthropic/v1/messages",
|
|
28255
|
+
modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
|
|
28253
28256
|
apiKeyEnvVar: "MOONSHOT_API_KEY",
|
|
28254
28257
|
apiKeyAliases: ["KIMI_API_KEY"],
|
|
28255
28258
|
apiKeyDescription: "Kimi/Moonshot API Key",
|
|
@@ -28301,6 +28304,7 @@ var init_provider_definitions = __esm(() => {
|
|
|
28301
28304
|
tokenStrategy: "delta-aware",
|
|
28302
28305
|
baseUrl: "https://api.z.ai",
|
|
28303
28306
|
apiPath: "/api/coding/paas/v4/chat/completions",
|
|
28307
|
+
modelDiscovery: { path: "/api/coding/paas/v4/models", format: "openai-models-list" },
|
|
28304
28308
|
apiKeyEnvVar: "GLM_CODING_API_KEY",
|
|
28305
28309
|
apiKeyAliases: ["ZAI_CODING_API_KEY"],
|
|
28306
28310
|
apiKeyDescription: "GLM Coding Plan API Key",
|
|
@@ -28377,6 +28381,7 @@ var init_provider_definitions = __esm(() => {
|
|
|
28377
28381
|
baseUrl: "https://opencode.ai/zen/go",
|
|
28378
28382
|
baseUrlEnvVars: ["OPENCODE_GO_BASE_URL"],
|
|
28379
28383
|
apiPath: "/v1/chat/completions",
|
|
28384
|
+
modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
|
|
28380
28385
|
apiKeyEnvVar: "OPENCODE_GO_API_KEY",
|
|
28381
28386
|
apiKeyAliases: ["OPENCODE_API_KEY"],
|
|
28382
28387
|
apiKeyDescription: "OpenCode Zen Go (Lite Plan) API Key",
|
|
@@ -28594,6 +28599,7 @@ var init_provider_definitions = __esm(() => {
|
|
|
28594
28599
|
baseUrl: "https://api.sakana.ai",
|
|
28595
28600
|
baseUrlEnvVars: ["SAKANA_BASE_URL"],
|
|
28596
28601
|
apiPath: "/v1/chat/completions",
|
|
28602
|
+
modelDiscovery: { path: "/v1/models", format: "openai-models-list" },
|
|
28597
28603
|
apiKeyEnvVar: "SAKANA_SUBSCRIPTION_API_KEY",
|
|
28598
28604
|
apiKeyAliases: ["SAKANA_CODING_API_KEY"],
|
|
28599
28605
|
apiKeyDescription: "Sakana Fugu Subscription API Key",
|
|
@@ -28741,6 +28747,8 @@ var init_all_models_cache = __esm(() => {
|
|
|
28741
28747
|
|
|
28742
28748
|
// src/providers/catalog-client.ts
|
|
28743
28749
|
function getCatalogEntries() {
|
|
28750
|
+
if (_catalogEntriesForTest !== undefined)
|
|
28751
|
+
return _catalogEntriesForTest;
|
|
28744
28752
|
if (_memCache)
|
|
28745
28753
|
return _memCache;
|
|
28746
28754
|
const cache = readAllModelsCache();
|
|
@@ -28760,6 +28768,26 @@ function getCatalogEntries() {
|
|
|
28760
28768
|
}
|
|
28761
28769
|
return null;
|
|
28762
28770
|
}
|
|
28771
|
+
function latestAnthropicTierModelId(tier) {
|
|
28772
|
+
const entries = getCatalogEntries();
|
|
28773
|
+
if (!entries)
|
|
28774
|
+
return null;
|
|
28775
|
+
const family = new RegExp(`^claude-${tier}-`, "i");
|
|
28776
|
+
const opus = entries.filter((e) => family.test(e.modelId));
|
|
28777
|
+
if (opus.length === 0)
|
|
28778
|
+
return null;
|
|
28779
|
+
opus.sort((a, b) => {
|
|
28780
|
+
const byDate = (b.releaseDate ?? "").localeCompare(a.releaseDate ?? "");
|
|
28781
|
+
if (byDate !== 0)
|
|
28782
|
+
return byDate;
|
|
28783
|
+
const aFast = /-fast$/i.test(a.modelId) ? 1 : 0;
|
|
28784
|
+
const bFast = /-fast$/i.test(b.modelId) ? 1 : 0;
|
|
28785
|
+
if (aFast !== bFast)
|
|
28786
|
+
return aFast - bFast;
|
|
28787
|
+
return b.modelId.localeCompare(a.modelId);
|
|
28788
|
+
});
|
|
28789
|
+
return opus[0].modelId;
|
|
28790
|
+
}
|
|
28763
28791
|
function isCatalogWarm() {
|
|
28764
28792
|
return _memCache !== null && _memCache.length > 0;
|
|
28765
28793
|
}
|
|
@@ -28902,7 +28930,7 @@ async function ensureCatalogReady(timeoutMs = 5000) {
|
|
|
28902
28930
|
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
28903
28931
|
]);
|
|
28904
28932
|
}
|
|
28905
|
-
var FIREBASE_CATALOG_URL, _memCache = null, _warmPromise = null;
|
|
28933
|
+
var FIREBASE_CATALOG_URL, _memCache = null, _catalogEntriesForTest, _warmPromise = null;
|
|
28906
28934
|
var init_catalog_client = __esm(() => {
|
|
28907
28935
|
init_all_models_cache();
|
|
28908
28936
|
FIREBASE_CATALOG_URL = process.env.CLAUDISH_CATALOG_URL ?? process.env.FIREBASE_CATALOG_URL ?? "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000";
|
|
@@ -39714,6 +39742,24 @@ var init_devin_stream_head_sniffer = __esm(() => {
|
|
|
39714
39742
|
QUOTA_MESSAGE_RE = /quota|out of credits|credit balance|billing|plan limit|exceeded your/i;
|
|
39715
39743
|
});
|
|
39716
39744
|
|
|
39745
|
+
// src/handlers/shared/model-unsupported.ts
|
|
39746
|
+
function hasModelUnsupportedWording(errorBody) {
|
|
39747
|
+
const lower = (errorBody || "").toLowerCase();
|
|
39748
|
+
return UNSUPPORTED_PHRASES.some((phrase) => lower.includes(phrase));
|
|
39749
|
+
}
|
|
39750
|
+
var UNSUPPORTED_PHRASES;
|
|
39751
|
+
var init_model_unsupported = __esm(() => {
|
|
39752
|
+
UNSUPPORTED_PHRASES = [
|
|
39753
|
+
"not supported",
|
|
39754
|
+
"unsupported model",
|
|
39755
|
+
"unsupported_model",
|
|
39756
|
+
"model not found",
|
|
39757
|
+
"model_not_found",
|
|
39758
|
+
"unknown model",
|
|
39759
|
+
"no such model"
|
|
39760
|
+
];
|
|
39761
|
+
});
|
|
39762
|
+
|
|
39717
39763
|
// src/handlers/shared/stream-head-sniffer.ts
|
|
39718
39764
|
function isRetryableStreamError(code, type, message) {
|
|
39719
39765
|
if (RETRYABLE_ERROR_CODES.has(code))
|
|
@@ -41668,6 +41714,33 @@ var init_token_tracker = __esm(() => {
|
|
|
41668
41714
|
init_remote_provider_types();
|
|
41669
41715
|
});
|
|
41670
41716
|
|
|
41717
|
+
// src/handlers/shared/upstream-error-capture.ts
|
|
41718
|
+
import { appendFileSync as appendFileSync4 } from "fs";
|
|
41719
|
+
function captureUpstreamError(record4) {
|
|
41720
|
+
const path = process.env[UPSTREAM_ERROR_LOG_ENV];
|
|
41721
|
+
if (!path)
|
|
41722
|
+
return false;
|
|
41723
|
+
try {
|
|
41724
|
+
const raw = record4.body ?? "";
|
|
41725
|
+
const truncated = raw.length > MAX_CAPTURED_BODY_BYTES;
|
|
41726
|
+
const line = JSON.stringify({
|
|
41727
|
+
at: record4.at ?? new Date().toISOString(),
|
|
41728
|
+
provider: record4.provider,
|
|
41729
|
+
model: record4.model,
|
|
41730
|
+
status: record4.status,
|
|
41731
|
+
body: truncated ? raw.slice(0, MAX_CAPTURED_BODY_BYTES) : raw,
|
|
41732
|
+
...truncated ? { truncated: true, original_bytes: raw.length } : {}
|
|
41733
|
+
});
|
|
41734
|
+
appendFileSync4(path, `${line}
|
|
41735
|
+
`);
|
|
41736
|
+
return true;
|
|
41737
|
+
} catch {
|
|
41738
|
+
return false;
|
|
41739
|
+
}
|
|
41740
|
+
}
|
|
41741
|
+
var UPSTREAM_ERROR_LOG_ENV = "CLAUDISH_UPSTREAM_ERROR_LOG", MAX_CAPTURED_BODY_BYTES = 2048;
|
|
41742
|
+
var init_upstream_error_capture = () => {};
|
|
41743
|
+
|
|
41671
41744
|
// src/handlers/composed-handler.ts
|
|
41672
41745
|
function extractAuthHeaders(c) {
|
|
41673
41746
|
const headers = c.req.header();
|
|
@@ -42067,6 +42140,12 @@ class ComposedHandler {
|
|
|
42067
42140
|
} else {
|
|
42068
42141
|
const errorText = await response.text();
|
|
42069
42142
|
log(`[${this.provider.displayName}] Error: ${errorText}`);
|
|
42143
|
+
captureUpstreamError({
|
|
42144
|
+
provider: this.provider.displayName,
|
|
42145
|
+
model: this.bareModelName,
|
|
42146
|
+
status: response.status,
|
|
42147
|
+
body: errorText
|
|
42148
|
+
});
|
|
42070
42149
|
const transportTerminal = this.provider.classifyTerminalError?.(response.status, errorText);
|
|
42071
42150
|
const hint = getRecoveryHint(response.status, errorText, this.provider.displayName, transportTerminal);
|
|
42072
42151
|
let parsedErrorBody;
|
|
@@ -42515,7 +42594,7 @@ function getRecoveryHint(status, errorText, providerName, transportTerminal429)
|
|
|
42515
42594
|
return "Rate limited. Wait, reduce concurrency, or check plan limits.";
|
|
42516
42595
|
}
|
|
42517
42596
|
if (status === 401 || status === 403) {
|
|
42518
|
-
if (
|
|
42597
|
+
if (hasModelUnsupportedWording(errorText)) {
|
|
42519
42598
|
return "Model not supported by this provider. Verify model name.";
|
|
42520
42599
|
}
|
|
42521
42600
|
if (isQuotaExhaustionError(status, errorText)) {
|
|
@@ -42558,6 +42637,7 @@ var init_composed_handler = __esm(() => {
|
|
|
42558
42637
|
init_collect_sse_message();
|
|
42559
42638
|
init_connection_error();
|
|
42560
42639
|
init_devin_stream_head_sniffer();
|
|
42640
|
+
init_model_unsupported();
|
|
42561
42641
|
init_openai_compat();
|
|
42562
42642
|
init_quota_exhaustion();
|
|
42563
42643
|
init_stream_head_sniffer();
|
|
@@ -42568,6 +42648,7 @@ var init_composed_handler = __esm(() => {
|
|
|
42568
42648
|
init_openai_responses_sse();
|
|
42569
42649
|
init_openai_sse();
|
|
42570
42650
|
init_token_tracker();
|
|
42651
|
+
init_upstream_error_capture();
|
|
42571
42652
|
STREAM_RETRY_DELAYS_MS = [3000, 15000, 30000];
|
|
42572
42653
|
});
|
|
42573
42654
|
|
|
@@ -43277,6 +43358,11 @@ async function discoverProviderModels(providerName) {
|
|
|
43277
43358
|
return recordFailure({ kind: "no-credentials", provider: providerName, endpoint });
|
|
43278
43359
|
}
|
|
43279
43360
|
}
|
|
43361
|
+
headers = {
|
|
43362
|
+
"User-Agent": `claudish/${VERSION}`,
|
|
43363
|
+
...def.headers ?? {},
|
|
43364
|
+
...headers
|
|
43365
|
+
};
|
|
43280
43366
|
let response;
|
|
43281
43367
|
try {
|
|
43282
43368
|
response = await fetch(endpoint, {
|
|
@@ -44954,6 +45040,7 @@ var init_default_routing_rules = __esm(() => {
|
|
|
44954
45040
|
"ministral-*": ["mistralai", "openrouter"],
|
|
44955
45041
|
"codestral-*": ["mistralai", "openrouter"],
|
|
44956
45042
|
"labs-*": ["mistralai"],
|
|
45043
|
+
"swe-*": ["devin"],
|
|
44957
45044
|
fugu: ["sakana-subscription", "sakana"],
|
|
44958
45045
|
"fugu-*": ["sakana-subscription", "sakana"],
|
|
44959
45046
|
"*-zen": ["opencode-zen"],
|
|
@@ -45558,7 +45645,7 @@ var init_prehydrate = __esm(() => {
|
|
|
45558
45645
|
});
|
|
45559
45646
|
|
|
45560
45647
|
// src/channel/diagnostics.ts
|
|
45561
|
-
import { appendFileSync as
|
|
45648
|
+
import { appendFileSync as appendFileSync5 } from "fs";
|
|
45562
45649
|
function traceEnabled() {
|
|
45563
45650
|
return process.env[TRACE_ENV] === "1";
|
|
45564
45651
|
}
|
|
@@ -45569,7 +45656,7 @@ function emit(line) {
|
|
|
45569
45656
|
const filePath = process.env[TRACE_FILE_ENV];
|
|
45570
45657
|
if (filePath) {
|
|
45571
45658
|
try {
|
|
45572
|
-
|
|
45659
|
+
appendFileSync5(filePath, formatted);
|
|
45573
45660
|
} catch {}
|
|
45574
45661
|
}
|
|
45575
45662
|
}
|
|
@@ -46369,6 +46456,15 @@ function normalizePricingDisplay(raw) {
|
|
|
46369
46456
|
return "FREE";
|
|
46370
46457
|
return pricing;
|
|
46371
46458
|
}
|
|
46459
|
+
function formatListingPrice(entry, opts) {
|
|
46460
|
+
const rate = normalizePricingDisplay(entry.pricing?.average);
|
|
46461
|
+
if (rate !== "N/A")
|
|
46462
|
+
return rate;
|
|
46463
|
+
const plan = entry.subscription?.plan;
|
|
46464
|
+
if (!plan)
|
|
46465
|
+
return "N/A";
|
|
46466
|
+
return opts?.compact ? "SUB" : `SUB (${plan})`;
|
|
46467
|
+
}
|
|
46372
46468
|
function computeQuickPicks(primaries) {
|
|
46373
46469
|
if (primaries.length === 0) {
|
|
46374
46470
|
return {
|
|
@@ -46603,6 +46699,412 @@ async function isPortAvailable(port) {
|
|
|
46603
46699
|
}
|
|
46604
46700
|
var init_port_manager = () => {};
|
|
46605
46701
|
|
|
46702
|
+
// src/providers/probe-live.ts
|
|
46703
|
+
function effortForProvider(provider) {
|
|
46704
|
+
return MINIMAL_EFFORT_UNSUPPORTED.has(provider) ? "low" : "minimal";
|
|
46705
|
+
}
|
|
46706
|
+
async function probeLink(proxyUrl, link, timeoutMs) {
|
|
46707
|
+
const isOAuth = OAUTH_PROVIDERS2.has(link.provider);
|
|
46708
|
+
if (!link.hasCredentials && !isOAuth) {
|
|
46709
|
+
return {
|
|
46710
|
+
state: "key-missing",
|
|
46711
|
+
latencyMs: 0,
|
|
46712
|
+
errorMessage: link.credentialHint
|
|
46713
|
+
};
|
|
46714
|
+
}
|
|
46715
|
+
const startedAt = Date.now();
|
|
46716
|
+
let response;
|
|
46717
|
+
try {
|
|
46718
|
+
response = await fetch(`${proxyUrl}/v1/messages`, {
|
|
46719
|
+
method: "POST",
|
|
46720
|
+
headers: {
|
|
46721
|
+
"Content-Type": "application/json"
|
|
46722
|
+
},
|
|
46723
|
+
body: JSON.stringify({
|
|
46724
|
+
model: link.modelSpec,
|
|
46725
|
+
system: "You are a helpful assistant.",
|
|
46726
|
+
messages: [{ role: "user", content: PROBE_PROMPT }],
|
|
46727
|
+
max_tokens: PROBE_MAX_TOKENS,
|
|
46728
|
+
output_config: { effort: effortForProvider(link.provider) },
|
|
46729
|
+
stream: true
|
|
46730
|
+
}),
|
|
46731
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
46732
|
+
});
|
|
46733
|
+
} catch (e) {
|
|
46734
|
+
const latencyMs = Date.now() - startedAt;
|
|
46735
|
+
const name = e?.name || "";
|
|
46736
|
+
const msg2 = String(e?.message || e);
|
|
46737
|
+
if (name === "TimeoutError" || name === "AbortError" || /timeout/i.test(msg2)) {
|
|
46738
|
+
return { state: "timeout", latencyMs, errorMessage: msg2 };
|
|
46739
|
+
}
|
|
46740
|
+
return { state: "network-error", latencyMs, errorMessage: msg2 };
|
|
46741
|
+
}
|
|
46742
|
+
const ttfbMs = Date.now() - startedAt;
|
|
46743
|
+
if (!response.ok) {
|
|
46744
|
+
const body = await safeReadBody(response);
|
|
46745
|
+
return annotateOAuthHint(classifyHttpError(response.status, body, ttfbMs), link.provider, isOAuth);
|
|
46746
|
+
}
|
|
46747
|
+
const streamResult = await consumeProbeStream(response, timeoutMs, startedAt);
|
|
46748
|
+
const totalMs = Date.now() - startedAt;
|
|
46749
|
+
let timing2;
|
|
46750
|
+
if (streamResult.state === "live" && streamResult.ttftMs !== undefined && !streamResult.truncated) {
|
|
46751
|
+
const ttftMs = streamResult.ttftMs;
|
|
46752
|
+
const tokens = streamResult.tokens ?? 0;
|
|
46753
|
+
const streamMs = Math.max(STREAM_MS_FLOOR, totalMs - ttftMs);
|
|
46754
|
+
const tokensPerSec = tokens > 0 ? tokens / streamMs * 1000 : 0;
|
|
46755
|
+
timing2 = { ttfbMs, ttftMs, totalMs, tokens, tokensPerSec };
|
|
46756
|
+
}
|
|
46757
|
+
const { ttftMs: _ttft, tokens: _tok, truncated: _trunc, ...rest } = streamResult;
|
|
46758
|
+
return annotateOAuthHint({
|
|
46759
|
+
...rest,
|
|
46760
|
+
latencyMs: totalMs,
|
|
46761
|
+
timing: timing2
|
|
46762
|
+
}, link.provider, isOAuth);
|
|
46763
|
+
}
|
|
46764
|
+
function annotateOAuthHint(result, provider, isOAuth) {
|
|
46765
|
+
if (!isOAuth)
|
|
46766
|
+
return result;
|
|
46767
|
+
if (result.state === "live")
|
|
46768
|
+
return result;
|
|
46769
|
+
const loginCommand = provider === "antigravity" ? "claudish login antigravity" : provider === "vertex" ? "gcloud auth application-default login" : provider === "devin" ? "devin login" : undefined;
|
|
46770
|
+
if (!loginCommand)
|
|
46771
|
+
return result;
|
|
46772
|
+
if (result.httpStatus === 403)
|
|
46773
|
+
return result;
|
|
46774
|
+
const looksLikeAuthFailure = result.state === "auth-failed" || /auth|token|login|credential|unauthor/i.test(result.errorMessage || "");
|
|
46775
|
+
if (!looksLikeAuthFailure)
|
|
46776
|
+
return result;
|
|
46777
|
+
return {
|
|
46778
|
+
...result,
|
|
46779
|
+
state: "auth-failed",
|
|
46780
|
+
actionHint: `run: ${loginCommand}`
|
|
46781
|
+
};
|
|
46782
|
+
}
|
|
46783
|
+
async function safeReadBody(response) {
|
|
46784
|
+
try {
|
|
46785
|
+
const text = await response.text();
|
|
46786
|
+
return text.slice(0, 500);
|
|
46787
|
+
} catch {
|
|
46788
|
+
return "";
|
|
46789
|
+
}
|
|
46790
|
+
}
|
|
46791
|
+
function extractUpstreamStatus(body) {
|
|
46792
|
+
if (!body)
|
|
46793
|
+
return;
|
|
46794
|
+
try {
|
|
46795
|
+
const parsed = JSON.parse(body);
|
|
46796
|
+
const status = parsed?.error?.upstream_status;
|
|
46797
|
+
return typeof status === "number" ? status : undefined;
|
|
46798
|
+
} catch {
|
|
46799
|
+
return;
|
|
46800
|
+
}
|
|
46801
|
+
}
|
|
46802
|
+
function extractErrorType(body) {
|
|
46803
|
+
if (!body)
|
|
46804
|
+
return;
|
|
46805
|
+
try {
|
|
46806
|
+
const parsed = JSON.parse(body);
|
|
46807
|
+
const t = parsed?.error?.type;
|
|
46808
|
+
return typeof t === "string" ? t : undefined;
|
|
46809
|
+
} catch {
|
|
46810
|
+
return;
|
|
46811
|
+
}
|
|
46812
|
+
}
|
|
46813
|
+
function classifyHttpError(status, body, latencyMs) {
|
|
46814
|
+
const lowered = body.toLowerCase();
|
|
46815
|
+
if (extractErrorType(body) === "connection_error") {
|
|
46816
|
+
return {
|
|
46817
|
+
state: "network-error",
|
|
46818
|
+
latencyMs,
|
|
46819
|
+
httpStatus: status,
|
|
46820
|
+
errorMessage: extractErrorMessage(body) || "Cannot reach provider"
|
|
46821
|
+
};
|
|
46822
|
+
}
|
|
46823
|
+
const upstream = status === 400 ? extractUpstreamStatus(body) : undefined;
|
|
46824
|
+
if (status === 401 || status === 403 || upstream === 401 || upstream === 403) {
|
|
46825
|
+
const authStatus = upstream ?? status;
|
|
46826
|
+
if (hasModelUnsupportedWording(body)) {
|
|
46827
|
+
return {
|
|
46828
|
+
state: "model-not-found",
|
|
46829
|
+
latencyMs,
|
|
46830
|
+
httpStatus: authStatus,
|
|
46831
|
+
errorMessage: extractErrorMessage(body) || `HTTP ${authStatus}`
|
|
46832
|
+
};
|
|
46833
|
+
}
|
|
46834
|
+
return {
|
|
46835
|
+
state: "auth-failed",
|
|
46836
|
+
latencyMs,
|
|
46837
|
+
httpStatus: authStatus,
|
|
46838
|
+
errorMessage: extractErrorMessage(body) || `HTTP ${authStatus}`
|
|
46839
|
+
};
|
|
46840
|
+
}
|
|
46841
|
+
if (status === 404 || /model[_ ]not[_ ]found|no such model|unknown model/.test(lowered)) {
|
|
46842
|
+
return {
|
|
46843
|
+
state: "model-not-found",
|
|
46844
|
+
latencyMs,
|
|
46845
|
+
httpStatus: status,
|
|
46846
|
+
errorMessage: extractErrorMessage(body) || `HTTP ${status}`
|
|
46847
|
+
};
|
|
46848
|
+
}
|
|
46849
|
+
if (status === 429) {
|
|
46850
|
+
return {
|
|
46851
|
+
state: "rate-limited",
|
|
46852
|
+
latencyMs,
|
|
46853
|
+
httpStatus: status,
|
|
46854
|
+
errorMessage: extractErrorMessage(body) || "Rate limited"
|
|
46855
|
+
};
|
|
46856
|
+
}
|
|
46857
|
+
if (upstream === 429 || status === 402) {
|
|
46858
|
+
return {
|
|
46859
|
+
state: "out-of-credit",
|
|
46860
|
+
latencyMs,
|
|
46861
|
+
httpStatus: upstream ?? status,
|
|
46862
|
+
errorMessage: extractErrorMessage(body) || "Out of credit \u2014 account balance or plan exhausted"
|
|
46863
|
+
};
|
|
46864
|
+
}
|
|
46865
|
+
if (status >= 500) {
|
|
46866
|
+
return {
|
|
46867
|
+
state: "server-error",
|
|
46868
|
+
latencyMs,
|
|
46869
|
+
httpStatus: status,
|
|
46870
|
+
errorMessage: extractErrorMessage(body) || `HTTP ${status}`
|
|
46871
|
+
};
|
|
46872
|
+
}
|
|
46873
|
+
return {
|
|
46874
|
+
state: "error",
|
|
46875
|
+
latencyMs,
|
|
46876
|
+
httpStatus: status,
|
|
46877
|
+
errorMessage: extractErrorMessage(body) || `HTTP ${status}`
|
|
46878
|
+
};
|
|
46879
|
+
}
|
|
46880
|
+
function extractErrorMessage(body) {
|
|
46881
|
+
if (!body)
|
|
46882
|
+
return;
|
|
46883
|
+
try {
|
|
46884
|
+
const parsed = JSON.parse(body);
|
|
46885
|
+
const msg2 = parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || parsed?.detail;
|
|
46886
|
+
if (typeof msg2 === "string" && msg2.length > 0) {
|
|
46887
|
+
return msg2.length > 160 ? `${msg2.slice(0, 157)}...` : msg2;
|
|
46888
|
+
}
|
|
46889
|
+
} catch {}
|
|
46890
|
+
const trimmed2 = body.trim();
|
|
46891
|
+
if (!trimmed2)
|
|
46892
|
+
return;
|
|
46893
|
+
return trimmed2.length > 160 ? `${trimmed2.slice(0, 157)}...` : trimmed2;
|
|
46894
|
+
}
|
|
46895
|
+
async function consumeProbeStream(response, timeoutMs, startedAt) {
|
|
46896
|
+
const body = response.body;
|
|
46897
|
+
if (!body) {
|
|
46898
|
+
return { state: "error", errorMessage: "empty response body" };
|
|
46899
|
+
}
|
|
46900
|
+
const reader = body.getReader();
|
|
46901
|
+
const decoder = new TextDecoder;
|
|
46902
|
+
let buffered = "";
|
|
46903
|
+
const deadline = Date.now() + timeoutMs;
|
|
46904
|
+
let ttftMs;
|
|
46905
|
+
let sawContent = false;
|
|
46906
|
+
let textChars = 0;
|
|
46907
|
+
let reportedTokens;
|
|
46908
|
+
let stopReason;
|
|
46909
|
+
let errorVerdict = null;
|
|
46910
|
+
let completed = false;
|
|
46911
|
+
try {
|
|
46912
|
+
while (Date.now() < deadline) {
|
|
46913
|
+
const { value, done } = await reader.read();
|
|
46914
|
+
if (done) {
|
|
46915
|
+
completed = true;
|
|
46916
|
+
break;
|
|
46917
|
+
}
|
|
46918
|
+
buffered += decoder.decode(value, { stream: true });
|
|
46919
|
+
const events = buffered.split(`
|
|
46920
|
+
|
|
46921
|
+
`);
|
|
46922
|
+
buffered = events.pop() ?? "";
|
|
46923
|
+
for (const event of events) {
|
|
46924
|
+
const verdict = interpretSseEvent(event);
|
|
46925
|
+
if (verdict && typeof verdict === "object" && verdict.state !== "live") {
|
|
46926
|
+
errorVerdict = verdict;
|
|
46927
|
+
break;
|
|
46928
|
+
}
|
|
46929
|
+
const acct = accountStreamEvent(event);
|
|
46930
|
+
if (acct.contentDelta) {
|
|
46931
|
+
if (ttftMs === undefined)
|
|
46932
|
+
ttftMs = Date.now() - startedAt;
|
|
46933
|
+
sawContent = true;
|
|
46934
|
+
}
|
|
46935
|
+
if (acct.textChars)
|
|
46936
|
+
textChars += acct.textChars;
|
|
46937
|
+
if (acct.outputTokens !== undefined)
|
|
46938
|
+
reportedTokens = acct.outputTokens;
|
|
46939
|
+
if (acct.stopReason)
|
|
46940
|
+
stopReason = acct.stopReason;
|
|
46941
|
+
}
|
|
46942
|
+
if (errorVerdict)
|
|
46943
|
+
break;
|
|
46944
|
+
}
|
|
46945
|
+
} catch (e) {
|
|
46946
|
+
if (!sawContent) {
|
|
46947
|
+
return { state: "network-error", errorMessage: String(e?.message || e) };
|
|
46948
|
+
}
|
|
46949
|
+
} finally {
|
|
46950
|
+
try {
|
|
46951
|
+
await reader.cancel();
|
|
46952
|
+
} catch {}
|
|
46953
|
+
}
|
|
46954
|
+
if (errorVerdict)
|
|
46955
|
+
return errorVerdict;
|
|
46956
|
+
if (sawContent) {
|
|
46957
|
+
const tokens = reportedTokens ?? Math.max(1, Math.round(textChars / 4));
|
|
46958
|
+
return { state: "live", ttftMs, tokens, truncated: !completed };
|
|
46959
|
+
}
|
|
46960
|
+
const truncationReason = stopReason === "max_tokens" || stopReason === "length" ? stopReason : undefined;
|
|
46961
|
+
if (truncationReason || reportedTokens !== undefined && reportedTokens >= PROBE_MAX_TOKENS) {
|
|
46962
|
+
const cause = truncationReason ? `finish: ${truncationReason}` : `${reportedTokens} tokens consumed, none visible`;
|
|
46963
|
+
return {
|
|
46964
|
+
state: "error",
|
|
46965
|
+
errorMessage: `no visible output within probe budget (${cause})`
|
|
46966
|
+
};
|
|
46967
|
+
}
|
|
46968
|
+
return { state: "error", errorMessage: "stream ended without content" };
|
|
46969
|
+
}
|
|
46970
|
+
function accountStreamEvent(rawEvent) {
|
|
46971
|
+
let dataPayload = "";
|
|
46972
|
+
for (const line of rawEvent.split(`
|
|
46973
|
+
`)) {
|
|
46974
|
+
if (line.startsWith("data:"))
|
|
46975
|
+
dataPayload += line.slice(5).trim();
|
|
46976
|
+
}
|
|
46977
|
+
if (!dataPayload || dataPayload === "[DONE]") {
|
|
46978
|
+
return { contentDelta: false, textChars: 0 };
|
|
46979
|
+
}
|
|
46980
|
+
let parsed;
|
|
46981
|
+
try {
|
|
46982
|
+
parsed = JSON.parse(dataPayload);
|
|
46983
|
+
} catch {
|
|
46984
|
+
return { contentDelta: false, textChars: 0 };
|
|
46985
|
+
}
|
|
46986
|
+
let textChars = 0;
|
|
46987
|
+
let contentDelta = false;
|
|
46988
|
+
const text = parsed?.delta?.text ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.delta?.content : undefined);
|
|
46989
|
+
if (typeof text === "string" && text.length > 0) {
|
|
46990
|
+
contentDelta = true;
|
|
46991
|
+
textChars = text.length;
|
|
46992
|
+
} else if (parsed?.type === "content_block_delta" || parsed?.type === "content_block_start") {
|
|
46993
|
+
contentDelta = true;
|
|
46994
|
+
}
|
|
46995
|
+
const outputTokens = parsed?.usage?.output_tokens ?? parsed?.message?.usage?.output_tokens ?? parsed?.usage?.completion_tokens;
|
|
46996
|
+
const stopReason = parsed?.delta?.stop_reason ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.finish_reason : undefined);
|
|
46997
|
+
return {
|
|
46998
|
+
contentDelta,
|
|
46999
|
+
textChars,
|
|
47000
|
+
outputTokens: typeof outputTokens === "number" ? outputTokens : undefined,
|
|
47001
|
+
stopReason: typeof stopReason === "string" ? stopReason : undefined
|
|
47002
|
+
};
|
|
47003
|
+
}
|
|
47004
|
+
function interpretSseEvent(rawEvent) {
|
|
47005
|
+
const lines = rawEvent.split(`
|
|
47006
|
+
`);
|
|
47007
|
+
let eventType = "";
|
|
47008
|
+
let dataPayload = "";
|
|
47009
|
+
for (const line of lines) {
|
|
47010
|
+
if (line.startsWith("event:"))
|
|
47011
|
+
eventType = line.slice(6).trim();
|
|
47012
|
+
else if (line.startsWith("data:"))
|
|
47013
|
+
dataPayload += line.slice(5).trim();
|
|
47014
|
+
}
|
|
47015
|
+
if (!dataPayload)
|
|
47016
|
+
return null;
|
|
47017
|
+
if (dataPayload === "[DONE]")
|
|
47018
|
+
return null;
|
|
47019
|
+
let parsed;
|
|
47020
|
+
try {
|
|
47021
|
+
parsed = JSON.parse(dataPayload);
|
|
47022
|
+
} catch {
|
|
47023
|
+
return null;
|
|
47024
|
+
}
|
|
47025
|
+
if (parsed?.type === "error" || eventType === "error" || parsed?.error) {
|
|
47026
|
+
const message = parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || "provider returned error event";
|
|
47027
|
+
const status = parsed?.error?.status || parsed?.status;
|
|
47028
|
+
if (typeof status === "number") {
|
|
47029
|
+
return {
|
|
47030
|
+
state: status === 401 || status === 403 ? "auth-failed" : "error",
|
|
47031
|
+
httpStatus: status,
|
|
47032
|
+
errorMessage: message
|
|
47033
|
+
};
|
|
47034
|
+
}
|
|
47035
|
+
return { state: "error", errorMessage: message };
|
|
47036
|
+
}
|
|
47037
|
+
if (isContentEvent(parsed, eventType)) {
|
|
47038
|
+
return "live";
|
|
47039
|
+
}
|
|
47040
|
+
return null;
|
|
47041
|
+
}
|
|
47042
|
+
function isContentEvent(parsed, eventType) {
|
|
47043
|
+
if (eventType === "content_block_start" || eventType === "content_block_delta")
|
|
47044
|
+
return true;
|
|
47045
|
+
if (eventType === "message_start")
|
|
47046
|
+
return true;
|
|
47047
|
+
if (parsed?.type === "content_block_start")
|
|
47048
|
+
return true;
|
|
47049
|
+
if (parsed?.type === "content_block_delta")
|
|
47050
|
+
return true;
|
|
47051
|
+
if (parsed?.type === "message_start")
|
|
47052
|
+
return true;
|
|
47053
|
+
if (parsed?.type === "message_delta")
|
|
47054
|
+
return true;
|
|
47055
|
+
if (Array.isArray(parsed?.choices) && parsed.choices.length > 0) {
|
|
47056
|
+
const choice = parsed.choices[0];
|
|
47057
|
+
if (choice?.delta || choice?.message || choice?.text || choice?.finish_reason)
|
|
47058
|
+
return true;
|
|
47059
|
+
}
|
|
47060
|
+
if (parsed?.candidates)
|
|
47061
|
+
return true;
|
|
47062
|
+
return false;
|
|
47063
|
+
}
|
|
47064
|
+
function withDetail(base, message) {
|
|
47065
|
+
return message ? `${base} \u2014 ${message}` : base;
|
|
47066
|
+
}
|
|
47067
|
+
function describeProbeState(result) {
|
|
47068
|
+
const status = result.httpStatus ?? "";
|
|
47069
|
+
const latency = result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : "";
|
|
47070
|
+
switch (result.state) {
|
|
47071
|
+
case "live":
|
|
47072
|
+
return `live \xB7 ${result.latencyMs}ms`;
|
|
47073
|
+
case "key-missing":
|
|
47074
|
+
return result.errorMessage ? `missing (${result.errorMessage})` : "missing";
|
|
47075
|
+
case "auth-failed":
|
|
47076
|
+
return withDetail(`auth failed \xB7 ${status}${latency}`.trim(), result.errorMessage);
|
|
47077
|
+
case "model-not-found":
|
|
47078
|
+
return withDetail(`model not found \xB7 ${status}${latency}`.trim(), result.errorMessage);
|
|
47079
|
+
case "rate-limited":
|
|
47080
|
+
return withDetail(`rate limited \xB7 ${result.latencyMs}ms`, result.errorMessage);
|
|
47081
|
+
case "out-of-credit":
|
|
47082
|
+
return withDetail(`out of credit \xB7 ${status}${latency}`.trim(), result.errorMessage);
|
|
47083
|
+
case "server-error":
|
|
47084
|
+
return withDetail(`server error \xB7 ${status} \xB7 ${result.latencyMs}ms`, result.errorMessage);
|
|
47085
|
+
case "timeout":
|
|
47086
|
+
return withDetail(`timeout \xB7 ${result.latencyMs}ms`, result.errorMessage);
|
|
47087
|
+
case "network-error":
|
|
47088
|
+
return withDetail(`network error \xB7 ${result.latencyMs}ms`, result.errorMessage);
|
|
47089
|
+
case "error": {
|
|
47090
|
+
const base = `error${result.httpStatus ? ` \xB7 ${result.httpStatus}` : ""}${latency}`;
|
|
47091
|
+
return withDetail(base, result.errorMessage);
|
|
47092
|
+
}
|
|
47093
|
+
}
|
|
47094
|
+
}
|
|
47095
|
+
function isReadyState(state) {
|
|
47096
|
+
return state === "live";
|
|
47097
|
+
}
|
|
47098
|
+
function isFailureState(state) {
|
|
47099
|
+
return state === "auth-failed" || state === "model-not-found" || state === "rate-limited" || state === "out-of-credit" || state === "server-error" || state === "timeout" || state === "network-error" || state === "error";
|
|
47100
|
+
}
|
|
47101
|
+
var STREAM_MS_FLOOR = 50, OAUTH_PROVIDERS2, PROBE_PROMPT = "Count from one to twenty in words, one per line.", PROBE_MAX_TOKENS = 512, MINIMAL_EFFORT_UNSUPPORTED;
|
|
47102
|
+
var init_probe_live = __esm(() => {
|
|
47103
|
+
init_model_unsupported();
|
|
47104
|
+
OAUTH_PROVIDERS2 = new Set(["vertex", "antigravity", "devin"]);
|
|
47105
|
+
MINIMAL_EFFORT_UNSUPPORTED = new Set(["native-anthropic", "anthropic"]);
|
|
47106
|
+
});
|
|
47107
|
+
|
|
46606
47108
|
// ../../node_modules/.bun/hono@4.10.6/node_modules/hono/dist/compose.js
|
|
46607
47109
|
var compose = (middleware, onError, onNotFound) => {
|
|
46608
47110
|
return (context, next) => {
|
|
@@ -48707,7 +49209,7 @@ var init_fallback_handler = __esm(() => {
|
|
|
48707
49209
|
});
|
|
48708
49210
|
|
|
48709
49211
|
// src/handlers/native-handler-advisor.ts
|
|
48710
|
-
import { appendFileSync as
|
|
49212
|
+
import { appendFileSync as appendFileSync6 } from "fs";
|
|
48711
49213
|
function loadAdvisorSwapConfig(cliModels, cliCollector) {
|
|
48712
49214
|
return {
|
|
48713
49215
|
enabled: process.env.CLAUDISH_SWAP_ADVISOR === "1" || (cliModels?.length ?? 0) > 0,
|
|
@@ -48762,7 +49264,7 @@ function logAdvisorEvent(cfg, event) {
|
|
|
48762
49264
|
const line = `${JSON.stringify({ ts: new Date().toISOString(), ...event })}
|
|
48763
49265
|
`;
|
|
48764
49266
|
try {
|
|
48765
|
-
|
|
49267
|
+
appendFileSync6(cfg.logPath, line);
|
|
48766
49268
|
} catch {}
|
|
48767
49269
|
}
|
|
48768
49270
|
function recordAdvisorEventsFromChunk(cfg, chunkText) {
|
|
@@ -51563,11 +52065,30 @@ ${plan.hint}` : `[Route] ${plan.reason}`;
|
|
|
51563
52065
|
}));
|
|
51564
52066
|
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
51565
52067
|
const servedSlotIds = options.servedSlotIds ?? [];
|
|
52068
|
+
const discoverableModelIds = (() => {
|
|
52069
|
+
const seen = new Set;
|
|
52070
|
+
try {
|
|
52071
|
+
const cfg = loadConfig();
|
|
52072
|
+
for (const name of Object.keys(cfg.routing ?? {})) {
|
|
52073
|
+
if (name === "*")
|
|
52074
|
+
continue;
|
|
52075
|
+
seen.add(name);
|
|
52076
|
+
}
|
|
52077
|
+
for (const ep of Object.values(cfg.customEndpoints ?? {})) {
|
|
52078
|
+
for (const m of ep.models ?? [])
|
|
52079
|
+
seen.add(m);
|
|
52080
|
+
}
|
|
52081
|
+
} catch (err) {
|
|
52082
|
+
log(`[Proxy] /v1/models discovery skipped: ${err instanceof Error ? err.message : err}`);
|
|
52083
|
+
}
|
|
52084
|
+
return [...seen];
|
|
52085
|
+
})();
|
|
51566
52086
|
app.get("/v1/models", (c) => {
|
|
52087
|
+
const ids = servedSlotIds.length > 0 ? servedSlotIds : discoverableModelIds;
|
|
51567
52088
|
return c.json({
|
|
51568
52089
|
object: "list",
|
|
51569
52090
|
has_more: false,
|
|
51570
|
-
data:
|
|
52091
|
+
data: ids.map((id) => ({
|
|
51571
52092
|
id,
|
|
51572
52093
|
object: "model",
|
|
51573
52094
|
type: "model",
|
|
@@ -53052,7 +53573,7 @@ Tokens: ${result.usage.input} input, ${result.usage.output} output`;
|
|
|
53052
53573
|
};
|
|
53053
53574
|
const renderGroup = (group) => {
|
|
53054
53575
|
const m = group.primary;
|
|
53055
|
-
const pricing =
|
|
53576
|
+
const pricing = formatListingPrice(m);
|
|
53056
53577
|
const ctx = m.context || "N/A";
|
|
53057
53578
|
const caps = [];
|
|
53058
53579
|
if (m.supportsTools)
|
|
@@ -53257,6 +53778,112 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
|
|
|
53257
53778
|
return { content: [{ type: "text", text: output }] };
|
|
53258
53779
|
}
|
|
53259
53780
|
});
|
|
53781
|
+
tools.push({
|
|
53782
|
+
name: "preflight",
|
|
53783
|
+
description: "Check a roster of models BEFORE spending a run on it. For each model: which provider " + "will actually serve it, whether that hop is covered by a SUBSCRIPTION or billed per " + "token, and whether it is reachable right now. Call this before `team` or a batch of " + "`create_session` calls \u2014 a dead or unexpectedly-metered model is then caught while " + "the roster can still be adjusted, instead of costing a slot minutes into the run.",
|
|
53784
|
+
inputSchema: {
|
|
53785
|
+
type: "object",
|
|
53786
|
+
properties: {
|
|
53787
|
+
models: {
|
|
53788
|
+
type: "array",
|
|
53789
|
+
items: { type: "string" },
|
|
53790
|
+
description: "Model ids to check \u2014 bare (`glm-5.2`) or explicit (`dv@swe-1.7`). Bare names go " + "through the SAME routing rules and credential filter a real run would use, so " + "the provider reported here is the provider that would serve it."
|
|
53791
|
+
},
|
|
53792
|
+
probe: {
|
|
53793
|
+
type: "boolean",
|
|
53794
|
+
description: "Send a real short request to each resolved route (default true). Set false for " + "a routing/billing answer only \u2014 far faster, but it cannot tell you the provider " + "is actually reachable, which is the failure this tool exists to catch."
|
|
53795
|
+
},
|
|
53796
|
+
timeout_ms: {
|
|
53797
|
+
type: "number",
|
|
53798
|
+
description: "Per-model probe timeout in ms (default 20000)."
|
|
53799
|
+
}
|
|
53800
|
+
},
|
|
53801
|
+
required: ["models"]
|
|
53802
|
+
},
|
|
53803
|
+
group: "agentic",
|
|
53804
|
+
heartbeat: true,
|
|
53805
|
+
handler: async (args, ctx) => {
|
|
53806
|
+
const models = Array.isArray(args.models) ? args.models : [];
|
|
53807
|
+
if (models.length === 0) {
|
|
53808
|
+
return {
|
|
53809
|
+
content: [{ type: "text", text: "preflight: no models given." }],
|
|
53810
|
+
isError: true
|
|
53811
|
+
};
|
|
53812
|
+
}
|
|
53813
|
+
const doProbe = args.probe !== false;
|
|
53814
|
+
const timeoutMs = typeof args.timeout_ms === "number" ? args.timeout_ms : 20000;
|
|
53815
|
+
const proxy = doProbe ? await getProxy() : null;
|
|
53816
|
+
const rows = [];
|
|
53817
|
+
const readyModels = [];
|
|
53818
|
+
const failedModels = [];
|
|
53819
|
+
let subCount = 0;
|
|
53820
|
+
let meteredCount = 0;
|
|
53821
|
+
for (const model of models) {
|
|
53822
|
+
ctx.reportProgress(`preflight: ${model}`);
|
|
53823
|
+
let plan;
|
|
53824
|
+
try {
|
|
53825
|
+
plan = await route(model);
|
|
53826
|
+
} catch (err) {
|
|
53827
|
+
failedModels.push(model);
|
|
53828
|
+
rows.push(`| \`${model}\` | \u2014 | \u2014 | \u274C route error | ${err instanceof Error ? err.message : String(err)} |`);
|
|
53829
|
+
continue;
|
|
53830
|
+
}
|
|
53831
|
+
if (plan.kind === "no-route") {
|
|
53832
|
+
failedModels.push(model);
|
|
53833
|
+
rows.push(`| \`${model}\` | \u2014 | \u2014 | \u274C no route | ${plan.reason} |`);
|
|
53834
|
+
continue;
|
|
53835
|
+
}
|
|
53836
|
+
const primary = plan.primary;
|
|
53837
|
+
const billing = isLocalProviderName(primary.provider) ? "local" : isSubscriptionProvider(primary.provider) ? "SUB" : "metered";
|
|
53838
|
+
if (billing === "SUB")
|
|
53839
|
+
subCount++;
|
|
53840
|
+
else if (billing === "metered")
|
|
53841
|
+
meteredCount++;
|
|
53842
|
+
let status = "not probed";
|
|
53843
|
+
let ok = true;
|
|
53844
|
+
if (proxy) {
|
|
53845
|
+
try {
|
|
53846
|
+
const result = await probeLink(proxy.url, {
|
|
53847
|
+
provider: primary.provider,
|
|
53848
|
+
modelSpec: primary.modelSpec,
|
|
53849
|
+
hasCredentials: true
|
|
53850
|
+
}, timeoutMs);
|
|
53851
|
+
ok = isReadyState(result.state);
|
|
53852
|
+
status = ok ? `\u2705 ${result.state}` : `\u274C ${result.state}${result.errorMessage ? ` \u2014 ${result.errorMessage}` : ""}`;
|
|
53853
|
+
} catch (err) {
|
|
53854
|
+
ok = false;
|
|
53855
|
+
status = `\u274C probe error \u2014 ${err instanceof Error ? err.message : String(err)}`;
|
|
53856
|
+
}
|
|
53857
|
+
}
|
|
53858
|
+
if (ok)
|
|
53859
|
+
readyModels.push(model);
|
|
53860
|
+
else
|
|
53861
|
+
failedModels.push(model);
|
|
53862
|
+
const fallbacks = plan.fallbacks.length > 0 ? ` (+${plan.fallbacks.length} fallback)` : "";
|
|
53863
|
+
rows.push(`| \`${model}\` | ${primary.displayName}${fallbacks} | ${billing} | ${status} | \`${primary.modelSpec}\` |`);
|
|
53864
|
+
}
|
|
53865
|
+
const lines = [
|
|
53866
|
+
`# Preflight \u2014 ${models.length} model${models.length === 1 ? "" : "s"}`,
|
|
53867
|
+
"",
|
|
53868
|
+
`**Ready: ${readyModels.length}** \xB7 **Failed: ${failedModels.length}** \xB7 ` + `subscription: ${subCount} \xB7 metered: ${meteredCount}`,
|
|
53869
|
+
"",
|
|
53870
|
+
"| Model | Provider | Billing | Status | Wire id |",
|
|
53871
|
+
"|---|---|---|---|---|",
|
|
53872
|
+
...rows
|
|
53873
|
+
];
|
|
53874
|
+
if (failedModels.length > 0) {
|
|
53875
|
+
lines.push("", `\u26A0\uFE0F Drop or replace before running: ${failedModels.map((m) => `\`${m}\``).join(", ")}`);
|
|
53876
|
+
}
|
|
53877
|
+
if (meteredCount > 0) {
|
|
53878
|
+
lines.push("", `\uD83D\uDCB8 ${meteredCount} model${meteredCount === 1 ? "" : "s"} will be billed PER TOKEN. ` + "A bare name can land on a metered provider when the subscription that covers it " + "has no credential configured \u2014 name the provider explicitly to pin it.");
|
|
53879
|
+
}
|
|
53880
|
+
if (!doProbe) {
|
|
53881
|
+
lines.push("", "\u2139\uFE0F `probe: false` \u2014 routing and billing only. Reachability was NOT checked.");
|
|
53882
|
+
}
|
|
53883
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
53884
|
+
`) }] };
|
|
53885
|
+
}
|
|
53886
|
+
});
|
|
53260
53887
|
tools.push({
|
|
53261
53888
|
name: "team",
|
|
53262
53889
|
description: "Run AI models on a task with anonymized outputs and optional blind judging. Modes: 'run' (execute models), 'judge' (blind-vote on existing outputs), 'run-and-judge' (full pipeline), 'status' (check progress).",
|
|
@@ -53851,11 +54478,15 @@ var init_mcp_server = __esm(() => {
|
|
|
53851
54478
|
init_prehydrate();
|
|
53852
54479
|
init_diagnostics();
|
|
53853
54480
|
init_channel();
|
|
54481
|
+
init_remote_provider_types();
|
|
53854
54482
|
init_progress_heartbeat();
|
|
53855
54483
|
init_model_loader();
|
|
53856
54484
|
init_port_manager();
|
|
54485
|
+
init_model_parser();
|
|
53857
54486
|
init_onepassword();
|
|
54487
|
+
init_probe_live();
|
|
53858
54488
|
init_provider_definitions();
|
|
54489
|
+
init_routing_rules();
|
|
53859
54490
|
init_proxy_server();
|
|
53860
54491
|
init_redact();
|
|
53861
54492
|
init_team_orchestrator();
|
|
@@ -68684,399 +69315,6 @@ var init_model_selector = __esm(() => {
|
|
|
68684
69315
|
};
|
|
68685
69316
|
});
|
|
68686
69317
|
|
|
68687
|
-
// src/providers/probe-live.ts
|
|
68688
|
-
async function probeLink(proxyUrl, link, timeoutMs) {
|
|
68689
|
-
const isOAuth = OAUTH_PROVIDERS2.has(link.provider);
|
|
68690
|
-
if (!link.hasCredentials && !isOAuth) {
|
|
68691
|
-
return {
|
|
68692
|
-
state: "key-missing",
|
|
68693
|
-
latencyMs: 0,
|
|
68694
|
-
errorMessage: link.credentialHint
|
|
68695
|
-
};
|
|
68696
|
-
}
|
|
68697
|
-
const startedAt = Date.now();
|
|
68698
|
-
let response;
|
|
68699
|
-
try {
|
|
68700
|
-
response = await fetch(`${proxyUrl}/v1/messages`, {
|
|
68701
|
-
method: "POST",
|
|
68702
|
-
headers: {
|
|
68703
|
-
"Content-Type": "application/json"
|
|
68704
|
-
},
|
|
68705
|
-
body: JSON.stringify({
|
|
68706
|
-
model: link.modelSpec,
|
|
68707
|
-
system: "You are a helpful assistant.",
|
|
68708
|
-
messages: [{ role: "user", content: PROBE_PROMPT }],
|
|
68709
|
-
max_tokens: PROBE_MAX_TOKENS,
|
|
68710
|
-
output_config: { effort: "minimal" },
|
|
68711
|
-
stream: true
|
|
68712
|
-
}),
|
|
68713
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
68714
|
-
});
|
|
68715
|
-
} catch (e) {
|
|
68716
|
-
const latencyMs = Date.now() - startedAt;
|
|
68717
|
-
const name = e?.name || "";
|
|
68718
|
-
const msg2 = String(e?.message || e);
|
|
68719
|
-
if (name === "TimeoutError" || name === "AbortError" || /timeout/i.test(msg2)) {
|
|
68720
|
-
return { state: "timeout", latencyMs, errorMessage: msg2 };
|
|
68721
|
-
}
|
|
68722
|
-
return { state: "network-error", latencyMs, errorMessage: msg2 };
|
|
68723
|
-
}
|
|
68724
|
-
const ttfbMs = Date.now() - startedAt;
|
|
68725
|
-
if (!response.ok) {
|
|
68726
|
-
const body = await safeReadBody(response);
|
|
68727
|
-
return annotateOAuthHint(classifyHttpError(response.status, body, ttfbMs), link.provider, isOAuth);
|
|
68728
|
-
}
|
|
68729
|
-
const streamResult = await consumeProbeStream(response, timeoutMs, startedAt);
|
|
68730
|
-
const totalMs = Date.now() - startedAt;
|
|
68731
|
-
let timing2;
|
|
68732
|
-
if (streamResult.state === "live" && streamResult.ttftMs !== undefined && !streamResult.truncated) {
|
|
68733
|
-
const ttftMs = streamResult.ttftMs;
|
|
68734
|
-
const tokens = streamResult.tokens ?? 0;
|
|
68735
|
-
const streamMs = Math.max(STREAM_MS_FLOOR, totalMs - ttftMs);
|
|
68736
|
-
const tokensPerSec = tokens > 0 ? tokens / streamMs * 1000 : 0;
|
|
68737
|
-
timing2 = { ttfbMs, ttftMs, totalMs, tokens, tokensPerSec };
|
|
68738
|
-
}
|
|
68739
|
-
const { ttftMs: _ttft, tokens: _tok, truncated: _trunc, ...rest } = streamResult;
|
|
68740
|
-
return annotateOAuthHint({
|
|
68741
|
-
...rest,
|
|
68742
|
-
latencyMs: totalMs,
|
|
68743
|
-
timing: timing2
|
|
68744
|
-
}, link.provider, isOAuth);
|
|
68745
|
-
}
|
|
68746
|
-
function annotateOAuthHint(result, provider, isOAuth) {
|
|
68747
|
-
if (!isOAuth)
|
|
68748
|
-
return result;
|
|
68749
|
-
if (result.state === "live")
|
|
68750
|
-
return result;
|
|
68751
|
-
const loginCommand2 = provider === "antigravity" ? "claudish login antigravity" : provider === "vertex" ? "gcloud auth application-default login" : provider === "devin" ? "devin login" : undefined;
|
|
68752
|
-
if (!loginCommand2)
|
|
68753
|
-
return result;
|
|
68754
|
-
if (result.httpStatus === 403)
|
|
68755
|
-
return result;
|
|
68756
|
-
const looksLikeAuthFailure = result.state === "auth-failed" || /auth|token|login|credential|unauthor/i.test(result.errorMessage || "");
|
|
68757
|
-
if (!looksLikeAuthFailure)
|
|
68758
|
-
return result;
|
|
68759
|
-
return {
|
|
68760
|
-
...result,
|
|
68761
|
-
state: "auth-failed",
|
|
68762
|
-
actionHint: `run: ${loginCommand2}`
|
|
68763
|
-
};
|
|
68764
|
-
}
|
|
68765
|
-
async function safeReadBody(response) {
|
|
68766
|
-
try {
|
|
68767
|
-
const text = await response.text();
|
|
68768
|
-
return text.slice(0, 500);
|
|
68769
|
-
} catch {
|
|
68770
|
-
return "";
|
|
68771
|
-
}
|
|
68772
|
-
}
|
|
68773
|
-
function extractUpstreamStatus(body) {
|
|
68774
|
-
if (!body)
|
|
68775
|
-
return;
|
|
68776
|
-
try {
|
|
68777
|
-
const parsed = JSON.parse(body);
|
|
68778
|
-
const status = parsed?.error?.upstream_status;
|
|
68779
|
-
return typeof status === "number" ? status : undefined;
|
|
68780
|
-
} catch {
|
|
68781
|
-
return;
|
|
68782
|
-
}
|
|
68783
|
-
}
|
|
68784
|
-
function extractErrorType(body) {
|
|
68785
|
-
if (!body)
|
|
68786
|
-
return;
|
|
68787
|
-
try {
|
|
68788
|
-
const parsed = JSON.parse(body);
|
|
68789
|
-
const t = parsed?.error?.type;
|
|
68790
|
-
return typeof t === "string" ? t : undefined;
|
|
68791
|
-
} catch {
|
|
68792
|
-
return;
|
|
68793
|
-
}
|
|
68794
|
-
}
|
|
68795
|
-
function classifyHttpError(status, body, latencyMs) {
|
|
68796
|
-
const lowered = body.toLowerCase();
|
|
68797
|
-
if (extractErrorType(body) === "connection_error") {
|
|
68798
|
-
return {
|
|
68799
|
-
state: "network-error",
|
|
68800
|
-
latencyMs,
|
|
68801
|
-
httpStatus: status,
|
|
68802
|
-
errorMessage: extractErrorMessage(body) || "Cannot reach provider"
|
|
68803
|
-
};
|
|
68804
|
-
}
|
|
68805
|
-
const upstream = status === 400 ? extractUpstreamStatus(body) : undefined;
|
|
68806
|
-
if (status === 401 || status === 403 || upstream === 401 || upstream === 403) {
|
|
68807
|
-
const authStatus = upstream ?? status;
|
|
68808
|
-
return {
|
|
68809
|
-
state: "auth-failed",
|
|
68810
|
-
latencyMs,
|
|
68811
|
-
httpStatus: authStatus,
|
|
68812
|
-
errorMessage: extractErrorMessage(body) || `HTTP ${authStatus}`
|
|
68813
|
-
};
|
|
68814
|
-
}
|
|
68815
|
-
if (status === 404 || /model[_ ]not[_ ]found|no such model|unknown model/.test(lowered)) {
|
|
68816
|
-
return {
|
|
68817
|
-
state: "model-not-found",
|
|
68818
|
-
latencyMs,
|
|
68819
|
-
httpStatus: status,
|
|
68820
|
-
errorMessage: extractErrorMessage(body) || `HTTP ${status}`
|
|
68821
|
-
};
|
|
68822
|
-
}
|
|
68823
|
-
if (status === 429) {
|
|
68824
|
-
return {
|
|
68825
|
-
state: "rate-limited",
|
|
68826
|
-
latencyMs,
|
|
68827
|
-
httpStatus: status,
|
|
68828
|
-
errorMessage: extractErrorMessage(body) || "Rate limited"
|
|
68829
|
-
};
|
|
68830
|
-
}
|
|
68831
|
-
if (upstream === 429 || status === 402) {
|
|
68832
|
-
return {
|
|
68833
|
-
state: "out-of-credit",
|
|
68834
|
-
latencyMs,
|
|
68835
|
-
httpStatus: upstream ?? status,
|
|
68836
|
-
errorMessage: extractErrorMessage(body) || "Out of credit \u2014 account balance or plan exhausted"
|
|
68837
|
-
};
|
|
68838
|
-
}
|
|
68839
|
-
if (status >= 500) {
|
|
68840
|
-
return {
|
|
68841
|
-
state: "server-error",
|
|
68842
|
-
latencyMs,
|
|
68843
|
-
httpStatus: status,
|
|
68844
|
-
errorMessage: extractErrorMessage(body) || `HTTP ${status}`
|
|
68845
|
-
};
|
|
68846
|
-
}
|
|
68847
|
-
return {
|
|
68848
|
-
state: "error",
|
|
68849
|
-
latencyMs,
|
|
68850
|
-
httpStatus: status,
|
|
68851
|
-
errorMessage: extractErrorMessage(body) || `HTTP ${status}`
|
|
68852
|
-
};
|
|
68853
|
-
}
|
|
68854
|
-
function extractErrorMessage(body) {
|
|
68855
|
-
if (!body)
|
|
68856
|
-
return;
|
|
68857
|
-
try {
|
|
68858
|
-
const parsed = JSON.parse(body);
|
|
68859
|
-
const msg2 = parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || parsed?.detail;
|
|
68860
|
-
if (typeof msg2 === "string" && msg2.length > 0) {
|
|
68861
|
-
return msg2.length > 160 ? `${msg2.slice(0, 157)}...` : msg2;
|
|
68862
|
-
}
|
|
68863
|
-
} catch {}
|
|
68864
|
-
const trimmed2 = body.trim();
|
|
68865
|
-
if (!trimmed2)
|
|
68866
|
-
return;
|
|
68867
|
-
return trimmed2.length > 160 ? `${trimmed2.slice(0, 157)}...` : trimmed2;
|
|
68868
|
-
}
|
|
68869
|
-
async function consumeProbeStream(response, timeoutMs, startedAt) {
|
|
68870
|
-
const body = response.body;
|
|
68871
|
-
if (!body) {
|
|
68872
|
-
return { state: "error", errorMessage: "empty response body" };
|
|
68873
|
-
}
|
|
68874
|
-
const reader = body.getReader();
|
|
68875
|
-
const decoder = new TextDecoder;
|
|
68876
|
-
let buffered = "";
|
|
68877
|
-
const deadline = Date.now() + timeoutMs;
|
|
68878
|
-
let ttftMs;
|
|
68879
|
-
let sawContent = false;
|
|
68880
|
-
let textChars = 0;
|
|
68881
|
-
let reportedTokens;
|
|
68882
|
-
let stopReason;
|
|
68883
|
-
let errorVerdict = null;
|
|
68884
|
-
let completed = false;
|
|
68885
|
-
try {
|
|
68886
|
-
while (Date.now() < deadline) {
|
|
68887
|
-
const { value, done } = await reader.read();
|
|
68888
|
-
if (done) {
|
|
68889
|
-
completed = true;
|
|
68890
|
-
break;
|
|
68891
|
-
}
|
|
68892
|
-
buffered += decoder.decode(value, { stream: true });
|
|
68893
|
-
const events = buffered.split(`
|
|
68894
|
-
|
|
68895
|
-
`);
|
|
68896
|
-
buffered = events.pop() ?? "";
|
|
68897
|
-
for (const event of events) {
|
|
68898
|
-
const verdict = interpretSseEvent(event);
|
|
68899
|
-
if (verdict && typeof verdict === "object" && verdict.state !== "live") {
|
|
68900
|
-
errorVerdict = verdict;
|
|
68901
|
-
break;
|
|
68902
|
-
}
|
|
68903
|
-
const acct = accountStreamEvent(event);
|
|
68904
|
-
if (acct.contentDelta) {
|
|
68905
|
-
if (ttftMs === undefined)
|
|
68906
|
-
ttftMs = Date.now() - startedAt;
|
|
68907
|
-
sawContent = true;
|
|
68908
|
-
}
|
|
68909
|
-
if (acct.textChars)
|
|
68910
|
-
textChars += acct.textChars;
|
|
68911
|
-
if (acct.outputTokens !== undefined)
|
|
68912
|
-
reportedTokens = acct.outputTokens;
|
|
68913
|
-
if (acct.stopReason)
|
|
68914
|
-
stopReason = acct.stopReason;
|
|
68915
|
-
}
|
|
68916
|
-
if (errorVerdict)
|
|
68917
|
-
break;
|
|
68918
|
-
}
|
|
68919
|
-
} catch (e) {
|
|
68920
|
-
if (!sawContent) {
|
|
68921
|
-
return { state: "network-error", errorMessage: String(e?.message || e) };
|
|
68922
|
-
}
|
|
68923
|
-
} finally {
|
|
68924
|
-
try {
|
|
68925
|
-
await reader.cancel();
|
|
68926
|
-
} catch {}
|
|
68927
|
-
}
|
|
68928
|
-
if (errorVerdict)
|
|
68929
|
-
return errorVerdict;
|
|
68930
|
-
if (sawContent) {
|
|
68931
|
-
const tokens = reportedTokens ?? Math.max(1, Math.round(textChars / 4));
|
|
68932
|
-
return { state: "live", ttftMs, tokens, truncated: !completed };
|
|
68933
|
-
}
|
|
68934
|
-
const truncationReason = stopReason === "max_tokens" || stopReason === "length" ? stopReason : undefined;
|
|
68935
|
-
if (truncationReason || reportedTokens !== undefined && reportedTokens >= PROBE_MAX_TOKENS) {
|
|
68936
|
-
const cause = truncationReason ? `finish: ${truncationReason}` : `${reportedTokens} tokens consumed, none visible`;
|
|
68937
|
-
return {
|
|
68938
|
-
state: "error",
|
|
68939
|
-
errorMessage: `no visible output within probe budget (${cause})`
|
|
68940
|
-
};
|
|
68941
|
-
}
|
|
68942
|
-
return { state: "error", errorMessage: "stream ended without content" };
|
|
68943
|
-
}
|
|
68944
|
-
function accountStreamEvent(rawEvent) {
|
|
68945
|
-
let dataPayload = "";
|
|
68946
|
-
for (const line of rawEvent.split(`
|
|
68947
|
-
`)) {
|
|
68948
|
-
if (line.startsWith("data:"))
|
|
68949
|
-
dataPayload += line.slice(5).trim();
|
|
68950
|
-
}
|
|
68951
|
-
if (!dataPayload || dataPayload === "[DONE]") {
|
|
68952
|
-
return { contentDelta: false, textChars: 0 };
|
|
68953
|
-
}
|
|
68954
|
-
let parsed;
|
|
68955
|
-
try {
|
|
68956
|
-
parsed = JSON.parse(dataPayload);
|
|
68957
|
-
} catch {
|
|
68958
|
-
return { contentDelta: false, textChars: 0 };
|
|
68959
|
-
}
|
|
68960
|
-
let textChars = 0;
|
|
68961
|
-
let contentDelta = false;
|
|
68962
|
-
const text = parsed?.delta?.text ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.delta?.content : undefined);
|
|
68963
|
-
if (typeof text === "string" && text.length > 0) {
|
|
68964
|
-
contentDelta = true;
|
|
68965
|
-
textChars = text.length;
|
|
68966
|
-
} else if (parsed?.type === "content_block_delta" || parsed?.type === "content_block_start") {
|
|
68967
|
-
contentDelta = true;
|
|
68968
|
-
}
|
|
68969
|
-
const outputTokens = parsed?.usage?.output_tokens ?? parsed?.message?.usage?.output_tokens ?? parsed?.usage?.completion_tokens;
|
|
68970
|
-
const stopReason = parsed?.delta?.stop_reason ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.finish_reason : undefined);
|
|
68971
|
-
return {
|
|
68972
|
-
contentDelta,
|
|
68973
|
-
textChars,
|
|
68974
|
-
outputTokens: typeof outputTokens === "number" ? outputTokens : undefined,
|
|
68975
|
-
stopReason: typeof stopReason === "string" ? stopReason : undefined
|
|
68976
|
-
};
|
|
68977
|
-
}
|
|
68978
|
-
function interpretSseEvent(rawEvent) {
|
|
68979
|
-
const lines = rawEvent.split(`
|
|
68980
|
-
`);
|
|
68981
|
-
let eventType = "";
|
|
68982
|
-
let dataPayload = "";
|
|
68983
|
-
for (const line of lines) {
|
|
68984
|
-
if (line.startsWith("event:"))
|
|
68985
|
-
eventType = line.slice(6).trim();
|
|
68986
|
-
else if (line.startsWith("data:"))
|
|
68987
|
-
dataPayload += line.slice(5).trim();
|
|
68988
|
-
}
|
|
68989
|
-
if (!dataPayload)
|
|
68990
|
-
return null;
|
|
68991
|
-
if (dataPayload === "[DONE]")
|
|
68992
|
-
return null;
|
|
68993
|
-
let parsed;
|
|
68994
|
-
try {
|
|
68995
|
-
parsed = JSON.parse(dataPayload);
|
|
68996
|
-
} catch {
|
|
68997
|
-
return null;
|
|
68998
|
-
}
|
|
68999
|
-
if (parsed?.type === "error" || eventType === "error" || parsed?.error) {
|
|
69000
|
-
const message = parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || "provider returned error event";
|
|
69001
|
-
const status = parsed?.error?.status || parsed?.status;
|
|
69002
|
-
if (typeof status === "number") {
|
|
69003
|
-
return {
|
|
69004
|
-
state: status === 401 || status === 403 ? "auth-failed" : "error",
|
|
69005
|
-
httpStatus: status,
|
|
69006
|
-
errorMessage: message
|
|
69007
|
-
};
|
|
69008
|
-
}
|
|
69009
|
-
return { state: "error", errorMessage: message };
|
|
69010
|
-
}
|
|
69011
|
-
if (isContentEvent(parsed, eventType)) {
|
|
69012
|
-
return "live";
|
|
69013
|
-
}
|
|
69014
|
-
return null;
|
|
69015
|
-
}
|
|
69016
|
-
function isContentEvent(parsed, eventType) {
|
|
69017
|
-
if (eventType === "content_block_start" || eventType === "content_block_delta")
|
|
69018
|
-
return true;
|
|
69019
|
-
if (eventType === "message_start")
|
|
69020
|
-
return true;
|
|
69021
|
-
if (parsed?.type === "content_block_start")
|
|
69022
|
-
return true;
|
|
69023
|
-
if (parsed?.type === "content_block_delta")
|
|
69024
|
-
return true;
|
|
69025
|
-
if (parsed?.type === "message_start")
|
|
69026
|
-
return true;
|
|
69027
|
-
if (parsed?.type === "message_delta")
|
|
69028
|
-
return true;
|
|
69029
|
-
if (Array.isArray(parsed?.choices) && parsed.choices.length > 0) {
|
|
69030
|
-
const choice = parsed.choices[0];
|
|
69031
|
-
if (choice?.delta || choice?.message || choice?.text || choice?.finish_reason)
|
|
69032
|
-
return true;
|
|
69033
|
-
}
|
|
69034
|
-
if (parsed?.candidates)
|
|
69035
|
-
return true;
|
|
69036
|
-
return false;
|
|
69037
|
-
}
|
|
69038
|
-
function withDetail(base, message) {
|
|
69039
|
-
return message ? `${base} \u2014 ${message}` : base;
|
|
69040
|
-
}
|
|
69041
|
-
function describeProbeState(result) {
|
|
69042
|
-
const status = result.httpStatus ?? "";
|
|
69043
|
-
const latency = result.latencyMs ? ` \xB7 ${result.latencyMs}ms` : "";
|
|
69044
|
-
switch (result.state) {
|
|
69045
|
-
case "live":
|
|
69046
|
-
return `live \xB7 ${result.latencyMs}ms`;
|
|
69047
|
-
case "key-missing":
|
|
69048
|
-
return result.errorMessage ? `missing (${result.errorMessage})` : "missing";
|
|
69049
|
-
case "auth-failed":
|
|
69050
|
-
return withDetail(`auth failed \xB7 ${status}${latency}`.trim(), result.errorMessage);
|
|
69051
|
-
case "model-not-found":
|
|
69052
|
-
return withDetail(`model not found \xB7 ${status}${latency}`.trim(), result.errorMessage);
|
|
69053
|
-
case "rate-limited":
|
|
69054
|
-
return withDetail(`rate limited \xB7 ${result.latencyMs}ms`, result.errorMessage);
|
|
69055
|
-
case "out-of-credit":
|
|
69056
|
-
return withDetail(`out of credit \xB7 ${status}${latency}`.trim(), result.errorMessage);
|
|
69057
|
-
case "server-error":
|
|
69058
|
-
return withDetail(`server error \xB7 ${status} \xB7 ${result.latencyMs}ms`, result.errorMessage);
|
|
69059
|
-
case "timeout":
|
|
69060
|
-
return withDetail(`timeout \xB7 ${result.latencyMs}ms`, result.errorMessage);
|
|
69061
|
-
case "network-error":
|
|
69062
|
-
return withDetail(`network error \xB7 ${result.latencyMs}ms`, result.errorMessage);
|
|
69063
|
-
case "error": {
|
|
69064
|
-
const base = `error${result.httpStatus ? ` \xB7 ${result.httpStatus}` : ""}${latency}`;
|
|
69065
|
-
return withDetail(base, result.errorMessage);
|
|
69066
|
-
}
|
|
69067
|
-
}
|
|
69068
|
-
}
|
|
69069
|
-
function isReadyState(state) {
|
|
69070
|
-
return state === "live";
|
|
69071
|
-
}
|
|
69072
|
-
function isFailureState(state) {
|
|
69073
|
-
return state === "auth-failed" || state === "model-not-found" || state === "rate-limited" || state === "out-of-credit" || state === "server-error" || state === "timeout" || state === "network-error" || state === "error";
|
|
69074
|
-
}
|
|
69075
|
-
var STREAM_MS_FLOOR = 50, OAUTH_PROVIDERS2, PROBE_PROMPT = "Count from one to twenty in words, one per line.", PROBE_MAX_TOKENS = 512;
|
|
69076
|
-
var init_probe_live = __esm(() => {
|
|
69077
|
-
OAUTH_PROVIDERS2 = new Set(["vertex", "antigravity", "devin"]);
|
|
69078
|
-
});
|
|
69079
|
-
|
|
69080
69318
|
// src/tui/theme.ts
|
|
69081
69319
|
import { createTextAttributes } from "@opentui/core";
|
|
69082
69320
|
function latencyBucket(ms) {
|
|
@@ -71350,6 +71588,21 @@ var init_api_key_map = __esm(() => {
|
|
|
71350
71588
|
};
|
|
71351
71589
|
});
|
|
71352
71590
|
|
|
71591
|
+
// src/providers/claude-code-aliases.ts
|
|
71592
|
+
function claudeCodeTierAlias(model) {
|
|
71593
|
+
return TIER_ALIASES[model.trim().toLowerCase()] ?? null;
|
|
71594
|
+
}
|
|
71595
|
+
var TIER_ALIASES;
|
|
71596
|
+
var init_claude_code_aliases = __esm(() => {
|
|
71597
|
+
TIER_ALIASES = {
|
|
71598
|
+
opus: "opus",
|
|
71599
|
+
sonnet: "sonnet",
|
|
71600
|
+
haiku: "haiku",
|
|
71601
|
+
internal: "opus",
|
|
71602
|
+
default: "opus"
|
|
71603
|
+
};
|
|
71604
|
+
});
|
|
71605
|
+
|
|
71353
71606
|
// src/providers/probe-runner.ts
|
|
71354
71607
|
function pinProbeModelSpec(link) {
|
|
71355
71608
|
if (link.provider === "native-anthropic")
|
|
@@ -72041,7 +72294,7 @@ async function printRecommendedModels(jsonOutput, forceUpdate) {
|
|
|
72041
72294
|
const rawId = m.id;
|
|
72042
72295
|
const modelId = rawId.length > 28 ? `${rawId.substring(0, 25)}...` : rawId;
|
|
72043
72296
|
const modelIdPadded = modelId.padEnd(28);
|
|
72044
|
-
const pricing =
|
|
72297
|
+
const pricing = formatListingPrice(m, { compact: true });
|
|
72045
72298
|
const pricingPadded = pricing.padEnd(10);
|
|
72046
72299
|
const context = m.context || "N/A";
|
|
72047
72300
|
const contextPadded = context.padEnd(6);
|
|
@@ -72133,13 +72386,19 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
|
|
|
72133
72386
|
};
|
|
72134
72387
|
}
|
|
72135
72388
|
if (parsed.provider === "native-anthropic") {
|
|
72136
|
-
const
|
|
72389
|
+
const tier = claudeCodeTierAlias(parsed.model);
|
|
72390
|
+
const tierEnv = {
|
|
72391
|
+
opus: process.env[ENV.CLAUDISH_MODEL_OPUS] || process.env[ENV.ANTHROPIC_DEFAULT_OPUS_MODEL],
|
|
72392
|
+
sonnet: process.env[ENV.CLAUDISH_MODEL_SONNET] || process.env[ENV.ANTHROPIC_DEFAULT_SONNET_MODEL],
|
|
72393
|
+
haiku: process.env[ENV.CLAUDISH_MODEL_HAIKU] || process.env[ENV.ANTHROPIC_DEFAULT_HAIKU_MODEL]
|
|
72394
|
+
};
|
|
72395
|
+
const opusModel = tier ? tierEnv[tier] || latestAnthropicTierModelId(tier) || "claude-opus-5" : parsed.model;
|
|
72137
72396
|
return {
|
|
72138
72397
|
routes: [
|
|
72139
72398
|
{
|
|
72140
72399
|
provider: "native-anthropic",
|
|
72141
72400
|
modelSpec: opusModel,
|
|
72142
|
-
displayName:
|
|
72401
|
+
displayName: tier ? `Claude Code (${tier})` : "Claude Code"
|
|
72143
72402
|
}
|
|
72144
72403
|
],
|
|
72145
72404
|
source: "auto-chain",
|
|
@@ -72233,6 +72492,33 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
|
|
|
72233
72492
|
}
|
|
72234
72493
|
return chain.source;
|
|
72235
72494
|
}
|
|
72495
|
+
function buildDirectChainEntry(parsed, directProbe) {
|
|
72496
|
+
const providerDef = getProviderByName(parsed.provider);
|
|
72497
|
+
const keyInfo = API_KEY_MAP[parsed.provider];
|
|
72498
|
+
if (!providerDef && !keyInfo)
|
|
72499
|
+
return [];
|
|
72500
|
+
let hasCredentials;
|
|
72501
|
+
let provenance;
|
|
72502
|
+
if (providerDef?.isLocal) {
|
|
72503
|
+
hasCredentials = isLocalProviderEnabled(parsed.provider);
|
|
72504
|
+
} else if (!keyInfo?.envVar) {
|
|
72505
|
+
hasCredentials = true;
|
|
72506
|
+
} else {
|
|
72507
|
+
provenance = resolveApiKeyProvenance(keyInfo.envVar, keyInfo.aliases);
|
|
72508
|
+
hasCredentials = provenance.hasValue || (keyInfo.aliases?.some((a) => !!process.env[a]) ?? false);
|
|
72509
|
+
}
|
|
72510
|
+
return [
|
|
72511
|
+
{
|
|
72512
|
+
provider: parsed.provider,
|
|
72513
|
+
displayName: providerDef?.displayName ?? parsed.provider,
|
|
72514
|
+
modelSpec: parsed.model,
|
|
72515
|
+
hasCredentials,
|
|
72516
|
+
credentialHint: !hasCredentials ? providerDef?.isLocal ? "enable local provider in global config" : keyInfo?.envVar : undefined,
|
|
72517
|
+
provenance,
|
|
72518
|
+
probe: directProbe
|
|
72519
|
+
}
|
|
72520
|
+
];
|
|
72521
|
+
}
|
|
72236
72522
|
function buildResultLinks(parsed, chainDetails, directProbe) {
|
|
72237
72523
|
if (chainDetails.length === 0) {
|
|
72238
72524
|
const directProviderDef = getProviderByName(parsed.provider);
|
|
@@ -72383,7 +72669,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
|
|
|
72383
72669
|
isExplicit: parsed.isExplicitProvider,
|
|
72384
72670
|
routingSource: chain.source,
|
|
72385
72671
|
matchedPattern: chain.matchedPattern,
|
|
72386
|
-
chain: chainDetails,
|
|
72672
|
+
chain: chainDetails.length > 0 ? chainDetails : buildDirectChainEntry(parsed, directProbeResult),
|
|
72387
72673
|
directProbe: directProbeResult,
|
|
72388
72674
|
wiring
|
|
72389
72675
|
});
|
|
@@ -73010,6 +73296,8 @@ var init_cli = __esm(() => {
|
|
|
73010
73296
|
init_profile_config();
|
|
73011
73297
|
init_api_key_map();
|
|
73012
73298
|
init_api_key_provenance();
|
|
73299
|
+
init_catalog_client();
|
|
73300
|
+
init_claude_code_aliases();
|
|
73013
73301
|
init_endpoint_registration();
|
|
73014
73302
|
init_model_parser();
|
|
73015
73303
|
init_probe_live();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudish",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.58.0",
|
|
4
4
|
"description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"ai"
|
|
61
61
|
],
|
|
62
62
|
"optionalDependencies": {
|
|
63
|
-
"@claudish/magmux-darwin-arm64": "7.
|
|
64
|
-
"@claudish/magmux-darwin-x64": "7.
|
|
65
|
-
"@claudish/magmux-linux-arm64": "7.
|
|
66
|
-
"@claudish/magmux-linux-x64": "7.
|
|
63
|
+
"@claudish/magmux-darwin-arm64": "7.58.0",
|
|
64
|
+
"@claudish/magmux-darwin-x64": "7.58.0",
|
|
65
|
+
"@claudish/magmux-linux-arm64": "7.58.0",
|
|
66
|
+
"@claudish/magmux-linux-x64": "7.58.0"
|
|
67
67
|
},
|
|
68
68
|
"author": "Jack Rudenko <i@madappgang.com>",
|
|
69
69
|
"license": "MIT",
|