@poolzin/pool-bot 2026.4.42 → 2026.4.44

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2026.4.42",
3
- "commit": "1904089b8bb7a9c40d78c2a210258b6182cb53af",
4
- "builtAt": "2026-04-08T02:20:04.246Z"
2
+ "version": "2026.4.44",
3
+ "commit": "2a04fd278aee8e7b369bdc1dca503419859df769",
4
+ "builtAt": "2026-04-08T03:20:50.969Z"
5
5
  }
@@ -1 +1 @@
1
- {"version":3,"file":"infer-cli.d.ts","sourceRoot":"","sources":["../../src/cli/infer-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkDzC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,QAyZhD"}
1
+ {"version":3,"file":"infer-cli.d.ts","sourceRoot":"","sources":["../../src/cli/infer-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkDzC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,QAobhD"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Guided Onboarding CLI - Step-by-step PoolBot setup wizard
3
+ *
4
+ * Interactive wizard that guides users through:
5
+ * 1. System checks
6
+ * 2. Gateway setup
7
+ * 3. Model/auth configuration
8
+ * 4. Channel setup (Telegram, Discord, etc.)
9
+ * 5. Memory/skills configuration
10
+ * 6. Final validation
11
+ *
12
+ * Usage:
13
+ * poolbot onboard # Full interactive onboarding
14
+ * poolbot onboard --skip-gateway # Skip gateway setup
15
+ * poolbot onboard --skip-channels # Skip channel setup
16
+ * poolbot onboard --yes # Use defaults (non-interactive)
17
+ */
18
+ import type { Command } from "commander";
19
+ export declare function registerOnboardCli(program: Command): void;
20
+ //# sourceMappingURL=onboard-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard-cli.d.ts","sourceRoot":"","sources":["../../src/cli/onboard-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsfzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,QAwBlD"}
@@ -0,0 +1,440 @@
1
+ /**
2
+ * Guided Onboarding CLI - Step-by-step PoolBot setup wizard
3
+ *
4
+ * Interactive wizard that guides users through:
5
+ * 1. System checks
6
+ * 2. Gateway setup
7
+ * 3. Model/auth configuration
8
+ * 4. Channel setup (Telegram, Discord, etc.)
9
+ * 5. Memory/skills configuration
10
+ * 6. Final validation
11
+ *
12
+ * Usage:
13
+ * poolbot onboard # Full interactive onboarding
14
+ * poolbot onboard --skip-gateway # Skip gateway setup
15
+ * poolbot onboard --skip-channels # Skip channel setup
16
+ * poolbot onboard --yes # Use defaults (non-interactive)
17
+ */
18
+ import { stdin as input, stdout as output } from "node:process";
19
+ import readline from "node:readline/promises";
20
+ import { loadConfig, writeConfigFile } from "../config/config.js";
21
+ import { defaultRuntime } from "../runtime.js";
22
+ import { theme } from "../terminal/theme.js";
23
+ import { danger, success } from "../globals.js";
24
+ import { formatDocsLink } from "../terminal/links.js";
25
+ import { isYes } from "../globals.js";
26
+ async function createReadlineInterface() {
27
+ return readline.createInterface({ input, output });
28
+ }
29
+ function printHeader() {
30
+ console.log("");
31
+ console.log(theme.heading("🎱 Welcome to Pool Bot Onboarding!"));
32
+ console.log(theme.muted("─────────────────────────────────────────"));
33
+ console.log("");
34
+ console.log("This wizard will guide you through setting up Pool Bot.");
35
+ console.log("Press Ctrl+C at any time to exit.");
36
+ console.log("");
37
+ }
38
+ function printStepHeader(step, total, title) {
39
+ console.log("");
40
+ console.log(theme.heading(`Step ${step}/${total}: ${title}`));
41
+ console.log(theme.muted("─────────────────────────────────────────"));
42
+ }
43
+ function printSuccess(message) {
44
+ console.log("");
45
+ console.log(success(`✓ ${message}`));
46
+ }
47
+ function printWarning(message) {
48
+ console.log("");
49
+ console.log(theme.warn(`⚠ ${message}`));
50
+ }
51
+ function printError(message) {
52
+ console.log("");
53
+ console.log(danger(`✗ ${message}`));
54
+ }
55
+ async function promptYesNo(rl, question, defaultYes = true) {
56
+ if (isYes())
57
+ return true;
58
+ const suffix = defaultYes ? " [Y/n]" : " [y/N]";
59
+ const answer = (await rl.question(`${question}${suffix}: `)).trim().toLowerCase();
60
+ if (!answer)
61
+ return defaultYes;
62
+ return answer.startsWith("y");
63
+ }
64
+ async function promptText(rl, question, defaultValue, required = false) {
65
+ if (isYes() && defaultValue)
66
+ return defaultValue;
67
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
68
+ while (true) {
69
+ const answer = (await rl.question(`${question}${suffix}: `)).trim();
70
+ if (answer || !required) {
71
+ return answer || defaultValue || "";
72
+ }
73
+ console.log(theme.warn("This field is required. Please enter a value."));
74
+ }
75
+ }
76
+ async function promptSelect(rl, question, options, defaultValue) {
77
+ if (isYes() && defaultValue)
78
+ return defaultValue;
79
+ console.log(question);
80
+ options.forEach((opt, i) => {
81
+ const defaultMarker = opt.value === defaultValue ? theme.success(" (default)") : "";
82
+ console.log(` ${i + 1}. ${opt.label}${defaultMarker}`);
83
+ });
84
+ while (true) {
85
+ const answer = (await rl.question("Select option (number): ")).trim();
86
+ if (!answer && defaultValue)
87
+ return defaultValue;
88
+ const index = Number.parseInt(answer, 10) - 1;
89
+ if (!Number.isNaN(index) && index >= 0 && index < options.length) {
90
+ return options[index].value;
91
+ }
92
+ console.log(theme.warn("Invalid selection. Please enter a number."));
93
+ }
94
+ }
95
+ // Step 1: System Checks
96
+ async function runSystemChecks(_options) {
97
+ printStepHeader(1, 6, "System Checks");
98
+ const checks = [
99
+ { name: "Node.js version", pass: process.version.startsWith("v22") || process.version.startsWith("v20") },
100
+ { name: "Home directory", pass: Boolean(process.env.HOME) },
101
+ { name: "Config directory writable", pass: true }, // Assume OK for now
102
+ ];
103
+ // Validation result tracking
104
+ for (const check of checks) {
105
+ const icon = check.pass ? theme.success("✓") : theme.warn("⚠");
106
+ console.log(` ${icon} ${check.name}${check.message ? `: ${check.message}` : ""}`);
107
+ // Track failures
108
+ }
109
+ printSuccess("System checks complete");
110
+ return true;
111
+ }
112
+ // Step 2: Gateway Setup
113
+ async function runGatewaySetup(_options) {
114
+ if (_options.skipGateway) {
115
+ printWarning("Skipping gateway setup (--skip-gateway)");
116
+ return true;
117
+ }
118
+ printStepHeader(2, 6, "Gateway Configuration");
119
+ const rl = await createReadlineInterface();
120
+ const config = loadConfig();
121
+ try {
122
+ console.log("");
123
+ console.log("Pool Bot uses a gateway to manage agents and channels.");
124
+ console.log("");
125
+ // Gateway mode
126
+ const mode = await promptSelect(rl, "Gateway mode:", [
127
+ { value: "local", label: "Local (run on this machine)" },
128
+ { value: "remote", label: "Remote (connect to existing gateway)" },
129
+ ], config.gateway?.mode || "local");
130
+ if (!config.gateway)
131
+ config.gateway = {};
132
+ config.gateway.mode = mode;
133
+ if (mode === "local") {
134
+ // Local gateway setup
135
+ const port = await promptText(rl, "Gateway port", "18789");
136
+ config.gateway.port = Number.parseInt(port, 10);
137
+ // Auth mode
138
+ const authMode = await promptSelect(rl, "Authentication mode:", [
139
+ { value: "token", label: "Token-based (recommended)" },
140
+ { value: "password", label: "Password-based" },
141
+ ], "token");
142
+ if (!config.gateway.auth)
143
+ config.gateway.auth = {};
144
+ config.gateway.auth.mode = authMode;
145
+ if (authMode === "token") {
146
+ const token = await promptText(rl, "Gateway token (min 16 chars)", undefined, true);
147
+ if (token.length < 16) {
148
+ printError("Token must be at least 16 characters");
149
+ return false;
150
+ }
151
+ config.gateway.auth.token = token;
152
+ }
153
+ }
154
+ else {
155
+ // Remote gateway setup
156
+ const url = await promptText(rl, "Gateway URL (wss://...)", undefined, true);
157
+ config.gateway.remote = { url };
158
+ const token = await promptText(rl, "Gateway token", undefined, true);
159
+ config.gateway.remote.token = token;
160
+ }
161
+ writeConfigFile(config);
162
+ printSuccess("Gateway configured");
163
+ }
164
+ finally {
165
+ rl.close();
166
+ }
167
+ return true;
168
+ }
169
+ // Step 3: Model & Auth Setup
170
+ async function runModelSetup(_options) {
171
+ if (_options.skipModels) {
172
+ printWarning("Skipping model setup (--skip-models)");
173
+ return true;
174
+ }
175
+ printStepHeader(3, 6, "Model & Authentication");
176
+ const rl = await createReadlineInterface();
177
+ const config = loadConfig();
178
+ try {
179
+ console.log("");
180
+ console.log("Configure your preferred AI model provider.");
181
+ console.log("");
182
+ // Provider selection
183
+ const provider = await promptSelect(rl, "Model provider:", [
184
+ { value: "anthropic", label: "Anthropic (Claude)" },
185
+ { value: "openai", label: "OpenAI (GPT)" },
186
+ { value: "google", label: "Google (Gemini)" },
187
+ { value: "moonshotai", label: "MoonshotAI (Kimi)" },
188
+ { value: "zai", label: "Z.AI (GLM)" },
189
+ ], "anthropic");
190
+ if (!config.auth)
191
+ config.auth = {};
192
+ if (!config.auth.profiles)
193
+ config.auth.profiles = {};
194
+ // Provider-specific setup
195
+ if (provider === "anthropic") {
196
+ console.log("");
197
+ console.log("Anthropic API Key:");
198
+ console.log(" Get your key at: https://console.anthropic.com/account/keys");
199
+ console.log("");
200
+ const _apiKey = await promptText(rl, "API Key", undefined, true);
201
+ config.auth.profiles["anthropic:default"] = {
202
+ provider: "anthropic",
203
+ mode: "api_key",
204
+ };
205
+ // Store actual API key in auth-profiles.json (separate from config)
206
+ // For now, just set up the profile reference
207
+ if (!config.agents)
208
+ config.agents = {};
209
+ if (!config.agents.defaults)
210
+ config.agents.defaults = {};
211
+ config.agents.defaults.model = {
212
+ primary: "anthropic/claude-sonnet-4-6",
213
+ };
214
+ }
215
+ else if (provider === "openai") {
216
+ console.log("");
217
+ console.log("OpenAI API Key:");
218
+ console.log(" Get your key at: https://platform.openai.com/api-keys");
219
+ console.log("");
220
+ const _apiKey = await promptText(rl, "API Key", undefined, true);
221
+ config.auth.profiles["openai:default"] = {
222
+ provider: "openai",
223
+ mode: "api_key",
224
+ };
225
+ if (!config.agents)
226
+ config.agents = {};
227
+ if (!config.agents.defaults)
228
+ config.agents.defaults = {};
229
+ config.agents.defaults.model = {
230
+ primary: "openai/gpt-5",
231
+ };
232
+ }
233
+ // Add more providers as needed...
234
+ writeConfigFile(config);
235
+ printSuccess("Model configured");
236
+ }
237
+ finally {
238
+ rl.close();
239
+ }
240
+ return true;
241
+ }
242
+ // Step 4: Channel Setup
243
+ async function runChannelSetup(_options) {
244
+ if (_options.skipChannels) {
245
+ printWarning("Skipping channel setup (--skip-channels)");
246
+ return true;
247
+ }
248
+ printStepHeader(4, 6, "Messaging Channels");
249
+ const rl = await createReadlineInterface();
250
+ const config = loadConfig();
251
+ try {
252
+ console.log("");
253
+ console.log("Configure messaging channels for Pool Bot to communicate.");
254
+ console.log("You can skip this and configure channels later.");
255
+ console.log("");
256
+ if (!config.channels)
257
+ config.channels = {};
258
+ // Telegram
259
+ const setupTelegram = await promptYesNo(rl, "Set up Telegram bot?", false);
260
+ if (setupTelegram) {
261
+ console.log("");
262
+ console.log("Telegram Bot Token:");
263
+ console.log(" Create a bot with @BotFather and get the token");
264
+ console.log("");
265
+ const token = await promptText(rl, "Bot Token", undefined, true);
266
+ config.channels.telegram = {
267
+ enabled: true,
268
+ accounts: {
269
+ default: {
270
+ token,
271
+ enabled: true,
272
+ },
273
+ },
274
+ };
275
+ printSuccess("Telegram configured");
276
+ }
277
+ // Discord
278
+ const setupDiscord = await promptYesNo(rl, "Set up Discord bot?", false);
279
+ if (setupDiscord) {
280
+ console.log("");
281
+ console.log("Discord Bot Token:");
282
+ console.log(" Create an app at https://discord.com/developers/applications");
283
+ console.log("");
284
+ const token = await promptText(rl, "Bot Token", undefined, true);
285
+ config.channels.discord = {
286
+ enabled: true,
287
+ accounts: {
288
+ default: {
289
+ token,
290
+ enabled: true,
291
+ },
292
+ },
293
+ };
294
+ printSuccess("Discord configured");
295
+ }
296
+ // More channels can be added...
297
+ writeConfigFile(config);
298
+ printSuccess("Channels configured");
299
+ }
300
+ finally {
301
+ rl.close();
302
+ }
303
+ return true;
304
+ }
305
+ // Step 5: Memory & Skills
306
+ async function runMemorySkillsSetup(_options) {
307
+ if (_options.skipMemory && _options.skipSkills) {
308
+ printWarning("Skipping memory & skills setup (--skip-memory --skip-skills)");
309
+ return true;
310
+ }
311
+ printStepHeader(5, 6, "Memory & Skills");
312
+ const rl = await createReadlineInterface();
313
+ const config = loadConfig();
314
+ try {
315
+ console.log("");
316
+ console.log("Configure memory and skills for Pool Bot.");
317
+ console.log("");
318
+ // Memory
319
+ if (!_options.skipMemory) {
320
+ const enableMemory = await promptYesNo(rl, "Enable session memory?", true);
321
+ if (!config.memory)
322
+ config.memory = {};
323
+ config.memory.backend = enableMemory ? "builtin" : undefined;
324
+ if (enableMemory) {
325
+ printSuccess("Memory enabled");
326
+ }
327
+ }
328
+ // Skills
329
+ if (!_options.skipSkills) {
330
+ const autoSkills = await promptYesNo(rl, "Auto-enable recommended skills?", true);
331
+ if (!config.skills)
332
+ config.skills = {};
333
+ // Skills auto-enable is handled by skills.auto in entries config
334
+ if (autoSkills) {
335
+ printSuccess("Skills auto-enable enabled");
336
+ }
337
+ }
338
+ writeConfigFile(config);
339
+ printSuccess("Memory & skills configured");
340
+ }
341
+ finally {
342
+ rl.close();
343
+ }
344
+ return true;
345
+ }
346
+ // Step 6: Final Validation
347
+ async function runFinalValidation(_options) {
348
+ printStepHeader(6, 6, "Final Validation");
349
+ const config = loadConfig();
350
+ console.log("");
351
+ console.log("Validating configuration...");
352
+ console.log("");
353
+ const checks = [
354
+ { name: "Gateway configured", pass: Boolean(config.gateway) },
355
+ { name: "Model configured", pass: Boolean(config.agents?.defaults?.model?.primary) },
356
+ { name: "Auth profiles", pass: Object.keys(config.auth?.profiles || {}).length > 0 },
357
+ ];
358
+ if (config.channels?.telegram) {
359
+ checks.push({ name: "Telegram configured", pass: true });
360
+ }
361
+ if (config.channels?.discord) {
362
+ checks.push({ name: "Discord configured", pass: true });
363
+ }
364
+ // Validation result tracking
365
+ for (const check of checks) {
366
+ const icon = check.pass ? theme.success("✓") : theme.warn("⚠");
367
+ console.log(` ${icon} ${check.name}${check.message ? `: ${check.message}` : ""}`);
368
+ // Track failures
369
+ }
370
+ console.log("");
371
+ printSuccess("All validations passed!");
372
+ // Print summary
373
+ console.log("");
374
+ console.log(theme.heading("🎉 Onboarding Complete!"));
375
+ console.log(theme.muted("─────────────────────────────────────────"));
376
+ console.log("");
377
+ console.log("Next steps:");
378
+ console.log("");
379
+ console.log(` 1. Start the gateway: ${theme.command("poolbot gateway run")}`);
380
+ console.log(` 2. Check status: ${theme.command("poolbot status")}`);
381
+ console.log(` 3. Send a message: ${theme.command("poolbot message send 'Hello!'")}`);
382
+ console.log("");
383
+ console.log(`For more help: ${formatDocsLink("/start/getting-started", "docs.molt.bot/start/getting-started")}`);
384
+ console.log("");
385
+ return true;
386
+ }
387
+ // Main onboarding function
388
+ async function runOnboarding(_options) {
389
+ printHeader();
390
+ const steps = [
391
+ { name: "system", description: "System checks", run: runSystemChecks },
392
+ { name: "gateway", description: "Gateway setup", run: runGatewaySetup },
393
+ { name: "models", description: "Model & auth", run: runModelSetup },
394
+ { name: "channels", description: "Channels", run: runChannelSetup },
395
+ { name: "memory", description: "Memory & skills", run: runMemorySkillsSetup },
396
+ { name: "validation", description: "Final validation", run: runFinalValidation },
397
+ ];
398
+ // Step counter
399
+ for (const step of steps) {
400
+ // Next step
401
+ try {
402
+ const success = await step.run(_options);
403
+ if (!success) {
404
+ printError(`Step failed: ${step.name}`);
405
+ console.log("");
406
+ console.log("You can restart onboarding with: poolbot onboard");
407
+ defaultRuntime.exit(1);
408
+ return;
409
+ }
410
+ }
411
+ catch (error) {
412
+ printError(`Step error: ${step.name} - ${error instanceof Error ? error.message : String(error)}`);
413
+ defaultRuntime.exit(1);
414
+ return;
415
+ }
416
+ }
417
+ defaultRuntime.exit(0);
418
+ }
419
+ export function registerOnboardCli(program) {
420
+ program
421
+ .command("onboard")
422
+ .description("Interactive setup wizard for new Pool Bot installations")
423
+ .option("--skip-gateway", "Skip gateway setup")
424
+ .option("--skip-channels", "Skip channel setup")
425
+ .option("--skip-models", "Skip model setup")
426
+ .option("--skip-memory", "Skip memory setup")
427
+ .option("--skip-skills", "Skip skills setup")
428
+ .option("--yes", "Use defaults without prompting (non-interactive)")
429
+ .addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/start/onboarding", "docs.molt.bot/start/onboarding")}\n` +
430
+ `${theme.muted("Tip:")} Use --skip-* flags to skip specific steps\n`)
431
+ .action(async (opts) => {
432
+ try {
433
+ await runOnboarding(opts);
434
+ }
435
+ catch (error) {
436
+ defaultRuntime.error(`Onboarding failed: ${error instanceof Error ? error.message : String(error)}`);
437
+ defaultRuntime.exit(1);
438
+ }
439
+ });
440
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Plugin Config TUI - Interactive plugin configuration prompts
3
+ *
4
+ * Provides interactive prompts for configuring plugins during install/setup.
5
+ * Integrates with existing plugins CLI for guided configuration flows.
6
+ *
7
+ * Usage:
8
+ * import { promptPluginConfig } from "./plugins-config-tui.js";
9
+ * const config = await promptPluginConfig(pluginId, pluginManifest);
10
+ */
11
+ type ConfigField = {
12
+ key: string;
13
+ label: string;
14
+ description?: string;
15
+ type: "string" | "number" | "boolean" | "select" | "secret";
16
+ default?: unknown;
17
+ options?: string[];
18
+ required?: boolean;
19
+ validate?: (value: unknown) => string | null;
20
+ };
21
+ type PluginManifest = {
22
+ name?: string;
23
+ version?: string;
24
+ description?: string;
25
+ poolbot?: {
26
+ config?: {
27
+ fields?: ConfigField[];
28
+ };
29
+ };
30
+ };
31
+ type PluginConfigResult = Record<string, unknown>;
32
+ /**
33
+ * Prompt user for plugin configuration interactively.
34
+ *
35
+ * @param pluginId - Plugin identifier
36
+ * @param manifest - Plugin manifest with config schema
37
+ * @param options - Optional config values (pre-filled)
38
+ * @returns Configured values
39
+ */
40
+ export declare function promptPluginConfig(pluginId: string, manifest: PluginManifest, options?: PluginConfigResult): Promise<PluginConfigResult>;
41
+ /**
42
+ * Display plugin configuration summary.
43
+ */
44
+ export declare function displayPluginConfigSummary(pluginId: string, config: PluginConfigResult): void;
45
+ /**
46
+ * Confirm plugin installation with config summary.
47
+ */
48
+ export declare function confirmPluginInstall(pluginId: string, config: PluginConfigResult): Promise<boolean>;
49
+ export {};
50
+ //# sourceMappingURL=plugins-config-tui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins-config-tui.d.ts","sourceRoot":"","sources":["../../src/cli/plugins-config-tui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,KAAK,WAAW,GAAG;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;CAC9C,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE;YACP,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;SACxB,CAAC;KACH,CAAC;CACH,CAAC;AAEF,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAiIlD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAuD7B;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAsB7F;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,OAAO,CAAC,CAUlB"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Plugin Config TUI - Interactive plugin configuration prompts
3
+ *
4
+ * Provides interactive prompts for configuring plugins during install/setup.
5
+ * Integrates with existing plugins CLI for guided configuration flows.
6
+ *
7
+ * Usage:
8
+ * import { promptPluginConfig } from "./plugins-config-tui.js";
9
+ * const config = await promptPluginConfig(pluginId, pluginManifest);
10
+ */
11
+ import { stdin as input, stdout as output } from "node:process";
12
+ import readline from "node:readline/promises";
13
+ import { isYes } from "../globals.js";
14
+ async function createReadlineInterface() {
15
+ return readline.createInterface({ input, output });
16
+ }
17
+ async function promptText(rl, question, defaultValue) {
18
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
19
+ const answer = (await rl.question(`${question}${suffix}: `)).trim();
20
+ return answer || defaultValue || "";
21
+ }
22
+ async function promptSecret(rl, question) {
23
+ // Note: readline doesn't support hidden input in Node.js without external deps
24
+ // For now, just warn user
25
+ console.log("(Input will be visible - consider using environment variables for secrets)");
26
+ return await promptText(rl, question);
27
+ }
28
+ async function promptNumber(rl, question, defaultValue) {
29
+ const suffix = defaultValue !== undefined ? ` [${defaultValue}]` : "";
30
+ const answer = (await rl.question(`${question}${suffix}: `)).trim();
31
+ if (!answer && defaultValue !== undefined)
32
+ return defaultValue;
33
+ const num = Number.parseInt(answer, 10);
34
+ if (Number.isNaN(num)) {
35
+ console.log("Please enter a valid number");
36
+ return await promptNumber(rl, question, defaultValue);
37
+ }
38
+ return num;
39
+ }
40
+ async function promptBoolean(rl, question, defaultValue) {
41
+ const defaultStr = defaultValue === true ? "Y/n" : defaultValue === false ? "y/N" : "y/n";
42
+ const answer = (await rl.question(`${question} [${defaultStr}]: `)).trim().toLowerCase();
43
+ if (!answer)
44
+ return defaultValue ?? false;
45
+ return answer.startsWith("y");
46
+ }
47
+ async function promptSelect(rl, question, options, defaultValue) {
48
+ console.log(question);
49
+ options.forEach((opt, i) => {
50
+ const defaultMarker = opt === defaultValue ? " (default)" : "";
51
+ console.log(` ${i + 1}. ${opt}${defaultMarker}`);
52
+ });
53
+ const answer = (await rl.question("Select option (number): ")).trim();
54
+ const index = Number.parseInt(answer, 10) - 1;
55
+ if (Number.isNaN(index) || index < 0 || index >= options.length) {
56
+ console.log("Invalid selection");
57
+ return await promptSelect(rl, question, options, defaultValue);
58
+ }
59
+ return options[index];
60
+ }
61
+ async function promptField(rl, field) {
62
+ const { key, label, description, type, default: defaultValue, options, required, validate, } = field;
63
+ if (description) {
64
+ console.log(` ${label}: ${description}`);
65
+ }
66
+ let value;
67
+ switch (type) {
68
+ case "string":
69
+ value = await promptText(rl, label, defaultValue);
70
+ break;
71
+ case "secret":
72
+ value = await promptSecret(rl, label);
73
+ break;
74
+ case "number":
75
+ value = await promptNumber(rl, label, defaultValue);
76
+ break;
77
+ case "boolean":
78
+ value = await promptBoolean(rl, label, defaultValue);
79
+ break;
80
+ case "select":
81
+ if (!options || options.length === 0) {
82
+ throw new Error(`Select field "${key}" has no options`);
83
+ }
84
+ value = await promptSelect(rl, label, options, defaultValue);
85
+ break;
86
+ default:
87
+ throw new Error(`Unknown field type: ${type}`);
88
+ }
89
+ // Validation
90
+ if (required && (value === undefined || value === null || value === "")) {
91
+ console.log(` ⚠️ ${label} is required`);
92
+ return await promptField(rl, field);
93
+ }
94
+ if (validate) {
95
+ const error = validate(value);
96
+ if (error) {
97
+ console.log(` ⚠️ ${error}`);
98
+ return await promptField(rl, field);
99
+ }
100
+ }
101
+ return value;
102
+ }
103
+ /**
104
+ * Prompt user for plugin configuration interactively.
105
+ *
106
+ * @param pluginId - Plugin identifier
107
+ * @param manifest - Plugin manifest with config schema
108
+ * @param options - Optional config values (pre-filled)
109
+ * @returns Configured values
110
+ */
111
+ export async function promptPluginConfig(pluginId, manifest, options) {
112
+ const configFields = manifest.poolbot?.config?.fields || [];
113
+ if (configFields.length === 0) {
114
+ // No config fields - nothing to prompt
115
+ return options || {};
116
+ }
117
+ // Honor global --yes flag
118
+ if (isYes()) {
119
+ // Use defaults or provided options
120
+ const result = { ...options };
121
+ for (const field of configFields) {
122
+ if (!(field.key in result) && field.default !== undefined) {
123
+ result[field.key] = field.default;
124
+ }
125
+ }
126
+ return result;
127
+ }
128
+ console.log("");
129
+ console.log(`Configuring plugin: ${pluginId}`);
130
+ if (manifest.name) {
131
+ console.log(` ${manifest.name}${manifest.version ? ` v${manifest.version}` : ""}`);
132
+ }
133
+ if (manifest.description) {
134
+ console.log(` ${manifest.description}`);
135
+ }
136
+ console.log("");
137
+ console.log("Enter configuration values (press Enter for defaults):");
138
+ console.log("─────────────────────────────────────────");
139
+ const rl = await createReadlineInterface();
140
+ const result = { ...options };
141
+ try {
142
+ for (const field of configFields) {
143
+ // Skip if already provided
144
+ if (field.key in result) {
145
+ continue;
146
+ }
147
+ const value = await promptField(rl, field);
148
+ result[field.key] = value;
149
+ console.log("");
150
+ }
151
+ }
152
+ finally {
153
+ rl.close();
154
+ }
155
+ console.log("─────────────────────────────────────────");
156
+ console.log("Configuration complete!");
157
+ console.log("");
158
+ return result;
159
+ }
160
+ /**
161
+ * Display plugin configuration summary.
162
+ */
163
+ export function displayPluginConfigSummary(pluginId, config) {
164
+ console.log("");
165
+ console.log(`Plugin Configuration: ${pluginId}`);
166
+ console.log("─────────────────────────────────────────");
167
+ const entries = Object.entries(config);
168
+ if (entries.length === 0) {
169
+ console.log(" (no configuration)");
170
+ }
171
+ else {
172
+ for (const [key, value] of entries) {
173
+ const masked = key.toLowerCase().includes("secret") ||
174
+ key.toLowerCase().includes("token") ||
175
+ key.toLowerCase().includes("password") ||
176
+ key.toLowerCase().includes("key")
177
+ ? "****"
178
+ : String(value);
179
+ console.log(` ${key}: ${masked}`);
180
+ }
181
+ }
182
+ console.log("");
183
+ }
184
+ /**
185
+ * Confirm plugin installation with config summary.
186
+ */
187
+ export async function confirmPluginInstall(pluginId, config) {
188
+ displayPluginConfigSummary(pluginId, config);
189
+ const rl = await createReadlineInterface();
190
+ try {
191
+ const answer = (await rl.question("Proceed with installation? [Y/n]: ")).trim().toLowerCase();
192
+ return !answer || answer.startsWith("y");
193
+ }
194
+ finally {
195
+ rl.close();
196
+ }
197
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"register.subclis.d.ts","sourceRoot":"","sources":["../../../src/cli/program/register.subclis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,KAAK,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAElE,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;CAC3B,CAAC;AAyTF,wBAAgB,gBAAgB,IAAI,WAAW,EAAE,CAEhD;AAED,wBAAgB,gCAAgC,IAAI,MAAM,EAAE,CAE3D;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ3F;AAaD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,MAAM,EAAiB,QAkBrF"}
1
+ {"version":3,"file":"register.subclis.d.ts","sourceRoot":"","sources":["../../../src/cli/program/register.subclis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,KAAK,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAElE,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;CAC3B,CAAC;AAkUF,wBAAgB,gBAAgB,IAAI,WAAW,EAAE,CAEhD;AAED,wBAAgB,gCAAgC,IAAI,MAAM,EAAE,CAE3D;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ3F;AAaD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,MAAM,EAAiB,QAkBrF"}
@@ -290,6 +290,15 @@ const entries = [
290
290
  mod.registerInferCli(program);
291
291
  },
292
292
  },
293
+ {
294
+ name: "onboard",
295
+ description: "Interactive setup wizard for new Pool Bot installations",
296
+ hasSubcommands: false,
297
+ register: async (program) => {
298
+ const mod = await import("../onboard-cli.js");
299
+ mod.registerOnboardCli(program);
300
+ },
301
+ },
293
302
  {
294
303
  name: "completion",
295
304
  description: "Generate shell completion script",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolzin/pool-bot",
3
- "version": "2026.4.42",
3
+ "version": "2026.4.44",
4
4
  "description": "🎱 Pool Bot - AI assistant with PLCODE integrations",
5
5
  "keywords": [],
6
6
  "license": "MIT",