@auto-engineer/cli 0.13.3 → 0.14.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 +5 -872
- package/dist/bin/auto.js +4 -4
- package/dist/bin/auto.js.map +1 -1
- package/dist/src/config-loader.d.ts +12 -0
- package/dist/src/config-loader.d.ts.map +1 -0
- package/dist/src/config-loader.js +24 -0
- package/dist/src/config-loader.js.map +1 -0
- package/dist/src/file-syncer/crypto/jwe-encryptor.d.ts.map +1 -0
- package/dist/src/{server/file-syncer → file-syncer}/crypto/jwe-encryptor.js +1 -1
- package/dist/src/file-syncer/crypto/jwe-encryptor.js.map +1 -0
- package/dist/src/file-syncer/crypto/provider-resolver.d.ts.map +1 -0
- package/dist/src/{server/file-syncer → file-syncer}/crypto/provider-resolver.js +2 -2
- package/dist/src/file-syncer/crypto/provider-resolver.js.map +1 -0
- package/dist/src/file-syncer/discovery/bareImports.d.ts +3 -0
- package/dist/src/file-syncer/discovery/bareImports.d.ts.map +1 -0
- package/dist/src/file-syncer/discovery/bareImports.js.map +1 -0
- package/dist/src/{server/file-syncer → file-syncer}/discovery/dts.d.ts +1 -1
- package/dist/src/file-syncer/discovery/dts.d.ts.map +1 -0
- package/dist/src/file-syncer/discovery/dts.js.map +1 -0
- package/dist/src/file-syncer/index.d.ts.map +1 -0
- package/dist/src/{server/file-syncer → file-syncer}/index.js +6 -6
- package/dist/src/file-syncer/index.js.map +1 -0
- package/dist/src/{server/file-syncer → file-syncer}/sync/resolveSyncFileSet.d.ts +1 -1
- package/dist/src/file-syncer/sync/resolveSyncFileSet.d.ts.map +1 -0
- package/dist/src/{server/file-syncer → file-syncer}/sync/resolveSyncFileSet.js +2 -2
- package/dist/src/file-syncer/sync/resolveSyncFileSet.js.map +1 -0
- package/dist/src/file-syncer/types/wire.d.ts.map +1 -0
- package/dist/src/file-syncer/types/wire.js.map +1 -0
- package/dist/src/{server/file-syncer → file-syncer}/utils/hash.d.ts +1 -1
- package/dist/src/file-syncer/utils/hash.d.ts.map +1 -0
- package/dist/src/file-syncer/utils/hash.js.map +1 -0
- package/dist/src/file-syncer/utils/path.d.ts.map +1 -0
- package/dist/src/{server/file-syncer → file-syncer}/utils/path.js +2 -2
- package/dist/src/file-syncer/utils/path.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +294 -416
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -50
- package/README.md +0 -283
- package/dist/src/dsl/index.d.ts +0 -71
- package/dist/src/dsl/index.d.ts.map +0 -1
- package/dist/src/dsl/index.js +0 -504
- package/dist/src/dsl/index.js.map +0 -1
- package/dist/src/dsl/types.d.ts +0 -39
- package/dist/src/dsl/types.d.ts.map +0 -1
- package/dist/src/dsl/types.js +0 -2
- package/dist/src/dsl/types.js.map +0 -1
- package/dist/src/dsl-exports.d.ts +0 -5
- package/dist/src/dsl-exports.d.ts.map +0 -1
- package/dist/src/dsl-exports.js +0 -4
- package/dist/src/dsl-exports.js.map +0 -1
- package/dist/src/plugin-loader.d.ts +0 -84
- package/dist/src/plugin-loader.d.ts.map +0 -1
- package/dist/src/plugin-loader.js +0 -801
- package/dist/src/plugin-loader.js.map +0 -1
- package/dist/src/server/command-metadata-service.d.ts +0 -27
- package/dist/src/server/command-metadata-service.d.ts.map +0 -1
- package/dist/src/server/command-metadata-service.js +0 -69
- package/dist/src/server/command-metadata-service.js.map +0 -1
- package/dist/src/server/command-registry.d.ts +0 -21
- package/dist/src/server/command-registry.d.ts.map +0 -1
- package/dist/src/server/command-registry.js +0 -58
- package/dist/src/server/command-registry.js.map +0 -1
- package/dist/src/server/config-loader.d.ts +0 -16
- package/dist/src/server/config-loader.d.ts.map +0 -1
- package/dist/src/server/config-loader.js +0 -106
- package/dist/src/server/config-loader.js.map +0 -1
- package/dist/src/server/event-processor.d.ts +0 -45
- package/dist/src/server/event-processor.d.ts.map +0 -1
- package/dist/src/server/event-processor.js +0 -287
- package/dist/src/server/event-processor.js.map +0 -1
- package/dist/src/server/file-syncer/crypto/jwe-encryptor.d.ts.map +0 -1
- package/dist/src/server/file-syncer/crypto/jwe-encryptor.js.map +0 -1
- package/dist/src/server/file-syncer/crypto/provider-resolver.d.ts.map +0 -1
- package/dist/src/server/file-syncer/crypto/provider-resolver.js.map +0 -1
- package/dist/src/server/file-syncer/discovery/bareImports.d.ts +0 -3
- package/dist/src/server/file-syncer/discovery/bareImports.d.ts.map +0 -1
- package/dist/src/server/file-syncer/discovery/bareImports.js.map +0 -1
- package/dist/src/server/file-syncer/discovery/dts.d.ts.map +0 -1
- package/dist/src/server/file-syncer/discovery/dts.js.map +0 -1
- package/dist/src/server/file-syncer/index.d.ts.map +0 -1
- package/dist/src/server/file-syncer/index.js.map +0 -1
- package/dist/src/server/file-syncer/sync/resolveSyncFileSet.d.ts.map +0 -1
- package/dist/src/server/file-syncer/sync/resolveSyncFileSet.js.map +0 -1
- package/dist/src/server/file-syncer/types/wire.d.ts.map +0 -1
- package/dist/src/server/file-syncer/types/wire.js.map +0 -1
- package/dist/src/server/file-syncer/utils/hash.d.ts.map +0 -1
- package/dist/src/server/file-syncer/utils/hash.js.map +0 -1
- package/dist/src/server/file-syncer/utils/path.d.ts.map +0 -1
- package/dist/src/server/file-syncer/utils/path.js.map +0 -1
- package/dist/src/server/http-routes.d.ts +0 -30
- package/dist/src/server/http-routes.d.ts.map +0 -1
- package/dist/src/server/http-routes.js +0 -394
- package/dist/src/server/http-routes.js.map +0 -1
- package/dist/src/server/sandbox-landing-page.html +0 -367
- package/dist/src/server/server.d.ts +0 -106
- package/dist/src/server/server.d.ts.map +0 -1
- package/dist/src/server/server.js +0 -255
- package/dist/src/server/server.js.map +0 -1
- package/dist/src/server/services/child-process-manager.d.ts +0 -27
- package/dist/src/server/services/child-process-manager.d.ts.map +0 -1
- package/dist/src/server/services/child-process-manager.js +0 -126
- package/dist/src/server/services/child-process-manager.js.map +0 -1
- package/dist/src/server/services/index.d.ts +0 -3
- package/dist/src/server/services/index.d.ts.map +0 -1
- package/dist/src/server/services/index.js +0 -2
- package/dist/src/server/services/index.js.map +0 -1
- package/dist/src/server/services/interface.d.ts +0 -6
- package/dist/src/server/services/interface.d.ts.map +0 -1
- package/dist/src/server/services/interface.js +0 -2
- package/dist/src/server/services/interface.js.map +0 -1
- package/dist/src/server/settled-tracker.d.ts +0 -29
- package/dist/src/server/settled-tracker.d.ts.map +0 -1
- package/dist/src/server/settled-tracker.js +0 -203
- package/dist/src/server/settled-tracker.js.map +0 -1
- package/dist/src/server/state-manager.d.ts +0 -24
- package/dist/src/server/state-manager.d.ts.map +0 -1
- package/dist/src/server/state-manager.js +0 -56
- package/dist/src/server/state-manager.js.map +0 -1
- package/dist/src/server/websocket-handler.d.ts +0 -5
- package/dist/src/server/websocket-handler.d.ts.map +0 -1
- package/dist/src/server/websocket-handler.js +0 -40
- package/dist/src/server/websocket-handler.js.map +0 -1
- package/dist/src/utils/analytics.d.ts +0 -21
- package/dist/src/utils/analytics.d.ts.map +0 -1
- package/dist/src/utils/analytics.js +0 -41
- package/dist/src/utils/analytics.js.map +0 -1
- package/dist/src/utils/config.d.ts +0 -11
- package/dist/src/utils/config.d.ts.map +0 -1
- package/dist/src/utils/config.js +0 -50
- package/dist/src/utils/config.js.map +0 -1
- package/dist/src/utils/correlation-id.d.ts +0 -3
- package/dist/src/utils/correlation-id.d.ts.map +0 -1
- package/dist/src/utils/correlation-id.js +0 -7
- package/dist/src/utils/correlation-id.js.map +0 -1
- package/dist/src/utils/errors.d.ts +0 -22
- package/dist/src/utils/errors.d.ts.map +0 -1
- package/dist/src/utils/errors.js +0 -50
- package/dist/src/utils/errors.js.map +0 -1
- package/dist/src/utils/get-package-version.d.ts +0 -7
- package/dist/src/utils/get-package-version.d.ts.map +0 -1
- package/dist/src/utils/get-package-version.js +0 -31
- package/dist/src/utils/get-package-version.js.map +0 -1
- package/dist/src/utils/terminal.d.ts +0 -13
- package/dist/src/utils/terminal.d.ts.map +0 -1
- package/dist/src/utils/terminal.js +0 -85
- package/dist/src/utils/terminal.js.map +0 -1
- /package/dist/src/{server/file-syncer → file-syncer}/crypto/jwe-encryptor.d.ts +0 -0
- /package/dist/src/{server/file-syncer → file-syncer}/crypto/provider-resolver.d.ts +0 -0
- /package/dist/src/{server/file-syncer → file-syncer}/discovery/bareImports.js +0 -0
- /package/dist/src/{server/file-syncer → file-syncer}/discovery/dts.js +0 -0
- /package/dist/src/{server/file-syncer → file-syncer}/index.d.ts +0 -0
- /package/dist/src/{server/file-syncer → file-syncer}/types/wire.d.ts +0 -0
- /package/dist/src/{server/file-syncer → file-syncer}/types/wire.js +0 -0
- /package/dist/src/{server/file-syncer → file-syncer}/utils/hash.js +0 -0
- /package/dist/src/{server/file-syncer → file-syncer}/utils/path.d.ts +0 -0
package/dist/src/index.js
CHANGED
|
@@ -1,32 +1,87 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
import { PipelineServer } from '@auto-engineer/pipeline';
|
|
3
7
|
import chalk from 'chalk';
|
|
8
|
+
import { Command } from 'commander';
|
|
4
9
|
import * as dotenv from 'dotenv';
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
|
-
import Debug from 'debug';
|
|
9
|
-
import { loadConfig, validateConfig } from './utils/config.js';
|
|
10
|
-
import { handleError } from './utils/errors.js';
|
|
11
|
-
import { createOutput } from './utils/terminal.js';
|
|
12
|
-
import { Analytics } from './utils/analytics.js';
|
|
13
|
-
import { PluginLoader } from './plugin-loader.js';
|
|
14
10
|
import getPort, { portNumbers } from 'get-port';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
import createJiti from 'jiti';
|
|
12
|
+
import ora from 'ora';
|
|
13
|
+
import { Server as SocketIOServer } from 'socket.io';
|
|
14
|
+
import { FileSyncer } from './file-syncer/index.js';
|
|
15
|
+
dotenv.config();
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
function isValidHandler(item) {
|
|
19
|
+
return item !== null && item !== undefined && typeof item === 'object' && 'handle' in item && 'name' in item;
|
|
20
|
+
}
|
|
21
|
+
function extractCommandHandler(module, filename, packageName) {
|
|
22
|
+
if (isValidHandler(module.default)) {
|
|
23
|
+
return module.default;
|
|
24
|
+
}
|
|
25
|
+
if (isValidHandler(module.commandHandler)) {
|
|
26
|
+
return module.commandHandler;
|
|
27
|
+
}
|
|
28
|
+
if (isValidHandler(module.handler)) {
|
|
29
|
+
return module.handler;
|
|
30
|
+
}
|
|
31
|
+
for (const [key, value] of Object.entries(module)) {
|
|
32
|
+
if (isValidHandler(value)) {
|
|
33
|
+
console.log(chalk.gray(` Found handler via named export "${key}" in ${filename}`));
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
console.log(chalk.gray(` No valid handler found in ${filename} from ${packageName}`));
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
async function detectCommandsByConvention(packageName) {
|
|
41
|
+
const handlers = [];
|
|
42
|
+
const packageShortName = packageName.replace('@auto-engineer/', '');
|
|
43
|
+
const workspaceCommandsPath = path.join(process.cwd(), 'packages', packageShortName, 'dist', 'commands');
|
|
44
|
+
const workspaceSrcCommandsPath = path.join(process.cwd(), 'packages', packageShortName, 'dist', 'src', 'commands');
|
|
45
|
+
const nodeModulesCommandsPath = path.join(process.cwd(), 'node_modules', packageName, 'dist', 'commands');
|
|
46
|
+
const nodeModulesSrcCommandsPath = path.join(process.cwd(), 'node_modules', packageName, 'dist', 'src', 'commands');
|
|
47
|
+
let commandsDir = null;
|
|
48
|
+
if (fs.existsSync(workspaceCommandsPath)) {
|
|
49
|
+
commandsDir = workspaceCommandsPath;
|
|
50
|
+
}
|
|
51
|
+
else if (fs.existsSync(workspaceSrcCommandsPath)) {
|
|
52
|
+
commandsDir = workspaceSrcCommandsPath;
|
|
53
|
+
}
|
|
54
|
+
else if (fs.existsSync(nodeModulesCommandsPath)) {
|
|
55
|
+
commandsDir = nodeModulesCommandsPath;
|
|
56
|
+
}
|
|
57
|
+
else if (fs.existsSync(nodeModulesSrcCommandsPath)) {
|
|
58
|
+
commandsDir = nodeModulesSrcCommandsPath;
|
|
59
|
+
}
|
|
60
|
+
if (!commandsDir) {
|
|
61
|
+
return handlers;
|
|
62
|
+
}
|
|
63
|
+
const commandFiles = fs.readdirSync(commandsDir).filter((file) => file.endsWith('.js'));
|
|
64
|
+
for (const filename of commandFiles) {
|
|
65
|
+
const filePath = path.join(commandsDir, filename);
|
|
66
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
67
|
+
try {
|
|
68
|
+
const module = (await import(fileUrl));
|
|
69
|
+
const handler = extractCommandHandler(module, filename, packageName);
|
|
70
|
+
if (handler) {
|
|
71
|
+
handlers.push(handler);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.warn(chalk.yellow(` Failed to load command file ${filename}:`), error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return handlers;
|
|
79
|
+
}
|
|
80
|
+
function getVersion() {
|
|
20
81
|
try {
|
|
21
|
-
// Try to read from package.json relative to this file
|
|
22
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
-
const __dirname = path.dirname(__filename);
|
|
24
|
-
// Try multiple possible locations for package.json
|
|
25
|
-
// In dev: src/index.ts -> ../package.json
|
|
26
|
-
// In dist: dist/src/index.js -> ../../package.json
|
|
27
82
|
const possiblePaths = [
|
|
28
|
-
path.join(__dirname, '..', 'package.json'),
|
|
29
|
-
path.join(__dirname, '..', '..', 'package.json'),
|
|
83
|
+
path.join(__dirname, '..', 'package.json'),
|
|
84
|
+
path.join(__dirname, '..', '..', 'package.json'),
|
|
30
85
|
];
|
|
31
86
|
for (const packageJsonPath of possiblePaths) {
|
|
32
87
|
if (fs.existsSync(packageJsonPath)) {
|
|
@@ -36,425 +91,248 @@ const getVersion = () => {
|
|
|
36
91
|
}
|
|
37
92
|
}
|
|
38
93
|
catch {
|
|
39
|
-
// Fall through
|
|
94
|
+
// Fall through
|
|
40
95
|
}
|
|
41
|
-
// Fallback to npm_package_version (works when run via npm scripts)
|
|
42
|
-
// If neither works, show unknown
|
|
43
96
|
return process.env.npm_package_version ?? 'unknown';
|
|
44
|
-
}
|
|
97
|
+
}
|
|
45
98
|
const VERSION = getVersion();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
99
|
+
async function loadPipelineConfig(configPath) {
|
|
100
|
+
try {
|
|
101
|
+
if (configPath.endsWith('.ts')) {
|
|
102
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
103
|
+
return await jiti.import(configPath);
|
|
104
|
+
}
|
|
105
|
+
const configUrl = pathToFileURL(path.resolve(configPath)).href;
|
|
106
|
+
const configModule = (await import(configUrl));
|
|
107
|
+
return configModule.default ?? configModule;
|
|
54
108
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error(chalk.red('Failed to load config:'), error);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function tryLoadFromPath(modulePath) {
|
|
115
|
+
if (!fs.existsSync(modulePath))
|
|
116
|
+
return null;
|
|
117
|
+
const packageUrl = pathToFileURL(modulePath).href;
|
|
118
|
+
return (await import(packageUrl));
|
|
119
|
+
}
|
|
120
|
+
async function loadPlugin(packageName) {
|
|
121
|
+
const packageShortName = packageName.replace('@auto-engineer/', '');
|
|
122
|
+
const workspaceBase = path.join(process.cwd(), 'packages', packageShortName, 'dist');
|
|
123
|
+
const nodeModulesBase = path.join(process.cwd(), 'node_modules', packageName, 'dist');
|
|
124
|
+
const pathsToTry = [
|
|
125
|
+
path.join(workspaceBase, 'index.js'),
|
|
126
|
+
path.join(workspaceBase, 'src', 'index.js'),
|
|
127
|
+
path.join(nodeModulesBase, 'index.js'),
|
|
128
|
+
path.join(nodeModulesBase, 'src', 'index.js'),
|
|
129
|
+
];
|
|
130
|
+
const nodePathsToTry = [path.join(workspaceBase, 'src', 'node.js'), path.join(nodeModulesBase, 'src', 'node.js')];
|
|
131
|
+
try {
|
|
132
|
+
for (const modulePath of pathsToTry) {
|
|
133
|
+
const plugin = await tryLoadFromPath(modulePath);
|
|
134
|
+
if (plugin?.COMMANDS)
|
|
135
|
+
return plugin;
|
|
66
136
|
}
|
|
67
|
-
|
|
68
|
-
|
|
137
|
+
for (const modulePath of nodePathsToTry) {
|
|
138
|
+
const plugin = await tryLoadFromPath(modulePath);
|
|
139
|
+
if (plugin?.COMMANDS)
|
|
140
|
+
return plugin;
|
|
69
141
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
console.error('Error stopping server:', error);
|
|
80
|
-
}
|
|
142
|
+
const dynamicPlugin = (await import(packageName));
|
|
143
|
+
if (dynamicPlugin?.COMMANDS)
|
|
144
|
+
return dynamicPlugin;
|
|
145
|
+
try {
|
|
146
|
+
const nodeImport = (await import(`${packageName}/node`));
|
|
147
|
+
if (nodeImport?.COMMANDS)
|
|
148
|
+
return nodeImport;
|
|
81
149
|
}
|
|
82
|
-
|
|
83
|
-
|
|
150
|
+
catch {
|
|
151
|
+
// /node subpath not available for this package
|
|
84
152
|
}
|
|
85
|
-
|
|
86
|
-
});
|
|
87
|
-
process.on('uncaughtException', (error) => {
|
|
88
|
-
console.error(chalk.red('Uncaught Exception:'), error);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
});
|
|
91
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
92
|
-
console.error(chalk.red('Unhandled Rejection at:'), promise, chalk.red('reason:'), reason);
|
|
93
|
-
process.exit(1);
|
|
94
|
-
});
|
|
95
|
-
};
|
|
96
|
-
const createCLI = () => {
|
|
97
|
-
const program = new Command();
|
|
98
|
-
program
|
|
99
|
-
.name('auto-engineer')
|
|
100
|
-
.description('Auto Engineer - Build production ready full-stack apps with AI')
|
|
101
|
-
.version(VERSION, '-v, --version')
|
|
102
|
-
.option('-d, --debug', 'Enable debug mode')
|
|
103
|
-
.option('--no-color', 'Disable colored output')
|
|
104
|
-
.option('--json', 'Output in JSON format')
|
|
105
|
-
.option('--api-token <token>', 'API token for external services')
|
|
106
|
-
.option('--project-path <path>', 'Project path to work with')
|
|
107
|
-
.option('--host <url>', 'Message bus server URL (e.g., localhost:5555)');
|
|
108
|
-
return program;
|
|
109
|
-
};
|
|
110
|
-
const prepareCommandData = (alias, args, options, pluginLoader) => {
|
|
111
|
-
const mapper = pluginLoader.getCommandMapper(alias);
|
|
112
|
-
if (mapper !== undefined) {
|
|
113
|
-
return mapper(args, options);
|
|
153
|
+
return null;
|
|
114
154
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.warn(chalk.yellow(`Warning: Failed to load plugin ${packageName}:`), error);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function loadCommandHandlers(plugins) {
|
|
161
|
+
const handlers = [];
|
|
162
|
+
for (const packageName of plugins) {
|
|
163
|
+
const plugin = await loadPlugin(packageName);
|
|
164
|
+
if (plugin?.COMMANDS) {
|
|
165
|
+
handlers.push(...plugin.COMMANDS);
|
|
166
|
+
continue;
|
|
120
167
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
baseData[key] = options[key];
|
|
168
|
+
const detectedHandlers = await detectCommandsByConvention(packageName);
|
|
169
|
+
if (detectedHandlers.length > 0) {
|
|
170
|
+
handlers.push(...detectedHandlers);
|
|
125
171
|
}
|
|
126
|
-
});
|
|
127
|
-
return baseData;
|
|
128
|
-
};
|
|
129
|
-
const filterNonEmptyArgs = (args) => {
|
|
130
|
-
return args.filter((arg) => {
|
|
131
|
-
if (typeof arg === 'string')
|
|
132
|
-
return arg !== '';
|
|
133
|
-
if (Array.isArray(arg))
|
|
134
|
-
return true;
|
|
135
|
-
return false;
|
|
136
|
-
});
|
|
137
|
-
};
|
|
138
|
-
const debugCommandExecution = (args, cmdArgs, options) => {
|
|
139
|
-
if (process.env.DEBUG !== undefined && process.env.DEBUG.includes('cli:')) {
|
|
140
|
-
console.error('DEBUG cli: Raw args:', args);
|
|
141
|
-
console.error('DEBUG cli: Command args:', cmdArgs);
|
|
142
|
-
console.error('DEBUG cli: Options:', options);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
const debugCommandData = (commandData) => {
|
|
146
|
-
if (process.env.DEBUG !== undefined && process.env.DEBUG.includes('cli:')) {
|
|
147
|
-
console.error('DEBUG cli: Prepared command data:', commandData);
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
const handleVersionOption = (options, alias, loadedCommands) => {
|
|
151
|
-
if (options.version === true) {
|
|
152
|
-
const commandInfo = loadedCommands.get(alias);
|
|
153
|
-
const version = commandInfo && 'version' in commandInfo && typeof commandInfo.version === 'string'
|
|
154
|
-
? commandInfo.version
|
|
155
|
-
: 'unknown';
|
|
156
|
-
console.log(version);
|
|
157
|
-
return true;
|
|
158
|
-
}
|
|
159
|
-
return false;
|
|
160
|
-
};
|
|
161
|
-
const executeCommandAction = async (args, alias, pluginLoader, loadedCommands, config, analytics) => {
|
|
162
|
-
const cmdObject = args[args.length - 1];
|
|
163
|
-
const cmdArgs = args.slice(0, -1);
|
|
164
|
-
const options = cmdObject.opts();
|
|
165
|
-
const globalOpts = cmdObject.parent?.opts();
|
|
166
|
-
const hostOption = globalOpts?.host;
|
|
167
|
-
if (handleVersionOption(options, alias, loadedCommands)) {
|
|
168
|
-
return;
|
|
169
172
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
173
|
+
return handlers;
|
|
174
|
+
}
|
|
175
|
+
async function dispatchCommand(baseUrl, commandType, data) {
|
|
176
|
+
const response = await fetch(`${baseUrl}/command`, {
|
|
177
|
+
method: 'POST',
|
|
178
|
+
headers: { 'Content-Type': 'application/json' },
|
|
179
|
+
body: JSON.stringify({ type: commandType, data }),
|
|
180
|
+
});
|
|
181
|
+
return response.json();
|
|
182
|
+
}
|
|
183
|
+
function findConfigFile() {
|
|
184
|
+
const candidates = ['auto.config.ts', 'auto.config.js'];
|
|
185
|
+
for (const candidate of candidates) {
|
|
186
|
+
const fullPath = path.join(process.cwd(), candidate);
|
|
187
|
+
if (fs.existsSync(fullPath)) {
|
|
188
|
+
return fullPath;
|
|
189
|
+
}
|
|
181
190
|
}
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
const loadedCommands = new Map();
|
|
189
|
-
// Configure help to be minimal since we add our own
|
|
190
|
-
program.configureHelp({
|
|
191
|
-
sortSubcommands: true,
|
|
192
|
-
subcommandTerm: (cmd) => cmd.name(),
|
|
193
|
-
// Hide the Commands section
|
|
194
|
-
formatHelp: (cmd, helper) => {
|
|
195
|
-
const termWidth = helper.padWidth(cmd, helper);
|
|
196
|
-
const helpWidth = helper.helpWidth ?? 80;
|
|
197
|
-
const itemIndentWidth = 2;
|
|
198
|
-
const itemSeparatorWidth = 2;
|
|
199
|
-
function formatItem(term, description) {
|
|
200
|
-
if (description !== '') {
|
|
201
|
-
const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
|
|
202
|
-
return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth);
|
|
203
|
-
}
|
|
204
|
-
return term;
|
|
205
|
-
}
|
|
206
|
-
function formatList(textArray) {
|
|
207
|
-
return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth));
|
|
208
|
-
}
|
|
209
|
-
const output = [];
|
|
210
|
-
// Usage
|
|
211
|
-
const commandUsage = helper.commandUsage(cmd);
|
|
212
|
-
output.push('Usage: ' + commandUsage, '');
|
|
213
|
-
// Description
|
|
214
|
-
const commandDescription = helper.commandDescription(cmd);
|
|
215
|
-
if (commandDescription !== '') {
|
|
216
|
-
output.push(commandDescription, '');
|
|
217
|
-
}
|
|
218
|
-
// Options
|
|
219
|
-
const optionList = helper.visibleOptions(cmd).map((option) => {
|
|
220
|
-
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
|
|
221
|
-
});
|
|
222
|
-
if (optionList.length > 0) {
|
|
223
|
-
output.push('Options:', formatList(optionList), '');
|
|
224
|
-
}
|
|
225
|
-
// Skip the Commands section - we'll add our own
|
|
226
|
-
return output.join('\n');
|
|
227
|
-
},
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
async function startFileSyncServer(syncPort, watchDir) {
|
|
194
|
+
const httpServer = createServer();
|
|
195
|
+
const io = new SocketIOServer(httpServer, {
|
|
196
|
+
cors: { origin: '*' },
|
|
228
197
|
});
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
198
|
+
const fileSyncer = new FileSyncer(io, watchDir);
|
|
199
|
+
fileSyncer.start();
|
|
200
|
+
return new Promise((resolve) => {
|
|
201
|
+
httpServer.listen(syncPort, () => {
|
|
202
|
+
resolve({ fileSyncer, httpServer });
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function startServer(opts) {
|
|
207
|
+
const port = parseInt(opts.port, 10);
|
|
208
|
+
const actualPort = await getPort({ port: portNumbers(port, port + 100) });
|
|
209
|
+
const syncPort = await getPort({ port: portNumbers(actualPort + 1, actualPort + 100) });
|
|
210
|
+
const configPath = opts.config ?? findConfigFile();
|
|
211
|
+
if (!configPath) {
|
|
212
|
+
console.error(chalk.red('No pipeline config found. Create an auto.config.ts file.'));
|
|
213
|
+
process.exit(1);
|
|
235
214
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
export default {
|
|
241
|
-
plugins: [
|
|
242
|
-
'@auto-engineer/narrative',
|
|
243
|
-
'@auto-engineer/server-generator-apollo-emmett',
|
|
244
|
-
'@auto-engineer/server-implementer',
|
|
245
|
-
// Add more plugins as needed
|
|
246
|
-
]
|
|
247
|
-
};`);
|
|
215
|
+
const spinner = ora('Loading pipeline config...').start();
|
|
216
|
+
const config = await loadPipelineConfig(configPath);
|
|
217
|
+
if (!config) {
|
|
218
|
+
spinner.fail('Failed to load pipeline config');
|
|
248
219
|
process.exit(1);
|
|
249
220
|
}
|
|
250
|
-
|
|
251
|
-
|
|
221
|
+
spinner.text = 'Loading plugins...';
|
|
222
|
+
const commandHandlers = await loadCommandHandlers(config.plugins);
|
|
223
|
+
spinner.succeed(`Loaded ${commandHandlers.length} command handlers from ${config.plugins.length} plugins`);
|
|
224
|
+
spinner.start('Starting pipeline server...');
|
|
252
225
|
try {
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
226
|
+
const server = new PipelineServer({ port: actualPort });
|
|
227
|
+
server.registerCommandHandlers(commandHandlers);
|
|
228
|
+
server.registerPipeline(config.pipeline);
|
|
229
|
+
const watchDir = path.dirname(configPath);
|
|
230
|
+
const { fileSyncer, httpServer: syncHttpServer } = await startFileSyncServer(syncPort, watchDir);
|
|
231
|
+
process.on('SIGINT', () => {
|
|
232
|
+
console.log(chalk.yellow('\nShutting down servers...'));
|
|
233
|
+
fileSyncer.stop();
|
|
234
|
+
syncHttpServer.close();
|
|
235
|
+
void server.stop();
|
|
236
|
+
process.exit(0);
|
|
258
237
|
});
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
// Single command
|
|
272
|
-
cmd = new Command(primaryCommand);
|
|
273
|
-
}
|
|
274
|
-
cmd.description(command.description);
|
|
275
|
-
// Add arguments from the command manifest
|
|
276
|
-
if (command.args) {
|
|
277
|
-
command.args.forEach((arg) => {
|
|
278
|
-
// Handle variadic arguments (e.g., flows...)
|
|
279
|
-
const isVariadic = arg.name.endsWith('...');
|
|
280
|
-
const argName = isVariadic ? arg.name : arg.name;
|
|
281
|
-
const argString = arg.required === true ? `<${argName}>` : `[${argName}]`;
|
|
282
|
-
cmd.argument(argString, arg.description ?? '');
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
// Add options from the command manifest
|
|
286
|
-
if (command.options) {
|
|
287
|
-
command.options.forEach((opt) => {
|
|
288
|
-
cmd.option(opt.name, opt.description ?? '');
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
// Add version option to each command if package has version
|
|
292
|
-
const commandInfo = loadedCommands.get(alias);
|
|
293
|
-
if (commandInfo && 'version' in commandInfo) {
|
|
294
|
-
cmd.option('--version', 'Display version of the package providing this command');
|
|
295
|
-
}
|
|
296
|
-
cmd.action(async (...args) => {
|
|
297
|
-
try {
|
|
298
|
-
await executeCommandAction(args, alias, pluginLoader, loadedCommands, config, analytics);
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
await analytics.track({ command: alias, success: false });
|
|
302
|
-
if (error instanceof Error) {
|
|
303
|
-
handleError(error);
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
handleError(new Error(String(error)));
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
program.addCommand(cmd);
|
|
311
|
-
}
|
|
312
|
-
const pluginCount = pluginLoader.getLoadedPluginCount();
|
|
313
|
-
debug('Loaded %d commands from %d plugins', commands.size, pluginCount);
|
|
238
|
+
await server.start();
|
|
239
|
+
spinner.succeed(`Pipeline server running at http://localhost:${actualPort}`);
|
|
240
|
+
console.log(chalk.cyan('\nEndpoints:'));
|
|
241
|
+
console.log(` ${chalk.gray('Health:')} http://localhost:${actualPort}/health`);
|
|
242
|
+
console.log(` ${chalk.gray('Registry:')} http://localhost:${actualPort}/registry`);
|
|
243
|
+
console.log(` ${chalk.gray('Pipeline:')} http://localhost:${actualPort}/pipeline`);
|
|
244
|
+
console.log(` ${chalk.gray('Diagram:')} http://localhost:${actualPort}/pipeline/diagram`);
|
|
245
|
+
console.log(` ${chalk.gray('Events:')} http://localhost:${actualPort}/events (SSE)`);
|
|
246
|
+
console.log(` ${chalk.gray('FileSync:')} ws://localhost:${syncPort} (Socket.IO)`);
|
|
247
|
+
console.log(chalk.gray('\nPress Ctrl+C to stop'));
|
|
314
248
|
}
|
|
315
249
|
catch (error) {
|
|
316
|
-
|
|
250
|
+
spinner.fail('Failed to start server');
|
|
317
251
|
console.error(error);
|
|
318
252
|
process.exit(1);
|
|
319
253
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const usage = command.usage ?? alias;
|
|
338
|
-
const description = command.description ?? 'No description available';
|
|
339
|
-
// Calculate padding for alignment
|
|
340
|
-
const padding = ' '.repeat(Math.max(0, 60 - usage.length));
|
|
341
|
-
helpText.push(` ${usage}${padding}${description}\n`);
|
|
342
|
-
// Add examples indented under the command
|
|
343
|
-
if (command.examples && command.examples.length > 0) {
|
|
344
|
-
command.examples.forEach((example) => {
|
|
345
|
-
helpText.push(` ${chalk.gray(example)}\n`);
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
// Add environment variables section
|
|
351
|
-
helpText.push(`
|
|
352
|
-
\nEnvironment Variables:
|
|
353
|
-
${chalk.gray('AI Providers (need at least one):')}
|
|
354
|
-
ANTHROPIC_API_KEY Anthropic Claude API key
|
|
355
|
-
OPENAI_API_KEY OpenAI API key
|
|
356
|
-
GEMINI_API_KEY Google Gemini API key
|
|
357
|
-
XAI_API_KEY X.AI Grok API key
|
|
358
|
-
|
|
359
|
-
${chalk.gray('Debugging & Configuration:')}
|
|
360
|
-
DEBUG=* Enable all debug output
|
|
361
|
-
DEBUG=auto-engineer:* Enable auto-engineer debug only
|
|
362
|
-
NO_COLOR=1 Disable colored output
|
|
363
|
-
AUTO_ENGINEER_ANALYTICS=false Disable usage analytics
|
|
364
|
-
|
|
365
|
-
Tips:
|
|
366
|
-
• Use DEBUG=* to troubleshoot command issues
|
|
367
|
-
• Run 'pnpm install' after create:example
|
|
368
|
-
• Commands available depend on plugins in auto.config.ts
|
|
369
|
-
|
|
370
|
-
For docs & support: https://github.com/SamHatoum/auto-engineer\n`);
|
|
371
|
-
return helpText.join('');
|
|
372
|
-
};
|
|
373
|
-
program.addHelpText('after', generateDynamicHelp());
|
|
374
|
-
return program;
|
|
375
|
-
};
|
|
376
|
-
const loadEnvFile = () => {
|
|
377
|
-
const envPath = path.resolve(process.cwd(), '.env');
|
|
378
|
-
if (fs.existsSync(envPath)) {
|
|
379
|
-
dotenv.config({ path: envPath });
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
const initializeEnvironment = () => {
|
|
383
|
-
checkNodeVersion();
|
|
384
|
-
loadEnvFile();
|
|
385
|
-
setupSignalHandlers();
|
|
386
|
-
};
|
|
387
|
-
const handleProgramError = (error) => {
|
|
388
|
-
if (error instanceof Error &&
|
|
389
|
-
(error.message.includes('commander') ||
|
|
390
|
-
error.message.includes('helpDisplayed') ||
|
|
391
|
-
error.message.includes('version'))) {
|
|
392
|
-
process.exit(0);
|
|
393
|
-
}
|
|
394
|
-
if (error instanceof Error) {
|
|
395
|
-
handleError(error);
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
console.error(chalk.red('Unknown error:'), error);
|
|
399
|
-
process.exit(1);
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
let serverStarted = false;
|
|
403
|
-
let serverInstance = null;
|
|
404
|
-
const startMessageBusServer = async () => {
|
|
405
|
-
if (serverStarted)
|
|
406
|
-
return;
|
|
407
|
-
else
|
|
408
|
-
serverStarted = true;
|
|
409
|
-
const { MessageBusServer } = await import('./server/server.js');
|
|
410
|
-
const { loadMessageBusConfig } = await import('./server/config-loader.js');
|
|
411
|
-
debug('Starting Auto Engineer Server...');
|
|
412
|
-
const server = new MessageBusServer({
|
|
413
|
-
port: await getPort({ port: portNumbers(5555, 6000) }),
|
|
414
|
-
enableFileSync: true,
|
|
415
|
-
fileSyncDir: process.cwd(),
|
|
416
|
-
fileSyncExtensions: ['.js', '.ts', '.tsx', '.jsx', '.html', '.css', 'auto.config'],
|
|
254
|
+
}
|
|
255
|
+
async function main() {
|
|
256
|
+
const program = new Command();
|
|
257
|
+
program
|
|
258
|
+
.name('auto')
|
|
259
|
+
.description('Auto Engineer - Build apps with Narrative Driven Development')
|
|
260
|
+
.version(VERSION, '-v, --version')
|
|
261
|
+
.option('-p, --port <number>', 'Server port', '5555')
|
|
262
|
+
.option('-d, --debug', 'Enable debug mode')
|
|
263
|
+
.option('-c, --config <path>', 'Path to pipeline config file')
|
|
264
|
+
.option('--host <url>', 'Connect to existing server instead of starting one');
|
|
265
|
+
program
|
|
266
|
+
.command('start', { isDefault: true })
|
|
267
|
+
.description('Start the pipeline server with loaded config (default)')
|
|
268
|
+
.action(async () => {
|
|
269
|
+
const opts = program.opts();
|
|
270
|
+
await startServer(opts);
|
|
417
271
|
});
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const hostValue = hostArgIndex !== -1 && hostArgIndex + 1 < process.argv.length ? process.argv[hostArgIndex + 1] : undefined;
|
|
432
|
-
const config = loadConfig({
|
|
433
|
-
debug: process.argv.includes('-d') || process.argv.includes('--debug'),
|
|
434
|
-
noColor: process.argv.includes('--no-color'),
|
|
435
|
-
output: process.argv.includes('--json') ? 'json' : 'text',
|
|
436
|
-
host: hostValue,
|
|
437
|
-
});
|
|
438
|
-
validateConfig(config);
|
|
439
|
-
createOutput(config);
|
|
440
|
-
// Check if no command arguments provided (just 'auto')
|
|
441
|
-
if (process.argv.length === 2 || (process.argv.length === 3 && process.argv[2] === 'serve')) {
|
|
442
|
-
await startMessageBusServer();
|
|
443
|
-
return;
|
|
272
|
+
program
|
|
273
|
+
.command('dispatch <command>')
|
|
274
|
+
.description('Dispatch a command to the pipeline server')
|
|
275
|
+
.option('--data <json>', 'Command data as JSON', '{}')
|
|
276
|
+
.action(async (commandType, options) => {
|
|
277
|
+
const opts = program.opts();
|
|
278
|
+
const baseUrl = opts.host ?? `http://localhost:${opts.port}`;
|
|
279
|
+
const spinner = ora(`Dispatching ${commandType}...`).start();
|
|
280
|
+
try {
|
|
281
|
+
const data = JSON.parse(options.data);
|
|
282
|
+
const result = await dispatchCommand(baseUrl, commandType, data);
|
|
283
|
+
spinner.succeed(`Command dispatched: ${commandType}`);
|
|
284
|
+
console.log(chalk.gray(JSON.stringify(result, null, 2)));
|
|
444
285
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
286
|
+
catch (error) {
|
|
287
|
+
spinner.fail(`Failed to dispatch ${commandType}`);
|
|
288
|
+
console.error(error);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
program
|
|
293
|
+
.command('status')
|
|
294
|
+
.description('Check pipeline server status')
|
|
295
|
+
.action(async () => {
|
|
296
|
+
const opts = program.opts();
|
|
297
|
+
const baseUrl = opts.host ?? `http://localhost:${opts.port}`;
|
|
298
|
+
try {
|
|
299
|
+
const healthResponse = await fetch(`${baseUrl}/health`);
|
|
300
|
+
if (!healthResponse.ok) {
|
|
301
|
+
console.log(chalk.red('Server is not healthy'));
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
const health = (await healthResponse.json());
|
|
305
|
+
console.log(chalk.green('Server is healthy'));
|
|
306
|
+
console.log(chalk.gray(` Status: ${health.status}`));
|
|
307
|
+
console.log(chalk.gray(` Uptime: ${Math.round(health.uptime)}s`));
|
|
308
|
+
const registryResponse = await fetch(`${baseUrl}/registry`);
|
|
309
|
+
const registry = (await registryResponse.json());
|
|
310
|
+
console.log(chalk.cyan('\nRegistry:'));
|
|
311
|
+
console.log(chalk.gray(` Commands: ${registry.commandHandlers.length}`));
|
|
312
|
+
console.log(chalk.gray(` Events: ${registry.eventHandlers.length}`));
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
console.log(chalk.red('Server is not running'));
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
program
|
|
320
|
+
.command('diagram')
|
|
321
|
+
.description('Open the pipeline diagram in a browser')
|
|
322
|
+
.action(async () => {
|
|
323
|
+
const opts = program.opts();
|
|
324
|
+
const baseUrl = opts.host ?? `http://localhost:${opts.port}`;
|
|
325
|
+
const diagramUrl = `${baseUrl}/pipeline/diagram`;
|
|
326
|
+
console.log(chalk.cyan(`Opening diagram at ${diagramUrl}`));
|
|
327
|
+
const { exec } = await import('node:child_process');
|
|
328
|
+
const platform = process.platform;
|
|
329
|
+
const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open';
|
|
330
|
+
exec(`${command} ${diagramUrl}`);
|
|
458
331
|
});
|
|
332
|
+
await program.parseAsync(process.argv);
|
|
459
333
|
}
|
|
334
|
+
main().catch((error) => {
|
|
335
|
+
console.error(chalk.red('Error:'), error);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
});
|
|
460
338
|
//# sourceMappingURL=index.js.map
|