claudish 6.2.1 → 6.3.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 CHANGED
@@ -37103,7 +37103,7 @@ async function fetchGLMCodingModels() {
37103
37103
  return [];
37104
37104
  }
37105
37105
  }
37106
- var __filename4, __dirname4, VERSION = "6.2.1", CACHE_MAX_AGE_DAYS2 = 2, CLAUDISH_CACHE_DIR2, BUNDLED_MODELS_PATH, CACHED_MODELS_PATH, ALL_MODELS_JSON_PATH;
37106
+ var __filename4, __dirname4, VERSION = "6.3.0", CACHE_MAX_AGE_DAYS2 = 2, CLAUDISH_CACHE_DIR2, BUNDLED_MODELS_PATH, CACHED_MODELS_PATH, ALL_MODELS_JSON_PATH;
37107
37107
  var init_cli = __esm(() => {
37108
37108
  init_config();
37109
37109
  init_model_loader();
@@ -98318,6 +98318,141 @@ var init_react = __esm(async () => {
98318
98318
  extend2({ "time-to-first-draw": TimeToFirstDrawRenderable });
98319
98319
  });
98320
98320
 
98321
+ // src/tui/test-provider.ts
98322
+ function resolveBaseUrl(def) {
98323
+ if (def.baseUrlEnvVars) {
98324
+ for (const envVar of def.baseUrlEnvVars) {
98325
+ const val = process.env[envVar];
98326
+ if (val)
98327
+ return val.replace(/\/$/, "");
98328
+ }
98329
+ }
98330
+ return def.baseUrl.replace(/\/$/, "");
98331
+ }
98332
+ function getApiFamily(def) {
98333
+ switch (def.transport) {
98334
+ case "openai":
98335
+ case "openrouter":
98336
+ case "litellm":
98337
+ case "kimi-coding":
98338
+ return "openai";
98339
+ case "anthropic":
98340
+ return "anthropic";
98341
+ case "gemini":
98342
+ case "gemini-oauth":
98343
+ return "gemini";
98344
+ case "ollamacloud":
98345
+ return "ollamacloud";
98346
+ default:
98347
+ return "unsupported";
98348
+ }
98349
+ }
98350
+ async function testOpenAI(baseUrl, apiKey) {
98351
+ const url2 = `${baseUrl}/v1/models`;
98352
+ const signal = AbortSignal.timeout(TIMEOUT_MS);
98353
+ try {
98354
+ const resp = await fetch(url2, {
98355
+ method: "GET",
98356
+ headers: {
98357
+ Authorization: `Bearer ${apiKey}`,
98358
+ "Content-Type": "application/json"
98359
+ },
98360
+ signal
98361
+ });
98362
+ if (resp.ok)
98363
+ return "valid";
98364
+ return `invalid (HTTP ${resp.status})`;
98365
+ } catch (err) {
98366
+ if (err instanceof Error && err.name === "TimeoutError")
98367
+ return "timeout";
98368
+ return `error: ${err instanceof Error ? err.message : String(err)}`;
98369
+ }
98370
+ }
98371
+ async function testAnthropic(baseUrl, apiKey, authScheme = "x-api-key") {
98372
+ const url2 = `${baseUrl}/anthropic/v1/messages`;
98373
+ const signal = AbortSignal.timeout(TIMEOUT_MS);
98374
+ const authHeader = authScheme === "bearer" ? { Authorization: `Bearer ${apiKey}` } : { "x-api-key": apiKey };
98375
+ try {
98376
+ const resp = await fetch(url2, {
98377
+ method: "POST",
98378
+ headers: {
98379
+ "Content-Type": "application/json",
98380
+ "anthropic-version": "2023-06-01",
98381
+ ...authHeader
98382
+ },
98383
+ body: JSON.stringify({
98384
+ model: "claude-3-haiku-20240307",
98385
+ max_tokens: 1,
98386
+ messages: [{ role: "user", content: "Hi" }]
98387
+ }),
98388
+ signal
98389
+ });
98390
+ if (resp.ok || resp.status === 400)
98391
+ return "valid";
98392
+ return `invalid (HTTP ${resp.status})`;
98393
+ } catch (err) {
98394
+ if (err instanceof Error && err.name === "TimeoutError")
98395
+ return "timeout";
98396
+ return `error: ${err instanceof Error ? err.message : String(err)}`;
98397
+ }
98398
+ }
98399
+ async function testGemini(baseUrl, apiKey) {
98400
+ const url2 = `${baseUrl}/v1beta/models?key=${encodeURIComponent(apiKey)}`;
98401
+ const signal = AbortSignal.timeout(TIMEOUT_MS);
98402
+ try {
98403
+ const resp = await fetch(url2, { signal });
98404
+ if (resp.ok)
98405
+ return "valid";
98406
+ return `invalid (HTTP ${resp.status})`;
98407
+ } catch (err) {
98408
+ if (err instanceof Error && err.name === "TimeoutError")
98409
+ return "timeout";
98410
+ return `error: ${err instanceof Error ? err.message : String(err)}`;
98411
+ }
98412
+ }
98413
+ async function testOllamaCloud(baseUrl, apiKey) {
98414
+ const url2 = `${baseUrl}/api/tags`;
98415
+ const signal = AbortSignal.timeout(TIMEOUT_MS);
98416
+ try {
98417
+ const resp = await fetch(url2, {
98418
+ headers: { Authorization: `Bearer ${apiKey}` },
98419
+ signal
98420
+ });
98421
+ if (resp.ok)
98422
+ return "valid";
98423
+ return `invalid (HTTP ${resp.status})`;
98424
+ } catch (err) {
98425
+ if (err instanceof Error && err.name === "TimeoutError")
98426
+ return "timeout";
98427
+ return `error: ${err instanceof Error ? err.message : String(err)}`;
98428
+ }
98429
+ }
98430
+ async function testProviderKey(providerName, apiKey) {
98431
+ const allDefs = getAllProviders();
98432
+ const canonicalName = providerName === "gemini" ? "google" : providerName;
98433
+ const def = allDefs.find((d2) => d2.name === canonicalName);
98434
+ if (!def)
98435
+ return "unsupported provider";
98436
+ const family = getApiFamily(def);
98437
+ const baseUrl = resolveBaseUrl(def);
98438
+ switch (family) {
98439
+ case "openai":
98440
+ return testOpenAI(baseUrl, apiKey);
98441
+ case "anthropic":
98442
+ return testAnthropic(baseUrl, apiKey, def.authScheme === "bearer" ? "bearer" : "x-api-key");
98443
+ case "gemini":
98444
+ return testGemini(baseUrl, apiKey);
98445
+ case "ollamacloud":
98446
+ return testOllamaCloud(baseUrl, apiKey);
98447
+ default:
98448
+ return "unsupported provider";
98449
+ }
98450
+ }
98451
+ var TIMEOUT_MS = 1e4;
98452
+ var init_test_provider = __esm(() => {
98453
+ init_provider_definitions();
98454
+ });
98455
+
98321
98456
  // src/tui/providers.ts
98322
98457
  function toProviderDef(def) {
98323
98458
  return {
@@ -98403,6 +98538,10 @@ function App() {
98403
98538
  const [chainOrder, setChainOrder] = import_react13.useState([]);
98404
98539
  const [chainCursor, setChainCursor] = import_react13.useState(0);
98405
98540
  const [statusMsg, setStatusMsg] = import_react13.useState(null);
98541
+ const [testResults, setTestResults] = import_react13.useState({});
98542
+ const [probeMode, setProbeMode] = import_react13.useState("idle");
98543
+ const [probeModel, setProbeModel] = import_react13.useState("");
98544
+ const [probeResults, setProbeResults] = import_react13.useState([]);
98406
98545
  const CHAIN_PROVIDERS = PROVIDERS;
98407
98546
  const quit = import_react13.useCallback(() => renderer.destroy(), [renderer]);
98408
98547
  const displayProviders = import_react13.useMemo(() => {
@@ -98434,6 +98573,110 @@ function App() {
98434
98573
  useKeyboard((key) => {
98435
98574
  if (key.ctrl && key.name === "c")
98436
98575
  return quit();
98576
+ if (probeMode === "input") {
98577
+ if (key.name === "return" || key.name === "enter") {
98578
+ const model = probeModel.trim();
98579
+ if (!model) {
98580
+ setProbeModel("");
98581
+ setProbeMode("idle");
98582
+ return;
98583
+ }
98584
+ const parsed = parseModelSpec(model);
98585
+ const chain = getFallbackChain(model, parsed.provider);
98586
+ if (chain.length === 0) {
98587
+ setProbeResults([
98588
+ {
98589
+ provider: "none",
98590
+ displayName: "No routes found",
98591
+ status: "failed",
98592
+ error: "No credentials configured for any provider"
98593
+ }
98594
+ ]);
98595
+ setProbeMode("done");
98596
+ return;
98597
+ }
98598
+ const ruleEntries2 = Object.entries(config3.routing ?? {});
98599
+ const matchedRule = ruleEntries2.find(([pat]) => {
98600
+ if (pat === model)
98601
+ return true;
98602
+ if (pat.includes("*")) {
98603
+ const regex2 = new RegExp("^" + pat.replace(/\*/g, ".*") + "$");
98604
+ return regex2.test(model);
98605
+ }
98606
+ return false;
98607
+ });
98608
+ const initial = chain.map((r) => {
98609
+ const provDef = PROVIDERS.find((p) => p.name === r.provider);
98610
+ const hk = !!(provDef && (config3.apiKeys?.[provDef.apiKeyEnvVar] || process.env[provDef.apiKeyEnvVar]));
98611
+ return {
98612
+ provider: r.provider,
98613
+ displayName: r.displayName,
98614
+ status: hk ? "pending" : "no_key",
98615
+ hasKey: hk,
98616
+ reason: matchedRule ? `Custom rule: ${matchedRule[0]}` : "Default fallback chain"
98617
+ };
98618
+ });
98619
+ setProbeResults(initial);
98620
+ setProbeMode("running");
98621
+ (async () => {
98622
+ for (let i = 0;i < chain.length; i++) {
98623
+ const entry = initial[i];
98624
+ if (!entry.hasKey) {
98625
+ continue;
98626
+ }
98627
+ setProbeResults((prev) => prev.map((e, idx) => idx === i ? { ...e, status: "testing" } : e));
98628
+ const startMs = Date.now();
98629
+ const provDef = PROVIDERS.find((p) => p.name === chain[i].provider);
98630
+ const apiKey = (provDef ? config3.apiKeys?.[provDef.apiKeyEnvVar] || process.env[provDef.apiKeyEnvVar] : undefined) ?? "";
98631
+ const elapsed = () => Date.now() - startMs;
98632
+ const result = await testProviderKey(chain[i].provider, apiKey);
98633
+ const ms = elapsed();
98634
+ const ok = result === "valid";
98635
+ setProbeResults((prev) => prev.map((e, idx) => {
98636
+ if (idx === i)
98637
+ return { ...e, status: ok ? "success" : "failed", error: ok ? undefined : result, ms };
98638
+ if (idx > i && ok && e.status !== "no_key")
98639
+ return { ...e, status: "skipped" };
98640
+ return e;
98641
+ }));
98642
+ if (ok)
98643
+ break;
98644
+ }
98645
+ setProbeMode("done");
98646
+ })();
98647
+ return;
98648
+ } else if (key.name === "escape") {
98649
+ setProbeModel("");
98650
+ setProbeMode("idle");
98651
+ } else if (key.name === "backspace" || key.name === "delete") {
98652
+ setProbeModel((p) => p.slice(0, -1));
98653
+ } else if (key.raw && key.raw.length === 1 && !key.ctrl && !key.meta) {
98654
+ setProbeModel((p) => p + key.raw);
98655
+ }
98656
+ return;
98657
+ }
98658
+ if (probeMode === "running" && activeTab === "routing") {
98659
+ if (key.name === "escape") {
98660
+ setProbeModel("");
98661
+ setProbeResults([]);
98662
+ setProbeMode("idle");
98663
+ }
98664
+ return;
98665
+ }
98666
+ if (probeMode === "done" && activeTab === "routing") {
98667
+ if (key.name === "q") {
98668
+ return quit();
98669
+ } else if (key.name === "escape" || key.name === "p") {
98670
+ setProbeModel("");
98671
+ setProbeResults([]);
98672
+ setProbeMode("idle");
98673
+ } else if (key.name === "return" || key.name === "enter") {
98674
+ setProbeModel("");
98675
+ setProbeResults([]);
98676
+ setProbeMode("input");
98677
+ }
98678
+ return;
98679
+ }
98437
98680
  if (mode === "input_key" || mode === "input_endpoint") {
98438
98681
  if (key.name === "return" || key.name === "enter") {
98439
98682
  const val = inputValue.trim();
@@ -98591,6 +98834,23 @@ function App() {
98591
98834
  } else {
98592
98835
  setStatusMsg("No stored key to remove.");
98593
98836
  }
98837
+ } else if (key.name === "t") {
98838
+ const apiKey = config3.apiKeys?.[selectedProvider.apiKeyEnvVar] || process.env[selectedProvider.apiKeyEnvVar];
98839
+ const provName = selectedProvider.name;
98840
+ if (!apiKey) {
98841
+ setTestResults((prev) => ({ ...prev, [provName]: { status: "failed", error: "No key configured" } }));
98842
+ return;
98843
+ }
98844
+ setTestResults((prev) => ({ ...prev, [provName]: { status: "testing" } }));
98845
+ const startMs = Date.now();
98846
+ testProviderKey(provName, apiKey).then((result) => {
98847
+ const ms = Date.now() - startMs;
98848
+ const ok = result === "valid";
98849
+ setTestResults((prev) => ({
98850
+ ...prev,
98851
+ [provName]: ok ? { status: "valid", ms } : { status: "failed", error: result, ms }
98852
+ }));
98853
+ });
98594
98854
  }
98595
98855
  } else if (activeTab === "routing") {
98596
98856
  if (key.name === "a") {
@@ -98615,6 +98875,11 @@ function App() {
98615
98875
  setProviderIndex((i) => Math.max(0, i - 1));
98616
98876
  } else if (key.name === "down" || key.name === "j") {
98617
98877
  setProviderIndex((i) => Math.min(Math.max(0, ruleEntries.length - 1), i + 1));
98878
+ } else if (key.name === "p") {
98879
+ setProbeModel("");
98880
+ setProbeResults([]);
98881
+ setStatusMsg(null);
98882
+ setProbeMode("input");
98618
98883
  }
98619
98884
  } else if (activeTab === "privacy") {
98620
98885
  if (key.name === "t") {
@@ -98771,6 +99036,21 @@ function App() {
98771
99036
  const isFirstUnready = !isReady && !separatorRendered;
98772
99037
  if (isFirstUnready)
98773
99038
  separatorRendered = true;
99039
+ const tr = testResults[p.name];
99040
+ let statusFg = isReady ? C2.green : C2.dim;
99041
+ let statusText = isReady ? "ready " : "not set";
99042
+ if (tr) {
99043
+ if (tr.status === "testing") {
99044
+ statusFg = C2.yellow;
99045
+ statusText = "testing";
99046
+ } else if (tr.status === "valid") {
99047
+ statusFg = C2.green;
99048
+ statusText = tr.ms !== undefined ? `ready ${tr.ms}ms` : "ready \u2713";
99049
+ } else {
99050
+ statusFg = C2.red;
99051
+ statusText = "FAIL ";
99052
+ }
99053
+ }
98774
99054
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
98775
99055
  flexDirection: "column",
98776
99056
  children: [
@@ -98794,8 +99074,8 @@ function App() {
98794
99074
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
98795
99075
  children: [
98796
99076
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
98797
- fg: isReady ? C2.green : C2.dim,
98798
- children: isReady ? "\u25CF" : "\u25CB"
99077
+ fg: tr?.status === "testing" ? C2.yellow : isReady ? C2.green : C2.dim,
99078
+ children: tr?.status === "testing" ? "\u25CC" : isReady ? "\u25CF" : "\u25CB"
98799
99079
  }, undefined, false, undefined, this),
98800
99080
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
98801
99081
  children: " "
@@ -98809,13 +99089,10 @@ function App() {
98809
99089
  fg: C2.dim,
98810
99090
  children: " "
98811
99091
  }, undefined, false, undefined, this),
98812
- isReady ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
98813
- fg: C2.green,
98814
- bold: true,
98815
- children: "ready "
98816
- }, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
98817
- fg: C2.dim,
98818
- children: "not set"
99092
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99093
+ fg: statusFg,
99094
+ bold: tr?.status === "valid" || isReady,
99095
+ children: statusText
98819
99096
  }, undefined, false, undefined, this),
98820
99097
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
98821
99098
  fg: C2.dim,
@@ -98951,6 +99228,7 @@ function App() {
98951
99228
  ]
98952
99229
  }, undefined, true, undefined, this);
98953
99230
  }
99231
+ const tr = testResults[selectedProvider.name];
98954
99232
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
98955
99233
  height: DETAIL_H,
98956
99234
  border: true,
@@ -99055,6 +99333,50 @@ function App() {
99055
99333
  children: selectedProvider.keyUrl
99056
99334
  }, undefined, false, undefined, this)
99057
99335
  ]
99336
+ }, undefined, true, undefined, this),
99337
+ tr && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99338
+ children: [
99339
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99340
+ fg: C2.blue,
99341
+ bold: true,
99342
+ children: "Test: "
99343
+ }, undefined, false, undefined, this),
99344
+ tr.status === "testing" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99345
+ fg: C2.yellow,
99346
+ bold: true,
99347
+ children: "\u25CC testing..."
99348
+ }, undefined, false, undefined, this),
99349
+ tr.status === "valid" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
99350
+ children: [
99351
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99352
+ fg: C2.green,
99353
+ bold: true,
99354
+ children: "\u25CF valid"
99355
+ }, undefined, false, undefined, this),
99356
+ tr.ms !== undefined && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99357
+ fg: C2.dim,
99358
+ children: ` ${tr.ms}ms`
99359
+ }, undefined, false, undefined, this),
99360
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99361
+ fg: C2.fgMuted,
99362
+ children: " API key is valid and endpoint is reachable."
99363
+ }, undefined, false, undefined, this)
99364
+ ]
99365
+ }, undefined, true, undefined, this),
99366
+ tr.status === "failed" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
99367
+ children: [
99368
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99369
+ fg: C2.red,
99370
+ bold: true,
99371
+ children: "\u2717 failed"
99372
+ }, undefined, false, undefined, this),
99373
+ tr.error && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99374
+ fg: C2.red,
99375
+ children: ` ${tr.error}`
99376
+ }, undefined, false, undefined, this)
99377
+ ]
99378
+ }, undefined, true, undefined, this)
99379
+ ]
99058
99380
  }, undefined, true, undefined, this)
