deploro 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +286 -25
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -148,6 +148,11 @@ async function logoutCommand() {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// src/lib/api.ts
|
|
151
|
+
function errorMessage(status, body, statusText) {
|
|
152
|
+
const parts = [body.error ?? statusText];
|
|
153
|
+
if (body.detail && body.detail !== body.error) parts.push(body.detail);
|
|
154
|
+
return `${status} ${parts.join(" \u2014 ")}`;
|
|
155
|
+
}
|
|
151
156
|
async function apiRequest(path, options, configOverride) {
|
|
152
157
|
const config = configOverride ?? requireConfig();
|
|
153
158
|
const headers = {
|
|
@@ -159,7 +164,7 @@ async function apiRequest(path, options, configOverride) {
|
|
|
159
164
|
const res = await fetch(`${config.baseUrl}${path}`, { ...options, headers });
|
|
160
165
|
if (!res.ok) {
|
|
161
166
|
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
162
|
-
throw new Error(
|
|
167
|
+
throw new Error(errorMessage(res.status, body, res.statusText));
|
|
163
168
|
}
|
|
164
169
|
return res.json();
|
|
165
170
|
}
|
|
@@ -175,7 +180,7 @@ async function apiRequestNoProject(path, options) {
|
|
|
175
180
|
});
|
|
176
181
|
if (!res.ok) {
|
|
177
182
|
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
178
|
-
throw new Error(
|
|
183
|
+
throw new Error(errorMessage(res.status, body, res.statusText));
|
|
179
184
|
}
|
|
180
185
|
return res.json();
|
|
181
186
|
}
|
|
@@ -226,6 +231,44 @@ function json(data) {
|
|
|
226
231
|
}
|
|
227
232
|
|
|
228
233
|
// src/commands/db.ts
|
|
234
|
+
async function dbCreateCommand() {
|
|
235
|
+
const config = requireProject();
|
|
236
|
+
const data = await apiRequestNoProject(
|
|
237
|
+
`/api/projects/${config.projectId}/database`,
|
|
238
|
+
{ method: "POST" }
|
|
239
|
+
);
|
|
240
|
+
console.log(`\u2713 Database ready: ${data.database_name}`);
|
|
241
|
+
}
|
|
242
|
+
async function dbInfoCommand() {
|
|
243
|
+
const config = requireProject();
|
|
244
|
+
const { projects } = await apiRequestNoProject("/api/projects");
|
|
245
|
+
const project = projects.find((p) => p.id === config.projectId);
|
|
246
|
+
if (!project) {
|
|
247
|
+
console.error("Active project not found or not visible to this token. Run: deploro projects");
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
console.log("\n\u2500\u2500 DATABASE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
251
|
+
console.log(` Project : ${project.slug}`);
|
|
252
|
+
console.log(` Database name : ${project.database_name ?? "(not provisioned \u2014 run: deploro db create)"}`);
|
|
253
|
+
console.log("");
|
|
254
|
+
if (project.database_name) {
|
|
255
|
+
console.log(
|
|
256
|
+
"Direct PostgreSQL access (host/port/user/password) is not available.\nThe database lives on an internal network reachable only by Deploro's own\nSQL proxy \u2014 and Cloudflare Workers can't open raw TCP sockets regardless of\nnetwork reachability, so a deployed Worker can never use a connection string\neven if one were issued. There is no DATABASE_URL / HYPERDRIVE binding to\nconfigure on your deployed app; none is provisioned automatically.\n"
|
|
257
|
+
);
|
|
258
|
+
console.log(
|
|
259
|
+
"For a deployed Worker to read/write this project's data at runtime, call\nthe per-project REST API instead (every table gets one automatically):\n"
|
|
260
|
+
);
|
|
261
|
+
console.log(` Base URL : ${config.baseUrl}/api/projects/${config.projectId}/studio`);
|
|
262
|
+
console.log(` Spec : ${config.baseUrl}/api/projects/${config.projectId}/studio/spec (OpenAPI 3.1)`);
|
|
263
|
+
console.log(` Auth : Authorization: Bearer <project-scoped PAT>`);
|
|
264
|
+
console.log(` create one with: deploro token create <name> --project ${project.slug}`);
|
|
265
|
+
console.log("");
|
|
266
|
+
console.log("To hand that to your deployed Worker as secrets:");
|
|
267
|
+
console.log(` deploro hosting set-env DEPLORO_API_URL "${config.baseUrl}/api/projects/${config.projectId}/studio"`);
|
|
268
|
+
console.log(` deploro hosting set-env DEPLORO_API_TOKEN "<pat>"`);
|
|
269
|
+
console.log("");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
229
272
|
async function dbTablesCommand() {
|
|
230
273
|
requireProject();
|
|
231
274
|
const data = await apiRequest("/api/db/tables");
|
|
@@ -319,6 +362,9 @@ async function dbDropTableCommand(tableName, opts) {
|
|
|
319
362
|
}
|
|
320
363
|
|
|
321
364
|
// src/commands/auth.ts
|
|
365
|
+
function isPatToken(accessToken) {
|
|
366
|
+
return accessToken.startsWith("gallium_pat_") || accessToken.startsWith("deploro_pat_");
|
|
367
|
+
}
|
|
322
368
|
async function authUsersCommand() {
|
|
323
369
|
requireConfig();
|
|
324
370
|
const data = await apiRequest("/api/auth/users");
|
|
@@ -345,30 +391,188 @@ async function authInviteCommand(email, opts) {
|
|
|
345
391
|
}
|
|
346
392
|
}
|
|
347
393
|
async function authWhoamiCommand() {
|
|
394
|
+
requireConfig();
|
|
395
|
+
const data = await apiRequestNoProject("/api/auth/whoami");
|
|
396
|
+
if (data.authMethod === "pat") {
|
|
397
|
+
console.log("Authenticated via personal access token.");
|
|
398
|
+
console.log(` User id : ${data.userId}`);
|
|
399
|
+
console.log(` Role : ${data.role}`);
|
|
400
|
+
console.log(` Scopes : ${(data.scopes ?? []).join(", ") || "(none)"}`);
|
|
401
|
+
console.log(` Scoped to ${data.projects?.length ?? 0} project(s):`);
|
|
402
|
+
if (data.projects && data.projects.length > 0) {
|
|
403
|
+
table(data.projects.map((p) => ({ id: p.id, slug: p.slug, name: p.name })));
|
|
404
|
+
} else {
|
|
405
|
+
console.log(" (none \u2014 this token cannot access any project)");
|
|
406
|
+
}
|
|
407
|
+
} else {
|
|
408
|
+
console.log("Authenticated via browser session.");
|
|
409
|
+
console.log(` User id : ${data.userId}`);
|
|
410
|
+
console.log(` Role : ${data.role}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function requireSessionAuth(config) {
|
|
414
|
+
if (isPatToken(config.accessToken)) {
|
|
415
|
+
console.error(
|
|
416
|
+
"Managing tokens requires a browser-session login, not a personal access token.\nRun 'deploro login' without --token (email + password) and try again."
|
|
417
|
+
);
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
async function tokenListCommand() {
|
|
348
422
|
const config = requireConfig();
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
423
|
+
requireSessionAuth(config);
|
|
424
|
+
const data = await apiRequestNoProject("/api/auth/tokens");
|
|
425
|
+
table(
|
|
426
|
+
data.tokens.map((t) => ({
|
|
427
|
+
id: t.id,
|
|
428
|
+
name: t.name,
|
|
429
|
+
prefix: `${t.token_prefix}\u2026`,
|
|
430
|
+
scopes: t.scopes.join(","),
|
|
431
|
+
projects: t.project_ids.length,
|
|
432
|
+
expires: new Date(t.expires_at).toLocaleDateString(),
|
|
433
|
+
status: t.revoked_at ? "revoked" : "active"
|
|
434
|
+
}))
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
async function tokenCreateCommand(name, opts) {
|
|
438
|
+
const config = requireConfig();
|
|
439
|
+
requireSessionAuth(config);
|
|
440
|
+
const scopes = !opts.read && !opts.write ? ["read"] : [opts.read && "read", opts.write && "write"].filter(Boolean);
|
|
441
|
+
let projectIds;
|
|
442
|
+
if (opts.project) {
|
|
443
|
+
const list = await apiRequestNoProject(
|
|
444
|
+
"/api/projects"
|
|
445
|
+
);
|
|
446
|
+
const wanted = opts.project.split(",").map((s) => s.trim().toLowerCase());
|
|
447
|
+
projectIds = list.projects.filter((p) => wanted.includes(p.id) || wanted.includes(p.slug.toLowerCase()) || wanted.includes(p.name.toLowerCase())).map((p) => p.id);
|
|
448
|
+
if (projectIds.length === 0) {
|
|
449
|
+
console.error(`No project matches "${opts.project}". Run: deploro projects`);
|
|
450
|
+
process.exit(1);
|
|
364
451
|
}
|
|
365
|
-
|
|
452
|
+
} else if (config.projectId) {
|
|
453
|
+
projectIds = [config.projectId];
|
|
454
|
+
} else {
|
|
455
|
+
console.error("No project specified. Pass --project <slug> or set an active project with 'deploro use'.");
|
|
366
456
|
process.exit(1);
|
|
367
457
|
}
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
458
|
+
const days = Number(opts.days ?? 90);
|
|
459
|
+
const data = await apiRequestNoProject(
|
|
460
|
+
"/api/auth/tokens",
|
|
461
|
+
{ method: "POST", body: JSON.stringify({ name, scopes, projectIds, expiresInDays: days }) }
|
|
462
|
+
);
|
|
463
|
+
console.log("\u2713 Token created \u2014 copy it now, it will not be shown again:");
|
|
464
|
+
console.log(`
|
|
465
|
+
${data.token}
|
|
466
|
+
`);
|
|
467
|
+
console.log(` Scopes : ${scopes.join(", ")}`);
|
|
468
|
+
console.log(` Projects: ${projectIds.length}`);
|
|
469
|
+
console.log(` Expires : in ${data.expiresInDays} days`);
|
|
470
|
+
console.log(`
|
|
471
|
+
Use it with: deploro login --token ${data.token}`);
|
|
472
|
+
}
|
|
473
|
+
async function tokenRevokeCommand(id, opts) {
|
|
474
|
+
const config = requireConfig();
|
|
475
|
+
requireSessionAuth(config);
|
|
476
|
+
await confirmOrDie(`Revoke token ${id}?`, opts.yes);
|
|
477
|
+
await apiRequestNoProject(`/api/auth/tokens/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
478
|
+
console.log(`\u2713 Token ${id} revoked`);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/commands/migrate.ts
|
|
482
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
483
|
+
function readSqlOpt(inline, file, label) {
|
|
484
|
+
if (file) {
|
|
485
|
+
try {
|
|
486
|
+
return readFileSync2(file, "utf-8");
|
|
487
|
+
} catch (err) {
|
|
488
|
+
console.error(`Could not read --${label}-file "${file}": ${err instanceof Error ? err.message : String(err)}`);
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return inline;
|
|
493
|
+
}
|
|
494
|
+
async function migrateCreateCommand(name, opts) {
|
|
495
|
+
requireProject();
|
|
496
|
+
const sqlUp = readSqlOpt(opts.up, opts.upFile, "up");
|
|
497
|
+
if (!sqlUp) {
|
|
498
|
+
console.error('Migration SQL is required: pass --up "<sql>" or --up-file <path>');
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
const sqlDown = readSqlOpt(opts.down, opts.downFile, "down");
|
|
502
|
+
const config = requireProject();
|
|
503
|
+
const data = await apiRequest(
|
|
504
|
+
`/api/projects/${config.projectId}/migrations`,
|
|
505
|
+
{ method: "POST", body: JSON.stringify({ name, sql_up: sqlUp, sql_down: sqlDown }) }
|
|
506
|
+
);
|
|
507
|
+
console.log(`\u2713 Migration created: ${data.migration.name} (${data.migration.id})`);
|
|
508
|
+
console.log(` Apply it with: deploro migrate apply ${data.migration.id}`);
|
|
509
|
+
}
|
|
510
|
+
async function migrateListCommand() {
|
|
511
|
+
const config = requireProject();
|
|
512
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/migrations`);
|
|
513
|
+
table(
|
|
514
|
+
data.migrations.map((m) => ({
|
|
515
|
+
id: m.id,
|
|
516
|
+
name: m.name,
|
|
517
|
+
status: m.applied_at ? "applied" : "pending",
|
|
518
|
+
applied_at: m.applied_at ? new Date(m.applied_at).toLocaleString() : "\u2014",
|
|
519
|
+
created_at: new Date(m.created_at).toLocaleDateString()
|
|
520
|
+
}))
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
async function resolveMigration(idOrName) {
|
|
524
|
+
const config = requireProject();
|
|
525
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/migrations`);
|
|
526
|
+
const matches = data.migrations.filter((m) => m.id === idOrName || m.name === idOrName);
|
|
527
|
+
if (matches.length === 0) {
|
|
528
|
+
console.error(`No migration matches "${idOrName}". Run: deploro migrate list`);
|
|
529
|
+
process.exit(1);
|
|
530
|
+
}
|
|
531
|
+
if (matches.length > 1) {
|
|
532
|
+
console.error(`"${idOrName}" is ambiguous (matches ${matches.length} migrations by name) \u2014 use the id.`);
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
return { config, migration: matches[0] };
|
|
536
|
+
}
|
|
537
|
+
async function migrateApplyCommand(idOrName) {
|
|
538
|
+
const { config, migration } = await resolveMigration(idOrName);
|
|
539
|
+
if (migration.applied_at) {
|
|
540
|
+
console.error(`Migration "${migration.name}" is already applied.`);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
const data = await apiRequest(
|
|
544
|
+
`/api/projects/${config.projectId}/migrations/${migration.id}/apply`,
|
|
545
|
+
{ method: "POST" }
|
|
546
|
+
);
|
|
547
|
+
console.log(`\u2713 Applied: ${data.migration.name}`);
|
|
548
|
+
console.log(` Applied at: ${data.migration.applied_at ? new Date(data.migration.applied_at).toLocaleString() : "(unknown)"}`);
|
|
549
|
+
}
|
|
550
|
+
async function migrateRollbackCommand(idOrName, opts) {
|
|
551
|
+
const { config, migration } = await resolveMigration(idOrName);
|
|
552
|
+
if (!migration.applied_at) {
|
|
553
|
+
console.error(`Migration "${migration.name}" has not been applied.`);
|
|
554
|
+
process.exit(1);
|
|
555
|
+
}
|
|
556
|
+
if (!migration.sql_down) {
|
|
557
|
+
console.error(`Migration "${migration.name}" has no rollback SQL defined.`);
|
|
558
|
+
process.exit(1);
|
|
559
|
+
}
|
|
560
|
+
await confirmOrDie(`Roll back "${migration.name}"? This runs its sql_down against the project database.`, opts.yes);
|
|
561
|
+
const data = await apiRequest(
|
|
562
|
+
`/api/projects/${config.projectId}/migrations/${migration.id}/rollback`,
|
|
563
|
+
{ method: "POST" }
|
|
564
|
+
);
|
|
565
|
+
console.log(`\u2713 Rolled back: ${data.migration.name}`);
|
|
566
|
+
}
|
|
567
|
+
async function migrateDeleteCommand(idOrName, opts) {
|
|
568
|
+
const { config, migration } = await resolveMigration(idOrName);
|
|
569
|
+
if (migration.applied_at) {
|
|
570
|
+
console.error(`"${migration.name}" is applied \u2014 roll it back first: deploro migrate rollback ${migration.id}`);
|
|
571
|
+
process.exit(1);
|
|
572
|
+
}
|
|
573
|
+
await confirmOrDie(`Delete unapplied migration "${migration.name}"?`, opts.yes);
|
|
574
|
+
await apiRequest(`/api/projects/${config.projectId}/migrations/${migration.id}`, { method: "DELETE" });
|
|
575
|
+
console.log(`\u2713 Migration "${migration.name}" deleted`);
|
|
372
576
|
}
|
|
373
577
|
|
|
374
578
|
// src/commands/hosting.ts
|
|
@@ -416,6 +620,17 @@ async function hostingSetEnvCommand(key, value) {
|
|
|
416
620
|
});
|
|
417
621
|
console.log(`\u2713 Secret '${key}' updated`);
|
|
418
622
|
}
|
|
623
|
+
async function hostingEnvCommand() {
|
|
624
|
+
requireConfig();
|
|
625
|
+
const data = await apiRequest("/api/hosting/env");
|
|
626
|
+
if (data.secrets.length === 0) {
|
|
627
|
+
console.log("No secrets set on the active project's Worker.");
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
console.log("\nSecrets set on the active project's Worker (values are never returned by the API):\n");
|
|
631
|
+
for (const s of data.secrets) console.log(` ${s.name}`);
|
|
632
|
+
console.log("");
|
|
633
|
+
}
|
|
419
634
|
var BARS = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
420
635
|
function spark(values) {
|
|
421
636
|
if (values.length === 0) return "(no data)";
|
|
@@ -499,6 +714,35 @@ async function useProjectCommand(idOrSlug) {
|
|
|
499
714
|
writeConfig({ ...config, projectId: matches[0].id });
|
|
500
715
|
console.log(`\u2713 Active project: ${matches[0].name} (${matches[0].slug})`);
|
|
501
716
|
}
|
|
717
|
+
async function projectsDeleteCommand(idOrSlug, opts) {
|
|
718
|
+
const config = requireConfig();
|
|
719
|
+
const data = await apiRequestNoProject("/api/projects");
|
|
720
|
+
const q = idOrSlug.toLowerCase();
|
|
721
|
+
const matches = data.projects.filter(
|
|
722
|
+
(p) => p.id === idOrSlug || p.slug.toLowerCase() === q || p.name.toLowerCase() === q
|
|
723
|
+
);
|
|
724
|
+
if (matches.length === 0) {
|
|
725
|
+
console.error(`No project matches "${idOrSlug}". Run: deploro projects`);
|
|
726
|
+
process.exit(1);
|
|
727
|
+
}
|
|
728
|
+
if (matches.length > 1) {
|
|
729
|
+
console.error(
|
|
730
|
+
`"${idOrSlug}" is ambiguous (matches: ${matches.map((m) => m.slug).join(", ")}). Use the full id.`
|
|
731
|
+
);
|
|
732
|
+
process.exit(1);
|
|
733
|
+
}
|
|
734
|
+
const project = matches[0];
|
|
735
|
+
await confirmOrDie(
|
|
736
|
+
`Delete project "${project.name}" (${project.slug}) and its database? This cannot be undone.`,
|
|
737
|
+
opts.yes
|
|
738
|
+
);
|
|
739
|
+
await apiRequestNoProject(`/api/projects/${project.id}`, { method: "DELETE" });
|
|
740
|
+
console.log(`\u2713 Project "${project.name}" deleted`);
|
|
741
|
+
if (config.projectId === project.id) {
|
|
742
|
+
writeConfig({ ...config, projectId: void 0 });
|
|
743
|
+
console.log(" (cleared as active project)");
|
|
744
|
+
}
|
|
745
|
+
}
|
|
502
746
|
async function deployCommand() {
|
|
503
747
|
const config = requireProject();
|
|
504
748
|
const data = await apiRequest(
|
|
@@ -561,13 +805,16 @@ async function deployLogsCommand(deploymentId) {
|
|
|
561
805
|
|
|
562
806
|
// src/index.ts
|
|
563
807
|
var program = new Command();
|
|
564
|
-
program.name("deploro").description("Deploro developer platform CLI").version("0.2.
|
|
808
|
+
program.name("deploro").description("Deploro developer platform CLI").version("0.2.1");
|
|
565
809
|
program.command("login").description("Log in to Deploro").option("--url <url>", "Worker base URL").option("--token <token>", "Personal access token from the Deploro dashboard").action(loginCommand);
|
|
566
810
|
program.command("logout").description("Log out and clear local credentials").action(logoutCommand);
|
|
567
811
|
program.command("projects").description("List projects").action(projectsListCommand);
|
|
568
812
|
program.command("create <name>").description("Create a new project (and set it active)").option("--slug <slug>", "Project slug (derived from name if omitted)").option("--repo <url>", "GitHub repo URL to link for deployments").option("--branch <branch>", "Git branch to deploy from").action(projectsCreateCommand);
|
|
569
813
|
program.command("use <project>").description("Set the active project (accepts id, slug, or name)").action(useProjectCommand);
|
|
814
|
+
program.command("delete <project>").description("Delete a project and its database (accepts id, slug, or name)").option("-y, --yes", "Skip confirmation prompt").action(projectsDeleteCommand);
|
|
570
815
|
var db = program.command("db").description("Database commands");
|
|
816
|
+
db.command("create").description("Provision the active project's database (idempotent)").action(dbCreateCommand);
|
|
817
|
+
db.command("info").description("Show database status and how to reach it from a deployed app").action(dbInfoCommand);
|
|
571
818
|
db.command("tables").description("List tables in the active project").action(dbTablesCommand);
|
|
572
819
|
db.command("rows <table>").description("List rows in a table").option("--limit <n>", "Rows per page (max 200)", "20").option("--page <n>", "Page number", "1").option("--json", "Output raw JSON").action(dbRowsCommand);
|
|
573
820
|
db.command("query <sql>").description("Run a SQL query (project admin)").option("--json", "Output raw JSON").action(dbQueryCommand);
|
|
@@ -576,16 +823,30 @@ db.command("insert <table>").description("Insert a row").requiredOption("--data
|
|
|
576
823
|
db.command("update <table> <id>").description("Update a row by id").requiredOption("--data <json>", "Fields to update as a JSON object").action(dbUpdateCommand);
|
|
577
824
|
db.command("delete-row <table> <id>").description("Delete a row by id").option("--yes", "Skip confirmation prompt").action(dbDeleteRowCommand);
|
|
578
825
|
db.command("drop <table>").description("Drop a table and all its data (project admin)").option("--yes", "Skip confirmation prompt").action(dbDropTableCommand);
|
|
826
|
+
var migrate = program.command("migrate").description("Database migration commands");
|
|
827
|
+
migrate.command("create <name>").description("Register a new migration (admin)").option("--up <sql>", "Migration SQL (inline)").option("--up-file <path>", "Read migration SQL from a file").option("--down <sql>", "Rollback SQL (inline)").option("--down-file <path>", "Read rollback SQL from a file").action(migrateCreateCommand);
|
|
828
|
+
migrate.command("list").description("List migrations for the active project").action(migrateListCommand);
|
|
829
|
+
migrate.command("apply <id-or-name>").description("Apply a pending migration (admin)").action(migrateApplyCommand);
|
|
830
|
+
migrate.command("rollback <id-or-name>").description("Roll back an applied migration (admin)").option("--yes", "Skip confirmation prompt").action(migrateRollbackCommand);
|
|
831
|
+
migrate.command("delete <id-or-name>").description("Delete an unapplied migration (admin)").option("--yes", "Skip confirmation prompt").action(migrateDeleteCommand);
|
|
579
832
|
var auth = program.command("auth").description("Auth management commands");
|
|
580
|
-
auth.command("whoami").description("Show the current identity").action(authWhoamiCommand);
|
|
833
|
+
auth.command("whoami").description("Show the current identity, and \u2014 for a token login \u2014 exactly what it's scoped to").action(authWhoamiCommand);
|
|
581
834
|
auth.command("users").description("List platform users").action(authUsersCommand);
|
|
582
835
|
auth.command("invite <email>").description("Invite a user by email").option("--admin", "Grant admin role").action(authInviteCommand);
|
|
836
|
+
var token = program.command("token").description("Personal access token commands");
|
|
837
|
+
token.command("list").description("List your personal access tokens").action(tokenListCommand);
|
|
838
|
+
token.command("create <name>").description("Create a new project-scoped personal access token").option("--read", "Grant read scope").option("--write", "Grant write scope").option("--project <slugs>", "Comma-separated project slugs/ids/names (defaults to the active project)").option("--days <n>", "Expiry in days (1-365)", "90").action(tokenCreateCommand);
|
|
839
|
+
token.command("revoke <id>").description("Revoke a personal access token").option("--yes", "Skip confirmation prompt").action(tokenRevokeCommand);
|
|
583
840
|
var hosting = program.command("hosting").description("Hosting commands");
|
|
584
841
|
hosting.command("status").description("Show Worker and VPS health").action(hostingStatusCommand);
|
|
585
842
|
hosting.command("metrics").description("Show recent VPS CPU/memory/disk sparklines").action(hostingMetricsCommand);
|
|
843
|
+
hosting.command("env").description("List secret names set on the active project's Worker (values are never returned)").action(hostingEnvCommand);
|
|
586
844
|
hosting.command("set-env <key> <value>").description("Set a Worker secret").action(hostingSetEnvCommand);
|
|
587
845
|
var deploy = program.command("deploy").description("Deployment commands");
|
|
588
846
|
deploy.command("trigger").description("Trigger a manual deployment").action(deployCommand);
|
|
589
847
|
deploy.command("status").description("Show latest deployment status").action(deployStatusCommand);
|
|
590
848
|
deploy.command("logs [deployment-id]").description("Show logs for a deployment (defaults to latest)").action(deployLogsCommand);
|
|
591
|
-
program.
|
|
849
|
+
program.parseAsync().catch((err) => {
|
|
850
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
851
|
+
process.exit(1);
|
|
852
|
+
});
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deploro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Command-line interface for the Deploro developer platform — manage projects, databases, deployments, and hosting from your terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"deploro": "
|
|
7
|
+
"deploro": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist"
|