deepspace 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +446 -178
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +188 -1
- package/dist/index.js +577 -103
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +115 -0
- package/features/file-attachments/feature.json +1 -0
- package/features/file-attachments/src/useFilePreview.ts +1 -1
- package/features/integration-test/src/IntegrationTestPage.tsx +1 -0
- package/features/integration-test/src/integration.spec.ts +14 -5
- package/features/messaging/src/components/chat/MessageList.tsx +1 -1
- package/features/presence-test/src/PresenceTestPage.tsx +1 -1
- package/features/search-bar/feature.json +34 -0
- package/features/search-bar/src/SearchOverlay.tsx +710 -0
- package/features/testing/src/subrequest-burn-action.ts +6 -3
- package/package.json +1 -1
- package/scripts/add-feature.cjs +5 -4
package/dist/cli.js
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
4
|
import { defineCommand as defineCommand19, runMain } from "citty";
|
|
5
5
|
import { execSync as execSync5 } from "child_process";
|
|
6
|
-
import { readFileSync as readFileSync11, existsSync as
|
|
7
|
-
import { dirname as
|
|
6
|
+
import { readFileSync as readFileSync11, existsSync as existsSync12 } from "fs";
|
|
7
|
+
import { dirname as dirname3, join as join14 } from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
10
|
// src/cli/commands/login.ts
|
|
11
11
|
import { defineCommand } from "citty";
|
|
12
12
|
import { writeFileSync as writeFileSync2, mkdirSync } from "fs";
|
|
13
|
-
import { join as
|
|
13
|
+
import { join as join3 } from "path";
|
|
14
14
|
import { homedir } from "os";
|
|
15
15
|
import { exec } from "child_process";
|
|
16
16
|
import { platform } from "os";
|
|
@@ -35,7 +35,11 @@ function createSpinner() {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// src/cli/env.ts
|
|
38
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
38
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
39
|
+
import { join as join2 } from "path";
|
|
40
|
+
|
|
41
|
+
// src/cli/lib/wrangler-env.ts
|
|
42
|
+
import { existsSync, readFileSync } from "fs";
|
|
39
43
|
import { join } from "path";
|
|
40
44
|
import { parse as parseToml } from "smol-toml";
|
|
41
45
|
|
|
@@ -92,6 +96,57 @@ function sanitizeAppName(raw) {
|
|
|
92
96
|
return raw.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
93
97
|
}
|
|
94
98
|
|
|
99
|
+
// src/cli/lib/wrangler-env.ts
|
|
100
|
+
function resolveAppNameForEnv(config, envName) {
|
|
101
|
+
if (!envName) {
|
|
102
|
+
return resolveAppName(config.name);
|
|
103
|
+
}
|
|
104
|
+
const envBlock = config.env?.[envName];
|
|
105
|
+
if (!envBlock) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
reason: `wrangler.toml: no [env.${envName}] block found. Add one with a distinct \`name\` to deploy this environment.`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (typeof envBlock.name !== "string" || envBlock.name.trim() === "") {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
reason: `wrangler.toml: [env.${envName}].name is missing. Set a distinct app name (e.g. "${typeof config.name === "string" ? config.name : "myapp"}-${envName}") so this environment doesn't overwrite the default.`
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (envBlock.name === config.name) {
|
|
118
|
+
return {
|
|
119
|
+
ok: false,
|
|
120
|
+
reason: `wrangler.toml: [env.${envName}].name "${envBlock.name}" is the same as the top-level name. Pick a distinct name (e.g. "${config.name}-${envName}") so deploys don't collide.`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return resolveAppName(envBlock.name);
|
|
124
|
+
}
|
|
125
|
+
function devVarsPathFor(appDir, envName) {
|
|
126
|
+
return envName ? join(appDir, `.dev.vars.${envName}`) : join(appDir, ".dev.vars");
|
|
127
|
+
}
|
|
128
|
+
var WranglerConfigError = class extends Error {
|
|
129
|
+
constructor(path, cause) {
|
|
130
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
131
|
+
super(`wrangler.toml: malformed TOML at ${path}: ${detail}`);
|
|
132
|
+
this.path = path;
|
|
133
|
+
this.cause = cause;
|
|
134
|
+
this.name = "WranglerConfigError";
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
function readWranglerConfig(appDir) {
|
|
138
|
+
const path = join(appDir, "wrangler.toml");
|
|
139
|
+
const raw = readFileSync(path, "utf-8");
|
|
140
|
+
try {
|
|
141
|
+
return parseToml(raw);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
throw new WranglerConfigError(path, err);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function hasWranglerConfig(appDir) {
|
|
147
|
+
return existsSync(join(appDir, "wrangler.toml"));
|
|
148
|
+
}
|
|
149
|
+
|
|
95
150
|
// src/cli/env.ts
|
|
96
151
|
var ENV_URLS = {
|
|
97
152
|
auth: "https://auth.deep.space",
|
|
@@ -115,19 +170,18 @@ async function fetchPublicKey(authUrl) {
|
|
|
115
170
|
}
|
|
116
171
|
return data.publicKey;
|
|
117
172
|
}
|
|
118
|
-
function readAppName(appDir) {
|
|
119
|
-
|
|
120
|
-
if (!existsSync(wranglerPath)) {
|
|
173
|
+
function readAppName(appDir, wranglerEnv) {
|
|
174
|
+
if (!hasWranglerConfig(appDir)) {
|
|
121
175
|
throw new Error("No wrangler.toml found. Are you in a DeepSpace app directory?");
|
|
122
176
|
}
|
|
123
|
-
const
|
|
124
|
-
const resolved =
|
|
125
|
-
if (!resolved.ok)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
177
|
+
const config = readWranglerConfig(appDir);
|
|
178
|
+
const resolved = resolveAppNameForEnv(config, wranglerEnv);
|
|
179
|
+
if (!resolved.ok) throw new Error(resolved.reason);
|
|
180
|
+
const rawName = wranglerEnv ? config.env?.[wranglerEnv]?.name : config.name;
|
|
181
|
+
if (rawName !== resolved.name) {
|
|
182
|
+
const where = wranglerEnv ? `[env.${wranglerEnv}].name` : "`name`";
|
|
129
183
|
throw new Error(
|
|
130
|
-
`wrangler.toml:
|
|
184
|
+
`wrangler.toml: ${where} "${rawName}" is not in canonical form. Update it to "${resolved.name}" and re-run.`
|
|
131
185
|
);
|
|
132
186
|
}
|
|
133
187
|
return resolved.name;
|
|
@@ -187,10 +241,12 @@ var DEV_VARS_DIVIDER = "# --- not managed by the SDK; preserved across dev/test
|
|
|
187
241
|
function extractCustomDevVars(content) {
|
|
188
242
|
const lines = content.split("\n");
|
|
189
243
|
const out = [];
|
|
244
|
+
let belowDivider = false;
|
|
190
245
|
let i = 0;
|
|
191
246
|
while (i < lines.length) {
|
|
192
247
|
const line = lines[i];
|
|
193
248
|
if (line.trim() === DEV_VARS_DIVIDER.trim()) {
|
|
249
|
+
belowDivider = true;
|
|
194
250
|
i++;
|
|
195
251
|
continue;
|
|
196
252
|
}
|
|
@@ -215,7 +271,7 @@ function extractCustomDevVars(content) {
|
|
|
215
271
|
} else {
|
|
216
272
|
i++;
|
|
217
273
|
}
|
|
218
|
-
if (!SDK_MANAGED_KEYS.has(key)) {
|
|
274
|
+
if (belowDivider || !SDK_MANAGED_KEYS.has(key)) {
|
|
219
275
|
out.push(...block);
|
|
220
276
|
}
|
|
221
277
|
}
|
|
@@ -287,10 +343,10 @@ function parseDevVars(content) {
|
|
|
287
343
|
}
|
|
288
344
|
return result;
|
|
289
345
|
}
|
|
290
|
-
async function writeDevVars(appDir, env, ownerId, callerJwt) {
|
|
346
|
+
async function writeDevVars(appDir, env, ownerId, callerJwt, wranglerEnv) {
|
|
291
347
|
const urls = ENVS[env];
|
|
292
348
|
const publicKey = await fetchPublicKey(urls.auth);
|
|
293
|
-
const appName = readAppName(appDir);
|
|
349
|
+
const appName = readAppName(appDir, wranglerEnv);
|
|
294
350
|
const appOwnerJwt = await mintAppOwnerJwt(urls.auth, callerJwt, appName);
|
|
295
351
|
const appIdentityToken = await fetchAppIdentityToken(urls.deploy, callerJwt, appName);
|
|
296
352
|
const sdkVars = [
|
|
@@ -306,8 +362,8 @@ async function writeDevVars(appDir, env, ownerId, callerJwt) {
|
|
|
306
362
|
// Production secrets never set this, so deployed apps 404 the route.
|
|
307
363
|
`ALLOW_DEBUG_ROUTES=true`
|
|
308
364
|
].join("\n");
|
|
309
|
-
const devVarsPath =
|
|
310
|
-
const existing =
|
|
365
|
+
const devVarsPath = join2(appDir, wranglerEnv ? `.dev.vars.${wranglerEnv}` : ".dev.vars");
|
|
366
|
+
const existing = existsSync2(devVarsPath) ? readFileSync2(devVarsPath, "utf-8") : "";
|
|
311
367
|
const custom = extractCustomDevVars(existing);
|
|
312
368
|
const body = custom ? `${sdkVars}
|
|
313
369
|
|
|
@@ -431,10 +487,10 @@ async function pollForCompletion(sessionId, codeVerifier) {
|
|
|
431
487
|
return null;
|
|
432
488
|
}
|
|
433
489
|
function storeCredentials(sessionToken, jwt) {
|
|
434
|
-
const dir =
|
|
490
|
+
const dir = join3(homedir(), ".deepspace");
|
|
435
491
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
436
|
-
writeFileSync2(
|
|
437
|
-
writeFileSync2(
|
|
492
|
+
writeFileSync2(join3(dir, "session"), sessionToken, { mode: 384 });
|
|
493
|
+
writeFileSync2(join3(dir, "token"), jwt, { mode: 384 });
|
|
438
494
|
}
|
|
439
495
|
function openBrowser(url) {
|
|
440
496
|
const cmd = platform() === "darwin" ? `open "${url}"` : platform() === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
@@ -473,20 +529,20 @@ async function doEmailLogin(email, password) {
|
|
|
473
529
|
|
|
474
530
|
// src/cli/commands/logout.ts
|
|
475
531
|
import { defineCommand as defineCommand2 } from "citty";
|
|
476
|
-
import { existsSync as
|
|
477
|
-
import { join as
|
|
532
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
533
|
+
import { join as join4 } from "path";
|
|
478
534
|
import { homedir as homedir2 } from "os";
|
|
479
535
|
var AUTH_URL2 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
480
|
-
var DIR =
|
|
481
|
-
var SESSION_PATH =
|
|
482
|
-
var TOKEN_PATH =
|
|
536
|
+
var DIR = join4(homedir2(), ".deepspace");
|
|
537
|
+
var SESSION_PATH = join4(DIR, "session");
|
|
538
|
+
var TOKEN_PATH = join4(DIR, "token");
|
|
483
539
|
var logout_default = defineCommand2({
|
|
484
540
|
meta: {
|
|
485
541
|
name: "logout",
|
|
486
542
|
description: "Sign out and remove cached credentials"
|
|
487
543
|
},
|
|
488
544
|
async run() {
|
|
489
|
-
const sessionToken =
|
|
545
|
+
const sessionToken = existsSync3(SESSION_PATH) ? readFileSync3(SESSION_PATH, "utf-8").trim() : null;
|
|
490
546
|
if (sessionToken) {
|
|
491
547
|
try {
|
|
492
548
|
await fetch(`${AUTH_URL2}/api/auth/sign-out`, {
|
|
@@ -501,7 +557,7 @@ var logout_default = defineCommand2({
|
|
|
501
557
|
}
|
|
502
558
|
let removed = 0;
|
|
503
559
|
for (const path of [SESSION_PATH, TOKEN_PATH]) {
|
|
504
|
-
if (
|
|
560
|
+
if (existsSync3(path)) {
|
|
505
561
|
rmSync(path);
|
|
506
562
|
removed++;
|
|
507
563
|
}
|
|
@@ -516,13 +572,12 @@ var logout_default = defineCommand2({
|
|
|
516
572
|
|
|
517
573
|
// src/cli/commands/dev.ts
|
|
518
574
|
import { defineCommand as defineCommand3 } from "citty";
|
|
519
|
-
import {
|
|
520
|
-
import { join as join6, resolve as resolve2 } from "path";
|
|
575
|
+
import { resolve as resolve3, basename } from "path";
|
|
521
576
|
import spawn from "cross-spawn";
|
|
522
577
|
|
|
523
578
|
// src/cli/auth.ts
|
|
524
|
-
import { readFileSync as
|
|
525
|
-
import { join as
|
|
579
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
580
|
+
import { join as join5 } from "path";
|
|
526
581
|
import { homedir as homedir3 } from "os";
|
|
527
582
|
|
|
528
583
|
// src/cli/jwt.ts
|
|
@@ -536,20 +591,20 @@ function decodeJwtPayload(token2) {
|
|
|
536
591
|
|
|
537
592
|
// src/cli/auth.ts
|
|
538
593
|
var AUTH_URL3 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
539
|
-
var DIR2 =
|
|
540
|
-
var SESSION_PATH2 =
|
|
541
|
-
var TOKEN_PATH2 =
|
|
594
|
+
var DIR2 = join5(homedir3(), ".deepspace");
|
|
595
|
+
var SESSION_PATH2 = join5(DIR2, "session");
|
|
596
|
+
var TOKEN_PATH2 = join5(DIR2, "token");
|
|
542
597
|
async function ensureToken() {
|
|
543
|
-
if (
|
|
544
|
-
const existing =
|
|
598
|
+
if (existsSync4(TOKEN_PATH2)) {
|
|
599
|
+
const existing = readFileSync4(TOKEN_PATH2, "utf-8").trim();
|
|
545
600
|
if (isTokenValid(existing)) {
|
|
546
601
|
return existing;
|
|
547
602
|
}
|
|
548
603
|
}
|
|
549
|
-
if (!
|
|
604
|
+
if (!existsSync4(SESSION_PATH2)) {
|
|
550
605
|
throw new Error("Not logged in. Run `deepspace login` first.");
|
|
551
606
|
}
|
|
552
|
-
const sessionToken =
|
|
607
|
+
const sessionToken = readFileSync4(SESSION_PATH2, "utf-8").trim();
|
|
553
608
|
const token2 = await exchangeSession(AUTH_URL3, sessionToken);
|
|
554
609
|
if (!token2) {
|
|
555
610
|
throw new Error("Session expired. Run `deepspace login` to re-authenticate.");
|
|
@@ -567,27 +622,86 @@ function isTokenValid(jwt) {
|
|
|
567
622
|
}
|
|
568
623
|
}
|
|
569
624
|
|
|
625
|
+
// src/cli/lib/app-context.ts
|
|
626
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
627
|
+
import { join as join6, resolve, dirname } from "path";
|
|
628
|
+
import { parse as parseToml2 } from "smol-toml";
|
|
629
|
+
function detectAppName(cwd = process.cwd()) {
|
|
630
|
+
const wranglerPath = join6(resolve(cwd), "wrangler.toml");
|
|
631
|
+
if (!existsSync5(wranglerPath)) return null;
|
|
632
|
+
try {
|
|
633
|
+
const cfg = parseToml2(readFileSync5(wranglerPath, "utf-8"));
|
|
634
|
+
return typeof cfg.name === "string" && cfg.name.length > 0 ? cfg.name : null;
|
|
635
|
+
} catch {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function requireAppName(explicit, cwd) {
|
|
640
|
+
if (explicit) return explicit;
|
|
641
|
+
const detected = detectAppName(cwd);
|
|
642
|
+
if (detected) return detected;
|
|
643
|
+
throw new Error(
|
|
644
|
+
"No app specified. Pass --app <name>, or run from an app directory with a wrangler.toml."
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
function findAppDir(start = process.cwd()) {
|
|
648
|
+
let dir = resolve(start);
|
|
649
|
+
for (; ; ) {
|
|
650
|
+
if (existsSync5(join6(dir, "wrangler.toml"))) return dir;
|
|
651
|
+
const parent = dirname(dir);
|
|
652
|
+
if (parent === dir) return null;
|
|
653
|
+
dir = parent;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function findChildApps(dir) {
|
|
657
|
+
try {
|
|
658
|
+
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync5(join6(dir, e.name, "wrangler.toml"))).map((e) => e.name).sort();
|
|
659
|
+
} catch {
|
|
660
|
+
return [];
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function writeLaunchConfigIfMissing(appDir, appName, port) {
|
|
664
|
+
const launchPath = join6(appDir, ".claude", "launch.json");
|
|
665
|
+
if (existsSync5(launchPath)) return;
|
|
666
|
+
const config = {
|
|
667
|
+
version: "0.0.1",
|
|
668
|
+
configurations: [
|
|
669
|
+
{
|
|
670
|
+
name: appName,
|
|
671
|
+
runtimeExecutable: "npx",
|
|
672
|
+
runtimeArgs: ["deepspace", "dev", "--port", String(port)],
|
|
673
|
+
port
|
|
674
|
+
}
|
|
675
|
+
]
|
|
676
|
+
};
|
|
677
|
+
try {
|
|
678
|
+
mkdirSync3(join6(appDir, ".claude"), { recursive: true });
|
|
679
|
+
writeFileSync4(launchPath, JSON.stringify(config, null, 2) + "\n");
|
|
680
|
+
} catch {
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
570
684
|
// src/cli/lib/install-status.ts
|
|
571
|
-
import { existsSync as
|
|
572
|
-
import { join as
|
|
685
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
686
|
+
import { join as join7 } from "path";
|
|
573
687
|
function ensureInstallReady(appDir) {
|
|
574
|
-
if (
|
|
575
|
-
const sentinelDir =
|
|
576
|
-
const errPath =
|
|
577
|
-
const startedPath =
|
|
578
|
-
const donePath =
|
|
579
|
-
const logPath =
|
|
580
|
-
if (
|
|
688
|
+
if (existsSync6(join7(appDir, "node_modules", "deepspace", "package.json"))) return;
|
|
689
|
+
const sentinelDir = join7(appDir, ".deepspace");
|
|
690
|
+
const errPath = join7(sentinelDir, "install.err");
|
|
691
|
+
const startedPath = join7(sentinelDir, "install.started");
|
|
692
|
+
const donePath = join7(sentinelDir, "install.done");
|
|
693
|
+
const logPath = join7(sentinelDir, "install.log");
|
|
694
|
+
if (existsSync6(errPath)) {
|
|
581
695
|
console.error("Background install failed:");
|
|
582
|
-
console.error(
|
|
583
|
-
if (
|
|
696
|
+
console.error(readFileSync6(errPath, "utf-8").trimEnd());
|
|
697
|
+
if (existsSync6(logPath)) console.error(`
|
|
584
698
|
Full log: ${logPath}`);
|
|
585
699
|
console.error("\nRun `npm install` (or `bun install`) manually, then retry.");
|
|
586
700
|
process.exit(1);
|
|
587
701
|
}
|
|
588
|
-
if (
|
|
702
|
+
if (existsSync6(startedPath) && !existsSync6(donePath)) {
|
|
589
703
|
console.error("Dependencies are still installing in the background.");
|
|
590
|
-
if (
|
|
704
|
+
if (existsSync6(logPath)) console.error(`Tail progress: tail -f ${logPath}`);
|
|
591
705
|
console.error("Retry once it finishes.");
|
|
592
706
|
process.exit(1);
|
|
593
707
|
}
|
|
@@ -597,12 +711,12 @@ Full log: ${logPath}`);
|
|
|
597
711
|
|
|
598
712
|
// src/cli/lib/preflight.ts
|
|
599
713
|
import { execFileSync } from "child_process";
|
|
600
|
-
import { existsSync as
|
|
601
|
-
import { resolve } from "path";
|
|
714
|
+
import { existsSync as existsSync7 } from "fs";
|
|
715
|
+
import { resolve as resolve2 } from "path";
|
|
602
716
|
var DLL_NOT_FOUND = -1073741515;
|
|
603
717
|
function preflightWindowsWorkerd(appDir) {
|
|
604
718
|
if (process.platform !== "win32") return;
|
|
605
|
-
const workerdExe =
|
|
719
|
+
const workerdExe = resolve2(
|
|
606
720
|
appDir,
|
|
607
721
|
"node_modules",
|
|
608
722
|
"@cloudflare",
|
|
@@ -610,7 +724,7 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
610
724
|
"bin",
|
|
611
725
|
"workerd.exe"
|
|
612
726
|
);
|
|
613
|
-
if (!
|
|
727
|
+
if (!existsSync7(workerdExe)) return;
|
|
614
728
|
try {
|
|
615
729
|
execFileSync(workerdExe, ["--help"], { stdio: "pipe", timeout: 1e4 });
|
|
616
730
|
return;
|
|
@@ -650,6 +764,11 @@ var dev_default = defineCommand3({
|
|
|
650
764
|
description: "Use production workers (real auth, real API calls)",
|
|
651
765
|
default: false
|
|
652
766
|
},
|
|
767
|
+
env: {
|
|
768
|
+
type: "string",
|
|
769
|
+
description: "wrangler.toml [env.<name>] block to run (e.g. --env staging). Reads .dev.vars.<name> and applies the env's overrides at build time.",
|
|
770
|
+
required: false
|
|
771
|
+
},
|
|
653
772
|
port: {
|
|
654
773
|
type: "string",
|
|
655
774
|
description: `Port to bind (default ${DEFAULT_PORT}, or $DEEPSPACE_PORT)`,
|
|
@@ -657,13 +776,23 @@ var dev_default = defineCommand3({
|
|
|
657
776
|
}
|
|
658
777
|
},
|
|
659
778
|
async run({ args }) {
|
|
660
|
-
const appDir = resolve2(args.dir ?? ".");
|
|
661
779
|
const env = args.prod ? "prod" : "dev";
|
|
780
|
+
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
662
781
|
const port = resolvePort(args.port);
|
|
663
|
-
|
|
664
|
-
|
|
782
|
+
const start = resolve3(args.dir ?? ".");
|
|
783
|
+
const appDir = findAppDir(start);
|
|
784
|
+
if (!appDir) {
|
|
785
|
+
console.error(`No wrangler.toml found at or above ${start}.`);
|
|
786
|
+
const children = findChildApps(start);
|
|
787
|
+
if (children.length > 0) {
|
|
788
|
+
console.error("Did you mean to run inside one of these app directories?");
|
|
789
|
+
for (const c of children) console.error(` cd ${c}`);
|
|
790
|
+
} else {
|
|
791
|
+
console.error("Run from a DeepSpace app directory (one containing wrangler.toml).");
|
|
792
|
+
}
|
|
665
793
|
process.exit(1);
|
|
666
794
|
}
|
|
795
|
+
writeLaunchConfigIfMissing(appDir, detectAppName(appDir) ?? basename(appDir), port);
|
|
667
796
|
ensureInstallReady(appDir);
|
|
668
797
|
let token2;
|
|
669
798
|
try {
|
|
@@ -680,9 +809,10 @@ var dev_default = defineCommand3({
|
|
|
680
809
|
process.exit(1);
|
|
681
810
|
}
|
|
682
811
|
console.log(`Logged in as ${payload.name ?? payload.email}`);
|
|
683
|
-
console.log(`
|
|
812
|
+
console.log(`Platform: ${env}`);
|
|
813
|
+
if (wranglerEnv) console.log(`Wrangler env: ${wranglerEnv}`);
|
|
684
814
|
console.log(`Port: ${port}`);
|
|
685
|
-
await writeDevVars(appDir, env, payload.sub, token2);
|
|
815
|
+
await writeDevVars(appDir, env, payload.sub, token2, wranglerEnv);
|
|
686
816
|
preflightWindowsWorkerd(appDir);
|
|
687
817
|
console.log("Starting dev server...\n");
|
|
688
818
|
const vite = spawn(
|
|
@@ -691,7 +821,15 @@ var dev_default = defineCommand3({
|
|
|
691
821
|
{
|
|
692
822
|
cwd: appDir,
|
|
693
823
|
stdio: "inherit",
|
|
694
|
-
|
|
824
|
+
// CLOUDFLARE_ENV is what the Cloudflare Vite plugin keys off — it
|
|
825
|
+
// applies `[env.<name>]` overrides and loads `.dev.vars.<name>`
|
|
826
|
+
// instead of `.dev.vars`. Set only when --env was passed so the
|
|
827
|
+
// default invocation behaves exactly as before.
|
|
828
|
+
env: {
|
|
829
|
+
...process.env,
|
|
830
|
+
DEEPSPACE_PORT: String(port),
|
|
831
|
+
...wranglerEnv ? { CLOUDFLARE_ENV: wranglerEnv } : {}
|
|
832
|
+
}
|
|
695
833
|
}
|
|
696
834
|
);
|
|
697
835
|
process.on("SIGINT", () => vite.kill());
|
|
@@ -873,8 +1011,7 @@ function isAlive(pid) {
|
|
|
873
1011
|
|
|
874
1012
|
// src/cli/commands/test.ts
|
|
875
1013
|
import { defineCommand as defineCommand5 } from "citty";
|
|
876
|
-
import {
|
|
877
|
-
import { join as join7, resolve as resolve3 } from "path";
|
|
1014
|
+
import { resolve as resolve4 } from "path";
|
|
878
1015
|
import { sync as spawnSync3 } from "cross-spawn";
|
|
879
1016
|
|
|
880
1017
|
// src/cli/lib/playwright.ts
|
|
@@ -921,11 +1058,19 @@ var test_default = defineCommand5({
|
|
|
921
1058
|
}
|
|
922
1059
|
},
|
|
923
1060
|
async run({ args }) {
|
|
924
|
-
const appDir = resolve3(".");
|
|
925
1061
|
const suite = args.suite ?? "default";
|
|
926
1062
|
const port = resolvePort3(args.port);
|
|
927
|
-
|
|
928
|
-
|
|
1063
|
+
const start = resolve4(".");
|
|
1064
|
+
const appDir = findAppDir(start);
|
|
1065
|
+
if (!appDir) {
|
|
1066
|
+
console.error(`No wrangler.toml found at or above ${start}.`);
|
|
1067
|
+
const children = findChildApps(start);
|
|
1068
|
+
if (children.length > 0) {
|
|
1069
|
+
console.error("Did you mean to run inside one of these app directories?");
|
|
1070
|
+
for (const c of children) console.error(` cd ${c}`);
|
|
1071
|
+
} else {
|
|
1072
|
+
console.error("Run from a DeepSpace app directory (one containing wrangler.toml).");
|
|
1073
|
+
}
|
|
929
1074
|
process.exit(1);
|
|
930
1075
|
}
|
|
931
1076
|
ensureInstallReady(appDir);
|
|
@@ -1012,7 +1157,7 @@ function resolvePort3(arg) {
|
|
|
1012
1157
|
// src/cli/commands/screenshot.ts
|
|
1013
1158
|
import { defineCommand as defineCommand6 } from "citty";
|
|
1014
1159
|
import { sync as spawnSync4 } from "cross-spawn";
|
|
1015
|
-
import { resolve as
|
|
1160
|
+
import { resolve as resolve5 } from "path";
|
|
1016
1161
|
var screenshot_default = defineCommand6({
|
|
1017
1162
|
meta: {
|
|
1018
1163
|
name: "screenshot",
|
|
@@ -1041,7 +1186,7 @@ var screenshot_default = defineCommand6({
|
|
|
1041
1186
|
}
|
|
1042
1187
|
},
|
|
1043
1188
|
run({ args }) {
|
|
1044
|
-
const appDir =
|
|
1189
|
+
const appDir = resolve5(".");
|
|
1045
1190
|
ensurePlaywright(appDir);
|
|
1046
1191
|
const playwrightArgs = ["playwright", "screenshot", args.url, args.output];
|
|
1047
1192
|
if (args["full-page"]) playwrightArgs.push("--full-page");
|
|
@@ -1059,7 +1204,7 @@ var screenshot_default = defineCommand6({
|
|
|
1059
1204
|
// src/cli/commands/test-accounts.ts
|
|
1060
1205
|
import { defineCommand as defineCommand7 } from "citty";
|
|
1061
1206
|
import * as p3 from "@clack/prompts";
|
|
1062
|
-
import { readFileSync as
|
|
1207
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
1063
1208
|
import { join as join8 } from "path";
|
|
1064
1209
|
import { homedir as homedir4 } from "os";
|
|
1065
1210
|
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
@@ -1069,17 +1214,17 @@ var ACCOUNTS_PATH = join8(DIR3, "test-accounts.json");
|
|
|
1069
1214
|
function loadAccounts() {
|
|
1070
1215
|
if (!existsSync8(ACCOUNTS_PATH)) return [];
|
|
1071
1216
|
try {
|
|
1072
|
-
return JSON.parse(
|
|
1217
|
+
return JSON.parse(readFileSync7(ACCOUNTS_PATH, "utf-8"));
|
|
1073
1218
|
} catch {
|
|
1074
1219
|
return [];
|
|
1075
1220
|
}
|
|
1076
1221
|
}
|
|
1077
1222
|
function saveAccounts(accounts) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1223
|
+
mkdirSync4(DIR3, { recursive: true });
|
|
1224
|
+
writeFileSync5(ACCOUNTS_PATH, JSON.stringify(accounts, null, 2), { mode: 384 });
|
|
1080
1225
|
}
|
|
1081
1226
|
function sessionCookie() {
|
|
1082
|
-
const token2 =
|
|
1227
|
+
const token2 = readFileSync7(SESSION_PATH2, "utf-8").trim();
|
|
1083
1228
|
return `${SESSION_COOKIE2}=${encodeURIComponent(token2)}`;
|
|
1084
1229
|
}
|
|
1085
1230
|
async function fetchRemoteAccounts() {
|
|
@@ -1346,9 +1491,9 @@ var test_accounts_default = defineCommand7({
|
|
|
1346
1491
|
// src/cli/commands/deploy.ts
|
|
1347
1492
|
import { defineCommand as defineCommand8 } from "citty";
|
|
1348
1493
|
import { execSync as execSync2 } from "child_process";
|
|
1349
|
-
import {
|
|
1350
|
-
import {
|
|
1351
|
-
import { join as join9, resolve as
|
|
1494
|
+
import { randomUUID } from "crypto";
|
|
1495
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync2, existsSync as existsSync9 } from "fs";
|
|
1496
|
+
import { join as join9, resolve as resolve6, dirname as dirname2 } from "path";
|
|
1352
1497
|
import { pathToFileURL } from "url";
|
|
1353
1498
|
import * as p4 from "@clack/prompts";
|
|
1354
1499
|
|
|
@@ -1599,6 +1744,59 @@ function formatDeployWorkerError(status2, error) {
|
|
|
1599
1744
|
|
|
1600
1745
|
Underlying error: ${detail}`;
|
|
1601
1746
|
}
|
|
1747
|
+
var MAX_GROUP_BYTES = 40 * 1024 * 1024;
|
|
1748
|
+
var GROUP_BYTES = (() => {
|
|
1749
|
+
const raw = Number(process.env.DEEPSPACE_DEPLOY_GROUP_BYTES);
|
|
1750
|
+
const v = Number.isInteger(raw) && raw > 0 ? raw : 3 * 1024 * 1024;
|
|
1751
|
+
return Math.min(v, MAX_GROUP_BYTES);
|
|
1752
|
+
})();
|
|
1753
|
+
function packAssetGroups(assets, maxBytes) {
|
|
1754
|
+
const groups = [];
|
|
1755
|
+
let current = [];
|
|
1756
|
+
let currentBytes = 2;
|
|
1757
|
+
for (const a of assets) {
|
|
1758
|
+
const entryBytes = Buffer.byteLength(JSON.stringify(a), "utf-8") + 1;
|
|
1759
|
+
if (current.length > 0 && currentBytes + entryBytes > maxBytes) {
|
|
1760
|
+
groups.push(current);
|
|
1761
|
+
current = [];
|
|
1762
|
+
currentBytes = 2;
|
|
1763
|
+
}
|
|
1764
|
+
current.push(a);
|
|
1765
|
+
currentBytes += entryBytes;
|
|
1766
|
+
}
|
|
1767
|
+
if (current.length > 0) groups.push(current);
|
|
1768
|
+
return groups;
|
|
1769
|
+
}
|
|
1770
|
+
async function postWithRetry(url, makeInit, { attempts = 4, retryServerErrors = true } = {}) {
|
|
1771
|
+
let lastRes;
|
|
1772
|
+
let lastErr;
|
|
1773
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
1774
|
+
try {
|
|
1775
|
+
const res = await fetch(url, makeInit());
|
|
1776
|
+
const isTransient = retryServerErrors && (res.status >= 500 || res.status === 408 || res.status === 429);
|
|
1777
|
+
if (!isTransient) {
|
|
1778
|
+
return res;
|
|
1779
|
+
}
|
|
1780
|
+
lastRes = res;
|
|
1781
|
+
} catch (err) {
|
|
1782
|
+
lastErr = err;
|
|
1783
|
+
}
|
|
1784
|
+
if (attempt < attempts) {
|
|
1785
|
+
await new Promise((r) => setTimeout(r, Math.min(8e3, 500 * 2 ** (attempt - 1))));
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
if (lastRes) return lastRes;
|
|
1789
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
1790
|
+
}
|
|
1791
|
+
async function abortStagedUpload(appName, uploadId, totalGroups, token2) {
|
|
1792
|
+
try {
|
|
1793
|
+
await fetch(
|
|
1794
|
+
`${DEPLOY_URL}/api/deploy/${appName}/assets?uploadId=${uploadId}&totalGroups=${totalGroups}`,
|
|
1795
|
+
{ method: "DELETE", headers: { Authorization: `Bearer ${token2}` } }
|
|
1796
|
+
);
|
|
1797
|
+
} catch {
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1602
1800
|
var deploy_default = defineCommand8({
|
|
1603
1801
|
meta: {
|
|
1604
1802
|
name: "deploy",
|
|
@@ -1609,31 +1807,38 @@ var deploy_default = defineCommand8({
|
|
|
1609
1807
|
type: "positional",
|
|
1610
1808
|
description: "App directory (default: current directory)",
|
|
1611
1809
|
required: false
|
|
1810
|
+
},
|
|
1811
|
+
env: {
|
|
1812
|
+
type: "string",
|
|
1813
|
+
description: "wrangler.toml [env.<name>] block to deploy (e.g. --env staging). Omit to deploy the top-level config.",
|
|
1814
|
+
required: false
|
|
1612
1815
|
}
|
|
1613
1816
|
},
|
|
1614
1817
|
async run({ args }) {
|
|
1615
|
-
const appDir =
|
|
1616
|
-
const
|
|
1617
|
-
if (!
|
|
1818
|
+
const appDir = resolve6(args.dir ?? ".");
|
|
1819
|
+
const envName = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
1820
|
+
if (!hasWranglerConfig(appDir)) {
|
|
1618
1821
|
console.error("No wrangler.toml found. Are you in a DeepSpace app directory?");
|
|
1619
1822
|
process.exit(1);
|
|
1620
1823
|
}
|
|
1621
1824
|
ensureInstallReady(appDir);
|
|
1622
1825
|
p4.intro("Deploying DeepSpace app");
|
|
1623
|
-
const wranglerConfig =
|
|
1624
|
-
const nameRes =
|
|
1826
|
+
const wranglerConfig = readWranglerConfig(appDir);
|
|
1827
|
+
const nameRes = resolveAppNameForEnv(wranglerConfig, envName);
|
|
1625
1828
|
if (!nameRes.ok) {
|
|
1626
|
-
p4.cancel(
|
|
1829
|
+
p4.cancel(nameRes.reason);
|
|
1627
1830
|
process.exit(1);
|
|
1628
1831
|
}
|
|
1629
|
-
|
|
1832
|
+
const declaredName = envName ? wranglerConfig.env?.[envName]?.name : wranglerConfig.name;
|
|
1833
|
+
if (declaredName !== nameRes.name) {
|
|
1834
|
+
const where = envName ? `[env.${envName}].name` : "`name`";
|
|
1630
1835
|
p4.cancel(
|
|
1631
|
-
`wrangler.toml:
|
|
1836
|
+
`wrangler.toml: ${where} "${declaredName}" is not in canonical form. Update it to "${nameRes.name}" and re-run.`
|
|
1632
1837
|
);
|
|
1633
1838
|
process.exit(1);
|
|
1634
1839
|
}
|
|
1635
1840
|
const appName = nameRes.name;
|
|
1636
|
-
p4.log.info(`App: ${appName}`);
|
|
1841
|
+
p4.log.info(envName ? `App: ${appName} (env: ${envName})` : `App: ${appName}`);
|
|
1637
1842
|
let token2;
|
|
1638
1843
|
try {
|
|
1639
1844
|
token2 = await ensureToken();
|
|
@@ -1644,10 +1849,15 @@ var deploy_default = defineCommand8({
|
|
|
1644
1849
|
const s = createSpinner();
|
|
1645
1850
|
s.start("Building...");
|
|
1646
1851
|
try {
|
|
1647
|
-
execSync2("npx vite build", {
|
|
1852
|
+
execSync2("npx vite build", {
|
|
1853
|
+
cwd: appDir,
|
|
1854
|
+
stdio: "pipe",
|
|
1855
|
+
env: envName ? { ...process.env, CLOUDFLARE_ENV: envName } : process.env
|
|
1856
|
+
});
|
|
1648
1857
|
} catch (err) {
|
|
1649
1858
|
s.stop("Build failed");
|
|
1650
|
-
|
|
1859
|
+
const detail = [err.stdout?.toString(), err.stderr?.toString()].filter(Boolean).join("\n").trim();
|
|
1860
|
+
console.error(detail || err.message);
|
|
1651
1861
|
process.exit(1);
|
|
1652
1862
|
}
|
|
1653
1863
|
s.stop("Built");
|
|
@@ -1656,16 +1866,23 @@ var deploy_default = defineCommand8({
|
|
|
1656
1866
|
p4.cancel("Build output config not found at .wrangler/deploy/config.json");
|
|
1657
1867
|
process.exit(1);
|
|
1658
1868
|
}
|
|
1659
|
-
const deployConfig = JSON.parse(
|
|
1660
|
-
const outputWranglerPath =
|
|
1869
|
+
const deployConfig = JSON.parse(readFileSync8(deployConfigPath, "utf-8"));
|
|
1870
|
+
const outputWranglerPath = resolve6(dirname2(deployConfigPath), deployConfig.configPath);
|
|
1661
1871
|
if (!existsSync9(outputWranglerPath)) {
|
|
1662
1872
|
p4.cancel(`Output wrangler.json not found at ${outputWranglerPath}`);
|
|
1663
1873
|
process.exit(1);
|
|
1664
1874
|
}
|
|
1665
|
-
const outputConfig = JSON.parse(
|
|
1666
|
-
|
|
1875
|
+
const outputConfig = JSON.parse(readFileSync8(outputWranglerPath, "utf-8"));
|
|
1876
|
+
if (outputConfig.name && outputConfig.name !== appName) {
|
|
1877
|
+
const envHint = envName ? ` (--env ${envName})` : "";
|
|
1878
|
+
p4.cancel(
|
|
1879
|
+
`Build output mismatch: expected name "${appName}"${envHint}, but the built wrangler.json declares "${outputConfig.name}". This usually means the Cloudflare Vite plugin didn't apply the requested env block. Check the plugin version and re-run.`
|
|
1880
|
+
);
|
|
1881
|
+
process.exit(1);
|
|
1882
|
+
}
|
|
1883
|
+
const workerDir = dirname2(outputWranglerPath);
|
|
1667
1884
|
const workerBundlePath = join9(workerDir, outputConfig.main);
|
|
1668
|
-
const clientDir = outputConfig.assets?.directory ?
|
|
1885
|
+
const clientDir = outputConfig.assets?.directory ? resolve6(workerDir, outputConfig.assets.directory) : null;
|
|
1669
1886
|
if (!existsSync9(workerBundlePath)) {
|
|
1670
1887
|
p4.cancel(`Worker bundle not found at ${workerBundlePath}`);
|
|
1671
1888
|
process.exit(1);
|
|
@@ -1677,7 +1894,7 @@ var deploy_default = defineCommand8({
|
|
|
1677
1894
|
s.start("Collecting assets...");
|
|
1678
1895
|
const assets = collectAssets(clientDir);
|
|
1679
1896
|
s.stop(`Collected ${assets.length} assets`);
|
|
1680
|
-
const workerJs =
|
|
1897
|
+
const workerJs = readFileSync8(workerBundlePath, "utf-8");
|
|
1681
1898
|
const doBindings = outputConfig.durable_objects?.bindings;
|
|
1682
1899
|
const sqliteClasses = new Set(
|
|
1683
1900
|
outputConfig.migrations?.flatMap((m) => m.new_sqlite_classes ?? []) ?? []
|
|
@@ -1704,10 +1921,10 @@ var deploy_default = defineCommand8({
|
|
|
1704
1921
|
`Custom bindings: ${customBindings.map((b) => `${b.name} (${b.type})`).join(", ")}`
|
|
1705
1922
|
);
|
|
1706
1923
|
}
|
|
1707
|
-
const devVarsPath =
|
|
1924
|
+
const devVarsPath = devVarsPathFor(appDir, envName);
|
|
1708
1925
|
let userSecrets = {};
|
|
1709
1926
|
if (existsSync9(devVarsPath)) {
|
|
1710
|
-
const customSection = extractCustomDevVars(
|
|
1927
|
+
const customSection = extractCustomDevVars(readFileSync8(devVarsPath, "utf-8"));
|
|
1711
1928
|
if (customSection) {
|
|
1712
1929
|
try {
|
|
1713
1930
|
userSecrets = parseDevVars(customSection);
|
|
@@ -1740,30 +1957,98 @@ var deploy_default = defineCommand8({
|
|
|
1740
1957
|
p4.log.info(`App secrets (from .dev.vars): ${userSecretNames.join(", ")}`);
|
|
1741
1958
|
}
|
|
1742
1959
|
s.start(`Deploying to ${appName}.app.space...`);
|
|
1743
|
-
const form = new FormData();
|
|
1744
|
-
form.append("worker", new Blob([workerJs], { type: "application/javascript" }), "worker.js");
|
|
1745
|
-
form.append("assets", JSON.stringify(assets));
|
|
1746
|
-
if (doManifest) {
|
|
1747
|
-
form.append("doManifest", JSON.stringify(doManifest));
|
|
1748
|
-
}
|
|
1749
|
-
if (customBindings.length) {
|
|
1750
|
-
form.append("bindingManifest", JSON.stringify(customBindings));
|
|
1751
|
-
}
|
|
1752
|
-
if (userSecretNames.length) {
|
|
1753
|
-
form.append("userSecrets", JSON.stringify(userSecrets));
|
|
1754
|
-
}
|
|
1755
1960
|
const extraRoutes = extractRunWorkerFirst(wranglerConfig);
|
|
1756
1961
|
if (extraRoutes.length) {
|
|
1757
|
-
form.append("extraRunWorkerFirst", JSON.stringify(extraRoutes));
|
|
1758
1962
|
p4.log.info(`Custom worker-first routes: ${extraRoutes.join(", ")}`);
|
|
1759
1963
|
}
|
|
1760
|
-
const
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1964
|
+
const assetGroups = packAssetGroups(assets, GROUP_BYTES);
|
|
1965
|
+
let inlineAssets = null;
|
|
1966
|
+
let uploadId = null;
|
|
1967
|
+
let totalGroups = 0;
|
|
1968
|
+
if (assetGroups.length <= 1) {
|
|
1969
|
+
inlineAssets = JSON.stringify(assets);
|
|
1970
|
+
} else {
|
|
1971
|
+
uploadId = randomUUID();
|
|
1972
|
+
totalGroups = assetGroups.length;
|
|
1973
|
+
try {
|
|
1974
|
+
for (let i = 0; i < totalGroups; i++) {
|
|
1975
|
+
const groupJson = JSON.stringify(assetGroups[i]);
|
|
1976
|
+
s.message(`Uploading assets \u2014 group ${i + 1}/${totalGroups}...`);
|
|
1977
|
+
const groupRes = await postWithRetry(
|
|
1978
|
+
`${DEPLOY_URL}/api/deploy/${appName}/assets?uploadId=${uploadId}&groupIndex=${i}&totalGroups=${totalGroups}`,
|
|
1979
|
+
() => ({
|
|
1980
|
+
method: "POST",
|
|
1981
|
+
headers: {
|
|
1982
|
+
Authorization: `Bearer ${token2}`,
|
|
1983
|
+
"Content-Type": "application/json"
|
|
1984
|
+
},
|
|
1985
|
+
body: groupJson
|
|
1986
|
+
})
|
|
1987
|
+
);
|
|
1988
|
+
if (!groupRes.ok) {
|
|
1989
|
+
const errBody = await groupRes.json().catch(() => ({}));
|
|
1990
|
+
await abortStagedUpload(appName, uploadId, totalGroups, token2);
|
|
1991
|
+
s.stop("Deploy failed");
|
|
1992
|
+
p4.cancel(
|
|
1993
|
+
`Asset upload failed on group ${i + 1}/${totalGroups}: ${errBody.error ?? `HTTP ${groupRes.status}`}`
|
|
1994
|
+
);
|
|
1995
|
+
process.exit(1);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
} catch (err) {
|
|
1999
|
+
await abortStagedUpload(appName, uploadId, totalGroups, token2);
|
|
2000
|
+
s.stop("Deploy failed");
|
|
2001
|
+
p4.cancel(`Asset upload failed (network): ${err?.message ?? err}`);
|
|
2002
|
+
process.exit(1);
|
|
2003
|
+
}
|
|
2004
|
+
s.message(`Deploying to ${appName}.app.space...`);
|
|
2005
|
+
}
|
|
2006
|
+
const makeForm = () => {
|
|
2007
|
+
const form = new FormData();
|
|
2008
|
+
form.append("worker", new Blob([workerJs], { type: "application/javascript" }), "worker.js");
|
|
2009
|
+
if (inlineAssets !== null) {
|
|
2010
|
+
form.append("assets", inlineAssets);
|
|
2011
|
+
} else {
|
|
2012
|
+
form.append("uploadId", uploadId);
|
|
2013
|
+
form.append("totalGroups", String(totalGroups));
|
|
2014
|
+
}
|
|
2015
|
+
if (doManifest) {
|
|
2016
|
+
form.append("doManifest", JSON.stringify(doManifest));
|
|
2017
|
+
}
|
|
2018
|
+
if (customBindings.length) {
|
|
2019
|
+
form.append("bindingManifest", JSON.stringify(customBindings));
|
|
2020
|
+
}
|
|
2021
|
+
if (userSecretNames.length) {
|
|
2022
|
+
form.append("userSecrets", JSON.stringify(userSecrets));
|
|
2023
|
+
}
|
|
2024
|
+
if (extraRoutes.length) {
|
|
2025
|
+
form.append("extraRunWorkerFirst", JSON.stringify(extraRoutes));
|
|
2026
|
+
}
|
|
2027
|
+
return form;
|
|
2028
|
+
};
|
|
2029
|
+
let res;
|
|
2030
|
+
try {
|
|
2031
|
+
res = await postWithRetry(
|
|
2032
|
+
`${DEPLOY_URL}/api/deploy/${appName}`,
|
|
2033
|
+
() => ({
|
|
2034
|
+
method: "POST",
|
|
2035
|
+
headers: { Authorization: `Bearer ${token2}` },
|
|
2036
|
+
body: makeForm()
|
|
2037
|
+
}),
|
|
2038
|
+
// Commit runs a real deploy — don't retry its 5xx (could double-deploy);
|
|
2039
|
+
// only a thrown fetch (dropped connection) is retried, and the staged
|
|
2040
|
+
// groups survive on the worker until this request succeeds.
|
|
2041
|
+
{ retryServerErrors: false }
|
|
2042
|
+
);
|
|
2043
|
+
} catch (err) {
|
|
2044
|
+
if (uploadId) await abortStagedUpload(appName, uploadId, totalGroups, token2);
|
|
2045
|
+
s.stop("Deploy failed");
|
|
2046
|
+
p4.cancel(`Deploy request failed: ${err?.message ?? err}`);
|
|
2047
|
+
process.exit(1);
|
|
2048
|
+
}
|
|
1765
2049
|
const body = await res.json().catch(() => ({}));
|
|
1766
2050
|
if (!res.ok || !body.success) {
|
|
2051
|
+
if (uploadId) await abortStagedUpload(appName, uploadId, totalGroups, token2);
|
|
1767
2052
|
s.stop("Deploy failed");
|
|
1768
2053
|
p4.cancel(formatDeployWorkerError(res.status, body.error));
|
|
1769
2054
|
process.exit(1);
|
|
@@ -2018,7 +2303,7 @@ async function waitForAssetsReady(url, timeoutMs) {
|
|
|
2018
2303
|
function collectAssets(dir) {
|
|
2019
2304
|
const assets = [];
|
|
2020
2305
|
function walk(d, prefix) {
|
|
2021
|
-
for (const entry of
|
|
2306
|
+
for (const entry of readdirSync2(d, { withFileTypes: true })) {
|
|
2022
2307
|
if (entry.name === ".assetsignore") continue;
|
|
2023
2308
|
const full = join9(d, entry.name);
|
|
2024
2309
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
@@ -2027,7 +2312,7 @@ function collectAssets(dir) {
|
|
|
2027
2312
|
} else {
|
|
2028
2313
|
assets.push({
|
|
2029
2314
|
path: "/" + rel,
|
|
2030
|
-
contentBase64:
|
|
2315
|
+
contentBase64: readFileSync8(full).toString("base64")
|
|
2031
2316
|
});
|
|
2032
2317
|
}
|
|
2033
2318
|
}
|
|
@@ -2038,9 +2323,6 @@ function collectAssets(dir) {
|
|
|
2038
2323
|
|
|
2039
2324
|
// src/cli/commands/undeploy.ts
|
|
2040
2325
|
import { defineCommand as defineCommand9 } from "citty";
|
|
2041
|
-
import { readFileSync as readFileSync7, existsSync as existsSync10 } from "fs";
|
|
2042
|
-
import { join as join10 } from "path";
|
|
2043
|
-
import { parse as parseToml3 } from "smol-toml";
|
|
2044
2326
|
import * as p5 from "@clack/prompts";
|
|
2045
2327
|
var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? ENVS.prod.deploy;
|
|
2046
2328
|
var undeploy_default = defineCommand9({
|
|
@@ -2053,15 +2335,25 @@ var undeploy_default = defineCommand9({
|
|
|
2053
2335
|
type: "positional",
|
|
2054
2336
|
description: "App name to undeploy (reads from wrangler.toml if omitted)",
|
|
2055
2337
|
required: false
|
|
2338
|
+
},
|
|
2339
|
+
env: {
|
|
2340
|
+
type: "string",
|
|
2341
|
+
description: "wrangler.toml [env.<name>] block whose deployed app to remove (e.g. --env staging). Ignored if a positional name is given.",
|
|
2342
|
+
required: false
|
|
2056
2343
|
}
|
|
2057
2344
|
},
|
|
2058
2345
|
async run({ args }) {
|
|
2059
2346
|
let appName = args.name;
|
|
2347
|
+
const envName = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
2060
2348
|
if (!appName) {
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
const
|
|
2064
|
-
if (
|
|
2349
|
+
if (hasWranglerConfig(process.cwd())) {
|
|
2350
|
+
const config = readWranglerConfig(process.cwd());
|
|
2351
|
+
const nameRes = resolveAppNameForEnv(config, envName);
|
|
2352
|
+
if (!nameRes.ok) {
|
|
2353
|
+
p5.cancel(nameRes.reason);
|
|
2354
|
+
process.exit(1);
|
|
2355
|
+
}
|
|
2356
|
+
appName = nameRes.name;
|
|
2065
2357
|
}
|
|
2066
2358
|
}
|
|
2067
2359
|
if (!appName) {
|
|
@@ -2151,14 +2443,14 @@ var whoami_default = defineCommand10({
|
|
|
2151
2443
|
|
|
2152
2444
|
// src/cli/commands/add.ts
|
|
2153
2445
|
import { defineCommand as defineCommand11 } from "citty";
|
|
2154
|
-
import { existsSync as
|
|
2155
|
-
import { resolve as
|
|
2446
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2447
|
+
import { resolve as resolve7, join as join10 } from "path";
|
|
2156
2448
|
import { execSync as execSync3 } from "child_process";
|
|
2157
2449
|
function findScript(from) {
|
|
2158
|
-
const inNodeModules =
|
|
2159
|
-
if (
|
|
2160
|
-
const inDotDeepspace =
|
|
2161
|
-
if (
|
|
2450
|
+
const inNodeModules = join10(from, "node_modules", "deepspace", "scripts", "add-feature.cjs");
|
|
2451
|
+
if (existsSync10(inNodeModules)) return inNodeModules;
|
|
2452
|
+
const inDotDeepspace = join10(from, ".deepspace", "scripts", "add-feature.cjs");
|
|
2453
|
+
if (existsSync10(inDotDeepspace)) return inDotDeepspace;
|
|
2162
2454
|
return null;
|
|
2163
2455
|
}
|
|
2164
2456
|
var add_default = defineCommand11({
|
|
@@ -2190,14 +2482,14 @@ var add_default = defineCommand11({
|
|
|
2190
2482
|
}
|
|
2191
2483
|
},
|
|
2192
2484
|
run({ args }) {
|
|
2193
|
-
const appDir =
|
|
2485
|
+
const appDir = resolve7(args.dir ?? ".");
|
|
2194
2486
|
ensureInstallReady(appDir);
|
|
2195
2487
|
const script = findScript(appDir);
|
|
2196
2488
|
if (!script) {
|
|
2197
2489
|
console.error("Could not find the deepspace feature installer.");
|
|
2198
2490
|
console.error("Looked in:");
|
|
2199
|
-
console.error(` ${
|
|
2200
|
-
console.error(` ${
|
|
2491
|
+
console.error(` ${join10(appDir, "node_modules", "deepspace", "scripts", "add-feature.cjs")}`);
|
|
2492
|
+
console.error(` ${join10(appDir, ".deepspace", "scripts", "add-feature.cjs")}`);
|
|
2201
2493
|
console.error("Are you in a DeepSpace app directory? Try `npm install` first.");
|
|
2202
2494
|
process.exit(1);
|
|
2203
2495
|
}
|
|
@@ -2221,8 +2513,8 @@ var add_default = defineCommand11({
|
|
|
2221
2513
|
// src/cli/commands/skills.ts
|
|
2222
2514
|
import { defineCommand as defineCommand12 } from "citty";
|
|
2223
2515
|
import spawn2 from "cross-spawn";
|
|
2224
|
-
import { mkdirSync as
|
|
2225
|
-
import { join as
|
|
2516
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
2517
|
+
import { join as join11 } from "path";
|
|
2226
2518
|
import { homedir as homedir5 } from "os";
|
|
2227
2519
|
var SKILL_REPO = "deepdotspace/deepspace-skill";
|
|
2228
2520
|
function runSkillsAdd(extraArgs) {
|
|
@@ -2241,7 +2533,7 @@ function runSkillsAdd(extraArgs) {
|
|
|
2241
2533
|
});
|
|
2242
2534
|
}
|
|
2243
2535
|
function precreateClaudeDir(baseDir) {
|
|
2244
|
-
|
|
2536
|
+
mkdirSync5(join11(baseDir, ".claude"), { recursive: true });
|
|
2245
2537
|
}
|
|
2246
2538
|
var add = defineCommand12({
|
|
2247
2539
|
meta: {
|
|
@@ -2298,31 +2590,6 @@ var skills_default = defineCommand12({
|
|
|
2298
2590
|
// src/cli/commands/domain.ts
|
|
2299
2591
|
import { defineCommand as defineCommand13 } from "citty";
|
|
2300
2592
|
import { execSync as execSync4 } from "child_process";
|
|
2301
|
-
|
|
2302
|
-
// src/cli/lib/app-context.ts
|
|
2303
|
-
import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
|
|
2304
|
-
import { join as join13, resolve as resolve7 } from "path";
|
|
2305
|
-
import { parse as parseToml4 } from "smol-toml";
|
|
2306
|
-
function detectAppName(cwd = process.cwd()) {
|
|
2307
|
-
const wranglerPath = join13(resolve7(cwd), "wrangler.toml");
|
|
2308
|
-
if (!existsSync12(wranglerPath)) return null;
|
|
2309
|
-
try {
|
|
2310
|
-
const cfg = parseToml4(readFileSync8(wranglerPath, "utf-8"));
|
|
2311
|
-
return typeof cfg.name === "string" && cfg.name.length > 0 ? cfg.name : null;
|
|
2312
|
-
} catch {
|
|
2313
|
-
return null;
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
function requireAppName(explicit, cwd) {
|
|
2317
|
-
if (explicit) return explicit;
|
|
2318
|
-
const detected = detectAppName(cwd);
|
|
2319
|
-
if (detected) return detected;
|
|
2320
|
-
throw new Error(
|
|
2321
|
-
"No app specified. Pass --app <name>, or run from an app directory with a wrangler.toml."
|
|
2322
|
-
);
|
|
2323
|
-
}
|
|
2324
|
-
|
|
2325
|
-
// src/cli/commands/domain.ts
|
|
2326
2593
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
2327
2594
|
async function api(token2, path, init) {
|
|
2328
2595
|
const res = await fetch(`${API_URL2}${path}`, {
|
|
@@ -2690,8 +2957,9 @@ function flushAndExit(code) {
|
|
|
2690
2957
|
process.stdout.write("", () => process.exit(code));
|
|
2691
2958
|
});
|
|
2692
2959
|
}
|
|
2693
|
-
async function fetchCatalog() {
|
|
2694
|
-
const
|
|
2960
|
+
async function fetchCatalog(opts = {}) {
|
|
2961
|
+
const url = opts.summary ? `${API_URL3}/api/integrations?summary=1` : `${API_URL3}/api/integrations`;
|
|
2962
|
+
const res = await fetch(url);
|
|
2695
2963
|
if (!res.ok) {
|
|
2696
2964
|
throw new Error(`Failed to fetch integration catalog (${res.status})`);
|
|
2697
2965
|
}
|
|
@@ -2707,7 +2975,7 @@ function formatCurrency(value, currency) {
|
|
|
2707
2975
|
return `${value} ${currency}`;
|
|
2708
2976
|
}
|
|
2709
2977
|
async function runList(args) {
|
|
2710
|
-
const catalog = await fetchCatalog();
|
|
2978
|
+
const catalog = await fetchCatalog({ summary: true });
|
|
2711
2979
|
if (args.json) {
|
|
2712
2980
|
process.stdout.write(JSON.stringify(catalog) + "\n");
|
|
2713
2981
|
await flushAndExit(0);
|
|
@@ -3254,9 +3522,9 @@ var managed_repos_default = defineCommand16({
|
|
|
3254
3522
|
|
|
3255
3523
|
// src/cli/commands/library.ts
|
|
3256
3524
|
import { defineCommand as defineCommand17 } from "citty";
|
|
3257
|
-
import { existsSync as
|
|
3258
|
-
import { join as
|
|
3259
|
-
import { parse as
|
|
3525
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
3526
|
+
import { join as join12, resolve as resolve8 } from "path";
|
|
3527
|
+
import { parse as parseToml3 } from "smol-toml";
|
|
3260
3528
|
import * as p6 from "@clack/prompts";
|
|
3261
3529
|
var DEFAULT_LIBRARY_APP = "deepdotspace-site";
|
|
3262
3530
|
var publish = defineCommand17({
|
|
@@ -3333,11 +3601,11 @@ var publish = defineCommand17({
|
|
|
3333
3601
|
let appName = typeof args.app === "string" && args.app.trim() ? args.app.trim() : void 0;
|
|
3334
3602
|
if (!appName) {
|
|
3335
3603
|
const appDir = resolve8(typeof args.dir === "string" && args.dir ? args.dir : ".");
|
|
3336
|
-
const wranglerPath =
|
|
3337
|
-
if (!
|
|
3604
|
+
const wranglerPath = join12(appDir, "wrangler.toml");
|
|
3605
|
+
if (!existsSync11(wranglerPath)) {
|
|
3338
3606
|
return die("No --app provided and no wrangler.toml found. Either pass --app <name> or run from your app's directory.");
|
|
3339
3607
|
}
|
|
3340
|
-
const cfg =
|
|
3608
|
+
const cfg = parseToml3(readFileSync10(wranglerPath, "utf-8"));
|
|
3341
3609
|
const nameRes = resolveAppName(cfg.name);
|
|
3342
3610
|
if (!nameRes.ok) return die(`wrangler.toml: ${nameRes.reason}`);
|
|
3343
3611
|
appName = nameRes.name;
|
|
@@ -3470,9 +3738,9 @@ var library_default = defineCommand17({
|
|
|
3470
3738
|
|
|
3471
3739
|
// src/cli/commands/admin.ts
|
|
3472
3740
|
import { defineCommand as defineCommand18 } from "citty";
|
|
3473
|
-
import { mkdirSync as
|
|
3741
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
3474
3742
|
import { homedir as homedir6 } from "os";
|
|
3475
|
-
import { join as
|
|
3743
|
+
import { join as join13 } from "path";
|
|
3476
3744
|
var AUTH_URL5 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
3477
3745
|
var impersonate = defineCommand18({
|
|
3478
3746
|
meta: {
|
|
@@ -3522,9 +3790,9 @@ var impersonate = defineCommand18({
|
|
|
3522
3790
|
console.error(`Failed: ${data.error ?? `HTTP ${res.status}`}`);
|
|
3523
3791
|
process.exit(1);
|
|
3524
3792
|
}
|
|
3525
|
-
const dir =
|
|
3526
|
-
|
|
3527
|
-
|
|
3793
|
+
const dir = join13(homedir6(), ".deepspace");
|
|
3794
|
+
mkdirSync6(dir, { recursive: true });
|
|
3795
|
+
writeFileSync6(TOKEN_PATH2, data.token, { mode: 384 });
|
|
3528
3796
|
console.log(`Impersonating ${data.user.name ?? data.user.email ?? data.user.id}`);
|
|
3529
3797
|
console.log(`Email: ${data.user.email ?? "(none)"}`);
|
|
3530
3798
|
console.log(`UserID: ${data.user.id}`);
|
|
@@ -3541,9 +3809,9 @@ var admin_default = defineCommand18({
|
|
|
3541
3809
|
});
|
|
3542
3810
|
|
|
3543
3811
|
// src/cli/cli.ts
|
|
3544
|
-
var __dirname =
|
|
3545
|
-
var pkg = JSON.parse(readFileSync11(
|
|
3546
|
-
if (process.platform === "linux" && !process.env.SSL_CERT_FILE &&
|
|
3812
|
+
var __dirname = dirname3(fileURLToPath(import.meta.url));
|
|
3813
|
+
var pkg = JSON.parse(readFileSync11(join14(__dirname, "..", "package.json"), "utf-8"));
|
|
3814
|
+
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync12("/etc/ssl/certs/ca-certificates.crt")) {
|
|
3547
3815
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
3548
3816
|
}
|
|
3549
3817
|
var create3 = defineCommand19({
|