claudish 7.50.0 → 7.51.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.
Files changed (2) hide show
  1. package/dist/index.js +729 -90
  2. 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.50.0";
732
+ var VERSION = "7.51.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -27509,6 +27509,9 @@ function loadConfig() {
27509
27509
  if (config2.customEndpoints !== undefined) {
27510
27510
  merged.customEndpoints = config2.customEndpoints;
27511
27511
  }
27512
+ if (config2.predefinedEndpoints !== undefined) {
27513
+ merged.predefinedEndpoints = config2.predefinedEndpoints;
27514
+ }
27512
27515
  if (config2.behavior !== undefined) {
27513
27516
  merged.behavior = config2.behavior;
27514
27517
  }
@@ -27933,24 +27936,44 @@ function getApiKeyInfo(providerName) {
27933
27936
  oauthFallback: def.oauthFallback
27934
27937
  };
27935
27938
  }
27939
+ function describeMissingCredential(providerName) {
27940
+ const info = getApiKeyInfo(providerName);
27941
+ const keyNames = info?.envVar ? [info.envVar, ...info.aliases ?? []].join(" or ") : undefined;
27942
+ const signup = info?.url ? ` Get one at ${info.url}.` : "";
27943
+ const def = getProviderByName(providerName);
27944
+ if (isLocalTransport(providerName)) {
27945
+ const where = def ? ` Claudish will use ${getEffectiveBaseUrl(def)}.` : "";
27946
+ const keyClause = keyNames ? ` (Only set ${keyNames} if your local server requires a bearer token.)` : "";
27947
+ return `Provider "${providerName}" is a LOCAL server and is not enabled. ` + "Enable it in `claudish config` (Providers tab), or add " + `"localProviders": ["${providerName}"] to ~/.claudish/config.json.${where}${keyClause}`;
27948
+ }
27949
+ if (def?.oauthFallback) {
27950
+ const keyClause = keyNames ? ` Or set ${keyNames} (env, config, or 1Password import) to use a metered API key instead.${signup}` : "";
27951
+ return `No credential for provider "${providerName}". Sign in with ` + `\`claudish login ${providerName}\` to use your existing subscription.${keyClause}`;
27952
+ }
27953
+ return keyNames ? `No API key for provider "${providerName}". Set ${keyNames} (env, config, or 1Password import).${signup}` : `No API key for provider "${providerName}".`;
27954
+ }
27936
27955
  function getDisplayName(providerName) {
27937
27956
  const def = getProviderByName(providerName);
27938
27957
  return def?.displayName || providerName.charAt(0).toUpperCase() + providerName.slice(1);
27939
27958
  }
