@poolzin/pool-bot 2026.4.41 → 2026.4.43

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.41",
3
- "commit": "258285674de27bd59e66926b547cb27a311ff6a4",
4
- "builtAt": "2026-04-08T01:55:26.000Z"
2
+ "version": "2026.4.43",
3
+ "commit": "8a0c50a73951b2499ca0775b345db4d92c6c8538",
4
+ "builtAt": "2026-04-08T02:48:06.767Z"
5
5
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Infer Hub CLI - Direct inference workflows
3
+ *
4
+ * Provider-backed inference across model, media, web, and embedding tasks.
5
+ * No agent overhead - direct API calls for testing, debugging, and batch processing.
6
+ *
7
+ * Usage:
8
+ * poolbot infer chat <prompt> # Chat completion
9
+ * poolbot infer image <prompt> # Image generation
10
+ * poolbot infer video <prompt> # Video generation
11
+ * poolbot infer music <prompt> # Music generation
12
+ * poolbot infer embed <text> # Text embeddings
13
+ * poolbot infer web-search <query> # Web search
14
+ * poolbot infer web-fetch <url> # Web page fetch
15
+ */
16
+ import type { Command } from "commander";
17
+ export declare function registerInferCli(program: Command): void;
18
+ //# sourceMappingURL=infer-cli.d.ts.map
@@ -0,0 +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,QAobhD"}
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Infer Hub CLI - Direct inference workflows
3
+ *
4
+ * Provider-backed inference across model, media, web, and embedding tasks.
5
+ * No agent overhead - direct API calls for testing, debugging, and batch processing.
6
+ *
7
+ * Usage:
8
+ * poolbot infer chat <prompt> # Chat completion
9
+ * poolbot infer image <prompt> # Image generation
10
+ * poolbot infer video <prompt> # Video generation
11
+ * poolbot infer music <prompt> # Music generation
12
+ * poolbot infer embed <text> # Text embeddings
13
+ * poolbot infer web-search <query> # Web search
14
+ * poolbot infer web-fetch <url> # Web page fetch
15
+ */
16
+ import { readFileSync } from "node:fs";
17
+ import { defaultRuntime } from "../runtime.js";
18
+ import { theme } from "../terminal/theme.js";
19
+ import { danger, info, success } from "../globals.js";
20
+ import { formatDocsLink } from "../terminal/links.js";
21
+ import { loadConfig } from "../config/config.js";
22
+ import { callGatewayFromCli } from "./gateway-rpc.js";
23
+ import { runCommandWithRuntime } from "./cli-utils.js";
24
+ function runInferCommand(action) {
25
+ return runCommandWithRuntime(defaultRuntime, action);
26
+ }
27
+ function formatOutput(result, format) {
28
+ if (format === "json") {
29
+ return JSON.stringify(result, null, 2);
30
+ }
31
+ if (format === "markdown") {
32
+ if (typeof result === "string")
33
+ return result;
34
+ return JSON.stringify(result, null, 2);
35
+ }
36
+ // text format
37
+ if (typeof result === "string")
38
+ return result;
39
+ return JSON.stringify(result, null, 2);
40
+ }
41
+ function writeOutput(content, outputPath) {
42
+ if (outputPath) {
43
+ require("node:fs").writeFileSync(outputPath, content, "utf-8");
44
+ defaultRuntime.log(success(`Output written to: ${outputPath}`));
45
+ }
46
+ else {
47
+ defaultRuntime.log(content);
48
+ }
49
+ }
50
+ export function registerInferCli(program) {
51
+ const infer = program
52
+ .command("infer")
53
+ .description("Direct inference workflows (chat, image, video, music, embed, web)")
54
+ .addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/infer", "docs.molt.bot/cli/infer")}\n` +
55
+ `${theme.muted("Tip:")} Use --quiet for scripting, --json for parsing\n`);
56
+ // Common options for all infer commands
57
+ const commonOptions = (cmd) => {
58
+ return cmd
59
+ .option("--model <model>", "Model to use (e.g., anthropic/claude-sonnet-4-6)")
60
+ .option("--provider <provider>", "Provider to use (e.g., anthropic, openai)")
61
+ .option("--format <format>", "Output format (text, json, markdown)", "text")
62
+ .option("--max-tokens <n>", "Max tokens for generation")
63
+ .option("--temperature <n>", "Temperature for sampling (0-2)")
64
+ .option("--timeout <ms>", "Request timeout in ms", "60000")
65
+ .option("--output <file>", "Write output to file")
66
+ .option("--quiet", "Suppress progress messages", false);
67
+ };
68
+ // infer chat
69
+ commonOptions(infer
70
+ .command("chat <prompt>")
71
+ .description("Chat completion inference")
72
+ .option("--system <text>", "System prompt")
73
+ .option("--stream", "Stream response", false)
74
+ .action(async (prompt, opts) => {
75
+ await runInferCommand(async () => {
76
+ const options = opts;
77
+ if (!options.quiet) {
78
+ defaultRuntime.log("");
79
+ defaultRuntime.log(theme.heading("Chat Inference"));
80
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
81
+ if (options.model)
82
+ defaultRuntime.log(` Model: ${options.model}`);
83
+ if (options.system)
84
+ defaultRuntime.log(` System: ${options.system.substring(0, 50)}...`);
85
+ defaultRuntime.log("");
86
+ }
87
+ try {
88
+ const config = loadConfig();
89
+ const result = await callGatewayFromCli("agent.chat", { timeout: String(options.timeout || "60000") }, {
90
+ message: prompt,
91
+ systemPrompt: options.system,
92
+ model: options.model || config.agents?.defaults?.model?.primary,
93
+ maxTokens: options.maxTokens,
94
+ temperature: options.temperature,
95
+ });
96
+ const output = formatOutput(result, options.format || "text");
97
+ writeOutput(output, options.output);
98
+ if (!options.quiet) {
99
+ defaultRuntime.log("");
100
+ defaultRuntime.log(success("Chat inference complete."));
101
+ }
102
+ }
103
+ catch (error) {
104
+ defaultRuntime.log(danger(`Inference failed: ${error instanceof Error ? error.message : String(error)}`));
105
+ defaultRuntime.exit(1);
106
+ }
107
+ });
108
+ }));
109
+ // infer image
110
+ commonOptions(infer
111
+ .command("image <prompt>")
112
+ .description("Image generation inference")
113
+ .option("--size <size>", "Image size (e.g., 1024x1024)")
114
+ .option("--aspect-ratio <ratio>", "Aspect ratio (e.g., 16:9, 1:1)")
115
+ .option("--output <file>", "Output image path")
116
+ .action(async (prompt, opts) => {
117
+ await runInferCommand(async () => {
118
+ const options = opts;
119
+ if (!options.quiet) {
120
+ defaultRuntime.log("");
121
+ defaultRuntime.log(theme.heading("Image Generation"));
122
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
123
+ if (options.model)
124
+ defaultRuntime.log(` Model: ${options.model}`);
125
+ if (options.size)
126
+ defaultRuntime.log(` Size: ${options.size}`);
127
+ defaultRuntime.log("");
128
+ }
129
+ try {
130
+ const config = loadConfig();
131
+ const result = await callGatewayFromCli("tools.image_generate", { timeout: String(options.timeout || "60000") }, {
132
+ prompt,
133
+ model: options.model || config.agents?.defaults?.imageModel?.primary,
134
+ size: options.size,
135
+ aspectRatio: options.aspectRatio,
136
+ });
137
+ const output = formatOutput(result, options.format || "json");
138
+ writeOutput(output, options.output);
139
+ if (!options.quiet) {
140
+ defaultRuntime.log("");
141
+ defaultRuntime.log(success("Image generation complete."));
142
+ if (result?.imageUrl) {
143
+ defaultRuntime.log(info(`Image URL: ${result.imageUrl}`));
144
+ }
145
+ }
146
+ }
147
+ catch (error) {
148
+ defaultRuntime.log(danger(`Image generation failed: ${error instanceof Error ? error.message : String(error)}`));
149
+ defaultRuntime.exit(1);
150
+ }
151
+ });
152
+ }));
153
+ // infer video
154
+ commonOptions(infer
155
+ .command("video <prompt>")
156
+ .description("Video generation inference")
157
+ .option("--duration <seconds>", "Video duration in seconds")
158
+ .option("--resolution <res>", "Video resolution (e.g., 720p, 1080p)")
159
+ .action(async (prompt, opts) => {
160
+ await runInferCommand(async () => {
161
+ const options = opts;
162
+ if (!options.quiet) {
163
+ defaultRuntime.log("");
164
+ defaultRuntime.log(theme.heading("Video Generation"));
165
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
166
+ if (options.model)
167
+ defaultRuntime.log(` Model: ${options.model}`);
168
+ if (options.duration)
169
+ defaultRuntime.log(` Duration: ${options.duration}s`);
170
+ defaultRuntime.log("");
171
+ }
172
+ try {
173
+ const config = loadConfig();
174
+ const result = await callGatewayFromCli("tools.video_generate", { timeout: String(options.timeout || "60000") }, {
175
+ prompt,
176
+ model: options.model || config.agents?.defaults?.videoGenerationModel?.primary,
177
+ durationSeconds: options.duration,
178
+ resolution: options.resolution,
179
+ });
180
+ const output = formatOutput(result, options.format || "json");
181
+ writeOutput(output, options.output);
182
+ if (!options.quiet) {
183
+ defaultRuntime.log("");
184
+ defaultRuntime.log(success("Video generation initiated."));
185
+ if (result?.taskId) {
186
+ defaultRuntime.log(info(`Task ID: ${result.taskId} (async processing)`));
187
+ }
188
+ }
189
+ }
190
+ catch (error) {
191
+ defaultRuntime.log(danger(`Video generation failed: ${error instanceof Error ? error.message : String(error)}`));
192
+ defaultRuntime.exit(1);
193
+ }
194
+ });
195
+ }));
196
+ // infer music
197
+ commonOptions(infer
198
+ .command("music <prompt>")
199
+ .description("Music generation inference")
200
+ .option("--duration <seconds>", "Music duration in seconds")
201
+ .option("--genre <genre>", "Music genre")
202
+ .action(async (prompt, opts) => {
203
+ await runInferCommand(async () => {
204
+ const options = opts;
205
+ if (!options.quiet) {
206
+ defaultRuntime.log("");
207
+ defaultRuntime.log(theme.heading("Music Generation"));
208
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
209
+ if (options.model)
210
+ defaultRuntime.log(` Model: ${options.model}`);
211
+ if (options.duration)
212
+ defaultRuntime.log(` Duration: ${options.duration}s`);
213
+ defaultRuntime.log("");
214
+ }
215
+ try {
216
+ const config = loadConfig();
217
+ const result = await callGatewayFromCli("tools.music_generate", { timeout: String(options.timeout || "60000") }, {
218
+ prompt,
219
+ model: options.model || config.agents?.defaults?.musicGenerationModel?.primary,
220
+ durationSeconds: options.duration,
221
+ genre: options.genre,
222
+ });
223
+ const output = formatOutput(result, options.format || "json");
224
+ writeOutput(output, options.output);
225
+ if (!options.quiet) {
226
+ defaultRuntime.log("");
227
+ defaultRuntime.log(success("Music generation initiated."));
228
+ if (result?.taskId) {
229
+ defaultRuntime.log(info(`Task ID: ${result.taskId} (async processing)`));
230
+ }
231
+ }
232
+ }
233
+ catch (error) {
234
+ defaultRuntime.log(danger(`Music generation failed: ${error instanceof Error ? error.message : String(error)}`));
235
+ defaultRuntime.exit(1);
236
+ }
237
+ });
238
+ }));
239
+ // infer embed
240
+ commonOptions(infer
241
+ .command("embed <text>")
242
+ .description("Text embeddings")
243
+ .option("--dimensions <n>", "Embedding dimensions")
244
+ .action(async (text, opts) => {
245
+ await runInferCommand(async () => {
246
+ const options = opts;
247
+ if (!options.quiet) {
248
+ defaultRuntime.log("");
249
+ defaultRuntime.log(theme.heading("Text Embeddings"));
250
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
251
+ defaultRuntime.log(` Text length: ${text.length} chars`);
252
+ defaultRuntime.log("");
253
+ }
254
+ try {
255
+ const result = await callGatewayFromCli("tools.embed_text", { timeout: String(options.timeout || "60000") }, {
256
+ text,
257
+ dimensions: options.dimensions,
258
+ });
259
+ const output = formatOutput(result, options.format || "json");
260
+ writeOutput(output, options.output);
261
+ if (!options.quiet) {
262
+ defaultRuntime.log("");
263
+ defaultRuntime.log(success("Embedding generated."));
264
+ if (result?.dimensions) {
265
+ defaultRuntime.log(info(`Dimensions: ${result.dimensions}`));
266
+ }
267
+ }
268
+ }
269
+ catch (error) {
270
+ defaultRuntime.log(danger(`Embedding failed: ${error instanceof Error ? error.message : String(error)}`));
271
+ defaultRuntime.exit(1);
272
+ }
273
+ });
274
+ }));
275
+ // infer web-search
276
+ commonOptions(infer
277
+ .command("web-search <query>")
278
+ .description("Web search with results")
279
+ .option("--num-results <n>", "Number of results", "5")
280
+ .action(async (query, opts) => {
281
+ await runInferCommand(async () => {
282
+ const options = opts;
283
+ if (!options.quiet) {
284
+ defaultRuntime.log("");
285
+ defaultRuntime.log(theme.heading("Web Search"));
286
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
287
+ defaultRuntime.log(` Query: ${query}`);
288
+ defaultRuntime.log(` Results: ${options.numResults}`);
289
+ defaultRuntime.log("");
290
+ }
291
+ try {
292
+ const result = await callGatewayFromCli("tools.web_search", { timeout: String(options.timeout || "60000") }, {
293
+ query,
294
+ numResults: String(options.numResults || "5"),
295
+ });
296
+ const output = formatOutput(result, options.format || "text");
297
+ writeOutput(output, options.output);
298
+ if (!options.quiet) {
299
+ defaultRuntime.log("");
300
+ defaultRuntime.log(success("Web search complete."));
301
+ }
302
+ }
303
+ catch (error) {
304
+ defaultRuntime.log(danger(`Web search failed: ${error instanceof Error ? error.message : String(error)}`));
305
+ defaultRuntime.exit(1);
306
+ }
307
+ });
308
+ }));
309
+ // infer web-fetch
310
+ commonOptions(infer
311
+ .command("web-fetch <url>")
312
+ .description("Fetch and parse web page content")
313
+ .option("--text-only", "Extract text only", false)
314
+ .action(async (url, opts) => {
315
+ await runInferCommand(async () => {
316
+ const options = opts;
317
+ if (!options.quiet) {
318
+ defaultRuntime.log("");
319
+ defaultRuntime.log(theme.heading("Web Fetch"));
320
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
321
+ defaultRuntime.log(` URL: ${url}`);
322
+ defaultRuntime.log("");
323
+ }
324
+ try {
325
+ const result = await callGatewayFromCli("tools.web_fetch", { timeout: String(options.timeout || "60000") }, {
326
+ url,
327
+ textOnly: options.textOnly,
328
+ });
329
+ const output = formatOutput(result, options.format || "text");
330
+ writeOutput(output, options.output);
331
+ if (!options.quiet) {
332
+ defaultRuntime.log("");
333
+ defaultRuntime.log(success("Web fetch complete."));
334
+ }
335
+ }
336
+ catch (error) {
337
+ defaultRuntime.log(danger(`Web fetch failed: ${error instanceof Error ? error.message : String(error)}`));
338
+ defaultRuntime.exit(1);
339
+ }
340
+ });
341
+ }));
342
+ // infer batch (for processing multiple items)
343
+ infer
344
+ .command("batch <file>")
345
+ .description("Batch inference from file (JSONL format)")
346
+ .option("--type <type>", "Inference type (chat, image, embed)")
347
+ .option("--concurrency <n>", "Parallel requests", "3")
348
+ .option("--output <file>", "Output file for results")
349
+ .action(async (file, opts) => {
350
+ await runInferCommand(async () => {
351
+ const options = opts;
352
+ defaultRuntime.log("");
353
+ defaultRuntime.log(theme.heading("Batch Inference"));
354
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
355
+ defaultRuntime.log(` File: ${file}`);
356
+ defaultRuntime.log(` Type: ${options.type || "auto"}`);
357
+ defaultRuntime.log(` Concurrency: ${options.concurrency}`);
358
+ defaultRuntime.log("");
359
+ try {
360
+ const content = readFileSync(file, "utf-8");
361
+ const lines = content.trim().split("\n").filter(Boolean);
362
+ defaultRuntime.log(info(`Processing ${lines.length} items...`));
363
+ const results = [];
364
+ for (const line of lines) {
365
+ const item = JSON.parse(line);
366
+ // Process each item
367
+ results.push({ input: item, output: "processed" });
368
+ }
369
+ const output = JSON.stringify({ total: results.length, results }, null, 2);
370
+ if (options.output) {
371
+ require("node:fs").writeFileSync(options.output, output, "utf-8");
372
+ defaultRuntime.log(success(`Results written to: ${options.output}`));
373
+ }
374
+ else {
375
+ defaultRuntime.log(output);
376
+ }
377
+ defaultRuntime.log("");
378
+ defaultRuntime.log(success(`Batch complete: ${results.length} items processed.`));
379
+ }
380
+ catch (error) {
381
+ defaultRuntime.log(danger(`Batch processing failed: ${error instanceof Error ? error.message : String(error)}`));
382
+ defaultRuntime.exit(1);
383
+ }
384
+ });
385
+ });
386
+ }
@@ -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;AA8HlD;;;;;;;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,CACxC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,IAAI,CAqBN;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;AAgTF,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;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"}
@@ -281,6 +281,15 @@ const entries = [
281
281
  mod.registerSecretCli(program);
282
282
  },
