@sylphx/cli 0.1.9 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1863 -443
- package/package.json +1 -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_chalk24 = __toESM(require("chalk"));
|
|
28
|
+
var import_commander23 = 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.0",
|
|
34
34
|
description: "Sylphx Platform CLI \u2014 deploy, manage logs, env vars, and more",
|
|
35
35
|
type: "commonjs",
|
|
36
36
|
bin: {
|
|
@@ -73,8 +73,7 @@ var package_default = {
|
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
// src/commands/
|
|
77
|
-
var import_node_readline = __toESM(require("readline"));
|
|
76
|
+
// src/commands/config-cmd.ts
|
|
78
77
|
var import_chalk = __toESM(require("chalk"));
|
|
79
78
|
var import_commander = require("commander");
|
|
80
79
|
var import_ora = __toESM(require("ora"));
|
|
@@ -154,6 +153,7 @@ var config = {
|
|
|
154
153
|
};
|
|
155
154
|
|
|
156
155
|
// src/api.ts
|
|
156
|
+
var { version } = package_default;
|
|
157
157
|
var BASE_URL = process.env.SYLPHX_API_URL ?? "https://api.sylphx.com";
|
|
158
158
|
var API_BASE = `${BASE_URL}/v1`;
|
|
159
159
|
var AUTH_BASE_URL = "https://sylphx.com";
|
|
@@ -178,7 +178,7 @@ async function request(method, path5, options) {
|
|
|
178
178
|
const headers = {
|
|
179
179
|
Authorization: `Bearer ${token}`,
|
|
180
180
|
"Content-Type": "application/json",
|
|
181
|
-
"User-Agent":
|
|
181
|
+
"User-Agent": `sylphx-cli/${version}`
|
|
182
182
|
};
|
|
183
183
|
const res = await fetch(url, {
|
|
184
184
|
method,
|
|
@@ -218,13 +218,12 @@ var api = {
|
|
|
218
218
|
query: { envType }
|
|
219
219
|
});
|
|
220
220
|
},
|
|
221
|
-
/** Rollback to previous deployment
|
|
222
|
-
*
|
|
221
|
+
/** Rollback to previous deployment.
|
|
222
|
+
* Accepts envId (rollback env to previous deploy), deploymentRecordId, or buildRecordId.
|
|
223
|
+
* One of these is required.
|
|
223
224
|
*/
|
|
224
|
-
async rollback(projectId,
|
|
225
|
-
return request("POST", `/projects/${projectId}/rollback`, {
|
|
226
|
-
body: { envType }
|
|
227
|
-
});
|
|
225
|
+
async rollback(projectId, body) {
|
|
226
|
+
return request("POST", `/projects/${projectId}/rollback`, { body });
|
|
228
227
|
},
|
|
229
228
|
/** List env vars for a project environment */
|
|
230
229
|
async listEnvVars(projectId, envType) {
|
|
@@ -252,30 +251,58 @@ var api = {
|
|
|
252
251
|
query: { envType }
|
|
253
252
|
});
|
|
254
253
|
},
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
);
|
|
262
|
-
return res.customDomains ?? [];
|
|
254
|
+
// ── Domain Management (Unified System) ──────────────────────────────────
|
|
255
|
+
/** List all apex domains for a project environment */
|
|
256
|
+
async listDomains(projectId, envType = "production") {
|
|
257
|
+
return request("GET", `/projects/${projectId}/domains`, {
|
|
258
|
+
query: { envType }
|
|
259
|
+
});
|
|
263
260
|
},
|
|
264
|
-
/**
|
|
265
|
-
async
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
body: { domain }
|
|
261
|
+
/** Register an apex domain for a project (full domain object with DNS records) */
|
|
262
|
+
async createDomain(projectId, apexDomain, envType = "production") {
|
|
263
|
+
return request("POST", `/projects/${projectId}/domains`, {
|
|
264
|
+
body: { apexDomain, envType }
|
|
269
265
|
});
|
|
270
266
|
},
|
|
271
|
-
/**
|
|
272
|
-
async
|
|
273
|
-
const
|
|
267
|
+
/** Add a hostname binding to an existing domain */
|
|
268
|
+
async addHostname(projectId, domainId, hostname, opts) {
|
|
269
|
+
const { envType = "production", ...bodyOpts } = opts ?? {};
|
|
270
|
+
return request("POST", `/projects/${projectId}/domains/${domainId}/hostnames`, {
|
|
271
|
+
query: { envType },
|
|
272
|
+
body: { hostname, ...bodyOpts }
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
/** Remove a hostname binding */
|
|
276
|
+
async removeHostname(projectId, domainId, hostnameId) {
|
|
274
277
|
return request(
|
|
275
278
|
"DELETE",
|
|
276
|
-
`/
|
|
279
|
+
`/projects/${projectId}/domains/${domainId}/hostnames/${hostnameId}`
|
|
280
|
+
);
|
|
281
|
+
},
|
|
282
|
+
/** Trigger DNS check for a hostname */
|
|
283
|
+
async checkHostname(projectId, domainId, hostnameId) {
|
|
284
|
+
return request(
|
|
285
|
+
"POST",
|
|
286
|
+
`/projects/${projectId}/domains/${domainId}/hostnames/${hostnameId}/check`
|
|
287
|
+
);
|
|
288
|
+
},
|
|
289
|
+
/** Enable email sending for a domain (generates DKIM keypair) */
|
|
290
|
+
async enableEmail(projectId, domainId, opts) {
|
|
291
|
+
return request("POST", `/projects/${projectId}/domains/${domainId}/email`, {
|
|
292
|
+
body: opts ?? {}
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
/** Check DKIM DNS propagation and load into Stalwart */
|
|
296
|
+
async checkEmail(projectId, domainId) {
|
|
297
|
+
return request(
|
|
298
|
+
"POST",
|
|
299
|
+
`/projects/${projectId}/domains/${domainId}/email/check`
|
|
277
300
|
);
|
|
278
301
|
},
|
|
302
|
+
/** Disable email sending for a domain */
|
|
303
|
+
async disableEmail(projectId, domainId) {
|
|
304
|
+
return request("DELETE", `/projects/${projectId}/domains/${domainId}/email`);
|
|
305
|
+
},
|
|
279
306
|
/** Get current user and orgs */
|
|
280
307
|
async whoami() {
|
|
281
308
|
return request("GET", "/user/me");
|
|
@@ -333,12 +360,282 @@ var api = {
|
|
|
333
360
|
/** CLI auth: poll for token (uses main sylphx.com domain) */
|
|
334
361
|
async pollCliAuth(code) {
|
|
335
362
|
const res = await fetch(`${AUTH_BASE_URL}/api/auth/cli/poll?code=${encodeURIComponent(code)}`, {
|
|
336
|
-
headers: { "User-Agent":
|
|
363
|
+
headers: { "User-Agent": `sylphx-cli/${version}` }
|
|
337
364
|
});
|
|
338
365
|
if (!res.ok) {
|
|
339
366
|
throw new ApiError(res.status, "Failed to poll auth status");
|
|
340
367
|
}
|
|
341
368
|
return res.json();
|
|
369
|
+
},
|
|
370
|
+
// ── Services ─────────────────────────────────────────────────────────────
|
|
371
|
+
/** List services for a project */
|
|
372
|
+
async listServices(projectId) {
|
|
373
|
+
const res = await request(
|
|
374
|
+
"GET",
|
|
375
|
+
`/projects/${encodeURIComponent(projectId)}/services`
|
|
376
|
+
);
|
|
377
|
+
return Array.isArray(res) ? res : res.data ?? [];
|
|
378
|
+
},
|
|
379
|
+
/** Get a service by name */
|
|
380
|
+
async getService(projectId, name) {
|
|
381
|
+
return request(
|
|
382
|
+
"GET",
|
|
383
|
+
`/projects/${encodeURIComponent(projectId)}/services/${encodeURIComponent(name)}`
|
|
384
|
+
);
|
|
385
|
+
},
|
|
386
|
+
/**
|
|
387
|
+
* Deploy a specific service.
|
|
388
|
+
* Backend expects { environmentId?, force?, image? } — NOT envType.
|
|
389
|
+
* Resolves envType → environmentId automatically.
|
|
390
|
+
*/
|
|
391
|
+
async deployService(projectId, name, envType) {
|
|
392
|
+
const environmentId = await this.resolveEnvId(projectId, envType);
|
|
393
|
+
return request(
|
|
394
|
+
"POST",
|
|
395
|
+
`/projects/${encodeURIComponent(projectId)}/services/${encodeURIComponent(name)}/deploy`,
|
|
396
|
+
{ body: environmentId ? { environmentId } : {} }
|
|
397
|
+
);
|
|
398
|
+
},
|
|
399
|
+
/** Delete a service */
|
|
400
|
+
async deleteService(projectId, name) {
|
|
401
|
+
return request(
|
|
402
|
+
"DELETE",
|
|
403
|
+
`/projects/${encodeURIComponent(projectId)}/services/${encodeURIComponent(name)}`
|
|
404
|
+
);
|
|
405
|
+
},
|
|
406
|
+
// ── Volumes ───────────────────────────────────────────────────────────────
|
|
407
|
+
/** List volumes for a project */
|
|
408
|
+
async listVolumes(projectId) {
|
|
409
|
+
const res = await request(
|
|
410
|
+
"GET",
|
|
411
|
+
`/projects/${encodeURIComponent(projectId)}/volumes`
|
|
412
|
+
);
|
|
413
|
+
return Array.isArray(res) ? res : res.volumes ?? [];
|
|
414
|
+
},
|
|
415
|
+
/** Create a volume */
|
|
416
|
+
async createVolume(projectId, data) {
|
|
417
|
+
return request(
|
|
418
|
+
"POST",
|
|
419
|
+
`/projects/${encodeURIComponent(projectId)}/volumes`,
|
|
420
|
+
{ body: data }
|
|
421
|
+
);
|
|
422
|
+
},
|
|
423
|
+
/** Delete a volume */
|
|
424
|
+
async deleteVolume(projectId, volId) {
|
|
425
|
+
return request(
|
|
426
|
+
"DELETE",
|
|
427
|
+
`/projects/${encodeURIComponent(projectId)}/volumes/${encodeURIComponent(volId)}`
|
|
428
|
+
);
|
|
429
|
+
},
|
|
430
|
+
// ── Storage (Blob) ────────────────────────────────────────────────────────
|
|
431
|
+
/** List storage resources for a project.
|
|
432
|
+
* Backend returns a plain array of binding objects (not wrapped in { data: [...] }).
|
|
433
|
+
* Optionally filter by envType client-side.
|
|
434
|
+
*/
|
|
435
|
+
async listStorage(projectId, envType) {
|
|
436
|
+
const res = await request(
|
|
437
|
+
"GET",
|
|
438
|
+
`/projects/${encodeURIComponent(projectId)}/storage`
|
|
439
|
+
);
|
|
440
|
+
const list = Array.isArray(res) ? res : [];
|
|
441
|
+
return envType ? list.filter((r) => !r.envType || r.envType === envType) : list;
|
|
442
|
+
},
|
|
443
|
+
/** Provision blob storage for a project environment */
|
|
444
|
+
async createStorage(projectId, data) {
|
|
445
|
+
return request("POST", `/projects/${encodeURIComponent(projectId)}/storage`, {
|
|
446
|
+
body: data
|
|
447
|
+
});
|
|
448
|
+
},
|
|
449
|
+
/** Delete a storage resource */
|
|
450
|
+
async deleteStorage(projectId, storageId) {
|
|
451
|
+
return request(
|
|
452
|
+
"DELETE",
|
|
453
|
+
`/projects/${encodeURIComponent(projectId)}/storage/${encodeURIComponent(storageId)}`
|
|
454
|
+
);
|
|
455
|
+
},
|
|
456
|
+
// ── Config (remote key-value) ─────────────────────────────────────────────
|
|
457
|
+
/**
|
|
458
|
+
* List all config keys for a project.
|
|
459
|
+
* Backend uses ?env=<envId> (not ?envType). Resolves envType → envId automatically.
|
|
460
|
+
*/
|
|
461
|
+
async listConfig(projectId, envType) {
|
|
462
|
+
const query = {};
|
|
463
|
+
if (envType) {
|
|
464
|
+
const envId = await this.resolveEnvId(projectId, envType);
|
|
465
|
+
if (envId) query.env = envId;
|
|
466
|
+
}
|
|
467
|
+
const res = await request(
|
|
468
|
+
"GET",
|
|
469
|
+
`/projects/${encodeURIComponent(projectId)}/config`,
|
|
470
|
+
{ query }
|
|
471
|
+
);
|
|
472
|
+
return Array.isArray(res) ? res : res.data ?? [];
|
|
473
|
+
},
|
|
474
|
+
/**
|
|
475
|
+
* Get a single config key.
|
|
476
|
+
* Backend uses ?env=<envId> (not ?envType). Resolves envType → envId automatically.
|
|
477
|
+
*/
|
|
478
|
+
async getConfig(projectId, key, envType) {
|
|
479
|
+
const query = {};
|
|
480
|
+
if (envType) {
|
|
481
|
+
const envId = await this.resolveEnvId(projectId, envType);
|
|
482
|
+
if (envId) query.env = envId;
|
|
483
|
+
}
|
|
484
|
+
return request(
|
|
485
|
+
"GET",
|
|
486
|
+
`/projects/${encodeURIComponent(projectId)}/config/${encodeURIComponent(key)}`,
|
|
487
|
+
{ query }
|
|
488
|
+
);
|
|
489
|
+
},
|
|
490
|
+
/**
|
|
491
|
+
* Set a config key.
|
|
492
|
+
* Backend expects { key, value, environmentId } (not envType).
|
|
493
|
+
* Resolves envType → environmentId automatically when envType is provided.
|
|
494
|
+
*/
|
|
495
|
+
async setConfig(projectId, key, value, envType) {
|
|
496
|
+
let environmentId = null;
|
|
497
|
+
if (envType) {
|
|
498
|
+
environmentId = await this.resolveEnvId(projectId, envType);
|
|
499
|
+
}
|
|
500
|
+
return request("POST", `/projects/${encodeURIComponent(projectId)}/config`, {
|
|
501
|
+
body: { key, value, environmentId }
|
|
502
|
+
});
|
|
503
|
+
},
|
|
504
|
+
/**
|
|
505
|
+
* Delete a config key.
|
|
506
|
+
* Backend uses ?env=<envId> (not ?envType). Resolves envType → envId automatically.
|
|
507
|
+
*/
|
|
508
|
+
async deleteConfig(projectId, key, envType) {
|
|
509
|
+
const query = {};
|
|
510
|
+
if (envType) {
|
|
511
|
+
const envId = await this.resolveEnvId(projectId, envType);
|
|
512
|
+
if (envId) query.env = envId;
|
|
513
|
+
}
|
|
514
|
+
return request(
|
|
515
|
+
"DELETE",
|
|
516
|
+
`/projects/${encodeURIComponent(projectId)}/config/${encodeURIComponent(key)}`,
|
|
517
|
+
{ query }
|
|
518
|
+
);
|
|
519
|
+
},
|
|
520
|
+
// ── Environments ──────────────────────────────────────────────────────────
|
|
521
|
+
/** List environments for a project */
|
|
522
|
+
async listEnvironments(projectId) {
|
|
523
|
+
const res = await request(
|
|
524
|
+
"GET",
|
|
525
|
+
`/projects/${encodeURIComponent(projectId)}/environments`
|
|
526
|
+
);
|
|
527
|
+
return Array.isArray(res) ? res : res.data ?? [];
|
|
528
|
+
},
|
|
529
|
+
/**
|
|
530
|
+
* Resolve an environment type string (e.g. "production") → its envId for the given project.
|
|
531
|
+
* Returns null if not found. Used internally by config commands.
|
|
532
|
+
*/
|
|
533
|
+
async resolveEnvId(projectId, envType) {
|
|
534
|
+
const envs = await this.listEnvironments(projectId);
|
|
535
|
+
const match = envs.find((e) => (e.envType ?? "").toLowerCase() === envType.toLowerCase());
|
|
536
|
+
return match?.id ?? null;
|
|
537
|
+
},
|
|
538
|
+
/** Promote an environment (e.g. staging → production).
|
|
539
|
+
* Backend returns { promoted: number, services: [...], sourceEnvironment, targetEnvironment }
|
|
540
|
+
*/
|
|
541
|
+
async promoteEnvironment(projectId, targetEnvId, sourceEnvId) {
|
|
542
|
+
return request(
|
|
543
|
+
"POST",
|
|
544
|
+
`/projects/${encodeURIComponent(projectId)}/environments/${encodeURIComponent(targetEnvId)}/promote`,
|
|
545
|
+
{ body: { sourceEnvId } }
|
|
546
|
+
);
|
|
547
|
+
},
|
|
548
|
+
// ── Resource Bindings ─────────────────────────────────────────────────────
|
|
549
|
+
/** List bindings for a resource (which envs it's linked to).
|
|
550
|
+
* Backend returns { bindings: [...] }.
|
|
551
|
+
*/
|
|
552
|
+
async listResourceBindings(resourceId) {
|
|
553
|
+
const res = await request(
|
|
554
|
+
"GET",
|
|
555
|
+
`/resources/${encodeURIComponent(resourceId)}/bindings`
|
|
556
|
+
);
|
|
557
|
+
return res.bindings ?? [];
|
|
558
|
+
},
|
|
559
|
+
/**
|
|
560
|
+
* Bind a resource to a project environment.
|
|
561
|
+
* Backend expects { appEnvironmentId, role? }.
|
|
562
|
+
* Accepts projectId + envType and resolves to appEnvironmentId automatically.
|
|
563
|
+
*/
|
|
564
|
+
async bindResource(resourceId, data) {
|
|
565
|
+
const appEnvironmentId = await this.resolveEnvId(data.projectId, data.envType);
|
|
566
|
+
if (!appEnvironmentId) {
|
|
567
|
+
throw new Error(`Environment '${data.envType}' not found for project '${data.projectId}'`);
|
|
568
|
+
}
|
|
569
|
+
return request(
|
|
570
|
+
"POST",
|
|
571
|
+
`/resources/${encodeURIComponent(resourceId)}/bindings`,
|
|
572
|
+
{ body: { appEnvironmentId, role: data.role ?? "primary" } }
|
|
573
|
+
);
|
|
574
|
+
},
|
|
575
|
+
/** Unbind a resource from a project environment */
|
|
576
|
+
async unbindResource(resourceId, bindingId) {
|
|
577
|
+
return request(
|
|
578
|
+
"DELETE",
|
|
579
|
+
`/resources/${encodeURIComponent(resourceId)}/bindings/${encodeURIComponent(bindingId)}`
|
|
580
|
+
);
|
|
581
|
+
},
|
|
582
|
+
// ── Tasks ─────────────────────────────────────────────────────────────────
|
|
583
|
+
/**
|
|
584
|
+
* List task definitions for a project environment.
|
|
585
|
+
* Uses ?envId=<envId> (resolved from envType automatically).
|
|
586
|
+
*/
|
|
587
|
+
async listTasks(projectId, envType) {
|
|
588
|
+
const query = {};
|
|
589
|
+
if (envType) {
|
|
590
|
+
const envId = await this.resolveEnvId(projectId, envType);
|
|
591
|
+
if (envId) query.envId = envId;
|
|
592
|
+
}
|
|
593
|
+
const res = await request(
|
|
594
|
+
"GET",
|
|
595
|
+
`/projects/${encodeURIComponent(projectId)}/tasks`,
|
|
596
|
+
{ query }
|
|
597
|
+
);
|
|
598
|
+
return res.tasks ?? [];
|
|
599
|
+
},
|
|
600
|
+
/**
|
|
601
|
+
* Create or update a task definition (upserts on taskName + environmentId).
|
|
602
|
+
* Backend requires envId — resolves from envType automatically.
|
|
603
|
+
*/
|
|
604
|
+
async createTask(projectId, data) {
|
|
605
|
+
const envId = await this.resolveEnvId(projectId, data.envType);
|
|
606
|
+
if (!envId)
|
|
607
|
+
throw new Error(`Environment '${data.envType}' not found for project '${projectId}'`);
|
|
608
|
+
const { envType: _drop, ...rest } = data;
|
|
609
|
+
const res = await request(
|
|
610
|
+
"POST",
|
|
611
|
+
`/projects/${encodeURIComponent(projectId)}/tasks`,
|
|
612
|
+
{ body: { ...rest, envId } }
|
|
613
|
+
);
|
|
614
|
+
return res.task;
|
|
615
|
+
},
|
|
616
|
+
/** Get a single task definition */
|
|
617
|
+
async getTask(projectId, taskId) {
|
|
618
|
+
const res = await request(
|
|
619
|
+
"GET",
|
|
620
|
+
`/projects/${encodeURIComponent(projectId)}/tasks/${encodeURIComponent(taskId)}`
|
|
621
|
+
);
|
|
622
|
+
return res.task;
|
|
623
|
+
},
|
|
624
|
+
/** Update a task definition */
|
|
625
|
+
async updateTask(projectId, taskId, data) {
|
|
626
|
+
const res = await request(
|
|
627
|
+
"PATCH",
|
|
628
|
+
`/projects/${encodeURIComponent(projectId)}/tasks/${encodeURIComponent(taskId)}`,
|
|
629
|
+
{ body: data }
|
|
630
|
+
);
|
|
631
|
+
return res.task;
|
|
632
|
+
},
|
|
633
|
+
/** Delete a task definition */
|
|
634
|
+
async deleteTask(projectId, taskId) {
|
|
635
|
+
await request(
|
|
636
|
+
"DELETE",
|
|
637
|
+
`/projects/${encodeURIComponent(projectId)}/tasks/${encodeURIComponent(taskId)}`
|
|
638
|
+
);
|
|
342
639
|
}
|
|
343
640
|
};
|
|
344
641
|
function createLogStream(projectId, envType) {
|
|
@@ -349,13 +646,105 @@ function createLogStream(projectId, envType) {
|
|
|
349
646
|
};
|
|
350
647
|
}
|
|
351
648
|
|
|
649
|
+
// src/commands/config-cmd.ts
|
|
650
|
+
function requireLinkedApp() {
|
|
651
|
+
const linked = config.getLinkedApp();
|
|
652
|
+
if (!linked) {
|
|
653
|
+
console.log(import_chalk.default.red("No app linked. Run `sylphx link` first."));
|
|
654
|
+
process.exit(1);
|
|
655
|
+
}
|
|
656
|
+
return linked;
|
|
657
|
+
}
|
|
658
|
+
function requireToken() {
|
|
659
|
+
const token = config.getToken();
|
|
660
|
+
if (!token) {
|
|
661
|
+
console.log(import_chalk.default.red("Not authenticated. Run `sylphx login` first."));
|
|
662
|
+
process.exit(1);
|
|
663
|
+
}
|
|
664
|
+
return token;
|
|
665
|
+
}
|
|
666
|
+
var listCmd = new import_commander.Command("list").description("List all remote config keys").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
|
|
667
|
+
requireToken();
|
|
668
|
+
const linked = requireLinkedApp();
|
|
669
|
+
const spinner = (0, import_ora.default)("Fetching config...").start();
|
|
670
|
+
try {
|
|
671
|
+
const entries = await api.listConfig(linked.appId, opts.env);
|
|
672
|
+
spinner.stop();
|
|
673
|
+
if (entries.length === 0) {
|
|
674
|
+
console.log(import_chalk.default.dim(" No config keys set."));
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
console.log();
|
|
678
|
+
const maxKey = Math.max(...entries.map((e) => e.key.length), 3);
|
|
679
|
+
for (const e of entries) {
|
|
680
|
+
const display = typeof e.value === "string" ? e.value : JSON.stringify(e.value);
|
|
681
|
+
console.log(` ${import_chalk.default.cyan(e.key.padEnd(maxKey))} ${import_chalk.default.white(display)}`);
|
|
682
|
+
}
|
|
683
|
+
console.log();
|
|
684
|
+
} catch (err) {
|
|
685
|
+
spinner.fail(import_chalk.default.red(err instanceof Error ? err.message : String(err)));
|
|
686
|
+
process.exit(1);
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
var getCmd = new import_commander.Command("get").description("Get a remote config value").argument("<key>", "Config key name").option("--env <env>", "Environment (e.g. production, staging)").action(async (key, opts) => {
|
|
690
|
+
requireToken();
|
|
691
|
+
const linked = requireLinkedApp();
|
|
692
|
+
const spinner = (0, import_ora.default)(`Fetching config key '${key}'...`).start();
|
|
693
|
+
try {
|
|
694
|
+
const entry = await api.getConfig(linked.appId, key, opts.env);
|
|
695
|
+
spinner.stop();
|
|
696
|
+
console.log(
|
|
697
|
+
typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value, null, 2)
|
|
698
|
+
);
|
|
699
|
+
} catch (err) {
|
|
700
|
+
spinner.fail(import_chalk.default.red(err instanceof Error ? err.message : String(err)));
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
var setCmd = new import_commander.Command("set").description("Set a remote config key").argument("<assignment>", "KEY=VALUE assignment").option("--env <env>", "Environment (e.g. production, staging)").action(async (assignment, opts) => {
|
|
705
|
+
requireToken();
|
|
706
|
+
const linked = requireLinkedApp();
|
|
707
|
+
const eqIdx = assignment.indexOf("=");
|
|
708
|
+
if (eqIdx === -1) {
|
|
709
|
+
console.log(import_chalk.default.red(" Invalid format. Use KEY=VALUE."));
|
|
710
|
+
process.exit(1);
|
|
711
|
+
}
|
|
712
|
+
const key = assignment.slice(0, eqIdx);
|
|
713
|
+
const value = assignment.slice(eqIdx + 1);
|
|
714
|
+
const spinner = (0, import_ora.default)(`Setting config '${key}'...`).start();
|
|
715
|
+
try {
|
|
716
|
+
await api.setConfig(linked.appId, key, value, opts.env);
|
|
717
|
+
spinner.succeed(import_chalk.default.green(`\u2713 Config '${key}' set`));
|
|
718
|
+
} catch (err) {
|
|
719
|
+
spinner.fail(import_chalk.default.red(err instanceof Error ? err.message : String(err)));
|
|
720
|
+
process.exit(1);
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
var rmCmd = new import_commander.Command("rm").description("Delete a remote config key").argument("<key>", "Config key to delete").option("--env <env>", "Environment (e.g. production, staging)").action(async (key, opts) => {
|
|
724
|
+
requireToken();
|
|
725
|
+
const linked = requireLinkedApp();
|
|
726
|
+
const spinner = (0, import_ora.default)(`Deleting config '${key}'...`).start();
|
|
727
|
+
try {
|
|
728
|
+
await api.deleteConfig(linked.appId, key, opts.env);
|
|
729
|
+
spinner.succeed(import_chalk.default.green(`\u2713 Config '${key}' deleted`));
|
|
730
|
+
} catch (err) {
|
|
731
|
+
spinner.fail(import_chalk.default.red(err instanceof Error ? err.message : String(err)));
|
|
732
|
+
process.exit(1);
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
var configCommand = new import_commander.Command("config").description("Manage remote config (non-secret key-value store)").addCommand(listCmd).addCommand(getCmd).addCommand(setCmd).addCommand(rmCmd).action(() => configCommand.help());
|
|
736
|
+
|
|
352
737
|
// src/commands/db.ts
|
|
738
|
+
var import_node_readline = __toESM(require("readline"));
|
|
739
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
740
|
+
var import_commander2 = require("commander");
|
|
741
|
+
var import_ora2 = __toESM(require("ora"));
|
|
353
742
|
var POLL_INTERVAL_MS = 3e3;
|
|
354
743
|
var POLL_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
355
744
|
function requireAuth() {
|
|
356
745
|
const token = config.getToken();
|
|
357
746
|
if (!token) {
|
|
358
|
-
console.log(
|
|
747
|
+
console.log(import_chalk2.default.red("Not authenticated. Run `sylphx login` first."));
|
|
359
748
|
process.exit(1);
|
|
360
749
|
}
|
|
361
750
|
return token;
|
|
@@ -371,36 +760,36 @@ function prompt(question) {
|
|
|
371
760
|
}
|
|
372
761
|
function printDatabase(db) {
|
|
373
762
|
console.log("");
|
|
374
|
-
console.log(
|
|
375
|
-
console.log(
|
|
376
|
-
console.log(` ID : ${
|
|
763
|
+
console.log(import_chalk2.default.bold(` ${db.name}`));
|
|
764
|
+
console.log(import_chalk2.default.dim(` ${"\u2500".repeat(50)}`));
|
|
765
|
+
console.log(` ID : ${import_chalk2.default.white(db.id)}`);
|
|
377
766
|
console.log(` Status : ${statusColour(db.status)}`);
|
|
378
|
-
console.log(` Tier : ${
|
|
379
|
-
if (db.env) console.log(` Env : ${
|
|
380
|
-
if (db.host) console.log(` Host : ${
|
|
381
|
-
if (db.port) console.log(` Port : ${
|
|
382
|
-
if (db.dbName) console.log(` DB Name : ${
|
|
383
|
-
if (db.dbUser) console.log(` DB User : ${
|
|
384
|
-
if (db.storageGb) console.log(` Storage : ${
|
|
767
|
+
console.log(` Tier : ${import_chalk2.default.white(db.tier)}`);
|
|
768
|
+
if (db.env) console.log(` Env : ${import_chalk2.default.white(db.env)}`);
|
|
769
|
+
if (db.host) console.log(` Host : ${import_chalk2.default.white(db.host)}`);
|
|
770
|
+
if (db.port) console.log(` Port : ${import_chalk2.default.white(String(db.port))}`);
|
|
771
|
+
if (db.dbName) console.log(` DB Name : ${import_chalk2.default.white(db.dbName)}`);
|
|
772
|
+
if (db.dbUser) console.log(` DB User : ${import_chalk2.default.white(db.dbUser)}`);
|
|
773
|
+
if (db.storageGb) console.log(` Storage : ${import_chalk2.default.white(`${db.storageGb} GB`)}`);
|
|
385
774
|
if (db.connectionString) {
|
|
386
775
|
console.log(` Connection :`);
|
|
387
|
-
console.log(` ${
|
|
776
|
+
console.log(` ${import_chalk2.default.green(db.connectionString)}`);
|
|
388
777
|
}
|
|
389
|
-
if (db.createdAt) console.log(
|
|
778
|
+
if (db.createdAt) console.log(import_chalk2.default.dim(` Created : ${db.createdAt}`));
|
|
390
779
|
console.log("");
|
|
391
780
|
}
|
|
392
781
|
function statusColour(status) {
|
|
393
782
|
switch (status) {
|
|
394
783
|
case "ready":
|
|
395
|
-
return
|
|
784
|
+
return import_chalk2.default.green(status);
|
|
396
785
|
case "provisioning":
|
|
397
786
|
case "branching":
|
|
398
|
-
return
|
|
787
|
+
return import_chalk2.default.yellow(status);
|
|
399
788
|
case "error":
|
|
400
789
|
case "failed":
|
|
401
|
-
return
|
|
790
|
+
return import_chalk2.default.red(status);
|
|
402
791
|
default:
|
|
403
|
-
return
|
|
792
|
+
return import_chalk2.default.white(status);
|
|
404
793
|
}
|
|
405
794
|
}
|
|
406
795
|
async function pollUntilReady(id, spinner) {
|
|
@@ -417,152 +806,162 @@ async function pollUntilReady(id, spinner) {
|
|
|
417
806
|
}
|
|
418
807
|
throw new Error("Timed out waiting for database to become ready (2 min).");
|
|
419
808
|
}
|
|
420
|
-
var dbListCommand = new
|
|
809
|
+
var dbListCommand = new import_commander2.Command("list").description("List all databases").action(async () => {
|
|
421
810
|
requireAuth();
|
|
422
|
-
const spinner = (0,
|
|
811
|
+
const spinner = (0, import_ora2.default)("Fetching databases...").start();
|
|
423
812
|
try {
|
|
424
813
|
const dbs = await api.listDatabases();
|
|
425
814
|
spinner.stop();
|
|
426
815
|
if (dbs.length === 0) {
|
|
427
|
-
console.log(
|
|
816
|
+
console.log(import_chalk2.default.dim("\n No databases found.\n"));
|
|
428
817
|
return;
|
|
429
818
|
}
|
|
430
819
|
const colName = Math.max(4, ...dbs.map((d) => d.name.length));
|
|
431
820
|
const colStatus = Math.max(6, ...dbs.map((d) => d.status.length));
|
|
432
821
|
const colTier = Math.max(4, ...dbs.map((d) => d.tier.length));
|
|
433
822
|
console.log("");
|
|
434
|
-
console.log(
|
|
435
|
-
console.log(
|
|
823
|
+
console.log(import_chalk2.default.bold(" Databases"));
|
|
824
|
+
console.log(import_chalk2.default.dim(` ${"\u2500".repeat(colName + colStatus + colTier + 20)}`));
|
|
436
825
|
console.log(
|
|
437
|
-
|
|
826
|
+
import_chalk2.default.dim(
|
|
438
827
|
` ${"NAME".padEnd(colName)} ${"STATUS".padEnd(colStatus)} ${"TIER".padEnd(colTier)} ID`
|
|
439
828
|
)
|
|
440
829
|
);
|
|
441
|
-
console.log(
|
|
830
|
+
console.log(import_chalk2.default.dim(` ${"\u2500".repeat(colName + colStatus + colTier + 20)}`));
|
|
442
831
|
for (const db of dbs) {
|
|
443
832
|
console.log(
|
|
444
|
-
` ${
|
|
833
|
+
` ${import_chalk2.default.cyan(db.name.padEnd(colName))} ${statusColour(db.status).padEnd(colStatus + 10)} ${db.tier.padEnd(colTier)} ${import_chalk2.default.dim(db.id)}`
|
|
445
834
|
);
|
|
446
835
|
}
|
|
447
836
|
console.log("");
|
|
448
837
|
} catch (err) {
|
|
449
|
-
spinner.fail(
|
|
838
|
+
spinner.fail(import_chalk2.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
450
839
|
process.exit(1);
|
|
451
840
|
}
|
|
452
841
|
});
|
|
453
|
-
var dbCreateCommand = new
|
|
842
|
+
var dbCreateCommand = new import_commander2.Command("create").description("Provision a new database").argument("<name>", "Database name").option(
|
|
843
|
+
"--tier <tier>",
|
|
844
|
+
"Database tier: starter | standard | performance | enterprise1 | enterprise2 (default: standard)",
|
|
845
|
+
"standard"
|
|
846
|
+
).option("--storage <gb>", "Storage in GB (default: 10)", "10").option("--env <env>", "Environment (default: production)", "production").action(async (name, opts) => {
|
|
454
847
|
requireAuth();
|
|
455
848
|
const storageGb = Number.parseInt(opts.storage, 10);
|
|
456
849
|
if (Number.isNaN(storageGb) || storageGb < 5) {
|
|
457
|
-
console.log(
|
|
850
|
+
console.log(import_chalk2.default.red("Storage must be at least 5 GB."));
|
|
458
851
|
process.exit(1);
|
|
459
852
|
}
|
|
853
|
+
const VALID_TIERS = ["starter", "standard", "performance", "enterprise1", "enterprise2"];
|
|
460
854
|
const tier = opts.tier === "pro" ? "performance" : opts.tier;
|
|
461
|
-
|
|
462
|
-
|
|
855
|
+
if (!VALID_TIERS.includes(tier)) {
|
|
856
|
+
console.log(import_chalk2.default.red(`Invalid tier '${tier}'. Valid: ${VALID_TIERS.join(", ")}`));
|
|
857
|
+
process.exit(1);
|
|
858
|
+
}
|
|
859
|
+
const spinner = (0, import_ora2.default)(
|
|
860
|
+
`Provisioning database ${import_chalk2.default.bold(name)} (${tier}, ${storageGb} GB)...`
|
|
463
861
|
).start();
|
|
464
862
|
try {
|
|
465
863
|
const db = await api.createDatabase({ name, env: opts.env, tier, storageGb });
|
|
466
864
|
spinner.text = `Waiting for database to become ready...`;
|
|
467
865
|
const ready = await pollUntilReady(db.id, spinner);
|
|
468
|
-
spinner.succeed(
|
|
866
|
+
spinner.succeed(import_chalk2.default.green(`\u2713 Database ${import_chalk2.default.bold(ready.name)} is ready`));
|
|
469
867
|
printDatabase(ready);
|
|
470
868
|
} catch (err) {
|
|
471
|
-
spinner.fail(
|
|
869
|
+
spinner.fail(import_chalk2.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
472
870
|
process.exit(1);
|
|
473
871
|
}
|
|
474
872
|
});
|
|
475
|
-
var dbGetCommand = new
|
|
873
|
+
var dbGetCommand = new import_commander2.Command("get").description("Get database details including connection string").argument("<id>", "Database ID").action(async (id) => {
|
|
476
874
|
requireAuth();
|
|
477
|
-
const spinner = (0,
|
|
875
|
+
const spinner = (0, import_ora2.default)("Fetching database...").start();
|
|
478
876
|
try {
|
|
479
877
|
const db = await api.getDatabase(id);
|
|
480
878
|
spinner.stop();
|
|
481
879
|
printDatabase(db);
|
|
482
880
|
} catch (err) {
|
|
483
|
-
spinner.fail(
|
|
881
|
+
spinner.fail(import_chalk2.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
484
882
|
process.exit(1);
|
|
485
883
|
}
|
|
486
884
|
});
|
|
487
|
-
var dbDeleteCommand = new
|
|
885
|
+
var dbDeleteCommand = new import_commander2.Command("delete").description("Delete a database (irreversible)").argument("<id>", "Database ID").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
|
|
488
886
|
requireAuth();
|
|
489
887
|
if (!opts.force) {
|
|
490
888
|
console.log("");
|
|
491
|
-
console.log(
|
|
492
|
-
console.log(
|
|
889
|
+
console.log(import_chalk2.default.red(` \u26A0 You are about to PERMANENTLY delete database ${import_chalk2.default.bold(id)}.`));
|
|
890
|
+
console.log(import_chalk2.default.red(" This deletes all data, backups, and PVCs. IRREVERSIBLE."));
|
|
493
891
|
console.log("");
|
|
494
892
|
const answer = await prompt(" Type the database ID to confirm");
|
|
495
893
|
if (answer !== id) {
|
|
496
|
-
console.log(
|
|
894
|
+
console.log(import_chalk2.default.red("Confirmation did not match. Aborted."));
|
|
497
895
|
process.exit(1);
|
|
498
896
|
}
|
|
499
897
|
}
|
|
500
|
-
const spinner = (0,
|
|
898
|
+
const spinner = (0, import_ora2.default)(`Deleting database ${import_chalk2.default.bold(id)}...`).start();
|
|
501
899
|
try {
|
|
502
900
|
await api.deleteDatabase(id);
|
|
503
|
-
spinner.succeed(
|
|
901
|
+
spinner.succeed(import_chalk2.default.green(`\u2713 Deleted database ${import_chalk2.default.bold(id)}`));
|
|
504
902
|
} catch (err) {
|
|
505
|
-
spinner.fail(
|
|
903
|
+
spinner.fail(import_chalk2.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
506
904
|
process.exit(1);
|
|
507
905
|
}
|
|
508
906
|
});
|
|
509
|
-
var dbBranchCommand = new
|
|
907
|
+
var dbBranchCommand = new import_commander2.Command("branch").description("Branch a database for staging or preview").argument("<id>", "Source database ID").option("--name <name>", "Branch database name").option("--env <env>", "Target environment (default: staging)", "staging").action(async (id, opts) => {
|
|
510
908
|
requireAuth();
|
|
511
909
|
const branchName = opts.name ?? `${opts.env}-branch`;
|
|
512
|
-
const spinner = (0,
|
|
513
|
-
`Branching database ${
|
|
910
|
+
const spinner = (0, import_ora2.default)(
|
|
911
|
+
`Branching database ${import_chalk2.default.bold(id)} \u2192 ${import_chalk2.default.bold(branchName)}...`
|
|
514
912
|
).start();
|
|
515
913
|
try {
|
|
516
914
|
const db = await api.branchDatabase(id, { name: branchName, env: opts.env, fast: true });
|
|
517
915
|
spinner.text = `Waiting for branch to become ready...`;
|
|
518
916
|
const ready = await pollUntilReady(db.id, spinner);
|
|
519
|
-
spinner.succeed(
|
|
917
|
+
spinner.succeed(import_chalk2.default.green(`\u2713 Branch ${import_chalk2.default.bold(ready.name)} is ready`));
|
|
520
918
|
printDatabase(ready);
|
|
521
919
|
} catch (err) {
|
|
522
|
-
spinner.fail(
|
|
920
|
+
spinner.fail(import_chalk2.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
523
921
|
process.exit(1);
|
|
524
922
|
}
|
|
525
923
|
});
|
|
526
|
-
var dbCommand = new
|
|
924
|
+
var dbCommand = new import_commander2.Command("db").description("Manage PostgreSQL databases").addCommand(dbListCommand).addCommand(dbCreateCommand).addCommand(dbGetCommand).addCommand(dbDeleteCommand).addCommand(dbBranchCommand);
|
|
527
925
|
|
|
528
926
|
// src/commands/deploy.ts
|
|
529
|
-
var
|
|
530
|
-
var
|
|
531
|
-
var
|
|
927
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
928
|
+
var import_commander3 = require("commander");
|
|
929
|
+
var import_ora3 = __toESM(require("ora"));
|
|
532
930
|
|
|
533
931
|
// src/utils/logs.ts
|
|
534
|
-
var
|
|
932
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
933
|
+
var { version: version2 } = package_default;
|
|
535
934
|
function formatLogEntry(raw) {
|
|
536
935
|
let entry;
|
|
537
936
|
try {
|
|
538
937
|
entry = JSON.parse(raw);
|
|
539
938
|
} catch {
|
|
540
|
-
return ` ${
|
|
939
|
+
return ` ${import_chalk3.default.white(raw)}`;
|
|
541
940
|
}
|
|
542
941
|
const message = entry.content ?? entry.message ?? entry.msg ?? raw;
|
|
543
942
|
const level = (entry.level ?? "info").toLowerCase();
|
|
544
943
|
const timestamp = entry.timestamp ?? entry.ts;
|
|
545
|
-
const prefix = timestamp ? `${
|
|
944
|
+
const prefix = timestamp ? `${import_chalk3.default.dim(`[${new Date(timestamp).toLocaleTimeString()}]`)} ` : "";
|
|
546
945
|
const status = entry.status?.toLowerCase();
|
|
547
946
|
const effectiveLevel = status === "error" || status === "failed" ? "error" : status === "success" || status === "done" ? "success" : level;
|
|
548
947
|
switch (effectiveLevel) {
|
|
549
948
|
case "error":
|
|
550
|
-
return ` ${prefix}${
|
|
949
|
+
return ` ${prefix}${import_chalk3.default.red("\u2717")} ${import_chalk3.default.red(message)}`;
|
|
551
950
|
case "warn":
|
|
552
|
-
return ` ${prefix}${
|
|
951
|
+
return ` ${prefix}${import_chalk3.default.yellow("\u26A0")} ${import_chalk3.default.yellow(message)}`;
|
|
553
952
|
case "success":
|
|
554
|
-
return ` ${prefix}${
|
|
953
|
+
return ` ${prefix}${import_chalk3.default.green("\u2713")} ${import_chalk3.default.green(message)}`;
|
|
555
954
|
case "debug":
|
|
556
|
-
return ` ${prefix}${
|
|
955
|
+
return ` ${prefix}${import_chalk3.default.dim(message)}`;
|
|
557
956
|
default:
|
|
558
|
-
return ` ${prefix}${
|
|
957
|
+
return ` ${prefix}${import_chalk3.default.white(message)}`;
|
|
559
958
|
}
|
|
560
959
|
}
|
|
561
960
|
function streamLogs(url, token, opts = {}) {
|
|
562
961
|
return new Promise((resolve) => {
|
|
563
962
|
const EventSource = require("eventsource");
|
|
564
963
|
const headers = {
|
|
565
|
-
"User-Agent":
|
|
964
|
+
"User-Agent": `sylphx-cli/${version2}`,
|
|
566
965
|
Accept: "text/event-stream"
|
|
567
966
|
};
|
|
568
967
|
if (token) {
|
|
@@ -615,7 +1014,7 @@ function streamLogs(url, token, opts = {}) {
|
|
|
615
1014
|
es.addEventListener("error", (event) => {
|
|
616
1015
|
const errorEvent = event;
|
|
617
1016
|
if (errorEvent.message) {
|
|
618
|
-
console.log(
|
|
1017
|
+
console.log(import_chalk3.default.red(` Stream error: ${errorEvent.message}`));
|
|
619
1018
|
}
|
|
620
1019
|
if (opts.exitOnDone) {
|
|
621
1020
|
finish(succeeded);
|
|
@@ -631,40 +1030,40 @@ function streamLogs(url, token, opts = {}) {
|
|
|
631
1030
|
}
|
|
632
1031
|
|
|
633
1032
|
// src/commands/deploy.ts
|
|
634
|
-
var deployCommand = new
|
|
1033
|
+
var deployCommand = new import_commander3.Command("deploy").description("Trigger a deployment for the linked app").option("--env <env>", "Environment to deploy to (e.g. production, staging)").action(async (opts) => {
|
|
635
1034
|
const token = config.getToken();
|
|
636
1035
|
if (!token) {
|
|
637
|
-
console.log(
|
|
1036
|
+
console.log(import_chalk4.default.red("Not authenticated. Run `sylphx login` first."));
|
|
638
1037
|
process.exit(1);
|
|
639
1038
|
}
|
|
640
1039
|
const linked = config.getLinkedApp();
|
|
641
1040
|
if (!linked) {
|
|
642
|
-
console.log(
|
|
1041
|
+
console.log(import_chalk4.default.red("No app linked. Run `sylphx link` first."));
|
|
643
1042
|
process.exit(1);
|
|
644
1043
|
}
|
|
645
1044
|
const env = opts.env ?? linked.defaultEnv;
|
|
646
1045
|
console.log("");
|
|
647
|
-
console.log(
|
|
1046
|
+
console.log(import_chalk4.default.bold(` Deploying ${import_chalk4.default.cyan(linked.appId)} \u2192 ${import_chalk4.default.white(env)}`));
|
|
648
1047
|
console.log("");
|
|
649
|
-
const spinner = (0,
|
|
1048
|
+
const spinner = (0, import_ora3.default)("Triggering deployment...").start();
|
|
650
1049
|
let deploymentId;
|
|
651
1050
|
try {
|
|
652
1051
|
const result = await api.triggerDeploy(linked.appId, env);
|
|
653
|
-
spinner.succeed(
|
|
1052
|
+
spinner.succeed(import_chalk4.default.green(`Deployment triggered: ${result.status}`));
|
|
654
1053
|
deploymentId = result.deploymentId;
|
|
655
1054
|
if (deploymentId) {
|
|
656
|
-
console.log(
|
|
1055
|
+
console.log(import_chalk4.default.dim(` Deployment ID: ${deploymentId}`));
|
|
657
1056
|
}
|
|
658
1057
|
if (result.environment) {
|
|
659
|
-
console.log(
|
|
1058
|
+
console.log(import_chalk4.default.dim(` Environment: ${result.environment}`));
|
|
660
1059
|
}
|
|
661
1060
|
} catch (err) {
|
|
662
|
-
spinner.fail(
|
|
1061
|
+
spinner.fail(import_chalk4.default.red(`Deploy failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
663
1062
|
process.exit(1);
|
|
664
1063
|
}
|
|
665
1064
|
console.log("");
|
|
666
|
-
console.log(
|
|
667
|
-
console.log(
|
|
1065
|
+
console.log(import_chalk4.default.bold(" Build Logs"));
|
|
1066
|
+
console.log(import_chalk4.default.dim(` ${"\u2500".repeat(50)}`));
|
|
668
1067
|
console.log("");
|
|
669
1068
|
const logStream = createLogStream(linked.appId, env);
|
|
670
1069
|
const success = await streamLogs(logStream.url, logStream.token, {
|
|
@@ -673,140 +1072,387 @@ var deployCommand = new import_commander2.Command("deploy").description("Trigger
|
|
|
673
1072
|
});
|
|
674
1073
|
if (!success) {
|
|
675
1074
|
console.log("");
|
|
676
|
-
console.log(
|
|
1075
|
+
console.log(import_chalk4.default.red(" Deployment failed."));
|
|
677
1076
|
process.exit(1);
|
|
678
1077
|
}
|
|
679
1078
|
console.log("");
|
|
680
|
-
console.log(
|
|
1079
|
+
console.log(import_chalk4.default.green.bold(" \u2713 Deployment successful!"));
|
|
681
1080
|
console.log("");
|
|
682
1081
|
});
|
|
683
1082
|
|
|
684
1083
|
// src/commands/domains.ts
|
|
685
|
-
var
|
|
686
|
-
var
|
|
687
|
-
var
|
|
688
|
-
function
|
|
1084
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
1085
|
+
var import_commander4 = require("commander");
|
|
1086
|
+
var import_ora4 = __toESM(require("ora"));
|
|
1087
|
+
function requireLinkedApp2() {
|
|
689
1088
|
const token = config.getToken();
|
|
690
1089
|
if (!token) {
|
|
691
|
-
console.
|
|
1090
|
+
console.error(import_chalk5.default.red("Not authenticated. Run `sylphx login` first."));
|
|
692
1091
|
process.exit(1);
|
|
693
1092
|
}
|
|
694
1093
|
const linked = config.getLinkedApp();
|
|
695
1094
|
if (!linked) {
|
|
696
|
-
console.
|
|
1095
|
+
console.error(import_chalk5.default.red("No project linked. Run `sylphx link` first."));
|
|
697
1096
|
process.exit(1);
|
|
698
1097
|
}
|
|
699
1098
|
return linked;
|
|
700
1099
|
}
|
|
701
|
-
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
1100
|
+
function extractApex(hostname) {
|
|
1101
|
+
const parts = hostname.split(".");
|
|
1102
|
+
return parts.length > 2 ? parts.slice(-2).join(".") : hostname;
|
|
1103
|
+
}
|
|
1104
|
+
function findDomainByApex(domains, apex) {
|
|
1105
|
+
return domains.find((d) => d.apexDomain === apex);
|
|
1106
|
+
}
|
|
1107
|
+
function findHostname(domain, hostname) {
|
|
1108
|
+
return domain.hostnames.find((h) => h.hostname === hostname);
|
|
1109
|
+
}
|
|
1110
|
+
function dnsStatusBadge(status) {
|
|
1111
|
+
switch (status) {
|
|
1112
|
+
case "active":
|
|
1113
|
+
case "verified":
|
|
1114
|
+
return import_chalk5.default.green("\u2713 active");
|
|
1115
|
+
case "verifying":
|
|
1116
|
+
return import_chalk5.default.yellow("\u27F3 verifying");
|
|
1117
|
+
case "pending":
|
|
1118
|
+
return import_chalk5.default.dim("\u25CB pending");
|
|
1119
|
+
case "error":
|
|
1120
|
+
case "failed":
|
|
1121
|
+
return import_chalk5.default.red("\u2717 failed");
|
|
1122
|
+
default:
|
|
1123
|
+
return import_chalk5.default.dim(status);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
function printDnsRecord(record) {
|
|
1127
|
+
const statusBadge = record.status ? ` ${dnsStatusBadge(record.status)}` : "";
|
|
1128
|
+
console.log(import_chalk5.default.dim(` ${record.type.padEnd(5)} ${import_chalk5.default.cyan(record.name)}`));
|
|
1129
|
+
console.log(import_chalk5.default.dim(` \u2192 ${record.value}${statusBadge}`));
|
|
1130
|
+
if (record.description) console.log(import_chalk5.default.dim(` ${import_chalk5.default.italic(record.description)}`));
|
|
1131
|
+
}
|
|
1132
|
+
function printHostname(h, indent = " ") {
|
|
1133
|
+
const primary = h.isPrimary ? import_chalk5.default.green(" [primary]") : "";
|
|
1134
|
+
const redirect = h.redirectTo ? import_chalk5.default.dim(` \u2192 ${h.redirectTo}`) : "";
|
|
1135
|
+
console.log(
|
|
1136
|
+
`${indent}${import_chalk5.default.bold(h.hostname)}${primary}${redirect} ${dnsStatusBadge(h.dnsStatus)}`
|
|
1137
|
+
);
|
|
1138
|
+
if (h.dnsStatus !== "active") {
|
|
1139
|
+
console.log(
|
|
1140
|
+
`${indent} ${import_chalk5.default.dim(h.instruction.type)} record: ${import_chalk5.default.cyan(h.instruction.name)} \u2192 ${h.instruction.value}`
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
if (h.lastCheckError) {
|
|
1144
|
+
console.log(`${indent} ${import_chalk5.default.red(h.lastCheckError)}`);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
function printEmailBinding(email, indent = " ") {
|
|
1148
|
+
console.log(
|
|
1149
|
+
`${indent}Email: ${dnsStatusBadge(email.dnsStatus)}${email.stalwartLoaded ? import_chalk5.default.dim(" (DKIM loaded)") : ""}`
|
|
1150
|
+
);
|
|
1151
|
+
if (email.defaultFromEmail) {
|
|
1152
|
+
console.log(
|
|
1153
|
+
`${indent} From: ${email.defaultFromEmail}${email.defaultFromName ? ` <${email.defaultFromName}>` : ""}`
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
if (email.dnsStatus !== "verified" && email.records.length > 0) {
|
|
1157
|
+
console.log(`${indent} ${import_chalk5.default.dim("DNS records to add:")}`);
|
|
1158
|
+
for (const rec of email.records) {
|
|
1159
|
+
printDnsRecord({ ...rec, status: rec.status ?? void 0 });
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
function printDomainResult(d) {
|
|
1164
|
+
console.log("");
|
|
1165
|
+
console.log(` ${import_chalk5.default.bold.white(d.apexDomain)}`);
|
|
1166
|
+
if (d.hostnames.length === 0) {
|
|
1167
|
+
console.log(import_chalk5.default.dim(" No hostname bindings"));
|
|
1168
|
+
} else {
|
|
1169
|
+
for (const h of d.hostnames) {
|
|
1170
|
+
printHostname(h, " ");
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
if (d.email) {
|
|
1174
|
+
printEmailBinding(d.email, " ");
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
var domainsListCommand = new import_commander4.Command("list").description("List all domains and their status").option("--env <env>", "Environment (production, staging, development)", "production").action(async (opts) => {
|
|
1178
|
+
const linked = requireLinkedApp2();
|
|
1179
|
+
const spinner = (0, import_ora4.default)("Fetching domains...").start();
|
|
705
1180
|
try {
|
|
706
|
-
const domains = await api.listDomains(linked.appId, env);
|
|
1181
|
+
const domains = await api.listDomains(linked.appId, opts.env);
|
|
707
1182
|
spinner.stop();
|
|
708
1183
|
if (domains.length === 0) {
|
|
709
|
-
console.log(
|
|
1184
|
+
console.log(import_chalk5.default.dim("\n No domains configured. Run `sylphx domains add <hostname>`\n"));
|
|
710
1185
|
return;
|
|
711
1186
|
}
|
|
712
1187
|
console.log("");
|
|
713
|
-
console.log(
|
|
714
|
-
console.log(
|
|
715
|
-
console.log("");
|
|
1188
|
+
console.log(import_chalk5.default.bold(` Domains [${import_chalk5.default.white(opts.env)}]`));
|
|
1189
|
+
console.log(import_chalk5.default.dim(` ${"\u2500".repeat(50)}`));
|
|
716
1190
|
for (const d of domains) {
|
|
717
|
-
|
|
718
|
-
const ssl = d.ssl ? import_chalk4.default.green(" \u{1F512}") : import_chalk4.default.yellow(" \u26A0 no SSL");
|
|
719
|
-
console.log(` ${import_chalk4.default.cyan(d.domain)}${status}${ssl}`);
|
|
1191
|
+
printDomainResult(d);
|
|
720
1192
|
}
|
|
721
1193
|
console.log("");
|
|
722
1194
|
} catch (err) {
|
|
723
|
-
spinner.fail(
|
|
1195
|
+
spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
724
1196
|
process.exit(1);
|
|
725
1197
|
}
|
|
726
1198
|
});
|
|
727
|
-
var domainsAddCommand = new
|
|
728
|
-
const linked =
|
|
729
|
-
const
|
|
730
|
-
const spinner = (0,
|
|
1199
|
+
var domainsAddCommand = new import_commander4.Command("add").description("Add a hostname binding (auto-registers apex domain if needed)").argument("<hostname>", "Hostname to add (e.g. app.example.com or example.com)").option("--env <env>", "Environment", "production").option("--primary", "Set as primary domain", false).option("--redirect <url>", "Redirect to this URL instead of serving traffic").action(async (hostname, opts) => {
|
|
1200
|
+
const linked = requireLinkedApp2();
|
|
1201
|
+
const apex = extractApex(hostname);
|
|
1202
|
+
const spinner = (0, import_ora4.default)(`Setting up ${hostname}...`).start();
|
|
731
1203
|
try {
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
if (
|
|
735
|
-
|
|
1204
|
+
const domains = await api.listDomains(linked.appId, opts.env);
|
|
1205
|
+
let domain = findDomainByApex(domains, apex);
|
|
1206
|
+
if (!domain) {
|
|
1207
|
+
spinner.text = `Registering apex domain ${apex}...`;
|
|
1208
|
+
domain = await api.createDomain(linked.appId, apex, opts.env);
|
|
736
1209
|
}
|
|
1210
|
+
spinner.text = `Adding hostname binding ${hostname}...`;
|
|
1211
|
+
const h = await api.addHostname(linked.appId, domain.id, hostname, {
|
|
1212
|
+
isPrimary: opts.primary,
|
|
1213
|
+
redirectTo: opts.redirect,
|
|
1214
|
+
envType: opts.env
|
|
1215
|
+
});
|
|
1216
|
+
spinner.succeed(import_chalk5.default.green(`Added ${import_chalk5.default.bold(hostname)}`));
|
|
1217
|
+
console.log("");
|
|
1218
|
+
console.log(import_chalk5.default.bold(" DNS Record to add:"));
|
|
1219
|
+
console.log("");
|
|
1220
|
+
console.log(
|
|
1221
|
+
` ${import_chalk5.default.dim(h.instruction.type.padEnd(5))} ${import_chalk5.default.cyan(h.instruction.name)}`
|
|
1222
|
+
);
|
|
1223
|
+
console.log(` ${import_chalk5.default.dim("value:")} ${h.instruction.value}`);
|
|
1224
|
+
console.log(` ${import_chalk5.default.dim("TTL:")} ${h.instruction.ttl}`);
|
|
1225
|
+
console.log("");
|
|
1226
|
+
console.log(import_chalk5.default.dim(` Status: ${dnsStatusBadge(h.dnsStatus)}`));
|
|
1227
|
+
console.log(import_chalk5.default.dim(` Run \`sylphx domains verify ${hostname}\` after updating DNS.`));
|
|
1228
|
+
console.log("");
|
|
737
1229
|
} catch (err) {
|
|
738
|
-
spinner.fail(
|
|
1230
|
+
spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
739
1231
|
process.exit(1);
|
|
740
1232
|
}
|
|
741
1233
|
});
|
|
742
|
-
var domainsRmCommand = new
|
|
743
|
-
const linked =
|
|
744
|
-
const
|
|
745
|
-
const spinner = (0,
|
|
1234
|
+
var domainsRmCommand = new import_commander4.Command("rm").description("Remove a hostname binding").argument("<hostname>", "Hostname to remove").option("--env <env>", "Environment", "production").action(async (hostname, opts) => {
|
|
1235
|
+
const linked = requireLinkedApp2();
|
|
1236
|
+
const apex = extractApex(hostname);
|
|
1237
|
+
const spinner = (0, import_ora4.default)(`Removing ${hostname}...`).start();
|
|
1238
|
+
try {
|
|
1239
|
+
const domains = await api.listDomains(linked.appId, opts.env);
|
|
1240
|
+
const domain = findDomainByApex(domains, apex);
|
|
1241
|
+
if (!domain) {
|
|
1242
|
+
spinner.fail(import_chalk5.default.red(`Domain ${apex} not found`));
|
|
1243
|
+
process.exit(1);
|
|
1244
|
+
}
|
|
1245
|
+
const h = findHostname(domain, hostname);
|
|
1246
|
+
if (!h) {
|
|
1247
|
+
spinner.fail(import_chalk5.default.red(`Hostname ${hostname} not found`));
|
|
1248
|
+
process.exit(1);
|
|
1249
|
+
}
|
|
1250
|
+
await api.removeHostname(linked.appId, domain.id, h.id);
|
|
1251
|
+
spinner.succeed(import_chalk5.default.green(`Removed ${import_chalk5.default.bold(hostname)}`));
|
|
1252
|
+
} catch (err) {
|
|
1253
|
+
spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1254
|
+
process.exit(1);
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
var domainsVerifyCommand = new import_commander4.Command("verify").description("Check DNS propagation for a hostname").argument("<hostname>", "Hostname to check").option("--env <env>", "Environment", "production").action(async (hostname, opts) => {
|
|
1258
|
+
const linked = requireLinkedApp2();
|
|
1259
|
+
const apex = extractApex(hostname);
|
|
1260
|
+
const spinner = (0, import_ora4.default)(`Checking DNS for ${hostname}...`).start();
|
|
1261
|
+
try {
|
|
1262
|
+
const domains = await api.listDomains(linked.appId, opts.env);
|
|
1263
|
+
const domain = findDomainByApex(domains, apex);
|
|
1264
|
+
if (!domain) {
|
|
1265
|
+
spinner.fail(
|
|
1266
|
+
import_chalk5.default.red(`Domain ${apex} not found. Run \`sylphx domains add ${hostname}\` first.`)
|
|
1267
|
+
);
|
|
1268
|
+
process.exit(1);
|
|
1269
|
+
}
|
|
1270
|
+
const h = findHostname(domain, hostname);
|
|
1271
|
+
if (!h) {
|
|
1272
|
+
spinner.fail(
|
|
1273
|
+
import_chalk5.default.red(
|
|
1274
|
+
`Hostname ${hostname} not registered. Run \`sylphx domains add ${hostname}\` first.`
|
|
1275
|
+
)
|
|
1276
|
+
);
|
|
1277
|
+
process.exit(1);
|
|
1278
|
+
}
|
|
1279
|
+
const updated = await api.checkHostname(linked.appId, domain.id, h.id);
|
|
1280
|
+
spinner.stop();
|
|
1281
|
+
if (updated.dnsStatus === "active") {
|
|
1282
|
+
console.log(import_chalk5.default.green(`
|
|
1283
|
+
\u2713 ${hostname} is active
|
|
1284
|
+
`));
|
|
1285
|
+
} else {
|
|
1286
|
+
console.log(import_chalk5.default.yellow(`
|
|
1287
|
+
\u26A0 ${hostname} \u2014 ${dnsStatusBadge(updated.dnsStatus)}`));
|
|
1288
|
+
if (updated.lastCheckError) console.log(import_chalk5.default.dim(` ${updated.lastCheckError}`));
|
|
1289
|
+
console.log("");
|
|
1290
|
+
console.log(import_chalk5.default.bold(" DNS record required:"));
|
|
1291
|
+
console.log(
|
|
1292
|
+
` ${updated.instruction.type} ${import_chalk5.default.cyan(updated.instruction.name)} \u2192 ${updated.instruction.value}`
|
|
1293
|
+
);
|
|
1294
|
+
console.log("");
|
|
1295
|
+
}
|
|
1296
|
+
} catch (err) {
|
|
1297
|
+
spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1298
|
+
process.exit(1);
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
var emailEnableCommand = new import_commander4.Command("enable").description("Enable DKIM email sending for a domain").argument("<apex-domain>", "Apex domain to enable email for (e.g. example.com)").option("--from <email>", "Default from address (e.g. support@example.com)").option("--name <name>", 'Default sender display name (e.g. "Acme Support")').option("--env <env>", "Environment", "production").action(async (apexDomain, opts) => {
|
|
1302
|
+
const linked = requireLinkedApp2();
|
|
1303
|
+
const spinner = (0, import_ora4.default)(`Enabling email for ${apexDomain}...`).start();
|
|
1304
|
+
try {
|
|
1305
|
+
const domains = await api.listDomains(linked.appId, opts.env);
|
|
1306
|
+
let domain = findDomainByApex(domains, apexDomain);
|
|
1307
|
+
if (!domain) {
|
|
1308
|
+
spinner.text = `Registering ${apexDomain}...`;
|
|
1309
|
+
domain = await api.createDomain(linked.appId, apexDomain, opts.env);
|
|
1310
|
+
}
|
|
1311
|
+
if (domain.email) {
|
|
1312
|
+
spinner.warn(
|
|
1313
|
+
import_chalk5.default.yellow(
|
|
1314
|
+
`Email already enabled for ${apexDomain}. Use \`sylphx domains email verify\` to check status.`
|
|
1315
|
+
)
|
|
1316
|
+
);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const binding = await api.enableEmail(linked.appId, domain.id, {
|
|
1320
|
+
defaultFromEmail: opts.from,
|
|
1321
|
+
defaultFromName: opts.name
|
|
1322
|
+
});
|
|
1323
|
+
spinner.succeed(import_chalk5.default.green(`Email enabled for ${import_chalk5.default.bold(apexDomain)}`));
|
|
1324
|
+
console.log("");
|
|
1325
|
+
console.log(import_chalk5.default.bold(" DNS Records to add:"));
|
|
1326
|
+
console.log("");
|
|
1327
|
+
for (const rec of binding.records) {
|
|
1328
|
+
console.log(` ${import_chalk5.default.dim(rec.type.padEnd(5))} ${import_chalk5.default.cyan(rec.name)}`);
|
|
1329
|
+
console.log(` ${import_chalk5.default.dim("value:")} ${rec.value}`);
|
|
1330
|
+
if (rec.priority !== void 0) console.log(` ${import_chalk5.default.dim("priority:")} ${rec.priority}`);
|
|
1331
|
+
console.log(` ${import_chalk5.default.dim("TTL:")} ${rec.ttl}`);
|
|
1332
|
+
if (rec.description) console.log(` ${import_chalk5.default.dim(rec.description)}`);
|
|
1333
|
+
console.log("");
|
|
1334
|
+
}
|
|
1335
|
+
console.log(
|
|
1336
|
+
import_chalk5.default.dim(` Run \`sylphx domains email verify ${apexDomain}\` after updating DNS.`)
|
|
1337
|
+
);
|
|
1338
|
+
console.log("");
|
|
1339
|
+
} catch (err) {
|
|
1340
|
+
spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1341
|
+
process.exit(1);
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
var emailVerifyCommand = new import_commander4.Command("verify").description("Check DKIM DNS propagation").argument("<apex-domain>", "Apex domain to verify").option("--env <env>", "Environment", "production").action(async (apexDomain, opts) => {
|
|
1345
|
+
const linked = requireLinkedApp2();
|
|
1346
|
+
const spinner = (0, import_ora4.default)(`Checking DKIM DNS for ${apexDomain}...`).start();
|
|
1347
|
+
try {
|
|
1348
|
+
const domains = await api.listDomains(linked.appId, opts.env);
|
|
1349
|
+
const domain = findDomainByApex(domains, apexDomain);
|
|
1350
|
+
if (!domain?.email) {
|
|
1351
|
+
spinner.fail(
|
|
1352
|
+
import_chalk5.default.red(
|
|
1353
|
+
`Email not enabled for ${apexDomain}. Run \`sylphx domains email enable ${apexDomain}\` first.`
|
|
1354
|
+
)
|
|
1355
|
+
);
|
|
1356
|
+
process.exit(1);
|
|
1357
|
+
}
|
|
1358
|
+
const binding = await api.checkEmail(linked.appId, domain.id);
|
|
1359
|
+
spinner.stop();
|
|
1360
|
+
if (binding.dnsStatus === "verified") {
|
|
1361
|
+
console.log(import_chalk5.default.green(`
|
|
1362
|
+
\u2713 ${apexDomain} email verified \u2014 DKIM active
|
|
1363
|
+
`));
|
|
1364
|
+
} else {
|
|
1365
|
+
console.log(import_chalk5.default.yellow(`
|
|
1366
|
+
${apexDomain} \u2014 ${dnsStatusBadge(binding.dnsStatus)}`));
|
|
1367
|
+
if (!binding.stalwartLoaded)
|
|
1368
|
+
console.log(import_chalk5.default.dim(" (DKIM not yet loaded into mail server)"));
|
|
1369
|
+
if (binding.verificationError) console.log(import_chalk5.default.red(` ${binding.verificationError}`));
|
|
1370
|
+
console.log("");
|
|
1371
|
+
console.log(import_chalk5.default.bold(" DNS records still pending:"));
|
|
1372
|
+
for (const rec of binding.records.filter((r) => r.status !== "verified")) {
|
|
1373
|
+
printDnsRecord({ ...rec, status: rec.status ?? void 0 });
|
|
1374
|
+
}
|
|
1375
|
+
console.log("");
|
|
1376
|
+
}
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1379
|
+
process.exit(1);
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
var emailDisableCommand = new import_commander4.Command("disable").description("Disable email sending for a domain").argument("<apex-domain>", "Apex domain").option("--env <env>", "Environment", "production").action(async (apexDomain, opts) => {
|
|
1383
|
+
const linked = requireLinkedApp2();
|
|
1384
|
+
const spinner = (0, import_ora4.default)(`Disabling email for ${apexDomain}...`).start();
|
|
746
1385
|
try {
|
|
747
|
-
await api.
|
|
748
|
-
|
|
1386
|
+
const domains = await api.listDomains(linked.appId, opts.env);
|
|
1387
|
+
const domain = findDomainByApex(domains, apexDomain);
|
|
1388
|
+
if (!domain?.email) {
|
|
1389
|
+
spinner.fail(import_chalk5.default.red(`Email not enabled for ${apexDomain}`));
|
|
1390
|
+
process.exit(1);
|
|
1391
|
+
}
|
|
1392
|
+
await api.disableEmail(linked.appId, domain.id);
|
|
1393
|
+
spinner.succeed(import_chalk5.default.green(`Email disabled for ${import_chalk5.default.bold(apexDomain)}`));
|
|
749
1394
|
} catch (err) {
|
|
750
|
-
spinner.fail(
|
|
1395
|
+
spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
751
1396
|
process.exit(1);
|
|
752
1397
|
}
|
|
753
1398
|
});
|
|
754
|
-
var
|
|
1399
|
+
var domainsEmailCommand = new import_commander4.Command("email").description("Manage email sending for a domain").addCommand(emailEnableCommand).addCommand(emailVerifyCommand).addCommand(emailDisableCommand);
|
|
1400
|
+
var domainsCommand = new import_commander4.Command("domains").description("Manage custom domains").addCommand(domainsListCommand).addCommand(domainsAddCommand).addCommand(domainsRmCommand).addCommand(domainsVerifyCommand).addCommand(domainsEmailCommand);
|
|
755
1401
|
|
|
756
1402
|
// src/commands/env.ts
|
|
757
1403
|
var import_node_fs = __toESM(require("fs"));
|
|
758
1404
|
var import_node_path2 = __toESM(require("path"));
|
|
759
|
-
var
|
|
760
|
-
var
|
|
761
|
-
var
|
|
762
|
-
function
|
|
1405
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
1406
|
+
var import_commander5 = require("commander");
|
|
1407
|
+
var import_ora5 = __toESM(require("ora"));
|
|
1408
|
+
function requireLinkedApp3() {
|
|
763
1409
|
const token = config.getToken();
|
|
764
1410
|
if (!token) {
|
|
765
|
-
console.log(
|
|
1411
|
+
console.log(import_chalk6.default.red("Not authenticated. Run `sylphx login` first."));
|
|
766
1412
|
process.exit(1);
|
|
767
1413
|
}
|
|
768
1414
|
const linked = config.getLinkedApp();
|
|
769
1415
|
if (!linked) {
|
|
770
|
-
console.log(
|
|
1416
|
+
console.log(import_chalk6.default.red("No app linked. Run `sylphx link` first."));
|
|
771
1417
|
process.exit(1);
|
|
772
1418
|
}
|
|
773
1419
|
return linked;
|
|
774
1420
|
}
|
|
775
|
-
var envListCommand = new
|
|
776
|
-
const linked =
|
|
1421
|
+
var envListCommand = new import_commander5.Command("list").description("List environment variables").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
|
|
1422
|
+
const linked = requireLinkedApp3();
|
|
777
1423
|
const env = opts.env ?? linked.defaultEnv;
|
|
778
|
-
const spinner = (0,
|
|
1424
|
+
const spinner = (0, import_ora5.default)(`Fetching env vars [${env}]...`).start();
|
|
779
1425
|
try {
|
|
780
1426
|
const vars = await api.listEnvVars(linked.appId, env);
|
|
781
1427
|
spinner.stop();
|
|
782
1428
|
if (vars.length === 0) {
|
|
783
|
-
console.log(
|
|
1429
|
+
console.log(import_chalk6.default.dim("\n No environment variables set.\n"));
|
|
784
1430
|
return;
|
|
785
1431
|
}
|
|
786
1432
|
console.log("");
|
|
787
|
-
console.log(
|
|
788
|
-
console.log(
|
|
1433
|
+
console.log(import_chalk6.default.bold(` Environment Variables [${import_chalk6.default.white(env)}]`));
|
|
1434
|
+
console.log(import_chalk6.default.dim(` ${"\u2500".repeat(50)}`));
|
|
789
1435
|
console.log("");
|
|
790
1436
|
const maxKeyLen = Math.max(...vars.map((v) => v.key.length));
|
|
791
1437
|
for (const v of vars) {
|
|
792
|
-
const key =
|
|
1438
|
+
const key = import_chalk6.default.cyan(v.key.padEnd(maxKeyLen));
|
|
793
1439
|
const isSecret = v.isSecret ?? v.secret;
|
|
794
|
-
const value = isSecret ?
|
|
1440
|
+
const value = isSecret ? import_chalk6.default.dim("****** (secret)") : import_chalk6.default.white(v.value);
|
|
795
1441
|
console.log(` ${key} ${value}`);
|
|
796
1442
|
}
|
|
797
1443
|
console.log("");
|
|
798
1444
|
} catch (err) {
|
|
799
|
-
spinner.fail(
|
|
1445
|
+
spinner.fail(import_chalk6.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
800
1446
|
process.exit(1);
|
|
801
1447
|
}
|
|
802
1448
|
});
|
|
803
|
-
var envSetCommand = new
|
|
804
|
-
const linked =
|
|
1449
|
+
var envSetCommand = new import_commander5.Command("set").description("Set an environment variable (KEY=VALUE)").argument("<assignment>", "Key=Value assignment, e.g. DATABASE_URL=postgres://...").option("--env <env>", "Environment (e.g. production, staging)").option("--secret", "Mark as secret").action(async (assignment, opts) => {
|
|
1450
|
+
const linked = requireLinkedApp3();
|
|
805
1451
|
const env = opts.env ?? linked.defaultEnv;
|
|
806
1452
|
const eqIdx = assignment.indexOf("=");
|
|
807
1453
|
if (eqIdx < 1) {
|
|
808
1454
|
console.log(
|
|
809
|
-
|
|
1455
|
+
import_chalk6.default.red(
|
|
810
1456
|
"Invalid format. Use KEY=VALUE (e.g. sylphx env set DATABASE_URL=postgres://...)"
|
|
811
1457
|
)
|
|
812
1458
|
);
|
|
@@ -815,40 +1461,40 @@ var envSetCommand = new import_commander4.Command("set").description("Set an env
|
|
|
815
1461
|
const key = assignment.slice(0, eqIdx).trim();
|
|
816
1462
|
const value = assignment.slice(eqIdx + 1);
|
|
817
1463
|
if (!key) {
|
|
818
|
-
console.log(
|
|
1464
|
+
console.log(import_chalk6.default.red("Key cannot be empty."));
|
|
819
1465
|
process.exit(1);
|
|
820
1466
|
}
|
|
821
|
-
const spinner = (0,
|
|
1467
|
+
const spinner = (0, import_ora5.default)(`Setting ${key} [${env}]...`).start();
|
|
822
1468
|
try {
|
|
823
1469
|
await api.setEnvVar(linked.appId, key, value, env, opts.secret);
|
|
824
|
-
spinner.succeed(
|
|
1470
|
+
spinner.succeed(import_chalk6.default.green(`\u2713 Set ${import_chalk6.default.bold(key)} in ${env}`));
|
|
825
1471
|
} catch (err) {
|
|
826
|
-
spinner.fail(
|
|
1472
|
+
spinner.fail(import_chalk6.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
827
1473
|
process.exit(1);
|
|
828
1474
|
}
|
|
829
1475
|
});
|
|
830
|
-
var envRmCommand = new
|
|
831
|
-
const linked =
|
|
1476
|
+
var envRmCommand = new import_commander5.Command("rm").description("Remove an environment variable").argument("<key>", "Variable name to remove").option("--env <env>", "Environment (e.g. production, staging)").action(async (key, opts) => {
|
|
1477
|
+
const linked = requireLinkedApp3();
|
|
832
1478
|
const env = opts.env ?? linked.defaultEnv;
|
|
833
|
-
const spinner = (0,
|
|
1479
|
+
const spinner = (0, import_ora5.default)(`Removing ${key} [${env}]...`).start();
|
|
834
1480
|
try {
|
|
835
1481
|
await api.deleteEnvVar(linked.appId, key, env);
|
|
836
|
-
spinner.succeed(
|
|
1482
|
+
spinner.succeed(import_chalk6.default.green(`\u2713 Removed ${import_chalk6.default.bold(key)} from ${env}`));
|
|
837
1483
|
} catch (err) {
|
|
838
|
-
spinner.fail(
|
|
1484
|
+
spinner.fail(import_chalk6.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
839
1485
|
process.exit(1);
|
|
840
1486
|
}
|
|
841
1487
|
});
|
|
842
|
-
var envPullCommand = new
|
|
843
|
-
const linked =
|
|
1488
|
+
var envPullCommand = new import_commander5.Command("pull").description("Download env vars from the platform to a local .env file").option("--env <env>", "Environment (e.g. production, staging)").option("--file <file>", ".env file path", ".env").action(async (opts) => {
|
|
1489
|
+
const linked = requireLinkedApp3();
|
|
844
1490
|
const env = opts.env ?? linked.defaultEnv;
|
|
845
1491
|
const filePath = import_node_path2.default.resolve(opts.file);
|
|
846
|
-
const spinner = (0,
|
|
1492
|
+
const spinner = (0, import_ora5.default)(`Pulling env vars [${env}]...`).start();
|
|
847
1493
|
try {
|
|
848
1494
|
const vars = await api.listEnvVars(linked.appId, env);
|
|
849
1495
|
spinner.stop();
|
|
850
1496
|
if (vars.length === 0) {
|
|
851
|
-
console.log(
|
|
1497
|
+
console.log(import_chalk6.default.dim("\n No environment variables to pull.\n"));
|
|
852
1498
|
return;
|
|
853
1499
|
}
|
|
854
1500
|
const lines = [
|
|
@@ -871,19 +1517,19 @@ var envPullCommand = new import_commander4.Command("pull").description("Download
|
|
|
871
1517
|
}
|
|
872
1518
|
lines.push("");
|
|
873
1519
|
import_node_fs.default.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
874
|
-
console.log(
|
|
875
|
-
console.log(
|
|
1520
|
+
console.log(import_chalk6.default.green(`\u2713 Wrote ${vars.length} variable(s) to ${import_chalk6.default.bold(opts.file)}`));
|
|
1521
|
+
console.log(import_chalk6.default.dim(` Secrets are shown as ${import_chalk6.default.white("KEY=")} (value omitted).`));
|
|
876
1522
|
} catch (err) {
|
|
877
|
-
spinner.fail(
|
|
1523
|
+
spinner.fail(import_chalk6.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
878
1524
|
process.exit(1);
|
|
879
1525
|
}
|
|
880
1526
|
});
|
|
881
|
-
var envPushCommand = new
|
|
882
|
-
const linked =
|
|
1527
|
+
var envPushCommand = new import_commander5.Command("push").description("Push a local .env file to the platform").option("--env <env>", "Environment (e.g. production, staging)").option("--file <file>", ".env file path", ".env").action(async (opts) => {
|
|
1528
|
+
const linked = requireLinkedApp3();
|
|
883
1529
|
const env = opts.env ?? linked.defaultEnv;
|
|
884
1530
|
const filePath = import_node_path2.default.resolve(opts.file);
|
|
885
1531
|
if (!import_node_fs.default.existsSync(filePath)) {
|
|
886
|
-
console.log(
|
|
1532
|
+
console.log(import_chalk6.default.red(`File not found: ${opts.file}`));
|
|
887
1533
|
process.exit(1);
|
|
888
1534
|
}
|
|
889
1535
|
const content = import_node_fs.default.readFileSync(filePath, "utf-8");
|
|
@@ -903,29 +1549,29 @@ var envPushCommand = new import_commander4.Command("push").description("Push a l
|
|
|
903
1549
|
vars.push({ key, value, secret: false });
|
|
904
1550
|
}
|
|
905
1551
|
if (vars.length === 0) {
|
|
906
|
-
console.log(
|
|
1552
|
+
console.log(import_chalk6.default.yellow(" No variables to push (all lines empty or comments)."));
|
|
907
1553
|
return;
|
|
908
1554
|
}
|
|
909
|
-
const spinner = (0,
|
|
1555
|
+
const spinner = (0, import_ora5.default)(`Pushing ${vars.length} variable(s) to [${env}]...`).start();
|
|
910
1556
|
try {
|
|
911
1557
|
await api.setEnvVars(linked.appId, vars, env);
|
|
912
|
-
spinner.succeed(
|
|
913
|
-
console.log(
|
|
1558
|
+
spinner.succeed(import_chalk6.default.green(`\u2713 Pushed ${vars.length} variable(s) to ${import_chalk6.default.bold(env)}`));
|
|
1559
|
+
console.log(import_chalk6.default.dim(` Run ${import_chalk6.default.cyan("sylphx deploy")} to apply the new variables.`));
|
|
914
1560
|
} catch (err) {
|
|
915
|
-
spinner.fail(
|
|
1561
|
+
spinner.fail(import_chalk6.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
916
1562
|
process.exit(1);
|
|
917
1563
|
}
|
|
918
1564
|
});
|
|
919
|
-
var envCommand = new
|
|
1565
|
+
var envCommand = new import_commander5.Command("env").description("Manage environment variables").addCommand(envListCommand).addCommand(envSetCommand).addCommand(envRmCommand).addCommand(envPullCommand).addCommand(envPushCommand);
|
|
920
1566
|
|
|
921
1567
|
// src/commands/init.ts
|
|
922
1568
|
var import_node_child_process = require("child_process");
|
|
923
1569
|
var import_node_fs2 = __toESM(require("fs"));
|
|
924
1570
|
var import_node_path3 = __toESM(require("path"));
|
|
925
1571
|
var import_node_readline2 = __toESM(require("readline"));
|
|
926
|
-
var
|
|
927
|
-
var
|
|
928
|
-
var
|
|
1572
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
1573
|
+
var import_commander6 = require("commander");
|
|
1574
|
+
var import_ora6 = __toESM(require("ora"));
|
|
929
1575
|
function prompt2(question, defaultValue) {
|
|
930
1576
|
return new Promise((resolve) => {
|
|
931
1577
|
const rl = import_node_readline2.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -960,10 +1606,10 @@ function detectGitRemote() {
|
|
|
960
1606
|
return void 0;
|
|
961
1607
|
}
|
|
962
1608
|
}
|
|
963
|
-
var initCommand = new
|
|
1609
|
+
var initCommand = new import_commander6.Command("init").description("Create and link a new Sylphx project in one step").argument("[name]", "Project name (detected from package.json if omitted)").option("--git", "Auto-detect git remote URL and set as gitRepository").option("--port <port>", "Application port").action(async (nameArg, opts) => {
|
|
964
1610
|
const token = config.getToken();
|
|
965
1611
|
if (!token) {
|
|
966
|
-
console.log(
|
|
1612
|
+
console.log(import_chalk7.default.red("Not authenticated. Run `sylphx login` first."));
|
|
967
1613
|
process.exit(1);
|
|
968
1614
|
}
|
|
969
1615
|
const pkg = readPackageJson();
|
|
@@ -971,51 +1617,51 @@ var initCommand = new import_commander5.Command("init").description("Create and
|
|
|
971
1617
|
let projectName;
|
|
972
1618
|
if (detectedName) {
|
|
973
1619
|
projectName = detectedName;
|
|
974
|
-
console.log(
|
|
1620
|
+
console.log(import_chalk7.default.dim(` Project name: ${import_chalk7.default.white(projectName)}`));
|
|
975
1621
|
} else {
|
|
976
1622
|
projectName = await prompt2(" Project name");
|
|
977
1623
|
if (!projectName) {
|
|
978
|
-
console.log(
|
|
1624
|
+
console.log(import_chalk7.default.red("Project name is required."));
|
|
979
1625
|
process.exit(1);
|
|
980
1626
|
}
|
|
981
1627
|
}
|
|
982
1628
|
const slug = slugify(projectName);
|
|
983
1629
|
let orgId = config.getDefaultOrg();
|
|
984
1630
|
if (!orgId) {
|
|
985
|
-
const spinner2 = (0,
|
|
1631
|
+
const spinner2 = (0, import_ora6.default)("Fetching your organizations...").start();
|
|
986
1632
|
let orgs = [];
|
|
987
1633
|
try {
|
|
988
1634
|
const me = await api.whoami();
|
|
989
1635
|
orgs = me.orgs;
|
|
990
1636
|
spinner2.stop();
|
|
991
1637
|
} catch (err) {
|
|
992
|
-
spinner2.fail(
|
|
1638
|
+
spinner2.fail(import_chalk7.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
993
1639
|
process.exit(1);
|
|
994
1640
|
}
|
|
995
1641
|
if (orgs.length === 0) {
|
|
996
|
-
console.log(
|
|
1642
|
+
console.log(import_chalk7.default.red("No organizations found. Create one at https://app.sylphx.com."));
|
|
997
1643
|
process.exit(1);
|
|
998
1644
|
}
|
|
999
1645
|
if (orgs.length === 1 && orgs[0]) {
|
|
1000
1646
|
orgId = orgs[0].slug;
|
|
1001
|
-
console.log(
|
|
1647
|
+
console.log(import_chalk7.default.dim(` Using org: ${import_chalk7.default.white(orgId)}`));
|
|
1002
1648
|
} else {
|
|
1003
|
-
console.log(
|
|
1649
|
+
console.log(import_chalk7.default.bold("\n Your organizations:\n"));
|
|
1004
1650
|
orgs.forEach((org, i) => {
|
|
1005
|
-
console.log(` ${
|
|
1651
|
+
console.log(` ${import_chalk7.default.cyan(String(i + 1))}. ${org.slug} ${import_chalk7.default.dim(`(${org.name})`)}`);
|
|
1006
1652
|
});
|
|
1007
1653
|
console.log("");
|
|
1008
1654
|
const choice = await prompt2(" Select org (number)");
|
|
1009
1655
|
const idx = Number.parseInt(choice, 10) - 1;
|
|
1010
1656
|
if (Number.isNaN(idx) || idx < 0 || idx >= orgs.length || !orgs[idx]) {
|
|
1011
|
-
console.log(
|
|
1657
|
+
console.log(import_chalk7.default.red("Invalid selection."));
|
|
1012
1658
|
process.exit(1);
|
|
1013
1659
|
}
|
|
1014
1660
|
orgId = orgs[idx]?.slug;
|
|
1015
1661
|
}
|
|
1016
1662
|
}
|
|
1017
1663
|
if (!orgId) {
|
|
1018
|
-
console.log(
|
|
1664
|
+
console.log(import_chalk7.default.red("Could not determine org."));
|
|
1019
1665
|
process.exit(1);
|
|
1020
1666
|
}
|
|
1021
1667
|
let gitRepository;
|
|
@@ -1026,9 +1672,9 @@ var initCommand = new import_commander5.Command("init").description("Create and
|
|
|
1026
1672
|
if (sshMatch?.[1]) gitRepository = sshMatch[1];
|
|
1027
1673
|
const httpsMatch = gitRepository.match(/github\.com\/(.+?)(?:\.git)?$/);
|
|
1028
1674
|
if (httpsMatch?.[1]) gitRepository = httpsMatch[1];
|
|
1029
|
-
console.log(
|
|
1675
|
+
console.log(import_chalk7.default.dim(` Git repo: ${import_chalk7.default.white(gitRepository)}`));
|
|
1030
1676
|
} else {
|
|
1031
|
-
console.log(
|
|
1677
|
+
console.log(import_chalk7.default.yellow(" \u26A0 No git remote found (--git flag ignored)."));
|
|
1032
1678
|
}
|
|
1033
1679
|
}
|
|
1034
1680
|
let appPort;
|
|
@@ -1036,10 +1682,10 @@ var initCommand = new import_commander5.Command("init").description("Create and
|
|
|
1036
1682
|
appPort = Number.parseInt(opts.port, 10);
|
|
1037
1683
|
} else if (pkg) {
|
|
1038
1684
|
appPort = detectPortFromPackage(pkg);
|
|
1039
|
-
if (appPort) console.log(
|
|
1685
|
+
if (appPort) console.log(import_chalk7.default.dim(` Detected port: ${import_chalk7.default.white(String(appPort))}`));
|
|
1040
1686
|
}
|
|
1041
1687
|
console.log("");
|
|
1042
|
-
const spinner = (0,
|
|
1688
|
+
const spinner = (0, import_ora6.default)(`Creating project ${import_chalk7.default.bold(projectName)}...`).start();
|
|
1043
1689
|
try {
|
|
1044
1690
|
const project = await api.createProject({
|
|
1045
1691
|
name: projectName,
|
|
@@ -1049,17 +1695,17 @@ var initCommand = new import_commander5.Command("init").description("Create and
|
|
|
1049
1695
|
});
|
|
1050
1696
|
config.linkApp({ appId: project.id, orgId, defaultEnv: "production" });
|
|
1051
1697
|
config.setDefaultOrg(orgId);
|
|
1052
|
-
spinner.succeed(
|
|
1698
|
+
spinner.succeed(import_chalk7.default.green(`\u2713 Created and linked project ${import_chalk7.default.bold(project.name)}`));
|
|
1053
1699
|
console.log("");
|
|
1054
|
-
console.log(
|
|
1055
|
-
console.log(
|
|
1056
|
-
console.log(
|
|
1057
|
-
console.log(
|
|
1700
|
+
console.log(import_chalk7.default.dim(` Project ID : ${project.id}`));
|
|
1701
|
+
console.log(import_chalk7.default.dim(` Slug : ${project.slug}`));
|
|
1702
|
+
console.log(import_chalk7.default.dim(` Org : ${orgId}`));
|
|
1703
|
+
console.log(import_chalk7.default.dim(` Directory : ${process.cwd()}`));
|
|
1058
1704
|
console.log("");
|
|
1059
|
-
console.log(
|
|
1705
|
+
console.log(import_chalk7.default.dim(` ${import_chalk7.default.cyan("sylphx deploy")} \u2014 deploy to production when ready`));
|
|
1060
1706
|
console.log("");
|
|
1061
1707
|
} catch (err) {
|
|
1062
|
-
spinner.fail(
|
|
1708
|
+
spinner.fail(import_chalk7.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1063
1709
|
process.exit(1);
|
|
1064
1710
|
}
|
|
1065
1711
|
});
|
|
@@ -1068,9 +1714,9 @@ var initCommand = new import_commander5.Command("init").description("Create and
|
|
|
1068
1714
|
var import_node_fs3 = __toESM(require("fs"));
|
|
1069
1715
|
var import_node_path4 = __toESM(require("path"));
|
|
1070
1716
|
var import_node_readline3 = __toESM(require("readline"));
|
|
1071
|
-
var
|
|
1072
|
-
var
|
|
1073
|
-
var
|
|
1717
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
1718
|
+
var import_commander7 = require("commander");
|
|
1719
|
+
var import_ora7 = __toESM(require("ora"));
|
|
1074
1720
|
function readPackageName() {
|
|
1075
1721
|
try {
|
|
1076
1722
|
const pkgPath = import_node_path4.default.join(process.cwd(), "package.json");
|
|
@@ -1092,86 +1738,86 @@ function prompt3(question) {
|
|
|
1092
1738
|
});
|
|
1093
1739
|
});
|
|
1094
1740
|
}
|
|
1095
|
-
var linkCommand = new
|
|
1741
|
+
var linkCommand = new import_commander7.Command("link").description("Link current directory to a Sylphx app").option("--org <org>", "Organization slug").action(async (opts) => {
|
|
1096
1742
|
const token = config.getToken();
|
|
1097
1743
|
if (!token) {
|
|
1098
|
-
console.log(
|
|
1744
|
+
console.log(import_chalk8.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1099
1745
|
process.exit(1);
|
|
1100
1746
|
}
|
|
1101
1747
|
const pkgName = readPackageName();
|
|
1102
1748
|
if (pkgName) {
|
|
1103
|
-
console.log(
|
|
1749
|
+
console.log(import_chalk8.default.dim(` Detected package: ${import_chalk8.default.white(pkgName)}`));
|
|
1104
1750
|
}
|
|
1105
1751
|
let orgId = opts.org ?? config.getDefaultOrg();
|
|
1106
1752
|
if (!orgId) {
|
|
1107
|
-
const spinner = (0,
|
|
1753
|
+
const spinner = (0, import_ora7.default)("Fetching your organizations...").start();
|
|
1108
1754
|
let orgs = [];
|
|
1109
1755
|
try {
|
|
1110
1756
|
const me = await api.whoami();
|
|
1111
1757
|
orgs = me.orgs;
|
|
1112
1758
|
spinner.stop();
|
|
1113
1759
|
} catch (err) {
|
|
1114
|
-
spinner.fail(
|
|
1760
|
+
spinner.fail(import_chalk8.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1115
1761
|
process.exit(1);
|
|
1116
1762
|
}
|
|
1117
1763
|
if (orgs.length === 0) {
|
|
1118
|
-
console.log(
|
|
1764
|
+
console.log(import_chalk8.default.red("No organizations found."));
|
|
1119
1765
|
process.exit(1);
|
|
1120
1766
|
}
|
|
1121
1767
|
if (orgs.length === 1 && orgs[0]) {
|
|
1122
1768
|
orgId = orgs[0].slug;
|
|
1123
|
-
console.log(
|
|
1769
|
+
console.log(import_chalk8.default.dim(` Using org: ${import_chalk8.default.white(orgId)}`));
|
|
1124
1770
|
} else {
|
|
1125
|
-
console.log(
|
|
1771
|
+
console.log(import_chalk8.default.bold("\n Your organizations:\n"));
|
|
1126
1772
|
orgs.forEach((org, i) => {
|
|
1127
|
-
console.log(` ${
|
|
1773
|
+
console.log(` ${import_chalk8.default.cyan(String(i + 1))}. ${org.slug} ${import_chalk8.default.dim(`(${org.name})`)}`);
|
|
1128
1774
|
});
|
|
1129
1775
|
console.log("");
|
|
1130
1776
|
const choice = await prompt3(" Select org (number): ");
|
|
1131
1777
|
const idx = Number.parseInt(choice, 10) - 1;
|
|
1132
1778
|
if (Number.isNaN(idx) || idx < 0 || idx >= orgs.length || !orgs[idx]) {
|
|
1133
|
-
console.log(
|
|
1779
|
+
console.log(import_chalk8.default.red("Invalid selection."));
|
|
1134
1780
|
process.exit(1);
|
|
1135
1781
|
}
|
|
1136
1782
|
orgId = orgs[idx]?.slug;
|
|
1137
1783
|
}
|
|
1138
1784
|
}
|
|
1139
|
-
const spinner2 = (0,
|
|
1785
|
+
const spinner2 = (0, import_ora7.default)("Fetching projects...").start();
|
|
1140
1786
|
let apps = [];
|
|
1141
1787
|
try {
|
|
1142
1788
|
apps = await api.listProjects();
|
|
1143
1789
|
spinner2.stop();
|
|
1144
1790
|
} catch (err) {
|
|
1145
|
-
spinner2.fail(
|
|
1791
|
+
spinner2.fail(import_chalk8.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1146
1792
|
process.exit(1);
|
|
1147
1793
|
}
|
|
1148
1794
|
if (apps.length === 0) {
|
|
1149
|
-
console.log(
|
|
1795
|
+
console.log(import_chalk8.default.yellow("No apps found for this org."));
|
|
1150
1796
|
process.exit(1);
|
|
1151
1797
|
}
|
|
1152
|
-
console.log(
|
|
1798
|
+
console.log(import_chalk8.default.bold("\n Available apps:\n"));
|
|
1153
1799
|
apps.forEach((app, i) => {
|
|
1154
|
-
console.log(` ${
|
|
1800
|
+
console.log(` ${import_chalk8.default.cyan(String(i + 1))}. ${app.slug} ${import_chalk8.default.dim(`(${app.name})`)}`);
|
|
1155
1801
|
});
|
|
1156
1802
|
console.log("");
|
|
1157
1803
|
const appChoice = await prompt3(" Select app (number): ");
|
|
1158
1804
|
const appIdx = Number.parseInt(appChoice, 10) - 1;
|
|
1159
1805
|
if (Number.isNaN(appIdx) || appIdx < 0 || appIdx >= apps.length || !apps[appIdx]) {
|
|
1160
|
-
console.log(
|
|
1806
|
+
console.log(import_chalk8.default.red("Invalid selection."));
|
|
1161
1807
|
process.exit(1);
|
|
1162
1808
|
}
|
|
1163
1809
|
const selectedApp = apps[appIdx];
|
|
1164
1810
|
if (!selectedApp) {
|
|
1165
|
-
console.log(
|
|
1811
|
+
console.log(import_chalk8.default.red("Invalid selection."));
|
|
1166
1812
|
process.exit(1);
|
|
1167
1813
|
}
|
|
1168
1814
|
let defaultEnv = "production";
|
|
1169
1815
|
const envs = selectedApp.environments ?? [];
|
|
1170
1816
|
if (envs.length > 1) {
|
|
1171
|
-
console.log(
|
|
1817
|
+
console.log(import_chalk8.default.bold("\n Available environments:\n"));
|
|
1172
1818
|
envs.forEach((env, i) => {
|
|
1173
1819
|
console.log(
|
|
1174
|
-
` ${
|
|
1820
|
+
` ${import_chalk8.default.cyan(String(i + 1))}. ${env.envType ?? env.slug ?? env.name} ${import_chalk8.default.dim(`(${env.name})`)}`
|
|
1175
1821
|
);
|
|
1176
1822
|
});
|
|
1177
1823
|
console.log("");
|
|
@@ -1194,18 +1840,19 @@ var linkCommand = new import_commander6.Command("link").description("Link curren
|
|
|
1194
1840
|
config.setDefaultOrg(orgId);
|
|
1195
1841
|
}
|
|
1196
1842
|
console.log("");
|
|
1197
|
-
console.log(
|
|
1843
|
+
console.log(import_chalk8.default.green(`\u2713 Linked to ${import_chalk8.default.bold(selectedApp.slug)}`));
|
|
1198
1844
|
console.log(
|
|
1199
|
-
|
|
1845
|
+
import_chalk8.default.dim(` App ID: ${selectedApp.id} | Env: ${defaultEnv} | Dir: ${process.cwd()}`)
|
|
1200
1846
|
);
|
|
1201
1847
|
console.log("");
|
|
1202
1848
|
});
|
|
1203
1849
|
|
|
1204
1850
|
// src/commands/login.ts
|
|
1205
1851
|
var import_node_crypto = __toESM(require("crypto"));
|
|
1206
|
-
var
|
|
1207
|
-
var
|
|
1208
|
-
var
|
|
1852
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
1853
|
+
var import_commander8 = require("commander");
|
|
1854
|
+
var import_ora8 = __toESM(require("ora"));
|
|
1855
|
+
var { version: version3 } = package_default;
|
|
1209
1856
|
var BASE_URL2 = process.env.SYLPHX_API_URL ?? "https://sylphx.com";
|
|
1210
1857
|
var POLL_INTERVAL_MS2 = 2e3;
|
|
1211
1858
|
var POLL_TIMEOUT_MS2 = 5 * 60 * 1e3;
|
|
@@ -1214,7 +1861,7 @@ async function initCode(code) {
|
|
|
1214
1861
|
method: "POST",
|
|
1215
1862
|
headers: {
|
|
1216
1863
|
"Content-Type": "application/json",
|
|
1217
|
-
"User-Agent":
|
|
1864
|
+
"User-Agent": `sylphx-cli/${version3}`
|
|
1218
1865
|
},
|
|
1219
1866
|
body: JSON.stringify({ code })
|
|
1220
1867
|
});
|
|
@@ -1222,22 +1869,17 @@ async function initCode(code) {
|
|
|
1222
1869
|
throw new Error(`Failed to initialize auth code (HTTP ${res.status})`);
|
|
1223
1870
|
}
|
|
1224
1871
|
}
|
|
1225
|
-
var loginCommand = new
|
|
1226
|
-
"--token <token>",
|
|
1227
|
-
"Authenticate with a pre-existing API token (for CI/CD)"
|
|
1228
|
-
).action(async (opts) => {
|
|
1872
|
+
var loginCommand = new import_commander8.Command("login").description("Authenticate with the Sylphx platform").option("--token <token>", "Authenticate with a pre-existing API token (for CI/CD)").action(async (opts) => {
|
|
1229
1873
|
if (opts.token) {
|
|
1230
|
-
const spinner2 = (0,
|
|
1874
|
+
const spinner2 = (0, import_ora8.default)("Validating token...").start();
|
|
1231
1875
|
try {
|
|
1232
1876
|
config.setToken(opts.token);
|
|
1233
1877
|
const me = await api.whoami();
|
|
1234
1878
|
config.setToken(opts.token);
|
|
1235
|
-
spinner2.succeed(
|
|
1236
|
-
import_chalk8.default.green(`Authenticated as ${import_chalk8.default.bold(me.user.email)}`)
|
|
1237
|
-
);
|
|
1879
|
+
spinner2.succeed(import_chalk9.default.green(`Authenticated as ${import_chalk9.default.bold(me.user.email)}`));
|
|
1238
1880
|
} catch (err) {
|
|
1239
1881
|
config.clearToken();
|
|
1240
|
-
spinner2.fail(
|
|
1882
|
+
spinner2.fail(import_chalk9.default.red("Invalid token"));
|
|
1241
1883
|
process.exit(1);
|
|
1242
1884
|
}
|
|
1243
1885
|
return;
|
|
@@ -1245,18 +1887,18 @@ var loginCommand = new import_commander7.Command("login").description("Authentic
|
|
|
1245
1887
|
const code = import_node_crypto.default.randomBytes(16).toString("hex").slice(0, 20).toUpperCase();
|
|
1246
1888
|
const authUrl = `${BASE_URL2}/cli-auth?code=${code}`;
|
|
1247
1889
|
console.log("");
|
|
1248
|
-
console.log(
|
|
1890
|
+
console.log(import_chalk9.default.bold(" Sylphx CLI Authentication"));
|
|
1249
1891
|
console.log("");
|
|
1250
1892
|
console.log(" Opening browser to:");
|
|
1251
|
-
console.log(` ${
|
|
1893
|
+
console.log(` ${import_chalk9.default.cyan(authUrl)}`);
|
|
1252
1894
|
console.log("");
|
|
1253
|
-
console.log(` One-time code: ${
|
|
1895
|
+
console.log(` One-time code: ${import_chalk9.default.yellow.bold(code)}`);
|
|
1254
1896
|
console.log("");
|
|
1255
1897
|
try {
|
|
1256
1898
|
await initCode(code);
|
|
1257
1899
|
} catch (err) {
|
|
1258
1900
|
console.log(
|
|
1259
|
-
|
|
1901
|
+
import_chalk9.default.yellow(
|
|
1260
1902
|
` Warning: Could not register code: ${err instanceof Error ? err.message : String(err)}`
|
|
1261
1903
|
)
|
|
1262
1904
|
);
|
|
@@ -1265,13 +1907,9 @@ var loginCommand = new import_commander7.Command("login").description("Authentic
|
|
|
1265
1907
|
const { default: open } = await import("open");
|
|
1266
1908
|
await open(authUrl);
|
|
1267
1909
|
} catch {
|
|
1268
|
-
console.log(
|
|
1269
|
-
import_chalk8.default.dim(
|
|
1270
|
-
" Could not open browser automatically. Please visit the URL above."
|
|
1271
|
-
)
|
|
1272
|
-
);
|
|
1910
|
+
console.log(import_chalk9.default.dim(" Could not open browser automatically. Please visit the URL above."));
|
|
1273
1911
|
}
|
|
1274
|
-
const spinner = (0,
|
|
1912
|
+
const spinner = (0, import_ora8.default)("Waiting for browser authorization...").start();
|
|
1275
1913
|
const deadline = Date.now() + POLL_TIMEOUT_MS2;
|
|
1276
1914
|
while (Date.now() < deadline) {
|
|
1277
1915
|
await sleep(POLL_INTERVAL_MS2);
|
|
@@ -1281,28 +1919,24 @@ var loginCommand = new import_commander7.Command("login").description("Authentic
|
|
|
1281
1919
|
config.setToken(result.token);
|
|
1282
1920
|
try {
|
|
1283
1921
|
const me = await api.whoami();
|
|
1284
|
-
spinner.succeed(
|
|
1285
|
-
import_chalk8.default.green(`Authenticated as ${import_chalk8.default.bold(me.user.email)}`)
|
|
1286
|
-
);
|
|
1922
|
+
spinner.succeed(import_chalk9.default.green(`Authenticated as ${import_chalk9.default.bold(me.user.email)}`));
|
|
1287
1923
|
} catch {
|
|
1288
|
-
spinner.succeed(
|
|
1924
|
+
spinner.succeed(import_chalk9.default.green("Authenticated successfully"));
|
|
1289
1925
|
}
|
|
1290
1926
|
console.log("");
|
|
1291
|
-
console.log(
|
|
1292
|
-
` Token stored at: ${import_chalk8.default.dim(config.getConfigPath())}`
|
|
1293
|
-
);
|
|
1927
|
+
console.log(` Token stored at: ${import_chalk9.default.dim(config.getConfigPath())}`);
|
|
1294
1928
|
console.log("");
|
|
1295
1929
|
return;
|
|
1296
1930
|
}
|
|
1297
1931
|
} catch (err) {
|
|
1298
1932
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1299
1933
|
if (msg.includes("404") || msg.includes("expired")) {
|
|
1300
|
-
spinner.fail(
|
|
1934
|
+
spinner.fail(import_chalk9.default.red("Code expired or invalid. Please try again."));
|
|
1301
1935
|
process.exit(1);
|
|
1302
1936
|
}
|
|
1303
1937
|
}
|
|
1304
1938
|
}
|
|
1305
|
-
spinner.fail(
|
|
1939
|
+
spinner.fail(import_chalk9.default.red("Authentication timed out. Please try again."));
|
|
1306
1940
|
process.exit(1);
|
|
1307
1941
|
});
|
|
1308
1942
|
function sleep(ms) {
|
|
@@ -1310,43 +1944,43 @@ function sleep(ms) {
|
|
|
1310
1944
|
}
|
|
1311
1945
|
|
|
1312
1946
|
// src/commands/logout.ts
|
|
1313
|
-
var
|
|
1314
|
-
var
|
|
1315
|
-
var logoutCommand = new
|
|
1947
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
1948
|
+
var import_commander9 = require("commander");
|
|
1949
|
+
var logoutCommand = new import_commander9.Command("logout").description("Clear stored credentials").action(() => {
|
|
1316
1950
|
const token = config.getToken();
|
|
1317
1951
|
if (!token) {
|
|
1318
|
-
console.log(
|
|
1952
|
+
console.log(import_chalk10.default.yellow("You are not currently logged in."));
|
|
1319
1953
|
return;
|
|
1320
1954
|
}
|
|
1321
1955
|
config.clearToken();
|
|
1322
|
-
console.log(
|
|
1956
|
+
console.log(import_chalk10.default.green("\u2713 Logged out successfully."));
|
|
1323
1957
|
console.log(
|
|
1324
|
-
|
|
1958
|
+
import_chalk10.default.dim(` Credentials cleared from ${config.getConfigPath()}`)
|
|
1325
1959
|
);
|
|
1326
1960
|
});
|
|
1327
1961
|
|
|
1328
1962
|
// src/commands/logs.ts
|
|
1329
|
-
var
|
|
1330
|
-
var
|
|
1331
|
-
var logsCommand = new
|
|
1963
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
1964
|
+
var import_commander10 = require("commander");
|
|
1965
|
+
var logsCommand = new import_commander10.Command("logs").description("Stream build/runtime logs").option("--env <env>", "Environment (e.g. production, staging)").option("-f, --follow", "Follow log output (stream continuously)").action(async (opts) => {
|
|
1332
1966
|
const token = config.getToken();
|
|
1333
1967
|
if (!token) {
|
|
1334
|
-
console.log(
|
|
1968
|
+
console.log(import_chalk11.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1335
1969
|
process.exit(1);
|
|
1336
1970
|
}
|
|
1337
1971
|
const linked = config.getLinkedApp();
|
|
1338
1972
|
if (!linked) {
|
|
1339
|
-
console.log(
|
|
1973
|
+
console.log(import_chalk11.default.red("No app linked. Run `sylphx link` first."));
|
|
1340
1974
|
process.exit(1);
|
|
1341
1975
|
}
|
|
1342
1976
|
const env = opts.env ?? linked.defaultEnv;
|
|
1343
1977
|
console.log("");
|
|
1344
1978
|
console.log(
|
|
1345
|
-
|
|
1346
|
-
` Logs for ${
|
|
1979
|
+
import_chalk11.default.bold(
|
|
1980
|
+
` Logs for ${import_chalk11.default.cyan(linked.appId)} [${import_chalk11.default.white(env)}]${opts.follow ? import_chalk11.default.dim(" (following...)") : ""}`
|
|
1347
1981
|
)
|
|
1348
1982
|
);
|
|
1349
|
-
console.log(
|
|
1983
|
+
console.log(import_chalk11.default.dim(` ${"\u2500".repeat(50)}`));
|
|
1350
1984
|
console.log("");
|
|
1351
1985
|
const logStream = createLogStream(linked.appId, env);
|
|
1352
1986
|
await streamLogs(logStream.url, logStream.token, {
|
|
@@ -1359,21 +1993,21 @@ var logsCommand = new import_commander9.Command("logs").description("Stream buil
|
|
|
1359
1993
|
});
|
|
1360
1994
|
|
|
1361
1995
|
// src/commands/open.ts
|
|
1362
|
-
var
|
|
1363
|
-
var
|
|
1364
|
-
var
|
|
1365
|
-
var openCommand = new
|
|
1996
|
+
var import_chalk12 = __toESM(require("chalk"));
|
|
1997
|
+
var import_commander11 = require("commander");
|
|
1998
|
+
var import_ora9 = __toESM(require("ora"));
|
|
1999
|
+
var openCommand = new import_commander11.Command("open").description("Open the app in your browser").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
|
|
1366
2000
|
const token = config.getToken();
|
|
1367
2001
|
if (!token) {
|
|
1368
|
-
console.log(
|
|
2002
|
+
console.log(import_chalk12.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1369
2003
|
process.exit(1);
|
|
1370
2004
|
}
|
|
1371
2005
|
const linked = config.getLinkedApp();
|
|
1372
2006
|
if (!linked) {
|
|
1373
|
-
console.log(
|
|
2007
|
+
console.log(import_chalk12.default.red("No app linked. Run `sylphx link` first."));
|
|
1374
2008
|
process.exit(1);
|
|
1375
2009
|
}
|
|
1376
|
-
const spinner = (0,
|
|
2010
|
+
const spinner = (0, import_ora9.default)("Fetching app URL...").start();
|
|
1377
2011
|
let url;
|
|
1378
2012
|
try {
|
|
1379
2013
|
const status = await api.getDeploymentStatus(linked.appId);
|
|
@@ -1383,7 +2017,7 @@ var openCommand = new import_commander10.Command("open").description("Open the a
|
|
|
1383
2017
|
const resolvedUrl = status.url ?? envMatch?.url ?? null;
|
|
1384
2018
|
if (!resolvedUrl) {
|
|
1385
2019
|
url = `https://sylphx.com/console/apps/${linked.appId}`;
|
|
1386
|
-
console.log(
|
|
2020
|
+
console.log(import_chalk12.default.yellow(" No URL found, opening console instead."));
|
|
1387
2021
|
} else {
|
|
1388
2022
|
url = resolvedUrl;
|
|
1389
2023
|
}
|
|
@@ -1391,28 +2025,28 @@ var openCommand = new import_commander10.Command("open").description("Open the a
|
|
|
1391
2025
|
spinner.stop();
|
|
1392
2026
|
url = `https://sylphx.com/console/apps/${linked.appId}`;
|
|
1393
2027
|
}
|
|
1394
|
-
console.log(` Opening ${
|
|
2028
|
+
console.log(` Opening ${import_chalk12.default.cyan(url)}`);
|
|
1395
2029
|
try {
|
|
1396
2030
|
const { default: open } = await import("open");
|
|
1397
2031
|
await open(url);
|
|
1398
2032
|
} catch (err) {
|
|
1399
2033
|
console.log(
|
|
1400
|
-
|
|
2034
|
+
import_chalk12.default.red(`Failed to open browser: ${err instanceof Error ? err.message : String(err)}`)
|
|
1401
2035
|
);
|
|
1402
|
-
console.log(
|
|
2036
|
+
console.log(import_chalk12.default.dim(` URL: ${url}`));
|
|
1403
2037
|
process.exit(1);
|
|
1404
2038
|
}
|
|
1405
2039
|
});
|
|
1406
2040
|
|
|
1407
2041
|
// src/commands/projects.ts
|
|
1408
2042
|
var import_node_readline4 = __toESM(require("readline"));
|
|
1409
|
-
var
|
|
1410
|
-
var
|
|
1411
|
-
var
|
|
2043
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
2044
|
+
var import_commander12 = require("commander");
|
|
2045
|
+
var import_ora10 = __toESM(require("ora"));
|
|
1412
2046
|
function requireAuth2() {
|
|
1413
2047
|
const token = config.getToken();
|
|
1414
2048
|
if (!token) {
|
|
1415
|
-
console.log(
|
|
2049
|
+
console.log(import_chalk13.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1416
2050
|
process.exit(1);
|
|
1417
2051
|
}
|
|
1418
2052
|
return token;
|
|
@@ -1429,41 +2063,41 @@ function prompt4(question) {
|
|
|
1429
2063
|
});
|
|
1430
2064
|
});
|
|
1431
2065
|
}
|
|
1432
|
-
var projectsListCommand = new
|
|
2066
|
+
var projectsListCommand = new import_commander12.Command("list").description("List all projects").action(async () => {
|
|
1433
2067
|
requireAuth2();
|
|
1434
|
-
const spinner = (0,
|
|
2068
|
+
const spinner = (0, import_ora10.default)("Fetching projects...").start();
|
|
1435
2069
|
try {
|
|
1436
2070
|
const projects = await api.listProjects();
|
|
1437
2071
|
spinner.stop();
|
|
1438
2072
|
if (projects.length === 0) {
|
|
1439
|
-
console.log(
|
|
2073
|
+
console.log(import_chalk13.default.dim("\n No projects found.\n"));
|
|
1440
2074
|
return;
|
|
1441
2075
|
}
|
|
1442
2076
|
const colSlug = Math.max(4, ...projects.map((p) => p.slug.length));
|
|
1443
2077
|
const colName = Math.max(4, ...projects.map((p) => p.name.length));
|
|
1444
2078
|
console.log("");
|
|
1445
|
-
console.log(
|
|
1446
|
-
console.log(
|
|
2079
|
+
console.log(import_chalk13.default.bold(" Projects"));
|
|
2080
|
+
console.log(import_chalk13.default.dim(` ${"\u2500".repeat(colSlug + colName + 30)}`));
|
|
1447
2081
|
console.log(
|
|
1448
|
-
|
|
2082
|
+
import_chalk13.default.dim(` ${"SLUG".padEnd(colSlug)} ${"NAME".padEnd(colName)} ${"ENVIRONMENTS"}`)
|
|
1449
2083
|
);
|
|
1450
|
-
console.log(
|
|
2084
|
+
console.log(import_chalk13.default.dim(` ${"\u2500".repeat(colSlug + colName + 30)}`));
|
|
1451
2085
|
for (const p of projects) {
|
|
1452
|
-
const envNames = p.environments && p.environments.length > 0 ? p.environments.map((e) => e.name || e.envType || e.id).join(", ") :
|
|
2086
|
+
const envNames = p.environments && p.environments.length > 0 ? p.environments.map((e) => e.name || e.envType || e.id).join(", ") : import_chalk13.default.dim("\u2014");
|
|
1453
2087
|
console.log(
|
|
1454
|
-
` ${
|
|
2088
|
+
` ${import_chalk13.default.cyan(p.slug.padEnd(colSlug))} ${p.name.padEnd(colName)} ${envNames}`
|
|
1455
2089
|
);
|
|
1456
2090
|
}
|
|
1457
2091
|
console.log("");
|
|
1458
2092
|
} catch (err) {
|
|
1459
|
-
spinner.fail(
|
|
2093
|
+
spinner.fail(import_chalk13.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1460
2094
|
process.exit(1);
|
|
1461
2095
|
}
|
|
1462
2096
|
});
|
|
1463
|
-
var projectsCreateCommand = new
|
|
2097
|
+
var projectsCreateCommand = new import_commander12.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) => {
|
|
1464
2098
|
requireAuth2();
|
|
1465
2099
|
const slug = slugify2(name);
|
|
1466
|
-
const spinner = (0,
|
|
2100
|
+
const spinner = (0, import_ora10.default)(`Creating project ${import_chalk13.default.bold(name)}...`).start();
|
|
1467
2101
|
try {
|
|
1468
2102
|
const project = await api.createProject({
|
|
1469
2103
|
name,
|
|
@@ -1471,75 +2105,77 @@ var projectsCreateCommand = new import_commander11.Command("create").description
|
|
|
1471
2105
|
...opts.git ? { gitRepository: opts.git } : {},
|
|
1472
2106
|
...opts.port ? { port: Number.parseInt(opts.port, 10) } : {}
|
|
1473
2107
|
});
|
|
1474
|
-
spinner.succeed(
|
|
2108
|
+
spinner.succeed(import_chalk13.default.green(`\u2713 Created project ${import_chalk13.default.bold(project.name)}`));
|
|
1475
2109
|
console.log("");
|
|
1476
|
-
console.log(
|
|
1477
|
-
console.log(
|
|
2110
|
+
console.log(import_chalk13.default.dim(` ID : ${project.id}`));
|
|
2111
|
+
console.log(import_chalk13.default.dim(` Slug : ${project.slug}`));
|
|
1478
2112
|
console.log("");
|
|
1479
2113
|
console.log(
|
|
1480
|
-
|
|
2114
|
+
import_chalk13.default.dim(` Run ${import_chalk13.default.cyan("sylphx link")} to link this project to a directory.`)
|
|
1481
2115
|
);
|
|
1482
2116
|
console.log("");
|
|
1483
2117
|
} catch (err) {
|
|
1484
|
-
spinner.fail(
|
|
2118
|
+
spinner.fail(import_chalk13.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1485
2119
|
process.exit(1);
|
|
1486
2120
|
}
|
|
1487
2121
|
});
|
|
1488
|
-
var projectsDeleteCommand = new
|
|
2122
|
+
var projectsDeleteCommand = new import_commander12.Command("delete").description("Delete a project").argument("<id>", "Project ID or slug").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
|
|
1489
2123
|
requireAuth2();
|
|
1490
2124
|
if (!opts.force) {
|
|
1491
2125
|
console.log("");
|
|
1492
2126
|
console.log(
|
|
1493
|
-
|
|
2127
|
+
import_chalk13.default.yellow(` \u26A0 You are about to permanently delete project ${import_chalk13.default.bold(id)}.`)
|
|
1494
2128
|
);
|
|
1495
|
-
console.log(
|
|
2129
|
+
console.log(import_chalk13.default.yellow(" This action cannot be undone."));
|
|
1496
2130
|
console.log("");
|
|
1497
2131
|
const answer = await prompt4(" Type the project ID/slug to confirm");
|
|
1498
2132
|
if (answer !== id) {
|
|
1499
|
-
console.log(
|
|
2133
|
+
console.log(import_chalk13.default.red("Confirmation did not match. Aborted."));
|
|
1500
2134
|
process.exit(1);
|
|
1501
2135
|
}
|
|
1502
2136
|
}
|
|
1503
|
-
const spinner = (0,
|
|
2137
|
+
const spinner = (0, import_ora10.default)(`Deleting project ${import_chalk13.default.bold(id)}...`).start();
|
|
1504
2138
|
try {
|
|
1505
2139
|
await api.deleteProject(id);
|
|
1506
|
-
spinner.succeed(
|
|
2140
|
+
spinner.succeed(import_chalk13.default.green(`\u2713 Deleted project ${import_chalk13.default.bold(id)}`));
|
|
1507
2141
|
} catch (err) {
|
|
1508
|
-
spinner.fail(
|
|
2142
|
+
spinner.fail(import_chalk13.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1509
2143
|
process.exit(1);
|
|
1510
2144
|
}
|
|
1511
2145
|
});
|
|
1512
|
-
var projectsCommand = new
|
|
2146
|
+
var projectsCommand = new import_commander12.Command("projects").description("Manage Sylphx projects").addCommand(projectsListCommand).addCommand(projectsCreateCommand).addCommand(projectsDeleteCommand);
|
|
1513
2147
|
|
|
1514
|
-
// src/commands/
|
|
1515
|
-
var
|
|
1516
|
-
var
|
|
1517
|
-
var
|
|
1518
|
-
var
|
|
2148
|
+
// src/commands/promote.ts
|
|
2149
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
2150
|
+
var import_commander13 = require("commander");
|
|
2151
|
+
var import_ora11 = __toESM(require("ora"));
|
|
2152
|
+
var promoteCommand = new import_commander13.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) => {
|
|
1519
2153
|
const token = config.getToken();
|
|
1520
2154
|
if (!token) {
|
|
1521
|
-
console.log(
|
|
2155
|
+
console.log(import_chalk14.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1522
2156
|
process.exit(1);
|
|
1523
2157
|
}
|
|
1524
2158
|
const linked = config.getLinkedApp();
|
|
1525
2159
|
if (!linked) {
|
|
1526
|
-
console.log(
|
|
2160
|
+
console.log(import_chalk14.default.red("No app linked. Run `sylphx link` first."));
|
|
2161
|
+
process.exit(1);
|
|
2162
|
+
}
|
|
2163
|
+
if (opts.from === opts.to) {
|
|
2164
|
+
console.log(import_chalk14.default.red("Source and target environments must be different."));
|
|
1527
2165
|
process.exit(1);
|
|
1528
2166
|
}
|
|
1529
|
-
const env = opts.env ?? linked.defaultEnv;
|
|
1530
2167
|
if (!opts.force) {
|
|
1531
|
-
const
|
|
1532
|
-
const rl =
|
|
1533
|
-
|
|
1534
|
-
output: process.stdout
|
|
1535
|
-
});
|
|
1536
|
-
await new Promise((resolve, reject) => {
|
|
2168
|
+
const readline9 = await import("readline");
|
|
2169
|
+
const rl = readline9.createInterface({ input: process.stdin, output: process.stdout });
|
|
2170
|
+
await new Promise((resolve) => {
|
|
1537
2171
|
rl.question(
|
|
1538
|
-
|
|
2172
|
+
import_chalk14.default.yellow(
|
|
2173
|
+
` Promote ${import_chalk14.default.bold(linked.appId)}: ${import_chalk14.default.cyan(opts.from)} \u2192 ${import_chalk14.default.green(opts.to)}? (y/N) `
|
|
2174
|
+
),
|
|
1539
2175
|
(answer) => {
|
|
1540
2176
|
rl.close();
|
|
1541
2177
|
if (answer.toLowerCase() !== "y") {
|
|
1542
|
-
console.log(
|
|
2178
|
+
console.log(import_chalk14.default.dim(" Cancelled."));
|
|
1543
2179
|
process.exit(0);
|
|
1544
2180
|
}
|
|
1545
2181
|
resolve();
|
|
@@ -1547,152 +2183,929 @@ var rollbackCommand = new import_commander12.Command("rollback").description("Ro
|
|
|
1547
2183
|
);
|
|
1548
2184
|
});
|
|
1549
2185
|
}
|
|
1550
|
-
const spinner = (0,
|
|
2186
|
+
const spinner = (0, import_ora11.default)(`Resolving environments...`).start();
|
|
1551
2187
|
try {
|
|
1552
|
-
const
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
2188
|
+
const envs = await api.listEnvironments(linked.appId);
|
|
2189
|
+
const sourceEnv = envs.find(
|
|
2190
|
+
(e) => (e.envType ?? e.name ?? "").toLowerCase() === opts.from.toLowerCase()
|
|
2191
|
+
);
|
|
2192
|
+
const targetEnv = envs.find(
|
|
2193
|
+
(e) => (e.envType ?? e.name ?? "").toLowerCase() === opts.to.toLowerCase()
|
|
2194
|
+
);
|
|
2195
|
+
if (!sourceEnv) {
|
|
2196
|
+
spinner.fail(import_chalk14.default.red(`Source environment '${opts.from}' not found.`));
|
|
2197
|
+
console.log(import_chalk14.default.dim(` Available: ${envs.map((e) => e.envType ?? e.name).join(", ")}`));
|
|
2198
|
+
process.exit(1);
|
|
1556
2199
|
}
|
|
1557
|
-
if (
|
|
1558
|
-
|
|
2200
|
+
if (!targetEnv) {
|
|
2201
|
+
spinner.fail(import_chalk14.default.red(`Target environment '${opts.to}' not found.`));
|
|
2202
|
+
console.log(import_chalk14.default.dim(` Available: ${envs.map((e) => e.envType ?? e.name).join(", ")}`));
|
|
2203
|
+
process.exit(1);
|
|
2204
|
+
}
|
|
2205
|
+
spinner.text = `Promoting ${opts.from} \u2192 ${opts.to}...`;
|
|
2206
|
+
const result = await api.promoteEnvironment(linked.appId, targetEnv.id, sourceEnv.id);
|
|
2207
|
+
if (result.promoted === 0) {
|
|
2208
|
+
spinner.warn(
|
|
2209
|
+
import_chalk14.default.yellow(
|
|
2210
|
+
`No services promoted from ${import_chalk14.default.bold(opts.from)} \u2014 nothing to push (source may have no deployed image yet)`
|
|
2211
|
+
)
|
|
2212
|
+
);
|
|
2213
|
+
} else {
|
|
2214
|
+
spinner.succeed(
|
|
2215
|
+
import_chalk14.default.green(
|
|
2216
|
+
`\u2713 Promoted ${import_chalk14.default.bold(opts.from)} \u2192 ${import_chalk14.default.bold(opts.to)} (${result.promoted} service${result.promoted !== 1 ? "s" : ""})`
|
|
2217
|
+
)
|
|
2218
|
+
);
|
|
2219
|
+
console.log(
|
|
2220
|
+
import_chalk14.default.dim(`
|
|
2221
|
+
Deployments queued. Run ${import_chalk14.default.cyan("sylphx status")} to monitor.`)
|
|
2222
|
+
);
|
|
1559
2223
|
}
|
|
1560
2224
|
} catch (err) {
|
|
1561
|
-
spinner.fail(
|
|
1562
|
-
import_chalk13.default.red(`Rollback failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
1563
|
-
);
|
|
2225
|
+
spinner.fail(import_chalk14.default.red(`Promote failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1564
2226
|
process.exit(1);
|
|
1565
2227
|
}
|
|
1566
2228
|
});
|
|
1567
2229
|
|
|
1568
|
-
// src/commands/
|
|
1569
|
-
var
|
|
1570
|
-
var
|
|
1571
|
-
var
|
|
1572
|
-
function
|
|
1573
|
-
const
|
|
1574
|
-
if (
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
if (s === "deploying" || s === "building" || s === "pending") {
|
|
1578
|
-
return import_chalk14.default.yellow(status);
|
|
1579
|
-
}
|
|
1580
|
-
if (s === "error" || s === "failed" || s === "stopped") {
|
|
1581
|
-
return import_chalk14.default.red(status);
|
|
2230
|
+
// src/commands/resources.ts
|
|
2231
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
2232
|
+
var import_commander14 = require("commander");
|
|
2233
|
+
var import_ora12 = __toESM(require("ora"));
|
|
2234
|
+
function requireLinkedApp4() {
|
|
2235
|
+
const linked = config.getLinkedApp();
|
|
2236
|
+
if (!linked) {
|
|
2237
|
+
console.log(import_chalk15.default.red("No app linked. Run `sylphx link` first."));
|
|
2238
|
+
process.exit(1);
|
|
1582
2239
|
}
|
|
1583
|
-
return
|
|
2240
|
+
return linked;
|
|
1584
2241
|
}
|
|
1585
|
-
|
|
2242
|
+
function requireToken2() {
|
|
1586
2243
|
const token = config.getToken();
|
|
1587
2244
|
if (!token) {
|
|
1588
|
-
console.log(
|
|
2245
|
+
console.log(import_chalk15.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1589
2246
|
process.exit(1);
|
|
1590
2247
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
2248
|
+
return token;
|
|
2249
|
+
}
|
|
2250
|
+
var listCmd2 = new import_commander14.Command("list").description("List managed resources (databases, volumes, storage)").option("--kind <kind>", "Filter by kind: database, volume, storage").action(async (opts) => {
|
|
2251
|
+
requireToken2();
|
|
2252
|
+
const spinner = (0, import_ora12.default)("Fetching resources...").start();
|
|
2253
|
+
try {
|
|
2254
|
+
const resources = await api.listDatabases();
|
|
2255
|
+
spinner.stop();
|
|
2256
|
+
if (resources.length === 0) {
|
|
2257
|
+
console.log(import_chalk15.default.dim(` No resources found.`));
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
console.log();
|
|
2261
|
+
console.log(
|
|
2262
|
+
` ${import_chalk15.default.bold("NAME".padEnd(24))} ${"KIND".padEnd(12)} ${"STATUS".padEnd(14)} ID`
|
|
2263
|
+
);
|
|
2264
|
+
console.log(` ${"\u2500".repeat(72)}`);
|
|
2265
|
+
for (const r of resources) {
|
|
2266
|
+
const statusColor2 = r.status === "ready" ? import_chalk15.default.green((r.status ?? "unknown").padEnd(14)) : import_chalk15.default.yellow((r.status ?? "unknown").padEnd(14));
|
|
2267
|
+
console.log(
|
|
2268
|
+
` ${import_chalk15.default.cyan(r.name.padEnd(24))} ${"database".padEnd(12)} ${statusColor2} ${import_chalk15.default.dim(r.id)}`
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
console.log();
|
|
2272
|
+
} catch (err) {
|
|
2273
|
+
spinner.fail(import_chalk15.default.red(err instanceof Error ? err.message : String(err)));
|
|
1594
2274
|
process.exit(1);
|
|
1595
2275
|
}
|
|
1596
|
-
|
|
1597
|
-
|
|
2276
|
+
});
|
|
2277
|
+
var getCmd2 = new import_commander14.Command("get").description("Get details of a resource").argument("<id>", "Resource ID").action(async (id) => {
|
|
2278
|
+
requireToken2();
|
|
2279
|
+
const spinner = (0, import_ora12.default)(`Fetching resource ${id}...`).start();
|
|
1598
2280
|
try {
|
|
1599
|
-
const
|
|
2281
|
+
const r = await api.getDatabase(id);
|
|
1600
2282
|
spinner.stop();
|
|
1601
|
-
console.log(
|
|
1602
|
-
console.log(
|
|
1603
|
-
console.log(
|
|
1604
|
-
console.log(
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
console.log(`
|
|
1608
|
-
if (
|
|
1609
|
-
|
|
1610
|
-
console.log(`
|
|
2283
|
+
console.log();
|
|
2284
|
+
console.log(` ${import_chalk15.default.bold("Name:")} ${import_chalk15.default.cyan(r.name)}`);
|
|
2285
|
+
console.log(` ${import_chalk15.default.bold("ID:")} ${r.id}`);
|
|
2286
|
+
console.log(
|
|
2287
|
+
` ${import_chalk15.default.bold("Status:")} ${r.status === "ready" ? import_chalk15.default.green(r.status) : import_chalk15.default.yellow(r.status ?? "unknown")}`
|
|
2288
|
+
);
|
|
2289
|
+
if (r.tier) console.log(` ${import_chalk15.default.bold("Tier:")} ${r.tier}`);
|
|
2290
|
+
if (r.host) console.log(` ${import_chalk15.default.bold("Host:")} ${r.host}${r.port ? `:${r.port}` : ""}`);
|
|
2291
|
+
if (r.connectionString) {
|
|
2292
|
+
console.log(` ${import_chalk15.default.bold("DSN:")} ${import_chalk15.default.dim(r.connectionString)}`);
|
|
1611
2293
|
}
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
2294
|
+
if (r.env) console.log(` ${import_chalk15.default.bold("Env:")} ${r.env}`);
|
|
2295
|
+
console.log();
|
|
2296
|
+
} catch (err) {
|
|
2297
|
+
spinner.fail(import_chalk15.default.red(err instanceof Error ? err.message : String(err)));
|
|
2298
|
+
process.exit(1);
|
|
2299
|
+
}
|
|
2300
|
+
});
|
|
2301
|
+
var bindingsCmd = new import_commander14.Command("bindings").description("List project bindings for a resource").argument("<id>", "Resource ID").action(async (id) => {
|
|
2302
|
+
requireToken2();
|
|
2303
|
+
const spinner = (0, import_ora12.default)(`Fetching bindings for ${id}...`).start();
|
|
2304
|
+
try {
|
|
2305
|
+
const bindings = await api.listResourceBindings(id);
|
|
2306
|
+
spinner.stop();
|
|
2307
|
+
if (bindings.length === 0) {
|
|
2308
|
+
console.log(import_chalk15.default.dim(" No bindings. Use `sylphx resources bind <id>` to link."));
|
|
2309
|
+
return;
|
|
1616
2310
|
}
|
|
1617
|
-
|
|
2311
|
+
console.log();
|
|
2312
|
+
console.log(
|
|
2313
|
+
` ${import_chalk15.default.bold("BINDING ID".padEnd(26))} ${"PROJECT".padEnd(24)} ${"ENV".padEnd(12)} ROLE`
|
|
2314
|
+
);
|
|
2315
|
+
console.log(` ${"\u2500".repeat(80)}`);
|
|
2316
|
+
for (const b of bindings) {
|
|
2317
|
+
const project = b.projectName ?? b.projectSlug ?? "\u2014";
|
|
2318
|
+
const env = b.envName ?? "\u2014";
|
|
1618
2319
|
console.log(
|
|
1619
|
-
`
|
|
2320
|
+
` ${import_chalk15.default.dim(b.id.padEnd(26))} ${project.padEnd(24)} ${env.padEnd(12)} ${b.role}`
|
|
1620
2321
|
);
|
|
1621
2322
|
}
|
|
1622
|
-
|
|
1623
|
-
const { ready, desired } = status.replicas;
|
|
1624
|
-
const replicaColor = ready === desired ? import_chalk14.default.green : import_chalk14.default.yellow;
|
|
1625
|
-
console.log(` Replicas: ${replicaColor(`${ready}/${desired}`)}`);
|
|
1626
|
-
}
|
|
1627
|
-
console.log("");
|
|
2323
|
+
console.log();
|
|
1628
2324
|
} catch (err) {
|
|
1629
|
-
spinner.fail(
|
|
2325
|
+
spinner.fail(import_chalk15.default.red(err instanceof Error ? err.message : String(err)));
|
|
1630
2326
|
process.exit(1);
|
|
1631
2327
|
}
|
|
1632
2328
|
});
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
2329
|
+
var bindCmd = new import_commander14.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(
|
|
2330
|
+
"--role <role>",
|
|
2331
|
+
"Binding role: primary, replica, analytics, backup (default: primary)",
|
|
2332
|
+
"primary"
|
|
2333
|
+
).action(async (id, opts) => {
|
|
2334
|
+
requireToken2();
|
|
2335
|
+
const linked = requireLinkedApp4();
|
|
2336
|
+
const spinner = (0, import_ora12.default)(`Binding resource ${id} to ${linked.appId} [${opts.env}]...`).start();
|
|
2337
|
+
try {
|
|
2338
|
+
const binding = await api.bindResource(id, {
|
|
2339
|
+
projectId: linked.appId,
|
|
2340
|
+
envType: opts.env,
|
|
2341
|
+
role: opts.role
|
|
2342
|
+
});
|
|
2343
|
+
spinner.succeed(import_chalk15.default.green(`\u2713 Resource bound (${binding.role})`));
|
|
2344
|
+
console.log();
|
|
2345
|
+
console.log(` ${import_chalk15.default.dim("Binding ID:")} ${binding.id}`);
|
|
2346
|
+
console.log(` ${import_chalk15.default.dim("Env:")} ${binding.envName ?? opts.env}`);
|
|
2347
|
+
console.log(
|
|
2348
|
+
import_chalk15.default.dim(
|
|
2349
|
+
`
|
|
2350
|
+
Run ${import_chalk15.default.cyan("sylphx deploy")} to apply the connection vars to your deployment.`
|
|
2351
|
+
)
|
|
2352
|
+
);
|
|
2353
|
+
} catch (err) {
|
|
2354
|
+
spinner.fail(import_chalk15.default.red(err instanceof Error ? err.message : String(err)));
|
|
2355
|
+
process.exit(1);
|
|
2356
|
+
}
|
|
2357
|
+
});
|
|
2358
|
+
var unbindCmd = new import_commander14.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) => {
|
|
2359
|
+
requireToken2();
|
|
2360
|
+
const spinner = (0, import_ora12.default)(`Removing binding ${bindingId}...`).start();
|
|
2361
|
+
try {
|
|
2362
|
+
await api.unbindResource(id, bindingId);
|
|
2363
|
+
spinner.succeed(import_chalk15.default.green(`\u2713 Binding removed`));
|
|
2364
|
+
} catch (err) {
|
|
2365
|
+
spinner.fail(import_chalk15.default.red(err instanceof Error ? err.message : String(err)));
|
|
2366
|
+
process.exit(1);
|
|
2367
|
+
}
|
|
2368
|
+
});
|
|
2369
|
+
var resourcesCommand = new import_commander14.Command("resources").description("Manage resources and project bindings").addCommand(listCmd2).addCommand(getCmd2).addCommand(bindingsCmd).addCommand(bindCmd).addCommand(unbindCmd).action(() => resourcesCommand.help());
|
|
2370
|
+
|
|
2371
|
+
// src/commands/rollback.ts
|
|
2372
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
2373
|
+
var import_commander15 = require("commander");
|
|
2374
|
+
var import_ora13 = __toESM(require("ora"));
|
|
2375
|
+
var rollbackCommand = new import_commander15.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) => {
|
|
2376
|
+
const token = config.getToken();
|
|
2377
|
+
if (!token) {
|
|
2378
|
+
console.log(import_chalk16.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2379
|
+
process.exit(1);
|
|
2380
|
+
}
|
|
2381
|
+
const linked = config.getLinkedApp();
|
|
2382
|
+
if (!linked) {
|
|
2383
|
+
console.log(import_chalk16.default.red("No app linked. Run `sylphx link` first."));
|
|
2384
|
+
process.exit(1);
|
|
2385
|
+
}
|
|
2386
|
+
const envType = opts.env ?? linked.defaultEnv ?? "production";
|
|
2387
|
+
if (!opts.force) {
|
|
2388
|
+
const readline9 = await import("readline");
|
|
2389
|
+
const rl = readline9.createInterface({ input: process.stdin, output: process.stdout });
|
|
2390
|
+
await new Promise((resolve) => {
|
|
2391
|
+
const label = opts.deployment ? `deployment ${import_chalk16.default.bold(opts.deployment)}` : `previous deployment`;
|
|
2392
|
+
rl.question(
|
|
2393
|
+
import_chalk16.default.yellow(` Rollback ${import_chalk16.default.bold(linked.appId)} [${envType}] to ${label}? (y/N) `),
|
|
2394
|
+
(answer) => {
|
|
2395
|
+
rl.close();
|
|
2396
|
+
if (answer.toLowerCase() !== "y") {
|
|
2397
|
+
console.log(import_chalk16.default.dim(" Cancelled."));
|
|
2398
|
+
process.exit(0);
|
|
2399
|
+
}
|
|
2400
|
+
resolve();
|
|
2401
|
+
}
|
|
2402
|
+
);
|
|
2403
|
+
});
|
|
2404
|
+
}
|
|
2405
|
+
const spinner = (0, import_ora13.default)(`Rolling back ${linked.appId} [${envType}]...`).start();
|
|
2406
|
+
try {
|
|
2407
|
+
let rollbackBody;
|
|
2408
|
+
if (opts.deployment) {
|
|
2409
|
+
rollbackBody = { deploymentRecordId: opts.deployment };
|
|
2410
|
+
} else {
|
|
2411
|
+
const status = await api.getDeploymentStatus(linked.appId);
|
|
2412
|
+
const envEntry = status.environments?.find(
|
|
2413
|
+
(e) => (e.envType ?? "").toLowerCase() === envType.toLowerCase()
|
|
2414
|
+
);
|
|
2415
|
+
if (!envEntry?.envId) {
|
|
2416
|
+
spinner.fail(
|
|
2417
|
+
import_chalk16.default.red(
|
|
2418
|
+
`Could not find environment '${envType}'. Run \`sylphx status\` to see available environments.`
|
|
2419
|
+
)
|
|
2420
|
+
);
|
|
2421
|
+
process.exit(1);
|
|
2422
|
+
}
|
|
2423
|
+
rollbackBody = { envId: envEntry.envId };
|
|
2424
|
+
}
|
|
2425
|
+
const result = await api.rollback(linked.appId, rollbackBody);
|
|
2426
|
+
spinner.succeed(import_chalk16.default.green(`\u2713 Rollback initiated`));
|
|
2427
|
+
if (result.deploymentId) {
|
|
2428
|
+
console.log(import_chalk16.default.dim(` Deployment ID: ${result.deploymentId}`));
|
|
2429
|
+
}
|
|
2430
|
+
if (result.status) {
|
|
2431
|
+
console.log(import_chalk16.default.dim(` Status: ${result.status}`));
|
|
2432
|
+
}
|
|
2433
|
+
if (result.message) {
|
|
2434
|
+
console.log(import_chalk16.default.dim(` ${result.message}`));
|
|
2435
|
+
}
|
|
2436
|
+
console.log(import_chalk16.default.dim(`
|
|
2437
|
+
Run ${import_chalk16.default.cyan("sylphx status")} to monitor progress.`));
|
|
2438
|
+
} catch (err) {
|
|
2439
|
+
spinner.fail(
|
|
2440
|
+
import_chalk16.default.red(`Rollback failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2441
|
+
);
|
|
2442
|
+
process.exit(1);
|
|
2443
|
+
}
|
|
2444
|
+
});
|
|
2445
|
+
|
|
2446
|
+
// src/commands/services.ts
|
|
2447
|
+
var import_node_readline5 = __toESM(require("readline"));
|
|
2448
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
2449
|
+
var import_commander16 = require("commander");
|
|
2450
|
+
var import_ora14 = __toESM(require("ora"));
|
|
2451
|
+
function requireLinkedApp5() {
|
|
2452
|
+
const linked = config.getLinkedApp();
|
|
2453
|
+
if (!linked) {
|
|
2454
|
+
console.log(import_chalk17.default.red("No app linked. Run `sylphx link` first."));
|
|
2455
|
+
process.exit(1);
|
|
2456
|
+
}
|
|
2457
|
+
return linked;
|
|
2458
|
+
}
|
|
2459
|
+
function requireToken3() {
|
|
2460
|
+
const token = config.getToken();
|
|
2461
|
+
if (!token) {
|
|
2462
|
+
console.log(import_chalk17.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2463
|
+
process.exit(1);
|
|
2464
|
+
}
|
|
2465
|
+
return token;
|
|
2466
|
+
}
|
|
2467
|
+
var listCmd3 = new import_commander16.Command("list").description("List all services in this project").action(async () => {
|
|
2468
|
+
requireToken3();
|
|
2469
|
+
const linked = requireLinkedApp5();
|
|
2470
|
+
const spinner = (0, import_ora14.default)("Fetching services...").start();
|
|
2471
|
+
try {
|
|
2472
|
+
const services = await api.listServices(linked.appId);
|
|
2473
|
+
spinner.stop();
|
|
2474
|
+
if (services.length === 0) {
|
|
2475
|
+
console.log(import_chalk17.default.dim(" No services defined."));
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
console.log();
|
|
2479
|
+
console.log(` ${import_chalk17.default.bold("NAME".padEnd(24))} ${"REPO".padEnd(36)} ENVS`);
|
|
2480
|
+
console.log(` ${"\u2500".repeat(70)}`);
|
|
2481
|
+
for (const svc of services) {
|
|
2482
|
+
const repo = svc.githubRepo ?? import_chalk17.default.dim("\u2014");
|
|
2483
|
+
const envs = svc.environmentCount ?? 0;
|
|
2484
|
+
console.log(` ${import_chalk17.default.cyan(svc.name.padEnd(24))} ${repo.padEnd(36)} ${envs}`);
|
|
2485
|
+
}
|
|
2486
|
+
console.log();
|
|
2487
|
+
} catch (err) {
|
|
2488
|
+
spinner.fail(import_chalk17.default.red(err instanceof Error ? err.message : String(err)));
|
|
2489
|
+
process.exit(1);
|
|
2490
|
+
}
|
|
2491
|
+
});
|
|
2492
|
+
var getCmd3 = new import_commander16.Command("get").description("Show details of a service").argument("<name>", "Service name").action(async (name) => {
|
|
2493
|
+
requireToken3();
|
|
2494
|
+
const linked = requireLinkedApp5();
|
|
2495
|
+
const spinner = (0, import_ora14.default)(`Fetching service '${name}'...`).start();
|
|
2496
|
+
try {
|
|
2497
|
+
const svc = await api.getService(linked.appId, name);
|
|
2498
|
+
spinner.stop();
|
|
2499
|
+
console.log();
|
|
2500
|
+
console.log(` ${import_chalk17.default.bold("Name:")} ${import_chalk17.default.cyan(svc.name)}`);
|
|
2501
|
+
console.log(` ${import_chalk17.default.bold("ID:")} ${svc.id}`);
|
|
2502
|
+
if (svc.githubRepo) console.log(` ${import_chalk17.default.bold("Repo:")} ${svc.githubRepo}`);
|
|
2503
|
+
if (svc.githubBranch) console.log(` ${import_chalk17.default.bold("Branch:")} ${svc.githubBranch}`);
|
|
2504
|
+
if (svc.dockerfilePath)
|
|
2505
|
+
console.log(` ${import_chalk17.default.bold("Dockerfile:")} ${svc.dockerfilePath}`);
|
|
2506
|
+
if (svc.port) console.log(` ${import_chalk17.default.bold("Port:")} ${svc.port}`);
|
|
2507
|
+
if (svc.publicNetworking !== void 0 && svc.publicNetworking !== null)
|
|
2508
|
+
console.log(
|
|
2509
|
+
` ${import_chalk17.default.bold("Public:")} ${svc.publicNetworking ? import_chalk17.default.green("yes") : import_chalk17.default.dim("no")}`
|
|
2510
|
+
);
|
|
2511
|
+
console.log();
|
|
2512
|
+
} catch (err) {
|
|
2513
|
+
spinner.fail(import_chalk17.default.red(err instanceof Error ? err.message : String(err)));
|
|
2514
|
+
process.exit(1);
|
|
2515
|
+
}
|
|
2516
|
+
});
|
|
2517
|
+
var deployCmd = new import_commander16.Command("deploy").description("Deploy a specific service").argument("<name>", "Service name").option("--env <env>", "Environment (default: production)", "production").action(async (name, opts) => {
|
|
2518
|
+
requireToken3();
|
|
2519
|
+
const linked = requireLinkedApp5();
|
|
2520
|
+
const spinner = (0, import_ora14.default)(`Deploying service '${name}' [${opts.env}]...`).start();
|
|
2521
|
+
try {
|
|
2522
|
+
const result = await api.deployService(linked.appId, name, opts.env);
|
|
2523
|
+
spinner.succeed(import_chalk17.default.green(`\u2713 Deploy triggered for service '${name}'`));
|
|
2524
|
+
if (result.deploymentId) {
|
|
2525
|
+
console.log(import_chalk17.default.dim(` Deployment ID: ${result.deploymentId}`));
|
|
2526
|
+
}
|
|
2527
|
+
console.log(import_chalk17.default.dim(`
|
|
2528
|
+
Run ${import_chalk17.default.cyan("sylphx status")} to monitor.`));
|
|
2529
|
+
} catch (err) {
|
|
2530
|
+
spinner.fail(import_chalk17.default.red(err instanceof Error ? err.message : String(err)));
|
|
2531
|
+
process.exit(1);
|
|
2532
|
+
}
|
|
2533
|
+
});
|
|
2534
|
+
var rmCmd2 = new import_commander16.Command("rm").description("Delete a service").argument("<name>", "Service name").option("--force", "Skip confirmation prompt").action(async (name, opts) => {
|
|
2535
|
+
requireToken3();
|
|
2536
|
+
const linked = requireLinkedApp5();
|
|
2537
|
+
if (!opts.force) {
|
|
2538
|
+
const rl = import_node_readline5.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
2539
|
+
await new Promise((resolve) => {
|
|
2540
|
+
rl.question(import_chalk17.default.yellow(` Delete service ${import_chalk17.default.bold(name)}? (y/N) `), (answer) => {
|
|
2541
|
+
rl.close();
|
|
2542
|
+
if (answer.toLowerCase() !== "y") {
|
|
2543
|
+
console.log(import_chalk17.default.dim(" Cancelled."));
|
|
2544
|
+
process.exit(0);
|
|
2545
|
+
}
|
|
2546
|
+
resolve();
|
|
2547
|
+
});
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
const spinner = (0, import_ora14.default)(`Deleting service '${name}'...`).start();
|
|
2551
|
+
try {
|
|
2552
|
+
await api.deleteService(linked.appId, name);
|
|
2553
|
+
spinner.succeed(import_chalk17.default.green(`\u2713 Service '${name}' deleted`));
|
|
2554
|
+
} catch (err) {
|
|
2555
|
+
spinner.fail(import_chalk17.default.red(err instanceof Error ? err.message : String(err)));
|
|
2556
|
+
process.exit(1);
|
|
2557
|
+
}
|
|
2558
|
+
});
|
|
2559
|
+
var servicesCommand = new import_commander16.Command("services").description("Manage project services").addCommand(listCmd3).addCommand(getCmd3).addCommand(deployCmd).addCommand(rmCmd2).action(() => servicesCommand.help());
|
|
2560
|
+
|
|
2561
|
+
// src/commands/status.ts
|
|
2562
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
2563
|
+
var import_commander17 = require("commander");
|
|
2564
|
+
var import_ora15 = __toESM(require("ora"));
|
|
2565
|
+
function statusColor(status) {
|
|
2566
|
+
const s = status.toLowerCase();
|
|
2567
|
+
if (s === "running" || s === "active" || s === "healthy" || s === "success") {
|
|
2568
|
+
return import_chalk18.default.green(status);
|
|
2569
|
+
}
|
|
2570
|
+
if (s === "deploying" || s === "building" || s === "pending") {
|
|
2571
|
+
return import_chalk18.default.yellow(status);
|
|
2572
|
+
}
|
|
2573
|
+
if (s === "error" || s === "failed" || s === "stopped") {
|
|
2574
|
+
return import_chalk18.default.red(status);
|
|
2575
|
+
}
|
|
2576
|
+
return import_chalk18.default.white(status);
|
|
2577
|
+
}
|
|
2578
|
+
var statusCommand = new import_commander17.Command("status").description("Show deployment status").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
|
|
2579
|
+
const token = config.getToken();
|
|
2580
|
+
if (!token) {
|
|
2581
|
+
console.log(import_chalk18.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2582
|
+
process.exit(1);
|
|
2583
|
+
}
|
|
2584
|
+
const linked = config.getLinkedApp();
|
|
2585
|
+
if (!linked) {
|
|
2586
|
+
console.log(import_chalk18.default.red("No app linked. Run `sylphx link` first."));
|
|
2587
|
+
process.exit(1);
|
|
2588
|
+
}
|
|
2589
|
+
const env = opts.env ?? linked.defaultEnv;
|
|
2590
|
+
const spinner = (0, import_ora15.default)(`Fetching status [${env}]...`).start();
|
|
2591
|
+
try {
|
|
2592
|
+
const status = await api.getDeploymentStatus(linked.appId);
|
|
2593
|
+
spinner.stop();
|
|
2594
|
+
console.log("");
|
|
2595
|
+
console.log(import_chalk18.default.bold(" Deployment Status"));
|
|
2596
|
+
console.log(import_chalk18.default.dim(` ${"\u2500".repeat(40)}`));
|
|
2597
|
+
console.log("");
|
|
2598
|
+
console.log(` App: ${import_chalk18.default.cyan(linked.appId)}`);
|
|
2599
|
+
console.log(` Environment: ${import_chalk18.default.white(env)}`);
|
|
2600
|
+
console.log(` Status: ${statusColor(status.status)}`);
|
|
2601
|
+
if (status.deployedAt) {
|
|
2602
|
+
const date = new Date(status.deployedAt);
|
|
2603
|
+
console.log(` Deployed: ${import_chalk18.default.white(date.toLocaleString())}`);
|
|
2604
|
+
}
|
|
2605
|
+
const envMatch = status.environments?.find((e) => e.envType === env) ?? status.environments?.find((e) => e.envType === "production") ?? status.environments?.[0];
|
|
2606
|
+
const displayUrl = status.url ?? envMatch?.url ?? null;
|
|
2607
|
+
if (displayUrl) {
|
|
2608
|
+
console.log(` URL: ${import_chalk18.default.cyan(displayUrl)}`);
|
|
2609
|
+
}
|
|
2610
|
+
if (envMatch?.lastDeployedAt) {
|
|
2611
|
+
console.log(
|
|
2612
|
+
` Deployed: ${import_chalk18.default.white(new Date(envMatch.lastDeployedAt).toLocaleString())}`
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
if (status.replicas) {
|
|
2616
|
+
const { ready, desired } = status.replicas;
|
|
2617
|
+
const replicaColor = ready === desired ? import_chalk18.default.green : import_chalk18.default.yellow;
|
|
2618
|
+
console.log(` Replicas: ${replicaColor(`${ready}/${desired}`)}`);
|
|
2619
|
+
}
|
|
2620
|
+
console.log("");
|
|
2621
|
+
} catch (err) {
|
|
2622
|
+
spinner.fail(import_chalk18.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
2623
|
+
process.exit(1);
|
|
2624
|
+
}
|
|
2625
|
+
});
|
|
2626
|
+
|
|
2627
|
+
// src/commands/storage.ts
|
|
2628
|
+
var import_node_readline6 = __toESM(require("readline"));
|
|
2629
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
2630
|
+
var import_commander18 = require("commander");
|
|
2631
|
+
var import_ora16 = __toESM(require("ora"));
|
|
2632
|
+
function requireLinkedApp6() {
|
|
2633
|
+
const linked = config.getLinkedApp();
|
|
2634
|
+
if (!linked) {
|
|
2635
|
+
console.log(import_chalk19.default.red("No app linked. Run `sylphx link` first."));
|
|
2636
|
+
process.exit(1);
|
|
2637
|
+
}
|
|
2638
|
+
return linked;
|
|
2639
|
+
}
|
|
2640
|
+
function requireToken4() {
|
|
2641
|
+
const token = config.getToken();
|
|
2642
|
+
if (!token) {
|
|
2643
|
+
console.log(import_chalk19.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2644
|
+
process.exit(1);
|
|
2645
|
+
}
|
|
2646
|
+
return token;
|
|
2647
|
+
}
|
|
2648
|
+
var listCmd4 = new import_commander18.Command("list").description("List storage resources for this project").option("--env <env>", "Environment (default: production)", "production").action(async (opts) => {
|
|
2649
|
+
requireToken4();
|
|
2650
|
+
const linked = requireLinkedApp6();
|
|
2651
|
+
const spinner = (0, import_ora16.default)("Fetching storage resources...").start();
|
|
2652
|
+
try {
|
|
2653
|
+
const resources = await api.listStorage(linked.appId, opts.env);
|
|
2654
|
+
spinner.stop();
|
|
2655
|
+
if (resources.length === 0) {
|
|
2656
|
+
console.log(import_chalk19.default.dim(" No storage resources provisioned."));
|
|
2657
|
+
console.log(
|
|
2658
|
+
import_chalk19.default.dim(`
|
|
2659
|
+
Run ${import_chalk19.default.cyan("sylphx storage create")} to provision storage.`)
|
|
2660
|
+
);
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
console.log();
|
|
2664
|
+
for (const r of resources) {
|
|
2665
|
+
const label = [r.role ?? "primary", r.envType].filter(Boolean).join(" \xB7 ");
|
|
2666
|
+
console.log(` ${import_chalk19.default.cyan(label)} ${import_chalk19.default.dim(r.id)}`);
|
|
2667
|
+
if (r.publicUrl) {
|
|
2668
|
+
console.log(` ${import_chalk19.default.dim("URL:")} ${import_chalk19.default.cyan(r.publicUrl)}`);
|
|
2669
|
+
}
|
|
2670
|
+
if (r.bucket) {
|
|
2671
|
+
console.log(` ${import_chalk19.default.dim("Bucket:")} ${r.bucket}`);
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
console.log();
|
|
2675
|
+
} catch (err) {
|
|
2676
|
+
spinner.fail(import_chalk19.default.red(err instanceof Error ? err.message : String(err)));
|
|
2677
|
+
process.exit(1);
|
|
2678
|
+
}
|
|
2679
|
+
});
|
|
2680
|
+
var createCmd = new import_commander18.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) => {
|
|
2681
|
+
requireToken4();
|
|
2682
|
+
const linked = requireLinkedApp6();
|
|
2683
|
+
const spinner = (0, import_ora16.default)(`Provisioning storage '${name}' [${opts.env}]...`).start();
|
|
2684
|
+
try {
|
|
2685
|
+
const resource = await api.createStorage(linked.appId, { name, envType: opts.env });
|
|
2686
|
+
spinner.succeed(import_chalk19.default.green(`\u2713 Storage '${name}' provisioned`));
|
|
2687
|
+
console.log();
|
|
2688
|
+
console.log(` ${import_chalk19.default.dim("ID:")} ${resource.id}`);
|
|
2689
|
+
if (resource.publicUrl) {
|
|
2690
|
+
console.log(` ${import_chalk19.default.dim("Public URL:")} ${import_chalk19.default.cyan(resource.publicUrl)}`);
|
|
2691
|
+
}
|
|
2692
|
+
if (resource.envVars && Object.keys(resource.envVars).length > 0) {
|
|
2693
|
+
console.log();
|
|
2694
|
+
console.log(import_chalk19.default.bold(" Env vars to add to your project:"));
|
|
2695
|
+
for (const [k, v] of Object.entries(resource.envVars)) {
|
|
2696
|
+
console.log(` ${import_chalk19.default.cyan(k)}=${v}`);
|
|
2697
|
+
}
|
|
2698
|
+
console.log();
|
|
2699
|
+
console.log(
|
|
2700
|
+
import_chalk19.default.dim(
|
|
2701
|
+
` Run ${import_chalk19.default.cyan("sylphx env set KEY=VALUE")} to add these to your project.`
|
|
2702
|
+
)
|
|
2703
|
+
);
|
|
2704
|
+
}
|
|
2705
|
+
} catch (err) {
|
|
2706
|
+
spinner.fail(import_chalk19.default.red(err instanceof Error ? err.message : String(err)));
|
|
2707
|
+
process.exit(1);
|
|
2708
|
+
}
|
|
2709
|
+
});
|
|
2710
|
+
var rmCmd3 = new import_commander18.Command("rm").description("Delete a storage resource").argument("<id>", "Storage resource ID").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
|
|
2711
|
+
requireToken4();
|
|
2712
|
+
const linked = requireLinkedApp6();
|
|
2713
|
+
if (!opts.force) {
|
|
2714
|
+
const rl = import_node_readline6.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
2715
|
+
await new Promise((resolve) => {
|
|
2716
|
+
rl.question(
|
|
2717
|
+
import_chalk19.default.yellow(` \u26A0\uFE0F Delete storage ${import_chalk19.default.bold(id)}? This is irreversible. (y/N) `),
|
|
2718
|
+
(answer) => {
|
|
2719
|
+
rl.close();
|
|
2720
|
+
if (answer.toLowerCase() !== "y") {
|
|
2721
|
+
console.log(import_chalk19.default.dim(" Cancelled."));
|
|
2722
|
+
process.exit(0);
|
|
2723
|
+
}
|
|
2724
|
+
resolve();
|
|
2725
|
+
}
|
|
2726
|
+
);
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2729
|
+
const spinner = (0, import_ora16.default)(`Deleting storage ${id}...`).start();
|
|
2730
|
+
try {
|
|
2731
|
+
await api.deleteStorage(linked.appId, id);
|
|
2732
|
+
spinner.succeed(import_chalk19.default.green(`\u2713 Storage ${id} deleted`));
|
|
2733
|
+
} catch (err) {
|
|
2734
|
+
spinner.fail(import_chalk19.default.red(err instanceof Error ? err.message : String(err)));
|
|
2735
|
+
process.exit(1);
|
|
2736
|
+
}
|
|
2737
|
+
});
|
|
2738
|
+
var storageCommand = new import_commander18.Command("storage").description("Manage blob storage resources").addCommand(listCmd4).addCommand(createCmd).addCommand(rmCmd3).action(() => storageCommand.help());
|
|
2739
|
+
|
|
2740
|
+
// src/commands/tasks.ts
|
|
2741
|
+
var import_node_readline7 = __toESM(require("readline"));
|
|
2742
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
2743
|
+
var import_commander19 = require("commander");
|
|
2744
|
+
var import_ora17 = __toESM(require("ora"));
|
|
2745
|
+
function requireLinkedApp7() {
|
|
2746
|
+
const linked = config.getLinkedApp();
|
|
2747
|
+
if (!linked) {
|
|
2748
|
+
console.log(import_chalk20.default.red("No app linked. Run `sylphx link` first."));
|
|
2749
|
+
process.exit(1);
|
|
2750
|
+
}
|
|
2751
|
+
return linked;
|
|
2752
|
+
}
|
|
2753
|
+
function requireToken5() {
|
|
2754
|
+
const token = config.getToken();
|
|
2755
|
+
if (!token) {
|
|
2756
|
+
console.log(import_chalk20.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2757
|
+
process.exit(1);
|
|
2758
|
+
}
|
|
2759
|
+
return token;
|
|
2760
|
+
}
|
|
2761
|
+
function formatMode(mode) {
|
|
2762
|
+
switch (mode) {
|
|
2763
|
+
case "job":
|
|
2764
|
+
return import_chalk20.default.blue("job");
|
|
2765
|
+
case "cron":
|
|
2766
|
+
return import_chalk20.default.magenta("cron");
|
|
2767
|
+
case "service":
|
|
2768
|
+
return import_chalk20.default.green("service");
|
|
2769
|
+
default:
|
|
2770
|
+
return import_chalk20.default.dim(mode);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
var listCmd5 = new import_commander19.Command("list").description("List task definitions for this project").option("--env <env>", "Environment (default: production)", "production").action(async (opts) => {
|
|
2774
|
+
requireToken5();
|
|
2775
|
+
const linked = requireLinkedApp7();
|
|
2776
|
+
const spinner = (0, import_ora17.default)("Fetching tasks...").start();
|
|
2777
|
+
try {
|
|
2778
|
+
const tasks = await api.listTasks(linked.appId, opts.env);
|
|
2779
|
+
spinner.stop();
|
|
2780
|
+
if (tasks.length === 0) {
|
|
2781
|
+
console.log(import_chalk20.default.dim(` No task definitions for [${opts.env}].`));
|
|
2782
|
+
console.log(
|
|
2783
|
+
import_chalk20.default.dim(`
|
|
2784
|
+
Run ${import_chalk20.default.cyan("sylphx tasks create <name>")} to define a task.`)
|
|
2785
|
+
);
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
console.log();
|
|
2789
|
+
console.log(
|
|
2790
|
+
` ${import_chalk20.default.bold("NAME".padEnd(28))} ${"MODE".padEnd(10)} ${"IMAGE / HANDLER".padEnd(40)} ID`
|
|
2791
|
+
);
|
|
2792
|
+
console.log(` ${"\u2500".repeat(90)}`);
|
|
2793
|
+
for (const t of tasks) {
|
|
2794
|
+
const label = t.imageRef ?? t.handlerPath ?? import_chalk20.default.dim("\u2014");
|
|
2795
|
+
console.log(
|
|
2796
|
+
` ${import_chalk20.default.cyan(t.taskName.padEnd(28))} ${formatMode(t.executionMode ?? "job").padEnd(10)} ${String(label).padEnd(40)} ${import_chalk20.default.dim(t.id)}`
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2799
|
+
console.log();
|
|
2800
|
+
} catch (err) {
|
|
2801
|
+
spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
|
|
2802
|
+
process.exit(1);
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
var getCmd4 = new import_commander19.Command("get").description("Show details of a task definition").argument("<task-id>", "Task ID (task_xxx)").action(async (taskId) => {
|
|
2806
|
+
requireToken5();
|
|
2807
|
+
const linked = requireLinkedApp7();
|
|
2808
|
+
const spinner = (0, import_ora17.default)(`Fetching task ${taskId}...`).start();
|
|
2809
|
+
try {
|
|
2810
|
+
const t = await api.getTask(linked.appId, taskId);
|
|
2811
|
+
spinner.stop();
|
|
2812
|
+
console.log();
|
|
2813
|
+
console.log(` ${import_chalk20.default.bold("Name:")} ${import_chalk20.default.cyan(t.taskName)}`);
|
|
2814
|
+
console.log(` ${import_chalk20.default.bold("ID:")} ${t.id}`);
|
|
2815
|
+
console.log(` ${import_chalk20.default.bold("Mode:")} ${formatMode(t.executionMode ?? "job")}`);
|
|
2816
|
+
if (t.imageRef) console.log(` ${import_chalk20.default.bold("Image:")} ${t.imageRef}`);
|
|
2817
|
+
if (t.handlerPath) console.log(` ${import_chalk20.default.bold("Handler:")} ${t.handlerPath}`);
|
|
2818
|
+
if (t.command?.length) console.log(` ${import_chalk20.default.bold("Command:")} ${t.command.join(" ")}`);
|
|
2819
|
+
if (t.timeoutSeconds) console.log(` ${import_chalk20.default.bold("Timeout:")} ${t.timeoutSeconds}s`);
|
|
2820
|
+
if (t.machineConfig) {
|
|
2821
|
+
const { cpu, memory, gpu } = t.machineConfig;
|
|
2822
|
+
const parts = [cpu && `cpu=${cpu}`, memory && `mem=${memory}`, gpu && `gpu=${gpu}`].filter(Boolean).join(" ");
|
|
2823
|
+
if (parts) console.log(` ${import_chalk20.default.bold("Resources:")} ${parts}`);
|
|
2824
|
+
}
|
|
2825
|
+
if (t.retryConfig) {
|
|
2826
|
+
console.log(
|
|
2827
|
+
` ${import_chalk20.default.bold("Retry:")} max=${t.retryConfig.maxAttempts ?? 1}, backoff=${t.retryConfig.backoff ?? "fixed"}`
|
|
2828
|
+
);
|
|
2829
|
+
}
|
|
2830
|
+
if (t.volumeMounts?.length) {
|
|
2831
|
+
console.log(` ${import_chalk20.default.bold("Volumes:")}`);
|
|
2832
|
+
for (const m of t.volumeMounts) {
|
|
2833
|
+
const ro = m.readOnly ? " (ro)" : "";
|
|
2834
|
+
console.log(` ${m.mountPath}${ro} \u2190 ${m.volumeId}`);
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
console.log();
|
|
2838
|
+
} catch (err) {
|
|
2839
|
+
spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
|
|
2840
|
+
process.exit(1);
|
|
2841
|
+
}
|
|
2842
|
+
});
|
|
2843
|
+
var createCmd2 = new import_commander19.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(
|
|
2844
|
+
async (name, opts) => {
|
|
2845
|
+
requireToken5();
|
|
2846
|
+
const linked = requireLinkedApp7();
|
|
2847
|
+
if (!["job", "cron", "service"].includes(opts.mode)) {
|
|
2848
|
+
console.log(import_chalk20.default.red(` --mode must be one of: job, cron, service`));
|
|
2849
|
+
process.exit(1);
|
|
2850
|
+
}
|
|
2851
|
+
const spinner = (0, import_ora17.default)(`Creating task '${name}' [${opts.env}]...`).start();
|
|
2852
|
+
try {
|
|
2853
|
+
const task = await api.createTask(linked.appId, {
|
|
2854
|
+
taskName: name,
|
|
2855
|
+
envType: opts.env,
|
|
2856
|
+
executionMode: opts.mode,
|
|
2857
|
+
imageRef: opts.image,
|
|
2858
|
+
handlerPath: opts.handler,
|
|
2859
|
+
command: opts.cmd ? opts.cmd.split(",").map((s) => s.trim()) : void 0,
|
|
2860
|
+
timeoutSeconds: opts.timeout ? Number.parseInt(opts.timeout, 10) : void 0,
|
|
2861
|
+
machineConfig: opts.cpu || opts.memory ? { cpu: opts.cpu, memory: opts.memory } : void 0
|
|
2862
|
+
});
|
|
2863
|
+
spinner.succeed(import_chalk20.default.green(`\u2713 Task '${task.taskName}' created`));
|
|
2864
|
+
console.log();
|
|
2865
|
+
console.log(` ${import_chalk20.default.dim("ID:")} ${task.id}`);
|
|
2866
|
+
console.log(` ${import_chalk20.default.dim("Mode:")} ${task.executionMode}`);
|
|
2867
|
+
if (task.imageRef) console.log(` ${import_chalk20.default.dim("Image:")} ${task.imageRef}`);
|
|
2868
|
+
console.log();
|
|
2869
|
+
} catch (err) {
|
|
2870
|
+
spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
|
|
2871
|
+
process.exit(1);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
);
|
|
2875
|
+
var updateCmd = new import_commander19.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(
|
|
2876
|
+
async (taskId, opts) => {
|
|
2877
|
+
requireToken5();
|
|
2878
|
+
const linked = requireLinkedApp7();
|
|
2879
|
+
const patch = {};
|
|
2880
|
+
if (opts.mode) patch.executionMode = opts.mode;
|
|
2881
|
+
if (opts.image) patch.imageRef = opts.image;
|
|
2882
|
+
if (opts.handler) patch.handlerPath = opts.handler;
|
|
2883
|
+
if (opts.cmd) patch.command = opts.cmd.split(",").map((s) => s.trim());
|
|
2884
|
+
if (opts.timeout) patch.timeoutSeconds = Number.parseInt(opts.timeout, 10);
|
|
2885
|
+
if (opts.cpu || opts.memory) patch.machineConfig = { cpu: opts.cpu, memory: opts.memory };
|
|
2886
|
+
if (Object.keys(patch).length === 0) {
|
|
2887
|
+
console.log(import_chalk20.default.yellow(" Nothing to update. Pass at least one option."));
|
|
2888
|
+
process.exit(1);
|
|
2889
|
+
}
|
|
2890
|
+
const spinner = (0, import_ora17.default)(`Updating task ${taskId}...`).start();
|
|
2891
|
+
try {
|
|
2892
|
+
const task = await api.updateTask(linked.appId, taskId, patch);
|
|
2893
|
+
spinner.succeed(import_chalk20.default.green(`\u2713 Task '${task.taskName}' updated`));
|
|
2894
|
+
} catch (err) {
|
|
2895
|
+
spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
|
|
2896
|
+
process.exit(1);
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
);
|
|
2900
|
+
var rmCmd4 = new import_commander19.Command("rm").description("Delete a task definition").argument("<task-id>", "Task ID (task_xxx)").option("--force", "Skip confirmation").action(async (taskId, opts) => {
|
|
2901
|
+
requireToken5();
|
|
2902
|
+
const linked = requireLinkedApp7();
|
|
2903
|
+
if (!opts.force) {
|
|
2904
|
+
const rl = import_node_readline7.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
2905
|
+
await new Promise((resolve) => {
|
|
2906
|
+
rl.question(import_chalk20.default.yellow(` Delete task ${import_chalk20.default.bold(taskId)}? (y/N) `), (answer) => {
|
|
2907
|
+
rl.close();
|
|
2908
|
+
if (answer.toLowerCase() !== "y") {
|
|
2909
|
+
console.log(import_chalk20.default.dim(" Cancelled."));
|
|
2910
|
+
process.exit(0);
|
|
2911
|
+
}
|
|
2912
|
+
resolve();
|
|
2913
|
+
});
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
const spinner = (0, import_ora17.default)(`Deleting task ${taskId}...`).start();
|
|
2917
|
+
try {
|
|
2918
|
+
await api.deleteTask(linked.appId, taskId);
|
|
2919
|
+
spinner.succeed(import_chalk20.default.green(`\u2713 Task ${taskId} deleted`));
|
|
2920
|
+
} catch (err) {
|
|
2921
|
+
spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
|
|
2922
|
+
process.exit(1);
|
|
2923
|
+
}
|
|
2924
|
+
});
|
|
2925
|
+
var tasksCommand = new import_commander19.Command("tasks").description("Manage task definitions (jobs, crons, services)").addCommand(listCmd5).addCommand(getCmd4).addCommand(createCmd2).addCommand(updateCmd).addCommand(rmCmd4).action(() => tasksCommand.help());
|
|
2926
|
+
|
|
2927
|
+
// src/commands/unlink.ts
|
|
2928
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
2929
|
+
var import_commander20 = require("commander");
|
|
2930
|
+
var unlinkCommand = new import_commander20.Command("unlink").description("Unlink current directory from its linked app").action(() => {
|
|
1638
2931
|
const linked = config.getLinkedApp();
|
|
1639
2932
|
if (!linked) {
|
|
1640
|
-
console.log(
|
|
2933
|
+
console.log(import_chalk21.default.yellow(" This directory is not linked to any app."));
|
|
1641
2934
|
process.exit(0);
|
|
1642
2935
|
}
|
|
1643
2936
|
const appId = linked.appId;
|
|
1644
2937
|
config.unlinkApp();
|
|
1645
2938
|
console.log("");
|
|
1646
|
-
console.log(
|
|
1647
|
-
console.log(
|
|
2939
|
+
console.log(import_chalk21.default.green(`\u2713 Unlinked from ${import_chalk21.default.bold(appId)}`));
|
|
2940
|
+
console.log(import_chalk21.default.dim(` Directory: ${process.cwd()}`));
|
|
1648
2941
|
console.log("");
|
|
1649
2942
|
});
|
|
1650
2943
|
|
|
2944
|
+
// src/commands/volumes.ts
|
|
2945
|
+
var import_node_readline8 = __toESM(require("readline"));
|
|
2946
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
2947
|
+
var import_commander21 = require("commander");
|
|
2948
|
+
var import_ora18 = __toESM(require("ora"));
|
|
2949
|
+
function requireLinkedApp8() {
|
|
2950
|
+
const linked = config.getLinkedApp();
|
|
2951
|
+
if (!linked) {
|
|
2952
|
+
console.log(import_chalk22.default.red("No app linked. Run `sylphx link` first."));
|
|
2953
|
+
process.exit(1);
|
|
2954
|
+
}
|
|
2955
|
+
return linked;
|
|
2956
|
+
}
|
|
2957
|
+
function requireToken6() {
|
|
2958
|
+
const token = config.getToken();
|
|
2959
|
+
if (!token) {
|
|
2960
|
+
console.log(import_chalk22.default.red("Not authenticated. Run `sylphx login` first."));
|
|
2961
|
+
process.exit(1);
|
|
2962
|
+
}
|
|
2963
|
+
return token;
|
|
2964
|
+
}
|
|
2965
|
+
var listCmd6 = new import_commander21.Command("list").description("List persistent volumes for this project").action(async () => {
|
|
2966
|
+
requireToken6();
|
|
2967
|
+
const linked = requireLinkedApp8();
|
|
2968
|
+
const spinner = (0, import_ora18.default)("Fetching volumes...").start();
|
|
2969
|
+
try {
|
|
2970
|
+
const volumes = await api.listVolumes(linked.appId);
|
|
2971
|
+
spinner.stop();
|
|
2972
|
+
if (volumes.length === 0) {
|
|
2973
|
+
console.log(import_chalk22.default.dim(" No volumes provisioned."));
|
|
2974
|
+
console.log(
|
|
2975
|
+
import_chalk22.default.dim(`
|
|
2976
|
+
Run ${import_chalk22.default.cyan("sylphx volumes create <name>")} to provision.`)
|
|
2977
|
+
);
|
|
2978
|
+
return;
|
|
2979
|
+
}
|
|
2980
|
+
console.log();
|
|
2981
|
+
console.log(
|
|
2982
|
+
` ${import_chalk22.default.bold("NAME".padEnd(24))} ${"SIZE".padEnd(8)} ${"STATUS".padEnd(14)} ID`
|
|
2983
|
+
);
|
|
2984
|
+
console.log(` ${"\u2500".repeat(70)}`);
|
|
2985
|
+
for (const v of volumes) {
|
|
2986
|
+
const statusColor2 = v.status === "ready" ? import_chalk22.default.green((v.status ?? "unknown").padEnd(14)) : import_chalk22.default.yellow((v.status ?? "unknown").padEnd(14));
|
|
2987
|
+
const size = v.sizeGb ? `${v.sizeGb}Gi`.padEnd(8) : "\u2014".padEnd(8);
|
|
2988
|
+
console.log(
|
|
2989
|
+
` ${import_chalk22.default.cyan(v.name.padEnd(24))} ${size} ${statusColor2} ${import_chalk22.default.dim(v.id)}`
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
console.log();
|
|
2993
|
+
} catch (err) {
|
|
2994
|
+
spinner.fail(import_chalk22.default.red(err instanceof Error ? err.message : String(err)));
|
|
2995
|
+
process.exit(1);
|
|
2996
|
+
}
|
|
2997
|
+
});
|
|
2998
|
+
var createCmd3 = new import_commander21.Command("create").description("Provision a persistent volume").argument("<name>", "Volume name").option("--size <gb>", "Size in GiB (default: 10)", "10").option(
|
|
2999
|
+
"--access-mode <mode>",
|
|
3000
|
+
"ReadWriteOnce or ReadWriteMany (default: ReadWriteOnce)",
|
|
3001
|
+
"ReadWriteOnce"
|
|
3002
|
+
).option("--mount <path>", "Default mount path (optional)").action(async (name, opts) => {
|
|
3003
|
+
requireToken6();
|
|
3004
|
+
const linked = requireLinkedApp8();
|
|
3005
|
+
const sizeGb = Number.parseInt(opts.size, 10);
|
|
3006
|
+
if (Number.isNaN(sizeGb) || sizeGb < 1) {
|
|
3007
|
+
console.log(import_chalk22.default.red(" --size must be a number >= 1"));
|
|
3008
|
+
process.exit(1);
|
|
3009
|
+
}
|
|
3010
|
+
if (!["ReadWriteOnce", "ReadWriteMany"].includes(opts.accessMode)) {
|
|
3011
|
+
console.log(import_chalk22.default.red(" --access-mode must be ReadWriteOnce or ReadWriteMany"));
|
|
3012
|
+
process.exit(1);
|
|
3013
|
+
}
|
|
3014
|
+
const spinner = (0, import_ora18.default)(`Provisioning volume '${name}' (${sizeGb}Gi)...`).start();
|
|
3015
|
+
try {
|
|
3016
|
+
const result = await api.createVolume(linked.appId, {
|
|
3017
|
+
name,
|
|
3018
|
+
sizeGb,
|
|
3019
|
+
accessMode: opts.accessMode,
|
|
3020
|
+
mountPath: opts.mount
|
|
3021
|
+
});
|
|
3022
|
+
const vol = result.volume;
|
|
3023
|
+
spinner.succeed(import_chalk22.default.green(`\u2713 Volume '${vol.name}' provisioned`));
|
|
3024
|
+
console.log();
|
|
3025
|
+
console.log(` ${import_chalk22.default.dim("ID:")} ${vol.id}`);
|
|
3026
|
+
console.log(` ${import_chalk22.default.dim("Size:")} ${vol.sizeGb}Gi`);
|
|
3027
|
+
console.log(` ${import_chalk22.default.dim("Access:")} ${vol.accessMode ?? "ReadWriteOnce"}`);
|
|
3028
|
+
console.log();
|
|
3029
|
+
} catch (err) {
|
|
3030
|
+
spinner.fail(import_chalk22.default.red(err instanceof Error ? err.message : String(err)));
|
|
3031
|
+
process.exit(1);
|
|
3032
|
+
}
|
|
3033
|
+
});
|
|
3034
|
+
var rmCmd5 = new import_commander21.Command("rm").description("Delete a persistent volume").argument("<id>", "Volume ID").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
|
|
3035
|
+
requireToken6();
|
|
3036
|
+
const linked = requireLinkedApp8();
|
|
3037
|
+
if (!opts.force) {
|
|
3038
|
+
const rl = import_node_readline8.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
3039
|
+
await new Promise((resolve) => {
|
|
3040
|
+
rl.question(
|
|
3041
|
+
import_chalk22.default.yellow(` \u26A0\uFE0F Delete volume ${import_chalk22.default.bold(id)}? This will destroy all data. (y/N) `),
|
|
3042
|
+
(answer) => {
|
|
3043
|
+
rl.close();
|
|
3044
|
+
if (answer.toLowerCase() !== "y") {
|
|
3045
|
+
console.log(import_chalk22.default.dim(" Cancelled."));
|
|
3046
|
+
process.exit(0);
|
|
3047
|
+
}
|
|
3048
|
+
resolve();
|
|
3049
|
+
}
|
|
3050
|
+
);
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
3053
|
+
const spinner = (0, import_ora18.default)(`Deleting volume ${id}...`).start();
|
|
3054
|
+
try {
|
|
3055
|
+
await api.deleteVolume(linked.appId, id);
|
|
3056
|
+
spinner.succeed(import_chalk22.default.green(`\u2713 Volume ${id} deleted`));
|
|
3057
|
+
} catch (err) {
|
|
3058
|
+
spinner.fail(import_chalk22.default.red(err instanceof Error ? err.message : String(err)));
|
|
3059
|
+
process.exit(1);
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
var volumesCommand = new import_commander21.Command("volumes").description("Manage persistent volumes").addCommand(listCmd6).addCommand(createCmd3).addCommand(rmCmd5).action(() => volumesCommand.help());
|
|
3063
|
+
|
|
1651
3064
|
// src/commands/whoami.ts
|
|
1652
|
-
var
|
|
1653
|
-
var
|
|
1654
|
-
var
|
|
1655
|
-
var whoamiCommand = new
|
|
3065
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
3066
|
+
var import_commander22 = require("commander");
|
|
3067
|
+
var import_ora19 = __toESM(require("ora"));
|
|
3068
|
+
var whoamiCommand = new import_commander22.Command("whoami").description("Show current user, org, and linked app").action(async () => {
|
|
1656
3069
|
const token = config.getToken();
|
|
1657
3070
|
if (!token) {
|
|
1658
|
-
console.log(
|
|
3071
|
+
console.log(import_chalk23.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1659
3072
|
process.exit(1);
|
|
1660
3073
|
}
|
|
1661
|
-
const spinner = (0,
|
|
3074
|
+
const spinner = (0, import_ora19.default)("Fetching account info...").start();
|
|
1662
3075
|
try {
|
|
1663
3076
|
const me = await api.whoami();
|
|
1664
3077
|
spinner.stop();
|
|
1665
3078
|
console.log("");
|
|
1666
|
-
console.log(
|
|
1667
|
-
console.log(` User: ${
|
|
1668
|
-
console.log(` Name: ${
|
|
1669
|
-
console.log(` ID: ${
|
|
3079
|
+
console.log(import_chalk23.default.bold(" Account"));
|
|
3080
|
+
console.log(` User: ${import_chalk23.default.cyan(me.user.email)}`);
|
|
3081
|
+
console.log(` Name: ${import_chalk23.default.white(me.user.name)}`);
|
|
3082
|
+
console.log(` ID: ${import_chalk23.default.dim(me.user.id)}`);
|
|
1670
3083
|
if (me.orgs.length > 0) {
|
|
1671
3084
|
console.log("");
|
|
1672
|
-
console.log(
|
|
3085
|
+
console.log(import_chalk23.default.bold(" Organizations"));
|
|
1673
3086
|
for (const org of me.orgs) {
|
|
1674
3087
|
console.log(
|
|
1675
|
-
` ${
|
|
3088
|
+
` ${import_chalk23.default.cyan(org.slug)} ${import_chalk23.default.dim(`(${org.name})`)}`
|
|
1676
3089
|
);
|
|
1677
3090
|
}
|
|
1678
3091
|
}
|
|
1679
3092
|
const linkedApp = config.getLinkedApp();
|
|
1680
3093
|
if (linkedApp) {
|
|
1681
3094
|
console.log("");
|
|
1682
|
-
console.log(
|
|
1683
|
-
console.log(` App: ${
|
|
1684
|
-
console.log(` Org: ${
|
|
1685
|
-
console.log(` Env: ${
|
|
3095
|
+
console.log(import_chalk23.default.bold(" Linked App"));
|
|
3096
|
+
console.log(` App: ${import_chalk23.default.cyan(linkedApp.appId)}`);
|
|
3097
|
+
console.log(` Org: ${import_chalk23.default.white(linkedApp.orgId)}`);
|
|
3098
|
+
console.log(` Env: ${import_chalk23.default.white(linkedApp.defaultEnv)}`);
|
|
1686
3099
|
} else {
|
|
1687
3100
|
console.log("");
|
|
1688
3101
|
console.log(
|
|
1689
|
-
|
|
3102
|
+
import_chalk23.default.dim(" No app linked. Run `sylphx link` to link one.")
|
|
1690
3103
|
);
|
|
1691
3104
|
}
|
|
1692
3105
|
console.log("");
|
|
1693
3106
|
} catch (err) {
|
|
1694
3107
|
spinner.fail(
|
|
1695
|
-
|
|
3108
|
+
import_chalk23.default.red(
|
|
1696
3109
|
`Failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1697
3110
|
)
|
|
1698
3111
|
);
|
|
@@ -1701,44 +3114,51 @@ var whoamiCommand = new import_commander15.Command("whoami").description("Show c
|
|
|
1701
3114
|
});
|
|
1702
3115
|
|
|
1703
3116
|
// src/index.ts
|
|
1704
|
-
var { version } = package_default;
|
|
1705
|
-
var program = new
|
|
1706
|
-
program.name("sylphx").description(`${
|
|
3117
|
+
var { version: version4 } = package_default;
|
|
3118
|
+
var program = new import_commander23.Command();
|
|
3119
|
+
program.name("sylphx").description(`${import_chalk24.default.bold("Sylphx Platform CLI")} \u2014 deploy and manage your applications`).version(version4, "-v, --version", "Print version").helpOption("-h, --help", "Show help").addHelpText(
|
|
1707
3120
|
"after",
|
|
1708
3121
|
`
|
|
1709
|
-
${
|
|
1710
|
-
${
|
|
1711
|
-
${
|
|
1712
|
-
${
|
|
1713
|
-
${
|
|
1714
|
-
${
|
|
1715
|
-
${
|
|
1716
|
-
${
|
|
1717
|
-
${
|
|
1718
|
-
${
|
|
1719
|
-
${
|
|
1720
|
-
${
|
|
1721
|
-
${
|
|
1722
|
-
${
|
|
1723
|
-
${
|
|
1724
|
-
${
|
|
3122
|
+
${import_chalk24.default.bold("Examples:")}
|
|
3123
|
+
${import_chalk24.default.cyan("sylphx login")} Authenticate with Sylphx
|
|
3124
|
+
${import_chalk24.default.cyan("sylphx init my-app")} Create and link a new project
|
|
3125
|
+
${import_chalk24.default.cyan("sylphx link")} Link current directory to an app
|
|
3126
|
+
${import_chalk24.default.cyan("sylphx deploy")} Deploy to production
|
|
3127
|
+
${import_chalk24.default.cyan("sylphx deploy --env staging")} Deploy to staging
|
|
3128
|
+
${import_chalk24.default.cyan("sylphx promote --from staging")} Promote staging build \u2192 production
|
|
3129
|
+
${import_chalk24.default.cyan("sylphx rollback")} Rollback to previous deployment
|
|
3130
|
+
${import_chalk24.default.cyan("sylphx logs -f")} Stream live logs
|
|
3131
|
+
${import_chalk24.default.cyan("sylphx status")} Check deployment status
|
|
3132
|
+
${import_chalk24.default.cyan("sylphx env list")} List environment variables
|
|
3133
|
+
${import_chalk24.default.cyan("sylphx env set PORT=3000")} Set an env var
|
|
3134
|
+
${import_chalk24.default.cyan("sylphx config list")} List remote config keys
|
|
3135
|
+
${import_chalk24.default.cyan("sylphx config set KEY=value")} Set a remote config key
|
|
3136
|
+
${import_chalk24.default.cyan("sylphx db create my-db")} Provision a PostgreSQL database
|
|
3137
|
+
${import_chalk24.default.cyan("sylphx storage create")} Provision blob storage
|
|
3138
|
+
${import_chalk24.default.cyan("sylphx volumes create data --size 20")} Provision a persistent volume
|
|
3139
|
+
${import_chalk24.default.cyan("sylphx services list")} List project services
|
|
3140
|
+
${import_chalk24.default.cyan("sylphx services deploy web")} Deploy a specific service
|
|
3141
|
+
${import_chalk24.default.cyan("sylphx tasks list")} List task definitions
|
|
3142
|
+
${import_chalk24.default.cyan("sylphx tasks create <name> --image nginx")} Create a task
|
|
3143
|
+
${import_chalk24.default.cyan("sylphx resources bind <id>")} Bind a resource to this project
|
|
3144
|
+
${import_chalk24.default.cyan("sylphx projects list")} List all projects
|
|
1725
3145
|
|
|
1726
|
-
${
|
|
1727
|
-
${
|
|
3146
|
+
${import_chalk24.default.bold("Documentation:")}
|
|
3147
|
+
${import_chalk24.default.underline("https://docs.sylphx.com/cli")}
|
|
1728
3148
|
`
|
|
1729
3149
|
);
|
|
1730
|
-
program.addCommand(loginCommand).addCommand(logoutCommand).addCommand(whoamiCommand).addCommand(initCommand).addCommand(linkCommand).addCommand(unlinkCommand).addCommand(deployCommand).addCommand(logsCommand).addCommand(envCommand).addCommand(
|
|
3150
|
+
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(tasksCommand).addCommand(servicesCommand).addCommand(domainsCommand).addCommand(openCommand);
|
|
1731
3151
|
program.on("command:*", (operands) => {
|
|
1732
|
-
console.error(
|
|
1733
|
-
Unknown command: ${
|
|
3152
|
+
console.error(import_chalk24.default.red(`
|
|
3153
|
+
Unknown command: ${import_chalk24.default.bold(operands[0] ?? "")}
|
|
1734
3154
|
`));
|
|
1735
|
-
console.log(` Run ${
|
|
3155
|
+
console.log(` Run ${import_chalk24.default.cyan("sylphx --help")} for usage.
|
|
1736
3156
|
`);
|
|
1737
3157
|
process.exit(1);
|
|
1738
3158
|
});
|
|
1739
3159
|
program.parseAsync(process.argv).catch((err) => {
|
|
1740
3160
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1741
|
-
console.error(
|
|
3161
|
+
console.error(import_chalk24.default.red(`
|
|
1742
3162
|
Error: ${msg}
|
|
1743
3163
|
`));
|
|
1744
3164
|
process.exit(1);
|