@poolzin/pool-bot 2026.2.3 → 2026.2.5

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 (81) hide show
  1. package/dist/agents/agent-paths.js +3 -1
  2. package/dist/agents/anthropic-payload-log.js +2 -1
  3. package/dist/agents/bash-tools.exec.js +2 -1
  4. package/dist/agents/cache-trace.js +8 -4
  5. package/dist/agents/live-auth-keys.js +2 -1
  6. package/dist/agents/model-auth.js +1 -0
  7. package/dist/agents/model-forward-compat.js +187 -0
  8. package/dist/agents/pi-embedded-runner/model.js +10 -56
  9. package/dist/agents/pi-embedded-runner/session-manager-cache.js +2 -1
  10. package/dist/agents/pi-embedded-subscribe.raw-stream.js +2 -1
  11. package/dist/agents/skills/bundled-dir.js +2 -1
  12. package/dist/browser/constants.js +1 -1
  13. package/dist/browser/profiles.js +1 -1
  14. package/dist/build-info.json +3 -3
  15. package/dist/channels/plugins/catalog.js +6 -1
  16. package/dist/cli/daemon-cli/install.js +4 -1
  17. package/dist/cli/daemon-cli/status.gather.js +4 -2
  18. package/dist/cli/memory-cli.js +9 -3
  19. package/dist/cli/profile.js +6 -2
  20. package/dist/cli/program/register.onboard.js +10 -0
  21. package/dist/commands/auth-choice-options.js +11 -0
  22. package/dist/commands/auth-choice.apply.api-providers.js +55 -1
  23. package/dist/commands/auth-choice.apply.plugin-provider.js +1 -56
  24. package/dist/commands/auth-choice.preferred-provider.js +1 -0
  25. package/dist/commands/configure.wizard.js +29 -10
  26. package/dist/commands/dashboard.js +4 -1
  27. package/dist/commands/doctor-gateway-daemon-flow.js +8 -2
  28. package/dist/commands/doctor-gateway-services.js +6 -2
  29. package/dist/commands/doctor-platform-notes.js +3 -1
  30. package/dist/commands/gateway-status/helpers.js +3 -1
  31. package/dist/commands/models/auth.js +1 -58
  32. package/dist/commands/models/list.errors.js +14 -0
  33. package/dist/commands/models/list.list-command.js +32 -21
  34. package/dist/commands/models/list.registry.js +120 -28
  35. package/dist/commands/models/list.status-command.js +1 -0
  36. package/dist/commands/models/shared.js +14 -0
  37. package/dist/commands/onboard-auth.config-core.js +60 -2
  38. package/dist/commands/onboard-auth.credentials.js +12 -1
  39. package/dist/commands/onboard-auth.js +3 -3
  40. package/dist/commands/onboard-auth.models.js +22 -0
  41. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +1 -0
  42. package/dist/commands/onboard-non-interactive/local/auth-choice.js +21 -1
  43. package/dist/commands/provider-auth-helpers.js +61 -0
  44. package/dist/commands/status-all.js +4 -2
  45. package/dist/commands/status.gateway-probe.js +4 -2
  46. package/dist/commands/status.scan.js +2 -1
  47. package/dist/config/paths.js +18 -6
  48. package/dist/daemon/launchd.js +4 -1
  49. package/dist/daemon/schtasks.js +4 -1
  50. package/dist/daemon/systemd.js +4 -1
  51. package/dist/entry.js +4 -2
  52. package/dist/gateway/auth.js +4 -1
  53. package/dist/gateway/call.js +4 -2
  54. package/dist/gateway/server/ws-connection/message-handler.js +4 -1
  55. package/dist/gateway/server-browser.js +4 -2
  56. package/dist/gateway/server-constants.js +5 -2
  57. package/dist/gateway/server-cron.js +3 -1
  58. package/dist/gateway/server-discovery-runtime.js +5 -2
  59. package/dist/infra/restart.js +2 -1
  60. package/dist/infra/shell-env.js +3 -2
  61. package/dist/infra/state-migrations.js +3 -1
  62. package/dist/infra/system-presence.js +4 -1
  63. package/dist/infra/update-runner.js +4 -1
  64. package/dist/macos/gateway-daemon.js +6 -3
  65. package/dist/macos/relay-smoke.js +5 -1
  66. package/dist/macos/relay.js +2 -1
  67. package/dist/media/image-ops.js +3 -1
  68. package/dist/node-host/runner.js +2 -1
  69. package/dist/plugins/bundled-dir.js +2 -1
  70. package/dist/plugins/manifest-registry.js +2 -1
  71. package/dist/security/audit.js +2 -1
  72. package/dist/telegram/bot-message-dispatch.js +1 -1
  73. package/dist/telegram/network-config.js +4 -2
  74. package/dist/terminal/palette.js +8 -6
  75. package/dist/terminal/theme.js +12 -12
  76. package/dist/tui/gateway-chat.js +2 -1
  77. package/dist/tui/theme/theme.js +5 -5
  78. package/dist/tui/tui.js +12 -6
  79. package/dist/version.js +2 -1
  80. package/dist/wizard/onboarding.js +6 -2
  81. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { resolveEnvApiKey } from "../agents/model-auth.js";
