deepspace 0.24.0 → 0.24.1
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 +7 -0
- package/dist/{chunk-2U2VG6L5.js → chunk-6RQ4GDEH.js} +2 -2
- package/dist/{chunk-2U2VG6L5.js.map → chunk-6RQ4GDEH.js.map} +1 -1
- package/dist/cli.js +591 -257
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.js +1 -1
- package/dist/documentation-default-renderer.cjs +1 -1
- package/dist/documentation-react.js +1 -1
- package/dist/documentation-runtime.js +1 -1
- package/dist/documentation-server-core.js +1 -1
- package/dist/documentation.js +1 -1
- package/dist/documentation.js.map +1 -1
- package/dist/server.d.ts +15 -1
- package/dist/server.js +46 -0
- package/dist/server.js.map +1 -1
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +3 -0
- package/dist/testing.js.map +1 -1
- package/dist/worker.d.ts +15 -1
- package/dist/worker.js +46 -0
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-OFDOJGFS.js +0 -1957
- package/dist/chunk-OFDOJGFS.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
renderUsage
|
|
9
9
|
} from "citty";
|
|
10
10
|
import { sync as spawnSync8 } from "cross-spawn";
|
|
11
|
-
import { readFileSync as
|
|
11
|
+
import { readFileSync as readFileSync27, existsSync as existsSync29 } from "fs";
|
|
12
12
|
import { dirname as dirname13, join as join29 } from "path";
|
|
13
13
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14
14
|
|
|
@@ -133,6 +133,41 @@ function printAction(action) {
|
|
|
133
133
|
p2.log.message(`Next: ${prefix}${command}`);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// src/cli/lib/fetch-retry.ts
|
|
137
|
+
async function retryTransient(run2, {
|
|
138
|
+
delaysMs,
|
|
139
|
+
shouldRetryError = () => true,
|
|
140
|
+
shouldRetryResult = () => false
|
|
141
|
+
}) {
|
|
142
|
+
for (let attempt = 0; ; attempt++) {
|
|
143
|
+
try {
|
|
144
|
+
const result = await run2();
|
|
145
|
+
if (!shouldRetryResult(result) || attempt >= delaysMs.length) return result;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (!shouldRetryError(error) || attempt >= delaysMs.length) throw error;
|
|
148
|
+
}
|
|
149
|
+
await new Promise((resolve23) => setTimeout(resolve23, delaysMs[attempt]));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function fetchWithTransientRetry(url, makeInit, {
|
|
153
|
+
attempts = 4,
|
|
154
|
+
retryServerErrors = true,
|
|
155
|
+
timeoutMs = 6e4
|
|
156
|
+
} = {}) {
|
|
157
|
+
return retryTransient(
|
|
158
|
+
async () => {
|
|
159
|
+
return await fetch(url, { ...makeInit(), signal: AbortSignal.timeout(timeoutMs) });
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
delaysMs: Array.from(
|
|
163
|
+
{ length: Math.max(0, attempts - 1) },
|
|
164
|
+
(_, attempt) => Math.min(8e3, 500 * 2 ** attempt)
|
|
165
|
+
),
|
|
166
|
+
shouldRetryResult: (response) => retryServerErrors && (response.status >= 500 || response.status === 408 || response.status === 429)
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
136
171
|
// src/cli/lib/api.ts
|
|
137
172
|
var ApiError = class extends Error {
|
|
138
173
|
constructor(message, status3, code, apiPath, details) {
|
|
@@ -159,11 +194,6 @@ async function apiFetch(baseUrl, token, path, init, isRetry = false) {
|
|
|
159
194
|
...init?.headers ?? {}
|
|
160
195
|
}
|
|
161
196
|
});
|
|
162
|
-
if (res.status === 401 && !isRetry && refreshAuthToken) {
|
|
163
|
-
const fresh = await refreshAuthToken();
|
|
164
|
-
if (fresh && fresh !== token) return apiFetch(baseUrl, fresh, path, init, true);
|
|
165
|
-
}
|
|
166
|
-
text2 = await res.text();
|
|
167
197
|
} catch (err) {
|
|
168
198
|
throw new ApiError(
|
|
169
199
|
`Could not reach the deploy service at ${baseUrl}: ${err instanceof Error ? err.message : String(err)}. Check your connection (and DEEPSPACE_DEPLOY_URL), then retry.`,
|
|
@@ -172,6 +202,20 @@ async function apiFetch(baseUrl, token, path, init, isRetry = false) {
|
|
|
172
202
|
path
|
|
173
203
|
);
|
|
174
204
|
}
|
|
205
|
+
if (res.status === 401 && !isRetry && refreshAuthToken) {
|
|
206
|
+
const fresh = await refreshAuthToken();
|
|
207
|
+
if (fresh && fresh !== token) return apiFetch(baseUrl, fresh, path, init, true);
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
text2 = await res.text();
|
|
211
|
+
} catch (err) {
|
|
212
|
+
throw new ApiError(
|
|
213
|
+
`Could not read the deploy service response at ${baseUrl}: ${err instanceof Error ? err.message : String(err)}. Check your connection (and DEEPSPACE_DEPLOY_URL), then retry.`,
|
|
214
|
+
0,
|
|
215
|
+
"network_error",
|
|
216
|
+
path
|
|
217
|
+
);
|
|
218
|
+
}
|
|
175
219
|
if (!res.ok) {
|
|
176
220
|
let msg = text2;
|
|
177
221
|
let code;
|
|
@@ -217,6 +261,27 @@ async function apiFetch(baseUrl, token, path, init, isRetry = false) {
|
|
|
217
261
|
}
|
|
218
262
|
return parsed;
|
|
219
263
|
}
|
|
264
|
+
async function apiFetchWithTransientRetry(baseUrl, token, path, init) {
|
|
265
|
+
return retryTransient(
|
|
266
|
+
async () => await apiFetch(baseUrl, token, path, {
|
|
267
|
+
...init,
|
|
268
|
+
signal: AbortSignal.timeout(15e3)
|
|
269
|
+
}),
|
|
270
|
+
{
|
|
271
|
+
delaysMs: [250, 750, 1500],
|
|
272
|
+
// A 401 refresh owns its own bounded auth-service retry loop. Only retry
|
|
273
|
+
// failures produced by this deploy-service request, never multiply an
|
|
274
|
+
// exhausted /api/auth/token exchange by replaying the rejected bearer.
|
|
275
|
+
shouldRetryError: (error) => isTransientFailure(error) && error.apiPath === path
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
function apiFetchReadWithRetry(baseUrl, token, path) {
|
|
280
|
+
return apiFetchWithTransientRetry(baseUrl, token, path);
|
|
281
|
+
}
|
|
282
|
+
function isTransientFailure(error) {
|
|
283
|
+
return error instanceof ApiError && (error.status === 0 || error.status === 408 || error.status === 429 || error.status >= 500);
|
|
284
|
+
}
|
|
220
285
|
|
|
221
286
|
// src/cli/lib/cli-errors.ts
|
|
222
287
|
var InputError = class extends Error {
|
|
@@ -567,16 +632,57 @@ function decodeJwtPayload(token) {
|
|
|
567
632
|
// src/cli/session.ts
|
|
568
633
|
var SESSION_COOKIE = "__Secure-better-auth.session_token";
|
|
569
634
|
async function exchangeSession(authUrl, sessionToken) {
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
635
|
+
const path = "/api/auth/token";
|
|
636
|
+
let res;
|
|
637
|
+
try {
|
|
638
|
+
res = await fetchWithTransientRetry(
|
|
639
|
+
`${authUrl}${path}`,
|
|
640
|
+
() => ({
|
|
641
|
+
method: "POST",
|
|
642
|
+
headers: {
|
|
643
|
+
Cookie: `${SESSION_COOKIE}=${encodeURIComponent(sessionToken)}`,
|
|
644
|
+
Origin: authUrl
|
|
645
|
+
}
|
|
646
|
+
}),
|
|
647
|
+
{ timeoutMs: 15e3 }
|
|
648
|
+
);
|
|
649
|
+
} catch (error) {
|
|
650
|
+
throw new ApiError(
|
|
651
|
+
`Could not reach the auth service at ${authUrl}: ${error instanceof Error ? error.message : String(error)}. Check your connection, then retry.`,
|
|
652
|
+
0,
|
|
653
|
+
"network_error",
|
|
654
|
+
path
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
if (res.status === 401) return null;
|
|
658
|
+
if (!res.ok) {
|
|
659
|
+
throw new ApiError(
|
|
660
|
+
`The auth service could not refresh the CLI token (HTTP ${res.status}). Retry without logging in again.`,
|
|
661
|
+
res.status,
|
|
662
|
+
res.status === 429 ? "rate_limited" : "auth_service_unavailable",
|
|
663
|
+
path
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
let data;
|
|
667
|
+
try {
|
|
668
|
+
data = await res.json();
|
|
669
|
+
} catch {
|
|
670
|
+
throw new ApiError(
|
|
671
|
+
"The auth service returned a malformed token response.",
|
|
672
|
+
res.status,
|
|
673
|
+
"invalid_response",
|
|
674
|
+
path
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
if (!data.token) {
|
|
678
|
+
throw new ApiError(
|
|
679
|
+
"The auth service returned no token for a valid session.",
|
|
680
|
+
res.status,
|
|
681
|
+
"invalid_response",
|
|
682
|
+
path
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
return data.token;
|
|
580
686
|
}
|
|
581
687
|
|
|
582
688
|
// src/cli/lib/secure-file.ts
|
|
@@ -609,19 +715,18 @@ function credentialPaths(authUrl = AUTH_URL) {
|
|
|
609
715
|
return { sessionPath: join(DIR, `session${suffix}`), tokenPath: join(DIR, `token${suffix}`) };
|
|
610
716
|
}
|
|
611
717
|
var { sessionPath: SESSION_PATH, tokenPath: TOKEN_PATH } = credentialPaths();
|
|
612
|
-
async function ensureToken() {
|
|
718
|
+
async function ensureToken(options = {}) {
|
|
719
|
+
const minimumValidityMs = options.minimumValidityMs ?? 3e4;
|
|
613
720
|
if (existsSync(TOKEN_PATH)) {
|
|
614
721
|
const existing = readFileSync2(TOKEN_PATH, "utf-8").trim();
|
|
615
|
-
if (isTokenValid(existing)) {
|
|
722
|
+
if (isTokenValid(existing, minimumValidityMs)) {
|
|
616
723
|
return existing;
|
|
617
724
|
}
|
|
618
725
|
}
|
|
619
726
|
if (!existsSync(SESSION_PATH)) throw notAuthenticated("Not logged in");
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
mkdirSync(DIR, { recursive: true, mode: 448 });
|
|
624
|
-
writeSecretFileSync(TOKEN_PATH, token);
|
|
727
|
+
const token = await exchangeStoredSession();
|
|
728
|
+
if (!token)
|
|
729
|
+
throw notAuthenticated("The stored session is no longer valid (expired or unreadable)");
|
|
625
730
|
return token;
|
|
626
731
|
}
|
|
627
732
|
function notAuthenticated(state) {
|
|
@@ -638,30 +743,30 @@ function notAuthenticated(state) {
|
|
|
638
743
|
{ action: cliAction("deepspace", "auth", "login") }
|
|
639
744
|
);
|
|
640
745
|
}
|
|
641
|
-
function isTokenValid(jwt) {
|
|
746
|
+
function isTokenValid(jwt, minimumValidityMs) {
|
|
642
747
|
try {
|
|
643
748
|
const payload = decodeJwtPayload(jwt);
|
|
644
|
-
return typeof payload.exp === "number" && payload.exp * 1e3 > Date.now() +
|
|
749
|
+
return typeof payload.exp === "number" && payload.exp * 1e3 > Date.now() + minimumValidityMs;
|
|
645
750
|
} catch {
|
|
646
751
|
return false;
|
|
647
752
|
}
|
|
648
753
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
754
|
+
async function refreshTokenFromSession() {
|
|
755
|
+
return await exchangeStoredSession();
|
|
756
|
+
}
|
|
757
|
+
async function exchangeStoredSession() {
|
|
758
|
+
if (!existsSync(SESSION_PATH)) return null;
|
|
759
|
+
const sessionToken = readFileSync2(SESSION_PATH, "utf-8").trim();
|
|
760
|
+
const token = await exchangeSession(AUTH_URL, sessionToken);
|
|
761
|
+
if (!token) return null;
|
|
762
|
+
mkdirSync(DIR, { recursive: true, mode: 448 });
|
|
763
|
+
writeSecretFileSync(TOKEN_PATH, token);
|
|
764
|
+
return token;
|
|
765
|
+
}
|
|
766
|
+
registerAuthRefresh(refreshTokenFromSession);
|
|
662
767
|
|
|
663
768
|
// src/cli/commands/login.ts
|
|
664
|
-
import { mkdirSync as mkdirSync2
|
|
769
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
665
770
|
import { dirname as dirname2 } from "path";
|
|
666
771
|
import { randomBytes, createHash } from "crypto";
|
|
667
772
|
import * as p4 from "@clack/prompts";
|
|
@@ -675,6 +780,25 @@ function openBrowser(url) {
|
|
|
675
780
|
}).unref();
|
|
676
781
|
}
|
|
677
782
|
|
|
783
|
+
// src/cli/lib/stdio.ts
|
|
784
|
+
var MAX_STDIN_BYTES = 1024 * 1024;
|
|
785
|
+
async function readStreamText(stream, maxBytes) {
|
|
786
|
+
const chunks = [];
|
|
787
|
+
let total = 0;
|
|
788
|
+
for await (const chunk of stream) {
|
|
789
|
+
const bytes = typeof chunk === "string" ? Buffer.from(chunk, "utf-8") : Buffer.from(chunk);
|
|
790
|
+
total += bytes.byteLength;
|
|
791
|
+
if (maxBytes !== void 0 && total > maxBytes) {
|
|
792
|
+
throw new InputError(
|
|
793
|
+
`Input on stdin exceeds ${Math.floor(maxBytes / 1024)} KiB \u2014 refusing to buffer more.`,
|
|
794
|
+
"input_too_large"
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
chunks.push(bytes);
|
|
798
|
+
}
|
|
799
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
800
|
+
}
|
|
801
|
+
|
|
678
802
|
// src/cli/commands/login.ts
|
|
679
803
|
var AUTH_URL2 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
680
804
|
var API_URL = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
@@ -708,7 +832,7 @@ var login_default = defineDeepspaceCommand({
|
|
|
708
832
|
passwordArg: args.password,
|
|
709
833
|
envEmail: interactive ? void 0 : process.env.DEEPSPACE_EMAIL,
|
|
710
834
|
envPassword: interactive ? void 0 : process.env.DEEPSPACE_PASSWORD,
|
|
711
|
-
passwordStdin: args["password-stdin"] ? readPasswordFromStdin() : void 0
|
|
835
|
+
passwordStdin: args["password-stdin"] ? await readPasswordFromStdin() : void 0
|
|
712
836
|
});
|
|
713
837
|
const passwordIntent = Boolean(args["password-stdin"]) || args.password !== void 0 || password !== void 0;
|
|
714
838
|
const decision = loginModeDecision({ email, password, passwordIntent });
|
|
@@ -735,27 +859,7 @@ var login_default = defineDeepspaceCommand({
|
|
|
735
859
|
if (!json) s.start("Creating login session...");
|
|
736
860
|
const codeVerifier = base64url(randomBytes(32));
|
|
737
861
|
const codeChallenge = base64url(createHash("sha256").update(codeVerifier).digest());
|
|
738
|
-
const
|
|
739
|
-
method: "POST",
|
|
740
|
-
headers: { "Content-Type": "application/json" },
|
|
741
|
-
body: JSON.stringify({ code_challenge: codeChallenge })
|
|
742
|
-
});
|
|
743
|
-
if (!sessionRes.ok) {
|
|
744
|
-
throw new Refusal(
|
|
745
|
-
`Could not create login session (${sessionRes.status})`,
|
|
746
|
-
"login_session_failed",
|
|
747
|
-
{ action: cliAction("deepspace", "auth", "login") }
|
|
748
|
-
);
|
|
749
|
-
}
|
|
750
|
-
const sessionData = await sessionRes.json();
|
|
751
|
-
if (!sessionData.sessionId || !sessionData.loginUrl) {
|
|
752
|
-
throw new Refusal(
|
|
753
|
-
"Could not create login session \u2014 the auth server returned no session id or login url.",
|
|
754
|
-
"login_session_failed",
|
|
755
|
-
{ action: cliAction("deepspace", "auth", "login") }
|
|
756
|
-
);
|
|
757
|
-
}
|
|
758
|
-
const { sessionId, loginUrl } = sessionData;
|
|
862
|
+
const { sessionId, loginUrl } = await createCliLoginSession(codeChallenge);
|
|
759
863
|
if (!json) {
|
|
760
864
|
s.stop("Opening browser...");
|
|
761
865
|
p4.log.info(`If the browser doesn't open, visit:
|
|
@@ -816,14 +920,50 @@ function loginModeDecision(opts) {
|
|
|
816
920
|
message: "Non-interactive login needs a non-empty password: --password-stdin, $DEEPSPACE_PASSWORD, or --password."
|
|
817
921
|
};
|
|
818
922
|
}
|
|
819
|
-
function readPasswordFromStdin() {
|
|
923
|
+
async function readPasswordFromStdin() {
|
|
820
924
|
if (process.stdin.isTTY) {
|
|
821
925
|
throw new Refusal(
|
|
822
926
|
'--password-stdin expects the password piped on stdin, e.g. `printf %s "$PW" | deepspace auth login --email you@x.com --password-stdin`.',
|
|
823
927
|
"password_stdin_needs_pipe"
|
|
824
928
|
);
|
|
825
929
|
}
|
|
826
|
-
return
|
|
930
|
+
return (await readStreamText(process.stdin, MAX_STDIN_BYTES)).replace(/\r?\n$/, "");
|
|
931
|
+
}
|
|
932
|
+
async function createCliLoginSession(codeChallenge) {
|
|
933
|
+
let sessionRes;
|
|
934
|
+
try {
|
|
935
|
+
sessionRes = await fetchWithTransientRetry(
|
|
936
|
+
`${AUTH_URL2}/api/auth/cli/session`,
|
|
937
|
+
() => ({
|
|
938
|
+
method: "POST",
|
|
939
|
+
headers: { "Content-Type": "application/json" },
|
|
940
|
+
body: JSON.stringify({ code_challenge: codeChallenge })
|
|
941
|
+
}),
|
|
942
|
+
{ timeoutMs: 15e3 }
|
|
943
|
+
);
|
|
944
|
+
} catch (error) {
|
|
945
|
+
throw new Refusal(
|
|
946
|
+
`Could not reach the auth service to create a login session: ${error instanceof Error ? error.message : String(error)}`,
|
|
947
|
+
"login_session_unreachable",
|
|
948
|
+
{ action: cliAction("deepspace", "auth", "login") }
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
if (!sessionRes.ok) {
|
|
952
|
+
throw new Refusal(
|
|
953
|
+
`Could not create login session (${sessionRes.status})`,
|
|
954
|
+
"login_session_failed",
|
|
955
|
+
{ action: cliAction("deepspace", "auth", "login") }
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
const sessionData = await sessionRes.json().catch(() => ({}));
|
|
959
|
+
if (!sessionData.sessionId || !sessionData.loginUrl) {
|
|
960
|
+
throw new Refusal(
|
|
961
|
+
"Could not create login session \u2014 the auth server returned no session id or login url.",
|
|
962
|
+
"login_session_failed",
|
|
963
|
+
{ action: cliAction("deepspace", "auth", "login") }
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
return { sessionId: sessionData.sessionId, loginUrl: sessionData.loginUrl };
|
|
827
967
|
}
|
|
828
968
|
async function pollForCompletion(sessionId, codeVerifier) {
|
|
829
969
|
const maxAttempts = 120;
|
|
@@ -879,7 +1019,7 @@ async function doEmailLogin(email, password) {
|
|
|
879
1019
|
const body = await res.json().catch(() => ({}));
|
|
880
1020
|
throw new Refusal(
|
|
881
1021
|
body.message ?? `Authentication failed (${res.status})`,
|
|
882
|
-
res.status === 401 ? "invalid_credentials" : "login_failed"
|
|
1022
|
+
res.status === 401 ? "invalid_credentials" : res.status === 429 ? "rate_limited" : "login_failed"
|
|
883
1023
|
);
|
|
884
1024
|
}
|
|
885
1025
|
const setCookie = res.headers.get("set-cookie") ?? "";
|
|
@@ -890,7 +1030,10 @@ async function doEmailLogin(email, password) {
|
|
|
890
1030
|
const sessionToken = decodeURIComponent(cookieMatch[1]);
|
|
891
1031
|
const jwt = await exchangeSession(AUTH_URL2, sessionToken);
|
|
892
1032
|
if (!jwt) {
|
|
893
|
-
throw new Refusal(
|
|
1033
|
+
throw new Refusal(
|
|
1034
|
+
"Signed in, but the auth service issued no token for the session.",
|
|
1035
|
+
"login_failed"
|
|
1036
|
+
);
|
|
894
1037
|
}
|
|
895
1038
|
storeCredentials(sessionToken, jwt);
|
|
896
1039
|
await provisionProfile(jwt);
|
|
@@ -903,7 +1046,7 @@ async function doEmailLogin(email, password) {
|
|
|
903
1046
|
}
|
|
904
1047
|
|
|
905
1048
|
// src/cli/commands/logout.ts
|
|
906
|
-
import { existsSync as existsSync2, readFileSync as
|
|
1049
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, rmSync } from "fs";
|
|
907
1050
|
var AUTH_URL3 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
908
1051
|
var { sessionPath: SESSION_PATH2, tokenPath: TOKEN_PATH2 } = credentialPaths(AUTH_URL3);
|
|
909
1052
|
var logout_default = defineDeepspaceCommand({
|
|
@@ -912,7 +1055,7 @@ var logout_default = defineDeepspaceCommand({
|
|
|
912
1055
|
description: "Sign out and remove cached credentials"
|
|
913
1056
|
},
|
|
914
1057
|
async run({ args }) {
|
|
915
|
-
const sessionToken = existsSync2(SESSION_PATH2) ?
|
|
1058
|
+
const sessionToken = existsSync2(SESSION_PATH2) ? readFileSync3(SESSION_PATH2, "utf-8").trim() : null;
|
|
916
1059
|
if (sessionToken) {
|
|
917
1060
|
try {
|
|
918
1061
|
await fetch(`${AUTH_URL3}/api/auth/sign-out`, {
|
|
@@ -938,11 +1081,11 @@ var logout_default = defineDeepspaceCommand({
|
|
|
938
1081
|
});
|
|
939
1082
|
|
|
940
1083
|
// src/cli/lib/app-identity.ts
|
|
941
|
-
import { existsSync as existsSync4, readFileSync as
|
|
1084
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
942
1085
|
import { join as join3, resolve as resolve2 } from "path";
|
|
943
1086
|
|
|
944
1087
|
// src/cli/lib/wrangler-env.ts
|
|
945
|
-
import { existsSync as existsSync3, readFileSync as
|
|
1088
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
946
1089
|
import { join as join2 } from "path";
|
|
947
1090
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
948
1091
|
|
|
@@ -1152,7 +1295,7 @@ function causeDetail(err) {
|
|
|
1152
1295
|
function readWranglerConfigFile(path) {
|
|
1153
1296
|
let raw;
|
|
1154
1297
|
try {
|
|
1155
|
-
raw =
|
|
1298
|
+
raw = readFileSync4(path, "utf-8");
|
|
1156
1299
|
} catch (err) {
|
|
1157
1300
|
const missing = err?.code === "ENOENT";
|
|
1158
1301
|
throw new WranglerConfigError(
|
|
@@ -1237,7 +1380,7 @@ function writeAppId(cwd, appId, opts = {}) {
|
|
|
1237
1380
|
);
|
|
1238
1381
|
}
|
|
1239
1382
|
}
|
|
1240
|
-
let src =
|
|
1383
|
+
let src = readFileSync5(wranglerPath, "utf-8");
|
|
1241
1384
|
const header = opts.wranglerEnv ? `[env.${opts.wranglerEnv}.vars]` : "[vars]";
|
|
1242
1385
|
const line = `DEEPSPACE_APP_ID = "${appId}"`;
|
|
1243
1386
|
const headerRe = new RegExp(`^\\${header.replace(/\./g, "\\.")}[ \\t]*$`, "m");
|
|
@@ -1295,7 +1438,7 @@ function findChildApps(dir) {
|
|
|
1295
1438
|
// src/cli/lib/launch-config.ts
|
|
1296
1439
|
import {
|
|
1297
1440
|
existsSync as existsSync7,
|
|
1298
|
-
readFileSync as
|
|
1441
|
+
readFileSync as readFileSync7,
|
|
1299
1442
|
realpathSync as realpathSync2,
|
|
1300
1443
|
writeFileSync as writeFileSync4,
|
|
1301
1444
|
mkdirSync as mkdirSync4,
|
|
@@ -1305,7 +1448,7 @@ import {
|
|
|
1305
1448
|
import { join as join6, resolve as resolve5, dirname as dirname5, basename, isAbsolute as isAbsolute2, sep } from "path";
|
|
1306
1449
|
|
|
1307
1450
|
// src/cli/lib/git/repository.ts
|
|
1308
|
-
import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as
|
|
1451
|
+
import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
1309
1452
|
import { dirname as dirname4, join as join5, resolve as resolve4 } from "path";
|
|
1310
1453
|
|
|
1311
1454
|
// src/cli/lib/git/process.ts
|
|
@@ -1529,7 +1672,7 @@ function ensureLocalExclude(cwd, pattern) {
|
|
|
1529
1672
|
const excludePath = join5(commonDir, "info", "exclude");
|
|
1530
1673
|
let current = "";
|
|
1531
1674
|
try {
|
|
1532
|
-
current =
|
|
1675
|
+
current = readFileSync6(excludePath, "utf-8");
|
|
1533
1676
|
} catch {
|
|
1534
1677
|
}
|
|
1535
1678
|
if (current.split("\n").some((line) => line.trim() === pattern)) return;
|
|
@@ -1562,7 +1705,7 @@ function writeLaunchFile(launchPath, config) {
|
|
|
1562
1705
|
function readLaunchFile(launchPath) {
|
|
1563
1706
|
const empty = { version: "0.0.1", configurations: [] };
|
|
1564
1707
|
if (!existsSync7(launchPath)) return empty;
|
|
1565
|
-
const raw =
|
|
1708
|
+
const raw = readFileSync7(launchPath, "utf-8").trim();
|
|
1566
1709
|
if (!raw) return empty;
|
|
1567
1710
|
const parsed = JSON.parse(raw);
|
|
1568
1711
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.configurations)) return null;
|
|
@@ -1732,7 +1875,7 @@ function githubSourceRefusal(appId, repository) {
|
|
|
1732
1875
|
}
|
|
1733
1876
|
async function getAppSource(deployUrl, token, appId) {
|
|
1734
1877
|
try {
|
|
1735
|
-
const state = await
|
|
1878
|
+
const state = await apiFetchReadWithRetry(
|
|
1736
1879
|
deployUrl,
|
|
1737
1880
|
token,
|
|
1738
1881
|
`/api/apps/${encodeURIComponent(appId)}/source`
|
|
@@ -1890,7 +2033,7 @@ function liveAppUrl(app2) {
|
|
|
1890
2033
|
return app2.status === "active" && app2.deployedAt !== null ? app2.url : null;
|
|
1891
2034
|
}
|
|
1892
2035
|
async function listAppsPage(deployUrl, token) {
|
|
1893
|
-
const body = await
|
|
2036
|
+
const body = await apiFetchReadWithRetry(deployUrl, token, "/api/apps");
|
|
1894
2037
|
if (!Array.isArray(body.apps)) {
|
|
1895
2038
|
throw new InputError(
|
|
1896
2039
|
`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?).`,
|
|
@@ -2098,15 +2241,15 @@ async function writeDevVars(appDir, ownerId, callerJwt, wranglerEnv, opts = {})
|
|
|
2098
2241
|
}
|
|
2099
2242
|
|
|
2100
2243
|
// src/cli/lib/install-status.ts
|
|
2101
|
-
import { existsSync as existsSync9, readFileSync as
|
|
2244
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, realpathSync as realpathSync3 } from "fs";
|
|
2102
2245
|
import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
|
|
2103
2246
|
|
|
2104
2247
|
// src/cli/lib/package-manager.ts
|
|
2105
|
-
import { existsSync as existsSync8, readFileSync as
|
|
2248
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
2106
2249
|
import { join as join7 } from "path";
|
|
2107
2250
|
function detectPackageManager(appDir) {
|
|
2108
2251
|
try {
|
|
2109
|
-
const pkg2 = JSON.parse(
|
|
2252
|
+
const pkg2 = JSON.parse(readFileSync8(join7(appDir, "package.json"), "utf-8"));
|
|
2110
2253
|
const name = typeof pkg2.packageManager === "string" ? pkg2.packageManager.split("@")[0] : "";
|
|
2111
2254
|
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
|
|
2112
2255
|
} catch {
|
|
@@ -2144,7 +2287,7 @@ function ensureInstallReady(appDir) {
|
|
|
2144
2287
|
const donePath = join8(sentinelDir, "install.done");
|
|
2145
2288
|
const logPath = join8(sentinelDir, "install.log");
|
|
2146
2289
|
if (existsSync9(errPath)) {
|
|
2147
|
-
const detail =
|
|
2290
|
+
const detail = readFileSync9(errPath, "utf-8").trimEnd();
|
|
2148
2291
|
const log29 = existsSync9(logPath) ? ` Full log: ${logPath}.` : "";
|
|
2149
2292
|
throw new Refusal(
|
|
2150
2293
|
`Dependency install failed: ${detail}.${log29} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
@@ -2200,7 +2343,7 @@ function canonicalPath2(path) {
|
|
|
2200
2343
|
function installWorkerAlive(sentinelDir) {
|
|
2201
2344
|
const pidPath = join8(sentinelDir, "install.pid");
|
|
2202
2345
|
if (!existsSync9(pidPath)) return false;
|
|
2203
|
-
return processAlive(Number(
|
|
2346
|
+
return processAlive(Number(readFileSync9(pidPath, "utf-8").trim()));
|
|
2204
2347
|
}
|
|
2205
2348
|
function processAlive(pid) {
|
|
2206
2349
|
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
@@ -2281,9 +2424,12 @@ function removeMacosJunk(dir) {
|
|
|
2281
2424
|
}
|
|
2282
2425
|
|
|
2283
2426
|
// src/server/rooms/binding-manifest.ts
|
|
2427
|
+
var MAX_ENV_VAR_BYTES = 5 * 1024;
|
|
2284
2428
|
var AUTO_PROVISION_SENTINEL = "auto";
|
|
2285
2429
|
var VECTORIZE_METRICS = /* @__PURE__ */ new Set(["cosine", "euclidean", "dot-product"]);
|
|
2286
2430
|
var ALLOWED_BINDING_TYPES = /* @__PURE__ */ new Set([
|
|
2431
|
+
"plain_text",
|
|
2432
|
+
"json",
|
|
2287
2433
|
"vectorize",
|
|
2288
2434
|
"ai",
|
|
2289
2435
|
"r2_bucket",
|
|
@@ -2361,6 +2507,17 @@ function validateBindingManifest(manifest) {
|
|
|
2361
2507
|
}
|
|
2362
2508
|
function requiredFieldError(b) {
|
|
2363
2509
|
switch (b.type) {
|
|
2510
|
+
case "plain_text":
|
|
2511
|
+
if (typeof b.text !== "string") {
|
|
2512
|
+
return `plain_text binding "${b.name}" missing text (string)`;
|
|
2513
|
+
}
|
|
2514
|
+
return envVarSizeError(b.name, b.text);
|
|
2515
|
+
case "json": {
|
|
2516
|
+
if (!isBindingJsonValue(b.json)) {
|
|
2517
|
+
return `json binding "${b.name}" must contain a JSON-serializable value`;
|
|
2518
|
+
}
|
|
2519
|
+
return envVarSizeError(b.name, JSON.stringify(b.json));
|
|
2520
|
+
}
|
|
2364
2521
|
case "ai_search":
|
|
2365
2522
|
if (b.instance_name !== AUTO_PROVISION_SENTINEL) {
|
|
2366
2523
|
return `ai_search binding "${b.name}" must set instance_name = "auto"; managed instances cannot be selected directly`;
|
|
@@ -2435,6 +2592,18 @@ function bindingManifestFromOutputConfig(outputConfig) {
|
|
|
2435
2592
|
);
|
|
2436
2593
|
}
|
|
2437
2594
|
const out = [];
|
|
2595
|
+
const vars = isObject(outputConfig.vars) ? outputConfig.vars : null;
|
|
2596
|
+
for (const [name, value] of Object.entries(vars ?? {})) {
|
|
2597
|
+
if (name === "DEEPSPACE_APP_ID" || name === "APP_NAME") continue;
|
|
2598
|
+
if (typeof value === "string") {
|
|
2599
|
+
out.push({ type: "plain_text", name, text: value });
|
|
2600
|
+
continue;
|
|
2601
|
+
}
|
|
2602
|
+
if (!isBindingJsonValue(value)) {
|
|
2603
|
+
throw new Error(`Wrangler var "${name}" is not a JSON-serializable value`);
|
|
2604
|
+
}
|
|
2605
|
+
out.push({ type: "json", name, json: value });
|
|
2606
|
+
}
|
|
2438
2607
|
const aiBinding = pluckString(outputConfig.ai, "binding");
|
|
2439
2608
|
if (aiBinding) out.push({ type: "ai", name: aiBinding });
|
|
2440
2609
|
for (const v of asObjectArray(outputConfig.vectorize)) {
|
|
@@ -2517,6 +2686,25 @@ function bindingManifestFromOutputConfig(outputConfig) {
|
|
|
2517
2686
|
function isObject(v) {
|
|
2518
2687
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
2519
2688
|
}
|
|
2689
|
+
function isBindingJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
2690
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) {
|
|
2691
|
+
return true;
|
|
2692
|
+
}
|
|
2693
|
+
if (typeof value !== "object") return false;
|
|
2694
|
+
if (ancestors.has(value)) return false;
|
|
2695
|
+
if (!Array.isArray(value)) {
|
|
2696
|
+
const prototype = Object.getPrototypeOf(value);
|
|
2697
|
+
if (prototype !== Object.prototype && prototype !== null) return false;
|
|
2698
|
+
}
|
|
2699
|
+
ancestors.add(value);
|
|
2700
|
+
const valid = Array.isArray(value) ? value.every((entry) => isBindingJsonValue(entry, ancestors)) : Object.values(value).every((entry) => isBindingJsonValue(entry, ancestors));
|
|
2701
|
+
ancestors.delete(value);
|
|
2702
|
+
return valid;
|
|
2703
|
+
}
|
|
2704
|
+
function envVarSizeError(name, serializedValue) {
|
|
2705
|
+
const size = new TextEncoder().encode(serializedValue).byteLength;
|
|
2706
|
+
return size <= MAX_ENV_VAR_BYTES ? null : `Environment variable binding "${name}" is ${size} bytes; maximum is ${MAX_ENV_VAR_BYTES}`;
|
|
2707
|
+
}
|
|
2520
2708
|
function pluckString(v, key) {
|
|
2521
2709
|
if (!isObject(v)) return null;
|
|
2522
2710
|
const val = v[key];
|
|
@@ -2573,21 +2761,31 @@ function defaultConfigNameForEnv(wranglerEnv) {
|
|
|
2573
2761
|
function secretsApi(deployUrl, token, path, init) {
|
|
2574
2762
|
return apiFetch(deployUrl, token, `/api/secrets${path}`, init);
|
|
2575
2763
|
}
|
|
2764
|
+
function readSecretsApi(deployUrl, token, path) {
|
|
2765
|
+
return apiFetchReadWithRetry(deployUrl, token, `/api/secrets${path}`);
|
|
2766
|
+
}
|
|
2767
|
+
function retryingSecretsApi(deployUrl, token, path, init) {
|
|
2768
|
+
return apiFetchWithTransientRetry(deployUrl, token, `/api/secrets${path}`, init);
|
|
2769
|
+
}
|
|
2576
2770
|
function listSecrets(deployUrl, token, appId, configName) {
|
|
2577
|
-
return
|
|
2771
|
+
return readSecretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}`);
|
|
2578
2772
|
}
|
|
2579
2773
|
function fetchSecretsValues(deployUrl, token, appId, configName) {
|
|
2580
|
-
return
|
|
2774
|
+
return readSecretsApi(
|
|
2775
|
+
deployUrl,
|
|
2776
|
+
token,
|
|
2777
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/values`
|
|
2778
|
+
);
|
|
2581
2779
|
}
|
|
2582
2780
|
function getSecretPlain(deployUrl, token, appId, configName, key) {
|
|
2583
|
-
return
|
|
2781
|
+
return readSecretsApi(
|
|
2584
2782
|
deployUrl,
|
|
2585
2783
|
token,
|
|
2586
2784
|
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}?plain=true`
|
|
2587
2785
|
);
|
|
2588
2786
|
}
|
|
2589
2787
|
function setSecret(deployUrl, token, appId, configName, key, value) {
|
|
2590
|
-
return
|
|
2788
|
+
return retryingSecretsApi(
|
|
2591
2789
|
deployUrl,
|
|
2592
2790
|
token,
|
|
2593
2791
|
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}`,
|
|
@@ -2595,7 +2793,7 @@ function setSecret(deployUrl, token, appId, configName, key, value) {
|
|
|
2595
2793
|
);
|
|
2596
2794
|
}
|
|
2597
2795
|
function deleteSecret(deployUrl, token, appId, configName, key) {
|
|
2598
|
-
return
|
|
2796
|
+
return retryingSecretsApi(
|
|
2599
2797
|
deployUrl,
|
|
2600
2798
|
token,
|
|
2601
2799
|
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}`,
|
|
@@ -2603,7 +2801,7 @@ function deleteSecret(deployUrl, token, appId, configName, key) {
|
|
|
2603
2801
|
);
|
|
2604
2802
|
}
|
|
2605
2803
|
function uploadSecrets(deployUrl, token, appId, configName, secrets, replace) {
|
|
2606
|
-
return
|
|
2804
|
+
return retryingSecretsApi(
|
|
2607
2805
|
deployUrl,
|
|
2608
2806
|
token,
|
|
2609
2807
|
`/${appId}/configs/${encodeURIComponent(configName)}/secrets`,
|
|
@@ -2611,7 +2809,7 @@ function uploadSecrets(deployUrl, token, appId, configName, secrets, replace) {
|
|
|
2611
2809
|
);
|
|
2612
2810
|
}
|
|
2613
2811
|
function listConfigs(deployUrl, token, appId) {
|
|
2614
|
-
return
|
|
2812
|
+
return readSecretsApi(deployUrl, token, `/${appId}/configs`);
|
|
2615
2813
|
}
|
|
2616
2814
|
function createConfig(deployUrl, token, appId, configName, copyFrom) {
|
|
2617
2815
|
return secretsApi(deployUrl, token, `/${appId}/configs`, {
|
|
@@ -3146,7 +3344,7 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
3146
3344
|
|
|
3147
3345
|
// src/cli/commands/kill.ts
|
|
3148
3346
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3149
|
-
import { readFileSync as
|
|
3347
|
+
import { readFileSync as readFileSync10, readdirSync as readdirSync3, readlinkSync } from "fs";
|
|
3150
3348
|
import { setTimeout as wait } from "timers/promises";
|
|
3151
3349
|
var SIGTERM_GRACE_MS = 1500;
|
|
3152
3350
|
var IS_WIN = process.platform === "win32";
|
|
@@ -3297,7 +3495,7 @@ function listenerPidsViaProc(port) {
|
|
|
3297
3495
|
for (const f of ["/proc/net/tcp", "/proc/net/tcp6"]) {
|
|
3298
3496
|
let data;
|
|
3299
3497
|
try {
|
|
3300
|
-
data =
|
|
3498
|
+
data = readFileSync10(f, "utf-8");
|
|
3301
3499
|
} catch {
|
|
3302
3500
|
continue;
|
|
3303
3501
|
}
|
|
@@ -3477,10 +3675,10 @@ function ensurePlaywright(appDir) {
|
|
|
3477
3675
|
}
|
|
3478
3676
|
|
|
3479
3677
|
// src/cli/lib/test-account-service.ts
|
|
3480
|
-
import { readFileSync as
|
|
3678
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
3481
3679
|
|
|
3482
3680
|
// src/testing/accounts.ts
|
|
3483
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as
|
|
3681
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11 } from "fs";
|
|
3484
3682
|
import { homedir as homedir2 } from "os";
|
|
3485
3683
|
import { join as join12 } from "path";
|
|
3486
3684
|
var TEST_ACCOUNTS_PATH = join12(homedir2(), ".deepspace", "test-accounts.json");
|
|
@@ -3550,7 +3748,7 @@ function removeTestAccounts(ids, emails = [], scope = currentTestAccountScope())
|
|
|
3550
3748
|
function readTestAccountStore() {
|
|
3551
3749
|
if (!existsSync12(TEST_ACCOUNTS_PATH)) return { version: 2, scopes: {} };
|
|
3552
3750
|
try {
|
|
3553
|
-
const raw = JSON.parse(
|
|
3751
|
+
const raw = JSON.parse(readFileSync11(TEST_ACCOUNTS_PATH, "utf-8"));
|
|
3554
3752
|
if (Array.isArray(raw)) return { version: 2, scopes: {} };
|
|
3555
3753
|
if (!raw || typeof raw !== "object") {
|
|
3556
3754
|
return { version: 2, scopes: {} };
|
|
@@ -3581,7 +3779,7 @@ function saveTestAccountStore(store) {
|
|
|
3581
3779
|
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
3582
3780
|
var AUTH_URL4 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
3583
3781
|
function sessionCookie() {
|
|
3584
|
-
const token =
|
|
3782
|
+
const token = readFileSync12(SESSION_PATH, "utf-8").trim();
|
|
3585
3783
|
return `${SESSION_COOKIE2}=${encodeURIComponent(token)}`;
|
|
3586
3784
|
}
|
|
3587
3785
|
async function fetchRemoteTestAccounts() {
|
|
@@ -4516,8 +4714,8 @@ function describeFilesFailure(status3, body) {
|
|
|
4516
4714
|
import * as p6 from "@clack/prompts";
|
|
4517
4715
|
import { execSync as execSync2 } from "child_process";
|
|
4518
4716
|
import { createHash as createHash2 } from "crypto";
|
|
4519
|
-
import { existsSync as existsSync16, readFileSync as
|
|
4520
|
-
import { dirname as dirname7, join as join17, resolve as resolve13 } from "path";
|
|
4717
|
+
import { existsSync as existsSync16, readFileSync as readFileSync15, readdirSync as readdirSync6 } from "fs";
|
|
4718
|
+
import { dirname as dirname7, isAbsolute as isAbsolute3, join as join17, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
|
|
4521
4719
|
|
|
4522
4720
|
// src/build/app-id.ts
|
|
4523
4721
|
import { resolve as resolve11 } from "path";
|
|
@@ -4547,7 +4745,7 @@ function removeBuildDevVars(workerDir) {
|
|
|
4547
4745
|
}
|
|
4548
4746
|
|
|
4549
4747
|
// src/documentation/deploy.ts
|
|
4550
|
-
import { existsSync as existsSync14, readFileSync as
|
|
4748
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
|
|
4551
4749
|
import { join as join15 } from "path";
|
|
4552
4750
|
function readDocumentationDeployManifest(appDir, clientDir) {
|
|
4553
4751
|
if (!existsSync14(join15(appDir, "documentation.json"))) return void 0;
|
|
@@ -4557,11 +4755,11 @@ function readDocumentationDeployManifest(appDir, clientDir) {
|
|
|
4557
4755
|
"documentation.json exists but the build produced no documentation manifest. Ensure vite.config.ts includes deepSpaceDocumentation() from deepspace/documentation."
|
|
4558
4756
|
);
|
|
4559
4757
|
}
|
|
4560
|
-
return JSON.parse(
|
|
4758
|
+
return JSON.parse(readFileSync13(manifestPath, "utf8"));
|
|
4561
4759
|
}
|
|
4562
4760
|
|
|
4563
4761
|
// src/cli/commands/update/app-migrations.ts
|
|
4564
|
-
import { existsSync as existsSync15, readFileSync as
|
|
4762
|
+
import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
|
|
4565
4763
|
import { join as join16 } from "path";
|
|
4566
4764
|
|
|
4567
4765
|
// src/shared/protocol/app-migrations.ts
|
|
@@ -4640,7 +4838,7 @@ function readAppliedAppMigrations(appDir) {
|
|
|
4640
4838
|
if (!existsSync15(path)) return [];
|
|
4641
4839
|
let parsed;
|
|
4642
4840
|
try {
|
|
4643
|
-
parsed = JSON.parse(
|
|
4841
|
+
parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
4644
4842
|
} catch {
|
|
4645
4843
|
throw new Refusal(
|
|
4646
4844
|
`${APP_MIGRATIONS_MANIFEST} must contain valid JSON`,
|
|
@@ -4706,12 +4904,12 @@ async function buildDeployBundle(options) {
|
|
|
4706
4904
|
"build_output_missing"
|
|
4707
4905
|
);
|
|
4708
4906
|
}
|
|
4709
|
-
const deployConfig = JSON.parse(
|
|
4907
|
+
const deployConfig = JSON.parse(readFileSync15(deployConfigPath, "utf-8"));
|
|
4710
4908
|
const outputWranglerPath = resolve13(dirname7(deployConfigPath), deployConfig.configPath);
|
|
4711
4909
|
if (!existsSync16(outputWranglerPath)) {
|
|
4712
4910
|
output.die(`Output wrangler.json not found at ${outputWranglerPath}`, "build_output_missing");
|
|
4713
4911
|
}
|
|
4714
|
-
const outputConfig = JSON.parse(
|
|
4912
|
+
const outputConfig = JSON.parse(readFileSync15(outputWranglerPath, "utf-8"));
|
|
4715
4913
|
if (outputConfig.name && outputConfig.name !== appName) {
|
|
4716
4914
|
const envHint = envName ? ` (--env ${envName})` : "";
|
|
4717
4915
|
output.die(
|
|
@@ -4745,7 +4943,13 @@ async function buildDeployBundle(options) {
|
|
|
4745
4943
|
spinner2.stop(`Collected ${assets.length} assets`);
|
|
4746
4944
|
const appIdRefusal = clientAppIdRefusal(assets, appId, envName);
|
|
4747
4945
|
if (appIdRefusal) output.die(appIdRefusal.message, appIdRefusal.code);
|
|
4748
|
-
const
|
|
4946
|
+
const worker = (() => {
|
|
4947
|
+
try {
|
|
4948
|
+
return collectWorkerBundle(workerDir, workerBundlePath, clientDir);
|
|
4949
|
+
} catch (error) {
|
|
4950
|
+
return output.die(errorMessage(error), "build_output_invalid");
|
|
4951
|
+
}
|
|
4952
|
+
})();
|
|
4749
4953
|
const doBindings = outputConfig.durable_objects?.bindings;
|
|
4750
4954
|
const sqliteClasses = new Set(
|
|
4751
4955
|
outputConfig.migrations?.flatMap((migration) => migration.new_sqlite_classes ?? []) ?? []
|
|
@@ -4780,7 +4984,7 @@ async function buildDeployBundle(options) {
|
|
|
4780
4984
|
return {
|
|
4781
4985
|
assets,
|
|
4782
4986
|
assetConfig,
|
|
4783
|
-
|
|
4987
|
+
worker,
|
|
4784
4988
|
appMigrations,
|
|
4785
4989
|
doManifest,
|
|
4786
4990
|
customBindings,
|
|
@@ -4790,6 +4994,43 @@ async function buildDeployBundle(options) {
|
|
|
4790
4994
|
notFoundHandling: typeof outputConfig.assets?.not_found_handling === "string" ? outputConfig.assets.not_found_handling : null
|
|
4791
4995
|
};
|
|
4792
4996
|
}
|
|
4997
|
+
var WORKER_ES_MODULE = /\.(?:js|mjs)$/i;
|
|
4998
|
+
function collectWorkerBundle(workerDir, mainPath, clientDir) {
|
|
4999
|
+
const root = resolve13(workerDir);
|
|
5000
|
+
const mainAbsolute = resolve13(mainPath);
|
|
5001
|
+
const excludedClient = clientDir ? resolve13(clientDir) : null;
|
|
5002
|
+
const main2 = moduleName(root, mainAbsolute);
|
|
5003
|
+
const modules = [];
|
|
5004
|
+
const walk = (directory) => {
|
|
5005
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true }).sort(
|
|
5006
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
5007
|
+
)) {
|
|
5008
|
+
const fullPath = join17(directory, entry.name);
|
|
5009
|
+
if (excludedClient && isWithin(excludedClient, fullPath)) continue;
|
|
5010
|
+
if (entry.isDirectory()) {
|
|
5011
|
+
walk(fullPath);
|
|
5012
|
+
} else if (entry.isFile() && WORKER_ES_MODULE.test(entry.name)) {
|
|
5013
|
+
modules.push({ name: moduleName(root, fullPath), content: readFileSync15(fullPath, "utf-8") });
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
};
|
|
5017
|
+
walk(root);
|
|
5018
|
+
const mainIndex = modules.findIndex((module) => module.name === main2);
|
|
5019
|
+
if (mainIndex < 0) throw new Error(`Worker main module not found in generated output: ${main2}`);
|
|
5020
|
+
const [mainModule] = modules.splice(mainIndex, 1);
|
|
5021
|
+
return { main: main2, modules: [mainModule, ...modules] };
|
|
5022
|
+
}
|
|
5023
|
+
function moduleName(root, path) {
|
|
5024
|
+
const name = relative2(root, path);
|
|
5025
|
+
if (!name || isAbsolute3(name) || name === ".." || name.startsWith(`..${sep3}`)) {
|
|
5026
|
+
throw new Error(`Worker module is outside the generated output directory: ${path}`);
|
|
5027
|
+
}
|
|
5028
|
+
return name.split(sep3).join("/");
|
|
5029
|
+
}
|
|
5030
|
+
function isWithin(root, path) {
|
|
5031
|
+
const name = relative2(root, path);
|
|
5032
|
+
return name === "" || !isAbsolute3(name) && name !== ".." && !name.startsWith(`..${sep3}`);
|
|
5033
|
+
}
|
|
4793
5034
|
function extractRunWorkerFirst(config) {
|
|
4794
5035
|
const raw = Array.isArray(config.assets?.run_worker_first) ? config.assets.run_worker_first : [];
|
|
4795
5036
|
const routes = [];
|
|
@@ -4821,7 +5062,7 @@ function clientAppIdRefusal(assets, appId, envName) {
|
|
|
4821
5062
|
let unsubstituted;
|
|
4822
5063
|
for (const asset of assets) {
|
|
4823
5064
|
if (!CLIENT_CODE_ASSET.test(asset.path)) continue;
|
|
4824
|
-
const source =
|
|
5065
|
+
const source = readFileSync15(asset.sourcePath, "utf-8");
|
|
4825
5066
|
if (unsubstituted === void 0 && source.includes(APP_ID_DEFINE)) unsubstituted = asset.path;
|
|
4826
5067
|
for (const found of source.matchAll(APP_ID_LITERAL)) {
|
|
4827
5068
|
if (found[0] !== appId && !foreign.has(found[0])) foreign.set(found[0], asset.path);
|
|
@@ -4855,7 +5096,7 @@ function collectAssets(dir) {
|
|
|
4855
5096
|
if (entry.isDirectory()) {
|
|
4856
5097
|
walk(fullPath, relativePath);
|
|
4857
5098
|
} else {
|
|
4858
|
-
const bytes =
|
|
5099
|
+
const bytes = readFileSync15(fullPath);
|
|
4859
5100
|
assets.push({
|
|
4860
5101
|
path: `/${relativePath}`,
|
|
4861
5102
|
hash: createHash2("sha256").update(bytes).digest("hex"),
|
|
@@ -4876,7 +5117,7 @@ function readDeployAssetConfig(clientDir) {
|
|
|
4876
5117
|
]) {
|
|
4877
5118
|
const filePath = join17(clientDir, file);
|
|
4878
5119
|
if (!existsSync16(filePath)) continue;
|
|
4879
|
-
const content =
|
|
5120
|
+
const content = readFileSync15(filePath, "utf8");
|
|
4880
5121
|
if (content.trim()) config[key] = content;
|
|
4881
5122
|
}
|
|
4882
5123
|
return config;
|
|
@@ -5201,7 +5442,7 @@ function createDeployOutput(json) {
|
|
|
5201
5442
|
|
|
5202
5443
|
// src/cli/commands/deploy/request.ts
|
|
5203
5444
|
import * as p10 from "@clack/prompts";
|
|
5204
|
-
import { readFileSync as
|
|
5445
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
5205
5446
|
|
|
5206
5447
|
// src/cli/commands/deploy/repository.ts
|
|
5207
5448
|
import * as p9 from "@clack/prompts";
|
|
@@ -5293,7 +5534,7 @@ function committedSecretRefusal(cwd, commitOid, options) {
|
|
|
5293
5534
|
// src/cli/lib/vc-remote.ts
|
|
5294
5535
|
import { existsSync as existsSync18 } from "fs";
|
|
5295
5536
|
import { tmpdir } from "os";
|
|
5296
|
-
import { isAbsolute as
|
|
5537
|
+
import { isAbsolute as isAbsolute4, relative as relative3, resolve as resolve14 } from "path";
|
|
5297
5538
|
function spaceRemoteName(environment = DEEPSPACE_ENV) {
|
|
5298
5539
|
if (environment === "staging") return "space-staging";
|
|
5299
5540
|
if (environment === "production") return "space";
|
|
@@ -5339,8 +5580,8 @@ function entryIsTransient(cwd) {
|
|
|
5339
5580
|
const entry = process.argv[1];
|
|
5340
5581
|
if (!entry) return true;
|
|
5341
5582
|
if (/[\\/]_npx[\\/]/.test(entry) || entry.startsWith(tmpdir())) return true;
|
|
5342
|
-
const rel =
|
|
5343
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
5583
|
+
const rel = relative3(resolve14(cwd), resolve14(entry));
|
|
5584
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute4(rel);
|
|
5344
5585
|
}
|
|
5345
5586
|
function helperEntryUsable(command) {
|
|
5346
5587
|
const match = /^!(?:DEEPSPACE_ENV=(production|staging) )?'([^']*)' '([^']*)' git-credential$/.exec(command);
|
|
@@ -5891,6 +6132,7 @@ function detachedHeadRefusal() {
|
|
|
5891
6132
|
}
|
|
5892
6133
|
|
|
5893
6134
|
// src/cli/commands/deploy/request.ts
|
|
6135
|
+
var postWithRetry = fetchWithTransientRetry;
|
|
5894
6136
|
var STALE_DISPLAY_NAME_LOCATIONS = [
|
|
5895
6137
|
"src/constants.ts:APP_NAME",
|
|
5896
6138
|
"wrangler.toml:[vars].APP_NAME"
|
|
@@ -5905,7 +6147,36 @@ function renamePromptMessage(rename2) {
|
|
|
5905
6147
|
var CLOUDFLARE_DEPLOY_ERROR_HINT = "Deployment reached the DeepSpace deploy worker, but Cloudflare's deployment control-plane API failed. This is often caused by a Cloudflare Dashboard/API incident or Workers for Platforms entitlement outage. Already deployed apps should keep serving; wait for Cloudflare to recover and retry.";
|
|
5906
6148
|
var DEPLOY_SERVICE_LIMIT_HINT = "The DeepSpace deploy service hit a resource limit processing this deploy (the deploy worker was terminated before it could answer). This is a DeepSpace-side failure, not a Cloudflare incident \u2014 your app was not changed. Retry; if it keeps happening, report the app id and asset count.";
|
|
5907
6149
|
var REQUIRED_ASSET_TRANSPORT = "content-addressed-v1";
|
|
6150
|
+
var REQUIRED_WORKER_MODULE_TRANSPORT = "multipart-v1";
|
|
5908
6151
|
var UPLOAD_CONCURRENCY = 16;
|
|
6152
|
+
var ASSET_UPLOAD_ATTEMPT_TIMEOUT_MS = 15e4;
|
|
6153
|
+
var ASSET_UPLOAD_ATTEMPTS = 2;
|
|
6154
|
+
function createDeployAuthSession(initialToken, refreshToken) {
|
|
6155
|
+
let currentToken = initialToken;
|
|
6156
|
+
let refreshInFlight = null;
|
|
6157
|
+
const recover2 = async (rejectedToken) => {
|
|
6158
|
+
if (currentToken !== rejectedToken) return currentToken;
|
|
6159
|
+
if (!refreshToken) return null;
|
|
6160
|
+
if (!refreshInFlight) {
|
|
6161
|
+
refreshInFlight = refreshToken().then((freshToken) => {
|
|
6162
|
+
if (freshToken && freshToken !== currentToken) currentToken = freshToken;
|
|
6163
|
+
return freshToken;
|
|
6164
|
+
}).finally(() => {
|
|
6165
|
+
refreshInFlight = null;
|
|
6166
|
+
});
|
|
6167
|
+
}
|
|
6168
|
+
return await refreshInFlight;
|
|
6169
|
+
};
|
|
6170
|
+
return {
|
|
6171
|
+
async request(run2) {
|
|
6172
|
+
const rejectedToken = currentToken;
|
|
6173
|
+
const response = await run2(rejectedToken);
|
|
6174
|
+
if (response.status !== 401) return response;
|
|
6175
|
+
const freshToken = await recover2(rejectedToken);
|
|
6176
|
+
return freshToken && freshToken !== rejectedToken ? await run2(freshToken) : response;
|
|
6177
|
+
}
|
|
6178
|
+
};
|
|
6179
|
+
}
|
|
5909
6180
|
async function deployBuiltBundle(options) {
|
|
5910
6181
|
const {
|
|
5911
6182
|
deployUrl,
|
|
@@ -5913,6 +6184,7 @@ async function deployBuiltBundle(options) {
|
|
|
5913
6184
|
appId,
|
|
5914
6185
|
appName,
|
|
5915
6186
|
token,
|
|
6187
|
+
refreshToken,
|
|
5916
6188
|
claimReleased,
|
|
5917
6189
|
ignoreStale,
|
|
5918
6190
|
bundle,
|
|
@@ -5921,14 +6193,18 @@ async function deployBuiltBundle(options) {
|
|
|
5921
6193
|
output,
|
|
5922
6194
|
spinner: spinner2
|
|
5923
6195
|
} = options;
|
|
6196
|
+
const auth2 = createDeployAuthSession(token, refreshToken);
|
|
5924
6197
|
spinner2.start(`Deploying ${appName}...`);
|
|
5925
6198
|
if (bundle.extraRoutes === true) {
|
|
5926
6199
|
p10.log.info("Worker-first routing: all requests");
|
|
5927
6200
|
} else if (bundle.extraRoutes.length) {
|
|
5928
6201
|
p10.log.info(`Custom worker-first routes: ${bundle.extraRoutes.join(", ")}`);
|
|
5929
6202
|
}
|
|
5930
|
-
await
|
|
5931
|
-
await
|
|
6203
|
+
await requireDeployCapabilities(deployUrl, bundle.worker.modules.length > 1, spinner2, output);
|
|
6204
|
+
await uploadDeployAssetsWithAuth(
|
|
6205
|
+
{ deployUrl, appId, assets: bundle.assets, output, spinner: spinner2 },
|
|
6206
|
+
auth2
|
|
6207
|
+
);
|
|
5932
6208
|
spinner2.message(`Deploying ${appName}...`);
|
|
5933
6209
|
let confirmRename = options.rename;
|
|
5934
6210
|
const bail = async (message, stopLabel = "Deploy failed", code, actionRequired = false, rawAction) => {
|
|
@@ -5949,11 +6225,27 @@ async function deployBuiltBundle(options) {
|
|
|
5949
6225
|
};
|
|
5950
6226
|
const makeForm = () => {
|
|
5951
6227
|
const form = new FormData();
|
|
6228
|
+
const main2 = bundle.worker.modules.find((module) => module.name === bundle.worker.main);
|
|
6229
|
+
if (!main2) return output.die("Built Worker bundle has no main module.", "build_output_invalid");
|
|
5952
6230
|
form.append(
|
|
5953
6231
|
"worker",
|
|
5954
|
-
new Blob([
|
|
5955
|
-
|
|
5956
|
-
);
|
|
6232
|
+
new Blob([main2.content], { type: "application/javascript+module" }),
|
|
6233
|
+
main2.name
|
|
6234
|
+
);
|
|
6235
|
+
form.append("workerMain", bundle.worker.main);
|
|
6236
|
+
const additionalModules = bundle.worker.modules.filter(
|
|
6237
|
+
(module) => module.name !== bundle.worker.main
|
|
6238
|
+
);
|
|
6239
|
+
if (additionalModules.length) {
|
|
6240
|
+
form.append("workerModules", JSON.stringify(additionalModules.map((module) => module.name)));
|
|
6241
|
+
for (const module of additionalModules) {
|
|
6242
|
+
form.append(
|
|
6243
|
+
"workerModule",
|
|
6244
|
+
new Blob([module.content], { type: "application/javascript+module" }),
|
|
6245
|
+
module.name
|
|
6246
|
+
);
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
5957
6249
|
form.append("assetManifest", JSON.stringify(assetManifest(bundle.assets)));
|
|
5958
6250
|
form.append("appMigrations", JSON.stringify(bundle.appMigrations));
|
|
5959
6251
|
if (bundle.doManifest) form.append("doManifest", JSON.stringify(bundle.doManifest));
|
|
@@ -5997,14 +6289,16 @@ async function deployBuiltBundle(options) {
|
|
|
5997
6289
|
let commitStarted = Date.now();
|
|
5998
6290
|
const postCommit = async () => {
|
|
5999
6291
|
commitStarted = Date.now();
|
|
6000
|
-
return
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6292
|
+
return auth2.request(
|
|
6293
|
+
(bearer) => postWithRetry(
|
|
6294
|
+
`${deployUrl}/api/deploy/${appId}`,
|
|
6295
|
+
() => ({
|
|
6296
|
+
method: "POST",
|
|
6297
|
+
headers: { Authorization: `Bearer ${bearer}` },
|
|
6298
|
+
body: makeForm()
|
|
6299
|
+
}),
|
|
6300
|
+
{ retryServerErrors: false, attempts: 2, timeoutMs: 24e4 }
|
|
6301
|
+
)
|
|
6008
6302
|
);
|
|
6009
6303
|
};
|
|
6010
6304
|
let response;
|
|
@@ -6012,9 +6306,9 @@ async function deployBuiltBundle(options) {
|
|
|
6012
6306
|
response = await postCommit();
|
|
6013
6307
|
} catch (error) {
|
|
6014
6308
|
await bail(
|
|
6015
|
-
`Deploy request failed: ${errorMessage4(error)}`,
|
|
6309
|
+
error instanceof ApiError ? errorMessage4(error) : `Deploy request failed: ${errorMessage4(error)}`,
|
|
6016
6310
|
"Deploy failed",
|
|
6017
|
-
"deploy_request_failed"
|
|
6311
|
+
error instanceof ApiError ? error.code : "deploy_request_failed"
|
|
6018
6312
|
);
|
|
6019
6313
|
throw error;
|
|
6020
6314
|
}
|
|
@@ -6075,13 +6369,15 @@ async function deployBuiltBundle(options) {
|
|
|
6075
6369
|
function assetManifest(assets) {
|
|
6076
6370
|
return assets.map((asset) => ({ path: asset.path, hash: asset.hash, size: asset.size }));
|
|
6077
6371
|
}
|
|
6078
|
-
async function
|
|
6079
|
-
let
|
|
6372
|
+
async function requireDeployCapabilities(deployUrl, needsWorkerModules, spinner2, output) {
|
|
6373
|
+
let capabilities;
|
|
6080
6374
|
try {
|
|
6081
|
-
const response = await
|
|
6375
|
+
const response = await postWithRetry(`${deployUrl}/api/health`, () => ({}), {
|
|
6376
|
+
timeoutMs: 15e3
|
|
6377
|
+
});
|
|
6082
6378
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
6083
6379
|
const body = await response.json();
|
|
6084
|
-
|
|
6380
|
+
capabilities = body.capabilities;
|
|
6085
6381
|
} catch (error) {
|
|
6086
6382
|
spinner2.stop("Deploy failed");
|
|
6087
6383
|
output.die(
|
|
@@ -6089,30 +6385,42 @@ async function requireAssetTransport(deployUrl, spinner2, output) {
|
|
|
6089
6385
|
"deploy_service_unreachable"
|
|
6090
6386
|
);
|
|
6091
6387
|
}
|
|
6092
|
-
if (
|
|
6388
|
+
if (capabilities?.assetTransport !== REQUIRED_ASSET_TRANSPORT) {
|
|
6093
6389
|
spinner2.stop("Deploy failed");
|
|
6094
6390
|
output.die(
|
|
6095
|
-
`The DeepSpace deploy service at ${deployUrl} does not support this CLI's asset transport (needs "${REQUIRED_ASSET_TRANSPORT}", found ${
|
|
6391
|
+
`The DeepSpace deploy service at ${deployUrl} does not support this CLI's asset transport (needs "${REQUIRED_ASSET_TRANSPORT}", found ${capabilities?.assetTransport === void 0 ? "none" : `"${String(capabilities.assetTransport)}"`}). Wait for the platform to finish updating, or pin an older deepspace CLI.`,
|
|
6096
6392
|
"asset_transport_unsupported"
|
|
6097
6393
|
);
|
|
6098
6394
|
}
|
|
6395
|
+
if (needsWorkerModules && capabilities?.workerModules !== REQUIRED_WORKER_MODULE_TRANSPORT) {
|
|
6396
|
+
spinner2.stop("Deploy failed");
|
|
6397
|
+
output.die(
|
|
6398
|
+
`This build contains multiple Worker modules, but the DeepSpace deploy service at ${deployUrl} does not support their transport (needs "${REQUIRED_WORKER_MODULE_TRANSPORT}", found ${capabilities?.workerModules === void 0 ? "none" : `"${String(capabilities.workerModules)}"`}). Wait for the platform to finish updating before retrying this deploy.`,
|
|
6399
|
+
"worker_module_transport_unsupported"
|
|
6400
|
+
);
|
|
6401
|
+
}
|
|
6099
6402
|
}
|
|
6100
|
-
async function
|
|
6101
|
-
const { deployUrl, appId,
|
|
6403
|
+
async function uploadDeployAssetsWithAuth(options, auth2) {
|
|
6404
|
+
const { deployUrl, appId, assets, spinner: spinner2 } = options;
|
|
6102
6405
|
const output = options.output;
|
|
6103
6406
|
const byHash = new Map(assets.map((asset) => [asset.hash, asset]));
|
|
6104
6407
|
spinner2.message("Checking which assets are already uploaded...");
|
|
6105
6408
|
let planResponse;
|
|
6106
6409
|
try {
|
|
6107
|
-
planResponse = await
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6410
|
+
planResponse = await auth2.request(
|
|
6411
|
+
(bearer) => postWithRetry(`${deployUrl}/api/deploy/${appId}/asset-plan`, () => ({
|
|
6412
|
+
method: "POST",
|
|
6413
|
+
headers: { Authorization: `Bearer ${bearer}`, "Content-Type": "application/json" },
|
|
6414
|
+
body: JSON.stringify({
|
|
6415
|
+
assets: [...byHash.values()].map((asset) => ({ hash: asset.hash, size: asset.size }))
|
|
6416
|
+
})
|
|
6417
|
+
}))
|
|
6418
|
+
);
|
|
6114
6419
|
} catch (error) {
|
|
6115
6420
|
spinner2.stop("Deploy failed");
|
|
6421
|
+
if (error instanceof ApiError) {
|
|
6422
|
+
output.die(errorMessage4(error), error.code ?? "upload_failed");
|
|
6423
|
+
}
|
|
6116
6424
|
output.die(`Asset plan failed (network): ${errorMessage4(error)}`, "upload_failed");
|
|
6117
6425
|
}
|
|
6118
6426
|
const planBody = await planResponse.json().catch(() => ({}));
|
|
@@ -6148,20 +6456,23 @@ async function uploadDeployAssets(options) {
|
|
|
6148
6456
|
const next = () => missing.pop();
|
|
6149
6457
|
const worker = async () => {
|
|
6150
6458
|
for (let asset = next(); asset; asset = next()) {
|
|
6151
|
-
const response = await
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6459
|
+
const response = await auth2.request(
|
|
6460
|
+
(bearer) => postWithRetry(
|
|
6461
|
+
`${deployUrl}/api/deploy/${appId}/assets/${asset.hash}`,
|
|
6462
|
+
() => ({
|
|
6463
|
+
method: "PUT",
|
|
6464
|
+
headers: {
|
|
6465
|
+
Authorization: `Bearer ${bearer}`,
|
|
6466
|
+
"Content-Type": "application/octet-stream"
|
|
6467
|
+
},
|
|
6468
|
+
// Read whole, per attempt. The platform matches Content-Length
|
|
6469
|
+
// against the plan before it touches storage, and a stream body
|
|
6470
|
+
// would be sent chunked with no length at all; a retry also needs a
|
|
6471
|
+
// body it can send again.
|
|
6472
|
+
body: readFileSync16(asset.sourcePath)
|
|
6473
|
+
}),
|
|
6474
|
+
{ attempts: ASSET_UPLOAD_ATTEMPTS, timeoutMs: ASSET_UPLOAD_ATTEMPT_TIMEOUT_MS }
|
|
6475
|
+
)
|
|
6165
6476
|
);
|
|
6166
6477
|
if (!response.ok) {
|
|
6167
6478
|
const body = await response.json().catch(() => ({}));
|
|
@@ -6178,14 +6489,15 @@ async function uploadDeployAssets(options) {
|
|
|
6178
6489
|
const uploadStarted = Date.now();
|
|
6179
6490
|
try {
|
|
6180
6491
|
await Promise.all(Array.from({ length: Math.min(UPLOAD_CONCURRENCY, missing.length) }, worker));
|
|
6181
|
-
p10.log.info(
|
|
6182
|
-
`Uploaded ${total} file(s) in ${Math.round((Date.now() - uploadStarted) / 1e3)}s`
|
|
6183
|
-
);
|
|
6492
|
+
p10.log.info(`Uploaded ${total} file(s) in ${Math.round((Date.now() - uploadStarted) / 1e3)}s`);
|
|
6184
6493
|
} catch (error) {
|
|
6185
6494
|
spinner2.stop("Deploy failed");
|
|
6186
6495
|
if (error instanceof AssetUploadError) {
|
|
6187
6496
|
output.die(formatDeployWorkerError(error.status, error.message, error.code), error.code);
|
|
6188
6497
|
}
|
|
6498
|
+
if (error instanceof ApiError) {
|
|
6499
|
+
output.die(errorMessage4(error), error.code ?? "upload_failed");
|
|
6500
|
+
}
|
|
6189
6501
|
output.die(`Asset upload failed (network): ${errorMessage4(error)}`, "upload_failed");
|
|
6190
6502
|
}
|
|
6191
6503
|
}
|
|
@@ -6224,29 +6536,6 @@ Underlying error: ${detail}`;
|
|
|
6224
6536
|
function errorMessage4(error) {
|
|
6225
6537
|
return error instanceof Error ? error.message : String(error);
|
|
6226
6538
|
}
|
|
6227
|
-
async function postWithRetry(url, makeInit, {
|
|
6228
|
-
attempts = 4,
|
|
6229
|
-
retryServerErrors = true,
|
|
6230
|
-
timeoutMs = 6e4
|
|
6231
|
-
} = {}) {
|
|
6232
|
-
let lastResponse;
|
|
6233
|
-
let lastError;
|
|
6234
|
-
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
6235
|
-
try {
|
|
6236
|
-
const response = await fetch(url, { ...makeInit(), signal: AbortSignal.timeout(timeoutMs) });
|
|
6237
|
-
const transient = retryServerErrors && (response.status >= 500 || response.status === 408 || response.status === 429);
|
|
6238
|
-
if (!transient) return response;
|
|
6239
|
-
lastResponse = response;
|
|
6240
|
-
} catch (error) {
|
|
6241
|
-
lastError = error;
|
|
6242
|
-
}
|
|
6243
|
-
if (attempt < attempts) {
|
|
6244
|
-
await new Promise((resolve23) => setTimeout(resolve23, Math.min(8e3, 500 * 2 ** (attempt - 1))));
|
|
6245
|
-
}
|
|
6246
|
-
}
|
|
6247
|
-
if (lastResponse) return lastResponse;
|
|
6248
|
-
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
6249
|
-
}
|
|
6250
6539
|
|
|
6251
6540
|
// src/cli/commands/deploy/secrets.ts
|
|
6252
6541
|
import * as p11 from "@clack/prompts";
|
|
@@ -6326,7 +6615,7 @@ function errorMessage5(error) {
|
|
|
6326
6615
|
|
|
6327
6616
|
// src/cli/commands/deploy/lock.ts
|
|
6328
6617
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6329
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
6618
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync17, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
6330
6619
|
import { dirname as dirname8, join as join19 } from "path";
|
|
6331
6620
|
function deployLockPath(appDir) {
|
|
6332
6621
|
return join19(appDir, ".deepspace", "deploy.lock");
|
|
@@ -6361,7 +6650,7 @@ function acquireDeployLock(appDir) {
|
|
|
6361
6650
|
}
|
|
6362
6651
|
function readLock(path) {
|
|
6363
6652
|
try {
|
|
6364
|
-
const parsed = JSON.parse(
|
|
6653
|
+
const parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
6365
6654
|
return typeof parsed.pid === "number" && typeof parsed.startedAt === "string" && typeof parsed.token === "string" ? { pid: parsed.pid, startedAt: parsed.startedAt, token: parsed.token } : null;
|
|
6366
6655
|
} catch {
|
|
6367
6656
|
return null;
|
|
@@ -6370,6 +6659,8 @@ function readLock(path) {
|
|
|
6370
6659
|
|
|
6371
6660
|
// src/cli/commands/deploy.ts
|
|
6372
6661
|
var DEPLOY_URL3 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
6662
|
+
var DEPLOY_TOKEN_MIN_VALIDITY_MS = 10 * 60 * 1e3;
|
|
6663
|
+
var POST_DEPLOY_TOKEN_MIN_VALIDITY_MS = 2 * 60 * 1e3;
|
|
6373
6664
|
var deploy_default = defineCommand3({
|
|
6374
6665
|
meta: {
|
|
6375
6666
|
name: "deploy",
|
|
@@ -6544,11 +6835,12 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
6544
6835
|
spinner2.stop("Deploy failed");
|
|
6545
6836
|
output.die(oversized, "assets_too_large");
|
|
6546
6837
|
}
|
|
6838
|
+
const { token: deployToken } = await authenticate(appDir, output, DEPLOY_TOKEN_MIN_VALIDITY_MS);
|
|
6547
6839
|
const repository = await syncDeployRepository({
|
|
6548
6840
|
deployUrl: DEPLOY_URL3,
|
|
6549
6841
|
appDir,
|
|
6550
6842
|
appId,
|
|
6551
|
-
token,
|
|
6843
|
+
token: deployToken,
|
|
6552
6844
|
push: args.push !== false,
|
|
6553
6845
|
ignoreStale: Boolean(args["ignore-stale"]),
|
|
6554
6846
|
output,
|
|
@@ -6565,7 +6857,8 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
6565
6857
|
appDir,
|
|
6566
6858
|
appId,
|
|
6567
6859
|
appName,
|
|
6568
|
-
token,
|
|
6860
|
+
token: deployToken,
|
|
6861
|
+
refreshToken: refreshTokenFromSession,
|
|
6569
6862
|
rename: confirmRename,
|
|
6570
6863
|
claimReleased: args["claim-released"] === true,
|
|
6571
6864
|
ignoreStale: Boolean(args["ignore-stale"]),
|
|
@@ -6597,7 +6890,7 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
6597
6890
|
serving === "unconfirmed" ? "Some requests still get the previous release. Cloudflare rolls a version out per edge machine; it usually settles within a couple of minutes. Wait before asserting against it." : "This release carries no serving stamp (older platform, or a resumed deploy), so the CLI cannot tell which release the edge answers with."
|
|
6598
6891
|
);
|
|
6599
6892
|
p12.log.info(`URL: ${body.url}`);
|
|
6600
|
-
await
|
|
6893
|
+
await syncPostDeployCommerce(appDir, appId, output.nonInteractive);
|
|
6601
6894
|
if (output.json) {
|
|
6602
6895
|
return output.emitJson({
|
|
6603
6896
|
ok: true,
|
|
@@ -6621,7 +6914,7 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
6621
6914
|
`Deployed! (edge confirmed in ${Math.round((Date.now() - edgeWaitStarted) / 1e3)}s)`
|
|
6622
6915
|
);
|
|
6623
6916
|
p12.log.success(`Live at: ${body.url}`);
|
|
6624
|
-
await
|
|
6917
|
+
await syncPostDeployCommerce(appDir, appId, output.nonInteractive);
|
|
6625
6918
|
if (output.json) {
|
|
6626
6919
|
return output.emitJson({
|
|
6627
6920
|
ok: true,
|
|
@@ -6645,6 +6938,14 @@ async function syncCommerce(appDir, appId, token, nonInteractive) {
|
|
|
6645
6938
|
await syncSubscriptionPlans(appDir, appId, token, nonInteractive);
|
|
6646
6939
|
await syncOneTimeProducts(appDir, appId, token);
|
|
6647
6940
|
}
|
|
6941
|
+
async function syncPostDeployCommerce(appDir, appId, nonInteractive) {
|
|
6942
|
+
try {
|
|
6943
|
+
const token = await ensureToken({ minimumValidityMs: POST_DEPLOY_TOKEN_MIN_VALIDITY_MS });
|
|
6944
|
+
await syncCommerce(appDir, appId, token, nonInteractive);
|
|
6945
|
+
} catch (error) {
|
|
6946
|
+
p12.log.warn(`Deployed successfully, but commerce sync was skipped: ${errorMessage6(error)}`);
|
|
6947
|
+
}
|
|
6948
|
+
}
|
|
6648
6949
|
function ownerJwtMissingRefusal(state) {
|
|
6649
6950
|
if (state.onBehalf?.ownerJwtLive !== false) return null;
|
|
6650
6951
|
return {
|
|
@@ -6691,11 +6992,12 @@ function forbiddenDeployMessage(appId, token) {
|
|
|
6691
6992
|
const grant = email ? `run \`deepspace app collaborators add ${email}\`` : "add your email as a collaborator";
|
|
6692
6993
|
return `${appId} belongs to another account \u2014 you are signed in as ${who}, who is neither its owner nor a collaborator. Ask the owner to ${grant} (collaborators can deploy), or log in as the owner; to publish this code as your OWN app instead, run \`deepspace app init --new-id\`.`;
|
|
6693
6994
|
}
|
|
6694
|
-
async function authenticate(appDir, output) {
|
|
6995
|
+
async function authenticate(appDir, output, minimumValidityMs) {
|
|
6695
6996
|
try {
|
|
6696
|
-
const token = await ensureToken();
|
|
6997
|
+
const token = await ensureToken({ minimumValidityMs });
|
|
6697
6998
|
return { token, ownerId: decodeJwtPayload(token).sub };
|
|
6698
6999
|
} catch (error) {
|
|
7000
|
+
if (errorCode(error) !== "not_authenticated") throw error;
|
|
6699
7001
|
output.die(errorMessage6(error), "not_authenticated", {
|
|
6700
7002
|
action: { cwd: appDir, argv: ["deepspace", "auth", "login"] }
|
|
6701
7003
|
});
|
|
@@ -7105,7 +7407,12 @@ var logs_default = defineCommand4({
|
|
|
7105
7407
|
throw new Refusal(
|
|
7106
7408
|
`${appId} has never been deployed, so it has no logs. Deploy it first (\`deepspace deploy\`), then read its logs.`,
|
|
7107
7409
|
"app_not_deployed",
|
|
7108
|
-
appDir ? {
|
|
7410
|
+
appDir ? {
|
|
7411
|
+
action: {
|
|
7412
|
+
cwd: appDir,
|
|
7413
|
+
argv: ["deepspace", "deploy", ...envArg ? ["--env", envArg] : []]
|
|
7414
|
+
}
|
|
7415
|
+
} : {}
|
|
7109
7416
|
);
|
|
7110
7417
|
}
|
|
7111
7418
|
const MAX_LIMIT = 500;
|
|
@@ -7115,12 +7422,8 @@ var logs_default = defineCommand4({
|
|
|
7115
7422
|
if (pageLimit !== void 0) params.set("limit", String(pageLimit));
|
|
7116
7423
|
if (args.level) params.set("level", args.level);
|
|
7117
7424
|
if (args.search) params.set("search", args.search);
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
token2,
|
|
7121
|
-
`/api/apps/${appId}/logs?${params}`,
|
|
7122
|
-
signal ? { signal } : void 0
|
|
7123
|
-
);
|
|
7425
|
+
const path = `/api/apps/${appId}/logs?${params}`;
|
|
7426
|
+
return signal ? apiFetch(DEPLOY_URL6, token2, path, { signal }) : apiFetchReadWithRetry(DEPLOY_URL6, token2, path);
|
|
7124
7427
|
};
|
|
7125
7428
|
const print = (events) => {
|
|
7126
7429
|
for (const e of events) {
|
|
@@ -7303,7 +7606,7 @@ var usage_default = defineDeepspaceCommand({
|
|
|
7303
7606
|
});
|
|
7304
7607
|
|
|
7305
7608
|
// src/cli/commands/add.ts
|
|
7306
|
-
import { existsSync as existsSync20, readFileSync as
|
|
7609
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, realpathSync as realpathSync4 } from "fs";
|
|
7307
7610
|
import { dirname as dirname10, join as join22, resolve as resolve17 } from "path";
|
|
7308
7611
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
7309
7612
|
|
|
@@ -7316,7 +7619,7 @@ import {
|
|
|
7316
7619
|
mkdirSync as mkdirSync7,
|
|
7317
7620
|
mkdtempSync,
|
|
7318
7621
|
openSync,
|
|
7319
|
-
readFileSync as
|
|
7622
|
+
readFileSync as readFileSync18,
|
|
7320
7623
|
readSync,
|
|
7321
7624
|
readdirSync as readdirSync7,
|
|
7322
7625
|
rmSync as rmSync5,
|
|
@@ -7324,7 +7627,7 @@ import {
|
|
|
7324
7627
|
} from "fs";
|
|
7325
7628
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
7326
7629
|
import { tmpdir as tmpdir2 } from "os";
|
|
7327
|
-
import { dirname as dirname9, isAbsolute as
|
|
7630
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join21, resolve as resolve16 } from "path";
|
|
7328
7631
|
import { fileURLToPath } from "url";
|
|
7329
7632
|
var NAV_MARKER = "// \u2500\u2500 Features add nav items below this line \u2500\u2500";
|
|
7330
7633
|
function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
@@ -7333,7 +7636,7 @@ function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
|
7333
7636
|
const packagePath = join21(dir, "package.json");
|
|
7334
7637
|
if (existsSync19(packagePath)) {
|
|
7335
7638
|
try {
|
|
7336
|
-
const pkg2 = JSON.parse(
|
|
7639
|
+
const pkg2 = JSON.parse(readFileSync18(packagePath, "utf-8"));
|
|
7337
7640
|
if (pkg2.name === "deepspace") return dir;
|
|
7338
7641
|
} catch {
|
|
7339
7642
|
}
|
|
@@ -7350,7 +7653,7 @@ function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
|
7350
7653
|
const configPath = join21(packageRoot, "features", id, "feature.json");
|
|
7351
7654
|
if (!existsSync19(configPath)) return null;
|
|
7352
7655
|
try {
|
|
7353
|
-
const config = JSON.parse(
|
|
7656
|
+
const config = JSON.parse(readFileSync18(configPath, "utf-8"));
|
|
7354
7657
|
if (typeof config.id !== "string" || typeof config.name !== "string" || !Array.isArray(config.files)) {
|
|
7355
7658
|
return null;
|
|
7356
7659
|
}
|
|
@@ -7399,7 +7702,7 @@ function suggestFeature(featureId, packageRoot = featurePackageRoot()) {
|
|
|
7399
7702
|
return closest !== null && closestDistance <= threshold ? closest : null;
|
|
7400
7703
|
}
|
|
7401
7704
|
function resolveTargetDir(raw) {
|
|
7402
|
-
return
|
|
7705
|
+
return isAbsolute5(raw) ? raw : resolve16(raw);
|
|
7403
7706
|
}
|
|
7404
7707
|
function resolveFeatureDestPath(fileDest, route) {
|
|
7405
7708
|
const pages = "src/pages/";
|
|
@@ -7415,7 +7718,7 @@ function copyFile(src, dest) {
|
|
|
7415
7718
|
function copyFileWithImportShift(src, dest, levels) {
|
|
7416
7719
|
mkdirSync7(dirname9(dest), { recursive: true });
|
|
7417
7720
|
const prefix = "../".repeat(levels);
|
|
7418
|
-
const shifted =
|
|
7721
|
+
const shifted = readFileSync18(src, "utf-8").replace(
|
|
7419
7722
|
/(from\s+|import\s+|import\()(['"])(\.\.\/)/g,
|
|
7420
7723
|
`$1$2${prefix}$3`
|
|
7421
7724
|
);
|
|
@@ -7474,7 +7777,7 @@ function integrateSchema(config, targetDir, emit) {
|
|
|
7474
7777
|
if (!existsSync19(schemasPath)) {
|
|
7475
7778
|
return [unresolvedSchema(config.schema, "src/schemas.ts was not found.", emit)];
|
|
7476
7779
|
}
|
|
7477
|
-
let content =
|
|
7780
|
+
let content = readFileSync18(schemasPath, "utf-8");
|
|
7478
7781
|
const { exportName, importPath, spreadOperator } = config.schema;
|
|
7479
7782
|
const importPattern = new RegExp(`^import\\s+\\{[^}]*\\b${exportName}\\b`, "m");
|
|
7480
7783
|
if (importPattern.test(content)) {
|
|
@@ -7531,7 +7834,7 @@ function integrateActions(config, targetDir, emit) {
|
|
|
7531
7834
|
if (!existsSync19(actionsPath)) {
|
|
7532
7835
|
return [unresolvedActions(config.actions, "src/actions/index.ts was not found.", emit)];
|
|
7533
7836
|
}
|
|
7534
|
-
let content =
|
|
7837
|
+
let content = readFileSync18(actionsPath, "utf-8");
|
|
7535
7838
|
const registryPattern = /export const actions:\s*Record<string,\s*ActionHandler(?:<[^>]*>)?>\s*=\s*\{/;
|
|
7536
7839
|
if (!registryPattern.test(content)) {
|
|
7537
7840
|
const reason = "src/actions/index.ts has an unexpected structure.";
|
|
@@ -7586,7 +7889,7 @@ function integrateCode(config, targetDir, emit) {
|
|
|
7586
7889
|
});
|
|
7587
7890
|
continue;
|
|
7588
7891
|
}
|
|
7589
|
-
const original =
|
|
7892
|
+
const original = readFileSync18(targetPath, "utf-8");
|
|
7590
7893
|
if (original.includes(insertion.insert.trim())) {
|
|
7591
7894
|
emit(` Integration already present: ${insertion.file}`);
|
|
7592
7895
|
continue;
|
|
@@ -7624,7 +7927,7 @@ ${content.slice(
|
|
|
7624
7927
|
function integrateIgnore(config, targetDir) {
|
|
7625
7928
|
if (!config.ignore?.length) return;
|
|
7626
7929
|
const targetPath = join21(targetDir, ".gitignore");
|
|
7627
|
-
const current = existsSync19(targetPath) ?
|
|
7930
|
+
const current = existsSync19(targetPath) ? readFileSync18(targetPath, "utf-8") : "";
|
|
7628
7931
|
const present = new Set(current.split(/\r?\n/).map((line) => line.trim()));
|
|
7629
7932
|
const missing = config.ignore.filter((pattern) => !present.has(pattern));
|
|
7630
7933
|
if (missing.length === 0) return;
|
|
@@ -7644,7 +7947,7 @@ function integrateWrangler(config, targetDir, emit) {
|
|
|
7644
7947
|
if (!existsSync19(targetPath)) {
|
|
7645
7948
|
return [{ area: "wrangler", reason: "wrangler.toml was not found.", steps }];
|
|
7646
7949
|
}
|
|
7647
|
-
let source =
|
|
7950
|
+
let source = readFileSync18(targetPath, "utf-8");
|
|
7648
7951
|
if (runWorkerFirst && !/^\[assets\]\s*$/m.test(source)) {
|
|
7649
7952
|
return [{ area: "wrangler", reason: "wrangler.toml has no [assets] section.", steps }];
|
|
7650
7953
|
}
|
|
@@ -7733,7 +8036,7 @@ function integrateCss(packageRoot, config, targetDir, emit) {
|
|
|
7733
8036
|
}
|
|
7734
8037
|
];
|
|
7735
8038
|
}
|
|
7736
|
-
let styles =
|
|
8039
|
+
let styles = readFileSync18(stylesPath, "utf-8");
|
|
7737
8040
|
let integrated = 0;
|
|
7738
8041
|
const unresolved = [];
|
|
7739
8042
|
for (const cssFile of config.css) {
|
|
@@ -7747,7 +8050,7 @@ function integrateCss(packageRoot, config, targetDir, emit) {
|
|
|
7747
8050
|
});
|
|
7748
8051
|
continue;
|
|
7749
8052
|
}
|
|
7750
|
-
const css =
|
|
8053
|
+
const css = readFileSync18(srcPath, "utf-8");
|
|
7751
8054
|
const fingerprint = css.match(/\.([\w-]+)\s*\{|@keyframes\s+([\w-]+)/);
|
|
7752
8055
|
const marker = fingerprint?.[1] ?? fingerprint?.[2];
|
|
7753
8056
|
if (marker && styles.includes(marker)) {
|
|
@@ -7785,7 +8088,7 @@ function integrateRoute(config, targetDir, emit) {
|
|
|
7785
8088
|
if (!existsSync19(navPath)) {
|
|
7786
8089
|
return [unresolvedRoute(config.route, config, "src/nav.ts was not found.", emit)];
|
|
7787
8090
|
}
|
|
7788
|
-
let content =
|
|
8091
|
+
let content = readFileSync18(navPath, "utf-8");
|
|
7789
8092
|
const routePath = config.route.path;
|
|
7790
8093
|
if (content.includes(`'${routePath}'`)) {
|
|
7791
8094
|
emit(` Nav already present: ${routePath}`);
|
|
@@ -7866,7 +8169,7 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
|
|
|
7866
8169
|
}
|
|
7867
8170
|
let pkg2;
|
|
7868
8171
|
try {
|
|
7869
|
-
pkg2 = JSON.parse(
|
|
8172
|
+
pkg2 = JSON.parse(readFileSync18(packagePath, "utf-8"));
|
|
7870
8173
|
} catch (error) {
|
|
7871
8174
|
emit(
|
|
7872
8175
|
` Could not patch package.json (parse error: ${error instanceof Error ? error.message : String(error)})`
|
|
@@ -7926,7 +8229,7 @@ ${child.diagnosticTail}` : "";
|
|
|
7926
8229
|
}
|
|
7927
8230
|
function dependencySnapshot(targetDir) {
|
|
7928
8231
|
try {
|
|
7929
|
-
const pkg2 = JSON.parse(
|
|
8232
|
+
const pkg2 = JSON.parse(readFileSync18(join21(targetDir, "package.json"), "utf-8"));
|
|
7930
8233
|
return {
|
|
7931
8234
|
dependencies: pkg2.dependencies ?? {},
|
|
7932
8235
|
devDependencies: pkg2.devDependencies ?? {}
|
|
@@ -8175,7 +8478,7 @@ function isDeepSpaceApp(dir) {
|
|
|
8175
8478
|
const packagePath = join22(dir, "package.json");
|
|
8176
8479
|
if (!existsSync20(packagePath)) return false;
|
|
8177
8480
|
try {
|
|
8178
|
-
const pkg2 = JSON.parse(
|
|
8481
|
+
const pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
|
|
8179
8482
|
return Boolean(pkg2.dependencies?.deepspace || pkg2.devDependencies?.deepspace);
|
|
8180
8483
|
} catch {
|
|
8181
8484
|
return false;
|
|
@@ -9455,7 +9758,7 @@ var transfer_default = defineCommand8({
|
|
|
9455
9758
|
import { defineCommand as defineCommand9 } from "citty";
|
|
9456
9759
|
|
|
9457
9760
|
// src/cli/commands/_invoke-impl.ts
|
|
9458
|
-
import { readFileSync as
|
|
9761
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
9459
9762
|
import * as p15 from "@clack/prompts";
|
|
9460
9763
|
var API_URL7 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
9461
9764
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -9488,7 +9791,7 @@ function parseTimeout(raw) {
|
|
|
9488
9791
|
}
|
|
9489
9792
|
return timeout;
|
|
9490
9793
|
}
|
|
9491
|
-
function resolveBody(opts) {
|
|
9794
|
+
async function resolveBody(opts) {
|
|
9492
9795
|
if (opts.body != null && opts.bodyFile != null) {
|
|
9493
9796
|
throw new Refusal("Pass either --body or --body-file, not both.", "conflicting_body_args");
|
|
9494
9797
|
}
|
|
@@ -9496,7 +9799,19 @@ function resolveBody(opts) {
|
|
|
9496
9799
|
if (opts.body != null) {
|
|
9497
9800
|
raw = opts.body;
|
|
9498
9801
|
} else if (opts.bodyFile != null) {
|
|
9499
|
-
|
|
9802
|
+
if (opts.bodyFile === "-" && process.stdin.isTTY) {
|
|
9803
|
+
throw new Refusal(
|
|
9804
|
+
'Reading the body from stdin ("-"), but stdin is a terminal \u2014 pipe JSON in, or pass --body / a file path.',
|
|
9805
|
+
"no_stdin"
|
|
9806
|
+
);
|
|
9807
|
+
}
|
|
9808
|
+
raw = opts.bodyFile === "-" ? await readStreamText(process.stdin) : readFileSync20(opts.bodyFile, "utf-8");
|
|
9809
|
+
if (opts.bodyFile === "-" && raw.trim() === "") {
|
|
9810
|
+
throw new Refusal(
|
|
9811
|
+
"Nothing arrived on stdin \u2014 check the command feeding this one, or pass --body / a file path.",
|
|
9812
|
+
"empty_input"
|
|
9813
|
+
);
|
|
9814
|
+
}
|
|
9500
9815
|
} else {
|
|
9501
9816
|
return "{}";
|
|
9502
9817
|
}
|
|
@@ -9642,7 +9957,7 @@ async function runInfo(args) {
|
|
|
9642
9957
|
}
|
|
9643
9958
|
async function runInvoke(args) {
|
|
9644
9959
|
const { integration, endpoint } = parseTarget(args.target);
|
|
9645
|
-
const body = resolveBody({ body: args.body, bodyFile: args.bodyFile });
|
|
9960
|
+
const body = await resolveBody({ body: args.body, bodyFile: args.bodyFile });
|
|
9646
9961
|
const timeoutMs = args.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
9647
9962
|
if (!args.yes) {
|
|
9648
9963
|
const info2 = requireEndpoint(await fetchCatalog({ summary: true }), integration, endpoint);
|
|
@@ -9822,7 +10137,7 @@ var integrations_default = defineCommand9({
|
|
|
9822
10137
|
|
|
9823
10138
|
// src/cli/commands/feedback.ts
|
|
9824
10139
|
import * as p16 from "@clack/prompts";
|
|
9825
|
-
import { existsSync as existsSync22, readFileSync as
|
|
10140
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21 } from "fs";
|
|
9826
10141
|
import { dirname as dirname11, join as join23 } from "path";
|
|
9827
10142
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9828
10143
|
import { release } from "os";
|
|
@@ -9851,7 +10166,7 @@ function readCliVersion() {
|
|
|
9851
10166
|
];
|
|
9852
10167
|
for (const path of candidates) {
|
|
9853
10168
|
if (!existsSync22(path)) continue;
|
|
9854
|
-
const pkg2 = JSON.parse(
|
|
10169
|
+
const pkg2 = JSON.parse(readFileSync21(path, "utf-8"));
|
|
9855
10170
|
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
9856
10171
|
}
|
|
9857
10172
|
} catch {
|
|
@@ -10047,7 +10362,7 @@ Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payl
|
|
|
10047
10362
|
|
|
10048
10363
|
// src/cli/commands/secrets.ts
|
|
10049
10364
|
import { defineCommand as defineCommand10 } from "citty";
|
|
10050
|
-
import { readFileSync as
|
|
10365
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
10051
10366
|
|
|
10052
10367
|
// src/cli/lib/citty-args.ts
|
|
10053
10368
|
function dedupePositionals(bound, rest) {
|
|
@@ -10261,20 +10576,25 @@ var del2 = defineCommand10({
|
|
|
10261
10576
|
for (const key of keys) {
|
|
10262
10577
|
const name = validateSecretName(key);
|
|
10263
10578
|
try {
|
|
10264
|
-
await deleteSecret(DEPLOY_URL11, t.token, t.appId, t.configName, name);
|
|
10265
|
-
deleted++;
|
|
10579
|
+
const result = await deleteSecret(DEPLOY_URL11, t.token, t.appId, t.configName, name);
|
|
10580
|
+
if (result.deleted === false) absent++;
|
|
10581
|
+
else deleted++;
|
|
10266
10582
|
} catch (err) {
|
|
10267
10583
|
if (err?.status === 404) absent++;
|
|
10268
10584
|
else throw err;
|
|
10269
10585
|
}
|
|
10270
10586
|
}
|
|
10271
10587
|
const note3 = absent > 0 ? ` (${absent} already absent)` : "";
|
|
10272
|
-
ok(
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
)
|
|
10276
|
-
|
|
10277
|
-
|
|
10588
|
+
ok(
|
|
10589
|
+
args.json === true,
|
|
10590
|
+
{ appId: t.appId, config: t.configName, deleted, absent, ...APPLIES_AT_DEPLOY },
|
|
10591
|
+
() => {
|
|
10592
|
+
console.log(
|
|
10593
|
+
`Deleted ${deleted} secret${deleted === 1 ? "" : "s"} from ${t.configName}.${note3}`
|
|
10594
|
+
);
|
|
10595
|
+
if (deleted > 0) console.log(APPLY_HINT);
|
|
10596
|
+
}
|
|
10597
|
+
);
|
|
10278
10598
|
} catch (err) {
|
|
10279
10599
|
fail(err);
|
|
10280
10600
|
}
|
|
@@ -10298,14 +10618,20 @@ var upload = defineCommand10({
|
|
|
10298
10618
|
async run({ args }) {
|
|
10299
10619
|
try {
|
|
10300
10620
|
const t = await resolveTarget(args);
|
|
10301
|
-
const secrets = parseSecretsUpload(readUploadSource(args.file));
|
|
10621
|
+
const secrets = parseSecretsUpload(await readUploadSource(args.file));
|
|
10302
10622
|
if (Object.keys(secrets).length === 0)
|
|
10303
10623
|
throw new InputError("No secrets found in the input.", "empty_input");
|
|
10304
10624
|
await uploadSecrets(DEPLOY_URL11, t.token, t.appId, t.configName, secrets, args.replace);
|
|
10305
10625
|
const uploaded = Object.keys(secrets);
|
|
10306
10626
|
ok(
|
|
10307
10627
|
args.json === true,
|
|
10308
|
-
{
|
|
10628
|
+
{
|
|
10629
|
+
appId: t.appId,
|
|
10630
|
+
config: t.configName,
|
|
10631
|
+
uploaded,
|
|
10632
|
+
replaced: args.replace === true,
|
|
10633
|
+
...APPLIES_AT_DEPLOY
|
|
10634
|
+
},
|
|
10309
10635
|
() => {
|
|
10310
10636
|
console.log(`Uploaded ${uploaded.length} secrets to ${t.configName}.`);
|
|
10311
10637
|
if (!warnIfDebugRoutesEnabled(secrets)) console.log(APPLY_HINT);
|
|
@@ -10316,10 +10642,18 @@ var upload = defineCommand10({
|
|
|
10316
10642
|
}
|
|
10317
10643
|
}
|
|
10318
10644
|
});
|
|
10319
|
-
function readUploadSource(file) {
|
|
10320
|
-
if (file === "-")
|
|
10645
|
+
async function readUploadSource(file) {
|
|
10646
|
+
if (file === "-") {
|
|
10647
|
+
if (process.stdin.isTTY) {
|
|
10648
|
+
throw new InputError(
|
|
10649
|
+
'Reading secrets from stdin ("-"), but stdin is a terminal \u2014 pipe dotenv or JSON input, or pass a file path.',
|
|
10650
|
+
"no_stdin"
|
|
10651
|
+
);
|
|
10652
|
+
}
|
|
10653
|
+
return readStreamText(process.stdin, MAX_STDIN_BYTES);
|
|
10654
|
+
}
|
|
10321
10655
|
try {
|
|
10322
|
-
return
|
|
10656
|
+
return readFileSync22(file, "utf-8");
|
|
10323
10657
|
} catch (err) {
|
|
10324
10658
|
if (err.code !== "ENOENT") throw err;
|
|
10325
10659
|
throw new InputError(
|
|
@@ -10656,7 +10990,7 @@ var init_default = defineDeepspaceCommand({
|
|
|
10656
10990
|
// src/cli/commands/status.ts
|
|
10657
10991
|
import { defineCommand as defineCommand11 } from "citty";
|
|
10658
10992
|
import * as p17 from "@clack/prompts";
|
|
10659
|
-
import { existsSync as existsSync24, readFileSync as
|
|
10993
|
+
import { existsSync as existsSync24, readFileSync as readFileSync23 } from "fs";
|
|
10660
10994
|
function timeAgo(iso) {
|
|
10661
10995
|
const ms = Date.now() - Date.parse(iso);
|
|
10662
10996
|
if (!Number.isFinite(ms) || ms < 0) return iso;
|
|
@@ -10757,7 +11091,7 @@ var status_default = defineCommand11({
|
|
|
10757
11091
|
let who = "logged in";
|
|
10758
11092
|
try {
|
|
10759
11093
|
const payload = decodeJwtPayload(
|
|
10760
|
-
|
|
11094
|
+
readFileSync23(TOKEN_PATH, "utf-8").trim()
|
|
10761
11095
|
);
|
|
10762
11096
|
if (payload.email) who = payload.email;
|
|
10763
11097
|
userId = payload.sub ?? null;
|
|
@@ -11827,7 +12161,7 @@ import { defineCommand as defineCommand12 } from "citty";
|
|
|
11827
12161
|
|
|
11828
12162
|
// src/cli/commands/workspace/new.ts
|
|
11829
12163
|
import * as p25 from "@clack/prompts";
|
|
11830
|
-
import { isAbsolute as
|
|
12164
|
+
import { isAbsolute as isAbsolute7, resolve as resolve20 } from "path";
|
|
11831
12165
|
|
|
11832
12166
|
// src/cli/commands/workspace/analysis.ts
|
|
11833
12167
|
import * as p23 from "@clack/prompts";
|
|
@@ -11963,23 +12297,23 @@ import * as p24 from "@clack/prompts";
|
|
|
11963
12297
|
import {
|
|
11964
12298
|
existsSync as existsSync26,
|
|
11965
12299
|
mkdirSync as mkdirSync8,
|
|
11966
|
-
readFileSync as
|
|
12300
|
+
readFileSync as readFileSync24,
|
|
11967
12301
|
realpathSync as realpathSync6,
|
|
11968
12302
|
renameSync as renameSync2,
|
|
11969
12303
|
statSync as statSync3,
|
|
11970
12304
|
writeFileSync as writeFileSync7
|
|
11971
12305
|
} from "fs";
|
|
11972
|
-
import { dirname as dirname12, isAbsolute as
|
|
12306
|
+
import { dirname as dirname12, isAbsolute as isAbsolute6, join as join26, relative as relative4, sep as sep4 } from "path";
|
|
11973
12307
|
var WORKSPACE_MARKER = "deepspace-workspace.json";
|
|
11974
12308
|
function excludeWorktreeDir(appDir, dir) {
|
|
11975
12309
|
try {
|
|
11976
12310
|
ensureLocalExclude(appDir, ".deepspace/");
|
|
11977
12311
|
const realDir = realpathSync6(dir);
|
|
11978
|
-
const rel =
|
|
11979
|
-
if (!rel || rel === ".." || rel.startsWith(`..${
|
|
11980
|
-
const pattern = rel.split(
|
|
12312
|
+
const rel = relative4(realpathSync6(repoToplevel(appDir)), realDir);
|
|
12313
|
+
if (!rel || rel === ".." || rel.startsWith(`..${sep4}`) || isAbsolute6(rel)) return;
|
|
12314
|
+
const pattern = rel.split(sep4).join("/");
|
|
11981
12315
|
if (pattern === ".deepspace" || pattern.startsWith(".deepspace/")) return;
|
|
11982
|
-
const relApp =
|
|
12316
|
+
const relApp = relative4(realpathSync6(appDir), realDir).split(sep4).join("/");
|
|
11983
12317
|
if (relApp === ".deepspace" || relApp.startsWith(".deepspace/")) return;
|
|
11984
12318
|
ensureLocalExclude(appDir, `/${pattern}/`);
|
|
11985
12319
|
} catch {
|
|
@@ -11990,7 +12324,7 @@ function worktreeMarkerPath(worktreeRoot) {
|
|
|
11990
12324
|
const result = runGit(worktreeRoot, ["rev-parse", "--absolute-git-dir"], { allowFail: true });
|
|
11991
12325
|
if (result.status !== 0) return null;
|
|
11992
12326
|
const gitDir = result.stdout.toString("utf-8").trim();
|
|
11993
|
-
return
|
|
12327
|
+
return isAbsolute6(gitDir) ? join26(gitDir, WORKSPACE_MARKER) : null;
|
|
11994
12328
|
} catch {
|
|
11995
12329
|
return null;
|
|
11996
12330
|
}
|
|
@@ -12026,7 +12360,7 @@ function isManagedWorkspaceWorktree(worktreeRoot, workspaceId) {
|
|
|
12026
12360
|
const markerPath = worktreeMarkerPath(worktreeRoot);
|
|
12027
12361
|
if (!markerPath) return false;
|
|
12028
12362
|
try {
|
|
12029
|
-
const marker = JSON.parse(
|
|
12363
|
+
const marker = JSON.parse(readFileSync24(markerPath, "utf-8"));
|
|
12030
12364
|
return marker.version === 1 && marker.workspaceId === workspaceId && marker.mode === "managed-linked";
|
|
12031
12365
|
} catch {
|
|
12032
12366
|
return false;
|
|
@@ -12038,7 +12372,7 @@ function defaultWorkspaceRoot(appDir, workspaceId) {
|
|
|
12038
12372
|
return join26(primary.path, ".deepspace", "ws", workspaceId.slice(3).toLowerCase());
|
|
12039
12373
|
}
|
|
12040
12374
|
function appDirInWorktree(appDir, worktreeRoot) {
|
|
12041
|
-
const appRelative =
|
|
12375
|
+
const appRelative = relative4(realpathSync6(repoToplevel(appDir)), realpathSync6(appDir));
|
|
12042
12376
|
return appRelative ? join26(worktreeRoot, appRelative) : worktreeRoot;
|
|
12043
12377
|
}
|
|
12044
12378
|
function primaryAppDir(appDir) {
|
|
@@ -12459,7 +12793,7 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
12459
12793
|
idempotencyKey: mintIdempotencyKey()
|
|
12460
12794
|
});
|
|
12461
12795
|
const dirInput = typeof args.dir === "string" ? args.dir.trim() : "";
|
|
12462
|
-
const worktreeRoot = dirInput ?
|
|
12796
|
+
const worktreeRoot = dirInput ? isAbsolute7(dirInput) ? dirInput : resolve20(appDir, dirInput) : defaultWorkspaceRoot(appDir, id);
|
|
12463
12797
|
const dir = appDirInWorktree(appDir, worktreeRoot);
|
|
12464
12798
|
try {
|
|
12465
12799
|
materializeWorkspaceWorktree(appDir, worktreeRoot, branch, baseOid, id);
|
|
@@ -12498,7 +12832,7 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
12498
12832
|
// src/cli/commands/workspace/attach.ts
|
|
12499
12833
|
import * as p26 from "@clack/prompts";
|
|
12500
12834
|
import { existsSync as existsSync27, readdirSync as readdirSync9, realpathSync as realpathSync7, rmSync as rmSync7 } from "fs";
|
|
12501
|
-
import { isAbsolute as
|
|
12835
|
+
import { isAbsolute as isAbsolute8, resolve as resolve21 } from "path";
|
|
12502
12836
|
function samePath(a, b) {
|
|
12503
12837
|
const real = (p35) => {
|
|
12504
12838
|
try {
|
|
@@ -12594,7 +12928,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
12594
12928
|
);
|
|
12595
12929
|
}
|
|
12596
12930
|
const requestedDir = typeof args.dir === "string" ? args.dir.trim() : "";
|
|
12597
|
-
const worktreeRoot = requestedDir ?
|
|
12931
|
+
const worktreeRoot = requestedDir ? isAbsolute8(requestedDir) ? requestedDir : resolve21(appDir, requestedDir) : defaultWorkspaceRoot(appDir, id);
|
|
12598
12932
|
const dir2 = appDirInWorktree(appDir, worktreeRoot);
|
|
12599
12933
|
const localTip = resolveCommit(appDir, `refs/heads/${branch}`);
|
|
12600
12934
|
if (localTip) {
|
|
@@ -13054,13 +13388,13 @@ import * as p30 from "@clack/prompts";
|
|
|
13054
13388
|
|
|
13055
13389
|
// src/cli/lib/validation.ts
|
|
13056
13390
|
import { sync as spawnSync7 } from "cross-spawn";
|
|
13057
|
-
import { readFileSync as
|
|
13391
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
13058
13392
|
import { join as join27 } from "path";
|
|
13059
13393
|
var MAX_SUMMARY_CHARS = 4e3;
|
|
13060
13394
|
var MAX_OUTPUT_BUFFER = 64 * 1024 * 1024;
|
|
13061
13395
|
function configuredCommand(appDir) {
|
|
13062
13396
|
try {
|
|
13063
|
-
const pkg2 = JSON.parse(
|
|
13397
|
+
const pkg2 = JSON.parse(readFileSync25(join27(appDir, "package.json"), "utf-8"));
|
|
13064
13398
|
if (pkg2.scripts?.validate) return `${detectPackageManager(appDir)} run --silent validate`;
|
|
13065
13399
|
} catch {
|
|
13066
13400
|
}
|
|
@@ -14149,13 +14483,13 @@ function sameRefs(left, right) {
|
|
|
14149
14483
|
|
|
14150
14484
|
// src/cli/commands/update.ts
|
|
14151
14485
|
import * as p34 from "@clack/prompts";
|
|
14152
|
-
import { existsSync as existsSync28, readFileSync as
|
|
14486
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26 } from "fs";
|
|
14153
14487
|
import { join as join28, resolve as resolve22 } from "path";
|
|
14154
14488
|
|
|
14155
14489
|
// package.json
|
|
14156
14490
|
var package_default = {
|
|
14157
14491
|
name: "deepspace",
|
|
14158
|
-
version: "0.24.
|
|
14492
|
+
version: "0.24.1",
|
|
14159
14493
|
engines: {
|
|
14160
14494
|
node: ">=22.15.0 <23 || >=24 <25 || >=26 <27"
|
|
14161
14495
|
},
|
|
@@ -14337,7 +14671,7 @@ function readManifest(appDir) {
|
|
|
14337
14671
|
throw new Refusal("This app has no package.json \u2014 nothing to update.", "not_in_app_repo");
|
|
14338
14672
|
}
|
|
14339
14673
|
try {
|
|
14340
|
-
return JSON.parse(
|
|
14674
|
+
return JSON.parse(readFileSync26(path, "utf8"));
|
|
14341
14675
|
} catch {
|
|
14342
14676
|
throw new Refusal("package.json must contain valid JSON.", "invalid_package_manifest");
|
|
14343
14677
|
}
|
|
@@ -14373,7 +14707,7 @@ function usersSchemaVisibilityUpgradeInstruction(appDir) {
|
|
|
14373
14707
|
const schemaName = "src/schemas/users-schema.ts";
|
|
14374
14708
|
const path = join28(appDir, schemaName);
|
|
14375
14709
|
if (!existsSync28(path)) return null;
|
|
14376
|
-
const source =
|
|
14710
|
+
const source = readFileSync26(path, "utf8");
|
|
14377
14711
|
if (!/\bmember\s*:\s*\{[^}]*\bread\s*:\s*true\b/s.test(source)) return null;
|
|
14378
14712
|
return `${schemaName}: review member user-row visibility at ${USERS_SCHEMA_VISIBILITY_GUIDE}; leave the app-owned schema unchanged until you choose its policy.`;
|
|
14379
14713
|
}
|
|
@@ -14469,7 +14803,7 @@ var update_default = defineDeepspaceCommand({
|
|
|
14469
14803
|
|
|
14470
14804
|
// src/cli/cli.ts
|
|
14471
14805
|
var __dirname = dirname13(fileURLToPath3(import.meta.url));
|
|
14472
|
-
var pkg = JSON.parse(
|
|
14806
|
+
var pkg = JSON.parse(readFileSync27(join29(__dirname, "..", "package.json"), "utf-8"));
|
|
14473
14807
|
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync29("/etc/ssl/certs/ca-certificates.crt")) {
|
|
14474
14808
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
14475
14809
|
}
|