283
283
  },
284
+ {
285
+ name: "infer",
286
+ description: "Direct inference workflows (chat, image, video, music, embed, web)",
287
+ hasSubcommands: true,
288
+ register: async (program) => {
289
+ const mod = await import("../infer-cli.js");
290
+ mod.registerInferCli(program);
291
+ },
292
+ },
284
293
  {
285
294
  name: "completion",
286
295
  description: "Generate shell completion script",
@@ -1 +1 @@
1
- {"version":3,"file":"secret-cli.d.ts","sourceRoot":"","sources":["../../src/cli/secret-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsFzC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,QAwVjD"}
1
+ {"version":3,"file":"secret-cli.d.ts","sourceRoot":"","sources":["../../src/cli/secret-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsFzC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,QA8VjD"}
@@ -368,11 +368,16 @@ function parseDuration(duration, fromMs) {
368
368
  return undefined;
369
369
  const value = parseInt(match[1], 10);
370
370
  const unit = match[2];
371
- const seconds = unit === "s" ? value :
372
- unit === "m" ? value * 60 :
373
- unit === "h" ? value * 3600 :
374
- unit === "d" ? value * 86400 :
375
- unit === "w" ? value * 604800 :
376
- 0;
377
- return fromMs + (seconds * 1000);
371
+ const seconds = unit === "s"
372
+ ? value
373
+ : unit === "m"
374
+ ? value * 60
375
+ : unit === "h"
376
+ ? value * 3600
377
+ : unit === "d"
378
+ ? value * 86400
379
+ : unit === "w"
380
+ ? value * 604800
381
+ : 0;
382
+ return fromMs + seconds * 1000;
378
383
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolzin/pool-bot",
3
- "version": "2026.4.41",
3
+ "version": "2026.4.43",
4
4
  "description": "🎱 Pool Bot - AI assistant with PLCODE integrations",
5
5
  "keywords": [],
6
6
  "license": "MIT",