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.
- package/dist/index.js +4 -0
- package/nexusapp-cli-2.0.0.tgz +0 -0
- package/package.json +8 -2
- package/src/commands/bucket.ts +261 -0
- package/src/commands/database.ts +35 -0
- package/src/commands/deploy.ts +12 -0
- package/src/commands/volume.ts +113 -0
- package/src/index.ts +4 -0
- package/dist/client.d.ts +0 -6
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -63
- package/dist/client.js.map +0 -1
- package/dist/commands/auth.d.ts +0 -3
- package/dist/commands/auth.d.ts.map +0 -1
- package/dist/commands/auth.js +0 -178
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/database.d.ts +0 -3
- package/dist/commands/database.d.ts.map +0 -1
- package/dist/commands/database.js +0 -312
- package/dist/commands/database.js.map +0 -1
- package/dist/commands/deploy.d.ts +0 -3
- package/dist/commands/deploy.d.ts.map +0 -1
- package/dist/commands/deploy.js +0 -868
- package/dist/commands/deploy.js.map +0 -1
- package/dist/commands/domain.d.ts +0 -3
- package/dist/commands/domain.d.ts.map +0 -1
- package/dist/commands/domain.js +0 -174
- package/dist/commands/domain.js.map +0 -1
- package/dist/commands/member.d.ts +0 -3
- package/dist/commands/member.d.ts.map +0 -1
- package/dist/commands/member.js +0 -175
- package/dist/commands/member.js.map +0 -1
- package/dist/commands/project.d.ts +0 -3
- package/dist/commands/project.d.ts.map +0 -1
- package/dist/commands/project.js +0 -92
- package/dist/commands/project.js.map +0 -1
- package/dist/commands/secret.d.ts +0 -3
- package/dist/commands/secret.d.ts.map +0 -1
- package/dist/commands/secret.js +0 -121
- package/dist/commands/secret.js.map +0 -1
- package/dist/commands/token.d.ts +0 -3
- package/dist/commands/token.d.ts.map +0 -1
- package/dist/commands/token.js +0 -179
- package/dist/commands/token.js.map +0 -1
- package/dist/config.d.ts +0 -10
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -53
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/output.d.ts +0 -9
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -71
- package/dist/output.js.map +0 -1
package/dist/commands/deploy.js
DELETED
|
@@ -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
|