@poolzin/pool-bot 2026.2.5 → 2026.2.7

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 (28) hide show
  1. package/dist/agents/auth-profiles/profiles.js +9 -0
  2. package/dist/agents/auth-profiles.js +1 -1
  3. package/dist/agents/huggingface-models.js +166 -0
  4. package/dist/agents/model-auth.js +5 -0
  5. package/dist/build-info.json +3 -3
  6. package/dist/cli/config-cli.js +17 -3
  7. package/dist/cli/program/register.onboard.js +34 -11
  8. package/dist/commands/auth-choice-options.js +60 -7
  9. package/dist/commands/auth-choice.apply.api-providers.js +149 -98
  10. package/dist/commands/auth-choice.apply.huggingface.js +130 -0
  11. package/dist/commands/auth-choice.apply.openrouter.js +77 -0
  12. package/dist/commands/auth-choice.apply.vllm.js +92 -0
  13. package/dist/commands/auth-choice.preferred-provider.js +9 -0
  14. package/dist/commands/onboard-auth.config-core.js +207 -8
  15. package/dist/commands/onboard-auth.credentials.js +35 -5
  16. package/dist/commands/onboard-auth.js +3 -3
  17. package/dist/commands/onboard-auth.models.js +45 -0
  18. package/dist/commands/onboard-custom.js +181 -70
  19. package/dist/commands/onboard-non-interactive/api-keys.js +10 -1
  20. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +14 -7
  21. package/dist/commands/onboard-non-interactive/local/auth-choice.js +322 -144
  22. package/dist/commands/zai-endpoint-detect.js +97 -0
  23. package/dist/config/legacy.migrations.part-3.js +57 -0
  24. package/dist/daemon/constants.js +7 -3
  25. package/dist/tui/components/filterable-select-list.js +5 -2
  26. package/dist/tui/components/searchable-select-list.js +6 -1
  27. package/dist/tui/tui-command-handlers.js +13 -5
  28. package/package.json +5 -5
