@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,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kickstart command - Quick setup for a new Orca project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs').promises;
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const inquirer = require('inquirer');
|
|
9
|
+
const ora = require('ora');
|
|
10
|
+
const simpleGit = require('simple-git');
|
|
11
|
+
const {
|
|
12
|
+
commandExists,
|
|
13
|
+
getPythonCommand,
|
|
14
|
+
getVenvPaths,
|
|
15
|
+
runCommand,
|
|
16
|
+
runCommandSilent,
|
|
17
|
+
spawnBackground,
|
|
18
|
+
waitForPort,
|
|
19
|
+
ensurePortAvailable,
|
|
20
|
+
print
|
|
21
|
+
} = require('../utils');
|
|
22
|
+
const { GITHUB_REPOS } = require('../config');
|
|
23
|
+
|
|
24
|
+
const REPO_URL = GITHUB_REPOS.PYTHON_STARTER;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check all prerequisites
|
|
28
|
+
*/
|
|
29
|
+
async function checkPrerequisites() {
|
|
30
|
+
const spinner = ora('Checking prerequisites...').start();
|
|
31
|
+
const missing = [];
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Check Python
|
|
35
|
+
const pythonCmd = await getPythonCommand();
|
|
36
|
+
if (!pythonCmd) {
|
|
37
|
+
missing.push('Python 3.8+');
|
|
38
|
+
spinner.warn(chalk.yellow('Python not found'));
|
|
39
|
+
} else {
|
|
40
|
+
spinner.succeed(chalk.green(`Python found: ${pythonCmd}`));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check Git
|
|
44
|
+
spinner.start('Checking Git...');
|
|
45
|
+
if (await commandExists('git')) {
|
|
46
|
+
spinner.succeed(chalk.green('Git found'));
|
|
47
|
+
} else {
|
|
48
|
+
missing.push('Git');
|
|
49
|
+
spinner.warn(chalk.yellow('Git not found'));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check Node.js/npm
|
|
53
|
+
spinner.start('Checking Node.js...');
|
|
54
|
+
if (await commandExists('node') && await commandExists('npm')) {
|
|
55
|
+
spinner.succeed(chalk.green('Node.js/npm found'));
|
|
56
|
+
} else {
|
|
57
|
+
missing.push('Node.js/npm');
|
|
58
|
+
spinner.warn(chalk.yellow('Node.js/npm not found'));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check npx
|
|
62
|
+
spinner.start('Checking npx...');
|
|
63
|
+
if (await commandExists('npx')) {
|
|
64
|
+
spinner.succeed(chalk.green('npx found'));
|
|
65
|
+
} else {
|
|
66
|
+
missing.push('npx');
|
|
67
|
+
spinner.warn(chalk.yellow('npx not found'));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (missing.length > 0) {
|
|
71
|
+
spinner.stop();
|
|
72
|
+
print.error('Missing prerequisites:');
|
|
73
|
+
missing.forEach(item => console.log(` - ${item}`));
|
|
74
|
+
console.log();
|
|
75
|
+
print.info('Please install the missing prerequisites:');
|
|
76
|
+
console.log(' - Python: https://www.python.org/downloads/');
|
|
77
|
+
console.log(' - Git: https://git-scm.com/downloads');
|
|
78
|
+
console.log(' - Node.js: https://nodejs.org/\n');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
spinner.stop();
|
|
83
|
+
return await getPythonCommand();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
spinner.fail('Failed to check prerequisites');
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Clone repository
|
|
92
|
+
*/
|
|
93
|
+
async function cloneRepository(directory) {
|
|
94
|
+
const spinner = ora('Cloning Orca starter kit from GitHub...').start();
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const git = simpleGit();
|
|
98
|
+
await git.clone(REPO_URL, directory);
|
|
99
|
+
spinner.succeed(chalk.green('Repository cloned successfully'));
|
|
100
|
+
} catch (error) {
|
|
101
|
+
spinner.fail('Failed to clone repository');
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Create virtual environment
|
|
108
|
+
*/
|
|
109
|
+
async function createVirtualEnv(projectPath, pythonCmd) {
|
|
110
|
+
const spinner = ora('Creating Python virtual environment...').start();
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
await runCommandSilent(pythonCmd, ['-m', 'venv', 'orca_env'], {
|
|
114
|
+
cwd: projectPath
|
|
115
|
+
});
|
|
116
|
+
spinner.succeed(chalk.green('Virtual environment created'));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
spinner.fail('Failed to create virtual environment');
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Install dependencies
|
|
125
|
+
*/
|
|
126
|
+
async function installDependencies(projectPath) {
|
|
127
|
+
const spinner = ora('Installing Python dependencies...').start();
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const venvPaths = getVenvPaths();
|
|
131
|
+
const pipPath = path.join(projectPath, venvPaths.pip);
|
|
132
|
+
|
|
133
|
+
await runCommandSilent(pipPath, ['install', '-r', 'requirements.txt'], {
|
|
134
|
+
cwd: projectPath
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
spinner.succeed(chalk.green('Dependencies installed'));
|
|
138
|
+
} catch (error) {
|
|
139
|
+
spinner.fail('Failed to install dependencies');
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Start backend server
|
|
146
|
+
*/
|
|
147
|
+
async function startBackend(projectPath, agentPort) {
|
|
148
|
+
const spinner = ora(`Starting agent server on port ${agentPort}...`).start();
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const venvPaths = getVenvPaths();
|
|
152
|
+
const pythonPath = path.join(projectPath, venvPaths.python);
|
|
153
|
+
|
|
154
|
+
const backendProcess = spawnBackground(pythonPath, ['main.py', '--dev'], {
|
|
155
|
+
cwd: projectPath,
|
|
156
|
+
env: { ...process.env, PORT: agentPort }
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Wait a bit for the server to start
|
|
160
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
161
|
+
|
|
162
|
+
// Check if process is still running
|
|
163
|
+
if (backendProcess.exitCode !== null) {
|
|
164
|
+
throw new Error('Backend process exited immediately');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
spinner.succeed(chalk.green(`Agent started (PID: ${backendProcess.pid})`));
|
|
168
|
+
return backendProcess;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
spinner.fail('Failed to start agent');
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Start frontend server
|
|
177
|
+
*/
|
|
178
|
+
async function startFrontend(projectPath, port, agentPort) {
|
|
179
|
+
const spinner = ora(`Starting Orca-UI server on port ${port}...`).start();
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const tryStart = async (pkgOrBin) => {
|
|
183
|
+
const proc = spawnBackground('npx', [
|
|
184
|
+
'-y',
|
|
185
|
+
pkgOrBin,
|
|
186
|
+
`--port=${port}`,
|
|
187
|
+
`--agent-port=${agentPort}`
|
|
188
|
+
], { cwd: projectPath });
|
|
189
|
+
// Wait for frontend to start
|
|
190
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
191
|
+
if (proc.exitCode !== null) {
|
|
192
|
+
throw new Error(`Frontend process exited immediately (${pkgOrBin})`);
|
|
193
|
+
}
|
|
194
|
+
return proc;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
let frontendProcess;
|
|
198
|
+
try {
|
|
199
|
+
// Prefer new bin name
|
|
200
|
+
frontendProcess = await tryStart('orca-ui');
|
|
201
|
+
} catch (_) {
|
|
202
|
+
// Fallback to package name
|
|
203
|
+
frontendProcess = await tryStart('@orca/ui');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
spinner.succeed(chalk.green(`Orca-UI started (PID: ${frontendProcess.pid})`));
|
|
207
|
+
return frontendProcess;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
spinner.fail('Failed to start Orca-UI');
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Main kickstart command
|
|
216
|
+
*/
|
|
217
|
+
async function kickstart(options) {
|
|
218
|
+
try {
|
|
219
|
+
const { directory, port, agentPort, start } = options;
|
|
220
|
+
|
|
221
|
+
print.title('🚀 Orca Kickstart - Python');
|
|
222
|
+
|
|
223
|
+
// Check prerequisites
|
|
224
|
+
const pythonCmd = await checkPrerequisites();
|
|
225
|
+
|
|
226
|
+
// Check if directory exists
|
|
227
|
+
const projectPath = path.resolve(process.cwd(), directory);
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
await fs.access(projectPath);
|
|
231
|
+
print.error(`Directory '${directory}' already exists`);
|
|
232
|
+
|
|
233
|
+
const { shouldRemove } = await inquirer.prompt([
|
|
234
|
+
{
|
|
235
|
+
type: 'confirm',
|
|
236
|
+
name: 'shouldRemove',
|
|
237
|
+
message: 'Do you want to remove it and continue?',
|
|
238
|
+
default: false
|
|
239
|
+
}
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
if (!shouldRemove) {
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
await fs.rm(projectPath, { recursive: true, force: true });
|
|
247
|
+
} catch (error) {
|
|
248
|
+
// Directory doesn't exist, which is fine
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Create directory
|
|
252
|
+
print.step(`Creating directory: ${directory}`);
|
|
253
|
+
await fs.mkdir(projectPath, { recursive: true });
|
|
254
|
+
print.success(`Created directory: ${projectPath}`);
|
|
255
|
+
|
|
256
|
+
// Clone repository
|
|
257
|
+
await cloneRepository(projectPath);
|
|
258
|
+
|
|
259
|
+
// Create virtual environment
|
|
260
|
+
await createVirtualEnv(projectPath, pythonCmd);
|
|
261
|
+
|
|
262
|
+
// Install dependencies
|
|
263
|
+
await installDependencies(projectPath);
|
|
264
|
+
|
|
265
|
+
// Setup complete
|
|
266
|
+
print.title('✓ Setup completed successfully!');
|
|
267
|
+
|
|
268
|
+
// Ask to start servers
|
|
269
|
+
const shouldStart = start !== false ? await inquirer.prompt([
|
|
270
|
+
{
|
|
271
|
+
type: 'confirm',
|
|
272
|
+
name: 'start',
|
|
273
|
+
message: 'Do you want to start the backend and frontend servers now?',
|
|
274
|
+
default: true
|
|
275
|
+
}
|
|
276
|
+
]).then(answers => answers.start) : false;
|
|
277
|
+
|
|
278
|
+
if (!shouldStart) {
|
|
279
|
+
console.log();
|
|
280
|
+
print.info('To start manually:');
|
|
281
|
+
console.log(chalk.gray(` cd ${directory}`));
|
|
282
|
+
console.log(chalk.gray(` ${getVenvPaths().activate}`));
|
|
283
|
+
console.log(chalk.gray(` python main.py --dev`));
|
|
284
|
+
console.log(chalk.gray(` # In another terminal:`));
|
|
285
|
+
console.log(chalk.gray(` npx -y @orca/ui orca --port=${port} --agent-port=${agentPort}`));
|
|
286
|
+
console.log();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Start servers
|
|
291
|
+
console.log();
|
|
292
|
+
// Ensure ports are available before starting
|
|
293
|
+
await ensurePortAvailable(agentPort, 'Agent');
|
|
294
|
+
await ensurePortAvailable(port, 'UI');
|
|
295
|
+
|
|
296
|
+
const backendProcess = await startBackend(projectPath, agentPort);
|
|
297
|
+
const frontendProcess = await startFrontend(projectPath, port, agentPort);
|
|
298
|
+
|
|
299
|
+
// Display success message
|
|
300
|
+
print.title('🎉 Orca is running!');
|
|
301
|
+
print.url('Orca-UI', `http://localhost:${port}`);
|
|
302
|
+
print.url('Agent ', `http://localhost:${agentPort}`);
|
|
303
|
+
console.log();
|
|
304
|
+
print.warning('Press Ctrl+C to stop both servers');
|
|
305
|
+
console.log();
|
|
306
|
+
|
|
307
|
+
// Handle graceful shutdown
|
|
308
|
+
const shutdown = () => {
|
|
309
|
+
console.log();
|
|
310
|
+
print.step('Shutting down servers...');
|
|
311
|
+
|
|
312
|
+
if (backendProcess && !backendProcess.killed) {
|
|
313
|
+
backendProcess.kill();
|
|
314
|
+
}
|
|
315
|
+
if (frontendProcess && !frontendProcess.killed) {
|
|
316
|
+
frontendProcess.kill();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
setTimeout(() => {
|
|
320
|
+
print.success('Servers stopped');
|
|
321
|
+
process.exit(0);
|
|
322
|
+
}, 1000);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
process.on('SIGINT', shutdown);
|
|
326
|
+
process.on('SIGTERM', shutdown);
|
|
327
|
+
|
|
328
|
+
// Monitor processes
|
|
329
|
+
backendProcess.on('exit', (code) => {
|
|
330
|
+
if (code !== 0 && code !== null) {
|
|
331
|
+
print.error('Agent stopped unexpectedly');
|
|
332
|
+
if (frontendProcess && !frontendProcess.killed) {
|
|
333
|
+
frontendProcess.kill();
|
|
334
|
+
}
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
frontendProcess.on('exit', (code) => {
|
|
340
|
+
if (code !== 0 && code !== null) {
|
|
341
|
+
print.error('Orca-UI stopped unexpectedly');
|
|
342
|
+
if (backendProcess && !backendProcess.killed) {
|
|
343
|
+
backendProcess.kill();
|
|
344
|
+
}
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Keep process alive
|
|
350
|
+
await new Promise(() => {});
|
|
351
|
+
|
|
352
|
+
} catch (error) {
|
|
353
|
+
print.error(`Failed: ${error.message}`);
|
|
354
|
+
console.error(error);
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
module.exports = kickstart;
|
|
360
|
+
|