@sylphx/cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +370 -133
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24,13 +24,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_chalk23 = __toESM(require("chalk"));
28
- var import_commander22 = require("commander");
27
+ var import_chalk24 = __toESM(require("chalk"));
28
+ var import_commander23 = require("commander");
29
29
 
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "@sylphx/cli",
33
- version: "0.2.0",
33
+ version: "0.3.0",
34
34
  description: "Sylphx Platform CLI \u2014 deploy, manage logs, env vars, and more",
35
35
  type: "commonjs",
36
36
  bin: {
@@ -254,73 +254,54 @@ var api = {
254
254
  // ── Domain Management (Unified System) ──────────────────────────────────
255
255
  /** List all apex domains for a project environment */
256
256
  async listDomains(projectId, envType = "production") {
257
- return request("GET", `/domains/projects/${projectId}/domains`, {
257
+ return request("GET", `/projects/${projectId}/domains`, {
258
258
  query: { envType }
259
259
  });
260
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
- );
268
- },
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
- );
276
- },
277
261
  /** Register an apex domain for a project (full domain object with DNS records) */
278
262
  async createDomain(projectId, apexDomain, envType = "production") {
279
- return request("POST", `/domains/projects/${projectId}/domains`, {
263
+ return request("POST", `/projects/${projectId}/domains`, {
280
264
  body: { apexDomain, envType }
281
265
  });
282
266
  },
283
267
  /** Add a hostname binding to an existing domain */
284
268
  async addHostname(projectId, domainId, hostname, opts) {
285
269
  const { envType = "production", ...bodyOpts } = opts ?? {};
286
- return request(
287
- "POST",
288
- `/domains/projects/${projectId}/domains/${domainId}/hostnames`,
289
- { query: { envType }, body: { hostname, ...bodyOpts } }
290
- );
270
+ return request("POST", `/projects/${projectId}/domains/${domainId}/hostnames`, {
271
+ query: { envType },
272
+ body: { hostname, ...bodyOpts }
273
+ });
291
274
  },
292
275
  /** Remove a hostname binding */
293
276
  async removeHostname(projectId, domainId, hostnameId) {
294
277
  return request(
295
278
  "DELETE",
296
- `/domains/projects/${projectId}/domains/${domainId}/hostnames/${hostnameId}`
279
+ `/projects/${projectId}/domains/${domainId}/hostnames/${hostnameId}`
297
280
  );
298
281
  },
299
282
  /** Trigger DNS check for a hostname */
300
283
  async checkHostname(projectId, domainId, hostnameId) {
301
284
  return request(
302
285
  "POST",
303
- `/domains/projects/${projectId}/domains/${domainId}/hostnames/${hostnameId}/check`
286
+ `/projects/${projectId}/domains/${domainId}/hostnames/${hostnameId}/check`
304
287
  );
305
288
  },
306
289
  /** Enable email sending for a domain (generates DKIM keypair) */
307
290
  async enableEmail(projectId, domainId, opts) {
308
- return request(
309
- "POST",
310
- `/domains/projects/${projectId}/domains/${domainId}/email`,
311
- { body: opts ?? {} }
312
- );
291
+ return request("POST", `/projects/${projectId}/domains/${domainId}/email`, {
292
+ body: opts ?? {}
293
+ });
313
294
  },
314
295
  /** Check DKIM DNS propagation and load into Stalwart */
315
296
  async checkEmail(projectId, domainId) {
316
297
  return request(
317
298
  "POST",
318
- `/domains/${projectId}/domains/${domainId}/email/check`
299
+ `/projects/${projectId}/domains/${domainId}/email/check`
319
300
  );
320
301
  },
321
302
  /** Disable email sending for a domain */
322
303
  async disableEmail(projectId, domainId) {
323
- return request("DELETE", `/domains/${projectId}/domains/${domainId}/email`);
304
+ return request("DELETE", `/projects/${projectId}/domains/${domainId}/email`);
324
305
  },
325
306
  /** Get current user and orgs */
326
307
  async whoami() {
@@ -597,6 +578,64 @@ var api = {
597
578
  "DELETE",
598
579
  `/resources/${encodeURIComponent(resourceId)}/bindings/${encodeURIComponent(bindingId)}`
599
580
  );
581
+ },
582
+ // ── Tasks ─────────────────────────────────────────────────────────────────
583
+ /**
584
+ * List task definitions for a project environment.
585
+ * Uses ?envId=<envId> (resolved from envType automatically).
586
+ */
587
+ async listTasks(projectId, envType) {
588
+ const query = {};
589
+ if (envType) {
590
+ const envId = await this.resolveEnvId(projectId, envType);
591
+ if (envId) query.envId = envId;
592
+ }
593
+ const res = await request(
594
+ "GET",
595
+ `/projects/${encodeURIComponent(projectId)}/tasks`,
596
+ { query }
597
+ );
598
+ return res.tasks ?? [];
599
+ },
600
+ /**
601
+ * Create or update a task definition (upserts on taskName + environmentId).
602
+ * Backend requires envId — resolves from envType automatically.
603
+ */
604
+ async createTask(projectId, data) {
605
+ const envId = await this.resolveEnvId(projectId, data.envType);
606
+ if (!envId)
607
+ throw new Error(`Environment '${data.envType}' not found for project '${projectId}'`);
608
+ const { envType: _drop, ...rest } = data;
609
+ const res = await request(
610
+ "POST",
611
+ `/projects/${encodeURIComponent(projectId)}/tasks`,
612
+ { body: { ...rest, envId } }
613
+ );
614
+ return res.task;
615
+ },
616
+ /** Get a single task definition */
617
+ async getTask(projectId, taskId) {
618
+ const res = await request(
619
+ "GET",
620
+ `/projects/${encodeURIComponent(projectId)}/tasks/${encodeURIComponent(taskId)}`
621
+ );
622
+ return res.task;
623
+ },
624
+ /** Update a task definition */
625
+ async updateTask(projectId, taskId, data) {
626
+ const res = await request(
627
+ "PATCH",
628
+ `/projects/${encodeURIComponent(projectId)}/tasks/${encodeURIComponent(taskId)}`,
629
+ { body: data }
630
+ );
631
+ return res.task;
632
+ },
633
+ /** Delete a task definition */
634
+ async deleteTask(projectId, taskId) {
635
+ await request(
636
+ "DELETE",
637
+ `/projects/${encodeURIComponent(projectId)}/tasks/${encodeURIComponent(taskId)}`
638
+ );
600
639
  }
601
640
  };
602
641
  function createLogStream(projectId, envType) {
@@ -800,14 +839,23 @@ var dbListCommand = new import_commander2.Command("list").description("List all
800
839
  process.exit(1);
801
840
  }
802
841
  });
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) => {
842
+ var dbCreateCommand = new import_commander2.Command("create").description("Provision a new database").argument("<name>", "Database name").option(
843
+ "--tier <tier>",
844
+ "Database tier: starter | standard | performance | enterprise1 | enterprise2 (default: standard)",
845
+ "standard"
846
+ ).option("--storage <gb>", "Storage in GB (default: 10)", "10").option("--env <env>", "Environment (default: production)", "production").action(async (name, opts) => {
804
847
  requireAuth();
805
848
  const storageGb = Number.parseInt(opts.storage, 10);
806
849
  if (Number.isNaN(storageGb) || storageGb < 5) {
807
850
  console.log(import_chalk2.default.red("Storage must be at least 5 GB."));
808
851
  process.exit(1);
809
852
  }
853
+ const VALID_TIERS = ["starter", "standard", "performance", "enterprise1", "enterprise2"];
810
854
  const tier = opts.tier === "pro" ? "performance" : opts.tier;
855
+ if (!VALID_TIERS.includes(tier)) {
856
+ console.log(import_chalk2.default.red(`Invalid tier '${tier}'. Valid: ${VALID_TIERS.join(", ")}`));
857
+ process.exit(1);
858
+ }
811
859
  const spinner = (0, import_ora2.default)(
812
860
  `Provisioning database ${import_chalk2.default.bold(name)} (${tier}, ${storageGb} GB)...`
813
861
  ).start();
@@ -2117,8 +2165,8 @@ var promoteCommand = new import_commander13.Command("promote").description("Prom
2117
2165
  process.exit(1);
2118
2166
  }
2119
2167
  if (!opts.force) {
2120
- const readline8 = await import("readline");
2121
- const rl = readline8.createInterface({ input: process.stdin, output: process.stdout });
2168
+ const readline9 = await import("readline");
2169
+ const rl = readline9.createInterface({ input: process.stdin, output: process.stdout });
2122
2170
  await new Promise((resolve) => {
2123
2171
  rl.question(
2124
2172
  import_chalk14.default.yellow(
@@ -2337,8 +2385,8 @@ var rollbackCommand = new import_commander15.Command("rollback").description("Ro
2337
2385
  }
2338
2386
  const envType = opts.env ?? linked.defaultEnv ?? "production";
2339
2387
  if (!opts.force) {
2340
- const readline8 = await import("readline");
2341
- const rl = readline8.createInterface({ input: process.stdin, output: process.stdout });
2388
+ const readline9 = await import("readline");
2389
+ const rl = readline9.createInterface({ input: process.stdin, output: process.stdout });
2342
2390
  await new Promise((resolve) => {
2343
2391
  const label = opts.deployment ? `deployment ${import_chalk16.default.bold(opts.deployment)}` : `previous deployment`;
2344
2392
  rl.question(
@@ -2689,94 +2737,281 @@ var rmCmd3 = new import_commander18.Command("rm").description("Delete a storage
2689
2737
  });
2690
2738
  var storageCommand = new import_commander18.Command("storage").description("Manage blob storage resources").addCommand(listCmd4).addCommand(createCmd).addCommand(rmCmd3).action(() => storageCommand.help());
2691
2739
 
2692
- // src/commands/unlink.ts
2740
+ // src/commands/tasks.ts
2741
+ var import_node_readline7 = __toESM(require("readline"));
2693
2742
  var import_chalk20 = __toESM(require("chalk"));
2694
2743
  var import_commander19 = require("commander");
2695
- var unlinkCommand = new import_commander19.Command("unlink").description("Unlink current directory from its linked app").action(() => {
2744
+ var import_ora17 = __toESM(require("ora"));
2745
+ function requireLinkedApp7() {
2746
+ const linked = config.getLinkedApp();
2747
+ if (!linked) {
2748
+ console.log(import_chalk20.default.red("No app linked. Run `sylphx link` first."));
2749
+ process.exit(1);
2750
+ }
2751
+ return linked;
2752
+ }
2753
+ function requireToken5() {
2754
+ const token = config.getToken();
2755
+ if (!token) {
2756
+ console.log(import_chalk20.default.red("Not authenticated. Run `sylphx login` first."));
2757
+ process.exit(1);
2758
+ }
2759
+ return token;
2760
+ }
2761
+ function formatMode(mode) {
2762
+ switch (mode) {
2763
+ case "job":
2764
+ return import_chalk20.default.blue("job");
2765
+ case "cron":
2766
+ return import_chalk20.default.magenta("cron");
2767
+ case "service":
2768
+ return import_chalk20.default.green("service");
2769
+ default:
2770
+ return import_chalk20.default.dim(mode);
2771
+ }
2772
+ }
2773
+ var listCmd5 = new import_commander19.Command("list").description("List task definitions for this project").option("--env <env>", "Environment (default: production)", "production").action(async (opts) => {
2774
+ requireToken5();
2775
+ const linked = requireLinkedApp7();
2776
+ const spinner = (0, import_ora17.default)("Fetching tasks...").start();
2777
+ try {
2778
+ const tasks = await api.listTasks(linked.appId, opts.env);
2779
+ spinner.stop();
2780
+ if (tasks.length === 0) {
2781
+ console.log(import_chalk20.default.dim(` No task definitions for [${opts.env}].`));
2782
+ console.log(
2783
+ import_chalk20.default.dim(`
2784
+ Run ${import_chalk20.default.cyan("sylphx tasks create <name>")} to define a task.`)
2785
+ );
2786
+ return;
2787
+ }
2788
+ console.log();
2789
+ console.log(
2790
+ ` ${import_chalk20.default.bold("NAME".padEnd(28))} ${"MODE".padEnd(10)} ${"IMAGE / HANDLER".padEnd(40)} ID`
2791
+ );
2792
+ console.log(` ${"\u2500".repeat(90)}`);
2793
+ for (const t of tasks) {
2794
+ const label = t.imageRef ?? t.handlerPath ?? import_chalk20.default.dim("\u2014");
2795
+ console.log(
2796
+ ` ${import_chalk20.default.cyan(t.taskName.padEnd(28))} ${formatMode(t.executionMode ?? "job").padEnd(10)} ${String(label).padEnd(40)} ${import_chalk20.default.dim(t.id)}`
2797
+ );
2798
+ }
2799
+ console.log();
2800
+ } catch (err) {
2801
+ spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
2802
+ process.exit(1);
2803
+ }
2804
+ });
2805
+ var getCmd4 = new import_commander19.Command("get").description("Show details of a task definition").argument("<task-id>", "Task ID (task_xxx)").action(async (taskId) => {
2806
+ requireToken5();
2807
+ const linked = requireLinkedApp7();
2808
+ const spinner = (0, import_ora17.default)(`Fetching task ${taskId}...`).start();
2809
+ try {
2810
+ const t = await api.getTask(linked.appId, taskId);
2811
+ spinner.stop();
2812
+ console.log();
2813
+ console.log(` ${import_chalk20.default.bold("Name:")} ${import_chalk20.default.cyan(t.taskName)}`);
2814
+ console.log(` ${import_chalk20.default.bold("ID:")} ${t.id}`);
2815
+ console.log(` ${import_chalk20.default.bold("Mode:")} ${formatMode(t.executionMode ?? "job")}`);
2816
+ if (t.imageRef) console.log(` ${import_chalk20.default.bold("Image:")} ${t.imageRef}`);
2817
+ if (t.handlerPath) console.log(` ${import_chalk20.default.bold("Handler:")} ${t.handlerPath}`);
2818
+ if (t.command?.length) console.log(` ${import_chalk20.default.bold("Command:")} ${t.command.join(" ")}`);
2819
+ if (t.timeoutSeconds) console.log(` ${import_chalk20.default.bold("Timeout:")} ${t.timeoutSeconds}s`);
2820
+ if (t.machineConfig) {
2821
+ const { cpu, memory, gpu } = t.machineConfig;
2822
+ const parts = [cpu && `cpu=${cpu}`, memory && `mem=${memory}`, gpu && `gpu=${gpu}`].filter(Boolean).join(" ");
2823
+ if (parts) console.log(` ${import_chalk20.default.bold("Resources:")} ${parts}`);
2824
+ }
2825
+ if (t.retryConfig) {
2826
+ console.log(
2827
+ ` ${import_chalk20.default.bold("Retry:")} max=${t.retryConfig.maxAttempts ?? 1}, backoff=${t.retryConfig.backoff ?? "fixed"}`
2828
+ );
2829
+ }
2830
+ if (t.volumeMounts?.length) {
2831
+ console.log(` ${import_chalk20.default.bold("Volumes:")}`);
2832
+ for (const m of t.volumeMounts) {
2833
+ const ro = m.readOnly ? " (ro)" : "";
2834
+ console.log(` ${m.mountPath}${ro} \u2190 ${m.volumeId}`);
2835
+ }
2836
+ }
2837
+ console.log();
2838
+ } catch (err) {
2839
+ spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
2840
+ process.exit(1);
2841
+ }
2842
+ });
2843
+ var createCmd2 = new import_commander19.Command("create").description("Create (or upsert) a task definition").argument("<name>", "Task name (unique within the environment)").option("--env <env>", "Environment (default: production)", "production").option("--mode <mode>", "Execution mode: job | cron | service (default: job)", "job").option("--image <ref>", "Docker image reference").option("--handler <path>", "Handler function path (e.g. src/tasks/send-email.ts)").option("--cmd <cmd>", 'Command to run (comma-separated, e.g. "node,dist/worker.js")').option("--timeout <secs>", "Timeout in seconds").option("--cpu <cpu>", "CPU request (e.g. 0.5, 2)").option("--memory <mem>", "Memory request (e.g. 512Mi, 2Gi)").action(
2844
+ async (name, opts) => {
2845
+ requireToken5();
2846
+ const linked = requireLinkedApp7();
2847
+ if (!["job", "cron", "service"].includes(opts.mode)) {
2848
+ console.log(import_chalk20.default.red(` --mode must be one of: job, cron, service`));
2849
+ process.exit(1);
2850
+ }
2851
+ const spinner = (0, import_ora17.default)(`Creating task '${name}' [${opts.env}]...`).start();
2852
+ try {
2853
+ const task = await api.createTask(linked.appId, {
2854
+ taskName: name,
2855
+ envType: opts.env,
2856
+ executionMode: opts.mode,
2857
+ imageRef: opts.image,
2858
+ handlerPath: opts.handler,
2859
+ command: opts.cmd ? opts.cmd.split(",").map((s) => s.trim()) : void 0,
2860
+ timeoutSeconds: opts.timeout ? Number.parseInt(opts.timeout, 10) : void 0,
2861
+ machineConfig: opts.cpu || opts.memory ? { cpu: opts.cpu, memory: opts.memory } : void 0
2862
+ });
2863
+ spinner.succeed(import_chalk20.default.green(`\u2713 Task '${task.taskName}' created`));
2864
+ console.log();
2865
+ console.log(` ${import_chalk20.default.dim("ID:")} ${task.id}`);
2866
+ console.log(` ${import_chalk20.default.dim("Mode:")} ${task.executionMode}`);
2867
+ if (task.imageRef) console.log(` ${import_chalk20.default.dim("Image:")} ${task.imageRef}`);
2868
+ console.log();
2869
+ } catch (err) {
2870
+ spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
2871
+ process.exit(1);
2872
+ }
2873
+ }
2874
+ );
2875
+ var updateCmd = new import_commander19.Command("update").description("Update a task definition").argument("<task-id>", "Task ID (task_xxx)").option("--mode <mode>", "Execution mode: job | cron | service").option("--image <ref>", "Docker image reference").option("--handler <path>", "Handler function path").option("--cmd <cmd>", "Command (comma-separated)").option("--timeout <secs>", "Timeout in seconds").option("--cpu <cpu>", "CPU request").option("--memory <mem>", "Memory request").action(
2876
+ async (taskId, opts) => {
2877
+ requireToken5();
2878
+ const linked = requireLinkedApp7();
2879
+ const patch = {};
2880
+ if (opts.mode) patch.executionMode = opts.mode;
2881
+ if (opts.image) patch.imageRef = opts.image;
2882
+ if (opts.handler) patch.handlerPath = opts.handler;
2883
+ if (opts.cmd) patch.command = opts.cmd.split(",").map((s) => s.trim());
2884
+ if (opts.timeout) patch.timeoutSeconds = Number.parseInt(opts.timeout, 10);
2885
+ if (opts.cpu || opts.memory) patch.machineConfig = { cpu: opts.cpu, memory: opts.memory };
2886
+ if (Object.keys(patch).length === 0) {
2887
+ console.log(import_chalk20.default.yellow(" Nothing to update. Pass at least one option."));
2888
+ process.exit(1);
2889
+ }
2890
+ const spinner = (0, import_ora17.default)(`Updating task ${taskId}...`).start();
2891
+ try {
2892
+ const task = await api.updateTask(linked.appId, taskId, patch);
2893
+ spinner.succeed(import_chalk20.default.green(`\u2713 Task '${task.taskName}' updated`));
2894
+ } catch (err) {
2895
+ spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
2896
+ process.exit(1);
2897
+ }
2898
+ }
2899
+ );
2900
+ var rmCmd4 = new import_commander19.Command("rm").description("Delete a task definition").argument("<task-id>", "Task ID (task_xxx)").option("--force", "Skip confirmation").action(async (taskId, opts) => {
2901
+ requireToken5();
2902
+ const linked = requireLinkedApp7();
2903
+ if (!opts.force) {
2904
+ const rl = import_node_readline7.default.createInterface({ input: process.stdin, output: process.stdout });
2905
+ await new Promise((resolve) => {
2906
+ rl.question(import_chalk20.default.yellow(` Delete task ${import_chalk20.default.bold(taskId)}? (y/N) `), (answer) => {
2907
+ rl.close();
2908
+ if (answer.toLowerCase() !== "y") {
2909
+ console.log(import_chalk20.default.dim(" Cancelled."));
2910
+ process.exit(0);
2911
+ }
2912
+ resolve();
2913
+ });
2914
+ });
2915
+ }
2916
+ const spinner = (0, import_ora17.default)(`Deleting task ${taskId}...`).start();
2917
+ try {
2918
+ await api.deleteTask(linked.appId, taskId);
2919
+ spinner.succeed(import_chalk20.default.green(`\u2713 Task ${taskId} deleted`));
2920
+ } catch (err) {
2921
+ spinner.fail(import_chalk20.default.red(err instanceof Error ? err.message : String(err)));
2922
+ process.exit(1);
2923
+ }
2924
+ });
2925
+ var tasksCommand = new import_commander19.Command("tasks").description("Manage task definitions (jobs, crons, services)").addCommand(listCmd5).addCommand(getCmd4).addCommand(createCmd2).addCommand(updateCmd).addCommand(rmCmd4).action(() => tasksCommand.help());
2926
+
2927
+ // src/commands/unlink.ts
2928
+ var import_chalk21 = __toESM(require("chalk"));
2929
+ var import_commander20 = require("commander");
2930
+ var unlinkCommand = new import_commander20.Command("unlink").description("Unlink current directory from its linked app").action(() => {
2696
2931
  const linked = config.getLinkedApp();
2697
2932
  if (!linked) {
2698
- console.log(import_chalk20.default.yellow(" This directory is not linked to any app."));
2933
+ console.log(import_chalk21.default.yellow(" This directory is not linked to any app."));
2699
2934
  process.exit(0);
2700
2935
  }
2701
2936
  const appId = linked.appId;
2702
2937
  config.unlinkApp();
2703
2938
  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()}`));
2939
+ console.log(import_chalk21.default.green(`\u2713 Unlinked from ${import_chalk21.default.bold(appId)}`));
2940
+ console.log(import_chalk21.default.dim(` Directory: ${process.cwd()}`));
2706
2941
  console.log("");
2707
2942
  });
2708
2943
 
2709
2944
  // 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() {
2945
+ var import_node_readline8 = __toESM(require("readline"));
2946
+ var import_chalk22 = __toESM(require("chalk"));
2947
+ var import_commander21 = require("commander");
2948
+ var import_ora18 = __toESM(require("ora"));
2949
+ function requireLinkedApp8() {
2715
2950
  const linked = config.getLinkedApp();
2716
2951
  if (!linked) {
2717
- console.log(import_chalk21.default.red("No app linked. Run `sylphx link` first."));
2952
+ console.log(import_chalk22.default.red("No app linked. Run `sylphx link` first."));
2718
2953
  process.exit(1);
2719
2954
  }
2720
2955
  return linked;
2721
2956
  }
2722
- function requireToken5() {
2957
+ function requireToken6() {
2723
2958
  const token = config.getToken();
2724
2959
  if (!token) {
2725
- console.log(import_chalk21.default.red("Not authenticated. Run `sylphx login` first."));
2960
+ console.log(import_chalk22.default.red("Not authenticated. Run `sylphx login` first."));
2726
2961
  process.exit(1);
2727
2962
  }
2728
2963
  return token;
2729
2964
  }
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();
2965
+ var listCmd6 = new import_commander21.Command("list").description("List persistent volumes for this project").action(async () => {
2966
+ requireToken6();
2967
+ const linked = requireLinkedApp8();
2968
+ const spinner = (0, import_ora18.default)("Fetching volumes...").start();
2734
2969
  try {
2735
2970
  const volumes = await api.listVolumes(linked.appId);
2736
2971
  spinner.stop();
2737
2972
  if (volumes.length === 0) {
2738
- console.log(import_chalk21.default.dim(" No volumes provisioned."));
2973
+ console.log(import_chalk22.default.dim(" No volumes provisioned."));
2739
2974
  console.log(
2740
- import_chalk21.default.dim(`
2741
- Run ${import_chalk21.default.cyan("sylphx volumes create <name>")} to provision.`)
2975
+ import_chalk22.default.dim(`
2976
+ Run ${import_chalk22.default.cyan("sylphx volumes create <name>")} to provision.`)
2742
2977
  );
2743
2978
  return;
2744
2979
  }
2745
2980
  console.log();
2746
2981
  console.log(
2747
- ` ${import_chalk21.default.bold("NAME".padEnd(24))} ${"SIZE".padEnd(8)} ${"STATUS".padEnd(14)} ID`
2982
+ ` ${import_chalk22.default.bold("NAME".padEnd(24))} ${"SIZE".padEnd(8)} ${"STATUS".padEnd(14)} ID`
2748
2983
  );
2749
2984
  console.log(` ${"\u2500".repeat(70)}`);
2750
2985
  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));
2986
+ const statusColor2 = v.status === "ready" ? import_chalk22.default.green((v.status ?? "unknown").padEnd(14)) : import_chalk22.default.yellow((v.status ?? "unknown").padEnd(14));
2752
2987
  const size = v.sizeGb ? `${v.sizeGb}Gi`.padEnd(8) : "\u2014".padEnd(8);
2753
2988
  console.log(
2754
- ` ${import_chalk21.default.cyan(v.name.padEnd(24))} ${size} ${statusColor2} ${import_chalk21.default.dim(v.id)}`
2989
+ ` ${import_chalk22.default.cyan(v.name.padEnd(24))} ${size} ${statusColor2} ${import_chalk22.default.dim(v.id)}`
2755
2990
  );
2756
2991
  }
2757
2992
  console.log();
2758
2993
  } catch (err) {
2759
- spinner.fail(import_chalk21.default.red(err instanceof Error ? err.message : String(err)));
2994
+ spinner.fail(import_chalk22.default.red(err instanceof Error ? err.message : String(err)));
2760
2995
  process.exit(1);
2761
2996
  }
2762
2997
  });
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(
2998
+ var createCmd3 = new import_commander21.Command("create").description("Provision a persistent volume").argument("<name>", "Volume name").option("--size <gb>", "Size in GiB (default: 10)", "10").option(
2764
2999
  "--access-mode <mode>",
2765
3000
  "ReadWriteOnce or ReadWriteMany (default: ReadWriteOnce)",
2766
3001
  "ReadWriteOnce"
2767
3002
  ).option("--mount <path>", "Default mount path (optional)").action(async (name, opts) => {
2768
- requireToken5();
2769
- const linked = requireLinkedApp7();
3003
+ requireToken6();
3004
+ const linked = requireLinkedApp8();
2770
3005
  const sizeGb = Number.parseInt(opts.size, 10);
2771
3006
  if (Number.isNaN(sizeGb) || sizeGb < 1) {
2772
- console.log(import_chalk21.default.red(" --size must be a number >= 1"));
3007
+ console.log(import_chalk22.default.red(" --size must be a number >= 1"));
2773
3008
  process.exit(1);
2774
3009
  }
2775
3010
  if (!["ReadWriteOnce", "ReadWriteMany"].includes(opts.accessMode)) {
2776
- console.log(import_chalk21.default.red(" --access-mode must be ReadWriteOnce or ReadWriteMany"));
3011
+ console.log(import_chalk22.default.red(" --access-mode must be ReadWriteOnce or ReadWriteMany"));
2777
3012
  process.exit(1);
2778
3013
  }
2779
- const spinner = (0, import_ora17.default)(`Provisioning volume '${name}' (${sizeGb}Gi)...`).start();
3014
+ const spinner = (0, import_ora18.default)(`Provisioning volume '${name}' (${sizeGb}Gi)...`).start();
2780
3015
  try {
2781
3016
  const result = await api.createVolume(linked.appId, {
2782
3017
  name,
@@ -2785,29 +3020,29 @@ var createCmd2 = new import_commander20.Command("create").description("Provision
2785
3020
  mountPath: opts.mount
2786
3021
  });
2787
3022
  const vol = result.volume;
2788
- spinner.succeed(import_chalk21.default.green(`\u2713 Volume '${vol.name}' provisioned`));
3023
+ spinner.succeed(import_chalk22.default.green(`\u2713 Volume '${vol.name}' provisioned`));
2789
3024
  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"}`);
3025
+ console.log(` ${import_chalk22.default.dim("ID:")} ${vol.id}`);
3026
+ console.log(` ${import_chalk22.default.dim("Size:")} ${vol.sizeGb}Gi`);
3027
+ console.log(` ${import_chalk22.default.dim("Access:")} ${vol.accessMode ?? "ReadWriteOnce"}`);
2793
3028
  console.log();
2794
3029
  } catch (err) {
2795
- spinner.fail(import_chalk21.default.red(err instanceof Error ? err.message : String(err)));
3030
+ spinner.fail(import_chalk22.default.red(err instanceof Error ? err.message : String(err)));
2796
3031
  process.exit(1);
2797
3032
  }
2798
3033
  });
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();
3034
+ var rmCmd5 = new import_commander21.Command("rm").description("Delete a persistent volume").argument("<id>", "Volume ID").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
3035
+ requireToken6();
3036
+ const linked = requireLinkedApp8();
2802
3037
  if (!opts.force) {
2803
- const rl = import_node_readline7.default.createInterface({ input: process.stdin, output: process.stdout });
3038
+ const rl = import_node_readline8.default.createInterface({ input: process.stdin, output: process.stdout });
2804
3039
  await new Promise((resolve) => {
2805
3040
  rl.question(
2806
- import_chalk21.default.yellow(` \u26A0\uFE0F Delete volume ${import_chalk21.default.bold(id)}? This will destroy all data. (y/N) `),
3041
+ import_chalk22.default.yellow(` \u26A0\uFE0F Delete volume ${import_chalk22.default.bold(id)}? This will destroy all data. (y/N) `),
2807
3042
  (answer) => {
2808
3043
  rl.close();
2809
3044
  if (answer.toLowerCase() !== "y") {
2810
- console.log(import_chalk21.default.dim(" Cancelled."));
3045
+ console.log(import_chalk22.default.dim(" Cancelled."));
2811
3046
  process.exit(0);
2812
3047
  }
2813
3048
  resolve();
@@ -2815,62 +3050,62 @@ var rmCmd4 = new import_commander20.Command("rm").description("Delete a persiste
2815
3050
  );
2816
3051
  });
2817
3052
  }
2818
- const spinner = (0, import_ora17.default)(`Deleting volume ${id}...`).start();
3053
+ const spinner = (0, import_ora18.default)(`Deleting volume ${id}...`).start();
2819
3054
  try {
2820
3055
  await api.deleteVolume(linked.appId, id);
2821
- spinner.succeed(import_chalk21.default.green(`\u2713 Volume ${id} deleted`));
3056
+ spinner.succeed(import_chalk22.default.green(`\u2713 Volume ${id} deleted`));
2822
3057
  } catch (err) {
2823
- spinner.fail(import_chalk21.default.red(err instanceof Error ? err.message : String(err)));
3058
+ spinner.fail(import_chalk22.default.red(err instanceof Error ? err.message : String(err)));
2824
3059
  process.exit(1);
2825
3060
  }
2826
3061
  });
2827
- var volumesCommand = new import_commander20.Command("volumes").description("Manage persistent volumes").addCommand(listCmd5).addCommand(createCmd2).addCommand(rmCmd4).action(() => volumesCommand.help());
3062
+ var volumesCommand = new import_commander21.Command("volumes").description("Manage persistent volumes").addCommand(listCmd6).addCommand(createCmd3).addCommand(rmCmd5).action(() => volumesCommand.help());
2828
3063
 
2829
3064
  // 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 () => {
3065
+ var import_chalk23 = __toESM(require("chalk"));
3066
+ var import_commander22 = require("commander");
3067
+ var import_ora19 = __toESM(require("ora"));
3068
+ var whoamiCommand = new import_commander22.Command("whoami").description("Show current user, org, and linked app").action(async () => {
2834
3069
  const token = config.getToken();
2835
3070
  if (!token) {
2836
- console.log(import_chalk22.default.red("Not authenticated. Run `sylphx login` first."));
3071
+ console.log(import_chalk23.default.red("Not authenticated. Run `sylphx login` first."));
2837
3072
  process.exit(1);
2838
3073
  }
2839
- const spinner = (0, import_ora18.default)("Fetching account info...").start();
3074
+ const spinner = (0, import_ora19.default)("Fetching account info...").start();
2840
3075
  try {
2841
3076
  const me = await api.whoami();
2842
3077
  spinner.stop();
2843
3078
  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)}`);
3079
+ console.log(import_chalk23.default.bold(" Account"));
3080
+ console.log(` User: ${import_chalk23.default.cyan(me.user.email)}`);
3081
+ console.log(` Name: ${import_chalk23.default.white(me.user.name)}`);
3082
+ console.log(` ID: ${import_chalk23.default.dim(me.user.id)}`);
2848
3083
  if (me.orgs.length > 0) {
2849
3084
  console.log("");
2850
- console.log(import_chalk22.default.bold(" Organizations"));
3085
+ console.log(import_chalk23.default.bold(" Organizations"));
2851
3086
  for (const org of me.orgs) {
2852
3087
  console.log(
2853
- ` ${import_chalk22.default.cyan(org.slug)} ${import_chalk22.default.dim(`(${org.name})`)}`
3088
+ ` ${import_chalk23.default.cyan(org.slug)} ${import_chalk23.default.dim(`(${org.name})`)}`
2854
3089
  );
2855
3090
  }
2856
3091
  }
2857
3092
  const linkedApp = config.getLinkedApp();
2858
3093
  if (linkedApp) {
2859
3094
  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)}`);
3095
+ console.log(import_chalk23.default.bold(" Linked App"));
3096
+ console.log(` App: ${import_chalk23.default.cyan(linkedApp.appId)}`);
3097
+ console.log(` Org: ${import_chalk23.default.white(linkedApp.orgId)}`);
3098
+ console.log(` Env: ${import_chalk23.default.white(linkedApp.defaultEnv)}`);
2864
3099
  } else {
2865
3100
  console.log("");
2866
3101
  console.log(
2867
- import_chalk22.default.dim(" No app linked. Run `sylphx link` to link one.")
3102
+ import_chalk23.default.dim(" No app linked. Run `sylphx link` to link one.")
2868
3103
  );
2869
3104
  }
2870
3105
  console.log("");
2871
3106
  } catch (err) {
2872
3107
  spinner.fail(
2873
- import_chalk22.default.red(
3108
+ import_chalk23.default.red(
2874
3109
  `Failed: ${err instanceof Error ? err.message : String(err)}`
2875
3110
  )
2876
3111
  );
@@ -2880,48 +3115,50 @@ var whoamiCommand = new import_commander21.Command("whoami").description("Show c
2880
3115
 
2881
3116
  // src/index.ts
2882
3117
  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(
3118
+ var program = new import_commander23.Command();
3119
+ program.name("sylphx").description(`${import_chalk24.default.bold("Sylphx Platform CLI")} \u2014 deploy and manage your applications`).version(version4, "-v, --version", "Print version").helpOption("-h, --help", "Show help").addHelpText(
2885
3120
  "after",
2886
3121
  `
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
3122
+ ${import_chalk24.default.bold("Examples:")}
3123
+ ${import_chalk24.default.cyan("sylphx login")} Authenticate with Sylphx
3124
+ ${import_chalk24.default.cyan("sylphx init my-app")} Create and link a new project
3125
+ ${import_chalk24.default.cyan("sylphx link")} Link current directory to an app
3126
+ ${import_chalk24.default.cyan("sylphx deploy")} Deploy to production
3127
+ ${import_chalk24.default.cyan("sylphx deploy --env staging")} Deploy to staging
3128
+ ${import_chalk24.default.cyan("sylphx promote --from staging")} Promote staging build \u2192 production
3129
+ ${import_chalk24.default.cyan("sylphx rollback")} Rollback to previous deployment
3130
+ ${import_chalk24.default.cyan("sylphx logs -f")} Stream live logs
3131
+ ${import_chalk24.default.cyan("sylphx status")} Check deployment status
3132
+ ${import_chalk24.default.cyan("sylphx env list")} List environment variables
3133
+ ${import_chalk24.default.cyan("sylphx env set PORT=3000")} Set an env var
3134
+ ${import_chalk24.default.cyan("sylphx config list")} List remote config keys
3135
+ ${import_chalk24.default.cyan("sylphx config set KEY=value")} Set a remote config key
3136
+ ${import_chalk24.default.cyan("sylphx db create my-db")} Provision a PostgreSQL database
3137
+ ${import_chalk24.default.cyan("sylphx storage create")} Provision blob storage
3138
+ ${import_chalk24.default.cyan("sylphx volumes create data --size 20")} Provision a persistent volume
3139
+ ${import_chalk24.default.cyan("sylphx services list")} List project services
3140
+ ${import_chalk24.default.cyan("sylphx services deploy web")} Deploy a specific service
3141
+ ${import_chalk24.default.cyan("sylphx tasks list")} List task definitions
3142
+ ${import_chalk24.default.cyan("sylphx tasks create <name> --image nginx")} Create a task
3143
+ ${import_chalk24.default.cyan("sylphx resources bind <id>")} Bind a resource to this project
3144
+ ${import_chalk24.default.cyan("sylphx projects list")} List all projects
2908
3145
 
2909
- ${import_chalk23.default.bold("Documentation:")}
2910
- ${import_chalk23.default.underline("https://docs.sylphx.com/cli")}
3146
+ ${import_chalk24.default.bold("Documentation:")}
3147
+ ${import_chalk24.default.underline("https://docs.sylphx.com/cli")}
2911
3148
  `
2912
3149
  );
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);
3150
+ program.addCommand(loginCommand).addCommand(logoutCommand).addCommand(whoamiCommand).addCommand(initCommand).addCommand(linkCommand).addCommand(unlinkCommand).addCommand(projectsCommand).addCommand(deployCommand).addCommand(promoteCommand).addCommand(rollbackCommand).addCommand(statusCommand).addCommand(logsCommand).addCommand(envCommand).addCommand(configCommand).addCommand(dbCommand).addCommand(storageCommand).addCommand(volumesCommand).addCommand(resourcesCommand).addCommand(tasksCommand).addCommand(servicesCommand).addCommand(domainsCommand).addCommand(openCommand);
2914
3151
  program.on("command:*", (operands) => {
2915
- console.error(import_chalk23.default.red(`
2916
- Unknown command: ${import_chalk23.default.bold(operands[0] ?? "")}
3152
+ console.error(import_chalk24.default.red(`
3153
+ Unknown command: ${import_chalk24.default.bold(operands[0] ?? "")}
2917
3154
  `));
2918
- console.log(` Run ${import_chalk23.default.cyan("sylphx --help")} for usage.
3155
+ console.log(` Run ${import_chalk24.default.cyan("sylphx --help")} for usage.
2919
3156
  `);
2920
3157
  process.exit(1);
2921
3158
  });
2922
3159
  program.parseAsync(process.argv).catch((err) => {
2923
3160
  const msg = err instanceof Error ? err.message : String(err);
2924
- console.error(import_chalk23.default.red(`
3161
+ console.error(import_chalk24.default.red(`
2925
3162
  Error: ${msg}
2926
3163
  `));
2927
3164
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Sylphx Platform CLI — deploy, manage logs, env vars, and more",
5
5
  "type": "commonjs",
6
6
  "bin": {