@@ -0,0 +1,97 @@
1
+ import { fetchWithTimeout } from "../utils/fetch-timeout.js";
2
+ import { ZAI_CN_BASE_URL, ZAI_CODING_CN_BASE_URL, ZAI_CODING_GLOBAL_BASE_URL, ZAI_GLOBAL_BASE_URL, } from "./onboard-auth.models.js";
3
+ async function probeZaiChatCompletions(params) {
4
+ try {
5
+ const res = await fetchWithTimeout(`${params.baseUrl}/chat/completions`, {
6
+ method: "POST",
7
+ headers: {
8
+ authorization: `Bearer ${params.apiKey}`,
9
+ "content-type": "application/json",
10
+ },
11
+ body: JSON.stringify({
12
+ model: params.modelId,
13
+ stream: false,
14
+ max_tokens: 1,
15
+ messages: [{ role: "user", content: "ping" }],
16
+ }),
17
+ }, params.timeoutMs, params.fetchFn);
18
+ if (res.ok) {
19
+ return { ok: true };
20
+ }
21
+ let errorCode;
22
+ let errorMessage;
23
+ try {
24
+ const json = (await res.json());
25
+ const code = json?.error?.code;
26
+ const msg = json?.error?.message ?? json?.msg ?? json?.message;
27
+ if (typeof code === "string") {
28
+ errorCode = code;
29
+ }
30
+ else if (typeof code === "number") {
31
+ errorCode = String(code);
32
+ }
33
+ if (typeof msg === "string") {
34
+ errorMessage = msg;
35
+ }
36
+ }
37
+ catch {
38
+ // ignore
39
+ }
40
+ return { ok: false, status: res.status, errorCode, errorMessage };
41
+ }
42
+ catch {
43
+ return { ok: false };
44
+ }
45
+ }
46
+ export async function detectZaiEndpoint(params) {
47
+ // Never auto-probe in vitest; it would create flaky network behavior.
48
+ if (process.env.VITEST && !params.fetchFn) {
49
+ return null;
50
+ }
51
+ const timeoutMs = params.timeoutMs ?? 5_000;
52
+ // Prefer GLM-5 on the general API endpoints.
53
+ const glm5 = [
54
+ { endpoint: "global", baseUrl: ZAI_GLOBAL_BASE_URL },
55
+ { endpoint: "cn", baseUrl: ZAI_CN_BASE_URL },
56
+ ];
57
+ for (const candidate of glm5) {
58
+ const result = await probeZaiChatCompletions({
59
+ baseUrl: candidate.baseUrl,
60
+ apiKey: params.apiKey,
61
+ modelId: "glm-5",
62
+ timeoutMs,
63
+ fetchFn: params.fetchFn,
64
+ });
65
+ if (result.ok) {
66
+ return {
67
+ endpoint: candidate.endpoint,
68
+ baseUrl: candidate.baseUrl,
69
+ modelId: "glm-5",
70
+ note: `Verified GLM-5 on ${candidate.endpoint} endpoint.`,
71
+ };
72
+ }
73
+ }
74
+ // Fallback: Coding Plan endpoint (GLM-5 not available there).
75
+ const coding = [
76
+ { endpoint: "coding-global", baseUrl: ZAI_CODING_GLOBAL_BASE_URL },
77
+ { endpoint: "coding-cn", baseUrl: ZAI_CODING_CN_BASE_URL },
78
+ ];
79
+ for (const candidate of coding) {
80
+ const result = await probeZaiChatCompletions({
81
+ baseUrl: candidate.baseUrl,
82
+ apiKey: params.apiKey,
83
+ modelId: "glm-4.7",
84
+ timeoutMs,
85
+ fetchFn: params.fetchFn,
86
+ });
87
+ if (result.ok) {
88
+ return {
89
+ endpoint: candidate.endpoint,
90
+ baseUrl: candidate.baseUrl,
91
+ modelId: "glm-4.7",
92
+ note: "Coding Plan endpoint detected; GLM-5 is not available there. Defaulting to GLM-4.7.",
93
+ };
94
+ }
95
+ }
96
+ return null;
97
+ }
@@ -161,4 +161,61 @@ export const LEGACY_CONFIG_MIGRATIONS_PART_3 = [
161
161
  delete raw.identity;
162
162
  },
163
163
  },
164
+ {
165
+ id: "gateway.bind-enum-fix",
166
+ describe: "Fix invalid gateway.bind values to 'auto'",
167
+ apply: (raw, changes) => {
168
+ const gateway = getRecord(raw.gateway);
169
+ if (!gateway)
170
+ return;
171
+ const bind = gateway.bind;
172
+ if (bind === undefined)
173
+ return;
174
+ const validBindValues = new Set(["auto", "lan", "loopback", "custom", "tailnet"]);
175
+ if (typeof bind !== "string" || !validBindValues.has(bind)) {
176
+ gateway.bind = "auto";
177
+ changes.push(`Fixed gateway.bind from ${JSON.stringify(bind)} → "auto".`);
178
+ }
179
+ },
180
+ },
181
+ {
182
+ id: "gateway.nodes.browser.mode-enum-fix",
183
+ describe: "Fix invalid gateway.nodes.browser.mode values to 'auto'",
184
+ apply: (raw, changes) => {
185
+ const gateway = getRecord(raw.gateway);
186
+ if (!gateway)
187
+ return;
188
+ const nodes = getRecord(gateway.nodes);
189
+ if (!nodes)
190
+ return;
191
+ const browser = getRecord(nodes.browser);
192
+ if (!browser)
193
+ return;
194
+ const mode = browser.mode;
195
+ if (mode === undefined)
196
+ return;
197
+ const validModes = new Set(["auto", "manual", "off"]);
198
+ if (typeof mode !== "string" || !validModes.has(mode)) {
199
+ browser.mode = "auto";
200
+ changes.push(`Fixed gateway.nodes.browser.mode from ${JSON.stringify(mode)} → "auto".`);
201
+ }
202
+ },
203
+ },
204
+ {
205
+ id: "gateway.mode-enum-fix",
206
+ describe: "Fix invalid gateway.mode values to 'local'",
207
+ apply: (raw, changes) => {
208
+ const gateway = getRecord(raw.gateway);
209
+ if (!gateway)
210
+ return;
211
+ const mode = gateway.mode;
212
+ if (mode === undefined)
213
+ return;
214
+ const validModes = new Set(["local", "remote"]);
215
+ if (typeof mode !== "string" || !validModes.has(mode)) {
216
+ gateway.mode = "local";
217
+ changes.push(`Fixed gateway.mode from ${JSON.stringify(mode)} → "local".`);
218
+ }
219
+ },
220
+ },
164
221
  ];
