@auto-engineer/cli 0.19.1 → 0.21.0
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 +24 -4
- package/dist/src/__fixtures__/auto.config.d.ts +7 -0
- package/dist/src/__fixtures__/auto.config.d.ts.map +1 -0
- package/dist/src/__fixtures__/auto.config.js +7 -0
- package/dist/src/__fixtures__/auto.config.js.map +1 -0
- package/dist/src/dsl/index.d.ts +71 -0
- package/dist/src/dsl/index.d.ts.map +1 -0
- package/dist/src/dsl/index.js +504 -0
- package/dist/src/dsl/index.js.map +1 -0
- package/dist/src/dsl/types.d.ts +39 -0
- package/dist/src/dsl/types.d.ts.map +1 -0
- package/dist/src/dsl/types.js +2 -0
- package/dist/src/dsl/types.js.map +1 -0
- package/dist/src/dsl-exports.d.ts +5 -0
- package/dist/src/dsl-exports.d.ts.map +1 -0
- package/dist/src/dsl-exports.js +4 -0
- package/dist/src/dsl-exports.js.map +1 -0
- package/dist/src/index.js +16 -201
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugin-loader.d.ts +84 -0
- package/dist/src/plugin-loader.d.ts.map +1 -0
- package/dist/src/plugin-loader.js +788 -0
- package/dist/src/plugin-loader.js.map +1 -0
- package/dist/src/server/command-metadata-service.d.ts +27 -0
- package/dist/src/server/command-metadata-service.d.ts.map +1 -0
- package/dist/src/server/command-metadata-service.js +69 -0
- package/dist/src/server/command-metadata-service.js.map +1 -0
- package/dist/src/server/command-registry.d.ts +21 -0
- package/dist/src/server/command-registry.d.ts.map +1 -0
- package/dist/src/server/command-registry.js +58 -0
- package/dist/src/server/command-registry.js.map +1 -0
- package/dist/src/server/config-loader.d.ts +16 -0
- package/dist/src/server/config-loader.d.ts.map +1 -0
- package/dist/src/server/config-loader.js +106 -0
- package/dist/src/server/config-loader.js.map +1 -0
- package/dist/src/server/event-processor.d.ts +45 -0
- package/dist/src/server/event-processor.d.ts.map +1 -0
- package/dist/src/server/event-processor.js +287 -0
- package/dist/src/server/event-processor.js.map +1 -0
- package/dist/src/server/file-syncer/crypto/jwe-encryptor.d.ts +15 -0
- package/dist/src/server/file-syncer/crypto/jwe-encryptor.d.ts.map +1 -0
- package/dist/src/server/file-syncer/crypto/jwe-encryptor.js +72 -0
- package/dist/src/server/file-syncer/crypto/jwe-encryptor.js.map +1 -0
- package/dist/src/server/file-syncer/crypto/provider-resolver.d.ts +24 -0
- package/dist/src/server/file-syncer/crypto/provider-resolver.d.ts.map +1 -0
- package/dist/src/server/file-syncer/crypto/provider-resolver.js +71 -0
- package/dist/src/server/file-syncer/crypto/provider-resolver.js.map +1 -0
- package/dist/src/server/file-syncer/discovery/bareImports.d.ts +3 -0
- package/dist/src/server/file-syncer/discovery/bareImports.d.ts.map +1 -0
- package/dist/src/server/file-syncer/discovery/bareImports.js +36 -0
- package/dist/src/server/file-syncer/discovery/bareImports.js.map +1 -0
- package/dist/src/server/file-syncer/discovery/dts.d.ts +11 -0
- package/dist/src/server/file-syncer/discovery/dts.d.ts.map +1 -0
- package/dist/src/server/file-syncer/discovery/dts.js +108 -0
- package/dist/src/server/file-syncer/discovery/dts.js.map +1 -0
- package/dist/src/server/file-syncer/index.d.ts +22 -0
- package/dist/src/server/file-syncer/index.d.ts.map +1 -0
- package/dist/src/server/file-syncer/index.js +378 -0
- package/dist/src/server/file-syncer/index.js.map +1 -0
- package/dist/src/server/file-syncer/sync/resolveSyncFileSet.d.ts +7 -0
- package/dist/src/server/file-syncer/sync/resolveSyncFileSet.d.ts.map +1 -0
- package/dist/src/server/file-syncer/sync/resolveSyncFileSet.js +103 -0
- package/dist/src/server/file-syncer/sync/resolveSyncFileSet.js.map +1 -0
- package/dist/src/server/file-syncer/types/wire.d.ts +14 -0
- package/dist/src/server/file-syncer/types/wire.d.ts.map +1 -0
- package/dist/src/server/file-syncer/types/wire.js +2 -0
- package/dist/src/server/file-syncer/types/wire.js.map +1 -0
- package/dist/src/server/file-syncer/utils/hash.d.ts +5 -0
- package/dist/src/server/file-syncer/utils/hash.d.ts.map +1 -0
- package/dist/src/server/file-syncer/utils/hash.js +20 -0
- package/dist/src/server/file-syncer/utils/hash.js.map +1 -0
- package/dist/src/server/file-syncer/utils/path.d.ts +15 -0
- package/dist/src/server/file-syncer/utils/path.d.ts.map +1 -0
- package/dist/src/server/file-syncer/utils/path.js +109 -0
- package/dist/src/server/file-syncer/utils/path.js.map +1 -0
- package/dist/src/server/http-routes.d.ts +30 -0
- package/dist/src/server/http-routes.d.ts.map +1 -0
- package/dist/src/server/http-routes.js +394 -0
- package/dist/src/server/http-routes.js.map +1 -0
- package/dist/src/server/sandbox-landing-page.html +367 -0
- package/dist/src/server/server.d.ts +106 -0
- package/dist/src/server/server.d.ts.map +1 -0
- package/dist/src/server/server.js +255 -0
- package/dist/src/server/server.js.map +1 -0
- package/dist/src/server/services/child-process-manager.d.ts +27 -0
- package/dist/src/server/services/child-process-manager.d.ts.map +1 -0
- package/dist/src/server/services/child-process-manager.js +126 -0
- package/dist/src/server/services/child-process-manager.js.map +1 -0
- package/dist/src/server/services/index.d.ts +3 -0
- package/dist/src/server/services/index.d.ts.map +1 -0
- package/dist/src/server/services/index.js +2 -0
- package/dist/src/server/services/index.js.map +1 -0
- package/dist/src/server/services/interface.d.ts +6 -0
- package/dist/src/server/services/interface.d.ts.map +1 -0
- package/dist/src/server/services/interface.js +2 -0
- package/dist/src/server/services/interface.js.map +1 -0
- package/dist/src/server/settled-tracker.d.ts +29 -0
- package/dist/src/server/settled-tracker.d.ts.map +1 -0
- package/dist/src/server/settled-tracker.js +203 -0
- package/dist/src/server/settled-tracker.js.map +1 -0
- package/dist/src/server/state-manager.d.ts +24 -0
- package/dist/src/server/state-manager.d.ts.map +1 -0
- package/dist/src/server/state-manager.js +56 -0
- package/dist/src/server/state-manager.js.map +1 -0
- package/dist/src/server/websocket-handler.d.ts +5 -0
- package/dist/src/server/websocket-handler.d.ts.map +1 -0
- package/dist/src/server/websocket-handler.js +40 -0
- package/dist/src/server/websocket-handler.js.map +1 -0
- package/dist/src/server.d.ts +20 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +204 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/utils/analytics.d.ts +21 -0
- package/dist/src/utils/analytics.d.ts.map +1 -0
- package/dist/src/utils/analytics.js +41 -0
- package/dist/src/utils/analytics.js.map +1 -0
- package/dist/src/utils/config.d.ts +11 -0
- package/dist/src/utils/config.d.ts.map +1 -0
- package/dist/src/utils/config.js +50 -0
- package/dist/src/utils/config.js.map +1 -0
- package/dist/src/utils/correlation-id.d.ts +3 -0
- package/dist/src/utils/correlation-id.d.ts.map +1 -0
- package/dist/src/utils/correlation-id.js +7 -0
- package/dist/src/utils/correlation-id.js.map +1 -0
- package/dist/src/utils/errors.d.ts +22 -0
- package/dist/src/utils/errors.d.ts.map +1 -0
- package/dist/src/utils/errors.js +50 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/get-package-version.d.ts +7 -0
- package/dist/src/utils/get-package-version.d.ts.map +1 -0
- package/dist/src/utils/get-package-version.js +31 -0
- package/dist/src/utils/get-package-version.js.map +1 -0
- package/dist/src/utils/terminal.d.ts +13 -0
- package/dist/src/utils/terminal.d.ts.map +1 -0
- package/dist/src/utils/terminal.js +85 -0
- package/dist/src/utils/terminal.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -4
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
import createJiti from 'jiti';
|
|
5
|
+
import createDebug from 'debug';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { createMessageBus } from '@auto-engineer/message-bus';
|
|
8
|
+
const debug = createDebug('auto:cli:plugin-loader');
|
|
9
|
+
const debugConfig = createDebug('auto:cli:plugin-loader:config');
|
|
10
|
+
const debugPlugins = createDebug('auto:cli:plugin-loader:plugins');
|
|
11
|
+
const debugConflicts = createDebug('auto:cli:plugin-loader:conflicts');
|
|
12
|
+
const debugBus = createDebug('auto:cli:plugin-loader:bus');
|
|
13
|
+
// Set non-error-like colors (avoid red/orange)
|
|
14
|
+
// Colors: 0=gray, 1=red, 2=green, 3=yellow, 4=blue, 5=magenta, 6=cyan
|
|
15
|
+
debug.color = '4'; // blue
|
|
16
|
+
debugConfig.color = '6'; // cyan
|
|
17
|
+
debugPlugins.color = '2'; // green
|
|
18
|
+
debugConflicts.color = '3'; // yellow (ok for conflicts)
|
|
19
|
+
debugBus.color = '4'; // blue
|
|
20
|
+
export class PluginLoader {
|
|
21
|
+
constructor(host) {
|
|
22
|
+
this.host = host;
|
|
23
|
+
this.commands = new Map();
|
|
24
|
+
this.unifiedHandlers = new Map(); // Store original handlers
|
|
25
|
+
this.conflicts = new Map(); // alias -> [package1, package2, ...]
|
|
26
|
+
this.loadedPlugins = new Set(); // Track successfully loaded plugins
|
|
27
|
+
this.aliasToHandlerName = new Map(); // Map CLI alias to handler name
|
|
28
|
+
this.hasGlobalEventSubscription = false; // Track if we've set up global event subscription
|
|
29
|
+
this.commandMappers = new Map(); // Dynamic command mappers
|
|
30
|
+
this.messageBus = createMessageBus();
|
|
31
|
+
this.setupGlobalEventSubscription();
|
|
32
|
+
}
|
|
33
|
+
setHost(host) {
|
|
34
|
+
this.host = host;
|
|
35
|
+
}
|
|
36
|
+
setupGlobalEventSubscription() {
|
|
37
|
+
if (this.hasGlobalEventSubscription)
|
|
38
|
+
return;
|
|
39
|
+
this.messageBus.subscribeAll({
|
|
40
|
+
name: 'CLI_GlobalEventLogger',
|
|
41
|
+
handle: async (event) => {
|
|
42
|
+
debugBus('CLI received event: %s', event.type);
|
|
43
|
+
this.handleCommandEvent(event.type, event);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
this.hasGlobalEventSubscription = true;
|
|
47
|
+
debugBus('Global event subscription set up');
|
|
48
|
+
}
|
|
49
|
+
isValidHandler(item) {
|
|
50
|
+
return item !== null && item !== undefined && typeof item === 'object' && 'handle' in item && 'name' in item;
|
|
51
|
+
}
|
|
52
|
+
findCommandHandler(module) {
|
|
53
|
+
// Look for the command handler - prefer default export
|
|
54
|
+
const handler = module.default ?? module.handler;
|
|
55
|
+
// If default is a valid handler, use it
|
|
56
|
+
if (this.isValidHandler(handler) ||
|
|
57
|
+
(handler !== null && handler !== undefined && typeof handler === 'object' && 'handle' in handler)) {
|
|
58
|
+
return handler;
|
|
59
|
+
}
|
|
60
|
+
// Otherwise, look for named exports
|
|
61
|
+
for (const key of Object.keys(module)) {
|
|
62
|
+
const exportedItem = module[key];
|
|
63
|
+
if (this.isValidHandler(exportedItem)) {
|
|
64
|
+
debugBus('Using named export %s as handler', key);
|
|
65
|
+
return exportedItem;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return handler; // Return whatever we found even if not valid
|
|
69
|
+
}
|
|
70
|
+
async loadConfigFile(configPath) {
|
|
71
|
+
try {
|
|
72
|
+
if (this.loadConfig) {
|
|
73
|
+
// Use test override
|
|
74
|
+
return await this.loadConfig(configPath);
|
|
75
|
+
}
|
|
76
|
+
// Handle TypeScript config files
|
|
77
|
+
if (configPath.endsWith('.ts')) {
|
|
78
|
+
debugConfig('Loading TypeScript config file with jiti');
|
|
79
|
+
const jiti = createJiti(import.meta.url, {
|
|
80
|
+
interopDefault: true,
|
|
81
|
+
});
|
|
82
|
+
const config = await jiti.import(configPath);
|
|
83
|
+
debugConfig('TypeScript config loaded successfully');
|
|
84
|
+
return config;
|
|
85
|
+
}
|
|
86
|
+
// Import JavaScript config file directly
|
|
87
|
+
const configUrl = pathToFileURL(path.resolve(configPath)).href;
|
|
88
|
+
debugConfig('Importing JavaScript config from: %s', configUrl);
|
|
89
|
+
const configModule = (await import(configUrl));
|
|
90
|
+
return (configModule.default ?? configModule);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
debugConfig('Error loading config: %O', error);
|
|
94
|
+
// Only show config loading errors when debugging
|
|
95
|
+
if (process.env.DEBUG?.includes('auto-engineer:') === true) {
|
|
96
|
+
console.error(`Failed to load config from ${configPath}:`, error);
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async loadPlugin(packageName) {
|
|
102
|
+
try {
|
|
103
|
+
// First, try loading from workspace packages (for monorepo)
|
|
104
|
+
const workspaceDistPath = path.join(process.cwd(), 'packages', packageName.replace('@auto-engineer/', ''), 'dist', 'index.js');
|
|
105
|
+
if (fs.existsSync(workspaceDistPath)) {
|
|
106
|
+
const packageUrl = pathToFileURL(workspaceDistPath).href;
|
|
107
|
+
debugPlugins('Loading plugin from workspace: %s', packageUrl);
|
|
108
|
+
return await import(packageUrl);
|
|
109
|
+
}
|
|
110
|
+
// Try loading from node_modules dist/index.js (most packages)
|
|
111
|
+
const distIndexPath = path.join(process.cwd(), 'node_modules', packageName, 'dist', 'index.js');
|
|
112
|
+
if (fs.existsSync(distIndexPath)) {
|
|
113
|
+
const packageUrl = pathToFileURL(distIndexPath).href;
|
|
114
|
+
debugPlugins('Loading plugin from node_modules dist/index.js: %s', packageUrl);
|
|
115
|
+
return await import(packageUrl);
|
|
116
|
+
}
|
|
117
|
+
// Try dist/src/index.js (some packages have src in dist)
|
|
118
|
+
const distSrcIndexPath = path.join(process.cwd(), 'node_modules', packageName, 'dist', 'src', 'index.js');
|
|
119
|
+
if (fs.existsSync(distSrcIndexPath)) {
|
|
120
|
+
const packageUrl = pathToFileURL(distSrcIndexPath).href;
|
|
121
|
+
debugPlugins('Loading plugin from node_modules dist/src/index.js: %s', packageUrl);
|
|
122
|
+
return await import(packageUrl);
|
|
123
|
+
}
|
|
124
|
+
throw new Error('Package index file not found');
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
debugPlugins('Failed to load from file system, trying default import: %s', e);
|
|
128
|
+
return this.importPlugin ? await this.importPlugin(packageName) : await import(packageName);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async loadPackageMetadata(packageName) {
|
|
132
|
+
try {
|
|
133
|
+
// Try loading package.json from workspace packages (for monorepo)
|
|
134
|
+
const workspacePackageJsonPath = path.join(process.cwd(), 'packages', packageName.replace('@auto-engineer/', ''), 'package.json');
|
|
135
|
+
if (fs.existsSync(workspacePackageJsonPath)) {
|
|
136
|
+
const packageJson = JSON.parse(fs.readFileSync(workspacePackageJsonPath, 'utf-8'));
|
|
137
|
+
return {
|
|
138
|
+
name: packageJson.name,
|
|
139
|
+
version: packageJson.version,
|
|
140
|
+
description: packageJson.description,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Try loading from dist directory (published packages)
|
|
144
|
+
const distPackageJsonPath = path.join(process.cwd(), 'packages', packageName.replace('@auto-engineer/', ''), 'dist', 'package.json');
|
|
145
|
+
if (fs.existsSync(distPackageJsonPath)) {
|
|
146
|
+
const packageJson = JSON.parse(fs.readFileSync(distPackageJsonPath, 'utf-8'));
|
|
147
|
+
return {
|
|
148
|
+
name: packageJson.name,
|
|
149
|
+
version: packageJson.version,
|
|
150
|
+
description: packageJson.description,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Try loading from node_modules
|
|
154
|
+
const nodeModulesPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
|
|
155
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
156
|
+
const packageJson = JSON.parse(fs.readFileSync(nodeModulesPath, 'utf-8'));
|
|
157
|
+
return {
|
|
158
|
+
name: packageJson.name,
|
|
159
|
+
version: packageJson.version,
|
|
160
|
+
description: packageJson.description,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
debugPlugins('Failed to load package metadata for %s: %O', packageName, error);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async convertUnifiedToCliCommand(handler, packageName) {
|
|
171
|
+
// All fields become options (no positional args anymore)
|
|
172
|
+
const options = [];
|
|
173
|
+
for (const [fieldName, fieldDef] of Object.entries(handler.fields)) {
|
|
174
|
+
if (fieldDef.flag === true) {
|
|
175
|
+
// Boolean flag option
|
|
176
|
+
const flagName = `--${fieldName.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, '')}`;
|
|
177
|
+
options.push({
|
|
178
|
+
name: flagName,
|
|
179
|
+
description: fieldDef.description,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Regular option
|
|
184
|
+
const optName = `--${fieldName.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, '')}`;
|
|
185
|
+
options.push({
|
|
186
|
+
name: `${optName} <value>`,
|
|
187
|
+
description: fieldDef.description,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Generate usage string without positional args
|
|
192
|
+
const usage = handler.alias;
|
|
193
|
+
// Load package metadata from package.json instead of using hardcoded values
|
|
194
|
+
const packageMetadata = await this.loadPackageMetadata(packageName);
|
|
195
|
+
const resolvedPackageName = packageMetadata?.name ?? packageName;
|
|
196
|
+
const packageVersion = packageMetadata?.version;
|
|
197
|
+
return {
|
|
198
|
+
handler: () => Promise.resolve({ default: handler }),
|
|
199
|
+
description: handler.description,
|
|
200
|
+
package: resolvedPackageName,
|
|
201
|
+
version: packageVersion,
|
|
202
|
+
category: handler.category ?? resolvedPackageName,
|
|
203
|
+
usage,
|
|
204
|
+
examples: handler.examples,
|
|
205
|
+
args: undefined, // No positional args
|
|
206
|
+
options: options.length > 0 ? options : undefined,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async detectCommandsByConvention(packageName) {
|
|
210
|
+
debugPlugins('Attempting convention-based command detection for %s', packageName);
|
|
211
|
+
const commands = [];
|
|
212
|
+
try {
|
|
213
|
+
// Determine the commands directory path
|
|
214
|
+
let commandsDir;
|
|
215
|
+
const workspaceDistPath = path.join(process.cwd(), 'packages', packageName.replace('@auto-engineer/', ''), 'dist', 'commands');
|
|
216
|
+
const nodeModulesDistPath = path.join(process.cwd(), 'node_modules', packageName, 'dist', 'commands');
|
|
217
|
+
const nodeModulesSrcDistPath = path.join(process.cwd(), 'node_modules', packageName, 'dist', 'src', 'commands');
|
|
218
|
+
if (fs.existsSync(workspaceDistPath)) {
|
|
219
|
+
commandsDir = workspaceDistPath;
|
|
220
|
+
debugPlugins('Found commands directory in workspace: %s', commandsDir);
|
|
221
|
+
}
|
|
222
|
+
else if (fs.existsSync(nodeModulesDistPath)) {
|
|
223
|
+
commandsDir = nodeModulesDistPath;
|
|
224
|
+
debugPlugins('Found commands directory in node_modules dist: %s', commandsDir);
|
|
225
|
+
}
|
|
226
|
+
else if (fs.existsSync(nodeModulesSrcDistPath)) {
|
|
227
|
+
commandsDir = nodeModulesSrcDistPath;
|
|
228
|
+
debugPlugins('Found commands directory in node_modules dist/src: %s', commandsDir);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
debugPlugins('No commands directory found for %s', packageName);
|
|
232
|
+
return commands;
|
|
233
|
+
}
|
|
234
|
+
// Find all .js files in commands directory
|
|
235
|
+
const commandFiles = fs.readdirSync(commandsDir).filter((file) => file.endsWith('.js'));
|
|
236
|
+
debugPlugins('Found %d command files in %s: %o', commandFiles.length, commandsDir, commandFiles);
|
|
237
|
+
// Load each command file and extract handlers
|
|
238
|
+
for (const filename of commandFiles) {
|
|
239
|
+
const filePath = path.join(commandsDir, filename);
|
|
240
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
241
|
+
try {
|
|
242
|
+
debugPlugins('Loading command file: %s', fileUrl);
|
|
243
|
+
const module = (await import(fileUrl));
|
|
244
|
+
// Look for exports that are command handlers
|
|
245
|
+
const handler = this.extractCommandHandler(module, filename, packageName);
|
|
246
|
+
if (handler) {
|
|
247
|
+
commands.push(handler);
|
|
248
|
+
debugPlugins('Found command handler in %s: %s', filename, handler.alias);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
debugPlugins('Failed to load command file %s: %O', filename, error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
debugPlugins('Convention-based detection found %d commands in %s', commands.length, packageName);
|
|
256
|
+
return commands;
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
debugPlugins('Error during convention-based detection for %s: %O', packageName, error);
|
|
260
|
+
return commands;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
extractCommandHandler(module, filename, packageName) {
|
|
264
|
+
// Look for default export first
|
|
265
|
+
if (this.isValidHandler(module.default)) {
|
|
266
|
+
return module.default;
|
|
267
|
+
}
|
|
268
|
+
// Look for commandHandler export
|
|
269
|
+
if (this.isValidHandler(module.commandHandler)) {
|
|
270
|
+
return module.commandHandler;
|
|
271
|
+
}
|
|
272
|
+
// Look for handler export
|
|
273
|
+
if (this.isValidHandler(module.handler)) {
|
|
274
|
+
return module.handler;
|
|
275
|
+
}
|
|
276
|
+
// Look for any export that looks like a command handler
|
|
277
|
+
for (const [key, value] of Object.entries(module)) {
|
|
278
|
+
if (this.isValidHandler(value)) {
|
|
279
|
+
debugPlugins('Found command handler via named export %s in %s', key, filename);
|
|
280
|
+
return value;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
debugPlugins('No valid command handler found in %s from %s', filename, packageName);
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
async processUnifiedCommands(packageName, commands, aliasMap) {
|
|
287
|
+
debugPlugins('Processing %d unified commands from %s', commands.length, packageName);
|
|
288
|
+
this.loadedPlugins.add(packageName);
|
|
289
|
+
for (const unifiedHandler of commands) {
|
|
290
|
+
const alias = unifiedHandler.alias;
|
|
291
|
+
debugPlugins('Processing unified command %s from %s', alias, packageName);
|
|
292
|
+
// Load package metadata from package.json and update handler
|
|
293
|
+
const packageMetadata = await this.loadPackageMetadata(packageName);
|
|
294
|
+
const enrichedHandler = {
|
|
295
|
+
...unifiedHandler,
|
|
296
|
+
package: packageMetadata
|
|
297
|
+
? {
|
|
298
|
+
name: packageMetadata.name,
|
|
299
|
+
version: packageMetadata.version,
|
|
300
|
+
description: packageMetadata.description,
|
|
301
|
+
}
|
|
302
|
+
: undefined,
|
|
303
|
+
};
|
|
304
|
+
// Store the enriched unified handler
|
|
305
|
+
this.unifiedHandlers.set(alias, enrichedHandler);
|
|
306
|
+
// Convert unified handler to CLI command format (now async)
|
|
307
|
+
const cliCommand = await this.convertUnifiedToCliCommand(enrichedHandler, packageName);
|
|
308
|
+
if (!aliasMap.has(alias)) {
|
|
309
|
+
aliasMap.set(alias, []);
|
|
310
|
+
}
|
|
311
|
+
aliasMap.get(alias).push({
|
|
312
|
+
packageName,
|
|
313
|
+
command: cliCommand,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async processPlugin(packageName, aliasMap) {
|
|
318
|
+
debugPlugins('Loading plugin: %s', packageName);
|
|
319
|
+
try {
|
|
320
|
+
const pkg = (await this.loadPlugin(packageName));
|
|
321
|
+
// Check for unified command format first (backward compatibility)
|
|
322
|
+
if (pkg.COMMANDS) {
|
|
323
|
+
await this.processUnifiedCommands(packageName, pkg.COMMANDS, aliasMap);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// Try convention-based command detection
|
|
327
|
+
const detectedCommands = await this.detectCommandsByConvention(packageName);
|
|
328
|
+
if (detectedCommands.length > 0) {
|
|
329
|
+
await this.processUnifiedCommands(packageName, detectedCommands, aliasMap);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
debugPlugins('Package %s has no commands (no COMMANDS export or commands/ directory)', packageName);
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
debugPlugins('Failed to load plugin %s: %O', packageName, error);
|
|
336
|
+
// Only show plugin loading errors when debugging
|
|
337
|
+
if (process.env.DEBUG?.includes('auto-engineer:') === true) {
|
|
338
|
+
console.warn(`Failed to load plugin ${packageName}:`, error);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async findMatchingCandidate(candidates, userOverride) {
|
|
343
|
+
for (const candidate of candidates) {
|
|
344
|
+
try {
|
|
345
|
+
const cmd = candidate.command;
|
|
346
|
+
const module = (await cmd.handler());
|
|
347
|
+
if (module === userOverride ||
|
|
348
|
+
module.default === userOverride ||
|
|
349
|
+
module.handler === userOverride ||
|
|
350
|
+
module.default?.handler === userOverride) {
|
|
351
|
+
return candidate;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch (e) {
|
|
355
|
+
debugConflicts('Error checking handler match: %o', e);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
async processConflictResolution(alias, candidates, config) {
|
|
361
|
+
const packages = candidates.map((c) => c.packageName);
|
|
362
|
+
debugConflicts('Conflict detected for alias "%s" between packages: %o', alias, packages);
|
|
363
|
+
const userOverride = config.aliases?.[alias];
|
|
364
|
+
if (userOverride === undefined) {
|
|
365
|
+
this.conflicts.set(alias, packages);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const isFunction = typeof userOverride === 'function';
|
|
369
|
+
const hasDefault = typeof userOverride.default === 'function';
|
|
370
|
+
if (!isFunction && !hasDefault) {
|
|
371
|
+
console.error(`\n❌ Invalid override for alias "${alias}"`);
|
|
372
|
+
console.error(` Expected a command handler function, got: ${typeof userOverride}`);
|
|
373
|
+
console.error(` Import the handler from one of: ${packages.join(', ')}`);
|
|
374
|
+
this.conflicts.set(alias, packages);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const matchingCandidate = await this.findMatchingCandidate(candidates, userOverride);
|
|
378
|
+
if (matchingCandidate !== null) {
|
|
379
|
+
debugConflicts('Using user override for %s with handler from %s', alias, matchingCandidate.packageName);
|
|
380
|
+
await this.registerCommand(alias, matchingCandidate);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
console.error(`\n❌ Invalid override for alias "${alias}"`);
|
|
384
|
+
console.error(` The provided handler does not match any of the conflicting commands`);
|
|
385
|
+
console.error(` Available packages: ${packages.join(', ')}`);
|
|
386
|
+
this.conflicts.set(alias, packages);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
processStringAlias(alias, target) {
|
|
390
|
+
if (!this.commands.has(target))
|
|
391
|
+
return;
|
|
392
|
+
debugPlugins('Creating alias %s -> %s', alias, target);
|
|
393
|
+
const existingCommand = this.commands.get(target);
|
|
394
|
+
this.commands.set(alias, {
|
|
395
|
+
...existingCommand,
|
|
396
|
+
description: `Alias for ${target}`,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
processHandlerAlias(alias, target) {
|
|
400
|
+
const t = target;
|
|
401
|
+
debugPlugins('Registering custom alias %s with CommandHandler', alias);
|
|
402
|
+
const commandEntry = {
|
|
403
|
+
handler: async () => ({ default: target }),
|
|
404
|
+
description: t.name !== undefined && t.name !== '' ? `${t.name} command` : `Custom command: ${alias}`,
|
|
405
|
+
package: 'custom-alias',
|
|
406
|
+
};
|
|
407
|
+
this.commands.set(alias, commandEntry);
|
|
408
|
+
}
|
|
409
|
+
processFunctionAlias(alias, target) {
|
|
410
|
+
debugPlugins('Registering custom alias %s with handler function', alias);
|
|
411
|
+
const handlerFunc = typeof target === 'function' ? target : target.default;
|
|
412
|
+
this.commands.set(alias, {
|
|
413
|
+
handler: async () => ({ default: handlerFunc }),
|
|
414
|
+
description: `Custom command: ${alias}`,
|
|
415
|
+
package: 'custom-alias',
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
processCustomAlias(alias, target) {
|
|
419
|
+
if (typeof target === 'string') {
|
|
420
|
+
this.processStringAlias(alias, target);
|
|
421
|
+
}
|
|
422
|
+
else if (target !== null &&
|
|
423
|
+
target !== undefined &&
|
|
424
|
+
typeof target.handle === 'function') {
|
|
425
|
+
this.processHandlerAlias(alias, target);
|
|
426
|
+
}
|
|
427
|
+
else if (typeof target === 'function' ||
|
|
428
|
+
(target !== null && target !== undefined && typeof target.default === 'function')) {
|
|
429
|
+
this.processFunctionAlias(alias, target);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async processCustomAliases(config, aliasMap) {
|
|
433
|
+
if (config.aliases === undefined)
|
|
434
|
+
return;
|
|
435
|
+
for (const [alias, target] of Object.entries(config.aliases)) {
|
|
436
|
+
// Skip if this was already processed as a conflict resolution
|
|
437
|
+
if (!aliasMap.has(alias)) {
|
|
438
|
+
this.processCustomAlias(alias, target);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
reportConflicts(config) {
|
|
443
|
+
if (this.conflicts.size === 0)
|
|
444
|
+
return;
|
|
445
|
+
console.error('\n❌ Command alias conflicts detected!\n');
|
|
446
|
+
console.error('Multiple packages are trying to register the same command aliases.');
|
|
447
|
+
console.error('Please add alias overrides to your auto.config.ts file:\n');
|
|
448
|
+
console.error('// Import the specific handler you want to use');
|
|
449
|
+
for (const [alias, packages] of this.conflicts.entries()) {
|
|
450
|
+
console.error(`// For "${alias}", import from one of: ${packages.join(', ')}`);
|
|
451
|
+
const examplePkg = packages[0];
|
|
452
|
+
const handlerName = alias.replace(/[:-]/g, '_') + 'Handler';
|
|
453
|
+
console.error(`import { ${handlerName} } from '${examplePkg}';`);
|
|
454
|
+
}
|
|
455
|
+
console.error('');
|
|
456
|
+
console.error('export default {');
|
|
457
|
+
console.error(' plugins: [');
|
|
458
|
+
for (const plugin of config.plugins ?? []) {
|
|
459
|
+
console.error(` '${plugin}',`);
|
|
460
|
+
}
|
|
461
|
+
console.error(' ],');
|
|
462
|
+
console.error(' aliases: {');
|
|
463
|
+
for (const [alias] of this.conflicts.entries()) {
|
|
464
|
+
const handlerName = alias.replace(/[:-]/g, '_') + 'Handler';
|
|
465
|
+
console.error(` '${alias}': ${handlerName},`);
|
|
466
|
+
}
|
|
467
|
+
console.error(' }');
|
|
468
|
+
console.error('};\n');
|
|
469
|
+
throw new Error('Unresolved command alias conflicts. Please update your configuration.');
|
|
470
|
+
}
|
|
471
|
+
async loadPlugins(configPath) {
|
|
472
|
+
debug('Loading plugins from config: %s', configPath);
|
|
473
|
+
// Check if config file exists (skip for testing)
|
|
474
|
+
if (!this.loadConfig && !fs.existsSync(configPath)) {
|
|
475
|
+
debugConfig('Config file not found, using default empty config');
|
|
476
|
+
return this.commands;
|
|
477
|
+
}
|
|
478
|
+
const config = await this.loadConfigFile(configPath);
|
|
479
|
+
if (!config) {
|
|
480
|
+
return this.commands;
|
|
481
|
+
}
|
|
482
|
+
debugConfig('Config loaded with %d plugins', config.plugins?.length ?? 0);
|
|
483
|
+
// Load each plugin
|
|
484
|
+
const aliasMap = new Map();
|
|
485
|
+
for (const packageName of config.plugins ?? []) {
|
|
486
|
+
await this.processPlugin(packageName, aliasMap);
|
|
487
|
+
}
|
|
488
|
+
// Process aliases and detect conflicts
|
|
489
|
+
for (const [alias, candidates] of aliasMap.entries()) {
|
|
490
|
+
if (candidates.length === 1) {
|
|
491
|
+
// No conflict, register the command
|
|
492
|
+
const candidate = candidates[0];
|
|
493
|
+
debugPlugins('Registering command %s from %s', alias, candidate.packageName);
|
|
494
|
+
await this.registerCommand(alias, candidate);
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
// Conflict detected
|
|
498
|
+
await this.processConflictResolution(alias, candidates, config);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// Process custom aliases (new command names mapping to handlers or existing commands)
|
|
502
|
+
await this.processCustomAliases(config, aliasMap);
|
|
503
|
+
// Report unresolved conflicts
|
|
504
|
+
this.reportConflicts(config);
|
|
505
|
+
debug('Plugin loading complete. Registered %d commands from %d plugins', this.commands.size, this.loadedPlugins.size);
|
|
506
|
+
return this.commands;
|
|
507
|
+
}
|
|
508
|
+
getLoadedPluginCount() {
|
|
509
|
+
return this.loadedPlugins.size;
|
|
510
|
+
}
|
|
511
|
+
getCommandMapper(alias) {
|
|
512
|
+
return this.commandMappers.get(alias);
|
|
513
|
+
}
|
|
514
|
+
buildCommandMapper(alias, command, unifiedHandler) {
|
|
515
|
+
const mapper = (args, options) => {
|
|
516
|
+
const commandData = {};
|
|
517
|
+
if (unifiedHandler !== undefined && unifiedHandler.fields !== undefined) {
|
|
518
|
+
// Handle unified command format - all fields are options now
|
|
519
|
+
const fieldsArray = Object.entries(unifiedHandler.fields);
|
|
520
|
+
// Map all options
|
|
521
|
+
fieldsArray.forEach(([fieldName]) => {
|
|
522
|
+
const kebabName = fieldName.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, '');
|
|
523
|
+
if (options[kebabName] !== undefined) {
|
|
524
|
+
let value = options[kebabName];
|
|
525
|
+
// Special handling for flowFiles - convert string to array
|
|
526
|
+
if (fieldName === 'flowFiles' && typeof value === 'string') {
|
|
527
|
+
value = [value];
|
|
528
|
+
}
|
|
529
|
+
commandData[fieldName] = value;
|
|
530
|
+
}
|
|
531
|
+
else if (options[fieldName] !== undefined) {
|
|
532
|
+
let value = options[fieldName];
|
|
533
|
+
// Special handling for flowFiles - convert string to array
|
|
534
|
+
if (fieldName === 'flowFiles' && typeof value === 'string') {
|
|
535
|
+
value = [value];
|
|
536
|
+
}
|
|
537
|
+
commandData[fieldName] = value;
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
// Fall back to old format
|
|
543
|
+
// Map positional arguments based on the command's args definition
|
|
544
|
+
if (command.args) {
|
|
545
|
+
command.args.forEach((argDef, index) => {
|
|
546
|
+
if (args[index] !== undefined) {
|
|
547
|
+
// Convert arg name to camelCase for the data property
|
|
548
|
+
const propName = argDef.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
549
|
+
commandData[propName] = args[index];
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
// Map options
|
|
554
|
+
if (command.options) {
|
|
555
|
+
command.options.forEach((optDef) => {
|
|
556
|
+
// Extract option name from format like "--fix" or "--scope <scope>"
|
|
557
|
+
const optMatch = optDef.name.match(/^--([a-zA-Z-]+)/);
|
|
558
|
+
if (optMatch) {
|
|
559
|
+
const optName = optMatch[1].replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
560
|
+
if (options[optName] !== undefined || options[optMatch[1]] !== undefined) {
|
|
561
|
+
commandData[optName] = options[optName] ?? options[optMatch[1]];
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// Handle special cases for variadic arguments
|
|
568
|
+
if (alias === 'generate:ia' && command.args && command.args.length > 1) {
|
|
569
|
+
commandData.outputDir = args[0];
|
|
570
|
+
commandData.flowFiles = Array.isArray(args[1]) ? args[1] : args.slice(1);
|
|
571
|
+
}
|
|
572
|
+
return commandData;
|
|
573
|
+
};
|
|
574
|
+
this.commandMappers.set(alias, mapper);
|
|
575
|
+
debugPlugins('Built command mapper for %s', alias);
|
|
576
|
+
}
|
|
577
|
+
async registerCommand(alias, candidate) {
|
|
578
|
+
// Store the command for CLI use with all metadata
|
|
579
|
+
this.commands.set(alias, {
|
|
580
|
+
handler: candidate.command.handler,
|
|
581
|
+
description: candidate.command.description,
|
|
582
|
+
package: candidate.packageName,
|
|
583
|
+
version: candidate.command.version,
|
|
584
|
+
usage: candidate.command.usage,
|
|
585
|
+
examples: candidate.command.examples,
|
|
586
|
+
args: candidate.command.args,
|
|
587
|
+
options: candidate.command.options,
|
|
588
|
+
category: candidate.command.category,
|
|
589
|
+
});
|
|
590
|
+
debugBus('Loading command handler for %s', alias);
|
|
591
|
+
try {
|
|
592
|
+
// Load the actual handler from the plugin
|
|
593
|
+
const module = (await candidate.command.handler());
|
|
594
|
+
debugBus('Loaded module for %s', alias);
|
|
595
|
+
// Look for the command handler
|
|
596
|
+
const handler = this.findCommandHandler(module);
|
|
597
|
+
if (handler === null || handler === undefined || typeof handler !== 'object' || !('handle' in handler)) {
|
|
598
|
+
debugBus('Available exports: %o', Object.keys(module));
|
|
599
|
+
debugBus('Default export type: %s', typeof module.default);
|
|
600
|
+
debugBus('Default export value: %o', module.default);
|
|
601
|
+
throw new Error(`No valid CommandHandler found for command ${alias} in ${candidate.packageName}`);
|
|
602
|
+
}
|
|
603
|
+
const commandHandler = handler;
|
|
604
|
+
// Build and store command mapper based on args and options
|
|
605
|
+
// Pass unified handler if it has fields property
|
|
606
|
+
const unifiedHandler = 'fields' in handler && handler.fields !== undefined
|
|
607
|
+
? handler
|
|
608
|
+
: undefined;
|
|
609
|
+
this.buildCommandMapper(alias, candidate.command, unifiedHandler);
|
|
610
|
+
// Store mapping from alias to handler name
|
|
611
|
+
this.aliasToHandlerName.set(alias, commandHandler.name);
|
|
612
|
+
// Register with the message bus using the handler's name
|
|
613
|
+
debugBus('Registering command handler %s with message bus', commandHandler.name);
|
|
614
|
+
this.messageBus.registerCommandHandler(commandHandler);
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
debugBus('Error registering command %s: %O', alias, error);
|
|
618
|
+
throw error;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
formatFieldKey(key) {
|
|
622
|
+
return key.replace(/([A-Z])/g, ' $1').replace(/^./, (str) => str.toUpperCase());
|
|
623
|
+
}
|
|
624
|
+
displayArrayValue(value, logFn) {
|
|
625
|
+
logFn(` ${value.length} items`);
|
|
626
|
+
if (typeof value[0] === 'string') {
|
|
627
|
+
value.slice(0, 3).forEach((item) => logFn(` - ${String(item)}`));
|
|
628
|
+
if (value.length > 3) {
|
|
629
|
+
logFn(` ... and ${value.length - 3} more`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
displayFieldValue(key, value, isError) {
|
|
634
|
+
const formattedKey = this.formatFieldKey(key);
|
|
635
|
+
const logFn = isError ? console.error : console.log;
|
|
636
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
637
|
+
logFn(` ${formattedKey}: ${String(value)}`);
|
|
638
|
+
}
|
|
639
|
+
else if (Array.isArray(value) && value.length > 0) {
|
|
640
|
+
logFn(` ${formattedKey}:`);
|
|
641
|
+
this.displayArrayValue(value, logFn);
|
|
642
|
+
}
|
|
643
|
+
else if (typeof value === 'object' && value !== null) {
|
|
644
|
+
logFn(` ${formattedKey}: [object]`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
handleCommandEvent(eventType, event) {
|
|
648
|
+
// For all command/event types, use minimal display - no verbose data
|
|
649
|
+
this.displayMinimalMessage(event);
|
|
650
|
+
}
|
|
651
|
+
displayMinimalMessage(message) {
|
|
652
|
+
// For non-error commands/events, show minimal output (just timestamp and type)
|
|
653
|
+
// Only show detailed parameters for debug mode
|
|
654
|
+
debugBus('Event data: %O', message.data);
|
|
655
|
+
const date = new Date(message.timestamp || Date.now());
|
|
656
|
+
const timestamp = date.toTimeString().split(' ')[0] + '.' + date.getMilliseconds().toString().padStart(3, '0');
|
|
657
|
+
const isCommand = 'type' in message && !('correlationId' in message);
|
|
658
|
+
const backgroundColor = isCommand ? '#00CED1' : '#FF6B35';
|
|
659
|
+
console.log(chalk.gray(timestamp), chalk.bgHex(backgroundColor).white.bold(` ${message.type} `));
|
|
660
|
+
}
|
|
661
|
+
displayMessage(message) {
|
|
662
|
+
// Redirect all messages to minimal display - verbose data only available via debug
|
|
663
|
+
this.displayMinimalMessage(message);
|
|
664
|
+
}
|
|
665
|
+
highlightYaml(yamlStr) {
|
|
666
|
+
// Apply syntax highlighting and indentation
|
|
667
|
+
const highlightedYaml = yamlStr
|
|
668
|
+
.split('\n')
|
|
669
|
+
.filter((line) => line.trim()) // Remove empty lines
|
|
670
|
+
.map((line) => {
|
|
671
|
+
// Apply syntax highlighting
|
|
672
|
+
let highlighted = line;
|
|
673
|
+
// Highlight keys (word before colon)
|
|
674
|
+
highlighted = highlighted.replace(/^(\s*)([a-zA-Z0-9_-]+)(:)/g, (match, indent, key, colon) => {
|
|
675
|
+
return indent + chalk.cyanBright(key) + chalk.gray(colon);
|
|
676
|
+
});
|
|
677
|
+
// Highlight string values (in quotes)
|
|
678
|
+
highlighted = highlighted.replace(/(["'])((?:\\.|(?!\1).)*?)\1/g, (match, quote, content) => {
|
|
679
|
+
return chalk.gray(quote) + chalk.green(content) + chalk.gray(quote);
|
|
680
|
+
});
|
|
681
|
+
// Highlight numbers
|
|
682
|
+
highlighted = highlighted.replace(/:\s*(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\s*$/g, (match, num) => {
|
|
683
|
+
return chalk.gray(':') + ' ' + chalk.yellow(num);
|
|
684
|
+
});
|
|
685
|
+
// Highlight booleans
|
|
686
|
+
highlighted = highlighted.replace(/:\s*(true|false)\s*$/g, (match, bool) => {
|
|
687
|
+
return chalk.gray(':') + ' ' + chalk.magenta(bool);
|
|
688
|
+
});
|
|
689
|
+
// Highlight null
|
|
690
|
+
highlighted = highlighted.replace(/:\s*(null)\s*$/g, (match, nullVal) => {
|
|
691
|
+
return chalk.gray(':') + ' ' + chalk.gray(nullVal);
|
|
692
|
+
});
|
|
693
|
+
// Highlight array markers
|
|
694
|
+
highlighted = highlighted.replace(/^(\s*)(- )/g, (match, indent, marker) => {
|
|
695
|
+
return indent + chalk.gray(marker);
|
|
696
|
+
});
|
|
697
|
+
return ` ${highlighted}`;
|
|
698
|
+
})
|
|
699
|
+
.join('\n');
|
|
700
|
+
return highlightedYaml;
|
|
701
|
+
}
|
|
702
|
+
displayErrorField(data) {
|
|
703
|
+
if (data.error !== undefined && data.error !== null) {
|
|
704
|
+
console.error(` Error: ${String(data.error)}`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
displayErrorsField(data) {
|
|
708
|
+
if (data.errors === undefined || data.errors === null)
|
|
709
|
+
return;
|
|
710
|
+
if (typeof data.errors === 'string') {
|
|
711
|
+
console.error(` Errors: ${data.errors}`);
|
|
712
|
+
}
|
|
713
|
+
else if (Array.isArray(data.errors)) {
|
|
714
|
+
console.error(` Errors: ${data.errors.length}`);
|
|
715
|
+
data.errors.slice(0, 3).forEach((err) => console.error(` - ${String(err)}`));
|
|
716
|
+
if (data.errors.length > 3) {
|
|
717
|
+
console.error(` ... and ${data.errors.length - 3} more`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
async executeCommand(commandAlias, data) {
|
|
722
|
+
debugBus('Executing command %s', commandAlias);
|
|
723
|
+
// Get the actual handler name from the alias
|
|
724
|
+
const handlerName = this.aliasToHandlerName.get(commandAlias) ?? commandAlias;
|
|
725
|
+
debugBus('Mapped alias %s to handler %s', commandAlias, handlerName);
|
|
726
|
+
// Create command object that matches the Command interface
|
|
727
|
+
const command = {
|
|
728
|
+
type: handlerName,
|
|
729
|
+
data: data,
|
|
730
|
+
timestamp: new Date(),
|
|
731
|
+
requestId: `cli-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
|
|
732
|
+
};
|
|
733
|
+
if (this.host !== undefined) {
|
|
734
|
+
const url = this.host.startsWith('http://') || this.host.startsWith('https://')
|
|
735
|
+
? `${this.host}/command`
|
|
736
|
+
: `http://${this.host}/command`;
|
|
737
|
+
try {
|
|
738
|
+
debugBus('Sending command to remote server at %s', url);
|
|
739
|
+
const response = await fetch(url, {
|
|
740
|
+
method: 'POST',
|
|
741
|
+
headers: {
|
|
742
|
+
'Content-Type': 'application/json',
|
|
743
|
+
},
|
|
744
|
+
body: JSON.stringify(command),
|
|
745
|
+
});
|
|
746
|
+
if (response.ok) {
|
|
747
|
+
debugBus('Command sent to remote message bus server at %s', url);
|
|
748
|
+
this.displayMessage(command);
|
|
749
|
+
const result = await response.json();
|
|
750
|
+
debugBus('Server response:', result);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
debugBus('Server responded with status %d', response.status);
|
|
755
|
+
throw new Error(`Server at ${url} responded with status ${response.status}`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
debugBus('Failed to send command to remote server at %s: %O', url, error);
|
|
760
|
+
throw new Error(`Failed to send command to remote server at ${url}: ${error instanceof Error ? error.message : String(error)}`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
debugBus('Executing command locally through message bus');
|
|
764
|
+
this.displayMessage(command);
|
|
765
|
+
await this.messageBus.sendCommand(command);
|
|
766
|
+
}
|
|
767
|
+
getCommands() {
|
|
768
|
+
return this.commands;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Get the unified command handlers with package metadata from package.json
|
|
772
|
+
* This is used by the message bus server for command registration
|
|
773
|
+
*/
|
|
774
|
+
getUnifiedHandlers() {
|
|
775
|
+
return this.unifiedHandlers;
|
|
776
|
+
}
|
|
777
|
+
getConflicts() {
|
|
778
|
+
return this.conflicts;
|
|
779
|
+
}
|
|
780
|
+
getMessageBus() {
|
|
781
|
+
return this.messageBus;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
export async function loadPlugins(configPath) {
|
|
785
|
+
const loader = new PluginLoader();
|
|
786
|
+
return loader.loadPlugins(configPath);
|
|
787
|
+
}
|
|
788
|
+
//# sourceMappingURL=plugin-loader.js.map
|