@sylphx/cli 0.2.0 → 0.3.1

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