99059
99381
  ]
99060
99382
  }, undefined, true, undefined, this);
@@ -99062,7 +99384,265 @@ function App() {
99062
99384
  function chainStr(chain) {
99063
99385
  return chain.join(" \u2192 ");
99064
99386
  }
99387
+ const PROVIDER_REASONS = {
99388
+ litellm: "LiteLLM proxy",
99389
+ "opencode-zen": "Free tier (OpenCode Zen)",
99390
+ "opencode-zen-go": "Zen Go plan",
99391
+ kimi: "Native Kimi API",
99392
+ "kimi-coding": "Kimi Coding Plan",
99393
+ minimax: "Native MiniMax API",
99394
+ "minimax-coding": "MiniMax Coding Plan",
99395
+ glm: "Native GLM API",
99396
+ "glm-coding": "GLM Coding Plan",
99397
+ google: "Direct Gemini API",
99398
+ openai: "Direct OpenAI API",
99399
+ zai: "Z.AI API",
99400
+ ollamacloud: "Cloud Ollama",
99401
+ vertex: "Vertex AI Express",
99402
+ openrouter: "Fallback: 580+ models"
99403
+ };
99065
99404
  function RoutingContent() {
99405
+ const probeBoxH = contentH + DETAIL_H + 1;
99406
+ if (probeMode === "input") {
99407
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
99408
+ height: probeBoxH,
99409
+ border: true,
99410
+ borderStyle: "single",
99411
+ borderColor: C2.focusBorder,
99412
+ backgroundColor: C2.bg,
99413
+ flexDirection: "column",
99414
+ paddingX: 2,
99415
+ paddingY: 1,
99416
+ children: [
99417
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99418
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99419
+ fg: C2.white,
99420
+ bold: true,
99421
+ children: "Route Probe"
99422
+ }, undefined, false, undefined, this)
99423
+ }, undefined, false, undefined, this),
99424
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99425
+ children: " "
99426
+ }, undefined, false, undefined, this),
99427
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99428
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99429
+ fg: C2.fgMuted,
99430
+ children: "Enter a model name to trace its routing chain:"
99431
+ }, undefined, false, undefined, this)
99432
+ }, undefined, false, undefined, this),
99433
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
99434
+ flexDirection: "row",
99435
+ height: 1,
99436
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99437
+ children: [
99438
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99439
+ fg: C2.green,
99440
+ bold: true,
99441
+ children: "> "
99442
+ }, undefined, false, undefined, this),
99443
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99444
+ fg: C2.white,
99445
+ children: probeModel
99446
+ }, undefined, false, undefined, this),
99447
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99448
+ fg: C2.cyan,
99449
+ children: "\u2588"
99450
+ }, undefined, false, undefined, this)
99451
+ ]
99452
+ }, undefined, true, undefined, this)
99453
+ }, undefined, false, undefined, this),
99454
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99455
+ children: " "
99456
+ }, undefined, false, undefined, this),
99457
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99458
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99459
+ fg: C2.dim,
99460
+ children: "Examples: kimi-k2 deepseek-r1 gemini-2.0-flash gpt-4o"
99461
+ }, undefined, false, undefined, this)
99462
+ }, undefined, false, undefined, this),
99463
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99464
+ children: " "
99465
+ }, undefined, false, undefined, this),
99466
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99467
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99468
+ fg: C2.fgMuted,
99469
+ children: "The probe resolves the fallback chain and tests each provider's"
99470
+ }, undefined, false, undefined, this)
99471
+ }, undefined, false, undefined, this),
99472
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99473
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99474
+ fg: C2.fgMuted,
99475
+ children: "API key in order, stopping at the first success."
99476
+ }, undefined, false, undefined, this)
99477
+ }, undefined, false, undefined, this)
99478
+ ]
99479
+ }, undefined, true, undefined, this);
99480
+ }
99481
+ if (probeMode === "running" || probeMode === "done") {
99482
+ const successEntry = probeResults.find((e) => e.status === "success");
99483
+ const allFailed = probeMode === "done" && !successEntry;
99484
+ const totalMs = successEntry?.ms;
99485
+ const statusBadge = probeMode === "running" ? { text: "probing...", color: C2.yellow } : successEntry ? { text: "routed", color: C2.green } : { text: "no route", color: C2.red };
99486
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
99487
+ height: probeBoxH,
99488
+ border: true,
99489
+ borderStyle: "single",
99490
+ borderColor: probeMode === "running" ? C2.focusBorder : C2.blue,
99491
+ backgroundColor: C2.bg,
99492
+ flexDirection: "column",
99493
+ paddingX: 2,
99494
+ paddingY: 1,
99495
+ children: [
99496
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
99497
+ flexDirection: "row",
99498
+ height: 1,
99499
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99500
+ children: [
99501
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99502
+ fg: C2.white,
99503
+ bold: true,
99504
+ children: probeMode === "done" ? "Probe: " : "Probing: "
99505
+ }, undefined, false, undefined, this),
99506
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99507
+ fg: C2.cyan,
99508
+ bold: true,
99509
+ children: probeModel
99510
+ }, undefined, false, undefined, this),
99511
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99512
+ fg: C2.dim,
99513
+ children: " "
99514
+ }, undefined, false, undefined, this),
99515
+ probeMode === "done" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99516
+ fg: statusBadge.color,
99517
+ bold: true,
99518
+ children: [
99519
+ successEntry ? "\u25CF " : "\u2717 ",
99520
+ statusBadge.text
99521
+ ]
99522
+ }, undefined, true, undefined, this),
99523
+ probeMode === "running" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99524
+ fg: C2.yellow,
99525
+ children: "\u25CC probing..."
99526
+ }, undefined, false, undefined, this)
99527
+ ]
99528
+ }, undefined, true, undefined, this)
99529
+ }, undefined, false, undefined, this),
99530
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99531
+ children: " "
99532
+ }, undefined, false, undefined, this),
99533
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99534
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99535
+ fg: C2.fgMuted,
99536
+ children: probeResults[0]?.reason ?? `Chain (${probeResults.length} providers):`
99537
+ }, undefined, false, undefined, this)
99538
+ }, undefined, false, undefined, this),
99539
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99540
+ children: " "
99541
+ }, undefined, false, undefined, this),
99542
+ probeResults.map((entry, idx) => {
99543
+ const isNoKey = entry.status === "no_key";
99544
+ const isNotReached = entry.status === "skipped";
99545
+ const isSelected = entry.status === "success" && probeMode === "done";
99546
+ const statusIcon = entry.status === "success" ? "\u25CF" : entry.status === "failed" ? "\u2717" : entry.status === "testing" ? "\u25CC" : isNoKey ? "\u25CB" : isNotReached ? "\xB7" : "\u25CB";
99547
+ const statusColor = entry.status === "success" ? C2.green : entry.status === "failed" ? C2.red : entry.status === "testing" ? C2.yellow : C2.dim;
99548
+ const nameCol = entry.displayName.padEnd(18).substring(0, 18);
99549
+ const statusText = entry.status === "success" ? entry.ms !== undefined ? `${entry.ms}ms` : "success" : entry.status === "failed" ? entry.error ?? "failed" : entry.status === "testing" ? "testing..." : isNoKey ? "not configured, skipping" : isNotReached ? "not reached" : "waiting";
99550
+ const reason = PROVIDER_REASONS[entry.provider] ?? entry.provider;
99551
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
99552
+ flexDirection: "column",
99553
+ children: [
99554
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99555
+ children: [
99556
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99557
+ fg: C2.dim,
99558
+ children: `${idx + 1}. `
99559
+ }, undefined, false, undefined, this),
99560
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99561
+ fg: isNoKey ? C2.dim : isSelected ? C2.white : isNotReached ? C2.dim : C2.fgMuted,
99562
+ bold: isSelected,
99563
+ children: nameCol
99564
+ }, undefined, false, undefined, this),
99565
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99566
+ fg: C2.dim,
99567
+ children: " "
99568
+ }, undefined, false, undefined, this),
99569
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99570
+ fg: statusColor,
99571
+ bold: entry.status === "success",
99572
+ children: [
99573
+ statusIcon,
99574
+ " ",
99575
+ statusText
99576
+ ]
99577
+ }, undefined, true, undefined, this),
99578
+ isSelected && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99579
+ fg: C2.green,
99580
+ bold: true,
99581
+ children: " \u2190 routed here"
99582
+ }, undefined, false, undefined, this)
99583
+ ]
99584
+ }, undefined, true, undefined, this),
99585
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99586
+ children: [
99587
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99588
+ fg: C2.dim,
99589
+ children: " \u21B3 "
99590
+ }, undefined, false, undefined, this),
99591
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99592
+ fg: isNoKey ? C2.dim : C2.fgMuted,
99593
+ children: reason
99594
+ }, undefined, false, undefined, this)
99595
+ ]
99596
+ }, undefined, true, undefined, this)
99597
+ ]
99598
+ }, entry.provider, true, undefined, this);
99599
+ }),
99600
+ probeMode === "done" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
99601
+ children: [
99602
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99603
+ children: " "
99604
+ }, undefined, false, undefined, this),
99605
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
99606
+ children: allFailed ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
99607
+ children: [
99608
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99609
+ fg: C2.red,
99610
+ bold: true,
99611
+ children: "Result: "
99612
+ }, undefined, false, undefined, this),
99613
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99614
+ fg: C2.red,
99615
+ children: "\u2717 No provider could serve this model"
99616
+ }, undefined, false, undefined, this)
99617
+ ]
99618
+ }, undefined, true, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
99619
+ children: [
99620
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99621
+ fg: C2.green,
99622
+ bold: true,
99623
+ children: "Result: "
99624
+ }, undefined, false, undefined, this),
99625
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99626
+ fg: C2.fgMuted,
99627
+ children: "Routed to "
99628
+ }, undefined, false, undefined, this),
99629
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99630
+ fg: C2.cyan,
99631
+ bold: true,
99632
+ children: successEntry.displayName
99633
+ }, undefined, false, undefined, this),
99634
+ totalMs !== undefined && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
99635
+ fg: C2.fgMuted,
99636
+ children: ` in ${totalMs}ms`
99637
+ }, undefined, false, undefined, this)
99638
+ ]
99639
+ }, undefined, true, undefined, this)
99640
+ }, undefined, false, undefined, this)
99641
+ ]
99642
+ }, undefined, true, undefined, this)
99643
+ ]
99644
+ }, undefined, true, undefined, this);
99645
+ }
99066
99646
  const innerH = contentH - 2;
