@orcapt/cli 1.0.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/LICENSE +22 -0
- package/QUICK_START.md +241 -0
- package/README.md +949 -0
- package/bin/orca.js +406 -0
- package/package.json +58 -0
- package/src/commands/db.js +248 -0
- package/src/commands/fetch-doc.js +220 -0
- package/src/commands/kickstart-node.js +431 -0
- package/src/commands/kickstart-python.js +360 -0
- package/src/commands/lambda.js +736 -0
- package/src/commands/login.js +277 -0
- package/src/commands/storage.js +911 -0
- package/src/commands/ui.js +286 -0
- package/src/config.js +62 -0
- package/src/utils/docker-helper.js +357 -0
- package/src/utils/index.js +349 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for Orca CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { spawn } = require('cross-spawn');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if a command exists in the system
|
|
10
|
+
* @param {string} command - Command to check
|
|
11
|
+
* @returns {Promise<boolean>}
|
|
12
|
+
*/
|
|
13
|
+
async function commandExists(command) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const child = spawn(command, ['--version'], {
|
|
16
|
+
stdio: 'ignore',
|
|
17
|
+
shell: false
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
child.on('close', (code) => {
|
|
21
|
+
resolve(code === 0);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
child.on('error', () => {
|
|
25
|
+
resolve(false);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get Python command (python3 or python)
|
|
32
|
+
* @returns {Promise<string|null>}
|
|
33
|
+
*/
|
|
34
|
+
async function getPythonCommand() {
|
|
35
|
+
if (await commandExists('python3')) {
|
|
36
|
+
return 'python3';
|
|
37
|
+
}
|
|
38
|
+
if (await commandExists('python')) {
|
|
39
|
+
return 'python';
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the virtual environment paths based on OS
|
|
46
|
+
* @returns {Object}
|
|
47
|
+
*/
|
|
48
|
+
function getVenvPaths() {
|
|
49
|
+
const isWindows = process.platform === 'win32';
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
python: isWindows ? 'orca_env\\Scripts\\python.exe' : 'orca_env/bin/python',
|
|
53
|
+
pip: isWindows ? 'orca_env\\Scripts\\pip.exe' : 'orca_env/bin/pip',
|
|
54
|
+
activate: isWindows ? 'orca_env\\Scripts\\activate' : 'source orca_env/bin/activate'
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Run a command and stream output
|
|
60
|
+
* @param {string} command - Command to run
|
|
61
|
+
* @param {Array} args - Command arguments
|
|
62
|
+
* @param {Object} options - Spawn options
|
|
63
|
+
* @returns {Promise<number>} Exit code
|
|
64
|
+
*/
|
|
65
|
+
function runCommand(command, args = [], options = {}) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const child = spawn(command, args, {
|
|
68
|
+
stdio: 'inherit',
|
|
69
|
+
shell: false,
|
|
70
|
+
...options
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
child.on('close', (code) => {
|
|
74
|
+
if (code === 0) {
|
|
75
|
+
resolve(code);
|
|
76
|
+
} else {
|
|
77
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
child.on('error', (error) => {
|
|
82
|
+
reject(error);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Run a command silently and return output
|
|
89
|
+
* @param {string} command - Command to run
|
|
90
|
+
* @param {Array} args - Command arguments
|
|
91
|
+
* @param {Object} options - Spawn options
|
|
92
|
+
* @returns {Promise<string>} Output
|
|
93
|
+
*/
|
|
94
|
+
function runCommandSilent(command, args = [], options = {}) {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
const child = spawn(command, args, {
|
|
97
|
+
stdio: 'pipe',
|
|
98
|
+
shell: false,
|
|
99
|
+
...options
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
let output = '';
|
|
103
|
+
let errorOutput = '';
|
|
104
|
+
|
|
105
|
+
if (child.stdout) {
|
|
106
|
+
child.stdout.on('data', (data) => {
|
|
107
|
+
output += data.toString();
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (child.stderr) {
|
|
112
|
+
child.stderr.on('data', (data) => {
|
|
113
|
+
errorOutput += data.toString();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
child.on('close', (code) => {
|
|
118
|
+
if (code === 0) {
|
|
119
|
+
resolve(output);
|
|
120
|
+
} else {
|
|
121
|
+
reject(new Error(errorOutput || output));
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
child.on('error', (error) => {
|
|
126
|
+
reject(error);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Spawn a background process
|
|
133
|
+
* @param {string} command - Command to run
|
|
134
|
+
* @param {Array} args - Command arguments
|
|
135
|
+
* @param {Object} options - Spawn options
|
|
136
|
+
* @returns {ChildProcess}
|
|
137
|
+
*/
|
|
138
|
+
function spawnBackground(command, args = [], options = {}) {
|
|
139
|
+
return spawn(command, args, {
|
|
140
|
+
stdio: 'pipe',
|
|
141
|
+
shell: false,
|
|
142
|
+
detached: false,
|
|
143
|
+
...options
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Wait for a port to be ready
|
|
149
|
+
* @param {number} port - Port to check
|
|
150
|
+
* @param {number} timeout - Timeout in ms
|
|
151
|
+
* @returns {Promise<boolean>}
|
|
152
|
+
*/
|
|
153
|
+
async function waitForPort(port, timeout = 10000) {
|
|
154
|
+
const net = require('net');
|
|
155
|
+
const startTime = Date.now();
|
|
156
|
+
|
|
157
|
+
return new Promise((resolve) => {
|
|
158
|
+
const checkPort = () => {
|
|
159
|
+
if (Date.now() - startTime > timeout) {
|
|
160
|
+
resolve(false);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const socket = new net.Socket();
|
|
165
|
+
|
|
166
|
+
socket.setTimeout(500);
|
|
167
|
+
socket.on('connect', () => {
|
|
168
|
+
socket.destroy();
|
|
169
|
+
resolve(true);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
socket.on('timeout', () => {
|
|
173
|
+
socket.destroy();
|
|
174
|
+
setTimeout(checkPort, 500);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
socket.on('error', () => {
|
|
178
|
+
socket.destroy();
|
|
179
|
+
setTimeout(checkPort, 500);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
socket.connect(port, '127.0.0.1');
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
checkPort();
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Check if a port is in use
|
|
191
|
+
* @param {number} port - Port to check
|
|
192
|
+
* @returns {Promise<boolean>}
|
|
193
|
+
*/
|
|
194
|
+
async function isPortInUse(port) {
|
|
195
|
+
const net = require('net');
|
|
196
|
+
|
|
197
|
+
return new Promise((resolve) => {
|
|
198
|
+
const server = net.createServer();
|
|
199
|
+
|
|
200
|
+
server.once('error', (err) => {
|
|
201
|
+
if (err.code === 'EADDRINUSE') {
|
|
202
|
+
resolve(true);
|
|
203
|
+
} else {
|
|
204
|
+
resolve(false);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
server.once('listening', () => {
|
|
209
|
+
server.close();
|
|
210
|
+
resolve(false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
server.listen(port, '127.0.0.1');
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Kill process using a specific port
|
|
219
|
+
* @param {number} port - Port number
|
|
220
|
+
* @returns {Promise<boolean>} True if a process was killed
|
|
221
|
+
*/
|
|
222
|
+
async function killPort(port) {
|
|
223
|
+
const { exec } = require('child_process');
|
|
224
|
+
const util = require('util');
|
|
225
|
+
const execPromise = util.promisify(exec);
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const isWindows = process.platform === 'win32';
|
|
229
|
+
const isMac = process.platform === 'darwin';
|
|
230
|
+
|
|
231
|
+
if (isWindows) {
|
|
232
|
+
// Windows: Find and kill process using the port
|
|
233
|
+
try {
|
|
234
|
+
const { stdout } = await execPromise(`netstat -ano | findstr :${port}`);
|
|
235
|
+
const lines = stdout.trim().split('\n');
|
|
236
|
+
const pids = new Set();
|
|
237
|
+
|
|
238
|
+
for (const line of lines) {
|
|
239
|
+
const parts = line.trim().split(/\s+/);
|
|
240
|
+
const pid = parts[parts.length - 1];
|
|
241
|
+
if (pid && pid !== '0') {
|
|
242
|
+
pids.add(pid);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (const pid of pids) {
|
|
247
|
+
try {
|
|
248
|
+
await execPromise(`taskkill /F /PID ${pid}`);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
// Process might already be dead
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return pids.size > 0;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
} else if (isMac) {
|
|
259
|
+
// macOS: Use lsof to find and kill process
|
|
260
|
+
try {
|
|
261
|
+
const { stdout } = await execPromise(`lsof -ti:${port}`);
|
|
262
|
+
const pids = stdout.trim().split('\n').filter(pid => pid);
|
|
263
|
+
|
|
264
|
+
for (const pid of pids) {
|
|
265
|
+
try {
|
|
266
|
+
await execPromise(`kill -9 ${pid}`);
|
|
267
|
+
} catch (e) {
|
|
268
|
+
// Process might already be dead
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return pids.length > 0;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
// Linux: Use fuser or lsof
|
|
278
|
+
try {
|
|
279
|
+
const { stdout } = await execPromise(`lsof -ti:${port} 2>/dev/null || fuser ${port}/tcp 2>/dev/null`);
|
|
280
|
+
const pids = stdout.trim().split(/\s+/).filter(pid => pid);
|
|
281
|
+
|
|
282
|
+
for (const pid of pids) {
|
|
283
|
+
try {
|
|
284
|
+
await execPromise(`kill -9 ${pid}`);
|
|
285
|
+
} catch (e) {
|
|
286
|
+
// Process might already be dead
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return pids.length > 0;
|
|
291
|
+
} catch (error) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Ensure port is available by killing any process using it
|
|
302
|
+
* @param {number} port - Port to check and free
|
|
303
|
+
* @param {string} portName - Name for logging (e.g., 'UI', 'Agent')
|
|
304
|
+
* @returns {Promise<void>}
|
|
305
|
+
*/
|
|
306
|
+
async function ensurePortAvailable(port, portName = 'Port') {
|
|
307
|
+
const chalk = require('chalk');
|
|
308
|
+
|
|
309
|
+
if (await isPortInUse(port)) {
|
|
310
|
+
console.log(chalk.yellow(`⚠ ${portName} port ${port} is in use, killing the process...`));
|
|
311
|
+
const killed = await killPort(port);
|
|
312
|
+
|
|
313
|
+
if (killed) {
|
|
314
|
+
console.log(chalk.green(`✓ Freed port ${port}`));
|
|
315
|
+
// Wait a moment for the port to be fully released
|
|
316
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
317
|
+
} else {
|
|
318
|
+
console.log(chalk.yellow(`⚠ Could not kill process on port ${port}, will try to start anyway...`));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Print formatted messages
|
|
325
|
+
*/
|
|
326
|
+
const print = {
|
|
327
|
+
title: (text) => console.log(chalk.magenta.bold(`\n${'='.repeat(60)}\n${text}\n${'='.repeat(60)}\n`)),
|
|
328
|
+
step: (text) => console.log(chalk.cyan(`► ${text}`)),
|
|
329
|
+
success: (text) => console.log(chalk.green(`✓ ${text}`)),
|
|
330
|
+
error: (text) => console.log(chalk.red(`✗ ${text}`)),
|
|
331
|
+
warning: (text) => console.log(chalk.yellow(`⚠ ${text}`)),
|
|
332
|
+
info: (text) => console.log(chalk.blue(`ℹ ${text}`)),
|
|
333
|
+
url: (label, url) => console.log(chalk.cyan(`${label}:`), chalk.underline(url))
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
module.exports = {
|
|
337
|
+
commandExists,
|
|
338
|
+
getPythonCommand,
|
|
339
|
+
getVenvPaths,
|
|
340
|
+
runCommand,
|
|
341
|
+
runCommandSilent,
|
|
342
|
+
spawnBackground,
|
|
343
|
+
waitForPort,
|
|
344
|
+
isPortInUse,
|
|
345
|
+
killPort,
|
|
346
|
+
ensurePortAvailable,
|
|
347
|
+
print
|
|
348
|
+
};
|
|
349
|
+
|