3
3
  import { formatApiKeyPreview, normalizeApiKeyInput, validateApiKeyInput, } from "./auth-choice.api-key.js";
4
4
  import { applyDefaultModelChoice } from "./auth-choice.default-model.js";
5
5
  import { applyGoogleGeminiModelDefault, GOOGLE_GEMINI_DEFAULT_MODEL, } from "./google-gemini-model-default.js";
6
- import { applyAuthProfileConfig, applyCloudflareAiGatewayConfig, applyCloudflareAiGatewayProviderConfig, applyQianfanConfig, applyQianfanProviderConfig, applyKimiCodeConfig, applyKimiCodeProviderConfig, applyMoonshotConfig, applyMoonshotConfigCn, applyMoonshotProviderConfig, applyMoonshotProviderConfigCn, applyOpencodeZenConfig, applyOpencodeZenProviderConfig, applyOpenrouterConfig, applyOpenrouterProviderConfig, applySyntheticConfig, applySyntheticProviderConfig, applyTogetherConfig, applyTogetherProviderConfig, applyVeniceConfig, applyVeniceProviderConfig, applyVercelAiGatewayConfig, applyVercelAiGatewayProviderConfig, applyXiaomiConfig, applyXiaomiProviderConfig, applyZaiConfig, CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF, QIANFAN_DEFAULT_MODEL_REF, KIMI_CODING_MODEL_REF, MOONSHOT_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_REF, TOGETHER_DEFAULT_MODEL_REF, VENICE_DEFAULT_MODEL_REF, VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, XIAOMI_DEFAULT_MODEL_REF, setCloudflareAiGatewayConfig, setQianfanApiKey, setGeminiApiKey, setKimiCodingApiKey, setMoonshotApiKey, setOpencodeZenApiKey, setOpenrouterApiKey, setSyntheticApiKey, setTogetherApiKey, setVeniceApiKey, setVercelAiGatewayApiKey, setXiaomiApiKey, setZaiApiKey, ZAI_DEFAULT_MODEL_REF, } from "./onboard-auth.js";
6
+ import { applyAuthProfileConfig, applyCloudflareAiGatewayConfig, applyCloudflareAiGatewayProviderConfig, applyQianfanConfig, applyQianfanProviderConfig, applyKimiCodeConfig, applyKimiCodeProviderConfig, applyMoonshotConfig, applyMoonshotConfigCn, applyMoonshotProviderConfig, applyMoonshotProviderConfigCn, applyOpencodeZenConfig, applyOpencodeZenProviderConfig, applyOpenrouterConfig, applyOpenrouterProviderConfig, applySyntheticConfig, applySyntheticProviderConfig, applyTogetherConfig, applyTogetherProviderConfig, applyVeniceConfig, applyVeniceProviderConfig, applyVercelAiGatewayConfig, applyVercelAiGatewayProviderConfig, applyXiaomiConfig, applyXiaomiProviderConfig, applyZaiConfig, applyNvidiaConfig, applyNvidiaProviderConfig, CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF, NVIDIA_DEFAULT_MODEL_REF, QIANFAN_DEFAULT_MODEL_REF, KIMI_CODING_MODEL_REF, MOONSHOT_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_REF, TOGETHER_DEFAULT_MODEL_REF, VENICE_DEFAULT_MODEL_REF, VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, XIAOMI_DEFAULT_MODEL_REF, setCloudflareAiGatewayConfig, setQianfanApiKey, setGeminiApiKey, setKimiCodingApiKey, setMoonshotApiKey, setNvidiaApiKey, setOpencodeZenApiKey, setOpenrouterApiKey, setSyntheticApiKey, setTogetherApiKey, setVeniceApiKey, setVercelAiGatewayApiKey, setXiaomiApiKey, setZaiApiKey, ZAI_DEFAULT_MODEL_REF, } from "./onboard-auth.js";
7
7
  import { OPENCODE_ZEN_DEFAULT_MODEL } from "./opencode-zen-model-default.js";
