deploro 0.3.4 → 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 +227 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -518,7 +518,8 @@ async function authProvidersListCommand() {
518
518
  "reddit",
519
519
  "dropbox",
520
520
  "notion",
521
- "apple"
521
+ "apple",
522
+ "upwork"
522
523
  ];
523
524
  const byProvider = new Map(data.providers.map((p) => [p.provider, p]));
524
525
  const rows = known.map((provider) => {
@@ -941,6 +942,211 @@ async function deployLogsCommand(deploymentId) {
941
942
  console.log("");
942
943
  }
943
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
+
944
1150
  // src/index.ts
945
1151
  var pkg = createRequire(import.meta.url)("../package.json");
946
1152
  var program = new Command();
@@ -996,6 +1202,26 @@ var deploy = program.command("deploy").description("Deployment commands");
996
1202
  deploy.command("trigger").description("Trigger a manual deployment").action(deployCommand);
997
1203
  deploy.command("status").description("Show latest deployment status").action(deployStatusCommand);
998
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);
999
1225
  program.parseAsync().catch((err) => {
1000
1226
  console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
1001
1227
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploro",
3
- "version": "0.3.4",
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": {