@@ -10,9 +10,13 @@ export const NODE_WINDOWS_TASK_NAME = "Poolbot Node";
10
10
  export const NODE_SERVICE_MARKER = "poolbot";
11
11
  export const NODE_SERVICE_KIND = "node";
12
12
  export const NODE_WINDOWS_TASK_SCRIPT_NAME = "node.cmd";
13
- export const LEGACY_GATEWAY_LAUNCH_AGENT_LABELS = ["com.steipete.poolbot.gateway"];
14
- export const LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES = [];
15
- export const LEGACY_GATEWAY_WINDOWS_TASK_NAMES = [];
13
+ export const LEGACY_GATEWAY_LAUNCH_AGENT_LABELS = [
14
+ "com.steipete.poolbot.gateway",
15
+ "com.moltbot.gateway",
16
+ "com.clawdbot.gateway",
17
+ ];
18
+ export const LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES = ["moltbot-gateway", "clawdbot-gateway"];
19
+ export const LEGACY_GATEWAY_WINDOWS_TASK_NAMES = ["Moltbot Gateway", "Clawdbot Gateway"];
16
20
  export function normalizeGatewayProfile(profile) {
17
21
  const trimmed = profile?.trim();
18
22
  if (!trimmed || trimmed.toLowerCase() === "default")
@@ -1,4 +1,4 @@
1
- import { Input, matchesKey, SelectList, getEditorKeybindings, } from "@mariozechner/pi-tui";
1
+ import { Input, matchesKey, SelectList, getEditorKeybindings, truncateToWidth, } from "@mariozechner/pi-tui";
2
2
  import chalk from "chalk";
3
3
  import { fuzzyFilterLower, prepareSearchItems } from "./fuzzy-filter.js";
4
4
  /**
@@ -46,7 +46,10 @@ export class FilterableSelectList {
46
46
  // Select list
47
47
  const listLines = this.selectList.render(width);
48
48
  lines.push(...listLines);
49
- return lines;
49
+ // Defensive truncation: ensure no line exceeds the target width after
50
+ // all ANSI formatting. Prevents pi-tui compositeLineAt crash on
51
+ // ANSI-heavy backgrounds ("Rendered line N exceeds terminal width").
52
+ return lines.map((line) => truncateToWidth(line, width));
50
53
  }
51
54
  handleInput(keyData) {
52
55
  const allowVimNav = !this.filterText.trim();
@@ -149,7 +149,12 @@ export class SearchableSelectList {
149
149
  const scrollInfo = `${this.selectedIndex + 1}/${this.filteredItems.length}`;
150
150
  lines.push(this.theme.scrollInfo(` ${scrollInfo}`));
151
151
  }
152
- return lines;
152
+ // Defensive truncation: ensure no line exceeds the target width after
153
+ // all ANSI formatting has been applied. pi-tui's compositeLineAt can
154
+ // miscalculate visible widths on ANSI-heavy lines, causing a crash
155
+ // ("Rendered line N exceeds terminal width"). Truncating here is a
156
+ // safe final guard that prevents the crash.
157
+ return lines.map((line) => truncateToWidth(line, width));
153
158
  }
154
159
  renderItemLine(item, isSelected, width, query) {
155
160
  const prefix = isSelected ? "→ " : " ";
@@ -1,3 +1,4 @@
1
+ import { truncateToWidth } from "@mariozechner/pi-tui";
1
2
  import { formatThinkingLevels, normalizeUsageDisplay, resolveResponseUsageMode, } from "../auto-reply/thinking.js";
2
3
  import { normalizeAgentId } from "../routing/session-key.js";
3
4
  import { formatRelativeTime } from "../utils/time-format.js";
@@ -10,6 +11,10 @@ export function createCommandHandlers(context) {
10
11
  state.currentAgentId = normalizeAgentId(id);
11
12
  await setSession("");
12
13
  };
14
+ // Maximum visible width for overlay item strings. The overlay has its own
15
+ // padding/chrome (~4 chars). We subtract a generous margin so that even
16
+ // after ANSI formatting, lines stay within terminal bounds.
17
+ const overlayItemWidth = () => Math.max(30, (tui.terminal.columns ?? 80) - 6);
13
18
  const openModelSelector = async () => {
14
19
  try {
15
20
  const models = await client.listModels();
@@ -18,10 +23,11 @@ export function createCommandHandlers(context) {
18
23
  tui.requestRender();
19
24
  return;
20
25
  }
26
+ const maxW = overlayItemWidth();
21
27
  const items = models.map((model) => ({
22
28
  value: `${model.provider}/${model.id}`,
23
- label: `${model.provider}/${model.id}`,
24
- description: model.name && model.name !== model.id ? model.name : "",
29
+ label: truncateToWidth(`${model.provider}/${model.id}`, maxW),
30
+ description: model.name && model.name !== model.id ? truncateToWidth(model.name, maxW) : "",
25
31
  }));
26
32
  const selector = createSearchableSelectList(items, 9);
27
33
  selector.onSelect = (item) => {
@@ -60,9 +66,10 @@ export function createCommandHandlers(context) {
60
66
  tui.requestRender();
61
67
  return;
62
68
  }
69
+ const maxW = overlayItemWidth();
63
70
  const items = state.agents.map((agent) => ({
64
71
  value: agent.id,
65
- label: agent.name ? `${agent.id} (${agent.name})` : agent.id,
72
+ label: truncateToWidth(agent.name ? `${agent.id} (${agent.name})` : agent.id, maxW),
66
73
  description: agent.id === state.agentDefaultId ? "default" : "",
67
74
  }));
68
75
  const selector = createSearchableSelectList(items, 9);
@@ -89,6 +96,7 @@ export function createCommandHandlers(context) {
89
96
  includeLastMessage: true,
90
97
  agentId: state.currentAgentId,
91
98
  });
99
+ const maxW = overlayItemWidth();
92
100
  const items = result.sessions.map((session) => {
93
101
  const title = session.derivedTitle ?? session.displayName;
94
102
  const formattedKey = formatSessionKey(session.key);
@@ -100,8 +108,8 @@ export function createCommandHandlers(context) {
100
108
  const description = timePart && preview ? `${timePart} · ${preview}` : (preview ?? timePart);
101
109
  return {
102
110
  value: session.key,
103
- label,
104
- description,
111
+ label: truncateToWidth(label, maxW),
112
+ description: truncateToWidth(description, maxW),
105
113
  searchText: [
106
114
  session.displayName,
107
115
  session.label,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolzin/pool-bot",
3
- "version": "2026.2.5",
3
+ "version": "2026.2.7",
4
4
  "description": "🎱 Pool Bot - AI assistant with PLCODE integrations",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -110,10 +110,10 @@
110
110
  "@larksuiteoapi/node-sdk": "^1.58.0",
111
111
  "@line/bot-sdk": "^10.6.0",
112
112
  "@lydell/node-pty": "1.2.0-beta.3",
113
- "@mariozechner/pi-agent-core": "0.52.9",
114
- "@mariozechner/pi-ai": "0.52.9",
115
- "@mariozechner/pi-coding-agent": "0.52.9",
116
- "@mariozechner/pi-tui": "0.52.9",
113
+ "@mariozechner/pi-agent-core": "0.52.12",
114
+ "@mariozechner/pi-ai": "0.52.12",
115
+ "@mariozechner/pi-coding-agent": "0.52.12",
116
+ "@mariozechner/pi-tui": "0.52.12",
117
117
  "@mozilla/readability": "^0.6.0",
118
118
  "@sinclair/typebox": "0.34.48",
119
119
  "@slack/bolt": "^4.6.0",