8
8
  export async function applyAuthChoiceApiProviders(params) {
9
9
  let nextConfig = params.config;
@@ -59,6 +59,9 @@ export async function applyAuthChoiceApiProviders(params) {
59
59
  else if (params.opts.tokenProvider === "qianfan") {
60
60
  authChoice = "qianfan-api-key";
61
61
  }
62
+ else if (params.opts.tokenProvider === "nvidia") {
63
+ authChoice = "nvidia-api-key";
64
+ }
62
65
  }
63
66
  if (authChoice === "openrouter-api-key") {
64
67
  const store = ensureAuthProfileStore(params.agentDir, {
@@ -786,5 +789,56 @@ export async function applyAuthChoiceApiProviders(params) {
786
789
  }
787
790
  return { config: nextConfig, agentModelOverride };
788
791
  }
792
+ if (authChoice === "nvidia-api-key") {
793
+ let hasCredential = false;
794
+ if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "nvidia") {
795
+ setNvidiaApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
796
+ hasCredential = true;
797
+ }
798
+ if (!hasCredential) {
799
+ await params.prompter.note([
800
+ "Get your API key at: https://build.nvidia.com/settings/api-key",
801
+ "Free tier includes 1,000 requests/day across 23+ frontier models.",
802
+ ].join("\n"), "NVIDIA");
803
+ }
804
+ const envKey = resolveEnvApiKey("nvidia");
805
+ if (envKey) {
806
+ const useExisting = await params.prompter.confirm({
807
+ message: `Use existing NVIDIA_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
808
+ initialValue: true,
809
+ });
810
+ if (useExisting) {
811
+ setNvidiaApiKey(envKey.apiKey, params.agentDir);
812
+ hasCredential = true;
813
+ }
814
+ }
815
+ if (!hasCredential) {
816
+ const key = await params.prompter.text({
817
+ message: "Enter NVIDIA API key",
818
+ validate: validateApiKeyInput,
819
+ });
820
+ setNvidiaApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
821
+ }
822
+ nextConfig = applyAuthProfileConfig(nextConfig, {
823
+ profileId: "nvidia:default",
824
+ provider: "nvidia",
825
+ mode: "api_key",
826
+ });
827
+ {
828
+ const applied = await applyDefaultModelChoice({
829
+ config: nextConfig,
830
+ setDefaultModel: params.setDefaultModel,
831
+ defaultModel: NVIDIA_DEFAULT_MODEL_REF,
832
+ applyDefaultConfig: applyNvidiaConfig,
833
+ applyProviderConfig: applyNvidiaProviderConfig,
834
+ noteDefault: NVIDIA_DEFAULT_MODEL_REF,
835
+ noteAgentModel,
836
+ prompter: params.prompter,
837
+ });
838
+ nextConfig = applied.config;
839
+ agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
840
+ }
841
+ return { config: nextConfig, agentModelOverride };
842
+ }
789
843
  return null;
790
844
  }
@@ -1,7 +1,6 @@
1
1
  import { resolvePoolbotAgentDir } from "../agents/agent-paths.js";
2
2
  import { resolveDefaultAgentId, resolveAgentDir, resolveAgentWorkspaceDir, } from "../agents/agent-scope.js";
3
3
  import { upsertAuthProfile } from "../agents/auth-profiles.js";
4
- import { normalizeProviderId } from "../agents/model-selection.js";
5
4
  import { resolveDefaultAgentWorkspaceDir } from "../agents/workspace.js";
6
5
  import { enablePluginInConfig } from "../plugins/enable.js";
7
6
  import { resolvePluginProviders } from "../plugins/providers.js";
@@ -9,61 +8,7 @@ import { applyAuthProfileConfig } from "./onboard-auth.js";
9
8
  import { openUrl } from "./onboard-helpers.js";
10
9
  import { createVpsAwareOAuthHandlers } from "./oauth-flow.js";
11
10
  import { isRemoteEnvironment } from "./oauth-env.js";
12
- function resolveProviderMatch(providers, rawProvider) {
13
- const normalized = normalizeProviderId(rawProvider);
14
- return (providers.find((provider) => normalizeProviderId(provider.id) === normalized) ??
15
- providers.find((provider) => provider.aliases?.some((alias) => normalizeProviderId(alias) === normalized) ?? false) ??
16
- null);
17
- }
18
- function pickAuthMethod(provider, rawMethod) {
19
- const raw = rawMethod?.trim();
20
- if (!raw)
21
- return null;
22
- const normalized = raw.toLowerCase();
23
- return (provider.auth.find((method) => method.id.toLowerCase() === normalized) ??
24
- provider.auth.find((method) => method.label.toLowerCase() === normalized) ??
25
- null);
26
- }
27
- function isPlainRecord(value) {
28
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
29
- }
30
- function mergeConfigPatch(base, patch) {
31
- if (!isPlainRecord(base) || !isPlainRecord(patch)) {
32
- return patch;
33
- }
34
- const next = { ...base };
35
- for (const [key, value] of Object.entries(patch)) {
36
- const existing = next[key];
37
- if (isPlainRecord(existing) && isPlainRecord(value)) {
38
- next[key] = mergeConfigPatch(existing, value);
39
- }
40
- else {
41
- next[key] = value;
42
- }
43
- }
44
- return next;
45
- }
46
- function applyDefaultModel(cfg, model) {
47
- const models = { ...cfg.agents?.defaults?.models };
48
- models[model] = models[model] ?? {};
49
- const existingModel = cfg.agents?.defaults?.model;
50
- return {
51
- ...cfg,
52
- agents: {
53
- ...cfg.agents,
54
- defaults: {
55
- ...cfg.agents?.defaults,
56
- models,
57
- model: {
58
- ...(existingModel && typeof existingModel === "object" && "fallbacks" in existingModel
59
- ? { fallbacks: existingModel.fallbacks }
60
- : undefined),
61
- primary: model,
62
- },
63
- },
64
- },
65
- };
66
- }
11
+ import { applyDefaultModel, mergeConfigPatch, pickAuthMethod, resolveProviderMatch, } from "./provider-auth-helpers.js";
67
12
  export async function applyAuthChoicePluginProvider(params, options) {
68
13
  if (params.authChoice !== options.authChoice)
69
14
  return null;
@@ -32,6 +32,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE = {
32
32
  "xai-api-key": "xai",
33
33
  "qwen-portal": "qwen-portal",
34
34
  "qianfan-api-key": "qianfan",
35
+ "nvidia-api-key": "nvidia",
35
36
  };
36
37
  export function resolvePreferredProviderForAuthChoice(choice) {
37
38
  return PREFERRED_PROVIDER_BY_AUTH_CHOICE[choice];
@@ -132,8 +132,12 @@ export async function runConfigureWizard(opts, runtime = defaultRuntime) {
132
132
  const localUrl = "ws://127.0.0.1:18789";
133
133
  const localProbe = await probeGatewayReachable({
134
134
  url: localUrl,
135
- token: baseConfig.gateway?.auth?.token ?? process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN,
136
- password: baseConfig.gateway?.auth?.password ?? process.env.POOLBOT_GATEWAY_PASSWORD ?? process.env.CLAWDBOT_GATEWAY_PASSWORD,
135
+ token: baseConfig.gateway?.auth?.token ??
136
+ process.env.POOLBOT_GATEWAY_TOKEN ??
137
+ process.env.CLAWDBOT_GATEWAY_TOKEN,
138
+ password: baseConfig.gateway?.auth?.password ??
139
+ process.env.POOLBOT_GATEWAY_PASSWORD ??
140
+ process.env.CLAWDBOT_GATEWAY_PASSWORD,
137
141
  });
138
142
  const remoteUrl = baseConfig.gateway?.remote?.url?.trim() ?? "";
139
143
  const remoteProbe = remoteUrl
@@ -192,7 +196,8 @@ export async function runConfigureWizard(opts, runtime = defaultRuntime) {
192
196
  let gatewayPort = resolveGatewayPort(baseConfig);
193
197
  let gatewayToken = nextConfig.gateway?.auth?.token ??
194
198
  baseConfig.gateway?.auth?.token ??
195
- process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN;
199
+ process.env.POOLBOT_GATEWAY_TOKEN ??
200
+ process.env.CLAWDBOT_GATEWAY_TOKEN;
196
201
  const persistConfig = async () => {
197
202
  nextConfig = applyWizardMetadata(nextConfig, {
198
203
  command: opts.command,
@@ -277,8 +282,12 @@ export async function runConfigureWizard(opts, runtime = defaultRuntime) {
277
282
  });
278
283
  const remoteUrl = nextConfig.gateway?.remote?.url?.trim();
279
284
  const wsUrl = nextConfig.gateway?.mode === "remote" && remoteUrl ? remoteUrl : localLinks.wsUrl;
280
- const token = nextConfig.gateway?.auth?.token ?? process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN;
281
- const password = nextConfig.gateway?.auth?.password ?? process.env.POOLBOT_GATEWAY_PASSWORD ?? process.env.CLAWDBOT_GATEWAY_PASSWORD;
285
+ const token = nextConfig.gateway?.auth?.token ??
286
+ process.env.POOLBOT_GATEWAY_TOKEN ??
287
+ process.env.CLAWDBOT_GATEWAY_TOKEN;
288
+ const password = nextConfig.gateway?.auth?.password ??
289
+ process.env.POOLBOT_GATEWAY_PASSWORD ??
290
+ process.env.CLAWDBOT_GATEWAY_PASSWORD;
282
291
  await waitForGatewayReachable({
283
292
  url: wsUrl,
284
293
  token,
@@ -386,8 +395,12 @@ export async function runConfigureWizard(opts, runtime = defaultRuntime) {
386
395
  });
387
396
  const remoteUrl = nextConfig.gateway?.remote?.url?.trim();
388
397
  const wsUrl = nextConfig.gateway?.mode === "remote" && remoteUrl ? remoteUrl : localLinks.wsUrl;
389
- const token = nextConfig.gateway?.auth?.token ?? process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN;
390
- const password = nextConfig.gateway?.auth?.password ?? process.env.POOLBOT_GATEWAY_PASSWORD ?? process.env.CLAWDBOT_GATEWAY_PASSWORD;
398
+ const token = nextConfig.gateway?.auth?.token ??
399
+ process.env.POOLBOT_GATEWAY_TOKEN ??
400
+ process.env.CLAWDBOT_GATEWAY_TOKEN;
401
+ const password = nextConfig.gateway?.auth?.password ??
402
+ process.env.POOLBOT_GATEWAY_PASSWORD ??
403
+ process.env.CLAWDBOT_GATEWAY_PASSWORD;
391
404
  await waitForGatewayReachable({
392
405
  url: wsUrl,
393
406
  token,
@@ -429,9 +442,15 @@ export async function runConfigureWizard(opts, runtime = defaultRuntime) {
429
442
  basePath: nextConfig.gateway?.controlUi?.basePath,
430
443
  });
431
444
  // Try both new and old passwords since gateway may still have old config.
432
- const newPassword = nextConfig.gateway?.auth?.password ?? process.env.POOLBOT_GATEWAY_PASSWORD ?? process.env.CLAWDBOT_GATEWAY_PASSWORD;
433
- const oldPassword = baseConfig.gateway?.auth?.password ?? process.env.POOLBOT_GATEWAY_PASSWORD ?? process.env.CLAWDBOT_GATEWAY_PASSWORD;
434
- const token = nextConfig.gateway?.auth?.token ?? process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN;
445
+ const newPassword = nextConfig.gateway?.auth?.password ??
446
+ process.env.POOLBOT_GATEWAY_PASSWORD ??
447
+ process.env.CLAWDBOT_GATEWAY_PASSWORD;
448
+ const oldPassword = baseConfig.gateway?.auth?.password ??
449
+ process.env.POOLBOT_GATEWAY_PASSWORD ??
450
+ process.env.CLAWDBOT_GATEWAY_PASSWORD;
451
+ const token = nextConfig.gateway?.auth?.token ??
452
+ process.env.POOLBOT_GATEWAY_TOKEN ??
453
+ process.env.CLAWDBOT_GATEWAY_TOKEN;
435
454
  let gatewayProbe = await probeGatewayReachable({
436
455
  url: links.wsUrl,
437
456
  token,
@@ -9,7 +9,10 @@ export async function dashboardCommand(runtime = defaultRuntime, options = {}) {
9
9
  const bind = cfg.gateway?.bind ?? "loopback";
10
10
  const basePath = cfg.gateway?.controlUi?.basePath;
11
11
  const customBindHost = cfg.gateway?.customBindHost;
12
- const token = cfg.gateway?.auth?.token ?? process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN ?? "";
12
+ const token = cfg.gateway?.auth?.token ??
13
+ process.env.POOLBOT_GATEWAY_TOKEN ??
14
+ process.env.CLAWDBOT_GATEWAY_TOKEN ??
15
+ "";
13
16
  const links = resolveControlUiLinks({
14
17
  port,
15
18
  bind,
@@ -72,7 +72,11 @@ export async function maybeRepairGatewayDaemon(params) {
72
72
  prompter: params.prompter,
73
73
  });
74
74
  await maybeRepairLaunchAgentBootstrap({
75
- env: { ...process.env, POOLBOT_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel(), CLAWDBOT_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel() },
75
+ env: {
76
+ ...process.env,
77
+ POOLBOT_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel(),
78
+ CLAWDBOT_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel(),
79
+ },
76
80
  title: "Node",
77
81
  runtime: params.runtime,
78
82
  prompter: params.prompter,
@@ -121,7 +125,9 @@ export async function maybeRepairGatewayDaemon(params) {
121
125
  const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
122
126
  env: process.env,
123
127
  port,
124
- token: params.cfg.gateway?.auth?.token ?? process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN,
128
+ token: params.cfg.gateway?.auth?.token ??
129
+ process.env.POOLBOT_GATEWAY_TOKEN ??
130
+ process.env.CLAWDBOT_GATEWAY_TOKEN,
125
131
  runtime: daemonRuntime,
126
132
  warn: (message, title) => note(message, title),
127
133
  config: params.cfg,
@@ -80,7 +80,9 @@ export async function maybeMigrateLegacyGatewayService(cfg, mode, runtime, promp
80
80
  const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
81
81
  env: process.env,
82
82
  port,
83
- token: cfg.gateway?.auth?.token ?? process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN,
83
+ token: cfg.gateway?.auth?.token ??
84
+ process.env.POOLBOT_GATEWAY_TOKEN ??
85
+ process.env.CLAWDBOT_GATEWAY_TOKEN,
84
86
  runtime: daemonRuntime,
85
87
  warn: (message, title) => note(message, title),
86
88
  config: cfg,
@@ -138,7 +140,9 @@ export async function maybeRepairGatewayServiceConfig(cfg, mode, runtime, prompt
138
140
  const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
139
141
  env: process.env,
140
142
  port,
141
- token: cfg.gateway?.auth?.token ?? process.env.POOLBOT_GATEWAY_TOKEN ?? process.env.CLAWDBOT_GATEWAY_TOKEN,
143
+ token: cfg.gateway?.auth?.token ??
144
+ process.env.POOLBOT_GATEWAY_TOKEN ??
145
+ process.env.CLAWDBOT_GATEWAY_TOKEN,
142
146
  runtime: needsNodeRuntime && systemNodePath ? "node" : runtimeChoice,
143
147
  nodePath: systemNodePath ?? undefined,
144
148
  warn: (message, title) => note(message, title),
@@ -69,7 +69,9 @@ export async function noteMacLaunchctlGatewayEnvOverrides(cfg, deps) {
69
69
  passwordUnsets.push(" launchctl unsetenv CLAWDBOT_GATEWAY_PASSWORD");
70
70
  const lines = [
71
71
  "- launchctl environment overrides detected (can cause confusing unauthorized errors).",
72
- hasToken ? `- \`${poolbotToken ? "POOLBOT" : "CLAWDBOT"}_GATEWAY_TOKEN\` is set; it overrides config tokens.` : undefined,
72
+ hasToken
73
+ ? `- \`${poolbotToken ? "POOLBOT" : "CLAWDBOT"}_GATEWAY_TOKEN\` is set; it overrides config tokens.`
74
+ : undefined,
73
75
  hasPassword
74
76
  ? `- \`${poolbotPassword ? "POOLBOT" : "CLAWDBOT"}_GATEWAY_PASSWORD\` is set; it overrides config passwords.`
75
77
  : undefined,
@@ -92,7 +92,9 @@ export function resolveAuthForTarget(cfg, target, overrides) {
92
92
  };
93
93
  }
94
94
  const envToken = process.env.POOLBOT_GATEWAY_TOKEN?.trim() || process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() || "";
95
- const envPassword = process.env.POOLBOT_GATEWAY_PASSWORD?.trim() || process.env.CLAWDBOT_GATEWAY_PASSWORD?.trim() || "";
95
+ const envPassword = process.env.POOLBOT_GATEWAY_PASSWORD?.trim() ||
96
+ process.env.CLAWDBOT_GATEWAY_PASSWORD?.trim() ||
97
+ "";
96
98
  const cfgToken = typeof cfg.gateway?.auth?.token === "string" ? cfg.gateway.auth.token.trim() : "";
97
99
  const cfgPassword = typeof cfg.gateway?.auth?.password === "string" ? cfg.gateway.auth.password.trim() : "";
98
100
  return {
@@ -12,6 +12,7 @@ import { applyAuthProfileConfig } from "../onboard-auth.js";
12
12
  import { isRemoteEnvironment } from "../oauth-env.js";
13
13
  import { openUrl } from "../onboard-helpers.js";
14
14
  import { createVpsAwareOAuthHandlers } from "../oauth-flow.js";
15
+ import { applyDefaultModel, mergeConfigPatch, pickAuthMethod, resolveProviderMatch, } from "../provider-auth-helpers.js";
15
16
  import { updateConfig } from "./shared.js";
16
17
  import { resolvePluginProviders } from "../../plugins/providers.js";
17
18
  import { createClackPrompter } from "../../wizard/clack-prompter.js";
@@ -167,64 +168,6 @@ export async function modelsAuthAddCommand(_opts, runtime) {
167
168
  : undefined;
168
169
  await modelsAuthPasteTokenCommand({ provider: providerId, profileId, expiresIn }, runtime);
169
170
  }
170
- function resolveProviderMatch(providers, rawProvider) {
171
- const raw = rawProvider?.trim();
172
- if (!raw)
173
- return null;
174
- const normalized = normalizeProviderId(raw);
175
- return (providers.find((provider) => normalizeProviderId(provider.id) === normalized) ??
176
- providers.find((provider) => provider.aliases?.some((alias) => normalizeProviderId(alias) === normalized) ?? false) ??
177
- null);
178
- }
179
- function pickAuthMethod(provider, rawMethod) {
180
- const raw = rawMethod?.trim();
181
- if (!raw)
182
- return null;
183
- const normalized = raw.toLowerCase();
184
- return (provider.auth.find((method) => method.id.toLowerCase() === normalized) ??
185
- provider.auth.find((method) => method.label.toLowerCase() === normalized) ??
186
- null);
187
- }
188
- function isPlainRecord(value) {
189
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
190
- }
191
- function mergeConfigPatch(base, patch) {
192
- if (!isPlainRecord(base) || !isPlainRecord(patch)) {
193
- return patch;
194
- }
195
- const next = { ...base };
196
- for (const [key, value] of Object.entries(patch)) {
197
- const existing = next[key];
198
- if (isPlainRecord(existing) && isPlainRecord(value)) {
199
- next[key] = mergeConfigPatch(existing, value);
200
- }
201
- else {
202
- next[key] = value;
203
- }
204
- }
205
- return next;
206
- }
207
- function applyDefaultModel(cfg, model) {
208
- const models = { ...cfg.agents?.defaults?.models };
209
- models[model] = models[model] ?? {};
210
- const existingModel = cfg.agents?.defaults?.model;
211
- return {
212
- ...cfg,
213
- agents: {
214
- ...cfg.agents,
215
- defaults: {
216
- ...cfg.agents?.defaults,
217
- models,
218
- model: {
219
- ...(existingModel && typeof existingModel === "object" && "fallbacks" in existingModel
220
- ? { fallbacks: existingModel.fallbacks }
221
- : undefined),
222
- primary: model,
223
- },
224
- },
225
- },
226
- };
227
- }
228
171
  function credentialMode(credential) {
229
172
  if (credential.type === "api_key")
230
173
  return "api_key";
@@ -0,0 +1,14 @@
1
+ export const MODEL_AVAILABILITY_UNAVAILABLE_CODE = "MODEL_AVAILABILITY_UNAVAILABLE";
2
+ export function formatErrorWithStack(err) {
3
+ if (err instanceof Error) {
4
+ return err.stack ?? `${err.name}: ${err.message}`;
5
+ }
6
+ return String(err);
7
+ }
8
+ export function shouldFallbackToAuthHeuristics(err) {
9
+ if (!(err instanceof Error)) {
10
+ return false;
11
+ }
12
+ const code = err.code;
13
+ return code === MODEL_AVAILABILITY_UNAVAILABLE_CODE;
14
+ }
@@ -1,10 +1,13 @@
1
1
  import { ensureAuthProfileStore } from "../../agents/auth-profiles.js";
2
+ import { resolveForwardCompatModel } from "../../agents/model-forward-compat.js";
2
3
  import { parseModelRef } from "../../agents/model-selection.js";
4
+ import { resolveModel } from "../../agents/pi-embedded-runner/model.js";
3
5
  import { loadConfig } from "../../config/config.js";
4
6
  import { resolveConfiguredEntries } from "./list.configured.js";
7
+ import { formatErrorWithStack } from "./list.errors.js";
5
8
  import { loadModelRegistry, toModelRow } from "./list.registry.js";
6
9
  import { printModelTable } from "./list.table.js";
7
- import { DEFAULT_PROVIDER, ensureFlagCompatibility, modelKey } from "./shared.js";
10
+ import { DEFAULT_PROVIDER, ensureFlagCompatibility, isLocalBaseUrl, modelKey } from "./shared.js";
8
11
  export async function modelsListCommand(opts, runtime) {
9
12
  ensureFlagCompatibility(opts);
10
13
  const cfg = loadConfig();
@@ -17,35 +20,30 @@ export async function modelsListCommand(opts, runtime) {
17
20
  return parsed?.provider ?? raw.toLowerCase();
18
21
  })();
19
22
  let models = [];
23
+ let modelRegistry;
20
24
  let availableKeys;
25
+ let availabilityErrorMessage;
21
26
  try {
22
27
  const loaded = await loadModelRegistry(cfg);
28
+ modelRegistry = loaded.registry;
23
29
  models = loaded.models;
24
30
  availableKeys = loaded.availableKeys;
31
+ availabilityErrorMessage = loaded.availabilityErrorMessage;
25
32
  }
26
33
  catch (err) {
27
- runtime.error(`Model registry unavailable: ${String(err)}`);
34
+ runtime.error(`Model registry unavailable:\n${formatErrorWithStack(err)}`);
35
+ process.exitCode = 1;
36
+ return;
37
+ }
38
+ if (availabilityErrorMessage !== undefined) {
39
+ runtime.error(`Model availability lookup failed; falling back to auth heuristics for discovered models: ${availabilityErrorMessage}`);
28
40
  }
29
41
  const modelByKey = new Map(models.map((model) => [modelKey(model.provider, model.id), model]));
30
42
  const { entries } = resolveConfiguredEntries(cfg);
31
43
  const configuredByKey = new Map(entries.map((entry) => [entry.key, entry]));
32
44
  const rows = [];
33
- const isLocalBaseUrl = (baseUrl) => {
34
- try {
35
- const url = new URL(baseUrl);
36
- const host = url.hostname.toLowerCase();
37
- return (host === "localhost" ||
38
- host === "127.0.0.1" ||
39
- host === "0.0.0.0" ||
40
- host === "::1" ||
41
- host.endsWith(".local"));
42
- }
43
- catch {
44
- return false;
45
- }
46
- };
47
45
  if (opts.all) {
48
- const sorted = [...models].sort((a, b) => {
46
+ const sorted = [...models].toSorted((a, b) => {
49
47
  const p = a.provider.localeCompare(b.provider);
50
48
  if (p !== 0)
51
49
  return p;
@@ -55,8 +53,9 @@ export async function modelsListCommand(opts, runtime) {
55
53
  if (providerFilter && model.provider.toLowerCase() !== providerFilter) {
56
54
  continue;
57
55
  }
58
- if (opts.local && !isLocalBaseUrl(model.baseUrl))
56
+ if (opts.local && !isLocalBaseUrl(model.baseUrl)) {
59
57
  continue;
58
+ }
60
59
  const key = modelKey(model.provider, model.id);
61
60
  const configured = configuredByKey.get(key);
62
61
  rows.push(toModelRow({
@@ -75,11 +74,23 @@ export async function modelsListCommand(opts, runtime) {
75
74
  if (providerFilter && entry.ref.provider.toLowerCase() !== providerFilter) {
76
75
  continue;
77
76
  }
78
- const model = modelByKey.get(entry.key);
79
- if (opts.local && model && !isLocalBaseUrl(model.baseUrl))
77
+ let model = modelByKey.get(entry.key);
78
+ if (!model && modelRegistry) {
79
+ const forwardCompat = resolveForwardCompatModel(entry.ref.provider, entry.ref.model, modelRegistry);
80
+ if (forwardCompat) {
81
+ model = forwardCompat;
82
+ modelByKey.set(entry.key, forwardCompat);
83
+ }
84
+ }
85
+ if (!model) {
86
+ model = resolveModel(entry.ref.provider, entry.ref.model, undefined, cfg).model;
87
+ }
88
+ if (opts.local && model && !isLocalBaseUrl(model.baseUrl)) {
80
89
  continue;
81
- if (opts.local && !model)
90
+ }
91
+ if (opts.local && !model) {
82
92
  continue;
93
+ }
83
94
  rows.push(toModelRow({
84
95
  model,
85
96
  key: entry.key,