deepspace 0.4.3 → 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 +1193 -169
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +47 -425
- package/dist/index.js +272 -1688
- 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,11 +1,11 @@
|
|
|
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";
|
|
@@ -122,9 +122,6 @@ function resolveAppNameForEnv(config, envName) {
|
|
|
122
122
|
}
|
|
123
123
|
return resolveAppName(envBlock.name);
|
|
124
124
|
}
|
|
125
|
-
function devVarsPathFor(appDir, envName) {
|
|
126
|
-
return envName ? join(appDir, `.dev.vars.${envName}`) : join(appDir, ".dev.vars");
|
|
127
|
-
}
|
|
128
125
|
var WranglerConfigError = class extends Error {
|
|
129
126
|
constructor(path, cause) {
|
|
130
127
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
@@ -235,8 +232,22 @@ var SDK_MANAGED_KEYS = /* @__PURE__ */ new Set([
|
|
|
235
232
|
"OWNER_USER_ID",
|
|
236
233
|
"APP_OWNER_JWT",
|
|
237
234
|
"APP_IDENTITY_TOKEN",
|
|
235
|
+
"APP_SECRETS_KEY",
|
|
236
|
+
"APP_SECRETS_LOCAL_FALLBACK",
|
|
238
237
|
"ALLOW_DEBUG_ROUTES"
|
|
239
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
|
+
}
|
|
240
251
|
var DEV_VARS_DIVIDER = "# --- not managed by the SDK; preserved across dev/test runs ---";
|
|
241
252
|
function extractCustomDevVars(content) {
|
|
242
253
|
const lines = content.split("\n");
|
|
@@ -343,6 +354,189 @@ function parseDevVars(content) {
|
|
|
343
354
|
}
|
|
344
355
|
return result;
|
|
345
356
|
}
|
|
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
|
+
}
|
|
346
540
|
async function writeDevVars(appDir, env, ownerId, callerJwt, wranglerEnv) {
|
|
347
541
|
const urls = ENVS[env];
|
|
348
542
|
const publicKey = await fetchPublicKey(urls.auth);
|
|
@@ -358,11 +552,18 @@ async function writeDevVars(appDir, env, ownerId, callerJwt, wranglerEnv) {
|
|
|
358
552
|
`OWNER_USER_ID=${ownerId}`,
|
|
359
553
|
`APP_OWNER_JWT=${appOwnerJwt}`,
|
|
360
554
|
...appIdentityToken ? [`APP_IDENTITY_TOKEN=${appIdentityToken}`] : [],
|
|
555
|
+
`APP_SECRETS_LOCAL_FALLBACK=true`,
|
|
361
556
|
// Open /api/debug/* (proxied to RecordRoom) for dev + tests only.
|
|
362
557
|
// Production secrets never set this, so deployed apps 404 the route.
|
|
363
558
|
`ALLOW_DEBUG_ROUTES=true`
|
|
364
559
|
].join("\n");
|
|
365
|
-
const
|
|
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);
|
|
366
567
|
const existing = existsSync2(devVarsPath) ? readFileSync2(devVarsPath, "utf-8") : "";
|
|
367
568
|
const custom = extractCustomDevVars(existing);
|
|
368
569
|
const body = custom ? `${sdkVars}
|
|
@@ -571,7 +772,7 @@ var logout_default = defineCommand2({
|
|
|
571
772
|
});
|
|
572
773
|
|
|
573
774
|
// src/cli/commands/dev.ts
|
|
574
|
-
import { defineCommand as
|
|
775
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
575
776
|
import { resolve as resolve3, basename } from "path";
|
|
576
777
|
import spawn from "cross-spawn";
|
|
577
778
|
|
|
@@ -746,9 +947,525 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
746
947
|
}
|
|
747
948
|
}
|
|
748
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
|
+
});
|
|
1465
|
+
|
|
749
1466
|
// src/cli/commands/dev.ts
|
|
750
1467
|
var DEFAULT_PORT = 5173;
|
|
751
|
-
var dev_default =
|
|
1468
|
+
var dev_default = defineCommand4({
|
|
752
1469
|
meta: {
|
|
753
1470
|
name: "dev",
|
|
754
1471
|
description: "Start local development server"
|
|
@@ -766,13 +1483,18 @@ var dev_default = defineCommand3({
|
|
|
766
1483
|
},
|
|
767
1484
|
env: {
|
|
768
1485
|
type: "string",
|
|
769
|
-
description: "wrangler.toml [env.<name>] block to run (e.g. --env staging).
|
|
1486
|
+
description: "wrangler.toml [env.<name>] block to run (e.g. --env staging). Legacy apps read .dev.vars.<name>; new apps use managed .env.",
|
|
770
1487
|
required: false
|
|
771
1488
|
},
|
|
772
1489
|
port: {
|
|
773
1490
|
type: "string",
|
|
774
1491
|
description: `Port to bind (default ${DEFAULT_PORT}, or $DEEPSPACE_PORT)`,
|
|
775
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
|
|
776
1498
|
}
|
|
777
1499
|
},
|
|
778
1500
|
async run({ args }) {
|
|
@@ -813,6 +1535,21 @@ var dev_default = defineCommand3({
|
|
|
813
1535
|
if (wranglerEnv) console.log(`Wrangler env: ${wranglerEnv}`);
|
|
814
1536
|
console.log(`Port: ${port}`);
|
|
815
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
|
+
}
|
|
816
1553
|
preflightWindowsWorkerd(appDir);
|
|
817
1554
|
console.log("Starting dev server...\n");
|
|
818
1555
|
const vite = spawn(
|
|
@@ -822,9 +1559,10 @@ var dev_default = defineCommand3({
|
|
|
822
1559
|
cwd: appDir,
|
|
823
1560
|
stdio: "inherit",
|
|
824
1561
|
// CLOUDFLARE_ENV is what the Cloudflare Vite plugin keys off — it
|
|
825
|
-
// applies `[env.<name>]` overrides
|
|
826
|
-
// instead of `.dev.vars
|
|
827
|
-
// default invocation behaves
|
|
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.
|
|
828
1566
|
env: {
|
|
829
1567
|
...process.env,
|
|
830
1568
|
DEEPSPACE_PORT: String(port),
|
|
@@ -847,15 +1585,23 @@ function resolvePort(arg) {
|
|
|
847
1585
|
}
|
|
848
1586
|
return n;
|
|
849
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
|
+
}
|
|
850
1596
|
|
|
851
1597
|
// src/cli/commands/kill.ts
|
|
852
|
-
import { defineCommand as
|
|
1598
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
853
1599
|
import { spawnSync } from "child_process";
|
|
854
1600
|
import { setTimeout as wait } from "timers/promises";
|
|
855
1601
|
var DEFAULT_PORT2 = 5173;
|
|
856
1602
|
var SIGTERM_GRACE_MS = 1500;
|
|
857
1603
|
var IS_WIN = process.platform === "win32";
|
|
858
|
-
var kill_default =
|
|
1604
|
+
var kill_default = defineCommand5({
|
|
859
1605
|
meta: {
|
|
860
1606
|
name: "kill",
|
|
861
1607
|
description: "Kill the local dev server and any orphaned workerd processes"
|
|
@@ -1010,7 +1756,7 @@ function isAlive(pid) {
|
|
|
1010
1756
|
}
|
|
1011
1757
|
|
|
1012
1758
|
// src/cli/commands/test.ts
|
|
1013
|
-
import { defineCommand as
|
|
1759
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
1014
1760
|
import { resolve as resolve4 } from "path";
|
|
1015
1761
|
import { sync as spawnSync3 } from "cross-spawn";
|
|
1016
1762
|
|
|
@@ -1040,7 +1786,7 @@ function ensurePlaywright(appDir) {
|
|
|
1040
1786
|
|
|
1041
1787
|
// src/cli/commands/test.ts
|
|
1042
1788
|
var DEFAULT_PORT3 = 5173;
|
|
1043
|
-
var test_default =
|
|
1789
|
+
var test_default = defineCommand6({
|
|
1044
1790
|
meta: {
|
|
1045
1791
|
name: "test",
|
|
1046
1792
|
description: "Run tests for your DeepSpace app"
|
|
@@ -1155,10 +1901,10 @@ function resolvePort3(arg) {
|
|
|
1155
1901
|
}
|
|
1156
1902
|
|
|
1157
1903
|
// src/cli/commands/screenshot.ts
|
|
1158
|
-
import { defineCommand as
|
|
1904
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
1159
1905
|
import { sync as spawnSync4 } from "cross-spawn";
|
|
1160
1906
|
import { resolve as resolve5 } from "path";
|
|
1161
|
-
var screenshot_default =
|
|
1907
|
+
var screenshot_default = defineCommand7({
|
|
1162
1908
|
meta: {
|
|
1163
1909
|
name: "screenshot",
|
|
1164
1910
|
description: "Take a Playwright screenshot, installing Chromium on demand"
|
|
@@ -1202,29 +1948,29 @@ var screenshot_default = defineCommand6({
|
|
|
1202
1948
|
});
|
|
1203
1949
|
|
|
1204
1950
|
// src/cli/commands/test-accounts.ts
|
|
1205
|
-
import { defineCommand as
|
|
1951
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
1206
1952
|
import * as p3 from "@clack/prompts";
|
|
1207
|
-
import { readFileSync as
|
|
1208
|
-
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";
|
|
1209
1955
|
import { homedir as homedir4 } from "os";
|
|
1210
1956
|
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
1211
1957
|
var AUTH_URL4 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
1212
|
-
var DIR3 =
|
|
1213
|
-
var ACCOUNTS_PATH =
|
|
1958
|
+
var DIR3 = join9(homedir4(), ".deepspace");
|
|
1959
|
+
var ACCOUNTS_PATH = join9(DIR3, "test-accounts.json");
|
|
1214
1960
|
function loadAccounts() {
|
|
1215
|
-
if (!
|
|
1961
|
+
if (!existsSync9(ACCOUNTS_PATH)) return [];
|
|
1216
1962
|
try {
|
|
1217
|
-
return JSON.parse(
|
|
1963
|
+
return JSON.parse(readFileSync8(ACCOUNTS_PATH, "utf-8"));
|
|
1218
1964
|
} catch {
|
|
1219
1965
|
return [];
|
|
1220
1966
|
}
|
|
1221
1967
|
}
|
|
1222
1968
|
function saveAccounts(accounts) {
|
|
1223
1969
|
mkdirSync4(DIR3, { recursive: true });
|
|
1224
|
-
|
|
1970
|
+
writeFileSync6(ACCOUNTS_PATH, JSON.stringify(accounts, null, 2), { mode: 384 });
|
|
1225
1971
|
}
|
|
1226
1972
|
function sessionCookie() {
|
|
1227
|
-
const token2 =
|
|
1973
|
+
const token2 = readFileSync8(SESSION_PATH2, "utf-8").trim();
|
|
1228
1974
|
return `${SESSION_COOKIE2}=${encodeURIComponent(token2)}`;
|
|
1229
1975
|
}
|
|
1230
1976
|
async function fetchRemoteAccounts() {
|
|
@@ -1247,7 +1993,7 @@ async function deleteRemote(id) {
|
|
|
1247
1993
|
throw new Error(data.error ?? `DELETE returned ${res.status}`);
|
|
1248
1994
|
}
|
|
1249
1995
|
}
|
|
1250
|
-
var create =
|
|
1996
|
+
var create = defineCommand8({
|
|
1251
1997
|
meta: {
|
|
1252
1998
|
name: "create",
|
|
1253
1999
|
description: "Create a test account"
|
|
@@ -1317,7 +2063,7 @@ var create = defineCommand7({
|
|
|
1317
2063
|
Saved to ${ACCOUNTS_PATH}`);
|
|
1318
2064
|
}
|
|
1319
2065
|
});
|
|
1320
|
-
var
|
|
2066
|
+
var list2 = defineCommand8({
|
|
1321
2067
|
meta: {
|
|
1322
2068
|
name: "list",
|
|
1323
2069
|
description: "List your test accounts"
|
|
@@ -1352,7 +2098,7 @@ var list = defineCommand7({
|
|
|
1352
2098
|
}
|
|
1353
2099
|
}
|
|
1354
2100
|
});
|
|
1355
|
-
var del =
|
|
2101
|
+
var del = defineCommand8({
|
|
1356
2102
|
meta: {
|
|
1357
2103
|
name: "delete",
|
|
1358
2104
|
description: "Delete a test account by --email or --id"
|
|
@@ -1409,7 +2155,7 @@ var del = defineCommand7({
|
|
|
1409
2155
|
console.log(`Test account deleted${targetEmail ? `: ${targetEmail}` : `: ${targetId}`}`);
|
|
1410
2156
|
}
|
|
1411
2157
|
});
|
|
1412
|
-
var clear =
|
|
2158
|
+
var clear = defineCommand8({
|
|
1413
2159
|
meta: {
|
|
1414
2160
|
name: "clear",
|
|
1415
2161
|
description: "Delete all your test accounts (or those matching --label)"
|
|
@@ -1475,25 +2221,25 @@ Failed:`);
|
|
|
1475
2221
|
}
|
|
1476
2222
|
}
|
|
1477
2223
|
});
|
|
1478
|
-
var test_accounts_default =
|
|
2224
|
+
var test_accounts_default = defineCommand8({
|
|
1479
2225
|
meta: {
|
|
1480
2226
|
name: "test-accounts",
|
|
1481
2227
|
description: "Manage test accounts for development"
|
|
1482
2228
|
},
|
|
1483
2229
|
subCommands: {
|
|
1484
2230
|
create,
|
|
1485
|
-
list,
|
|
2231
|
+
list: list2,
|
|
1486
2232
|
delete: del,
|
|
1487
2233
|
clear
|
|
1488
2234
|
}
|
|
1489
2235
|
});
|
|
1490
2236
|
|
|
1491
2237
|
// src/cli/commands/deploy.ts
|
|
1492
|
-
import { defineCommand as
|
|
2238
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
1493
2239
|
import { execSync as execSync2 } from "child_process";
|
|
1494
2240
|
import { randomUUID } from "crypto";
|
|
1495
|
-
import { readFileSync as
|
|
1496
|
-
import { join as
|
|
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";
|
|
1497
2243
|
import { pathToFileURL } from "url";
|
|
1498
2244
|
import * as p4 from "@clack/prompts";
|
|
1499
2245
|
|
|
@@ -1522,6 +2268,7 @@ var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
|
1522
2268
|
"AUTH_WORKER_URL",
|
|
1523
2269
|
"APP_IDENTITY_TOKEN",
|
|
1524
2270
|
"APP_OWNER_JWT",
|
|
2271
|
+
"APP_SECRETS_KEY",
|
|
1525
2272
|
"USAGE_EVENTS"
|
|
1526
2273
|
]);
|
|
1527
2274
|
function validateBindingManifest(manifest) {
|
|
@@ -1702,6 +2449,7 @@ function asObjectArray(v) {
|
|
|
1702
2449
|
// src/cli/commands/deploy.ts
|
|
1703
2450
|
var DEPLOY_URL = process.env.DEEPSPACE_DEPLOY_URL ?? ENVS.prod.deploy;
|
|
1704
2451
|
var API_URL = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
2452
|
+
var SECRET_NAME_RE2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1705
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.";
|
|
1706
2454
|
var RESERVED_RUN_WORKER_FIRST = /* @__PURE__ */ new Set([
|
|
1707
2455
|
"/api/*",
|
|
@@ -1744,6 +2492,14 @@ function formatDeployWorkerError(status2, error) {
|
|
|
1744
2492
|
|
|
1745
2493
|
Underlying error: ${detail}`;
|
|
1746
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
|
+
}
|
|
1747
2503
|
var MAX_GROUP_BYTES = 40 * 1024 * 1024;
|
|
1748
2504
|
var GROUP_BYTES = (() => {
|
|
1749
2505
|
const raw = Number(process.env.DEEPSPACE_DEPLOY_GROUP_BYTES);
|
|
@@ -1797,7 +2553,7 @@ async function abortStagedUpload(appName, uploadId, totalGroups, token2) {
|
|
|
1797
2553
|
} catch {
|
|
1798
2554
|
}
|
|
1799
2555
|
}
|
|
1800
|
-
var deploy_default =
|
|
2556
|
+
var deploy_default = defineCommand9({
|
|
1801
2557
|
meta: {
|
|
1802
2558
|
name: "deploy",
|
|
1803
2559
|
description: "Build and deploy your DeepSpace app"
|
|
@@ -1861,18 +2617,18 @@ var deploy_default = defineCommand8({
|
|
|
1861
2617
|
process.exit(1);
|
|
1862
2618
|
}
|
|
1863
2619
|
s.stop("Built");
|
|
1864
|
-
const deployConfigPath =
|
|
1865
|
-
if (!
|
|
2620
|
+
const deployConfigPath = join10(appDir, ".wrangler", "deploy", "config.json");
|
|
2621
|
+
if (!existsSync10(deployConfigPath)) {
|
|
1866
2622
|
p4.cancel("Build output config not found at .wrangler/deploy/config.json");
|
|
1867
2623
|
process.exit(1);
|
|
1868
2624
|
}
|
|
1869
|
-
const deployConfig = JSON.parse(
|
|
2625
|
+
const deployConfig = JSON.parse(readFileSync9(deployConfigPath, "utf-8"));
|
|
1870
2626
|
const outputWranglerPath = resolve6(dirname2(deployConfigPath), deployConfig.configPath);
|
|
1871
|
-
if (!
|
|
2627
|
+
if (!existsSync10(outputWranglerPath)) {
|
|
1872
2628
|
p4.cancel(`Output wrangler.json not found at ${outputWranglerPath}`);
|
|
1873
2629
|
process.exit(1);
|
|
1874
2630
|
}
|
|
1875
|
-
const outputConfig = JSON.parse(
|
|
2631
|
+
const outputConfig = JSON.parse(readFileSync9(outputWranglerPath, "utf-8"));
|
|
1876
2632
|
if (outputConfig.name && outputConfig.name !== appName) {
|
|
1877
2633
|
const envHint = envName ? ` (--env ${envName})` : "";
|
|
1878
2634
|
p4.cancel(
|
|
@@ -1881,20 +2637,20 @@ var deploy_default = defineCommand8({
|
|
|
1881
2637
|
process.exit(1);
|
|
1882
2638
|
}
|
|
1883
2639
|
const workerDir = dirname2(outputWranglerPath);
|
|
1884
|
-
const workerBundlePath =
|
|
2640
|
+
const workerBundlePath = join10(workerDir, outputConfig.main);
|
|
1885
2641
|
const clientDir = outputConfig.assets?.directory ? resolve6(workerDir, outputConfig.assets.directory) : null;
|
|
1886
|
-
if (!
|
|
2642
|
+
if (!existsSync10(workerBundlePath)) {
|
|
1887
2643
|
p4.cancel(`Worker bundle not found at ${workerBundlePath}`);
|
|
1888
2644
|
process.exit(1);
|
|
1889
2645
|
}
|
|
1890
|
-
if (!clientDir || !
|
|
2646
|
+
if (!clientDir || !existsSync10(clientDir)) {
|
|
1891
2647
|
p4.cancel(`Client assets not found at ${clientDir}`);
|
|
1892
2648
|
process.exit(1);
|
|
1893
2649
|
}
|
|
1894
2650
|
s.start("Collecting assets...");
|
|
1895
2651
|
const assets = collectAssets(clientDir);
|
|
1896
2652
|
s.stop(`Collected ${assets.length} assets`);
|
|
1897
|
-
const workerJs =
|
|
2653
|
+
const workerJs = readFileSync9(workerBundlePath, "utf-8");
|
|
1898
2654
|
const doBindings = outputConfig.durable_objects?.bindings;
|
|
1899
2655
|
const sqliteClasses = new Set(
|
|
1900
2656
|
outputConfig.migrations?.flatMap((m) => m.new_sqlite_classes ?? []) ?? []
|
|
@@ -1921,40 +2677,20 @@ var deploy_default = defineCommand8({
|
|
|
1921
2677
|
`Custom bindings: ${customBindings.map((b) => `${b.name} (${b.type})`).join(", ")}`
|
|
1922
2678
|
);
|
|
1923
2679
|
}
|
|
1924
|
-
const devVarsPath = devVarsPathFor(appDir, envName);
|
|
1925
2680
|
let userSecrets = {};
|
|
1926
|
-
if (
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
p4.cancel(`.dev.vars: ${err.message}`);
|
|
1933
|
-
process.exit(1);
|
|
1934
|
-
}
|
|
1935
|
-
}
|
|
1936
|
-
}
|
|
1937
|
-
const userSecretNames = Object.keys(userSecrets);
|
|
1938
|
-
const declaredBindingNames = /* @__PURE__ */ new Set([
|
|
1939
|
-
...customBindings.map((b) => b.name),
|
|
1940
|
-
...(doManifest ?? []).map((d) => d.binding)
|
|
1941
|
-
]);
|
|
1942
|
-
for (const name of userSecretNames) {
|
|
1943
|
-
if (RESERVED_BINDING_NAMES.has(name)) {
|
|
1944
|
-
p4.cancel(
|
|
1945
|
-
`.dev.vars: "${name}" is a reserved binding name and cannot be used as an app secret.`
|
|
1946
|
-
);
|
|
1947
|
-
process.exit(1);
|
|
1948
|
-
}
|
|
1949
|
-
if (declaredBindingNames.has(name)) {
|
|
1950
|
-
p4.cancel(
|
|
1951
|
-
`.dev.vars: "${name}" collides with a binding declared in wrangler.toml \u2014 rename one or the other.`
|
|
1952
|
-
);
|
|
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);
|
|
1953
2687
|
process.exit(1);
|
|
1954
2688
|
}
|
|
1955
2689
|
}
|
|
1956
|
-
if (
|
|
1957
|
-
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);
|
|
1958
2694
|
}
|
|
1959
2695
|
s.start(`Deploying to ${appName}.app.space...`);
|
|
1960
2696
|
const extraRoutes = extractRunWorkerFirst(wranglerConfig);
|
|
@@ -2018,12 +2754,12 @@ var deploy_default = defineCommand8({
|
|
|
2018
2754
|
if (customBindings.length) {
|
|
2019
2755
|
form.append("bindingManifest", JSON.stringify(customBindings));
|
|
2020
2756
|
}
|
|
2021
|
-
if (userSecretNames.length) {
|
|
2022
|
-
form.append("userSecrets", JSON.stringify(userSecrets));
|
|
2023
|
-
}
|
|
2024
2757
|
if (extraRoutes.length) {
|
|
2025
2758
|
form.append("extraRunWorkerFirst", JSON.stringify(extraRoutes));
|
|
2026
2759
|
}
|
|
2760
|
+
if (Object.keys(userSecrets).length) {
|
|
2761
|
+
form.append("userSecrets", JSON.stringify(userSecrets));
|
|
2762
|
+
}
|
|
2027
2763
|
return form;
|
|
2028
2764
|
};
|
|
2029
2765
|
let res;
|
|
@@ -2060,6 +2796,7 @@ var deploy_default = defineCommand8({
|
|
|
2060
2796
|
s.stop("Deployed (edge propagation still in progress after 90s)");
|
|
2061
2797
|
p4.log.warn("First requests may briefly hit the assets transitional page; retry in a minute.");
|
|
2062
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.");
|
|
2063
2800
|
await syncSubscriptionPlans(appDir, appName, token2);
|
|
2064
2801
|
await syncOneTimeProducts(appDir, appName, token2);
|
|
2065
2802
|
p4.outro("Done");
|
|
@@ -2068,21 +2805,43 @@ var deploy_default = defineCommand8({
|
|
|
2068
2805
|
}
|
|
2069
2806
|
s.stop("Deployed!");
|
|
2070
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.");
|
|
2071
2809
|
await syncSubscriptionPlans(appDir, appName, token2);
|
|
2072
2810
|
await syncOneTimeProducts(appDir, appName, token2);
|
|
2073
2811
|
p4.outro("Done");
|
|
2074
2812
|
}
|
|
2075
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
|
+
}
|
|
2076
2835
|
async function syncSubscriptionPlans(appDir, appName, token2) {
|
|
2077
|
-
const subsPath =
|
|
2078
|
-
if (!
|
|
2836
|
+
const subsPath = join10(appDir, "src", "subscriptions.ts");
|
|
2837
|
+
if (!existsSync10(subsPath)) return;
|
|
2079
2838
|
const s = createSpinner();
|
|
2080
2839
|
s.start("Syncing subscription plans...");
|
|
2081
2840
|
let plans;
|
|
2082
2841
|
try {
|
|
2083
2842
|
const esbuild = await import("esbuild");
|
|
2084
|
-
const outDir =
|
|
2085
|
-
const outFile =
|
|
2843
|
+
const outDir = join10(appDir, ".wrangler", "deploy");
|
|
2844
|
+
const outFile = join10(outDir, "subscriptions.bundle.mjs");
|
|
2086
2845
|
await esbuild.build({
|
|
2087
2846
|
entryPoints: [subsPath],
|
|
2088
2847
|
bundle: true,
|
|
@@ -2223,15 +2982,15 @@ current billing period? (Skip to leave them grandfathered.)`
|
|
|
2223
2982
|
}
|
|
2224
2983
|
}
|
|
2225
2984
|
async function syncOneTimeProducts(appDir, appName, token2) {
|
|
2226
|
-
const productsPath =
|
|
2227
|
-
if (!
|
|
2985
|
+
const productsPath = join10(appDir, "src", "products.ts");
|
|
2986
|
+
if (!existsSync10(productsPath)) return;
|
|
2228
2987
|
const s = createSpinner();
|
|
2229
2988
|
s.start("Syncing one-time products...");
|
|
2230
2989
|
let products;
|
|
2231
2990
|
try {
|
|
2232
2991
|
const esbuild = await import("esbuild");
|
|
2233
|
-
const outDir =
|
|
2234
|
-
const outFile =
|
|
2992
|
+
const outDir = join10(appDir, ".wrangler", "deploy");
|
|
2993
|
+
const outFile = join10(outDir, "products.bundle.mjs");
|
|
2235
2994
|
await esbuild.build({
|
|
2236
2995
|
entryPoints: [productsPath],
|
|
2237
2996
|
bundle: true,
|
|
@@ -2305,14 +3064,14 @@ function collectAssets(dir) {
|
|
|
2305
3064
|
function walk(d, prefix) {
|
|
2306
3065
|
for (const entry of readdirSync2(d, { withFileTypes: true })) {
|
|
2307
3066
|
if (entry.name === ".assetsignore") continue;
|
|
2308
|
-
const full =
|
|
3067
|
+
const full = join10(d, entry.name);
|
|
2309
3068
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2310
3069
|
if (entry.isDirectory()) {
|
|
2311
3070
|
walk(full, rel);
|
|
2312
3071
|
} else {
|
|
2313
3072
|
assets.push({
|
|
2314
3073
|
path: "/" + rel,
|
|
2315
|
-
contentBase64:
|
|
3074
|
+
contentBase64: readFileSync9(full).toString("base64")
|
|
2316
3075
|
});
|
|
2317
3076
|
}
|
|
2318
3077
|
}
|
|
@@ -2322,10 +3081,10 @@ function collectAssets(dir) {
|
|
|
2322
3081
|
}
|
|
2323
3082
|
|
|
2324
3083
|
// src/cli/commands/undeploy.ts
|
|
2325
|
-
import { defineCommand as
|
|
3084
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
2326
3085
|
import * as p5 from "@clack/prompts";
|
|
2327
3086
|
var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? ENVS.prod.deploy;
|
|
2328
|
-
var undeploy_default =
|
|
3087
|
+
var undeploy_default = defineCommand10({
|
|
2329
3088
|
meta: {
|
|
2330
3089
|
name: "undeploy",
|
|
2331
3090
|
description: "Remove a deployed DeepSpace app"
|
|
@@ -2386,8 +3145,8 @@ var undeploy_default = defineCommand9({
|
|
|
2386
3145
|
});
|
|
2387
3146
|
|
|
2388
3147
|
// src/cli/commands/whoami.ts
|
|
2389
|
-
import { defineCommand as
|
|
2390
|
-
var whoami_default =
|
|
3148
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
3149
|
+
var whoami_default = defineCommand11({
|
|
2391
3150
|
meta: {
|
|
2392
3151
|
name: "whoami",
|
|
2393
3152
|
description: "Show the currently logged-in user"
|
|
@@ -2442,18 +3201,18 @@ var whoami_default = defineCommand10({
|
|
|
2442
3201
|
});
|
|
2443
3202
|
|
|
2444
3203
|
// src/cli/commands/add.ts
|
|
2445
|
-
import { defineCommand as
|
|
2446
|
-
import { existsSync as
|
|
2447
|
-
import { resolve as resolve7, join as
|
|
3204
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
3205
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3206
|
+
import { resolve as resolve7, join as join11 } from "path";
|
|
2448
3207
|
import { execSync as execSync3 } from "child_process";
|
|
2449
3208
|
function findScript(from) {
|
|
2450
|
-
const inNodeModules =
|
|
2451
|
-
if (
|
|
2452
|
-
const inDotDeepspace =
|
|
2453
|
-
if (
|
|
3209
|
+
const inNodeModules = join11(from, "node_modules", "deepspace", "scripts", "add-feature.cjs");
|
|
3210
|
+
if (existsSync11(inNodeModules)) return inNodeModules;
|
|
3211
|
+
const inDotDeepspace = join11(from, ".deepspace", "scripts", "add-feature.cjs");
|
|
3212
|
+
if (existsSync11(inDotDeepspace)) return inDotDeepspace;
|
|
2454
3213
|
return null;
|
|
2455
3214
|
}
|
|
2456
|
-
var add_default =
|
|
3215
|
+
var add_default = defineCommand12({
|
|
2457
3216
|
meta: {
|
|
2458
3217
|
name: "add",
|
|
2459
3218
|
description: "Add a feature to your DeepSpace app"
|
|
@@ -2488,8 +3247,8 @@ var add_default = defineCommand11({
|
|
|
2488
3247
|
if (!script) {
|
|
2489
3248
|
console.error("Could not find the deepspace feature installer.");
|
|
2490
3249
|
console.error("Looked in:");
|
|
2491
|
-
console.error(` ${
|
|
2492
|
-
console.error(` ${
|
|
3250
|
+
console.error(` ${join11(appDir, "node_modules", "deepspace", "scripts", "add-feature.cjs")}`);
|
|
3251
|
+
console.error(` ${join11(appDir, ".deepspace", "scripts", "add-feature.cjs")}`);
|
|
2493
3252
|
console.error("Are you in a DeepSpace app directory? Try `npm install` first.");
|
|
2494
3253
|
process.exit(1);
|
|
2495
3254
|
}
|
|
@@ -2511,10 +3270,10 @@ var add_default = defineCommand11({
|
|
|
2511
3270
|
});
|
|
2512
3271
|
|
|
2513
3272
|
// src/cli/commands/skills.ts
|
|
2514
|
-
import { defineCommand as
|
|
3273
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
2515
3274
|
import spawn2 from "cross-spawn";
|
|
2516
3275
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
2517
|
-
import { join as
|
|
3276
|
+
import { join as join12 } from "path";
|
|
2518
3277
|
import { homedir as homedir5 } from "os";
|
|
2519
3278
|
var SKILL_REPO = "deepdotspace/deepspace-skill";
|
|
2520
3279
|
function runSkillsAdd(extraArgs) {
|
|
@@ -2533,9 +3292,9 @@ function runSkillsAdd(extraArgs) {
|
|
|
2533
3292
|
});
|
|
2534
3293
|
}
|
|
2535
3294
|
function precreateClaudeDir(baseDir) {
|
|
2536
|
-
mkdirSync5(
|
|
3295
|
+
mkdirSync5(join12(baseDir, ".claude"), { recursive: true });
|
|
2537
3296
|
}
|
|
2538
|
-
var add =
|
|
3297
|
+
var add = defineCommand13({
|
|
2539
3298
|
meta: {
|
|
2540
3299
|
name: "add",
|
|
2541
3300
|
description: "Install the DeepSpace skill for your coding agent"
|
|
@@ -2577,7 +3336,7 @@ var add = defineCommand12({
|
|
|
2577
3336
|
if (code !== 0) process.exit(code);
|
|
2578
3337
|
}
|
|
2579
3338
|
});
|
|
2580
|
-
var skills_default =
|
|
3339
|
+
var skills_default = defineCommand13({
|
|
2581
3340
|
meta: {
|
|
2582
3341
|
name: "skills",
|
|
2583
3342
|
description: "Manage the DeepSpace agent skill (Claude Code, Codex, OpenCode, Cursor, ...)"
|
|
@@ -2588,7 +3347,7 @@ var skills_default = defineCommand12({
|
|
|
2588
3347
|
});
|
|
2589
3348
|
|
|
2590
3349
|
// src/cli/commands/domain.ts
|
|
2591
|
-
import { defineCommand as
|
|
3350
|
+
import { defineCommand as defineCommand14 } from "citty";
|
|
2592
3351
|
import { execSync as execSync4 } from "child_process";
|
|
2593
3352
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
2594
3353
|
async function api(token2, path, init) {
|
|
@@ -2600,16 +3359,16 @@ async function api(token2, path, init) {
|
|
|
2600
3359
|
...init?.headers ?? {}
|
|
2601
3360
|
}
|
|
2602
3361
|
});
|
|
2603
|
-
const
|
|
3362
|
+
const text2 = await res.text();
|
|
2604
3363
|
if (!res.ok) {
|
|
2605
|
-
let msg =
|
|
3364
|
+
let msg = text2;
|
|
2606
3365
|
try {
|
|
2607
|
-
msg = JSON.parse(
|
|
3366
|
+
msg = JSON.parse(text2).error ?? text2;
|
|
2608
3367
|
} catch {
|
|
2609
3368
|
}
|
|
2610
3369
|
throw new Error(`API ${path} (${res.status}): ${msg}`);
|
|
2611
3370
|
}
|
|
2612
|
-
return JSON.parse(
|
|
3371
|
+
return JSON.parse(text2);
|
|
2613
3372
|
}
|
|
2614
3373
|
function scrubInternal(value) {
|
|
2615
3374
|
if (Array.isArray(value)) return value.map(scrubInternal);
|
|
@@ -2644,7 +3403,7 @@ async function confirm3(message) {
|
|
|
2644
3403
|
});
|
|
2645
3404
|
});
|
|
2646
3405
|
}
|
|
2647
|
-
var search =
|
|
3406
|
+
var search = defineCommand14({
|
|
2648
3407
|
meta: { name: "search", description: "Search for available domains" },
|
|
2649
3408
|
args: {
|
|
2650
3409
|
query: { type: "positional", description: "Domain or keyword", required: true },
|
|
@@ -2674,7 +3433,7 @@ var search = defineCommand13({
|
|
|
2674
3433
|
}
|
|
2675
3434
|
}
|
|
2676
3435
|
});
|
|
2677
|
-
var buy =
|
|
3436
|
+
var buy = defineCommand14({
|
|
2678
3437
|
meta: { name: "buy", description: "Buy a domain and attach it to an app" },
|
|
2679
3438
|
args: {
|
|
2680
3439
|
domain: { type: "positional", description: "Domain to buy", required: true },
|
|
@@ -2763,7 +3522,7 @@ var buy = defineCommand13({
|
|
|
2763
3522
|
process.exit(1);
|
|
2764
3523
|
}
|
|
2765
3524
|
});
|
|
2766
|
-
var
|
|
3525
|
+
var list3 = defineCommand14({
|
|
2767
3526
|
meta: { name: "list", description: "List your domains" },
|
|
2768
3527
|
args: {
|
|
2769
3528
|
json: { type: "boolean", description: "Emit JSON" }
|
|
@@ -2787,7 +3546,7 @@ var list2 = defineCommand13({
|
|
|
2787
3546
|
}
|
|
2788
3547
|
}
|
|
2789
3548
|
});
|
|
2790
|
-
var status =
|
|
3549
|
+
var status = defineCommand14({
|
|
2791
3550
|
meta: { name: "status", description: "Show details for one domain" },
|
|
2792
3551
|
args: {
|
|
2793
3552
|
domain: { type: "positional", description: "Domain", required: true },
|
|
@@ -2815,7 +3574,7 @@ var status = defineCommand13({
|
|
|
2815
3574
|
console.log(`Charged: ${fmtCents(found.chargedCents)}/yr`);
|
|
2816
3575
|
}
|
|
2817
3576
|
});
|
|
2818
|
-
var attach =
|
|
3577
|
+
var attach = defineCommand14({
|
|
2819
3578
|
meta: { name: "attach", description: "Re-point a domain at a different app" },
|
|
2820
3579
|
args: {
|
|
2821
3580
|
domain: { type: "positional", description: "Domain to re-point", required: true },
|
|
@@ -2846,7 +3605,7 @@ var attach = defineCommand13({
|
|
|
2846
3605
|
console.log(`\u2713 ${args.domain} now points at ${targetApp}`);
|
|
2847
3606
|
}
|
|
2848
3607
|
});
|
|
2849
|
-
var detach =
|
|
3608
|
+
var detach = defineCommand14({
|
|
2850
3609
|
meta: {
|
|
2851
3610
|
name: "detach",
|
|
2852
3611
|
description: "Stop routing the domain (keeps the registration; auto-renew unchanged)"
|
|
@@ -2876,7 +3635,7 @@ var detach = defineCommand13({
|
|
|
2876
3635
|
console.log(`\u2713 ${args.domain} detached. Use \`deepspace domain attach\` to re-route, or \`renew --auto off\` to stop auto-renewal.`);
|
|
2877
3636
|
}
|
|
2878
3637
|
});
|
|
2879
|
-
var renew =
|
|
3638
|
+
var renew = defineCommand14({
|
|
2880
3639
|
meta: { name: "renew", description: "Toggle auto-renewal at the registrar" },
|
|
2881
3640
|
args: {
|
|
2882
3641
|
domain: { type: "positional", description: "Domain", required: true },
|
|
@@ -2910,16 +3669,16 @@ function openBrowser2(url) {
|
|
|
2910
3669
|
} catch {
|
|
2911
3670
|
}
|
|
2912
3671
|
}
|
|
2913
|
-
var domain_default =
|
|
3672
|
+
var domain_default = defineCommand14({
|
|
2914
3673
|
meta: { name: "domain", description: "Buy and manage custom domains" },
|
|
2915
|
-
subCommands: { search, buy, list:
|
|
3674
|
+
subCommands: { search, buy, list: list3, status, attach, detach, renew }
|
|
2916
3675
|
});
|
|
2917
3676
|
|
|
2918
3677
|
// src/cli/commands/integrations.ts
|
|
2919
|
-
import { defineCommand as
|
|
3678
|
+
import { defineCommand as defineCommand15 } from "citty";
|
|
2920
3679
|
|
|
2921
3680
|
// src/cli/commands/_invoke-impl.ts
|
|
2922
|
-
import { readFileSync as
|
|
3681
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
2923
3682
|
var API_URL3 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
2924
3683
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
2925
3684
|
function parseTarget(target) {
|
|
@@ -2940,7 +3699,7 @@ function resolveBody(opts) {
|
|
|
2940
3699
|
if (opts.body != null) {
|
|
2941
3700
|
raw = opts.body;
|
|
2942
3701
|
} else if (opts.bodyFile != null) {
|
|
2943
|
-
raw = opts.bodyFile === "-" ?
|
|
3702
|
+
raw = opts.bodyFile === "-" ? readFileSync10(0, "utf-8") : readFileSync10(opts.bodyFile, "utf-8");
|
|
2944
3703
|
} else {
|
|
2945
3704
|
return "{}";
|
|
2946
3705
|
}
|
|
@@ -3091,7 +3850,7 @@ async function runInvoke(args) {
|
|
|
3091
3850
|
}
|
|
3092
3851
|
|
|
3093
3852
|
// src/cli/commands/integrations.ts
|
|
3094
|
-
var
|
|
3853
|
+
var list4 = defineCommand15({
|
|
3095
3854
|
meta: {
|
|
3096
3855
|
name: "list",
|
|
3097
3856
|
description: "List all available integrations"
|
|
@@ -3112,7 +3871,7 @@ var list3 = defineCommand14({
|
|
|
3112
3871
|
}
|
|
3113
3872
|
}
|
|
3114
3873
|
});
|
|
3115
|
-
var info =
|
|
3874
|
+
var info = defineCommand15({
|
|
3116
3875
|
meta: {
|
|
3117
3876
|
name: "info",
|
|
3118
3877
|
description: "Print schema + example body for an integration endpoint"
|
|
@@ -3138,7 +3897,7 @@ var info = defineCommand14({
|
|
|
3138
3897
|
}
|
|
3139
3898
|
}
|
|
3140
3899
|
});
|
|
3141
|
-
var invoke =
|
|
3900
|
+
var invoke = defineCommand15({
|
|
3142
3901
|
meta: {
|
|
3143
3902
|
name: "invoke",
|
|
3144
3903
|
description: "Invoke a platform integration as the logged-in user"
|
|
@@ -3191,21 +3950,21 @@ var invoke = defineCommand14({
|
|
|
3191
3950
|
}
|
|
3192
3951
|
}
|
|
3193
3952
|
});
|
|
3194
|
-
var integrations_default =
|
|
3953
|
+
var integrations_default = defineCommand15({
|
|
3195
3954
|
meta: {
|
|
3196
3955
|
name: "integrations",
|
|
3197
3956
|
description: "List, inspect, and invoke platform integrations"
|
|
3198
3957
|
},
|
|
3199
3958
|
subCommands: {
|
|
3200
|
-
list:
|
|
3959
|
+
list: list4,
|
|
3201
3960
|
info,
|
|
3202
3961
|
invoke
|
|
3203
3962
|
}
|
|
3204
3963
|
});
|
|
3205
3964
|
|
|
3206
3965
|
// src/cli/commands/invoke.ts
|
|
3207
|
-
import { defineCommand as
|
|
3208
|
-
var invoke_default =
|
|
3966
|
+
import { defineCommand as defineCommand16 } from "citty";
|
|
3967
|
+
var invoke_default = defineCommand16({
|
|
3209
3968
|
meta: {
|
|
3210
3969
|
name: "invoke",
|
|
3211
3970
|
description: "Invoke a platform integration as the logged-in user"
|
|
@@ -3278,7 +4037,7 @@ var invoke_default = defineCommand15({
|
|
|
3278
4037
|
});
|
|
3279
4038
|
|
|
3280
4039
|
// src/cli/commands/managed-repos.ts
|
|
3281
|
-
import { defineCommand as
|
|
4040
|
+
import { defineCommand as defineCommand17 } from "citty";
|
|
3282
4041
|
var API_URL4 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
3283
4042
|
var SIMPLE_APP_ID = /^[a-z0-9][a-z0-9_.:-]{0,119}$/i;
|
|
3284
4043
|
var SIMPLE_APP_NAME = /^[a-z0-9][a-z0-9_-]{0,119}$/i;
|
|
@@ -3291,16 +4050,16 @@ async function api2(token2, path, init) {
|
|
|
3291
4050
|
...init?.headers ?? {}
|
|
3292
4051
|
}
|
|
3293
4052
|
});
|
|
3294
|
-
const
|
|
4053
|
+
const text2 = await res.text();
|
|
3295
4054
|
if (!res.ok) {
|
|
3296
|
-
let msg =
|
|
4055
|
+
let msg = text2;
|
|
3297
4056
|
try {
|
|
3298
|
-
msg = JSON.parse(
|
|
4057
|
+
msg = JSON.parse(text2).error ?? text2;
|
|
3299
4058
|
} catch {
|
|
3300
4059
|
}
|
|
3301
4060
|
throw new Error(`API ${path} (${res.status}): ${msg}`);
|
|
3302
4061
|
}
|
|
3303
|
-
return JSON.parse(
|
|
4062
|
+
return JSON.parse(text2);
|
|
3304
4063
|
}
|
|
3305
4064
|
function defaultAppId(appName) {
|
|
3306
4065
|
return `cli-${slugId(appName)}`;
|
|
@@ -3337,7 +4096,7 @@ async function confirm4(message) {
|
|
|
3337
4096
|
});
|
|
3338
4097
|
});
|
|
3339
4098
|
}
|
|
3340
|
-
var
|
|
4099
|
+
var list5 = defineCommand17({
|
|
3341
4100
|
meta: {
|
|
3342
4101
|
name: "list",
|
|
3343
4102
|
description: "List your DeepSpace-managed repos and quota"
|
|
@@ -3367,7 +4126,7 @@ var list4 = defineCommand16({
|
|
|
3367
4126
|
}
|
|
3368
4127
|
}
|
|
3369
4128
|
});
|
|
3370
|
-
var create2 =
|
|
4129
|
+
var create2 = defineCommand17({
|
|
3371
4130
|
meta: {
|
|
3372
4131
|
name: "create",
|
|
3373
4132
|
description: "Create a private DeepSpace-managed GitHub repo"
|
|
@@ -3419,7 +4178,7 @@ var create2 = defineCommand16({
|
|
|
3419
4178
|
printRepo(result.repo);
|
|
3420
4179
|
}
|
|
3421
4180
|
});
|
|
3422
|
-
var token =
|
|
4181
|
+
var token = defineCommand17({
|
|
3423
4182
|
meta: {
|
|
3424
4183
|
name: "token",
|
|
3425
4184
|
description: "Mint a short-lived GitHub token for a DeepSpace-managed repo"
|
|
@@ -3454,7 +4213,7 @@ var token = defineCommand16({
|
|
|
3454
4213
|
console.log(`Clone: git clone https://x-access-token:${encodeURIComponent(result.token.token)}@github.com/${result.repo.fullName}.git`);
|
|
3455
4214
|
}
|
|
3456
4215
|
});
|
|
3457
|
-
var remove =
|
|
4216
|
+
var remove = defineCommand17({
|
|
3458
4217
|
meta: {
|
|
3459
4218
|
name: "delete",
|
|
3460
4219
|
description: "Delete a DeepSpace-managed GitHub repo"
|
|
@@ -3507,13 +4266,13 @@ var remove = defineCommand16({
|
|
|
3507
4266
|
);
|
|
3508
4267
|
}
|
|
3509
4268
|
});
|
|
3510
|
-
var managed_repos_default =
|
|
4269
|
+
var managed_repos_default = defineCommand17({
|
|
3511
4270
|
meta: {
|
|
3512
4271
|
name: "managed-repos",
|
|
3513
4272
|
description: "Create and manage DeepSpace-owned GitHub repos"
|
|
3514
4273
|
},
|
|
3515
4274
|
subCommands: {
|
|
3516
|
-
list:
|
|
4275
|
+
list: list5,
|
|
3517
4276
|
create: create2,
|
|
3518
4277
|
token,
|
|
3519
4278
|
delete: remove
|
|
@@ -3521,13 +4280,13 @@ var managed_repos_default = defineCommand16({
|
|
|
3521
4280
|
});
|
|
3522
4281
|
|
|
3523
4282
|
// src/cli/commands/library.ts
|
|
3524
|
-
import { defineCommand as
|
|
3525
|
-
import { existsSync as
|
|
3526
|
-
import { join 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";
|
|
3527
4286
|
import { parse as parseToml3 } from "smol-toml";
|
|
3528
4287
|
import * as p6 from "@clack/prompts";
|
|
3529
4288
|
var DEFAULT_LIBRARY_APP = "deepdotspace-site";
|
|
3530
|
-
var publish =
|
|
4289
|
+
var publish = defineCommand18({
|
|
3531
4290
|
meta: {
|
|
3532
4291
|
name: "publish",
|
|
3533
4292
|
description: "Publish the current app to the DeepSpace library"
|
|
@@ -3586,7 +4345,7 @@ var publish = defineCommand17({
|
|
|
3586
4345
|
},
|
|
3587
4346
|
async run({ args }) {
|
|
3588
4347
|
const json = !!args.json;
|
|
3589
|
-
const
|
|
4348
|
+
const intro6 = (s) => {
|
|
3590
4349
|
if (!json) p6.intro(s);
|
|
3591
4350
|
};
|
|
3592
4351
|
const info2 = (s) => {
|
|
@@ -3597,15 +4356,15 @@ var publish = defineCommand17({
|
|
|
3597
4356
|
else p6.cancel(msg);
|
|
3598
4357
|
process.exit(1);
|
|
3599
4358
|
};
|
|
3600
|
-
|
|
4359
|
+
intro6("Publishing to the DeepSpace library");
|
|
3601
4360
|
let appName = typeof args.app === "string" && args.app.trim() ? args.app.trim() : void 0;
|
|
3602
4361
|
if (!appName) {
|
|
3603
4362
|
const appDir = resolve8(typeof args.dir === "string" && args.dir ? args.dir : ".");
|
|
3604
|
-
const wranglerPath =
|
|
3605
|
-
if (!
|
|
4363
|
+
const wranglerPath = join13(appDir, "wrangler.toml");
|
|
4364
|
+
if (!existsSync12(wranglerPath)) {
|
|
3606
4365
|
return die("No --app provided and no wrangler.toml found. Either pass --app <name> or run from your app's directory.");
|
|
3607
4366
|
}
|
|
3608
|
-
const cfg = parseToml3(
|
|
4367
|
+
const cfg = parseToml3(readFileSync11(wranglerPath, "utf-8"));
|
|
3609
4368
|
const nameRes = resolveAppName(cfg.name);
|
|
3610
4369
|
if (!nameRes.ok) return die(`wrangler.toml: ${nameRes.reason}`);
|
|
3611
4370
|
appName = nameRes.name;
|
|
@@ -3666,7 +4425,7 @@ var publish = defineCommand17({
|
|
|
3666
4425
|
p6.outro("Done");
|
|
3667
4426
|
}
|
|
3668
4427
|
});
|
|
3669
|
-
var unpublish =
|
|
4428
|
+
var unpublish = defineCommand18({
|
|
3670
4429
|
meta: {
|
|
3671
4430
|
name: "unpublish",
|
|
3672
4431
|
description: "Remove a library entry (owner or admin only)"
|
|
@@ -3725,7 +4484,7 @@ var unpublish = defineCommand17({
|
|
|
3725
4484
|
p6.outro("Done");
|
|
3726
4485
|
}
|
|
3727
4486
|
});
|
|
3728
|
-
var library_default =
|
|
4487
|
+
var library_default = defineCommand18({
|
|
3729
4488
|
meta: {
|
|
3730
4489
|
name: "library",
|
|
3731
4490
|
description: "Manage your DeepSpace library entries"
|
|
@@ -3737,12 +4496,12 @@ var library_default = defineCommand17({
|
|
|
3737
4496
|
});
|
|
3738
4497
|
|
|
3739
4498
|
// src/cli/commands/admin.ts
|
|
3740
|
-
import { defineCommand as
|
|
3741
|
-
import { mkdirSync as mkdirSync6, writeFileSync as
|
|
4499
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
4500
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
3742
4501
|
import { homedir as homedir6 } from "os";
|
|
3743
|
-
import { join as
|
|
4502
|
+
import { join as join14 } from "path";
|
|
3744
4503
|
var AUTH_URL5 = process.env.DEEPSPACE_AUTH_URL ?? ENVS.prod.auth;
|
|
3745
|
-
var impersonate =
|
|
4504
|
+
var impersonate = defineCommand19({
|
|
3746
4505
|
meta: {
|
|
3747
4506
|
name: "impersonate",
|
|
3748
4507
|
description: "Store a short-lived CLI token for another user"
|
|
@@ -3790,15 +4549,15 @@ var impersonate = defineCommand18({
|
|
|
3790
4549
|
console.error(`Failed: ${data.error ?? `HTTP ${res.status}`}`);
|
|
3791
4550
|
process.exit(1);
|
|
3792
4551
|
}
|
|
3793
|
-
const dir =
|
|
4552
|
+
const dir = join14(homedir6(), ".deepspace");
|
|
3794
4553
|
mkdirSync6(dir, { recursive: true });
|
|
3795
|
-
|
|
4554
|
+
writeFileSync7(TOKEN_PATH2, data.token, { mode: 384 });
|
|
3796
4555
|
console.log(`Impersonating ${data.user.name ?? data.user.email ?? data.user.id}`);
|
|
3797
4556
|
console.log(`Email: ${data.user.email ?? "(none)"}`);
|
|
3798
4557
|
console.log(`UserID: ${data.user.id}`);
|
|
3799
4558
|
}
|
|
3800
4559
|
});
|
|
3801
|
-
var admin_default =
|
|
4560
|
+
var admin_default = defineCommand19({
|
|
3802
4561
|
meta: {
|
|
3803
4562
|
name: "admin",
|
|
3804
4563
|
description: "Maintainer-only admin commands"
|
|
@@ -3808,13 +4567,276 @@ var admin_default = defineCommand18({
|
|
|
3808
4567
|
}
|
|
3809
4568
|
});
|
|
3810
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
|
+
|
|
3811
4833
|
// src/cli/cli.ts
|
|
3812
|
-
var __dirname =
|
|
3813
|
-
var pkg = JSON.parse(
|
|
3814
|
-
if (process.platform === "linux" && !process.env.SSL_CERT_FILE &&
|
|
4834
|
+
var __dirname = dirname4(fileURLToPath2(import.meta.url));
|
|
4835
|
+
var pkg = JSON.parse(readFileSync13(join16(__dirname, "..", "package.json"), "utf-8"));
|
|
4836
|
+
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync14("/etc/ssl/certs/ca-certificates.crt")) {
|
|
3815
4837
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
3816
4838
|
}
|
|
3817
|
-
var create3 =
|
|
4839
|
+
var create3 = defineCommand21({
|
|
3818
4840
|
meta: {
|
|
3819
4841
|
name: "create",
|
|
3820
4842
|
description: "Create a new DeepSpace app"
|
|
@@ -3833,7 +4855,7 @@ var create3 = defineCommand19({
|
|
|
3833
4855
|
execSync5(`npm create deepspace@latest${name}`, { stdio: "inherit" });
|
|
3834
4856
|
}
|
|
3835
4857
|
});
|
|
3836
|
-
var main =
|
|
4858
|
+
var main = defineCommand21({
|
|
3837
4859
|
meta: {
|
|
3838
4860
|
name: "deepspace",
|
|
3839
4861
|
version: pkg.version,
|
|
@@ -3858,7 +4880,9 @@ var main = defineCommand19({
|
|
|
3858
4880
|
undeploy: undeploy_default,
|
|
3859
4881
|
domain: domain_default,
|
|
3860
4882
|
library: library_default,
|
|
3861
|
-
admin: admin_default
|
|
4883
|
+
admin: admin_default,
|
|
4884
|
+
feedback: feedback_default,
|
|
4885
|
+
secrets: secrets_default
|
|
3862
4886
|
}
|
|
3863
4887
|
// No `run()` here — citty cascades parent run() AFTER subcommand finishes,
|
|
3864
4888
|
// which corrupts agent-friendly output (`--json`) and prints noise on every
|