99067
99647
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
99068
99648
  height: contentH,
@@ -99340,6 +99920,9 @@ function App() {
99340
99920
  }, undefined, true, undefined, this);
99341
99921
  }
99342
99922
  function RoutingDetail() {
99923
+ if (probeMode !== "idle") {
99924
+ return null;
99925
+ }
99343
99926
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
99344
99927
  height: DETAIL_H,
99345
99928
  border: true,
@@ -99661,11 +100244,29 @@ function App() {
99661
100244
  }
99662
100245
  function Footer() {
99663
100246
  let keys;
99664
- if (activeTab === "providers") {
100247
+ if (activeTab === "routing" && probeMode === "input") {
100248
+ keys = [
100249
+ [C2.green, "Enter", "probe"],
100250
+ [C2.red, "Esc", "cancel"]
100251
+ ];
100252
+ } else if (activeTab === "routing" && probeMode === "running") {
100253
+ keys = [
100254
+ [C2.yellow, "\u25CC", "probing..."],
100255
+ [C2.red, "Esc", "cancel"]
100256
+ ];
100257
+ } else if (activeTab === "routing" && probeMode === "done") {
100258
+ keys = [
100259
+ [C2.cyan, "p", "back to routes"],
100260
+ [C2.green, "Enter", "probe another"],
100261
+ [C2.red, "Esc", "back to routes"],
100262
+ [C2.dim, "q", "quit"]
100263
+ ];
100264
+ } else if (activeTab === "providers") {
99665
100265
  keys = [
99666
100266
  [C2.blue, "\u2191\u2193", "navigate"],
99667
100267
  [C2.green, "s", "set key"],
99668
100268
  [C2.green, "e", "endpoint"],
100269
+ [C2.cyan, "t", "test key"],
99669
100270
  [C2.red, "x", "remove"],
99670
100271
  [C2.blue, "Tab", "section"],
99671
100272
  [C2.dim, "q", "quit"]
@@ -99675,6 +100276,7 @@ function App() {
99675
100276
  [C2.blue, "\u2191\u2193", "navigate"],
99676
100277
  [C2.green, "a", "add rule"],
99677
100278
  [C2.red, "d", "delete"],
100279
+ [C2.cyan, "p", "probe"],
99678
100280
  [C2.blue, "Tab", "section"],
99679
100281
  [C2.dim, "q", "quit"]
99680
100282
  ];
@@ -99801,7 +100403,10 @@ function App() {
99801
100403
  var import_react13, VERSION2 = "v5.16";
99802
100404
  var init_App = __esm(async () => {
99803
100405
  init_profile_config();
100406
+ init_auto_route();
100407
+ init_model_parser();
99804
100408
  init_stats_buffer();
100409
+ init_test_provider();
99805
100410
  init_providers();
99806
100411
  init_theme2();
99807
100412
  init_jsx_dev_runtime();
@@ -105265,7 +105870,7 @@ async function describeImage(image, auth) {
105265
105870
  if (auth["x-api-key"])
105266
105871
  headers["x-api-key"] = auth["x-api-key"];
105267
105872
  const controller = new AbortController;
105268
- const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
105873
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS2);
105269
105874
  try {
105270
105875
  const response = await fetch(VISION_ENDPOINT, {
105271
105876
  method: "POST",
@@ -105289,7 +105894,7 @@ async function describeImage(image, auth) {
105289
105894
  } catch (err) {
105290
105895
  clearTimeout(timeoutId);
105291
105896
  if (err.name === "AbortError") {
105292
- log(`[VisionProxy] Request timed out after ${TIMEOUT_MS}ms`);
105897
+ log(`[VisionProxy] Request timed out after ${TIMEOUT_MS2}ms`);
105293
105898
  } else {
105294
105899
  log(`[VisionProxy] Fetch error: ${err.message}`);
105295
105900
  }
@@ -105312,7 +105917,7 @@ async function describeImages(images, auth) {
105312
105917
  return null;
105313
105918
  }
105314
105919
  }
105315
- var VISION_MODEL = "claude-sonnet-4-20250514", MAX_TOKENS_PER_IMAGE = 1024, VISION_ENDPOINT = "https://api.anthropic.com/v1/messages", TIMEOUT_MS = 30000, DESCRIPTION_PROMPT = `Describe this image in detail for a model that cannot see images. Provide:
105920
+ var VISION_MODEL = "claude-sonnet-4-20250514", MAX_TOKENS_PER_IMAGE = 1024, VISION_ENDPOINT = "https://api.anthropic.com/v1/messages", TIMEOUT_MS2 = 30000, DESCRIPTION_PROMPT = `Describe this image in detail for a model that cannot see images. Provide:
105316
105921
  - All visible text content (exact quotes where possible)
105317
105922
  - Layout and structure (how elements are arranged spatially)
105318
105923
  - Colors, visual style, and key visual elements
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "6.2.1",
3
+ "version": "6.3.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",
@@ -39,6 +39,7 @@
39
39
  },
40
40
  "files": [
41
41
  "dist/",
42
+ "native/mtm/mtm-*",
42
43
  "AI_AGENT_GUIDE.md",
43
44
  "recommended-models.json",
44
45
  "skills/"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "1.2.0",
3
- "lastUpdated": "2026-03-24",
3
+ "lastUpdated": "2026-03-25",
4
4
  "source": "https://openrouter.ai/models?categories=programming&fmt=cards&order=top-weekly",
5
5
  "models": [
6
6
  {