deploro 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +358 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -493,6 +493,129 @@ async function tokenRevokeCommand(id, opts) {
493
493
  await apiRequestNoProject(`/api/auth/tokens/${encodeURIComponent(id)}`, { method: "DELETE" });
494
494
  console.log(`\u2713 Token ${id} revoked`);
495
495
  }
496
+ function isEmailProvider(provider) {
497
+ return provider === "email" || provider === "email_password";
498
+ }
499
+ async function authProvidersListCommand() {
500
+ const config = requireProject();
501
+ const data = await apiRequest(
502
+ `/api/projects/${config.projectId}/auth/providers`
503
+ );
504
+ const known = [
505
+ "email",
506
+ "email_password",
507
+ "google",
508
+ "github",
509
+ "facebook",
510
+ "twitter",
511
+ "discord",
512
+ "microsoft",
513
+ "linkedin",
514
+ "slack",
515
+ "gitlab",
516
+ "twitch",
517
+ "spotify",
518
+ "reddit",
519
+ "dropbox",
520
+ "notion",
521
+ "apple",
522
+ "upwork"
523
+ ];
524
+ const byProvider = new Map(data.providers.map((p) => [p.provider, p]));
525
+ const rows = known.map((provider) => {
526
+ const row = byProvider.get(provider);
527
+ const enabled = row?.enabled ?? false;
528
+ const configured = isEmailProvider(provider) || !!(row?.client_id && row?.client_secret);
529
+ return {
530
+ provider,
531
+ status: enabled && configured ? "active" : enabled ? "setup required" : "disabled"
532
+ };
533
+ });
534
+ table(rows);
535
+ }
536
+ async function authProviderEnableCommand(provider, enabled) {
537
+ const config = requireProject();
538
+ await apiRequest(
539
+ `/api/projects/${config.projectId}/auth/providers/${encodeURIComponent(provider)}`,
540
+ { method: "PUT", body: JSON.stringify({ enabled }) }
541
+ );
542
+ console.log(`\u2713 ${provider} ${enabled ? "enabled" : "disabled"}`);
543
+ }
544
+ async function authProviderConfigureCommand(provider, opts) {
545
+ const config = requireProject();
546
+ if (isEmailProvider(provider)) {
547
+ console.error(
548
+ `'${provider}' doesn't take OAuth credentials \u2014 it just needs enabling.
549
+ Run: deploro auth providers enable ${provider}`
550
+ );
551
+ process.exit(1);
552
+ }
553
+ if (!opts.clientId || !opts.clientSecret) {
554
+ console.error("Both --client-id and --client-secret are required.");
555
+ process.exit(1);
556
+ }
557
+ if (provider === "apple" && (!opts.teamId || !opts.keyId)) {
558
+ console.error("Apple also requires --team-id and --key-id (--client-secret is the .p8 private key contents).");
559
+ process.exit(1);
560
+ }
561
+ await apiRequest(
562
+ `/api/projects/${config.projectId}/auth/providers/${encodeURIComponent(provider)}`,
563
+ {
564
+ method: "PUT",
565
+ body: JSON.stringify({
566
+ client_id: opts.clientId,
567
+ client_secret: opts.clientSecret,
568
+ ...provider === "apple" ? { extra_config: { team_id: opts.teamId, key_id: opts.keyId } } : {}
569
+ })
570
+ }
571
+ );
572
+ console.log(`\u2713 ${provider} credentials saved (still needs enabling \u2014 run: deploro auth providers enable ${provider})`);
573
+ }
574
+ async function authEndUsersListCommand(opts) {
575
+ const config = requireProject();
576
+ const page = Math.max(1, Number(opts.page ?? 1) || 1);
577
+ const data = await apiRequest(
578
+ `/api/projects/${config.projectId}/auth/users?page=${page}`
579
+ );
580
+ table(
581
+ data.users.map((u) => ({
582
+ id: u.id,
583
+ email: u.email ?? "",
584
+ name: u.name ?? "",
585
+ methods: u.providers.join(", "),
586
+ verified: u.email_verified,
587
+ lastLogin: u.last_login_at ? u.last_login_at.slice(0, 10) : "never"
588
+ }))
589
+ );
590
+ console.log(`
591
+ ${data.total} total end user(s), page ${data.page}`);
592
+ }
593
+ async function authEndUserAddCommand(email, opts) {
594
+ const config = requireProject();
595
+ const data = await apiRequest(
596
+ `/api/projects/${config.projectId}/auth/users`,
597
+ { method: "POST", body: JSON.stringify({ email, name: opts.name }) }
598
+ );
599
+ console.log(`\u2713 Added ${data.user.email}`);
600
+ console.log(" A confirmation email was sent \u2014 they can't sign in until they click the link (one time, first use only).");
601
+ }
602
+ async function authEndUserDeleteCommand(id, opts) {
603
+ const config = requireProject();
604
+ await confirmOrDie(`Delete end user ${id}? This removes every sign-in method and session linked to them.`, opts.yes);
605
+ await apiRequest(`/api/projects/${config.projectId}/auth/users/${encodeURIComponent(id)}`, { method: "DELETE" });
606
+ console.log(`\u2713 End user ${id} deleted`);
607
+ }
608
+ async function authStatsCommand() {
609
+ const config = requireProject();
610
+ const data = await apiRequest(`/api/projects/${config.projectId}/auth/stats`);
611
+ console.log(`Total end users : ${data.total_users}`);
612
+ console.log(`Active sessions : ${data.sessions_active}`);
613
+ console.log(`Providers enabled : ${data.providers_enabled}`);
614
+ if (data.users_by_provider.length > 0) {
615
+ console.log("\nBy sign-in method:");
616
+ table(data.users_by_provider.map((p) => ({ provider: p.provider, users: p.count })));
617
+ }
618
+ }
496
619
 
