@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.
- package/dist/index.js +913 -387
- 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
|
|
28
|
-
var
|
|
27
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
28
|
+
var import_commander16 = require("commander");
|
|
29
29
|
|
|
30
|
-
// src/commands/
|
|
31
|
-
var
|
|
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}/
|
|
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,
|
|
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}${
|
|
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 =
|
|
146
|
+
body = JSON.parse(text);
|
|
144
147
|
} catch {
|
|
145
|
-
body =
|
|
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
|
|
157
|
-
async getDeploymentStatus(
|
|
158
|
-
return request("GET",
|
|
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
|
|
163
|
-
async triggerDeploy(
|
|
164
|
-
return request("POST", `/
|
|
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
|
|
167
|
-
async getDeploymentHistory(
|
|
168
|
-
return request("GET", `/
|
|
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
|
-
|
|
172
|
-
|
|
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
|
|
175
|
-
async listEnvVars(
|
|
176
|
-
return request("GET", `/
|
|
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(
|
|
180
|
-
return request("POST", `/
|
|
181
|
-
body: {
|
|
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(
|
|
186
|
-
return request(
|
|
187
|
-
|
|
188
|
-
|
|
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
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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", "/
|
|
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
|
-
/**
|
|
213
|
-
async
|
|
214
|
-
return request("
|
|
215
|
-
|
|
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
|
-
|
|
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(
|
|
285
|
+
function createLogStream(projectId, envType) {
|
|
286
|
+
const params = new URLSearchParams({ envType, tail: "100" });
|
|
233
287
|
return {
|
|
234
|
-
url: `${API_BASE}/
|
|
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
|
|
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 ` ${
|
|
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 ? `${
|
|
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}${
|
|
491
|
+
return ` ${prefix}${import_chalk2.default.red("\u2717")} ${import_chalk2.default.red(message)}`;
|
|
257
492
|
case "warn":
|
|
258
|
-
return ` ${prefix}${
|
|
493
|
+
return ` ${prefix}${import_chalk2.default.yellow("\u26A0")} ${import_chalk2.default.yellow(message)}`;
|
|
259
494
|
case "success":
|
|
260
|
-
return ` ${prefix}${
|
|
495
|
+
return ` ${prefix}${import_chalk2.default.green("\u2713")} ${import_chalk2.default.green(message)}`;
|
|
261
496
|
case "debug":
|
|
262
|
-
return ` ${prefix}${
|
|
497
|
+
return ` ${prefix}${import_chalk2.default.dim(message)}`;
|
|
263
498
|
default:
|
|
264
|
-
return ` ${prefix}${
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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,
|
|
367
|
-
let
|
|
597
|
+
const spinner = (0, import_ora2.default)("Triggering deployment...").start();
|
|
598
|
+
let deploymentId;
|
|
368
599
|
try {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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(
|
|
387
|
-
console.log(
|
|
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(
|
|
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(
|
|
624
|
+
console.log(import_chalk3.default.red(" Deployment failed."));
|
|
397
625
|
process.exit(1);
|
|
398
626
|
}
|
|
399
627
|
console.log("");
|
|
400
|
-
console.log(
|
|
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
|
|
406
|
-
var
|
|
407
|
-
var
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
424
|
-
const spinner = (0,
|
|
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(
|
|
654
|
+
const domains = await api.listDomains(linked.appId, env);
|
|
427
655
|
spinner.stop();
|
|
428
656
|
if (domains.length === 0) {
|
|
429
|
-
console.log(
|
|
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(
|
|
434
|
-
console.log(
|
|
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 ?
|
|
438
|
-
const ssl = d.ssl ?
|
|
439
|
-
console.log(` ${
|
|
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
|
|
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
|
|
454
|
-
const spinner = (0,
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
475
|
-
const spinner = (0,
|
|
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(
|
|
478
|
-
spinner.succeed(
|
|
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
|
|
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
|
|
492
|
-
var
|
|
493
|
-
var
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
728
|
+
const vars = await api.listEnvVars(linked.appId, env);
|
|
514
729
|
spinner.stop();
|
|
515
730
|
if (vars.length === 0) {
|
|
516
|
-
console.log(
|
|
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(
|
|
521
|
-
console.log(
|
|
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 =
|
|
526
|
-
const
|
|
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
|
-
|
|
533
|
-
|
|
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
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
-
|
|
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.
|
|
565
|
-
|
|
936
|
+
const me = await api.whoami();
|
|
937
|
+
orgs = me.orgs;
|
|
938
|
+
spinner2.stop();
|
|
566
939
|
} catch (err) {
|
|
567
|
-
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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.
|
|
583
|
-
|
|
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
|
|
597
|
-
var
|
|
598
|
-
var
|
|
599
|
-
var
|
|
600
|
-
var
|
|
601
|
-
var
|
|
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 =
|
|
605
|
-
const pkg = JSON.parse(
|
|
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
|
|
1031
|
+
function prompt3(question) {
|
|
612
1032
|
return new Promise((resolve) => {
|
|
613
|
-
const rl =
|
|
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
|
|
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(
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
1071
|
+
console.log(import_chalk7.default.dim(` Using org: ${import_chalk7.default.white(orgId)}`));
|
|
656
1072
|
} else {
|
|
657
|
-
console.log(
|
|
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
|
|
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(
|
|
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,
|
|
1087
|
+
const spinner2 = (0, import_ora6.default)("Fetching projects...").start();
|
|
674
1088
|
let apps = [];
|
|
675
1089
|
try {
|
|
676
|
-
apps = await api.
|
|
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(
|
|
1097
|
+
console.log(import_chalk7.default.yellow("No apps found for this org."));
|
|
688
1098
|
process.exit(1);
|
|
689
1099
|
}
|
|
690
|
-
console.log(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
1143
|
+
console.log(import_chalk7.default.green(`\u2713 Linked to ${import_chalk7.default.bold(selectedApp.slug)}`));
|
|
738
1144
|
console.log(
|
|
739
|
-
|
|
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
|
|
749
|
-
var
|
|
750
|
-
var
|
|
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
|
|
753
|
-
var
|
|
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
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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(
|
|
1194
|
+
console.log(import_chalk8.default.bold(" Sylphx CLI Authentication"));
|
|
791
1195
|
console.log("");
|
|
792
1196
|
console.log(" Opening browser to:");
|
|
793
|
-
console.log(` ${
|
|
1197
|
+
console.log(` ${import_chalk8.default.cyan(authUrl)}`);
|
|
794
1198
|
console.log("");
|
|
795
|
-
console.log(` One-time 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
|
-
|
|
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
|
-
|
|
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,
|
|
817
|
-
const deadline = Date.now() +
|
|
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(
|
|
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
|
-
|
|
1231
|
+
import_chalk8.default.green(`Authenticated as ${import_chalk8.default.bold(me.user.email)}`)
|
|
828
1232
|
);
|
|
829
1233
|
} catch {
|
|
830
|
-
spinner.succeed(
|
|
1234
|
+
spinner.succeed(import_chalk8.default.green("Authenticated successfully"));
|
|
831
1235
|
}
|
|
832
1236
|
console.log("");
|
|
833
1237
|
console.log(
|
|
834
|
-
` Token stored at: ${
|
|
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(
|
|
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(
|
|
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
|
|
856
|
-
var
|
|
857
|
-
var logoutCommand = new
|
|
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(
|
|
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(
|
|
1268
|
+
console.log(import_chalk9.default.green("\u2713 Logged out successfully."));
|
|
865
1269
|
console.log(
|
|
866
|
-
|
|
1270
|
+
import_chalk9.default.dim(` Credentials cleared from ${config.getConfigPath()}`)
|
|
867
1271
|
);
|
|
868
1272
|
});
|
|
869
1273
|
|
|
870
1274
|
// src/commands/logs.ts
|
|
871
|
-
var
|
|
872
|
-
var
|
|
873
|
-
var logsCommand = new
|
|
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(
|
|
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(
|
|
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
|
-
|
|
889
|
-
` Logs for ${
|
|
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(
|
|
1295
|
+
console.log(import_chalk10.default.dim(` ${"\u2500".repeat(50)}`));
|
|
893
1296
|
console.log("");
|
|
894
|
-
const logStream = createLogStream(
|
|
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
|
|
906
|
-
var
|
|
907
|
-
var
|
|
908
|
-
var openCommand = new
|
|
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(
|
|
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(
|
|
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,
|
|
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(
|
|
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 ${
|
|
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
|
-
|
|
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(
|
|
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
|
|
951
|
-
var
|
|
952
|
-
var
|
|
953
|
-
var rollbackCommand = new
|
|
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(
|
|
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(
|
|
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
|
|
968
|
-
const rl =
|
|
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
|
-
|
|
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(
|
|
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,
|
|
1495
|
+
const spinner = (0, import_ora10.default)(`Rolling back ${linked.appId} [${env}]...`).start();
|
|
989
1496
|
try {
|
|
990
|
-
const result = await api.rollback(
|
|
991
|
-
spinner.succeed(
|
|
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(
|
|
1500
|
+
console.log(import_chalk13.default.dim(` Deployment ID: ${result.deploymentId}`));
|
|
994
1501
|
}
|
|
995
1502
|
if (result.message) {
|
|
996
|
-
console.log(
|
|
1503
|
+
console.log(import_chalk13.default.dim(` ${result.message}`));
|
|
997
1504
|
}
|
|
998
1505
|
} catch (err) {
|
|
999
1506
|
spinner.fail(
|
|
1000
|
-
|
|
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
|
|
1010
|
-
var
|
|
1011
|
-
var
|
|
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
|
|
1520
|
+
return import_chalk14.default.green(status);
|
|
1016
1521
|
}
|
|
1017
1522
|
if (s === "deploying" || s === "building" || s === "pending") {
|
|
1018
|
-
return
|
|
1523
|
+
return import_chalk14.default.yellow(status);
|
|
1019
1524
|
}
|
|
1020
1525
|
if (s === "error" || s === "failed" || s === "stopped") {
|
|
1021
|
-
return
|
|
1526
|
+
return import_chalk14.default.red(status);
|
|
1022
1527
|
}
|
|
1023
|
-
return
|
|
1528
|
+
return import_chalk14.default.white(status);
|
|
1024
1529
|
}
|
|
1025
|
-
var statusCommand = new
|
|
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(
|
|
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(
|
|
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,
|
|
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(
|
|
1043
|
-
console.log(
|
|
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: ${
|
|
1046
|
-
console.log(` Environment: ${
|
|
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: ${
|
|
1555
|
+
console.log(` Deployed: ${import_chalk14.default.white(date.toLocaleString())}`);
|
|
1051
1556
|
}
|
|
1052
1557
|
if (status.url) {
|
|
1053
|
-
console.log(` 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
|
|
1068
|
-
var
|
|
1069
|
-
var
|
|
1070
|
-
var whoamiCommand = new
|
|
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(
|
|
1596
|
+
console.log(import_chalk16.default.red("Not authenticated. Run `sylphx login` first."));
|
|
1074
1597
|
process.exit(1);
|
|
1075
1598
|
}
|
|
1076
|
-
const spinner = (0,
|
|
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(
|
|
1082
|
-
console.log(` User: ${
|
|
1083
|
-
console.log(` Name: ${
|
|
1084
|
-
console.log(` 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(
|
|
1610
|
+
console.log(import_chalk16.default.bold(" Organizations"));
|
|
1088
1611
|
for (const org of me.orgs) {
|
|
1089
1612
|
console.log(
|
|
1090
|
-
` ${
|
|
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(
|
|
1098
|
-
console.log(` App: ${
|
|
1099
|
-
console.log(` Org: ${
|
|
1100
|
-
console.log(` Env: ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
${
|
|
1126
|
-
${
|
|
1127
|
-
${
|
|
1128
|
-
${
|
|
1129
|
-
${
|
|
1130
|
-
${
|
|
1131
|
-
${
|
|
1132
|
-
${
|
|
1133
|
-
${
|
|
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
|
-
${
|
|
1136
|
-
${
|
|
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
|
-
|
|
1143
|
-
|
|
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(
|
|
1678
|
+
console.error(import_chalk17.default.red(`
|
|
1153
1679
|
Error: ${msg}
|
|
1154
1680
|
`));
|
|
1155
1681
|
process.exit(1);
|