deploro 0.5.0 → 0.6.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 +266 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -148,6 +148,88 @@ async function logoutCommand() {
|
|
|
148
148
|
console.log("\u2713 Logged out \u2014 local credentials cleared");
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
// src/commands/export.ts
|
|
152
|
+
import { createWriteStream, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
153
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
154
|
+
import { Readable } from "stream";
|
|
155
|
+
import { pipeline } from "stream/promises";
|
|
156
|
+
function authHeaders(config) {
|
|
157
|
+
return {
|
|
158
|
+
Authorization: `Bearer ${config.accessToken}`,
|
|
159
|
+
"x-project-id": config.projectId
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async function throwOnError(res) {
|
|
163
|
+
if (res.ok) return;
|
|
164
|
+
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
165
|
+
throw new Error(`${res.status} ${body.error ?? res.statusText}`);
|
|
166
|
+
}
|
|
167
|
+
async function exportCommand(opts) {
|
|
168
|
+
const config = requireProject();
|
|
169
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
170
|
+
const outPath = opts.out ?? `deploro-export-${stamp}.ndjson`;
|
|
171
|
+
const url = new URL(`${config.baseUrl}/api/projects/${config.projectId}/export`);
|
|
172
|
+
if (opts.skipStorage) url.searchParams.set("storage", "false");
|
|
173
|
+
console.log(`Exporting to ${outPath} \u2026`);
|
|
174
|
+
const res = await fetch(url, { headers: authHeaders(config) });
|
|
175
|
+
await throwOnError(res);
|
|
176
|
+
if (!res.body) throw new Error("Export returned an empty response");
|
|
177
|
+
mkdirSync2(dirname2(outPath) || ".", { recursive: true });
|
|
178
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(outPath));
|
|
179
|
+
const { readFileSync: readFileSync4 } = await import("fs");
|
|
180
|
+
const lines = readFileSync4(outPath, "utf8").split("\n").filter(Boolean);
|
|
181
|
+
const last = lines.length ? JSON.parse(lines[lines.length - 1]) : null;
|
|
182
|
+
if (!last || last.type !== "end") {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Export did not finish cleanly \u2014 ${outPath} is incomplete (last record: ${last?.type ?? "none"}). Re-run it.`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
console.log(`Wrote ${outPath}: ${last.rows ?? 0} rows across ${last.tables ?? 0} tables`);
|
|
188
|
+
if (last.errors?.length) {
|
|
189
|
+
console.log("\nSome parts could not be exported:");
|
|
190
|
+
for (const err of last.errors) console.log(` - ${err}`);
|
|
191
|
+
}
|
|
192
|
+
if (!opts.storage) {
|
|
193
|
+
const objects = lines.filter((l) => l.includes('"type":"storage"')).length;
|
|
194
|
+
if (objects > 0) {
|
|
195
|
+
console.log(
|
|
196
|
+
`
|
|
197
|
+
${objects} stored file(s) are listed by key only. Re-run with --storage <dir> to download their contents.`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const keys = lines.map((l) => JSON.parse(l)).filter((r) => r.type === "storage");
|
|
203
|
+
if (keys.length === 0) {
|
|
204
|
+
console.log("\nNo stored files to download.");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
console.log(`
|
|
208
|
+
Downloading ${keys.length} file(s) to ${opts.storage} \u2026`);
|
|
209
|
+
let downloaded = 0;
|
|
210
|
+
const failures = [];
|
|
211
|
+
for (const object of keys) {
|
|
212
|
+
const target = join2(opts.storage, object.key);
|
|
213
|
+
try {
|
|
214
|
+
const fileRes = await fetch(
|
|
215
|
+
`${config.baseUrl}/api/projects/${config.projectId}/storage/download?key=${encodeURIComponent(object.key)}`,
|
|
216
|
+
{ headers: authHeaders(config) }
|
|
217
|
+
);
|
|
218
|
+
if (!fileRes.ok) throw new Error(`${fileRes.status} ${fileRes.statusText}`);
|
|
219
|
+
mkdirSync2(dirname2(target), { recursive: true });
|
|
220
|
+
writeFileSync2(target, Buffer.from(await fileRes.arrayBuffer()));
|
|
221
|
+
downloaded++;
|
|
222
|
+
} catch (err) {
|
|
223
|
+
failures.push(`${object.key}: ${err instanceof Error ? err.message : String(err)}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
console.log(`Downloaded ${downloaded}/${keys.length} file(s).`);
|
|
227
|
+
if (failures.length) {
|
|
228
|
+
console.log("\nFailed:");
|
|
229
|
+
for (const failure of failures) console.log(` - ${failure}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
151
233
|
// src/lib/api.ts
|
|
152
234
|
function errorMessage(status, body, statusText) {
|
|
153
235
|
const parts = [body.error ?? statusText];
|
|
@@ -805,12 +887,12 @@ async function resolveDomainId(projectId, idOrHostname) {
|
|
|
805
887
|
}
|
|
806
888
|
|
|
807
889
|
// src/commands/storage.ts
|
|
808
|
-
import { readFileSync as readFileSync2, writeFileSync as
|
|
890
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
809
891
|
import { basename } from "path";
|
|
810
|
-
function
|
|
892
|
+
function authHeaders2(config) {
|
|
811
893
|
return { Authorization: `Bearer ${config.accessToken}` };
|
|
812
894
|
}
|
|
813
|
-
async function
|
|
895
|
+
async function throwOnError2(res) {
|
|
814
896
|
if (res.ok) return;
|
|
815
897
|
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
816
898
|
throw new Error(`${res.status} ${body.error ?? res.statusText}`);
|
|
@@ -818,9 +900,9 @@ async function throwOnError(res) {
|
|
|
818
900
|
async function storageListCommand() {
|
|
819
901
|
const config = requireProject();
|
|
820
902
|
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage`, {
|
|
821
|
-
headers:
|
|
903
|
+
headers: authHeaders2(config)
|
|
822
904
|
});
|
|
823
|
-
await
|
|
905
|
+
await throwOnError2(res);
|
|
824
906
|
const data = await res.json();
|
|
825
907
|
table(
|
|
826
908
|
data.objects.map((o) => ({
|
|
@@ -838,12 +920,13 @@ async function storageUploadCommand(filePath, opts) {
|
|
|
838
920
|
const filename = opts.key ?? basename(filePath);
|
|
839
921
|
const form = new FormData();
|
|
840
922
|
form.append("file", new Blob([bytes]), filename);
|
|
923
|
+
if (opts.bucket) form.append("bucket", opts.bucket);
|
|
841
924
|
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage/upload`, {
|
|
842
925
|
method: "POST",
|
|
843
|
-
headers:
|
|
926
|
+
headers: authHeaders2(config),
|
|
844
927
|
body: form
|
|
845
928
|
});
|
|
846
|
-
await
|
|
929
|
+
await throwOnError2(res);
|
|
847
930
|
const data = await res.json();
|
|
848
931
|
console.log(`\u2713 Uploaded as ${data.key} (${data.size} bytes, ${data.contentType})`);
|
|
849
932
|
}
|
|
@@ -851,12 +934,12 @@ async function storageDownloadCommand(key, opts) {
|
|
|
851
934
|
const config = requireProject();
|
|
852
935
|
const res = await fetch(
|
|
853
936
|
`${config.baseUrl}/api/projects/${config.projectId}/storage/download?key=${encodeURIComponent(key)}`,
|
|
854
|
-
{ headers:
|
|
937
|
+
{ headers: authHeaders2(config) }
|
|
855
938
|
);
|
|
856
|
-
await
|
|
939
|
+
await throwOnError2(res);
|
|
857
940
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
858
941
|
const outPath = opts.out ?? basename(key);
|
|
859
|
-
|
|
942
|
+
writeFileSync3(outPath, bytes);
|
|
860
943
|
console.log(`\u2713 Saved to ${outPath} (${bytes.length} bytes)`);
|
|
861
944
|
}
|
|
862
945
|
async function storageDeleteCommand(key, opts) {
|
|
@@ -864,11 +947,124 @@ async function storageDeleteCommand(key, opts) {
|
|
|
864
947
|
await confirmOrDie(`Delete "${key}" from storage?`, opts.yes);
|
|
865
948
|
const res = await fetch(
|
|
866
949
|
`${config.baseUrl}/api/projects/${config.projectId}/storage?key=${encodeURIComponent(key)}`,
|
|
867
|
-
{ method: "DELETE", headers:
|
|
950
|
+
{ method: "DELETE", headers: authHeaders2(config) }
|
|
868
951
|
);
|
|
869
|
-
await
|
|
952
|
+
await throwOnError2(res);
|
|
870
953
|
console.log(`\u2713 Deleted ${key}`);
|
|
871
954
|
}
|
|
955
|
+
async function storagePublicCommand(mode, opts) {
|
|
956
|
+
const config = requireProject();
|
|
957
|
+
const normalized = mode.toLowerCase();
|
|
958
|
+
if (!["on", "off", "status"].includes(normalized)) {
|
|
959
|
+
throw new Error(`Expected "on", "off", or "status" \u2014 got "${mode}"`);
|
|
960
|
+
}
|
|
961
|
+
const projectRes = await fetch(`${config.baseUrl}/api/projects/${config.projectId}`, {
|
|
962
|
+
headers: authHeaders2(config)
|
|
963
|
+
});
|
|
964
|
+
await throwOnError2(projectRes);
|
|
965
|
+
const project = await projectRes.json();
|
|
966
|
+
const enabled = Boolean(project.public_storage_enabled);
|
|
967
|
+
const publicBase = `${config.baseUrl}/files/${project.slug}/`;
|
|
968
|
+
if (normalized === "status") {
|
|
969
|
+
console.log(enabled ? `Public read is ON \u2014 ${publicBase}<key>` : "Public read is OFF");
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const target = normalized === "on";
|
|
973
|
+
if (target === enabled) {
|
|
974
|
+
console.log(`Public read is already ${target ? "on" : "off"} \u2014 nothing to do.`);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
if (target) {
|
|
978
|
+
await confirmOrDie(
|
|
979
|
+
`Make every file in "${project.slug}" readable by anyone who knows its key (no login)?`,
|
|
980
|
+
opts.yes
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}`, {
|
|
984
|
+
method: "PATCH",
|
|
985
|
+
headers: { ...authHeaders2(config), "Content-Type": "application/json" },
|
|
986
|
+
body: JSON.stringify({ public_storage_enabled: target })
|
|
987
|
+
});
|
|
988
|
+
await throwOnError2(res);
|
|
989
|
+
console.log(target ? `\u2713 Public read ON \u2014 ${publicBase}<key>` : "\u2713 Public read OFF");
|
|
990
|
+
}
|
|
991
|
+
async function storageBucketsListCommand() {
|
|
992
|
+
const config = requireProject();
|
|
993
|
+
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage/buckets`, {
|
|
994
|
+
headers: authHeaders2(config)
|
|
995
|
+
});
|
|
996
|
+
await throwOnError2(res);
|
|
997
|
+
const data = await res.json();
|
|
998
|
+
if (!data.buckets.length) {
|
|
999
|
+
console.log("No buckets yet \u2014 files without a --bucket are stored at the project root.");
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
table(
|
|
1003
|
+
data.buckets.map((b) => ({
|
|
1004
|
+
name: b.name,
|
|
1005
|
+
access: b.public ? "public" : "private",
|
|
1006
|
+
created: new Date(b.created_at).toLocaleString()
|
|
1007
|
+
}))
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
async function storageBucketsCreateCommand(name, opts) {
|
|
1011
|
+
const config = requireProject();
|
|
1012
|
+
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage/buckets`, {
|
|
1013
|
+
method: "POST",
|
|
1014
|
+
headers: { ...authHeaders2(config), "Content-Type": "application/json" },
|
|
1015
|
+
body: JSON.stringify({ name, public: Boolean(opts.public) })
|
|
1016
|
+
});
|
|
1017
|
+
await throwOnError2(res);
|
|
1018
|
+
const data = await res.json();
|
|
1019
|
+
console.log(`\u2713 Created bucket "${data.name}" (${data.public ? "public" : "private"}) \u2014 upload with --bucket ${data.name}`);
|
|
1020
|
+
}
|
|
1021
|
+
async function storageBucketsSetPublicCommand(name, mode, opts) {
|
|
1022
|
+
const config = requireProject();
|
|
1023
|
+
const normalized = mode.toLowerCase();
|
|
1024
|
+
if (!["on", "off"].includes(normalized)) throw new Error(`Expected "on" or "off" \u2014 got "${mode}"`);
|
|
1025
|
+
const target = normalized === "on";
|
|
1026
|
+
if (target) {
|
|
1027
|
+
await confirmOrDie(`Make every file in bucket "${name}" readable by anyone who knows its key (no login)?`, opts.yes);
|
|
1028
|
+
}
|
|
1029
|
+
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage/buckets/${encodeURIComponent(name)}`, {
|
|
1030
|
+
method: "PATCH",
|
|
1031
|
+
headers: { ...authHeaders2(config), "Content-Type": "application/json" },
|
|
1032
|
+
body: JSON.stringify({ public: target })
|
|
1033
|
+
});
|
|
1034
|
+
await throwOnError2(res);
|
|
1035
|
+
console.log(target ? `\u2713 Bucket "${name}" is now public` : `\u2713 Bucket "${name}" is now private`);
|
|
1036
|
+
}
|
|
1037
|
+
async function storageBucketsDeleteCommand(name, opts) {
|
|
1038
|
+
const config = requireProject();
|
|
1039
|
+
await confirmOrDie(`Remove bucket "${name}"? (files already stored under it are kept, only the access policy is removed)`, opts.yes);
|
|
1040
|
+
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage/buckets/${encodeURIComponent(name)}`, {
|
|
1041
|
+
method: "DELETE",
|
|
1042
|
+
headers: authHeaders2(config)
|
|
1043
|
+
});
|
|
1044
|
+
await throwOnError2(res);
|
|
1045
|
+
console.log(`\u2713 Removed bucket "${name}"`);
|
|
1046
|
+
}
|
|
1047
|
+
function parseDuration(input) {
|
|
1048
|
+
const match = /^(\d+)([smhd])?$/.exec(input.trim());
|
|
1049
|
+
if (!match) throw new Error(`Invalid duration "${input}" \u2014 expected e.g. "3600", "30m", "1h", "7d"`);
|
|
1050
|
+
const n = Number(match[1]);
|
|
1051
|
+
const unit = match[2] ?? "s";
|
|
1052
|
+
const mult = { s: 1, m: 60, h: 3600, d: 86400 }[unit] ?? 1;
|
|
1053
|
+
return n * mult;
|
|
1054
|
+
}
|
|
1055
|
+
async function storageSignCommand(key, opts) {
|
|
1056
|
+
const config = requireProject();
|
|
1057
|
+
const expiresIn = opts.expires ? parseDuration(opts.expires) : void 0;
|
|
1058
|
+
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage/signed-url`, {
|
|
1059
|
+
method: "POST",
|
|
1060
|
+
headers: { ...authHeaders2(config), "Content-Type": "application/json" },
|
|
1061
|
+
body: JSON.stringify({ key, expiresIn })
|
|
1062
|
+
});
|
|
1063
|
+
await throwOnError2(res);
|
|
1064
|
+
const data = await res.json();
|
|
1065
|
+
console.log(data.url);
|
|
1066
|
+
console.log(`(expires ${new Date(data.expiresAt).toLocaleString()})`);
|
|
1067
|
+
}
|
|
872
1068
|
|
|
873
1069
|
// src/commands/webhooks.ts
|
|
874
1070
|
var KNOWN_EVENTS = [
|
|
@@ -1512,6 +1708,51 @@ async function vpsStatusCommand() {
|
|
|
1512
1708
|
);
|
|
1513
1709
|
console.log("");
|
|
1514
1710
|
}
|
|
1711
|
+
async function vpsRestartCommand(service) {
|
|
1712
|
+
const config = requireProject();
|
|
1713
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/vps/restart`, {
|
|
1714
|
+
method: "POST",
|
|
1715
|
+
body: JSON.stringify({ service })
|
|
1716
|
+
});
|
|
1717
|
+
console.log(`\u2713 Restarted ${data.restarted}`);
|
|
1718
|
+
}
|
|
1719
|
+
async function vpsServiceMetricsCommand() {
|
|
1720
|
+
const config = requireProject();
|
|
1721
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/vps/service-metrics`);
|
|
1722
|
+
if (!data.services.length) {
|
|
1723
|
+
console.log("(no services found \u2014 deploy first, or check that raw Postgres/Redis are provisioned)");
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
console.log("\n\u2500\u2500 SERVICE METRICS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1727
|
+
for (const s of data.services) {
|
|
1728
|
+
const restarts = s.restartCount ? ` \xB7 ${s.restartCount} restart${s.restartCount === 1 ? "" : "s"}` : "";
|
|
1729
|
+
const health = s.health ? ` \xB7 health: ${s.health}` : "";
|
|
1730
|
+
console.log(` ${s.state === "running" ? "\u25CF" : "\u25CB"} ${s.service} (${s.kind})`);
|
|
1731
|
+
console.log(` cpu: ${s.cpuPercent ?? "?"}% mem: ${s.memUsage ?? "?"} (${s.memPercent ?? "?"}%) net: ${s.netIO ?? "?"}${restarts}${health}`);
|
|
1732
|
+
}
|
|
1733
|
+
console.log("");
|
|
1734
|
+
}
|
|
1735
|
+
async function vpsWebMetricsCommand(opts) {
|
|
1736
|
+
const config = requireProject();
|
|
1737
|
+
const minutes = Math.min(Math.max(parseInt(opts.minutes ?? "15", 10) || 15, 1), 1440);
|
|
1738
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/vps/web-metrics?minutes=${minutes}`);
|
|
1739
|
+
console.log(`
|
|
1740
|
+
\u2500\u2500 WEB REQUESTS (last ${data.windowMinutes}m) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
1741
|
+
if (data.note) {
|
|
1742
|
+
console.log(` ${data.note}`);
|
|
1743
|
+
} else if (data.requests === 0) {
|
|
1744
|
+
console.log(" No requests in this window.");
|
|
1745
|
+
} else {
|
|
1746
|
+
const statusLine = Object.entries(data.byStatusClass).sort(([a], [b]) => a.localeCompare(b)).map(([cls, n]) => `${cls}: ${n}`).join(" ");
|
|
1747
|
+
console.log(` ${data.requests} requests \xB7 avg ${data.avgResponseMs}ms \xB7 p95 ${data.p95ResponseMs}ms`);
|
|
1748
|
+
console.log(` ${statusLine}`);
|
|
1749
|
+
if (data.recentErrors?.length) {
|
|
1750
|
+
console.log("\n Recent 5xx:");
|
|
1751
|
+
for (const e of data.recentErrors) console.log(` ${e.time} ${e.status} ${e.method} ${e.uri} (${e.responseMs}ms)`);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
console.log("");
|
|
1755
|
+
}
|
|
1515
1756
|
async function vpsLogsCommand(service, opts) {
|
|
1516
1757
|
const config = requireProject();
|
|
1517
1758
|
const tail = Math.min(Math.max(parseInt(opts.tail ?? "200", 10) || 200, 1), 5e3);
|
|
@@ -1531,6 +1772,7 @@ program.command("projects").description("List projects").action(projectsListComm
|
|
|
1531
1772
|
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>", "Public repo URL to link for deployments (no embedded tokens/credentials \u2014 for a private repo or auto-deploy on push, use 'deploro github connect' + 'deploro repo link' instead)").option("--branch <branch>", "Git branch to deploy from").action(projectsCreateCommand);
|
|
1532
1773
|
program.command("use <project>").description("Set the active project (accepts id, slug, or name)").action(useProjectCommand);
|
|
1533
1774
|
program.command("delete <project>").description("Delete a project and its database (accepts id, slug, or name)").option("-y, --yes", "Skip confirmation prompt").action(projectsDeleteCommand);
|
|
1775
|
+
program.command("export").description("Export the active project's database and storage manifest (NDJSON)").option("--out <file>", "Where to write the export (default: deploro-export-<date>.ndjson)").option("--storage <dir>", "Also download stored files into this directory").option("--skip-storage", "Skip the object-storage listing entirely").action(exportCommand);
|
|
1534
1776
|
var db = program.command("db").description("Database commands");
|
|
1535
1777
|
db.command("create").description("Provision the active project's database (idempotent)").action(dbCreateCommand);
|
|
1536
1778
|
db.command("info").description("Show database status and how to reach it from a deployed app").action(dbInfoCommand);
|
|
@@ -1578,7 +1820,7 @@ var deploy = program.command("deploy").description("Deployment commands");
|
|
|
1578
1820
|
deploy.command("trigger").description("Trigger a manual deployment").action(deployCommand);
|
|
1579
1821
|
deploy.command("status").description("Show latest deployment status").action(deployStatusCommand);
|
|
1580
1822
|
deploy.command("logs [deployment-id]").description("Show logs for a deployment (defaults to latest)").action(deployLogsCommand);
|
|
1581
|
-
var vps = program.command("vps").description("VPS compute hosting
|
|
1823
|
+
var vps = program.command("vps").description("VPS compute hosting (raw Postgres/Redis, custom docker-compose deploys) \u2014 grandfathered projects only, closed to new projects");
|
|
1582
1824
|
var vpsDatabase = vps.command("database").description("Raw Postgres (dedicated container, real connection string \u2014 for drizzle-kit, transactions, etc.)");
|
|
1583
1825
|
vpsDatabase.command("create").description("Provision raw Postgres (idempotent; connection string shown once)").action(vpsDatabaseCreateCommand);
|
|
1584
1826
|
vpsDatabase.command("info").description("Show raw Postgres connection info (no password)").action(vpsDatabaseInfoCommand);
|
|
@@ -1598,6 +1840,9 @@ vps.command("deployments").description("List VPS deploy history").action(vpsDepl
|
|
|
1598
1840
|
vps.command("deploy-status [deployment-id]").description("Show a VPS deployment's status and logs (defaults to latest)").action(vpsDeployStatusCommand);
|
|
1599
1841
|
vps.command("status").description("Show running compute services + raw Postgres/Redis container status").action(vpsStatusCommand);
|
|
1600
1842
|
vps.command("logs [service]").description("Tail logs for a compute service (or 'postgres'/'redis')").option("--tail <n>", "Number of lines", "200").action(vpsLogsCommand);
|
|
1843
|
+
vps.command("restart <service>").description("Restart one compute service (or 'postgres'/'redis') without a full redeploy").action(vpsRestartCommand);
|
|
1844
|
+
vps.command("service-metrics").description("Per-container CPU/memory/health/restart count for every compute service").action(vpsServiceMetricsCommand);
|
|
1845
|
+
vps.command("web-metrics").description("Request count, status breakdown, and response-time stats for the public web service").option("--minutes <n>", "Time window", "15").action(vpsWebMetricsCommand);
|
|
1601
1846
|
var github = program.command("github").description("GitHub account connection (OAuth)");
|
|
1602
1847
|
github.command("status").description("Show whether your GitHub account is connected").action(githubStatusCommand);
|
|
1603
1848
|
github.command("connect").description("Print the URL to open in a browser to connect GitHub").action(githubConnectCommand);
|
|
@@ -1616,9 +1861,16 @@ domains.command("status <id-or-hostname>").description("Poll domain verification
|
|
|
1616
1861
|
domains.command("remove <id-or-hostname>").description("Remove a domain").option("-y, --yes", "Skip confirmation prompt").action(domainsRemoveCommand);
|
|
1617
1862
|
var storage = program.command("storage").description("R2 file storage for the active project");
|
|
1618
1863
|
storage.command("list").description("List stored files").action(storageListCommand);
|
|
1619
|
-
storage.command("upload <file>").description("Upload a local file").option("--key <name>", "Stored file name (defaults to the local file's name)").action(storageUploadCommand);
|
|
1864
|
+
storage.command("upload <file>").description("Upload a local file").option("--key <name>", "Stored file name (defaults to the local file's name)").option("--bucket <name>", "Upload into an existing bucket instead of the project root").action(storageUploadCommand);
|
|
1620
1865
|
storage.command("download <key>").description("Download a stored file").option("--out <path>", "Local path to save to (defaults to the file's name)").action(storageDownloadCommand);
|
|
1621
1866
|
storage.command("delete <key>").description("Delete a stored file").option("-y, --yes", "Skip confirmation prompt").action(storageDeleteCommand);
|
|
1867
|
+
storage.command("public <mode>").description("Turn unauthenticated read access on|off, or show status").option("-y, --yes", "Skip confirmation prompt").action(storagePublicCommand);
|
|
1868
|
+
storage.command("sign <key>").description("Generate a time-limited public URL for one file").option("--expires <duration>", "e.g. 1h, 30m, 7d (default 1h)").action(storageSignCommand);
|
|
1869
|
+
var buckets = storage.command("buckets").description("Named, independently-public storage groupings within this project");
|
|
1870
|
+
buckets.command("list").description("List buckets").action(storageBucketsListCommand);
|
|
1871
|
+
buckets.command("create <name>").description("Create a bucket").option("--public", "Make it public read on creation").action(storageBucketsCreateCommand);
|
|
1872
|
+
buckets.command("set-public <name> <mode>").description("Turn a bucket's unauthenticated read access on|off").option("-y, --yes", "Skip confirmation prompt").action(storageBucketsSetPublicCommand);
|
|
1873
|
+
buckets.command("delete <name>").description("Remove a bucket's access policy (keeps its files)").option("-y, --yes", "Skip confirmation prompt").action(storageBucketsDeleteCommand);
|
|
1622
1874
|
var webhooks = program.command("webhooks").description("Outbound webhooks for the active project");
|
|
1623
1875
|
webhooks.command("list").description("List webhooks").action(webhooksListCommand);
|
|
1624
1876
|
webhooks.command("create <url>").description("Create a webhook").option("--events <list>", "Comma-separated event names (default: test)").option("--secret <secret>", "HMAC signing secret").action(webhooksCreateCommand);
|
package/package.json
CHANGED