@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.
- package/dist/build-info.json +3 -3
- package/dist/cli/infer-cli.d.ts.map +1 -1
- package/dist/cli/onboard-cli.d.ts +20 -0
- package/dist/cli/onboard-cli.d.ts.map +1 -0
- package/dist/cli/onboard-cli.js +440 -0
- package/dist/cli/plugins-config-tui.d.ts +50 -0
- package/dist/cli/plugins-config-tui.d.ts.map +1 -0
- package/dist/cli/plugins-config-tui.js +197 -0
- package/dist/cli/program/register.subclis.d.ts.map +1 -1
- package/dist/cli/program/register.subclis.js +9 -0
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -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,
|
|
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;
|
|
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",
|