nexusapp-cli 2.1.1 → 2.2.2

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.
Files changed (55) hide show
  1. package/dist/index.js +4 -0
  2. package/nexusapp-cli-2.0.0.tgz +0 -0
  3. package/package.json +8 -2
  4. package/src/commands/bucket.ts +261 -0
  5. package/src/commands/database.ts +35 -0
  6. package/src/commands/deploy.ts +12 -0
  7. package/src/commands/volume.ts +113 -0
  8. package/src/index.ts +4 -0
  9. package/dist/client.d.ts +0 -6
  10. package/dist/client.d.ts.map +0 -1
  11. package/dist/client.js +0 -63
  12. package/dist/client.js.map +0 -1
  13. package/dist/commands/auth.d.ts +0 -3
  14. package/dist/commands/auth.d.ts.map +0 -1
  15. package/dist/commands/auth.js +0 -178
  16. package/dist/commands/auth.js.map +0 -1
  17. package/dist/commands/database.d.ts +0 -3
  18. package/dist/commands/database.d.ts.map +0 -1
  19. package/dist/commands/database.js +0 -312
  20. package/dist/commands/database.js.map +0 -1
  21. package/dist/commands/deploy.d.ts +0 -3
  22. package/dist/commands/deploy.d.ts.map +0 -1
  23. package/dist/commands/deploy.js +0 -868
  24. package/dist/commands/deploy.js.map +0 -1
  25. package/dist/commands/domain.d.ts +0 -3
  26. package/dist/commands/domain.d.ts.map +0 -1
  27. package/dist/commands/domain.js +0 -174
  28. package/dist/commands/domain.js.map +0 -1
  29. package/dist/commands/member.d.ts +0 -3
  30. package/dist/commands/member.d.ts.map +0 -1
  31. package/dist/commands/member.js +0 -175
  32. package/dist/commands/member.js.map +0 -1
  33. package/dist/commands/project.d.ts +0 -3
  34. package/dist/commands/project.d.ts.map +0 -1
  35. package/dist/commands/project.js +0 -92
  36. package/dist/commands/project.js.map +0 -1
  37. package/dist/commands/secret.d.ts +0 -3
  38. package/dist/commands/secret.d.ts.map +0 -1
  39. package/dist/commands/secret.js +0 -121
  40. package/dist/commands/secret.js.map +0 -1
  41. package/dist/commands/token.d.ts +0 -3
  42. package/dist/commands/token.d.ts.map +0 -1
  43. package/dist/commands/token.js +0 -179
  44. package/dist/commands/token.js.map +0 -1
  45. package/dist/config.d.ts +0 -10
  46. package/dist/config.d.ts.map +0 -1
  47. package/dist/config.js +0 -53
  48. package/dist/config.js.map +0 -1
  49. package/dist/index.d.ts +0 -3
  50. package/dist/index.d.ts.map +0 -1
  51. package/dist/index.js.map +0 -1
  52. package/dist/output.d.ts +0 -9
  53. package/dist/output.d.ts.map +0 -1
  54. package/dist/output.js +0 -71
  55. package/dist/output.js.map +0 -1