497
620
  // src/commands/migrate.ts
498
621
  import { readFileSync as readFileSync2 } from "fs";
@@ -819,6 +942,211 @@ async function deployLogsCommand(deploymentId) {
819
942
  console.log("");
820
943
  }
821
944
 
945
+ // src/commands/vps.ts
946
+ async function vpsDatabaseCreateCommand() {
947
+ const config = requireProject();
948
+ const data = await apiRequest(`/api/projects/${config.projectId}/vps/database`, { method: "POST" });
949
+ if (data.already_provisioned) {
950
+ console.log("Already provisioned. The connection string is only ever shown once (on creation, or after a rotate).");
951
+ console.log(` Host : ${data.host ?? "(unknown \u2014 VPS_PUBLIC_HOST not configured on the platform)"}`);
952
+ console.log(` Port : ${data.port}`);
953
+ console.log(` User : ${data.user}`);
954
+ console.log(` DB : ${data.database}`);
955
+ return;
956
+ }
957
+ console.log("\u2713 Raw Postgres provisioned:");
958
+ console.log(` ${data.database_url}`);
959
+ console.log(`
960
+ ${data.note ?? ""}`);
961
+ }
962
+ async function vpsDatabaseInfoCommand() {
963
+ const config = requireProject();
964
+ const data = await apiRequest(
965
+ `/api/projects/${config.projectId}/vps/database`
966
+ );
967
+ if (!data.provisioned) {
968
+ console.log("Not provisioned. Run: deploro vps database create");
969
+ return;
970
+ }
971
+ console.log("\n\u2500\u2500 RAW POSTGRES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
972
+ console.log(` Host : ${data.host ?? "(unknown \u2014 VPS_PUBLIC_HOST not configured on the platform)"}`);
973
+ console.log(` Port : ${data.port}`);
974
+ console.log(` User : ${data.user}`);
975
+ console.log(` DB : ${data.database}`);
976
+ console.log(" (password not stored in plaintext \u2014 run 'deploro vps database rotate' to issue a new one)\n");
977
+ }
978
+ async function vpsDatabaseRotateCommand(opts) {
979
+ const config = requireProject();
980
+ await confirmOrDie("Rotate the raw Postgres password? Anything using the old connection string will stop working until redeployed.", opts.yes);
981
+ const data = await apiRequest(
982
+ `/api/projects/${config.projectId}/vps/database/rotate`,
983
+ { method: "POST" }
984
+ );
985
+ console.log("\u2713 Password rotated:");
986
+ console.log(` ${data.database_url}`);
987
+ console.log(`
988
+ ${data.note}`);
989
+ }
990
+ async function vpsRedisCreateCommand() {
991
+ const config = requireProject();
992
+ const data = await apiRequest(`/api/projects/${config.projectId}/vps/redis`, { method: "POST" });
993
+ if (data.already_provisioned) {
994
+ console.log("Already provisioned. The connection string is only ever shown once (on creation, or after a rotate).");
995
+ console.log(` Host : ${data.host ?? "(unknown \u2014 VPS_PUBLIC_HOST not configured on the platform)"}`);
996
+ console.log(` Port : ${data.port}`);
997
+ return;
998
+ }
999
+ console.log("\u2713 Raw Redis provisioned:");
1000
+ console.log(` ${data.redis_url}`);
1001
+ console.log(`
1002
+ ${data.note ?? ""}`);
1003
+ }
1004
+ async function vpsRedisInfoCommand() {
1005
+ const config = requireProject();
1006
+ const data = await apiRequest(
1007
+ `/api/projects/${config.projectId}/vps/redis`
1008
+ );
1009
+ if (!data.provisioned) {
1010
+ console.log("Not provisioned. Run: deploro vps redis create");
1011
+ return;
1012
+ }
1013
+ console.log("\n\u2500\u2500 RAW REDIS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1014
+ console.log(` Host : ${data.host ?? "(unknown \u2014 VPS_PUBLIC_HOST not configured on the platform)"}`);
1015
+ console.log(` Port : ${data.port}`);
1016
+ console.log(" (password not stored in plaintext \u2014 run 'deploro vps redis rotate' to issue a new one)\n");
1017
+ }
1018
+ async function vpsRedisRotateCommand(opts) {
1019
+ const config = requireProject();
1020
+ await confirmOrDie("Rotate the raw Redis password? This briefly restarts the Redis container. Anything using the old connection string will stop working until redeployed.", opts.yes);
1021
+ const data = await apiRequest(
1022
+ `/api/projects/${config.projectId}/vps/redis/rotate`,
1023
+ { method: "POST" }
1024
+ );
1025
+ console.log("\u2713 Password rotated:");
1026
+ console.log(` ${data.redis_url}`);
1027
+ console.log(`
1028
+ ${data.note}`);
1029
+ }
1030
+ async function vpsAllowlistListCommand() {
1031
+ const config = requireProject();
1032
+ const data = await apiRequest(
1033
+ `/api/projects/${config.projectId}/vps/allowlist`
1034
+ );
1035
+ if (data.entries.length === 0) {
1036
+ console.log("No allowlist entries \u2014 raw Postgres/Redis ports accept no inbound connections until you add one.");
1037
+ return;
1038
+ }
1039
+ table(data.entries.map((e) => ({ id: e.id, cidr: e.cidr, label: e.label ?? "", added: new Date(e.created_at).toLocaleString() })));
1040
+ }
1041
+ async function vpsAllowlistAddCommand(cidr, opts) {
1042
+ const config = requireProject();
1043
+ await apiRequest(`/api/projects/${config.projectId}/vps/allowlist`, {
1044
+ method: "POST",
1045
+ body: JSON.stringify({ cidr, label: opts.label })
1046
+ });
1047
+ console.log(`\u2713 Allowed ${cidr}${opts.label ? ` (${opts.label})` : ""} to reach this project's raw Postgres/Redis ports`);
1048
+ }
1049
+ async function vpsAllowlistRemoveCommand(id, opts) {
1050
+ const config = requireProject();
1051
+ await confirmOrDie(`Remove allowlist entry ${id}?`, opts.yes);
1052
+ await apiRequest(`/api/projects/${config.projectId}/vps/allowlist/${encodeURIComponent(id)}`, { method: "DELETE" });
1053
+ console.log(`\u2713 Allowlist entry ${id} removed`);
1054
+ }
1055
+ async function vpsEnvCommand() {
1056
+ const config = requireProject();
1057
+ const data = await apiRequest(`/api/projects/${config.projectId}/vps/env`);
1058
+ if (data.vars.length === 0) {
1059
+ console.log("No VPS env vars set on the active project.");
1060
+ return;
1061
+ }
1062
+ console.log("\nVPS-scoped env vars (written into the compute stack's .env; values are never returned by the API):\n");
1063
+ for (const v of data.vars) console.log(` ${v.key}`);
1064
+ console.log("");
1065
+ }
1066
+ async function vpsSetEnvCommand(key, value) {
1067
+ const config = requireProject();
1068
+ await apiRequest(`/api/projects/${config.projectId}/vps/env`, {
1069
+ method: "POST",
1070
+ body: JSON.stringify({ key, value })
1071
+ });
1072
+ console.log(`\u2713 VPS env var '${key}' set \u2014 run 'deploro vps deploy' to apply it`);
1073
+ }
1074
+ async function vpsDeployCommand() {
1075
+ const config = requireProject();
1076
+ const data = await apiRequest(
1077
+ `/api/projects/${config.projectId}/vps/deploy`,
1078
+ { method: "POST" }
1079
+ );
1080
+ console.log(`\u2713 VPS deploy triggered: ${data.deployment.id}`);
1081
+ console.log(" Run 'deploro vps deployments' to see history, or 'deploro vps logs' once containers are up.");
1082
+ }
1083
+ async function vpsDeploymentsCommand() {
1084
+ const config = requireProject();
1085
+ const data = await apiRequest(`/api/projects/${config.projectId}/vps/deployments`);
1086
+ if (data.deployments.length === 0) {
1087
+ console.log("No VPS deployments yet. Run: deploro vps deploy");
1088
+ return;
1089
+ }
1090
+ table(data.deployments.map((d) => ({
1091
+ id: d.id,
1092
+ status: d.status,
1093
+ branch: d.git_branch ?? "",
1094
+ started: new Date(d.created_at).toLocaleString(),
1095
+ finished: d.finished_at ? new Date(d.finished_at).toLocaleString() : "(in progress)"
1096
+ })));
1097
+ }
1098
+ async function vpsDeployStatusCommand(deploymentId) {
1099
+ const config = requireProject();
1100
+ let id = deploymentId;
1101
+ if (!id) {
1102
+ const list = await apiRequest(`/api/projects/${config.projectId}/vps/deployments`);
1103
+ id = list.deployments[0]?.id;
1104
+ if (!id) {
1105
+ console.log("No VPS deployments yet.");
1106
+ return;
1107
+ }
1108
+ }
1109
+ const data = await apiRequest(`/api/projects/${config.projectId}/vps/deployments/${id}`);
1110
+ const d = data.deployment;
1111
+ console.log(`
1112
+ \u2500\u2500 VPS DEPLOYMENT ${d.id} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
1113
+ console.log(` Status : ${d.status}`);
1114
+ console.log(` Branch : ${d.git_branch ?? "(none)"}`);
1115
+ if (d.agent_error) console.log(` Agent error: ${d.agent_error}`);
1116
+ console.log(`
1117
+ \u2500\u2500 LOGS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
1118
+ console.log(typeof d.logs === "string" && d.logs.trim().length > 0 ? d.logs : "(no logs)");
1119
+ console.log("");
1120
+ }
1121
+ async function vpsStatusCommand() {
1122
+ const config = requireProject();
1123
+ const data = await apiRequest(`/api/projects/${config.projectId}/vps/status`);
1124
+ console.log("\n\u2500\u2500 COMPUTE SERVICES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1125
+ if (data.servicesError) {
1126
+ console.log(` ${data.servicesError}`);
1127
+ } else if (data.services.length === 0) {
1128
+ console.log(" (none deployed \u2014 run: deploro vps deploy)");
1129
+ } else {
1130
+ for (const s of data.services) {
1131
+ const running = (s.State ?? "").toLowerCase().includes("running");
1132
+ console.log(` ${running ? "\u25CF" : "\u25CB"} ${s.Service ?? "?"} ${s.Status ?? s.State ?? ""}`);
1133
+ }
1134
+ }
1135
+ console.log("\n\u2500\u2500 RAW POSTGRES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1136
+ console.log(data.postgres ? ` ${data.postgres.running ? "\u25CF" : "\u25CB"} ${data.postgres.status}` : " (not provisioned)");
1137
+ console.log("\n\u2500\u2500 RAW REDIS \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");
1138
+ console.log(data.redis ? ` ${data.redis.running ? "\u25CF" : "\u25CB"} ${data.redis.status}` : " (not provisioned)");
1139
+ console.log("");
1140
+ }
1141
+ async function vpsLogsCommand(service, opts) {
1142
+ const config = requireProject();
1143
+ const tail = Math.min(Math.max(parseInt(opts.tail ?? "200", 10) || 200, 1), 5e3);
1144
+ const qs = new URLSearchParams({ tail: String(tail) });
1145
+ if (service) qs.set("service", service);
1146
+ const data = await apiRequest(`/api/projects/${config.projectId}/vps/logs?${qs.toString()}`);
1147
+ console.log(data.logs.trim().length > 0 ? data.logs : "(no logs)");
1148
+ }
1149
+
822
1150
  // src/index.ts
823
1151
  var pkg = createRequire(import.meta.url)("../package.json");
824
1152
  var program = new Command();
@@ -851,6 +1179,16 @@ auth.command("whoami").description("Show the current identity, and \u2014 for a
851
1179
  auth.command("users").description("List platform users").action(authUsersCommand);
852
1180
  auth.command("invite <email>").description("Invite a user by email").option("--admin", "Grant admin role").action(authInviteCommand);
853
1181
  auth.command("site-url [url]").description("Get or set the active project's Auth Site URL (where end-user confirmation-email links redirect back to)").option("--clear", "Clear the Site URL \u2014 confirmation links fall back to the project's own deployment URL").action(authSiteUrlCommand);
1182
+ auth.command("stats").description("Show Auth-as-a-Service stats for the active project's end users").action(authStatsCommand);
1183
+ var authProviders = auth.command("providers").description("Manage the active project's end-user sign-in providers");
1184
+ authProviders.command("list").description("List sign-in providers and their status").action(authProvidersListCommand);
1185
+ authProviders.command("enable <provider>").description("Enable a sign-in provider (e.g. email, email_password, google, github, apple, ...)").action((provider) => authProviderEnableCommand(provider, true));
1186
+ authProviders.command("disable <provider>").description("Disable a sign-in provider").action((provider) => authProviderEnableCommand(provider, false));
1187
+ authProviders.command("configure <provider>").description("Set OAuth client credentials for a provider (not applicable to email / email_password)").option("--client-id <id>", "OAuth client ID").option("--client-secret <secret>", "OAuth client secret (Apple: the .p8 private key contents)").option("--team-id <id>", "Apple only \u2014 Team ID").option("--key-id <id>", "Apple only \u2014 Key ID").action(authProviderConfigureCommand);
1188
+ var authEndUsers = auth.command("end-users").description("Manage the active project's end users (your app's sign-ups)");
1189
+ authEndUsers.command("list").description("List end users").option("--page <n>", "Page number", "1").action(authEndUsersListCommand);
1190
+ authEndUsers.command("add <email>").description("Manually add an end user \u2014 sends a confirmation email; they can't sign in until they click it").option("--name <name>", "Display name").action(authEndUserAddCommand);
1191
+ authEndUsers.command("delete <id>").description("Delete an end user and all their sign-in methods/sessions").option("--yes", "Skip confirmation prompt").action(authEndUserDeleteCommand);
854
1192
  var token = program.command("token").description("Personal access token commands");
855
1193
  token.command("list").description("List your personal access tokens").action(tokenListCommand);
856
1194
  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);
@@ -864,6 +1202,26 @@ var deploy = program.command("deploy").description("Deployment commands");
864
1202
  deploy.command("trigger").description("Trigger a manual deployment").action(deployCommand);
865
1203
  deploy.command("status").description("Show latest deployment status").action(deployStatusCommand);
866
1204
  deploy.command("logs [deployment-id]").description("Show logs for a deployment (defaults to latest)").action(deployLogsCommand);
1205
+ var vps = program.command("vps").description("VPS compute hosting commands (raw Postgres/Redis, custom docker-compose deploys)");
1206
+ var vpsDatabase = vps.command("database").description("Raw Postgres (dedicated container, real connection string \u2014 for drizzle-kit, transactions, etc.)");
1207
+ vpsDatabase.command("create").description("Provision raw Postgres (idempotent; connection string shown once)").action(vpsDatabaseCreateCommand);
1208
+ vpsDatabase.command("info").description("Show raw Postgres connection info (no password)").action(vpsDatabaseInfoCommand);
1209
+ vpsDatabase.command("rotate").description("Rotate the raw Postgres password").option("--yes", "Skip confirmation prompt").action(vpsDatabaseRotateCommand);
1210
+ var vpsRedis = vps.command("redis").description("Raw Redis (dedicated container \u2014 for BullMQ, ioredis, Lua EVAL scripts)");
1211
+ vpsRedis.command("create").description("Provision raw Redis (idempotent; connection string shown once)").action(vpsRedisCreateCommand);
1212
+ vpsRedis.command("info").description("Show raw Redis connection info (no password)").action(vpsRedisInfoCommand);
1213
+ vpsRedis.command("rotate").description("Rotate the raw Redis password").option("--yes", "Skip confirmation prompt").action(vpsRedisRotateCommand);
1214
+ var vpsAllowlist = vps.command("allowlist").description("IP allowlist gating access to raw Postgres/Redis ports");
1215
+ vpsAllowlist.command("list").description("List allowlist entries").action(vpsAllowlistListCommand);
1216
+ vpsAllowlist.command("add <cidr>").description("Allow an IP or CIDR block (e.g. 203.0.113.4 or 203.0.113.0/24)").option("--label <label>", "Human-readable label").action(vpsAllowlistAddCommand);
1217
+ vpsAllowlist.command("remove <id>").description("Remove an allowlist entry").option("--yes", "Skip confirmation prompt").action(vpsAllowlistRemoveCommand);
1218
+ vps.command("env").description("List VPS-scoped env var names set on the active project (values are never returned)").action(vpsEnvCommand);
1219
+ vps.command("set-env <key> <value>").description("Set a VPS-scoped env var (e.g. JWT_SECRET) \u2014 distinct from 'hosting set-env', which sets Worker secrets").action(vpsSetEnvCommand);
1220
+ vps.command("deploy").description("Build and start the active project's docker-compose stack on the VPS").action(vpsDeployCommand);
1221
+ vps.command("deployments").description("List VPS deploy history").action(vpsDeploymentsCommand);
1222
+ vps.command("deploy-status [deployment-id]").description("Show a VPS deployment's status and logs (defaults to latest)").action(vpsDeployStatusCommand);
1223
+ vps.command("status").description("Show running compute services + raw Postgres/Redis container status").action(vpsStatusCommand);
1224
+ vps.command("logs [service]").description("Tail logs for a compute service (or 'postgres'/'redis')").option("--tail <n>", "Number of lines", "200").action(vpsLogsCommand);
867
1225
  program.parseAsync().catch((err) => {
868
1226
  console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
869
1227
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploro",
3
- "version": "0.3.3",
3
+ "version": "0.4.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": {