@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.
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Orca Database Commands
3
+ * Manage PostgreSQL databases via Orca Deploy API
4
+ */
5
+
6
+ const chalk = require('chalk');
7
+ const ora = require('ora');
8
+ const https = require('https');
9
+ const http = require('http');
10
+ const { getCredentials } = require('./login');
11
+ const { API_BASE_URL, API_ENDPOINTS } = require('../config');
12
+
13
+ /**
14
+ * Make API request to Orca Deploy API
15
+ */
16
+ function makeApiRequest(method, path, credentials) {
17
+ return new Promise((resolve, reject) => {
18
+ const url = new URL(path, API_BASE_URL);
19
+ const isHttps = url.protocol === 'https:';
20
+ const httpModule = isHttps ? https : http;
21
+
22
+ const options = {
23
+ hostname: url.hostname,
24
+ port: url.port || (isHttps ? 443 : 80),
25
+ path: url.pathname,
26
+ method: method,
27
+ headers: {
28
+ 'x-workspace': credentials.workspace,
29
+ 'x-token': credentials.token,
30
+ 'Content-Type': 'application/json',
31
+ 'x-mode' : credentials.mode
32
+ }
33
+ };
34
+
35
+ const req = httpModule.request(options, (res) => {
36
+ let data = '';
37
+
38
+ res.on('data', (chunk) => {
39
+ data += chunk;
40
+ });
41
+
42
+ res.on('end', () => {
43
+ try {
44
+ const response = JSON.parse(data);
45
+ if (res.statusCode >= 200 && res.statusCode < 300) {
46
+ resolve(response);
47
+ } else {
48
+ reject({ statusCode: res.statusCode, response });
49
+ }
50
+ } catch (error) {
51
+ reject(new Error(`Invalid response: ${data}`));
52
+ }
53
+ });
54
+ });
55
+
56
+ req.on('error', (error) => {
57
+ reject(error);
58
+ });
59
+
60
+ req.end();
61
+ });
62
+ }
63
+
64
+ /**
65
+ * DB Create Command - Create a new PostgreSQL database
66
+ */
67
+ async function dbCreate(options) {
68
+ console.log(chalk.cyan('\n============================================================'));
69
+ console.log(chalk.cyan('šŸ—„ļø Creating PostgreSQL Database'));
70
+ console.log(chalk.cyan('============================================================\n'));
71
+
72
+ // Get credentials from login
73
+ const credentials = getCredentials();
74
+ if (!credentials) {
75
+ console.log(chalk.red('āœ— Not authenticated'));
76
+ console.log(chalk.cyan('Please run:'), chalk.yellow('orca login'), chalk.cyan('first\n'));
77
+ process.exit(1);
78
+ }
79
+
80
+ console.log(chalk.white('Workspace:'), chalk.yellow(credentials.workspace));
81
+ console.log(chalk.white('API: '), chalk.yellow(API_BASE_URL));
82
+
83
+ const spinner = ora('Creating database...').start();
84
+
85
+ try {
86
+ // Hybrid DB API endpoint: creates/uses workspace DB and creates a schema
87
+ const response = await makeApiRequest('POST', API_ENDPOINTS.DB_CREATE, credentials);
88
+
89
+ spinner.succeed(chalk.green('Database created successfully!'));
90
+
91
+ console.log(chalk.cyan('\n============================================================'));
92
+ console.log(chalk.green('āœ“ Database Ready'));
93
+ console.log(chalk.cyan('============================================================'));
94
+ // Show actual DB and schema
95
+ console.log(chalk.white('Database: '), chalk.yellow(response.database_name));
96
+ console.log(chalk.white('Schema: '), chalk.yellow(response.schema_name));
97
+ console.log(chalk.white('Username: '), chalk.yellow(response.username));
98
+ console.log(chalk.white('Password: '), chalk.yellow(response.password));
99
+ console.log(chalk.white('Workspace: '), chalk.yellow(response.workspace_name));
100
+
101
+ console.log(chalk.cyan('\nšŸ“‹ Connection String:'));
102
+ // Use the connection string from the response if available
103
+ const connString = response.connection_info?.connection_string ||
104
+ `postgresql://${response.username}:${response.password}@localhost:6432/${response.database_name}?options=-csearch_path%3D${response.schema_name}`;
105
+ console.log(chalk.yellow(` ${connString}`));
106
+
107
+ console.log(chalk.cyan('\nšŸ’” Save these credentials - they won\'t be shown again!'));
108
+ console.log(chalk.cyan('============================================================\n'));
109
+
110
+ } catch (error) {
111
+ spinner.fail(chalk.red('Failed to create database'));
112
+
113
+ if (error.statusCode === 401) {
114
+ console.log(chalk.red('\nāœ— Authentication failed'));
115
+ console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orca login\n'));
116
+ } else if (error.response && error.response.detail) {
117
+ console.log(chalk.red(`\nāœ— ${error.response.detail}\n`));
118
+ } else {
119
+ console.log(chalk.red(`\nāœ— ${error.message}\n`));
120
+ }
121
+ process.exit(1);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * DB List Command - List all databases for workspace
127
+ */
128
+ async function dbList() {
129
+ console.log(chalk.cyan('\n============================================================'));
130
+ console.log(chalk.cyan('šŸ“‹ Listing Databases'));
131
+ console.log(chalk.cyan('============================================================\n'));
132
+
133
+ // Get credentials from login
134
+ const credentials = getCredentials();
135
+ if (!credentials) {
136
+ console.log(chalk.red('āœ— Not authenticated'));
137
+ console.log(chalk.cyan('Please run:'), chalk.yellow('orca login'), chalk.cyan('first\n'));
138
+ process.exit(1);
139
+ }
140
+
141
+ console.log(chalk.white('Workspace:'), chalk.yellow(credentials.workspace));
142
+
143
+ const spinner = ora('Fetching databases...').start();
144
+
145
+ try {
146
+ // DB API endpoint (returns list of { database_name, schema_name })
147
+ const response = await makeApiRequest('GET', API_ENDPOINTS.DB_LIST, credentials);
148
+
149
+ spinner.succeed(chalk.green('Databases retrieved'));
150
+
151
+ console.log(chalk.cyan('\n============================================================'));
152
+
153
+ if (response.count === 0) {
154
+ console.log(chalk.yellow('No databases found'));
155
+ console.log(chalk.cyan('\nCreate one with:'), chalk.white('orca db create --postgres'));
156
+ } else {
157
+ console.log(chalk.green(`āœ“ Found ${response.count} database${response.count > 1 ? 's' : ''}`));
158
+ console.log(chalk.cyan('============================================================\n'));
159
+
160
+ // Show schemas as databases to avoid confusion
161
+ const items = response.databases || response.schemas || [];
162
+ items.forEach((item, index) => {
163
+ const name = typeof item === 'string' ? item : (item.schema_name || item.database_name);
164
+ console.log(chalk.white(` ${index + 1}. ${name}`));
165
+ });
166
+ }
167
+
168
+ console.log(chalk.cyan('============================================================\n'));
169
+
170
+ } catch (error) {
171
+ spinner.fail(chalk.red('Failed to list databases'));
172
+
173
+ if (error.statusCode === 401) {
174
+ console.log(chalk.red('\nāœ— Authentication failed'));
175
+ console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orca login\n'));
176
+ } else if (error.response && error.response.detail) {
177
+ console.log(chalk.red(`\nāœ— ${error.response.detail}\n`));
178
+ } else {
179
+ console.log(chalk.red(`\nāœ— ${error.message}\n`));
180
+ }
181
+ process.exit(1);
182
+ }
183
+ }
184
+
185
+ /**
186
+ * DB Remove Command - Delete a database
187
+ */
188
+ async function dbRemove(databaseName) {
189
+ if (!databaseName) {
190
+ console.log(chalk.red('\nāœ— Database name is required'));
191
+ console.log(chalk.cyan('Usage:'), chalk.white('orca db remove <database-name>\n'));
192
+ process.exit(1);
193
+ }
194
+
195
+ console.log(chalk.cyan('\n============================================================'));
196
+ console.log(chalk.cyan('šŸ—‘ļø Removing Database'));
197
+ console.log(chalk.cyan('============================================================\n'));
198
+
199
+ // Get credentials from login
200
+ const credentials = getCredentials();
201
+ if (!credentials) {
202
+ console.log(chalk.red('āœ— Not authenticated'));
203
+ console.log(chalk.cyan('Please run:'), chalk.yellow('orca login'), chalk.cyan('first\n'));
204
+ process.exit(1);
205
+ }
206
+
207
+ console.log(chalk.white('Database: '), chalk.yellow(databaseName));
208
+ console.log(chalk.white('Workspace:'), chalk.yellow(credentials.workspace));
209
+
210
+ const spinner = ora('Deleting database...').start();
211
+
212
+ try {
213
+ // Delete a single schema under the workspace database
214
+ const response = await makeApiRequest('DELETE', `${API_ENDPOINTS.DB_DELETE}/${databaseName}`, credentials);
215
+
216
+ spinner.succeed(chalk.green('Database deleted successfully!'));
217
+
218
+ console.log(chalk.cyan('\n============================================================'));
219
+ console.log(chalk.green('āœ“ Database Removed'));
220
+ console.log(chalk.cyan('============================================================'));
221
+ // Present schema as database name
222
+ console.log(chalk.white('Database: '), chalk.yellow(response.schema_name || databaseName));
223
+ console.log(chalk.white('Workspace: '), chalk.yellow(response.workspace_name));
224
+ console.log(chalk.cyan('============================================================\n'));
225
+
226
+ } catch (error) {
227
+ spinner.fail(chalk.red('Failed to delete database'));
228
+
229
+ if (error.statusCode === 401) {
230
+ console.log(chalk.red('\nāœ— Authentication failed'));
231
+ console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orca login\n'));
232
+ } else if (error.statusCode === 404) {
233
+ console.log(chalk.red(`\nāœ— Database '${databaseName}' not found or doesn't belong to your workspace\n`));
234
+ } else if (error.response && error.response.detail) {
235
+ console.log(chalk.red(`\nāœ— ${error.response.detail}\n`));
236
+ } else {
237
+ console.log(chalk.red(`\nāœ— ${error.message}\n`));
238
+ }
239
+ process.exit(1);
240
+ }
241
+ }
242
+
243
+ module.exports = {
244
+ dbCreate,
245
+ dbList,
246
+ dbRemove
247
+ };
248
+
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Fetch Documentation Command
3
+ * Downloads Orca SDK documentation based on project type
4
+ */
5
+
6
+ const fs = require('fs').promises;
7
+ const path = require('path');
8
+ const chalk = require('chalk');
9
+ const inquirer = require('inquirer');
10
+ const ora = require('ora');
11
+ const https = require('https');
12
+ const { DOCS_URLS } = require('../config');
13
+
14
+ // Map project types to config keys
15
+ const DOCS_MAP = {
16
+ python: DOCS_URLS.PYTHON,
17
+ nodejs: DOCS_URLS.NODEJS
18
+ };
19
+
20
+ /**
21
+ * Detect project type based on files in current directory
22
+ * @returns {Promise<string|null>} 'python', 'nodejs', or null
23
+ */
24
+ async function detectProjectType() {
25
+ try {
26
+ const files = await fs.readdir(process.cwd());
27
+
28
+ // Check for Python project indicators
29
+ const hasPythonFiles = files.some(file =>
30
+ file === 'requirements.txt' ||
31
+ file === 'setup.py' ||
32
+ file === 'pyproject.toml' ||
33
+ file === 'Pipfile'
34
+ );
35
+
36
+ if (hasPythonFiles) {
37
+ return 'python';
38
+ }
39
+
40
+ // Check for Node.js project indicators
41
+ const hasNodeFiles = files.some(file =>
42
+ file === 'package.json' ||
43
+ file === 'package-lock.json' ||
44
+ file === 'yarn.lock'
45
+ );
46
+
47
+ if (hasNodeFiles) {
48
+ return 'nodejs';
49
+ }
50
+
51
+ return null;
52
+ } catch (error) {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Check if we're in a project root directory
59
+ * @returns {Promise<{isRoot: boolean, detectedType: string|null}>}
60
+ */
61
+ async function checkProjectRoot() {
62
+ const detectedType = await detectProjectType();
63
+ return {
64
+ isRoot: detectedType !== null,
65
+ detectedType
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Download file from URL
71
+ * @param {string} url - URL to download from
72
+ * @param {string} destPath - Destination path
73
+ * @returns {Promise<void>}
74
+ */
75
+ function downloadFile(url, destPath) {
76
+ return new Promise((resolve, reject) => {
77
+ https.get(url, (response) => {
78
+ if (response.statusCode === 302 || response.statusCode === 301) {
79
+ // Handle redirects
80
+ downloadFile(response.headers.location, destPath)
81
+ .then(resolve)
82
+ .catch(reject);
83
+ return;
84
+ }
85
+
86
+ if (response.statusCode !== 200) {
87
+ reject(new Error(`Failed to download: HTTP ${response.statusCode}`));
88
+ return;
89
+ }
90
+
91
+ let data = '';
92
+ response.on('data', (chunk) => {
93
+ data += chunk;
94
+ });
95
+
96
+ response.on('end', async () => {
97
+ try {
98
+ await fs.writeFile(destPath, data, 'utf8');
99
+ resolve();
100
+ } catch (error) {
101
+ reject(error);
102
+ }
103
+ });
104
+ }).on('error', (error) => {
105
+ reject(error);
106
+ });
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Ensure docs directory exists
112
+ * @returns {Promise<string>} Path to docs directory
113
+ */
114
+ async function ensureDocsDirectory() {
115
+ const docsPath = path.join(process.cwd(), 'docs');
116
+
117
+ try {
118
+ await fs.access(docsPath);
119
+ } catch (error) {
120
+ // Directory doesn't exist, create it
121
+ await fs.mkdir(docsPath, { recursive: true });
122
+ console.log(chalk.green('āœ“ Created docs directory'));
123
+ }
124
+
125
+ return docsPath;
126
+ }
127
+
128
+ /**
129
+ * Download documentation for a specific type
130
+ * @param {string} type - 'python' or 'nodejs'
131
+ * @returns {Promise<void>}
132
+ */
133
+ async function downloadDocumentation(type) {
134
+ const spinner = ora(`Downloading Orca ${type === 'python' ? 'Python' : 'Node.js'} SDK documentation...`).start();
135
+
136
+ try {
137
+ const docsPath = await ensureDocsDirectory();
138
+ const fileName = type === 'python' ? 'ORCA_PYTHON_SDK.md' : 'ORCA_NODEJS_SDK.md';
139
+ const filePath = path.join(docsPath, fileName);
140
+
141
+ await downloadFile(DOCS_MAP[type], filePath);
142
+
143
+ spinner.succeed(chalk.green('Documentation downloaded successfully!'));
144
+
145
+ console.log(chalk.cyan('\n============================================================'));
146
+ console.log(chalk.green('āœ“ Documentation Ready'));
147
+ console.log(chalk.cyan('============================================================'));
148
+ console.log(chalk.white('šŸ“„ File:'), chalk.yellow(path.relative(process.cwd(), filePath)));
149
+ console.log(chalk.white('šŸ“¦ Type:'), chalk.yellow(type === 'python' ? 'Python SDK' : 'Node.js SDK'));
150
+ console.log(chalk.white('šŸ“‚ Location:'), chalk.yellow(docsPath));
151
+ console.log(chalk.cyan('============================================================\n'));
152
+
153
+ return filePath;
154
+ } catch (error) {
155
+ spinner.fail(chalk.red('Failed to download documentation'));
156
+ throw error;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Main fetch-doc command
162
+ */
163
+ async function fetchDoc() {
164
+ try {
165
+ console.log(chalk.cyan('\n============================================================'));
166
+ console.log(chalk.cyan('šŸ“š Orca - Fetch Documentation'));
167
+ console.log(chalk.cyan('============================================================\n'));
168
+
169
+ // Check if we're in a project root
170
+ const { isRoot, detectedType } = await checkProjectRoot();
171
+
172
+ let selectedType;
173
+
174
+ if (!isRoot) {
175
+ // Not in project root - show warning and ask user
176
+ console.log(chalk.yellow('⚠ Warning: Could not detect project type'));
177
+ console.log(chalk.white('We recommend running this command in the root directory of your project.'));
178
+ console.log(chalk.gray('(Looking for requirements.txt, package.json, etc.)\n'));
179
+
180
+ const { choice } = await inquirer.prompt([
181
+ {
182
+ type: 'list',
183
+ name: 'choice',
184
+ message: 'Which documentation would you like to download?',
185
+ choices: [
186
+ { name: 'šŸ Python SDK Documentation', value: 'python' },
187
+ { name: 'šŸ“¦ Node.js SDK Documentation', value: 'nodejs' },
188
+ { name: 'āŒ Skip, cancel', value: 'none' }
189
+ ]
190
+ }
191
+ ]);
192
+
193
+ if (choice === 'none') {
194
+ console.log(chalk.yellow('\n⚠ Cancelled\n'));
195
+ return;
196
+ }
197
+
198
+ selectedType = choice;
199
+ } else {
200
+ // In project root - auto-detect
201
+ console.log(chalk.green('āœ“ Project detected:'), chalk.yellow(detectedType === 'python' ? 'Python' : 'Node.js'));
202
+ console.log();
203
+
204
+ selectedType = detectedType;
205
+ }
206
+
207
+ // Download documentation
208
+ await downloadDocumentation(selectedType);
209
+
210
+ } catch (error) {
211
+ console.log(chalk.red(`\nāœ— Error: ${error.message}\n`));
212
+ if (process.env.DEBUG) {
213
+ console.error(error);
214
+ }
215
+ process.exit(1);
216
+ }
217
+ }
218
+
219
+ module.exports = fetchDoc;
220
+