claudish 7.49.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 +914 -104
  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.49.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" });
27967
+ }
27968
+ for (const envVar of baseUrlEnvVars) {
27969
+ const value = process.env[envVar];
27970
+ if (value)
27971
+ found.push({ envVar, value, source: "env" });
27952
27972
  }
27953
- return def.baseUrl;
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();
@@ -50087,12 +50683,132 @@ function writeStatusFile(sessionPath, manifest, status, opts) {
50087
50683
  var CHANNEL_LINE_BUDGET = 58;
50088
50684
  var init_team_stats = () => {};
50089
50685
 
50686
+ // src/team-stream-capture.ts
50687
+ function extractAssistantText(event) {
50688
+ if (event.type !== "assistant")
50689
+ return [];
50690
+ const message = event.message;
50691
+ const content = message?.content;
50692
+ if (typeof content === "string") {
50693
+ return content.length > 0 ? [content] : [];
50694
+ }
50695
+ if (!Array.isArray(content))
50696
+ return [];
50697
+ const out = [];
50698
+ for (const raw2 of content) {
50699
+ const block = raw2;
50700
+ if (block?.type !== "text")
50701
+ continue;
50702
+ if (typeof block.text !== "string" || block.text.length === 0)
50703
+ continue;
50704
+ out.push(block.text);
50705
+ }
50706
+ return out;
50707
+ }
50708
+ function createAssistantTextCapture() {
50709
+ let pending = "";
50710
+ let emittedAny = false;
50711
+ let endsWithNewline = false;
50712
+ let dedupeTail = "";
50713
+ let lastWasMessage = false;
50714
+ const record4 = (text, kind) => {
50715
+ emittedAny = true;
50716
+ lastWasMessage = kind === "message";
50717
+ endsWithNewline = text.endsWith(`
50718
+ `);
50719
+ dedupeTail = (dedupeTail + text).slice(-DEDUPE_TAIL_LIMIT);
50720
+ };
50721
+ const messageSeparator = () => {
50722
+ if (!emittedAny)
50723
+ return "";
50724
+ return endsWithNewline ? `
50725
+ ` : `
50726
+
50727
+ `;
50728
+ };
50729
+ const rawSeparator = () => {
50730
+ if (!emittedAny || endsWithNewline)
50731
+ return "";
50732
+ return `
50733
+ `;
50734
+ };
50735
+ const consumeLine = (line, terminated) => {
50736
+ if (line.trim().length === 0)
50737
+ return "";
50738
+ const passthrough = () => {
50739
+ const out = `${rawSeparator()}${line}${terminated ? `
50740
+ ` : ""}`;
50741
+ record4(out, "raw");
50742
+ return out;
50743
+ };
50744
+ let event;
50745
+ try {
50746
+ event = JSON.parse(line);
50747
+ } catch {
50748
+ return passthrough();
50749
+ }
50750
+ if (typeof event.type !== "string" || !STREAM_JSON_EVENT_TYPES.has(event.type)) {
50751
+ return passthrough();
50752
+ }
50753
+ const texts = extractAssistantText(event);
50754
+ if (texts.length > 0) {
50755
+ let out = "";
50756
+ for (const text of texts) {
50757
+ const piece = `${messageSeparator()}${text}`;
50758
+ out += piece;
50759
+ record4(piece, "message");
50760
+ }
50761
+ return out;
50762
+ }
50763
+ if (event.type === "result" && event.is_error === true) {
50764
+ const result = typeof event.result === "string" ? event.result : "";
50765
+ if (result.trim().length > 0 && !dedupeTail.includes(result)) {
50766
+ const out = `${messageSeparator()}${result}`;
50767
+ record4(out, "message");
50768
+ return out;
50769
+ }
50770
+ }
50771
+ return "";
50772
+ };
50773
+ return {
50774
+ write(chunk) {
50775
+ pending += chunk;
50776
+ let out = "";
50777
+ let newlineAt = pending.indexOf(`
50778
+ `);
50779
+ while (newlineAt !== -1) {
50780
+ const line = pending.slice(0, newlineAt);
50781
+ pending = pending.slice(newlineAt + 1);
50782
+ out += consumeLine(line, true);
50783
+ newlineAt = pending.indexOf(`
50784
+ `);
50785
+ }
50786
+ return out;
50787
+ },
50788
+ end() {
50789
+ let out = pending.length > 0 ? consumeLine(pending, false) : "";
50790
+ pending = "";
50791
+ if (emittedAny && !endsWithNewline && lastWasMessage) {
50792
+ out += `
50793
+ `;
50794
+ endsWithNewline = true;
50795
+ }
50796
+ return out;
50797
+ }
50798
+ };
50799
+ }
50800
+ var DEDUPE_TAIL_LIMIT = 4096, STREAM_JSON_EVENT_TYPES;
50801
+ var init_team_stream_capture = __esm(() => {
50802
+ STREAM_JSON_EVENT_TYPES = new Set(["system", "assistant", "user", "result"]);
50803
+ });
50804
+
50090
50805
  // src/team-orchestrator.ts
50091
50806
  var exports_team_orchestrator = {};
50092
50807
  __export(exports_team_orchestrator, {
50093
50808
  validateSessionPath: () => validateSessionPath,
50094
50809
  setupSession: () => setupSession,
50095
50810
  runModels: () => runModels,
50811
+ resolveCaptureMode: () => resolveCaptureMode,
50096
50812
  parseJudgeVotes: () => parseJudgeVotes,
50097
50813
  judgeResponses: () => judgeResponses,
50098
50814
  getStatus: () => getStatus,
@@ -50100,6 +50816,7 @@ __export(exports_team_orchestrator, {
50100
50816
  classifyRunOutput: () => classifyRunOutput,
50101
50817
  buildJudgePrompt: () => buildJudgePrompt,
50102
50818
  aggregateVerdict: () => aggregateVerdict,
50819
+ TEAM_CAPTURE_ENV_VAR: () => TEAM_CAPTURE_ENV_VAR,
50103
50820
  STDOUT_TAIL_LIMIT: () => STDOUT_TAIL_LIMIT,
50104
50821
  DEFAULT_MIN_OUTPUT_BYTES: () => DEFAULT_MIN_OUTPUT_BYTES
50105
50822
  });
@@ -50113,8 +50830,21 @@ import {
50113
50830
  writeFileSync as writeFileSync12
50114
50831
  } from "fs";
50115
50832
  import { join as join28, resolve as resolve3 } from "path";
50833
+ function resolveCaptureMode(explicit, env = process.env) {
50834
+ if (explicit)
50835
+ return explicit;
50836
+ return env[TEAM_CAPTURE_ENV_VAR]?.trim().toLowerCase() === "print" ? "print" : "stream-json";
50837
+ }
50116
50838
  function classifyRunOutput(opts) {
50117
- const { outputSize, stdoutTail, stderr, minOutputBytes, requirePattern, fullOutput } = opts;
50839
+ const {
50840
+ outputSize,
50841
+ stdoutTail,
50842
+ stderr,
50843
+ minOutputBytes,
50844
+ requirePattern,
50845
+ fullOutput,
50846
+ captureMode = "print"
50847
+ } = opts;
50118
50848
  const apiError = API_ERROR_RE.exec(stdoutTail);
50119
50849
  if (apiError) {
50120
50850
  return {
@@ -50151,9 +50881,10 @@ function classifyRunOutput(opts) {
50151
50881
  re = null;
50152
50882
  }
50153
50883
  if (re && !re.test(haystack)) {
50884
+ const cause = captureMode === "stream-json" ? "Every assistant message this child produced was captured and concatenated, " + "so this is not the print-mode dropout: the model genuinely never emitted the " + "required shape. Re-prompt it, or relax the contract." : "This is the signature of a child that answered and then took one more turn: " + "`claude -p` prints only the FINAL assistant message, so a background task " + "completing (or any late notification) replaces the real answer with an " + "epilogue about it. The answer was generated, it just was not the last thing " + "said \u2014 re-run with the default stream-json capture to keep it.";
50154
50885
  return {
50155
50886
  reason: "shape_mismatch",
50156
- detail: `Child exited 0 with ${outputSize} B, but the response does not match the ` + `required pattern /${requirePattern}/. This is the signature of a child that ` + "answered and then took one more turn: `claude -p` prints only the FINAL " + "assistant message, so a background task completing (or any late notification) " + "replaces the real answer with an epilogue about it. Check the child's " + "transcript \u2014 the answer was generated, it just was not the last thing said."
50887
+ detail: `Child exited 0 with ${outputSize} B, but the response does not match the ` + `required pattern /${requirePattern}/. ${cause}`
50157
50888
  };
50158
50889
  }
50159
50890
  }
@@ -50269,6 +51000,7 @@ async function runModels(sessionPath, opts = {}) {
50269
51000
  }
50270
51001
  const minOutputBytes = opts.minOutputBytes ?? DEFAULT_MIN_OUTPUT_BYTES;
50271
51002
  const requirePattern = opts.requirePattern;
51003
+ const captureMode = resolveCaptureMode(opts.captureMode);
50272
51004
  mkdirSync12(statsDir(sessionPath), { recursive: true });
50273
51005
  const processes = new Map;
50274
51006
  const runtimes = new Map;
@@ -50285,7 +51017,14 @@ async function runModels(sessionPath, opts = {}) {
50285
51017
  const outputPath = join28(sessionPath, `response-${anonId}.md`);
50286
51018
  const errorLogPath = join28(sessionPath, "errors", `${anonId}.log`);
50287
51019
  const spawnModel = spawnPlan.pinned.get(entry.model) ?? entry.model;
50288
- const args = ["--model", spawnModel, "-y", "--stdin", "--quiet", ...opts.claudeFlags ?? []];
51020
+ const args = [
51021
+ "--model",
51022
+ spawnModel,
51023
+ "-y",
51024
+ "--stdin",
51025
+ ...captureMode === "stream-json" ? ["--verbose", "--quiet", "--output-format", "stream-json"] : ["--quiet"],
51026
+ ...opts.claudeFlags ?? []
51027
+ ];
50289
51028
  updateModelStatus(anonId, {
50290
51029
  state: "RUNNING",
50291
51030
  startedAt: new Date().toISOString()
@@ -50301,12 +51040,36 @@ async function runModels(sessionPath, opts = {}) {
50301
51040
  });
50302
51041
  let byteCount = 0;
50303
51042
  let stdoutTail = "";
50304
- proc.stdout?.on("data", (chunk) => {
50305
- byteCount += chunk.length;
50306
- stdoutTail = (stdoutTail + chunk.toString()).slice(-STDOUT_TAIL_LIMIT);
50307
- });
50308
51043
  const outputStream = createWriteStream2(outputPath);
50309
- proc.stdout?.pipe(outputStream);
51044
+ let flushPartial = () => {};
51045
+ if (captureMode === "print") {
51046
+ proc.stdout?.on("data", (chunk) => {
51047
+ byteCount += chunk.length;
51048
+ stdoutTail = (stdoutTail + chunk.toString()).slice(-STDOUT_TAIL_LIMIT);
51049
+ });
51050
+ proc.stdout?.pipe(outputStream);
51051
+ } else {
51052
+ const capture = createAssistantTextCapture();
51053
+ const absorb = (text) => {
51054
+ if (text.length === 0)
51055
+ return;
51056
+ byteCount += Buffer.byteLength(text);
51057
+ stdoutTail = (stdoutTail + text).slice(-STDOUT_TAIL_LIMIT);
51058
+ outputStream.write(text);
51059
+ };
51060
+ proc.stdout?.on("data", (chunk) => absorb(capture.write(chunk.toString())));
51061
+ flushPartial = () => absorb(capture.end());
51062
+ let captureFinalized = false;
51063
+ const finalizeCapture = () => {
51064
+ if (captureFinalized)
51065
+ return;
51066
+ captureFinalized = true;
51067
+ absorb(capture.end());
51068
+ outputStream.end();
51069
+ };
51070
+ proc.stdout?.on("end", finalizeCapture);
51071
+ proc.stdout?.on("close", finalizeCapture);
51072
+ }
50310
51073
  let stderr = "";
50311
51074
  proc.stderr?.on("data", (chunk) => {
50312
51075
  stderr += chunk.toString();
@@ -50317,7 +51080,8 @@ async function runModels(sessionPath, opts = {}) {
50317
51080
  errorLogPath,
50318
51081
  getStderr: () => stderr,
50319
51082
  getStdoutTail: () => stdoutTail,
50320
- getByteCount: () => byteCount
51083
+ getByteCount: () => byteCount,
51084
+ flushPartial: () => flushPartial()
50321
51085
  });
50322
51086
  proc.stdin?.write(inputContent);
50323
51087
  proc.stdin?.end();
@@ -50347,7 +51111,8 @@ async function runModels(sessionPath, opts = {}) {
50347
51111
  stderr,
50348
51112
  minOutputBytes,
50349
51113
  requirePattern,
50350
- fullOutput
51114
+ fullOutput,
51115
+ captureMode
50351
51116
  });
50352
51117
  const failed = crashed || degraded !== null;
50353
51118
  const state = crashed ? "FAILED" : degraded ? "EMPTY" : "COMPLETED";
@@ -50444,10 +51209,11 @@ async function runModels(sessionPath, opts = {}) {
50444
51209
  if (!proc.killed)
50445
51210
  proc.kill("SIGTERM");
50446
51211
  const rt = runtimes.get(id);
51212
+ rt?.flushPartial();
50447
51213
  const stderr = rt?.getStderr() ?? "";
50448
51214
  const stdoutTail = rt?.getStdoutTail() ?? "";
50449
51215
  const bytes2 = rt?.getByteCount() ?? 0;
50450
- const detail = `Killed by the orchestrator after ${timeoutMs / 1000}s with ${bytes2} B of stdout. ` + "In --quiet print mode the child emits its answer only at the end, so 0 B means " + `"did not finish", not "produced nothing".`;
51216
+ const detail = `Killed by the orchestrator after ${timeoutMs / 1000}s with ${bytes2} B of stdout. ` + "That figure counts the ANSWER, not the wire format, so 0 B means the child had " + `not produced an assistant message yet \u2014 "did not finish", not "produced nothing".`;
50451
51217
  if (rt)
50452
51218
  persistErrorLog(rt.errorLogPath, `TIMEOUT: ${detail}`, stderr, stdoutTail);
50453
51219
  updateModelStatus(id, {
@@ -50659,11 +51425,12 @@ function formatVerdict(verdict, sessionPath) {
50659
51425
  }
50660
51426
  return output;
50661
51427
  }
50662
- var STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0, SENTINEL_MODELS;
51428
+ var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0, SENTINEL_MODELS;
50663
51429
  var init_team_orchestrator = __esm(() => {
50664
51430
  init_prehydrate();
50665
51431
  init_redact();
50666
51432
  init_team_stats();
51433
+ init_team_stream_capture();
50667
51434
  API_ERROR_RE = /\[API Error:\s*([^\]]{0,300})\]/i;
50668
51435
  BG_CEILING_RE = /Background tasks still running after (\d+)s; terminating/i;
50669
51436
  SENTINEL_MODELS = new Set([
@@ -51189,7 +51956,7 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
51189
51956
  timeout: { type: "number", description: "Per-model timeout in seconds (default: 300)" },
51190
51957
  require_pattern: {
51191
51958
  type: "string",
51192
- description: "Regex the response MUST match, or the slot is reported FAILED (state EMPTY, " + "reason 'shape_mismatch') instead of succeeded. Strongly recommended whenever " + "your prompt mandates an output shape \u2014 e.g. '```vote' for a voting panel. " + "Exit code 0 is not a success oracle: a child that answers and then takes one " + "more turn (a background Task finishing, a late notification) has its real " + "answer replaced by a short epilogue, because print mode emits only the FINAL " + "assistant message. That lands as a few hundred bytes of plausible prose with " + "exit 0 and no error, and is otherwise indistinguishable from success."
51959
+ description: "Regex the response MUST match, or the slot is reported FAILED (state EMPTY, " + "reason 'shape_mismatch') instead of succeeded. Strongly recommended whenever " + "your prompt mandates an output shape \u2014 e.g. '```vote' for a voting panel. " + "Exit code 0 is not a success oracle: it is 0 on API errors and on a child " + "that simply never followed the format. Answers are no longer LOST to print " + "mode (every assistant message is captured), so a mismatch now means the model " + "did not produce the shape, not that the shape was discarded."
51193
51960
  },
51194
51961
  min_output_bytes: {
51195
51962
  type: "number",
@@ -51771,7 +52538,7 @@ var init_mcp_server = __esm(() => {
51771
52538
  api_error: "retry once, or route via a different provider (or@<model>)",
51772
52539
  background_task_ceiling: "set CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0 for children, or forbid background work in the prompt",
51773
52540
  empty_output: "retry once; if it repeats, drop the model",
51774
- shape_mismatch: "the answer was generated but not captured \u2014 print mode keeps only the FINAL " + "assistant message, so a late background task or notification overwrote it. " + "Retry, and forbid background work in the prompt; do NOT count this slot as a vote"
52541
+ shape_mismatch: "the response does not carry the shape you required. Every assistant message the " + "child emitted was captured, so nothing was lost in transit \u2014 the model did not " + "produce it. Re-prompt with the required format restated; do NOT count this slot " + "as a vote"
51775
52542
  };
51776
52543
  sanitize = sanitizeForReport;
51777
52544
  EVENT_TO_TASK_STATUS = new Map([
@@ -51863,6 +52630,7 @@ async function serveCommand(args) {
51863
52630
  console.error("[claudish serve] --models <path> is required");
51864
52631
  process.exit(1);
51865
52632
  }
52633
+ ensureEndpointsRegistered();
51866
52634
  let slotMap;
51867
52635
  let slotIds;
51868
52636
  try {
@@ -51883,6 +52651,7 @@ async function serveCommand(args) {
51883
52651
  await new Promise(() => {});
51884
52652
  }
51885
52653
  var init_serve_command = __esm(() => {
52654
+ init_endpoint_registration();
51886
52655
  init_proxy_server();
51887
52656
  });
51888
52657
 
@@ -65179,7 +65948,8 @@ var init_antigravity_oauth = __esm(() => {
65179
65948
  var exports_auth_commands = {};
65180
65949
  __export(exports_auth_commands, {
65181
65950
  logoutCommand: () => logoutCommand,
65182
- loginCommand: () => loginCommand
65951
+ loginCommand: () => loginCommand,
65952
+ findProvider: () => findProvider
65183
65953
  });
65184
65954
  function getAuthStatus(provider) {
65185
65955
  const hasCredentials = provider.registryKeys.some((k) => hasOAuthCredentials(k)) || provider.name === "antigravity" && hasSharedAntigravityToken();
@@ -65694,7 +66464,7 @@ __export(exports_model_selector, {
65694
66464
  buildDiscoveredModelRows: () => buildDiscoveredModelRows
65695
66465
  });
65696
66466
  function isUserDeployedProvider(value) {
65697
- return LOCAL_OR_USER_DEPLOYED.has(value);
66467
+ return LOCAL_OR_USER_DEPLOYED.has(value) || getRuntimeProviders().has(value);
65698
66468
  }
65699
66469
  function firebaseSlugToProviderName(slug) {
65700
66470
  const lower = slug.toLowerCase();
@@ -66454,7 +67224,7 @@ async function selectProfile(profiles) {
66454
67224
  async function confirmAction(message) {
66455
67225
  return dist_default4({ message, default: false });
66456
67226
  }
66457
- 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;
66458
67228
  var init_model_selector = __esm(() => {
66459
67229
  init_dist16();
66460
67230
  init_model_catalog();
@@ -66464,7 +67234,9 @@ var init_model_selector = __esm(() => {
66464
67234
  init_model_catalog2();
66465
67235
  init_model_discovery();
66466
67236
  init_registry2();
67237
+ init_picker_alias_extra();
66467
67238
  init_provider_definitions();
67239
+ init_runtime_providers();
66468
67240
  init_probe_discovery();
66469
67241
  pickerProviderToFirebaseSlug = {
66470
67242
  openrouter: "openrouter",
@@ -66492,10 +67264,6 @@ var init_model_selector = __esm(() => {
66492
67264
  output: "SUB",
66493
67265
  average: "SUB"
66494
67266
  };
66495
- PROVIDER_FILTER_ALIAS_EXTRA = {
66496
- gem: "google",
66497
- zen: "opencode-zen"
66498
- };
66499
67267
  PICKER_COPY = {
66500
67268
  openrouter: { description: "580+ models via unified API" },
66501
67269
  "opencode-zen": { name: "OpenCode Zen", description: "Free models, no API key needed" },
@@ -69198,6 +69966,38 @@ var init_probe_tui_runtime = __esm(() => {
69198
69966
  init_probe_tui_app();
69199
69967
  });
69200
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
+
69201
70001
  // src/providers/probe-runner.ts
69202
70002
  function pinProbeModelSpec(link) {
69203
70003
  if (link.provider === "native-anthropic")
@@ -69517,6 +70317,7 @@ async function parseArgs(args) {
69517
70317
  probeTimeoutMs = parsed * 1000;
69518
70318
  }
69519
70319
  }
70320
+ ensureEndpointsRegistered();
69520
70321
  await probeModelRouting(expandedModels, hasJsonFlag, {
69521
70322
  live: !noProbeFlag,
69522
70323
  timeoutMs: probeTimeoutMs
@@ -70858,6 +71659,7 @@ var init_cli = __esm(() => {
70858
71659
  init_profile_config();
70859
71660
  init_api_key_map();
70860
71661
  init_api_key_provenance();
71662
+ init_endpoint_registration();
70861
71663
  init_model_parser();
70862
71664
  init_probe_live();
70863
71665
  init_probe_runner();
@@ -76563,6 +77365,7 @@ function App({ requestLogin } = {}) {
76563
77365
  const selectedProviderIsLocal = !!(selectedProvider.isLocal || selectedProviderDef?.isLocal);
76564
77366
  const selectedLocalEnabled = selectedProviderIsLocal && isLocalProviderEnabled(selectedProvider.catalogName, config3);
76565
77367
  const refreshConfig = useCallback3(() => {
77368
+ ensureEndpointsRegistered({ force: true });
76566
77369
  setConfig(loadConfig());
76567
77370
  setBufStats(getBufferStats());
76568
77371
  setOpTick((t) => t + 1);
@@ -78116,6 +78919,7 @@ var init_App = __esm(() => {
78116
78919
  init_op_source();
78117
78920
  init_profile_config();
78118
78921
  init_default_routing_rules();
78922
+ init_endpoint_registration();
78119
78923
  init_local_liveness();
78120
78924
  init_onepassword_config();
78121
78925
  init_onepassword();
@@ -78155,6 +78959,7 @@ import { createCliRenderer as createCliRenderer2 } from "@opentui/core";
78155
78959
  import { createRoot as createRoot2 } from "@opentui/react";
78156
78960
  import { jsxDEV as jsxDEV17 } from "@opentui/react/jsx-dev-runtime";
78157
78961
  async function startConfigTui() {
78962
+ ensureEndpointsRegistered();
78158
78963
  setStderrQuiet(true);
78159
78964
  const loginRequest = {
78160
78965
  slug: null
@@ -78214,6 +79019,7 @@ var init_tui = __esm(() => {
78214
79019
  init_codex_oauth();
78215
79020
  init_kimi_oauth();
78216
79021
  init_logger();
79022
+ init_endpoint_registration();
78217
79023
  init_App();
78218
79024
  if (isDirectRun) {
78219
79025
  startConfigTui().catch((err) => {
@@ -82344,6 +83150,10 @@ async function runCli() {
82344
83150
  }
82345
83151
  try {
82346
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
+ });
82347
83157
  if (cliConfig.team && cliConfig.team.length > 0) {
82348
83158
  let prompt = cliConfig.claudeArgs.join(" ");
82349
83159
  if (cliConfig.inputFile) {