deepspace 0.11.0 → 0.13.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/CHANGELOG.md +198 -0
- package/dist/{chunk-ZJ43EK3N.js → chunk-233GQU2H.js} +492 -365
- package/dist/chunk-233GQU2H.js.map +1 -0
- package/dist/cli.js +872 -256
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.d.ts +2 -2
- package/dist/documentation-client-core.js +1 -1
- package/dist/documentation-default-renderer.cjs +34 -34
- package/dist/documentation-react.d.ts +1 -1
- package/dist/documentation-react.js +1 -1
- package/dist/documentation-runtime.js +10 -10
- package/dist/documentation-server-core.d.ts +2 -2
- package/dist/documentation-server-core.js +1 -1
- package/dist/documentation.js +2 -2
- package/dist/documentation.js.map +1 -1
- package/dist/index.d.ts +28 -3
- package/dist/index.js +131 -5
- package/dist/index.js.map +1 -1
- package/dist/{public-BGRX_Cjd.d.ts → public-BtArwbVM.d.ts} +8 -0
- package/dist/server.d.ts +104 -5
- package/dist/server.js +355 -77
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +104 -5
- package/dist/worker.js +360 -77
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZJ43EK3N.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
|
-
import { defineCommand as
|
|
4
|
+
import { defineCommand as defineCommand14, runMain, runCommand, renderUsage } from "citty";
|
|
5
5
|
import { sync as spawnSync8 } from "cross-spawn";
|
|
6
|
-
import { readFileSync as readFileSync28, existsSync as
|
|
6
|
+
import { readFileSync as readFileSync28, existsSync as existsSync26 } from "fs";
|
|
7
7
|
import { dirname as dirname10, join as join24 } from "path";
|
|
8
8
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9
9
|
|
|
@@ -44,6 +44,13 @@ import * as p2 from "@clack/prompts";
|
|
|
44
44
|
import { isAbsolute } from "path";
|
|
45
45
|
|
|
46
46
|
// src/cli/lib/cli-format.ts
|
|
47
|
+
var ANSI_RE = /\[[0-9;]*m/g;
|
|
48
|
+
function stripAnsi(s) {
|
|
49
|
+
return s.replace(ANSI_RE, "");
|
|
50
|
+
}
|
|
51
|
+
function forTerminal(s) {
|
|
52
|
+
return process.stdout.isTTY ? s : stripAnsi(s);
|
|
53
|
+
}
|
|
47
54
|
function shQuote(s) {
|
|
48
55
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
49
56
|
}
|
|
@@ -176,7 +183,10 @@ var API_ERROR_HINTS = {
|
|
|
176
183
|
test_account_cannot_be_collaborator: "Test accounts cannot be added as collaborators. Use a real DeepSpace account.",
|
|
177
184
|
user_not_found: "No DeepSpace user with that email. They need to log in to DeepSpace at least once (`deepspace auth login`, or sign in to any app) before they can be referenced by email.",
|
|
178
185
|
insufficient_credits: "Out of credits. Inviting a new collaborator by email sends them a transactional email billed to your account \u2014 top up your credits and try again.",
|
|
179
|
-
|
|
186
|
+
// "Try again" alone sends callers into a loop when the address itself is the
|
|
187
|
+
// problem — the send is what failed, and an undeliverable address fails the
|
|
188
|
+
// same way every time.
|
|
189
|
+
invite_email_failed: "The invite itself was not created and the charge was voided \u2014 nothing to undo. Retrying helps only if the send was transient; check the address is real and deliverable first. An email is needed ONLY for someone with no DeepSpace account yet: an existing user is added instantly, so having them sign in once and re-running this skips email entirely. If a retry now reports `already_invited` with no email sent, cancel the stuck invite (`deepspace app collaborators cancel <email>`) and invite again."
|
|
180
190
|
};
|
|
181
191
|
function formatCliError(err) {
|
|
182
192
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -222,6 +232,18 @@ function wrapCommandErrors(cmd) {
|
|
|
222
232
|
}
|
|
223
233
|
|
|
224
234
|
// src/cli/lib/command-suggestions.ts
|
|
235
|
+
function splitTokenPath(token, table) {
|
|
236
|
+
const parts = token.trim().split(/\s+/);
|
|
237
|
+
if (parts.length < 2) return null;
|
|
238
|
+
let node = table;
|
|
239
|
+
for (const part of parts) {
|
|
240
|
+
const next = node?.[part];
|
|
241
|
+
if (!next) return null;
|
|
242
|
+
node = next.subCommands;
|
|
243
|
+
}
|
|
244
|
+
return parts;
|
|
245
|
+
}
|
|
246
|
+
var DESTRUCTIVE_VERBS = /* @__PURE__ */ new Set(["rm", "delete", "remove", "drop", "clear", "undeploy", "kill"]);
|
|
225
247
|
function editDistance(a, b) {
|
|
226
248
|
const left = a.toLowerCase();
|
|
227
249
|
const right = b.toLowerCase();
|
|
@@ -263,12 +285,15 @@ function findUnknownCommand(argv, rootCommands) {
|
|
|
263
285
|
if (!table || token.startsWith("-")) return null;
|
|
264
286
|
const command = table[token];
|
|
265
287
|
if (!command) {
|
|
266
|
-
const
|
|
288
|
+
const split = splitTokenPath(token, table);
|
|
289
|
+
const relativeSuggestion = split ?? closestCommandPath(token, table);
|
|
267
290
|
if (!relativeSuggestion) return null;
|
|
268
291
|
return {
|
|
269
292
|
attemptedPath: ["deepspace", ...accepted, token],
|
|
270
293
|
helpPath: ["deepspace", ...accepted],
|
|
271
|
-
suggestion: ["deepspace", ...accepted, ...relativeSuggestion]
|
|
294
|
+
suggestion: ["deepspace", ...accepted, ...relativeSuggestion],
|
|
295
|
+
executable: !DESTRUCTIVE_VERBS.has(relativeSuggestion.at(-1) ?? ""),
|
|
296
|
+
...split ? { quotedToken: true } : {}
|
|
272
297
|
};
|
|
273
298
|
}
|
|
274
299
|
accepted.push(token);
|
|
@@ -709,7 +734,7 @@ async function provisionProfile(jwt) {
|
|
|
709
734
|
}
|
|
710
735
|
}
|
|
711
736
|
function sleep(ms) {
|
|
712
|
-
return new Promise((
|
|
737
|
+
return new Promise((resolve19) => setTimeout(resolve19, ms));
|
|
713
738
|
}
|
|
714
739
|
function base64url(bytes) {
|
|
715
740
|
return bytes.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
@@ -929,6 +954,7 @@ import { dirname as dirname2, join as join5, resolve as resolve3 } from "path";
|
|
|
929
954
|
// src/cli/lib/git/process.ts
|
|
930
955
|
import { sync as spawnSync } from "cross-spawn";
|
|
931
956
|
var MAX_GIT_BUFFER = 512 * 1024 * 1024;
|
|
957
|
+
var GIT_TIMEOUT_MS = 3e5;
|
|
932
958
|
var GitError = class extends Error {
|
|
933
959
|
constructor(message, code) {
|
|
934
960
|
super(message);
|
|
@@ -937,10 +963,16 @@ var GitError = class extends Error {
|
|
|
937
963
|
}
|
|
938
964
|
};
|
|
939
965
|
function runGit(cwd, args, opts = {}) {
|
|
966
|
+
const timeout = opts.timeoutMs ?? GIT_TIMEOUT_MS;
|
|
940
967
|
const result = spawnSync("git", args, {
|
|
941
968
|
cwd,
|
|
942
969
|
input: opts.input,
|
|
943
970
|
maxBuffer: MAX_GIT_BUFFER,
|
|
971
|
+
timeout,
|
|
972
|
+
// SIGKILL, not the default SIGTERM: a child that ignores or traps TERM
|
|
973
|
+
// would otherwise keep running for its full duration and the "timeout"
|
|
974
|
+
// would bound nothing.
|
|
975
|
+
killSignal: "SIGKILL",
|
|
944
976
|
stdio: ["pipe", "pipe", "pipe"],
|
|
945
977
|
env: {
|
|
946
978
|
...process.env,
|
|
@@ -949,6 +981,12 @@ function runGit(cwd, args, opts = {}) {
|
|
|
949
981
|
...opts.env ?? {}
|
|
950
982
|
}
|
|
951
983
|
});
|
|
984
|
+
if (result.error?.code === "ETIMEDOUT") {
|
|
985
|
+
throw new GitError(
|
|
986
|
+
`git ${args[0]} did not finish within ${Math.round(timeout / 1e3)}s and was stopped. If this was a push, the history is probably too large to transfer \u2014 see \`deepspace push\` for the size limits; otherwise retry, and report it with \`deepspace feedback\` if it persists.`,
|
|
987
|
+
"git_timeout"
|
|
988
|
+
);
|
|
989
|
+
}
|
|
952
990
|
if (result.error) {
|
|
953
991
|
const code = result.error.code;
|
|
954
992
|
if (code === "ENOENT") {
|
|
@@ -2531,16 +2569,31 @@ function formatSchemaLintFindings(findings) {
|
|
|
2531
2569
|
}
|
|
2532
2570
|
|
|
2533
2571
|
// src/cli/lib/port.ts
|
|
2534
|
-
import { createServer } from "net";
|
|
2572
|
+
import { createServer, Socket } from "net";
|
|
2535
2573
|
var DEFAULT_PORT = 5173;
|
|
2536
|
-
function
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2574
|
+
function isPortListening(port, host, timeoutMs = 500) {
|
|
2575
|
+
const target2 = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
|
|
2576
|
+
return new Promise((resolve19) => {
|
|
2577
|
+
const socket = new Socket();
|
|
2578
|
+
const done = (listening) => {
|
|
2579
|
+
socket.destroy();
|
|
2580
|
+
resolve19(listening);
|
|
2581
|
+
};
|
|
2582
|
+
socket.setTimeout(timeoutMs);
|
|
2583
|
+
socket.once("connect", () => done(true));
|
|
2584
|
+
socket.once("timeout", () => done(false));
|
|
2585
|
+
socket.once("error", () => done(false));
|
|
2586
|
+
socket.connect(port, target2);
|
|
2542
2587
|
});
|
|
2543
2588
|
}
|
|
2589
|
+
async function waitForPortListening(port, host, timeoutMs) {
|
|
2590
|
+
const deadline = Date.now() + timeoutMs;
|
|
2591
|
+
while (Date.now() < deadline) {
|
|
2592
|
+
if (await isPortListening(port, host)) return true;
|
|
2593
|
+
await new Promise((resolve19) => setTimeout(resolve19, 250));
|
|
2594
|
+
}
|
|
2595
|
+
return false;
|
|
2596
|
+
}
|
|
2544
2597
|
function resolvePort(arg) {
|
|
2545
2598
|
const raw = arg ?? process.env.DEEPSPACE_PORT;
|
|
2546
2599
|
if (!raw) return DEFAULT_PORT;
|
|
@@ -2552,6 +2605,8 @@ function resolvePort(arg) {
|
|
|
2552
2605
|
}
|
|
2553
2606
|
|
|
2554
2607
|
// src/cli/commands/dev.ts
|
|
2608
|
+
var DEFAULT_DEV_HOST = "0.0.0.0";
|
|
2609
|
+
var READINESS_TIMEOUT_MS = 12e4;
|
|
2555
2610
|
var DEPLOY_URL = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
2556
2611
|
function noAppDirMessage(start, children) {
|
|
2557
2612
|
const lines = [`No wrangler.toml found at or above ${start}.`];
|
|
@@ -2587,6 +2642,11 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2587
2642
|
type: "string",
|
|
2588
2643
|
description: `Port to bind (default ${DEFAULT_PORT}, or $DEEPSPACE_PORT)`,
|
|
2589
2644
|
required: false
|
|
2645
|
+
},
|
|
2646
|
+
host: {
|
|
2647
|
+
type: "string",
|
|
2648
|
+
description: `Interface to bind (default ${DEFAULT_DEV_HOST} \u2014 reachable from outside a container)`,
|
|
2649
|
+
required: false
|
|
2590
2650
|
}
|
|
2591
2651
|
},
|
|
2592
2652
|
async run({ args }) {
|
|
@@ -2595,6 +2655,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2595
2655
|
};
|
|
2596
2656
|
preflightNodeVersion("dev start");
|
|
2597
2657
|
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
2658
|
+
const host = typeof args.host === "string" && args.host.trim() ? args.host.trim() : DEFAULT_DEV_HOST;
|
|
2598
2659
|
const start = resolve7(args.dir ?? ".");
|
|
2599
2660
|
const appDir = findAppDir(start);
|
|
2600
2661
|
if (!appDir) {
|
|
@@ -2658,6 +2719,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2658
2719
|
say(`Logged in as ${payload.name ?? payload.email}`);
|
|
2659
2720
|
if (wranglerEnv) say(`Wrangler env: ${wranglerEnv}`);
|
|
2660
2721
|
say(`Port: ${port}`);
|
|
2722
|
+
if (host !== DEFAULT_DEV_HOST) say(`Host: ${host}`);
|
|
2661
2723
|
let generatedSecretsCache;
|
|
2662
2724
|
const appIdForSecrets = readAppId(appDir, wranglerEnv);
|
|
2663
2725
|
if (appIdForSecrets) {
|
|
@@ -2685,7 +2747,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2685
2747
|
{ sharedDevVarsCache }
|
|
2686
2748
|
);
|
|
2687
2749
|
preflightWindowsWorkerd(appDir);
|
|
2688
|
-
if (
|
|
2750
|
+
if (await isPortListening(port, host)) {
|
|
2689
2751
|
const killArgv = port === DEFAULT_PORT ? ["deepspace", "dev", "kill"] : ["deepspace", "dev", "kill", "--port", String(port)];
|
|
2690
2752
|
const killCmd = killArgv.join(" ");
|
|
2691
2753
|
wranglerConfig.cleanup();
|
|
@@ -2696,12 +2758,34 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
2696
2758
|
{ extra: { port } }
|
|
2697
2759
|
);
|
|
2698
2760
|
}
|
|
2761
|
+
const url = `http://${host === DEFAULT_DEV_HOST ? "localhost" : host}:${port}`;
|
|
2699
2762
|
say("Starting dev server...\n");
|
|
2700
|
-
const vite = spawn(
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2763
|
+
const vite = spawn(
|
|
2764
|
+
"npx",
|
|
2765
|
+
["vite", "--port", String(port), "--strictPort", "--host", host],
|
|
2766
|
+
{
|
|
2767
|
+
cwd: appDir,
|
|
2768
|
+
stdio: "inherit",
|
|
2769
|
+
env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
|
|
2770
|
+
}
|
|
2771
|
+
);
|
|
2772
|
+
if (args.json) {
|
|
2773
|
+
void waitForPortListening(port, host, READINESS_TIMEOUT_MS).then((ready) => {
|
|
2774
|
+
console.log(
|
|
2775
|
+
JSON.stringify(
|
|
2776
|
+
ready ? { ok: true, ready: true, url, port, host, appDir } : {
|
|
2777
|
+
ok: false,
|
|
2778
|
+
code: "dev_server_not_ready",
|
|
2779
|
+
error: `The dev server did not answer on ${url} within ${READINESS_TIMEOUT_MS / 1e3}s.`,
|
|
2780
|
+
url,
|
|
2781
|
+
port,
|
|
2782
|
+
host,
|
|
2783
|
+
appDir
|
|
2784
|
+
}
|
|
2785
|
+
)
|
|
2786
|
+
);
|
|
2787
|
+
});
|
|
2788
|
+
}
|
|
2705
2789
|
let stopping = false;
|
|
2706
2790
|
const stop = () => {
|
|
2707
2791
|
stopping = true;
|
|
@@ -2723,7 +2807,7 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
2723
2807
|
extra: { port, appDir, exitCode: code }
|
|
2724
2808
|
});
|
|
2725
2809
|
}
|
|
2726
|
-
return { data: { port, appDir, wranglerEnv: wranglerEnv ?? null } };
|
|
2810
|
+
return { data: { port, host, url, appDir, wranglerEnv: wranglerEnv ?? null } };
|
|
2727
2811
|
}
|
|
2728
2812
|
});
|
|
2729
2813
|
|
|
@@ -3679,8 +3763,8 @@ var clear = defineDeepspaceCommand({
|
|
|
3679
3763
|
failures.push({ email: a.email, error: err.message });
|
|
3680
3764
|
}
|
|
3681
3765
|
}
|
|
3682
|
-
const deleted = targets.filter((
|
|
3683
|
-
removeTestAccounts(deleted.map((
|
|
3766
|
+
const deleted = targets.filter((target2) => !failures.some((failure) => failure.email === target2.email));
|
|
3767
|
+
removeTestAccounts(deleted.map((target2) => target2.id), deleted.map((target2) => target2.email));
|
|
3684
3768
|
if (failures.length > 0) {
|
|
3685
3769
|
throw new Refusal(
|
|
3686
3770
|
`Deleted ${ok2}/${targets.length} test account(s).
|
|
@@ -3712,6 +3796,96 @@ import * as p12 from "@clack/prompts";
|
|
|
3712
3796
|
import { defineCommand as defineCommand3 } from "citty";
|
|
3713
3797
|
import { resolve as resolve12 } from "path";
|
|
3714
3798
|
|
|
3799
|
+
// src/cli/lib/edge-propagation.ts
|
|
3800
|
+
var NOT_SERVING = ["Assets have not yet deployed", "No app configured"];
|
|
3801
|
+
async function waitForEdgePropagation(url, timeoutMs) {
|
|
3802
|
+
const deadline = Date.now() + timeoutMs;
|
|
3803
|
+
let attempt = 0;
|
|
3804
|
+
let streak = 0;
|
|
3805
|
+
while (Date.now() < deadline) {
|
|
3806
|
+
attempt++;
|
|
3807
|
+
try {
|
|
3808
|
+
const response = await fetch(url, { redirect: "manual" });
|
|
3809
|
+
const body = await response.text();
|
|
3810
|
+
if (!NOT_SERVING.some((marker) => body.includes(marker))) {
|
|
3811
|
+
streak++;
|
|
3812
|
+
if (streak >= 3) return true;
|
|
3813
|
+
await new Promise((resolve19) => setTimeout(resolve19, 1500));
|
|
3814
|
+
continue;
|
|
3815
|
+
}
|
|
3816
|
+
streak = 0;
|
|
3817
|
+
} catch {
|
|
3818
|
+
streak = 0;
|
|
3819
|
+
}
|
|
3820
|
+
const waitMs = Math.min(8e3, 1e3 * 2 ** (attempt - 1));
|
|
3821
|
+
await new Promise((resolve19) => setTimeout(resolve19, waitMs));
|
|
3822
|
+
}
|
|
3823
|
+
return false;
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
// src/shared/app-files.ts
|
|
3827
|
+
var MAX_APP_FILE_BYTES = 1024 * 1024 * 1024;
|
|
3828
|
+
var MAX_UPLOAD_REQUEST_BYTES = 25 * 1024 * 1024;
|
|
3829
|
+
var UPLOAD_PART_BYTES = 20 * 1024 * 1024;
|
|
3830
|
+
var MAX_UPLOAD_PARTS = Math.ceil(MAX_APP_FILE_BYTES / UPLOAD_PART_BYTES);
|
|
3831
|
+
var MAX_BASE64_UPLOAD_BYTES = Math.floor(MAX_UPLOAD_REQUEST_BYTES * 3 / 4);
|
|
3832
|
+
function planUploadParts(totalBytes, partSize) {
|
|
3833
|
+
const parts = [];
|
|
3834
|
+
for (let start = 0; start < totalBytes; start += partSize) {
|
|
3835
|
+
parts.push({
|
|
3836
|
+
partNumber: parts.length + 1,
|
|
3837
|
+
start,
|
|
3838
|
+
end: Math.min(start + partSize, totalBytes)
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3841
|
+
return parts;
|
|
3842
|
+
}
|
|
3843
|
+
var MAX_DEPLOY_ASSET_FILE_BYTES = 25 * 1024 * 1024;
|
|
3844
|
+
var DANGEROUS_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
3845
|
+
"text/html",
|
|
3846
|
+
"application/xhtml+xml",
|
|
3847
|
+
"image/svg+xml",
|
|
3848
|
+
"application/xml",
|
|
3849
|
+
"text/xml",
|
|
3850
|
+
"application/javascript",
|
|
3851
|
+
"text/javascript",
|
|
3852
|
+
"application/ecmascript",
|
|
3853
|
+
"text/ecmascript"
|
|
3854
|
+
]);
|
|
3855
|
+
function activeContentMessage(mimeType) {
|
|
3856
|
+
return `${mimeType} is not accepted: the files API serves your app's own origin, so stored HTML/SVG/JavaScript would execute there with your users' sessions. Ship it as a build asset under \`public/\` instead, where it is served as part of your app rather than as user-uploaded content.`;
|
|
3857
|
+
}
|
|
3858
|
+
function formatBytes(bytes) {
|
|
3859
|
+
if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GiB`;
|
|
3860
|
+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
3861
|
+
if (bytes >= 1024) return `${Math.round(bytes / 1024)} KiB`;
|
|
3862
|
+
return `${bytes} B`;
|
|
3863
|
+
}
|
|
3864
|
+
function encodeKeyPath(key) {
|
|
3865
|
+
return key.split("/").map(encodeURIComponent).join("/");
|
|
3866
|
+
}
|
|
3867
|
+
function oversizeMessage(bytes, limit = MAX_APP_FILE_BYTES) {
|
|
3868
|
+
return `That file is ${formatBytes(bytes)}; the limit is ${formatBytes(limit)} per file.`;
|
|
3869
|
+
}
|
|
3870
|
+
function describeFilesFailure(status3, body) {
|
|
3871
|
+
const trimmed = body.trim();
|
|
3872
|
+
if (trimmed.startsWith("{")) {
|
|
3873
|
+
try {
|
|
3874
|
+
const parsed = JSON.parse(trimmed);
|
|
3875
|
+
if (parsed.error) return parsed.error;
|
|
3876
|
+
} catch {
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
if (status3 === 413) {
|
|
3880
|
+
return `The upload is too large. Files are limited to ${formatBytes(MAX_APP_FILE_BYTES)}, and one request may carry ${formatBytes(MAX_UPLOAD_REQUEST_BYTES)} \u2014 a larger request is rejected by the network before the app receives it.`;
|
|
3881
|
+
}
|
|
3882
|
+
if (status3 === 411) return "The upload did not declare its length.";
|
|
3883
|
+
if (status3 === 401 || status3 === 403) return "Not authorized to write these files.";
|
|
3884
|
+
if (status3 === 415) return "That file type is not accepted.";
|
|
3885
|
+
if (status3 >= 500) return `The files service failed (HTTP ${status3}). Retry shortly.`;
|
|
3886
|
+
return `Upload failed (HTTP ${status3}).`;
|
|
3887
|
+
}
|
|
3888
|
+
|
|
3715
3889
|
// src/cli/commands/deploy/build.ts
|
|
3716
3890
|
import * as p6 from "@clack/prompts";
|
|
3717
3891
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -4031,7 +4205,14 @@ var RESERVED_RUN_WORKER_FIRST = /* @__PURE__ */ new Set([
|
|
|
4031
4205
|
"/_documentation",
|
|
4032
4206
|
"/_documentation/*",
|
|
4033
4207
|
"/_documentation-root",
|
|
4034
|
-
"/_documentation-root/*"
|
|
4208
|
+
"/_documentation-root/*",
|
|
4209
|
+
// Agent-protocol paths: the worker must see them so they cannot fall through
|
|
4210
|
+
// to the SPA shell (templates/base/src/server/http-routes.ts reserves them).
|
|
4211
|
+
"/llms.txt",
|
|
4212
|
+
"/llms-full.txt",
|
|
4213
|
+
"/.well-known/mcp",
|
|
4214
|
+
"/.well-known/mcp.json",
|
|
4215
|
+
"/.well-known/mcp/*"
|
|
4035
4216
|
]);
|
|
4036
4217
|
async function buildDeployBundle(options) {
|
|
4037
4218
|
const { appDir, appName, envName, sharedDevVarsCache, output, spinner: spinner2 } = options;
|
|
@@ -4165,6 +4346,12 @@ function resolveDeployRunWorkerFirst(config) {
|
|
|
4165
4346
|
const routes = extractRunWorkerFirst(config);
|
|
4166
4347
|
return routes;
|
|
4167
4348
|
}
|
|
4349
|
+
function oversizedAssetRefusal(assets) {
|
|
4350
|
+
const tooBig = assets.find((asset) => asset.size > MAX_DEPLOY_ASSET_FILE_BYTES);
|
|
4351
|
+
if (!tooBig) return null;
|
|
4352
|
+
return `${tooBig.path} is ${formatBytes(tooBig.size)}; the per-file limit for deploy assets is ${formatBytes(MAX_DEPLOY_ASSET_FILE_BYTES)}. ${LARGE_MEDIA_HOME}`;
|
|
4353
|
+
}
|
|
4354
|
+
var LARGE_MEDIA_HOME = "Large media belongs in your app files (`deepspace app files put`), not in the deploy bundle \u2014 move it out of `public/` and reference it at runtime.";
|
|
4168
4355
|
function collectAssets(dir) {
|
|
4169
4356
|
const assets = [];
|
|
4170
4357
|
const walk = (currentDir, prefix) => {
|
|
@@ -4821,7 +5008,7 @@ function isThinPackRejection(result) {
|
|
|
4821
5008
|
function isRecoverablePushFailure(status3) {
|
|
4822
5009
|
return status3 === "non_fast_forward" || status3 === "ref_conflict";
|
|
4823
5010
|
}
|
|
4824
|
-
function classifyPushTransportFailure(error) {
|
|
5011
|
+
function classifyPushTransportFailure(error, cwd) {
|
|
4825
5012
|
const message = error instanceof Error ? error.message : String(error);
|
|
4826
5013
|
if (/(?:HTTP |error: )409\b/i.test(message)) {
|
|
4827
5014
|
return {
|
|
@@ -4841,6 +5028,9 @@ function classifyPushTransportFailure(error) {
|
|
|
4841
5028
|
error: "The cloud repo rate-limited this push. Wait a few seconds, then retry."
|
|
4842
5029
|
};
|
|
4843
5030
|
}
|
|
5031
|
+
if (/(?:HTTP |error: )413\b/i.test(message)) {
|
|
5032
|
+
return { code: "push_too_large", error: pushTooLargeMessage(cwd) };
|
|
5033
|
+
}
|
|
4844
5034
|
return null;
|
|
4845
5035
|
}
|
|
4846
5036
|
function pushToSpace(cwd, token, refspec, opts = {}) {
|
|
@@ -4865,18 +5055,29 @@ function pushToSpace(cwd, token, refspec, opts = {}) {
|
|
|
4865
5055
|
}
|
|
4866
5056
|
var OVERSIZED_PUSH_RE = /too large|exceeds|\bLFS\b/i;
|
|
4867
5057
|
var SERVER_OBJECT_CAP_BYTES = 20 * 1024 * 1024;
|
|
4868
|
-
var
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
}
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
if (objects.length === 0) return OVERSIZED_PUSH_FIX
|
|
5058
|
+
var SERVER_PACK_CAP_BYTES = 32 * 1024 * 1024;
|
|
5059
|
+
var PUSH_CEILINGS = `${formatBytes(SERVER_OBJECT_CAP_BYTES)} per file, ${formatBytes(SERVER_PACK_CAP_BYTES)} per push`;
|
|
5060
|
+
var OVERSIZED_PUSH_FIX = `Find the offending object with \`git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectsize) %(rest)' | sort -k2 -n | tail\`.`;
|
|
5061
|
+
function removeFromHistoryAdvice(path) {
|
|
5062
|
+
const file = path ? shQuote(path) : "<file>";
|
|
5063
|
+
return `Media belongs in the app's files rather than Git history \u2014 \`deepspace app files put ${file}\` stores it in the app's own allocation and serves it over HTTP. Then get it out of both the push and the bundle, in this order: (1) move it OUT of \`public/\` \u2014 .gitignore keeps it out of Git but not out of the deploy bundle, which uploads every file under \`public/\`; (2) if the commits that carry it are still local, \`git reset --soft <last pushed commit>\`, then \`git restore --staged ${file}\` \u2014 reset --soft moves HEAD only and leaves the file staged, so skipping this step re-commits the same blob; (3) commit what remains, then push \u2014 if git says "nothing to commit", that commit held only the file, so skip straight to the push and "Everything up-to-date" means you are done. If it already reached pushed history, rewrite it out instead (\`git filter-repo --path ${file} --invert-paths\`).`;
|
|
5064
|
+
}
|
|
5065
|
+
function fixForObjects(objects) {
|
|
5066
|
+
if (objects.length === 0) return `${OVERSIZED_PUSH_FIX} ${removeFromHistoryAdvice(null)}`;
|
|
4877
5067
|
const one = objects.length === 1;
|
|
4878
5068
|
const named = objects.map((object) => `${object.path} (${formatBytes(object.bytes)})`).join(", ");
|
|
4879
|
-
return `The oversized ${one ? "file is" : "files are"} ${named}.
|
|
5069
|
+
return `The oversized ${one ? "file is" : "files are"} ${named}. ${removeFromHistoryAdvice(one ? objects[0].path : null)}`;
|
|
5070
|
+
}
|
|
5071
|
+
function oversizedPushFix(cwd, cap = SERVER_OBJECT_CAP_BYTES) {
|
|
5072
|
+
return fixForObjects(cwd ? findOversizedObjects(cwd, cap) : []);
|
|
5073
|
+
}
|
|
5074
|
+
function pushTooLargeMessage(cwd) {
|
|
5075
|
+
const ceilings = `The cloud repo refused this push as too large (HTTP 413). The ceilings are ${formatBytes(SERVER_OBJECT_CAP_BYTES)} per file and ${formatBytes(SERVER_PACK_CAP_BYTES)} of compressed history per push. Large media never compresses, so every clone would pay for it forever.`;
|
|
5076
|
+
const objects = cwd ? findOversizedObjects(cwd, SERVER_OBJECT_CAP_BYTES) : [];
|
|
5077
|
+
if (objects.length === 0) {
|
|
5078
|
+
return `${ceilings} No single file is over the per-file cap, so this is the volume of history \u2014 push it in smaller batches (\`git push space <earlier-commit>:refs/heads/<branch>\` first, then the rest). ${OVERSIZED_PUSH_FIX}`;
|
|
5079
|
+
}
|
|
5080
|
+
return `${ceilings} ${fixForObjects(objects)}`;
|
|
4880
5081
|
}
|
|
4881
5082
|
function pushFailureMessage(label, push, cwd) {
|
|
4882
5083
|
const reason = push.reason ?? push.summary ?? "";
|
|
@@ -4966,7 +5167,7 @@ function remotePublicRefs(cwd, remote) {
|
|
|
4966
5167
|
function remoteBranchOid(cwd, remote, branch) {
|
|
4967
5168
|
return remotePublicRefs(cwd, remote).find((ref) => ref.name === `refs/heads/${branch}`)?.oid ?? null;
|
|
4968
5169
|
}
|
|
4969
|
-
function localTransferRefs(cwd, prefix,
|
|
5170
|
+
function localTransferRefs(cwd, prefix, target2) {
|
|
4970
5171
|
const output = runGit(cwd, [
|
|
4971
5172
|
"for-each-ref",
|
|
4972
5173
|
"--format=%(refname) %(objectname)",
|
|
@@ -4974,7 +5175,7 @@ function localTransferRefs(cwd, prefix, target) {
|
|
|
4974
5175
|
]).stdout.toString("utf-8");
|
|
4975
5176
|
return output.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
4976
5177
|
const [name, oid] = line.split(/\s+/, 2);
|
|
4977
|
-
return { name: `${
|
|
5178
|
+
return { name: `${target2}/${name.slice(`${prefix}/`.length)}`, oid };
|
|
4978
5179
|
}).filter((ref) => /^[0-9a-f]{40}$/.test(ref.oid));
|
|
4979
5180
|
}
|
|
4980
5181
|
|
|
@@ -5087,7 +5288,7 @@ async function syncDeployRepository(options) {
|
|
|
5087
5288
|
}
|
|
5088
5289
|
} else if (pushResult.status === "rejected") {
|
|
5089
5290
|
output.die(
|
|
5090
|
-
|
|
5291
|
+
`${pushFailureMessage("Cloud repo push", pushResult, appDir)} Publish the commit before deploying.`,
|
|
5091
5292
|
"vc_push_rejected"
|
|
5092
5293
|
);
|
|
5093
5294
|
} else {
|
|
@@ -5131,7 +5332,7 @@ async function syncDeployRepository(options) {
|
|
|
5131
5332
|
sourceRevision = claimed.revision;
|
|
5132
5333
|
}
|
|
5133
5334
|
} catch (error) {
|
|
5134
|
-
const failure = deployRepositoryFailure(error);
|
|
5335
|
+
const failure = deployRepositoryFailure(error, appDir);
|
|
5135
5336
|
output.die(failure.error, failure.code);
|
|
5136
5337
|
}
|
|
5137
5338
|
return { commitOid, recoverable, deployKey, source, sourceRevision, baseReleaseId };
|
|
@@ -5158,8 +5359,8 @@ function classifyRemoteState(appDir, token, branch, localTip, remote) {
|
|
|
5158
5359
|
function errorMessage3(error) {
|
|
5159
5360
|
return error instanceof Error ? error.message : String(error);
|
|
5160
5361
|
}
|
|
5161
|
-
function deployRepositoryFailure(error) {
|
|
5162
|
-
const transportFailure = classifyPushTransportFailure(error);
|
|
5362
|
+
function deployRepositoryFailure(error, cwd) {
|
|
5363
|
+
const transportFailure = classifyPushTransportFailure(error, cwd);
|
|
5163
5364
|
if (transportFailure) return transportFailure;
|
|
5164
5365
|
const code = errorCode(error);
|
|
5165
5366
|
const message = errorMessage3(error);
|
|
@@ -5169,15 +5370,18 @@ function deployRepositoryFailure(error) {
|
|
|
5169
5370
|
error: `Version-control sync failed: ${message}`
|
|
5170
5371
|
};
|
|
5171
5372
|
}
|
|
5373
|
+
var PUSH_RETRY_BUDGET_MS = 12e4;
|
|
5172
5374
|
async function pushWithTransientRetry(push) {
|
|
5173
5375
|
const backoffMs = [500, 1500, 3500];
|
|
5376
|
+
const deadline = Date.now() + PUSH_RETRY_BUDGET_MS;
|
|
5174
5377
|
for (let attempt = 0; ; attempt++) {
|
|
5175
5378
|
try {
|
|
5176
5379
|
return push();
|
|
5177
5380
|
} catch (error) {
|
|
5178
5381
|
const message = errorMessage3(error);
|
|
5179
|
-
|
|
5180
|
-
|
|
5382
|
+
const transient = /(?:HTTP |error: )(?:429|503)\b/i.test(message);
|
|
5383
|
+
if (transient && attempt < backoffMs.length && Date.now() + backoffMs[attempt] < deadline) {
|
|
5384
|
+
await new Promise((resolve19) => setTimeout(resolve19, backoffMs[attempt]));
|
|
5181
5385
|
continue;
|
|
5182
5386
|
}
|
|
5183
5387
|
throw error;
|
|
@@ -5342,7 +5546,7 @@ async function deployBuiltBundle(options) {
|
|
|
5342
5546
|
);
|
|
5343
5547
|
}
|
|
5344
5548
|
if (body.onBehalfOfOwner) {
|
|
5345
|
-
p10.log.warn(
|
|
5549
|
+
p10.log.warn("This release is attributed to the app's owner, not to your account.");
|
|
5346
5550
|
}
|
|
5347
5551
|
if (body.staleBaseGuard === "skipped") {
|
|
5348
5552
|
p10.log.warn(
|
|
@@ -5423,10 +5627,11 @@ async function uploadDeployAssets(options) {
|
|
|
5423
5627
|
p10.log.info(`Assets: ${byHash.size} file(s), all already uploaded`);
|
|
5424
5628
|
return;
|
|
5425
5629
|
}
|
|
5426
|
-
const missingBytes = missing.reduce((
|
|
5630
|
+
const missingBytes = missing.reduce((total2, asset) => total2 + asset.size, 0);
|
|
5427
5631
|
p10.log.info(
|
|
5428
5632
|
`Assets: ${byHash.size} file(s), uploading ${missing.length} (${Math.ceil(missingBytes / 1024)} KiB); the rest are already stored`
|
|
5429
5633
|
);
|
|
5634
|
+
const total = missing.length;
|
|
5430
5635
|
let uploaded = 0;
|
|
5431
5636
|
const next = () => missing.pop();
|
|
5432
5637
|
const worker = async () => {
|
|
@@ -5455,7 +5660,7 @@ async function uploadDeployAssets(options) {
|
|
|
5455
5660
|
);
|
|
5456
5661
|
}
|
|
5457
5662
|
uploaded++;
|
|
5458
|
-
spinner2.message(`Uploading assets \u2014 ${uploaded}/${
|
|
5663
|
+
spinner2.message(`Uploading assets \u2014 ${uploaded}/${total}...`);
|
|
5459
5664
|
}
|
|
5460
5665
|
};
|
|
5461
5666
|
try {
|
|
@@ -5521,7 +5726,7 @@ async function postWithRetry(url, makeInit, {
|
|
|
5521
5726
|
lastError = error;
|
|
5522
5727
|
}
|
|
5523
5728
|
if (attempt < attempts) {
|
|
5524
|
-
await new Promise((
|
|
5729
|
+
await new Promise((resolve19) => setTimeout(resolve19, Math.min(8e3, 500 * 2 ** (attempt - 1))));
|
|
5525
5730
|
}
|
|
5526
5731
|
}
|
|
5527
5732
|
if (lastResponse) return lastResponse;
|
|
@@ -5686,7 +5891,11 @@ var DEPLOY_URL3 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
|
5686
5891
|
var deploy_default = defineCommand3({
|
|
5687
5892
|
meta: {
|
|
5688
5893
|
name: "deploy",
|
|
5689
|
-
|
|
5894
|
+
// The per-file ceiling belongs in --help: it was previously discoverable
|
|
5895
|
+
// only by hitting it, after the build and the push had already run. The
|
|
5896
|
+
// per-deploy total is deliberately absent — it is env-configurable, so any
|
|
5897
|
+
// number stated here would be wrong for some environment.
|
|
5898
|
+
description: `Build and deploy your DeepSpace app (assets: ${formatBytes(MAX_DEPLOY_ASSET_FILE_BYTES)} per file)`
|
|
5690
5899
|
},
|
|
5691
5900
|
args: {
|
|
5692
5901
|
dir: {
|
|
@@ -5782,6 +5991,20 @@ var deploy_default = defineCommand3({
|
|
|
5782
5991
|
output
|
|
5783
5992
|
});
|
|
5784
5993
|
const sourceState = await getAppSource(DEPLOY_URL3, token, appId);
|
|
5994
|
+
const spinner2 = createSpinner();
|
|
5995
|
+
const bundle = await buildDeployBundle({
|
|
5996
|
+
appDir,
|
|
5997
|
+
appName,
|
|
5998
|
+
envName,
|
|
5999
|
+
sharedDevVarsCache: secretsCache.linked !== null,
|
|
6000
|
+
output,
|
|
6001
|
+
spinner: spinner2
|
|
6002
|
+
});
|
|
6003
|
+
const oversized = oversizedAssetRefusal(bundle.assets);
|
|
6004
|
+
if (oversized) {
|
|
6005
|
+
spinner2.stop("Deploy failed");
|
|
6006
|
+
output.die(oversized, "assets_too_large");
|
|
6007
|
+
}
|
|
5785
6008
|
const repository = await syncDeployRepository({
|
|
5786
6009
|
deployUrl: DEPLOY_URL3,
|
|
5787
6010
|
appDir,
|
|
@@ -5792,15 +6015,6 @@ var deploy_default = defineCommand3({
|
|
|
5792
6015
|
output,
|
|
5793
6016
|
sourceState
|
|
5794
6017
|
});
|
|
5795
|
-
const spinner2 = createSpinner();
|
|
5796
|
-
const bundle = await buildDeployBundle({
|
|
5797
|
-
appDir,
|
|
5798
|
-
appName,
|
|
5799
|
-
envName,
|
|
5800
|
-
sharedDevVarsCache: secretsCache.linked !== null,
|
|
5801
|
-
output,
|
|
5802
|
-
spinner: spinner2
|
|
5803
|
-
});
|
|
5804
6018
|
const secrets = prepareDeploySecrets({
|
|
5805
6019
|
appDir,
|
|
5806
6020
|
envName,
|
|
@@ -5826,7 +6040,7 @@ var deploy_default = defineCommand3({
|
|
|
5826
6040
|
});
|
|
5827
6041
|
if (body.url) {
|
|
5828
6042
|
spinner2.message("Waiting for edge propagation...");
|
|
5829
|
-
const ready = await
|
|
6043
|
+
const ready = await waitForEdgePropagation(body.url, 9e4);
|
|
5830
6044
|
if (!ready) {
|
|
5831
6045
|
spinner2.stop("Deployed (edge propagation still in progress after 90s)");
|
|
5832
6046
|
p12.log.warn(
|
|
@@ -5891,30 +6105,6 @@ function blankSelectorRefusal(args) {
|
|
|
5891
6105
|
function staleBaseGuardFields(body) {
|
|
5892
6106
|
return body.staleBaseGuard === "skipped" ? { staleBaseGuard: "skipped" } : {};
|
|
5893
6107
|
}
|
|
5894
|
-
async function waitForAssetsReady(url, timeoutMs) {
|
|
5895
|
-
const deadline = Date.now() + timeoutMs;
|
|
5896
|
-
let attempt = 0;
|
|
5897
|
-
let streak = 0;
|
|
5898
|
-
while (Date.now() < deadline) {
|
|
5899
|
-
attempt++;
|
|
5900
|
-
try {
|
|
5901
|
-
const response = await fetch(url, { redirect: "manual" });
|
|
5902
|
-
const body = await response.text();
|
|
5903
|
-
if (!body.includes("Assets have not yet deployed") && !body.includes("No app configured")) {
|
|
5904
|
-
streak++;
|
|
5905
|
-
if (streak >= 3) return true;
|
|
5906
|
-
await new Promise((resolve18) => setTimeout(resolve18, 1500));
|
|
5907
|
-
continue;
|
|
5908
|
-
}
|
|
5909
|
-
streak = 0;
|
|
5910
|
-
} catch {
|
|
5911
|
-
streak = 0;
|
|
5912
|
-
}
|
|
5913
|
-
const waitMs = Math.min(8e3, 1e3 * 2 ** (attempt - 1));
|
|
5914
|
-
await new Promise((resolve18) => setTimeout(resolve18, waitMs));
|
|
5915
|
-
}
|
|
5916
|
-
return false;
|
|
5917
|
-
}
|
|
5918
6108
|
function errorMessage6(error) {
|
|
5919
6109
|
return error instanceof Error ? error.message : String(error);
|
|
5920
6110
|
}
|
|
@@ -5954,6 +6144,19 @@ function requireAppIdArg(explicit) {
|
|
|
5954
6144
|
}
|
|
5955
6145
|
return id;
|
|
5956
6146
|
}
|
|
6147
|
+
function liveAppUrl(app2) {
|
|
6148
|
+
return app2.status === "active" && app2.deployedAt !== null ? app2.url : null;
|
|
6149
|
+
}
|
|
6150
|
+
async function listApps(deployUrl, token) {
|
|
6151
|
+
const { apps } = await apiFetch(deployUrl, token, "/api/apps");
|
|
6152
|
+
if (!Array.isArray(apps)) {
|
|
6153
|
+
throw new InputError(
|
|
6154
|
+
`The deploy service at ${deployUrl} returned an unexpected response shape for the app list \u2014 check DEEPSPACE_DEPLOY_URL (is this the service the app lives on?).`,
|
|
6155
|
+
"invalid_response"
|
|
6156
|
+
);
|
|
6157
|
+
}
|
|
6158
|
+
return apps;
|
|
6159
|
+
}
|
|
5957
6160
|
function matchAppSelector(apps, selector) {
|
|
5958
6161
|
const byId = apps.find((app2) => app2.appId === selector);
|
|
5959
6162
|
if (byId) return byId.appId;
|
|
@@ -5963,14 +6166,7 @@ function matchAppSelector(apps, selector) {
|
|
|
5963
6166
|
}
|
|
5964
6167
|
async function resolveAppSelector(deployUrl, token, selector) {
|
|
5965
6168
|
if (STRICT_APP_ID_RE.test(selector)) return selector;
|
|
5966
|
-
const
|
|
5967
|
-
if (!Array.isArray(apps)) {
|
|
5968
|
-
throw new InputError(
|
|
5969
|
-
`The deploy service at ${deployUrl} returned an unexpected response shape for the app list \u2014 check DEEPSPACE_DEPLOY_URL (is this the service the app lives on?).`,
|
|
5970
|
-
"invalid_response"
|
|
5971
|
-
);
|
|
5972
|
-
}
|
|
5973
|
-
const id = matchAppSelector(apps, selector);
|
|
6169
|
+
const id = matchAppSelector(await listApps(deployUrl, token), selector);
|
|
5974
6170
|
if (id) return id;
|
|
5975
6171
|
const shared = await resolveAppIdByName(deployUrl, token, selector);
|
|
5976
6172
|
if (shared) return shared;
|
|
@@ -6053,7 +6249,7 @@ function assertAppTargetResolvable(appArg, options = {}) {
|
|
|
6053
6249
|
async function warnIfPhantomApp(deployUrl, token, appId, explicit) {
|
|
6054
6250
|
if (!explicit || !STRICT_APP_ID_RE.test(explicit.trim())) return;
|
|
6055
6251
|
try {
|
|
6056
|
-
const
|
|
6252
|
+
const apps = await listApps(deployUrl, token);
|
|
6057
6253
|
if (!apps.some((app2) => app2.appId === appId)) {
|
|
6058
6254
|
process.stderr.write(
|
|
6059
6255
|
`warning: ${appId} is not among your apps. If it exists and you have access (collaborator/admin), the server will use it; if it doesn't exist, this will register it as a NEW app under your account \u2014 check the id if that's not what you meant (a subdomain name also works with -a).
|
|
@@ -6167,7 +6363,6 @@ var whoami_default = defineDeepspaceCommand({
|
|
|
6167
6363
|
if (!args.json) {
|
|
6168
6364
|
console.log(`Name: ${payload.name ?? "(none)"}`);
|
|
6169
6365
|
console.log(`Email: ${payload.email ?? "(none)"}`);
|
|
6170
|
-
console.log(`UserID: ${payload.sub ?? "(none)"}`);
|
|
6171
6366
|
console.log(`Type: ${accountType}`);
|
|
6172
6367
|
if (payload.iss) console.log(`Issuer: ${payload.iss}`);
|
|
6173
6368
|
console.log(`Dashboard: ${DASHBOARD_URL}`);
|
|
@@ -6199,7 +6394,7 @@ var apps_default = defineDeepspaceCommand({
|
|
|
6199
6394
|
},
|
|
6200
6395
|
async run({ args }) {
|
|
6201
6396
|
const token = await ensureToken();
|
|
6202
|
-
const
|
|
6397
|
+
const apps = await listApps(DEPLOY_URL5, token);
|
|
6203
6398
|
if (!args.json) {
|
|
6204
6399
|
if (!apps.length) {
|
|
6205
6400
|
console.log("No apps yet. Create one with `npx create-deepspace <name>` and `deepspace deploy`.");
|
|
@@ -6207,16 +6402,18 @@ var apps_default = defineDeepspaceCommand({
|
|
|
6207
6402
|
const rows = [...apps].sort(
|
|
6208
6403
|
(left, right) => Number(right.status !== "undeployed") - Number(left.status !== "undeployed")
|
|
6209
6404
|
);
|
|
6405
|
+
const roleOf = (a) => a.role ?? "owner";
|
|
6406
|
+
const roleWidth = Math.max(4, ...rows.map((a) => roleOf(a).length));
|
|
6210
6407
|
const nameWidth = Math.max(4, ...rows.map((a) => (a.name ?? "\u2014").length));
|
|
6211
6408
|
const statusWidth = Math.max(6, ...rows.map((a) => a.status.length));
|
|
6212
6409
|
const idWidth = Math.max(6, ...rows.map((a) => a.appId.length));
|
|
6213
6410
|
console.log(
|
|
6214
|
-
`${"NAME".padEnd(nameWidth)} ${"STATUS".padEnd(statusWidth)} ${"APP ID".padEnd(idWidth)} URL`
|
|
6411
|
+
`${"NAME".padEnd(nameWidth)} ${"STATUS".padEnd(statusWidth)} ${"ROLE".padEnd(roleWidth)} ${"APP ID".padEnd(idWidth)} URL`
|
|
6215
6412
|
);
|
|
6216
6413
|
for (const a of rows) {
|
|
6217
|
-
const url = a
|
|
6414
|
+
const url = liveAppUrl(a) ?? "(not deployed)";
|
|
6218
6415
|
console.log(
|
|
6219
|
-
`${(a.name ?? "\u2014").padEnd(nameWidth)} ${a.status.padEnd(statusWidth)} ${a.appId.padEnd(idWidth)} ${url}`
|
|
6416
|
+
`${(a.name ?? "\u2014").padEnd(nameWidth)} ${a.status.padEnd(statusWidth)} ${roleOf(a).padEnd(roleWidth)} ${a.appId.padEnd(idWidth)} ${url}`
|
|
6220
6417
|
);
|
|
6221
6418
|
}
|
|
6222
6419
|
}
|
|
@@ -7621,11 +7818,11 @@ function fmtDate(iso) {
|
|
|
7621
7818
|
}
|
|
7622
7819
|
async function confirm4(message) {
|
|
7623
7820
|
process.stdout.write(`${message} [y/N] `);
|
|
7624
|
-
return new Promise((
|
|
7821
|
+
return new Promise((resolve19) => {
|
|
7625
7822
|
process.stdin.setEncoding("utf-8");
|
|
7626
7823
|
process.stdin.once("data", (data) => {
|
|
7627
7824
|
const answer = data.toString().trim().toLowerCase();
|
|
7628
|
-
|
|
7825
|
+
resolve19(answer === "y" || answer === "yes");
|
|
7629
7826
|
});
|
|
7630
7827
|
});
|
|
7631
7828
|
}
|
|
@@ -7913,8 +8110,401 @@ var domain_default = defineCommand5({
|
|
|
7913
8110
|
subCommands: { search, buy, list: list2, status, attach, detach, renew }
|
|
7914
8111
|
});
|
|
7915
8112
|
|
|
7916
|
-
// src/cli/commands/
|
|
8113
|
+
// src/cli/commands/app-files.ts
|
|
7917
8114
|
import { defineCommand as defineCommand6 } from "citty";
|
|
8115
|
+
import { basename as basename4, resolve as resolve15 } from "path";
|
|
8116
|
+
import { existsSync as existsSync21, statSync as statSync2 } from "fs";
|
|
8117
|
+
|
|
8118
|
+
// src/cli/lib/app-files-api.ts
|
|
8119
|
+
import { createWriteStream, openAsBlob, statSync } from "fs";
|
|
8120
|
+
import { basename as basename3, extname } from "path";
|
|
8121
|
+
import { Readable } from "stream";
|
|
8122
|
+
import { pipeline } from "stream/promises";
|
|
8123
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
8124
|
+
var CONTENT_TYPES = {
|
|
8125
|
+
".avif": "image/avif",
|
|
8126
|
+
".css": "text/css",
|
|
8127
|
+
".csv": "text/csv",
|
|
8128
|
+
".gif": "image/gif",
|
|
8129
|
+
".htm": "text/html",
|
|
8130
|
+
".html": "text/html",
|
|
8131
|
+
".ico": "image/x-icon",
|
|
8132
|
+
".jpeg": "image/jpeg",
|
|
8133
|
+
".jpg": "image/jpeg",
|
|
8134
|
+
".js": "text/javascript",
|
|
8135
|
+
".json": "application/json",
|
|
8136
|
+
".md": "text/markdown",
|
|
8137
|
+
".mjs": "text/javascript",
|
|
8138
|
+
".svg": "image/svg+xml",
|
|
8139
|
+
".mp3": "audio/mpeg",
|
|
8140
|
+
".mp4": "video/mp4",
|
|
8141
|
+
".otf": "font/otf",
|
|
8142
|
+
".pdf": "application/pdf",
|
|
8143
|
+
".png": "image/png",
|
|
8144
|
+
".txt": "text/plain",
|
|
8145
|
+
".wasm": "application/wasm",
|
|
8146
|
+
".wav": "audio/wav",
|
|
8147
|
+
".webm": "video/webm",
|
|
8148
|
+
".webp": "image/webp",
|
|
8149
|
+
".woff": "font/woff",
|
|
8150
|
+
".woff2": "font/woff2",
|
|
8151
|
+
".zip": "application/zip"
|
|
8152
|
+
};
|
|
8153
|
+
function contentTypeFor(path) {
|
|
8154
|
+
return CONTENT_TYPES[extname(path).toLowerCase()] ?? "application/octet-stream";
|
|
8155
|
+
}
|
|
8156
|
+
function filesPath(appId, rest = "") {
|
|
8157
|
+
return `/api/app-files/${appId}${rest}`;
|
|
8158
|
+
}
|
|
8159
|
+
async function request(baseUrl, token, path, init = {}) {
|
|
8160
|
+
let res;
|
|
8161
|
+
try {
|
|
8162
|
+
res = await fetch(`${baseUrl}${path}`, {
|
|
8163
|
+
...init,
|
|
8164
|
+
headers: { Authorization: `Bearer ${token}`, ...init.headers ?? {} }
|
|
8165
|
+
});
|
|
8166
|
+
} catch (err) {
|
|
8167
|
+
throw new ApiError(
|
|
8168
|
+
`Could not reach the DeepSpace platform at ${baseUrl}: ${err instanceof Error ? err.message : String(err)}. Check your connection (and DEEPSPACE_PLATFORM_URL), then retry.`,
|
|
8169
|
+
0,
|
|
8170
|
+
"network_error",
|
|
8171
|
+
path
|
|
8172
|
+
);
|
|
8173
|
+
}
|
|
8174
|
+
if (res.ok) return res;
|
|
8175
|
+
const text2 = await res.text();
|
|
8176
|
+
let code;
|
|
8177
|
+
try {
|
|
8178
|
+
code = JSON.parse(text2.trim()).code;
|
|
8179
|
+
} catch {
|
|
8180
|
+
}
|
|
8181
|
+
throw new ApiError(
|
|
8182
|
+
describeFilesFailure(res.status, text2),
|
|
8183
|
+
res.status,
|
|
8184
|
+
// Same slug the worker emits for the same condition — the edge cutting an
|
|
8185
|
+
// oversized request short is not a different failure from the worker
|
|
8186
|
+
// refusing it, and two codes made it look like one.
|
|
8187
|
+
code ?? (res.status === 413 ? "too_large" : res.status >= 500 ? "server_error" : "http_error"),
|
|
8188
|
+
path
|
|
8189
|
+
);
|
|
8190
|
+
}
|
|
8191
|
+
async function readJson(res, path) {
|
|
8192
|
+
const text2 = await res.text();
|
|
8193
|
+
try {
|
|
8194
|
+
return JSON.parse(text2);
|
|
8195
|
+
} catch {
|
|
8196
|
+
throw new ApiError(
|
|
8197
|
+
`The DeepSpace platform returned a malformed (non-JSON) response.`,
|
|
8198
|
+
res.status,
|
|
8199
|
+
"invalid_response",
|
|
8200
|
+
path
|
|
8201
|
+
);
|
|
8202
|
+
}
|
|
8203
|
+
}
|
|
8204
|
+
async function multipartBody(localPath, fileName, contentType) {
|
|
8205
|
+
const boundary = `----deepspace${randomUUID2().replace(/-/g, "")}`;
|
|
8206
|
+
const safeName = fileName.replace(/["\\\r\n]/g, "_");
|
|
8207
|
+
const head = `--${boundary}\r
|
|
8208
|
+
Content-Disposition: form-data; name="file"; filename="${safeName}"\r
|
|
8209
|
+
Content-Type: ${contentType}\r
|
|
8210
|
+
\r
|
|
8211
|
+
`;
|
|
8212
|
+
const file = await openAsBlob(localPath, { type: contentType });
|
|
8213
|
+
return {
|
|
8214
|
+
blob: new Blob([head, file, `\r
|
|
8215
|
+
--${boundary}--\r
|
|
8216
|
+
`]),
|
|
8217
|
+
contentType: `multipart/form-data; boundary=${boundary}`
|
|
8218
|
+
};
|
|
8219
|
+
}
|
|
8220
|
+
async function uploadAppFile(baseUrl, token, appId, localPath, key, onProgress) {
|
|
8221
|
+
const path = filesPath(appId, `/upload?key=${encodeKeyPath(key)}`);
|
|
8222
|
+
const size = statSync(localPath).size;
|
|
8223
|
+
if (size > MAX_APP_FILE_BYTES) {
|
|
8224
|
+
throw new ApiError(oversizeMessage(size), 0, "too_large", path);
|
|
8225
|
+
}
|
|
8226
|
+
const declaredType = contentTypeFor(localPath);
|
|
8227
|
+
if (DANGEROUS_MIME_TYPES.has(declaredType)) {
|
|
8228
|
+
throw new ApiError(activeContentMessage(declaredType), 0, "active_content", path);
|
|
8229
|
+
}
|
|
8230
|
+
if (size > UPLOAD_PART_BYTES) {
|
|
8231
|
+
return uploadInParts(baseUrl, token, appId, localPath, key, size, onProgress);
|
|
8232
|
+
}
|
|
8233
|
+
const { blob, contentType } = await multipartBody(localPath, basename3(localPath), declaredType);
|
|
8234
|
+
const res = await request(baseUrl, token, path, {
|
|
8235
|
+
method: "POST",
|
|
8236
|
+
headers: { "Content-Type": contentType },
|
|
8237
|
+
body: blob
|
|
8238
|
+
});
|
|
8239
|
+
const body = await readJson(res, path);
|
|
8240
|
+
onProgress?.(size, size, 1, 1);
|
|
8241
|
+
return { key: body.key, name: body.name, size, parts: 1 };
|
|
8242
|
+
}
|
|
8243
|
+
function isRetryable(err) {
|
|
8244
|
+
if (!(err instanceof ApiError)) return false;
|
|
8245
|
+
return err.status === 0 || err.status === 429 || err.status >= 500;
|
|
8246
|
+
}
|
|
8247
|
+
async function uploadInParts(baseUrl, token, appId, localPath, key, size, onProgress) {
|
|
8248
|
+
const contentType = contentTypeFor(localPath);
|
|
8249
|
+
const initPath = filesPath(appId, `/multipart?key=${encodeKeyPath(key)}`);
|
|
8250
|
+
const session = await readJson(
|
|
8251
|
+
await request(baseUrl, token, initPath, {
|
|
8252
|
+
method: "POST",
|
|
8253
|
+
headers: { "Content-Type": "application/json" },
|
|
8254
|
+
body: JSON.stringify({ name: basename3(localPath), mimeType: contentType, size })
|
|
8255
|
+
}),
|
|
8256
|
+
initPath
|
|
8257
|
+
);
|
|
8258
|
+
const query = `uploadKey=${encodeKeyPath(session.uploadKey)}&uploadId=${encodeURIComponent(session.uploadId)}`;
|
|
8259
|
+
if (!Number.isSafeInteger(session.partSize) || session.partSize < 1) {
|
|
8260
|
+
throw new ApiError(
|
|
8261
|
+
`The platform advertised an unusable part size (${session.partSize}).`,
|
|
8262
|
+
0,
|
|
8263
|
+
"invalid_response",
|
|
8264
|
+
initPath
|
|
8265
|
+
);
|
|
8266
|
+
}
|
|
8267
|
+
const plan = planUploadParts(size, session.partSize);
|
|
8268
|
+
const file = await openAsBlob(localPath, { type: contentType });
|
|
8269
|
+
const uploaded = [];
|
|
8270
|
+
try {
|
|
8271
|
+
for (const part of plan) {
|
|
8272
|
+
const partPath = filesPath(appId, `/multipart/part?${query}&partNumber=${part.partNumber}`);
|
|
8273
|
+
const send = () => request(baseUrl, token, partPath, {
|
|
8274
|
+
method: "PUT",
|
|
8275
|
+
body: file.slice(part.start, part.end, contentType)
|
|
8276
|
+
});
|
|
8277
|
+
const res = await send().catch(async (err) => {
|
|
8278
|
+
if (!isRetryable(err)) throw err;
|
|
8279
|
+
return send();
|
|
8280
|
+
});
|
|
8281
|
+
uploaded.push(await readJson(res, partPath));
|
|
8282
|
+
onProgress?.(part.end, size, part.partNumber, plan.length);
|
|
8283
|
+
}
|
|
8284
|
+
const completePath = filesPath(appId, `/multipart/complete?${query}`);
|
|
8285
|
+
const body = await readJson(
|
|
8286
|
+
await request(baseUrl, token, completePath, {
|
|
8287
|
+
method: "POST",
|
|
8288
|
+
headers: { "Content-Type": "application/json" },
|
|
8289
|
+
body: JSON.stringify({ parts: uploaded })
|
|
8290
|
+
}),
|
|
8291
|
+
completePath
|
|
8292
|
+
);
|
|
8293
|
+
return { key: body.key, name: body.name, size, parts: plan.length };
|
|
8294
|
+
} catch (err) {
|
|
8295
|
+
await request(baseUrl, token, filesPath(appId, `/multipart?${query}`), {
|
|
8296
|
+
method: "DELETE"
|
|
8297
|
+
}).catch(() => void 0);
|
|
8298
|
+
throw err;
|
|
8299
|
+
}
|
|
8300
|
+
}
|
|
8301
|
+
async function listAppFiles(baseUrl, token, appId, query) {
|
|
8302
|
+
const path = filesPath(appId, `?${query}`);
|
|
8303
|
+
return readJson(await request(baseUrl, token, path), path);
|
|
8304
|
+
}
|
|
8305
|
+
async function deleteAppFile(baseUrl, token, appId, key) {
|
|
8306
|
+
const path = filesPath(appId, `/${encodeKeyPath(key)}`);
|
|
8307
|
+
const res = await request(baseUrl, token, path, { method: "DELETE" });
|
|
8308
|
+
const body = await readJson(res, path);
|
|
8309
|
+
return { existed: body.existed !== false };
|
|
8310
|
+
}
|
|
8311
|
+
async function downloadAppFile(baseUrl, token, appId, key, destination) {
|
|
8312
|
+
const path = filesPath(appId, `/${encodeKeyPath(key)}`);
|
|
8313
|
+
const res = await request(baseUrl, token, path);
|
|
8314
|
+
if (!res.body) {
|
|
8315
|
+
throw new ApiError("The server returned an empty response", 502, "invalid_response", path);
|
|
8316
|
+
}
|
|
8317
|
+
let bytes = 0;
|
|
8318
|
+
await pipeline(
|
|
8319
|
+
Readable.fromWeb(res.body),
|
|
8320
|
+
async function* (chunks) {
|
|
8321
|
+
for await (const chunk of chunks) {
|
|
8322
|
+
bytes += chunk.length;
|
|
8323
|
+
yield chunk;
|
|
8324
|
+
}
|
|
8325
|
+
},
|
|
8326
|
+
createWriteStream(destination)
|
|
8327
|
+
);
|
|
8328
|
+
return { bytes, contentType: res.headers.get("content-type") ?? "application/octet-stream" };
|
|
8329
|
+
}
|
|
8330
|
+
|
|
8331
|
+
// src/cli/commands/app-files.ts
|
|
8332
|
+
var DEPLOY_URL8 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
8333
|
+
var PLATFORM_URL = process.env.DEEPSPACE_PLATFORM_URL ?? PLATFORM_URLS.platform;
|
|
8334
|
+
async function target(app2) {
|
|
8335
|
+
const token = await ensureToken();
|
|
8336
|
+
const appId = await resolveAppTarget(DEPLOY_URL8, token, app2);
|
|
8337
|
+
return { token, appId };
|
|
8338
|
+
}
|
|
8339
|
+
var MAX_LIST_LIMIT = 1e3;
|
|
8340
|
+
function parseLimit(raw) {
|
|
8341
|
+
const limit = Number(raw ?? 100);
|
|
8342
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_LIST_LIMIT) {
|
|
8343
|
+
throw new Refusal(`--limit must be a whole number from 1 to ${MAX_LIST_LIMIT}.`, "invalid_limit");
|
|
8344
|
+
}
|
|
8345
|
+
return limit;
|
|
8346
|
+
}
|
|
8347
|
+
function requireKey(raw) {
|
|
8348
|
+
const key = raw.trim().replace(/^\/+/, "");
|
|
8349
|
+
if (!key) throw new Refusal("A key is required (for example `logo.png`).", "invalid_key");
|
|
8350
|
+
if (key.split("/").some((segment) => segment === ".." || segment === ".")) {
|
|
8351
|
+
throw new Refusal(
|
|
8352
|
+
`"${raw}" contains a path traversal segment. Keys are relative to the app, e.g. \`img/hero.jpg\`.`,
|
|
8353
|
+
"invalid_key"
|
|
8354
|
+
);
|
|
8355
|
+
}
|
|
8356
|
+
return key;
|
|
8357
|
+
}
|
|
8358
|
+
function appPrefixOf(key) {
|
|
8359
|
+
const segments = key.split("/");
|
|
8360
|
+
return segments.length > 2 ? `${segments[0]}/${segments[1]}/` : "";
|
|
8361
|
+
}
|
|
8362
|
+
function relativeKey(key, prefix) {
|
|
8363
|
+
return prefix && key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
8364
|
+
}
|
|
8365
|
+
function servedPath(key) {
|
|
8366
|
+
return `/api/files/${encodeKeyPath(key)}?scope=app`;
|
|
8367
|
+
}
|
|
8368
|
+
var put = defineDeepspaceCommand({
|
|
8369
|
+
meta: {
|
|
8370
|
+
name: "put",
|
|
8371
|
+
description: `Upload a file to the app\u2019s files (${formatBytes(MAX_APP_FILE_BYTES)} per file)`
|
|
8372
|
+
},
|
|
8373
|
+
args: {
|
|
8374
|
+
file: { type: "positional", description: "Local file to upload", required: true },
|
|
8375
|
+
key: { type: "string", description: "Key to store it under (default: the file name)" },
|
|
8376
|
+
app: { type: "string", description: "App id or live name (defaults to ./wrangler.toml)" }
|
|
8377
|
+
},
|
|
8378
|
+
async run({ args }) {
|
|
8379
|
+
const localPath = resolve15(String(args.file));
|
|
8380
|
+
if (!existsSync21(localPath) || !statSync2(localPath).isFile()) {
|
|
8381
|
+
throw new Refusal(`No such file: ${args.file}`, "file_not_found");
|
|
8382
|
+
}
|
|
8383
|
+
const key = requireKey(args.key ?? basename4(localPath));
|
|
8384
|
+
const { token, appId } = await target(args.app);
|
|
8385
|
+
const spinner2 = args.json ? null : createSpinner();
|
|
8386
|
+
let started = false;
|
|
8387
|
+
const result = await uploadAppFile(
|
|
8388
|
+
PLATFORM_URL,
|
|
8389
|
+
token,
|
|
8390
|
+
appId,
|
|
8391
|
+
localPath,
|
|
8392
|
+
key,
|
|
8393
|
+
(sent, total, part, parts) => {
|
|
8394
|
+
if (parts === 1 || !spinner2) return;
|
|
8395
|
+
if (!started) {
|
|
8396
|
+
spinner2.start(`Uploading ${key} in ${parts} parts`);
|
|
8397
|
+
started = true;
|
|
8398
|
+
}
|
|
8399
|
+
spinner2.message(
|
|
8400
|
+
`${key} \u2014 part ${part}/${parts} (${formatBytes(sent)} of ${formatBytes(total)})`
|
|
8401
|
+
);
|
|
8402
|
+
}
|
|
8403
|
+
).finally(() => {
|
|
8404
|
+
if (started) spinner2?.stop();
|
|
8405
|
+
});
|
|
8406
|
+
if (!args.json) {
|
|
8407
|
+
const how = result.parts > 1 ? ` in ${result.parts} parts` : "";
|
|
8408
|
+
console.log(`\u2713 ${key} (${formatBytes(result.size)})${how}`);
|
|
8409
|
+
console.log(` Served from your app at ${servedPath(result.key)}`);
|
|
8410
|
+
}
|
|
8411
|
+
return {
|
|
8412
|
+
data: {
|
|
8413
|
+
appId,
|
|
8414
|
+
key,
|
|
8415
|
+
storedKey: result.key,
|
|
8416
|
+
size: result.size,
|
|
8417
|
+
parts: result.parts,
|
|
8418
|
+
path: servedPath(result.key)
|
|
8419
|
+
}
|
|
8420
|
+
};
|
|
8421
|
+
}
|
|
8422
|
+
});
|
|
8423
|
+
var list3 = defineDeepspaceCommand({
|
|
8424
|
+
meta: { name: "list", description: "List the app\u2019s files" },
|
|
8425
|
+
args: {
|
|
8426
|
+
prefix: { type: "string", description: "Only keys starting with this prefix" },
|
|
8427
|
+
limit: {
|
|
8428
|
+
type: "string",
|
|
8429
|
+
description: `Max entries, 1-${MAX_LIST_LIMIT} (default 100)`,
|
|
8430
|
+
default: "100"
|
|
8431
|
+
},
|
|
8432
|
+
app: { type: "string", description: "App id or live name (defaults to ./wrangler.toml)" }
|
|
8433
|
+
},
|
|
8434
|
+
async run({ args }) {
|
|
8435
|
+
const limit = parseLimit(args.limit);
|
|
8436
|
+
const { token, appId } = await target(args.app);
|
|
8437
|
+
const query = new URLSearchParams({ limit: String(limit) });
|
|
8438
|
+
if (args.prefix) query.set("prefix", String(args.prefix));
|
|
8439
|
+
const result = await listAppFiles(PLATFORM_URL, token, appId, query);
|
|
8440
|
+
const prefix = result.files.length ? appPrefixOf(result.files[0].key) : "";
|
|
8441
|
+
const files = result.files.map((file) => ({
|
|
8442
|
+
key: relativeKey(file.key, prefix),
|
|
8443
|
+
size: file.size,
|
|
8444
|
+
uploaded: file.uploaded,
|
|
8445
|
+
path: servedPath(file.key)
|
|
8446
|
+
}));
|
|
8447
|
+
if (!args.json) {
|
|
8448
|
+
if (files.length === 0) {
|
|
8449
|
+
console.log("No files. Upload one with `deepspace app files put <file>`.");
|
|
8450
|
+
} else {
|
|
8451
|
+
const keyWidth = Math.max(3, ...files.map((file) => file.key.length));
|
|
8452
|
+
console.log(`${"KEY".padEnd(keyWidth)} ${"SIZE".padStart(9)} UPLOADED`);
|
|
8453
|
+
for (const file of files) {
|
|
8454
|
+
console.log(
|
|
8455
|
+
`${file.key.padEnd(keyWidth)} ${formatBytes(file.size).padStart(9)} ${file.uploaded.slice(0, 10)}`
|
|
8456
|
+
);
|
|
8457
|
+
}
|
|
8458
|
+
const more = limit < MAX_LIST_LIMIT ? "raise --limit or narrow with --prefix" : "narrow with --prefix";
|
|
8459
|
+
if (result.truncated) console.log(`(truncated at ${limit} \u2014 ${more})`);
|
|
8460
|
+
}
|
|
8461
|
+
}
|
|
8462
|
+
return { data: { appId, files, truncated: result.truncated } };
|
|
8463
|
+
}
|
|
8464
|
+
});
|
|
8465
|
+
var get = defineDeepspaceCommand({
|
|
8466
|
+
meta: { name: "get", description: "Download one file" },
|
|
8467
|
+
args: {
|
|
8468
|
+
key: { type: "positional", description: "Key to download", required: true },
|
|
8469
|
+
out: { type: "string", description: "Write here (default: the key\u2019s file name)" },
|
|
8470
|
+
app: { type: "string", description: "App id or live name (defaults to ./wrangler.toml)" }
|
|
8471
|
+
},
|
|
8472
|
+
async run({ args }) {
|
|
8473
|
+
const key = requireKey(String(args.key));
|
|
8474
|
+
const destination = resolve15(String(args.out ?? basename4(key)));
|
|
8475
|
+
const { token, appId } = await target(args.app);
|
|
8476
|
+
const result = await downloadAppFile(PLATFORM_URL, token, appId, key, destination);
|
|
8477
|
+
if (!args.json) console.log(`\u2713 ${key} \u2192 ${destination} (${formatBytes(result.bytes)})`);
|
|
8478
|
+
return { data: { appId, key, out: destination, size: result.bytes, contentType: result.contentType } };
|
|
8479
|
+
}
|
|
8480
|
+
});
|
|
8481
|
+
var rm = defineDeepspaceCommand({
|
|
8482
|
+
meta: { name: "rm", description: "Delete one file" },
|
|
8483
|
+
args: {
|
|
8484
|
+
key: { type: "positional", description: "Key to delete", required: true },
|
|
8485
|
+
app: { type: "string", description: "App id or live name (defaults to ./wrangler.toml)" }
|
|
8486
|
+
},
|
|
8487
|
+
async run({ args }) {
|
|
8488
|
+
const key = requireKey(String(args.key));
|
|
8489
|
+
const { token, appId } = await target(args.app);
|
|
8490
|
+
const { existed } = await deleteAppFile(PLATFORM_URL, token, appId, key);
|
|
8491
|
+
if (!existed) {
|
|
8492
|
+
throw new Refusal(
|
|
8493
|
+
`No file at ${key} \u2014 nothing was deleted. Check the key with \`deepspace app files list\`.`,
|
|
8494
|
+
"file_not_found"
|
|
8495
|
+
);
|
|
8496
|
+
}
|
|
8497
|
+
if (!args.json) console.log(`\u2713 deleted ${key}`);
|
|
8498
|
+
return { data: { appId, key, deleted: true } };
|
|
8499
|
+
}
|
|
8500
|
+
});
|
|
8501
|
+
var app_files_default = defineCommand6({
|
|
8502
|
+
meta: { name: "files", description: "Upload and manage the app\u2019s files (images, media)" },
|
|
8503
|
+
subCommands: { put, list: list3, get, rm }
|
|
8504
|
+
});
|
|
8505
|
+
|
|
8506
|
+
// src/cli/commands/collaborators.ts
|
|
8507
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
7918
8508
|
|
|
7919
8509
|
// src/server/auth/testAccounts.ts
|
|
7920
8510
|
var TEST_ACCOUNT_EMAIL_SUFFIX = "@deepspace.test";
|
|
@@ -7924,18 +8514,18 @@ function isTestAccountEmail(email) {
|
|
|
7924
8514
|
|
|
7925
8515
|
// src/cli/commands/collaborators.ts
|
|
7926
8516
|
var API_URL5 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
7927
|
-
var
|
|
8517
|
+
var DEPLOY_URL9 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
7928
8518
|
function api2(token, path, init) {
|
|
7929
8519
|
return apiFetch(API_URL5, token, path, init);
|
|
7930
8520
|
}
|
|
7931
|
-
var
|
|
8521
|
+
var list4 = defineDeepspaceCommand({
|
|
7932
8522
|
meta: { name: "list", description: "List collaborators on your app" },
|
|
7933
8523
|
args: {
|
|
7934
8524
|
app: { type: "string", alias: "a", description: "App id or name (defaults to ./wrangler.toml)" }
|
|
7935
8525
|
},
|
|
7936
8526
|
async run({ args }) {
|
|
7937
8527
|
const token = await ensureToken();
|
|
7938
|
-
const app2 = await resolveAppTarget(
|
|
8528
|
+
const app2 = await resolveAppTarget(DEPLOY_URL9, token, args.app);
|
|
7939
8529
|
const { collaborators, pending = [] } = await api2(token, `/api/app-collaborators/${encodeURIComponent(app2)}`);
|
|
7940
8530
|
if (!collaborators.length && !pending.length) {
|
|
7941
8531
|
if (!args.json) {
|
|
@@ -7973,7 +8563,7 @@ var add = defineDeepspaceCommand({
|
|
|
7973
8563
|
async run({ args }) {
|
|
7974
8564
|
const email = String(args.email);
|
|
7975
8565
|
const token = await ensureToken();
|
|
7976
|
-
const app2 = await resolveAppTarget(
|
|
8566
|
+
const app2 = await resolveAppTarget(DEPLOY_URL9, token, args.app);
|
|
7977
8567
|
if (isTestAccountEmail(email)) {
|
|
7978
8568
|
throw new Refusal(
|
|
7979
8569
|
"Test accounts (@deepspace.test) cannot be added as collaborators. Use a real DeepSpace account.",
|
|
@@ -8015,13 +8605,13 @@ var remove = defineDeepspaceCommand({
|
|
|
8015
8605
|
async run({ args }) {
|
|
8016
8606
|
const email = String(args.email);
|
|
8017
8607
|
const token = await ensureToken();
|
|
8018
|
-
const app2 = await resolveAppTarget(
|
|
8608
|
+
const app2 = await resolveAppTarget(DEPLOY_URL9, token, args.app);
|
|
8019
8609
|
const { collaborators } = await api2(
|
|
8020
8610
|
token,
|
|
8021
8611
|
`/api/app-collaborators/${encodeURIComponent(app2)}`
|
|
8022
8612
|
);
|
|
8023
|
-
const
|
|
8024
|
-
const found = collaborators.find((c) => c.emailDisplay.toLowerCase() ===
|
|
8613
|
+
const target2 = email.trim().toLowerCase();
|
|
8614
|
+
const found = collaborators.find((c) => c.emailDisplay.toLowerCase() === target2);
|
|
8025
8615
|
if (!found) {
|
|
8026
8616
|
throw new Refusal(`${email} is not a collaborator on ${app2}`, "not_a_collaborator", {
|
|
8027
8617
|
action: cliAction("deepspace", "app", "collaborators", "list", "--app", app2)
|
|
@@ -8045,13 +8635,13 @@ var cancel3 = defineDeepspaceCommand({
|
|
|
8045
8635
|
async run({ args }) {
|
|
8046
8636
|
const email = String(args.email);
|
|
8047
8637
|
const token = await ensureToken();
|
|
8048
|
-
const app2 = await resolveAppTarget(
|
|
8638
|
+
const app2 = await resolveAppTarget(DEPLOY_URL9, token, args.app);
|
|
8049
8639
|
const { pending = [] } = await api2(
|
|
8050
8640
|
token,
|
|
8051
8641
|
`/api/app-collaborators/${encodeURIComponent(app2)}`
|
|
8052
8642
|
);
|
|
8053
|
-
const
|
|
8054
|
-
const found = pending.find((p34) => p34.email.toLowerCase() ===
|
|
8643
|
+
const target2 = email.trim().toLowerCase();
|
|
8644
|
+
const found = pending.find((p34) => p34.email.toLowerCase() === target2);
|
|
8055
8645
|
if (!found) {
|
|
8056
8646
|
throw new Refusal(`${email} has no pending invite on ${app2}`, "no_pending_invite", {
|
|
8057
8647
|
action: cliAction("deepspace", "app", "collaborators", "list", "--app", app2)
|
|
@@ -8066,16 +8656,16 @@ var cancel3 = defineDeepspaceCommand({
|
|
|
8066
8656
|
return { data: { cancelled: found } };
|
|
8067
8657
|
}
|
|
8068
8658
|
});
|
|
8069
|
-
var collaborators_default =
|
|
8659
|
+
var collaborators_default = defineCommand7({
|
|
8070
8660
|
meta: { name: "collaborators", description: "Manage who can deploy your app" },
|
|
8071
|
-
subCommands: { list:
|
|
8661
|
+
subCommands: { list: list4, add, remove, cancel: cancel3 }
|
|
8072
8662
|
});
|
|
8073
8663
|
|
|
8074
8664
|
// src/cli/commands/transfer.ts
|
|
8075
|
-
import { defineCommand as
|
|
8665
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
8076
8666
|
import * as p14 from "@clack/prompts";
|
|
8077
8667
|
var API_URL6 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
8078
|
-
var
|
|
8668
|
+
var DEPLOY_URL10 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
8079
8669
|
var offer = defineDeepspaceCommand({
|
|
8080
8670
|
meta: { name: "offer", description: "Offer this app to another DeepSpace user" },
|
|
8081
8671
|
args: {
|
|
@@ -8089,7 +8679,7 @@ var offer = defineDeepspaceCommand({
|
|
|
8089
8679
|
async run({ args }) {
|
|
8090
8680
|
const email = String(args.email);
|
|
8091
8681
|
const token = await ensureToken();
|
|
8092
|
-
const app2 = await resolveAppTarget(
|
|
8682
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
8093
8683
|
const pending = await apiFetch(
|
|
8094
8684
|
API_URL6,
|
|
8095
8685
|
token,
|
|
@@ -8141,7 +8731,7 @@ var status2 = defineDeepspaceCommand({
|
|
|
8141
8731
|
},
|
|
8142
8732
|
async run({ args }) {
|
|
8143
8733
|
const token = await ensureToken();
|
|
8144
|
-
const app2 = await resolveAppTarget(
|
|
8734
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
8145
8735
|
const { transfer } = await apiFetch(API_URL6, token, `/api/app-transfers/${encodeURIComponent(app2)}`);
|
|
8146
8736
|
if (!transfer) {
|
|
8147
8737
|
if (!args.json) console.log(`No pending transfer for ${app2}.`);
|
|
@@ -8168,7 +8758,7 @@ var accept = defineDeepspaceCommand({
|
|
|
8168
8758
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8169
8759
|
throw new Refusal(msg, msg.startsWith("No app id") ? "not_in_app_repo" : "invalid_app");
|
|
8170
8760
|
}
|
|
8171
|
-
await apiFetch(
|
|
8761
|
+
await apiFetch(DEPLOY_URL10, token, `/api/apps/${encodeURIComponent(app2)}/transfer/accept`, {
|
|
8172
8762
|
method: "POST"
|
|
8173
8763
|
});
|
|
8174
8764
|
if (!args.json) {
|
|
@@ -8186,37 +8776,37 @@ var cancel4 = defineDeepspaceCommand({
|
|
|
8186
8776
|
},
|
|
8187
8777
|
async run({ args }) {
|
|
8188
8778
|
const token = await ensureToken();
|
|
8189
|
-
const app2 = await resolveAppTarget(
|
|
8779
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
8190
8780
|
await apiFetch(API_URL6, token, `/api/app-transfers/${encodeURIComponent(app2)}`, { method: "DELETE" });
|
|
8191
8781
|
if (!args.json) console.log(`\u2713 Transfer offer for ${app2} cancelled.`);
|
|
8192
8782
|
return { data: { app: app2, cancelled: true } };
|
|
8193
8783
|
}
|
|
8194
8784
|
});
|
|
8195
|
-
var transfer_default =
|
|
8785
|
+
var transfer_default = defineCommand8({
|
|
8196
8786
|
meta: { name: "transfer", description: "Transfer app ownership to another user" },
|
|
8197
8787
|
subCommands: { offer, status: status2, accept, cancel: cancel4 }
|
|
8198
8788
|
});
|
|
8199
8789
|
|
|
8200
8790
|
// src/cli/commands/integrations.ts
|
|
8201
|
-
import { defineCommand as
|
|
8791
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
8202
8792
|
|
|
8203
8793
|
// src/cli/commands/_invoke-impl.ts
|
|
8204
8794
|
import { readFileSync as readFileSync22 } from "fs";
|
|
8205
8795
|
import * as p15 from "@clack/prompts";
|
|
8206
8796
|
var API_URL7 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
8207
8797
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
8208
|
-
function parseTarget(
|
|
8209
|
-
if (!
|
|
8798
|
+
function parseTarget(target2) {
|
|
8799
|
+
if (!target2 || typeof target2 !== "string") {
|
|
8210
8800
|
throw new Refusal(
|
|
8211
8801
|
"Missing target. Expected '<integration>/<endpoint>' (e.g. 'openai/chat-completion').",
|
|
8212
8802
|
"missing_target",
|
|
8213
8803
|
{ action: cliAction("deepspace", "integrations", "list") }
|
|
8214
8804
|
);
|
|
8215
8805
|
}
|
|
8216
|
-
const parts =
|
|
8806
|
+
const parts = target2.split("/");
|
|
8217
8807
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
8218
8808
|
throw new Refusal(
|
|
8219
|
-
`Bad target '${
|
|
8809
|
+
`Bad target '${target2}'. Expected '<integration>/<endpoint>' (e.g. 'openai/chat-completion').`,
|
|
8220
8810
|
"bad_target",
|
|
8221
8811
|
{ action: cliAction("deepspace", "integrations", "list") }
|
|
8222
8812
|
);
|
|
@@ -8436,7 +9026,7 @@ async function runInvoke(args) {
|
|
|
8436
9026
|
}
|
|
8437
9027
|
|
|
8438
9028
|
// src/cli/commands/integrations.ts
|
|
8439
|
-
var
|
|
9029
|
+
var list5 = defineDeepspaceCommand({
|
|
8440
9030
|
meta: {
|
|
8441
9031
|
name: "list",
|
|
8442
9032
|
description: "List all available integrations"
|
|
@@ -8507,13 +9097,13 @@ var invoke = defineDeepspaceCommand({
|
|
|
8507
9097
|
});
|
|
8508
9098
|
}
|
|
8509
9099
|
});
|
|
8510
|
-
var integrations_default =
|
|
9100
|
+
var integrations_default = defineCommand9({
|
|
8511
9101
|
meta: {
|
|
8512
9102
|
name: "integrations",
|
|
8513
9103
|
description: "List, inspect, and invoke platform integrations"
|
|
8514
9104
|
},
|
|
8515
9105
|
subCommands: {
|
|
8516
|
-
list:
|
|
9106
|
+
list: list5,
|
|
8517
9107
|
info,
|
|
8518
9108
|
invoke
|
|
8519
9109
|
}
|
|
@@ -8521,7 +9111,7 @@ var integrations_default = defineCommand8({
|
|
|
8521
9111
|
|
|
8522
9112
|
// src/cli/commands/feedback.ts
|
|
8523
9113
|
import * as p16 from "@clack/prompts";
|
|
8524
|
-
import { existsSync as
|
|
9114
|
+
import { existsSync as existsSync22, readFileSync as readFileSync23 } from "fs";
|
|
8525
9115
|
import { dirname as dirname8, join as join20 } from "path";
|
|
8526
9116
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8527
9117
|
import { release } from "os";
|
|
@@ -8549,7 +9139,7 @@ function readCliVersion() {
|
|
|
8549
9139
|
join20(here, "..", "..", "..", "package.json")
|
|
8550
9140
|
];
|
|
8551
9141
|
for (const path of candidates) {
|
|
8552
|
-
if (!
|
|
9142
|
+
if (!existsSync22(path)) continue;
|
|
8553
9143
|
const pkg2 = JSON.parse(readFileSync23(path, "utf-8"));
|
|
8554
9144
|
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
8555
9145
|
}
|
|
@@ -8745,7 +9335,7 @@ Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payl
|
|
|
8745
9335
|
});
|
|
8746
9336
|
|
|
8747
9337
|
// src/cli/commands/secrets.ts
|
|
8748
|
-
import { defineCommand as
|
|
9338
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
8749
9339
|
import { readFileSync as readFileSync24 } from "fs";
|
|
8750
9340
|
|
|
8751
9341
|
// src/cli/lib/citty-args.ts
|
|
@@ -8779,7 +9369,7 @@ function parseCursorArg(raw) {
|
|
|
8779
9369
|
}
|
|
8780
9370
|
|
|
8781
9371
|
// src/cli/commands/secrets.ts
|
|
8782
|
-
var
|
|
9372
|
+
var DEPLOY_URL11 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
8783
9373
|
var COMMON_ARGS = {
|
|
8784
9374
|
app: {
|
|
8785
9375
|
type: "string",
|
|
@@ -8849,7 +9439,7 @@ function warnIfDebugRoutesEnabled(secrets) {
|
|
|
8849
9439
|
}
|
|
8850
9440
|
var APPLY_HINT = "Run `deepspace deploy` to apply \u2014 secrets take effect at deploy time.";
|
|
8851
9441
|
var LOCAL_DEV_HINT = "Running `deepspace dev start`? Restart it to load the change locally.";
|
|
8852
|
-
var
|
|
9442
|
+
var list6 = defineCommand10({
|
|
8853
9443
|
meta: { name: "list", description: "List masked secrets in a config" },
|
|
8854
9444
|
args: {
|
|
8855
9445
|
...COMMON_ARGS,
|
|
@@ -8862,7 +9452,7 @@ var list5 = defineCommand9({
|
|
|
8862
9452
|
async run({ args }) {
|
|
8863
9453
|
try {
|
|
8864
9454
|
const t = await resolveTarget(args);
|
|
8865
|
-
const { secrets } = await listSecrets(
|
|
9455
|
+
const { secrets } = await listSecrets(DEPLOY_URL11, t.token, t.appId, t.configName);
|
|
8866
9456
|
if (args.json) {
|
|
8867
9457
|
console.log(JSON.stringify({ ok: true, appId: t.appId, config: t.configName, secrets }));
|
|
8868
9458
|
return;
|
|
@@ -8879,7 +9469,7 @@ var list5 = defineCommand9({
|
|
|
8879
9469
|
}
|
|
8880
9470
|
}
|
|
8881
9471
|
});
|
|
8882
|
-
var set =
|
|
9472
|
+
var set = defineCommand10({
|
|
8883
9473
|
meta: { name: "set", description: "Set secrets from KEY=value pairs" },
|
|
8884
9474
|
args: {
|
|
8885
9475
|
...COMMON_ARGS,
|
|
@@ -8907,9 +9497,9 @@ var set = defineCommand9({
|
|
|
8907
9497
|
}
|
|
8908
9498
|
if (Object.keys(secrets).length === 1) {
|
|
8909
9499
|
const [[k, v]] = Object.entries(secrets);
|
|
8910
|
-
await setSecret(
|
|
9500
|
+
await setSecret(DEPLOY_URL11, t.token, t.appId, t.configName, k, v);
|
|
8911
9501
|
} else {
|
|
8912
|
-
await uploadSecrets(
|
|
9502
|
+
await uploadSecrets(DEPLOY_URL11, t.token, t.appId, t.configName, secrets, false);
|
|
8913
9503
|
}
|
|
8914
9504
|
const n = Object.keys(secrets).length;
|
|
8915
9505
|
ok(
|
|
@@ -8928,7 +9518,7 @@ var set = defineCommand9({
|
|
|
8928
9518
|
}
|
|
8929
9519
|
}
|
|
8930
9520
|
});
|
|
8931
|
-
var
|
|
9521
|
+
var get2 = defineCommand10({
|
|
8932
9522
|
meta: { name: "get", description: "Get a secret; pass --plain to print the value" },
|
|
8933
9523
|
args: {
|
|
8934
9524
|
...COMMON_ARGS,
|
|
@@ -8940,11 +9530,11 @@ var get = defineCommand9({
|
|
|
8940
9530
|
const t = await resolveTarget(args);
|
|
8941
9531
|
const key = validateSecretName(args.key);
|
|
8942
9532
|
if (args.plain) {
|
|
8943
|
-
const { value } = await getSecretPlain(
|
|
9533
|
+
const { value } = await getSecretPlain(DEPLOY_URL11, t.token, t.appId, t.configName, key);
|
|
8944
9534
|
process.stdout.write(process.stdout.isTTY ? value + "\n" : value);
|
|
8945
9535
|
return;
|
|
8946
9536
|
}
|
|
8947
|
-
const { secrets } = await listSecrets(
|
|
9537
|
+
const { secrets } = await listSecrets(DEPLOY_URL11, t.token, t.appId, t.configName);
|
|
8948
9538
|
const item = secrets.find((s) => s.key === key);
|
|
8949
9539
|
if (!item)
|
|
8950
9540
|
fail(new InputError(`Secret "${key}" not found in ${t.configName}`, "secret_not_found"));
|
|
@@ -8964,7 +9554,7 @@ var get = defineCommand9({
|
|
|
8964
9554
|
}
|
|
8965
9555
|
}
|
|
8966
9556
|
});
|
|
8967
|
-
var del2 =
|
|
9557
|
+
var del2 = defineCommand10({
|
|
8968
9558
|
meta: { name: "delete", description: "Delete one or more secrets" },
|
|
8969
9559
|
args: {
|
|
8970
9560
|
...COMMON_ARGS,
|
|
@@ -8979,7 +9569,7 @@ var del2 = defineCommand9({
|
|
|
8979
9569
|
for (const key of keys) {
|
|
8980
9570
|
const name = validateSecretName(key);
|
|
8981
9571
|
try {
|
|
8982
|
-
await deleteSecret(
|
|
9572
|
+
await deleteSecret(DEPLOY_URL11, t.token, t.appId, t.configName, name);
|
|
8983
9573
|
deleted++;
|
|
8984
9574
|
} catch (err) {
|
|
8985
9575
|
if (err?.status === 404) absent++;
|
|
@@ -8998,7 +9588,7 @@ var del2 = defineCommand9({
|
|
|
8998
9588
|
}
|
|
8999
9589
|
}
|
|
9000
9590
|
});
|
|
9001
|
-
var upload =
|
|
9591
|
+
var upload = defineCommand10({
|
|
9002
9592
|
meta: { name: "upload", description: "Upload secrets from a dotenv or JSON file" },
|
|
9003
9593
|
args: {
|
|
9004
9594
|
...COMMON_ARGS,
|
|
@@ -9020,7 +9610,7 @@ var upload = defineCommand9({
|
|
|
9020
9610
|
const secrets = parseSecretsUpload(content);
|
|
9021
9611
|
if (Object.keys(secrets).length === 0)
|
|
9022
9612
|
fail(new InputError("No secrets found in the input.", "empty_input"));
|
|
9023
|
-
await uploadSecrets(
|
|
9613
|
+
await uploadSecrets(DEPLOY_URL11, t.token, t.appId, t.configName, secrets, args.replace);
|
|
9024
9614
|
const uploaded = Object.keys(secrets);
|
|
9025
9615
|
ok(
|
|
9026
9616
|
args.json === true,
|
|
@@ -9035,7 +9625,7 @@ var upload = defineCommand9({
|
|
|
9035
9625
|
}
|
|
9036
9626
|
}
|
|
9037
9627
|
});
|
|
9038
|
-
var download =
|
|
9628
|
+
var download = defineCommand10({
|
|
9039
9629
|
meta: { name: "download", description: "Download a config\u2019s secrets (dotenv/json/shell)" },
|
|
9040
9630
|
args: {
|
|
9041
9631
|
...COMMON_ARGS,
|
|
@@ -9057,14 +9647,14 @@ var download = defineCommand9({
|
|
|
9057
9647
|
)
|
|
9058
9648
|
);
|
|
9059
9649
|
}
|
|
9060
|
-
const { secrets } = await fetchSecretsValues(
|
|
9650
|
+
const { secrets } = await fetchSecretsValues(DEPLOY_URL11, t.token, t.appId, t.configName);
|
|
9061
9651
|
process.stdout.write(formatSecretsDownload(secrets, format));
|
|
9062
9652
|
} catch (err) {
|
|
9063
9653
|
fail(err);
|
|
9064
9654
|
}
|
|
9065
9655
|
}
|
|
9066
9656
|
});
|
|
9067
|
-
var pull =
|
|
9657
|
+
var pull = defineCommand10({
|
|
9068
9658
|
meta: { name: "pull", description: "Refresh the .dev.vars cache from the app store" },
|
|
9069
9659
|
args: { ...COMMON_ARGS },
|
|
9070
9660
|
async run({ args }) {
|
|
@@ -9080,7 +9670,7 @@ var pull = defineCommand9({
|
|
|
9080
9670
|
);
|
|
9081
9671
|
const t = await resolveTarget(args);
|
|
9082
9672
|
const ownerId = decodeJwtPayload(t.token).sub;
|
|
9083
|
-
const pulled = await pullAppSecretsCache(
|
|
9673
|
+
const pulled = await pullAppSecretsCache(DEPLOY_URL11, t.token, t.appId, t.configName);
|
|
9084
9674
|
if (!pulled) {
|
|
9085
9675
|
ok(
|
|
9086
9676
|
args.json === true,
|
|
@@ -9107,13 +9697,13 @@ var pull = defineCommand9({
|
|
|
9107
9697
|
}
|
|
9108
9698
|
}
|
|
9109
9699
|
});
|
|
9110
|
-
var configsList =
|
|
9700
|
+
var configsList = defineCommand10({
|
|
9111
9701
|
meta: { name: "list", description: "List the app\u2019s configs" },
|
|
9112
9702
|
args: { ...COMMON_ARGS },
|
|
9113
9703
|
async run({ args }) {
|
|
9114
9704
|
try {
|
|
9115
9705
|
const t = await resolveTarget(args);
|
|
9116
|
-
const { configs: configs2 } = await listConfigs(
|
|
9706
|
+
const { configs: configs2 } = await listConfigs(DEPLOY_URL11, t.token, t.appId);
|
|
9117
9707
|
ok(args.json === true, { appId: t.appId, configs: configs2 }, () => {
|
|
9118
9708
|
if (configs2.length === 0) {
|
|
9119
9709
|
console.log("No configs yet \u2014 the first `secrets set` creates one.");
|
|
@@ -9128,7 +9718,7 @@ var configsList = defineCommand9({
|
|
|
9128
9718
|
}
|
|
9129
9719
|
}
|
|
9130
9720
|
});
|
|
9131
|
-
var configsCreate =
|
|
9721
|
+
var configsCreate = defineCommand10({
|
|
9132
9722
|
meta: { name: "create", description: "Create a config (optionally copying another)" },
|
|
9133
9723
|
args: {
|
|
9134
9724
|
...COMMON_ARGS,
|
|
@@ -9140,7 +9730,7 @@ var configsCreate = defineCommand9({
|
|
|
9140
9730
|
const t = await resolveTarget(args);
|
|
9141
9731
|
const name = validateConfigName(args.name);
|
|
9142
9732
|
const copyFrom = args["copy-from"] || void 0;
|
|
9143
|
-
const { configs: configs2 } = await listConfigs(
|
|
9733
|
+
const { configs: configs2 } = await listConfigs(DEPLOY_URL11, t.token, t.appId);
|
|
9144
9734
|
if (configs2.some((c) => c.name === name)) {
|
|
9145
9735
|
if (copyFrom) {
|
|
9146
9736
|
fail(
|
|
@@ -9157,7 +9747,7 @@ var configsCreate = defineCommand9({
|
|
|
9157
9747
|
);
|
|
9158
9748
|
return;
|
|
9159
9749
|
}
|
|
9160
|
-
await createConfig(
|
|
9750
|
+
await createConfig(DEPLOY_URL11, t.token, t.appId, name, copyFrom);
|
|
9161
9751
|
ok(
|
|
9162
9752
|
args.json === true,
|
|
9163
9753
|
{ appId: t.appId, config: name, created: true, ...copyFrom ? { copyFrom } : {} },
|
|
@@ -9168,7 +9758,7 @@ var configsCreate = defineCommand9({
|
|
|
9168
9758
|
}
|
|
9169
9759
|
}
|
|
9170
9760
|
});
|
|
9171
|
-
var configsDelete =
|
|
9761
|
+
var configsDelete = defineCommand10({
|
|
9172
9762
|
meta: { name: "delete", description: "Delete a config and all of its secrets" },
|
|
9173
9763
|
args: {
|
|
9174
9764
|
...COMMON_ARGS,
|
|
@@ -9178,7 +9768,7 @@ var configsDelete = defineCommand9({
|
|
|
9178
9768
|
try {
|
|
9179
9769
|
const t = await resolveTarget(args);
|
|
9180
9770
|
const name = validateConfigName(args.name);
|
|
9181
|
-
await deleteConfig(
|
|
9771
|
+
await deleteConfig(DEPLOY_URL11, t.token, t.appId, name);
|
|
9182
9772
|
ok(
|
|
9183
9773
|
args.json === true,
|
|
9184
9774
|
{ appId: t.appId, config: name, deleted: true },
|
|
@@ -9189,16 +9779,16 @@ var configsDelete = defineCommand9({
|
|
|
9189
9779
|
}
|
|
9190
9780
|
}
|
|
9191
9781
|
});
|
|
9192
|
-
var configs =
|
|
9782
|
+
var configs = defineCommand10({
|
|
9193
9783
|
meta: { name: "configs", description: "Manage the app\u2019s secrets configs" },
|
|
9194
9784
|
subCommands: { list: configsList, create: configsCreate, delete: configsDelete }
|
|
9195
9785
|
});
|
|
9196
|
-
var secrets_default =
|
|
9786
|
+
var secrets_default = defineCommand10({
|
|
9197
9787
|
meta: { name: "secrets", description: "Manage the app\u2019s secrets store" },
|
|
9198
9788
|
subCommands: {
|
|
9199
|
-
list:
|
|
9789
|
+
list: list6,
|
|
9200
9790
|
set,
|
|
9201
|
-
get,
|
|
9791
|
+
get: get2,
|
|
9202
9792
|
delete: del2,
|
|
9203
9793
|
upload,
|
|
9204
9794
|
download,
|
|
@@ -9270,9 +9860,9 @@ var init_default = defineDeepspaceCommand({
|
|
|
9270
9860
|
});
|
|
9271
9861
|
|
|
9272
9862
|
// src/cli/commands/status.ts
|
|
9273
|
-
import { defineCommand as
|
|
9863
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
9274
9864
|
import * as p17 from "@clack/prompts";
|
|
9275
|
-
import { existsSync as
|
|
9865
|
+
import { existsSync as existsSync23, readFileSync as readFileSync25 } from "fs";
|
|
9276
9866
|
function timeAgo(iso) {
|
|
9277
9867
|
const ms = Date.now() - Date.parse(iso);
|
|
9278
9868
|
if (!Number.isFinite(ms) || ms < 0) return iso;
|
|
@@ -9305,7 +9895,7 @@ function resolveStatusApp(appDir, wranglerEnv) {
|
|
|
9305
9895
|
if (!resolved.ok) throw new InputError(resolved.reason, "invalid_env");
|
|
9306
9896
|
return { appName: resolved.name, appId: readAppId(appDir, wranglerEnv) };
|
|
9307
9897
|
}
|
|
9308
|
-
var status_default =
|
|
9898
|
+
var status_default = defineCommand11({
|
|
9309
9899
|
meta: {
|
|
9310
9900
|
name: "status",
|
|
9311
9901
|
description: "One screenful of current state: session, app, workspace, and live release"
|
|
@@ -9357,7 +9947,7 @@ var status_default = defineCommand10({
|
|
|
9357
9947
|
json.services = urls;
|
|
9358
9948
|
if (urlOverrides.length > 0) json.urlOverrides = urlOverrides;
|
|
9359
9949
|
}
|
|
9360
|
-
facts.loggedIn =
|
|
9950
|
+
facts.loggedIn = existsSync23(SESSION_PATH);
|
|
9361
9951
|
let userId = null;
|
|
9362
9952
|
if (!facts.loggedIn) {
|
|
9363
9953
|
lines.push(["Session", "not logged in"]);
|
|
@@ -9454,24 +10044,36 @@ var status_default = defineCommand10({
|
|
|
9454
10044
|
const failure = statusRemoteFailure(error);
|
|
9455
10045
|
lines.push([label, failure.human]);
|
|
9456
10046
|
json[key] = failure.json;
|
|
9457
|
-
},
|
|
10047
|
+
}, reportLive2 = function(rel, entry) {
|
|
10048
|
+
if (rel === null) {
|
|
10049
|
+
lines.push(["Live", "never deployed"]);
|
|
10050
|
+
json.live = null;
|
|
10051
|
+
return;
|
|
10052
|
+
}
|
|
9458
10053
|
const byYou = userId !== null && rel.actor === userId;
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
`release #${rel.seq} (${rel.kind} of ${rel.commitOid?.slice(0, 10) ?? "unknown source"}, ${timeAgo(rel.createdAt)}${byYou ? ", by you" : ""})` + (rel.url ? ` \xB7 ${rel.url}` : "")
|
|
9462
|
-
]);
|
|
9463
|
-
json.live = {
|
|
10054
|
+
const release2 = `release #${rel.seq} (${rel.kind} of ${rel.commitOid?.slice(0, 10) ?? "unknown source"}, ${timeAgo(rel.createdAt)}${byYou ? ", by you" : ""})`;
|
|
10055
|
+
const details = {
|
|
9464
10056
|
seq: rel.seq,
|
|
9465
10057
|
kind: rel.kind,
|
|
9466
10058
|
commitOid: rel.commitOid,
|
|
9467
10059
|
createdAt: rel.createdAt,
|
|
9468
|
-
url: rel.url,
|
|
9469
10060
|
byYou,
|
|
9470
10061
|
source: rel.source,
|
|
9471
10062
|
sourceRevision: rel.sourceRevision
|
|
9472
10063
|
};
|
|
10064
|
+
if (!entry) {
|
|
10065
|
+
lines.push(["Live", `${release2} \xB7 serving state not visible from your account`]);
|
|
10066
|
+
json.live = { ...details, serving: null, url: null };
|
|
10067
|
+
return;
|
|
10068
|
+
}
|
|
10069
|
+
const url = liveAppUrl(entry);
|
|
10070
|
+
lines.push([
|
|
10071
|
+
"Live",
|
|
10072
|
+
url ? `${release2} \xB7 ${url}` : `not serving \u2014 ${entry.status}, last ${release2}`
|
|
10073
|
+
]);
|
|
10074
|
+
json.live = { ...details, serving: url !== null, url };
|
|
9473
10075
|
};
|
|
9474
|
-
var reportWorkspace = reportWorkspace2, reportRemoteFailure = reportRemoteFailure2,
|
|
10076
|
+
var reportWorkspace = reportWorkspace2, reportRemoteFailure = reportRemoteFailure2, reportLive = reportLive2;
|
|
9475
10077
|
const api4 = repoApi(deployBaseUrl(), token, appId);
|
|
9476
10078
|
const sourceResult = await observeRemote(getAppSource(deployBaseUrl(), token, appId));
|
|
9477
10079
|
const source = sourceResult.ok ? sourceResult.value.source : null;
|
|
@@ -9491,10 +10093,11 @@ var status_default = defineCommand10({
|
|
|
9491
10093
|
lines.push(["Source", "unclaimed \xB7 next normal deploy defaults to DeepSpace"]);
|
|
9492
10094
|
json.source = null;
|
|
9493
10095
|
}
|
|
9494
|
-
const [refsResult, wsResult, releaseResult] = await Promise.all([
|
|
10096
|
+
const [refsResult, wsResult, releaseResult, appsResult] = await Promise.all([
|
|
9495
10097
|
githubSource ? Promise.resolve({ ok: true, value: null }) : observeRemote(api4.getRefs()),
|
|
9496
10098
|
facts.workspaceId && !githubSource ? observeRemote(api4.getWorkspace(facts.workspaceId).then((r) => r.view)) : Promise.resolve({ ok: true, value: null }),
|
|
9497
|
-
observeRemote(api4.latestRelease().then((r) => r.release))
|
|
10099
|
+
observeRemote(api4.latestRelease().then((r) => r.release)),
|
|
10100
|
+
observeRemote(listApps(deployBaseUrl(), token))
|
|
9498
10101
|
]);
|
|
9499
10102
|
if (facts.workspaceId) {
|
|
9500
10103
|
if (githubSource) {
|
|
@@ -9523,12 +10126,8 @@ var status_default = defineCommand10({
|
|
|
9523
10126
|
}
|
|
9524
10127
|
}
|
|
9525
10128
|
if (!releaseResult.ok) reportRemoteFailure2("Live", "live", releaseResult.error);
|
|
9526
|
-
else if (
|
|
9527
|
-
|
|
9528
|
-
json.live = null;
|
|
9529
|
-
} else {
|
|
9530
|
-
reportRelease2(releaseResult.value);
|
|
9531
|
-
}
|
|
10129
|
+
else if (!appsResult.ok) reportRemoteFailure2("Live", "live", appsResult.error);
|
|
10130
|
+
else reportLive2(releaseResult.value, appsResult.value.find((a) => a.appId === appId));
|
|
9532
10131
|
}
|
|
9533
10132
|
spinner2?.stop("Cloud state checked.");
|
|
9534
10133
|
}
|
|
@@ -9571,7 +10170,7 @@ function workspaceBranchPushRefusal(branch) {
|
|
|
9571
10170
|
var push_default = defineDeepspaceCommand({
|
|
9572
10171
|
meta: {
|
|
9573
10172
|
name: "push",
|
|
9574
|
-
description:
|
|
10173
|
+
description: `Push local git commits to the app's cloud repo (${PUSH_CEILINGS})`
|
|
9575
10174
|
},
|
|
9576
10175
|
args: {
|
|
9577
10176
|
branch: {
|
|
@@ -9596,8 +10195,8 @@ var push_default = defineDeepspaceCommand({
|
|
|
9596
10195
|
const branchArg = args.branch === void 0 ? void 0 : String(args.branch);
|
|
9597
10196
|
const appArg = typeof args.app === "string" ? args.app : void 0;
|
|
9598
10197
|
const force = args.force === true;
|
|
10198
|
+
const appDir = findAppDir();
|
|
9599
10199
|
try {
|
|
9600
|
-
const appDir = findAppDir();
|
|
9601
10200
|
if (!appDir)
|
|
9602
10201
|
throw new Refusal(
|
|
9603
10202
|
"No wrangler.toml found \u2014 run from inside an app directory.",
|
|
@@ -9726,7 +10325,7 @@ var push_default = defineDeepspaceCommand({
|
|
|
9726
10325
|
}
|
|
9727
10326
|
const recoverable = isRecoverablePushFailure(result.status);
|
|
9728
10327
|
const rejectReason = result.reason ?? result.summary;
|
|
9729
|
-
const errorMsg = ok2 ? null : result.status === "ref_conflict" ? `The cloud repo's ${branch} moved while you worked (${rejectReason}). Run \`deepspace pull --app ${shQuote(appId)} --branch ${shQuote(branch)}\` to integrate, then push again.` : result.status === "rejected" ? `The cloud repo rejected ${branch}
|
|
10328
|
+
const errorMsg = ok2 ? null : result.status === "ref_conflict" ? `The cloud repo's ${branch} moved while you worked (${rejectReason}). Run \`deepspace pull --app ${shQuote(appId)} --branch ${shQuote(branch)}\` to integrate, then push again.` : result.status === "rejected" ? pushFailureMessage(`The cloud repo rejected ${branch}`, result, appDir ?? void 0) : `The cloud repo's ${branch} has commit(s) your local ${branch} doesn't. Run \`deepspace pull --app ${shQuote(appId)} --branch ${shQuote(branch)}\` and merge (or rebase), then push. Avoid --force here \u2014 it's guarded and will refuse rather than silently drop a peer's commits.`;
|
|
9730
10329
|
if (!args.json && ok2) p18.log.info(`App: ${appId}`);
|
|
9731
10330
|
const action = recoverable ? pullRecoveryAction(pullRecoveryCwd, appId, branch) : void 0;
|
|
9732
10331
|
if (!ok2) {
|
|
@@ -9739,7 +10338,7 @@ var push_default = defineDeepspaceCommand({
|
|
|
9739
10338
|
return { data: { status: result.status, appId, branch, oid: tipOid } };
|
|
9740
10339
|
} catch (err) {
|
|
9741
10340
|
if (err instanceof Refusal) throw err;
|
|
9742
|
-
const transportFailure = classifyPushTransportFailure(err);
|
|
10341
|
+
const transportFailure = classifyPushTransportFailure(err, appDir ?? void 0);
|
|
9743
10342
|
if (transportFailure) {
|
|
9744
10343
|
throw new Refusal(transportFailure.error, transportFailure.code);
|
|
9745
10344
|
}
|
|
@@ -10031,14 +10630,14 @@ var pull_default = defineDeepspaceCommand({
|
|
|
10031
10630
|
|
|
10032
10631
|
// src/cli/commands/clone.ts
|
|
10033
10632
|
import * as p20 from "@clack/prompts";
|
|
10034
|
-
import { existsSync as
|
|
10035
|
-
import { resolve as
|
|
10633
|
+
import { existsSync as existsSync24, readdirSync as readdirSync6, rmSync as rmSync5 } from "fs";
|
|
10634
|
+
import { resolve as resolve16, basename as basename5, join as join21 } from "path";
|
|
10036
10635
|
function cleanFailedCloneDir(dir) {
|
|
10037
10636
|
let entries;
|
|
10038
10637
|
try {
|
|
10039
10638
|
entries = readdirSync6(dir);
|
|
10040
10639
|
} catch {
|
|
10041
|
-
return
|
|
10640
|
+
return existsSync24(dir) ? ["<unreadable>"] : [];
|
|
10042
10641
|
}
|
|
10043
10642
|
if (entries.length === 1 && entries[0] === ".git") {
|
|
10044
10643
|
try {
|
|
@@ -10051,7 +10650,7 @@ function cleanFailedCloneDir(dir) {
|
|
|
10051
10650
|
return entries;
|
|
10052
10651
|
}
|
|
10053
10652
|
function validateCloneTarget(dir) {
|
|
10054
|
-
if (!
|
|
10653
|
+
if (!existsSync24(dir)) return false;
|
|
10055
10654
|
let entries;
|
|
10056
10655
|
try {
|
|
10057
10656
|
entries = readdirSync6(dir);
|
|
@@ -10086,7 +10685,7 @@ var clone_default = defineDeepspaceCommand({
|
|
|
10086
10685
|
async run({ args }) {
|
|
10087
10686
|
assertAppTargetResolvable(String(args.app));
|
|
10088
10687
|
const selector = String(args.app).trim();
|
|
10089
|
-
const dir =
|
|
10688
|
+
const dir = resolve16(String(args.dir ?? selector));
|
|
10090
10689
|
const dirPreexisted = validateCloneTarget(dir);
|
|
10091
10690
|
const spinner2 = args.json ? null : createSpinner();
|
|
10092
10691
|
spinner2?.start(`Resolving ${selector}\u2026`);
|
|
@@ -10129,7 +10728,7 @@ var clone_default = defineDeepspaceCommand({
|
|
|
10129
10728
|
const head = resolveCommit(dir, "HEAD");
|
|
10130
10729
|
const branch = currentBranch(dir);
|
|
10131
10730
|
spinner2?.stop(
|
|
10132
|
-
`Cloned ${selector} (${branches.length} ${branches.length === 1 ? "branch" : "branches"}) into ${
|
|
10731
|
+
`Cloned ${selector} (${branches.length} ${branches.length === 1 ? "branch" : "branches"}) into ${basename5(dir)}/ at ${head?.slice(0, 10) ?? "(unborn)"}.`
|
|
10133
10732
|
);
|
|
10134
10733
|
if (!args.json && !branch) {
|
|
10135
10734
|
p20.log.warn("HEAD is detached \u2014 check `git branch -a`.");
|
|
@@ -10152,7 +10751,28 @@ var clone_default = defineDeepspaceCommand({
|
|
|
10152
10751
|
|
|
10153
10752
|
// src/cli/commands/releases.ts
|
|
10154
10753
|
import * as p21 from "@clack/prompts";
|
|
10155
|
-
|
|
10754
|
+
|
|
10755
|
+
// src/cli/lib/actor-labels.ts
|
|
10756
|
+
var API_URL9 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
10757
|
+
async function actorLabels(token, appId) {
|
|
10758
|
+
const labels = /* @__PURE__ */ new Map();
|
|
10759
|
+
try {
|
|
10760
|
+
const { sub, email } = decodeJwtPayload(token);
|
|
10761
|
+
if (sub && email) labels.set(sub, `${email} (you)`);
|
|
10762
|
+
} catch {
|
|
10763
|
+
}
|
|
10764
|
+
try {
|
|
10765
|
+
const { collaborators } = await apiFetch(API_URL9, token, `/api/app-collaborators/${encodeURIComponent(appId)}`);
|
|
10766
|
+
for (const c of collaborators) {
|
|
10767
|
+
if (!labels.has(c.userId)) labels.set(c.userId, c.emailDisplay);
|
|
10768
|
+
}
|
|
10769
|
+
} catch {
|
|
10770
|
+
}
|
|
10771
|
+
return labels;
|
|
10772
|
+
}
|
|
10773
|
+
|
|
10774
|
+
// src/cli/commands/releases.ts
|
|
10775
|
+
var DEPLOY_URL12 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
10156
10776
|
var releases_default = defineDeepspaceCommand({
|
|
10157
10777
|
meta: {
|
|
10158
10778
|
name: "releases",
|
|
@@ -10187,18 +10807,19 @@ var releases_default = defineDeepspaceCommand({
|
|
|
10187
10807
|
const spinner2 = args.json ? null : createSpinner();
|
|
10188
10808
|
spinner2?.start("Loading release history\u2026");
|
|
10189
10809
|
const token = await ensureToken();
|
|
10190
|
-
const appId = await resolveAppTarget(
|
|
10191
|
-
const { releases } = await repoApi(
|
|
10810
|
+
const appId = await resolveAppTarget(DEPLOY_URL12, token, appArg, { wranglerEnv: envArg });
|
|
10811
|
+
const { releases } = await repoApi(DEPLOY_URL12, token, appId).listReleases(limit);
|
|
10192
10812
|
spinner2?.stop(`Loaded ${releases.length} ${releases.length === 1 ? "release" : "releases"}.`);
|
|
10193
10813
|
if (!args.json) {
|
|
10194
10814
|
if (releases.length === 0) {
|
|
10195
10815
|
p21.log.info("No releases yet \u2014 the history starts with the next `deepspace deploy`.");
|
|
10196
10816
|
} else {
|
|
10817
|
+
const actors = await actorLabels(token, appId);
|
|
10197
10818
|
for (const r of releases) {
|
|
10198
10819
|
const source = r.commitOid ? `commit ${r.commitOid.slice(0, 10)}` : "(no source recorded)";
|
|
10199
10820
|
const rollback = r.rollbackAvailable ? "rollback available" : "rollback unavailable";
|
|
10200
10821
|
console.log(
|
|
10201
|
-
`#${r.seq} ${r.id} ${r.kind.padEnd(8)} ${r.createdAt} ${r.actor} ${source} ${rollback}`
|
|
10822
|
+
`#${r.seq} ${r.id} ${r.kind.padEnd(8)} ${r.createdAt} ${actors.get(r.actor) ?? r.actor} ${source} ${rollback}`
|
|
10202
10823
|
);
|
|
10203
10824
|
}
|
|
10204
10825
|
p21.log.info(
|
|
@@ -10220,7 +10841,7 @@ Retry in a moment, or re-run with --allow-do-deletion to accept the risk.`,
|
|
|
10220
10841
|
"do_guard_unavailable"
|
|
10221
10842
|
);
|
|
10222
10843
|
}
|
|
10223
|
-
var
|
|
10844
|
+
var DEPLOY_URL13 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
10224
10845
|
function pickPreviousRelease(releases) {
|
|
10225
10846
|
if (releases.length === 0) {
|
|
10226
10847
|
return { error: "No releases recorded yet \u2014 nothing to roll back to.", code: "no_releases" };
|
|
@@ -10282,8 +10903,8 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
10282
10903
|
const spinner2 = args.json ? null : createSpinner();
|
|
10283
10904
|
spinner2?.start("Preparing rollback\u2026");
|
|
10284
10905
|
const token = await ensureToken();
|
|
10285
|
-
const appId = await resolveAppTarget(
|
|
10286
|
-
const api4 = repoApi(
|
|
10906
|
+
const appId = await resolveAppTarget(DEPLOY_URL13, token, appArg, { wranglerEnv: envArg });
|
|
10907
|
+
const api4 = repoApi(DEPLOY_URL13, token, appId);
|
|
10287
10908
|
let releaseId = args.release ? String(args.release).trim() : "";
|
|
10288
10909
|
if (!releaseId) {
|
|
10289
10910
|
const { releases } = await api4.listReleases(2);
|
|
@@ -10294,7 +10915,7 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
10294
10915
|
spinner2?.message(`Rolling back to ${releaseId}\u2026`);
|
|
10295
10916
|
let result;
|
|
10296
10917
|
try {
|
|
10297
|
-
result = await apiFetch(
|
|
10918
|
+
result = await apiFetch(DEPLOY_URL13, token, `/api/deploy/${appId}/rollback`, {
|
|
10298
10919
|
method: "POST",
|
|
10299
10920
|
body: JSON.stringify({
|
|
10300
10921
|
releaseId,
|
|
@@ -10321,10 +10942,20 @@ Re-run with --allow-do-deletion to accept the data loss.`,
|
|
|
10321
10942
|
"invalid_response"
|
|
10322
10943
|
);
|
|
10323
10944
|
}
|
|
10324
|
-
|
|
10945
|
+
let edgePropagating = false;
|
|
10946
|
+
if (result.url) {
|
|
10947
|
+
spinner2?.message("Waiting for edge propagation...");
|
|
10948
|
+
edgePropagating = !await waitForEdgePropagation(result.url, 9e4);
|
|
10949
|
+
}
|
|
10950
|
+
spinner2?.stop(
|
|
10951
|
+
edgePropagating ? `Rolled back to ${result.rolledBackTo ?? releaseId} (edge propagation still in progress after 90s).` : `Rolled back to ${result.rolledBackTo ?? releaseId}.`
|
|
10952
|
+
);
|
|
10325
10953
|
const doClassGuard = result.doClassGuard === "unverified" ? "unverified" : void 0;
|
|
10326
10954
|
if (!args.json) {
|
|
10327
|
-
if (result.url)
|
|
10955
|
+
if (result.url) {
|
|
10956
|
+
if (edgePropagating) p22.log.warn(`URL: ${result.url} (accepted; serving not yet verified)`);
|
|
10957
|
+
else p22.log.success(`Live at: ${result.url}`);
|
|
10958
|
+
}
|
|
10328
10959
|
if (result.bundleRetained === false) {
|
|
10329
10960
|
p22.log.warn(
|
|
10330
10961
|
"The rollback is live, but its bundle was concurrently retired and is not available for a future rollback."
|
|
@@ -10348,6 +10979,7 @@ Re-run with --allow-do-deletion to accept the data loss.`,
|
|
|
10348
10979
|
url: result.url ?? null,
|
|
10349
10980
|
versionId: result.versionId ?? null,
|
|
10350
10981
|
wranglerEnv: wranglerEnv ?? null,
|
|
10982
|
+
...edgePropagating ? { edgePropagating: true } : {},
|
|
10351
10983
|
...doClassGuard ? { doClassGuard } : {}
|
|
10352
10984
|
}
|
|
10353
10985
|
};
|
|
@@ -10355,11 +10987,11 @@ Re-run with --allow-do-deletion to accept the data loss.`,
|
|
|
10355
10987
|
});
|
|
10356
10988
|
|
|
10357
10989
|
// src/cli/commands/workspace.ts
|
|
10358
|
-
import { defineCommand as
|
|
10990
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
10359
10991
|
|
|
10360
10992
|
// src/cli/commands/workspace/new.ts
|
|
10361
10993
|
import * as p25 from "@clack/prompts";
|
|
10362
|
-
import { isAbsolute as isAbsolute6, resolve as
|
|
10994
|
+
import { isAbsolute as isAbsolute6, resolve as resolve17 } from "path";
|
|
10363
10995
|
|
|
10364
10996
|
// src/cli/commands/workspace/analysis.ts
|
|
10365
10997
|
import * as p23 from "@clack/prompts";
|
|
@@ -10484,7 +11116,7 @@ function printOverlaps(overlaps, json) {
|
|
|
10484
11116
|
|
|
10485
11117
|
// src/cli/commands/workspace/local.ts
|
|
10486
11118
|
import * as p24 from "@clack/prompts";
|
|
10487
|
-
import { mkdirSync as mkdirSync7, readFileSync as readFileSync26, realpathSync as realpathSync5, renameSync as renameSync2, statSync, writeFileSync as writeFileSync9 } from "fs";
|
|
11119
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync26, realpathSync as realpathSync5, renameSync as renameSync2, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
10488
11120
|
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join22, relative as relative2, sep as sep2 } from "path";
|
|
10489
11121
|
var WORKSPACE_MARKER = "deepspace-workspace.json";
|
|
10490
11122
|
function excludeWorktreeDir(appDir, dir) {
|
|
@@ -10573,7 +11205,7 @@ function materializeWorkspaceWorktree(appDir, worktreeRoot, branch, startPoint,
|
|
|
10573
11205
|
function inOwnLinkedWorktree(appDir, id) {
|
|
10574
11206
|
if (workspaceIdFromBranch(currentBranch(appDir)) !== id) return false;
|
|
10575
11207
|
try {
|
|
10576
|
-
return
|
|
11208
|
+
return statSync3(join22(repoToplevel(appDir), ".git")).isFile();
|
|
10577
11209
|
} catch {
|
|
10578
11210
|
return false;
|
|
10579
11211
|
}
|
|
@@ -10919,7 +11551,7 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10919
11551
|
idempotencyKey: mintIdempotencyKey()
|
|
10920
11552
|
});
|
|
10921
11553
|
const dirInput = typeof args.dir === "string" ? args.dir.trim() : "";
|
|
10922
|
-
const worktreeRoot = dirInput ? isAbsolute6(dirInput) ? dirInput :
|
|
11554
|
+
const worktreeRoot = dirInput ? isAbsolute6(dirInput) ? dirInput : resolve17(appDir, dirInput) : defaultWorkspaceRoot(appDir, id);
|
|
10923
11555
|
const dir = appDirInWorktree(appDir, worktreeRoot);
|
|
10924
11556
|
try {
|
|
10925
11557
|
materializeWorkspaceWorktree(appDir, worktreeRoot, branch, baseOid, id);
|
|
@@ -10957,8 +11589,8 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10957
11589
|
|
|
10958
11590
|
// src/cli/commands/workspace/attach.ts
|
|
10959
11591
|
import * as p26 from "@clack/prompts";
|
|
10960
|
-
import { existsSync as
|
|
10961
|
-
import { isAbsolute as isAbsolute7, resolve as
|
|
11592
|
+
import { existsSync as existsSync25, readdirSync as readdirSync7, rmSync as rmSync6 } from "fs";
|
|
11593
|
+
import { isAbsolute as isAbsolute7, resolve as resolve18 } from "path";
|
|
10962
11594
|
function finishedWorkspaceMessage(id, view) {
|
|
10963
11595
|
const workspace = view.workspace;
|
|
10964
11596
|
if (workspace.status === "landed") {
|
|
@@ -10967,7 +11599,7 @@ function finishedWorkspaceMessage(id, view) {
|
|
|
10967
11599
|
return `Workspace ${id} is ${workspace.status} \u2014 its ref is retained briefly (fetch ${workspace.ref} to inspect), but attach only works on active workspaces.`;
|
|
10968
11600
|
}
|
|
10969
11601
|
function cleanFailedFreshAttachDir(dir) {
|
|
10970
|
-
if (!
|
|
11602
|
+
if (!existsSync25(dir)) return [];
|
|
10971
11603
|
let entries;
|
|
10972
11604
|
try {
|
|
10973
11605
|
entries = readdirSync7(dir);
|
|
@@ -11043,7 +11675,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11043
11675
|
);
|
|
11044
11676
|
}
|
|
11045
11677
|
const requestedDir = typeof args.dir === "string" ? args.dir.trim() : "";
|
|
11046
|
-
const worktreeRoot = requestedDir ? isAbsolute7(requestedDir) ? requestedDir :
|
|
11678
|
+
const worktreeRoot = requestedDir ? isAbsolute7(requestedDir) ? requestedDir : resolve18(appDir, requestedDir) : defaultWorkspaceRoot(appDir, id);
|
|
11047
11679
|
const dir2 = appDirInWorktree(appDir, worktreeRoot);
|
|
11048
11680
|
if (resolveCommit(appDir, `refs/heads/${branch}`)) {
|
|
11049
11681
|
throw new Refusal(
|
|
@@ -11082,11 +11714,11 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11082
11714
|
extra: { status: view.workspace.status }
|
|
11083
11715
|
});
|
|
11084
11716
|
}
|
|
11085
|
-
const dir =
|
|
11717
|
+
const dir = resolve18(
|
|
11086
11718
|
process.cwd(),
|
|
11087
11719
|
(typeof args.dir === "string" ? args.dir.trim() : "") || `ws-${id.slice(3).toLowerCase()}`
|
|
11088
11720
|
);
|
|
11089
|
-
if (
|
|
11721
|
+
if (existsSync25(dir)) {
|
|
11090
11722
|
spinner2?.stop("Target exists.");
|
|
11091
11723
|
throw new Refusal(`${dir} already exists \u2014 pass a different directory.`, "dir_exists");
|
|
11092
11724
|
}
|
|
@@ -11232,27 +11864,6 @@ var syncWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11232
11864
|
|
|
11233
11865
|
// src/cli/commands/workspace/list.ts
|
|
11234
11866
|
import * as p28 from "@clack/prompts";
|
|
11235
|
-
|
|
11236
|
-
// src/cli/lib/actor-labels.ts
|
|
11237
|
-
var API_URL9 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
11238
|
-
async function actorLabels(token, appId) {
|
|
11239
|
-
const labels = /* @__PURE__ */ new Map();
|
|
11240
|
-
try {
|
|
11241
|
-
const { sub, email } = decodeJwtPayload(token);
|
|
11242
|
-
if (sub && email) labels.set(sub, `${email} (you)`);
|
|
11243
|
-
} catch {
|
|
11244
|
-
}
|
|
11245
|
-
try {
|
|
11246
|
-
const { collaborators } = await apiFetch(API_URL9, token, `/api/app-collaborators/${encodeURIComponent(appId)}`);
|
|
11247
|
-
for (const c of collaborators) {
|
|
11248
|
-
if (!labels.has(c.userId)) labels.set(c.userId, c.emailDisplay);
|
|
11249
|
-
}
|
|
11250
|
-
} catch {
|
|
11251
|
-
}
|
|
11252
|
-
return labels;
|
|
11253
|
-
}
|
|
11254
|
-
|
|
11255
|
-
// src/cli/commands/workspace/list.ts
|
|
11256
11867
|
function withWorkspaceOverlaps(views, overlapsById) {
|
|
11257
11868
|
return views.map((view) => ({
|
|
11258
11869
|
...view,
|
|
@@ -11830,7 +12441,7 @@ var dropWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11830
12441
|
});
|
|
11831
12442
|
|
|
11832
12443
|
// src/cli/commands/workspace.ts
|
|
11833
|
-
var workspace_default =
|
|
12444
|
+
var workspace_default = defineCommand12({
|
|
11834
12445
|
meta: {
|
|
11835
12446
|
name: "workspace",
|
|
11836
12447
|
description: "Durable parallel-agent workspaces (new/attach/sync/list/status/land/drop)"
|
|
@@ -12001,7 +12612,7 @@ var activity_default = defineDeepspaceCommand({
|
|
|
12001
12612
|
});
|
|
12002
12613
|
|
|
12003
12614
|
// src/cli/commands/git-credential.ts
|
|
12004
|
-
import { defineCommand as
|
|
12615
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
12005
12616
|
function parseFields(input) {
|
|
12006
12617
|
const fields = /* @__PURE__ */ new Map();
|
|
12007
12618
|
for (const line of input.split("\n")) {
|
|
@@ -12050,7 +12661,7 @@ async function readStdin() {
|
|
|
12050
12661
|
for await (const chunk of process.stdin) data += chunk;
|
|
12051
12662
|
return data;
|
|
12052
12663
|
}
|
|
12053
|
-
var git_credential_default =
|
|
12664
|
+
var git_credential_default = defineCommand13({
|
|
12054
12665
|
meta: {
|
|
12055
12666
|
name: "git-credential",
|
|
12056
12667
|
description: "Git credential helper (installed automatically; not for direct use)"
|
|
@@ -12168,8 +12779,8 @@ function reportSource(appId, state, json) {
|
|
|
12168
12779
|
async function moveToGitHub(input) {
|
|
12169
12780
|
const { appDir, appId, token, deployUrl, state, remoteArg } = input;
|
|
12170
12781
|
const selected = requireGitHubRemote(appDir, remoteArg);
|
|
12171
|
-
const
|
|
12172
|
-
if (sameSource(state.source,
|
|
12782
|
+
const target2 = { provider: "github", repository: selected.repository };
|
|
12783
|
+
if (sameSource(state.source, target2)) return { source: target2, revision: state.revision };
|
|
12173
12784
|
if (state.source?.provider === "deepspace") {
|
|
12174
12785
|
requireLocalHead(appDir);
|
|
12175
12786
|
const api4 = repoApi(deployUrl, token, appId);
|
|
@@ -12225,7 +12836,7 @@ async function moveToGitHub(input) {
|
|
|
12225
12836
|
);
|
|
12226
12837
|
}
|
|
12227
12838
|
return setAppSource(deployUrl, token, appId, {
|
|
12228
|
-
source:
|
|
12839
|
+
source: target2,
|
|
12229
12840
|
expectedRevision: state.revision,
|
|
12230
12841
|
refs: wanted,
|
|
12231
12842
|
...releaseExpectation(latest)
|
|
@@ -12233,17 +12844,17 @@ async function moveToGitHub(input) {
|
|
|
12233
12844
|
}
|
|
12234
12845
|
requirePublishedHead(appDir, selected, appId);
|
|
12235
12846
|
return setAppSource(deployUrl, token, appId, {
|
|
12236
|
-
source:
|
|
12847
|
+
source: target2,
|
|
12237
12848
|
expectedRevision: state.revision
|
|
12238
12849
|
});
|
|
12239
12850
|
}
|
|
12240
12851
|
async function moveToDeepSpace(input) {
|
|
12241
12852
|
const { appDir, appId, token, deployUrl, state, remoteArg } = input;
|
|
12242
|
-
const
|
|
12243
|
-
if (sameSource(state.source,
|
|
12853
|
+
const target2 = { provider: "deepspace" };
|
|
12854
|
+
if (sameSource(state.source, target2)) return { source: target2, revision: state.revision };
|
|
12244
12855
|
if (state.source?.provider !== "github") {
|
|
12245
12856
|
return setAppSource(deployUrl, token, appId, {
|
|
12246
|
-
source:
|
|
12857
|
+
source: target2,
|
|
12247
12858
|
expectedRevision: state.revision
|
|
12248
12859
|
});
|
|
12249
12860
|
}
|
|
@@ -12277,7 +12888,7 @@ async function moveToDeepSpace(input) {
|
|
|
12277
12888
|
);
|
|
12278
12889
|
}
|
|
12279
12890
|
return setAppSource(deployUrl, token, appId, {
|
|
12280
|
-
source:
|
|
12891
|
+
source: target2,
|
|
12281
12892
|
expectedRevision: state.revision,
|
|
12282
12893
|
branch,
|
|
12283
12894
|
commitOid: oid,
|
|
@@ -12905,10 +13516,10 @@ function reportMigration(migration, json) {
|
|
|
12905
13516
|
// src/cli/cli.ts
|
|
12906
13517
|
var __dirname = dirname10(fileURLToPath3(import.meta.url));
|
|
12907
13518
|
var pkg = JSON.parse(readFileSync28(join24(__dirname, "..", "package.json"), "utf-8"));
|
|
12908
|
-
if (process.platform === "linux" && !process.env.SSL_CERT_FILE &&
|
|
13519
|
+
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync26("/etc/ssl/certs/ca-certificates.crt")) {
|
|
12909
13520
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
12910
13521
|
}
|
|
12911
|
-
var create2 =
|
|
13522
|
+
var create2 = defineCommand14({
|
|
12912
13523
|
meta: {
|
|
12913
13524
|
name: "create",
|
|
12914
13525
|
description: "Create a new DeepSpace app (runs create-deepspace; all flags are forwarded)"
|
|
@@ -12947,19 +13558,20 @@ var create2 = defineCommand13({
|
|
|
12947
13558
|
}
|
|
12948
13559
|
});
|
|
12949
13560
|
var rename = (cmd, name) => ({ ...cmd, meta: { ...cmd.meta ?? {}, name } });
|
|
12950
|
-
var auth =
|
|
13561
|
+
var auth = defineCommand14({
|
|
12951
13562
|
meta: { name: "auth", description: "Session: login, logout, whoami" },
|
|
12952
13563
|
subCommands: { login: login_default, logout: logout_default, whoami: whoami_default }
|
|
12953
13564
|
});
|
|
12954
|
-
var app =
|
|
13565
|
+
var app = defineCommand14({
|
|
12955
13566
|
meta: {
|
|
12956
13567
|
name: "app",
|
|
12957
|
-
description: "The app itself: create, init, list, source, migrate, undeploy, transfer, collaborators, domain, usage"
|
|
13568
|
+
description: "The app itself: create, init, list, files, source, migrate, undeploy, transfer, collaborators, domain, usage"
|
|
12958
13569
|
},
|
|
12959
13570
|
subCommands: {
|
|
12960
13571
|
create: create2,
|
|
12961
13572
|
init: init_default,
|
|
12962
13573
|
list: rename(apps_default, "list"),
|
|
13574
|
+
files: app_files_default,
|
|
12963
13575
|
undeploy: undeploy_default,
|
|
12964
13576
|
transfer: transfer_default,
|
|
12965
13577
|
collaborators: collaborators_default,
|
|
@@ -12969,11 +13581,11 @@ var app = defineCommand13({
|
|
|
12969
13581
|
migrate: migrate_default
|
|
12970
13582
|
}
|
|
12971
13583
|
});
|
|
12972
|
-
var devGroup =
|
|
13584
|
+
var devGroup = defineCommand14({
|
|
12973
13585
|
meta: { name: "dev", description: "Local development" },
|
|
12974
13586
|
subCommands: { start: rename(dev_default, "start"), kill: kill_default }
|
|
12975
13587
|
});
|
|
12976
|
-
var testGroup =
|
|
13588
|
+
var testGroup = defineCommand14({
|
|
12977
13589
|
meta: { name: "test", description: "App tests and test tooling" },
|
|
12978
13590
|
subCommands: {
|
|
12979
13591
|
run: rename(test_default, "run"),
|
|
@@ -12981,7 +13593,7 @@ var testGroup = defineCommand13({
|
|
|
12981
13593
|
accounts: rename(test_accounts_default, "accounts")
|
|
12982
13594
|
}
|
|
12983
13595
|
});
|
|
12984
|
-
var main =
|
|
13596
|
+
var main = defineCommand14({
|
|
12985
13597
|
meta: {
|
|
12986
13598
|
name: "deepspace",
|
|
12987
13599
|
version: pkg.version,
|
|
@@ -13021,7 +13633,7 @@ function commandTree() {
|
|
|
13021
13633
|
return main.subCommands ?? {};
|
|
13022
13634
|
}
|
|
13023
13635
|
function unknownCommandMessage(unknown) {
|
|
13024
|
-
return `Unknown command: ${unknown.attemptedPath
|
|
13636
|
+
return `Unknown command: ${humanCommand(unknown.attemptedPath)} \u2014 did you mean \`${humanCommand(unknown.suggestion)}\`?` + (unknown.quotedToken ? " The whole command arrived as one quoted argument; pass each word separately." : "");
|
|
13025
13637
|
}
|
|
13026
13638
|
function assertKnownCommandPath(argv) {
|
|
13027
13639
|
const unknown = findUnknownCommand(argv, commandTree());
|
|
@@ -13030,7 +13642,7 @@ function assertKnownCommandPath(argv) {
|
|
|
13030
13642
|
`${unknownCommandMessage(unknown)}
|
|
13031
13643
|
Run \`${unknown.helpPath.join(" ")} --help\` for the command list.`
|
|
13032
13644
|
);
|
|
13033
|
-
printAction({ cwd: process.cwd(), argv: unknown.suggestion });
|
|
13645
|
+
if (unknown.executable) printAction({ cwd: process.cwd(), argv: unknown.suggestion });
|
|
13034
13646
|
process.exit(1);
|
|
13035
13647
|
}
|
|
13036
13648
|
var rawArgs = process.argv.slice(2);
|
|
@@ -13052,7 +13664,7 @@ if (rawArgs.includes("--json") && !wantsHelp) {
|
|
|
13052
13664
|
ok: false,
|
|
13053
13665
|
code: "unknown_command",
|
|
13054
13666
|
error: unknownCommandMessage(unknown),
|
|
13055
|
-
action: { cwd: process.cwd(), argv: unknown.suggestion }
|
|
13667
|
+
...unknown.executable ? { action: { cwd: process.cwd(), argv: unknown.suggestion } } : {}
|
|
13056
13668
|
})
|
|
13057
13669
|
);
|
|
13058
13670
|
process.exit(1);
|
|
@@ -13061,16 +13673,20 @@ if (rawArgs.includes("--json") && !wantsHelp) {
|
|
|
13061
13673
|
stopActiveSpinner();
|
|
13062
13674
|
const cittyCode = err instanceof Error && err.name === "CLIError" ? err.code : void 0;
|
|
13063
13675
|
const code = cittyCode === "EARG" ? "missing_argument" : cittyCode === "E_UNKNOWN_COMMAND" ? "unknown_command" : cittyCode === "E_NO_COMMAND" ? "no_command" : errorCode(err);
|
|
13064
|
-
const message = (err instanceof Error ? err.message : String(err))
|
|
13065
|
-
// eslint-disable-next-line no-control-regex
|
|
13066
|
-
/\u001b\[[0-9;]*m/g,
|
|
13067
|
-
""
|
|
13068
|
-
);
|
|
13676
|
+
const message = stripAnsi(err instanceof Error ? err.message : String(err));
|
|
13069
13677
|
console.log(JSON.stringify({ ok: false, error: message, ...code ? { code } : {} }));
|
|
13070
13678
|
process.exit(1);
|
|
13071
13679
|
});
|
|
13072
13680
|
} else {
|
|
13073
13681
|
assertKnownCommandPath(rawArgs);
|
|
13074
|
-
runMain(wrapCommandErrors(main)
|
|
13682
|
+
runMain(wrapCommandErrors(main), {
|
|
13683
|
+
showUsage: async (cmd, parent) => {
|
|
13684
|
+
try {
|
|
13685
|
+
console.log(forTerminal(await renderUsage(cmd, parent)) + "\n");
|
|
13686
|
+
} catch (error) {
|
|
13687
|
+
console.error(error);
|
|
13688
|
+
}
|
|
13689
|
+
}
|
|
13690
|
+
});
|
|
13075
13691
|
}
|
|
13076
13692
|
//# sourceMappingURL=cli.js.map
|