@openbuilder/cli 0.31.11
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/README.md +1053 -0
- package/bin/openbuilder.js +31 -0
- package/dist/chunks/Banner-D4tqKfzA.js +113 -0
- package/dist/chunks/Banner-D4tqKfzA.js.map +1 -0
- package/dist/chunks/auto-update-Dj3lWPWO.js +350 -0
- package/dist/chunks/auto-update-Dj3lWPWO.js.map +1 -0
- package/dist/chunks/build-D0qYqIq0.js +116 -0
- package/dist/chunks/build-D0qYqIq0.js.map +1 -0
- package/dist/chunks/cleanup-qVTsA3tk.js +141 -0
- package/dist/chunks/cleanup-qVTsA3tk.js.map +1 -0
- package/dist/chunks/cli-error-BjQwvWtK.js +140 -0
- package/dist/chunks/cli-error-BjQwvWtK.js.map +1 -0
- package/dist/chunks/config-BGP1jZJ4.js +167 -0
- package/dist/chunks/config-BGP1jZJ4.js.map +1 -0
- package/dist/chunks/config-manager-BkbjtN-H.js +133 -0
- package/dist/chunks/config-manager-BkbjtN-H.js.map +1 -0
- package/dist/chunks/database-BvAbD4sP.js +68 -0
- package/dist/chunks/database-BvAbD4sP.js.map +1 -0
- package/dist/chunks/database-setup-BYjIRAmT.js +253 -0
- package/dist/chunks/database-setup-BYjIRAmT.js.map +1 -0
- package/dist/chunks/exports-ij9sv4UM.js +7793 -0
- package/dist/chunks/exports-ij9sv4UM.js.map +1 -0
- package/dist/chunks/init-CZoN6soU.js +468 -0
- package/dist/chunks/init-CZoN6soU.js.map +1 -0
- package/dist/chunks/init-tui-BNzk_7Yx.js +1127 -0
- package/dist/chunks/init-tui-BNzk_7Yx.js.map +1 -0
- package/dist/chunks/logger-ZpJi7chw.js +38 -0
- package/dist/chunks/logger-ZpJi7chw.js.map +1 -0
- package/dist/chunks/main-tui-Cq1hLCx-.js +644 -0
- package/dist/chunks/main-tui-Cq1hLCx-.js.map +1 -0
- package/dist/chunks/manager-CvGX9qqe.js +1161 -0
- package/dist/chunks/manager-CvGX9qqe.js.map +1 -0
- package/dist/chunks/port-allocator-BRFzgH9b.js +749 -0
- package/dist/chunks/port-allocator-BRFzgH9b.js.map +1 -0
- package/dist/chunks/process-killer-CaUL7Kpl.js +87 -0
- package/dist/chunks/process-killer-CaUL7Kpl.js.map +1 -0
- package/dist/chunks/prompts-1QbE_bRr.js +128 -0
- package/dist/chunks/prompts-1QbE_bRr.js.map +1 -0
- package/dist/chunks/repo-cloner-CpOQjFSo.js +219 -0
- package/dist/chunks/repo-cloner-CpOQjFSo.js.map +1 -0
- package/dist/chunks/repo-detector-B_oj696o.js +66 -0
- package/dist/chunks/repo-detector-B_oj696o.js.map +1 -0
- package/dist/chunks/run-D23hg4xy.js +630 -0
- package/dist/chunks/run-D23hg4xy.js.map +1 -0
- package/dist/chunks/runner-logger-instance-nDWv2h2T.js +899 -0
- package/dist/chunks/runner-logger-instance-nDWv2h2T.js.map +1 -0
- package/dist/chunks/spinner-BJL9zWAJ.js +53 -0
- package/dist/chunks/spinner-BJL9zWAJ.js.map +1 -0
- package/dist/chunks/start-BygPCbvw.js +1708 -0
- package/dist/chunks/start-BygPCbvw.js.map +1 -0
- package/dist/chunks/start-traditional-uoLZXdxm.js +255 -0
- package/dist/chunks/start-traditional-uoLZXdxm.js.map +1 -0
- package/dist/chunks/status-cS8YwtUx.js +97 -0
- package/dist/chunks/status-cS8YwtUx.js.map +1 -0
- package/dist/chunks/theme-DhorI2Hb.js +44 -0
- package/dist/chunks/theme-DhorI2Hb.js.map +1 -0
- package/dist/chunks/upgrade-CT6w0lKp.js +323 -0
- package/dist/chunks/upgrade-CT6w0lKp.js.map +1 -0
- package/dist/chunks/useBuildState-CdBSu9y_.js +331 -0
- package/dist/chunks/useBuildState-CdBSu9y_.js.map +1 -0
- package/dist/cli/index.js +694 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.js +14358 -0
- package/dist/index.js.map +1 -0
- package/dist/instrument.js +64226 -0
- package/dist/instrument.js.map +1 -0
- package/dist/templates.json +295 -0
- package/package.json +98 -0
- package/scripts/install-vendor-deps.js +34 -0
- package/scripts/install-vendor.js +167 -0
- package/scripts/prepare-release.js +71 -0
- package/templates/config.template.json +18 -0
- package/templates.json +295 -0
- package/vendor/ai-sdk-provider-claude-code-LOCAL.tgz +0 -0
- package/vendor/sentry-core-LOCAL.tgz +0 -0
- package/vendor/sentry-nextjs-LOCAL.tgz +0 -0
- package/vendor/sentry-node-LOCAL.tgz +0 -0
- package/vendor/sentry-node-core-LOCAL.tgz +0 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// OpenBuilder CLI - Built with Rollup
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { C as CLIError } from '../chunks/cli-error-BjQwvWtK.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ASCII art banner for OpenBuilder CLI
|
|
13
|
+
*/
|
|
14
|
+
// ANSI color codes
|
|
15
|
+
const colors = {
|
|
16
|
+
reset: '\x1b[0m',
|
|
17
|
+
brightPurple: '\x1b[95m',
|
|
18
|
+
cyan: '\x1b[36m'};
|
|
19
|
+
/**
|
|
20
|
+
* Displays the OpenBuilder banner
|
|
21
|
+
*/
|
|
22
|
+
function displayBanner() {
|
|
23
|
+
const banner = `
|
|
24
|
+
${colors.cyan} ██████╗ ██████╗ ███████╗███╗ ██╗${colors.brightPurple}██████╗ ██╗ ██╗██╗██╗ ██████╗ ███████╗██████╗${colors.reset}
|
|
25
|
+
${colors.cyan}██╔═══██╗██╔══██╗██╔════╝████╗ ██║${colors.brightPurple}██╔══██╗██║ ██║██║██║ ██╔══██╗██╔════╝██╔══██╗${colors.reset}
|
|
26
|
+
${colors.cyan}██║ ██║██████╔╝█████╗ ██╔██╗ ██║${colors.brightPurple}██████╔╝██║ ██║██║██║ ██║ ██║█████╗ ██████╔╝${colors.reset}
|
|
27
|
+
${colors.cyan}██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║${colors.brightPurple}██╔══██╗██║ ██║██║██║ ██║ ██║██╔══╝ ██╔══██╗${colors.reset}
|
|
28
|
+
${colors.cyan}╚██████╔╝██║ ███████╗██║ ╚████║${colors.brightPurple}██████╔╝╚██████╔╝██║███████╗██████╔╝███████╗██║ ██║${colors.reset}
|
|
29
|
+
${colors.cyan} ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝${colors.brightPurple}╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝ ╚══════╝╚═╝ ╚═╝${colors.reset}
|
|
30
|
+
`;
|
|
31
|
+
console.log(banner);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Centralized error handling and formatting for CLI
|
|
36
|
+
* Displays errors with context, suggestions, and proper formatting
|
|
37
|
+
*/
|
|
38
|
+
class CLIErrorHandler {
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.debug = options.debug ?? process.env.DEBUG === '1';
|
|
41
|
+
this.exitOnError = options.exitOnError ?? true;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Main error handling method
|
|
45
|
+
* Formats and displays error, then exits if fatal
|
|
46
|
+
*/
|
|
47
|
+
handle(error) {
|
|
48
|
+
// Convert to CLIError if needed
|
|
49
|
+
const cliError = error instanceof CLIError
|
|
50
|
+
? error
|
|
51
|
+
: this.convertToCLIError(error);
|
|
52
|
+
// Display the error
|
|
53
|
+
this.display(cliError);
|
|
54
|
+
// Exit if fatal and exitOnError is enabled
|
|
55
|
+
if (cliError.fatal && this.exitOnError) {
|
|
56
|
+
process.exit(cliError.getExitCode());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Display formatted error to console
|
|
61
|
+
*/
|
|
62
|
+
display(error) {
|
|
63
|
+
console.error(); // Empty line for spacing
|
|
64
|
+
// Error header
|
|
65
|
+
console.error(chalk.red('✗'), chalk.bold(error.message));
|
|
66
|
+
// Error code (in debug mode)
|
|
67
|
+
if (this.debug) {
|
|
68
|
+
console.error(chalk.dim(` Code: ${error.code}`));
|
|
69
|
+
}
|
|
70
|
+
// Context information
|
|
71
|
+
if (error.context && Object.keys(error.context).length > 0) {
|
|
72
|
+
console.error();
|
|
73
|
+
console.error(chalk.dim(' Details:'));
|
|
74
|
+
Object.entries(error.context).forEach(([key, value]) => {
|
|
75
|
+
console.error(chalk.dim(` ${key}: ${this.formatValue(value)}`));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// Suggestions
|
|
79
|
+
if (error.suggestions.length > 0) {
|
|
80
|
+
console.error();
|
|
81
|
+
console.error(chalk.yellow(' Try this:'));
|
|
82
|
+
error.suggestions.forEach((suggestion, index) => {
|
|
83
|
+
console.error(chalk.yellow(` ${index + 1}. ${suggestion}`));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Documentation link
|
|
87
|
+
if (error.docs) {
|
|
88
|
+
console.error();
|
|
89
|
+
console.error(chalk.dim(' Documentation:'), chalk.cyan(error.docs));
|
|
90
|
+
}
|
|
91
|
+
// Stack trace (debug mode only)
|
|
92
|
+
if (this.debug && error.stack) {
|
|
93
|
+
console.error();
|
|
94
|
+
console.error(chalk.dim(' Stack trace:'));
|
|
95
|
+
console.error(chalk.dim(error.stack));
|
|
96
|
+
}
|
|
97
|
+
// Original error cause (if available)
|
|
98
|
+
if (this.debug && error.cause) {
|
|
99
|
+
console.error();
|
|
100
|
+
console.error(chalk.dim(' Caused by:'));
|
|
101
|
+
console.error(chalk.dim(error.cause.message));
|
|
102
|
+
if (error.cause.stack) {
|
|
103
|
+
console.error(chalk.dim(error.cause.stack));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
console.error(); // Empty line for spacing
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Format context values for display
|
|
110
|
+
*/
|
|
111
|
+
formatValue(value) {
|
|
112
|
+
if (typeof value === 'string')
|
|
113
|
+
return value;
|
|
114
|
+
if (typeof value === 'number')
|
|
115
|
+
return String(value);
|
|
116
|
+
if (typeof value === 'boolean')
|
|
117
|
+
return String(value);
|
|
118
|
+
if (value === null || value === undefined)
|
|
119
|
+
return 'null';
|
|
120
|
+
if (Array.isArray(value))
|
|
121
|
+
return value.join(', ');
|
|
122
|
+
return JSON.stringify(value);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Convert generic errors to CLIError
|
|
126
|
+
*/
|
|
127
|
+
convertToCLIError(error) {
|
|
128
|
+
// Try to infer error code from message
|
|
129
|
+
const code = this.inferErrorCode(error);
|
|
130
|
+
return new CLIError({
|
|
131
|
+
code,
|
|
132
|
+
message: error.message || 'An unexpected error occurred',
|
|
133
|
+
cause: error,
|
|
134
|
+
suggestions: ['Run with --debug flag for more details'],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Infer error code from generic error
|
|
139
|
+
*/
|
|
140
|
+
inferErrorCode(error) {
|
|
141
|
+
const message = error.message.toLowerCase();
|
|
142
|
+
if (message.includes('econnrefused') || message.includes('connection refused')) {
|
|
143
|
+
return 'DB_CONNECTION_FAILED';
|
|
144
|
+
}
|
|
145
|
+
if (message.includes('eaddrinuse') || message.includes('address already in use')) {
|
|
146
|
+
return 'PORT_IN_USE';
|
|
147
|
+
}
|
|
148
|
+
if (message.includes('eacces') || message.includes('permission denied')) {
|
|
149
|
+
return 'PERMISSION_DENIED';
|
|
150
|
+
}
|
|
151
|
+
if (message.includes('enoent') || message.includes('no such file')) {
|
|
152
|
+
return 'CONFIG_NOT_FOUND';
|
|
153
|
+
}
|
|
154
|
+
return 'UNKNOWN_ERROR';
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Wrap an async function with error handling
|
|
158
|
+
* Usage: await errorHandler.wrap(() => someAsyncFunction(), 'Description')
|
|
159
|
+
*/
|
|
160
|
+
async wrap(fn, context) {
|
|
161
|
+
try {
|
|
162
|
+
return await fn();
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
if (context && error instanceof CLIError) {
|
|
166
|
+
// Create new CLIError with added context (can't modify readonly property)
|
|
167
|
+
const enhancedError = new CLIError({
|
|
168
|
+
code: error.code,
|
|
169
|
+
message: error.message,
|
|
170
|
+
context: { ...error.context, operation: context },
|
|
171
|
+
suggestions: error.suggestions,
|
|
172
|
+
fatal: error.fatal,
|
|
173
|
+
cause: error.cause,
|
|
174
|
+
docs: error.docs,
|
|
175
|
+
});
|
|
176
|
+
this.handle(enhancedError);
|
|
177
|
+
throw enhancedError;
|
|
178
|
+
}
|
|
179
|
+
this.handle(error);
|
|
180
|
+
throw error; // Re-throw for callers that want to handle it
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Enable or disable debug mode
|
|
185
|
+
*/
|
|
186
|
+
setDebug(debug) {
|
|
187
|
+
this.debug = debug;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Enable or disable exit on error
|
|
191
|
+
*/
|
|
192
|
+
setExitOnError(exitOnError) {
|
|
193
|
+
this.exitOnError = exitOnError;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Global error handler instance
|
|
198
|
+
* Can be configured once and used throughout the CLI
|
|
199
|
+
*/
|
|
200
|
+
const globalErrorHandler = new CLIErrorHandler();
|
|
201
|
+
/**
|
|
202
|
+
* Setup global error handlers for uncaught errors
|
|
203
|
+
*/
|
|
204
|
+
function setupGlobalErrorHandlers() {
|
|
205
|
+
process.on('uncaughtException', (error) => {
|
|
206
|
+
console.error(chalk.red('\n✗ Uncaught exception:'));
|
|
207
|
+
globalErrorHandler.handle(error);
|
|
208
|
+
});
|
|
209
|
+
process.on('unhandledRejection', (reason) => {
|
|
210
|
+
console.error(chalk.red('\n✗ Unhandled promise rejection:'));
|
|
211
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
212
|
+
globalErrorHandler.handle(error);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Graceful shutdown handler for CLI
|
|
218
|
+
* Ensures proper cleanup when user presses Ctrl+C or process is terminated
|
|
219
|
+
*/
|
|
220
|
+
class ShutdownHandler {
|
|
221
|
+
constructor(options = {}) {
|
|
222
|
+
this.cleanupFunctions = [];
|
|
223
|
+
this.childProcesses = [];
|
|
224
|
+
this.isShuttingDown = false;
|
|
225
|
+
this.timeout = options.timeout ?? 5000;
|
|
226
|
+
this.verbose = options.verbose !== false;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Register a cleanup function to run on shutdown
|
|
230
|
+
*/
|
|
231
|
+
onShutdown(fn) {
|
|
232
|
+
this.cleanupFunctions.push(fn);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Register a child process to kill on shutdown
|
|
236
|
+
*/
|
|
237
|
+
registerProcess(process) {
|
|
238
|
+
this.childProcesses.push(process);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Setup signal handlers for graceful shutdown
|
|
242
|
+
*/
|
|
243
|
+
setup() {
|
|
244
|
+
// Handle Ctrl+C (SIGINT)
|
|
245
|
+
process.on('SIGINT', () => {
|
|
246
|
+
if (this.verbose) {
|
|
247
|
+
console.log(); // New line after ^C
|
|
248
|
+
console.log(chalk.yellow('⚠'), 'Received interrupt signal (Ctrl+C)');
|
|
249
|
+
}
|
|
250
|
+
this.shutdown('SIGINT');
|
|
251
|
+
});
|
|
252
|
+
// Handle termination (SIGTERM)
|
|
253
|
+
process.on('SIGTERM', () => {
|
|
254
|
+
if (this.verbose) {
|
|
255
|
+
console.log(chalk.yellow('⚠'), 'Received termination signal (SIGTERM)');
|
|
256
|
+
}
|
|
257
|
+
this.shutdown('SIGTERM');
|
|
258
|
+
});
|
|
259
|
+
// Handle process exit
|
|
260
|
+
process.on('exit', (code) => {
|
|
261
|
+
if (this.verbose && code !== 0) {
|
|
262
|
+
console.log(chalk.red('✗'), `Process exiting with code ${code}`);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Execute graceful shutdown
|
|
268
|
+
*/
|
|
269
|
+
async shutdown(signal) {
|
|
270
|
+
// Prevent multiple shutdown attempts
|
|
271
|
+
if (this.isShuttingDown) {
|
|
272
|
+
if (this.verbose) {
|
|
273
|
+
console.log(chalk.dim(' Already shutting down...'));
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.isShuttingDown = true;
|
|
278
|
+
if (this.verbose) {
|
|
279
|
+
console.log(chalk.cyan('ℹ'), 'Shutting down gracefully...');
|
|
280
|
+
}
|
|
281
|
+
// Set a timeout to force exit if cleanup takes too long
|
|
282
|
+
const forceExitTimeout = setTimeout(() => {
|
|
283
|
+
console.error(chalk.red('✗'), 'Shutdown timeout exceeded, forcing exit');
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}, this.timeout);
|
|
286
|
+
try {
|
|
287
|
+
// 1. Kill child processes first
|
|
288
|
+
if (this.childProcesses.length > 0) {
|
|
289
|
+
if (this.verbose) {
|
|
290
|
+
console.log(chalk.dim(' Stopping child processes...'));
|
|
291
|
+
}
|
|
292
|
+
await this.killChildProcesses();
|
|
293
|
+
}
|
|
294
|
+
// 2. Run cleanup functions
|
|
295
|
+
if (this.cleanupFunctions.length > 0) {
|
|
296
|
+
if (this.verbose) {
|
|
297
|
+
console.log(chalk.dim(' Running cleanup tasks...'));
|
|
298
|
+
}
|
|
299
|
+
await this.runCleanup();
|
|
300
|
+
}
|
|
301
|
+
// 3. Success
|
|
302
|
+
if (this.verbose) {
|
|
303
|
+
console.log(chalk.green('✓'), 'Shutdown complete');
|
|
304
|
+
}
|
|
305
|
+
clearTimeout(forceExitTimeout);
|
|
306
|
+
process.exit(0);
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
console.error(chalk.red('✗'), 'Error during shutdown:', error);
|
|
310
|
+
clearTimeout(forceExitTimeout);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Kill all registered child processes
|
|
316
|
+
*/
|
|
317
|
+
async killChildProcesses() {
|
|
318
|
+
const killPromises = this.childProcesses.map(async (child) => {
|
|
319
|
+
if (!child.killed && child.pid) {
|
|
320
|
+
try {
|
|
321
|
+
// Try graceful SIGTERM first
|
|
322
|
+
child.kill('SIGTERM');
|
|
323
|
+
// Wait up to 2 seconds for graceful shutdown
|
|
324
|
+
await new Promise((resolve) => {
|
|
325
|
+
const timeout = setTimeout(() => {
|
|
326
|
+
// Force kill if still alive
|
|
327
|
+
if (!child.killed) {
|
|
328
|
+
child.kill('SIGKILL');
|
|
329
|
+
}
|
|
330
|
+
resolve();
|
|
331
|
+
}, 2000);
|
|
332
|
+
child.on('exit', () => {
|
|
333
|
+
clearTimeout(timeout);
|
|
334
|
+
resolve();
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
// Process might already be dead
|
|
340
|
+
if (this.verbose) {
|
|
341
|
+
console.log(chalk.dim(` Could not kill process ${child.pid}`));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
await Promise.all(killPromises);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Run all cleanup functions
|
|
350
|
+
*/
|
|
351
|
+
async runCleanup() {
|
|
352
|
+
const cleanupPromises = this.cleanupFunctions.map(async (fn) => {
|
|
353
|
+
try {
|
|
354
|
+
await fn();
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
if (this.verbose) {
|
|
358
|
+
console.error(chalk.yellow('⚠'), 'Cleanup function failed:', error);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
await Promise.all(cleanupPromises);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Manually trigger shutdown (for testing or programmatic use)
|
|
366
|
+
*/
|
|
367
|
+
async triggerShutdown() {
|
|
368
|
+
await this.shutdown('MANUAL');
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Remove a child process from tracking (e.g., after it exits naturally)
|
|
372
|
+
*/
|
|
373
|
+
unregisterProcess(process) {
|
|
374
|
+
const index = this.childProcesses.indexOf(process);
|
|
375
|
+
if (index > -1) {
|
|
376
|
+
this.childProcesses.splice(index, 1);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Global shutdown handler instance
|
|
382
|
+
*/
|
|
383
|
+
new ShutdownHandler();
|
|
384
|
+
/**
|
|
385
|
+
* Helper to setup shutdown handler with common options
|
|
386
|
+
*/
|
|
387
|
+
function setupShutdownHandler(options) {
|
|
388
|
+
const handler = new ShutdownHandler(options);
|
|
389
|
+
handler.setup();
|
|
390
|
+
return handler;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// EARLY TUI DETECTION: Set SILENT_MODE before any modules are imported
|
|
394
|
+
// This suppresses console output in TUI mode
|
|
395
|
+
// Must be done before imports because file-logger.ts captures console at load time
|
|
396
|
+
{
|
|
397
|
+
const args = process.argv.slice(2);
|
|
398
|
+
const isRunnerTUI = args[0] === 'runner' && !args.includes('--no-tui');
|
|
399
|
+
const isRunTUI = args[0] === 'run';
|
|
400
|
+
const isInitTUI = args[0] === 'init' && (args.includes('-y') || args.includes('--yes') || args.includes('--non-interactive'));
|
|
401
|
+
const isNoArgsTUI = args.length === 0 || (args.length === 1 && args[0] === '--debug');
|
|
402
|
+
if (isRunnerTUI || isRunTUI || isInitTUI || isNoArgsTUI) {
|
|
403
|
+
process.env.SILENT_MODE = '1';
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const __filename$1 = fileURLToPath(import.meta.url);
|
|
407
|
+
const __dirname$1 = dirname(__filename$1);
|
|
408
|
+
// Find the package root by looking for package.json
|
|
409
|
+
// This works regardless of where the bundled code ends up (dist/, dist/cli/, etc.)
|
|
410
|
+
function findPackageRoot(startDir) {
|
|
411
|
+
let dir = startDir;
|
|
412
|
+
for (let i = 0; i < 5; i++) { // Max 5 levels up
|
|
413
|
+
if (existsSync(join(dir, 'package.json'))) {
|
|
414
|
+
return dir;
|
|
415
|
+
}
|
|
416
|
+
const parent = dirname(dir);
|
|
417
|
+
if (parent === dir)
|
|
418
|
+
break; // Reached filesystem root
|
|
419
|
+
dir = parent;
|
|
420
|
+
}
|
|
421
|
+
return startDir; // Fallback to start dir
|
|
422
|
+
}
|
|
423
|
+
const packageRoot = findPackageRoot(__dirname$1);
|
|
424
|
+
// Check if running in development mode (linked via pnpm/npm link)
|
|
425
|
+
// Skip vendor install if we're in the monorepo - dependencies are handled by pnpm
|
|
426
|
+
const isLinkedDevelopment = packageRoot.includes('/openbuilder/apps/runner');
|
|
427
|
+
// Only run vendor install for production global installs
|
|
428
|
+
if (!isLinkedDevelopment) {
|
|
429
|
+
// Check if Sentry packages are missing and extract from vendor if needed
|
|
430
|
+
// (agent-core is bundled by tsup, but Sentry packages come from vendor tarballs)
|
|
431
|
+
const nodeModulesDir = dirname(packageRoot); // Go up from package to node_modules/@openbuilder
|
|
432
|
+
const sentryNodePath = join(nodeModulesDir, "..", "@sentry", "node");
|
|
433
|
+
if (!existsSync(sentryNodePath)) {
|
|
434
|
+
// Silently initialize vendor packages in background
|
|
435
|
+
try {
|
|
436
|
+
const installScript = join(packageRoot, "scripts/install-vendor.js");
|
|
437
|
+
execFileSync("node", [installScript], {
|
|
438
|
+
cwd: packageRoot,
|
|
439
|
+
stdio: "pipe" // Silent mode - output only shown if VERBOSE=1
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
console.error("Failed to initialize vendor packages:", error);
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
// Get package.json for version info
|
|
449
|
+
const packageJson = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf-8'));
|
|
450
|
+
// Setup global error handlers for uncaught errors
|
|
451
|
+
setupGlobalErrorHandlers();
|
|
452
|
+
// Setup graceful shutdown handlers for Ctrl+C
|
|
453
|
+
const shutdownHandler = setupShutdownHandler({
|
|
454
|
+
timeout: 5000,
|
|
455
|
+
verbose: true,
|
|
456
|
+
});
|
|
457
|
+
// Check if we're running in TUI mode or version mode - skip banner if so
|
|
458
|
+
const args = process.argv.slice(2);
|
|
459
|
+
const isInitWithYes = args[0] === 'init' && (args.includes('-y') || args.includes('--yes') || args.includes('--non-interactive'));
|
|
460
|
+
const isNoArgs = args.length === 0 || (args.length === 1 && args[0] === '--debug');
|
|
461
|
+
const isRunCommand = args[0] === 'run'; // `openbuilder run` uses TUI Dashboard
|
|
462
|
+
const isRunnerCommand = args[0] === 'runner' && !args.includes('--no-tui'); // `openbuilder runner` uses TUI Dashboard (unless --no-tui)
|
|
463
|
+
const isVersionCommand = args.includes('--version') || args.includes('-V'); // Skip banner for version output
|
|
464
|
+
const isSkipBanner = process.env.OPENBUILDER_SKIP_BANNER === '1'; // Skip banner after auto-update restart
|
|
465
|
+
const isTUIMode = isInitWithYes || isNoArgs || isRunCommand || isRunnerCommand;
|
|
466
|
+
const isSilentMode = isTUIMode || isVersionCommand || isSkipBanner;
|
|
467
|
+
// Set SILENT_MODE for TUI/version to suppress all console output from other modules
|
|
468
|
+
// This must be set early, before modules that use console.log are imported
|
|
469
|
+
if (isSilentMode) {
|
|
470
|
+
process.env.SILENT_MODE = '1';
|
|
471
|
+
}
|
|
472
|
+
// Auto-update check - do this BEFORE displaying banner to avoid double banners
|
|
473
|
+
// For TUI modes: check only and store result for display (don't auto-update to avoid disruption)
|
|
474
|
+
// For CLI modes: full auto-update with restart
|
|
475
|
+
// For version mode: skip entirely - just show version
|
|
476
|
+
let willAutoUpdate = false;
|
|
477
|
+
if (!process.env.OPENBUILDER_SKIP_UPDATE_CHECK && !isVersionCommand) {
|
|
478
|
+
const { checkAndAutoUpdate, checkForUpdate } = await import('../chunks/auto-update-Dj3lWPWO.js');
|
|
479
|
+
try {
|
|
480
|
+
if (isTUIMode) {
|
|
481
|
+
// TUI mode: just check for updates, store result for TUI to display
|
|
482
|
+
const updateInfo = await checkForUpdate(packageJson.version);
|
|
483
|
+
if (updateInfo?.updateAvailable) {
|
|
484
|
+
// Store update info for TUI components to access
|
|
485
|
+
process.env.OPENBUILDER_UPDATE_AVAILABLE = updateInfo.latestVersion;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
// CLI mode: full auto-update
|
|
490
|
+
// Show banner first since we're in CLI mode and might not auto-update
|
|
491
|
+
displayBanner();
|
|
492
|
+
const didUpdate = await checkAndAutoUpdate(packageJson.version);
|
|
493
|
+
if (didUpdate) {
|
|
494
|
+
// CLI will be relaunched by auto-update, exit this process
|
|
495
|
+
willAutoUpdate = true;
|
|
496
|
+
process.exit(0);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
else if (!isSilentMode) {
|
|
504
|
+
// Update check skipped, show banner for CLI mode (but not version mode)
|
|
505
|
+
displayBanner();
|
|
506
|
+
}
|
|
507
|
+
const program = new Command();
|
|
508
|
+
program
|
|
509
|
+
.name('openbuilder')
|
|
510
|
+
.description('OpenBuilder CLI - AI App Builder')
|
|
511
|
+
.version(packageJson.version)
|
|
512
|
+
.option('--runner', 'Start runner only (connect to remote server)')
|
|
513
|
+
.option('--debug', 'Enable debug mode with verbose error output')
|
|
514
|
+
.hook('preAction', (thisCommand) => {
|
|
515
|
+
// Enable debug mode if --debug flag is present
|
|
516
|
+
const opts = thisCommand.opts();
|
|
517
|
+
if (opts.debug) {
|
|
518
|
+
globalErrorHandler.setDebug(true);
|
|
519
|
+
process.env.DEBUG = '1';
|
|
520
|
+
}
|
|
521
|
+
})
|
|
522
|
+
.action(async (options) => {
|
|
523
|
+
try {
|
|
524
|
+
// Default action when no subcommand is provided
|
|
525
|
+
if (options.runner) {
|
|
526
|
+
// Start runner only (legacy flag)
|
|
527
|
+
const { runCommand } = await import('../chunks/run-D23hg4xy.js');
|
|
528
|
+
await runCommand({});
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
// Show TUI main menu
|
|
532
|
+
const { mainTUICommand } = await import('../chunks/main-tui-Cq1hLCx-.js');
|
|
533
|
+
await mainTUICommand();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
globalErrorHandler.handle(error);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
// Import commands
|
|
541
|
+
program
|
|
542
|
+
.command('init')
|
|
543
|
+
.description('Initialize workspace and configuration for local development')
|
|
544
|
+
.option('--workspace <path>', 'Set workspace directory')
|
|
545
|
+
.option('--url <url>', 'Set server URL (default: http://localhost:3000)')
|
|
546
|
+
.option('--secret <secret>', 'Set shared secret')
|
|
547
|
+
.option('--branch <branch>', 'Git branch to clone (default: main)')
|
|
548
|
+
.option('--database [value]', 'Database setup: connection string, or omit to auto-setup Neon in -y mode')
|
|
549
|
+
.option('-y, --yes', 'Accept all defaults (non-interactive mode)')
|
|
550
|
+
.option('--non-interactive', 'Use defaults without prompts (alias for -y)')
|
|
551
|
+
.action(async (options) => {
|
|
552
|
+
try {
|
|
553
|
+
const { initCommand } = await import('../chunks/init-CZoN6soU.js');
|
|
554
|
+
await initCommand(options);
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
globalErrorHandler.handle(error);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
program
|
|
561
|
+
.command('run')
|
|
562
|
+
.description('Start the full stack locally (web app + runner)')
|
|
563
|
+
.option('-p, --port <port>', 'Web app port (default: 3000)')
|
|
564
|
+
.option('--dev', 'Use development mode (hot reload, slower startup)')
|
|
565
|
+
.option('--rebuild', 'Rebuild services before starting')
|
|
566
|
+
.option('--no-local', 'Disable local mode (require authentication)')
|
|
567
|
+
.option('--no-tui', 'Disable TUI dashboard, use plain text logs')
|
|
568
|
+
.option('-v, --verbose', 'Enable verbose logging (show debug info)')
|
|
569
|
+
.action(async (options) => {
|
|
570
|
+
try {
|
|
571
|
+
const { startCommand } = await import('../chunks/start-BygPCbvw.js');
|
|
572
|
+
await startCommand(options);
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
globalErrorHandler.handle(error);
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
program
|
|
579
|
+
.command('build')
|
|
580
|
+
.description('Build all services without starting (useful while app is running)')
|
|
581
|
+
.option('--watch', 'Watch for changes and rebuild automatically')
|
|
582
|
+
.action(async (options) => {
|
|
583
|
+
try {
|
|
584
|
+
const { buildCommand } = await import('../chunks/build-D0qYqIq0.js');
|
|
585
|
+
await buildCommand(options);
|
|
586
|
+
}
|
|
587
|
+
catch (error) {
|
|
588
|
+
globalErrorHandler.handle(error);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
program
|
|
592
|
+
.command('runner')
|
|
593
|
+
.description('Start runner only (connect to OpenBuilder server)')
|
|
594
|
+
.option('-u, --url <url>', 'OpenBuilder server URL (default: https://openbuilder.up.railway.app)')
|
|
595
|
+
.option('-w, --workspace <path>', 'Workspace directory (default: ~/openbuilder-workspace)')
|
|
596
|
+
.option('-i, --runner-id <id>', 'Runner identifier (default: system username)')
|
|
597
|
+
.option('-s, --secret <secret>', 'Shared secret for authentication (required)')
|
|
598
|
+
.option('-b, --broker <url>', 'WebSocket URL override (advanced, inferred from --url)')
|
|
599
|
+
.option('-v, --verbose', 'Enable verbose logging')
|
|
600
|
+
.option('-l, --local', 'Enable local mode (bypasses authentication)')
|
|
601
|
+
.option('--no-tui', 'Disable TUI dashboard, use plain text logs')
|
|
602
|
+
.action(async (options) => {
|
|
603
|
+
try {
|
|
604
|
+
const { runCommand } = await import('../chunks/run-D23hg4xy.js');
|
|
605
|
+
await runCommand(options);
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
globalErrorHandler.handle(error);
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
program
|
|
612
|
+
.command('config <action> [key] [value]')
|
|
613
|
+
.description('Manage configuration (actions: get, set, list, path, validate, reset)')
|
|
614
|
+
.action(async (action, key, value) => {
|
|
615
|
+
try {
|
|
616
|
+
const { configCommand } = await import('../chunks/config-BGP1jZJ4.js');
|
|
617
|
+
await configCommand(action, key, value);
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
globalErrorHandler.handle(error);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
// Alias for config validate
|
|
624
|
+
program
|
|
625
|
+
.command('verify')
|
|
626
|
+
.description('Verify configuration is valid (alias for config validate)')
|
|
627
|
+
.action(async () => {
|
|
628
|
+
try {
|
|
629
|
+
const { configCommand } = await import('../chunks/config-BGP1jZJ4.js');
|
|
630
|
+
await configCommand('validate');
|
|
631
|
+
}
|
|
632
|
+
catch (error) {
|
|
633
|
+
globalErrorHandler.handle(error);
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
program
|
|
637
|
+
.command('status')
|
|
638
|
+
.description('Show runner status and configuration')
|
|
639
|
+
.action(async () => {
|
|
640
|
+
try {
|
|
641
|
+
const { statusCommand } = await import('../chunks/status-cS8YwtUx.js');
|
|
642
|
+
await statusCommand();
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
globalErrorHandler.handle(error);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
program
|
|
649
|
+
.command('cleanup')
|
|
650
|
+
.description('Clean up projects and resources')
|
|
651
|
+
.option('--project <slug>', 'Delete specific project')
|
|
652
|
+
.option('--all', 'Clean all projects in workspace')
|
|
653
|
+
.option('--tunnels', 'Close all active tunnels')
|
|
654
|
+
.option('--processes', 'Kill all dev servers')
|
|
655
|
+
.action(async (options) => {
|
|
656
|
+
try {
|
|
657
|
+
const { cleanupCommand } = await import('../chunks/cleanup-qVTsA3tk.js');
|
|
658
|
+
await cleanupCommand(options);
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
globalErrorHandler.handle(error);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
program
|
|
665
|
+
.command('database')
|
|
666
|
+
.alias('db')
|
|
667
|
+
.description('Set up a new database and initialize schema')
|
|
668
|
+
.action(async () => {
|
|
669
|
+
try {
|
|
670
|
+
const { databaseCommand } = await import('../chunks/database-BvAbD4sP.js');
|
|
671
|
+
await databaseCommand();
|
|
672
|
+
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
globalErrorHandler.handle(error);
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
program
|
|
678
|
+
.command('upgrade')
|
|
679
|
+
.description('Upgrade to latest version (preserves configuration)')
|
|
680
|
+
.option('--branch <branch>', 'Upgrade to specific branch (default: main)')
|
|
681
|
+
.option('--force', 'Skip safety checks (uncommitted changes)')
|
|
682
|
+
.action(async (options) => {
|
|
683
|
+
try {
|
|
684
|
+
const { upgradeCommand } = await import('../chunks/upgrade-CT6w0lKp.js');
|
|
685
|
+
await upgradeCommand(options);
|
|
686
|
+
}
|
|
687
|
+
catch (error) {
|
|
688
|
+
globalErrorHandler.handle(error);
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
program.parse();
|
|
692
|
+
|
|
693
|
+
export { shutdownHandler };
|
|
694
|
+
//# sourceMappingURL=index.js.map
|