deepspace 0.4.2 → 0.4.4
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 +1442 -275
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +197 -425
- package/dist/index.js +621 -1665
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +207 -28
- package/dist/server.js +713 -26
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +207 -28
- package/dist/worker.js +713 -26
- package/dist/worker.js.map +1 -1
- package/features/admin-page/src/AdminPage.tsx +1 -1
- package/features/canvas/src/CanvasListPage.tsx +1 -1
- package/features/canvas/src/components/CanvasView.tsx +1 -1
- package/features/docs/src/DocsEditorPage.tsx +1 -1
- package/features/docs/src/InviteDialog.tsx +130 -139
- package/features/file-attachments/src/AttachmentsPage.tsx +1 -4
- package/features/file-manager/src/FileManagerPage.tsx +2 -1
- package/features/items/src/ItemsPage.tsx +1 -1
- package/features/landing/src/LandingPage.tsx +1 -1
- package/features/landing/src/primitives.tsx +1 -1
- package/features/leaderboard/src/LeaderboardPage.tsx +1 -1
- package/features/leaderboard/src/leaderboard-constants.ts +1 -1
- package/features/search-bar/src/SearchOverlay.tsx +1 -1
- package/features/tasks/src/TasksPage.tsx +1 -1
- package/features/tasks/src/tasks-constants.ts +1 -1
- package/package.json +1 -18
- package/dist/testing.d.ts +0 -115
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
|
-
import { defineCommand as
|
|
4
|
+
import { defineCommand as defineCommand21, runMain } from "citty";
|
|
5
5
|
import { execSync as execSync5 } from "child_process";
|
|
6
|
-
import { readFileSync as
|
|
7
|
-
import { dirname as
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
6
|
+
import { readFileSync as readFileSync13, existsSync as existsSync14 } from "fs";
|
|
7
|
+
import { dirname as dirname4, join as join16 } from "path";
|
|
8
|
+
import { fileURLToPath as fileURLToPath2 } 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,54 @@ 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
|
+
var WranglerConfigError = class extends Error {
|
|
126
|
+
constructor(path, cause) {
|
|
127
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
128
|
+
super(`wrangler.toml: malformed TOML at ${path}: ${detail}`);
|
|
129
|
+
this.path = path;
|
|
130
|
+
this.cause = cause;
|
|
131
|
+
this.name = "WranglerConfigError";
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
function readWranglerConfig(appDir) {
|
|
135
|
+
const path = join(appDir, "wrangler.toml");
|
|
136
|
+
const raw = readFileSync(path, "utf-8");
|
|
137
|
+
try {
|
|
138
|
+
return parseToml(raw);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
throw new WranglerConfigError(path, err);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function hasWranglerConfig(appDir) {
|
|
144
|
+
return existsSync(join(appDir, "wrangler.toml"));
|
|
145
|
+
}
|
|
146
|
+
|
|
95
147
|
// src/cli/env.ts
|
|
96
148
|
var ENV_URLS = {
|
|
97
149
|
auth: "https://auth.deep.space",
|
|
@@ -115,19 +167,18 @@ async function fetchPublicKey(authUrl) {
|
|
|
115
167
|
}
|
|
116
168
|
return data.publicKey;
|
|
117
169
|
}
|
|
118
|
-
function readAppName(appDir) {
|
|
119
|
-
|
|
120
|
-
if (!existsSync(wranglerPath)) {
|
|
170
|
+
function readAppName(appDir, wranglerEnv) {
|
|
171
|
+
if (!hasWranglerConfig(appDir)) {
|
|
121
172
|
throw new Error("No wrangler.toml found. Are you in a DeepSpace app directory?");
|
|
122
173
|
}
|
|
123
|
-
const
|
|
124
|
-
const resolved =
|
|
125
|
-
if (!resolved.ok)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
174
|
+
const config = readWranglerConfig(appDir);
|
|
175
|
+
const resolved = resolveAppNameForEnv(config, wranglerEnv);
|
|
176
|
+
if (!resolved.ok) throw new Error(resolved.reason);
|
|
177
|
+
const rawName = wranglerEnv ? config.env?.[wranglerEnv]?.name : config.name;
|
|
178
|
+
if (rawName !== resolved.name) {
|
|
179
|
+
const where = wranglerEnv ? `[env.${wranglerEnv}].name` : "`name`";
|
|
129
180
|
throw new Error(
|
|
130
|
-
`wrangler.toml:
|
|
181
|
+
`wrangler.toml: ${where} "${rawName}" is not in canonical form. Update it to "${resolved.name}" and re-run.`
|
|
131
182
|
);
|
|
132
183
|
}
|
|
133
184
|
return resolved.name;
|
|
@@ -181,8 +232,22 @@ var SDK_MANAGED_KEYS = /* @__PURE__ */ new Set([
|
|
|
181
232
|
"OWNER_USER_ID",
|
|
182
233
|
"APP_OWNER_JWT",
|
|
183
234
|
"APP_IDENTITY_TOKEN",
|
|
235
|
+
"APP_SECRETS_KEY",
|
|
236
|
+
"APP_SECRETS_LOCAL_FALLBACK",
|
|
184
237
|
"ALLOW_DEBUG_ROUTES"
|
|
185
238
|
]);
|
|
239
|
+
var MANAGED_ENV_HEADER = "# Managed by `deepspace secrets` and regenerated by `deepspace dev`/`deepspace test`.";
|
|
240
|
+
var MANAGED_ENV_SYNCED_HEADER = "# DeepSpace synced secrets - mirrored with the deployed app secrets store.";
|
|
241
|
+
var MANAGED_ENV_DEV_ONLY_HEADER = "# DeepSpace dev-only secrets - used by `deepspace dev`; never synced unless pushed.";
|
|
242
|
+
function localEnvPath(appDir) {
|
|
243
|
+
return join2(appDir, ".env");
|
|
244
|
+
}
|
|
245
|
+
function legacyDevVarsPath(appDir, wranglerEnv) {
|
|
246
|
+
return join2(appDir, wranglerEnv ? `.dev.vars.${wranglerEnv}` : ".dev.vars");
|
|
247
|
+
}
|
|
248
|
+
function hasLegacyDevVars(appDir, wranglerEnv) {
|
|
249
|
+
return existsSync2(legacyDevVarsPath(appDir, wranglerEnv));
|
|
250
|
+
}
|
|
186
251
|
var DEV_VARS_DIVIDER = "# --- not managed by the SDK; preserved across dev/test runs ---";
|
|
187
252
|
function extractCustomDevVars(content) {
|
|
188
253
|
const lines = content.split("\n");
|
|
@@ -289,10 +354,193 @@ function parseDevVars(content) {
|
|
|
289
354
|
}
|
|
290
355
|
return result;
|
|
291
356
|
}
|
|
292
|
-
|
|
357
|
+
function parseDevVarsStrict(content, file = ".env") {
|
|
358
|
+
const result = {};
|
|
359
|
+
const lines = content.split("\n");
|
|
360
|
+
let i = 0;
|
|
361
|
+
while (i < lines.length) {
|
|
362
|
+
const line = lines[i];
|
|
363
|
+
const trimmed = line.trim();
|
|
364
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
365
|
+
i++;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const eq = line.indexOf("=");
|
|
369
|
+
if (eq < 0) {
|
|
370
|
+
throw new Error(`${file}: invalid dotenv syntax on line ${i + 1}. Expected KEY=value.`);
|
|
371
|
+
}
|
|
372
|
+
const keyMatch = line.slice(0, eq).match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*$/);
|
|
373
|
+
if (!keyMatch) {
|
|
374
|
+
throw new Error(`${file}: invalid variable name on line ${i + 1}.`);
|
|
375
|
+
}
|
|
376
|
+
const key = keyMatch[1];
|
|
377
|
+
let value = line.slice(eq + 1);
|
|
378
|
+
let closed = !value.startsWith('"') || hasUnescapedQuote(value.slice(1));
|
|
379
|
+
if (!closed) {
|
|
380
|
+
i++;
|
|
381
|
+
while (i < lines.length) {
|
|
382
|
+
value += "\n" + lines[i];
|
|
383
|
+
if (hasUnescapedQuote(lines[i])) {
|
|
384
|
+
closed = true;
|
|
385
|
+
i++;
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
i++;
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
i++;
|
|
392
|
+
}
|
|
393
|
+
if (value.startsWith('"') && !closed) {
|
|
394
|
+
throw new Error(`${file}: unterminated quoted value for key "${key}".`);
|
|
395
|
+
}
|
|
396
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
397
|
+
value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
398
|
+
}
|
|
399
|
+
result[key] = value;
|
|
400
|
+
}
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
function quoteEnvValue(value) {
|
|
404
|
+
return /^[A-Za-z0-9_./:@-]*$/.test(value) && !value.includes("\n") ? value : `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
405
|
+
}
|
|
406
|
+
function stripSdkManaged(vars) {
|
|
407
|
+
const out = {};
|
|
408
|
+
for (const [name, value] of Object.entries(vars)) {
|
|
409
|
+
if (!SDK_MANAGED_KEYS.has(name)) out[name] = value;
|
|
410
|
+
}
|
|
411
|
+
return out;
|
|
412
|
+
}
|
|
413
|
+
function sortRecord(input) {
|
|
414
|
+
const out = {};
|
|
415
|
+
for (const key of Object.keys(input).sort()) out[key] = input[key];
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
function readManagedEnvSections(appDir) {
|
|
419
|
+
const path = localEnvPath(appDir);
|
|
420
|
+
if (!existsSync2(path)) return { sdk: {}, synced: {}, devOnly: {} };
|
|
421
|
+
return parseManagedEnvSections(readFileSync2(path, "utf-8"));
|
|
422
|
+
}
|
|
423
|
+
function parseManagedEnvSections(content) {
|
|
424
|
+
const sections = { sdk: {}, synced: {}, devOnly: {} };
|
|
425
|
+
const legacyAppLines = [];
|
|
426
|
+
let target = "sdk";
|
|
427
|
+
const lines = content.split("\n");
|
|
428
|
+
for (let i = 0; i < lines.length; i++) {
|
|
429
|
+
const line = lines[i];
|
|
430
|
+
if (line.trim() === MANAGED_ENV_SYNCED_HEADER.trim()) {
|
|
431
|
+
target = "synced";
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (line.trim() === MANAGED_ENV_DEV_ONLY_HEADER.trim()) {
|
|
435
|
+
target = "devOnly";
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (line.trim().startsWith("#")) continue;
|
|
439
|
+
const name = assignmentName(line);
|
|
440
|
+
if (!name) {
|
|
441
|
+
if (line.trim()) legacyAppLines.push(line);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const block = [line];
|
|
445
|
+
const eq = line.indexOf("=");
|
|
446
|
+
const valStart = eq >= 0 ? line.slice(eq + 1) : "";
|
|
447
|
+
if (valStart.startsWith('"') && !hasUnescapedQuote(valStart.slice(1))) {
|
|
448
|
+
i++;
|
|
449
|
+
while (i < lines.length) {
|
|
450
|
+
block.push(lines[i]);
|
|
451
|
+
const closed = hasUnescapedQuote(lines[i]);
|
|
452
|
+
if (closed) break;
|
|
453
|
+
i++;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (target === "sdk" && !SDK_MANAGED_KEYS.has(name)) {
|
|
457
|
+
legacyAppLines.push(...block);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const parsed = parseDevVarsStrict(block.join("\n"), ".env");
|
|
461
|
+
Object.assign(sections[target], parsed);
|
|
462
|
+
}
|
|
463
|
+
if (legacyAppLines.length) {
|
|
464
|
+
Object.assign(sections.synced, stripSdkManaged(parseDevVarsStrict(legacyAppLines.join("\n"), ".env")));
|
|
465
|
+
}
|
|
466
|
+
return sections;
|
|
467
|
+
}
|
|
468
|
+
function assignmentName(line) {
|
|
469
|
+
const eq = line.indexOf("=");
|
|
470
|
+
if (eq < 0) return null;
|
|
471
|
+
const match = line.slice(0, eq).match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*$/);
|
|
472
|
+
return match?.[1] ?? null;
|
|
473
|
+
}
|
|
474
|
+
function readManagedAppEnv(appDir) {
|
|
475
|
+
const sections = readManagedEnvSections(appDir);
|
|
476
|
+
return { ...sections.synced, ...sections.devOnly };
|
|
477
|
+
}
|
|
478
|
+
function readManagedSyncedEnv(appDir) {
|
|
479
|
+
return { ...readManagedEnvSections(appDir).synced };
|
|
480
|
+
}
|
|
481
|
+
function readManagedDevOnlyEnv(appDir) {
|
|
482
|
+
return { ...readManagedEnvSections(appDir).devOnly };
|
|
483
|
+
}
|
|
484
|
+
function writeManagedEnvSections(appDir, sections) {
|
|
485
|
+
ensureLocalEnvIgnored(appDir);
|
|
486
|
+
const lines = [
|
|
487
|
+
MANAGED_ENV_HEADER,
|
|
488
|
+
"# Do not commit this file. Edit secrets with `deepspace secrets`, not by hand."
|
|
489
|
+
];
|
|
490
|
+
for (const [name, value] of Object.entries(sortRecord(sections.sdk))) {
|
|
491
|
+
lines.push(`${name}=${quoteEnvValue(value)}`);
|
|
492
|
+
}
|
|
493
|
+
lines.push("", MANAGED_ENV_SYNCED_HEADER);
|
|
494
|
+
for (const [name, value] of Object.entries(sortRecord(sections.synced))) {
|
|
495
|
+
lines.push(`${name}=${quoteEnvValue(value)}`);
|
|
496
|
+
}
|
|
497
|
+
lines.push("", MANAGED_ENV_DEV_ONLY_HEADER);
|
|
498
|
+
for (const [name, value] of Object.entries(sortRecord(sections.devOnly))) {
|
|
499
|
+
lines.push(`${name}=${quoteEnvValue(value)}`);
|
|
500
|
+
}
|
|
501
|
+
writeFileSync(localEnvPath(appDir), lines.join("\n") + "\n", { mode: 384 });
|
|
502
|
+
}
|
|
503
|
+
function ensureLocalEnvIgnored(appDir) {
|
|
504
|
+
const path = join2(appDir, ".gitignore");
|
|
505
|
+
const existing = existsSync2(path) ? readFileSync2(path, "utf-8") : "";
|
|
506
|
+
const lines = new Set(existing.split("\n").map((line) => line.trim()));
|
|
507
|
+
const additions = [".env", ".dev.vars"].filter((entry) => !lines.has(entry));
|
|
508
|
+
if (!additions.length) return;
|
|
509
|
+
const prefix = existing && !existing.endsWith("\n") ? existing + "\n" : existing;
|
|
510
|
+
writeFileSync(path, `${prefix}${additions.join("\n")}
|
|
511
|
+
`);
|
|
512
|
+
}
|
|
513
|
+
function patchManagedSyncedEnv(appDir, set2 = {}, removeNames = []) {
|
|
514
|
+
const sections = readManagedEnvSections(appDir);
|
|
515
|
+
for (const [name, value] of Object.entries(set2)) sections.synced[name] = value;
|
|
516
|
+
for (const name of removeNames) delete sections.synced[name];
|
|
517
|
+
writeManagedEnvSections(appDir, sections);
|
|
518
|
+
}
|
|
519
|
+
function patchManagedDevOnlyEnv(appDir, set2 = {}, removeNames = []) {
|
|
520
|
+
const sections = readManagedEnvSections(appDir);
|
|
521
|
+
for (const [name, value] of Object.entries(set2)) sections.devOnly[name] = value;
|
|
522
|
+
for (const name of removeNames) delete sections.devOnly[name];
|
|
523
|
+
writeManagedEnvSections(appDir, sections);
|
|
524
|
+
}
|
|
525
|
+
function replaceManagedSyncedEnv(appDir, syncedVars) {
|
|
526
|
+
const sections = readManagedEnvSections(appDir);
|
|
527
|
+
sections.synced = stripSdkManaged(syncedVars);
|
|
528
|
+
writeManagedEnvSections(appDir, sections);
|
|
529
|
+
}
|
|
530
|
+
function moveManagedDevOnlyToSynced(appDir, names) {
|
|
531
|
+
const sections = readManagedEnvSections(appDir);
|
|
532
|
+
for (const name of names) {
|
|
533
|
+
if (name in sections.devOnly) {
|
|
534
|
+
sections.synced[name] = sections.devOnly[name];
|
|
535
|
+
delete sections.devOnly[name];
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
writeManagedEnvSections(appDir, sections);
|
|
539
|
+
}
|
|
540
|
+
async function writeDevVars(appDir, env, ownerId, callerJwt, wranglerEnv) {
|
|
293
541
|
const urls = ENVS[env];
|
|
294
542
|
const publicKey = await fetchPublicKey(urls.auth);
|
|
295
|
-
const appName = readAppName(appDir);
|
|
543
|
+
const appName = readAppName(appDir, wranglerEnv);
|
|
296
544
|
const appOwnerJwt = await mintAppOwnerJwt(urls.auth, callerJwt, appName);
|
|
297
545
|
const appIdentityToken = await fetchAppIdentityToken(urls.deploy, callerJwt, appName);
|
|
298
546
|
const sdkVars = [
|
|
@@ -304,12 +552,19 @@ async function writeDevVars(appDir, env, ownerId, callerJwt) {
|
|
|
304
552
|
`OWNER_USER_ID=${ownerId}`,
|
|
305
553
|
`APP_OWNER_JWT=${appOwnerJwt}`,
|
|
306
554
|
...appIdentityToken ? [`APP_IDENTITY_TOKEN=${appIdentityToken}`] : [],
|
|
555
|
+
`APP_SECRETS_LOCAL_FALLBACK=true`,
|
|
307
556
|
// Open /api/debug/* (proxied to RecordRoom) for dev + tests only.
|
|
308
557
|
// Production secrets never set this, so deployed apps 404 the route.
|
|
309
558
|
`ALLOW_DEBUG_ROUTES=true`
|
|
310
559
|
].join("\n");
|
|
311
|
-
const
|
|
312
|
-
|
|
560
|
+
const sdkEnv = parseDevVarsStrict(sdkVars, "SDK env");
|
|
561
|
+
if (!hasLegacyDevVars(appDir, wranglerEnv)) {
|
|
562
|
+
const sections = readManagedEnvSections(appDir);
|
|
563
|
+
writeManagedEnvSections(appDir, { ...sections, sdk: sdkEnv });
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const devVarsPath = legacyDevVarsPath(appDir, wranglerEnv);
|
|
567
|
+
const existing = existsSync2(devVarsPath) ? readFileSync2(devVarsPath, "utf-8") : "";
|
|
313
568
|
const custom = extractCustomDevVars(existing);
|
|
314
569
|
const body = custom ? `${sdkVars}
|
|
315
570
|
|
|
@@ -433,10 +688,10 @@ async function pollForCompletion(sessionId, codeVerifier) {
|
|
|
433
688
|
return null;
|
|
434
689
|
}
|
|
435
690
|
function storeCredentials(sessionToken, jwt) {
|
|
436
|
-
const dir =
|
|
691
|
+
const dir = join3(homedir(), ".deepspace");
|
|
437
692
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
438
|
-
writeFileSync2(
|
|
439
|
-
writeFileSync2(
|
|
693
|
+
writeFileSync2(join3(dir, "session"), sessionToken, { mode: 384 });
|
|
694
|
+
writeFileSync2(join3(dir, "token"), jwt, { mode: 384 });
|
|
440
695
|
}
|
|
441
696
|
function openBrowser(url) {
|
|
442
697
|
const cmd = platform() === "darwin" ? `open "${url}"` : platform() === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
@@ -475,20 +730,20 @@ async function doEmailLogin(email, password) {
|
|
|
475
730
|
|
|
476
731
|
// src/cli/commands/logout.ts
|
|
477
732
|
import { defineCommand as defineCommand2 } from "citty";
|
|
478
|
-
import { existsSync as
|
|
479
|
-
import { join as
|
|
733
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
734
|
+
import { join as join4 } from "path";
|
|
480
735
|
import { homedir as homedir2 } from "os";
|
|
481
736
|
var AUTH_URL2 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
482
|
-
var DIR =
|
|
483
|
-
var SESSION_PATH =
|
|
484
|
-
var TOKEN_PATH =
|
|
737
|
+
var DIR = join4(homedir2(), ".deepspace");
|
|
738
|
+
var SESSION_PATH = join4(DIR, "session");
|
|
739
|
+
var TOKEN_PATH = join4(DIR, "token");
|
|
485
740
|
var logout_default = defineCommand2({
|
|
486
741
|
meta: {
|
|
487
742
|
name: "logout",
|
|
488
743
|
description: "Sign out and remove cached credentials"
|
|
489
744
|
},
|
|
490
745
|
async run() {
|
|
491
|
-
const sessionToken =
|
|
746
|
+
const sessionToken = existsSync3(SESSION_PATH) ? readFileSync3(SESSION_PATH, "utf-8").trim() : null;
|
|
492
747
|
if (sessionToken) {
|
|
493
748
|
try {
|
|
494
749
|
await fetch(`${AUTH_URL2}/api/auth/sign-out`, {
|
|
@@ -503,7 +758,7 @@ var logout_default = defineCommand2({
|
|
|
503
758
|
}
|
|
504
759
|
let removed = 0;
|
|
505
760
|
for (const path of [SESSION_PATH, TOKEN_PATH]) {
|
|
506
|
-
if (
|
|
761
|
+
if (existsSync3(path)) {
|
|
507
762
|
rmSync(path);
|
|
508
763
|
removed++;
|
|
509
764
|
}
|
|
@@ -517,14 +772,13 @@ var logout_default = defineCommand2({
|
|
|
517
772
|
});
|
|
518
773
|
|
|
519
774
|
// src/cli/commands/dev.ts
|
|
520
|
-
import { defineCommand as
|
|
521
|
-
import {
|
|
522
|
-
import { join as join6, resolve as resolve2 } from "path";
|
|
775
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
776
|
+
import { resolve as resolve3, basename } from "path";
|
|
523
777
|
import spawn from "cross-spawn";
|
|
524
778
|
|
|
525
779
|
// src/cli/auth.ts
|
|
526
|
-
import { readFileSync as
|
|
527
|
-
import { join as
|
|
780
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
781
|
+
import { join as join5 } from "path";
|
|
528
782
|
import { homedir as homedir3 } from "os";
|
|
529
783
|
|
|
530
784
|
// src/cli/jwt.ts
|
|
@@ -538,20 +792,20 @@ function decodeJwtPayload(token2) {
|
|
|
538
792
|
|
|
539
793
|
// src/cli/auth.ts
|
|
540
794
|
var AUTH_URL3 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
541
|
-
var DIR2 =
|
|
542
|
-
var SESSION_PATH2 =
|
|
543
|
-
var TOKEN_PATH2 =
|
|
795
|
+
var DIR2 = join5(homedir3(), ".deepspace");
|
|
796
|
+
var SESSION_PATH2 = join5(DIR2, "session");
|
|
797
|
+
var TOKEN_PATH2 = join5(DIR2, "token");
|
|
544
798
|
async function ensureToken() {
|
|
545
|
-
if (
|
|
546
|
-
const existing =
|
|
799
|
+
if (existsSync4(TOKEN_PATH2)) {
|
|
800
|
+
const existing = readFileSync4(TOKEN_PATH2, "utf-8").trim();
|
|
547
801
|
if (isTokenValid(existing)) {
|
|
548
802
|
return existing;
|
|
549
803
|
}
|
|
550
804
|
}
|
|
551
|
-
if (!
|
|
805
|
+
if (!existsSync4(SESSION_PATH2)) {
|
|
552
806
|
throw new Error("Not logged in. Run `deepspace login` first.");
|
|
553
807
|
}
|
|
554
|
-
const sessionToken =
|
|
808
|
+
const sessionToken = readFileSync4(SESSION_PATH2, "utf-8").trim();
|
|
555
809
|
const token2 = await exchangeSession(AUTH_URL3, sessionToken);
|
|
556
810
|
if (!token2) {
|
|
557
811
|
throw new Error("Session expired. Run `deepspace login` to re-authenticate.");
|
|
@@ -569,27 +823,86 @@ function isTokenValid(jwt) {
|
|
|
569
823
|
}
|
|
570
824
|
}
|
|
571
825
|
|
|
826
|
+
// src/cli/lib/app-context.ts
|
|
827
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
828
|
+
import { join as join6, resolve, dirname } from "path";
|
|
829
|
+
import { parse as parseToml2 } from "smol-toml";
|
|
830
|
+
function detectAppName(cwd = process.cwd()) {
|
|
831
|
+
const wranglerPath = join6(resolve(cwd), "wrangler.toml");
|
|
832
|
+
if (!existsSync5(wranglerPath)) return null;
|
|
833
|
+
try {
|
|
834
|
+
const cfg = parseToml2(readFileSync5(wranglerPath, "utf-8"));
|
|
835
|
+
return typeof cfg.name === "string" && cfg.name.length > 0 ? cfg.name : null;
|
|
836
|
+
} catch {
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
function requireAppName(explicit, cwd) {
|
|
841
|
+
if (explicit) return explicit;
|
|
842
|
+
const detected = detectAppName(cwd);
|
|
843
|
+
if (detected) return detected;
|
|
844
|
+
throw new Error(
|
|
845
|
+
"No app specified. Pass --app <name>, or run from an app directory with a wrangler.toml."
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
function findAppDir(start = process.cwd()) {
|
|
849
|
+
let dir = resolve(start);
|
|
850
|
+
for (; ; ) {
|
|
851
|
+
if (existsSync5(join6(dir, "wrangler.toml"))) return dir;
|
|
852
|
+
const parent = dirname(dir);
|
|
853
|
+
if (parent === dir) return null;
|
|
854
|
+
dir = parent;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function findChildApps(dir) {
|
|
858
|
+
try {
|
|
859
|
+
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync5(join6(dir, e.name, "wrangler.toml"))).map((e) => e.name).sort();
|
|
860
|
+
} catch {
|
|
861
|
+
return [];
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
function writeLaunchConfigIfMissing(appDir, appName, port) {
|
|
865
|
+
const launchPath = join6(appDir, ".claude", "launch.json");
|
|
866
|
+
if (existsSync5(launchPath)) return;
|
|
867
|
+
const config = {
|
|
868
|
+
version: "0.0.1",
|
|
869
|
+
configurations: [
|
|
870
|
+
{
|
|
871
|
+
name: appName,
|
|
872
|
+
runtimeExecutable: "npx",
|
|
873
|
+
runtimeArgs: ["deepspace", "dev", "--port", String(port)],
|
|
874
|
+
port
|
|
875
|
+
}
|
|
876
|
+
]
|
|
877
|
+
};
|
|
878
|
+
try {
|
|
879
|
+
mkdirSync3(join6(appDir, ".claude"), { recursive: true });
|
|
880
|
+
writeFileSync4(launchPath, JSON.stringify(config, null, 2) + "\n");
|
|
881
|
+
} catch {
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
572
885
|
// src/cli/lib/install-status.ts
|
|
573
|
-
import { existsSync as
|
|
574
|
-
import { join as
|
|
886
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
887
|
+
import { join as join7 } from "path";
|
|
575
888
|
function ensureInstallReady(appDir) {
|
|
576
|
-
if (
|
|
577
|
-
const sentinelDir =
|
|
578
|
-
const errPath =
|
|
579
|
-
const startedPath =
|
|
580
|
-
const donePath =
|
|
581
|
-
const logPath =
|
|
582
|
-
if (
|
|
889
|
+
if (existsSync6(join7(appDir, "node_modules", "deepspace", "package.json"))) return;
|
|
890
|
+
const sentinelDir = join7(appDir, ".deepspace");
|
|
891
|
+
const errPath = join7(sentinelDir, "install.err");
|
|
892
|
+
const startedPath = join7(sentinelDir, "install.started");
|
|
893
|
+
const donePath = join7(sentinelDir, "install.done");
|
|
894
|
+
const logPath = join7(sentinelDir, "install.log");
|
|
895
|
+
if (existsSync6(errPath)) {
|
|
583
896
|
console.error("Background install failed:");
|
|
584
|
-
console.error(
|
|
585
|
-
if (
|
|
897
|
+
console.error(readFileSync6(errPath, "utf-8").trimEnd());
|
|
898
|
+
if (existsSync6(logPath)) console.error(`
|
|
586
899
|
Full log: ${logPath}`);
|
|
587
900
|
console.error("\nRun `npm install` (or `bun install`) manually, then retry.");
|
|
588
901
|
process.exit(1);
|
|
589
902
|
}
|
|
590
|
-
if (
|
|
903
|
+
if (existsSync6(startedPath) && !existsSync6(donePath)) {
|
|
591
904
|
console.error("Dependencies are still installing in the background.");
|
|
592
|
-
if (
|
|
905
|
+
if (existsSync6(logPath)) console.error(`Tail progress: tail -f ${logPath}`);
|
|
593
906
|
console.error("Retry once it finishes.");
|
|
594
907
|
process.exit(1);
|
|
595
908
|
}
|
|
@@ -599,12 +912,12 @@ Full log: ${logPath}`);
|
|
|
599
912
|
|
|
600
913
|
// src/cli/lib/preflight.ts
|
|
601
914
|
import { execFileSync } from "child_process";
|
|
602
|
-
import { existsSync as
|
|
603
|
-
import { resolve } from "path";
|
|
915
|
+
import { existsSync as existsSync7 } from "fs";
|
|
916
|
+
import { resolve as resolve2 } from "path";
|
|
604
917
|
var DLL_NOT_FOUND = -1073741515;
|
|
605
918
|
function preflightWindowsWorkerd(appDir) {
|
|
606
919
|
if (process.platform !== "win32") return;
|
|
607
|
-
const workerdExe =
|
|
920
|
+
const workerdExe = resolve2(
|
|
608
921
|
appDir,
|
|
609
922
|
"node_modules",
|
|
610
923
|
"@cloudflare",
|
|
@@ -612,7 +925,7 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
612
925
|
"bin",
|
|
613
926
|
"workerd.exe"
|
|
614
927
|
);
|
|
615
|
-
if (!
|
|
928
|
+
if (!existsSync7(workerdExe)) return;
|
|
616
929
|
try {
|
|
617
930
|
execFileSync(workerdExe, ["--help"], { stdio: "pipe", timeout: 1e4 });
|
|
618
931
|
return;
|
|
@@ -632,11 +945,527 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
632
945
|
console.error("");
|
|
633
946
|
process.exit(1);
|
|
634
947
|
}
|
|
635
|
-
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// src/cli/commands/secrets.ts
|
|
951
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
952
|
+
import { join as join8 } from "path";
|
|
953
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
954
|
+
var SECRET_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
955
|
+
var ERROR_SNIPPET_CHARS = 500;
|
|
956
|
+
var COLLABORATOR_ROUTES_404 = "Platform API does not expose app-collaborator routes; deploy/update api-worker before using collaborator commands.";
|
|
957
|
+
var COLLABORATOR_SCHEMA_NOT_MIGRATED = "Platform API is missing app-collaborator database tables. Run api-worker /_migrate with x-migration-secret, then retry collaborator commands.";
|
|
958
|
+
function parseAssignment(value) {
|
|
959
|
+
const eq = value.indexOf("=");
|
|
960
|
+
if (eq <= 0) throw new Error("Use KEY=value.");
|
|
961
|
+
const name = value.slice(0, eq);
|
|
962
|
+
if (!SECRET_NAME_RE.test(name)) throw new Error(`Invalid secret name "${name}".`);
|
|
963
|
+
if (SDK_MANAGED_KEYS.has(name)) throw new Error(`${name} is managed by DeepSpace and cannot be set as an app secret.`);
|
|
964
|
+
return [name, value.slice(eq + 1)];
|
|
965
|
+
}
|
|
966
|
+
function devTargetPath() {
|
|
967
|
+
return join8(process.cwd(), ".dev.vars");
|
|
968
|
+
}
|
|
969
|
+
function readDevTarget() {
|
|
970
|
+
if (!hasLegacyDevVars(process.cwd())) {
|
|
971
|
+
return readManagedAppEnv(process.cwd());
|
|
972
|
+
}
|
|
973
|
+
const path = devTargetPath();
|
|
974
|
+
if (!existsSync8(path)) return {};
|
|
975
|
+
return stripSdkManaged(parseDevVars(splitDevTarget(readFileSync7(path, "utf-8")).custom));
|
|
976
|
+
}
|
|
977
|
+
function readSyncedTarget() {
|
|
978
|
+
if (hasLegacyDevVars(process.cwd())) return readDevTarget();
|
|
979
|
+
return readManagedSyncedEnv(process.cwd());
|
|
980
|
+
}
|
|
981
|
+
function readDevOnlyTarget() {
|
|
982
|
+
if (hasLegacyDevVars(process.cwd())) return readDevTarget();
|
|
983
|
+
return readManagedDevOnlyEnv(process.cwd());
|
|
984
|
+
}
|
|
985
|
+
function writeLocalPatch(set2 = {}, removeNames = []) {
|
|
986
|
+
for (const name of [...Object.keys(set2), ...removeNames]) {
|
|
987
|
+
if (SDK_MANAGED_KEYS.has(name)) {
|
|
988
|
+
throw new Error(`${name} is managed by DeepSpace and cannot be set as an app secret.`);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (hasLegacyDevVars(process.cwd())) writeDevTargetPatch(set2, removeNames);
|
|
992
|
+
else {
|
|
993
|
+
patchManagedDevOnlyEnv(process.cwd(), set2, removeNames);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
function quote(value) {
|
|
997
|
+
return quoteEnvValue(value);
|
|
998
|
+
}
|
|
999
|
+
function splitLocal(raw) {
|
|
1000
|
+
const idx = raw.indexOf(DEV_VARS_DIVIDER);
|
|
1001
|
+
if (idx < 0) return { top: "", custom: raw };
|
|
1002
|
+
const after = raw.slice(idx + DEV_VARS_DIVIDER.length);
|
|
1003
|
+
return {
|
|
1004
|
+
top: raw.slice(0, idx).trimEnd(),
|
|
1005
|
+
custom: after.startsWith("\n") ? after.slice(1) : after
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
function splitDevTarget(raw) {
|
|
1009
|
+
const idx = raw.indexOf(DEV_VARS_DIVIDER);
|
|
1010
|
+
if (idx >= 0) return splitLocal(raw);
|
|
1011
|
+
const lines = raw.split("\n");
|
|
1012
|
+
const top = [];
|
|
1013
|
+
const custom = [];
|
|
1014
|
+
let target = top;
|
|
1015
|
+
let i = 0;
|
|
1016
|
+
while (i < lines.length) {
|
|
1017
|
+
const line = lines[i];
|
|
1018
|
+
const name = assignmentName2(line);
|
|
1019
|
+
const eq = line.indexOf("=");
|
|
1020
|
+
const valStart = eq >= 0 ? line.slice(eq + 1) : "";
|
|
1021
|
+
const block = [line];
|
|
1022
|
+
i++;
|
|
1023
|
+
if (name && valStart.startsWith('"') && !hasUnescapedQuote2(valStart.slice(1))) {
|
|
1024
|
+
while (i < lines.length) {
|
|
1025
|
+
block.push(lines[i]);
|
|
1026
|
+
const closed = hasUnescapedQuote2(lines[i]);
|
|
1027
|
+
i++;
|
|
1028
|
+
if (closed) break;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
if (name && !SDK_MANAGED_KEYS.has(name)) target = custom;
|
|
1032
|
+
target.push(...block);
|
|
1033
|
+
}
|
|
1034
|
+
while (top.length && top[top.length - 1] === "") top.pop();
|
|
1035
|
+
while (custom.length && custom[custom.length - 1] === "") custom.pop();
|
|
1036
|
+
return { top: top.join("\n"), custom: custom.join("\n") };
|
|
1037
|
+
}
|
|
1038
|
+
function assignmentName2(line) {
|
|
1039
|
+
const eq = line.indexOf("=");
|
|
1040
|
+
if (eq < 0) return null;
|
|
1041
|
+
const match = line.slice(0, eq).match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*$/);
|
|
1042
|
+
return match?.[1] ?? null;
|
|
1043
|
+
}
|
|
1044
|
+
function hasUnescapedQuote2(s) {
|
|
1045
|
+
let escaped = false;
|
|
1046
|
+
for (const ch of s) {
|
|
1047
|
+
if (escaped) {
|
|
1048
|
+
escaped = false;
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
if (ch === "\\") {
|
|
1052
|
+
escaped = true;
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
if (ch === '"') return true;
|
|
1056
|
+
}
|
|
1057
|
+
return false;
|
|
1058
|
+
}
|
|
1059
|
+
function rewriteCustomSection(custom, set2, remove2) {
|
|
1060
|
+
const lines = custom.split("\n");
|
|
1061
|
+
const out = [];
|
|
1062
|
+
const pending = new Set(Object.keys(set2));
|
|
1063
|
+
let i = 0;
|
|
1064
|
+
while (i < lines.length) {
|
|
1065
|
+
const line = lines[i];
|
|
1066
|
+
const name = assignmentName2(line);
|
|
1067
|
+
const eq = line.indexOf("=");
|
|
1068
|
+
const valStart = eq >= 0 ? line.slice(eq + 1) : "";
|
|
1069
|
+
const block = [line];
|
|
1070
|
+
i++;
|
|
1071
|
+
if (name && valStart.startsWith('"') && !hasUnescapedQuote2(valStart.slice(1))) {
|
|
1072
|
+
while (i < lines.length) {
|
|
1073
|
+
block.push(lines[i]);
|
|
1074
|
+
const closed = hasUnescapedQuote2(lines[i]);
|
|
1075
|
+
i++;
|
|
1076
|
+
if (closed) break;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
if (name && (remove2.has(name) || pending.has(name))) {
|
|
1080
|
+
if (pending.has(name)) {
|
|
1081
|
+
out.push(`${name}=${quote(set2[name])}`);
|
|
1082
|
+
pending.delete(name);
|
|
1083
|
+
}
|
|
1084
|
+
continue;
|
|
1085
|
+
}
|
|
1086
|
+
out.push(...block);
|
|
1087
|
+
}
|
|
1088
|
+
while (out.length && out[out.length - 1] === "") out.pop();
|
|
1089
|
+
for (const name of Array.from(pending).sort()) {
|
|
1090
|
+
out.push(`${name}=${quote(set2[name])}`);
|
|
1091
|
+
}
|
|
1092
|
+
return out.join("\n");
|
|
1093
|
+
}
|
|
1094
|
+
function writeDevTargetPatch(set2 = {}, removeNames = []) {
|
|
1095
|
+
const path = devTargetPath();
|
|
1096
|
+
const existing = existsSync8(path) ? readFileSync7(path, "utf-8") : "";
|
|
1097
|
+
const { top, custom: existingCustom } = splitDevTarget(existing);
|
|
1098
|
+
const custom = rewriteCustomSection(existingCustom, set2, new Set(removeNames));
|
|
1099
|
+
const body = top ? `${top}
|
|
1100
|
+
|
|
1101
|
+
${DEV_VARS_DIVIDER}
|
|
1102
|
+
${custom}${custom ? "\n" : ""}` : `${DEV_VARS_DIVIDER}
|
|
1103
|
+
${custom}${custom ? "\n" : ""}`;
|
|
1104
|
+
writeFileSync5(path, body);
|
|
1105
|
+
}
|
|
1106
|
+
async function remoteDeployState(appName, token2) {
|
|
1107
|
+
const res = await fetch(`${process.env.DEEPSPACE_DEPLOY_URL ?? "https://deploy-worker.deep.space"}/api/deploy/${encodeURIComponent(appName)}`, {
|
|
1108
|
+
headers: { Authorization: `Bearer ${token2}` }
|
|
1109
|
+
});
|
|
1110
|
+
const text2 = await res.text().catch(() => "");
|
|
1111
|
+
let body = {};
|
|
1112
|
+
if (text2) {
|
|
1113
|
+
try {
|
|
1114
|
+
body = JSON.parse(text2);
|
|
1115
|
+
} catch (err) {
|
|
1116
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1117
|
+
throw new Error(`Could not determine deploy status for ${appName}: invalid JSON response (${message}).`);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
if (!res.ok) {
|
|
1121
|
+
const detail = body.error ?? (text2 || "unknown error");
|
|
1122
|
+
throw new Error(`Could not determine deploy status for ${appName} (${res.status}): ${detail}`);
|
|
1123
|
+
}
|
|
1124
|
+
if (body.deployed === true) return "deployed";
|
|
1125
|
+
if (body.deployed === false) return "not-deployed";
|
|
1126
|
+
throw new Error(`Could not determine deploy status for ${appName}: response missing "deployed".`);
|
|
1127
|
+
}
|
|
1128
|
+
async function remoteAvailable(appName, token2) {
|
|
1129
|
+
return await remoteDeployState(appName, token2) === "deployed";
|
|
1130
|
+
}
|
|
1131
|
+
async function mintScopedSecretsToken(appName, token2) {
|
|
1132
|
+
const body = await collaboratorFetch(appName, token2, "/secrets-token", { method: "POST" });
|
|
1133
|
+
if (!body.token) throw new Error("Platform API did not return an app-secret access token.");
|
|
1134
|
+
return body.token;
|
|
1135
|
+
}
|
|
1136
|
+
async function remoteFetch(appName, token2, path = "", init = {}) {
|
|
1137
|
+
const scopedToken = await mintScopedSecretsToken(appName, token2);
|
|
1138
|
+
const res = await fetch(`https://${appName}.app.space/_deepspace/secrets${path}`, {
|
|
1139
|
+
...init,
|
|
1140
|
+
headers: {
|
|
1141
|
+
"Content-Type": "application/json",
|
|
1142
|
+
Authorization: `Bearer ${scopedToken}`,
|
|
1143
|
+
...init.headers ?? {}
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
const contentType = res.headers.get("content-type") ?? "unknown";
|
|
1147
|
+
const text2 = await res.text().catch(() => "");
|
|
1148
|
+
let body = null;
|
|
1149
|
+
let parseError = null;
|
|
1150
|
+
if (text2 && contentType.toLowerCase().includes("json")) {
|
|
1151
|
+
try {
|
|
1152
|
+
body = JSON.parse(text2);
|
|
1153
|
+
} catch (err) {
|
|
1154
|
+
parseError = err instanceof Error ? err.message : String(err);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
if (!res.ok) {
|
|
1158
|
+
if (body?.error) throw new Error(body.error);
|
|
1159
|
+
const snippet = text2 ? ` Body: ${text2.slice(0, ERROR_SNIPPET_CHARS)}` : " Body: <empty>";
|
|
1160
|
+
const parseDetail2 = parseError ? ` JSON parse failed: ${parseError}.` : "";
|
|
1161
|
+
throw new Error(`Remote secrets request failed (${res.status}; content-type: ${contentType}).${parseDetail2}${snippet}`);
|
|
1162
|
+
}
|
|
1163
|
+
if (body) return body;
|
|
1164
|
+
if (!text2) return {};
|
|
1165
|
+
const parseDetail = parseError ? ` JSON parse failed: ${parseError}.` : "";
|
|
1166
|
+
throw new Error(`Remote secrets response was not JSON (${res.status}; content-type: ${contentType}).${parseDetail} Body: ${text2.slice(0, ERROR_SNIPPET_CHARS)}`);
|
|
1167
|
+
}
|
|
1168
|
+
function apiUrl() {
|
|
1169
|
+
return process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
1170
|
+
}
|
|
1171
|
+
async function collaboratorFetch(appName, token2, path, init = {}) {
|
|
1172
|
+
const url = `${apiUrl()}/api/app-collaborators/${encodeURIComponent(appName)}${path}`;
|
|
1173
|
+
const res = await fetch(url, {
|
|
1174
|
+
...init,
|
|
1175
|
+
headers: {
|
|
1176
|
+
"Content-Type": "application/json",
|
|
1177
|
+
Authorization: `Bearer ${token2}`,
|
|
1178
|
+
...init.headers ?? {}
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
const text2 = await res.text().catch(() => "");
|
|
1182
|
+
let body = {};
|
|
1183
|
+
if (text2) {
|
|
1184
|
+
try {
|
|
1185
|
+
body = JSON.parse(text2);
|
|
1186
|
+
} catch {
|
|
1187
|
+
if (res.status === 404) throw new Error(COLLABORATOR_ROUTES_404);
|
|
1188
|
+
if (!res.ok) throw new Error(`Collaborator request failed (${res.status}): ${text2.slice(0, ERROR_SNIPPET_CHARS)}`);
|
|
1189
|
+
throw new Error(`Collaborator response was not JSON (${res.status}): ${text2.slice(0, ERROR_SNIPPET_CHARS)}`);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
if (!res.ok) {
|
|
1193
|
+
if (res.status === 404 && (!body.error || body.error === "Not found" || body.error === "not_found")) {
|
|
1194
|
+
throw new Error(COLLABORATOR_ROUTES_404);
|
|
1195
|
+
}
|
|
1196
|
+
if (body.error === "platform_schema_not_migrated") {
|
|
1197
|
+
const diagnostic = body;
|
|
1198
|
+
const action = typeof diagnostic.action === "string" ? ` ${diagnostic.action}` : "";
|
|
1199
|
+
throw new Error(`${COLLABORATOR_SCHEMA_NOT_MIGRATED}${action}`);
|
|
1200
|
+
}
|
|
1201
|
+
throw new Error(body.error ?? `Collaborator request failed (${res.status})`);
|
|
1202
|
+
}
|
|
1203
|
+
return body;
|
|
1204
|
+
}
|
|
1205
|
+
async function requireRemoteSecretsAvailable(appName, token2) {
|
|
1206
|
+
const state = await remoteDeployState(appName, token2);
|
|
1207
|
+
if (state === "deployed") return;
|
|
1208
|
+
throw new Error(
|
|
1209
|
+
"Remote secrets are available after the first deploy. Deploy this app first, then run `deepspace secrets push`; `deepspace secrets set --dev` writes dev-only secrets for `deepspace dev` without touching remote."
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
function diffMaps(local, remote) {
|
|
1213
|
+
const localOnly = [];
|
|
1214
|
+
const remoteOnly = [];
|
|
1215
|
+
const changed = [];
|
|
1216
|
+
const matching = [];
|
|
1217
|
+
for (const [name, value] of Object.entries(local)) {
|
|
1218
|
+
if (!(name in remote)) localOnly.push(name);
|
|
1219
|
+
else if (remote[name] !== value) changed.push(name);
|
|
1220
|
+
else matching.push(name);
|
|
1221
|
+
}
|
|
1222
|
+
for (const name of Object.keys(remote)) {
|
|
1223
|
+
if (!(name in local)) remoteOnly.push(name);
|
|
1224
|
+
}
|
|
1225
|
+
return { localOnly, remoteOnly, changed, matching };
|
|
1226
|
+
}
|
|
1227
|
+
function diffNames(local, remote) {
|
|
1228
|
+
const localSet = new Set(local);
|
|
1229
|
+
const remoteSet = new Set(remote);
|
|
1230
|
+
return {
|
|
1231
|
+
localOnly: local.filter((name) => !remoteSet.has(name)).sort(),
|
|
1232
|
+
remoteOnly: remote.filter((name) => !localSet.has(name)).sort()
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
function statusLine(d, remote) {
|
|
1236
|
+
if (!remote) return "Secrets: remote unavailable until first deploy; dev-only .env section updated.";
|
|
1237
|
+
if (!d || !d.localOnly.length && !d.remoteOnly.length && !d.changed.length) {
|
|
1238
|
+
return "Secrets: synced .env section matches remote.";
|
|
1239
|
+
}
|
|
1240
|
+
return `Secrets: ${d.localOnly.length} local-only, ${d.remoteOnly.length} remote-only, ${d.changed.length} changed. Run \`deepspace secrets diff\`.`;
|
|
1241
|
+
}
|
|
1242
|
+
function nameStatusLine(d) {
|
|
1243
|
+
if (!d.localOnly.length && !d.remoteOnly.length) {
|
|
1244
|
+
return "Secrets: synced .env section has the same names as remote.";
|
|
1245
|
+
}
|
|
1246
|
+
return `Secrets: ${d.localOnly.length} local-only, ${d.remoteOnly.length} remote-only. Run \`deepspace secrets diff\` to compare values.`;
|
|
1247
|
+
}
|
|
1248
|
+
async function compare(appName, token2) {
|
|
1249
|
+
if (!await remoteAvailable(appName, token2)) return null;
|
|
1250
|
+
const local = readSyncedTarget();
|
|
1251
|
+
const remote = await remoteFetch(appName, token2, "/pull");
|
|
1252
|
+
return diffMaps(local, remote.secrets ?? {});
|
|
1253
|
+
}
|
|
1254
|
+
var set = defineCommand3({
|
|
1255
|
+
meta: { name: "set", description: "Set an app secret" },
|
|
1256
|
+
args: {
|
|
1257
|
+
assignment: { type: "positional", required: true, description: "KEY=value" },
|
|
1258
|
+
dev: {
|
|
1259
|
+
type: "boolean",
|
|
1260
|
+
default: false,
|
|
1261
|
+
description: "Write only to the dev-only .env section used by `deepspace dev`; never sync to the deployed app"
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
async run({ args }) {
|
|
1265
|
+
const [name, value] = parseAssignment(String(args.assignment));
|
|
1266
|
+
const token2 = await ensureToken();
|
|
1267
|
+
const appName = requireAppName(void 0);
|
|
1268
|
+
if (args.dev) {
|
|
1269
|
+
writeLocalPatch({ [name]: value });
|
|
1270
|
+
console.log("Secrets: updated dev-only .env section for `deepspace dev`; remote was not changed.");
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
const state = await remoteDeployState(appName, token2);
|
|
1274
|
+
if (state === "not-deployed") {
|
|
1275
|
+
writeLocalPatch({ [name]: value });
|
|
1276
|
+
console.log("Secrets: app is not deployed; wrote to dev-only .env section. Run `deepspace secrets push` after deploy.");
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
await remoteFetch(appName, token2, "", { method: "PATCH", body: JSON.stringify({ set: { [name]: value } }) });
|
|
1280
|
+
if (hasLegacyDevVars(process.cwd())) writeDevTargetPatch({ [name]: value });
|
|
1281
|
+
else patchManagedSyncedEnv(process.cwd(), { [name]: value });
|
|
1282
|
+
console.log("Secrets: updated remote and synced .env section.");
|
|
1283
|
+
}
|
|
1284
|
+
});
|
|
1285
|
+
var unset = defineCommand3({
|
|
1286
|
+
meta: { name: "unset", description: "Remove an app secret" },
|
|
1287
|
+
args: {
|
|
1288
|
+
name: { type: "positional", required: true, description: "Secret name" },
|
|
1289
|
+
dev: {
|
|
1290
|
+
type: "boolean",
|
|
1291
|
+
default: false,
|
|
1292
|
+
description: "Remove only from the dev-only .env section used by `deepspace dev`; never sync to the deployed app"
|
|
1293
|
+
}
|
|
1294
|
+
},
|
|
1295
|
+
async run({ args }) {
|
|
1296
|
+
const name = String(args.name);
|
|
1297
|
+
if (!SECRET_NAME_RE.test(name)) throw new Error(`Invalid secret name "${name}".`);
|
|
1298
|
+
const token2 = await ensureToken();
|
|
1299
|
+
const appName = requireAppName(void 0);
|
|
1300
|
+
if (args.dev) {
|
|
1301
|
+
writeLocalPatch({}, [name]);
|
|
1302
|
+
console.log("Secrets: removed from dev-only .env section; remote was not changed.");
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
const state = await remoteDeployState(appName, token2);
|
|
1306
|
+
if (state === "not-deployed") {
|
|
1307
|
+
writeLocalPatch({}, [name]);
|
|
1308
|
+
console.log("Secrets: app is not deployed; removed from dev-only .env section if present.");
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
await remoteFetch(appName, token2, "", { method: "PATCH", body: JSON.stringify({ delete: [name] }) });
|
|
1312
|
+
if (hasLegacyDevVars(process.cwd())) writeDevTargetPatch({}, [name]);
|
|
1313
|
+
else patchManagedSyncedEnv(process.cwd(), {}, [name]);
|
|
1314
|
+
console.log("Secrets: removed from remote and synced .env section.");
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
var list = defineCommand3({
|
|
1318
|
+
meta: { name: "list", description: "List app secret names and metadata" },
|
|
1319
|
+
async run() {
|
|
1320
|
+
const token2 = await ensureToken();
|
|
1321
|
+
const appName = requireAppName(void 0);
|
|
1322
|
+
await requireRemoteSecretsAvailable(appName, token2);
|
|
1323
|
+
const body = await remoteFetch(appName, token2);
|
|
1324
|
+
for (const item of body.secrets ?? []) console.log(`${item.name} version=${item.version} updated=${item.updatedAt}`);
|
|
1325
|
+
const d = diffNames(Object.keys(readSyncedTarget()), (body.secrets ?? []).map((item) => item.name));
|
|
1326
|
+
console.log(nameStatusLine(d));
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1329
|
+
var pull = defineCommand3({
|
|
1330
|
+
meta: { name: "pull", description: "Pull remote app secrets into the synced .env section" },
|
|
1331
|
+
async run() {
|
|
1332
|
+
const token2 = await ensureToken();
|
|
1333
|
+
const appName = requireAppName(void 0);
|
|
1334
|
+
await requireRemoteSecretsAvailable(appName, token2);
|
|
1335
|
+
const body = await remoteFetch(appName, token2, "/pull");
|
|
1336
|
+
const remoteSecrets = body.secrets ?? {};
|
|
1337
|
+
if (hasLegacyDevVars(process.cwd())) {
|
|
1338
|
+
writeDevTargetPatch(remoteSecrets);
|
|
1339
|
+
} else {
|
|
1340
|
+
replaceManagedSyncedEnv(process.cwd(), remoteSecrets);
|
|
1341
|
+
}
|
|
1342
|
+
const count = Object.keys(remoteSecrets).length;
|
|
1343
|
+
console.log(`Secrets: pulled ${count} remote secret${count === 1 ? "" : "s"} into synced .env section.`);
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
var push = defineCommand3({
|
|
1347
|
+
meta: { name: "push", description: "Push dev-only .env secrets to remote and move them to synced" },
|
|
1348
|
+
async run() {
|
|
1349
|
+
const token2 = await ensureToken();
|
|
1350
|
+
const appName = requireAppName(void 0);
|
|
1351
|
+
const local = readDevOnlyTarget();
|
|
1352
|
+
await requireRemoteSecretsAvailable(appName, token2);
|
|
1353
|
+
const names = Object.keys(local);
|
|
1354
|
+
if (!names.length) {
|
|
1355
|
+
console.log("Secrets: no dev-only .env secrets to push.");
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
await remoteFetch(appName, token2, "", { method: "PATCH", body: JSON.stringify({ set: local }) });
|
|
1359
|
+
if (hasLegacyDevVars(process.cwd())) {
|
|
1360
|
+
console.log(`Secrets: pushed ${names.length} dev-only secret${names.length === 1 ? "" : "s"} to remote.`);
|
|
1361
|
+
} else {
|
|
1362
|
+
moveManagedDevOnlyToSynced(process.cwd(), names);
|
|
1363
|
+
console.log(`Secrets: pushed ${names.length} dev-only secret${names.length === 1 ? "" : "s"} to remote and moved ${names.length === 1 ? "it" : "them"} to synced .env section.`);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
var diff = defineCommand3({
|
|
1368
|
+
meta: { name: "diff", description: "Compare synced .env section with remote app secrets" },
|
|
1369
|
+
async run() {
|
|
1370
|
+
const token2 = await ensureToken();
|
|
1371
|
+
const appName = requireAppName(void 0);
|
|
1372
|
+
await requireRemoteSecretsAvailable(appName, token2);
|
|
1373
|
+
const d = await compare(appName, token2);
|
|
1374
|
+
for (const name of d?.localOnly ?? []) console.log(`local-only ${name}`);
|
|
1375
|
+
for (const name of d?.remoteOnly ?? []) console.log(`remote-only ${name}`);
|
|
1376
|
+
for (const name of d?.changed ?? []) console.log(`changed ${name}`);
|
|
1377
|
+
console.log(statusLine(d, true));
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1380
|
+
var collaboratorsList = defineCommand3({
|
|
1381
|
+
meta: { name: "list", description: "List app secret collaborators" },
|
|
1382
|
+
async run() {
|
|
1383
|
+
const token2 = await ensureToken();
|
|
1384
|
+
const appName = requireAppName(void 0);
|
|
1385
|
+
const body = await collaboratorFetch(appName, token2, "/collaborators");
|
|
1386
|
+
for (const item of body.collaborators ?? []) {
|
|
1387
|
+
console.log(`${item.emailDisplay} ${item.userId} created=${new Date(item.createdAt).toISOString()}`);
|
|
1388
|
+
}
|
|
1389
|
+
if (!body.collaborators?.length) console.log("No secrets collaborators.");
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
var collaboratorsAdd = defineCommand3({
|
|
1393
|
+
meta: { name: "add", description: "Add an existing DeepSpace user as a secrets collaborator" },
|
|
1394
|
+
args: {
|
|
1395
|
+
email: { type: "positional", required: true, description: "Collaborator email" }
|
|
1396
|
+
},
|
|
1397
|
+
async run({ args }) {
|
|
1398
|
+
const token2 = await ensureToken();
|
|
1399
|
+
const appName = requireAppName(void 0);
|
|
1400
|
+
const body = await collaboratorFetch(appName, token2, "/collaborators", {
|
|
1401
|
+
method: "POST",
|
|
1402
|
+
body: JSON.stringify({ email: String(args.email) })
|
|
1403
|
+
});
|
|
1404
|
+
console.log(`Added ${body.collaborator.emailDisplay} (${body.collaborator.userId}) as a secrets collaborator.`);
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
var collaboratorsRemove = defineCommand3({
|
|
1408
|
+
meta: { name: "remove", description: "Remove a secrets collaborator" },
|
|
1409
|
+
args: {
|
|
1410
|
+
target: { type: "positional", required: true, description: "Collaborator email or user id" }
|
|
1411
|
+
},
|
|
1412
|
+
async run({ args }) {
|
|
1413
|
+
const token2 = await ensureToken();
|
|
1414
|
+
const appName = requireAppName(void 0);
|
|
1415
|
+
const target = String(args.target);
|
|
1416
|
+
let userId = target;
|
|
1417
|
+
if (target.includes("@")) {
|
|
1418
|
+
const body = await collaboratorFetch(appName, token2, "/collaborators");
|
|
1419
|
+
const normalized = target.trim().toLowerCase();
|
|
1420
|
+
const match = body.collaborators.find((item) => item.emailNormalized === normalized);
|
|
1421
|
+
if (!match) throw new Error(`No collaborator found for ${target}.`);
|
|
1422
|
+
userId = match.userId;
|
|
1423
|
+
}
|
|
1424
|
+
await collaboratorFetch(appName, token2, `/collaborators/${encodeURIComponent(userId)}`, { method: "DELETE" });
|
|
1425
|
+
console.log(`Removed secrets collaborator ${target}.`);
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
var invitesList = defineCommand3({
|
|
1429
|
+
meta: { name: "list", description: "List app secret collaborator invites" },
|
|
1430
|
+
async run() {
|
|
1431
|
+
const token2 = await ensureToken();
|
|
1432
|
+
const appName = requireAppName(void 0);
|
|
1433
|
+
const body = await collaboratorFetch(appName, token2, "/invites");
|
|
1434
|
+
for (const invite of body.invites ?? []) {
|
|
1435
|
+
const status2 = invite.revokedAt ? "revoked" : invite.acceptedAt ? "accepted" : new Date(invite.expiresAt).getTime() <= Date.now() ? "expired" : "pending";
|
|
1436
|
+
console.log(`${invite.id} ${invite.emailDisplay} status=${status2} expires=${new Date(invite.expiresAt).toISOString()}`);
|
|
1437
|
+
}
|
|
1438
|
+
if (!body.invites?.length) console.log("No secrets collaborator invites.");
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
var invitesRevoke = defineCommand3({
|
|
1442
|
+
meta: { name: "revoke", description: "Revoke a pending app secret collaborator invite" },
|
|
1443
|
+
args: {
|
|
1444
|
+
inviteId: { type: "positional", required: true, description: "Invite id" }
|
|
1445
|
+
},
|
|
1446
|
+
async run({ args }) {
|
|
1447
|
+
const token2 = await ensureToken();
|
|
1448
|
+
const appName = requireAppName(void 0);
|
|
1449
|
+
await collaboratorFetch(appName, token2, `/invites/${encodeURIComponent(String(args.inviteId))}`, { method: "DELETE" });
|
|
1450
|
+
console.log(`Revoked secrets collaborator invite ${String(args.inviteId)}.`);
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
var invites = defineCommand3({
|
|
1454
|
+
meta: { name: "invites", description: "Manage app secret collaborator invites" },
|
|
1455
|
+
subCommands: { list: invitesList, revoke: invitesRevoke }
|
|
1456
|
+
});
|
|
1457
|
+
var collaborators = defineCommand3({
|
|
1458
|
+
meta: { name: "collaborators", description: "Manage app secret collaborators" },
|
|
1459
|
+
subCommands: { list: collaboratorsList, add: collaboratorsAdd, remove: collaboratorsRemove, invites }
|
|
1460
|
+
});
|
|
1461
|
+
var secrets_default = defineCommand3({
|
|
1462
|
+
meta: { name: "secrets", description: "Manage app-owned DeepSpace secrets" },
|
|
1463
|
+
subCommands: { set, unset, list, pull, push, diff, collaborators }
|
|
1464
|
+
});
|
|
636
1465
|
|
|
637
1466
|
// src/cli/commands/dev.ts
|
|
638
1467
|
var DEFAULT_PORT = 5173;
|
|
639
|
-
var dev_default =
|
|
1468
|
+
var dev_default = defineCommand4({
|
|
640
1469
|
meta: {
|
|
641
1470
|
name: "dev",
|
|
642
1471
|
description: "Start local development server"
|
|
@@ -652,20 +1481,40 @@ var dev_default = defineCommand3({
|
|
|
652
1481
|
description: "Use production workers (real auth, real API calls)",
|
|
653
1482
|
default: false
|
|
654
1483
|
},
|
|
1484
|
+
env: {
|
|
1485
|
+
type: "string",
|
|
1486
|
+
description: "wrangler.toml [env.<name>] block to run (e.g. --env staging). Legacy apps read .dev.vars.<name>; new apps use managed .env.",
|
|
1487
|
+
required: false
|
|
1488
|
+
},
|
|
655
1489
|
port: {
|
|
656
1490
|
type: "string",
|
|
657
1491
|
description: `Port to bind (default ${DEFAULT_PORT}, or $DEEPSPACE_PORT)`,
|
|
658
1492
|
required: false
|
|
1493
|
+
},
|
|
1494
|
+
"no-pull-env": {
|
|
1495
|
+
type: "boolean",
|
|
1496
|
+
description: "Skip pulling remote app secrets into the synced .env section on startup",
|
|
1497
|
+
default: false
|
|
659
1498
|
}
|
|
660
1499
|
},
|
|
661
1500
|
async run({ args }) {
|
|
662
|
-
const appDir = resolve2(args.dir ?? ".");
|
|
663
1501
|
const env = args.prod ? "prod" : "dev";
|
|
1502
|
+
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
664
1503
|
const port = resolvePort(args.port);
|
|
665
|
-
|
|
666
|
-
|
|
1504
|
+
const start = resolve3(args.dir ?? ".");
|
|
1505
|
+
const appDir = findAppDir(start);
|
|
1506
|
+
if (!appDir) {
|
|
1507
|
+
console.error(`No wrangler.toml found at or above ${start}.`);
|
|
1508
|
+
const children = findChildApps(start);
|
|
1509
|
+
if (children.length > 0) {
|
|
1510
|
+
console.error("Did you mean to run inside one of these app directories?");
|
|
1511
|
+
for (const c of children) console.error(` cd ${c}`);
|
|
1512
|
+
} else {
|
|
1513
|
+
console.error("Run from a DeepSpace app directory (one containing wrangler.toml).");
|
|
1514
|
+
}
|
|
667
1515
|
process.exit(1);
|
|
668
1516
|
}
|
|
1517
|
+
writeLaunchConfigIfMissing(appDir, detectAppName(appDir) ?? basename(appDir), port);
|
|
669
1518
|
ensureInstallReady(appDir);
|
|
670
1519
|
let token2;
|
|
671
1520
|
try {
|
|
@@ -682,9 +1531,25 @@ var dev_default = defineCommand3({
|
|
|
682
1531
|
process.exit(1);
|
|
683
1532
|
}
|
|
684
1533
|
console.log(`Logged in as ${payload.name ?? payload.email}`);
|
|
685
|
-
console.log(`
|
|
1534
|
+
console.log(`Platform: ${env}`);
|
|
1535
|
+
if (wranglerEnv) console.log(`Wrangler env: ${wranglerEnv}`);
|
|
686
1536
|
console.log(`Port: ${port}`);
|
|
687
|
-
await writeDevVars(appDir, env, payload.sub, token2);
|
|
1537
|
+
await writeDevVars(appDir, env, payload.sub, token2, wranglerEnv);
|
|
1538
|
+
if (env === "dev" && !args["no-pull-env"] && !hasLegacyDevVars(appDir, wranglerEnv)) {
|
|
1539
|
+
const appName = resolveCurrentAppName(appDir, wranglerEnv);
|
|
1540
|
+
if (appName) {
|
|
1541
|
+
try {
|
|
1542
|
+
if (await remoteAvailable(appName, token2)) {
|
|
1543
|
+
const body = await remoteFetch(appName, token2, "/pull");
|
|
1544
|
+
replaceManagedSyncedEnv(appDir, body.secrets ?? {});
|
|
1545
|
+
console.log("Secrets: pulled remote app secrets into synced .env section.");
|
|
1546
|
+
}
|
|
1547
|
+
} catch (err) {
|
|
1548
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1549
|
+
console.warn(`Secrets: remote app secrets pull skipped (${message}).`);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
688
1553
|
preflightWindowsWorkerd(appDir);
|
|
689
1554
|
console.log("Starting dev server...\n");
|
|
690
1555
|
const vite = spawn(
|
|
@@ -693,7 +1558,16 @@ var dev_default = defineCommand3({
|
|
|
693
1558
|
{
|
|
694
1559
|
cwd: appDir,
|
|
695
1560
|
stdio: "inherit",
|
|
696
|
-
|
|
1561
|
+
// CLOUDFLARE_ENV is what the Cloudflare Vite plugin keys off — it
|
|
1562
|
+
// applies `[env.<name>]` overrides. Legacy .dev.vars apps use
|
|
1563
|
+
// `.dev.vars.<name>` instead of `.dev.vars`; new apps use managed .env.
|
|
1564
|
+
// Set only when --env was passed so the default invocation behaves
|
|
1565
|
+
// exactly as before.
|
|
1566
|
+
env: {
|
|
1567
|
+
...process.env,
|
|
1568
|
+
DEEPSPACE_PORT: String(port),
|
|
1569
|
+
...wranglerEnv ? { CLOUDFLARE_ENV: wranglerEnv } : {}
|
|
1570
|
+
}
|
|
697
1571
|
}
|
|
698
1572
|
);
|
|
699
1573
|
process.on("SIGINT", () => vite.kill());
|
|
@@ -711,15 +1585,23 @@ function resolvePort(arg) {
|
|
|
711
1585
|
}
|
|
712
1586
|
return n;
|
|
713
1587
|
}
|
|
1588
|
+
function resolveCurrentAppName(appDir, wranglerEnv) {
|
|
1589
|
+
try {
|
|
1590
|
+
const resolved = resolveAppNameForEnv(readWranglerConfig(appDir), wranglerEnv);
|
|
1591
|
+
return resolved.ok ? resolved.name : null;
|
|
1592
|
+
} catch {
|
|
1593
|
+
return null;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
714
1596
|
|
|
715
1597
|
// src/cli/commands/kill.ts
|
|
716
|
-
import { defineCommand as
|
|
1598
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
717
1599
|
import { spawnSync } from "child_process";
|
|
718
1600
|
import { setTimeout as wait } from "timers/promises";
|
|
719
1601
|
var DEFAULT_PORT2 = 5173;
|
|
720
1602
|
var SIGTERM_GRACE_MS = 1500;
|
|
721
1603
|
var IS_WIN = process.platform === "win32";
|
|
722
|
-
var kill_default =
|
|
1604
|
+
var kill_default = defineCommand5({
|
|
723
1605
|
meta: {
|
|
724
1606
|
name: "kill",
|
|
725
1607
|
description: "Kill the local dev server and any orphaned workerd processes"
|
|
@@ -874,9 +1756,8 @@ function isAlive(pid) {
|
|
|
874
1756
|
}
|
|
875
1757
|
|
|
876
1758
|
// src/cli/commands/test.ts
|
|
877
|
-
import { defineCommand as
|
|
878
|
-
import {
|
|
879
|
-
import { join as join7, resolve as resolve3 } from "path";
|
|
1759
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
1760
|
+
import { resolve as resolve4 } from "path";
|
|
880
1761
|
import { sync as spawnSync3 } from "cross-spawn";
|
|
881
1762
|
|
|
882
1763
|
// src/cli/lib/playwright.ts
|
|
@@ -905,7 +1786,7 @@ function ensurePlaywright(appDir) {
|
|
|
905
1786
|
|
|
906
1787
|
// src/cli/commands/test.ts
|
|
907
1788
|
var DEFAULT_PORT3 = 5173;
|
|
908
|
-
var test_default =
|
|
1789
|
+
var test_default = defineCommand6({
|
|
909
1790
|
meta: {
|
|
910
1791
|
name: "test",
|
|
911
1792
|
description: "Run tests for your DeepSpace app"
|
|
@@ -923,11 +1804,19 @@ var test_default = defineCommand5({
|
|
|
923
1804
|
}
|
|
924
1805
|
},
|
|
925
1806
|
async run({ args }) {
|
|
926
|
-
const appDir = resolve3(".");
|
|
927
1807
|
const suite = args.suite ?? "default";
|
|
928
1808
|
const port = resolvePort3(args.port);
|
|
929
|
-
|
|
930
|
-
|
|
1809
|
+
const start = resolve4(".");
|
|
1810
|
+
const appDir = findAppDir(start);
|
|
1811
|
+
if (!appDir) {
|
|
1812
|
+
console.error(`No wrangler.toml found at or above ${start}.`);
|
|
1813
|
+
const children = findChildApps(start);
|
|
1814
|
+
if (children.length > 0) {
|
|
1815
|
+
console.error("Did you mean to run inside one of these app directories?");
|
|
1816
|
+
for (const c of children) console.error(` cd ${c}`);
|
|
1817
|
+
} else {
|
|
1818
|
+
console.error("Run from a DeepSpace app directory (one containing wrangler.toml).");
|
|
1819
|
+
}
|
|
931
1820
|
process.exit(1);
|
|
932
1821
|
}
|
|
933
1822
|
ensureInstallReady(appDir);
|
|
@@ -1012,10 +1901,10 @@ function resolvePort3(arg) {
|
|
|
1012
1901
|
}
|
|
1013
1902
|
|
|
1014
1903
|
// src/cli/commands/screenshot.ts
|
|
1015
|
-
import { defineCommand as
|
|
1904
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
1016
1905
|
import { sync as spawnSync4 } from "cross-spawn";
|
|
1017
|
-
import { resolve as
|
|
1018
|
-
var screenshot_default =
|
|
1906
|
+
import { resolve as resolve5 } from "path";
|
|
1907
|
+
var screenshot_default = defineCommand7({
|
|
1019
1908
|
meta: {
|
|
1020
1909
|
name: "screenshot",
|
|
1021
1910
|
description: "Take a Playwright screenshot, installing Chromium on demand"
|
|
@@ -1043,7 +1932,7 @@ var screenshot_default = defineCommand6({
|
|
|
1043
1932
|
}
|
|
1044
1933
|
},
|
|
1045
1934
|
run({ args }) {
|
|
1046
|
-
const appDir =
|
|
1935
|
+
const appDir = resolve5(".");
|
|
1047
1936
|
ensurePlaywright(appDir);
|
|
1048
1937
|
const playwrightArgs = ["playwright", "screenshot", args.url, args.output];
|
|
1049
1938
|
if (args["full-page"]) playwrightArgs.push("--full-page");
|
|
@@ -1059,29 +1948,29 @@ var screenshot_default = defineCommand6({
|
|
|
1059
1948
|
});
|
|
1060
1949
|
|
|
1061
1950
|
// src/cli/commands/test-accounts.ts
|
|
1062
|
-
import { defineCommand as
|
|
1951
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
1063
1952
|
import * as p3 from "@clack/prompts";
|
|
1064
|
-
import { readFileSync as
|
|
1065
|
-
import { join as
|
|
1953
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
1954
|
+
import { join as join9 } from "path";
|
|
1066
1955
|
import { homedir as homedir4 } from "os";
|
|
1067
1956
|
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
1068
1957
|
var AUTH_URL4 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
1069
|
-
var DIR3 =
|
|
1070
|
-
var ACCOUNTS_PATH =
|
|
1958
|
+
var DIR3 = join9(homedir4(), ".deepspace");
|
|
1959
|
+
var ACCOUNTS_PATH = join9(DIR3, "test-accounts.json");
|
|
1071
1960
|
function loadAccounts() {
|
|
1072
|
-
if (!
|
|
1961
|
+
if (!existsSync9(ACCOUNTS_PATH)) return [];
|
|
1073
1962
|
try {
|
|
1074
|
-
return JSON.parse(
|
|
1963
|
+
return JSON.parse(readFileSync8(ACCOUNTS_PATH, "utf-8"));
|
|
1075
1964
|
} catch {
|
|
1076
1965
|
return [];
|
|
1077
1966
|
}
|
|
1078
1967
|
}
|
|
1079
1968
|
function saveAccounts(accounts) {
|
|
1080
|
-
|
|
1081
|
-
|
|
1969
|
+
mkdirSync4(DIR3, { recursive: true });
|
|
1970
|
+
writeFileSync6(ACCOUNTS_PATH, JSON.stringify(accounts, null, 2), { mode: 384 });
|
|
1082
1971
|
}
|
|
1083
1972
|
function sessionCookie() {
|
|
1084
|
-
const token2 =
|
|
1973
|
+
const token2 = readFileSync8(SESSION_PATH2, "utf-8").trim();
|
|
1085
1974
|
return `${SESSION_COOKIE2}=${encodeURIComponent(token2)}`;
|
|
1086
1975
|
}
|
|
1087
1976
|
async function fetchRemoteAccounts() {
|
|
@@ -1104,7 +1993,7 @@ async function deleteRemote(id) {
|
|
|
1104
1993
|
throw new Error(data.error ?? `DELETE returned ${res.status}`);
|
|
1105
1994
|
}
|
|
1106
1995
|
}
|
|
1107
|
-
var create =
|
|
1996
|
+
var create = defineCommand8({
|
|
1108
1997
|
meta: {
|
|
1109
1998
|
name: "create",
|
|
1110
1999
|
description: "Create a test account"
|
|
@@ -1174,7 +2063,7 @@ var create = defineCommand7({
|
|
|
1174
2063
|
Saved to ${ACCOUNTS_PATH}`);
|
|
1175
2064
|
}
|
|
1176
2065
|
});
|
|
1177
|
-
var
|
|
2066
|
+
var list2 = defineCommand8({
|
|
1178
2067
|
meta: {
|
|
1179
2068
|
name: "list",
|
|
1180
2069
|
description: "List your test accounts"
|
|
@@ -1209,7 +2098,7 @@ var list = defineCommand7({
|
|
|
1209
2098
|
}
|
|
1210
2099
|
}
|
|
1211
2100
|
});
|
|
1212
|
-
var del =
|
|
2101
|
+
var del = defineCommand8({
|
|
1213
2102
|
meta: {
|
|
1214
2103
|
name: "delete",
|
|
1215
2104
|
description: "Delete a test account by --email or --id"
|
|
@@ -1266,7 +2155,7 @@ var del = defineCommand7({
|
|
|
1266
2155
|
console.log(`Test account deleted${targetEmail ? `: ${targetEmail}` : `: ${targetId}`}`);
|
|
1267
2156
|
}
|
|
1268
2157
|
});
|
|
1269
|
-
var clear =
|
|
2158
|
+
var clear = defineCommand8({
|
|
1270
2159
|
meta: {
|
|
1271
2160
|
name: "clear",
|
|
1272
2161
|
description: "Delete all your test accounts (or those matching --label)"
|
|
@@ -1332,26 +2221,25 @@ Failed:`);
|
|
|
1332
2221
|
}
|
|
1333
2222
|
}
|
|
1334
2223
|
});
|
|
1335
|
-
var test_accounts_default =
|
|
2224
|
+
var test_accounts_default = defineCommand8({
|
|
1336
2225
|
meta: {
|
|
1337
2226
|
name: "test-accounts",
|
|
1338
2227
|
description: "Manage test accounts for development"
|
|
1339
2228
|
},
|
|
1340
2229
|
subCommands: {
|
|
1341
2230
|
create,
|
|
1342
|
-
list,
|
|
2231
|
+
list: list2,
|
|
1343
2232
|
delete: del,
|
|
1344
2233
|
clear
|
|
1345
2234
|
}
|
|
1346
2235
|
});
|
|
1347
2236
|
|
|
1348
2237
|
// src/cli/commands/deploy.ts
|
|
1349
|
-
import { defineCommand as
|
|
2238
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
1350
2239
|
import { execSync as execSync2 } from "child_process";
|
|
1351
2240
|
import { randomUUID } from "crypto";
|
|
1352
|
-
import { readFileSync as
|
|
1353
|
-
import {
|
|
1354
|
-
import { join as join9, resolve as resolve5, dirname } from "path";
|
|
2241
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync2, existsSync as existsSync10 } from "fs";
|
|
2242
|
+
import { join as join10, resolve as resolve6, dirname as dirname2 } from "path";
|
|
1355
2243
|
import { pathToFileURL } from "url";
|
|
1356
2244
|
import * as p4 from "@clack/prompts";
|
|
1357
2245
|
|
|
@@ -1380,6 +2268,7 @@ var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
|
1380
2268
|
"AUTH_WORKER_URL",
|
|
1381
2269
|
"APP_IDENTITY_TOKEN",
|
|
1382
2270
|
"APP_OWNER_JWT",
|
|
2271
|
+
"APP_SECRETS_KEY",
|
|
1383
2272
|
"USAGE_EVENTS"
|
|
1384
2273
|
]);
|
|
1385
2274
|
function validateBindingManifest(manifest) {
|
|
@@ -1560,6 +2449,7 @@ function asObjectArray(v) {
|
|
|
1560
2449
|
// src/cli/commands/deploy.ts
|
|
1561
2450
|
var DEPLOY_URL = process.env.DEEPSPACE_DEPLOY_URL ?? ENVS.prod.deploy;
|
|
1562
2451
|
var API_URL = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
2452
|
+
var SECRET_NAME_RE2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1563
2453
|
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.";
|
|
1564
2454
|
var RESERVED_RUN_WORKER_FIRST = /* @__PURE__ */ new Set([
|
|
1565
2455
|
"/api/*",
|
|
@@ -1602,6 +2492,14 @@ function formatDeployWorkerError(status2, error) {
|
|
|
1602
2492
|
|
|
1603
2493
|
Underlying error: ${detail}`;
|
|
1604
2494
|
}
|
|
2495
|
+
function legacyDevVarsAppSecretsRoomWarning(userSecrets, doManifest) {
|
|
2496
|
+
if (!Object.keys(userSecrets).length) return null;
|
|
2497
|
+
const hasAppSecretsRoom = (doManifest ?? []).some(
|
|
2498
|
+
(binding) => binding.binding === "SECRETS_ROOMS" || binding.className === "AppSecretsRoom"
|
|
2499
|
+
);
|
|
2500
|
+
if (!hasAppSecretsRoom) return null;
|
|
2501
|
+
return "Legacy .dev.vars secrets are still being deployed as direct Worker bindings even though AppSecretsRoom is configured. Move these values with `deepspace secrets set` to manage them through the app secrets store.";
|
|
2502
|
+
}
|
|
1605
2503
|
var MAX_GROUP_BYTES = 40 * 1024 * 1024;
|
|
1606
2504
|
var GROUP_BYTES = (() => {
|
|
1607
2505
|
const raw = Number(process.env.DEEPSPACE_DEPLOY_GROUP_BYTES);
|
|
@@ -1655,7 +2553,7 @@ async function abortStagedUpload(appName, uploadId, totalGroups, token2) {
|
|
|
1655
2553
|
} catch {
|
|
1656
2554
|
}
|
|
1657
2555
|
}
|
|
1658
|
-
var deploy_default =
|
|
2556
|
+
var deploy_default = defineCommand9({
|
|
1659
2557
|
meta: {
|
|
1660
2558
|
name: "deploy",
|
|
1661
2559
|
description: "Build and deploy your DeepSpace app"
|
|
@@ -1665,31 +2563,38 @@ var deploy_default = defineCommand8({
|
|
|
1665
2563
|
type: "positional",
|
|
1666
2564
|
description: "App directory (default: current directory)",
|
|
1667
2565
|
required: false
|
|
2566
|
+
},
|
|
2567
|
+
env: {
|
|
2568
|
+
type: "string",
|
|
2569
|
+
description: "wrangler.toml [env.<name>] block to deploy (e.g. --env staging). Omit to deploy the top-level config.",
|
|
2570
|
+
required: false
|
|
1668
2571
|
}
|
|
1669
2572
|
},
|
|
1670
2573
|
async run({ args }) {
|
|
1671
|
-
const appDir =
|
|
1672
|
-
const
|
|
1673
|
-
if (!
|
|
2574
|
+
const appDir = resolve6(args.dir ?? ".");
|
|
2575
|
+
const envName = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
2576
|
+
if (!hasWranglerConfig(appDir)) {
|
|
1674
2577
|
console.error("No wrangler.toml found. Are you in a DeepSpace app directory?");
|
|
1675
2578
|
process.exit(1);
|
|
1676
2579
|
}
|
|
1677
2580
|
ensureInstallReady(appDir);
|
|
1678
2581
|
p4.intro("Deploying DeepSpace app");
|
|
1679
|
-
const wranglerConfig =
|
|
1680
|
-
const nameRes =
|
|
2582
|
+
const wranglerConfig = readWranglerConfig(appDir);
|
|
2583
|
+
const nameRes = resolveAppNameForEnv(wranglerConfig, envName);
|
|
1681
2584
|
if (!nameRes.ok) {
|
|
1682
|
-
p4.cancel(
|
|
2585
|
+
p4.cancel(nameRes.reason);
|
|
1683
2586
|
process.exit(1);
|
|
1684
2587
|
}
|
|
1685
|
-
|
|
2588
|
+
const declaredName = envName ? wranglerConfig.env?.[envName]?.name : wranglerConfig.name;
|
|
2589
|
+
if (declaredName !== nameRes.name) {
|
|
2590
|
+
const where = envName ? `[env.${envName}].name` : "`name`";
|
|
1686
2591
|
p4.cancel(
|
|
1687
|
-
`wrangler.toml:
|
|
2592
|
+
`wrangler.toml: ${where} "${declaredName}" is not in canonical form. Update it to "${nameRes.name}" and re-run.`
|
|
1688
2593
|
);
|
|
1689
2594
|
process.exit(1);
|
|
1690
2595
|
}
|
|
1691
2596
|
const appName = nameRes.name;
|
|
1692
|
-
p4.log.info(`App: ${appName}`);
|
|
2597
|
+
p4.log.info(envName ? `App: ${appName} (env: ${envName})` : `App: ${appName}`);
|
|
1693
2598
|
let token2;
|
|
1694
2599
|
try {
|
|
1695
2600
|
token2 = await ensureToken();
|
|
@@ -1700,7 +2605,11 @@ var deploy_default = defineCommand8({
|
|
|
1700
2605
|
const s = createSpinner();
|
|
1701
2606
|
s.start("Building...");
|
|
1702
2607
|
try {
|
|
1703
|
-
execSync2("npx vite build", {
|
|
2608
|
+
execSync2("npx vite build", {
|
|
2609
|
+
cwd: appDir,
|
|
2610
|
+
stdio: "pipe",
|
|
2611
|
+
env: envName ? { ...process.env, CLOUDFLARE_ENV: envName } : process.env
|
|
2612
|
+
});
|
|
1704
2613
|
} catch (err) {
|
|
1705
2614
|
s.stop("Build failed");
|
|
1706
2615
|
const detail = [err.stdout?.toString(), err.stderr?.toString()].filter(Boolean).join("\n").trim();
|
|
@@ -1708,33 +2617,40 @@ var deploy_default = defineCommand8({
|
|
|
1708
2617
|
process.exit(1);
|
|
1709
2618
|
}
|
|
1710
2619
|
s.stop("Built");
|
|
1711
|
-
const deployConfigPath =
|
|
1712
|
-
if (!
|
|
2620
|
+
const deployConfigPath = join10(appDir, ".wrangler", "deploy", "config.json");
|
|
2621
|
+
if (!existsSync10(deployConfigPath)) {
|
|
1713
2622
|
p4.cancel("Build output config not found at .wrangler/deploy/config.json");
|
|
1714
2623
|
process.exit(1);
|
|
1715
2624
|
}
|
|
1716
|
-
const deployConfig = JSON.parse(
|
|
1717
|
-
const outputWranglerPath =
|
|
1718
|
-
if (!
|
|
2625
|
+
const deployConfig = JSON.parse(readFileSync9(deployConfigPath, "utf-8"));
|
|
2626
|
+
const outputWranglerPath = resolve6(dirname2(deployConfigPath), deployConfig.configPath);
|
|
2627
|
+
if (!existsSync10(outputWranglerPath)) {
|
|
1719
2628
|
p4.cancel(`Output wrangler.json not found at ${outputWranglerPath}`);
|
|
1720
2629
|
process.exit(1);
|
|
1721
2630
|
}
|
|
1722
|
-
const outputConfig = JSON.parse(
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
2631
|
+
const outputConfig = JSON.parse(readFileSync9(outputWranglerPath, "utf-8"));
|
|
2632
|
+
if (outputConfig.name && outputConfig.name !== appName) {
|
|
2633
|
+
const envHint = envName ? ` (--env ${envName})` : "";
|
|
2634
|
+
p4.cancel(
|
|
2635
|
+
`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.`
|
|
2636
|
+
);
|
|
2637
|
+
process.exit(1);
|
|
2638
|
+
}
|
|
2639
|
+
const workerDir = dirname2(outputWranglerPath);
|
|
2640
|
+
const workerBundlePath = join10(workerDir, outputConfig.main);
|
|
2641
|
+
const clientDir = outputConfig.assets?.directory ? resolve6(workerDir, outputConfig.assets.directory) : null;
|
|
2642
|
+
if (!existsSync10(workerBundlePath)) {
|
|
1727
2643
|
p4.cancel(`Worker bundle not found at ${workerBundlePath}`);
|
|
1728
2644
|
process.exit(1);
|
|
1729
2645
|
}
|
|
1730
|
-
if (!clientDir || !
|
|
2646
|
+
if (!clientDir || !existsSync10(clientDir)) {
|
|
1731
2647
|
p4.cancel(`Client assets not found at ${clientDir}`);
|
|
1732
2648
|
process.exit(1);
|
|
1733
2649
|
}
|
|
1734
2650
|
s.start("Collecting assets...");
|
|
1735
2651
|
const assets = collectAssets(clientDir);
|
|
1736
2652
|
s.stop(`Collected ${assets.length} assets`);
|
|
1737
|
-
const workerJs =
|
|
2653
|
+
const workerJs = readFileSync9(workerBundlePath, "utf-8");
|
|
1738
2654
|
const doBindings = outputConfig.durable_objects?.bindings;
|
|
1739
2655
|
const sqliteClasses = new Set(
|
|
1740
2656
|
outputConfig.migrations?.flatMap((m) => m.new_sqlite_classes ?? []) ?? []
|
|
@@ -1761,40 +2677,20 @@ var deploy_default = defineCommand8({
|
|
|
1761
2677
|
`Custom bindings: ${customBindings.map((b) => `${b.name} (${b.type})`).join(", ")}`
|
|
1762
2678
|
);
|
|
1763
2679
|
}
|
|
1764
|
-
const devVarsPath = join9(appDir, ".dev.vars");
|
|
1765
2680
|
let userSecrets = {};
|
|
1766
|
-
if (
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
p4.cancel(`.dev.vars: ${err.message}`);
|
|
1773
|
-
process.exit(1);
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
const userSecretNames = Object.keys(userSecrets);
|
|
1778
|
-
const declaredBindingNames = /* @__PURE__ */ new Set([
|
|
1779
|
-
...customBindings.map((b) => b.name),
|
|
1780
|
-
...(doManifest ?? []).map((d) => d.binding)
|
|
1781
|
-
]);
|
|
1782
|
-
for (const name of userSecretNames) {
|
|
1783
|
-
if (RESERVED_BINDING_NAMES.has(name)) {
|
|
1784
|
-
p4.cancel(
|
|
1785
|
-
`.dev.vars: "${name}" is a reserved binding name and cannot be used as an app secret.`
|
|
1786
|
-
);
|
|
1787
|
-
process.exit(1);
|
|
1788
|
-
}
|
|
1789
|
-
if (declaredBindingNames.has(name)) {
|
|
1790
|
-
p4.cancel(
|
|
1791
|
-
`.dev.vars: "${name}" collides with a binding declared in wrangler.toml \u2014 rename one or the other.`
|
|
1792
|
-
);
|
|
2681
|
+
if (hasLegacyDevVars(appDir, envName)) {
|
|
2682
|
+
try {
|
|
2683
|
+
userSecrets = readLegacyUserSecrets(appDir, envName, customBindings, doManifest);
|
|
2684
|
+
} catch (err) {
|
|
2685
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2686
|
+
p4.cancel(message);
|
|
1793
2687
|
process.exit(1);
|
|
1794
2688
|
}
|
|
1795
2689
|
}
|
|
1796
|
-
if (
|
|
1797
|
-
p4.log.info(`
|
|
2690
|
+
if (Object.keys(userSecrets).length) {
|
|
2691
|
+
p4.log.info(`Legacy .dev.vars secrets: ${Object.keys(userSecrets).sort().join(", ")}`);
|
|
2692
|
+
const warning = legacyDevVarsAppSecretsRoomWarning(userSecrets, doManifest);
|
|
2693
|
+
if (warning) p4.log.warn(warning);
|
|
1798
2694
|
}
|
|
1799
2695
|
s.start(`Deploying to ${appName}.app.space...`);
|
|
1800
2696
|
const extraRoutes = extractRunWorkerFirst(wranglerConfig);
|
|
@@ -1858,12 +2754,12 @@ var deploy_default = defineCommand8({
|
|
|
1858
2754
|
if (customBindings.length) {
|
|
1859
2755
|
form.append("bindingManifest", JSON.stringify(customBindings));
|
|
1860
2756
|
}
|
|
1861
|
-
if (userSecretNames.length) {
|
|
1862
|
-
form.append("userSecrets", JSON.stringify(userSecrets));
|
|
1863
|
-
}
|
|
1864
2757
|
if (extraRoutes.length) {
|
|
1865
2758
|
form.append("extraRunWorkerFirst", JSON.stringify(extraRoutes));
|
|
1866
2759
|
}
|
|
2760
|
+
if (Object.keys(userSecrets).length) {
|
|
2761
|
+
form.append("userSecrets", JSON.stringify(userSecrets));
|
|
2762
|
+
}
|
|
1867
2763
|
return form;
|
|
1868
2764
|
};
|
|
1869
2765
|
let res;
|
|
@@ -1900,6 +2796,7 @@ var deploy_default = defineCommand8({
|
|
|
1900
2796
|
s.stop("Deployed (edge propagation still in progress after 90s)");
|
|
1901
2797
|
p4.log.warn("First requests may briefly hit the assets transitional page; retry in a minute.");
|
|
1902
2798
|
p4.log.success(`Live at: ${body.url}`);
|
|
2799
|
+
p4.log.info("Secrets: local dev-only values are not pushed automatically. Run `deepspace secrets push` when you want to sync them.");
|
|
1903
2800
|
await syncSubscriptionPlans(appDir, appName, token2);
|
|
1904
2801
|
await syncOneTimeProducts(appDir, appName, token2);
|
|
1905
2802
|
p4.outro("Done");
|
|
@@ -1908,21 +2805,43 @@ var deploy_default = defineCommand8({
|
|
|
1908
2805
|
}
|
|
1909
2806
|
s.stop("Deployed!");
|
|
1910
2807
|
p4.log.success(`Live at: ${body.url}`);
|
|
2808
|
+
p4.log.info("Secrets: local dev-only values are not pushed automatically. Run `deepspace secrets push` when you want to sync them.");
|
|
1911
2809
|
await syncSubscriptionPlans(appDir, appName, token2);
|
|
1912
2810
|
await syncOneTimeProducts(appDir, appName, token2);
|
|
1913
2811
|
p4.outro("Done");
|
|
1914
2812
|
}
|
|
1915
2813
|
});
|
|
2814
|
+
function readLegacyUserSecrets(appDir, wranglerEnv, customBindings, doManifest) {
|
|
2815
|
+
const path = join10(appDir, wranglerEnv ? `.dev.vars.${wranglerEnv}` : ".dev.vars");
|
|
2816
|
+
if (!existsSync10(path)) return {};
|
|
2817
|
+
const vars = parseDevVars(extractCustomDevVars(readFileSync9(path, "utf-8")));
|
|
2818
|
+
const reserved = /* @__PURE__ */ new Set([
|
|
2819
|
+
...SDK_MANAGED_KEYS,
|
|
2820
|
+
...customBindings.map((b) => b.name),
|
|
2821
|
+
...(doManifest ?? []).flatMap((b) => [b.binding, b.className])
|
|
2822
|
+
]);
|
|
2823
|
+
const out = {};
|
|
2824
|
+
for (const [name, value] of Object.entries(vars)) {
|
|
2825
|
+
if (!SECRET_NAME_RE2.test(name)) {
|
|
2826
|
+
throw new Error(`.dev.vars: invalid secret name "${name}".`);
|
|
2827
|
+
}
|
|
2828
|
+
if (reserved.has(name)) {
|
|
2829
|
+
throw new Error(`.dev.vars: "${name}" is reserved and cannot be deployed as a user secret.`);
|
|
2830
|
+
}
|
|
2831
|
+
out[name] = value;
|
|
2832
|
+
}
|
|
2833
|
+
return out;
|
|
2834
|
+
}
|
|
1916
2835
|
async function syncSubscriptionPlans(appDir, appName, token2) {
|
|
1917
|
-
const subsPath =
|
|
1918
|
-
if (!
|
|
2836
|
+
const subsPath = join10(appDir, "src", "subscriptions.ts");
|
|
2837
|
+
if (!existsSync10(subsPath)) return;
|
|
1919
2838
|
const s = createSpinner();
|
|
1920
2839
|
s.start("Syncing subscription plans...");
|
|
1921
2840
|
let plans;
|
|
1922
2841
|
try {
|
|
1923
2842
|
const esbuild = await import("esbuild");
|
|
1924
|
-
const outDir =
|
|
1925
|
-
const outFile =
|
|
2843
|
+
const outDir = join10(appDir, ".wrangler", "deploy");
|
|
2844
|
+
const outFile = join10(outDir, "subscriptions.bundle.mjs");
|
|
1926
2845
|
await esbuild.build({
|
|
1927
2846
|
entryPoints: [subsPath],
|
|
1928
2847
|
bundle: true,
|
|
@@ -2063,15 +2982,15 @@ current billing period? (Skip to leave them grandfathered.)`
|
|
|
2063
2982
|
}
|
|
2064
2983
|
}
|
|
2065
2984
|
async function syncOneTimeProducts(appDir, appName, token2) {
|
|
2066
|
-
const productsPath =
|
|
2067
|
-
if (!
|
|
2985
|
+
const productsPath = join10(appDir, "src", "products.ts");
|
|
2986
|
+
if (!existsSync10(productsPath)) return;
|
|
2068
2987
|
const s = createSpinner();
|
|
2069
2988
|
s.start("Syncing one-time products...");
|
|
2070
2989
|
let products;
|
|
2071
2990
|
try {
|
|
2072
2991
|
const esbuild = await import("esbuild");
|
|
2073
|
-
const outDir =
|
|
2074
|
-
const outFile =
|
|
2992
|
+
const outDir = join10(appDir, ".wrangler", "deploy");
|
|
2993
|
+
const outFile = join10(outDir, "products.bundle.mjs");
|
|
2075
2994
|
await esbuild.build({
|
|
2076
2995
|
entryPoints: [productsPath],
|
|
2077
2996
|
bundle: true,
|
|
@@ -2143,16 +3062,16 @@ async function waitForAssetsReady(url, timeoutMs) {
|
|
|
2143
3062
|
function collectAssets(dir) {
|
|
2144
3063
|
const assets = [];
|
|
2145
3064
|
function walk(d, prefix) {
|
|
2146
|
-
for (const entry of
|
|
3065
|
+
for (const entry of readdirSync2(d, { withFileTypes: true })) {
|
|
2147
3066
|
if (entry.name === ".assetsignore") continue;
|
|
2148
|
-
const full =
|
|
3067
|
+
const full = join10(d, entry.name);
|
|
2149
3068
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2150
3069
|
if (entry.isDirectory()) {
|
|
2151
3070
|
walk(full, rel);
|
|
2152
3071
|
} else {
|
|
2153
3072
|
assets.push({
|
|
2154
3073
|
path: "/" + rel,
|
|
2155
|
-
contentBase64:
|
|
3074
|
+
contentBase64: readFileSync9(full).toString("base64")
|
|
2156
3075
|
});
|
|
2157
3076
|
}
|
|
2158
3077
|
}
|
|
@@ -2162,13 +3081,10 @@ function collectAssets(dir) {
|
|
|
2162
3081
|
}
|
|
2163
3082
|
|
|
2164
3083
|
// src/cli/commands/undeploy.ts
|
|
2165
|
-
import { defineCommand as
|
|
2166
|
-
import { readFileSync as readFileSync7, existsSync as existsSync10 } from "fs";
|
|
2167
|
-
import { join as join10 } from "path";
|
|
2168
|
-
import { parse as parseToml3 } from "smol-toml";
|
|
3084
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
2169
3085
|
import * as p5 from "@clack/prompts";
|
|
2170
3086
|
var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? ENVS.prod.deploy;
|
|
2171
|
-
var undeploy_default =
|
|
3087
|
+
var undeploy_default = defineCommand10({
|
|
2172
3088
|
meta: {
|
|
2173
3089
|
name: "undeploy",
|
|
2174
3090
|
description: "Remove a deployed DeepSpace app"
|
|
@@ -2178,15 +3094,25 @@ var undeploy_default = defineCommand9({
|
|
|
2178
3094
|
type: "positional",
|
|
2179
3095
|
description: "App name to undeploy (reads from wrangler.toml if omitted)",
|
|
2180
3096
|
required: false
|
|
3097
|
+
},
|
|
3098
|
+
env: {
|
|
3099
|
+
type: "string",
|
|
3100
|
+
description: "wrangler.toml [env.<name>] block whose deployed app to remove (e.g. --env staging). Ignored if a positional name is given.",
|
|
3101
|
+
required: false
|
|
2181
3102
|
}
|
|
2182
3103
|
},
|
|
2183
3104
|
async run({ args }) {
|
|
2184
3105
|
let appName = args.name;
|
|
3106
|
+
const envName = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
2185
3107
|
if (!appName) {
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
const
|
|
2189
|
-
if (
|
|
3108
|
+
if (hasWranglerConfig(process.cwd())) {
|
|
3109
|
+
const config = readWranglerConfig(process.cwd());
|
|
3110
|
+
const nameRes = resolveAppNameForEnv(config, envName);
|
|
3111
|
+
if (!nameRes.ok) {
|
|
3112
|
+
p5.cancel(nameRes.reason);
|
|
3113
|
+
process.exit(1);
|
|
3114
|
+
}
|
|
3115
|
+
appName = nameRes.name;
|
|
2190
3116
|
}
|
|
2191
3117
|
}
|
|
2192
3118
|
if (!appName) {
|
|
@@ -2219,8 +3145,8 @@ var undeploy_default = defineCommand9({
|
|
|
2219
3145
|
});
|
|
2220
3146
|
|
|
2221
3147
|
// src/cli/commands/whoami.ts
|
|
2222
|
-
import { defineCommand as
|
|
2223
|
-
var whoami_default =
|
|
3148
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
3149
|
+
var whoami_default = defineCommand11({
|
|
2224
3150
|
meta: {
|
|
2225
3151
|
name: "whoami",
|
|
2226
3152
|
description: "Show the currently logged-in user"
|
|
@@ -2275,9 +3201,9 @@ var whoami_default = defineCommand10({
|
|
|
2275
3201
|
});
|
|
2276
3202
|
|
|
2277
3203
|
// src/cli/commands/add.ts
|
|
2278
|
-
import { defineCommand as
|
|
3204
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
2279
3205
|
import { existsSync as existsSync11 } from "fs";
|
|
2280
|
-
import { resolve as
|
|
3206
|
+
import { resolve as resolve7, join as join11 } from "path";
|
|
2281
3207
|
import { execSync as execSync3 } from "child_process";
|
|
2282
3208
|
function findScript(from) {
|
|
2283
3209
|
const inNodeModules = join11(from, "node_modules", "deepspace", "scripts", "add-feature.cjs");
|
|
@@ -2286,7 +3212,7 @@ function findScript(from) {
|
|
|
2286
3212
|
if (existsSync11(inDotDeepspace)) return inDotDeepspace;
|
|
2287
3213
|
return null;
|
|
2288
3214
|
}
|
|
2289
|
-
var add_default =
|
|
3215
|
+
var add_default = defineCommand12({
|
|
2290
3216
|
meta: {
|
|
2291
3217
|
name: "add",
|
|
2292
3218
|
description: "Add a feature to your DeepSpace app"
|
|
@@ -2315,7 +3241,7 @@ var add_default = defineCommand11({
|
|
|
2315
3241
|
}
|
|
2316
3242
|
},
|
|
2317
3243
|
run({ args }) {
|
|
2318
|
-
const appDir =
|
|
3244
|
+
const appDir = resolve7(args.dir ?? ".");
|
|
2319
3245
|
ensureInstallReady(appDir);
|
|
2320
3246
|
const script = findScript(appDir);
|
|
2321
3247
|
if (!script) {
|
|
@@ -2344,9 +3270,9 @@ var add_default = defineCommand11({
|
|
|
2344
3270
|
});
|
|
2345
3271
|
|
|
2346
3272
|
// src/cli/commands/skills.ts
|
|
2347
|
-
import { defineCommand as
|
|
3273
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
2348
3274
|
import spawn2 from "cross-spawn";
|
|
2349
|
-
import { mkdirSync as
|
|
3275
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
2350
3276
|
import { join as join12 } from "path";
|
|
2351
3277
|
import { homedir as homedir5 } from "os";
|
|
2352
3278
|
var SKILL_REPO = "deepdotspace/deepspace-skill";
|
|
@@ -2366,9 +3292,9 @@ function runSkillsAdd(extraArgs) {
|
|
|
2366
3292
|
});
|
|
2367
3293
|
}
|
|
2368
3294
|
function precreateClaudeDir(baseDir) {
|
|
2369
|
-
|
|
3295
|
+
mkdirSync5(join12(baseDir, ".claude"), { recursive: true });
|
|
2370
3296
|
}
|
|
2371
|
-
var add =
|
|
3297
|
+
var add = defineCommand13({
|
|
2372
3298
|
meta: {
|
|
2373
3299
|
name: "add",
|
|
2374
3300
|
description: "Install the DeepSpace skill for your coding agent"
|
|
@@ -2410,7 +3336,7 @@ var add = defineCommand12({
|
|
|
2410
3336
|
if (code !== 0) process.exit(code);
|
|
2411
3337
|
}
|
|
2412
3338
|
});
|
|
2413
|
-
var skills_default =
|
|
3339
|
+
var skills_default = defineCommand13({
|
|
2414
3340
|
meta: {
|
|
2415
3341
|
name: "skills",
|
|
2416
3342
|
description: "Manage the DeepSpace agent skill (Claude Code, Codex, OpenCode, Cursor, ...)"
|
|
@@ -2421,33 +3347,8 @@ var skills_default = defineCommand12({
|
|
|
2421
3347
|
});
|
|
2422
3348
|
|
|
2423
3349
|
// src/cli/commands/domain.ts
|
|
2424
|
-
import { defineCommand as
|
|
3350
|
+
import { defineCommand as defineCommand14 } from "citty";
|
|
2425
3351
|
import { execSync as execSync4 } from "child_process";
|
|
2426
|
-
|
|
2427
|
-
// src/cli/lib/app-context.ts
|
|
2428
|
-
import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
|
|
2429
|
-
import { join as join13, resolve as resolve7 } from "path";
|
|
2430
|
-
import { parse as parseToml4 } from "smol-toml";
|
|
2431
|
-
function detectAppName(cwd = process.cwd()) {
|
|
2432
|
-
const wranglerPath = join13(resolve7(cwd), "wrangler.toml");
|
|
2433
|
-
if (!existsSync12(wranglerPath)) return null;
|
|
2434
|
-
try {
|
|
2435
|
-
const cfg = parseToml4(readFileSync8(wranglerPath, "utf-8"));
|
|
2436
|
-
return typeof cfg.name === "string" && cfg.name.length > 0 ? cfg.name : null;
|
|
2437
|
-
} catch {
|
|
2438
|
-
return null;
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
function requireAppName(explicit, cwd) {
|
|
2442
|
-
if (explicit) return explicit;
|
|
2443
|
-
const detected = detectAppName(cwd);
|
|
2444
|
-
if (detected) return detected;
|
|
2445
|
-
throw new Error(
|
|
2446
|
-
"No app specified. Pass --app <name>, or run from an app directory with a wrangler.toml."
|
|
2447
|
-
);
|
|
2448
|
-
}
|
|
2449
|
-
|
|
2450
|
-
// src/cli/commands/domain.ts
|
|
2451
3352
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
2452
3353
|
async function api(token2, path, init) {
|
|
2453
3354
|
const res = await fetch(`${API_URL2}${path}`, {
|
|
@@ -2458,16 +3359,16 @@ async function api(token2, path, init) {
|
|
|
2458
3359
|
...init?.headers ?? {}
|
|
2459
3360
|
}
|
|
2460
3361
|
});
|
|
2461
|
-
const
|
|
3362
|
+
const text2 = await res.text();
|
|
2462
3363
|
if (!res.ok) {
|
|
2463
|
-
let msg =
|
|
3364
|
+
let msg = text2;
|
|
2464
3365
|
try {
|
|
2465
|
-
msg = JSON.parse(
|
|
3366
|
+
msg = JSON.parse(text2).error ?? text2;
|
|
2466
3367
|
} catch {
|
|
2467
3368
|
}
|
|
2468
3369
|
throw new Error(`API ${path} (${res.status}): ${msg}`);
|
|
2469
3370
|
}
|
|
2470
|
-
return JSON.parse(
|
|
3371
|
+
return JSON.parse(text2);
|
|
2471
3372
|
}
|
|
2472
3373
|
function scrubInternal(value) {
|
|
2473
3374
|
if (Array.isArray(value)) return value.map(scrubInternal);
|
|
@@ -2502,7 +3403,7 @@ async function confirm3(message) {
|
|
|
2502
3403
|
});
|
|
2503
3404
|
});
|
|
2504
3405
|
}
|
|
2505
|
-
var search =
|
|
3406
|
+
var search = defineCommand14({
|
|
2506
3407
|
meta: { name: "search", description: "Search for available domains" },
|
|
2507
3408
|
args: {
|
|
2508
3409
|
query: { type: "positional", description: "Domain or keyword", required: true },
|
|
@@ -2532,7 +3433,7 @@ var search = defineCommand13({
|
|
|
2532
3433
|
}
|
|
2533
3434
|
}
|
|
2534
3435
|
});
|
|
2535
|
-
var buy =
|
|
3436
|
+
var buy = defineCommand14({
|
|
2536
3437
|
meta: { name: "buy", description: "Buy a domain and attach it to an app" },
|
|
2537
3438
|
args: {
|
|
2538
3439
|
domain: { type: "positional", description: "Domain to buy", required: true },
|
|
@@ -2621,7 +3522,7 @@ var buy = defineCommand13({
|
|
|
2621
3522
|
process.exit(1);
|
|
2622
3523
|
}
|
|
2623
3524
|
});
|
|
2624
|
-
var
|
|
3525
|
+
var list3 = defineCommand14({
|
|
2625
3526
|
meta: { name: "list", description: "List your domains" },
|
|
2626
3527
|
args: {
|
|
2627
3528
|
json: { type: "boolean", description: "Emit JSON" }
|
|
@@ -2645,7 +3546,7 @@ var list2 = defineCommand13({
|
|
|
2645
3546
|
}
|
|
2646
3547
|
}
|
|
2647
3548
|
});
|
|
2648
|
-
var status =
|
|
3549
|
+
var status = defineCommand14({
|
|
2649
3550
|
meta: { name: "status", description: "Show details for one domain" },
|
|
2650
3551
|
args: {
|
|
2651
3552
|
domain: { type: "positional", description: "Domain", required: true },
|
|
@@ -2673,7 +3574,7 @@ var status = defineCommand13({
|
|
|
2673
3574
|
console.log(`Charged: ${fmtCents(found.chargedCents)}/yr`);
|
|
2674
3575
|
}
|
|
2675
3576
|
});
|
|
2676
|
-
var attach =
|
|
3577
|
+
var attach = defineCommand14({
|
|
2677
3578
|
meta: { name: "attach", description: "Re-point a domain at a different app" },
|
|
2678
3579
|
args: {
|
|
2679
3580
|
domain: { type: "positional", description: "Domain to re-point", required: true },
|
|
@@ -2704,7 +3605,7 @@ var attach = defineCommand13({
|
|
|
2704
3605
|
console.log(`\u2713 ${args.domain} now points at ${targetApp}`);
|
|
2705
3606
|
}
|
|
2706
3607
|
});
|
|
2707
|
-
var detach =
|
|
3608
|
+
var detach = defineCommand14({
|
|
2708
3609
|
meta: {
|
|
2709
3610
|
name: "detach",
|
|
2710
3611
|
description: "Stop routing the domain (keeps the registration; auto-renew unchanged)"
|
|
@@ -2734,7 +3635,7 @@ var detach = defineCommand13({
|
|
|
2734
3635
|
console.log(`\u2713 ${args.domain} detached. Use \`deepspace domain attach\` to re-route, or \`renew --auto off\` to stop auto-renewal.`);
|
|
2735
3636
|
}
|
|
2736
3637
|
});
|
|
2737
|
-
var renew =
|
|
3638
|
+
var renew = defineCommand14({
|
|
2738
3639
|
meta: { name: "renew", description: "Toggle auto-renewal at the registrar" },
|
|
2739
3640
|
args: {
|
|
2740
3641
|
domain: { type: "positional", description: "Domain", required: true },
|
|
@@ -2768,16 +3669,16 @@ function openBrowser2(url) {
|
|
|
2768
3669
|
} catch {
|
|
2769
3670
|
}
|
|
2770
3671
|
}
|
|
2771
|
-
var domain_default =
|
|
3672
|
+
var domain_default = defineCommand14({
|
|
2772
3673
|
meta: { name: "domain", description: "Buy and manage custom domains" },
|
|
2773
|
-
subCommands: { search, buy, list:
|
|
3674
|
+
subCommands: { search, buy, list: list3, status, attach, detach, renew }
|
|
2774
3675
|
});
|
|
2775
3676
|
|
|
2776
3677
|
// src/cli/commands/integrations.ts
|
|
2777
|
-
import { defineCommand as
|
|
3678
|
+
import { defineCommand as defineCommand15 } from "citty";
|
|
2778
3679
|
|
|
2779
3680
|
// src/cli/commands/_invoke-impl.ts
|
|
2780
|
-
import { readFileSync as
|
|
3681
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
2781
3682
|
var API_URL3 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
2782
3683
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
2783
3684
|
function parseTarget(target) {
|
|
@@ -2798,7 +3699,7 @@ function resolveBody(opts) {
|
|
|
2798
3699
|
if (opts.body != null) {
|
|
2799
3700
|
raw = opts.body;
|
|
2800
3701
|
} else if (opts.bodyFile != null) {
|
|
2801
|
-
raw = opts.bodyFile === "-" ?
|
|
3702
|
+
raw = opts.bodyFile === "-" ? readFileSync10(0, "utf-8") : readFileSync10(opts.bodyFile, "utf-8");
|
|
2802
3703
|
} else {
|
|
2803
3704
|
return "{}";
|
|
2804
3705
|
}
|
|
@@ -2815,8 +3716,9 @@ function flushAndExit(code) {
|
|
|
2815
3716
|
process.stdout.write("", () => process.exit(code));
|
|
2816
3717
|
});
|
|
2817
3718
|
}
|
|
2818
|
-
async function fetchCatalog() {
|
|
2819
|
-
const
|
|
3719
|
+
async function fetchCatalog(opts = {}) {
|
|
3720
|
+
const url = opts.summary ? `${API_URL3}/api/integrations?summary=1` : `${API_URL3}/api/integrations`;
|
|
3721
|
+
const res = await fetch(url);
|
|
2820
3722
|
if (!res.ok) {
|
|
2821
3723
|
throw new Error(`Failed to fetch integration catalog (${res.status})`);
|
|
2822
3724
|
}
|
|
@@ -2832,7 +3734,7 @@ function formatCurrency(value, currency) {
|
|
|
2832
3734
|
return `${value} ${currency}`;
|
|
2833
3735
|
}
|
|
2834
3736
|
async function runList(args) {
|
|
2835
|
-
const catalog = await fetchCatalog();
|
|
3737
|
+
const catalog = await fetchCatalog({ summary: true });
|
|
2836
3738
|
if (args.json) {
|
|
2837
3739
|
process.stdout.write(JSON.stringify(catalog) + "\n");
|
|
2838
3740
|
await flushAndExit(0);
|
|
@@ -2948,7 +3850,7 @@ async function runInvoke(args) {
|
|
|
2948
3850
|
}
|
|
2949
3851
|
|
|
2950
3852
|
// src/cli/commands/integrations.ts
|
|
2951
|
-
var
|
|
3853
|
+
var list4 = defineCommand15({
|
|
2952
3854
|
meta: {
|
|
2953
3855
|
name: "list",
|
|
2954
3856
|
description: "List all available integrations"
|
|
@@ -2969,7 +3871,7 @@ var list3 = defineCommand14({
|
|
|
2969
3871
|
}
|
|
2970
3872
|
}
|
|
2971
3873
|
});
|
|
2972
|
-
var info =
|
|
3874
|
+
var info = defineCommand15({
|
|
2973
3875
|
meta: {
|
|
2974
3876
|
name: "info",
|
|
2975
3877
|
description: "Print schema + example body for an integration endpoint"
|
|
@@ -2995,7 +3897,7 @@ var info = defineCommand14({
|
|
|
2995
3897
|
}
|
|
2996
3898
|
}
|
|
2997
3899
|
});
|
|
2998
|
-
var invoke =
|
|
3900
|
+
var invoke = defineCommand15({
|
|
2999
3901
|
meta: {
|
|
3000
3902
|
name: "invoke",
|
|
3001
3903
|
description: "Invoke a platform integration as the logged-in user"
|
|
@@ -3048,21 +3950,21 @@ var invoke = defineCommand14({
|
|
|
3048
3950
|
}
|
|
3049
3951
|
}
|
|
3050
3952
|
});
|
|
3051
|
-
var integrations_default =
|
|
3953
|
+
var integrations_default = defineCommand15({
|
|
3052
3954
|
meta: {
|
|
3053
3955
|
name: "integrations",
|
|
3054
3956
|
description: "List, inspect, and invoke platform integrations"
|
|
3055
3957
|
},
|
|
3056
3958
|
subCommands: {
|
|
3057
|
-
list:
|
|
3959
|
+
list: list4,
|
|
3058
3960
|
info,
|
|
3059
3961
|
invoke
|
|
3060
3962
|
}
|
|
3061
3963
|
});
|
|
3062
3964
|
|
|
3063
3965
|
// src/cli/commands/invoke.ts
|
|
3064
|
-
import { defineCommand as
|
|
3065
|
-
var invoke_default =
|
|
3966
|
+
import { defineCommand as defineCommand16 } from "citty";
|
|
3967
|
+
var invoke_default = defineCommand16({
|
|
3066
3968
|
meta: {
|
|
3067
3969
|
name: "invoke",
|
|
3068
3970
|
description: "Invoke a platform integration as the logged-in user"
|
|
@@ -3135,7 +4037,7 @@ var invoke_default = defineCommand15({
|
|
|
3135
4037
|
});
|
|
3136
4038
|
|
|
3137
4039
|
// src/cli/commands/managed-repos.ts
|
|
3138
|
-
import { defineCommand as
|
|
4040
|
+
import { defineCommand as defineCommand17 } from "citty";
|
|
3139
4041
|
var API_URL4 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
3140
4042
|
var SIMPLE_APP_ID = /^[a-z0-9][a-z0-9_.:-]{0,119}$/i;
|
|
3141
4043
|
var SIMPLE_APP_NAME = /^[a-z0-9][a-z0-9_-]{0,119}$/i;
|
|
@@ -3148,16 +4050,16 @@ async function api2(token2, path, init) {
|
|
|
3148
4050
|
...init?.headers ?? {}
|
|
3149
4051
|
}
|
|
3150
4052
|
});
|
|
3151
|
-
const
|
|
4053
|
+
const text2 = await res.text();
|
|
3152
4054
|
if (!res.ok) {
|
|
3153
|
-
let msg =
|
|
4055
|
+
let msg = text2;
|
|
3154
4056
|
try {
|
|
3155
|
-
msg = JSON.parse(
|
|
4057
|
+
msg = JSON.parse(text2).error ?? text2;
|
|
3156
4058
|
} catch {
|
|
3157
4059
|
}
|
|
3158
4060
|
throw new Error(`API ${path} (${res.status}): ${msg}`);
|
|
3159
4061
|
}
|
|
3160
|
-
return JSON.parse(
|
|
4062
|
+
return JSON.parse(text2);
|
|
3161
4063
|
}
|
|
3162
4064
|
function defaultAppId(appName) {
|
|
3163
4065
|
return `cli-${slugId(appName)}`;
|
|
@@ -3194,7 +4096,7 @@ async function confirm4(message) {
|
|
|
3194
4096
|
});
|
|
3195
4097
|
});
|
|
3196
4098
|
}
|
|
3197
|
-
var
|
|
4099
|
+
var list5 = defineCommand17({
|
|
3198
4100
|
meta: {
|
|
3199
4101
|
name: "list",
|
|
3200
4102
|
description: "List your DeepSpace-managed repos and quota"
|
|
@@ -3224,7 +4126,7 @@ var list4 = defineCommand16({
|
|
|
3224
4126
|
}
|
|
3225
4127
|
}
|
|
3226
4128
|
});
|
|
3227
|
-
var create2 =
|
|
4129
|
+
var create2 = defineCommand17({
|
|
3228
4130
|
meta: {
|
|
3229
4131
|
name: "create",
|
|
3230
4132
|
description: "Create a private DeepSpace-managed GitHub repo"
|
|
@@ -3276,7 +4178,7 @@ var create2 = defineCommand16({
|
|
|
3276
4178
|
printRepo(result.repo);
|
|
3277
4179
|
}
|
|
3278
4180
|
});
|
|
3279
|
-
var token =
|
|
4181
|
+
var token = defineCommand17({
|
|
3280
4182
|
meta: {
|
|
3281
4183
|
name: "token",
|
|
3282
4184
|
description: "Mint a short-lived GitHub token for a DeepSpace-managed repo"
|
|
@@ -3311,7 +4213,7 @@ var token = defineCommand16({
|
|
|
3311
4213
|
console.log(`Clone: git clone https://x-access-token:${encodeURIComponent(result.token.token)}@github.com/${result.repo.fullName}.git`);
|
|
3312
4214
|
}
|
|
3313
4215
|
});
|
|
3314
|
-
var remove =
|
|
4216
|
+
var remove = defineCommand17({
|
|
3315
4217
|
meta: {
|
|
3316
4218
|
name: "delete",
|
|
3317
4219
|
description: "Delete a DeepSpace-managed GitHub repo"
|
|
@@ -3364,13 +4266,13 @@ var remove = defineCommand16({
|
|
|
3364
4266
|
);
|
|
3365
4267
|
}
|
|
3366
4268
|
});
|
|
3367
|
-
var managed_repos_default =
|
|
4269
|
+
var managed_repos_default = defineCommand17({
|
|
3368
4270
|
meta: {
|
|
3369
4271
|
name: "managed-repos",
|
|
3370
4272
|
description: "Create and manage DeepSpace-owned GitHub repos"
|
|
3371
4273
|
},
|
|
3372
4274
|
subCommands: {
|
|
3373
|
-
list:
|
|
4275
|
+
list: list5,
|
|
3374
4276
|
create: create2,
|
|
3375
4277
|
token,
|
|
3376
4278
|
delete: remove
|
|
@@ -3378,13 +4280,13 @@ var managed_repos_default = defineCommand16({
|
|
|
3378
4280
|
});
|
|
3379
4281
|
|
|
3380
4282
|
// src/cli/commands/library.ts
|
|
3381
|
-
import { defineCommand as
|
|
3382
|
-
import { existsSync as
|
|
3383
|
-
import { join as
|
|
3384
|
-
import { parse as
|
|
4283
|
+
import { defineCommand as defineCommand18 } from "citty";
|
|
4284
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
4285
|
+
import { join as join13, resolve as resolve8 } from "path";
|
|
4286
|
+
import { parse as parseToml3 } from "smol-toml";
|
|
3385
4287
|
import * as p6 from "@clack/prompts";
|
|
3386
4288
|
var DEFAULT_LIBRARY_APP = "deepdotspace-site";
|
|
3387
|
-
var publish =
|
|
4289
|
+
var publish = defineCommand18({
|
|
3388
4290
|
meta: {
|
|
3389
4291
|
name: "publish",
|
|
3390
4292
|
description: "Publish the current app to the DeepSpace library"
|
|
@@ -3443,7 +4345,7 @@ var publish = defineCommand17({
|
|
|
3443
4345
|
},
|
|
3444
4346
|
async run({ args }) {
|
|
3445
4347
|
const json = !!args.json;
|
|
3446
|
-
const
|
|
4348
|
+
const intro6 = (s) => {
|
|
3447
4349
|
if (!json) p6.intro(s);
|
|
3448
4350
|
};
|
|
3449
4351
|
const info2 = (s) => {
|
|
@@ -3454,15 +4356,15 @@ var publish = defineCommand17({
|
|
|
3454
4356
|
else p6.cancel(msg);
|
|
3455
4357
|
process.exit(1);
|
|
3456
4358
|
};
|
|
3457
|
-
|
|
4359
|
+
intro6("Publishing to the DeepSpace library");
|
|
3458
4360
|
let appName = typeof args.app === "string" && args.app.trim() ? args.app.trim() : void 0;
|
|
3459
4361
|
if (!appName) {
|
|
3460
4362
|
const appDir = resolve8(typeof args.dir === "string" && args.dir ? args.dir : ".");
|
|
3461
|
-
const wranglerPath =
|
|
3462
|
-
if (!
|
|
4363
|
+
const wranglerPath = join13(appDir, "wrangler.toml");
|
|
4364
|
+
if (!existsSync12(wranglerPath)) {
|
|
3463
4365
|
return die("No --app provided and no wrangler.toml found. Either pass --app <name> or run from your app's directory.");
|
|
3464
4366
|
}
|
|
3465
|
-
const cfg =
|
|
4367
|
+
const cfg = parseToml3(readFileSync11(wranglerPath, "utf-8"));
|
|
3466
4368
|
const nameRes = resolveAppName(cfg.name);
|
|
3467
4369
|
if (!nameRes.ok) return die(`wrangler.toml: ${nameRes.reason}`);
|
|
3468
4370
|
appName = nameRes.name;
|
|
@@ -3523,7 +4425,7 @@ var publish = defineCommand17({
|
|
|
3523
4425
|
p6.outro("Done");
|
|
3524
4426
|
}
|
|
3525
4427
|
});
|
|
3526
|
-
var unpublish =
|
|
4428
|
+
var unpublish = defineCommand18({
|
|
3527
4429
|
meta: {
|
|
3528
4430
|
name: "unpublish",
|
|
3529
4431
|
description: "Remove a library entry (owner or admin only)"
|
|
@@ -3582,7 +4484,7 @@ var unpublish = defineCommand17({
|
|
|
3582
4484
|
p6.outro("Done");
|
|
3583
4485
|
}
|
|
3584
4486
|
});
|
|
3585
|
-
var library_default =
|
|
4487
|
+
var library_default = defineCommand18({
|
|
3586
4488
|
meta: {
|
|
3587
4489
|
name: "library",
|
|
3588
4490
|
description: "Manage your DeepSpace library entries"
|
|
@@ -3594,12 +4496,12 @@ var library_default = defineCommand17({
|
|
|
3594
4496
|
});
|
|
3595
4497
|
|
|
3596
4498
|
// src/cli/commands/admin.ts
|
|
3597
|
-
import { defineCommand as
|
|
3598
|
-
import { mkdirSync as
|
|
4499
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
4500
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
3599
4501
|
import { homedir as homedir6 } from "os";
|
|
3600
|
-
import { join as
|
|
4502
|
+
import { join as join14 } from "path";
|
|
3601
4503
|
var AUTH_URL5 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
3602
|
-
var impersonate =
|
|
4504
|
+
var impersonate = defineCommand19({
|
|
3603
4505
|
meta: {
|
|
3604
4506
|
name: "impersonate",
|
|
3605
4507
|
description: "Store a short-lived CLI token for another user"
|
|
@@ -3647,15 +4549,15 @@ var impersonate = defineCommand18({
|
|
|
3647
4549
|
console.error(`Failed: ${data.error ?? `HTTP ${res.status}`}`);
|
|
3648
4550
|
process.exit(1);
|
|
3649
4551
|
}
|
|
3650
|
-
const dir =
|
|
3651
|
-
|
|
3652
|
-
|
|
4552
|
+
const dir = join14(homedir6(), ".deepspace");
|
|
4553
|
+
mkdirSync6(dir, { recursive: true });
|
|
4554
|
+
writeFileSync7(TOKEN_PATH2, data.token, { mode: 384 });
|
|
3653
4555
|
console.log(`Impersonating ${data.user.name ?? data.user.email ?? data.user.id}`);
|
|
3654
4556
|
console.log(`Email: ${data.user.email ?? "(none)"}`);
|
|
3655
4557
|
console.log(`UserID: ${data.user.id}`);
|
|
3656
4558
|
}
|
|
3657
4559
|
});
|
|
3658
|
-
var admin_default =
|
|
4560
|
+
var admin_default = defineCommand19({
|
|
3659
4561
|
meta: {
|
|
3660
4562
|
name: "admin",
|
|
3661
4563
|
description: "Maintainer-only admin commands"
|
|
@@ -3665,13 +4567,276 @@ var admin_default = defineCommand18({
|
|
|
3665
4567
|
}
|
|
3666
4568
|
});
|
|
3667
4569
|
|
|
4570
|
+
// src/cli/commands/feedback.ts
|
|
4571
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
4572
|
+
import * as p7 from "@clack/prompts";
|
|
4573
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
4574
|
+
import { dirname as dirname3, join as join15 } from "path";
|
|
4575
|
+
import { fileURLToPath } from "url";
|
|
4576
|
+
import { release } from "os";
|
|
4577
|
+
var API_URL5 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
4578
|
+
var TYPES = ["bug", "feature", "other"];
|
|
4579
|
+
var TYPE_LABELS = {
|
|
4580
|
+
bug: "Bug report",
|
|
4581
|
+
feature: "Feature request",
|
|
4582
|
+
other: "Other feedback"
|
|
4583
|
+
};
|
|
4584
|
+
var MAX_TITLE_LEN = 200;
|
|
4585
|
+
var MAX_BODY_LEN = 1e4;
|
|
4586
|
+
var MAX_CONTEXT_LEN = 200;
|
|
4587
|
+
async function api3(token2, path, init) {
|
|
4588
|
+
const res = await fetch(`${API_URL5}${path}`, {
|
|
4589
|
+
...init,
|
|
4590
|
+
headers: {
|
|
4591
|
+
Authorization: `Bearer ${token2}`,
|
|
4592
|
+
"Content-Type": "application/json",
|
|
4593
|
+
...init?.headers ?? {}
|
|
4594
|
+
}
|
|
4595
|
+
});
|
|
4596
|
+
const text2 = await res.text();
|
|
4597
|
+
if (!res.ok) {
|
|
4598
|
+
let msg = text2;
|
|
4599
|
+
try {
|
|
4600
|
+
msg = JSON.parse(text2).error ?? text2;
|
|
4601
|
+
} catch {
|
|
4602
|
+
}
|
|
4603
|
+
throw new Error(`API ${path} (${res.status}): ${msg}`);
|
|
4604
|
+
}
|
|
4605
|
+
return JSON.parse(text2);
|
|
4606
|
+
}
|
|
4607
|
+
function normalizeType(raw) {
|
|
4608
|
+
const value = (raw ?? "bug").toLowerCase();
|
|
4609
|
+
if (TYPES.includes(value)) return value;
|
|
4610
|
+
throw new Error(`Invalid --type "${raw}". Use one of: ${TYPES.join(", ")}.`);
|
|
4611
|
+
}
|
|
4612
|
+
function readCliVersion() {
|
|
4613
|
+
try {
|
|
4614
|
+
const here = dirname3(fileURLToPath(import.meta.url));
|
|
4615
|
+
const candidates = [
|
|
4616
|
+
join15(here, "..", "package.json"),
|
|
4617
|
+
join15(here, "..", "..", "..", "package.json")
|
|
4618
|
+
];
|
|
4619
|
+
for (const path of candidates) {
|
|
4620
|
+
if (!existsSync13(path)) continue;
|
|
4621
|
+
const pkg2 = JSON.parse(readFileSync12(path, "utf-8"));
|
|
4622
|
+
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
4623
|
+
}
|
|
4624
|
+
} catch {
|
|
4625
|
+
}
|
|
4626
|
+
return "unknown";
|
|
4627
|
+
}
|
|
4628
|
+
function detectAppName2() {
|
|
4629
|
+
try {
|
|
4630
|
+
const cwd = process.cwd();
|
|
4631
|
+
if (!hasWranglerConfig(cwd)) return void 0;
|
|
4632
|
+
const config = readWranglerConfig(cwd);
|
|
4633
|
+
return typeof config.name === "string" ? config.name : void 0;
|
|
4634
|
+
} catch {
|
|
4635
|
+
return void 0;
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
function clampContext(value) {
|
|
4639
|
+
return value.length > MAX_CONTEXT_LEN ? value.slice(0, MAX_CONTEXT_LEN) : value;
|
|
4640
|
+
}
|
|
4641
|
+
function collectContext() {
|
|
4642
|
+
const appName = detectAppName2();
|
|
4643
|
+
return {
|
|
4644
|
+
cliVersion: clampContext(readCliVersion()),
|
|
4645
|
+
nodeVersion: clampContext(process.version),
|
|
4646
|
+
platform: clampContext(`${process.platform} ${release()}`),
|
|
4647
|
+
...appName ? { appName: clampContext(appName) } : {}
|
|
4648
|
+
};
|
|
4649
|
+
}
|
|
4650
|
+
function buildFeedbackPayload(input) {
|
|
4651
|
+
const title = input.title.trim();
|
|
4652
|
+
const body = input.body.trim();
|
|
4653
|
+
if (!title) throw new Error("A title is required.");
|
|
4654
|
+
if (!body) throw new Error("A message/description is required.");
|
|
4655
|
+
if (title.length > MAX_TITLE_LEN) {
|
|
4656
|
+
throw new Error(
|
|
4657
|
+
`Title must be ${MAX_TITLE_LEN} characters or fewer (got ${title.length}).`
|
|
4658
|
+
);
|
|
4659
|
+
}
|
|
4660
|
+
if (body.length > MAX_BODY_LEN) {
|
|
4661
|
+
throw new Error(
|
|
4662
|
+
`Description must be ${MAX_BODY_LEN} characters or fewer (got ${body.length}).`
|
|
4663
|
+
);
|
|
4664
|
+
}
|
|
4665
|
+
return {
|
|
4666
|
+
type: normalizeType(input.type),
|
|
4667
|
+
title,
|
|
4668
|
+
body,
|
|
4669
|
+
...input.context
|
|
4670
|
+
};
|
|
4671
|
+
}
|
|
4672
|
+
var feedback_default = defineCommand20({
|
|
4673
|
+
meta: {
|
|
4674
|
+
name: "feedback",
|
|
4675
|
+
description: "Submit a bug report, feature request, or feedback to DeepSpace"
|
|
4676
|
+
},
|
|
4677
|
+
args: {
|
|
4678
|
+
title: {
|
|
4679
|
+
type: "positional",
|
|
4680
|
+
description: "Short summary (prompted if omitted in a terminal)",
|
|
4681
|
+
required: false
|
|
4682
|
+
},
|
|
4683
|
+
type: {
|
|
4684
|
+
type: "string",
|
|
4685
|
+
alias: "t",
|
|
4686
|
+
description: `Report type: ${TYPES.join(" | ")} (default: bug)`,
|
|
4687
|
+
required: false
|
|
4688
|
+
},
|
|
4689
|
+
message: {
|
|
4690
|
+
type: "string",
|
|
4691
|
+
alias: "m",
|
|
4692
|
+
description: "Details / description (prompted if omitted in a terminal)",
|
|
4693
|
+
required: false
|
|
4694
|
+
},
|
|
4695
|
+
yes: {
|
|
4696
|
+
type: "boolean",
|
|
4697
|
+
description: "Skip the confirmation prompt",
|
|
4698
|
+
default: false
|
|
4699
|
+
},
|
|
4700
|
+
json: {
|
|
4701
|
+
type: "boolean",
|
|
4702
|
+
description: "Emit JSON instead of human output",
|
|
4703
|
+
default: false
|
|
4704
|
+
}
|
|
4705
|
+
},
|
|
4706
|
+
async run({ args }) {
|
|
4707
|
+
const interactive = process.stdin.isTTY && !args.json;
|
|
4708
|
+
const fail = (message) => {
|
|
4709
|
+
if (args.json) {
|
|
4710
|
+
process.stdout.write(JSON.stringify({ ok: false, error: message }) + "\n");
|
|
4711
|
+
} else {
|
|
4712
|
+
console.error(message);
|
|
4713
|
+
}
|
|
4714
|
+
process.exit(1);
|
|
4715
|
+
};
|
|
4716
|
+
let type;
|
|
4717
|
+
try {
|
|
4718
|
+
type = normalizeType(args.type ? String(args.type) : void 0);
|
|
4719
|
+
} catch (err) {
|
|
4720
|
+
return fail(err.message);
|
|
4721
|
+
}
|
|
4722
|
+
let title = args.title ? String(args.title) : "";
|
|
4723
|
+
let body = args.message ? String(args.message) : "";
|
|
4724
|
+
if ((!title || !body) && !interactive) {
|
|
4725
|
+
return fail(
|
|
4726
|
+
'Provide a title and --message to submit non-interactively, e.g. `deepspace feedback "Title" --message "Details"`.'
|
|
4727
|
+
);
|
|
4728
|
+
}
|
|
4729
|
+
if (interactive && (!args.type || !title || !body)) {
|
|
4730
|
+
p7.intro("DeepSpace Feedback");
|
|
4731
|
+
if (!args.type) {
|
|
4732
|
+
const picked = await p7.select({
|
|
4733
|
+
message: "What kind of feedback is this?",
|
|
4734
|
+
options: TYPES.map((t) => ({ value: t, label: TYPE_LABELS[t] })),
|
|
4735
|
+
initialValue: type
|
|
4736
|
+
});
|
|
4737
|
+
if (p7.isCancel(picked)) {
|
|
4738
|
+
p7.cancel("Cancelled.");
|
|
4739
|
+
process.exit(0);
|
|
4740
|
+
}
|
|
4741
|
+
type = picked;
|
|
4742
|
+
}
|
|
4743
|
+
if (!title) {
|
|
4744
|
+
const t = await p7.text({
|
|
4745
|
+
message: "Title",
|
|
4746
|
+
placeholder: "Short summary of the issue",
|
|
4747
|
+
validate: (v) => {
|
|
4748
|
+
if (!v?.trim()) return "Title is required";
|
|
4749
|
+
if (v.length > MAX_TITLE_LEN)
|
|
4750
|
+
return `Title must be ${MAX_TITLE_LEN} characters or fewer`;
|
|
4751
|
+
return void 0;
|
|
4752
|
+
}
|
|
4753
|
+
});
|
|
4754
|
+
if (p7.isCancel(t)) {
|
|
4755
|
+
p7.cancel("Cancelled.");
|
|
4756
|
+
process.exit(0);
|
|
4757
|
+
}
|
|
4758
|
+
title = String(t);
|
|
4759
|
+
}
|
|
4760
|
+
if (!body) {
|
|
4761
|
+
const m = await p7.text({
|
|
4762
|
+
message: "Details",
|
|
4763
|
+
placeholder: "What happened? Steps to reproduce, what you expected\u2026",
|
|
4764
|
+
validate: (v) => {
|
|
4765
|
+
if (!v?.trim()) return "A description is required";
|
|
4766
|
+
if (v.length > MAX_BODY_LEN)
|
|
4767
|
+
return `Description must be ${MAX_BODY_LEN} characters or fewer`;
|
|
4768
|
+
return void 0;
|
|
4769
|
+
}
|
|
4770
|
+
});
|
|
4771
|
+
if (p7.isCancel(m)) {
|
|
4772
|
+
p7.cancel("Cancelled.");
|
|
4773
|
+
process.exit(0);
|
|
4774
|
+
}
|
|
4775
|
+
body = String(m);
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
let payload;
|
|
4779
|
+
try {
|
|
4780
|
+
payload = buildFeedbackPayload({
|
|
4781
|
+
type,
|
|
4782
|
+
title,
|
|
4783
|
+
body,
|
|
4784
|
+
context: collectContext()
|
|
4785
|
+
});
|
|
4786
|
+
} catch (err) {
|
|
4787
|
+
return fail(err.message);
|
|
4788
|
+
}
|
|
4789
|
+
if (interactive && !args.yes) {
|
|
4790
|
+
p7.note(
|
|
4791
|
+
`${TYPE_LABELS[payload.type]}
|
|
4792
|
+
${payload.title}
|
|
4793
|
+
|
|
4794
|
+
${payload.body}
|
|
4795
|
+
|
|
4796
|
+
Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payload.platform}` + (payload.appName ? ` \xB7 app ${payload.appName}` : ""),
|
|
4797
|
+
"Review"
|
|
4798
|
+
);
|
|
4799
|
+
const ok = await p7.confirm({ message: "Send this to the DeepSpace team?" });
|
|
4800
|
+
if (p7.isCancel(ok) || !ok) {
|
|
4801
|
+
p7.cancel("Not sent.");
|
|
4802
|
+
process.exit(0);
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
let token2;
|
|
4806
|
+
try {
|
|
4807
|
+
token2 = await ensureToken();
|
|
4808
|
+
} catch (err) {
|
|
4809
|
+
return fail(err.message ?? "Not signed in. Run `deepspace login`.");
|
|
4810
|
+
}
|
|
4811
|
+
let result;
|
|
4812
|
+
try {
|
|
4813
|
+
result = await api3(token2, "/api/feedback", {
|
|
4814
|
+
method: "POST",
|
|
4815
|
+
body: JSON.stringify(payload)
|
|
4816
|
+
});
|
|
4817
|
+
} catch (err) {
|
|
4818
|
+
return fail(err.message);
|
|
4819
|
+
}
|
|
4820
|
+
if (args.json) {
|
|
4821
|
+
process.stdout.write(JSON.stringify({ ok: true, report: result.report }) + "\n");
|
|
4822
|
+
return;
|
|
4823
|
+
}
|
|
4824
|
+
if (interactive) {
|
|
4825
|
+
p7.outro(`Thanks! Submitted as ${result.report.id}.`);
|
|
4826
|
+
} else {
|
|
4827
|
+
console.log(`Submitted feedback ${result.report.id} (${result.report.type}).`);
|
|
4828
|
+
}
|
|
4829
|
+
console.log(`Track triage status at ${DASHBOARD_URL}.`);
|
|
4830
|
+
}
|
|
4831
|
+
});
|
|
4832
|
+
|
|
3668
4833
|
// src/cli/cli.ts
|
|
3669
|
-
var __dirname =
|
|
3670
|
-
var pkg = JSON.parse(
|
|
4834
|
+
var __dirname = dirname4(fileURLToPath2(import.meta.url));
|
|
4835
|
+
var pkg = JSON.parse(readFileSync13(join16(__dirname, "..", "package.json"), "utf-8"));
|
|
3671
4836
|
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync14("/etc/ssl/certs/ca-certificates.crt")) {
|
|
3672
4837
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
3673
4838
|
}
|
|
3674
|
-
var create3 =
|
|
4839
|
+
var create3 = defineCommand21({
|
|
3675
4840
|
meta: {
|
|
3676
4841
|
name: "create",
|
|
3677
4842
|
description: "Create a new DeepSpace app"
|
|
@@ -3690,7 +4855,7 @@ var create3 = defineCommand19({
|
|
|
3690
4855
|
execSync5(`npm create deepspace@latest${name}`, { stdio: "inherit" });
|
|
3691
4856
|
}
|
|
3692
4857
|
});
|
|
3693
|
-
var main =
|
|
4858
|
+
var main = defineCommand21({
|
|
3694
4859
|
meta: {
|
|
3695
4860
|
name: "deepspace",
|
|
3696
4861
|
version: pkg.version,
|
|
@@ -3715,7 +4880,9 @@ var main = defineCommand19({
|
|
|
3715
4880
|
undeploy: undeploy_default,
|
|
3716
4881
|
domain: domain_default,
|
|
3717
4882
|
library: library_default,
|
|
3718
|
-
admin: admin_default
|
|
4883
|
+
admin: admin_default,
|
|
4884
|
+
feedback: feedback_default,
|
|
4885
|
+
secrets: secrets_default
|
|
3719
4886
|
}
|
|
3720
4887
|
// No `run()` here — citty cascades parent run() AFTER subcommand finishes,
|
|
3721
4888
|
// which corrupts agent-friendly output (`--json`) and prints noise on every
|