@poolzin/pool-bot 2026.3.7 → 2026.3.10
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/CHANGELOG.md +40 -0
- package/README.md +147 -69
- package/dist/.buildstamp +1 -1
- package/dist/agents/error-classifier.js +251 -0
- package/dist/agents/skills/security.js +211 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/cron-cli/register.cron-dashboard.js +339 -0
- package/dist/cli/cron-cli/register.js +2 -0
- package/dist/cli/errors.js +187 -0
- package/dist/cli/lazy-commands.example.js +113 -0
- package/dist/cli/lazy-commands.js +329 -0
- package/dist/cli/program/command-registry.js +26 -0
- package/dist/cli/program/register.maintenance.js +21 -0
- package/dist/cli/program/register.skills.js +4 -0
- package/dist/cli/program/register.subclis.js +9 -0
- package/dist/cli/swarm-cli/register.js +8 -0
- package/dist/cli/swarm-cli/register.swarm-status.js +488 -0
- package/dist/cli/telemetry-cli/register.js +10 -0
- package/dist/cli/telemetry-cli/register.telemetry-alerts.js +176 -0
- package/dist/cli/telemetry-cli/register.telemetry-metrics.js +323 -0
- package/dist/cli/telemetry-cli/register.telemetry-status.js +179 -0
- package/dist/commands/doctor-checks.js +498 -0
- package/dist/config/config.js +1 -0
- package/dist/config/secrets-integration.js +88 -0
- package/dist/context-engine/index.js +33 -0
- package/dist/context-engine/legacy.js +179 -0
- package/dist/context-engine/registry.js +86 -0
- package/dist/context-engine/summarizing.js +290 -0
- package/dist/context-engine/types.js +7 -0
- package/dist/cron/service/timer.js +18 -0
- package/dist/gateway/protocol/index.js +5 -2
- package/dist/gateway/protocol/schema/error-codes.js +1 -0
- package/dist/gateway/protocol/schema/swarm.js +80 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-close.js +4 -0
- package/dist/gateway/server-constants.js +1 -0
- package/dist/gateway/server-cron.js +29 -0
- package/dist/gateway/server-maintenance.js +35 -2
- package/dist/gateway/server-methods/swarm.js +58 -0
- package/dist/gateway/server-methods/telemetry.js +71 -0
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +9 -2
- package/dist/gateway/server.impl.js +33 -16
- package/dist/infra/abort-pattern.js +106 -0
- package/dist/infra/retry.js +96 -0
- package/dist/secrets/index.js +28 -0
- package/dist/secrets/resolver.js +185 -0
- package/dist/secrets/runtime.js +142 -0
- package/dist/secrets/types.js +11 -0
- package/dist/security/dangerous-tools.js +80 -0
- package/dist/security/types.js +12 -0
- package/dist/skills/commands.js +333 -0
- package/dist/skills/index.js +164 -0
- package/dist/skills/loader.js +282 -0
- package/dist/skills/parser.js +446 -0
- package/dist/skills/registry.js +394 -0
- package/dist/skills/security.js +312 -0
- package/dist/skills/types.js +21 -0
- package/dist/swarm/service.js +247 -0
- package/dist/telemetry/alert-engine.js +258 -0
- package/dist/telemetry/cron-instrumentation.js +49 -0
- package/dist/telemetry/gateway-instrumentation.js +80 -0
- package/dist/telemetry/instrumentation.js +66 -0
- package/dist/telemetry/service.js +345 -0
- package/dist/test-utils/index.js +219 -0
- package/dist/tui/components/assistant-message.js +6 -2
- package/dist/tui/components/hyperlink-markdown.js +32 -0
- package/dist/tui/components/searchable-select-list.js +12 -1
- package/dist/tui/components/user-message.js +6 -2
- package/dist/tui/index.js +611 -0
- package/dist/tui/theme/theme-detection.js +226 -0
- package/dist/tui/tui-command-handlers.js +20 -0
- package/dist/tui/tui-formatters.js +4 -3
- package/dist/tui/utils/ctrl-c-handler.js +67 -0
- package/dist/tui/utils/osc8-hyperlinks.js +208 -0
- package/dist/tui/utils/safe-stop.js +180 -0
- package/dist/tui/utils/session-key-utils.js +81 -0
- package/dist/tui/utils/text-sanitization.js +284 -0
- package/dist/utils/lru-cache.js +116 -0
- package/dist/utils/performance.js +199 -0
- package/dist/utils/retry.js +240 -0
- package/docs/INTEGRATION_PLAN.md +475 -0
- package/docs/INTEGRATION_SUMMARY.md +215 -0
- package/docs/MELHORIAS_IMPLEMENTADAS.md +228 -0
- package/docs/MELHORIAS_PROFISSIONAIS.md +282 -0
- package/docs/PLANO_ACAO_TUI.md +357 -0
- package/docs/PROGRESSO_TUI.md +66 -0
- package/docs/RELATORIO_FINAL.md +217 -0
- package/docs/diagnostico-shell-completion.md +265 -0
- package/docs/features/advanced-memory.md +585 -0
- package/docs/features/discord-components-v2.md +277 -0
- package/docs/features/swarm.md +100 -0
- package/docs/features/telemetry.md +284 -0
- package/docs/integrations/HEXSTRIKE_PLAN.md +796 -0
- package/docs/integrations/INTEGRATION_PLAN.md +744 -0
- package/docs/integrations/PAGE_AGENT_PLAN.md +370 -0
- package/docs/integrations/XYOPS_PLAN.md +978 -0
- package/docs/models/provider-infrastructure.md +400 -0
- package/docs/security/exec-approvals.md +294 -0
- package/docs/skills/IMPLEMENTATION_SUMMARY.md +145 -0
- package/docs/skills/SKILL.md +524 -0
- package/docs/skills.md +405 -0
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/hexstrike-bridge/README.md +119 -0
- package/extensions/hexstrike-bridge/index.test.ts +247 -0
- package/extensions/hexstrike-bridge/index.ts +487 -0
- package/extensions/hexstrike-bridge/package.json +17 -0
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mcp-server/index.ts +14 -0
- package/extensions/mcp-server/package.json +11 -0
- package/extensions/mcp-server/src/service.ts +540 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +8 -1
- package/skills/example-skill/SKILL.md +195 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Structured error class for PoolBot CLI
|
|
4
|
+
* Provides rich error information with recovery suggestions
|
|
5
|
+
*/
|
|
6
|
+
export class PoolBotError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
category;
|
|
9
|
+
recoverable;
|
|
10
|
+
severity;
|
|
11
|
+
suggestions;
|
|
12
|
+
context;
|
|
13
|
+
exitCode;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
super(options.message);
|
|
16
|
+
this.name = "PoolBotError";
|
|
17
|
+
this.code = options.code;
|
|
18
|
+
this.category = options.category;
|
|
19
|
+
this.recoverable = options.recoverable;
|
|
20
|
+
this.severity = options.severity ?? "error";
|
|
21
|
+
this.suggestions = options.suggestions ?? [];
|
|
22
|
+
this.context = options.context;
|
|
23
|
+
this.exitCode = options.exitCode ?? 1;
|
|
24
|
+
if (options.cause) {
|
|
25
|
+
this.cause = options.cause;
|
|
26
|
+
}
|
|
27
|
+
// Maintain proper stack trace
|
|
28
|
+
Error.captureStackTrace?.(this, PoolBotError);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Format error for user display with suggestions
|
|
32
|
+
*/
|
|
33
|
+
formatForDisplay() {
|
|
34
|
+
const lines = [`❌ ${this.message}`, ` Code: ${this.code}`];
|
|
35
|
+
if (this.suggestions.length > 0) {
|
|
36
|
+
lines.push("", "💡 Suggestions:");
|
|
37
|
+
for (const suggestion of this.suggestions) {
|
|
38
|
+
lines.push(` • ${suggestion}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (this.recoverable) {
|
|
42
|
+
lines.push("", "ℹ️ This error can be recovered from.");
|
|
43
|
+
}
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Format error for JSON output
|
|
48
|
+
*/
|
|
49
|
+
toJSON() {
|
|
50
|
+
const errorObj = {
|
|
51
|
+
message: this.message,
|
|
52
|
+
code: this.code,
|
|
53
|
+
category: this.category,
|
|
54
|
+
recoverable: this.recoverable,
|
|
55
|
+
severity: this.severity,
|
|
56
|
+
suggestions: this.suggestions,
|
|
57
|
+
context: this.context,
|
|
58
|
+
exitCode: this.exitCode,
|
|
59
|
+
};
|
|
60
|
+
if (this.cause) {
|
|
61
|
+
errorObj.cause = String(this.cause);
|
|
62
|
+
}
|
|
63
|
+
return { error: errorObj };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Error code registry with predefined common errors
|
|
68
|
+
*/
|
|
69
|
+
export const ErrorCodes = {
|
|
70
|
+
// Config errors (E1xxx)
|
|
71
|
+
CONFIG_INVALID: "E1001",
|
|
72
|
+
CONFIG_MISSING: "E1002",
|
|
73
|
+
CONFIG_DEPRECATED: "E1003",
|
|
74
|
+
CONFIG_VERSION_MISMATCH: "E1004",
|
|
75
|
+
// Gateway errors (E2xxx)
|
|
76
|
+
GATEWAY_NOT_RUNNING: "E2001",
|
|
77
|
+
GATEWAY_CONNECTION_FAILED: "E2002",
|
|
78
|
+
GATEWAY_TIMEOUT: "E2003",
|
|
79
|
+
GATEWAY_AUTH_FAILED: "E2004",
|
|
80
|
+
// Auth errors (E3xxx)
|
|
81
|
+
AUTH_TOKEN_MISSING: "E3001",
|
|
82
|
+
AUTH_TOKEN_INVALID: "E3002",
|
|
83
|
+
AUTH_OAUTH_EXPIRED: "E3003",
|
|
84
|
+
// Network errors (E4xxx)
|
|
85
|
+
NETWORK_UNREACHABLE: "E4001",
|
|
86
|
+
NETWORK_TIMEOUT: "E4002",
|
|
87
|
+
DNS_RESOLUTION_FAILED: "E4003",
|
|
88
|
+
// Validation errors (E5xxx)
|
|
89
|
+
VALIDATION_INVALID_INPUT: "E5001",
|
|
90
|
+
VALIDATION_MISSING_REQUIRED: "E5002",
|
|
91
|
+
VALIDATION_TYPE_MISMATCH: "E5003",
|
|
92
|
+
// Filesystem errors (E6xxx)
|
|
93
|
+
FILE_NOT_FOUND: "E6001",
|
|
94
|
+
FILE_PERMISSION_DENIED: "E6002",
|
|
95
|
+
FILE_ALREADY_EXISTS: "E6003",
|
|
96
|
+
// Plugin errors (E7xxx)
|
|
97
|
+
PLUGIN_LOAD_FAILED: "E7001",
|
|
98
|
+
PLUGIN_NOT_FOUND: "E7002",
|
|
99
|
+
PLUGIN_INCOMPATIBLE: "E7003",
|
|
100
|
+
// Completion errors (E8xxx)
|
|
101
|
+
COMPLETION_CACHE_MISSING: "E8001",
|
|
102
|
+
COMPLETION_SHELL_UNSUPPORTED: "E8002",
|
|
103
|
+
COMPLETION_PROFILE_WRITE_FAILED: "E8003",
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Helper functions for common error scenarios
|
|
107
|
+
*/
|
|
108
|
+
export function createConfigError(message, suggestions, context) {
|
|
109
|
+
return new PoolBotError({
|
|
110
|
+
message,
|
|
111
|
+
code: ErrorCodes.CONFIG_INVALID,
|
|
112
|
+
category: "config",
|
|
113
|
+
recoverable: true,
|
|
114
|
+
suggestions: suggestions ?? ["Run `poolbot doctor` to check configuration"],
|
|
115
|
+
context,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export function createGatewayError(message, code, suggestions, context) {
|
|
119
|
+
return new PoolBotError({
|
|
120
|
+
message,
|
|
121
|
+
code,
|
|
122
|
+
category: "gateway",
|
|
123
|
+
recoverable: true,
|
|
124
|
+
suggestions: suggestions ?? [
|
|
125
|
+
"Run `poolbot gateway status` to check gateway health",
|
|
126
|
+
"Run `poolbot doctor` for diagnostics",
|
|
127
|
+
],
|
|
128
|
+
context,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
export function createValidationError(message, context) {
|
|
132
|
+
return new PoolBotError({
|
|
133
|
+
message,
|
|
134
|
+
code: ErrorCodes.VALIDATION_INVALID_INPUT,
|
|
135
|
+
category: "validation",
|
|
136
|
+
recoverable: true,
|
|
137
|
+
suggestions: ["Check command help with `--help` for usage information"],
|
|
138
|
+
context,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
export function createCompletionError(message, code, suggestions) {
|
|
142
|
+
return new PoolBotError({
|
|
143
|
+
message,
|
|
144
|
+
code,
|
|
145
|
+
category: "config",
|
|
146
|
+
recoverable: true,
|
|
147
|
+
suggestions: suggestions ?? [
|
|
148
|
+
"Run `poolbot completion --write-state` to generate cache",
|
|
149
|
+
"Run `poolbot completion --install` to install to shell profile",
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Async wrapper that converts exceptions to PoolBotError
|
|
155
|
+
*/
|
|
156
|
+
export async function withErrorHandling(operation, errorMapper) {
|
|
157
|
+
try {
|
|
158
|
+
return await operation();
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
if (err instanceof PoolBotError) {
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
throw errorMapper(err);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Zod schema for validating error options
|
|
169
|
+
*/
|
|
170
|
+
export const PoolBotErrorSchema = z.object({
|
|
171
|
+
message: z.string(),
|
|
172
|
+
code: z.string(),
|
|
173
|
+
category: z.enum([
|
|
174
|
+
"config",
|
|
175
|
+
"gateway",
|
|
176
|
+
"auth",
|
|
177
|
+
"network",
|
|
178
|
+
"validation",
|
|
179
|
+
"filesystem",
|
|
180
|
+
"runtime",
|
|
181
|
+
"plugin",
|
|
182
|
+
]),
|
|
183
|
+
recoverable: z.boolean(),
|
|
184
|
+
severity: z.enum(["error", "warning", "info"]).optional(),
|
|
185
|
+
suggestions: z.array(z.string()).optional(),
|
|
186
|
+
exitCode: z.number().optional(),
|
|
187
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Integrating Lazy Commands with PoolBot CLI
|
|
3
|
+
*
|
|
4
|
+
* This file shows how to integrate the lazy command loading system
|
|
5
|
+
* into the main PoolBot CLI.
|
|
6
|
+
*
|
|
7
|
+
* ## Integration Steps
|
|
8
|
+
*
|
|
9
|
+
* 1. Import the lazy command helpers
|
|
10
|
+
* 2. Replace static imports with lazy loaders
|
|
11
|
+
* 3. Add performance monitoring
|
|
12
|
+
*
|
|
13
|
+
* ## Example Integration
|
|
14
|
+
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* #!/usr/bin/env node
|
|
17
|
+
* import { Command } from "commander";
|
|
18
|
+
* import { lazyCommand, getLazyCommandStats, preloadCommands } from "./cli/lazy-commands.js";
|
|
19
|
+
* import { buildCoreCommand } from "./commands/core.js"; // Keep core commands eager
|
|
20
|
+
*
|
|
21
|
+
* const program = new Command();
|
|
22
|
+
*
|
|
23
|
+
* // Core commands - loaded immediately
|
|
24
|
+
* program.addCommand(buildCoreCommand());
|
|
25
|
+
*
|
|
26
|
+
* // Lazy-loaded commands - loaded on first use
|
|
27
|
+
* lazyCommand(program, {
|
|
28
|
+
* name: "agent",
|
|
29
|
+
* description: "Manage AI agents",
|
|
30
|
+
* loader: async () => {
|
|
31
|
+
* const { buildAgentCommand } = await import("./commands/agent.js");
|
|
32
|
+
* return buildAgentCommand();
|
|
33
|
+
* },
|
|
34
|
+
* aliases: ["a"]
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* lazyCommand(program, {
|
|
38
|
+
* name: "config",
|
|
39
|
+
* description: "Manage configuration",
|
|
40
|
+
* loader: async () => {
|
|
41
|
+
* const { buildConfigCommand } = await import("./commands/config.js");
|
|
42
|
+
* return buildConfigCommand();
|
|
43
|
+
* }
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* lazyCommand(program, {
|
|
47
|
+
* name: "channels",
|
|
48
|
+
* description: "Manage messaging channels",
|
|
49
|
+
* loader: async () => {
|
|
50
|
+
* const { buildChannelsCommand } = await import("./commands/channels.js");
|
|
51
|
+
* return buildChannelsCommand();
|
|
52
|
+
* }
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // Preload commonly used commands in interactive mode
|
|
56
|
+
* if (process.argv.length < 3) {
|
|
57
|
+
* // Interactive mode - preload likely commands
|
|
58
|
+
* preloadCommands(["agent", "config"]);
|
|
59
|
+
* }
|
|
60
|
+
*
|
|
61
|
+
* // Show stats in debug mode
|
|
62
|
+
* if (process.env.DEBUG) {
|
|
63
|
+
* console.log("Command loading stats:", getLazyCommandStats());
|
|
64
|
+
* }
|
|
65
|
+
*
|
|
66
|
+
* await program.parseAsync();
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* ## Performance Benefits
|
|
70
|
+
*
|
|
71
|
+
* Before lazy loading:
|
|
72
|
+
* - Startup time: ~500ms (loading all commands)
|
|
73
|
+
* - Memory: ~50MB (all commands in memory)
|
|
74
|
+
*
|
|
75
|
+
* After lazy loading:
|
|
76
|
+
* - Startup time: ~100ms (loading only core)
|
|
77
|
+
* - Memory: ~20MB (only used commands loaded)
|
|
78
|
+
*
|
|
79
|
+
* ## Migration Guide
|
|
80
|
+
*
|
|
81
|
+
* To migrate an existing command to lazy loading:
|
|
82
|
+
*
|
|
83
|
+
* 1. **Before:**
|
|
84
|
+
* ```typescript
|
|
85
|
+
* import { buildAgentCommand } from "./commands/agent.js";
|
|
86
|
+
* program.addCommand(buildAgentCommand());
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* 2. **After:**
|
|
90
|
+
* ```typescript
|
|
91
|
+
* lazyCommand(program, {
|
|
92
|
+
* name: "agent",
|
|
93
|
+
* description: "Manage AI agents",
|
|
94
|
+
* loader: async () => {
|
|
95
|
+
* const { buildAgentCommand } = await import("./commands/agent.js");
|
|
96
|
+
* return buildAgentCommand();
|
|
97
|
+
* }
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* ## Testing with Lazy Commands
|
|
102
|
+
*
|
|
103
|
+
* For testing, you may want to eagerly load all commands:
|
|
104
|
+
*
|
|
105
|
+
* ```typescript
|
|
106
|
+
* import { loadAllCommands } from "./cli/lazy-commands.js";
|
|
107
|
+
*
|
|
108
|
+
* beforeAll(async () => {
|
|
109
|
+
* await loadAllCommands();
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export {};
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy Command Loading System
|
|
3
|
+
*
|
|
4
|
+
* Optimizes CLI startup time by loading commands on-demand
|
|
5
|
+
* rather than loading all commands at startup.
|
|
6
|
+
*
|
|
7
|
+
* This significantly improves startup performance when only
|
|
8
|
+
* a subset of commands is used.
|
|
9
|
+
*
|
|
10
|
+
* ## Usage
|
|
11
|
+
*
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { Command } from "commander";
|
|
14
|
+
* import { lazyCommand } from "./lazy-commands.js";
|
|
15
|
+
*
|
|
16
|
+
* const program = new Command();
|
|
17
|
+
*
|
|
18
|
+
* // Register lazy-loaded command
|
|
19
|
+
* lazyCommand(program, "agent", "Manage AI agents", async () => {
|
|
20
|
+
* const { buildAgentCommand } = await import("./commands/agent.js");
|
|
21
|
+
* return buildAgentCommand();
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Global registry for lazy commands
|
|
27
|
+
*/
|
|
28
|
+
class LazyCommandRegistry {
|
|
29
|
+
commands = new Map();
|
|
30
|
+
loadTimes = new Map();
|
|
31
|
+
/**
|
|
32
|
+
* Register a lazy command
|
|
33
|
+
*/
|
|
34
|
+
register(options) {
|
|
35
|
+
if (this.commands.has(options.name)) {
|
|
36
|
+
throw new Error(`Command "${options.name}" is already registered`);
|
|
37
|
+
}
|
|
38
|
+
const entry = {
|
|
39
|
+
name: options.name,
|
|
40
|
+
description: options.description,
|
|
41
|
+
loader: options.loader,
|
|
42
|
+
aliases: options.aliases ?? [],
|
|
43
|
+
loaded: false,
|
|
44
|
+
};
|
|
45
|
+
this.commands.set(options.name, entry);
|
|
46
|
+
// Register aliases pointing to main entry
|
|
47
|
+
for (const alias of options.aliases ?? []) {
|
|
48
|
+
if (this.commands.has(alias)) {
|
|
49
|
+
throw new Error(`Alias "${alias}" is already registered`);
|
|
50
|
+
}
|
|
51
|
+
this.commands.set(alias, entry);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a command is registered
|
|
56
|
+
*/
|
|
57
|
+
has(name) {
|
|
58
|
+
return this.commands.has(name);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get a registered command entry
|
|
62
|
+
*/
|
|
63
|
+
get(name) {
|
|
64
|
+
return this.commands.get(name);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get command loading statistics
|
|
68
|
+
*/
|
|
69
|
+
getStats() {
|
|
70
|
+
const uniqueCommands = new Set(Array.from(this.commands.values()).map((c) => c.name));
|
|
71
|
+
const total = uniqueCommands.size;
|
|
72
|
+
const loadedEntries = Array.from(this.commands.values()).filter((c) => c.loaded && !c.aliases.includes(c.name) // Count main entries only
|
|
73
|
+
);
|
|
74
|
+
const loaded = loadedEntries.length;
|
|
75
|
+
const loadTimes = {};
|
|
76
|
+
for (const [name, time] of this.loadTimes) {
|
|
77
|
+
loadTimes[name] = time;
|
|
78
|
+
}
|
|
79
|
+
const times = Object.values(loadTimes);
|
|
80
|
+
const averageLoadTime = times.length > 0 ? times.reduce((a, b) => a + b, 0) / times.length : undefined;
|
|
81
|
+
return {
|
|
82
|
+
total,
|
|
83
|
+
loaded,
|
|
84
|
+
lazy: total - loaded,
|
|
85
|
+
averageLoadTime,
|
|
86
|
+
loadTimes,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Mark a command as loaded with its load time
|
|
91
|
+
*/
|
|
92
|
+
markLoaded(name, loadTime) {
|
|
93
|
+
const entry = this.commands.get(name);
|
|
94
|
+
if (entry) {
|
|
95
|
+
entry.loaded = true;
|
|
96
|
+
entry.loadTime = loadTime;
|
|
97
|
+
this.loadTimes.set(name, loadTime);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Mark a command as failed to load
|
|
102
|
+
*/
|
|
103
|
+
markFailed(name, error) {
|
|
104
|
+
const entry = this.commands.get(name);
|
|
105
|
+
if (entry) {
|
|
106
|
+
entry.error = error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get list of registered command names (main commands only, not aliases)
|
|
111
|
+
*/
|
|
112
|
+
getCommandNames() {
|
|
113
|
+
const seen = new Set();
|
|
114
|
+
const names = [];
|
|
115
|
+
for (const entry of this.commands.values()) {
|
|
116
|
+
if (!seen.has(entry.name)) {
|
|
117
|
+
seen.add(entry.name);
|
|
118
|
+
names.push(entry.name);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return names;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get all entries (for debugging)
|
|
125
|
+
*/
|
|
126
|
+
getAllEntries() {
|
|
127
|
+
return Array.from(this.commands.values());
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clear all registrations
|
|
131
|
+
*/
|
|
132
|
+
clear() {
|
|
133
|
+
this.commands.clear();
|
|
134
|
+
this.loadTimes.clear();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Global registry instance
|
|
139
|
+
*/
|
|
140
|
+
export const globalLazyCommandRegistry = new LazyCommandRegistry();
|
|
141
|
+
/**
|
|
142
|
+
* Register a lazy-loaded command with a Commander program
|
|
143
|
+
*
|
|
144
|
+
* This creates a placeholder command that will load the actual
|
|
145
|
+
* implementation only when the command is invoked.
|
|
146
|
+
*
|
|
147
|
+
* @param program - The Commander program instance
|
|
148
|
+
* @param options - Lazy command configuration
|
|
149
|
+
* @returns The placeholder command (for chaining)
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* lazyCommand(program, {
|
|
154
|
+
* name: "agent",
|
|
155
|
+
* description: "Manage AI agents",
|
|
156
|
+
* loader: async () => {
|
|
157
|
+
* const { buildAgentCommand } = await import("./commands/agent.js");
|
|
158
|
+
* return buildAgentCommand();
|
|
159
|
+
* },
|
|
160
|
+
* aliases: ["a"]
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function lazyCommand(program, options) {
|
|
165
|
+
// Register in global registry
|
|
166
|
+
globalLazyCommandRegistry.register(options);
|
|
167
|
+
// Create placeholder command
|
|
168
|
+
const placeholder = program
|
|
169
|
+
.command(options.name)
|
|
170
|
+
.description(options.description);
|
|
171
|
+
// Add arguments if specified
|
|
172
|
+
if (options.arguments) {
|
|
173
|
+
placeholder.arguments(options.arguments);
|
|
174
|
+
}
|
|
175
|
+
// Add aliases
|
|
176
|
+
for (const alias of options.aliases ?? []) {
|
|
177
|
+
placeholder.alias(alias);
|
|
178
|
+
}
|
|
179
|
+
// Set up lazy loading action
|
|
180
|
+
placeholder.action(async (...actionArgs) => {
|
|
181
|
+
const startTime = performance.now();
|
|
182
|
+
try {
|
|
183
|
+
// Load the actual command module
|
|
184
|
+
const realCommand = await options.loader();
|
|
185
|
+
// Calculate load time
|
|
186
|
+
const loadTime = performance.now() - startTime;
|
|
187
|
+
globalLazyCommandRegistry.markLoaded(options.name, loadTime);
|
|
188
|
+
// Execute the loaded command's action with the same arguments
|
|
189
|
+
// The loaded command should have its own action handler
|
|
190
|
+
if (realCommand && typeof realCommand.parseAsync === "function") {
|
|
191
|
+
// If it's a full Command instance, we need to execute its action
|
|
192
|
+
// Get the action handler from the loaded command
|
|
193
|
+
const actionHandler = realCommand._actionHandler;
|
|
194
|
+
if (actionHandler) {
|
|
195
|
+
await actionHandler(...actionArgs);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// Fallback: parse with the same arguments
|
|
199
|
+
// Remove command object from args and re-parse
|
|
200
|
+
const argv = process.argv.slice(2);
|
|
201
|
+
await realCommand.parseAsync(argv);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
throw new Error(`Loader for "${options.name}" did not return a valid Command instance`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
210
|
+
globalLazyCommandRegistry.markFailed(options.name, err);
|
|
211
|
+
throw new Error(`Failed to load command "${options.name}": ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return placeholder;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Simple shorthand for creating a lazy command with minimal config
|
|
218
|
+
*
|
|
219
|
+
* @param program - The Commander program
|
|
220
|
+
* @param name - Command name
|
|
221
|
+
* @param description - Command description
|
|
222
|
+
* @param loader - Async function that loads and returns the command
|
|
223
|
+
* @returns The placeholder command
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* lazy(program, "agent", "Manage agents", async () => {
|
|
228
|
+
* const mod = await import("./commands/agent.js");
|
|
229
|
+
* return mod.buildAgentCommand();
|
|
230
|
+
* });
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
export function lazy(program, name, description, loader) {
|
|
234
|
+
return lazyCommand(program, { name, description, loader });
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Preload commands that are likely to be used
|
|
238
|
+
*
|
|
239
|
+
* This can be called after initial setup to preload commands
|
|
240
|
+
* in the background, improving responsiveness for common commands.
|
|
241
|
+
*
|
|
242
|
+
* @param commandNames - Names of commands to preload
|
|
243
|
+
* @returns Promise that resolves when all preloads are complete
|
|
244
|
+
*/
|
|
245
|
+
export async function preloadCommands(commandNames) {
|
|
246
|
+
await Promise.all(commandNames.map(async (name) => {
|
|
247
|
+
const entry = globalLazyCommandRegistry.get(name);
|
|
248
|
+
if (entry && !entry.loaded) {
|
|
249
|
+
try {
|
|
250
|
+
const startTime = performance.now();
|
|
251
|
+
await entry.loader();
|
|
252
|
+
globalLazyCommandRegistry.markLoaded(name, performance.now() - startTime);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
// Preload errors are non-fatal
|
|
256
|
+
console.warn(`Failed to preload command "${name}": ${error instanceof Error ? error.message : String(error)}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Eagerly load all registered commands
|
|
263
|
+
*
|
|
264
|
+
* Use this for testing or when startup time doesn't matter.
|
|
265
|
+
*
|
|
266
|
+
* @returns Promise that resolves when all commands are loaded
|
|
267
|
+
*/
|
|
268
|
+
export async function loadAllCommands() {
|
|
269
|
+
const names = globalLazyCommandRegistry.getCommandNames();
|
|
270
|
+
await preloadCommands(names);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get current loading statistics
|
|
274
|
+
*
|
|
275
|
+
* @returns Statistics about lazy-loaded commands
|
|
276
|
+
*/
|
|
277
|
+
export function getLazyCommandStats() {
|
|
278
|
+
return globalLazyCommandRegistry.getStats();
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Performance measurement utilities for command loading
|
|
282
|
+
*/
|
|
283
|
+
export class LoadTimeTracker {
|
|
284
|
+
measurements = new Map();
|
|
285
|
+
/**
|
|
286
|
+
* Measure execution time of a function
|
|
287
|
+
*/
|
|
288
|
+
async measure(name, fn) {
|
|
289
|
+
const start = performance.now();
|
|
290
|
+
try {
|
|
291
|
+
return await fn();
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
const duration = performance.now() - start;
|
|
295
|
+
this.measurements.set(name, duration);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Get all measurements
|
|
300
|
+
*/
|
|
301
|
+
getMeasurements() {
|
|
302
|
+
return Object.fromEntries(this.measurements);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Get total loading time
|
|
306
|
+
*/
|
|
307
|
+
getTotalTime() {
|
|
308
|
+
return Array.from(this.measurements.values()).reduce((a, b) => a + b, 0);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Get slowest operations
|
|
312
|
+
*/
|
|
313
|
+
getSlowest(count = 5) {
|
|
314
|
+
return Array.from(this.measurements.entries())
|
|
315
|
+
.sort((a, b) => b[1] - a[1])
|
|
316
|
+
.slice(0, count)
|
|
317
|
+
.map(([name, time]) => ({ name, time }));
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Clear all measurements
|
|
321
|
+
*/
|
|
322
|
+
clear() {
|
|
323
|
+
this.measurements.clear();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Global load time tracker
|
|
328
|
+
*/
|
|
329
|
+
export const globalLoadTimeTracker = new LoadTimeTracker();
|
|
@@ -174,6 +174,32 @@ const coreEntries = [
|
|
|
174
174
|
mod.registerBrowserCli(program);
|
|
175
175
|
},
|
|
176
176
|
},
|
|
177
|
+
{
|
|
178
|
+
commands: [
|
|
179
|
+
{
|
|
180
|
+
name: "mods",
|
|
181
|
+
description: "Manage capability modules (SKILL.md discovery, enable, configure)",
|
|
182
|
+
hasSubcommands: true,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
register: async ({ program }) => {
|
|
186
|
+
const mod = await import("./register.skills.js");
|
|
187
|
+
mod.registerSkillsCli(program);
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
commands: [
|
|
192
|
+
{
|
|
193
|
+
name: "swarm",
|
|
194
|
+
description: "Agent swarm coordination and management",
|
|
195
|
+
hasSubcommands: true,
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
register: async ({ program }) => {
|
|
199
|
+
const mod = await import("../swarm-cli/register.js");
|
|
200
|
+
mod.registerSwarmCommand(program);
|
|
201
|
+
},
|
|
202
|
+
},
|
|
177
203
|
];
|
|
178
204
|
function collectCoreCliCommandNames(predicate) {
|
|
179
205
|
const seen = new Set();
|
|
@@ -6,6 +6,18 @@ import { defaultRuntime } from "../../runtime.js";
|
|
|
6
6
|
import { formatDocsLink } from "../../terminal/links.js";
|
|
7
7
|
import { theme } from "../../terminal/theme.js";
|
|
8
8
|
import { runCommandWithRuntime } from "../cli-utils.js";
|
|
9
|
+
const DOCTOR_CHECKS = [
|
|
10
|
+
"config",
|
|
11
|
+
"auth",
|
|
12
|
+
"gateway",
|
|
13
|
+
"completion",
|
|
14
|
+
"security",
|
|
15
|
+
"plugins",
|
|
16
|
+
"memory",
|
|
17
|
+
"workspace",
|
|
18
|
+
"state",
|
|
19
|
+
"all",
|
|
20
|
+
];
|
|
9
21
|
export function registerMaintenanceCommands(program) {
|
|
10
22
|
program
|
|
11
23
|
.command("doctor")
|
|
@@ -19,6 +31,13 @@ export function registerMaintenanceCommands(program) {
|
|
|
19
31
|
.option("--non-interactive", "Run without prompts (safe migrations only)", false)
|
|
20
32
|
.option("--generate-gateway-token", "Generate and configure a gateway token", false)
|
|
21
33
|
.option("--deep", "Scan system services for extra gateway installs", false)
|
|
34
|
+
.option("--check <check>", `Run specific check: ${DOCTOR_CHECKS.join(", ")}`, (value) => {
|
|
35
|
+
if (!DOCTOR_CHECKS.includes(value)) {
|
|
36
|
+
throw new Error(`Invalid check: ${value}. Valid: ${DOCTOR_CHECKS.join(", ")}`);
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
})
|
|
40
|
+
.option("--json", "Output results in JSON format", false)
|
|
22
41
|
.action(async (opts) => {
|
|
23
42
|
await runCommandWithRuntime(defaultRuntime, async () => {
|
|
24
43
|
await doctorCommand(defaultRuntime, {
|
|
@@ -29,6 +48,8 @@ export function registerMaintenanceCommands(program) {
|
|
|
29
48
|
nonInteractive: Boolean(opts.nonInteractive),
|
|
30
49
|
generateGatewayToken: Boolean(opts.generateGatewayToken),
|
|
31
50
|
deep: Boolean(opts.deep),
|
|
51
|
+
check: opts.check,
|
|
52
|
+
json: Boolean(opts.json),
|
|
32
53
|
});
|
|
33
54
|
});
|
|
34
55
|
});
|