@sylphx/cli 0.1.0 → 0.1.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 +913 -387
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -24,11 +24,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_chalk13 = __toESM(require("chalk"));
28
- var import_commander12 = require("commander");
27
+ var import_chalk17 = __toESM(require("chalk"));
28
+ var import_commander16 = require("commander");
29
29
 
30
- // src/commands/deploy.ts
31
- var import_chalk2 = __toESM(require("chalk"));
30
+ // src/commands/db.ts
31
+ var import_node_readline = __toESM(require("readline"));
32
+ var import_chalk = __toESM(require("chalk"));
32
33
  var import_commander = require("commander");
33
34
  var import_ora = __toESM(require("ora"));
34
35
 
@@ -107,8 +108,9 @@ var config = {
107
108
  };
108
109
 
109
110
  // src/api.ts
110
- var BASE_URL = process.env.SYLPHX_API_URL ?? "https://sylphx.com";
111
- var API_BASE = `${BASE_URL}/api/console/v1`;
111
+ var BASE_URL = process.env.SYLPHX_API_URL ?? "https://api.sylphx.com";
112
+ var API_BASE = `${BASE_URL}/v1`;
113
+ var AUTH_BASE_URL = "https://sylphx.com";
112
114
  var ApiError = class extends Error {
113
115
  constructor(status, message, body) {
114
116
  super(message);
@@ -117,12 +119,12 @@ var ApiError = class extends Error {
117
119
  this.name = "ApiError";
118
120
  }
119
121
  };
120
- async function request(method, path3, options) {
122
+ async function request(method, path5, options) {
121
123
  const token = options?.token ?? config.getToken();
122
124
  if (!token) {
123
125
  throw new ApiError(401, "Not authenticated. Run `sylphx login` first.");
124
126
  }
125
- let url = `${API_BASE}${path3}`;
127
+ let url = `${API_BASE}${path5}`;
126
128
  if (options?.query) {
127
129
  const params = new URLSearchParams(options.query);
128
130
  url += `?${params.toString()}`;
@@ -139,10 +141,11 @@ async function request(method, path3, options) {
139
141
  });
140
142
  if (!res.ok) {
141
143
  let body;
144
+ const text = await res.text();
142
145
  try {
143
- body = await res.json();
146
+ body = JSON.parse(text);
144
147
  } catch {
145
- body = await res.text();
148
+ body = text;
146
149
  }
147
150
  const message = typeof body === "object" && body !== null && "message" in body ? String(body.message) : `HTTP ${res.status}`;
148
151
  throw new ApiError(res.status, message, body);
@@ -153,115 +156,347 @@ async function request(method, path3, options) {
153
156
  return res.json();
154
157
  }
155
158
  var api = {
156
- /** Get deployment status for an app */
157
- async getDeploymentStatus(appId) {
158
- return request("GET", "/deployments/status", {
159
- query: { appId }
160
- });
159
+ /** Get deployment status for a project */
160
+ async getDeploymentStatus(projectId) {
161
+ return request("GET", `/projects/${projectId}/status`);
161
162
  },
162
- /** Trigger a deploy for an environment */
163
- async triggerDeploy(envId) {
164
- return request("POST", `/deployments/${envId}/deploy`);
163
+ /** Trigger a deploy for a project environment */
164
+ async triggerDeploy(projectId, envType) {
165
+ return request("POST", `/projects/${projectId}/deploy`, {
166
+ body: { envType }
167
+ });
165
168
  },
166
- /** Get deployment history for an environment */
167
- async getDeploymentHistory(envId) {
168
- return request("GET", `/deployments/${envId}/history`);
169
+ /** Get deployment history for a project environment */
170
+ async getDeploymentHistory(projectId, envType) {
171
+ return request("GET", `/projects/${projectId}/deployments`, {
172
+ query: { envType }
173
+ });
169
174
  },
170
- /** Rollback to previous deployment */
171
- async rollback(envId) {
172
- return request("POST", `/deployments/${envId}/rollback`);
175
+ /** Rollback to previous deployment
176
+ * NOTE: Endpoint needs backend verification; follows RESTful convention.
177
+ */
178
+ async rollback(projectId, envType) {
179
+ return request("POST", `/projects/${projectId}/rollback`, {
180
+ body: { envType }
181
+ });
173
182
  },
174
- /** List env vars for an environment */
175
- async listEnvVars(envId) {
176
- return request("GET", `/deployments/${envId}/envvars`);
183
+ /** List env vars for a project environment */
184
+ async listEnvVars(projectId, envType) {
185
+ return request("GET", `/projects/${projectId}/env-vars`, {
186
+ query: { envType }
187
+ });
177
188
  },
178
- /** Set an env var */
179
- async setEnvVar(envId, key, value) {
180
- return request("POST", `/deployments/${envId}/envvars`, {
181
- body: { key, value }
189
+ /** Set an env var (or batch of vars) for a project environment */
190
+ async setEnvVar(projectId, key, value, envType, secret) {
191
+ return request("POST", `/projects/${projectId}/env-vars`, {
192
+ body: {
193
+ envType,
194
+ vars: [{ key, value, secret: secret ?? false }]
195
+ }
182
196
  });
183
197
  },
184
- /** Delete an env var */
185
- async deleteEnvVar(envId, key) {
186
- return request(
187
- "DELETE",
188
- `/deployments/${envId}/envvars/${encodeURIComponent(key)}`
189
- );
198
+ /** Delete an env var for a project environment */
199
+ async deleteEnvVar(projectId, key, envType) {
200
+ return request("DELETE", `/projects/${projectId}/env-vars/${encodeURIComponent(key)}`, {
201
+ query: { envType }
202
+ });
190
203
  },
191
- /** List domains for an environment */
192
- async listDomains(envId) {
193
- return request("GET", `/deployments/${envId}/domains`);
204
+ /** List domains for a project environment
205
+ * NOTE: Endpoint needs backend verification; follows RESTful convention.
206
+ */
207
+ async listDomains(projectId, envType) {
208
+ return request("GET", `/projects/${projectId}/domains`, {
209
+ query: { envType }
210
+ });
194
211
  },
195
- /** Add a domain */
196
- async addDomain(envId, domain) {
197
- return request("POST", `/deployments/${envId}/domains`, {
198
- body: { domain }
212
+ /** Add a domain for a project environment
213
+ * NOTE: Endpoint needs backend verification; follows RESTful convention.
214
+ */
215
+ async addDomain(projectId, domain, envType) {
216
+ return request("POST", `/projects/${projectId}/domains`, {
217
+ body: { domain, envType }
199
218
  });
200
219
  },
201
- /** Remove a domain */
202
- async removeDomain(envId, domain) {
203
- return request(
204
- "DELETE",
205
- `/deployments/${envId}/domains/${encodeURIComponent(domain)}`
206
- );
220
+ /** Remove a domain from a project environment
221
+ * NOTE: Endpoint needs backend verification; follows RESTful convention.
222
+ */
223
+ async removeDomain(projectId, domain, envType) {
224
+ return request("DELETE", `/projects/${projectId}/domains/${encodeURIComponent(domain)}`, {
225
+ query: { envType }
226
+ });
207
227
  },
208
228
  /** Get current user and orgs */
209
229
  async whoami() {
210
- return request("GET", "/auth/me");
230
+ return request("GET", "/users/me");
231
+ },
232
+ /** List projects (org is scoped to token) */
233
+ async listProjects() {
234
+ return request("GET", "/projects");
235
+ },
236
+ /** Create a new project */
237
+ async createProject(data) {
238
+ return request("POST", "/projects", { body: data });
239
+ },
240
+ /** Delete a project */
241
+ async deleteProject(id) {
242
+ return request("DELETE", `/projects/${encodeURIComponent(id)}`);
243
+ },
244
+ /** @deprecated Use listProjects() — org is from token scope */
245
+ async listApps(_orgId) {
246
+ return this.listProjects();
247
+ },
248
+ /** List all databases */
249
+ async listDatabases() {
250
+ return request("GET", "/databases");
251
+ },
252
+ /** Provision a new database */
253
+ async createDatabase(data) {
254
+ return request("POST", "/databases", { body: data });
255
+ },
256
+ /** Get database details (includes connectionString) */
257
+ async getDatabase(id) {
258
+ return request("GET", `/databases/${encodeURIComponent(id)}`);
259
+ },
260
+ /** Delete a database */
261
+ async deleteDatabase(id) {
262
+ return request("DELETE", `/databases/${encodeURIComponent(id)}`);
263
+ },
264
+ /** Branch a database for staging/preview */
265
+ async branchDatabase(id, data) {
266
+ return request("POST", `/databases/${encodeURIComponent(id)}/branch`, { body: data });
211
267
  },
212
- /** List apps for an org */
213
- async listApps(orgId) {
214
- return request("GET", "/apps", {
215
- query: { orgId }
268
+ /** Batch-set env vars for a project environment */
269
+ async setEnvVars(projectId, vars, envType) {
270
+ return request("POST", `/projects/${projectId}/env-vars`, {
271
+ body: { envType, vars }
216
272
  });
217
273
  },
218
- /** CLI auth: poll for token */
274
+ /** CLI auth: poll for token (uses main sylphx.com domain) */
219
275
  async pollCliAuth(code) {
220
- const res = await fetch(
221
- `${BASE_URL}/api/auth/cli/poll?code=${encodeURIComponent(code)}`,
222
- {
223
- headers: { "User-Agent": "sylphx-cli/0.1.0" }
224
- }
225
- );
276
+ const res = await fetch(`${AUTH_BASE_URL}/api/auth/cli/poll?code=${encodeURIComponent(code)}`, {
277
+ headers: { "User-Agent": "sylphx-cli/0.1.0" }
278
+ });
226
279
  if (!res.ok) {
227
280
  throw new ApiError(res.status, "Failed to poll auth status");
228
281
  }
229
282
  return res.json();
230
283
  }
231
284
  };
232
- function createLogStream(envId) {
285
+ function createLogStream(projectId, envType) {
286
+ const params = new URLSearchParams({ envType, tail: "100" });
233
287
  return {
234
- url: `${API_BASE}/deployments/${envId}/logs`,
288
+ url: `${API_BASE}/projects/${projectId}/logs?${params.toString()}`,
235
289
  token: config.getToken()
236
290
  };
237
291
  }
238
292
 
293
+ // src/commands/db.ts
294
+ var POLL_INTERVAL_MS = 3e3;
295
+ var POLL_TIMEOUT_MS = 2 * 60 * 1e3;
296
+ function requireAuth() {
297
+ const token = config.getToken();
298
+ if (!token) {
299
+ console.log(import_chalk.default.red("Not authenticated. Run `sylphx login` first."));
300
+ process.exit(1);
301
+ }
302
+ return token;
303
+ }
304
+ function prompt(question) {
305
+ return new Promise((resolve) => {
306
+ const rl = import_node_readline.default.createInterface({ input: process.stdin, output: process.stdout });
307
+ rl.question(`${question}: `, (answer) => {
308
+ rl.close();
309
+ resolve(answer.trim());
310
+ });
311
+ });
312
+ }
313
+ function printDatabase(db) {
314
+ console.log("");
315
+ console.log(import_chalk.default.bold(` ${db.name}`));
316
+ console.log(import_chalk.default.dim(` ${"\u2500".repeat(50)}`));
317
+ console.log(` ID : ${import_chalk.default.white(db.id)}`);
318
+ console.log(` Status : ${statusColour(db.status)}`);
319
+ console.log(` Tier : ${import_chalk.default.white(db.tier)}`);
320
+ if (db.env) console.log(` Env : ${import_chalk.default.white(db.env)}`);
321
+ if (db.host) console.log(` Host : ${import_chalk.default.white(db.host)}`);
322
+ if (db.port) console.log(` Port : ${import_chalk.default.white(String(db.port))}`);
323
+ if (db.dbName) console.log(` DB Name : ${import_chalk.default.white(db.dbName)}`);
324
+ if (db.dbUser) console.log(` DB User : ${import_chalk.default.white(db.dbUser)}`);
325
+ if (db.storageGb) console.log(` Storage : ${import_chalk.default.white(`${db.storageGb} GB`)}`);
326
+ if (db.connectionString) {
327
+ console.log(` Connection :`);
328
+ console.log(` ${import_chalk.default.green(db.connectionString)}`);
329
+ }
330
+ if (db.createdAt) console.log(import_chalk.default.dim(` Created : ${db.createdAt}`));
331
+ console.log("");
332
+ }
333
+ function statusColour(status) {
334
+ switch (status) {
335
+ case "ready":
336
+ return import_chalk.default.green(status);
337
+ case "provisioning":
338
+ case "branching":
339
+ return import_chalk.default.yellow(status);
340
+ case "error":
341
+ case "failed":
342
+ return import_chalk.default.red(status);
343
+ default:
344
+ return import_chalk.default.white(status);
345
+ }
346
+ }
347
+ async function pollUntilReady(id, spinner) {
348
+ const start = Date.now();
349
+ while (Date.now() - start < POLL_TIMEOUT_MS) {
350
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
351
+ const db = await api.getDatabase(id);
352
+ if (db.status === "ready") return db;
353
+ if (db.status === "error" || db.status === "failed") {
354
+ throw new Error(`Database entered error state: ${db.status}`);
355
+ }
356
+ const elapsed = Math.round((Date.now() - start) / 1e3);
357
+ spinner.text = `Provisioning database... (${elapsed}s)`;
358
+ }
359
+ throw new Error("Timed out waiting for database to become ready (2 min).");
360
+ }
361
+ var dbListCommand = new import_commander.Command("list").description("List all databases").action(async () => {
362
+ requireAuth();
363
+ const spinner = (0, import_ora.default)("Fetching databases...").start();
364
+ try {
365
+ const dbs = await api.listDatabases();
366
+ spinner.stop();
367
+ if (dbs.length === 0) {
368
+ console.log(import_chalk.default.dim("\n No databases found.\n"));
369
+ return;
370
+ }
371
+ const colName = Math.max(4, ...dbs.map((d) => d.name.length));
372
+ const colStatus = Math.max(6, ...dbs.map((d) => d.status.length));
373
+ const colTier = Math.max(4, ...dbs.map((d) => d.tier.length));
374
+ console.log("");
375
+ console.log(import_chalk.default.bold(" Databases"));
376
+ console.log(import_chalk.default.dim(` ${"\u2500".repeat(colName + colStatus + colTier + 20)}`));
377
+ console.log(
378
+ import_chalk.default.dim(
379
+ ` ${"NAME".padEnd(colName)} ${"STATUS".padEnd(colStatus)} ${"TIER".padEnd(colTier)} ID`
380
+ )
381
+ );
382
+ console.log(import_chalk.default.dim(` ${"\u2500".repeat(colName + colStatus + colTier + 20)}`));
383
+ for (const db of dbs) {
384
+ console.log(
385
+ ` ${import_chalk.default.cyan(db.name.padEnd(colName))} ${statusColour(db.status).padEnd(colStatus + 10)} ${db.tier.padEnd(colTier)} ${import_chalk.default.dim(db.id)}`
386
+ );
387
+ }
388
+ console.log("");
389
+ } catch (err) {
390
+ spinner.fail(import_chalk.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
391
+ process.exit(1);
392
+ }
393
+ });
394
+ var dbCreateCommand = new import_commander.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) => {
395
+ requireAuth();
396
+ const storageGb = Number.parseInt(opts.storage, 10);
397
+ if (Number.isNaN(storageGb) || storageGb < 5) {
398
+ console.log(import_chalk.default.red("Storage must be at least 5 GB."));
399
+ process.exit(1);
400
+ }
401
+ const tier = opts.tier === "pro" ? "performance" : opts.tier;
402
+ const spinner = (0, import_ora.default)(
403
+ `Provisioning database ${import_chalk.default.bold(name)} (${tier}, ${storageGb} GB)...`
404
+ ).start();
405
+ try {
406
+ const db = await api.createDatabase({ name, env: opts.env, tier, storageGb });
407
+ spinner.text = `Waiting for database to become ready...`;
408
+ const ready = await pollUntilReady(db.id, spinner);
409
+ spinner.succeed(import_chalk.default.green(`\u2713 Database ${import_chalk.default.bold(ready.name)} is ready`));
410
+ printDatabase(ready);
411
+ } catch (err) {
412
+ spinner.fail(import_chalk.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
413
+ process.exit(1);
414
+ }
415
+ });
416
+ var dbGetCommand = new import_commander.Command("get").description("Get database details including connection string").argument("<id>", "Database ID").action(async (id) => {
417
+ requireAuth();
418
+ const spinner = (0, import_ora.default)("Fetching database...").start();
419
+ try {
420
+ const db = await api.getDatabase(id);
421
+ spinner.stop();
422
+ printDatabase(db);
423
+ } catch (err) {
424
+ spinner.fail(import_chalk.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
425
+ process.exit(1);
426
+ }
427
+ });
428
+ var dbDeleteCommand = new import_commander.Command("delete").description("Delete a database (irreversible)").argument("<id>", "Database ID").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
429
+ requireAuth();
430
+ if (!opts.force) {
431
+ console.log("");
432
+ console.log(import_chalk.default.red(` \u26A0 You are about to PERMANENTLY delete database ${import_chalk.default.bold(id)}.`));
433
+ console.log(import_chalk.default.red(" This deletes all data, backups, and PVCs. IRREVERSIBLE."));
434
+ console.log("");
435
+ const answer = await prompt(" Type the database ID to confirm");
436
+ if (answer !== id) {
437
+ console.log(import_chalk.default.red("Confirmation did not match. Aborted."));
438
+ process.exit(1);
439
+ }
440
+ }
441
+ const spinner = (0, import_ora.default)(`Deleting database ${import_chalk.default.bold(id)}...`).start();
442
+ try {
443
+ await api.deleteDatabase(id);
444
+ spinner.succeed(import_chalk.default.green(`\u2713 Deleted database ${import_chalk.default.bold(id)}`));
445
+ } catch (err) {
446
+ spinner.fail(import_chalk.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
447
+ process.exit(1);
448
+ }
449
+ });
450
+ var dbBranchCommand = new import_commander.Command("branch").description("Branch a database for staging or preview").argument("<id>", "Source database ID").option("--name <name>", "Branch database name").option("--env <env>", "Target environment (default: staging)", "staging").action(async (id, opts) => {
451
+ requireAuth();
452
+ const branchName = opts.name ?? `${opts.env}-branch`;
453
+ const spinner = (0, import_ora.default)(
454
+ `Branching database ${import_chalk.default.bold(id)} \u2192 ${import_chalk.default.bold(branchName)}...`
455
+ ).start();
456
+ try {
457
+ const db = await api.branchDatabase(id, { name: branchName, env: opts.env, fast: true });
458
+ spinner.text = `Waiting for branch to become ready...`;
459
+ const ready = await pollUntilReady(db.id, spinner);
460
+ spinner.succeed(import_chalk.default.green(`\u2713 Branch ${import_chalk.default.bold(ready.name)} is ready`));
461
+ printDatabase(ready);
462
+ } catch (err) {
463
+ spinner.fail(import_chalk.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
464
+ process.exit(1);
465
+ }
466
+ });
467
+ var dbCommand = new import_commander.Command("db").description("Manage PostgreSQL databases").addCommand(dbListCommand).addCommand(dbCreateCommand).addCommand(dbGetCommand).addCommand(dbDeleteCommand).addCommand(dbBranchCommand);
468
+
469
+ // src/commands/deploy.ts
470
+ var import_chalk3 = __toESM(require("chalk"));
471
+ var import_commander2 = require("commander");
472
+ var import_ora2 = __toESM(require("ora"));
473
+
239
474
  // src/utils/logs.ts
240
- var import_chalk = __toESM(require("chalk"));
475
+ var import_chalk2 = __toESM(require("chalk"));
241
476
  function formatLogEntry(raw) {
242
477
  let entry;
243
478
  try {
244
479
  entry = JSON.parse(raw);
245
480
  } catch {
246
- return ` ${import_chalk.default.white(raw)}`;
481
+ return ` ${import_chalk2.default.white(raw)}`;
247
482
  }
248
483
  const message = entry.message ?? entry.msg ?? raw;
249
484
  const level = (entry.level ?? "info").toLowerCase();
250
485
  const timestamp = entry.timestamp ?? entry.ts;
251
- const prefix = timestamp ? `${import_chalk.default.dim(`[${new Date(timestamp).toLocaleTimeString()}]`)} ` : "";
486
+ const prefix = timestamp ? `${import_chalk2.default.dim(`[${new Date(timestamp).toLocaleTimeString()}]`)} ` : "";
252
487
  const status = entry.status?.toLowerCase();
253
488
  const effectiveLevel = status === "error" || status === "failed" ? "error" : status === "success" || status === "done" ? "success" : level;
254
489
  switch (effectiveLevel) {
255
490
  case "error":
256
- return ` ${prefix}${import_chalk.default.red("\u2717")} ${import_chalk.default.red(message)}`;
491
+ return ` ${prefix}${import_chalk2.default.red("\u2717")} ${import_chalk2.default.red(message)}`;
257
492
  case "warn":
258
- return ` ${prefix}${import_chalk.default.yellow("\u26A0")} ${import_chalk.default.yellow(message)}`;
493
+ return ` ${prefix}${import_chalk2.default.yellow("\u26A0")} ${import_chalk2.default.yellow(message)}`;
259
494
  case "success":
260
- return ` ${prefix}${import_chalk.default.green("\u2713")} ${import_chalk.default.green(message)}`;
495
+ return ` ${prefix}${import_chalk2.default.green("\u2713")} ${import_chalk2.default.green(message)}`;
261
496
  case "debug":
262
- return ` ${prefix}${import_chalk.default.dim(message)}`;
497
+ return ` ${prefix}${import_chalk2.default.dim(message)}`;
263
498
  default:
264
- return ` ${prefix}${import_chalk.default.white(message)}`;
499
+ return ` ${prefix}${import_chalk2.default.white(message)}`;
265
500
  }
266
501
  }
267
502
  function streamLogs(url, token, opts = {}) {
@@ -328,7 +563,7 @@ function streamLogs(url, token, opts = {}) {
328
563
  es.addEventListener("error", (event) => {
329
564
  const errorEvent = event;
330
565
  if (errorEvent.message) {
331
- console.log(import_chalk.default.red(` Stream error: ${errorEvent.message}`));
566
+ console.log(import_chalk2.default.red(` Stream error: ${errorEvent.message}`));
332
567
  }
333
568
  if (opts.exitOnDone) {
334
569
  finish(succeeded);
@@ -344,273 +579,458 @@ function streamLogs(url, token, opts = {}) {
344
579
  }
345
580
 
346
581
  // src/commands/deploy.ts
347
- var deployCommand = new import_commander.Command("deploy").description("Trigger a deployment for the linked app").option("--env <env>", "Environment to deploy to (e.g. production, staging)").action(async (opts) => {
582
+ var deployCommand = new import_commander2.Command("deploy").description("Trigger a deployment for the linked app").option("--env <env>", "Environment to deploy to (e.g. production, staging)").action(async (opts) => {
348
583
  const token = config.getToken();
349
584
  if (!token) {
350
- console.log(import_chalk2.default.red("Not authenticated. Run `sylphx login` first."));
585
+ console.log(import_chalk3.default.red("Not authenticated. Run `sylphx login` first."));
351
586
  process.exit(1);
352
587
  }
353
588
  const linked = config.getLinkedApp();
354
589
  if (!linked) {
355
- console.log(import_chalk2.default.red("No app linked. Run `sylphx link` first."));
590
+ console.log(import_chalk3.default.red("No app linked. Run `sylphx link` first."));
356
591
  process.exit(1);
357
592
  }
358
593
  const env = opts.env ?? linked.defaultEnv;
359
594
  console.log("");
360
- console.log(
361
- import_chalk2.default.bold(
362
- ` Deploying ${import_chalk2.default.cyan(linked.appId)} \u2192 ${import_chalk2.default.white(env)}`
363
- )
364
- );
595
+ console.log(import_chalk3.default.bold(` Deploying ${import_chalk3.default.cyan(linked.appId)} \u2192 ${import_chalk3.default.white(env)}`));
365
596
  console.log("");
366
- const spinner = (0, import_ora.default)("Triggering deployment...").start();
367
- let envId;
597
+ const spinner = (0, import_ora2.default)("Triggering deployment...").start();
598
+ let deploymentId;
368
599
  try {
369
- envId = linked.appId;
370
- const status = await api.getDeploymentStatus(linked.appId);
371
- envId = linked.appId;
372
- const result = await api.triggerDeploy(linked.appId);
373
- spinner.succeed(import_chalk2.default.green(`Deployment triggered: ${result.status}`));
374
- if (result.deploymentId) {
375
- console.log(import_chalk2.default.dim(` Deployment ID: ${result.deploymentId}`));
600
+ const result = await api.triggerDeploy(linked.appId, env);
601
+ spinner.succeed(import_chalk3.default.green(`Deployment triggered: ${result.status}`));
602
+ deploymentId = result.deploymentId;
603
+ if (deploymentId) {
604
+ console.log(import_chalk3.default.dim(` Deployment ID: ${deploymentId}`));
605
+ }
606
+ if (result.environment) {
607
+ console.log(import_chalk3.default.dim(` Environment: ${result.environment}`));
376
608
  }
377
609
  } catch (err) {
378
- spinner.fail(
379
- import_chalk2.default.red(
380
- `Deploy failed: ${err instanceof Error ? err.message : String(err)}`
381
- )
382
- );
610
+ spinner.fail(import_chalk3.default.red(`Deploy failed: ${err instanceof Error ? err.message : String(err)}`));
383
611
  process.exit(1);
384
612
  }
385
613
  console.log("");
386
- console.log(import_chalk2.default.bold(" Build Logs"));
387
- console.log(import_chalk2.default.dim(` ${"\u2500".repeat(50)}`));
614
+ console.log(import_chalk3.default.bold(" Build Logs"));
615
+ console.log(import_chalk3.default.dim(` ${"\u2500".repeat(50)}`));
388
616
  console.log("");
389
- const logStream = createLogStream(envId);
617
+ const logStream = createLogStream(linked.appId, env);
390
618
  const success = await streamLogs(logStream.url, logStream.token, {
391
619
  follow: true,
392
620
  exitOnDone: true
393
621
  });
394
622
  if (!success) {
395
623
  console.log("");
396
- console.log(import_chalk2.default.red(" Deployment failed."));
624
+ console.log(import_chalk3.default.red(" Deployment failed."));
397
625
  process.exit(1);
398
626
  }
399
627
  console.log("");
400
- console.log(import_chalk2.default.green.bold(" \u2713 Deployment successful!"));
628
+ console.log(import_chalk3.default.green.bold(" \u2713 Deployment successful!"));
401
629
  console.log("");
402
630
  });
403
631
 
404
632
  // src/commands/domains.ts
405
- var import_chalk3 = __toESM(require("chalk"));
406
- var import_commander2 = require("commander");
407
- var import_ora2 = __toESM(require("ora"));
633
+ var import_chalk4 = __toESM(require("chalk"));
634
+ var import_commander3 = require("commander");
635
+ var import_ora3 = __toESM(require("ora"));
408
636
  function requireLinkedApp() {
409
637
  const token = config.getToken();
410
638
  if (!token) {
411
- console.log(import_chalk3.default.red("Not authenticated. Run `sylphx login` first."));
639
+ console.log(import_chalk4.default.red("Not authenticated. Run `sylphx login` first."));
412
640
  process.exit(1);
413
641
  }
414
642
  const linked = config.getLinkedApp();
415
643
  if (!linked) {
416
- console.log(import_chalk3.default.red("No app linked. Run `sylphx link` first."));
644
+ console.log(import_chalk4.default.red("No app linked. Run `sylphx link` first."));
417
645
  process.exit(1);
418
646
  }
419
647
  return linked;
420
648
  }
421
- var domainsListCommand = new import_commander2.Command("list").description("List custom domains").action(async () => {
649
+ var domainsListCommand = new import_commander3.Command("list").description("List custom domains").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
422
650
  const linked = requireLinkedApp();
423
- const envId = linked.appId;
424
- const spinner = (0, import_ora2.default)("Fetching domains...").start();
651
+ const env = opts.env ?? linked.defaultEnv;
652
+ const spinner = (0, import_ora3.default)(`Fetching domains [${env}]...`).start();
425
653
  try {
426
- const domains = await api.listDomains(envId);
654
+ const domains = await api.listDomains(linked.appId, env);
427
655
  spinner.stop();
428
656
  if (domains.length === 0) {
429
- console.log(import_chalk3.default.dim("\n No custom domains configured.\n"));
657
+ console.log(import_chalk4.default.dim("\n No custom domains configured.\n"));
430
658
  return;
431
659
  }
432
660
  console.log("");
433
- console.log(import_chalk3.default.bold(" Custom Domains"));
434
- console.log(import_chalk3.default.dim(` ${"\u2500".repeat(50)}`));
661
+ console.log(import_chalk4.default.bold(` Custom Domains [${import_chalk4.default.white(env)}]`));
662
+ console.log(import_chalk4.default.dim(` ${"\u2500".repeat(50)}`));
435
663
  console.log("");
436
664
  for (const d of domains) {
437
- const status = d.status ? import_chalk3.default.dim(` [${d.status}]`) : "";
438
- const ssl = d.ssl ? import_chalk3.default.green(" \u{1F512}") : import_chalk3.default.yellow(" \u26A0 no SSL");
439
- console.log(` ${import_chalk3.default.cyan(d.domain)}${status}${ssl}`);
665
+ const status = d.status ? import_chalk4.default.dim(` [${d.status}]`) : "";
666
+ const ssl = d.ssl ? import_chalk4.default.green(" \u{1F512}") : import_chalk4.default.yellow(" \u26A0 no SSL");
667
+ console.log(` ${import_chalk4.default.cyan(d.domain)}${status}${ssl}`);
440
668
  }
441
669
  console.log("");
442
670
  } catch (err) {
443
- spinner.fail(
444
- import_chalk3.default.red(
445
- `Failed: ${err instanceof Error ? err.message : String(err)}`
446
- )
447
- );
671
+ spinner.fail(import_chalk4.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
448
672
  process.exit(1);
449
673
  }
450
674
  });
451
- var domainsAddCommand = new import_commander2.Command("add").description("Add a custom domain").argument("<domain>", "Domain to add (e.g. example.com)").action(async (domain) => {
675
+ var domainsAddCommand = new import_commander3.Command("add").description("Add a custom domain").argument("<domain>", "Domain to add (e.g. example.com)").option("--env <env>", "Environment (e.g. production, staging)").action(async (domain, opts) => {
452
676
  const linked = requireLinkedApp();
453
- const envId = linked.appId;
454
- const spinner = (0, import_ora2.default)(`Adding domain ${domain}...`).start();
677
+ const env = opts.env ?? linked.defaultEnv;
678
+ const spinner = (0, import_ora3.default)(`Adding domain ${domain} [${env}]...`).start();
455
679
  try {
456
- const result = await api.addDomain(envId, domain);
457
- spinner.succeed(
458
- import_chalk3.default.green(`\u2713 Added domain ${import_chalk3.default.bold(result.domain ?? domain)}`)
459
- );
680
+ const result = await api.addDomain(linked.appId, domain, env);
681
+ spinner.succeed(import_chalk4.default.green(`\u2713 Added domain ${import_chalk4.default.bold(result.domain ?? domain)}`));
460
682
  if (result.status) {
461
- console.log(import_chalk3.default.dim(` Status: ${result.status}`));
683
+ console.log(import_chalk4.default.dim(` Status: ${result.status}`));
462
684
  }
463
685
  } catch (err) {
464
- spinner.fail(
465
- import_chalk3.default.red(
466
- `Failed: ${err instanceof Error ? err.message : String(err)}`
467
- )
468
- );
686
+ spinner.fail(import_chalk4.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
469
687
  process.exit(1);
470
688
  }
471
689
  });
472
- var domainsRmCommand = new import_commander2.Command("rm").description("Remove a custom domain").argument("<domain>", "Domain to remove (e.g. example.com)").action(async (domain) => {
690
+ var domainsRmCommand = new import_commander3.Command("rm").description("Remove a custom domain").argument("<domain>", "Domain to remove (e.g. example.com)").option("--env <env>", "Environment (e.g. production, staging)").action(async (domain, opts) => {
473
691
  const linked = requireLinkedApp();
474
- const envId = linked.appId;
475
- const spinner = (0, import_ora2.default)(`Removing domain ${domain}...`).start();
692
+ const env = opts.env ?? linked.defaultEnv;
693
+ const spinner = (0, import_ora3.default)(`Removing domain ${domain} [${env}]...`).start();
476
694
  try {
477
- await api.removeDomain(envId, domain);
478
- spinner.succeed(import_chalk3.default.green(`\u2713 Removed domain ${import_chalk3.default.bold(domain)}`));
695
+ await api.removeDomain(linked.appId, domain, env);
696
+ spinner.succeed(import_chalk4.default.green(`\u2713 Removed domain ${import_chalk4.default.bold(domain)}`));
479
697
  } catch (err) {
480
- spinner.fail(
481
- import_chalk3.default.red(
482
- `Failed: ${err instanceof Error ? err.message : String(err)}`
483
- )
484
- );
698
+ spinner.fail(import_chalk4.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
485
699
  process.exit(1);
486
700
  }
487
701
  });
488
- var domainsCommand = new import_commander2.Command("domains").description("Manage custom domains").addCommand(domainsListCommand).addCommand(domainsAddCommand).addCommand(domainsRmCommand);
702
+ var domainsCommand = new import_commander3.Command("domains").description("Manage custom domains").addCommand(domainsListCommand).addCommand(domainsAddCommand).addCommand(domainsRmCommand);
489
703
 
490
704
  // src/commands/env.ts
491
- var import_chalk4 = __toESM(require("chalk"));
492
- var import_commander3 = require("commander");
493
- var import_ora3 = __toESM(require("ora"));
705
+ var import_node_fs = __toESM(require("fs"));
706
+ var import_node_path2 = __toESM(require("path"));
707
+ var import_chalk5 = __toESM(require("chalk"));
708
+ var import_commander4 = require("commander");
709
+ var import_ora4 = __toESM(require("ora"));
494
710
  function requireLinkedApp2() {
495
711
  const token = config.getToken();
496
712
  if (!token) {
497
- console.log(import_chalk4.default.red("Not authenticated. Run `sylphx login` first."));
713
+ console.log(import_chalk5.default.red("Not authenticated. Run `sylphx login` first."));
498
714
  process.exit(1);
499
715
  }
500
716
  const linked = config.getLinkedApp();
501
717
  if (!linked) {
502
- console.log(import_chalk4.default.red("No app linked. Run `sylphx link` first."));
718
+ console.log(import_chalk5.default.red("No app linked. Run `sylphx link` first."));
503
719
  process.exit(1);
504
720
  }
505
721
  return linked;
506
722
  }
507
- var envListCommand = new import_commander3.Command("list").description("List environment variables").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
723
+ var envListCommand = new import_commander4.Command("list").description("List environment variables").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
508
724
  const linked = requireLinkedApp2();
509
725
  const env = opts.env ?? linked.defaultEnv;
510
- const envId = linked.appId;
511
- const spinner = (0, import_ora3.default)(`Fetching env vars [${env}]...`).start();
726
+ const spinner = (0, import_ora4.default)(`Fetching env vars [${env}]...`).start();
512
727
  try {
513
- const vars = await api.listEnvVars(envId);
728
+ const vars = await api.listEnvVars(linked.appId, env);
514
729
  spinner.stop();
515
730
  if (vars.length === 0) {
516
- console.log(import_chalk4.default.dim("\n No environment variables set.\n"));
731
+ console.log(import_chalk5.default.dim("\n No environment variables set.\n"));
517
732
  return;
518
733
  }
519
734
  console.log("");
520
- console.log(import_chalk4.default.bold(` Environment Variables [${import_chalk4.default.white(env)}]`));
521
- console.log(import_chalk4.default.dim(` ${"\u2500".repeat(50)}`));
735
+ console.log(import_chalk5.default.bold(` Environment Variables [${import_chalk5.default.white(env)}]`));
736
+ console.log(import_chalk5.default.dim(` ${"\u2500".repeat(50)}`));
522
737
  console.log("");
523
738
  const maxKeyLen = Math.max(...vars.map((v) => v.key.length));
524
739
  for (const v of vars) {
525
- const key = import_chalk4.default.cyan(v.key.padEnd(maxKeyLen));
526
- const value = v.isSecret ? import_chalk4.default.dim("****** (secret)") : import_chalk4.default.white(v.value);
740
+ const key = import_chalk5.default.cyan(v.key.padEnd(maxKeyLen));
741
+ const isSecret = v.isSecret ?? v.secret;
742
+ const value = isSecret ? import_chalk5.default.dim("****** (secret)") : import_chalk5.default.white(v.value);
527
743
  console.log(` ${key} ${value}`);
528
744
  }
529
745
  console.log("");
530
746
  } catch (err) {
531
- spinner.fail(
532
- import_chalk4.default.red(
533
- `Failed: ${err instanceof Error ? err.message : String(err)}`
747
+ spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
748
+ process.exit(1);
749
+ }
750
+ });
751
+ var envSetCommand = new import_commander4.Command("set").description("Set an environment variable (KEY=VALUE)").argument("<assignment>", "Key=Value assignment, e.g. DATABASE_URL=postgres://...").option("--env <env>", "Environment (e.g. production, staging)").option("--secret", "Mark as secret").action(async (assignment, opts) => {
752
+ const linked = requireLinkedApp2();
753
+ const env = opts.env ?? linked.defaultEnv;
754
+ const eqIdx = assignment.indexOf("=");
755
+ if (eqIdx < 1) {
756
+ console.log(
757
+ import_chalk5.default.red(
758
+ "Invalid format. Use KEY=VALUE (e.g. sylphx env set DATABASE_URL=postgres://...)"
534
759
  )
535
760
  );
536
761
  process.exit(1);
537
762
  }
763
+ const key = assignment.slice(0, eqIdx).trim();
764
+ const value = assignment.slice(eqIdx + 1);
765
+ if (!key) {
766
+ console.log(import_chalk5.default.red("Key cannot be empty."));
767
+ process.exit(1);
768
+ }
769
+ const spinner = (0, import_ora4.default)(`Setting ${key} [${env}]...`).start();
770
+ try {
771
+ await api.setEnvVar(linked.appId, key, value, env, opts.secret);
772
+ spinner.succeed(import_chalk5.default.green(`\u2713 Set ${import_chalk5.default.bold(key)} in ${env}`));
773
+ } catch (err) {
774
+ spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
775
+ process.exit(1);
776
+ }
538
777
  });
539
- var envSetCommand = new import_commander3.Command("set").description("Set an environment variable (KEY=VALUE)").argument(
540
- "<assignment>",
541
- "Key=Value assignment, e.g. DATABASE_URL=postgres://..."
542
- ).option("--env <env>", "Environment (e.g. production, staging)").option("--secret", "Mark as secret").action(
543
- async (assignment, opts) => {
544
- const linked = requireLinkedApp2();
545
- const env = opts.env ?? linked.defaultEnv;
546
- const envId = linked.appId;
547
- const eqIdx = assignment.indexOf("=");
548
- if (eqIdx < 1) {
549
- console.log(
550
- import_chalk4.default.red(
551
- "Invalid format. Use KEY=VALUE (e.g. sylphx env set DATABASE_URL=postgres://...)"
552
- )
553
- );
554
- process.exit(1);
778
+ var envRmCommand = new import_commander4.Command("rm").description("Remove an environment variable").argument("<key>", "Variable name to remove").option("--env <env>", "Environment (e.g. production, staging)").action(async (key, opts) => {
779
+ const linked = requireLinkedApp2();
780
+ const env = opts.env ?? linked.defaultEnv;
781
+ const spinner = (0, import_ora4.default)(`Removing ${key} [${env}]...`).start();
782
+ try {
783
+ await api.deleteEnvVar(linked.appId, key, env);
784
+ spinner.succeed(import_chalk5.default.green(`\u2713 Removed ${import_chalk5.default.bold(key)} from ${env}`));
785
+ } catch (err) {
786
+ spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
787
+ process.exit(1);
788
+ }
789
+ });
790
+ var envPullCommand = new import_commander4.Command("pull").description("Download env vars from the platform to a local .env file").option("--env <env>", "Environment (e.g. production, staging)").option("--file <file>", ".env file path", ".env").action(async (opts) => {
791
+ const linked = requireLinkedApp2();
792
+ const env = opts.env ?? linked.defaultEnv;
793
+ const filePath = import_node_path2.default.resolve(opts.file);
794
+ const spinner = (0, import_ora4.default)(`Pulling env vars [${env}]...`).start();
795
+ try {
796
+ const vars = await api.listEnvVars(linked.appId, env);
797
+ spinner.stop();
798
+ if (vars.length === 0) {
799
+ console.log(import_chalk5.default.dim("\n No environment variables to pull.\n"));
800
+ return;
555
801
  }
556
- const key = assignment.slice(0, eqIdx).trim();
557
- const value = assignment.slice(eqIdx + 1);
558
- if (!key) {
559
- console.log(import_chalk4.default.red("Key cannot be empty."));
802
+ const lines = [
803
+ `# Sylphx env vars \u2014 ${env}`,
804
+ `# Pulled: ${(/* @__PURE__ */ new Date()).toISOString()}`,
805
+ `# App ID: ${linked.appId}`,
806
+ ""
807
+ ];
808
+ for (const v of vars) {
809
+ const isSecret = v.isSecret ?? v.secret;
810
+ if (isSecret) {
811
+ lines.push(`# ${v.key} is a secret \u2014 value omitted`);
812
+ lines.push(`${v.key}=`);
813
+ } else {
814
+ const raw = v.value ?? "";
815
+ const needsQuotes = /[\s"'\\$`#]/.test(raw);
816
+ const val = needsQuotes ? `"${raw.replace(/"/g, '\\"')}"` : raw;
817
+ lines.push(`${v.key}=${val}`);
818
+ }
819
+ }
820
+ lines.push("");
821
+ import_node_fs.default.writeFileSync(filePath, lines.join("\n"), "utf-8");
822
+ console.log(import_chalk5.default.green(`\u2713 Wrote ${vars.length} variable(s) to ${import_chalk5.default.bold(opts.file)}`));
823
+ console.log(import_chalk5.default.dim(` Secrets are shown as ${import_chalk5.default.white("KEY=")} (value omitted).`));
824
+ } catch (err) {
825
+ spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
826
+ process.exit(1);
827
+ }
828
+ });
829
+ var envPushCommand = new import_commander4.Command("push").description("Push a local .env file to the platform").option("--env <env>", "Environment (e.g. production, staging)").option("--file <file>", ".env file path", ".env").action(async (opts) => {
830
+ const linked = requireLinkedApp2();
831
+ const env = opts.env ?? linked.defaultEnv;
832
+ const filePath = import_node_path2.default.resolve(opts.file);
833
+ if (!import_node_fs.default.existsSync(filePath)) {
834
+ console.log(import_chalk5.default.red(`File not found: ${opts.file}`));
835
+ process.exit(1);
836
+ }
837
+ const content = import_node_fs.default.readFileSync(filePath, "utf-8");
838
+ const vars = [];
839
+ for (const rawLine of content.split("\n")) {
840
+ const line = rawLine.trim();
841
+ if (!line || line.startsWith("#")) continue;
842
+ const eqIdx = line.indexOf("=");
843
+ if (eqIdx < 1) continue;
844
+ const key = line.slice(0, eqIdx).trim();
845
+ let value = line.slice(eqIdx + 1);
846
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
847
+ value = value.slice(1, -1).replace(/\\"/g, '"');
848
+ }
849
+ if (!key) continue;
850
+ if (value === "") continue;
851
+ vars.push({ key, value, secret: false });
852
+ }
853
+ if (vars.length === 0) {
854
+ console.log(import_chalk5.default.yellow(" No variables to push (all lines empty or comments)."));
855
+ return;
856
+ }
857
+ const spinner = (0, import_ora4.default)(`Pushing ${vars.length} variable(s) to [${env}]...`).start();
858
+ try {
859
+ await api.setEnvVars(linked.appId, vars, env);
860
+ spinner.succeed(import_chalk5.default.green(`\u2713 Pushed ${vars.length} variable(s) to ${import_chalk5.default.bold(env)}`));
861
+ console.log(import_chalk5.default.dim(` Run ${import_chalk5.default.cyan("sylphx deploy")} to apply the new variables.`));
862
+ } catch (err) {
863
+ spinner.fail(import_chalk5.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
864
+ process.exit(1);
865
+ }
866
+ });
867
+ var envCommand = new import_commander4.Command("env").description("Manage environment variables").addCommand(envListCommand).addCommand(envSetCommand).addCommand(envRmCommand).addCommand(envPullCommand).addCommand(envPushCommand);
868
+
869
+ // src/commands/init.ts
870
+ var import_node_child_process = require("child_process");
871
+ var import_node_fs2 = __toESM(require("fs"));
872
+ var import_node_path3 = __toESM(require("path"));
873
+ var import_node_readline2 = __toESM(require("readline"));
874
+ var import_chalk6 = __toESM(require("chalk"));
875
+ var import_commander5 = require("commander");
876
+ var import_ora5 = __toESM(require("ora"));
877
+ function prompt2(question, defaultValue) {
878
+ return new Promise((resolve) => {
879
+ const rl = import_node_readline2.default.createInterface({ input: process.stdin, output: process.stdout });
880
+ const q = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
881
+ rl.question(q, (answer) => {
882
+ rl.close();
883
+ resolve(answer.trim() || defaultValue || "");
884
+ });
885
+ });
886
+ }
887
+ function slugify(name) {
888
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
889
+ }
890
+ function readPackageJson() {
891
+ try {
892
+ const pkgPath = import_node_path3.default.join(process.cwd(), "package.json");
893
+ return JSON.parse(import_node_fs2.default.readFileSync(pkgPath, "utf-8"));
894
+ } catch {
895
+ return void 0;
896
+ }
897
+ }
898
+ function detectPortFromPackage(pkg) {
899
+ const scripts = Object.values(pkg.scripts ?? {}).join(" ");
900
+ const match = scripts.match(/(?:--port\s+|PORT=)(\d{3,5})/);
901
+ if (match?.[1]) return Number.parseInt(match[1], 10);
902
+ return void 0;
903
+ }
904
+ function detectGitRemote() {
905
+ try {
906
+ return (0, import_node_child_process.execSync)("git remote get-url origin", { stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
907
+ } catch {
908
+ return void 0;
909
+ }
910
+ }
911
+ var initCommand = new import_commander5.Command("init").description("Create and link a new Sylphx project in one step").argument("[name]", "Project name (detected from package.json if omitted)").option("--git", "Auto-detect git remote URL and set as gitRepository").option("--port <port>", "Application port").action(async (nameArg, opts) => {
912
+ const token = config.getToken();
913
+ if (!token) {
914
+ console.log(import_chalk6.default.red("Not authenticated. Run `sylphx login` first."));
915
+ process.exit(1);
916
+ }
917
+ const pkg = readPackageJson();
918
+ const detectedName = nameArg ?? pkg?.name;
919
+ let projectName;
920
+ if (detectedName) {
921
+ projectName = detectedName;
922
+ console.log(import_chalk6.default.dim(` Project name: ${import_chalk6.default.white(projectName)}`));
923
+ } else {
924
+ projectName = await prompt2(" Project name");
925
+ if (!projectName) {
926
+ console.log(import_chalk6.default.red("Project name is required."));
560
927
  process.exit(1);
561
928
  }
562
- const spinner = (0, import_ora3.default)(`Setting ${key} [${env}]...`).start();
929
+ }
930
+ const slug = slugify(projectName);
931
+ let orgId = config.getDefaultOrg();
932
+ if (!orgId) {
933
+ const spinner2 = (0, import_ora5.default)("Fetching your organizations...").start();
934
+ let orgs = [];
563
935
  try {
564
- await api.setEnvVar(envId, key, value);
565
- spinner.succeed(import_chalk4.default.green(`\u2713 Set ${import_chalk4.default.bold(key)} in ${env}`));
936
+ const me = await api.whoami();
937
+ orgs = me.orgs;
938
+ spinner2.stop();
566
939
  } catch (err) {
567
- spinner.fail(
568
- import_chalk4.default.red(
569
- `Failed: ${err instanceof Error ? err.message : String(err)}`
570
- )
571
- );
940
+ spinner2.fail(import_chalk6.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
572
941
  process.exit(1);
573
942
  }
943
+ if (orgs.length === 0) {
944
+ console.log(import_chalk6.default.red("No organizations found. Create one at https://app.sylphx.com."));
945
+ process.exit(1);
946
+ }
947
+ if (orgs.length === 1 && orgs[0]) {
948
+ orgId = orgs[0].slug;
949
+ console.log(import_chalk6.default.dim(` Using org: ${import_chalk6.default.white(orgId)}`));
950
+ } else {
951
+ console.log(import_chalk6.default.bold("\n Your organizations:\n"));
952
+ orgs.forEach((org, i) => {
953
+ console.log(` ${import_chalk6.default.cyan(String(i + 1))}. ${org.slug} ${import_chalk6.default.dim(`(${org.name})`)}`);
954
+ });
955
+ console.log("");
956
+ const choice = await prompt2(" Select org (number)");
957
+ const idx = Number.parseInt(choice, 10) - 1;
958
+ if (Number.isNaN(idx) || idx < 0 || idx >= orgs.length || !orgs[idx]) {
959
+ console.log(import_chalk6.default.red("Invalid selection."));
960
+ process.exit(1);
961
+ }
962
+ orgId = orgs[idx]?.slug;
963
+ }
574
964
  }
575
- );
576
- var envRmCommand = new import_commander3.Command("rm").description("Remove an environment variable").argument("<key>", "Variable name to remove").option("--env <env>", "Environment (e.g. production, staging)").action(async (key, opts) => {
577
- const linked = requireLinkedApp2();
578
- const env = opts.env ?? linked.defaultEnv;
579
- const envId = linked.appId;
580
- const spinner = (0, import_ora3.default)(`Removing ${key} [${env}]...`).start();
965
+ if (!orgId) {
966
+ console.log(import_chalk6.default.red("Could not determine org."));
967
+ process.exit(1);
968
+ }
969
+ let gitRepository;
970
+ if (opts.git) {
971
+ gitRepository = detectGitRemote();
972
+ if (gitRepository) {
973
+ const sshMatch = gitRepository.match(/git@github\.com:(.+?)(?:\.git)?$/);
974
+ if (sshMatch?.[1]) gitRepository = sshMatch[1];
975
+ const httpsMatch = gitRepository.match(/github\.com\/(.+?)(?:\.git)?$/);
976
+ if (httpsMatch?.[1]) gitRepository = httpsMatch[1];
977
+ console.log(import_chalk6.default.dim(` Git repo: ${import_chalk6.default.white(gitRepository)}`));
978
+ } else {
979
+ console.log(import_chalk6.default.yellow(" \u26A0 No git remote found (--git flag ignored)."));
980
+ }
981
+ }
982
+ let appPort;
983
+ if (opts.port) {
984
+ appPort = Number.parseInt(opts.port, 10);
985
+ } else if (pkg) {
986
+ appPort = detectPortFromPackage(pkg);
987
+ if (appPort) console.log(import_chalk6.default.dim(` Detected port: ${import_chalk6.default.white(String(appPort))}`));
988
+ }
989
+ console.log("");
990
+ const spinner = (0, import_ora5.default)(`Creating project ${import_chalk6.default.bold(projectName)}...`).start();
581
991
  try {
582
- await api.deleteEnvVar(envId, key);
583
- spinner.succeed(import_chalk4.default.green(`\u2713 Removed ${import_chalk4.default.bold(key)} from ${env}`));
992
+ const project = await api.createProject({
993
+ name: projectName,
994
+ slug,
995
+ ...gitRepository ? { gitRepository } : {},
996
+ ...appPort ? { port: appPort } : {}
997
+ });
998
+ config.linkApp({ appId: project.id, orgId, defaultEnv: "production" });
999
+ config.setDefaultOrg(orgId);
1000
+ spinner.succeed(import_chalk6.default.green(`\u2713 Created and linked project ${import_chalk6.default.bold(project.name)}`));
1001
+ console.log("");
1002
+ console.log(import_chalk6.default.dim(` Project ID : ${project.id}`));
1003
+ console.log(import_chalk6.default.dim(` Slug : ${project.slug}`));
1004
+ console.log(import_chalk6.default.dim(` Org : ${orgId}`));
1005
+ console.log(import_chalk6.default.dim(` Directory : ${process.cwd()}`));
1006
+ console.log("");
1007
+ console.log(import_chalk6.default.dim(` ${import_chalk6.default.cyan("sylphx deploy")} \u2014 deploy to production when ready`));
1008
+ console.log("");
584
1009
  } catch (err) {
585
- spinner.fail(
586
- import_chalk4.default.red(
587
- `Failed: ${err instanceof Error ? err.message : String(err)}`
588
- )
589
- );
1010
+ spinner.fail(import_chalk6.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
590
1011
  process.exit(1);
591
1012
  }
592
1013
  });
593
- var envCommand = new import_commander3.Command("env").description("Manage environment variables").addCommand(envListCommand).addCommand(envSetCommand).addCommand(envRmCommand);
594
1014
 
595
1015
  // src/commands/link.ts
596
- var import_node_fs = __toESM(require("fs"));
597
- var import_node_path2 = __toESM(require("path"));
598
- var import_node_readline = __toESM(require("readline"));
599
- var import_chalk5 = __toESM(require("chalk"));
600
- var import_commander4 = require("commander");
601
- var import_ora4 = __toESM(require("ora"));
1016
+ var import_node_fs3 = __toESM(require("fs"));
1017
+ var import_node_path4 = __toESM(require("path"));
1018
+ var import_node_readline3 = __toESM(require("readline"));
1019
+ var import_chalk7 = __toESM(require("chalk"));
1020
+ var import_commander6 = require("commander");
1021
+ var import_ora6 = __toESM(require("ora"));
602
1022
  function readPackageName() {
603
1023
  try {
604
- const pkgPath = import_node_path2.default.join(process.cwd(), "package.json");
605
- const pkg = JSON.parse(import_node_fs.default.readFileSync(pkgPath, "utf-8"));
1024
+ const pkgPath = import_node_path4.default.join(process.cwd(), "package.json");
1025
+ const pkg = JSON.parse(import_node_fs3.default.readFileSync(pkgPath, "utf-8"));
606
1026
  return pkg.name;
607
1027
  } catch {
608
1028
  return void 0;
609
1029
  }
610
1030
  }
611
- function prompt(question) {
1031
+ function prompt3(question) {
612
1032
  return new Promise((resolve) => {
613
- const rl = import_node_readline.default.createInterface({
1033
+ const rl = import_node_readline3.default.createInterface({
614
1034
  input: process.stdin,
615
1035
  output: process.stdout
616
1036
  });
@@ -620,102 +1040,88 @@ function prompt(question) {
620
1040
  });
621
1041
  });
622
1042
  }
623
- var linkCommand = new import_commander4.Command("link").description("Link current directory to a Sylphx app").option("--org <org>", "Organization slug").action(async (opts) => {
1043
+ var linkCommand = new import_commander6.Command("link").description("Link current directory to a Sylphx app").option("--org <org>", "Organization slug").action(async (opts) => {
624
1044
  const token = config.getToken();
625
1045
  if (!token) {
626
- console.log(import_chalk5.default.red("Not authenticated. Run `sylphx login` first."));
1046
+ console.log(import_chalk7.default.red("Not authenticated. Run `sylphx login` first."));
627
1047
  process.exit(1);
628
1048
  }
629
1049
  const pkgName = readPackageName();
630
1050
  if (pkgName) {
631
- console.log(import_chalk5.default.dim(` Detected package: ${import_chalk5.default.white(pkgName)}`));
1051
+ console.log(import_chalk7.default.dim(` Detected package: ${import_chalk7.default.white(pkgName)}`));
632
1052
  }
633
1053
  let orgId = opts.org ?? config.getDefaultOrg();
634
1054
  if (!orgId) {
635
- const spinner = (0, import_ora4.default)("Fetching your organizations...").start();
1055
+ const spinner = (0, import_ora6.default)("Fetching your organizations...").start();
636
1056
  let orgs = [];
637
1057
  try {
638
1058
  const me = await api.whoami();
639
1059
  orgs = me.orgs;
640
1060
  spinner.stop();
641
1061
  } catch (err) {
642
- spinner.fail(
643
- import_chalk5.default.red(
644
- `Failed: ${err instanceof Error ? err.message : String(err)}`
645
- )
646
- );
1062
+ spinner.fail(import_chalk7.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
647
1063
  process.exit(1);
648
1064
  }
649
1065
  if (orgs.length === 0) {
650
- console.log(import_chalk5.default.red("No organizations found."));
1066
+ console.log(import_chalk7.default.red("No organizations found."));
651
1067
  process.exit(1);
652
1068
  }
653
1069
  if (orgs.length === 1 && orgs[0]) {
654
1070
  orgId = orgs[0].slug;
655
- console.log(import_chalk5.default.dim(` Using org: ${import_chalk5.default.white(orgId)}`));
1071
+ console.log(import_chalk7.default.dim(` Using org: ${import_chalk7.default.white(orgId)}`));
656
1072
  } else {
657
- console.log(import_chalk5.default.bold("\n Your organizations:\n"));
1073
+ console.log(import_chalk7.default.bold("\n Your organizations:\n"));
658
1074
  orgs.forEach((org, i) => {
659
- console.log(
660
- ` ${import_chalk5.default.cyan(String(i + 1))}. ${org.slug} ${import_chalk5.default.dim(`(${org.name})`)}`
661
- );
1075
+ console.log(` ${import_chalk7.default.cyan(String(i + 1))}. ${org.slug} ${import_chalk7.default.dim(`(${org.name})`)}`);
662
1076
  });
663
1077
  console.log("");
664
- const choice = await prompt(" Select org (number): ");
1078
+ const choice = await prompt3(" Select org (number): ");
665
1079
  const idx = Number.parseInt(choice, 10) - 1;
666
1080
  if (Number.isNaN(idx) || idx < 0 || idx >= orgs.length || !orgs[idx]) {
667
- console.log(import_chalk5.default.red("Invalid selection."));
1081
+ console.log(import_chalk7.default.red("Invalid selection."));
668
1082
  process.exit(1);
669
1083
  }
670
1084
  orgId = orgs[idx]?.slug;
671
1085
  }
672
1086
  }
673
- const spinner2 = (0, import_ora4.default)(`Fetching apps for org ${orgId}...`).start();
1087
+ const spinner2 = (0, import_ora6.default)("Fetching projects...").start();
674
1088
  let apps = [];
675
1089
  try {
676
- apps = await api.listApps(orgId);
1090
+ apps = await api.listProjects();
677
1091
  spinner2.stop();
678
1092
  } catch (err) {
679
- spinner2.fail(
680
- import_chalk5.default.red(
681
- `Failed: ${err instanceof Error ? err.message : String(err)}`
682
- )
683
- );
1093
+ spinner2.fail(import_chalk7.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
684
1094
  process.exit(1);
685
1095
  }
686
1096
  if (apps.length === 0) {
687
- console.log(import_chalk5.default.yellow("No apps found for this org."));
1097
+ console.log(import_chalk7.default.yellow("No apps found for this org."));
688
1098
  process.exit(1);
689
1099
  }
690
- console.log(import_chalk5.default.bold("\n Available apps:\n"));
1100
+ console.log(import_chalk7.default.bold("\n Available apps:\n"));
691
1101
  apps.forEach((app, i) => {
692
- console.log(
693
- ` ${import_chalk5.default.cyan(String(i + 1))}. ${app.slug} ${import_chalk5.default.dim(`(${app.name})`)}`
694
- );
1102
+ console.log(` ${import_chalk7.default.cyan(String(i + 1))}. ${app.slug} ${import_chalk7.default.dim(`(${app.name})`)}`);
695
1103
  });
696
1104
  console.log("");
697
- const appChoice = await prompt(" Select app (number): ");
1105
+ const appChoice = await prompt3(" Select app (number): ");
698
1106
  const appIdx = Number.parseInt(appChoice, 10) - 1;
699
1107
  if (Number.isNaN(appIdx) || appIdx < 0 || appIdx >= apps.length || !apps[appIdx]) {
700
- console.log(import_chalk5.default.red("Invalid selection."));
1108
+ console.log(import_chalk7.default.red("Invalid selection."));
701
1109
  process.exit(1);
702
1110
  }
703
1111
  const selectedApp = apps[appIdx];
704
1112
  if (!selectedApp) {
705
- console.log(import_chalk5.default.red("Invalid selection."));
1113
+ console.log(import_chalk7.default.red("Invalid selection."));
706
1114
  process.exit(1);
707
1115
  }
708
1116
  let defaultEnv = "production";
709
1117
  const envs = selectedApp.environments ?? [];
710
1118
  if (envs.length > 1) {
711
- console.log(import_chalk5.default.bold("\n Available environments:\n"));
1119
+ console.log(import_chalk7.default.bold("\n Available environments:\n"));
712
1120
  envs.forEach((env, i) => {
713
- console.log(
714
- ` ${import_chalk5.default.cyan(String(i + 1))}. ${env.slug} ${import_chalk5.default.dim(`(${env.name})`)}`
715
- );
1121
+ console.log(` ${import_chalk7.default.cyan(String(i + 1))}. ${env.slug} ${import_chalk7.default.dim(`(${env.name})`)}`);
716
1122
  });
717
1123
  console.log("");
718
- const envChoice = await prompt(
1124
+ const envChoice = await prompt3(
719
1125
  " Select default environment (number, or Enter for production): "
720
1126
  );
721
1127
  if (envChoice) {
@@ -734,23 +1140,21 @@ var linkCommand = new import_commander4.Command("link").description("Link curren
734
1140
  config.setDefaultOrg(orgId);
735
1141
  }
736
1142
  console.log("");
737
- console.log(import_chalk5.default.green(`\u2713 Linked to ${import_chalk5.default.bold(selectedApp.slug)}`));
1143
+ console.log(import_chalk7.default.green(`\u2713 Linked to ${import_chalk7.default.bold(selectedApp.slug)}`));
738
1144
  console.log(
739
- import_chalk5.default.dim(
740
- ` App ID: ${selectedApp.id} | Env: ${defaultEnv} | Dir: ${process.cwd()}`
741
- )
1145
+ import_chalk7.default.dim(` App ID: ${selectedApp.id} | Env: ${defaultEnv} | Dir: ${process.cwd()}`)
742
1146
  );
743
1147
  console.log("");
744
1148
  });
745
1149
 
746
1150
  // src/commands/login.ts
747
1151
  var import_node_crypto = __toESM(require("crypto"));
748
- var import_chalk6 = __toESM(require("chalk"));
749
- var import_commander5 = require("commander");
750
- var import_ora5 = __toESM(require("ora"));
1152
+ var import_chalk8 = __toESM(require("chalk"));
1153
+ var import_commander7 = require("commander");
1154
+ var import_ora7 = __toESM(require("ora"));
751
1155
  var BASE_URL2 = process.env.SYLPHX_API_URL ?? "https://sylphx.com";
752
- var POLL_INTERVAL_MS = 2e3;
753
- var POLL_TIMEOUT_MS = 5 * 60 * 1e3;
1156
+ var POLL_INTERVAL_MS2 = 2e3;
1157
+ var POLL_TIMEOUT_MS2 = 5 * 60 * 1e3;
754
1158
  async function initCode(code) {
755
1159
  const res = await fetch(`${BASE_URL2}/api/auth/cli/init`, {
756
1160
  method: "POST",
@@ -764,22 +1168,22 @@ async function initCode(code) {
764
1168
  throw new Error(`Failed to initialize auth code (HTTP ${res.status})`);
765
1169
  }
766
1170
  }
767
- var loginCommand = new import_commander5.Command("login").description("Authenticate with the Sylphx platform").option(
1171
+ var loginCommand = new import_commander7.Command("login").description("Authenticate with the Sylphx platform").option(
768
1172
  "--token <token>",
769
1173
  "Authenticate with a pre-existing API token (for CI/CD)"
770
1174
  ).action(async (opts) => {
771
1175
  if (opts.token) {
772
- const spinner2 = (0, import_ora5.default)("Validating token...").start();
1176
+ const spinner2 = (0, import_ora7.default)("Validating token...").start();
773
1177
  try {
774
1178
  config.setToken(opts.token);
775
1179
  const me = await api.whoami();
776
1180
  config.setToken(opts.token);
777
1181
  spinner2.succeed(
778
- import_chalk6.default.green(`Authenticated as ${import_chalk6.default.bold(me.user.email)}`)
1182
+ import_chalk8.default.green(`Authenticated as ${import_chalk8.default.bold(me.user.email)}`)
779
1183
  );
780
1184
  } catch (err) {
781
1185
  config.clearToken();
782
- spinner2.fail(import_chalk6.default.red("Invalid token"));
1186
+ spinner2.fail(import_chalk8.default.red("Invalid token"));
783
1187
  process.exit(1);
784
1188
  }
785
1189
  return;
@@ -787,18 +1191,18 @@ var loginCommand = new import_commander5.Command("login").description("Authentic
787
1191
  const code = import_node_crypto.default.randomBytes(16).toString("hex").slice(0, 20).toUpperCase();
788
1192
  const authUrl = `${BASE_URL2}/cli-auth?code=${code}`;
789
1193
  console.log("");
790
- console.log(import_chalk6.default.bold(" Sylphx CLI Authentication"));
1194
+ console.log(import_chalk8.default.bold(" Sylphx CLI Authentication"));
791
1195
  console.log("");
792
1196
  console.log(" Opening browser to:");
793
- console.log(` ${import_chalk6.default.cyan(authUrl)}`);
1197
+ console.log(` ${import_chalk8.default.cyan(authUrl)}`);
794
1198
  console.log("");
795
- console.log(` One-time code: ${import_chalk6.default.yellow.bold(code)}`);
1199
+ console.log(` One-time code: ${import_chalk8.default.yellow.bold(code)}`);
796
1200
  console.log("");
797
1201
  try {
798
1202
  await initCode(code);
799
1203
  } catch (err) {
800
1204
  console.log(
801
- import_chalk6.default.yellow(
1205
+ import_chalk8.default.yellow(
802
1206
  ` Warning: Could not register code: ${err instanceof Error ? err.message : String(err)}`
803
1207
  )
804
1208
  );
@@ -808,15 +1212,15 @@ var loginCommand = new import_commander5.Command("login").description("Authentic
808
1212
  await open(authUrl);
809
1213
  } catch {
810
1214
  console.log(
811
- import_chalk6.default.dim(
1215
+ import_chalk8.default.dim(
812
1216
  " Could not open browser automatically. Please visit the URL above."
813
1217
  )
814
1218
  );
815
1219
  }
816
- const spinner = (0, import_ora5.default)("Waiting for browser authorization...").start();
817
- const deadline = Date.now() + POLL_TIMEOUT_MS;
1220
+ const spinner = (0, import_ora7.default)("Waiting for browser authorization...").start();
1221
+ const deadline = Date.now() + POLL_TIMEOUT_MS2;
818
1222
  while (Date.now() < deadline) {
819
- await sleep(POLL_INTERVAL_MS);
1223
+ await sleep(POLL_INTERVAL_MS2);
820
1224
  try {
821
1225
  const result = await api.pollCliAuth(code);
822
1226
  if (result.status === "authorized" && result.token) {
@@ -824,14 +1228,14 @@ var loginCommand = new import_commander5.Command("login").description("Authentic
824
1228
  try {
825
1229
  const me = await api.whoami();
826
1230
  spinner.succeed(
827
- import_chalk6.default.green(`Authenticated as ${import_chalk6.default.bold(me.user.email)}`)
1231
+ import_chalk8.default.green(`Authenticated as ${import_chalk8.default.bold(me.user.email)}`)
828
1232
  );
829
1233
  } catch {
830
- spinner.succeed(import_chalk6.default.green("Authenticated successfully"));
1234
+ spinner.succeed(import_chalk8.default.green("Authenticated successfully"));
831
1235
  }
832
1236
  console.log("");
833
1237
  console.log(
834
- ` Token stored at: ${import_chalk6.default.dim(config.getConfigPath())}`
1238
+ ` Token stored at: ${import_chalk8.default.dim(config.getConfigPath())}`
835
1239
  );
836
1240
  console.log("");
837
1241
  return;
@@ -839,12 +1243,12 @@ var loginCommand = new import_commander5.Command("login").description("Authentic
839
1243
  } catch (err) {
840
1244
  const msg = err instanceof Error ? err.message : String(err);
841
1245
  if (msg.includes("404") || msg.includes("expired")) {
842
- spinner.fail(import_chalk6.default.red("Code expired or invalid. Please try again."));
1246
+ spinner.fail(import_chalk8.default.red("Code expired or invalid. Please try again."));
843
1247
  process.exit(1);
844
1248
  }
845
1249
  }
846
1250
  }
847
- spinner.fail(import_chalk6.default.red("Authentication timed out. Please try again."));
1251
+ spinner.fail(import_chalk8.default.red("Authentication timed out. Please try again."));
848
1252
  process.exit(1);
849
1253
  });
850
1254
  function sleep(ms) {
@@ -852,46 +1256,45 @@ function sleep(ms) {
852
1256
  }
853
1257
 
854
1258
  // src/commands/logout.ts
855
- var import_chalk7 = __toESM(require("chalk"));
856
- var import_commander6 = require("commander");
857
- var logoutCommand = new import_commander6.Command("logout").description("Clear stored credentials").action(() => {
1259
+ var import_chalk9 = __toESM(require("chalk"));
1260
+ var import_commander8 = require("commander");
1261
+ var logoutCommand = new import_commander8.Command("logout").description("Clear stored credentials").action(() => {
858
1262
  const token = config.getToken();
859
1263
  if (!token) {
860
- console.log(import_chalk7.default.yellow("You are not currently logged in."));
1264
+ console.log(import_chalk9.default.yellow("You are not currently logged in."));
861
1265
  return;
862
1266
  }
863
1267
  config.clearToken();
864
- console.log(import_chalk7.default.green("\u2713 Logged out successfully."));
1268
+ console.log(import_chalk9.default.green("\u2713 Logged out successfully."));
865
1269
  console.log(
866
- import_chalk7.default.dim(` Credentials cleared from ${config.getConfigPath()}`)
1270
+ import_chalk9.default.dim(` Credentials cleared from ${config.getConfigPath()}`)
867
1271
  );
868
1272
  });
869
1273
 
870
1274
  // src/commands/logs.ts
871
- var import_chalk8 = __toESM(require("chalk"));
872
- var import_commander7 = require("commander");
873
- var logsCommand = new import_commander7.Command("logs").description("Stream build/runtime logs").option("--env <env>", "Environment (e.g. production, staging)").option("-f, --follow", "Follow log output (stream continuously)").action(async (opts) => {
1275
+ var import_chalk10 = __toESM(require("chalk"));
1276
+ var import_commander9 = require("commander");
1277
+ var logsCommand = new import_commander9.Command("logs").description("Stream build/runtime logs").option("--env <env>", "Environment (e.g. production, staging)").option("-f, --follow", "Follow log output (stream continuously)").action(async (opts) => {
874
1278
  const token = config.getToken();
875
1279
  if (!token) {
876
- console.log(import_chalk8.default.red("Not authenticated. Run `sylphx login` first."));
1280
+ console.log(import_chalk10.default.red("Not authenticated. Run `sylphx login` first."));
877
1281
  process.exit(1);
878
1282
  }
879
1283
  const linked = config.getLinkedApp();
880
1284
  if (!linked) {
881
- console.log(import_chalk8.default.red("No app linked. Run `sylphx link` first."));
1285
+ console.log(import_chalk10.default.red("No app linked. Run `sylphx link` first."));
882
1286
  process.exit(1);
883
1287
  }
884
1288
  const env = opts.env ?? linked.defaultEnv;
885
- const envId = linked.appId;
886
1289
  console.log("");
887
1290
  console.log(
888
- import_chalk8.default.bold(
889
- ` Logs for ${import_chalk8.default.cyan(linked.appId)} [${import_chalk8.default.white(env)}]${opts.follow ? import_chalk8.default.dim(" (following...)") : ""}`
1291
+ import_chalk10.default.bold(
1292
+ ` Logs for ${import_chalk10.default.cyan(linked.appId)} [${import_chalk10.default.white(env)}]${opts.follow ? import_chalk10.default.dim(" (following...)") : ""}`
890
1293
  )
891
1294
  );
892
- console.log(import_chalk8.default.dim(` ${"\u2500".repeat(50)}`));
1295
+ console.log(import_chalk10.default.dim(` ${"\u2500".repeat(50)}`));
893
1296
  console.log("");
894
- const logStream = createLogStream(envId);
1297
+ const logStream = createLogStream(linked.appId, env);
895
1298
  await streamLogs(logStream.url, logStream.token, {
896
1299
  follow: opts.follow ?? false,
897
1300
  exitOnDone: false
@@ -902,28 +1305,28 @@ var logsCommand = new import_commander7.Command("logs").description("Stream buil
902
1305
  });
903
1306
 
904
1307
  // src/commands/open.ts
905
- var import_chalk9 = __toESM(require("chalk"));
906
- var import_commander8 = require("commander");
907
- var import_ora6 = __toESM(require("ora"));
908
- var openCommand = new import_commander8.Command("open").description("Open the app in your browser").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
1308
+ var import_chalk11 = __toESM(require("chalk"));
1309
+ var import_commander10 = require("commander");
1310
+ var import_ora8 = __toESM(require("ora"));
1311
+ var openCommand = new import_commander10.Command("open").description("Open the app in your browser").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
909
1312
  const token = config.getToken();
910
1313
  if (!token) {
911
- console.log(import_chalk9.default.red("Not authenticated. Run `sylphx login` first."));
1314
+ console.log(import_chalk11.default.red("Not authenticated. Run `sylphx login` first."));
912
1315
  process.exit(1);
913
1316
  }
914
1317
  const linked = config.getLinkedApp();
915
1318
  if (!linked) {
916
- console.log(import_chalk9.default.red("No app linked. Run `sylphx link` first."));
1319
+ console.log(import_chalk11.default.red("No app linked. Run `sylphx link` first."));
917
1320
  process.exit(1);
918
1321
  }
919
- const spinner = (0, import_ora6.default)("Fetching app URL...").start();
1322
+ const spinner = (0, import_ora8.default)("Fetching app URL...").start();
920
1323
  let url;
921
1324
  try {
922
1325
  const status = await api.getDeploymentStatus(linked.appId);
923
1326
  spinner.stop();
924
1327
  if (!status.url) {
925
1328
  url = `https://sylphx.com/console/apps/${linked.appId}`;
926
- console.log(import_chalk9.default.yellow(" No URL found, opening console instead."));
1329
+ console.log(import_chalk11.default.yellow(" No URL found, opening console instead."));
927
1330
  } else {
928
1331
  url = status.url;
929
1332
  }
@@ -931,53 +1334,157 @@ var openCommand = new import_commander8.Command("open").description("Open the ap
931
1334
  spinner.stop();
932
1335
  url = `https://sylphx.com/console/apps/${linked.appId}`;
933
1336
  }
934
- console.log(` Opening ${import_chalk9.default.cyan(url)}`);
1337
+ console.log(` Opening ${import_chalk11.default.cyan(url)}`);
935
1338
  try {
936
1339
  const { default: open } = await import("open");
937
1340
  await open(url);
938
1341
  } catch (err) {
939
1342
  console.log(
940
- import_chalk9.default.red(
1343
+ import_chalk11.default.red(
941
1344
  `Failed to open browser: ${err instanceof Error ? err.message : String(err)}`
942
1345
  )
943
1346
  );
944
- console.log(import_chalk9.default.dim(` URL: ${url}`));
1347
+ console.log(import_chalk11.default.dim(` URL: ${url}`));
1348
+ process.exit(1);
1349
+ }
1350
+ });
1351
+
1352
+ // src/commands/projects.ts
1353
+ var import_node_readline4 = __toESM(require("readline"));
1354
+ var import_chalk12 = __toESM(require("chalk"));
1355
+ var import_commander11 = require("commander");
1356
+ var import_ora9 = __toESM(require("ora"));
1357
+ function requireAuth2() {
1358
+ const token = config.getToken();
1359
+ if (!token) {
1360
+ console.log(import_chalk12.default.red("Not authenticated. Run `sylphx login` first."));
1361
+ process.exit(1);
1362
+ }
1363
+ return token;
1364
+ }
1365
+ function slugify2(name) {
1366
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1367
+ }
1368
+ function prompt4(question) {
1369
+ return new Promise((resolve) => {
1370
+ const rl = import_node_readline4.default.createInterface({ input: process.stdin, output: process.stdout });
1371
+ rl.question(`${question}: `, (answer) => {
1372
+ rl.close();
1373
+ resolve(answer.trim());
1374
+ });
1375
+ });
1376
+ }
1377
+ var projectsListCommand = new import_commander11.Command("list").description("List all projects").action(async () => {
1378
+ requireAuth2();
1379
+ const spinner = (0, import_ora9.default)("Fetching projects...").start();
1380
+ try {
1381
+ const projects = await api.listProjects();
1382
+ spinner.stop();
1383
+ if (projects.length === 0) {
1384
+ console.log(import_chalk12.default.dim("\n No projects found.\n"));
1385
+ return;
1386
+ }
1387
+ const colSlug = Math.max(4, ...projects.map((p) => p.slug.length));
1388
+ const colName = Math.max(4, ...projects.map((p) => p.name.length));
1389
+ console.log("");
1390
+ console.log(import_chalk12.default.bold(" Projects"));
1391
+ console.log(import_chalk12.default.dim(` ${"\u2500".repeat(colSlug + colName + 30)}`));
1392
+ console.log(
1393
+ import_chalk12.default.dim(` ${"SLUG".padEnd(colSlug)} ${"NAME".padEnd(colName)} ${"ENVIRONMENTS"}`)
1394
+ );
1395
+ console.log(import_chalk12.default.dim(` ${"\u2500".repeat(colSlug + colName + 30)}`));
1396
+ for (const p of projects) {
1397
+ const envNames = p.environments && p.environments.length > 0 ? p.environments.map((e) => e.slug).join(", ") : import_chalk12.default.dim("\u2014");
1398
+ console.log(
1399
+ ` ${import_chalk12.default.cyan(p.slug.padEnd(colSlug))} ${p.name.padEnd(colName)} ${envNames}`
1400
+ );
1401
+ }
1402
+ console.log("");
1403
+ } catch (err) {
1404
+ spinner.fail(import_chalk12.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
1405
+ process.exit(1);
1406
+ }
1407
+ });
1408
+ var projectsCreateCommand = new import_commander11.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) => {
1409
+ requireAuth2();
1410
+ const slug = slugify2(name);
1411
+ const spinner = (0, import_ora9.default)(`Creating project ${import_chalk12.default.bold(name)}...`).start();
1412
+ try {
1413
+ const project = await api.createProject({
1414
+ name,
1415
+ slug,
1416
+ ...opts.git ? { gitRepository: opts.git } : {},
1417
+ ...opts.port ? { port: Number.parseInt(opts.port, 10) } : {}
1418
+ });
1419
+ spinner.succeed(import_chalk12.default.green(`\u2713 Created project ${import_chalk12.default.bold(project.name)}`));
1420
+ console.log("");
1421
+ console.log(import_chalk12.default.dim(` ID : ${project.id}`));
1422
+ console.log(import_chalk12.default.dim(` Slug : ${project.slug}`));
1423
+ console.log("");
1424
+ console.log(
1425
+ import_chalk12.default.dim(` Run ${import_chalk12.default.cyan("sylphx link")} to link this project to a directory.`)
1426
+ );
1427
+ console.log("");
1428
+ } catch (err) {
1429
+ spinner.fail(import_chalk12.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
1430
+ process.exit(1);
1431
+ }
1432
+ });
1433
+ var projectsDeleteCommand = new import_commander11.Command("delete").description("Delete a project").argument("<id>", "Project ID or slug").option("--force", "Skip confirmation prompt").action(async (id, opts) => {
1434
+ requireAuth2();
1435
+ if (!opts.force) {
1436
+ console.log("");
1437
+ console.log(
1438
+ import_chalk12.default.yellow(` \u26A0 You are about to permanently delete project ${import_chalk12.default.bold(id)}.`)
1439
+ );
1440
+ console.log(import_chalk12.default.yellow(" This action cannot be undone."));
1441
+ console.log("");
1442
+ const answer = await prompt4(" Type the project ID/slug to confirm");
1443
+ if (answer !== id) {
1444
+ console.log(import_chalk12.default.red("Confirmation did not match. Aborted."));
1445
+ process.exit(1);
1446
+ }
1447
+ }
1448
+ const spinner = (0, import_ora9.default)(`Deleting project ${import_chalk12.default.bold(id)}...`).start();
1449
+ try {
1450
+ await api.deleteProject(id);
1451
+ spinner.succeed(import_chalk12.default.green(`\u2713 Deleted project ${import_chalk12.default.bold(id)}`));
1452
+ } catch (err) {
1453
+ spinner.fail(import_chalk12.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
945
1454
  process.exit(1);
946
1455
  }
947
1456
  });
1457
+ var projectsCommand = new import_commander11.Command("projects").description("Manage Sylphx projects").addCommand(projectsListCommand).addCommand(projectsCreateCommand).addCommand(projectsDeleteCommand);
948
1458
 
949
1459
  // src/commands/rollback.ts
950
- var import_chalk10 = __toESM(require("chalk"));
951
- var import_commander9 = require("commander");
952
- var import_ora7 = __toESM(require("ora"));
953
- var rollbackCommand = new import_commander9.Command("rollback").description("Rollback to the previous deployment").option("--env <env>", "Environment (e.g. production, staging)").option("--force", "Skip confirmation prompt").action(async (opts) => {
1460
+ var import_chalk13 = __toESM(require("chalk"));
1461
+ var import_commander12 = require("commander");
1462
+ var import_ora10 = __toESM(require("ora"));
1463
+ var rollbackCommand = new import_commander12.Command("rollback").description("Rollback to the previous deployment").option("--env <env>", "Environment (e.g. production, staging)").option("--force", "Skip confirmation prompt").action(async (opts) => {
954
1464
  const token = config.getToken();
955
1465
  if (!token) {
956
- console.log(import_chalk10.default.red("Not authenticated. Run `sylphx login` first."));
1466
+ console.log(import_chalk13.default.red("Not authenticated. Run `sylphx login` first."));
957
1467
  process.exit(1);
958
1468
  }
959
1469
  const linked = config.getLinkedApp();
960
1470
  if (!linked) {
961
- console.log(import_chalk10.default.red("No app linked. Run `sylphx link` first."));
1471
+ console.log(import_chalk13.default.red("No app linked. Run `sylphx link` first."));
962
1472
  process.exit(1);
963
1473
  }
964
1474
  const env = opts.env ?? linked.defaultEnv;
965
- const envId = linked.appId;
966
1475
  if (!opts.force) {
967
- const readline2 = await import("readline");
968
- const rl = readline2.createInterface({
1476
+ const readline5 = await import("readline");
1477
+ const rl = readline5.createInterface({
969
1478
  input: process.stdin,
970
1479
  output: process.stdout
971
1480
  });
972
1481
  await new Promise((resolve, reject) => {
973
1482
  rl.question(
974
- import_chalk10.default.yellow(
975
- ` Rollback ${import_chalk10.default.bold(linked.appId)} [${env}]? (y/N) `
976
- ),
1483
+ import_chalk13.default.yellow(` Rollback ${import_chalk13.default.bold(linked.appId)} [${env}]? (y/N) `),
977
1484
  (answer) => {
978
1485
  rl.close();
979
1486
  if (answer.toLowerCase() !== "y") {
980
- console.log(import_chalk10.default.dim(" Cancelled."));
1487
+ console.log(import_chalk13.default.dim(" Cancelled."));
981
1488
  process.exit(0);
982
1489
  }
983
1490
  resolve();
@@ -985,129 +1492,145 @@ var rollbackCommand = new import_commander9.Command("rollback").description("Rol
985
1492
  );
986
1493
  });
987
1494
  }
988
- const spinner = (0, import_ora7.default)(`Rolling back ${linked.appId} [${env}]...`).start();
1495
+ const spinner = (0, import_ora10.default)(`Rolling back ${linked.appId} [${env}]...`).start();
989
1496
  try {
990
- const result = await api.rollback(envId);
991
- spinner.succeed(import_chalk10.default.green(`\u2713 Rollback initiated: ${result.status}`));
1497
+ const result = await api.rollback(linked.appId, env);
1498
+ spinner.succeed(import_chalk13.default.green(`\u2713 Rollback initiated: ${result.status}`));
992
1499
  if (result.deploymentId) {
993
- console.log(import_chalk10.default.dim(` Deployment ID: ${result.deploymentId}`));
1500
+ console.log(import_chalk13.default.dim(` Deployment ID: ${result.deploymentId}`));
994
1501
  }
995
1502
  if (result.message) {
996
- console.log(import_chalk10.default.dim(` ${result.message}`));
1503
+ console.log(import_chalk13.default.dim(` ${result.message}`));
997
1504
  }
998
1505
  } catch (err) {
999
1506
  spinner.fail(
1000
- import_chalk10.default.red(
1001
- `Rollback failed: ${err instanceof Error ? err.message : String(err)}`
1002
- )
1507
+ import_chalk13.default.red(`Rollback failed: ${err instanceof Error ? err.message : String(err)}`)
1003
1508
  );
1004
1509
  process.exit(1);
1005
1510
  }
1006
1511
  });
1007
1512
 
1008
1513
  // src/commands/status.ts
1009
- var import_chalk11 = __toESM(require("chalk"));
1010
- var import_commander10 = require("commander");
1011
- var import_ora8 = __toESM(require("ora"));
1514
+ var import_chalk14 = __toESM(require("chalk"));
1515
+ var import_commander13 = require("commander");
1516
+ var import_ora11 = __toESM(require("ora"));
1012
1517
  function statusColor(status) {
1013
1518
  const s = status.toLowerCase();
1014
1519
  if (s === "running" || s === "active" || s === "healthy" || s === "success") {
1015
- return import_chalk11.default.green(status);
1520
+ return import_chalk14.default.green(status);
1016
1521
  }
1017
1522
  if (s === "deploying" || s === "building" || s === "pending") {
1018
- return import_chalk11.default.yellow(status);
1523
+ return import_chalk14.default.yellow(status);
1019
1524
  }
1020
1525
  if (s === "error" || s === "failed" || s === "stopped") {
1021
- return import_chalk11.default.red(status);
1526
+ return import_chalk14.default.red(status);
1022
1527
  }
1023
- return import_chalk11.default.white(status);
1528
+ return import_chalk14.default.white(status);
1024
1529
  }
1025
- var statusCommand = new import_commander10.Command("status").description("Show deployment status").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
1530
+ var statusCommand = new import_commander13.Command("status").description("Show deployment status").option("--env <env>", "Environment (e.g. production, staging)").action(async (opts) => {
1026
1531
  const token = config.getToken();
1027
1532
  if (!token) {
1028
- console.log(import_chalk11.default.red("Not authenticated. Run `sylphx login` first."));
1533
+ console.log(import_chalk14.default.red("Not authenticated. Run `sylphx login` first."));
1029
1534
  process.exit(1);
1030
1535
  }
1031
1536
  const linked = config.getLinkedApp();
1032
1537
  if (!linked) {
1033
- console.log(import_chalk11.default.red("No app linked. Run `sylphx link` first."));
1538
+ console.log(import_chalk14.default.red("No app linked. Run `sylphx link` first."));
1034
1539
  process.exit(1);
1035
1540
  }
1036
1541
  const env = opts.env ?? linked.defaultEnv;
1037
- const spinner = (0, import_ora8.default)(`Fetching status [${env}]...`).start();
1542
+ const spinner = (0, import_ora11.default)(`Fetching status [${env}]...`).start();
1038
1543
  try {
1039
1544
  const status = await api.getDeploymentStatus(linked.appId);
1040
1545
  spinner.stop();
1041
1546
  console.log("");
1042
- console.log(import_chalk11.default.bold(" Deployment Status"));
1043
- console.log(import_chalk11.default.dim(` ${"\u2500".repeat(40)}`));
1547
+ console.log(import_chalk14.default.bold(" Deployment Status"));
1548
+ console.log(import_chalk14.default.dim(` ${"\u2500".repeat(40)}`));
1044
1549
  console.log("");
1045
- console.log(` App: ${import_chalk11.default.cyan(linked.appId)}`);
1046
- console.log(` Environment: ${import_chalk11.default.white(env)}`);
1550
+ console.log(` App: ${import_chalk14.default.cyan(linked.appId)}`);
1551
+ console.log(` Environment: ${import_chalk14.default.white(env)}`);
1047
1552
  console.log(` Status: ${statusColor(status.status)}`);
1048
1553
  if (status.deployedAt) {
1049
1554
  const date = new Date(status.deployedAt);
1050
- console.log(` Deployed: ${import_chalk11.default.white(date.toLocaleString())}`);
1555
+ console.log(` Deployed: ${import_chalk14.default.white(date.toLocaleString())}`);
1051
1556
  }
1052
1557
  if (status.url) {
1053
- console.log(` URL: ${import_chalk11.default.cyan(status.url)}`);
1558
+ console.log(` URL: ${import_chalk14.default.cyan(status.url)}`);
1559
+ }
1560
+ if (status.replicas) {
1561
+ const { ready, desired } = status.replicas;
1562
+ const replicaColor = ready === desired ? import_chalk14.default.green : import_chalk14.default.yellow;
1563
+ console.log(` Replicas: ${replicaColor(`${ready}/${desired}`)}`);
1054
1564
  }
1055
1565
  console.log("");
1056
1566
  } catch (err) {
1057
- spinner.fail(
1058
- import_chalk11.default.red(
1059
- `Failed: ${err instanceof Error ? err.message : String(err)}`
1060
- )
1061
- );
1567
+ spinner.fail(import_chalk14.default.red(`Failed: ${err instanceof Error ? err.message : String(err)}`));
1062
1568
  process.exit(1);
1063
1569
  }
1064
1570
  });
1065
1571
 
1572
+ // src/commands/unlink.ts
1573
+ var import_chalk15 = __toESM(require("chalk"));
1574
+ var import_commander14 = require("commander");
1575
+ var unlinkCommand = new import_commander14.Command("unlink").description("Unlink current directory from its linked app").action(() => {
1576
+ const linked = config.getLinkedApp();
1577
+ if (!linked) {
1578
+ console.log(import_chalk15.default.yellow(" This directory is not linked to any app."));
1579
+ process.exit(0);
1580
+ }
1581
+ const appId = linked.appId;
1582
+ config.unlinkApp();
1583
+ console.log("");
1584
+ console.log(import_chalk15.default.green(`\u2713 Unlinked from ${import_chalk15.default.bold(appId)}`));
1585
+ console.log(import_chalk15.default.dim(` Directory: ${process.cwd()}`));
1586
+ console.log("");
1587
+ });
1588
+
1066
1589
  // src/commands/whoami.ts
1067
- var import_chalk12 = __toESM(require("chalk"));
1068
- var import_commander11 = require("commander");
1069
- var import_ora9 = __toESM(require("ora"));
1070
- var whoamiCommand = new import_commander11.Command("whoami").description("Show current user, org, and linked app").action(async () => {
1590
+ var import_chalk16 = __toESM(require("chalk"));
1591
+ var import_commander15 = require("commander");
1592
+ var import_ora12 = __toESM(require("ora"));
1593
+ var whoamiCommand = new import_commander15.Command("whoami").description("Show current user, org, and linked app").action(async () => {
1071
1594
  const token = config.getToken();
1072
1595
  if (!token) {
1073
- console.log(import_chalk12.default.red("Not authenticated. Run `sylphx login` first."));
1596
+ console.log(import_chalk16.default.red("Not authenticated. Run `sylphx login` first."));
1074
1597
  process.exit(1);
1075
1598
  }
1076
- const spinner = (0, import_ora9.default)("Fetching account info...").start();
1599
+ const spinner = (0, import_ora12.default)("Fetching account info...").start();
1077
1600
  try {
1078
1601
  const me = await api.whoami();
1079
1602
  spinner.stop();
1080
1603
  console.log("");
1081
- console.log(import_chalk12.default.bold(" Account"));
1082
- console.log(` User: ${import_chalk12.default.cyan(me.user.email)}`);
1083
- console.log(` Name: ${import_chalk12.default.white(me.user.name)}`);
1084
- console.log(` ID: ${import_chalk12.default.dim(me.user.id)}`);
1604
+ console.log(import_chalk16.default.bold(" Account"));
1605
+ console.log(` User: ${import_chalk16.default.cyan(me.user.email)}`);
1606
+ console.log(` Name: ${import_chalk16.default.white(me.user.name)}`);
1607
+ console.log(` ID: ${import_chalk16.default.dim(me.user.id)}`);
1085
1608
  if (me.orgs.length > 0) {
1086
1609
  console.log("");
1087
- console.log(import_chalk12.default.bold(" Organizations"));
1610
+ console.log(import_chalk16.default.bold(" Organizations"));
1088
1611
  for (const org of me.orgs) {
1089
1612
  console.log(
1090
- ` ${import_chalk12.default.cyan(org.slug)} ${import_chalk12.default.dim(`(${org.name})`)}`
1613
+ ` ${import_chalk16.default.cyan(org.slug)} ${import_chalk16.default.dim(`(${org.name})`)}`
1091
1614
  );
1092
1615
  }
1093
1616
  }
1094
1617
  const linkedApp = config.getLinkedApp();
1095
1618
  if (linkedApp) {
1096
1619
  console.log("");
1097
- console.log(import_chalk12.default.bold(" Linked App"));
1098
- console.log(` App: ${import_chalk12.default.cyan(linkedApp.appId)}`);
1099
- console.log(` Org: ${import_chalk12.default.white(linkedApp.orgId)}`);
1100
- console.log(` Env: ${import_chalk12.default.white(linkedApp.defaultEnv)}`);
1620
+ console.log(import_chalk16.default.bold(" Linked App"));
1621
+ console.log(` App: ${import_chalk16.default.cyan(linkedApp.appId)}`);
1622
+ console.log(` Org: ${import_chalk16.default.white(linkedApp.orgId)}`);
1623
+ console.log(` Env: ${import_chalk16.default.white(linkedApp.defaultEnv)}`);
1101
1624
  } else {
1102
1625
  console.log("");
1103
1626
  console.log(
1104
- import_chalk12.default.dim(" No app linked. Run `sylphx link` to link one.")
1627
+ import_chalk16.default.dim(" No app linked. Run `sylphx link` to link one.")
1105
1628
  );
1106
1629
  }
1107
1630
  console.log("");
1108
1631
  } catch (err) {
1109
1632
  spinner.fail(
1110
- import_chalk12.default.red(
1633
+ import_chalk16.default.red(
1111
1634
  `Failed: ${err instanceof Error ? err.message : String(err)}`
1112
1635
  )
1113
1636
  );
@@ -1116,40 +1639,43 @@ var whoamiCommand = new import_commander11.Command("whoami").description("Show c
1116
1639
  });
1117
1640
 
1118
1641
  // src/index.ts
1119
- var program = new import_commander12.Command();
1120
- program.name("sylphx").description(
1121
- `${import_chalk13.default.bold("Sylphx Platform CLI")} \u2014 deploy and manage your applications`
1122
- ).version("0.1.0", "-v, --version", "Print version").helpOption("-h, --help", "Show help").addHelpText(
1642
+ var program = new import_commander16.Command();
1643
+ program.name("sylphx").description(`${import_chalk17.default.bold("Sylphx Platform CLI")} \u2014 deploy and manage your applications`).version("0.1.0", "-v, --version", "Print version").helpOption("-h, --help", "Show help").addHelpText(
1123
1644
  "after",
1124
1645
  `
1125
- ${import_chalk13.default.bold("Examples:")}
1126
- ${import_chalk13.default.cyan("sylphx login")} Authenticate with Sylphx
1127
- ${import_chalk13.default.cyan("sylphx link")} Link current project to an app
1128
- ${import_chalk13.default.cyan("sylphx deploy")} Deploy to production
1129
- ${import_chalk13.default.cyan("sylphx deploy --env staging")} Deploy to staging
1130
- ${import_chalk13.default.cyan("sylphx logs -f")} Stream live logs
1131
- ${import_chalk13.default.cyan("sylphx env list")} List environment variables
1132
- ${import_chalk13.default.cyan("sylphx env set PORT=3000")} Set an env var
1133
- ${import_chalk13.default.cyan("sylphx status")} Check deployment status
1646
+ ${import_chalk17.default.bold("Examples:")}
1647
+ ${import_chalk17.default.cyan("sylphx login")} Authenticate with Sylphx
1648
+ ${import_chalk17.default.cyan("sylphx init my-app")} Create and link a new project
1649
+ ${import_chalk17.default.cyan("sylphx link")} Link current directory to an app
1650
+ ${import_chalk17.default.cyan("sylphx unlink")} Unlink current directory from its app
1651
+ ${import_chalk17.default.cyan("sylphx deploy")} Deploy to production
1652
+ ${import_chalk17.default.cyan("sylphx deploy --env staging")} Deploy to staging
1653
+ ${import_chalk17.default.cyan("sylphx logs -f")} Stream live logs
1654
+ ${import_chalk17.default.cyan("sylphx env list")} List environment variables
1655
+ ${import_chalk17.default.cyan("sylphx env set PORT=3000")} Set an env var
1656
+ ${import_chalk17.default.cyan("sylphx env pull --file .env")} Pull env vars to a local .env file
1657
+ ${import_chalk17.default.cyan("sylphx env push --file .env")} Push .env to the platform
1658
+ ${import_chalk17.default.cyan("sylphx status")} Check deployment status
1659
+ ${import_chalk17.default.cyan("sylphx projects list")} List all projects
1660
+ ${import_chalk17.default.cyan("sylphx db create my-db")} Provision a database
1661
+ ${import_chalk17.default.cyan("sylphx db list")} List databases
1134
1662
 
1135
- ${import_chalk13.default.bold("Documentation:")}
1136
- ${import_chalk13.default.underline("https://docs.sylphx.com/cli")}
1663
+ ${import_chalk17.default.bold("Documentation:")}
1664
+ ${import_chalk17.default.underline("https://docs.sylphx.com/cli")}
1137
1665
  `
1138
1666
  );
1139
- program.addCommand(loginCommand).addCommand(logoutCommand).addCommand(whoamiCommand).addCommand(linkCommand).addCommand(deployCommand).addCommand(logsCommand).addCommand(envCommand).addCommand(domainsCommand).addCommand(rollbackCommand).addCommand(openCommand).addCommand(statusCommand);
1667
+ program.addCommand(loginCommand).addCommand(logoutCommand).addCommand(whoamiCommand).addCommand(initCommand).addCommand(linkCommand).addCommand(unlinkCommand).addCommand(deployCommand).addCommand(logsCommand).addCommand(envCommand).addCommand(domainsCommand).addCommand(rollbackCommand).addCommand(openCommand).addCommand(statusCommand).addCommand(projectsCommand).addCommand(dbCommand);
1140
1668
  program.on("command:*", (operands) => {
1141
- console.error(
1142
- import_chalk13.default.red(`
1143
- Unknown command: ${import_chalk13.default.bold(operands[0] ?? "")}
1144
- `)
1145
- );
1146
- console.log(` Run ${import_chalk13.default.cyan("sylphx --help")} for usage.
1669
+ console.error(import_chalk17.default.red(`
1670
+ Unknown command: ${import_chalk17.default.bold(operands[0] ?? "")}
1671
+ `));
1672
+ console.log(` Run ${import_chalk17.default.cyan("sylphx --help")} for usage.
1147
1673
  `);
1148
1674
  process.exit(1);
1149
1675
  });
1150
1676
  program.parseAsync(process.argv).catch((err) => {
1151
1677
  const msg = err instanceof Error ? err.message : String(err);
1152
- console.error(import_chalk13.default.red(`
1678
+ console.error(import_chalk17.default.red(`
1153
1679
  Error: ${msg}
1154
1680
  `));
1155
1681
  process.exit(1);