@sylphx/cli 0.2.0 → 0.3.1
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 +740 -650
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -24,13 +24,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
27
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
28
|
+
var import_commander24 = require("commander");
|
|
29
29
|
|
|
30
30
|
// package.json
|
|
31
31
|
var package_default = {
|
|
32
32
|
name: "@sylphx/cli",
|
|
33
|
-
version: "0.
|
|
33
|
+
version: "0.3.1",
|
|
34
34
|
description: "Sylphx Platform CLI \u2014 deploy, manage logs, env vars, and more",
|
|
35
35
|
type: "commonjs",
|
|
36
36
|
bin: {
|
|
@@ -55,6 +55,7 @@ var package_default = {
|
|
|
55
55
|
registry: "https://registry.npmjs.org/"
|
|
56
56
|
},
|
|
57
57
|
dependencies: {
|
|
58
|
+
"@sylphx/management": "workspace:*",
|
|
58
59
|
chalk: "^5.3.0",
|
|
59
60
|
commander: "^12.1.0",
|
|
60
61
|
conf: "^13.0.0",
|
|
@@ -153,452 +154,213 @@ var config = {
|
|
|
153
154
|
};
|
|
154
155
|
|
|
155
156
|
// src/api.ts
|
|
157
|
+
var import_management = require("@sylphx/management");
|
|
156
158
|
var { version } = package_default;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
var AUTH_BASE_URL = "https://sylphx.com";
|
|
160
|
-
var ApiError = class extends Error {
|
|
161
|
-
constructor(status, message, body) {
|
|
162
|
-
super(message);
|
|
163
|
-
this.status = status;
|
|
164
|
-
this.body = body;
|
|
165
|
-
this.name = "ApiError";
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
async function request(method, path5, options) {
|
|
169
|
-
const token = options?.token ?? config.getToken();
|
|
159
|
+
function getMgmt() {
|
|
160
|
+
const token = config.getToken();
|
|
170
161
|
if (!token) {
|
|
171
|
-
throw new ApiError(401, "Not authenticated. Run `sylphx login` first.");
|
|
172
|
-
}
|
|
173
|
-
let url = `${API_BASE}${path5}`;
|
|
174
|
-
if (options?.query) {
|
|
175
|
-
const params = new URLSearchParams(options.query);
|
|
176
|
-
url += `?${params.toString()}`;
|
|
162
|
+
throw new import_management.ApiError(401, "Not authenticated. Run `sylphx login` first.");
|
|
177
163
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"User-Agent": `sylphx-cli/${version}`
|
|
182
|
-
};
|
|
183
|
-
const res = await fetch(url, {
|
|
184
|
-
method,
|
|
185
|
-
headers,
|
|
186
|
-
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
164
|
+
return new import_management.SylphxManagement({
|
|
165
|
+
token,
|
|
166
|
+
userAgent: `sylphx-cli/${version}`
|
|
187
167
|
});
|
|
188
|
-
if (!res.ok) {
|
|
189
|
-
let body;
|
|
190
|
-
const text = await res.text();
|
|
191
|
-
try {
|
|
192
|
-
body = JSON.parse(text);
|
|
193
|
-
} catch {
|
|
194
|
-
body = text;
|
|
195
|
-
}
|
|
196
|
-
const message = typeof body === "object" && body !== null && "message" in body ? String(body.message) : `HTTP ${res.status}`;
|
|
197
|
-
throw new ApiError(res.status, message, body);
|
|
198
|
-
}
|
|
199
|
-
if (res.status === 204) {
|
|
200
|
-
return void 0;
|
|
201
|
-
}
|
|
202
|
-
return res.json();
|
|
203
168
|
}
|
|
204
169
|
var api = {
|
|
205
|
-
|
|
170
|
+
// Deployments
|
|
206
171
|
async getDeploymentStatus(projectId) {
|
|
207
|
-
return
|
|
172
|
+
return getMgmt().deployments.status(projectId);
|
|
208
173
|
},
|
|
209
|
-
/** Trigger a deploy for a project environment */
|
|
210
174
|
async triggerDeploy(projectId, envType) {
|
|
211
|
-
return
|
|
212
|
-
body: { envType }
|
|
213
|
-
});
|
|
175
|
+
return getMgmt().deployments.trigger(projectId, envType);
|
|
214
176
|
},
|
|
215
|
-
/** Get deployment history for a project environment */
|
|
216
177
|
async getDeploymentHistory(projectId, envType) {
|
|
217
|
-
return
|
|
218
|
-
query: { envType }
|
|
219
|
-
});
|
|
178
|
+
return getMgmt().deployments.history(projectId, envType);
|
|
220
179
|
},
|
|
221
|
-
/** Rollback to previous deployment.
|
|
222
|
-
* Accepts envId (rollback env to previous deploy), deploymentRecordId, or buildRecordId.
|
|
223
|
-
* One of these is required.
|
|
224
|
-
*/
|
|
225
180
|
async rollback(projectId, body) {
|
|
226
|
-
return
|
|
181
|
+
return getMgmt().deployments.rollback(projectId, body);
|
|
227
182
|
},
|
|
228
|
-
|
|
183
|
+
// Env Vars
|
|
229
184
|
async listEnvVars(projectId, envType) {
|
|
230
|
-
|
|
231
|
-
"GET",
|
|
232
|
-
`/projects/${projectId}/env-vars`,
|
|
233
|
-
{
|
|
234
|
-
query: { envType }
|
|
235
|
-
}
|
|
236
|
-
);
|
|
237
|
-
return Array.isArray(res) ? res : res.data ?? [];
|
|
185
|
+
return getMgmt().envVars.list(projectId, envType);
|
|
238
186
|
},
|
|
239
|
-
/** Set an env var (or batch of vars) for a project environment */
|
|
240
187
|
async setEnvVar(projectId, key, value, envType, secret) {
|
|
241
|
-
return
|
|
242
|
-
body: {
|
|
243
|
-
envType,
|
|
244
|
-
vars: [{ key, value, secret: secret ?? false }]
|
|
245
|
-
}
|
|
246
|
-
});
|
|
188
|
+
return getMgmt().envVars.set(projectId, key, value, envType, secret);
|
|
247
189
|
},
|
|
248
|
-
/** Delete an env var for a project environment */
|
|
249
190
|
async deleteEnvVar(projectId, key, envType) {
|
|
250
|
-
return
|
|
251
|
-
query: { envType }
|
|
252
|
-
});
|
|
191
|
+
return getMgmt().envVars.delete(projectId, key, envType);
|
|
253
192
|
},
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
async listDomains(projectId, envType = "production") {
|
|
257
|
-
return request("GET", `/domains/projects/${projectId}/domains`, {
|
|
258
|
-
query: { envType }
|
|
259
|
-
});
|
|
260
|
-
},
|
|
261
|
-
/** Add a custom domain to a project */
|
|
262
|
-
async addDomain(projectId, domain, envType = "production") {
|
|
263
|
-
return request(
|
|
264
|
-
"POST",
|
|
265
|
-
`/domains/projects/${projectId}/domains`,
|
|
266
|
-
{ query: { envType }, body: { domain } }
|
|
267
|
-
);
|
|
193
|
+
async setEnvVars(projectId, vars, envType) {
|
|
194
|
+
return getMgmt().envVars.setMany(projectId, vars, envType);
|
|
268
195
|
},
|
|
269
|
-
|
|
270
|
-
async
|
|
271
|
-
return
|
|
272
|
-
"DELETE",
|
|
273
|
-
`/domains/projects/${projectId}/domains/${encodeURIComponent(domain)}`,
|
|
274
|
-
{ query: { envType } }
|
|
275
|
-
);
|
|
196
|
+
// Domains
|
|
197
|
+
async listDomains(projectId, envType = "production") {
|
|
198
|
+
return getMgmt().domains.list(projectId, envType);
|
|
276
199
|
},
|
|
277
|
-
/** Register an apex domain for a project (full domain object with DNS records) */
|
|
278
200
|
async createDomain(projectId, apexDomain, envType = "production") {
|
|
279
|
-
return
|
|
280
|
-
body: { apexDomain, envType }
|
|
281
|
-
});
|
|
201
|
+
return getMgmt().domains.create(projectId, apexDomain, envType);
|
|
282
202
|
},
|
|
283
|
-
/** Add a hostname binding to an existing domain */
|
|
284
203
|
async addHostname(projectId, domainId, hostname, opts) {
|
|
285
|
-
|
|
286
|
-
return request(
|
|
287
|
-
"POST",
|
|
288
|
-
`/domains/projects/${projectId}/domains/${domainId}/hostnames`,
|
|
289
|
-
{ query: { envType }, body: { hostname, ...bodyOpts } }
|
|
290
|
-
);
|
|
204
|
+
return getMgmt().domains.addHostname(projectId, domainId, hostname, opts);
|
|
291
205
|
},
|
|
292
|
-
/** Remove a hostname binding */
|
|
293
206
|
async removeHostname(projectId, domainId, hostnameId) {
|
|
294
|
-
return
|
|
295
|
-
"DELETE",
|
|
296
|
-
`/domains/projects/${projectId}/domains/${domainId}/hostnames/${hostnameId}`
|
|
297
|
-
);
|
|
207
|
+
return getMgmt().domains.removeHostname(projectId, domainId, hostnameId);
|
|
298
208
|
},
|
|
299
|
-
/** Trigger DNS check for a hostname */
|
|
300
209
|
async checkHostname(projectId, domainId, hostnameId) {
|
|
301
|
-
return
|
|
302
|
-
"POST",
|
|
303
|
-
`/domains/projects/${projectId}/domains/${domainId}/hostnames/${hostnameId}/check`
|
|
304
|
-
);
|
|
210
|
+
return getMgmt().domains.checkHostname(projectId, domainId, hostnameId);
|
|
305
211
|
},
|
|
306
|
-
/** Enable email sending for a domain (generates DKIM keypair) */
|
|
307
212
|
async enableEmail(projectId, domainId, opts) {
|
|
308
|
-
return
|
|
309
|
-
"POST",
|
|
310
|
-
`/domains/projects/${projectId}/domains/${domainId}/email`,
|
|
311
|
-
{ body: opts ?? {} }
|
|
312
|
-
);
|
|
213
|
+
return getMgmt().domains.enableEmail(projectId, domainId, opts);
|
|
313
214
|
},
|
|
314
|
-
/** Check DKIM DNS propagation and load into Stalwart */
|
|
315
215
|
async checkEmail(projectId, domainId) {
|
|
316
|
-
return
|
|
317
|
-
"POST",
|
|
318
|
-
`/domains/${projectId}/domains/${domainId}/email/check`
|
|
319
|
-
);
|
|
216
|
+
return getMgmt().domains.checkEmail(projectId, domainId);
|
|
320
217
|
},
|
|
321
|
-
/** Disable email sending for a domain */
|
|
322
218
|
async disableEmail(projectId, domainId) {
|
|
323
|
-
return
|
|
219
|
+
return getMgmt().domains.disableEmail(projectId, domainId);
|
|
324
220
|
},
|
|
325
|
-
|
|
221
|
+
// User
|
|
326
222
|
async whoami() {
|
|
327
|
-
return
|
|
223
|
+
return getMgmt().user.whoami();
|
|
328
224
|
},
|
|
329
|
-
|
|
225
|
+
// Projects
|
|
330
226
|
async listProjects() {
|
|
331
|
-
|
|
332
|
-
return Array.isArray(res) ? res : res.data ?? [];
|
|
227
|
+
return getMgmt().projects.list();
|
|
333
228
|
},
|
|
334
|
-
/** Create a new project */
|
|
335
229
|
async createProject(data) {
|
|
336
|
-
return
|
|
230
|
+
return getMgmt().projects.create(data);
|
|
337
231
|
},
|
|
338
|
-
/** Delete a project */
|
|
339
232
|
async deleteProject(id) {
|
|
340
|
-
return
|
|
233
|
+
return getMgmt().projects.delete(id);
|
|
234
|
+
},
|
|
235
|
+
// Organization
|
|
236
|
+
async getCurrentOrg() {
|
|
237
|
+
return getMgmt().org.current();
|
|
238
|
+
},
|
|
239
|
+
async listOrgMembers(orgId) {
|
|
240
|
+
return getMgmt().org.listMembers(orgId);
|
|
241
|
+
},
|
|
242
|
+
async inviteOrgMember(orgId, email, role) {
|
|
243
|
+
return getMgmt().org.inviteMember(orgId, email, role);
|
|
244
|
+
},
|
|
245
|
+
async removeOrgMember(orgId, userId) {
|
|
246
|
+
return getMgmt().org.removeMember(orgId, userId);
|
|
341
247
|
},
|
|
342
|
-
/** @deprecated Use listProjects()
|
|
248
|
+
/** @deprecated Use listProjects() */
|
|
343
249
|
async listApps(_orgId) {
|
|
344
250
|
return this.listProjects();
|
|
345
251
|
},
|
|
346
|
-
|
|
252
|
+
// Databases
|
|
347
253
|
async listDatabases() {
|
|
348
|
-
|
|
349
|
-
"GET",
|
|
350
|
-
"/resources",
|
|
351
|
-
{
|
|
352
|
-
query: { kind: "database" }
|
|
353
|
-
}
|
|
354
|
-
);
|
|
355
|
-
return Array.isArray(res) ? res : res.data ?? [];
|
|
254
|
+
return getMgmt().databases.list();
|
|
356
255
|
},
|
|
357
|
-
/** Provision a new database (via unified Resources API) */
|
|
358
256
|
async createDatabase(data) {
|
|
359
|
-
return
|
|
257
|
+
return getMgmt().databases.create(data);
|
|
360
258
|
},
|
|
361
|
-
/** Get database details (via unified Resources API) */
|
|
362
259
|
async getDatabase(id) {
|
|
363
|
-
return
|
|
260
|
+
return getMgmt().databases.get(id);
|
|
364
261
|
},
|
|
365
|
-
/** Delete a database (via unified Resources API) */
|
|
366
262
|
async deleteDatabase(id) {
|
|
367
|
-
return
|
|
263
|
+
return getMgmt().databases.delete(id);
|
|
368
264
|
},
|
|
369
|
-
/** Branch a database for staging/preview (via unified Resources API) */
|
|
370
265
|
async branchDatabase(id, data) {
|
|
371
|
-
return
|
|
372
|
-
},
|
|
373
|
-
/** Batch-set env vars for a project environment */
|
|
374
|
-
async setEnvVars(projectId, vars, envType) {
|
|
375
|
-
return request("POST", `/projects/${projectId}/env-vars`, {
|
|
376
|
-
body: { envType, vars }
|
|
377
|
-
});
|
|
266
|
+
return getMgmt().databases.branch(id, data);
|
|
378
267
|
},
|
|
379
|
-
|
|
380
|
-
async pollCliAuth(code) {
|
|
381
|
-
const res = await fetch(`${AUTH_BASE_URL}/api/auth/cli/poll?code=${encodeURIComponent(code)}`, {
|
|
382
|
-
headers: { "User-Agent": `sylphx-cli/${version}` }
|
|
383
|
-
});
|
|
384
|
-
if (!res.ok) {
|
|
385
|
-
throw new ApiError(res.status, "Failed to poll auth status");
|
|
386
|
-
}
|
|
387
|
-
return res.json();
|
|
388
|
-
},
|
|
389
|
-
// ── Services ─────────────────────────────────────────────────────────────
|
|
390
|
-
/** List services for a project */
|
|
268
|
+
// Services
|
|
391
269
|
async listServices(projectId) {
|
|
392
|
-
|
|
393
|
-
"GET",
|
|
394
|
-
`/projects/${encodeURIComponent(projectId)}/services`
|
|
395
|
-
);
|
|
396
|
-
return Array.isArray(res) ? res : res.data ?? [];
|
|
270
|
+
return getMgmt().services.list(projectId);
|
|
397
271
|
},
|
|
398
|
-
/** Get a service by name */
|
|
399
272
|
async getService(projectId, name) {
|
|
400
|
-
return
|
|
401
|
-
"GET",
|
|
402
|
-
`/projects/${encodeURIComponent(projectId)}/services/${encodeURIComponent(name)}`
|
|
403
|
-
);
|
|
273
|
+
return getMgmt().services.get(projectId, name);
|
|
404
274
|
},
|
|
405
|
-
/**
|
|
406
|
-
* Deploy a specific service.
|
|
407
|
-
* Backend expects { environmentId?, force?, image? } — NOT envType.
|
|
408
|
-
* Resolves envType → environmentId automatically.
|
|
409
|
-
*/
|
|
410
275
|
async deployService(projectId, name, envType) {
|
|
411
|
-
|
|
412
|
-
return request(
|
|
413
|
-
"POST",
|
|
414
|
-
`/projects/${encodeURIComponent(projectId)}/services/${encodeURIComponent(name)}/deploy`,
|
|
415
|
-
{ body: environmentId ? { environmentId } : {} }
|
|
416
|
-
);
|
|
276
|
+
return getMgmt().services.deploy(projectId, name, envType);
|
|
417
277
|
},
|
|
418
|
-
/** Delete a service */
|
|
419
278
|
async deleteService(projectId, name) {
|
|
420
|
-
return
|
|
421
|
-
"DELETE",
|
|
422
|
-
`/projects/${encodeURIComponent(projectId)}/services/${encodeURIComponent(name)}`
|
|
423
|
-
);
|
|
279
|
+
return getMgmt().services.delete(projectId, name);
|
|
424
280
|
},
|
|
425
|
-
//
|
|
426
|
-
/** List volumes for a project */
|
|
281
|
+
// Volumes
|
|
427
282
|
async listVolumes(projectId) {
|
|
428
|
-
|
|
429
|
-
"GET",
|
|
430
|
-
`/projects/${encodeURIComponent(projectId)}/volumes`
|
|
431
|
-
);
|
|
432
|
-
return Array.isArray(res) ? res : res.volumes ?? [];
|
|
283
|
+
return getMgmt().volumes.list(projectId);
|
|
433
284
|
},
|
|
434
|
-
/** Create a volume */
|
|
435
285
|
async createVolume(projectId, data) {
|
|
436
|
-
return
|
|
437
|
-
"POST",
|
|
438
|
-
`/projects/${encodeURIComponent(projectId)}/volumes`,
|
|
439
|
-
{ body: data }
|
|
440
|
-
);
|
|
286
|
+
return getMgmt().volumes.create(projectId, data);
|
|
441
287
|
},
|
|
442
|
-
/** Delete a volume */
|
|
443
288
|
async deleteVolume(projectId, volId) {
|
|
444
|
-
return
|
|
445
|
-
"DELETE",
|
|
446
|
-
`/projects/${encodeURIComponent(projectId)}/volumes/${encodeURIComponent(volId)}`
|
|
447
|
-
);
|
|
289
|
+
return getMgmt().volumes.delete(projectId, volId);
|
|
448
290
|
},
|
|
449
|
-
//
|
|
450
|
-
/** List storage resources for a project.
|
|
451
|
-
* Backend returns a plain array of binding objects (not wrapped in { data: [...] }).
|
|
452
|
-
* Optionally filter by envType client-side.
|
|
453
|
-
*/
|
|
291
|
+
// Storage
|
|
454
292
|
async listStorage(projectId, envType) {
|
|
455
|
-
|
|
456
|
-
"GET",
|
|
457
|
-
`/projects/${encodeURIComponent(projectId)}/storage`
|
|
458
|
-
);
|
|
459
|
-
const list = Array.isArray(res) ? res : [];
|
|
460
|
-
return envType ? list.filter((r) => !r.envType || r.envType === envType) : list;
|
|
293
|
+
return getMgmt().storage.list(projectId, envType);
|
|
461
294
|
},
|
|
462
|
-
/** Provision blob storage for a project environment */
|
|
463
295
|
async createStorage(projectId, data) {
|
|
464
|
-
return
|
|
465
|
-
body: data
|
|
466
|
-
});
|
|
296
|
+
return getMgmt().storage.create(projectId, data);
|
|
467
297
|
},
|
|
468
|
-
/** Delete a storage resource */
|
|
469
298
|
async deleteStorage(projectId, storageId) {
|
|
470
|
-
return
|
|
471
|
-
"DELETE",
|
|
472
|
-
`/projects/${encodeURIComponent(projectId)}/storage/${encodeURIComponent(storageId)}`
|
|
473
|
-
);
|
|
299
|
+
return getMgmt().storage.delete(projectId, storageId);
|
|
474
300
|
},
|
|
475
|
-
//
|
|
476
|
-
/**
|
|
477
|
-
* List all config keys for a project.
|
|
478
|
-
* Backend uses ?env=<envId> (not ?envType). Resolves envType → envId automatically.
|
|
479
|
-
*/
|
|
301
|
+
// Config
|
|
480
302
|
async listConfig(projectId, envType) {
|
|
481
|
-
|
|
482
|
-
if (envType) {
|
|
483
|
-
const envId = await this.resolveEnvId(projectId, envType);
|
|
484
|
-
if (envId) query.env = envId;
|
|
485
|
-
}
|
|
486
|
-
const res = await request(
|
|
487
|
-
"GET",
|
|
488
|
-
`/projects/${encodeURIComponent(projectId)}/config`,
|
|
489
|
-
{ query }
|
|
490
|
-
);
|
|
491
|
-
return Array.isArray(res) ? res : res.data ?? [];
|
|
303
|
+
return getMgmt().config.list(projectId, envType);
|
|
492
304
|
},
|
|
493
|
-
/**
|
|
494
|
-
* Get a single config key.
|
|
495
|
-
* Backend uses ?env=<envId> (not ?envType). Resolves envType → envId automatically.
|
|
496
|
-
*/
|
|
497
305
|
async getConfig(projectId, key, envType) {
|
|
498
|
-
|
|
499
|
-
if (envType) {
|
|
500
|
-
const envId = await this.resolveEnvId(projectId, envType);
|
|
501
|
-
if (envId) query.env = envId;
|
|
502
|
-
}
|
|
503
|
-
return request(
|
|
504
|
-
"GET",
|
|
505
|
-
`/projects/${encodeURIComponent(projectId)}/config/${encodeURIComponent(key)}`,
|
|
506
|
-
{ query }
|
|
507
|
-
);
|
|
306
|
+
return getMgmt().config.get(projectId, key, envType);
|
|
508
307
|
},
|
|
509
|
-
/**
|
|
510
|
-
* Set a config key.
|
|
511
|
-
* Backend expects { key, value, environmentId } (not envType).
|
|
512
|
-
* Resolves envType → environmentId automatically when envType is provided.
|
|
513
|
-
*/
|
|
514
308
|
async setConfig(projectId, key, value, envType) {
|
|
515
|
-
|
|
516
|
-
if (envType) {
|
|
517
|
-
environmentId = await this.resolveEnvId(projectId, envType);
|
|
518
|
-
}
|
|
519
|
-
return request("POST", `/projects/${encodeURIComponent(projectId)}/config`, {
|
|
520
|
-
body: { key, value, environmentId }
|
|
521
|
-
});
|
|
309
|
+
return getMgmt().config.set(projectId, key, value, envType);
|
|
522
310
|
},
|
|
523
|
-
/**
|
|
524
|
-
* Delete a config key.
|
|
525
|
-
* Backend uses ?env=<envId> (not ?envType). Resolves envType → envId automatically.
|
|
526
|
-
*/
|
|
527
311
|
async deleteConfig(projectId, key, envType) {
|
|
528
|
-
|
|
529
|
-
if (envType) {
|
|
530
|
-
const envId = await this.resolveEnvId(projectId, envType);
|
|
531
|
-
if (envId) query.env = envId;
|
|
532
|
-
}
|
|
533
|
-
return request(
|
|
534
|
-
"DELETE",
|
|
535
|
-
`/projects/${encodeURIComponent(projectId)}/config/${encodeURIComponent(key)}`,
|
|
536
|
-
{ query }
|
|
537
|
-
);
|
|
312
|
+
return getMgmt().config.delete(projectId, key, envType);
|
|
538
313
|
},
|
|
539
|
-
//
|
|
540
|
-
/** List environments for a project */
|
|
314
|
+
// Environments
|
|
541
315
|
async listEnvironments(projectId) {
|
|
542
|
-
|
|
543
|
-
"GET",
|
|
544
|
-
`/projects/${encodeURIComponent(projectId)}/environments`
|
|
545
|
-
);
|
|
546
|
-
return Array.isArray(res) ? res : res.data ?? [];
|
|
316
|
+
return getMgmt().environments.list(projectId);
|
|
547
317
|
},
|
|
548
|
-
/**
|
|
549
|
-
* Resolve an environment type string (e.g. "production") → its envId for the given project.
|
|
550
|
-
* Returns null if not found. Used internally by config commands.
|
|
551
|
-
*/
|
|
552
318
|
async resolveEnvId(projectId, envType) {
|
|
553
|
-
|
|
554
|
-
const match = envs.find((e) => (e.envType ?? "").toLowerCase() === envType.toLowerCase());
|
|
555
|
-
return match?.id ?? null;
|
|
319
|
+
return getMgmt().environments.resolveId(projectId, envType);
|
|
556
320
|
},
|
|
557
|
-
/** Promote an environment (e.g. staging → production).
|
|
558
|
-
* Backend returns { promoted: number, services: [...], sourceEnvironment, targetEnvironment }
|
|
559
|
-
*/
|
|
560
321
|
async promoteEnvironment(projectId, targetEnvId, sourceEnvId) {
|
|
561
|
-
return
|
|
562
|
-
"POST",
|
|
563
|
-
`/projects/${encodeURIComponent(projectId)}/environments/${encodeURIComponent(targetEnvId)}/promote`,
|
|
564
|
-
{ body: { sourceEnvId } }
|
|
565
|
-
);
|
|
322
|
+
return getMgmt().environments.promote(projectId, targetEnvId, sourceEnvId);
|
|
566
323
|
},
|
|
567
|
-
//
|
|
568
|
-
/** List bindings for a resource (which envs it's linked to).
|
|
569
|
-
* Backend returns { bindings: [...] }.
|
|
570
|
-
*/
|
|
324
|
+
// Resource Bindings
|
|
571
325
|
async listResourceBindings(resourceId) {
|
|
572
|
-
|
|
573
|
-
"GET",
|
|
574
|
-
`/resources/${encodeURIComponent(resourceId)}/bindings`
|
|
575
|
-
);
|
|
576
|
-
return res.bindings ?? [];
|
|
326
|
+
return getMgmt().resources.listBindings(resourceId);
|
|
577
327
|
},
|
|
578
|
-
/**
|
|
579
|
-
* Bind a resource to a project environment.
|
|
580
|
-
* Backend expects { appEnvironmentId, role? }.
|
|
581
|
-
* Accepts projectId + envType and resolves to appEnvironmentId automatically.
|
|
582
|
-
*/
|
|
583
328
|
async bindResource(resourceId, data) {
|
|
584
|
-
|
|
585
|
-
if (!appEnvironmentId) {
|
|
586
|
-
throw new Error(`Environment '${data.envType}' not found for project '${data.projectId}'`);
|
|
587
|
-
}
|
|
588
|
-
return request(
|
|
589
|
-
"POST",
|
|
590
|
-
`/resources/${encodeURIComponent(resourceId)}/bindings`,
|
|
591
|
-
{ body: { appEnvironmentId, role: data.role ?? "primary" } }
|
|
592
|
-
);
|
|
329
|
+
return getMgmt().resources.bind(resourceId, data);
|
|
593
330
|
},
|
|
594
|
-
/** Unbind a resource from a project environment */
|
|
595
331
|
async unbindResource(resourceId, bindingId) {
|
|
596
|
-
return
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
332
|
+
return getMgmt().resources.unbind(resourceId, bindingId);
|
|
333
|
+
},
|
|
334
|
+
// Tasks
|
|
335
|
+
async listTasks(projectId, envType) {
|
|
336
|
+
return getMgmt().tasks.list(projectId, envType);
|
|
337
|
+
},
|
|
338
|
+
async createTask(projectId, data) {
|
|
339
|
+
return getMgmt().tasks.create(projectId, data);
|
|
340
|
+
},
|
|
341
|
+
async getTask(projectId, taskId) {
|
|
342
|
+
return getMgmt().tasks.get(projectId, taskId);
|
|
343
|
+
},
|
|
344
|
+
async updateTask(projectId, taskId, data) {
|
|
345
|
+
return getMgmt().tasks.update(projectId, taskId, data);
|
|
346
|
+
},
|
|
347
|
+
async deleteTask(projectId, taskId) {
|
|
348
|
+
return getMgmt().tasks.delete(projectId, taskId);
|
|
349
|
+
},
|
|
350
|
+
// CLI-specific: auth poll (uses main sylphx.com domain, not api.sylphx.com)
|
|
351
|
+
async pollCliAuth(code) {
|
|
352
|
+
const AUTH_BASE_URL = "https://sylphx.com";
|
|
353
|
+
const res = await fetch(`${AUTH_BASE_URL}/api/auth/cli/poll?code=${encodeURIComponent(code)}`, {
|
|
354
|
+
headers: { "User-Agent": `sylphx-cli/${version}` }
|
|
355
|
+
});
|
|
356
|
+
if (!res.ok) {
|
|
357
|
+
throw new import_management.ApiError(res.status, "Failed to poll auth status");
|
|
358
|
+
}
|
|
359
|
+
return res.json();
|
|
600
360
|
}
|
|
601
361
|
};
|
|
362
|
+
var BASE_URL = process.env.SYLPHX_API_URL ?? "https://api.sylphx.com";
|
|
363
|
+
var API_BASE = `${BASE_URL}/v1`;
|
|
602
364
|
function createLogStream(projectId, envType) {
|
|
603
365
|
const params = new URLSearchParams({ envType, stream: "true", limit: "200" });
|
|
604
366
|
return {
|
|
@@ -800,14 +562,23 @@ var dbListCommand = new import_commander2.Command("list").description("List all
|
|
|
800
562
|
process.exit(1);
|
|
801
563
|
}
|
|
802
564
|
});
|
|
803
|
-
var dbCreateCommand = new import_commander2.Command("create").description("Provision a new database").argument("<name>", "Database name").option(
|
|
565
|
+
var dbCreateCommand = new import_commander2.Command("create").description("Provision a new database").argument("<name>", "Database name").option(
|
|
566
|
+
"--tier <tier>",
|
|
567
|
+
"Database tier: starter | standard | performance | enterprise1 | enterprise2 (default: standard)",
|
|
568
|
+
"standard"
|
|
569
|
+
).option("--storage <gb>", "Storage in GB (default: 10)", "10").option("--env <env>", "Environment (default: production)", "production").action(async (name, opts) => {
|
|
804
570
|
requireAuth();
|
|
805
571
|
const storageGb = Number.parseInt(opts.storage, 10);
|
|
806
572
|
if (Number.isNaN(storageGb) || storageGb < 5) {
|
|
807
573
|
console.log(import_chalk2.default.red("Storage must be at least 5 GB."));
|
|
808
574
|
process.exit(1);
|
|
809
575
|
}
|
|
576
|
+
const VALID_TIERS = ["starter", "standard", "performance", "enterprise1", "enterprise2"];
|
|
810
577
|
const tier = opts.tier === "pro" ? "performance" : opts.tier;
|
|
578
|
+
if (!VALID_TIERS.includes(tier)) {
|
|
579
|
+
console.log(import_chalk2.default.red(`Invalid tier '${tier}'. Valid: ${VALID_TIERS.join(", ")}`));
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
811
582
|
const spinner = (0, import_ora2.default)(
|
|
812
583
|
`Provisioning database ${import_chalk2.default.bold(name)} (${tier}, ${storageGb} GB)...`
|
|
813
584
|
).start();
|
|
@@ -1827,7 +1598,6 @@ var loginCommand = new import_commander8.Command("login").description("Authentic
|
|
|
1827
1598
|
try {
|
|
1828
1599
|
config.setToken(opts.token);
|
|
1829
1600
|
const me = await api.whoami();
|
|
1830
|
-
config.setToken(opts.token);
|
|
1831
1601
|
spinner2.succeed(import_chalk9.default.green(`Authenticated as ${import_chalk9.default.bold(me.user.email)}`));
|
|
1832
1602
|
} catch (err) {
|
|
1833
1603
|
config.clearToken();
|
|
@@ -1990,15 +1760,146 @@ var openCommand = new import_commander11.Command("open").description("Open the a
|
|
|
1990
1760
|
}
|
|
1991
1761
|
});
|
|
1992
1762
|
|
|
1993
|
-
// src/commands/
|
|
1994
|
-
var import_node_readline4 = __toESM(require("readline"));
|
|
1763
|
+
// src/commands/orgs.ts
|
|
1995
1764
|
var import_chalk13 = __toESM(require("chalk"));
|
|
1996
1765
|
var import_commander12 = require("commander");
|
|
1997
1766
|
var import_ora10 = __toESM(require("ora"));
|
|
1998
1767
|
function requireAuth2() {
|
|
1999
1768
|
const token = config.getToken();
|
|
2000
1769
|
if (!token) {
|
|
2001
|
-
console.
|
|
1770
|
+
console.error(import_chalk13.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1771
|
+
process.exit(1);
|
|
1772
|
+
}
|
|
1773
|
+
return token;
|
|
1774
|
+
}
|
|
1775
|
+
function roleBadge(role) {
|
|
1776
|
+
switch (role.toLowerCase()) {
|
|
1777
|
+
case "owner":
|
|
1778
|
+
return import_chalk13.default.yellow("owner ");
|
|
1779
|
+
case "admin":
|
|
1780
|
+
return import_chalk13.default.cyan("admin ");
|
|
1781
|
+
default:
|
|
1782
|
+
return import_chalk13.default.dim("member");
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
async function showCurrentOrg() {
|
|
1786
|
+
requireAuth2();
|
|
1787
|
+
const spinner = (0, import_ora10.default)("Fetching org info...").start();
|
|
1788
|
+
try {
|
|
1789
|
+
const [org, whoami] = await Promise.all([api.getCurrentOrg(), api.whoami()]);
|
|
1790
|
+
spinner.stop();
|
|
1791
|
+
console.log("");
|
|
1792
|
+
console.log(import_chalk13.default.bold(" Organization"));
|
|
1793
|
+
console.log(import_chalk13.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1794
|
+
console.log(` ${import_chalk13.default.bold("Name")} ${org.name}`);
|
|
1795
|
+
console.log(` ${import_chalk13.default.bold("Slug")} ${import_chalk13.default.cyan(org.slug)}`);
|
|
1796
|
+
console.log(` ${import_chalk13.default.bold("ID")} ${import_chalk13.default.dim(org.id)}`);
|
|
1797
|
+
if (org.email) console.log(` ${import_chalk13.default.bold("Email")} ${org.email}`);
|
|
1798
|
+
console.log("");
|
|
1799
|
+
const allOrgs = whoami.orgs;
|
|
1800
|
+
if (allOrgs.length > 1) {
|
|
1801
|
+
console.log(import_chalk13.default.dim(` You are a member of ${allOrgs.length} organizations:`));
|
|
1802
|
+
for (const o of allOrgs) {
|
|
1803
|
+
const isCurrent = o.id === org.id;
|
|
1804
|
+
console.log(
|
|
1805
|
+
` ${isCurrent ? import_chalk13.default.green("\u2192") : " "} ${import_chalk13.default.bold(o.name)} ${import_chalk13.default.dim(`(${o.slug})`)}`
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
console.log("");
|
|
1809
|
+
}
|
|
1810
|
+
} catch (err) {
|
|
1811
|
+
spinner.fail("Failed to fetch org info");
|
|
1812
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1813
|
+
console.error(import_chalk13.default.red(message));
|
|
1814
|
+
process.exit(1);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
var membersListCommand = new import_commander12.Command("list").alias("ls").description("List organization members").action(async () => {
|
|
1818
|
+
requireAuth2();
|
|
1819
|
+
const spinner = (0, import_ora10.default)("Fetching members...").start();
|
|
1820
|
+
try {
|
|
1821
|
+
const org = await api.getCurrentOrg();
|
|
1822
|
+
const members = await api.listOrgMembers(org.id);
|
|
1823
|
+
spinner.stop();
|
|
1824
|
+
if (members.length === 0) {
|
|
1825
|
+
console.log(import_chalk13.default.dim("\n No members found.\n"));
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
const colEmail = Math.max(5, ...members.map((m) => m.email.length));
|
|
1829
|
+
const colName = Math.max(4, ...members.map((m) => (m.name ?? "\u2014").length));
|
|
1830
|
+
console.log("");
|
|
1831
|
+
console.log(import_chalk13.default.bold(` Members of ${org.name}`));
|
|
1832
|
+
console.log(import_chalk13.default.dim(` ${"\u2500".repeat(colEmail + colName + 20)}`));
|
|
1833
|
+
console.log(
|
|
1834
|
+
import_chalk13.default.dim(` ${"ROLE ".padEnd(7)} ${"EMAIL".padEnd(colEmail)} ${"NAME".padEnd(colName)}`)
|
|
1835
|
+
);
|
|
1836
|
+
console.log(import_chalk13.default.dim(` ${"\u2500".repeat(colEmail + colName + 20)}`));
|
|
1837
|
+
for (const m of members) {
|
|
1838
|
+
const joined = m.joinedAt ? new Date(m.joinedAt).toLocaleDateString() : "";
|
|
1839
|
+
console.log(
|
|
1840
|
+
` ${roleBadge(m.role)} ${m.email.padEnd(colEmail)} ${(m.name ?? import_chalk13.default.dim("\u2014")).padEnd(colName)} ${import_chalk13.default.dim(joined)}`
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
console.log("");
|
|
1844
|
+
console.log(import_chalk13.default.dim(` ${members.length} member${members.length !== 1 ? "s" : ""}`));
|
|
1845
|
+
console.log("");
|
|
1846
|
+
} catch (err) {
|
|
1847
|
+
spinner.fail("Failed to fetch members");
|
|
1848
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1849
|
+
console.error(import_chalk13.default.red(message));
|
|
1850
|
+
process.exit(1);
|
|
1851
|
+
}
|
|
1852
|
+
});
|
|
1853
|
+
var membersInviteCommand = new import_commander12.Command("invite").description("Invite a member to the organization by email").argument("<email>", "Email address to invite").option("--role <role>", "Role to assign (admin | member)", "member").action(async (email, options) => {
|
|
1854
|
+
requireAuth2();
|
|
1855
|
+
const role = options.role;
|
|
1856
|
+
if (role !== "admin" && role !== "member") {
|
|
1857
|
+
console.error(import_chalk13.default.red(`Invalid role "${role}". Must be "admin" or "member".`));
|
|
1858
|
+
process.exit(1);
|
|
1859
|
+
}
|
|
1860
|
+
const spinner = (0, import_ora10.default)(`Inviting ${email}...`).start();
|
|
1861
|
+
try {
|
|
1862
|
+
const org = await api.getCurrentOrg();
|
|
1863
|
+
await api.inviteOrgMember(org.id, email, role);
|
|
1864
|
+
spinner.succeed(
|
|
1865
|
+
`Invited ${import_chalk13.default.bold(email)} as ${import_chalk13.default.cyan(role)} to ${import_chalk13.default.bold(org.name)}`
|
|
1866
|
+
);
|
|
1867
|
+
console.log(import_chalk13.default.dim(" They will receive an invitation email shortly."));
|
|
1868
|
+
} catch (err) {
|
|
1869
|
+
spinner.fail("Failed to send invitation");
|
|
1870
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1871
|
+
console.error(import_chalk13.default.red(message));
|
|
1872
|
+
process.exit(1);
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
var membersRemoveCommand = new import_commander12.Command("remove").description("Remove a member from the organization").argument("<userId>", "User ID to remove (use `sylphx orgs members` to list)").action(async (userId) => {
|
|
1876
|
+
requireAuth2();
|
|
1877
|
+
const spinner = (0, import_ora10.default)(`Removing member ${userId}...`).start();
|
|
1878
|
+
try {
|
|
1879
|
+
const org = await api.getCurrentOrg();
|
|
1880
|
+
await api.removeOrgMember(org.id, userId);
|
|
1881
|
+
spinner.succeed(`Removed member ${import_chalk13.default.bold(userId)} from ${import_chalk13.default.bold(org.name)}`);
|
|
1882
|
+
} catch (err) {
|
|
1883
|
+
spinner.fail("Failed to remove member");
|
|
1884
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1885
|
+
console.error(import_chalk13.default.red(message));
|
|
1886
|
+
process.exit(1);
|
|
1887
|
+
}
|
|
1888
|
+
});
|
|
1889
|
+
var membersCommand = new import_commander12.Command("members").description("Manage organization members").action(async () => {
|
|
1890
|
+
await membersListCommand.parseAsync([], { from: "user" });
|
|
1891
|
+
}).addCommand(membersListCommand).addCommand(membersInviteCommand).addCommand(membersRemoveCommand);
|
|
1892
|
+
var orgsCommand = new import_commander12.Command("orgs").description("Manage organizations").action(showCurrentOrg).addCommand(membersCommand);
|
|
1893
|
+
|
|
1894
|
+
// src/commands/projects.ts
|
|
1895
|
+
var import_node_readline4 = __toESM(require("readline"));
|
|
1896
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
1897
|
+
var import_commander13 = require("commander");
|
|
1898
|
+
var import_ora11 = __toESM(require("ora"));
|
|
1899
|
+
function requireAuth3() {
|
|
1900
|
+
const token = config.getToken();
|
|
1901
|
+
if (!token) {
|
|
1902
|
+
console.log(import_chalk14.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2002
1903
|
process.exit(1);
|
|
2003
1904
|
}
|
|
2004
1905
|
return token;
|
|
@@ -2015,41 +1916,41 @@ function prompt4(question) {
|
|
|
2015
1916
|
});
|
|
2016
1917
|
});
|
|
2017
1918
|
}
|
|
2018
|
-
var projectsListCommand = new
|
|
2019
|
-
|
|
2020
|
-
const spinner = (0,
|
|
1919
|
+
var projectsListCommand = new import_commander13.Command("list").description("List all projects").action(async () => {
|
|
1920
|
+
requireAuth3();
|
|
1921
|
+
const spinner = (0, import_ora11.default)("Fetching projects...").start();
|
|
2021
1922
|
try {
|
|
2022
1923
|
const projects = await api.listProjects();
|
|
2023
1924
|
spinner.stop();
|
|
2024
1925
|
if (projects.length === 0) {
|
|
2025
|
-
console.log(
|
|
1926
|
+
console.log(import_chalk14.default.dim("\n No projects found.\n"));
|
|
2026
1927
|
return;
|
|
2027
1928
|
}
|
|
2028
1929
|
const colSlug = Math.max(4, ...projects.map((p) => p.slug.length));
|
|
2029
1930
|
const colName = Math.max(4, ...projects.map((p) => p.name.length));
|
|
2030
1931
|
console.log("");
|
|
2031
|
-
console.log(
|
|
2032
|
-
console.log(
|
|
1932
|
+
console.log(import_chalk14.default.bold(" Projects"));
|
|
1933
|
+
console.log(import_chalk14.default.dim(` ${"\u2500".repeat(colSlug + colName + 30)}`));
|
|
2033
1934
|
console.log(
|
|
2034
|
-
|
|
1935
|
+
import_chalk14.default.dim(` ${"SLUG".padEnd(colSlug)} ${"NAME".padEnd(colName)} ${"ENVIRONMENTS"}`)
|
|
2035
1936
|
);
|
|
2036
|
-
console.log(
|
|
1937
|
+
console.log(import_chalk14.default.dim(` ${"\u2500".repeat(colSlug + colName + 30)}`));
|
|
2037
1938
|
for (const p of projects) {
|
|
2038
|
-
const envNames = p.environments && p.environments.length > 0 ? p.environments.map((e) => e.name || e.envType || e.id).join(", ") :
|
|
1939
|
+
const envNames = p.environments && p.environments.length > 0 ? p.environments.map((e) => e.name || e.envType || e.id).join(", ") : import_chalk14.default.dim("\u2014");
|
|
2039
1940
|
console.log(
|
|
2040
|
-
` ${
|
|
1941
|
+
` ${import_chalk14.default.cyan(p.slug.padEnd(colSlug))} ${p.name.padEnd(colName)} ${envNames}`
|
|
2041
1942
|
);
|
|
2042
1943
|
}
|
|
2043
1944
|
console.log("");
|
|
2044
1945
|
} catch (err) {
|
|
2045
|
-
spinner.fail(
|
|
1946
|
+
spinner.fail(import_chalk14.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
2046
1947
|
process.exit(1);
|
|
2047
1948
|
}
|
|
2048
1949
|
});
|
|
2049
|
-
var projectsCreateCommand = new
|
|
2050
|
-
|
|
1950
|
+
var projectsCreateCommand = new import_commander13.Command("create").description("Create a new project (without linking the current directory)").argument("<name>", "Project name").option("--git <repo>", "Git repository (e.g. owner/repo)").option("--port <port>", "Application port").action(async (name, opts) => {
|
|
1951
|
+
requireAuth3();
|
|
2051
1952
|
const slug = slugify2(name);
|
|
2052
|
-
const spinner = (0,
|
|
1953
|
+
const spinner = (0, import_ora11.default)(`Creating project ${import_chalk14.default.bold(name)}...`).start();
|
|
2053
1954
|
try {
|
|
2054
1955
|
const project = await api.createProject({
|
|
2055
1956
|
name,
|
|
@@ -2057,77 +1958,77 @@ var projectsCreateCommand = new import_commander12.Command("create").description
|
|
|
2057
1958
|
...opts.git ? { gitRepository: opts.git } : {},
|
|
2058
1959
|
...opts.port ? { port: Number.parseInt(opts.port, 10) } : {}
|
|
2059
1960
|
});
|
|
2060
|
-
spinner.succeed(
|
|
1961
|
+
spinner.succeed(import_chalk14.default.green(`\u2713 Created project ${import_chalk14.default.bold(project.name)}`));
|
|
2061
1962
|
console.log("");
|
|
2062
|
-
console.log(
|
|
2063
|
-
console.log(
|
|
1963
|
+
console.log(import_chalk14.default.dim(` ID : ${project.id}`));
|
|
1964
|
+
console.log(import_chalk14.default.dim(` Slug : ${project.slug}`));
|
|
2064
1965
|
console.log("");
|
|
2065
1966
|
console.log(
|
|
2066
|
-
|
|
1967
|
+
import_chalk14.default.dim(` Run ${import_chalk14.default.cyan("sylphx link")} to link this project to a directory.`)
|
|
2067
1968
|
);
|
|
2068
1969
|
console.log("");
|
|
2069
1970
|
} catch (err) {
|
|
2070
|
-
spinner.fail(
|
|
1971
|
+
spinner.fail(import_chalk14.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
2071
1972
|
process.exit(1);
|
|
2072
1973
|
}
|
|
2073
1974
|
});
|
|
2074
|
-
var projectsDeleteCommand = new
|
|
2075
|
-
|
|
1975
|
+
var projectsDeleteCommand = new import_commander13.Command("delete").description("Delete a project").argument("<id>", "Project ID or slug").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
|
|
1976
|
+
requireAuth3();
|
|
2076
1977
|
if (!opts.force) {
|
|
2077
1978
|
console.log("");
|
|
2078
1979
|
console.log(
|
|
2079
|
-
|
|
1980
|
+
import_chalk14.default.yellow(` \u26A0 You are about to permanently delete project ${import_chalk14.default.bold(id)}.`)
|
|
2080
1981
|
);
|
|
2081
|
-
console.log(
|
|
1982
|
+
console.log(import_chalk14.default.yellow(" This action cannot be undone."));
|
|
2082
1983
|
console.log("");
|
|
2083
1984
|
const answer = await prompt4(" Type the project ID/slug to confirm");
|
|
2084
1985
|
if (answer !== id) {
|
|
2085
|
-
console.log(
|
|
1986
|
+
console.log(import_chalk14.default.red("Confirmation did not match. Aborted."));
|
|
2086
1987
|
process.exit(1);
|
|
2087
1988
|
}
|
|
2088
1989
|
}
|
|
2089
|
-
const spinner = (0,
|
|
1990
|
+
const spinner = (0, import_ora11.default)(`Deleting project ${import_chalk14.default.bold(id)}...`).start();
|
|
2090
1991
|
try {
|
|
2091
1992
|
await api.deleteProject(id);
|
|
2092
|
-
spinner.succeed(
|
|
1993
|
+
spinner.succeed(import_chalk14.default.green(`\u2713 Deleted project ${import_chalk14.default.bold(id)}`));
|
|
2093
1994
|
} catch (err) {
|
|
2094
|
-
spinner.fail(
|
|
1995
|
+
spinner.fail(import_chalk14.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
2095
1996
|
process.exit(1);
|
|
2096
1997
|
}
|
|
2097
1998
|
});
|
|
2098
|
-
var projectsCommand = new
|
|
1999
|
+
var projectsCommand = new import_commander13.Command("projects").description("Manage Sylphx projects").addCommand(projectsListCommand).addCommand(projectsCreateCommand).addCommand(projectsDeleteCommand);
|
|
2099
2000
|
|
|
2100
2001
|
// src/commands/promote.ts
|
|
2101
|
-
var
|
|
2102
|
-
var
|
|
2103
|
-
var
|
|
2104
|
-
var promoteCommand = new
|
|
2002
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
2003
|
+
var import_commander14 = require("commander");
|
|
2004
|
+
var import_ora12 = __toESM(require("ora"));
|
|
2005
|
+
var promoteCommand = new import_commander14.Command("promote").description("Promote an environment (e.g. staging \u2192 production)").option("--from <env>", "Source environment (default: staging)", "staging").option("--to <env>", "Target environment (default: production)", "production").option("--force", "Skip confirmation prompt").action(async (opts) => {
|
|
2105
2006
|
const token = config.getToken();
|
|
2106
2007
|
if (!token) {
|
|
2107
|
-
console.log(
|
|
2008
|
+
console.log(import_chalk15.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2108
2009
|
process.exit(1);
|
|
2109
2010
|
}
|
|
2110
2011
|
const linked = config.getLinkedApp();
|
|
2111
2012
|
if (!linked) {
|
|
2112
|
-
console.log(
|
|
2013
|
+
console.log(import_chalk15.default.red("No app linked. Run `sylphx link` first."));
|
|
2113
2014
|
process.exit(1);
|
|
2114
2015
|
}
|
|
2115
2016
|
if (opts.from === opts.to) {
|
|
2116
|
-
console.log(
|
|
2017
|
+
console.log(import_chalk15.default.red("Source and target environments must be different."));
|
|
2117
2018
|
process.exit(1);
|
|
2118
2019
|
}
|
|
2119
2020
|
if (!opts.force) {
|
|
2120
|
-
const
|
|
2121
|
-
const rl =
|
|
2021
|
+
const readline9 = await import("readline");
|
|
2022
|
+
const rl = readline9.createInterface({ input: process.stdin, output: process.stdout });
|
|
2122
2023
|
await new Promise((resolve) => {
|
|
2123
2024
|
rl.question(
|
|
2124
|
-
|
|
2125
|
-
` Promote ${
|
|
2025
|
+
import_chalk15.default.yellow(
|
|
2026
|
+
` Promote ${import_chalk15.default.bold(linked.appId)}: ${import_chalk15.default.cyan(opts.from)} \u2192 ${import_chalk15.default.green(opts.to)}? (y/N) `
|
|
2126
2027
|
),
|
|
2127
2028
|
(answer) => {
|
|
2128
2029
|
rl.close();
|
|
2129
2030
|
if (answer.toLowerCase() !== "y") {
|
|
2130
|
-
console.log(
|
|
2031
|
+
console.log(import_chalk15.default.dim(" Cancelled."));
|
|
2131
2032
|
process.exit(0);
|
|
2132
2033
|
}
|
|
2133
2034
|
resolve();
|
|
@@ -2135,7 +2036,7 @@ var promoteCommand = new import_commander13.Command("promote").description("Prom
|
|
|
2135
2036
|
);
|
|
2136
2037
|
});
|
|
2137
2038
|
}
|
|
2138
|
-
const spinner = (0,
|
|
2039
|
+
const spinner = (0, import_ora12.default)(`Resolving environments...`).start();
|
|
2139
2040
|
try {
|
|
2140
2041
|
const envs = await api.listEnvironments(linked.appId);
|
|
2141
2042
|
const sourceEnv = envs.find(
|
|
@@ -2145,48 +2046,48 @@ var promoteCommand = new import_commander13.Command("promote").description("Prom
|
|
|
2145
2046
|
(e) => (e.envType ?? e.name ?? "").toLowerCase() === opts.to.toLowerCase()
|
|
2146
2047
|
);
|
|
2147
2048
|
if (!sourceEnv) {
|
|
2148
|
-
spinner.fail(
|
|
2149
|
-
console.log(
|
|
2049
|
+
spinner.fail(import_chalk15.default.red(`Source environment '${opts.from}' not found.`));
|
|
2050
|
+
console.log(import_chalk15.default.dim(` Available: ${envs.map((e) => e.envType ?? e.name).join(", ")}`));
|
|
2150
2051
|
process.exit(1);
|
|
2151
2052
|
}
|
|
2152
2053
|
if (!targetEnv) {
|
|
2153
|
-
spinner.fail(
|
|
2154
|
-
console.log(
|
|
2054
|
+
spinner.fail(import_chalk15.default.red(`Target environment '${opts.to}' not found.`));
|
|
2055
|
+
console.log(import_chalk15.default.dim(` Available: ${envs.map((e) => e.envType ?? e.name).join(", ")}`));
|
|
2155
2056
|
process.exit(1);
|
|
2156
2057
|
}
|
|
2157
2058
|
spinner.text = `Promoting ${opts.from} \u2192 ${opts.to}...`;
|
|
2158
2059
|
const result = await api.promoteEnvironment(linked.appId, targetEnv.id, sourceEnv.id);
|
|
2159
2060
|
if (result.promoted === 0) {
|
|
2160
2061
|
spinner.warn(
|
|
2161
|
-
|
|
2162
|
-
`No services promoted from ${
|
|
2062
|
+
import_chalk15.default.yellow(
|
|
2063
|
+
`No services promoted from ${import_chalk15.default.bold(opts.from)} \u2014 nothing to push (source may have no deployed image yet)`
|
|
2163
2064
|
)
|
|
2164
2065
|
);
|
|
2165
2066
|
} else {
|
|
2166
2067
|
spinner.succeed(
|
|
2167
|
-
|
|
2168
|
-
`\u2713 Promoted ${
|
|
2068
|
+
import_chalk15.default.green(
|
|
2069
|
+
`\u2713 Promoted ${import_chalk15.default.bold(opts.from)} \u2192 ${import_chalk15.default.bold(opts.to)} (${result.promoted} service${result.promoted !== 1 ? "s" : ""})`
|
|
2169
2070
|
)
|
|
2170
2071
|
);
|
|
2171
2072
|
console.log(
|
|
2172
|
-
|
|
2173
|
-
Deployments queued. Run ${
|
|
2073
|
+
import_chalk15.default.dim(`
|
|
2074
|
+
Deployments queued. Run ${import_chalk15.default.cyan("sylphx status")} to monitor.`)
|
|
2174
2075
|
);
|
|
2175
2076
|
}
|
|
2176
2077
|
} catch (err) {
|
|
2177
|
-
spinner.fail(
|
|
2078
|
+
spinner.fail(import_chalk15.default.red(`Promote failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
2178
2079
|
process.exit(1);
|
|
2179
2080
|
}
|
|
2180
2081
|
});
|
|
2181
2082
|
|
|
2182
2083
|
// src/commands/resources.ts
|
|
2183
|
-
var
|
|
2184
|
-
var
|
|
2185
|
-
var
|
|
2084
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
2085
|
+
var import_commander15 = require("commander");
|
|
2086
|
+
var import_ora13 = __toESM(require("ora"));
|
|
2186
2087
|
function requireLinkedApp4() {
|
|
2187
2088
|
const linked = config.getLinkedApp();
|
|
2188
2089
|
if (!linked) {
|
|
2189
|
-
console.log(
|
|
2090
|
+
console.log(import_chalk16.default.red("No app linked. Run `sylphx link` first."));
|
|
2190
2091
|
process.exit(1);
|
|
2191
2092
|
}
|
|
2192
2093
|
return linked;
|
|
@@ -2194,159 +2095,159 @@ function requireLinkedApp4() {
|
|
|
2194
2095
|
function requireToken2() {
|
|
2195
2096
|
const token = config.getToken();
|
|
2196
2097
|
if (!token) {
|
|
2197
|
-
console.log(
|
|
2098
|
+
console.log(import_chalk16.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2198
2099
|
process.exit(1);
|
|
2199
2100
|
}
|
|
2200
2101
|
return token;
|
|
2201
2102
|
}
|
|
2202
|
-
var listCmd2 = new
|
|
2103
|
+
var listCmd2 = new import_commander15.Command("list").description("List managed resources (databases, volumes, storage)").option("--kind <kind>", "Filter by kind: database, volume, storage").action(async (opts) => {
|
|
2203
2104
|
requireToken2();
|
|
2204
|
-
const spinner = (0,
|
|
2105
|
+
const spinner = (0, import_ora13.default)("Fetching resources...").start();
|
|
2205
2106
|
try {
|
|
2206
2107
|
const resources = await api.listDatabases();
|
|
2207
2108
|
spinner.stop();
|
|
2208
2109
|
if (resources.length === 0) {
|
|
2209
|
-
console.log(
|
|
2110
|
+
console.log(import_chalk16.default.dim(` No resources found.`));
|
|
2210
2111
|
return;
|
|
2211
2112
|
}
|
|
2212
2113
|
console.log();
|
|
2213
2114
|
console.log(
|
|
2214
|
-
` ${
|
|
2115
|
+
` ${import_chalk16.default.bold("NAME".padEnd(24))} ${"KIND".padEnd(12)} ${"STATUS".padEnd(14)} ID`
|
|
2215
2116
|
);
|
|
2216
2117
|
console.log(` ${"\u2500".repeat(72)}`);
|
|
2217
2118
|
for (const r of resources) {
|
|
2218
|
-
const statusColor2 = r.status === "ready" ?
|
|
2119
|
+
const statusColor2 = r.status === "ready" ? import_chalk16.default.green((r.status ?? "unknown").padEnd(14)) : import_chalk16.default.yellow((r.status ?? "unknown").padEnd(14));
|
|
2219
2120
|
console.log(
|
|
2220
|
-
` ${
|
|
2121
|
+
` ${import_chalk16.default.cyan(r.name.padEnd(24))} ${"database".padEnd(12)} ${statusColor2} ${import_chalk16.default.dim(r.id)}`
|
|
2221
2122
|
);
|
|
2222
2123
|
}
|
|
2223
2124
|
console.log();
|
|
2224
2125
|
} catch (err) {
|
|
2225
|
-
spinner.fail(
|
|
2126
|
+
spinner.fail(import_chalk16.default.red(err instanceof Error ? err.message : String(err)));
|
|
2226
2127
|
process.exit(1);
|
|
2227
2128
|
}
|
|
2228
2129
|
});
|
|
2229
|
-
var getCmd2 = new
|
|
2130
|
+
var getCmd2 = new import_commander15.Command("get").description("Get details of a resource").argument("<id>", "Resource ID").action(async (id) => {
|
|
2230
2131
|
requireToken2();
|
|
2231
|
-
const spinner = (0,
|
|
2132
|
+
const spinner = (0, import_ora13.default)(`Fetching resource ${id}...`).start();
|
|
2232
2133
|
try {
|
|
2233
2134
|
const r = await api.getDatabase(id);
|
|
2234
2135
|
spinner.stop();
|
|
2235
2136
|
console.log();
|
|
2236
|
-
console.log(` ${
|
|
2237
|
-
console.log(` ${
|
|
2137
|
+
console.log(` ${import_chalk16.default.bold("Name:")} ${import_chalk16.default.cyan(r.name)}`);
|
|
2138
|
+
console.log(` ${import_chalk16.default.bold("ID:")} ${r.id}`);
|
|
2238
2139
|
console.log(
|
|
2239
|
-
` ${
|
|
2140
|
+
` ${import_chalk16.default.bold("Status:")} ${r.status === "ready" ? import_chalk16.default.green(r.status) : import_chalk16.default.yellow(r.status ?? "unknown")}`
|
|
2240
2141
|
);
|
|
2241
|
-
if (r.tier) console.log(` ${
|
|
2242
|
-
if (r.host) console.log(` ${
|
|
2142
|
+
if (r.tier) console.log(` ${import_chalk16.default.bold("Tier:")} ${r.tier}`);
|
|
2143
|
+
if (r.host) console.log(` ${import_chalk16.default.bold("Host:")} ${r.host}${r.port ? `:${r.port}` : ""}`);
|
|
2243
2144
|
if (r.connectionString) {
|
|
2244
|
-
console.log(` ${
|
|
2145
|
+
console.log(` ${import_chalk16.default.bold("DSN:")} ${import_chalk16.default.dim(r.connectionString)}`);
|
|
2245
2146
|
}
|
|
2246
|
-
if (r.env) console.log(` ${
|
|
2147
|
+
if (r.env) console.log(` ${import_chalk16.default.bold("Env:")} ${r.env}`);
|
|
2247
2148
|
console.log();
|
|
2248
2149
|
} catch (err) {
|
|
2249
|
-
spinner.fail(
|
|
2150
|
+
spinner.fail(import_chalk16.default.red(err instanceof Error ? err.message : String(err)));
|
|
2250
2151
|
process.exit(1);
|
|
2251
2152
|
}
|
|
2252
2153
|
});
|
|
2253
|
-
var bindingsCmd = new
|
|
2154
|
+
var bindingsCmd = new import_commander15.Command("bindings").description("List project bindings for a resource").argument("<id>", "Resource ID").action(async (id) => {
|
|
2254
2155
|
requireToken2();
|
|
2255
|
-
const spinner = (0,
|
|
2156
|
+
const spinner = (0, import_ora13.default)(`Fetching bindings for ${id}...`).start();
|
|
2256
2157
|
try {
|
|
2257
2158
|
const bindings = await api.listResourceBindings(id);
|
|
2258
2159
|
spinner.stop();
|
|
2259
2160
|
if (bindings.length === 0) {
|
|
2260
|
-
console.log(
|
|
2161
|
+
console.log(import_chalk16.default.dim(" No bindings. Use `sylphx resources bind <id>` to link."));
|
|
2261
2162
|
return;
|
|
2262
2163
|
}
|
|
2263
2164
|
console.log();
|
|
2264
2165
|
console.log(
|
|
2265
|
-
` ${
|
|
2166
|
+
` ${import_chalk16.default.bold("BINDING ID".padEnd(26))} ${"PROJECT".padEnd(24)} ${"ENV".padEnd(12)} ROLE`
|
|
2266
2167
|
);
|
|
2267
2168
|
console.log(` ${"\u2500".repeat(80)}`);
|
|
2268
2169
|
for (const b of bindings) {
|
|
2269
2170
|
const project = b.projectName ?? b.projectSlug ?? "\u2014";
|
|
2270
2171
|
const env = b.envName ?? "\u2014";
|
|
2271
2172
|
console.log(
|
|
2272
|
-
` ${
|
|
2173
|
+
` ${import_chalk16.default.dim(b.id.padEnd(26))} ${project.padEnd(24)} ${env.padEnd(12)} ${b.role}`
|
|
2273
2174
|
);
|
|
2274
2175
|
}
|
|
2275
2176
|
console.log();
|
|
2276
2177
|
} catch (err) {
|
|
2277
|
-
spinner.fail(
|
|
2178
|
+
spinner.fail(import_chalk16.default.red(err instanceof Error ? err.message : String(err)));
|
|
2278
2179
|
process.exit(1);
|
|
2279
2180
|
}
|
|
2280
2181
|
});
|
|
2281
|
-
var bindCmd = new
|
|
2182
|
+
var bindCmd = new import_commander15.Command("bind").description("Bind a resource to this project environment").argument("<id>", "Resource ID (e.g. res_xxx)").option("--env <env>", "Environment (default: production)", "production").option(
|
|
2282
2183
|
"--role <role>",
|
|
2283
2184
|
"Binding role: primary, replica, analytics, backup (default: primary)",
|
|
2284
2185
|
"primary"
|
|
2285
2186
|
).action(async (id, opts) => {
|
|
2286
2187
|
requireToken2();
|
|
2287
2188
|
const linked = requireLinkedApp4();
|
|
2288
|
-
const spinner = (0,
|
|
2189
|
+
const spinner = (0, import_ora13.default)(`Binding resource ${id} to ${linked.appId} [${opts.env}]...`).start();
|
|
2289
2190
|
try {
|
|
2290
2191
|
const binding = await api.bindResource(id, {
|
|
2291
2192
|
projectId: linked.appId,
|
|
2292
2193
|
envType: opts.env,
|
|
2293
2194
|
role: opts.role
|
|
2294
2195
|
});
|
|
2295
|
-
spinner.succeed(
|
|
2196
|
+
spinner.succeed(import_chalk16.default.green(`\u2713 Resource bound (${binding.role})`));
|
|
2296
2197
|
console.log();
|
|
2297
|
-
console.log(` ${
|
|
2298
|
-
console.log(` ${
|
|
2198
|
+
console.log(` ${import_chalk16.default.dim("Binding ID:")} ${binding.id}`);
|
|
2199
|
+
console.log(` ${import_chalk16.default.dim("Env:")} ${binding.envName ?? opts.env}`);
|
|
2299
2200
|
console.log(
|
|
2300
|
-
|
|
2201
|
+
import_chalk16.default.dim(
|
|
2301
2202
|
`
|
|
2302
|
-
Run ${
|
|
2203
|
+
Run ${import_chalk16.default.cyan("sylphx deploy")} to apply the connection vars to your deployment.`
|
|
2303
2204
|
)
|
|
2304
2205
|
);
|
|
2305
2206
|
} catch (err) {
|
|
2306
|
-
spinner.fail(
|
|
2207
|
+
spinner.fail(import_chalk16.default.red(err instanceof Error ? err.message : String(err)));
|
|
2307
2208
|
process.exit(1);
|
|
2308
2209
|
}
|
|
2309
2210
|
});
|
|
2310
|
-
var unbindCmd = new
|
|
2211
|
+
var unbindCmd = new import_commander15.Command("unbind").description("Remove a resource binding").argument("<id>", "Resource ID").argument("<binding-id>", "Binding ID (from `sylphx resources bindings <id>`)").action(async (id, bindingId) => {
|
|
2311
2212
|
requireToken2();
|
|
2312
|
-
const spinner = (0,
|
|
2213
|
+
const spinner = (0, import_ora13.default)(`Removing binding ${bindingId}...`).start();
|
|
2313
2214
|
try {
|
|
2314
2215
|
await api.unbindResource(id, bindingId);
|
|
2315
|
-
spinner.succeed(
|
|
2216
|
+
spinner.succeed(import_chalk16.default.green(`\u2713 Binding removed`));
|
|
2316
2217
|
} catch (err) {
|
|
2317
|
-
spinner.fail(
|
|
2218
|
+
spinner.fail(import_chalk16.default.red(err instanceof Error ? err.message : String(err)));
|
|
2318
2219
|
process.exit(1);
|
|
2319
2220
|
}
|
|
2320
2221
|
});
|
|
2321
|
-
var resourcesCommand = new
|
|
2222
|
+
var resourcesCommand = new import_commander15.Command("resources").description("Manage resources and project bindings").addCommand(listCmd2).addCommand(getCmd2).addCommand(bindingsCmd).addCommand(bindCmd).addCommand(unbindCmd).action(() => resourcesCommand.help());
|
|
2322
2223
|
|
|
2323
2224
|
// src/commands/rollback.ts
|
|
2324
|
-
var
|
|
2325
|
-
var
|
|
2326
|
-
var
|
|
2327
|
-
var rollbackCommand = new
|
|
2225
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
2226
|
+
var import_commander16 = require("commander");
|
|
2227
|
+
var import_ora14 = __toESM(require("ora"));
|
|
2228
|
+
var rollbackCommand = new import_commander16.Command("rollback").description("Rollback to the previous deployment").option("--env <env>", "Environment to rollback (e.g. production, staging)").option("--deployment <id>", "Rollback to a specific deployment ID").option("--force", "Skip confirmation prompt").action(async (opts) => {
|
|
2328
2229
|
const token = config.getToken();
|
|
2329
2230
|
if (!token) {
|
|
2330
|
-
console.log(
|
|
2231
|
+
console.log(import_chalk17.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2331
2232
|
process.exit(1);
|
|
2332
2233
|
}
|
|
2333
2234
|
const linked = config.getLinkedApp();
|
|
2334
2235
|
if (!linked) {
|
|
2335
|
-
console.log(
|
|
2236
|
+
console.log(import_chalk17.default.red("No app linked. Run `sylphx link` first."));
|
|
2336
2237
|
process.exit(1);
|
|
2337
2238
|
}
|
|
2338
2239
|
const envType = opts.env ?? linked.defaultEnv ?? "production";
|
|
2339
2240
|
if (!opts.force) {
|
|
2340
|
-
const
|
|
2341
|
-
const rl =
|
|
2241
|
+
const readline9 = await import("readline");
|
|
2242
|
+
const rl = readline9.createInterface({ input: process.stdin, output: process.stdout });
|
|
2342
2243
|
await new Promise((resolve) => {
|
|
2343
|
-
const label = opts.deployment ? `deployment ${
|
|
2244
|
+
const label = opts.deployment ? `deployment ${import_chalk17.default.bold(opts.deployment)}` : `previous deployment`;
|
|
2344
2245
|
rl.question(
|
|
2345
|
-
|
|
2246
|
+
import_chalk17.default.yellow(` Rollback ${import_chalk17.default.bold(linked.appId)} [${envType}] to ${label}? (y/N) `),
|
|
2346
2247
|
(answer) => {
|
|
2347
2248
|
rl.close();
|
|
2348
2249
|
if (answer.toLowerCase() !== "y") {
|
|
2349
|
-
console.log(
|
|
2250
|
+
console.log(import_chalk17.default.dim(" Cancelled."));
|
|
2350
2251
|
process.exit(0);
|
|
2351
2252
|
}
|
|
2352
2253
|
resolve();
|
|
@@ -2354,7 +2255,7 @@ var rollbackCommand = new import_commander15.Command("rollback").description("Ro
|
|
|
2354
2255
|
);
|
|
2355
2256
|
});
|
|
2356
2257
|
}
|
|
2357
|
-
const spinner = (0,
|
|
2258
|
+
const spinner = (0, import_ora14.default)(`Rolling back ${linked.appId} [${envType}]...`).start();
|
|
2358
2259
|
try {
|
|
2359
2260
|
let rollbackBody;
|
|
2360
2261
|
if (opts.deployment) {
|
|
@@ -2366,7 +2267,7 @@ var rollbackCommand = new import_commander15.Command("rollback").description("Ro
|
|
|
2366
2267
|
);
|
|
2367
2268
|
if (!envEntry?.envId) {
|
|
2368
2269
|
spinner.fail(
|
|
2369
|
-
|
|
2270
|
+
import_chalk17.default.red(
|
|
2370
2271
|
`Could not find environment '${envType}'. Run \`sylphx status\` to see available environments.`
|
|
2371
2272
|
)
|
|
2372
2273
|
);
|
|
@@ -2375,21 +2276,21 @@ var rollbackCommand = new import_commander15.Command("rollback").description("Ro
|
|
|
2375
2276
|
rollbackBody = { envId: envEntry.envId };
|
|
2376
2277
|
}
|
|
2377
2278
|
const result = await api.rollback(linked.appId, rollbackBody);
|
|
2378
|
-
spinner.succeed(
|
|
2279
|
+
spinner.succeed(import_chalk17.default.green(`\u2713 Rollback initiated`));
|
|
2379
2280
|
if (result.deploymentId) {
|
|
2380
|
-
console.log(
|
|
2281
|
+
console.log(import_chalk17.default.dim(` Deployment ID: ${result.deploymentId}`));
|
|
2381
2282
|
}
|
|
2382
2283
|
if (result.status) {
|
|
2383
|
-
console.log(
|
|
2284
|
+
console.log(import_chalk17.default.dim(` Status: ${result.status}`));
|
|
2384
2285
|
}
|
|
2385
2286
|
if (result.message) {
|
|
2386
|
-
console.log(
|
|
2287
|
+
console.log(import_chalk17.default.dim(` ${result.message}`));
|
|
2387
2288
|
}
|
|
2388
|
-
console.log(
|
|
2389
|
-
Run ${
|
|
2289
|
+
console.log(import_chalk17.default.dim(`
|
|
2290
|
+
Run ${import_chalk17.default.cyan("sylphx status")} to monitor progress.`));
|
|
2390
2291
|
} catch (err) {
|
|
2391
2292
|
spinner.fail(
|
|
2392
|
-
|
|
2293
|
+
import_chalk17.default.red(`Rollback failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2393
2294
|
);
|
|
2394
2295
|
process.exit(1);
|
|
2395
2296
|
}
|
|
@@ -2397,13 +2298,13 @@ var rollbackCommand = new import_commander15.Command("rollback").description("Ro
|
|
|
2397
2298
|
|
|
2398
2299
|
// src/commands/services.ts
|
|
2399
2300
|
var import_node_readline5 = __toESM(require("readline"));
|
|
2400
|
-
var
|
|
2401
|
-
var
|
|
2402
|
-
var
|
|
2301
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
2302
|
+
var import_commander17 = require("commander");
|
|
2303
|
+
var import_ora15 = __toESM(require("ora"));
|
|
2403
2304
|
function requireLinkedApp5() {
|
|
2404
2305
|
const linked = config.getLinkedApp();
|
|
2405
2306
|
if (!linked) {
|
|
2406
|
-
console.log(
|
|
2307
|
+
console.log(import_chalk18.default.red("No app linked. Run `sylphx link` first."));
|
|
2407
2308
|
process.exit(1);
|
|
2408
2309
|
}
|
|
2409
2310
|
return linked;
|
|
@@ -2411,180 +2312,180 @@ function requireLinkedApp5() {
|
|
|
2411
2312
|
function requireToken3() {
|
|
2412
2313
|
const token = config.getToken();
|
|
2413
2314
|
if (!token) {
|
|
2414
|
-
console.log(
|
|
2315
|
+
console.log(import_chalk18.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2415
2316
|
process.exit(1);
|
|
2416
2317
|
}
|
|
2417
2318
|
return token;
|
|
2418
2319
|
}
|
|
2419
|
-
var listCmd3 = new
|
|
2320
|
+
var listCmd3 = new import_commander17.Command("list").description("List all services in this project").action(async () => {
|
|
2420
2321
|
requireToken3();
|
|
2421
2322
|
const linked = requireLinkedApp5();
|
|
2422
|
-
const spinner = (0,
|
|
2323
|
+
const spinner = (0, import_ora15.default)("Fetching services...").start();
|
|
2423
2324
|
try {
|
|
2424
2325
|
const services = await api.listServices(linked.appId);
|
|
2425
2326
|
spinner.stop();
|
|
2426
2327
|
if (services.length === 0) {
|
|
2427
|
-
console.log(
|
|
2328
|
+
console.log(import_chalk18.default.dim(" No services defined."));
|
|
2428
2329
|
return;
|
|
2429
2330
|
}
|
|
2430
2331
|
console.log();
|
|
2431
|
-
console.log(` ${
|
|
2332
|
+
console.log(` ${import_chalk18.default.bold("NAME".padEnd(24))} ${"REPO".padEnd(36)} ENVS`);
|
|
2432
2333
|
console.log(` ${"\u2500".repeat(70)}`);
|
|
2433
2334
|
for (const svc of services) {
|
|
2434
|
-
const repo = svc.githubRepo ??
|
|
2335
|
+
const repo = svc.githubRepo ?? import_chalk18.default.dim("\u2014");
|
|
2435
2336
|
const envs = svc.environmentCount ?? 0;
|
|
2436
|
-
console.log(` ${
|
|
2337
|
+
console.log(` ${import_chalk18.default.cyan(svc.name.padEnd(24))} ${repo.padEnd(36)} ${envs}`);
|
|
2437
2338
|
}
|
|
2438
2339
|
console.log();
|
|
2439
2340
|
} catch (err) {
|
|
2440
|
-
spinner.fail(
|
|
2341
|
+
spinner.fail(import_chalk18.default.red(err instanceof Error ? err.message : String(err)));
|
|
2441
2342
|
process.exit(1);
|
|
2442
2343
|
}
|
|
2443
2344
|
});
|
|
2444
|
-
var getCmd3 = new
|
|
2345
|
+
var getCmd3 = new import_commander17.Command("get").description("Show details of a service").argument("<name>", "Service name").action(async (name) => {
|
|
2445
2346
|
requireToken3();
|
|
2446
2347
|
const linked = requireLinkedApp5();
|
|
2447
|
-
const spinner = (0,
|
|
2348
|
+
const spinner = (0, import_ora15.default)(`Fetching service '${name}'...`).start();
|
|
2448
2349
|
try {
|
|
2449
2350
|
const svc = await api.getService(linked.appId, name);
|
|
2450
2351
|
spinner.stop();
|
|
2451
2352
|
console.log();
|
|
2452
|
-
console.log(` ${
|
|
2453
|
-
console.log(` ${
|
|
2454
|
-
if (svc.githubRepo) console.log(` ${
|
|
2455
|
-
if (svc.githubBranch) console.log(` ${
|
|
2353
|
+
console.log(` ${import_chalk18.default.bold("Name:")} ${import_chalk18.default.cyan(svc.name)}`);
|
|
2354
|
+
console.log(` ${import_chalk18.default.bold("ID:")} ${svc.id}`);
|
|
2355
|
+
if (svc.githubRepo) console.log(` ${import_chalk18.default.bold("Repo:")} ${svc.githubRepo}`);
|
|
2356
|
+
if (svc.githubBranch) console.log(` ${import_chalk18.default.bold("Branch:")} ${svc.githubBranch}`);
|
|
2456
2357
|
if (svc.dockerfilePath)
|
|
2457
|
-
console.log(` ${
|
|
2458
|
-
if (svc.port) console.log(` ${
|
|
2358
|
+
console.log(` ${import_chalk18.default.bold("Dockerfile:")} ${svc.dockerfilePath}`);
|
|
2359
|
+
if (svc.port) console.log(` ${import_chalk18.default.bold("Port:")} ${svc.port}`);
|
|
2459
2360
|
if (svc.publicNetworking !== void 0 && svc.publicNetworking !== null)
|
|
2460
2361
|
console.log(
|
|
2461
|
-
` ${
|
|
2362
|
+
` ${import_chalk18.default.bold("Public:")} ${svc.publicNetworking ? import_chalk18.default.green("yes") : import_chalk18.default.dim("no")}`
|
|
2462
2363
|
);
|
|
2463
2364
|
console.log();
|
|
2464
2365
|
} catch (err) {
|
|
2465
|
-
spinner.fail(
|
|
2366
|
+
spinner.fail(import_chalk18.default.red(err instanceof Error ? err.message : String(err)));
|
|
2466
2367
|
process.exit(1);
|
|
2467
2368
|
}
|
|
2468
2369
|
});
|
|
2469
|
-
var deployCmd = new
|
|
2370
|
+
var deployCmd = new import_commander17.Command("deploy").description("Deploy a specific service").argument("<name>", "Service name").option("--env <env>", "Environment (default: production)", "production").action(async (name, opts) => {
|
|
2470
2371
|
requireToken3();
|
|
2471
2372
|
const linked = requireLinkedApp5();
|
|
2472
|
-
const spinner = (0,
|
|
2373
|
+
const spinner = (0, import_ora15.default)(`Deploying service '${name}' [${opts.env}]...`).start();
|
|
2473
2374
|
try {
|
|
2474
2375
|
const result = await api.deployService(linked.appId, name, opts.env);
|
|
2475
|
-
spinner.succeed(
|
|
2376
|
+
spinner.succeed(import_chalk18.default.green(`\u2713 Deploy triggered for service '${name}'`));
|
|
2476
2377
|
if (result.deploymentId) {
|
|
2477
|
-
console.log(
|
|
2378
|
+
console.log(import_chalk18.default.dim(` Deployment ID: ${result.deploymentId}`));
|
|
2478
2379
|
}
|
|
2479
|
-
console.log(
|
|
2480
|
-
Run ${
|
|
2380
|
+
console.log(import_chalk18.default.dim(`
|
|
2381
|
+
Run ${import_chalk18.default.cyan("sylphx status")} to monitor.`));
|
|
2481
2382
|
} catch (err) {
|
|
2482
|
-
spinner.fail(
|
|
2383
|
+
spinner.fail(import_chalk18.default.red(err instanceof Error ? err.message : String(err)));
|
|
2483
2384
|
process.exit(1);
|
|
2484
2385
|
}
|
|
2485
2386
|
});
|
|
2486
|
-
var rmCmd2 = new
|
|
2387
|
+
var rmCmd2 = new import_commander17.Command("rm").description("Delete a service").argument("<name>", "Service name").option("--force", "Skip confirmation prompt").action(async (name, opts) => {
|
|
2487
2388
|
requireToken3();
|
|
2488
2389
|
const linked = requireLinkedApp5();
|
|
2489
2390
|
if (!opts.force) {
|
|
2490
2391
|
const rl = import_node_readline5.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
2491
2392
|
await new Promise((resolve) => {
|
|
2492
|
-
rl.question(
|
|
2393
|
+
rl.question(import_chalk18.default.yellow(` Delete service ${import_chalk18.default.bold(name)}? (y/N) `), (answer) => {
|
|
2493
2394
|
rl.close();
|
|
2494
2395
|
if (answer.toLowerCase() !== "y") {
|
|
2495
|
-
console.log(
|
|
2396
|
+
console.log(import_chalk18.default.dim(" Cancelled."));
|
|
2496
2397
|
process.exit(0);
|
|
2497
2398
|
}
|
|
2498
2399
|
resolve();
|
|
2499
2400
|
});
|
|
2500
2401
|
});
|
|
2501
2402
|
}
|
|
2502
|
-
const spinner = (0,
|
|
2403
|
+
const spinner = (0, import_ora15.default)(`Deleting service '${name}'...`).start();
|
|
2503
2404
|
try {
|
|
2504
2405
|
await api.deleteService(linked.appId, name);
|
|
2505
|
-
spinner.succeed(
|
|
2406
|
+
spinner.succeed(import_chalk18.default.green(`\u2713 Service '${name}' deleted`));
|
|
2506
2407
|
} catch (err) {
|
|
2507
|
-
spinner.fail(
|
|
2408
|
+
spinner.fail(import_chalk18.default.red(err instanceof Error ? err.message : String(err)));
|
|
2508
2409
|
process.exit(1);
|
|
2509
2410
|
}
|
|
2510
2411
|
});
|
|
2511
|
-
var servicesCommand = new
|
|
2412
|
+
var servicesCommand = new import_commander17.Command("services").description("Manage project services").addCommand(listCmd3).addCommand(getCmd3).addCommand(deployCmd).addCommand(rmCmd2).action(() => servicesCommand.help());
|
|
2512
2413
|
|
|
2513
2414
|
// src/commands/status.ts
|
|
2514
|
-
var
|
|
2515
|
-
var
|
|
2516
|
-
var
|
|
2415
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
2416
|
+
var import_commander18 = require("commander");
|
|
2417
|
+
var import_ora16 = __toESM(require("ora"));
|
|
2517
2418
|
function statusColor(status) {
|
|
2518
2419
|
const s = status.toLowerCase();
|
|
2519
2420
|
if (s === "running" || s === "active" || s === "healthy" || s === "success") {
|
|
2520
|
-
return
|
|
2421
|
+
return import_chalk19.default.green(status);
|
|
2521
2422
|
}
|
|
2522
2423
|
if (s === "deploying" || s === "building" || s === "pending") {
|
|
2523
|
-
return
|
|
2424
|
+
return import_chalk19.default.yellow(status);
|
|
2524
2425
|
}
|
|
2525
2426
|
if (s === "error" || s === "failed" || s === "stopped") {
|
|
2526
|
-
return
|
|
2427
|
+
return import_chalk19.default.red(status);
|
|
2527
2428
|
}
|
|
2528
|
-
return
|
|
2429
|
+
return import_chalk19.default.white(status);
|
|
2529
2430
|
}
|
|
2530
|
-
var statusCommand = new
|
|
2431
|
+
var statusCommand = new import_commander18.Command("status").description("Show deployment status").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
|
|
2531
2432
|
const token = config.getToken();
|
|
2532
2433
|
if (!token) {
|
|
2533
|
-
console.log(
|
|
2434
|
+
console.log(import_chalk19.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2534
2435
|
process.exit(1);
|
|
2535
2436
|
}
|
|
2536
2437
|
const linked = config.getLinkedApp();
|
|
2537
2438
|
if (!linked) {
|
|
2538
|
-
console.log(
|
|
2439
|
+
console.log(import_chalk19.default.red("No app linked. Run `sylphx link` first."));
|
|
2539
2440
|
process.exit(1);
|
|
2540
2441
|
}
|
|
2541
2442
|
const env = opts.env ?? linked.defaultEnv;
|
|
2542
|
-
const spinner = (0,
|
|
2443
|
+
const spinner = (0, import_ora16.default)(`Fetching status [${env}]...`).start();
|
|
2543
2444
|
try {
|
|
2544
2445
|
const status = await api.getDeploymentStatus(linked.appId);
|
|
2545
2446
|
spinner.stop();
|
|
2546
2447
|
console.log("");
|
|
2547
|
-
console.log(
|
|
2548
|
-
console.log(
|
|
2448
|
+
console.log(import_chalk19.default.bold(" Deployment Status"));
|
|
2449
|
+
console.log(import_chalk19.default.dim(` ${"\u2500".repeat(40)}`));
|
|
2549
2450
|
console.log("");
|
|
2550
|
-
console.log(` App: ${
|
|
2551
|
-
console.log(` Environment: ${
|
|
2451
|
+
console.log(` App: ${import_chalk19.default.cyan(linked.appId)}`);
|
|
2452
|
+
console.log(` Environment: ${import_chalk19.default.white(env)}`);
|
|
2552
2453
|
console.log(` Status: ${statusColor(status.status)}`);
|
|
2553
2454
|
if (status.deployedAt) {
|
|
2554
2455
|
const date = new Date(status.deployedAt);
|
|
2555
|
-
console.log(` Deployed: ${
|
|
2456
|
+
console.log(` Deployed: ${import_chalk19.default.white(date.toLocaleString())}`);
|
|
2556
2457
|
}
|
|
2557
2458
|
const envMatch = status.environments?.find((e) => e.envType === env) ?? status.environments?.find((e) => e.envType === "production") ?? status.environments?.[0];
|
|
2558
2459
|
const displayUrl = status.url ?? envMatch?.url ?? null;
|
|
2559
2460
|
if (displayUrl) {
|
|
2560
|
-
console.log(` URL: ${
|
|
2461
|
+
console.log(` URL: ${import_chalk19.default.cyan(displayUrl)}`);
|
|
2561
2462
|
}
|
|
2562
2463
|
if (envMatch?.lastDeployedAt) {
|
|
2563
2464
|
console.log(
|
|
2564
|
-
` Deployed: ${
|
|
2465
|
+
` Deployed: ${import_chalk19.default.white(new Date(envMatch.lastDeployedAt).toLocaleString())}`
|
|
2565
2466
|
);
|
|
2566
2467
|
}
|
|
2567
2468
|
if (status.replicas) {
|
|
2568
2469
|
const { ready, desired } = status.replicas;
|
|
2569
|
-
const replicaColor = ready === desired ?
|
|
2470
|
+
const replicaColor = ready === desired ? import_chalk19.default.green : import_chalk19.default.yellow;
|
|
2570
2471
|
console.log(` Replicas: ${replicaColor(`${ready}/${desired}`)}`);
|
|
2571
2472
|
}
|
|
2572
2473
|
console.log("");
|
|
2573
2474
|
} catch (err) {
|
|
2574
|
-
spinner.fail(
|
|
2475
|
+
spinner.fail(import_chalk19.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
2575
2476
|
process.exit(1);
|
|
2576
2477
|
}
|
|
2577
2478
|
});
|
|
2578
2479
|
|
|
2579
2480
|
// src/commands/storage.ts
|
|
2580
2481
|
var import_node_readline6 = __toESM(require("readline"));
|
|
2581
|
-
var
|
|
2582
|
-
var
|
|
2583
|
-
var
|
|
2482
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
2483
|
+
var import_commander19 = require("commander");
|
|
2484
|
+
var import_ora17 = __toESM(require("ora"));
|
|
2584
2485
|
function requireLinkedApp6() {
|
|
2585
2486
|
const linked = config.getLinkedApp();
|
|
2586
2487
|
if (!linked) {
|
|
2587
|
-
console.log(
|
|
2488
|
+
console.log(import_chalk20.default.red("No app linked. Run `sylphx link` first."));
|
|
2588
2489
|
process.exit(1);
|
|
2589
2490
|
}
|
|
2590
2491
|
return linked;
|
|
@@ -2592,85 +2493,85 @@ function requireLinkedApp6() {
|
|
|
2592
2493
|
function requireToken4() {
|
|
2593
2494
|
const token = config.getToken();
|
|
2594
2495
|
if (!token) {
|
|
2595
|
-
console.log(
|
|
2496
|
+
console.log(import_chalk20.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2596
2497
|
process.exit(1);
|
|
2597
2498
|
}
|
|
2598
2499
|
return token;
|
|
2599
2500
|
}
|
|
2600
|
-
var listCmd4 = new
|
|
2501
|
+
var listCmd4 = new import_commander19.Command("list").description("List storage resources for this project").option("--env <env>", "Environment (default: production)", "production").action(async (opts) => {
|
|
2601
2502
|
requireToken4();
|
|
2602
2503
|
const linked = requireLinkedApp6();
|
|
2603
|
-
const spinner = (0,
|
|
2504
|
+
const spinner = (0, import_ora17.default)("Fetching storage resources...").start();
|
|
2604
2505
|
try {
|
|
2605
2506
|
const resources = await api.listStorage(linked.appId, opts.env);
|
|
2606
2507
|
spinner.stop();
|
|
2607
2508
|
if (resources.length === 0) {
|
|
2608
|
-
console.log(
|
|
2509
|
+
console.log(import_chalk20.default.dim(" No storage resources provisioned."));
|
|
2609
2510
|
console.log(
|
|
2610
|
-
|
|
2611
|
-
Run ${
|
|
2511
|
+
import_chalk20.default.dim(`
|
|
2512
|
+
Run ${import_chalk20.default.cyan("sylphx storage create")} to provision storage.`)
|
|
2612
2513
|
);
|
|
2613
2514
|
return;
|
|
2614
2515
|
}
|
|
2615
2516
|
console.log();
|
|
2616
2517
|
for (const r of resources) {
|
|
2617
2518
|
const label = [r.role ?? "primary", r.envType].filter(Boolean).join(" \xB7 ");
|
|
2618
|
-
console.log(` ${
|
|
2519
|
+
console.log(` ${import_chalk20.default.cyan(label)} ${import_chalk20.default.dim(r.id)}`);
|
|
2619
2520
|
if (r.publicUrl) {
|
|
2620
|
-
console.log(` ${
|
|
2521
|
+
console.log(` ${import_chalk20.default.dim("URL:")} ${import_chalk20.default.cyan(r.publicUrl)}`);
|
|
2621
2522
|
}
|
|
2622
2523
|
if (r.bucket) {
|
|
2623
|
-
console.log(` ${
|
|
2524
|
+
console.log(` ${import_chalk20.default.dim("Bucket:")} ${r.bucket}`);
|
|
2624
2525
|
}
|
|
2625
2526
|
}
|
|
2626
2527
|
console.log();
|
|
2627
2528
|
} catch (err) {
|
|
2628
|
-
spinner.fail(
|
|
2529
|
+
spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
|
|
2629
2530
|
process.exit(1);
|
|
2630
2531
|
}
|
|
2631
2532
|
});
|
|
2632
|
-
var createCmd = new
|
|
2533
|
+
var createCmd = new import_commander19.Command("create").description("Provision blob storage for this project").argument("[name]", "Storage resource name (default: primary)", "primary").option("--env <env>", "Environment (default: production)", "production").action(async (name, opts) => {
|
|
2633
2534
|
requireToken4();
|
|
2634
2535
|
const linked = requireLinkedApp6();
|
|
2635
|
-
const spinner = (0,
|
|
2536
|
+
const spinner = (0, import_ora17.default)(`Provisioning storage '${name}' [${opts.env}]...`).start();
|
|
2636
2537
|
try {
|
|
2637
2538
|
const resource = await api.createStorage(linked.appId, { name, envType: opts.env });
|
|
2638
|
-
spinner.succeed(
|
|
2539
|
+
spinner.succeed(import_chalk20.default.green(`\u2713 Storage '${name}' provisioned`));
|
|
2639
2540
|
console.log();
|
|
2640
|
-
console.log(` ${
|
|
2541
|
+
console.log(` ${import_chalk20.default.dim("ID:")} ${resource.id}`);
|
|
2641
2542
|
if (resource.publicUrl) {
|
|
2642
|
-
console.log(` ${
|
|
2543
|
+
console.log(` ${import_chalk20.default.dim("Public URL:")} ${import_chalk20.default.cyan(resource.publicUrl)}`);
|
|
2643
2544
|
}
|
|
2644
2545
|
if (resource.envVars && Object.keys(resource.envVars).length > 0) {
|
|
2645
2546
|
console.log();
|
|
2646
|
-
console.log(
|
|
2547
|
+
console.log(import_chalk20.default.bold(" Env vars to add to your project:"));
|
|
2647
2548
|
for (const [k, v] of Object.entries(resource.envVars)) {
|
|
2648
|
-
console.log(` ${
|
|
2549
|
+
console.log(` ${import_chalk20.default.cyan(k)}=${v}`);
|
|
2649
2550
|
}
|
|
2650
2551
|
console.log();
|
|
2651
2552
|
console.log(
|
|
2652
|
-
|
|
2653
|
-
` Run ${
|
|
2553
|
+
import_chalk20.default.dim(
|
|
2554
|
+
` Run ${import_chalk20.default.cyan("sylphx env set KEY=VALUE")} to add these to your project.`
|
|
2654
2555
|
)
|
|
2655
2556
|
);
|
|
2656
2557
|
}
|
|
2657
2558
|
} catch (err) {
|
|
2658
|
-
spinner.fail(
|
|
2559
|
+
spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
|
|
2659
2560
|
process.exit(1);
|
|
2660
2561
|
}
|
|
2661
2562
|
});
|
|
2662
|
-
var rmCmd3 = new
|
|
2563
|
+
var rmCmd3 = new import_commander19.Command("rm").description("Delete a storage resource").argument("<id>", "Storage resource ID").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
|
|
2663
2564
|
requireToken4();
|
|
2664
2565
|
const linked = requireLinkedApp6();
|
|
2665
2566
|
if (!opts.force) {
|
|
2666
2567
|
const rl = import_node_readline6.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
2667
2568
|
await new Promise((resolve) => {
|
|
2668
2569
|
rl.question(
|
|
2669
|
-
|
|
2570
|
+
import_chalk20.default.yellow(` \u26A0\uFE0F Delete storage ${import_chalk20.default.bold(id)}? This is irreversible. (y/N) `),
|
|
2670
2571
|
(answer) => {
|
|
2671
2572
|
rl.close();
|
|
2672
2573
|
if (answer.toLowerCase() !== "y") {
|
|
2673
|
-
console.log(
|
|
2574
|
+
console.log(import_chalk20.default.dim(" Cancelled."));
|
|
2674
2575
|
process.exit(0);
|
|
2675
2576
|
}
|
|
2676
2577
|
resolve();
|
|
@@ -2678,105 +2579,292 @@ var rmCmd3 = new import_commander18.Command("rm").description("Delete a storage
|
|
|
2678
2579
|
);
|
|
2679
2580
|
});
|
|
2680
2581
|
}
|
|
2681
|
-
const spinner = (0,
|
|
2582
|
+
const spinner = (0, import_ora17.default)(`Deleting storage ${id}...`).start();
|
|
2682
2583
|
try {
|
|
2683
2584
|
await api.deleteStorage(linked.appId, id);
|
|
2684
|
-
spinner.succeed(
|
|
2585
|
+
spinner.succeed(import_chalk20.default.green(`\u2713 Storage ${id} deleted`));
|
|
2586
|
+
} catch (err) {
|
|
2587
|
+
spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
|
|
2588
|
+
process.exit(1);
|
|
2589
|
+
}
|
|
2590
|
+
});
|
|
2591
|
+
var storageCommand = new import_commander19.Command("storage").description("Manage blob storage resources").addCommand(listCmd4).addCommand(createCmd).addCommand(rmCmd3).action(() => storageCommand.help());
|
|
2592
|
+
|
|
2593
|
+
// src/commands/tasks.ts
|
|
2594
|
+
var import_node_readline7 = __toESM(require("readline"));
|
|
2595
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
2596
|
+
var import_commander20 = require("commander");
|
|
2597
|
+
var import_ora18 = __toESM(require("ora"));
|
|
2598
|
+
function requireLinkedApp7() {
|
|
2599
|
+
const linked = config.getLinkedApp();
|
|
2600
|
+
if (!linked) {
|
|
2601
|
+
console.log(import_chalk21.default.red("No app linked. Run `sylphx link` first."));
|
|
2602
|
+
process.exit(1);
|
|
2603
|
+
}
|
|
2604
|
+
return linked;
|
|
2605
|
+
}
|
|
2606
|
+
function requireToken5() {
|
|
2607
|
+
const token = config.getToken();
|
|
2608
|
+
if (!token) {
|
|
2609
|
+
console.log(import_chalk21.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2610
|
+
process.exit(1);
|
|
2611
|
+
}
|
|
2612
|
+
return token;
|
|
2613
|
+
}
|
|
2614
|
+
function formatMode(mode) {
|
|
2615
|
+
switch (mode) {
|
|
2616
|
+
case "job":
|
|
2617
|
+
return import_chalk21.default.blue("job");
|
|
2618
|
+
case "cron":
|
|
2619
|
+
return import_chalk21.default.magenta("cron");
|
|
2620
|
+
case "service":
|
|
2621
|
+
return import_chalk21.default.green("service");
|
|
2622
|
+
default:
|
|
2623
|
+
return import_chalk21.default.dim(mode);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
var listCmd5 = new import_commander20.Command("list").description("List task definitions for this project").option("--env <env>", "Environment (default: production)", "production").action(async (opts) => {
|
|
2627
|
+
requireToken5();
|
|
2628
|
+
const linked = requireLinkedApp7();
|
|
2629
|
+
const spinner = (0, import_ora18.default)("Fetching tasks...").start();
|
|
2630
|
+
try {
|
|
2631
|
+
const tasks = await api.listTasks(linked.appId, opts.env);
|
|
2632
|
+
spinner.stop();
|
|
2633
|
+
if (tasks.length === 0) {
|
|
2634
|
+
console.log(import_chalk21.default.dim(` No task definitions for [${opts.env}].`));
|
|
2635
|
+
console.log(
|
|
2636
|
+
import_chalk21.default.dim(`
|
|
2637
|
+
Run ${import_chalk21.default.cyan("sylphx tasks create <name>")} to define a task.`)
|
|
2638
|
+
);
|
|
2639
|
+
return;
|
|
2640
|
+
}
|
|
2641
|
+
console.log();
|
|
2642
|
+
console.log(
|
|
2643
|
+
` ${import_chalk21.default.bold("NAME".padEnd(28))} ${"MODE".padEnd(10)} ${"IMAGE / HANDLER".padEnd(40)} ID`
|
|
2644
|
+
);
|
|
2645
|
+
console.log(` ${"\u2500".repeat(90)}`);
|
|
2646
|
+
for (const t of tasks) {
|
|
2647
|
+
const label = t.imageRef ?? t.handlerPath ?? import_chalk21.default.dim("\u2014");
|
|
2648
|
+
console.log(
|
|
2649
|
+
` ${import_chalk21.default.cyan(t.taskName.padEnd(28))} ${formatMode(t.executionMode ?? "job").padEnd(10)} ${String(label).padEnd(40)} ${import_chalk21.default.dim(t.id)}`
|
|
2650
|
+
);
|
|
2651
|
+
}
|
|
2652
|
+
console.log();
|
|
2653
|
+
} catch (err) {
|
|
2654
|
+
spinner.fail(import_chalk21.default.red(err instanceof Error ? err.message : String(err)));
|
|
2655
|
+
process.exit(1);
|
|
2656
|
+
}
|
|
2657
|
+
});
|
|
2658
|
+
var getCmd4 = new import_commander20.Command("get").description("Show details of a task definition").argument("<task-id>", "Task ID (task_xxx)").action(async (taskId) => {
|
|
2659
|
+
requireToken5();
|
|
2660
|
+
const linked = requireLinkedApp7();
|
|
2661
|
+
const spinner = (0, import_ora18.default)(`Fetching task ${taskId}...`).start();
|
|
2662
|
+
try {
|
|
2663
|
+
const t = await api.getTask(linked.appId, taskId);
|
|
2664
|
+
spinner.stop();
|
|
2665
|
+
console.log();
|
|
2666
|
+
console.log(` ${import_chalk21.default.bold("Name:")} ${import_chalk21.default.cyan(t.taskName)}`);
|
|
2667
|
+
console.log(` ${import_chalk21.default.bold("ID:")} ${t.id}`);
|
|
2668
|
+
console.log(` ${import_chalk21.default.bold("Mode:")} ${formatMode(t.executionMode ?? "job")}`);
|
|
2669
|
+
if (t.imageRef) console.log(` ${import_chalk21.default.bold("Image:")} ${t.imageRef}`);
|
|
2670
|
+
if (t.handlerPath) console.log(` ${import_chalk21.default.bold("Handler:")} ${t.handlerPath}`);
|
|
2671
|
+
if (t.command?.length) console.log(` ${import_chalk21.default.bold("Command:")} ${t.command.join(" ")}`);
|
|
2672
|
+
if (t.timeoutSeconds) console.log(` ${import_chalk21.default.bold("Timeout:")} ${t.timeoutSeconds}s`);
|
|
2673
|
+
if (t.machineConfig) {
|
|
2674
|
+
const { cpu, memory, gpu } = t.machineConfig;
|
|
2675
|
+
const parts = [cpu && `cpu=${cpu}`, memory && `mem=${memory}`, gpu && `gpu=${gpu}`].filter(Boolean).join(" ");
|
|
2676
|
+
if (parts) console.log(` ${import_chalk21.default.bold("Resources:")} ${parts}`);
|
|
2677
|
+
}
|
|
2678
|
+
if (t.retryConfig) {
|
|
2679
|
+
console.log(
|
|
2680
|
+
` ${import_chalk21.default.bold("Retry:")} max=${t.retryConfig.maxAttempts ?? 1}, backoff=${t.retryConfig.backoff ?? "fixed"}`
|
|
2681
|
+
);
|
|
2682
|
+
}
|
|
2683
|
+
if (t.volumeMounts?.length) {
|
|
2684
|
+
console.log(` ${import_chalk21.default.bold("Volumes:")}`);
|
|
2685
|
+
for (const m of t.volumeMounts) {
|
|
2686
|
+
const ro = m.readOnly ? " (ro)" : "";
|
|
2687
|
+
console.log(` ${m.mountPath}${ro} \u2190 ${m.volumeId}`);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
console.log();
|
|
2691
|
+
} catch (err) {
|
|
2692
|
+
spinner.fail(import_chalk21.default.red(err instanceof Error ? err.message : String(err)));
|
|
2693
|
+
process.exit(1);
|
|
2694
|
+
}
|
|
2695
|
+
});
|
|
2696
|
+
var createCmd2 = new import_commander20.Command("create").description("Create (or upsert) a task definition").argument("<name>", "Task name (unique within the environment)").option("--env <env>", "Environment (default: production)", "production").option("--mode <mode>", "Execution mode: job | cron | service (default: job)", "job").option("--image <ref>", "Docker image reference").option("--handler <path>", "Handler function path (e.g. src/tasks/send-email.ts)").option("--cmd <cmd>", 'Command to run (comma-separated, e.g. "node,dist/worker.js")').option("--timeout <secs>", "Timeout in seconds").option("--cpu <cpu>", "CPU request (e.g. 0.5, 2)").option("--memory <mem>", "Memory request (e.g. 512Mi, 2Gi)").action(
|
|
2697
|
+
async (name, opts) => {
|
|
2698
|
+
requireToken5();
|
|
2699
|
+
const linked = requireLinkedApp7();
|
|
2700
|
+
if (!["job", "cron", "service"].includes(opts.mode)) {
|
|
2701
|
+
console.log(import_chalk21.default.red(` --mode must be one of: job, cron, service`));
|
|
2702
|
+
process.exit(1);
|
|
2703
|
+
}
|
|
2704
|
+
const spinner = (0, import_ora18.default)(`Creating task '${name}' [${opts.env}]...`).start();
|
|
2705
|
+
try {
|
|
2706
|
+
const task = await api.createTask(linked.appId, {
|
|
2707
|
+
taskName: name,
|
|
2708
|
+
envType: opts.env,
|
|
2709
|
+
executionMode: opts.mode,
|
|
2710
|
+
imageRef: opts.image,
|
|
2711
|
+
handlerPath: opts.handler,
|
|
2712
|
+
command: opts.cmd ? opts.cmd.split(",").map((s) => s.trim()) : void 0,
|
|
2713
|
+
timeoutSeconds: opts.timeout ? Number.parseInt(opts.timeout, 10) : void 0,
|
|
2714
|
+
machineConfig: opts.cpu || opts.memory ? { cpu: opts.cpu, memory: opts.memory } : void 0
|
|
2715
|
+
});
|
|
2716
|
+
spinner.succeed(import_chalk21.default.green(`\u2713 Task '${task.taskName}' created`));
|
|
2717
|
+
console.log();
|
|
2718
|
+
console.log(` ${import_chalk21.default.dim("ID:")} ${task.id}`);
|
|
2719
|
+
console.log(` ${import_chalk21.default.dim("Mode:")} ${task.executionMode}`);
|
|
2720
|
+
if (task.imageRef) console.log(` ${import_chalk21.default.dim("Image:")} ${task.imageRef}`);
|
|
2721
|
+
console.log();
|
|
2722
|
+
} catch (err) {
|
|
2723
|
+
spinner.fail(import_chalk21.default.red(err instanceof Error ? err.message : String(err)));
|
|
2724
|
+
process.exit(1);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
);
|
|
2728
|
+
var updateCmd = new import_commander20.Command("update").description("Update a task definition").argument("<task-id>", "Task ID (task_xxx)").option("--mode <mode>", "Execution mode: job | cron | service").option("--image <ref>", "Docker image reference").option("--handler <path>", "Handler function path").option("--cmd <cmd>", "Command (comma-separated)").option("--timeout <secs>", "Timeout in seconds").option("--cpu <cpu>", "CPU request").option("--memory <mem>", "Memory request").action(
|
|
2729
|
+
async (taskId, opts) => {
|
|
2730
|
+
requireToken5();
|
|
2731
|
+
const linked = requireLinkedApp7();
|
|
2732
|
+
const patch = {};
|
|
2733
|
+
if (opts.mode) patch.executionMode = opts.mode;
|
|
2734
|
+
if (opts.image) patch.imageRef = opts.image;
|
|
2735
|
+
if (opts.handler) patch.handlerPath = opts.handler;
|
|
2736
|
+
if (opts.cmd) patch.command = opts.cmd.split(",").map((s) => s.trim());
|
|
2737
|
+
if (opts.timeout) patch.timeoutSeconds = Number.parseInt(opts.timeout, 10);
|
|
2738
|
+
if (opts.cpu || opts.memory) patch.machineConfig = { cpu: opts.cpu, memory: opts.memory };
|
|
2739
|
+
if (Object.keys(patch).length === 0) {
|
|
2740
|
+
console.log(import_chalk21.default.yellow(" Nothing to update. Pass at least one option."));
|
|
2741
|
+
process.exit(1);
|
|
2742
|
+
}
|
|
2743
|
+
const spinner = (0, import_ora18.default)(`Updating task ${taskId}...`).start();
|
|
2744
|
+
try {
|
|
2745
|
+
const task = await api.updateTask(linked.appId, taskId, patch);
|
|
2746
|
+
spinner.succeed(import_chalk21.default.green(`\u2713 Task '${task.taskName}' updated`));
|
|
2747
|
+
} catch (err) {
|
|
2748
|
+
spinner.fail(import_chalk21.default.red(err instanceof Error ? err.message : String(err)));
|
|
2749
|
+
process.exit(1);
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
);
|
|
2753
|
+
var rmCmd4 = new import_commander20.Command("rm").description("Delete a task definition").argument("<task-id>", "Task ID (task_xxx)").option("--force", "Skip confirmation").action(async (taskId, opts) => {
|
|
2754
|
+
requireToken5();
|
|
2755
|
+
const linked = requireLinkedApp7();
|
|
2756
|
+
if (!opts.force) {
|
|
2757
|
+
const rl = import_node_readline7.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
2758
|
+
await new Promise((resolve) => {
|
|
2759
|
+
rl.question(import_chalk21.default.yellow(` Delete task ${import_chalk21.default.bold(taskId)}? (y/N) `), (answer) => {
|
|
2760
|
+
rl.close();
|
|
2761
|
+
if (answer.toLowerCase() !== "y") {
|
|
2762
|
+
console.log(import_chalk21.default.dim(" Cancelled."));
|
|
2763
|
+
process.exit(0);
|
|
2764
|
+
}
|
|
2765
|
+
resolve();
|
|
2766
|
+
});
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
const spinner = (0, import_ora18.default)(`Deleting task ${taskId}...`).start();
|
|
2770
|
+
try {
|
|
2771
|
+
await api.deleteTask(linked.appId, taskId);
|
|
2772
|
+
spinner.succeed(import_chalk21.default.green(`\u2713 Task ${taskId} deleted`));
|
|
2685
2773
|
} catch (err) {
|
|
2686
|
-
spinner.fail(
|
|
2774
|
+
spinner.fail(import_chalk21.default.red(err instanceof Error ? err.message : String(err)));
|
|
2687
2775
|
process.exit(1);
|
|
2688
2776
|
}
|
|
2689
2777
|
});
|
|
2690
|
-
var
|
|
2778
|
+
var tasksCommand = new import_commander20.Command("tasks").description("Manage task definitions (jobs, crons, services)").addCommand(listCmd5).addCommand(getCmd4).addCommand(createCmd2).addCommand(updateCmd).addCommand(rmCmd4).action(() => tasksCommand.help());
|
|
2691
2779
|
|
|
2692
2780
|
// src/commands/unlink.ts
|
|
2693
|
-
var
|
|
2694
|
-
var
|
|
2695
|
-
var unlinkCommand = new
|
|
2781
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
2782
|
+
var import_commander21 = require("commander");
|
|
2783
|
+
var unlinkCommand = new import_commander21.Command("unlink").description("Unlink current directory from its linked app").action(() => {
|
|
2696
2784
|
const linked = config.getLinkedApp();
|
|
2697
2785
|
if (!linked) {
|
|
2698
|
-
console.log(
|
|
2786
|
+
console.log(import_chalk22.default.yellow(" This directory is not linked to any app."));
|
|
2699
2787
|
process.exit(0);
|
|
2700
2788
|
}
|
|
2701
2789
|
const appId = linked.appId;
|
|
2702
2790
|
config.unlinkApp();
|
|
2703
2791
|
console.log("");
|
|
2704
|
-
console.log(
|
|
2705
|
-
console.log(
|
|
2792
|
+
console.log(import_chalk22.default.green(`\u2713 Unlinked from ${import_chalk22.default.bold(appId)}`));
|
|
2793
|
+
console.log(import_chalk22.default.dim(` Directory: ${process.cwd()}`));
|
|
2706
2794
|
console.log("");
|
|
2707
2795
|
});
|
|
2708
2796
|
|
|
2709
2797
|
// src/commands/volumes.ts
|
|
2710
|
-
var
|
|
2711
|
-
var
|
|
2712
|
-
var
|
|
2713
|
-
var
|
|
2714
|
-
function
|
|
2798
|
+
var import_node_readline8 = __toESM(require("readline"));
|
|
2799
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
2800
|
+
var import_commander22 = require("commander");
|
|
2801
|
+
var import_ora19 = __toESM(require("ora"));
|
|
2802
|
+
function requireLinkedApp8() {
|
|
2715
2803
|
const linked = config.getLinkedApp();
|
|
2716
2804
|
if (!linked) {
|
|
2717
|
-
console.log(
|
|
2805
|
+
console.log(import_chalk23.default.red("No app linked. Run `sylphx link` first."));
|
|
2718
2806
|
process.exit(1);
|
|
2719
2807
|
}
|
|
2720
2808
|
return linked;
|
|
2721
2809
|
}
|
|
2722
|
-
function
|
|
2810
|
+
function requireToken6() {
|
|
2723
2811
|
const token = config.getToken();
|
|
2724
2812
|
if (!token) {
|
|
2725
|
-
console.log(
|
|
2813
|
+
console.log(import_chalk23.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2726
2814
|
process.exit(1);
|
|
2727
2815
|
}
|
|
2728
2816
|
return token;
|
|
2729
2817
|
}
|
|
2730
|
-
var
|
|
2731
|
-
|
|
2732
|
-
const linked =
|
|
2733
|
-
const spinner = (0,
|
|
2818
|
+
var listCmd6 = new import_commander22.Command("list").description("List persistent volumes for this project").action(async () => {
|
|
2819
|
+
requireToken6();
|
|
2820
|
+
const linked = requireLinkedApp8();
|
|
2821
|
+
const spinner = (0, import_ora19.default)("Fetching volumes...").start();
|
|
2734
2822
|
try {
|
|
2735
2823
|
const volumes = await api.listVolumes(linked.appId);
|
|
2736
2824
|
spinner.stop();
|
|
2737
2825
|
if (volumes.length === 0) {
|
|
2738
|
-
console.log(
|
|
2826
|
+
console.log(import_chalk23.default.dim(" No volumes provisioned."));
|
|
2739
2827
|
console.log(
|
|
2740
|
-
|
|
2741
|
-
Run ${
|
|
2828
|
+
import_chalk23.default.dim(`
|
|
2829
|
+
Run ${import_chalk23.default.cyan("sylphx volumes create <name>")} to provision.`)
|
|
2742
2830
|
);
|
|
2743
2831
|
return;
|
|
2744
2832
|
}
|
|
2745
2833
|
console.log();
|
|
2746
2834
|
console.log(
|
|
2747
|
-
` ${
|
|
2835
|
+
` ${import_chalk23.default.bold("NAME".padEnd(24))} ${"SIZE".padEnd(8)} ${"STATUS".padEnd(14)} ID`
|
|
2748
2836
|
);
|
|
2749
2837
|
console.log(` ${"\u2500".repeat(70)}`);
|
|
2750
2838
|
for (const v of volumes) {
|
|
2751
|
-
const statusColor2 = v.status === "ready" ?
|
|
2839
|
+
const statusColor2 = v.status === "ready" ? import_chalk23.default.green((v.status ?? "unknown").padEnd(14)) : import_chalk23.default.yellow((v.status ?? "unknown").padEnd(14));
|
|
2752
2840
|
const size = v.sizeGb ? `${v.sizeGb}Gi`.padEnd(8) : "\u2014".padEnd(8);
|
|
2753
2841
|
console.log(
|
|
2754
|
-
` ${
|
|
2842
|
+
` ${import_chalk23.default.cyan(v.name.padEnd(24))} ${size} ${statusColor2} ${import_chalk23.default.dim(v.id)}`
|
|
2755
2843
|
);
|
|
2756
2844
|
}
|
|
2757
2845
|
console.log();
|
|
2758
2846
|
} catch (err) {
|
|
2759
|
-
spinner.fail(
|
|
2847
|
+
spinner.fail(import_chalk23.default.red(err instanceof Error ? err.message : String(err)));
|
|
2760
2848
|
process.exit(1);
|
|
2761
2849
|
}
|
|
2762
2850
|
});
|
|
2763
|
-
var
|
|
2851
|
+
var createCmd3 = new import_commander22.Command("create").description("Provision a persistent volume").argument("<name>", "Volume name").option("--size <gb>", "Size in GiB (default: 10)", "10").option(
|
|
2764
2852
|
"--access-mode <mode>",
|
|
2765
2853
|
"ReadWriteOnce or ReadWriteMany (default: ReadWriteOnce)",
|
|
2766
2854
|
"ReadWriteOnce"
|
|
2767
2855
|
).option("--mount <path>", "Default mount path (optional)").action(async (name, opts) => {
|
|
2768
|
-
|
|
2769
|
-
const linked =
|
|
2856
|
+
requireToken6();
|
|
2857
|
+
const linked = requireLinkedApp8();
|
|
2770
2858
|
const sizeGb = Number.parseInt(opts.size, 10);
|
|
2771
2859
|
if (Number.isNaN(sizeGb) || sizeGb < 1) {
|
|
2772
|
-
console.log(
|
|
2860
|
+
console.log(import_chalk23.default.red(" --size must be a number >= 1"));
|
|
2773
2861
|
process.exit(1);
|
|
2774
2862
|
}
|
|
2775
2863
|
if (!["ReadWriteOnce", "ReadWriteMany"].includes(opts.accessMode)) {
|
|
2776
|
-
console.log(
|
|
2864
|
+
console.log(import_chalk23.default.red(" --access-mode must be ReadWriteOnce or ReadWriteMany"));
|
|
2777
2865
|
process.exit(1);
|
|
2778
2866
|
}
|
|
2779
|
-
const spinner = (0,
|
|
2867
|
+
const spinner = (0, import_ora19.default)(`Provisioning volume '${name}' (${sizeGb}Gi)...`).start();
|
|
2780
2868
|
try {
|
|
2781
2869
|
const result = await api.createVolume(linked.appId, {
|
|
2782
2870
|
name,
|
|
@@ -2785,29 +2873,29 @@ var createCmd2 = new import_commander20.Command("create").description("Provision
|
|
|
2785
2873
|
mountPath: opts.mount
|
|
2786
2874
|
});
|
|
2787
2875
|
const vol = result.volume;
|
|
2788
|
-
spinner.succeed(
|
|
2876
|
+
spinner.succeed(import_chalk23.default.green(`\u2713 Volume '${vol.name}' provisioned`));
|
|
2789
2877
|
console.log();
|
|
2790
|
-
console.log(` ${
|
|
2791
|
-
console.log(` ${
|
|
2792
|
-
console.log(` ${
|
|
2878
|
+
console.log(` ${import_chalk23.default.dim("ID:")} ${vol.id}`);
|
|
2879
|
+
console.log(` ${import_chalk23.default.dim("Size:")} ${vol.sizeGb}Gi`);
|
|
2880
|
+
console.log(` ${import_chalk23.default.dim("Access:")} ${vol.accessMode ?? "ReadWriteOnce"}`);
|
|
2793
2881
|
console.log();
|
|
2794
2882
|
} catch (err) {
|
|
2795
|
-
spinner.fail(
|
|
2883
|
+
spinner.fail(import_chalk23.default.red(err instanceof Error ? err.message : String(err)));
|
|
2796
2884
|
process.exit(1);
|
|
2797
2885
|
}
|
|
2798
2886
|
});
|
|
2799
|
-
var
|
|
2800
|
-
|
|
2801
|
-
const linked =
|
|
2887
|
+
var rmCmd5 = new import_commander22.Command("rm").description("Delete a persistent volume").argument("<id>", "Volume ID").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
|
|
2888
|
+
requireToken6();
|
|
2889
|
+
const linked = requireLinkedApp8();
|
|
2802
2890
|
if (!opts.force) {
|
|
2803
|
-
const rl =
|
|
2891
|
+
const rl = import_node_readline8.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
2804
2892
|
await new Promise((resolve) => {
|
|
2805
2893
|
rl.question(
|
|
2806
|
-
|
|
2894
|
+
import_chalk23.default.yellow(` \u26A0\uFE0F Delete volume ${import_chalk23.default.bold(id)}? This will destroy all data. (y/N) `),
|
|
2807
2895
|
(answer) => {
|
|
2808
2896
|
rl.close();
|
|
2809
2897
|
if (answer.toLowerCase() !== "y") {
|
|
2810
|
-
console.log(
|
|
2898
|
+
console.log(import_chalk23.default.dim(" Cancelled."));
|
|
2811
2899
|
process.exit(0);
|
|
2812
2900
|
}
|
|
2813
2901
|
resolve();
|
|
@@ -2815,62 +2903,62 @@ var rmCmd4 = new import_commander20.Command("rm").description("Delete a persiste
|
|
|
2815
2903
|
);
|
|
2816
2904
|
});
|
|
2817
2905
|
}
|
|
2818
|
-
const spinner = (0,
|
|
2906
|
+
const spinner = (0, import_ora19.default)(`Deleting volume ${id}...`).start();
|
|
2819
2907
|
try {
|
|
2820
2908
|
await api.deleteVolume(linked.appId, id);
|
|
2821
|
-
spinner.succeed(
|
|
2909
|
+
spinner.succeed(import_chalk23.default.green(`\u2713 Volume ${id} deleted`));
|
|
2822
2910
|
} catch (err) {
|
|
2823
|
-
spinner.fail(
|
|
2911
|
+
spinner.fail(import_chalk23.default.red(err instanceof Error ? err.message : String(err)));
|
|
2824
2912
|
process.exit(1);
|
|
2825
2913
|
}
|
|
2826
2914
|
});
|
|
2827
|
-
var volumesCommand = new
|
|
2915
|
+
var volumesCommand = new import_commander22.Command("volumes").description("Manage persistent volumes").addCommand(listCmd6).addCommand(createCmd3).addCommand(rmCmd5).action(() => volumesCommand.help());
|
|
2828
2916
|
|
|
2829
2917
|
// src/commands/whoami.ts
|
|
2830
|
-
var
|
|
2831
|
-
var
|
|
2832
|
-
var
|
|
2833
|
-
var whoamiCommand = new
|
|
2918
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
2919
|
+
var import_commander23 = require("commander");
|
|
2920
|
+
var import_ora20 = __toESM(require("ora"));
|
|
2921
|
+
var whoamiCommand = new import_commander23.Command("whoami").description("Show current user, org, and linked app").action(async () => {
|
|
2834
2922
|
const token = config.getToken();
|
|
2835
2923
|
if (!token) {
|
|
2836
|
-
console.log(
|
|
2924
|
+
console.log(import_chalk24.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2837
2925
|
process.exit(1);
|
|
2838
2926
|
}
|
|
2839
|
-
const spinner = (0,
|
|
2927
|
+
const spinner = (0, import_ora20.default)("Fetching account info...").start();
|
|
2840
2928
|
try {
|
|
2841
2929
|
const me = await api.whoami();
|
|
2842
2930
|
spinner.stop();
|
|
2843
2931
|
console.log("");
|
|
2844
|
-
console.log(
|
|
2845
|
-
console.log(` User: ${
|
|
2846
|
-
console.log(` Name: ${
|
|
2847
|
-
console.log(` ID: ${
|
|
2932
|
+
console.log(import_chalk24.default.bold(" Account"));
|
|
2933
|
+
console.log(` User: ${import_chalk24.default.cyan(me.user.email)}`);
|
|
2934
|
+
console.log(` Name: ${import_chalk24.default.white(me.user.name)}`);
|
|
2935
|
+
console.log(` ID: ${import_chalk24.default.dim(me.user.id)}`);
|
|
2848
2936
|
if (me.orgs.length > 0) {
|
|
2849
2937
|
console.log("");
|
|
2850
|
-
console.log(
|
|
2938
|
+
console.log(import_chalk24.default.bold(" Organizations"));
|
|
2851
2939
|
for (const org of me.orgs) {
|
|
2852
2940
|
console.log(
|
|
2853
|
-
` ${
|
|
2941
|
+
` ${import_chalk24.default.cyan(org.slug)} ${import_chalk24.default.dim(`(${org.name})`)}`
|
|
2854
2942
|
);
|
|
2855
2943
|
}
|
|
2856
2944
|
}
|
|
2857
2945
|
const linkedApp = config.getLinkedApp();
|
|
2858
2946
|
if (linkedApp) {
|
|
2859
2947
|
console.log("");
|
|
2860
|
-
console.log(
|
|
2861
|
-
console.log(` App: ${
|
|
2862
|
-
console.log(` Org: ${
|
|
2863
|
-
console.log(` Env: ${
|
|
2948
|
+
console.log(import_chalk24.default.bold(" Linked App"));
|
|
2949
|
+
console.log(` App: ${import_chalk24.default.cyan(linkedApp.appId)}`);
|
|
2950
|
+
console.log(` Org: ${import_chalk24.default.white(linkedApp.orgId)}`);
|
|
2951
|
+
console.log(` Env: ${import_chalk24.default.white(linkedApp.defaultEnv)}`);
|
|
2864
2952
|
} else {
|
|
2865
2953
|
console.log("");
|
|
2866
2954
|
console.log(
|
|
2867
|
-
|
|
2955
|
+
import_chalk24.default.dim(" No app linked. Run `sylphx link` to link one.")
|
|
2868
2956
|
);
|
|
2869
2957
|
}
|
|
2870
2958
|
console.log("");
|
|
2871
2959
|
} catch (err) {
|
|
2872
2960
|
spinner.fail(
|
|
2873
|
-
|
|
2961
|
+
import_chalk24.default.red(
|
|
2874
2962
|
`Failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2875
2963
|
)
|
|
2876
2964
|
);
|
|
@@ -2880,48 +2968,50 @@ var whoamiCommand = new import_commander21.Command("whoami").description("Show c
|
|
|
2880
2968
|
|
|
2881
2969
|
// src/index.ts
|
|
2882
2970
|
var { version: version4 } = package_default;
|
|
2883
|
-
var program = new
|
|
2884
|
-
program.name("sylphx").description(`${
|
|
2971
|
+
var program = new import_commander24.Command();
|
|
2972
|
+
program.name("sylphx").description(`${import_chalk25.default.bold("Sylphx Platform CLI")} \u2014 deploy and manage your applications`).version(version4, "-v, --version", "Print version").helpOption("-h, --help", "Show help").addHelpText(
|
|
2885
2973
|
"after",
|
|
2886
2974
|
`
|
|
2887
|
-
${
|
|
2888
|
-
${
|
|
2889
|
-
${
|
|
2890
|
-
${
|
|
2891
|
-
${
|
|
2892
|
-
${
|
|
2893
|
-
${
|
|
2894
|
-
${
|
|
2895
|
-
${
|
|
2896
|
-
${
|
|
2897
|
-
${
|
|
2898
|
-
${
|
|
2899
|
-
${
|
|
2900
|
-
${
|
|
2901
|
-
${
|
|
2902
|
-
${
|
|
2903
|
-
${
|
|
2904
|
-
${
|
|
2905
|
-
${
|
|
2906
|
-
${
|
|
2907
|
-
${
|
|
2975
|
+
${import_chalk25.default.bold("Examples:")}
|
|
2976
|
+
${import_chalk25.default.cyan("sylphx login")} Authenticate with Sylphx
|
|
2977
|
+
${import_chalk25.default.cyan("sylphx init my-app")} Create and link a new project
|
|
2978
|
+
${import_chalk25.default.cyan("sylphx link")} Link current directory to an app
|
|
2979
|
+
${import_chalk25.default.cyan("sylphx deploy")} Deploy to production
|
|
2980
|
+
${import_chalk25.default.cyan("sylphx deploy --env staging")} Deploy to staging
|
|
2981
|
+
${import_chalk25.default.cyan("sylphx promote --from staging")} Promote staging build \u2192 production
|
|
2982
|
+
${import_chalk25.default.cyan("sylphx rollback")} Rollback to previous deployment
|
|
2983
|
+
${import_chalk25.default.cyan("sylphx logs -f")} Stream live logs
|
|
2984
|
+
${import_chalk25.default.cyan("sylphx status")} Check deployment status
|
|
2985
|
+
${import_chalk25.default.cyan("sylphx env list")} List environment variables
|
|
2986
|
+
${import_chalk25.default.cyan("sylphx env set PORT=3000")} Set an env var
|
|
2987
|
+
${import_chalk25.default.cyan("sylphx config list")} List remote config keys
|
|
2988
|
+
${import_chalk25.default.cyan("sylphx config set KEY=value")} Set a remote config key
|
|
2989
|
+
${import_chalk25.default.cyan("sylphx db create my-db")} Provision a PostgreSQL database
|
|
2990
|
+
${import_chalk25.default.cyan("sylphx storage create")} Provision blob storage
|
|
2991
|
+
${import_chalk25.default.cyan("sylphx volumes create data --size 20")} Provision a persistent volume
|
|
2992
|
+
${import_chalk25.default.cyan("sylphx services list")} List project services
|
|
2993
|
+
${import_chalk25.default.cyan("sylphx services deploy web")} Deploy a specific service
|
|
2994
|
+
${import_chalk25.default.cyan("sylphx tasks list")} List task definitions
|
|
2995
|
+
${import_chalk25.default.cyan("sylphx tasks create <name> --image nginx")} Create a task
|
|
2996
|
+
${import_chalk25.default.cyan("sylphx resources bind <id>")} Bind a resource to this project
|
|
2997
|
+
${import_chalk25.default.cyan("sylphx projects list")} List all projects
|
|
2908
2998
|
|
|
2909
|
-
${
|
|
2910
|
-
${
|
|
2999
|
+
${import_chalk25.default.bold("Documentation:")}
|
|
3000
|
+
${import_chalk25.default.underline("https://docs.sylphx.com/cli")}
|
|
2911
3001
|
`
|
|
2912
3002
|
);
|
|
2913
|
-
program.addCommand(loginCommand).addCommand(logoutCommand).addCommand(whoamiCommand).addCommand(initCommand).addCommand(linkCommand).addCommand(unlinkCommand).addCommand(projectsCommand).addCommand(deployCommand).addCommand(promoteCommand).addCommand(rollbackCommand).addCommand(statusCommand).addCommand(logsCommand).addCommand(envCommand).addCommand(configCommand).addCommand(dbCommand).addCommand(storageCommand).addCommand(volumesCommand).addCommand(resourcesCommand).addCommand(servicesCommand).addCommand(domainsCommand).addCommand(openCommand);
|
|
3003
|
+
program.addCommand(loginCommand).addCommand(logoutCommand).addCommand(whoamiCommand).addCommand(initCommand).addCommand(linkCommand).addCommand(unlinkCommand).addCommand(projectsCommand).addCommand(orgsCommand).addCommand(deployCommand).addCommand(promoteCommand).addCommand(rollbackCommand).addCommand(statusCommand).addCommand(logsCommand).addCommand(envCommand).addCommand(configCommand).addCommand(dbCommand).addCommand(storageCommand).addCommand(volumesCommand).addCommand(resourcesCommand).addCommand(tasksCommand).addCommand(servicesCommand).addCommand(domainsCommand).addCommand(openCommand);
|
|
2914
3004
|
program.on("command:*", (operands) => {
|
|
2915
|
-
console.error(
|
|
2916
|
-
Unknown command: ${
|
|
3005
|
+
console.error(import_chalk25.default.red(`
|
|
3006
|
+
Unknown command: ${import_chalk25.default.bold(operands[0] ?? "")}
|
|
2917
3007
|
`));
|
|
2918
|
-
console.log(` Run ${
|
|
3008
|
+
console.log(` Run ${import_chalk25.default.cyan("sylphx --help")} for usage.
|
|
2919
3009
|
`);
|
|
2920
3010
|
process.exit(1);
|
|
2921
3011
|
});
|
|
2922
3012
|
program.parseAsync(process.argv).catch((err) => {
|
|
2923
3013
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2924
|
-
console.error(
|
|
3014
|
+
console.error(import_chalk25.default.red(`
|
|
2925
3015
|
Error: ${msg}
|
|
2926
3016
|
`));
|
|
2927
3017
|
process.exit(1);
|