27940
- function getEffectiveBaseUrl(def) {
27941
- if (def.baseUrlEnvVars) {
27942
- for (const envVar of def.baseUrlEnvVars) {
27943
- const fromConfig = getEndpoint(envVar);
27944
- if (fromConfig)
27945
- return fromConfig;
27946
- }
27947
- for (const envVar of def.baseUrlEnvVars) {
27948
- const value = process.env[envVar];
27949
- if (value)
27950
- return value;
27951
- }
27959
+ function baseUrlOverrideCandidates(baseUrlEnvVars) {
27960
+ const found = [];
27961
+ if (!baseUrlEnvVars || baseUrlEnvVars.length === 0)
27962
+ return found;
27963
+ for (const envVar of baseUrlEnvVars) {
27964
+ const fromConfig = getEndpoint(envVar);
27965
+ if (fromConfig)
27966
+ found.push({ envVar, value: fromConfig, source: "config" });
27952
27967
  }
27953
- return def.baseUrl;
27968
+ for (const envVar of baseUrlEnvVars) {
27969
+ const value = process.env[envVar];
27970
+ if (value)
27971
+ found.push({ envVar, value, source: "env" });
27972
+ }
27973
+ return found;
27974
+ }
27975
+ function getEffectiveBaseUrl(def) {
27976
+ return baseUrlOverrideCandidates(def.baseUrlEnvVars)[0]?.value ?? def.baseUrl;
27954
27977
  }
27955
27978
  function isLocalTransport(providerName) {
27956
27979
  if (!_localProvidersCache) {
@@ -30850,6 +30873,22 @@ var init_antigravity_credential = __esm(() => {
30850
30873
  init_antigravity_user();
30851
30874
  });
30852
30875
 
30876
+ // src/auth/credentials/local-api-key.ts
30877
+ function resolveLocalApiKey(q) {
30878
+ return realValue(process.env[q.envVar]) || (q.aliases ?? []).map((a) => realValue(process.env[a])).find((v) => !!v) || realValue(getApiKey(q.envVar));
30879
+ }
30880
+ function hasLocalApiKey(q) {
30881
+ try {
30882
+ return !!resolveLocalApiKey(q);
30883
+ } catch {
30884
+ return false;
30885
+ }
30886
+ }
30887
+ var init_local_api_key = __esm(() => {
30888
+ init_env_placeholder();
30889
+ init_profile_config();
30890
+ });
30891
+
30853
30892
  // src/auth/credentials/api-key-credential.ts
30854
30893
  import { existsSync as existsSync8 } from "fs";
30855
30894
  import { homedir as homedir10 } from "os";
@@ -30877,7 +30916,7 @@ class ApiKeyCredentialProvider {
30877
30916
  this.declaredKey = descriptor.declaredKey;
30878
30917
  }
30879
30918
  resolveFromEnvConfig() {
30880
- return realValue(process.env[this.envVar]) || this.aliases.map((a) => realValue(process.env[a])).find((v) => !!v) || realValue(getApiKey(this.envVar)) || realValue(this.resolveDeclared());
30919
+ return resolveLocalApiKey({ envVar: this.envVar, aliases: this.aliases }) || realValue(this.resolveDeclared());
30881
30920
  }
30882
30921
  resolveDeclared() {
30883
30922
  try {
@@ -30958,7 +30997,7 @@ class ApiKeyCredentialProvider {
30958
30997
  }
30959
30998
  var init_api_key_credential = __esm(() => {
30960
30999
  init_env_placeholder();
30961
- init_profile_config();
31000
+ init_local_api_key();
30962
31001
  init_op_source();
30963
31002
  });
30964
31003
 
@@ -32359,7 +32398,7 @@ var init_authority = __esm(() => {
32359
32398
  });
32360
32399
 
32361
32400
  // src/config-schema.ts
32362
- var BuiltinDefaultProviderSchema, CustomEndpointSimpleSchema, CustomEndpointComplexSchema, CustomEndpointSchema, DefaultProviderSchema;
32401
+ var BuiltinDefaultProviderSchema, CustomEndpointSimpleSchema, CustomEndpointComplexSchema, CustomEndpointSchema, PredefinedEndpointsConfigSchema, DefaultProviderSchema;
32363
32402
  var init_config_schema = __esm(() => {
32364
32403
  init_zod();
32365
32404
  BuiltinDefaultProviderSchema = exports_external.enum([
@@ -32394,6 +32433,11 @@ var init_config_schema = __esm(() => {
32394
32433
  CustomEndpointSimpleSchema,
32395
32434
  CustomEndpointComplexSchema
32396
32435
  ]);
32436
+ PredefinedEndpointsConfigSchema = exports_external.object({
32437
+ enabled: exports_external.boolean().optional(),
32438
+ disable: exports_external.array(exports_external.string()).optional(),
32439
+ enable: exports_external.array(exports_external.string()).optional()
32440
+ });
32397
32441
  DefaultProviderSchema = exports_external.union([BuiltinDefaultProviderSchema, exports_external.string().min(1)]);
32398
32442
  });
32399
32443
 
@@ -36581,7 +36625,14 @@ class OpenAIProviderTransport {
36581
36625
  async getHeaders() {
36582
36626
  const headers = {};
36583
36627
  if (this.apiKey) {
36584
- headers.Authorization = `Bearer ${this.apiKey}`;
36628
+ if (this.provider.authScheme === "x-api-key") {
36629
+ headers["x-api-key"] = this.apiKey;
36630
+ } else {
36631
+ headers.Authorization = `Bearer ${this.apiKey}`;
36632
+ }
36633
+ }
36634
+ if (this.provider.headers) {
36635
+ Object.assign(headers, this.provider.headers);
36585
36636
  }
36586
36637
  return headers;
36587
36638
  }
@@ -41383,6 +41434,21 @@ var init_composed_handler = __esm(() => {
41383
41434
  STREAM_RETRY_DELAYS_MS = [3000, 15000, 30000];
41384
41435
  });
41385
41436
 
41437
+ // src/providers/endpoint-diagnostics.ts
41438
+ function recordEndpointUnavailable(name, reason) {
41439
+ reasons.set(name, reason);
41440
+ }
41441
+ function clearEndpointUnavailable(name) {
41442
+ reasons.delete(name);
41443
+ }
41444
+ function getEndpointUnavailableReason(name) {
41445
+ return reasons.get(name);
41446
+ }
41447
+ var reasons;
41448
+ var init_endpoint_diagnostics = __esm(() => {
41449
+ reasons = new Map;
41450
+ });
41451
+
41386
41452
  // src/providers/devin/devin-request.ts
41387
41453
  import { randomUUID as randomUUID3 } from "crypto";
41388
41454
  function encodeChatMetadata(meta3) {
@@ -42645,6 +42711,21 @@ var init_litellm = __esm(() => {
42645
42711
  });
42646
42712
 
42647
42713
  // src/providers/custom-endpoints-loader.ts
42714
+ function registerEndpoint(name, entry, ovr) {
42715
+ const validated = CustomEndpointSchema.parse(entry);
42716
+ const def = buildProviderDefinition(name, validated, ovr);
42717
+ const profile = buildProviderProfile(validated, ovr);
42718
+ registerRuntimeProvider(def);
42719
+ registerRuntimeProfile(name, profile);
42720
+ credentials.registerApiKeyProvider({
42721
+ name: def.name,
42722
+ envVar: def.apiKeyEnvVar,
42723
+ aliases: def.apiKeyAliases,
42724
+ authScheme: def.authScheme === "x-api-key" ? "x-api-key" : "bearer",
42725
+ declaredKey: () => resolveDeclaredEndpointKey(validated)
42726
+ });
42727
+ clearEndpointUnavailable(name);
42728
+ }
42648
42729
  function loadCustomEndpoints(config2) {
42649
42730
  const result = { registered: 0, errors: [] };
42650
42731
  const raw = config2.customEndpoints;
@@ -42652,17 +42733,7 @@ function loadCustomEndpoints(config2) {
42652
42733
  return result;
42653
42734
  for (const [name, entry] of Object.entries(raw)) {
42654
42735
  try {
42655
- const validated = CustomEndpointSchema.parse(entry);
42656
- const def = buildProviderDefinition(name, validated);
42657
- const profile = buildProviderProfile(validated);
42658
- registerRuntimeProvider(def);
42659
- registerRuntimeProfile(name, profile);
42660
- credentials.registerApiKeyProvider({
42661
- name: def.name,
42662
- envVar: def.apiKeyEnvVar,
42663
- authScheme: def.authScheme === "x-api-key" ? "x-api-key" : "bearer",
42664
- declaredKey: () => resolveDeclaredEndpointKey(validated)
42665
- });
42736
+ registerEndpoint(name, entry);
42666
42737
  result.registered++;
42667
42738
  } catch (err) {
42668
42739
  const message = err instanceof exports_external.ZodError ? err.issues.map((i) => i.message).join(", ") : err instanceof Error ? err.message : String(err);
@@ -42671,22 +42742,28 @@ function loadCustomEndpoints(config2) {
42671
42742
  }
42672
42743
  return result;
42673
42744
  }
42674
- function buildProviderDefinition(name, ep) {
42745
+ function buildProviderDefinition(name, ep, ovr) {
42746
+ const apiKeyEnvVar = ovr?.apiKeyEnvVar ?? customEndpointKeyEnvVar(name);
42747
+ const apiKeyAliases = ovr?.apiKeyAliases;
42748
+ const baseUrlEnvVars = ovr?.baseUrlEnvVars;
42749
+ const apiKeyUrl = ovr?.apiKeyUrl ?? "";
42675
42750
  if (ep.kind === "simple") {
42676
42751
  return {
42677
42752
  name,
42678
42753
  displayName: name,
42679
42754
  transport: ep.format,
42680
42755
  baseUrl: stripTrailingSlash(ep.url),
42756
+ baseUrlEnvVars,
42681
42757
  apiPath: "/chat/completions",
42682
- apiKeyEnvVar: `CUSTOM_${sanitizeEnvName(name)}_KEY`,
42683
- apiKeyDescription: `${name} (custom endpoint)`,
42684
- apiKeyUrl: "",
42758
+ apiKeyEnvVar,
42759
+ apiKeyAliases,
42760
+ apiKeyDescription: ovr?.apiKeyDescription ?? `${name} (custom endpoint)`,
42761
+ apiKeyUrl,
42685
42762
  shortcuts: [name],
42686
42763
  legacyPrefixes: [],
42687
42764
  isDirectApi: true,
42688
42765
  shortestPrefix: name,
42689
- description: `Custom endpoint: ${name}`,
42766
+ description: ovr?.description ?? `Custom endpoint: ${name}`,
42690
42767
  authScheme: "bearer"
42691
42768
  };
42692
42769
  }
@@ -42695,33 +42772,68 @@ function buildProviderDefinition(name, ep) {
42695
42772
  displayName: ep.displayName,
42696
42773
  transport: ep.transport,
42697
42774
  baseUrl: stripTrailingSlash(ep.baseUrl),
42775
+ baseUrlEnvVars,
42698
42776
  apiPath: ep.apiPath ?? "/v1/chat/completions",
42699
- apiKeyEnvVar: `CUSTOM_${sanitizeEnvName(name)}_KEY`,
42700
- apiKeyDescription: `${ep.displayName} (custom endpoint)`,
42701
- apiKeyUrl: "",
42777
+ apiKeyEnvVar,
42778
+ apiKeyAliases,
42779
+ apiKeyDescription: ovr?.apiKeyDescription ?? `${ep.displayName} (custom endpoint)`,
42780
+ apiKeyUrl,
42702
42781
  shortcuts: [name],
42703
42782
  legacyPrefixes: [],
42704
42783
  isDirectApi: true,
42705
42784
  shortestPrefix: name,
42706
- description: `Custom endpoint: ${ep.displayName}`,
42785
+ description: ovr?.description ?? `Custom endpoint: ${ep.displayName}`,
42707
42786
  headers: ep.headers,
42708
42787
  authScheme: ep.authScheme ?? "bearer"
42709
42788
  };
42710
42789
  }
42711
- function buildProviderProfile(ep) {
42790
+ function buildProviderProfile(ep, ovr) {
42712
42791
  return {
42713
42792
  createHandler(ctx) {
42714
42793
  const apiKey = ctx.apiKey || resolveCustomEndpointApiKey(ep);
42794
+ const declaredBaseUrl = ep.kind === "simple" ? ep.url : ep.baseUrl;
42795
+ const resolved = classifyEndpointBaseUrl(declaredBaseUrl, ovr?.baseUrlEnvVars);
42796
+ if (!resolved.ok) {
42797
+ const reason = describeBadBaseUrlOverride(resolved, declaredBaseUrl);
42798
+ console.error(`[claudish] ${reason}`);
42799
+ recordEndpointUnavailable(ctx.provider.name, `its ${reason}`);
42800
+ return null;
42801
+ }
42802
+ const baseUrl = resolved.url;
42715
42803
  if (ep.kind === "simple") {
42716
- return buildSimpleHandler(ep, ctx, apiKey);
42804
+ return buildSimpleHandler(ep, ctx, apiKey, baseUrl);
42717
42805
  }
42718
- return buildComplexHandler(ep, ctx, apiKey);
42806
+ return buildComplexHandler(ep, ctx, apiKey, baseUrl);
42719
42807
  }
42720
42808
  };
42721
42809
  }
42722
- function buildSimpleHandler(ep, ctx, apiKey) {
42810
+ function classifyEndpointBaseUrl(declared, baseUrlEnvVars) {
42811
+ for (const candidate of baseUrlOverrideCandidates(baseUrlEnvVars)) {
42812
+ const value = realValue(candidate.value)?.trim();
42813
+ if (!value)
42814
+ continue;
42815
+ if (!isHttpUrl(value)) {
42816
+ return { ok: false, envVar: candidate.envVar, value, source: candidate.source };
42817
+ }
42818
+ return { ok: true, url: stripTrailingSlash(value) };
42819
+ }
42820
+ return { ok: true, url: stripTrailingSlash(declared) };
42821
+ }
42822
+ function describeBadBaseUrlOverride(bad, declared) {
42823
+ const where = bad.source === "config" ? `config.endpoints["${bad.envVar}"] is set to '${bad.value}'` : `${bad.envVar} is set to '${bad.value}'`;
42824
+ const remedy = bad.source === "config" ? "Fix or remove it (claudish config -> Providers)." : "Fix or unset it.";
42825
+ return `${where}, which is not a valid http(s) URL. ` + `${remedy} (Not falling back to ${declared} \u2014 the override was set on purpose.)`;
42826
+ }
42827
+ function isHttpUrl(value) {
42828
+ try {
42829
+ const parsed = new URL(value);
42830
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
42831
+ } catch {
42832
+ return false;
42833
+ }
42834
+ }
42835
+ function buildSimpleHandler(ep, ctx, apiKey, baseUrl) {
42723
42836
  const finalModel = ep.modelPrefix ? `${ep.modelPrefix}${ctx.modelName}` : ctx.modelName;
42724
- const baseUrl = stripTrailingSlash(ep.url);
42725
42837
  if (ep.format === "openai") {
42726
42838
  const remoteProvider2 = {
42727
42839
  name: ctx.provider.name,
@@ -42756,9 +42868,8 @@ function buildSimpleHandler(ep, ctx, apiKey) {
42756
42868
  ...ctx.sharedOpts
42757
42869
  });
42758
42870
  }
42759
- function buildComplexHandler(ep, ctx, apiKey) {
42871
+ function buildComplexHandler(ep, ctx, apiKey, baseUrl) {
42760
42872
  const finalModel = ep.modelPrefix ? `${ep.modelPrefix}${ctx.modelName}` : ctx.modelName;
42761
- const baseUrl = stripTrailingSlash(ep.baseUrl);
42762
42873
  const apiPath = ep.apiPath ?? "/v1/chat/completions";
42763
42874
  switch (ep.transport) {
42764
42875
  case "litellm": {
@@ -42830,6 +42941,9 @@ function resolveDeclaredEndpointKey(ep) {
42830
42941
  function stripTrailingSlash(url2) {
42831
42942
  return url2.replace(/\/+$/, "");
42832
42943
  }
42944
+ function customEndpointKeyEnvVar(name) {
42945
+ return `CUSTOM_${sanitizeEnvName(name)}_KEY`;
42946
+ }
42833
42947
  function sanitizeEnvName(name) {
42834
42948
  return name.toUpperCase().replace(/[^A-Z0-9]/g, "_");
42835
42949
  }
@@ -42840,13 +42954,518 @@ var init_custom_endpoints_loader = __esm(() => {
42840
42954
  init_openai_api_format();
42841
42955
  init_authority();
42842
42956
  init_config_schema();
42957
+ init_env_placeholder();
42843
42958
  init_composed_handler();
42959
+ init_endpoint_diagnostics();
42960
+ init_provider_definitions();
42844
42961
  init_runtime_providers();
42845
42962
  init_anthropic_compat();
42846
42963
  init_litellm();
42847
42964
  init_openai();
42848
42965
  });
42849
42966
 
42967
+ // src/providers/picker-alias-extra.ts
42968
+ var PROVIDER_FILTER_ALIAS_EXTRA;
42969
+ var init_picker_alias_extra = __esm(() => {
42970
+ PROVIDER_FILTER_ALIAS_EXTRA = {
42971
+ gem: "google",
42972
+ zen: "opencode-zen"
42973
+ };
42974
+ });
42975
+
42976
+ // src/providers/predefined-catalog.ts
42977
+ var PREDEFINED_ENDPOINTS;
42978
+ var init_predefined_catalog = __esm(() => {
42979
+ PREDEFINED_ENDPOINTS = [
42980
+ {
42981
+ name: "groq",
42982
+ displayName: "Groq",
42983
+ baseUrl: "https://api.groq.com/openai",
42984
+ apiPath: "/v1/chat/completions",
42985
+ format: "openai",
42986
+ apiKeyEnvVar: "GROQ_API_KEY",
42987
+ apiKeyUrl: "https://console.groq.com/keys",
42988
+ description: "Groq LPU inference (groq@)",
42989
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
42990
+ },
42991
+ {
42992
+ name: "cerebras",
42993
+ displayName: "Cerebras",
42994
+ baseUrl: "https://api.cerebras.ai",
42995
+ apiPath: "/v1/chat/completions",
42996
+ format: "openai",
42997
+ apiKeyEnvVar: "CEREBRAS_API_KEY",
42998
+ apiKeyUrl: "https://cloud.cerebras.ai",
42999
+ description: "Cerebras Inference wafer-scale hosting (cerebras@)",
43000
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43001
+ },
43002
+ {
43003
+ name: "together",
43004
+ displayName: "Together AI",
43005
+ baseUrl: "https://api.together.xyz",
43006
+ apiPath: "/v1/chat/completions",
43007
+ format: "openai",
43008
+ apiKeyEnvVar: "TOGETHER_API_KEY",
43009
+ apiKeyUrl: "https://api.together.xyz/settings/api-keys",
43010
+ description: "Together AI direct inference API (together@) \u2014 distinct from the 'together-ai' vendor id models-index uses as an aggregator label",
43011
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43012
+ },
43013
+ {
43014
+ name: "fireworks",
43015
+ displayName: "Fireworks AI",
43016
+ baseUrl: "https://api.fireworks.ai/inference",
43017
+ apiPath: "/v1/chat/completions",
43018
+ format: "openai",
43019
+ apiKeyEnvVar: "FIREWORKS_API_KEY",
43020
+ apiKeyUrl: "https://fireworks.ai/account/api-keys",
43021
+ description: "Fireworks AI serverless inference (fireworks@)",
43022
+ evidence: {
43023
+ tier: "probe",
43024
+ verdict: "model-gate",
43025
+ status: 404,
43026
+ measuredAt: "2026-08-14",
43027
+ note: "404 rejecting the fake model ('Model not found, inaccessible, and/or not deployed'); the bogus sibling path answered 'Path not found', so the route resolved"
43028
+ }
43029
+ },
43030
+ {
43031
+ name: "deepinfra",
43032
+ displayName: "DeepInfra",
43033
+ baseUrl: "https://api.deepinfra.com/v1/openai",
43034
+ apiPath: "/chat/completions",
43035
+ format: "openai",
43036
+ apiKeyEnvVar: "DEEPINFRA_API_KEY",
43037
+ apiKeyUrl: "https://deepinfra.com/dash/api_keys",
43038
+ description: "DeepInfra hosted open models (deepinfra@)",
43039
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43040
+ },
43041
+ {
43042
+ name: "nebius",
43043
+ displayName: "Nebius AI Studio",
43044
+ baseUrl: "https://api.studio.nebius.com",
43045
+ apiPath: "/v1/chat/completions",
43046
+ format: "openai",
43047
+ apiKeyEnvVar: "NEBIUS_API_KEY",
43048
+ apiKeyUrl: "https://studio.nebius.com",
43049
+ description: "Nebius AI Studio hosted open models (nebius@)",
43050
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43051
+ },
43052
+ {
43053
+ name: "hyperbolic",
43054
+ displayName: "Hyperbolic",
43055
+ baseUrl: "https://api.hyperbolic.xyz",
43056
+ apiPath: "/v1/chat/completions",
43057
+ format: "openai",
43058
+ apiKeyEnvVar: "HYPERBOLIC_API_KEY",
43059
+ apiKeyUrl: "https://app.hyperbolic.xyz",
43060
+ description: "Hyperbolic decentralized GPU inference (hyperbolic@)",
43061
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43062
+ },
43063
+ {
43064
+ name: "sambanova",
43065
+ displayName: "SambaNova Cloud",
43066
+ baseUrl: "https://api.sambanova.ai",
43067
+ apiPath: "/v1/chat/completions",
43068
+ format: "openai",
43069
+ apiKeyEnvVar: "SAMBANOVA_API_KEY",
43070
+ apiKeyUrl: "https://cloud.sambanova.ai",
43071
+ description: "SambaNova Cloud RDU inference (sambanova@)",
43072
+ evidence: {
43073
+ tier: "probe",
43074
+ verdict: "model-gate",
43075
+ status: 404,
43076
+ measuredAt: "2026-08-14",
43077
+ note: "404 naming our fake model ('The model `probe-nonexistent-model-zzz` does not exist'), i.e. the route resolved and the model was rejected before auth"
43078
+ }
43079
+ },
43080
+ {
43081
+ name: "novita",
43082
+ displayName: "Novita AI",
43083
+ baseUrl: "https://api.novita.ai/v3/openai",
43084
+ apiPath: "/chat/completions",
43085
+ format: "openai",
43086
+ apiKeyEnvVar: "NOVITA_API_KEY",
43087
+ apiKeyUrl: "https://novita.ai/settings",
43088
+ description: "Novita AI hosted open models (novita@)",
43089
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43090
+ },
43091
+ {
43092
+ name: "baseten",
43093
+ displayName: "Baseten",
43094
+ baseUrl: "https://inference.baseten.co",
43095
+ apiPath: "/v1/chat/completions",
43096
+ format: "openai",
43097
+ apiKeyEnvVar: "BASETEN_API_KEY",
43098
+ apiKeyUrl: "https://app.baseten.co",
43099
+ description: "Baseten model APIs (baseten@)",
43100
+ evidence: { tier: "probe", verdict: "auth-realm", status: 403, measuredAt: "2026-08-14" }
43101
+ },
43102
+ {
43103
+ name: "perplexity",
43104
+ displayName: "Perplexity",
43105
+ baseUrl: "https://api.perplexity.ai",
43106
+ apiPath: "/chat/completions",
43107
+ format: "openai",
43108
+ apiKeyEnvVar: "PERPLEXITY_API_KEY",
43109
+ apiKeyUrl: "https://www.perplexity.ai/settings/api",
43110
+ description: "Perplexity Sonar search-grounded models (perplexity@)",
43111
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43112
+ },
43113
+ {
43114
+ name: "venice",
43115
+ displayName: "Venice AI",
43116
+ baseUrl: "https://api.venice.ai/api",
43117
+ apiPath: "/v1/chat/completions",
43118
+ format: "openai",
43119
+ apiKeyEnvVar: "VENICE_API_KEY",
43120
+ apiKeyUrl: "https://venice.ai/settings/api",
43121
+ description: "Venice AI privacy-focused open models (venice@)",
43122
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43123
+ },
43124
+ {
43125
+ name: "chutes",
43126
+ displayName: "Chutes",
43127
+ baseUrl: "https://llm.chutes.ai",
43128
+ apiPath: "/v1/chat/completions",
43129
+ format: "openai",
43130
+ apiKeyEnvVar: "CHUTES_API_KEY",
43131
+ description: "Chutes decentralized inference on Bittensor (chutes@)",
43132
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43133
+ },
43134
+ {
43135
+ name: "featherless",
43136
+ displayName: "Featherless AI",
43137
+ baseUrl: "https://api.featherless.ai",
43138
+ apiPath: "/v1/chat/completions",
43139
+ format: "openai",
43140
+ apiKeyEnvVar: "FEATHERLESS_API_KEY",
43141
+ description: "Featherless AI serverless HuggingFace model hosting (featherless@)",
43142
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43143
+ },
43144
+ {
43145
+ name: "parasail",
43146
+ displayName: "Parasail",
43147
+ baseUrl: "https://api.parasail.io",
43148
+ apiPath: "/v1/chat/completions",
43149
+ format: "openai",
43150
+ apiKeyEnvVar: "PARASAIL_API_KEY",
43151
+ description: "Parasail on-demand GPU inference (parasail@)",
43152
+ evidence: {
43153
+ tier: "probe",
43154
+ verdict: "auth-realm",
43155
+ status: 401,
43156
+ measuredAt: "2026-08-14",
43157
+ note: "non-OpenAI error shape: plain text 'Unauthorized. Invalid token.' under content-type application/json;charset=ISO-8859-1. Classified terminal on the 401 status, so no retry is burned; see validation/error-shape-probe.md"
43158
+ }
43159
+ },
43160
+ {
43161
+ name: "inference-net",
43162
+ displayName: "Inference.net",
43163
+ baseUrl: "https://api.inference.net",
43164
+ apiPath: "/v1/chat/completions",
43165
+ format: "openai",
43166
+ apiKeyEnvVar: "INFERENCE_NET_API_KEY",
43167
+ description: "Inference.net distributed open-model inference (inference-net@)",
43168
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43169
+ },
43170
+ {
43171
+ name: "aimlapi",
43172
+ displayName: "AI/ML API",
43173
+ baseUrl: "https://api.aimlapi.com",
43174
+ apiPath: "/v1/chat/completions",
43175
+ format: "openai",
43176
+ apiKeyEnvVar: "AIMLAPI_API_KEY",
43177
+ description: "AI/ML API multi-vendor aggregator (aimlapi@)",
43178
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43179
+ },
43180
+ {
43181
+ name: "requesty",
43182
+ displayName: "Requesty",
43183
+ baseUrl: "https://router.requesty.ai",
43184
+ apiPath: "/v1/chat/completions",
43185
+ format: "openai",
43186
+ apiKeyEnvVar: "REQUESTY_API_KEY",
43187
+ description: "Requesty LLM router (requesty@)",
43188
+ evidence: { tier: "probe", verdict: "auth-realm", status: 403, measuredAt: "2026-08-14" }
43189
+ },
43190
+ {
43191
+ name: "nanogpt",
43192
+ displayName: "NanoGPT",
43193
+ baseUrl: "https://nano-gpt.com/api",
43194
+ apiPath: "/v1/chat/completions",
43195
+ format: "openai",
43196
+ apiKeyEnvVar: "NANOGPT_API_KEY",
43197
+ description: "NanoGPT pay-per-prompt multi-vendor aggregator (nanogpt@)",
43198
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43199
+ },
43200
+ {
43201
+ name: "cohere",
43202
+ displayName: "Cohere",
43203
+ baseUrl: "https://api.cohere.ai/compatibility",
43204
+ apiPath: "/v1/chat/completions",
43205
+ format: "openai",
43206
+ apiKeyEnvVar: "COHERE_API_KEY",
43207
+ apiKeyUrl: "https://dashboard.cohere.com/api-keys",
43208
+ description: "Cohere Command models via their OpenAI-compatibility layer (cohere@)",
43209
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43210
+ },
43211
+ {
43212
+ name: "scaleway",
43213
+ displayName: "Scaleway Generative APIs",
43214
+ baseUrl: "https://api.scaleway.ai",
43215
+ apiPath: "/v1/chat/completions",
43216
+ format: "openai",
43217
+ apiKeyEnvVar: "SCALEWAY_API_KEY",
43218
+ apiKeyUrl: "https://console.scaleway.com",
43219
+ description: "Scaleway Generative APIs, EU-hosted open models (scaleway@)",
43220
+ evidence: { tier: "probe", verdict: "auth-realm", status: 403, measuredAt: "2026-08-14" }
43221
+ },
43222
+ {
43223
+ name: "upstage",
43224
+ displayName: "Upstage",
43225
+ baseUrl: "https://api.upstage.ai",
43226
+ apiPath: "/v1/chat/completions",
43227
+ format: "openai",
43228
+ apiKeyEnvVar: "UPSTAGE_API_KEY",
43229
+ apiKeyUrl: "https://console.upstage.ai",
43230
+ description: "Upstage Solar models (upstage@)",
43231
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43232
+ },
43233
+ {
43234
+ name: "writer",
43235
+ displayName: "Writer",
43236
+ baseUrl: "https://api.writer.com",
43237
+ apiPath: "/v1/chat/completions",
43238
+ format: "openai",
43239
+ apiKeyEnvVar: "WRITER_API_KEY",
43240
+ description: "Writer Palmyra models (writer@)",
43241
+ evidence: {
43242
+ tier: "probe",
43243
+ verdict: "auth-realm",
43244
+ status: 401,
43245
+ measuredAt: "2026-08-14",
43246
+ note: 'non-OpenAI error shape: {"tpe":"fail.auth","errors":[{"description":\u2026}]} with no top-level `error` object. Classified terminal on the 401 status, so no retry is burned; see validation/error-shape-probe.md'
43247
+ }
43248
+ },
43249
+ {
43250
+ name: "moonshot-cn",
43251
+ displayName: "Moonshot AI (China)",
43252
+ baseUrl: "https://api.moonshot.cn",
43253
+ apiPath: "/v1/chat/completions",
43254
+ format: "openai",
43255
+ apiKeyEnvVar: "MOONSHOT_CN_API_KEY",
43256
+ apiKeyUrl: "https://platform.moonshot.cn",
43257
+ description: "Moonshot AI China-region endpoint (moonshot-cn@) \u2014 a separate product from the builtin Kimi provider reached by moonshot@/kimi@",
43258
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43259
+ },
43260
+ {
43261
+ name: "tuningengines",
43262
+ displayName: "Tuning Engines",
43263
+ baseUrl: "https://api.tuningengines.com",
43264
+ apiPath: "/v1/chat/completions",
43265
+ format: "openai",
43266
+ apiKeyEnvVar: "TUNING_ENGINES_API_KEY",
43267
+ baseUrlEnvVars: ["TUNING_ENGINES_BASE_URL"],
43268
+ description: "Tuning Engines enterprise LLM gateway (tuningengines@); self-hosted instances point TUNING_ENGINES_BASE_URL at their own host",
43269
+ evidence: { tier: "probe", verdict: "auth-realm", status: 401, measuredAt: "2026-08-14" }
43270
+ }
43271
+ ];
43272
+ });
43273
+
43274
+ // src/providers/predefined-endpoints.ts
43275
+ function warnOnce2(message) {
43276
+ if (warnedMessages2.has(message))
43277
+ return;
43278
+ warnedMessages2.add(message);
43279
+ console.error(message);
43280
+ }
43281
+ function activeCatalog() {
43282
+ return catalogOverride ?? PREDEFINED_ENDPOINTS;
43283
+ }
43284
+ function reservedNamespace() {
43285
+ const reserved = new Map;
43286
+ for (const def of BUILTIN_PROVIDERS) {
43287
+ reserved.set(def.name.toLowerCase(), def.name);
43288
+ for (const shortcut of def.shortcuts) {
43289
+ reserved.set(shortcut.toLowerCase(), def.name);
43290
+ }
43291
+ for (const legacy of def.legacyPrefixes) {
43292
+ reserved.set(legacy.prefix.replace(/[/:]+$/, "").toLowerCase(), def.name);
43293
+ }
43294
+ }
43295
+ for (const [alias, owner] of Object.entries(PROVIDER_FILTER_ALIAS_EXTRA)) {
43296
+ reserved.set(alias.toLowerCase(), owner);
43297
+ }
43298
+ return reserved;
43299
+ }
43300
+ function readOptOut(config2) {
43301
+ const off = { disabled: true, disable: new Set, enable: new Set };
43302
+ if (process.env[KILL_SWITCH_ENV] === "1")
43303
+ return off;
43304
+ const raw = config2?.predefinedEndpoints;
43305
+ let parsed = {};
43306
+ if (raw !== undefined) {
43307
+ const result = PredefinedEndpointsConfigSchema.safeParse(raw);
43308
+ if (result.success) {
43309
+ parsed = result.data;
43310
+ } else {
43311
+ warnOnce2("[claudish] config 'predefinedEndpoints' is not valid and was ignored: " + result.error.issues.map((i) => `${i.path.join(".") || "(root)"} ${i.message}`).join(", "));
43312
+ }
43313
+ }
43314
+ return {
43315
+ disabled: parsed.enabled === false,
43316
+ disable: new Set((parsed.disable ?? []).map((n) => n.trim().toLowerCase())),
43317
+ enable: new Set((parsed.enable ?? []).map((n) => n.trim().toLowerCase()))
43318
+ };
43319
+ }
43320
+ function compileToCustomEndpoint(entry) {
43321
+ return {
43322
+ kind: "complex",
43323
+ displayName: entry.displayName,
43324
+ transport: entry.format,
43325
+ baseUrl: entry.baseUrl,
43326
+ apiPath: entry.apiPath,
43327
+ apiKey: `\${${entry.apiKeyEnvVar}}`,
43328
+ authScheme: entry.authScheme,
43329
+ headers: entry.headers,
43330
+ modelPrefix: entry.modelPrefix
43331
+ };
43332
+ }
43333
+ function credentialEnvVars(entry) {
43334
+ return {
43335
+ envVar: entry.apiKeyEnvVar,
43336
+ aliases: [...entry.apiKeyAliases ?? [], customEndpointKeyEnvVar(entry.name)]
43337
+ };
43338
+ }
43339
+ function overridesFor(entry) {
43340
+ const { envVar, aliases } = credentialEnvVars(entry);
43341
+ return {
43342
+ apiKeyEnvVar: envVar,
43343
+ apiKeyAliases: aliases,
43344
+ baseUrlEnvVars: entry.baseUrlEnvVars,
43345
+ apiKeyUrl: entry.apiKeyUrl ?? "",
43346
+ apiKeyDescription: `${entry.displayName} (${envVar})`,
43347
+ description: entry.description ?? `${entry.displayName} (bundled endpoint)`
43348
+ };
43349
+ }
43350
+ function loadPredefinedEndpoints(config2, opts = {}) {
43351
+ const result = { registered: [], skipped: [] };
43352
+ const optOut = readOptOut(config2);
43353
+ const catalog = opts.catalog ?? activeCatalog();
43354
+ const runtime = getRuntimeProviders();
43355
+ const noteStale = (entry, name, reason) => {
43356
+ if (!ownRegistrations.has(name) || !runtime.has(entry.name))
43357
+ return;
43358
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' is no longer eligible (${reason}) but stays ` + "registered for the rest of this process \u2014 runtime provider registration cannot be " + "undone. Restart claudish to apply the change.");
43359
+ };
43360
+ if (optOut.disabled) {
43361
+ for (const entry of catalog) {
43362
+ result.skipped.push({ name: entry.name, reason: "catalog off" });
43363
+ noteStale(entry, entry.name.toLowerCase(), "catalog off");
43364
+ }
43365
+ return result;
43366
+ }
43367
+ const reserved = reservedNamespace();
43368
+ const userEndpoints = new Set(Object.keys(config2?.customEndpoints ?? {}).map((n) => n.toLowerCase()));
43369
+ const seen = new Set;
43370
+ for (const entry of catalog) {
43371
+ const name = entry.name.toLowerCase();
43372
+ const skip = (reason) => result.skipped.push({ name: entry.name, reason });
43373
+ const skipStale = (reason) => {
43374
+ skip(reason);
43375
+ noteStale(entry, name, reason);
43376
+ };
43377
+ if (seen.has(name)) {
43378
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' appears more than once in the bundled ` + "catalog. The first row wins; the later one is ignored.");
43379
+ skip("duplicate row");
43380
+ continue;
43381
+ }
43382
+ seen.add(name);
43383
+ const owner = reserved.get(name);
43384
+ if (owner) {
43385
+ const reason = `'${entry.name}' is already claimed by builtin provider '${owner}' ` + "(as its name, a shortcut, or a legacy prefix)";
43386
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' skipped: ${reason}. ` + "The builtin wins; the bundled entry is inactive.");
43387
+ recordEndpointUnavailable(entry.name, `bundled endpoint was skipped because ${reason}`);
43388
+ skip("collides with builtin");
43389
+ continue;
43390
+ }
43391
+ if (runtime.has(entry.name) && !ownRegistrations.has(name)) {
43392
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' skipped: a provider named ` + `'${entry.name}' is already registered for this process.`);
43393
+ skip("already registered");
43394
+ continue;
43395
+ }
43396
+ if (userEndpoints.has(name)) {
43397
+ if (hasLocalApiKey({ envVar: entry.apiKeyEnvVar })) {
43398
+ warnOnce2(`[claudish] customEndpoints['${entry.name}'] replaces the bundled entry entirely; ` + `${entry.apiKeyEnvVar} no longer applies to it. Add ` + `"apiKey": "\${${entry.apiKeyEnvVar}}" to that entry to keep using it.`);
43399
+ }
43400
+ skipStale("replaced by customEndpoints");
43401
+ continue;
43402
+ }
43403
+ if (optOut.disable.has(name)) {
43404
+ skipStale("disabled in config");
43405
+ continue;
43406
+ }
43407
+ const { envVar, aliases } = credentialEnvVars(entry);
43408
+ const permitted = optOut.enable.has(name) || hasLocalApiKey({ envVar, aliases });
43409
+ if (!permitted) {
43410
+ skipStale("no local credential");
43411
+ continue;
43412
+ }
43413
+ const resolvedUrl = classifyEndpointBaseUrl(entry.baseUrl, entry.baseUrlEnvVars);
43414
+ if (!resolvedUrl.ok) {
43415
+ const detail = describeBadBaseUrlOverride(resolvedUrl, entry.baseUrl);
43416
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' skipped: ${detail}`);
43417
+ recordEndpointUnavailable(entry.name, detail);
43418
+ skipStale("invalid base URL override");
43419
+ continue;
43420
+ }
43421
+ try {
43422
+ registerEndpoint(entry.name, compileToCustomEndpoint(entry), overridesFor(entry));
43423
+ ownRegistrations.add(name);
43424
+ result.registered.push(entry.name);
43425
+ } catch (err) {
43426
+ warnOnce2(`[claudish] predefined endpoint '${entry.name}' skipped: ` + `${err instanceof Error ? err.message : String(err)}`);
43427
+ skip("invalid catalog row");
43428
+ }
43429
+ }
43430
+ return result;
43431
+ }
43432
+ var KILL_SWITCH_ENV = "CLAUDISH_NO_PREDEFINED_ENDPOINTS", warnedMessages2, ownRegistrations, catalogOverride = null;
43433
+ var init_predefined_endpoints = __esm(() => {
43434
+ init_local_api_key();
43435
+ init_config_schema();
43436
+ init_custom_endpoints_loader();
43437
+ init_endpoint_diagnostics();
43438
+ init_picker_alias_extra();
43439
+ init_predefined_catalog();
43440
+ init_provider_definitions();
43441
+ init_runtime_providers();
43442
+ warnedMessages2 = new Set;
43443
+ ownRegistrations = new Set;
43444
+ });
43445
+
43446
+ // src/providers/endpoint-registration.ts
43447
+ var exports_endpoint_registration = {};
43448
+ __export(exports_endpoint_registration, {
43449
+ invalidateEndpointRegistration: () => invalidateEndpointRegistration,
43450
+ ensureEndpointsRegistered: () => ensureEndpointsRegistered
43451
+ });
43452
+ function ensureEndpointsRegistered(opts = {}) {
43453
+ if (registered && !opts.force)
43454
+ return;
43455
+ registered = true;
43456
+ try {
43457
+ loadPredefinedEndpoints(opts.config ?? loadConfig());
43458
+ } catch {}
43459
+ }
43460
+ function invalidateEndpointRegistration() {
43461
+ registered = false;
43462
+ }
43463
+ var registered = false;
43464
+ var init_endpoint_registration = __esm(() => {
43465
+ init_profile_config();
43466
+ init_predefined_endpoints();
43467
+ });
43468
+
42850
43469
  // src/providers/provider-registry.ts
42851
43470
  function resolveBaseUrl2(envVar, fallbackEnvVars, staticDefault) {
42852
43471
  for (const v of [envVar, ...fallbackEnvVars]) {
@@ -43715,7 +44334,9 @@ async function prepareParentRoutingContext() {
43715
44334
  if (!parentRoutingContextReady) {
43716
44335
  parentRoutingContextReady = true;
43717
44336
  try {
43718
- loadCustomEndpoints(loadConfig());
44337
+ const config2 = loadConfig();
44338
+ ensureEndpointsRegistered({ config: config2 });
44339
+ loadCustomEndpoints(config2);
43719
44340
  } catch {}
43720
44341
  }
43721
44342
  try {
@@ -43765,6 +44386,7 @@ var init_prehydrate = __esm(() => {
43765
44386
  init_auto_route();
43766
44387
  init_catalog_client();
43767
44388
  init_custom_endpoints_loader();
44389
+ init_endpoint_registration();
43768
44390
  init_model_parser();
43769
44391
  init_onepassword();
43770
44392
  init_provider_resolver();
@@ -47534,38 +48156,6 @@ var init_native_handler = __esm(() => {
47534
48156
  init_anthropic_error();
47535
48157
  });
47536
48158
 
47537
- // src/providers/api-key-map.ts
47538
- var API_KEY_MAP;
47539
- var init_api_key_map = __esm(() => {
47540
- API_KEY_MAP = {
47541
- litellm: { envVar: "LITELLM_API_KEY" },
47542
- openrouter: { envVar: "OPENROUTER_API_KEY" },
47543
- google: { envVar: "GEMINI_API_KEY" },
47544
- openai: { envVar: "OPENAI_API_KEY" },
47545
- minimax: { envVar: "MINIMAX_API_KEY" },
47546
- "minimax-coding": { envVar: "MINIMAX_CODING_API_KEY" },
47547
- kimi: { envVar: "MOONSHOT_API_KEY", aliases: ["KIMI_API_KEY"] },
47548
- "kimi-coding": { envVar: "KIMI_CODING_API_KEY" },
47549
- glm: { envVar: "ZHIPU_API_KEY", aliases: ["GLM_API_KEY"] },
47550
- "glm-coding": { envVar: "GLM_CODING_API_KEY", aliases: ["ZAI_CODING_API_KEY"] },
47551
- "z-ai": { envVar: "ZAI_API_KEY" },
47552
- deepseek: { envVar: "DEEPSEEK_API_KEY" },
47553
- mistralai: { envVar: "MISTRAL_API_KEY" },
47554
- sakana: { envVar: "SAKANA_API_KEY" },
47555
- "sakana-subscription": {
47556
- envVar: "SAKANA_SUBSCRIPTION_API_KEY",
47557
- aliases: ["SAKANA_CODING_API_KEY"]
47558
- },
47559
- "qwen-cloud": { envVar: "QWEN_CLOUD_PLAN_API_KEY" },
47560
- "qwen-payg": { envVar: "DASHSCOPE_API_KEY", aliases: ["QWEN_API_KEY"] },
47561
- ollamacloud: { envVar: "OLLAMA_API_KEY" },
47562
- "opencode-zen": { envVar: "OPENCODE_API_KEY" },
47563
- "opencode-zen-go": { envVar: "OPENCODE_GO_API_KEY", aliases: ["OPENCODE_API_KEY"] },
47564
- vertex: { envVar: "VERTEX_API_KEY", aliases: ["VERTEX_PROJECT"] },
47565
- poe: { envVar: "POE_API_KEY" }
47566
- };
47567
- });
47568
-
47569
48159
  // src/providers/devin/tool-descriptions.ts
47570
48160
  function currentMonthAndYear(now = new Date) {
47571
48161
  return `${MONTHS[now.getMonth()]} ${now.getFullYear()}`;
@@ -49411,7 +50001,9 @@ __export(exports_proxy_server, {
49411
50001
  });
49412
50002
  async function createProxyServer(port, _openrouterApiKey, model, monitorMode = false, anthropicApiKey, modelMap, options = {}) {
49413
50003
  try {
49414
- const customEpResult = loadCustomEndpoints(loadConfig());
50004
+ const config2 = loadConfig();
50005
+ ensureEndpointsRegistered({ config: config2 });
50006
+ const customEpResult = loadCustomEndpoints(config2);
49415
50007
  if (customEpResult.registered > 0) {
49416
50008
  log(`[Proxy] Registered ${customEpResult.registered} custom endpoint(s) from config`);
49417
50009
  }
@@ -49704,9 +50296,11 @@ ${plan.hint}` : `[Route] ${plan.reason}`;
49704
50296
  if (hasExplicitProvider) {
49705
50297
  const parsedExplicit = parseModelSpec(target);
49706
50298
  if (parsedExplicit.provider !== "openrouter") {
49707
- const keyInfo = API_KEY_MAP[parsedExplicit.provider];
49708
- const keyNames = keyInfo ? [keyInfo.envVar, ...keyInfo.aliases ?? []].join(" or ") : undefined;
49709
- const hint = keyNames ? `No API key for provider "${parsedExplicit.provider}". Set ${keyNames} (env, config, or 1Password import).` : `No API key for provider "${parsedExplicit.provider}".`;
50299
+ const recorded = getEndpointUnavailableReason(parsedExplicit.provider);
50300
+ if (recorded) {
50301
+ throw new RoutingError(`Explicit model "${target}" could not be routed \u2014 ${recorded}`);
50302
+ }
50303
+ const hint = describeMissingCredential(parsedExplicit.provider);
49710
50304
  throw new RoutingError(`Explicit model "${target}" could not be routed \u2014 its provider has no credential. ${hint}`);
49711
50305
  }
49712
50306
  }
@@ -49858,11 +50452,13 @@ var init_proxy_server = __esm(() => {
49858
50452
  init_logger();
49859
50453
  init_model_loader();
49860
50454
  init_profile_config();
49861
- init_api_key_map();
49862
50455
  init_auto_route();
49863
50456
  init_catalog_client();
49864
50457
  init_custom_endpoints_loader();
50458
+ init_endpoint_diagnostics();
50459
+ init_endpoint_registration();
49865
50460
  init_model_parser();
50461
+ init_provider_definitions();
49866
50462
  init_provider_profiles();
49867
50463
  init_provider_registry();
49868
50464
  init_provider_resolver();
@@ -52034,6 +52630,7 @@ async function serveCommand(args) {
52034
52630
  console.error("[claudish serve] --models <path> is required");
52035
52631
  process.exit(1);
52036
52632
  }
52633
+ ensureEndpointsRegistered();
52037
52634
  let slotMap;
52038
52635
  let slotIds;
52039
52636
  try {
@@ -52054,6 +52651,7 @@ async function serveCommand(args) {
52054
52651
  await new Promise(() => {});
52055
52652
  }
52056
52653
  var init_serve_command = __esm(() => {
52654
+ init_endpoint_registration();
52057
52655
  init_proxy_server();
52058
52656
  });
52059
52657
 
@@ -65350,7 +65948,8 @@ var init_antigravity_oauth = __esm(() => {
65350
65948
  var exports_auth_commands = {};
65351
65949
  __export(exports_auth_commands, {
65352
65950
  logoutCommand: () => logoutCommand,
65353
- loginCommand: () => loginCommand
65951
+ loginCommand: () => loginCommand,
65952
+ findProvider: () => findProvider
65354
65953
  });
65355
65954
  function getAuthStatus(provider) {
65356
65955
  const hasCredentials = provider.registryKeys.some((k) => hasOAuthCredentials(k)) || provider.name === "antigravity" && hasSharedAntigravityToken();
@@ -65865,7 +66464,7 @@ __export(exports_model_selector, {
65865
66464
  buildDiscoveredModelRows: () => buildDiscoveredModelRows
65866
66465
  });
65867
66466
  function isUserDeployedProvider(value) {
65868
- return LOCAL_OR_USER_DEPLOYED.has(value);
66467
+ return LOCAL_OR_USER_DEPLOYED.has(value) || getRuntimeProviders().has(value);
65869
66468
  }
65870
66469
  function firebaseSlugToProviderName(slug) {
65871
66470
  const lower = slug.toLowerCase();
@@ -66625,7 +67224,7 @@ async function selectProfile(profiles) {
66625
67224
  async function confirmAction(message) {
66626
67225
  return dist_default4({ message, default: false });
66627
67226
  }
66628
- var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PROVIDER_FILTER_ALIAS_EXTRA, PICKER_COPY, PICKER_ORDER, PROVIDER_MODEL_PREFIX_OVERRIDE;
67227
+ var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PICKER_COPY, PICKER_ORDER, PROVIDER_MODEL_PREFIX_OVERRIDE;
66629
67228
  var init_model_selector = __esm(() => {
66630
67229
  init_dist16();
66631
67230
  init_model_catalog();
@@ -66635,7 +67234,9 @@ var init_model_selector = __esm(() => {
66635
67234
  init_model_catalog2();
66636
67235
  init_model_discovery();
66637
67236
  init_registry2();
67237
+ init_picker_alias_extra();
66638
67238
  init_provider_definitions();
67239
+ init_runtime_providers();
66639
67240
  init_probe_discovery();
66640
67241
  pickerProviderToFirebaseSlug = {
66641
67242
  openrouter: "openrouter",
@@ -66663,10 +67264,6 @@ var init_model_selector = __esm(() => {
66663
67264
  output: "SUB",
66664
67265
  average: "SUB"
66665
67266
  };
66666
- PROVIDER_FILTER_ALIAS_EXTRA = {
66667
- gem: "google",
66668
- zen: "opencode-zen"
66669
- };
66670
67267
  PICKER_COPY = {
66671
67268
  openrouter: { description: "580+ models via unified API" },
66672
67269
  "opencode-zen": { name: "OpenCode Zen", description: "Free models, no API key needed" },
@@ -69369,6 +69966,38 @@ var init_probe_tui_runtime = __esm(() => {
69369
69966
  init_probe_tui_app();
69370
69967
  });
69371
69968
 
69969
+ // src/providers/api-key-map.ts
69970
+ var API_KEY_MAP;
69971
+ var init_api_key_map = __esm(() => {
69972
+ API_KEY_MAP = {
69973
+ litellm: { envVar: "LITELLM_API_KEY" },
69974
+ openrouter: { envVar: "OPENROUTER_API_KEY" },
69975
+ google: { envVar: "GEMINI_API_KEY" },
69976
+ openai: { envVar: "OPENAI_API_KEY" },
69977
+ minimax: { envVar: "MINIMAX_API_KEY" },
69978
+ "minimax-coding": { envVar: "MINIMAX_CODING_API_KEY" },
69979
+ kimi: { envVar: "MOONSHOT_API_KEY", aliases: ["KIMI_API_KEY"] },
69980
+ "kimi-coding": { envVar: "KIMI_CODING_API_KEY" },
69981
+ glm: { envVar: "ZHIPU_API_KEY", aliases: ["GLM_API_KEY"] },
69982
+ "glm-coding": { envVar: "GLM_CODING_API_KEY", aliases: ["ZAI_CODING_API_KEY"] },
69983
+ "z-ai": { envVar: "ZAI_API_KEY" },
69984
+ deepseek: { envVar: "DEEPSEEK_API_KEY" },
69985
+ mistralai: { envVar: "MISTRAL_API_KEY" },
69986
+ sakana: { envVar: "SAKANA_API_KEY" },
69987
+ "sakana-subscription": {
69988
+ envVar: "SAKANA_SUBSCRIPTION_API_KEY",
69989
+ aliases: ["SAKANA_CODING_API_KEY"]
69990
+ },
69991
+ "qwen-cloud": { envVar: "QWEN_CLOUD_PLAN_API_KEY" },
69992
+ "qwen-payg": { envVar: "DASHSCOPE_API_KEY", aliases: ["QWEN_API_KEY"] },
69993
+ ollamacloud: { envVar: "OLLAMA_API_KEY" },
69994
+ "opencode-zen": { envVar: "OPENCODE_API_KEY" },
69995
+ "opencode-zen-go": { envVar: "OPENCODE_GO_API_KEY", aliases: ["OPENCODE_API_KEY"] },
69996
+ vertex: { envVar: "VERTEX_API_KEY", aliases: ["VERTEX_PROJECT"] },
69997
+ poe: { envVar: "POE_API_KEY" }
69998
+ };
69999
+ });
70000
+
69372
70001
  // src/providers/probe-runner.ts
69373
70002
  function pinProbeModelSpec(link) {
69374
70003
  if (link.provider === "native-anthropic")
@@ -69688,6 +70317,7 @@ async function parseArgs(args) {
69688
70317
  probeTimeoutMs = parsed * 1000;
69689
70318
  }
69690
70319
  }
70320
+ ensureEndpointsRegistered();
69691
70321
  await probeModelRouting(expandedModels, hasJsonFlag, {
69692
70322
  live: !noProbeFlag,
69693
70323
  timeoutMs: probeTimeoutMs
@@ -71029,6 +71659,7 @@ var init_cli = __esm(() => {
71029
71659
  init_profile_config();
71030
71660
  init_api_key_map();
71031
71661
  init_api_key_provenance();
71662
+ init_endpoint_registration();
71032
71663
  init_model_parser();
71033
71664
  init_probe_live();
71034
71665
  init_probe_runner();
@@ -76734,6 +77365,7 @@ function App({ requestLogin } = {}) {
76734
77365
  const selectedProviderIsLocal = !!(selectedProvider.isLocal || selectedProviderDef?.isLocal);
76735
77366
  const selectedLocalEnabled = selectedProviderIsLocal && isLocalProviderEnabled(selectedProvider.catalogName, config3);
76736
77367
  const refreshConfig = useCallback3(() => {
77368
+ ensureEndpointsRegistered({ force: true });
76737
77369
  setConfig(loadConfig());
76738
77370
  setBufStats(getBufferStats());
76739
77371
  setOpTick((t) => t + 1);
@@ -78287,6 +78919,7 @@ var init_App = __esm(() => {
78287
78919
  init_op_source();
78288
78920
  init_profile_config();
78289
78921
  init_default_routing_rules();
78922
+ init_endpoint_registration();
78290
78923
  init_local_liveness();
78291
78924
  init_onepassword_config();
78292
78925
  init_onepassword();
@@ -78326,6 +78959,7 @@ import { createCliRenderer as createCliRenderer2 } from "@opentui/core";
78326
78959
  import { createRoot as createRoot2 } from "@opentui/react";
78327
78960
  import { jsxDEV as jsxDEV17 } from "@opentui/react/jsx-dev-runtime";
78328
78961
  async function startConfigTui() {
78962
+ ensureEndpointsRegistered();
78329
78963
  setStderrQuiet(true);
78330
78964
  const loginRequest = {
78331
78965
  slug: null
@@ -78385,6 +79019,7 @@ var init_tui = __esm(() => {
78385
79019
  init_codex_oauth();
78386
79020
  init_kimi_oauth();
78387
79021
  init_logger();
79022
+ init_endpoint_registration();
78388
79023
  init_App();
78389
79024
  if (isDirectRun) {
78390
79025
  startConfigTui().catch((err) => {
@@ -82515,6 +83150,10 @@ async function runCli() {
82515
83150
  }
82516
83151
  try {
82517
83152
  const cliConfig = await traceSpan("startup:parse-args", () => parseArgs2(process.argv.slice(2)));
83153
+ await traceSpan("startup:endpoint-registration", async () => {
83154
+ const { ensureEndpointsRegistered: ensureEndpointsRegistered2 } = await Promise.resolve().then(() => (init_endpoint_registration(), exports_endpoint_registration));
83155
+ ensureEndpointsRegistered2();
83156
+ });
82518
83157
  if (cliConfig.team && cliConfig.team.length > 0) {
82519
83158
  let prompt = cliConfig.claudeArgs.join(" ");
82520
83159
  if (cliConfig.inputFile) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "7.50.0",
3
+ "version": "7.51.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.50.0",
64
- "@claudish/magmux-darwin-x64": "7.50.0",
65
- "@claudish/magmux-linux-arm64": "7.50.0",
66
- "@claudish/magmux-linux-x64": "7.50.0"
63
+ "@claudish/magmux-darwin-arm64": "7.51.0",
64
+ "@claudish/magmux-darwin-x64": "7.51.0",
65
+ "@claudish/magmux-linux-arm64": "7.51.0",
66
+ "@claudish/magmux-linux-x64": "7.51.0"
67
67
  },
68
68
  "author": "Jack Rudenko <i@madappgang.com>",
69
69
  "license": "MIT",