@@ -1,868 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.registerDeploy = registerDeploy;
7
- const crypto_1 = require("crypto");
8
- const fs_1 = require("fs");
9
- const inquirer_1 = __importDefault(require("inquirer"));
10
- const client_js_1 = require("../client.js");
11
- const output_js_1 = require("../output.js");
12
- const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13
- /** Resolve a deployment name or UUID to its full record. */
14
- async function resolveDeployment(nameOrId) {
15
- if (UUID_RE.test(nameOrId)) {
16
- try {
17
- const res = await client_js_1.client.get(`/api/deployments/${nameOrId}`);
18
- return (0, client_js_1.unwrap)(res.data);
19
- }
20
- catch { /* fall through to name search */ }
21
- }
22
- // Search by name across all deployments
23
- const listRes = await client_js_1.client.get('/api/deployments');
24
- const all = Array.isArray((0, client_js_1.unwrap)(listRes.data)) ? (0, client_js_1.unwrap)(listRes.data) : [];
25
- const match = all.find((d) => d.name === nameOrId || d.displayName === nameOrId);
26
- if (!match)
27
- throw new Error(`Deployment not found: "${nameOrId}"`);
28
- // Fetch full record for the resolved ID
29
- const res = await client_js_1.client.get(`/api/deployments/${match.id}`);
30
- return (0, client_js_1.unwrap)(res.data);
31
- }
32
- function isInternalDeploymentImage(image) {
33
- if (!image)
34
- return false;
35
- return /^deploy-[0-9a-f-]+(?::|$)/i.test(image.trim());
36
- }
37
- async function pollUntilDone(deploymentId, spin) {
38
- const terminal = new Set(['RUNNING', 'FAILED', 'TERMINATED', 'STOPPED']);
39
- while (true) {
40
- await new Promise((r) => setTimeout(r, 3000));
41
- try {
42
- const res = await client_js_1.client.get(`/api/deployments/${deploymentId}`);
43
- const d = (0, client_js_1.unwrap)(res.data);
44
- const status = (d.status || '').toUpperCase();
45
- spin.text = `Deploying ${d.name || deploymentId}... [${status}]`;
46
- if (terminal.has(status)) {
47
- if (status === 'RUNNING') {
48
- spin.succeed(`Deployed: ${d.name} → ${d.url || d.serviceUrl || '—'}`);
49
- }
50
- else {
51
- spin.fail(`Deployment ended with status: ${status}`);
52
- }
53
- return;
54
- }
55
- }
56
- catch (err) {
57
- spin.fail('Failed to poll status: ' + (0, client_js_1.apiError)(err));
58
- return;
59
- }
60
- }
61
- }
62
- /**
63
- * Parse a .env-style file into a key→value map.
64
- * Supports: KEY=VALUE, KEY="quoted value", KEY='quoted value', # comments, blank lines.
65
- * --env pairs always win over file values (caller merges file first, then pairs).
66
- */
67
- function parseEnvFile(filePath) {
68
- let raw;
69
- try {
70
- raw = (0, fs_1.readFileSync)(filePath, 'utf8');
71
- }
72
- catch {
73
- (0, output_js_1.errorMsg)(`Cannot read env file: ${filePath}`);
74
- process.exit(1);
75
- }
76
- const result = {};
77
- for (const line of raw.split('\n')) {
78
- const trimmed = line.trim();
79
- if (!trimmed || trimmed.startsWith('#'))
80
- continue;
81
- const idx = trimmed.indexOf('=');
82
- if (idx <= 0)
83
- continue;
84
- const key = trimmed.slice(0, idx).trim();
85
- let val = trimmed.slice(idx + 1);
86
- // Strip inline comments after unquoted values
87
- if ((val.startsWith('"') && val.includes('"', 1)) || (val.startsWith("'") && val.includes("'", 1))) {
88
- const q = val[0];
89
- const close = val.indexOf(q, 1);
90
- val = val.slice(1, close);
91
- }
92
- else {
93
- val = val.split('#')[0].trim();
94
- }
95
- if (key)
96
- result[key] = val;
97
- }
98
- return result;
99
- }
100
- function registerDeploy(program) {
101
- const deploy = program.command('deploy').description('Deployment commands');
102
- // list
103
- deploy
104
- .command('list')
105
- .description('List deployments')
106
- .option('--project <id>', 'Filter by project ID')
107
- .option('--status <status>', 'Filter by status')
108
- .option('--json', 'Output raw JSON')
109
- .action(async (opts) => {
110
- try {
111
- const url = opts.project ? `/api/deployments/project/${opts.project}` : '/api/deployments';
112
- const params = {};
113
- if (opts.status)
114
- params.status = opts.status;
115
- const res = await client_js_1.client.get(url, { params });
116
- const raw = (0, client_js_1.unwrap)(res.data);
117
- const deployments = Array.isArray(raw) ? raw : raw.deployments || [];
118
- if (opts.json) {
119
- (0, output_js_1.printJson)(deployments);
120
- return;
121
- }
122
- if (!deployments.length) {
123
- console.log('No deployments found.');
124
- return;
125
- }
126
- (0, output_js_1.printTable)(['NAME', 'ID', 'STATUS', 'PROVIDER', 'URL', 'CREATED'], deployments.map((d) => [
127
- d.displayName || d.name,
128
- d.id,
129
- (0, output_js_1.statusBadge)(d.status),
130
- d.provider || '—',
131
- d.url || d.serviceUrl || '—',
132
- d.createdAt ? (0, output_js_1.timeAgo)(d.createdAt) : '—',
133
- ]));
134
- }
135
- catch (err) {
136
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
137
- process.exit(1);
138
- }
139
- });
140
- // get
141
- deploy
142
- .command('get <name-or-id>')
143
- .description('Get deployment details')
144
- .option('--json', 'Output raw JSON')
145
- .action(async (nameOrId, opts) => {
146
- try {
147
- const d = await resolveDeployment(nameOrId);
148
- if (opts.json) {
149
- (0, output_js_1.printJson)(d);
150
- return;
151
- }
152
- (0, output_js_1.printTable)(['Field', 'Value'], [
153
- ['ID', d.id],
154
- ['Name', d.displayName || d.name],
155
- ['Status', (0, output_js_1.statusBadge)(d.status)],
156
- ['Provider', d.provider],
157
- ['Image', d.imageName || '—'],
158
- ['Port', String(d.port || '—')],
159
- ['URL', d.url || d.serviceUrl || '—'],
160
- ['Replicas', String(d.replicas ?? '—')],
161
- ['Project', d.projectId || '—'],
162
- ['Created', d.createdAt ? (0, output_js_1.timeAgo)(d.createdAt) : '—'],
163
- ['Updated', d.updatedAt ? (0, output_js_1.timeAgo)(d.updatedAt) : '—'],
164
- ]);
165
- }
166
- catch (err) {
167
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
168
- process.exit(1);
169
- }
170
- });
171
- // create (image-based)
172
- deploy
173
- .command('create')
174
- .description('Create a deployment from a container image')
175
- .requiredOption('--image <image>', 'Container image (e.g. nginx:latest)')
176
- .requiredOption('--port <port>', 'Container port', parseInt)
177
- .option('--name <name>', 'Deployment name')
178
- .option('--project <id>', 'Project ID')
179
- .option('--provider <provider>', 'Provider (docker|gcp_cloud_run|aws_ecs_fargate|azure_container_apps)')
180
- .option('--env <pairs...>', 'Environment variables as KEY=VALUE')
181
- .option('--env-file <file>', 'Load environment variables from a .env file')
182
- .option('--no-health-check', 'Disable health checks for this deployment')
183
- .option('--wait', 'Wait until deployment is RUNNING or FAILED')
184
- .option('--json', 'Output raw JSON')
185
- .action(async (opts) => {
186
- const envVars = {};
187
- if (opts.envFile)
188
- Object.assign(envVars, parseEnvFile(opts.envFile));
189
- if (opts.env) {
190
- for (const pair of opts.env) {
191
- const idx = pair.indexOf('=');
192
- if (idx > 0)
193
- envVars[pair.slice(0, idx)] = pair.slice(idx + 1);
194
- }
195
- }
196
- const payload = { image: opts.image, port: opts.port };
197
- if (opts.name)
198
- payload.name = opts.name;
199
- if (opts.project)
200
- payload.projectId = opts.project;
201
- if (opts.provider)
202
- payload.provider = opts.provider;
203
- if (opts.healthCheck === false)
204
- payload.healthCheckEnabled = false;
205
- if (Object.keys(envVars).length)
206
- payload.envVars = envVars;
207
- try {
208
- const res = await client_js_1.client.post('/api/gpt/deploy', payload);
209
- const d = res.data;
210
- if (opts.json) {
211
- (0, output_js_1.printJson)(d);
212
- return;
213
- }
214
- if (opts.wait) {
215
- const spin = (0, output_js_1.spinner)(`Deploying ${d.name || opts.name || opts.image}...`);
216
- await pollUntilDone(d.id, spin);
217
- }
218
- else {
219
- (0, output_js_1.success)(`Deployment queued: ${d.name || d.id}`);
220
- console.log(` Run 'nexus deploy status ${d.id} --watch' to track progress`);
221
- }
222
- }
223
- catch (err) {
224
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
225
- process.exit(1);
226
- }
227
- });
228
- // source (repo-based)
229
- deploy
230
- .command('source')
231
- .description('Deploy from a Git repository')
232
- .requiredOption('--repo <url>', 'Git repository URL')
233
- .option('--name <name>', 'Deployment name')
234
- .option('--branch <branch>', 'Git branch')
235
- .option('--provider <provider>', 'Provider (docker|gcp_cloud_run|aws_ecs_fargate|azure_container_apps)')
236
- .option('--env <pairs...>', 'Environment variables as KEY=VALUE')
237
- .option('--env-file <file>', 'Load environment variables from a .env file')
238
- .option('--framework <framework>', 'Framework hint (e.g. node, python, go)')
239
- .option('--build-command <cmd>', 'Custom build command')
240
- .option('--start-command <cmd>', 'Custom start command')
241
- .option('--install-command <cmd>', 'Custom install command')
242
- .option('--output-dir <dir>', 'Build output directory')
243
- .option('--dockerfile <path>', 'Path to Dockerfile in repo')
244
- .option('--repo-secret <name>', 'Secret name containing private repo token')
245
- .option('--environment <env>', 'Deployment environment (DEVELOPMENT|STAGING|PRODUCTION)', 'DEVELOPMENT')
246
- .option('--auto-destroy <hours>', 'Auto-destroy after N hours', parseInt)
247
- .option('--services <types>', 'Comma-separated database services to provision (e.g. postgresql,redis)')
248
- .option('--no-health-check', 'Disable health checks for this deployment')
249
- .option('--wait', 'Wait until deployment is RUNNING or FAILED')
250
- .option('--json', 'Output raw JSON')
251
- .action(async (opts) => {
252
- const envVars = {};
253
- if (opts.envFile)
254
- Object.assign(envVars, parseEnvFile(opts.envFile));
255
- if (opts.env) {
256
- for (const pair of opts.env) {
257
- const idx = pair.indexOf('=');
258
- if (idx > 0)
259
- envVars[pair.slice(0, idx)] = pair.slice(idx + 1);
260
- }
261
- }
262
- const payload = { sourceType: 'repo', repoUrl: opts.repo };
263
- if (opts.name)
264
- payload.name = opts.name;
265
- if (opts.branch)
266
- payload.repoBranch = opts.branch;
267
- if (opts.provider)
268
- payload.provider = opts.provider;
269
- if (opts.environment)
270
- payload.environment = opts.environment;
271
- if (opts.framework)
272
- payload.framework = opts.framework;
273
- if (opts.buildCommand)
274
- payload.buildCommand = opts.buildCommand;
275
- if (opts.startCommand)
276
- payload.startCommand = opts.startCommand;
277
- if (opts.installCommand)
278
- payload.installCommand = opts.installCommand;
279
- if (opts.outputDir)
280
- payload.outputDir = opts.outputDir;
281
- if (opts.dockerfile)
282
- payload.dockerfile = opts.dockerfile;
283
- if (opts.repoSecret)
284
- payload.repoSecretName = opts.repoSecret;
285
- if (opts.autoDestroy)
286
- payload.autoDestroyHours = opts.autoDestroy;
287
- if (opts.healthCheck === false)
288
- payload.healthCheckEnabled = false;
289
- if (opts.services) {
290
- payload.services = opts.services.split(',').map((s) => ({ type: s.trim() }));
291
- }
292
- if (Object.keys(envVars).length)
293
- payload.envVars = envVars;
294
- try {
295
- const res = await client_js_1.client.post('/api/gpt/deploy/source', payload);
296
- const d = res.data;
297
- if (opts.json) {
298
- (0, output_js_1.printJson)(d);
299
- return;
300
- }
301
- if (opts.wait) {
302
- const spin = (0, output_js_1.spinner)(`Building and deploying ${d.name || opts.name || opts.repo}...`);
303
- await pollUntilDone(d.id, spin);
304
- }
305
- else {
306
- (0, output_js_1.success)(`Source deployment queued: ${d.name || d.id}`);
307
- console.log(` ID: ${d.id}`);
308
- console.log(` Run 'nexus deploy status ${d.id} --watch' to track progress`);
309
- }
310
- }
311
- catch (err) {
312
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
313
- process.exit(1);
314
- }
315
- });
316
- // redeploy
317
- deploy
318
- .command('redeploy <name-or-id>')
319
- .description('Redeploy an existing deployment with the same config')
320
- .option('--name <name>', 'Override deployment name')
321
- .option('--provider <provider>', 'Override provider')
322
- .option('--env <pairs...>', 'Override / add environment variables as KEY=VALUE')
323
- .option('--env-file <file>', 'Load environment variables from a .env file (merged with existing, --env wins)')
324
- .option('--wait', 'Wait until deployment is RUNNING or FAILED')
325
- .option('--yes', 'Skip confirmation prompt')
326
- .option('--json', 'Output raw JSON')
327
- .action(async (nameOrId, opts) => {
328
- let deployment;
329
- try {
330
- deployment = await resolveDeployment(nameOrId);
331
- }
332
- catch (err) {
333
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
334
- process.exit(1);
335
- }
336
- if (!opts.yes) {
337
- const { confirm } = await inquirer_1.default.prompt([
338
- { type: 'confirm', name: 'confirm', message: `Redeploy "${deployment.displayName || deployment.name}"?`, default: true },
339
- ]);
340
- if (!confirm) {
341
- console.log('Cancelled.');
342
- return;
343
- }
344
- }
345
- const baseEnvVars = { ...(deployment.envVars || {}) };
346
- if (opts.envFile)
347
- Object.assign(baseEnvVars, parseEnvFile(opts.envFile));
348
- if (opts.env) {
349
- for (const pair of opts.env) {
350
- const idx = pair.indexOf('=');
351
- if (idx > 0)
352
- baseEnvVars[pair.slice(0, idx)] = pair.slice(idx + 1);
353
- }
354
- }
355
- const provider = opts.provider || undefined;
356
- try {
357
- let project = null;
358
- if (deployment.projectId) {
359
- const projectRes = await client_js_1.client.get(`/api/projects/${deployment.projectId}`);
360
- project = (0, client_js_1.unwrap)(projectRes.data);
361
- }
362
- const hasAttachedServices = Array.isArray(deployment.services) && deployment.services.length > 0;
363
- if (project?.repoUrl && !(hasAttachedServices && deployment.code)) {
364
- const payload = {
365
- sourceType: 'repo',
366
- repoUrl: project.repoUrl,
367
- name: opts.name || `${deployment.name}-redeploy`,
368
- };
369
- if (project.gitBranch)
370
- payload.repoBranch = project.gitBranch;
371
- if (project.framework)
372
- payload.framework = project.framework;
373
- if (provider)
374
- payload.provider = provider;
375
- if (Object.keys(baseEnvVars).length)
376
- payload.envVars = baseEnvVars;
377
- const res = await client_js_1.client.post('/api/gpt/deploy/source', payload);
378
- const d = res.data;
379
- if (opts.json) {
380
- (0, output_js_1.printJson)(d);
381
- return;
382
- }
383
- if (opts.wait) {
384
- await pollUntilDone(d.id, (0, output_js_1.spinner)(`Rebuilding ${d.name || nameOrId}...`));
385
- }
386
- else {
387
- (0, output_js_1.success)(`Redeploy queued: ${d.name || d.id}`);
388
- console.log(` Repo: ${project.repoUrl}${project.gitBranch ? ` @ ${project.gitBranch}` : ''}`);
389
- console.log(` Run 'nexus deploy status ${d.id} --watch' to track progress`);
390
- }
391
- return;
392
- }
393
- if (deployment.code) {
394
- const payload = {
395
- deploymentId: deployment.id,
396
- projectId: deployment.projectId,
397
- name: opts.name || `${deployment.name}-redeploy`,
398
- displayName: opts.name || `Redeploy of ${deployment.displayName || deployment.name}`,
399
- code: deployment.code,
400
- dockerfile: deployment.dockerfile,
401
- deploymentProvider: provider || deployment.provider,
402
- environment: deployment.environment,
403
- healthCheckEnabled: deployment.healthCheckEnabled,
404
- healthCheckType: deployment.healthCheckType,
405
- healthCheckUrl: deployment.healthCheckUrl,
406
- };
407
- if (hasAttachedServices) {
408
- payload.services = deployment.services.map((service) => ({
409
- type: service.serviceType,
410
- displayName: service.displayName,
411
- }));
412
- }
413
- if (Object.keys(baseEnvVars).length)
414
- payload.envVars = baseEnvVars;
415
- const res = await client_js_1.client.post('/api/deployments', payload);
416
- const d = (0, client_js_1.unwrap)(res.data);
417
- if (opts.json) {
418
- (0, output_js_1.printJson)(d);
419
- return;
420
- }
421
- if (opts.wait) {
422
- await pollUntilDone(d.id, (0, output_js_1.spinner)(`Redeploying ${d.name || nameOrId}...`));
423
- }
424
- else {
425
- (0, output_js_1.success)(`Redeploy queued: ${d.name || d.id}`);
426
- console.log(` Run 'nexus deploy status ${d.id} --watch' to track progress`);
427
- }
428
- return;
429
- }
430
- if (deployment.imageName && !isInternalDeploymentImage(deployment.imageName)) {
431
- const payload = {
432
- image: deployment.imageName,
433
- port: deployment.port,
434
- name: opts.name || `${deployment.name}-redeploy`,
435
- };
436
- if (provider)
437
- payload.provider = provider;
438
- if (Object.keys(baseEnvVars).length)
439
- payload.envVars = baseEnvVars;
440
- const res = await client_js_1.client.post('/api/gpt/deploy', payload);
441
- const d = res.data;
442
- if (opts.json) {
443
- (0, output_js_1.printJson)(d);
444
- return;
445
- }
446
- if (opts.wait) {
447
- await pollUntilDone(d.id, (0, output_js_1.spinner)(`Redeploying ${d.name || nameOrId}...`));
448
- }
449
- else {
450
- (0, output_js_1.success)(`Redeploy queued: ${d.name || d.id}`);
451
- console.log(` Run 'nexus deploy status ${d.id} --watch' to track progress`);
452
- }
453
- return;
454
- }
455
- (0, output_js_1.errorMsg)('Cannot redeploy: no image or repo URL found. Use "nexus deploy source --repo <url>" instead.');
456
- process.exit(1);
457
- }
458
- catch (err) {
459
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
460
- process.exit(1);
461
- }
462
- });
463
- // stop
464
- deploy
465
- .command('stop <name-or-id>')
466
- .description('Stop a running deployment')
467
- .option('--yes', 'Skip confirmation prompt')
468
- .action(async (nameOrId, opts) => {
469
- try {
470
- const d = await resolveDeployment(nameOrId);
471
- if (!opts.yes) {
472
- const { confirm } = await inquirer_1.default.prompt([
473
- { type: 'confirm', name: 'confirm', message: `Stop "${d.displayName || d.name}"?`, default: false },
474
- ]);
475
- if (!confirm) {
476
- console.log('Cancelled.');
477
- return;
478
- }
479
- }
480
- await client_js_1.client.post(`/api/deployments/${d.id}/stop`);
481
- (0, output_js_1.success)(`Deployment "${d.displayName || d.name}" stopped.`);
482
- }
483
- catch (err) {
484
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
485
- process.exit(1);
486
- }
487
- });
488
- // start
489
- deploy
490
- .command('start <name-or-id>')
491
- .description('Start a stopped deployment')
492
- .action(async (nameOrId) => {
493
- try {
494
- const d = await resolveDeployment(nameOrId);
495
- await client_js_1.client.post(`/api/deployments/${d.id}/start`);
496
- (0, output_js_1.success)(`Deployment "${d.displayName || d.name}" started.`);
497
- }
498
- catch (err) {
499
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
500
- process.exit(1);
501
- }
502
- });
503
- // delete
504
- deploy
505
- .command('delete <name-or-id>')
506
- .description('Delete a deployment')
507
- .option('--yes', 'Skip confirmation prompt')
508
- .action(async (nameOrId, opts) => {
509
- try {
510
- const d = await resolveDeployment(nameOrId);
511
- if (!opts.yes) {
512
- const { confirm } = await inquirer_1.default.prompt([
513
- { type: 'confirm', name: 'confirm', message: `Delete "${d.displayName || d.name}"? This cannot be undone.`, default: false },
514
- ]);
515
- if (!confirm) {
516
- console.log('Cancelled.');
517
- return;
518
- }
519
- }
520
- await client_js_1.client.delete(`/api/deployments/${d.id}`);
521
- (0, output_js_1.success)(`Deployment "${d.displayName || d.name}" deleted.`);
522
- }
523
- catch (err) {
524
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
525
- process.exit(1);
526
- }
527
- });
528
- // logs
529
- deploy
530
- .command('logs <name-or-id>')
531
- .description('View deployment logs')
532
- .option('--type <type>', 'Log type: runtime or build', 'runtime')
533
- .option('--lines <n>', 'Number of log lines', '100')
534
- .option('--follow', 'Poll for new logs every 2s')
535
- .action(async (nameOrId, opts) => {
536
- let deployId;
537
- try {
538
- const d = await resolveDeployment(nameOrId);
539
- deployId = d.id;
540
- }
541
- catch (err) {
542
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
543
- process.exit(1);
544
- return;
545
- }
546
- const limit = parseInt(opts.lines, 10) || 100;
547
- /** Normalise the API response into an array of { message, timestamp? } */
548
- const fetchLogs = async (lastTimestamp) => {
549
- const params = { type: opts.type, limit };
550
- if (lastTimestamp)
551
- params.after = lastTimestamp;
552
- const res = await client_js_1.client.get(`/api/deployments/${deployId}/logs`, { params });
553
- const raw = (0, client_js_1.unwrap)(res.data);
554
- // Shape: { logs: "line1\nline2\n..." }
555
- if (raw && typeof raw.logs === 'string') {
556
- return raw.logs
557
- .split('\n')
558
- .filter((l) => l.length > 0)
559
- .map((l) => ({ message: l }));
560
- }
561
- // Shape: [ { message, timestamp }, ... ] or [ "line1", ... ]
562
- const arr = Array.isArray(raw) ? raw : Array.isArray(raw?.logs) ? raw.logs : [];
563
- return arr.map((entry) => typeof entry === 'string'
564
- ? { message: entry }
565
- : { message: entry.message || entry.log || String(entry), timestamp: entry.timestamp });
566
- };
567
- try {
568
- const logs = await fetchLogs();
569
- let lastTimestamp;
570
- for (const log of logs) {
571
- const ts = log.timestamp ? `[${new Date(log.timestamp).toLocaleTimeString()}] ` : '';
572
- console.log(`${ts}${log.message}`);
573
- lastTimestamp = log.timestamp || lastTimestamp;
574
- }
575
- if (!opts.follow)
576
- return;
577
- while (true) {
578
- await new Promise((r) => setTimeout(r, 2000));
579
- try {
580
- const newLogs = await fetchLogs(lastTimestamp);
581
- for (const log of newLogs) {
582
- const ts = log.timestamp ? `[${new Date(log.timestamp).toLocaleTimeString()}] ` : '';
583
- console.log(`${ts}${log.message}`);
584
- lastTimestamp = log.timestamp || lastTimestamp;
585
- }
586
- }
587
- catch { /* ignore transient errors in follow mode */ }
588
- }
589
- }
590
- catch (err) {
591
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
592
- process.exit(1);
593
- }
594
- });
595
- // scale
596
- deploy
597
- .command('scale <name-or-id> <replicas>')
598
- .description('Scale deployment replicas')
599
- .action(async (nameOrId, replicas) => {
600
- const count = parseInt(replicas, 10);
601
- if (isNaN(count) || count < 1 || count > 10) {
602
- (0, output_js_1.errorMsg)('Replicas must be a number between 1 and 10.');
603
- process.exit(1);
604
- }
605
- try {
606
- const d = await resolveDeployment(nameOrId);
607
- await client_js_1.client.post(`/api/deployments/${d.id}/scale`, { replicas: count });
608
- (0, output_js_1.success)(`Deployment "${d.displayName || d.name}" scaled to ${count} replica(s).`);
609
- }
610
- catch (err) {
611
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
612
- process.exit(1);
613
- }
614
- });
615
- // rollback
616
- deploy
617
- .command('rollback <name-or-id>')
618
- .description('Roll back a deployment to the previous version')
619
- .option('--target <deployment-id>', 'Target deployment ID to roll back to')
620
- .option('--yes', 'Skip confirmation prompt')
621
- .action(async (nameOrId, opts) => {
622
- try {
623
- const d = await resolveDeployment(nameOrId);
624
- if (!opts.yes) {
625
- const { confirm } = await inquirer_1.default.prompt([
626
- { type: 'confirm', name: 'confirm', message: `Roll back "${d.displayName || d.name}" to the previous version?`, default: false },
627
- ]);
628
- if (!confirm) {
629
- console.log('Cancelled.');
630
- return;
631
- }
632
- }
633
- const payload = {};
634
- if (opts.target)
635
- payload.targetDeploymentId = opts.target;
636
- const res = await client_js_1.client.post(`/api/deployments/${d.id}/rollback`, payload);
637
- const result = (0, client_js_1.unwrap)(res.data);
638
- (0, output_js_1.success)(`Rollback initiated → new deployment ${result.id || '?'}`);
639
- }
640
- catch (err) {
641
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
642
- process.exit(1);
643
- }
644
- });
645
- // openclaw
646
- deploy
647
- .command('openclaw')
648
- .description('Deploy an OpenClaw gateway (alpine/openclaw:latest) on port 18789')
649
- .option('--name <name>', 'Deployment name', 'openclaw-gateway')
650
- .option('--gateway-token <token>', 'OpenClaw gateway auth token (auto-generated if not set)')
651
- .option('--claude-api-key <key>', 'CLAUDE_AI_SESSION_KEY value')
652
- .option('--claude-web-session <key>', 'CLAUDE_WEB_SESSION_KEY value')
653
- .option('--claude-web-cookie <cookie>', 'CLAUDE_WEB_COOKIE value')
654
- .option('--provider <provider>', 'Provider (docker|gcp_cloud_run|aws_ecs_fargate|azure_container_apps)')
655
- .option('--env <pairs...>', 'Additional environment variables as KEY=VALUE')
656
- .option('--env-file <file>', 'Load environment variables from a .env file')
657
- .option('--wait', 'Wait until deployment is RUNNING or FAILED')
658
- .option('--json', 'Output raw JSON')
659
- .action(async (opts) => {
660
- const gatewayToken = opts.gatewayToken || (0, crypto_1.randomBytes)(32).toString('hex');
661
- const envVars = {
662
- HOME: '/home/node',
663
- OPENCLAW_GATEWAY_TOKEN: gatewayToken,
664
- OPENCLAW_GATEWAY_BIND: 'lan',
665
- OPENCLAW_GATEWAY_CONTROL_UI_DANGEROUSLY_ALLOW_HOST_HEADER_ORIGIN_FALLBACK: 'true',
666
- };
667
- if (opts.claudeApiKey)
668
- envVars['CLAUDE_AI_SESSION_KEY'] = opts.claudeApiKey;
669
- if (opts.claudeWebSession)
670
- envVars['CLAUDE_WEB_SESSION_KEY'] = opts.claudeWebSession;
671
- if (opts.claudeWebCookie)
672
- envVars['CLAUDE_WEB_COOKIE'] = opts.claudeWebCookie;
673
- if (opts.envFile)
674
- Object.assign(envVars, parseEnvFile(opts.envFile));
675
- if (opts.env) {
676
- for (const pair of opts.env) {
677
- const idx = pair.indexOf('=');
678
- if (idx > 0)
679
- envVars[pair.slice(0, idx)] = pair.slice(idx + 1);
680
- }
681
- }
682
- const payload = {
683
- image: 'alpine/openclaw:latest',
684
- port: 18789,
685
- name: opts.name,
686
- envVars,
687
- startCommand: 'mkdir -p /home/node/.openclaw && echo \'{"gateway":{"controlUi":{"dangerouslyAllowHostHeaderOriginFallback":true,"dangerouslyDisableDeviceAuth":true},"trustedProxies":["172.16.0.0/12","10.0.0.0/8"]}}\' > /home/node/.openclaw/openclaw.json && node dist/index.js gateway --bind lan --port 18789 --allow-unconfigured',
688
- healthCheckEnabled: false, // OpenClaw gateway has no HTTP health endpoint
689
- };
690
- if (opts.provider)
691
- payload.provider = opts.provider;
692
- try {
693
- const res = await client_js_1.client.post('/api/gpt/deploy', payload);
694
- const d = res.data;
695
- if (opts.json) {
696
- (0, output_js_1.printJson)({ ...d, gatewayToken });
697
- return;
698
- }
699
- if (opts.wait) {
700
- const spin = (0, output_js_1.spinner)('Deploying OpenClaw gateway...');
701
- await pollUntilDone(d.id, spin);
702
- }
703
- else {
704
- (0, output_js_1.success)(`OpenClaw gateway queued: ${d.name || d.id}`);
705
- console.log(` Gateway token: ${gatewayToken}`);
706
- console.log(` Port: 18789`);
707
- console.log(` Run 'nexus deploy status ${d.id} --watch' to track progress`);
708
- }
709
- }
710
- catch (err) {
711
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
712
- process.exit(1);
713
- }
714
- });
715
- // flixty
716
- deploy
717
- .command('flixty')
718
- .description('Deploy Flixty social media creator studio from source (github.com/nexusrun/flixty)')
719
- .option('--name <name>', 'Deployment name', 'flixty')
720
- .option('--session-secret <secret>', 'Express session secret (auto-generated if not set)')
721
- .option('--base-url <url>', 'Public URL of the deployment (for OAuth redirect URIs)')
722
- .option('--anthropic-api-key <key>', 'Anthropic API key for AI Assist')
723
- .option('--x-client-id <id>', 'X/Twitter OAuth 2.0 Client ID')
724
- .option('--x-client-secret <secret>', 'X/Twitter OAuth 2.0 Client Secret')
725
- .option('--linkedin-client-id <id>', 'LinkedIn OAuth Client ID')
726
- .option('--linkedin-client-secret <secret>', 'LinkedIn OAuth Client Secret')
727
- .option('--fb-app-id <id>', 'Facebook App ID')
728
- .option('--fb-app-secret <secret>', 'Facebook App Secret')
729
- .option('--tiktok-client-key <key>', 'TikTok Client Key')
730
- .option('--tiktok-client-secret <secret>', 'TikTok Client Secret')
731
- .option('--google-client-id <id>', 'Google Client ID (YouTube)')
732
- .option('--google-client-secret <secret>', 'Google Client Secret')
733
- .option('--provider <provider>', 'Provider (docker|gcp_cloud_run|aws_ecs_fargate|azure_container_apps)')
734
- .option('--env <pairs...>', 'Additional environment variables as KEY=VALUE')
735
- .option('--env-file <file>', 'Load environment variables from a .env file')
736
- .option('--wait', 'Wait until deployment is RUNNING or FAILED')
737
- .option('--json', 'Output raw JSON')
738
- .action(async (opts) => {
739
- const sessionSecret = opts.sessionSecret || (0, crypto_1.randomBytes)(32).toString('hex');
740
- const envVars = {
741
- SESSION_SECRET: sessionSecret,
742
- PORT: '3000',
743
- NODE_ENV: 'production',
744
- };
745
- if (opts.baseUrl)
746
- envVars['BASE_URL'] = opts.baseUrl;
747
- if (opts.anthropicApiKey)
748
- envVars['ANTHROPIC_API_KEY'] = opts.anthropicApiKey;
749
- if (opts.xClientId)
750
- envVars['X_CLIENT_ID'] = opts.xClientId;
751
- if (opts.xClientSecret)
752
- envVars['X_CLIENT_SECRET'] = opts.xClientSecret;
753
- if (opts.linkedinClientId)
754
- envVars['LINKEDIN_CLIENT_ID'] = opts.linkedinClientId;
755
- if (opts.linkedinClientSecret)
756
- envVars['LINKEDIN_CLIENT_SECRET'] = opts.linkedinClientSecret;
757
- if (opts.fbAppId)
758
- envVars['FB_APP_ID'] = opts.fbAppId;
759
- if (opts.fbAppSecret)
760
- envVars['FB_APP_SECRET'] = opts.fbAppSecret;
761
- if (opts.tiktokClientKey)
762
- envVars['TIKTOK_CLIENT_KEY'] = opts.tiktokClientKey;
763
- if (opts.tiktokClientSecret)
764
- envVars['TIKTOK_CLIENT_SECRET'] = opts.tiktokClientSecret;
765
- if (opts.googleClientId)
766
- envVars['GOOGLE_CLIENT_ID'] = opts.googleClientId;
767
- if (opts.googleClientSecret)
768
- envVars['GOOGLE_CLIENT_SECRET'] = opts.googleClientSecret;
769
- if (opts.envFile)
770
- Object.assign(envVars, parseEnvFile(opts.envFile));
771
- if (opts.env) {
772
- for (const pair of opts.env) {
773
- const idx = pair.indexOf('=');
774
- if (idx > 0)
775
- envVars[pair.slice(0, idx)] = pair.slice(idx + 1);
776
- }
777
- }
778
- const payload = {
779
- sourceType: 'repo',
780
- repoUrl: 'https://github.com/nexusrun/flixty.git',
781
- name: opts.name,
782
- environment: 'PRODUCTION',
783
- startCommand: 'node server.js',
784
- envVars,
785
- healthCheckEnabled: true,
786
- };
787
- if (opts.provider)
788
- payload.provider = opts.provider;
789
- try {
790
- const res = await client_js_1.client.post('/api/gpt/deploy/source', payload);
791
- const d = res.data;
792
- if (opts.json) {
793
- (0, output_js_1.printJson)({ ...d, sessionSecret });
794
- return;
795
- }
796
- if (opts.wait) {
797
- const spin = (0, output_js_1.spinner)('Deploying Flixty...');
798
- await pollUntilDone(d.id, spin);
799
- }
800
- else {
801
- (0, output_js_1.success)(`Flixty queued: ${d.name || d.id}`);
802
- console.log(` Session secret: ${sessionSecret}`);
803
- console.log(` Port: 3000`);
804
- if (!opts.baseUrl) {
805
- console.log(` Note: once running, redeploy with --base-url <public-url> for OAuth to work`);
806
- }
807
- console.log(` Run 'nexus deploy status ${d.id} --watch' to track progress`);
808
- }
809
- }
810
- catch (err) {
811
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
812
- process.exit(1);
813
- }
814
- });
815
- // status
816
- deploy
817
- .command('status <name-or-id>')
818
- .description('Show deployment status')
819
- .option('--watch', 'Refresh every 3s')
820
- .option('--json', 'Output raw JSON')
821
- .action(async (nameOrId, opts) => {
822
- let deployId;
823
- try {
824
- const d = await resolveDeployment(nameOrId);
825
- deployId = d.id;
826
- }
827
- catch (err) {
828
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
829
- process.exit(1);
830
- return;
831
- }
832
- const show = async () => {
833
- const res = await client_js_1.client.get(`/api/deployments/${deployId}`);
834
- const d = (0, client_js_1.unwrap)(res.data);
835
- if (opts.json) {
836
- (0, output_js_1.printJson)(d);
837
- return;
838
- }
839
- if (opts.watch)
840
- process.stdout.write('\x1Bc');
841
- (0, output_js_1.printTable)(['Field', 'Value'], [
842
- ['Name', d.displayName || d.name],
843
- ['Status', (0, output_js_1.statusBadge)(d.status)],
844
- ['Provider', d.provider || '—'],
845
- ['URL', d.url || d.serviceUrl || '—'],
846
- ['Replicas', String(d.replicas ?? '—')],
847
- ['Updated', d.updatedAt ? (0, output_js_1.timeAgo)(d.updatedAt) : '—'],
848
- ]);
849
- };
850
- try {
851
- await show();
852
- if (!opts.watch)
853
- return;
854
- while (true) {
855
- await new Promise((r) => setTimeout(r, 3000));
856
- try {
857
- await show();
858
- }
859
- catch { /* ignore */ }
860
- }
861
- }
862
- catch (err) {
863
- (0, output_js_1.errorMsg)((0, client_js_1.apiError)(err));
864
- process.exit(1);
865
- }
866
- });
867
- }
868
- //# sourceMappingURL=deploy.js.map