deepspace 0.10.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/chunk-ZJ43EK3N.js +1751 -0
- package/dist/chunk-ZJ43EK3N.js.map +1 -0
- package/dist/cli.js +848 -650
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.d.ts +18 -0
- package/dist/documentation-client-core.js +395 -0
- package/dist/documentation-client-core.js.map +1 -0
- package/dist/documentation-default-renderer.cjs +124 -0
- package/dist/documentation-react.d.ts +2 -0
- package/dist/documentation-react.js +35 -0
- package/dist/documentation-react.js.map +1 -0
- package/dist/documentation-runtime.js +71 -0
- package/dist/documentation-server-core.d.ts +11 -0
- package/dist/documentation-server-core.js +48 -0
- package/dist/documentation-server-core.js.map +1 -0
- package/dist/documentation.d.ts +367 -0
- package/dist/documentation.js +4122 -0
- package/dist/documentation.js.map +1 -0
- package/dist/feature-installer.js +145 -0
- package/dist/feature-installer.js.map +1 -1
- package/dist/fonts/geist-mono-OFL.txt +93 -0
- package/dist/fonts/geist-mono-variable.woff2 +0 -0
- package/dist/fonts/inter-OFL.txt +92 -0
- package/dist/fonts/inter-variable.woff2 +0 -0
- package/dist/index.d.ts +177 -1
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -1
- package/dist/public-BGRX_Cjd.d.ts +234 -0
- package/dist/schema.d.ts +186 -0
- package/dist/schema.js +610 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts +83 -4
- package/dist/server.js +296 -1
- package/dist/server.js.map +1 -1
- package/dist/testing.d.ts +5 -8
- package/dist/testing.js +48 -14
- package/dist/testing.js.map +1 -1
- package/dist/worker.d.ts +249 -5
- package/dist/worker.js +1438 -19
- package/dist/worker.js.map +1 -1
- package/features/admin-page/src/admin-schema.ts +1 -1
- package/features/ai-chat/feature.json +2 -2
- package/features/ai-chat/src/ChatPanel.tsx +6 -11
- package/features/ai-chat/src/ai-chat-schema.ts +1 -2
- package/features/canvas/src/canvas-schema.ts +1 -1
- package/features/documentation/content/index.mdx +14 -0
- package/features/documentation/content/quickstart.mdx +12 -0
- package/features/documentation/documentation.json +18 -0
- package/features/documentation/feature.json +68 -0
- package/features/{docs → documents}/feature.json +27 -27
- package/features/{docs/src/DocsEditorPage.tsx → documents/src/DocumentsEditorPage.tsx} +74 -74
- package/features/{docs/src/DocsListPage.tsx → documents/src/DocumentsListPage.tsx} +102 -102
- package/features/{docs/src/DocsOutlinePanel.tsx → documents/src/DocumentsOutlinePanel.tsx} +9 -9
- package/features/{docs/src/DocsPresence.tsx → documents/src/DocumentsPresence.tsx} +18 -18
- package/features/{docs → documents}/src/InviteDialog.tsx +21 -21
- package/features/{docs → documents}/src/LibrarySidebar.tsx +25 -25
- package/features/documents/src/__tests__/documents-presence-access.test.ts +71 -0
- package/features/{docs/src/docs-library-types.ts → documents/src/documents-library-types.ts} +3 -3
- package/features/{docs/src/docs-schema.ts → documents/src/documents-schema.ts} +6 -6
- package/features/{docs/src/docs-ui.css → documents/src/documents-ui.css} +89 -89
- package/features/{docs → documents}/src/editor/DocEditorSurface.tsx +11 -11
- package/features/{docs/src/editor/DocsTiptapToolbar.tsx → documents/src/editor/DocumentsTiptapToolbar.tsx} +21 -21
- package/features/{docs → documents}/src/editor/useDocEditor.ts +4 -4
- package/features/{docs/src/use-docs-presence-access.ts → documents/src/use-documents-presence-access.ts} +51 -51
- package/features/file-attachments/src/attachments-schema.ts +1 -1
- package/features/items/src/items-schema.ts +1 -1
- package/features/leaderboard/src/leaderboard-schema.ts +1 -1
- package/features/messaging/src/ChatMultiPage.tsx +1 -1
- package/features/messaging/src/ChatPage.tsx +1 -1
- package/features/messaging/src/messaging-schema.ts +2 -2
- package/features/tasks/src/tasks-schema.ts +1 -1
- package/features/testing/src/test-schema.ts +2 -2
- package/package.json +26 -1
- package/features/docs/src/__tests__/docs-presence-access.test.ts +0 -71
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
|
-
import { defineCommand as
|
|
4
|
+
import { defineCommand as defineCommand13, runMain, runCommand } from "citty";
|
|
5
5
|
import { sync as spawnSync8 } from "cross-spawn";
|
|
6
|
-
import { readFileSync as
|
|
6
|
+
import { readFileSync as readFileSync27, existsSync as existsSync25 } from "fs";
|
|
7
7
|
import { dirname as dirname10, join as join24 } from "path";
|
|
8
8
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9
9
|
|
|
@@ -709,7 +709,7 @@ async function provisionProfile(jwt) {
|
|
|
709
709
|
}
|
|
710
710
|
}
|
|
711
711
|
function sleep(ms) {
|
|
712
|
-
return new Promise((
|
|
712
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
713
713
|
}
|
|
714
714
|
function base64url(bytes) {
|
|
715
715
|
return bytes.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
@@ -1267,7 +1267,7 @@ function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
|
1267
1267
|
let port = opts.port;
|
|
1268
1268
|
if (opts.probePort) {
|
|
1269
1269
|
const taken = new Set(
|
|
1270
|
-
config.configurations.map((c) => Number(c?.port)).filter((
|
|
1270
|
+
config.configurations.map((c) => Number(c?.port)).filter((p34) => Number.isInteger(p34) && p34 > 0)
|
|
1271
1271
|
);
|
|
1272
1272
|
for (let i = 0; i < WORKTREE_PORT_RANGE && taken.has(port); i++) {
|
|
1273
1273
|
port = WORKTREE_PORT_MIN + (port - WORKTREE_PORT_MIN + 1) % WORKTREE_PORT_RANGE;
|
|
@@ -1372,7 +1372,8 @@ var ALLOWED_BINDING_TYPES = /* @__PURE__ */ new Set([
|
|
|
1372
1372
|
"queue",
|
|
1373
1373
|
"browser_rendering",
|
|
1374
1374
|
"analytics_engine",
|
|
1375
|
-
"hyperdrive"
|
|
1375
|
+
"hyperdrive",
|
|
1376
|
+
"ratelimit"
|
|
1376
1377
|
]);
|
|
1377
1378
|
var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
1378
1379
|
"ASSETS",
|
|
@@ -1380,6 +1381,7 @@ var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
|
1380
1381
|
"API_WORKER",
|
|
1381
1382
|
"DEEPSPACE_APP_ID",
|
|
1382
1383
|
"DEEPSPACE_RESOURCE_ID",
|
|
1384
|
+
"DEEPSPACE_RELEASE_OPERATION_ID",
|
|
1383
1385
|
"APP_NAME",
|
|
1384
1386
|
"OWNER_USER_ID",
|
|
1385
1387
|
"AUTH_JWT_PUBLIC_KEY",
|
|
@@ -1470,6 +1472,17 @@ function requiredFieldError(b) {
|
|
|
1470
1472
|
return `hyperdrive binding "${b.name}": auto-provisioning not yet supported (provide a real id)`;
|
|
1471
1473
|
}
|
|
1472
1474
|
return null;
|
|
1475
|
+
case "ratelimit":
|
|
1476
|
+
if (!/^\d+$/u.test(b.namespace_id) || Number(b.namespace_id) <= 0) {
|
|
1477
|
+
return `ratelimit binding "${b.name}" requires a positive integer namespace_id string`;
|
|
1478
|
+
}
|
|
1479
|
+
if (!Number.isInteger(b.simple?.limit) || b.simple.limit <= 0) {
|
|
1480
|
+
return `ratelimit binding "${b.name}" requires simple.limit (positive integer)`;
|
|
1481
|
+
}
|
|
1482
|
+
if (b.simple.period !== 10 && b.simple.period !== 60) {
|
|
1483
|
+
return `ratelimit binding "${b.name}" requires simple.period (10 or 60)`;
|
|
1484
|
+
}
|
|
1485
|
+
return null;
|
|
1473
1486
|
case "ai":
|
|
1474
1487
|
case "browser_rendering":
|
|
1475
1488
|
case "analytics_engine":
|
|
@@ -1547,6 +1560,15 @@ function bindingManifestFromOutputConfig(outputConfig) {
|
|
|
1547
1560
|
const id = pluckString(h, "id");
|
|
1548
1561
|
if (name && id) out.push({ type: "hyperdrive", name, id });
|
|
1549
1562
|
}
|
|
1563
|
+
for (const r of asObjectArray(outputConfig.ratelimits)) {
|
|
1564
|
+
const name = pluckString(r, "name");
|
|
1565
|
+
const namespace_id = pluckString(r, "namespace_id");
|
|
1566
|
+
const simple = isObject(r.simple) ? r.simple : null;
|
|
1567
|
+
const limit = pluckNumber(simple, "limit");
|
|
1568
|
+
const period = pluckNumber(simple, "period");
|
|
1569
|
+
if (!name || !namespace_id || limit === null || period !== 10 && period !== 60) continue;
|
|
1570
|
+
out.push({ type: "ratelimit", name, namespace_id, simple: { limit, period } });
|
|
1571
|
+
}
|
|
1550
1572
|
return out;
|
|
1551
1573
|
}
|
|
1552
1574
|
function isObject(v) {
|
|
@@ -1816,7 +1838,8 @@ async function refreshSecretsCache(deployUrl, token, appId, wranglerEnv) {
|
|
|
1816
1838
|
return {
|
|
1817
1839
|
pulled,
|
|
1818
1840
|
rendered: renderSecretsCache(pulled.values, pulled),
|
|
1819
|
-
|
|
1841
|
+
// An empty store is the common case and not worth a status line.
|
|
1842
|
+
summary: count ? `Secrets: ${pulled.configName} (${count} secret${count === 1 ? "" : "s"})` : null
|
|
1820
1843
|
};
|
|
1821
1844
|
}
|
|
1822
1845
|
|
|
@@ -1897,6 +1920,7 @@ var NON_INHERITABLE_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
|
1897
1920
|
"r2_buckets",
|
|
1898
1921
|
"ai_search_namespaces",
|
|
1899
1922
|
"ai_search",
|
|
1923
|
+
"ratelimits",
|
|
1900
1924
|
"vectorize",
|
|
1901
1925
|
"services",
|
|
1902
1926
|
"queues",
|
|
@@ -1998,6 +2022,13 @@ function flattenWranglerEnvConfig(config, envName) {
|
|
|
1998
2022
|
for (const [key, value] of Object.entries(envBlock)) {
|
|
1999
2023
|
flattened[key] = value;
|
|
2000
2024
|
}
|
|
2025
|
+
if (!("durable_objects" in envBlock) && config.durable_objects) {
|
|
2026
|
+
flattened.durable_objects = config.durable_objects;
|
|
2027
|
+
}
|
|
2028
|
+
const envVars = asTomlRecord(envBlock.vars) ?? {};
|
|
2029
|
+
if (!("APP_NAME" in envVars) && typeof envBlock.name === "string") {
|
|
2030
|
+
flattened.vars = { ...envVars, APP_NAME: envBlock.name };
|
|
2031
|
+
}
|
|
2001
2032
|
return flattened;
|
|
2002
2033
|
}
|
|
2003
2034
|
function asTomlRecord(value) {
|
|
@@ -2259,18 +2290,18 @@ function ensureInstallReady(appDir) {
|
|
|
2259
2290
|
const logPath = join9(sentinelDir, "install.log");
|
|
2260
2291
|
if (existsSync9(errPath)) {
|
|
2261
2292
|
const detail = readFileSync11(errPath, "utf-8").trimEnd();
|
|
2262
|
-
const
|
|
2293
|
+
const log28 = existsSync9(logPath) ? ` Full log: ${logPath}.` : "";
|
|
2263
2294
|
throw new Refusal(
|
|
2264
|
-
`Dependency install failed: ${detail}.${
|
|
2295
|
+
`Dependency install failed: ${detail}.${log28} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
2265
2296
|
"install_failed",
|
|
2266
2297
|
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
2267
2298
|
);
|
|
2268
2299
|
}
|
|
2269
2300
|
if (existsSync9(startedPath) && !existsSync9(donePath)) {
|
|
2270
2301
|
if (!installWorkerAlive(sentinelDir)) {
|
|
2271
|
-
const
|
|
2302
|
+
const log28 = existsSync9(logPath) ? ` See what happened: ${logPath}.` : "";
|
|
2272
2303
|
throw new Refusal(
|
|
2273
|
-
`The dependency install is no longer running and never finished.${
|
|
2304
|
+
`The dependency install is no longer running and never finished.${log28} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
2274
2305
|
"install_failed",
|
|
2275
2306
|
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
2276
2307
|
);
|
|
@@ -2419,7 +2450,7 @@ function lintSchema(schema) {
|
|
|
2419
2450
|
const offendingRoles = Object.entries(schema.permissions).filter(([, perms]) => perms.read === true).map(([role]) => role);
|
|
2420
2451
|
if (offendingRoles.length > 0) {
|
|
2421
2452
|
const usesAware = Object.values(schema.permissions).some(
|
|
2422
|
-
(
|
|
2453
|
+
(p34) => typeof p34.read === "string" && VISIBILITY_AWARE.includes(p34.read)
|
|
2423
2454
|
);
|
|
2424
2455
|
if (!usesAware) {
|
|
2425
2456
|
warnings.push(
|
|
@@ -2503,10 +2534,10 @@ function formatSchemaLintFindings(findings) {
|
|
|
2503
2534
|
import { createServer } from "net";
|
|
2504
2535
|
var DEFAULT_PORT = 5173;
|
|
2505
2536
|
function checkPortAvailable(port, host = "0.0.0.0") {
|
|
2506
|
-
return new Promise((
|
|
2537
|
+
return new Promise((resolve18) => {
|
|
2507
2538
|
const srv = createServer();
|
|
2508
|
-
srv.once("error", () =>
|
|
2509
|
-
srv.once("listening", () => srv.close(() =>
|
|
2539
|
+
srv.once("error", () => resolve18(false));
|
|
2540
|
+
srv.once("listening", () => srv.close(() => resolve18(true)));
|
|
2510
2541
|
srv.listen(port, host);
|
|
2511
2542
|
});
|
|
2512
2543
|
}
|
|
@@ -2532,6 +2563,9 @@ function noAppDirMessage(start, children) {
|
|
|
2532
2563
|
}
|
|
2533
2564
|
return lines.join("\n");
|
|
2534
2565
|
}
|
|
2566
|
+
function devExitSucceeded(code, stopping) {
|
|
2567
|
+
return stopping || code === null || code === 0;
|
|
2568
|
+
}
|
|
2535
2569
|
var dev_default = defineDeepspaceCommand({
|
|
2536
2570
|
meta: {
|
|
2537
2571
|
name: "dev",
|
|
@@ -2631,7 +2665,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2631
2665
|
const refreshed = await refreshSecretsCache(DEPLOY_URL, token, appIdForSecrets, wranglerEnv);
|
|
2632
2666
|
if (refreshed) {
|
|
2633
2667
|
generatedSecretsCache = refreshed.rendered;
|
|
2634
|
-
say(refreshed.summary);
|
|
2668
|
+
if (refreshed.summary) say(refreshed.summary);
|
|
2635
2669
|
}
|
|
2636
2670
|
} catch (err) {
|
|
2637
2671
|
throw new Refusal(
|
|
@@ -2668,7 +2702,9 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
2668
2702
|
stdio: "inherit",
|
|
2669
2703
|
env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
|
|
2670
2704
|
});
|
|
2705
|
+
let stopping = false;
|
|
2671
2706
|
const stop = () => {
|
|
2707
|
+
stopping = true;
|
|
2672
2708
|
wranglerConfig.cleanup();
|
|
2673
2709
|
vite.kill();
|
|
2674
2710
|
};
|
|
@@ -2680,7 +2716,9 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
2680
2716
|
res(c);
|
|
2681
2717
|
});
|
|
2682
2718
|
});
|
|
2683
|
-
|
|
2719
|
+
process.off("SIGINT", stop);
|
|
2720
|
+
process.off("SIGTERM", stop);
|
|
2721
|
+
if (!devExitSucceeded(code, stopping)) {
|
|
2684
2722
|
throw new Refusal(`Dev server exited with code ${code}.`, "dev_server_failed", {
|
|
2685
2723
|
extra: { port, appDir, exitCode: code }
|
|
2686
2724
|
});
|
|
@@ -3000,6 +3038,169 @@ function ensurePlaywright(appDir) {
|
|
|
3000
3038
|
}
|
|
3001
3039
|
}
|
|
3002
3040
|
|
|
3041
|
+
// src/cli/lib/test-account-service.ts
|
|
3042
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
3043
|
+
|
|
3044
|
+
// src/testing/accounts.ts
|
|
3045
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
|
|
3046
|
+
import { homedir as homedir3 } from "os";
|
|
3047
|
+
import { join as join13 } from "path";
|
|
3048
|
+
var TEST_ACCOUNTS_PATH = join13(homedir3(), ".deepspace", "test-accounts.json");
|
|
3049
|
+
var PRODUCTION_AUTH_ORIGIN = "https://auth.deep.space";
|
|
3050
|
+
var STAGING_AUTH_ORIGIN = "https://auth.deepspacesites.com";
|
|
3051
|
+
function currentTestAccountScope(env = process.env) {
|
|
3052
|
+
const selected = env.DEEPSPACE_AUTH_URL ?? (env.DEEPSPACE_ENV === "staging" ? STAGING_AUTH_ORIGIN : PRODUCTION_AUTH_ORIGIN);
|
|
3053
|
+
return normalizeTestAccountScope(selected);
|
|
3054
|
+
}
|
|
3055
|
+
function normalizeTestAccountScope(value) {
|
|
3056
|
+
try {
|
|
3057
|
+
return new URL(value).origin;
|
|
3058
|
+
} catch {
|
|
3059
|
+
return value;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
function loadAllTestAccounts(scope = currentTestAccountScope()) {
|
|
3063
|
+
const store = readTestAccountStore();
|
|
3064
|
+
return mergeTestAccounts(store.scopes[scope] ?? [], store.unscoped);
|
|
3065
|
+
}
|
|
3066
|
+
function reconcileTestAccountStore(remote, scope = currentTestAccountScope()) {
|
|
3067
|
+
const result = reconcileTestAccountScopes(readTestAccountStore(), remote, scope);
|
|
3068
|
+
saveTestAccountStore(result.store);
|
|
3069
|
+
return { accounts: result.accounts, removed: result.removed };
|
|
3070
|
+
}
|
|
3071
|
+
function reconcileTestAccountScopes(store, remote, scope) {
|
|
3072
|
+
const scoped = store.scopes[scope] ?? [];
|
|
3073
|
+
const current = reconcileTestAccounts(scoped, remote);
|
|
3074
|
+
const migrated = reconcileTestAccounts(store.unscoped, remote);
|
|
3075
|
+
const accounts = mergeTestAccounts(current, migrated);
|
|
3076
|
+
return {
|
|
3077
|
+
accounts,
|
|
3078
|
+
removed: Math.max(0, scoped.length - current.length),
|
|
3079
|
+
store: {
|
|
3080
|
+
version: 2,
|
|
3081
|
+
scopes: { ...store.scopes, [scope]: accounts },
|
|
3082
|
+
unscoped: store.unscoped.filter((account) => !remote.some((item) => accountMatchesRemote(account, item)))
|
|
3083
|
+
}
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
function reconcileTestAccounts(local, remote) {
|
|
3087
|
+
const localById = new Map(local.filter((account) => account.id).map((account) => [account.id, account]));
|
|
3088
|
+
const legacyByEmail = new Map(local.filter((account) => !account.id).map((account) => [account.email, account]));
|
|
3089
|
+
return remote.flatMap((account) => {
|
|
3090
|
+
const stored = localById.get(account.id) ?? legacyByEmail.get(account.email);
|
|
3091
|
+
return stored ? [{ ...stored, ...account }] : [];
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
function upsertTestAccount(account, scope = currentTestAccountScope()) {
|
|
3095
|
+
const store = readTestAccountStore();
|
|
3096
|
+
const accounts = (store.scopes[scope] ?? []).filter(
|
|
3097
|
+
(stored) => stored.id !== account.id && stored.email !== account.email
|
|
3098
|
+
);
|
|
3099
|
+
saveTestAccountStore({
|
|
3100
|
+
version: 2,
|
|
3101
|
+
scopes: { ...store.scopes, [scope]: [...accounts, account] },
|
|
3102
|
+
unscoped: store.unscoped.filter(
|
|
3103
|
+
(stored) => stored.id !== account.id && stored.email !== account.email
|
|
3104
|
+
)
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
function removeTestAccounts(ids, emails = [], scope = currentTestAccountScope()) {
|
|
3108
|
+
const idSet = new Set(ids);
|
|
3109
|
+
const emailSet = new Set(emails);
|
|
3110
|
+
const keep = (account) => !idSet.has(account.id ?? "") && !emailSet.has(account.email);
|
|
3111
|
+
const store = readTestAccountStore();
|
|
3112
|
+
saveTestAccountStore({
|
|
3113
|
+
version: 2,
|
|
3114
|
+
scopes: { ...store.scopes, [scope]: (store.scopes[scope] ?? []).filter(keep) },
|
|
3115
|
+
unscoped: store.unscoped.filter(keep)
|
|
3116
|
+
});
|
|
3117
|
+
}
|
|
3118
|
+
function readTestAccountStore() {
|
|
3119
|
+
if (!existsSync12(TEST_ACCOUNTS_PATH)) return { version: 2, scopes: {}, unscoped: [] };
|
|
3120
|
+
try {
|
|
3121
|
+
const raw = JSON.parse(readFileSync13(TEST_ACCOUNTS_PATH, "utf-8"));
|
|
3122
|
+
if (Array.isArray(raw)) return { version: 2, scopes: {}, unscoped: validTestAccounts(raw) };
|
|
3123
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
3124
|
+
return { version: 2, scopes: {}, unscoped: [] };
|
|
3125
|
+
}
|
|
3126
|
+
const candidate = raw;
|
|
3127
|
+
const scopes = candidate.scopes && typeof candidate.scopes === "object" && !Array.isArray(candidate.scopes) ? Object.fromEntries(Object.entries(candidate.scopes).map(([key, value]) => [key, validTestAccounts(value)])) : {};
|
|
3128
|
+
return { version: 2, scopes, unscoped: validTestAccounts(candidate.unscoped) };
|
|
3129
|
+
} catch {
|
|
3130
|
+
return { version: 2, scopes: {}, unscoped: [] };
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
function validTestAccounts(value) {
|
|
3134
|
+
if (!Array.isArray(value)) return [];
|
|
3135
|
+
return value.filter((item) => Boolean(
|
|
3136
|
+
item && typeof item === "object" && typeof item.email === "string" && typeof item.password === "string"
|
|
3137
|
+
));
|
|
3138
|
+
}
|
|
3139
|
+
function mergeTestAccounts(...groups) {
|
|
3140
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
3141
|
+
for (const account of groups.flat()) {
|
|
3142
|
+
const key = account.id ? `id:${account.id}` : `email:${account.email}`;
|
|
3143
|
+
if (!accounts.has(key)) accounts.set(key, account);
|
|
3144
|
+
}
|
|
3145
|
+
return [...accounts.values()];
|
|
3146
|
+
}
|
|
3147
|
+
function accountMatchesRemote(local, remote) {
|
|
3148
|
+
return local.id ? local.id === remote.id : local.email === remote.email;
|
|
3149
|
+
}
|
|
3150
|
+
function saveTestAccountStore(store) {
|
|
3151
|
+
mkdirSync5(join13(homedir3(), ".deepspace"), { recursive: true });
|
|
3152
|
+
writeFileSync7(TEST_ACCOUNTS_PATH, JSON.stringify(store, null, 2), { mode: 384 });
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
// src/cli/lib/test-account-service.ts
|
|
3156
|
+
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
3157
|
+
var AUTH_URL4 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
3158
|
+
function sessionCookie() {
|
|
3159
|
+
const token = readFileSync14(SESSION_PATH, "utf-8").trim();
|
|
3160
|
+
return `${SESSION_COOKIE2}=${encodeURIComponent(token)}`;
|
|
3161
|
+
}
|
|
3162
|
+
async function fetchRemoteTestAccounts() {
|
|
3163
|
+
const response = await fetch(`${AUTH_URL4}/api/auth/test-accounts`, {
|
|
3164
|
+
headers: { Cookie: sessionCookie(), Origin: AUTH_URL4 }
|
|
3165
|
+
});
|
|
3166
|
+
const data = await response.json().catch(() => ({}));
|
|
3167
|
+
if (!response.ok || !data.accounts) {
|
|
3168
|
+
throw new Error(data.error ?? "Failed to list test accounts");
|
|
3169
|
+
}
|
|
3170
|
+
return data.accounts;
|
|
3171
|
+
}
|
|
3172
|
+
async function createRemoteTestAccount(input) {
|
|
3173
|
+
const response = await fetch(`${AUTH_URL4}/api/auth/test-accounts`, {
|
|
3174
|
+
method: "POST",
|
|
3175
|
+
headers: {
|
|
3176
|
+
"Content-Type": "application/json",
|
|
3177
|
+
Cookie: sessionCookie(),
|
|
3178
|
+
Origin: AUTH_URL4
|
|
3179
|
+
},
|
|
3180
|
+
body: JSON.stringify(input)
|
|
3181
|
+
});
|
|
3182
|
+
const data = await response.json().catch(() => ({}));
|
|
3183
|
+
if (!response.ok || typeof data.id !== "string" || typeof data.email !== "string" || typeof data.userId !== "string" || typeof data.createdAt !== "number") {
|
|
3184
|
+
throw new Error(data.error ?? "Unknown error");
|
|
3185
|
+
}
|
|
3186
|
+
return { ...data, label: data.label ?? null };
|
|
3187
|
+
}
|
|
3188
|
+
async function deleteRemoteTestAccount(id) {
|
|
3189
|
+
const response = await fetch(`${AUTH_URL4}/api/auth/test-accounts/${id}`, {
|
|
3190
|
+
method: "DELETE",
|
|
3191
|
+
headers: { Cookie: sessionCookie(), Origin: AUTH_URL4 }
|
|
3192
|
+
});
|
|
3193
|
+
if (!response.ok) {
|
|
3194
|
+
const data = await response.json().catch(() => ({}));
|
|
3195
|
+
throw new Error(data.error ?? `DELETE returned ${response.status}`);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
async function syncTestAccountStore() {
|
|
3199
|
+
const accounts = await fetchRemoteTestAccounts();
|
|
3200
|
+
const { removed } = reconcileTestAccountStore(accounts, normalizeTestAccountScope(AUTH_URL4));
|
|
3201
|
+
return { accounts, removed };
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3003
3204
|
// src/cli/commands/test.ts
|
|
3004
3205
|
var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
3005
3206
|
var test_default = defineDeepspaceCommand({
|
|
@@ -3061,7 +3262,7 @@ var test_default = defineDeepspaceCommand({
|
|
|
3061
3262
|
const refreshed = await refreshSecretsCache(DEPLOY_URL2, token, appIdForSecrets, wranglerEnv);
|
|
3062
3263
|
if (refreshed) {
|
|
3063
3264
|
generatedSecretsCache = refreshed.rendered;
|
|
3064
|
-
say(refreshed.summary);
|
|
3265
|
+
if (refreshed.summary) say(refreshed.summary);
|
|
3065
3266
|
}
|
|
3066
3267
|
} catch (err) {
|
|
3067
3268
|
throw new Refusal(
|
|
@@ -3075,6 +3276,14 @@ var test_default = defineDeepspaceCommand({
|
|
|
3075
3276
|
generatedSecretsCache,
|
|
3076
3277
|
sharedDevVarsCache
|
|
3077
3278
|
});
|
|
3279
|
+
if (suite !== "unit") {
|
|
3280
|
+
try {
|
|
3281
|
+
const { removed } = await syncTestAccountStore();
|
|
3282
|
+
if (removed > 0) say(`Removed ${removed} stale test account credential(s).`);
|
|
3283
|
+
} catch (error) {
|
|
3284
|
+
say(`Warning: could not reconcile test accounts: ${error instanceof Error ? error.message : String(error)}`);
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3078
3287
|
if (suite !== "unit") {
|
|
3079
3288
|
preflightWindowsWorkerd(appDir);
|
|
3080
3289
|
ensurePlaywright(appDir);
|
|
@@ -3226,49 +3435,6 @@ var screenshot_default = defineDeepspaceCommand({
|
|
|
3226
3435
|
// src/cli/commands/test-accounts.ts
|
|
3227
3436
|
import { defineCommand as defineCommand2 } from "citty";
|
|
3228
3437
|
import * as p5 from "@clack/prompts";
|
|
3229
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
|
|
3230
|
-
import { join as join13 } from "path";
|
|
3231
|
-
import { homedir as homedir3 } from "os";
|
|
3232
|
-
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
3233
|
-
var AUTH_URL4 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
3234
|
-
var DIR2 = join13(homedir3(), ".deepspace");
|
|
3235
|
-
var ACCOUNTS_PATH = join13(DIR2, "test-accounts.json");
|
|
3236
|
-
function loadAccounts() {
|
|
3237
|
-
if (!existsSync12(ACCOUNTS_PATH)) return [];
|
|
3238
|
-
try {
|
|
3239
|
-
return JSON.parse(readFileSync13(ACCOUNTS_PATH, "utf-8"));
|
|
3240
|
-
} catch {
|
|
3241
|
-
return [];
|
|
3242
|
-
}
|
|
3243
|
-
}
|
|
3244
|
-
function saveAccounts(accounts) {
|
|
3245
|
-
mkdirSync5(DIR2, { recursive: true });
|
|
3246
|
-
writeFileSync7(ACCOUNTS_PATH, JSON.stringify(accounts, null, 2), { mode: 384 });
|
|
3247
|
-
}
|
|
3248
|
-
function sessionCookie() {
|
|
3249
|
-
const token = readFileSync13(SESSION_PATH, "utf-8").trim();
|
|
3250
|
-
return `${SESSION_COOKIE2}=${encodeURIComponent(token)}`;
|
|
3251
|
-
}
|
|
3252
|
-
async function fetchRemoteAccounts() {
|
|
3253
|
-
const res = await fetch(`${AUTH_URL4}/api/auth/test-accounts`, {
|
|
3254
|
-
headers: { Cookie: sessionCookie(), Origin: AUTH_URL4 }
|
|
3255
|
-
});
|
|
3256
|
-
const data = await res.json().catch(() => ({}));
|
|
3257
|
-
if (!res.ok || !data.accounts) {
|
|
3258
|
-
throw new Error(data.error ?? "Failed to list test accounts");
|
|
3259
|
-
}
|
|
3260
|
-
return data.accounts;
|
|
3261
|
-
}
|
|
3262
|
-
async function deleteRemote(id) {
|
|
3263
|
-
const res = await fetch(`${AUTH_URL4}/api/auth/test-accounts/${id}`, {
|
|
3264
|
-
method: "DELETE",
|
|
3265
|
-
headers: { Cookie: sessionCookie(), Origin: AUTH_URL4 }
|
|
3266
|
-
});
|
|
3267
|
-
if (!res.ok) {
|
|
3268
|
-
const data = await res.json().catch(() => ({}));
|
|
3269
|
-
throw new Error(data.error ?? `DELETE returned ${res.status}`);
|
|
3270
|
-
}
|
|
3271
|
-
}
|
|
3272
3438
|
var create = defineDeepspaceCommand({
|
|
3273
3439
|
meta: {
|
|
3274
3440
|
name: "create",
|
|
@@ -3298,45 +3464,37 @@ var create = defineDeepspaceCommand({
|
|
|
3298
3464
|
},
|
|
3299
3465
|
async run({ args }) {
|
|
3300
3466
|
await ensureToken();
|
|
3301
|
-
const
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
}
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
})
|
|
3314
|
-
});
|
|
3315
|
-
const data = await res.json().catch(() => ({}));
|
|
3316
|
-
if (!res.ok || !data.id) {
|
|
3317
|
-
throw new Refusal(`Failed: ${data.error ?? "Unknown error"}`, "test_account_create_failed");
|
|
3467
|
+
const email = args.email;
|
|
3468
|
+
const password = args.password;
|
|
3469
|
+
const name = args.name;
|
|
3470
|
+
const label = args.label;
|
|
3471
|
+
let account;
|
|
3472
|
+
try {
|
|
3473
|
+
account = await createRemoteTestAccount({ email, password, name, label });
|
|
3474
|
+
} catch (error) {
|
|
3475
|
+
throw new Refusal(
|
|
3476
|
+
`Failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
3477
|
+
"test_account_create_failed"
|
|
3478
|
+
);
|
|
3318
3479
|
}
|
|
3319
|
-
|
|
3320
|
-
const accounts = loadAccounts();
|
|
3321
|
-
accounts.push({
|
|
3480
|
+
upsertTestAccount({
|
|
3322
3481
|
id: account.id,
|
|
3323
|
-
email
|
|
3324
|
-
password
|
|
3482
|
+
email,
|
|
3483
|
+
password,
|
|
3325
3484
|
userId: account.userId,
|
|
3326
|
-
name
|
|
3485
|
+
name,
|
|
3327
3486
|
label: account.label,
|
|
3328
3487
|
createdAt: account.createdAt
|
|
3329
3488
|
});
|
|
3330
|
-
saveAccounts(accounts);
|
|
3331
3489
|
if (!args.json) {
|
|
3332
3490
|
console.log(`Created test account:`);
|
|
3333
3491
|
console.log(` ID: ${account.id}`);
|
|
3334
3492
|
console.log(` Email: ${account.email}`);
|
|
3335
|
-
console.log(` Password: ${
|
|
3493
|
+
console.log(` Password: ${password}`);
|
|
3336
3494
|
console.log(` UserID: ${account.userId}`);
|
|
3337
3495
|
if (account.label) console.log(` Label: ${account.label}`);
|
|
3338
3496
|
console.log(`
|
|
3339
|
-
Saved to ${
|
|
3497
|
+
Saved to ${TEST_ACCOUNTS_PATH}`);
|
|
3340
3498
|
}
|
|
3341
3499
|
return {
|
|
3342
3500
|
data: {
|
|
@@ -3345,11 +3503,11 @@ Saved to ${ACCOUNTS_PATH}`);
|
|
|
3345
3503
|
// The password is already echoed on the human path and stored 0600 in
|
|
3346
3504
|
// ACCOUNTS_PATH — withholding it from --json would only force scripts
|
|
3347
3505
|
// to parse the file themselves.
|
|
3348
|
-
password
|
|
3506
|
+
password,
|
|
3349
3507
|
userId: account.userId,
|
|
3350
3508
|
label: account.label ?? null,
|
|
3351
3509
|
createdAt: account.createdAt,
|
|
3352
|
-
savedTo:
|
|
3510
|
+
savedTo: TEST_ACCOUNTS_PATH
|
|
3353
3511
|
}
|
|
3354
3512
|
};
|
|
3355
3513
|
}
|
|
@@ -3363,11 +3521,11 @@ var list = defineDeepspaceCommand({
|
|
|
3363
3521
|
await ensureToken();
|
|
3364
3522
|
let remote;
|
|
3365
3523
|
try {
|
|
3366
|
-
remote = await
|
|
3524
|
+
remote = (await syncTestAccountStore()).accounts;
|
|
3367
3525
|
} catch (err) {
|
|
3368
3526
|
throw new Refusal(`Failed: ${err.message}`, "test_accounts_list_failed");
|
|
3369
3527
|
}
|
|
3370
|
-
const local =
|
|
3528
|
+
const local = loadAllTestAccounts();
|
|
3371
3529
|
const localByEmail = new Map(local.map((a) => [a.email, a]));
|
|
3372
3530
|
if (remote.length === 0) {
|
|
3373
3531
|
if (!args.json) {
|
|
@@ -3438,7 +3596,7 @@ var del = defineDeepspaceCommand({
|
|
|
3438
3596
|
if (targetEmail && !targetId) {
|
|
3439
3597
|
let remote;
|
|
3440
3598
|
try {
|
|
3441
|
-
remote = await
|
|
3599
|
+
remote = await fetchRemoteTestAccounts();
|
|
3442
3600
|
} catch (err) {
|
|
3443
3601
|
throw new Refusal(`Failed: ${err.message}`, "test_accounts_list_failed");
|
|
3444
3602
|
}
|
|
@@ -3451,14 +3609,11 @@ var del = defineDeepspaceCommand({
|
|
|
3451
3609
|
targetId = match.id;
|
|
3452
3610
|
}
|
|
3453
3611
|
try {
|
|
3454
|
-
await
|
|
3612
|
+
await deleteRemoteTestAccount(targetId);
|
|
3455
3613
|
} catch (err) {
|
|
3456
3614
|
throw new Refusal(`Failed: ${err.message}`, "test_account_delete_failed");
|
|
3457
3615
|
}
|
|
3458
|
-
|
|
3459
|
-
(a) => a.id !== targetId && (!targetEmail || a.email !== targetEmail)
|
|
3460
|
-
);
|
|
3461
|
-
saveAccounts(accounts);
|
|
3616
|
+
removeTestAccounts([targetId], targetEmail ? [targetEmail] : []);
|
|
3462
3617
|
if (!args.json) {
|
|
3463
3618
|
console.log(`Test account deleted${targetEmail ? `: ${targetEmail}` : `: ${targetId}`}`);
|
|
3464
3619
|
}
|
|
@@ -3487,7 +3642,7 @@ var clear = defineDeepspaceCommand({
|
|
|
3487
3642
|
await ensureToken();
|
|
3488
3643
|
let remote;
|
|
3489
3644
|
try {
|
|
3490
|
-
remote = await
|
|
3645
|
+
remote = await fetchRemoteTestAccounts();
|
|
3491
3646
|
} catch (err) {
|
|
3492
3647
|
throw new Refusal(`Failed: ${err.message}`, "test_accounts_list_failed");
|
|
3493
3648
|
}
|
|
@@ -3518,16 +3673,14 @@ var clear = defineDeepspaceCommand({
|
|
|
3518
3673
|
const failures = [];
|
|
3519
3674
|
for (const a of targets) {
|
|
3520
3675
|
try {
|
|
3521
|
-
await
|
|
3676
|
+
await deleteRemoteTestAccount(a.id);
|
|
3522
3677
|
ok2++;
|
|
3523
3678
|
} catch (err) {
|
|
3524
3679
|
failures.push({ email: a.email, error: err.message });
|
|
3525
3680
|
}
|
|
3526
3681
|
}
|
|
3527
|
-
const
|
|
3528
|
-
|
|
3529
|
-
);
|
|
3530
|
-
saveAccounts(loadAccounts().filter((a) => !deletedIds.has(a.id)));
|
|
3682
|
+
const deleted = targets.filter((target) => !failures.some((failure) => failure.email === target.email));
|
|
3683
|
+
removeTestAccounts(deleted.map((target) => target.id), deleted.map((target) => target.email));
|
|
3531
3684
|
if (failures.length > 0) {
|
|
3532
3685
|
throw new Refusal(
|
|
3533
3686
|
`Deleted ${ok2}/${targets.length} test account(s).
|
|
@@ -3562,22 +3715,36 @@ import { resolve as resolve12 } from "path";
|
|
|
3562
3715
|
// src/cli/commands/deploy/build.ts
|
|
3563
3716
|
import * as p6 from "@clack/prompts";
|
|
3564
3717
|
import { execSync as execSync2 } from "child_process";
|
|
3565
|
-
import { existsSync as
|
|
3566
|
-
import { dirname as dirname5, join as
|
|
3718
|
+
import { existsSync as existsSync15, readFileSync as readFileSync17, readdirSync as readdirSync4 } from "fs";
|
|
3719
|
+
import { dirname as dirname5, join as join16, resolve as resolve10 } from "path";
|
|
3720
|
+
|
|
3721
|
+
// src/documentation/deploy.ts
|
|
3722
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
|
|
3723
|
+
import { join as join14 } from "path";
|
|
3724
|
+
function readDocumentationDeployManifest(appDir, clientDir) {
|
|
3725
|
+
if (!existsSync13(join14(appDir, "documentation.json"))) return void 0;
|
|
3726
|
+
const manifestPath = join14(clientDir, "_documentation", "manifest.json");
|
|
3727
|
+
if (!existsSync13(manifestPath)) {
|
|
3728
|
+
throw new Error(
|
|
3729
|
+
"documentation.json exists but the build produced no documentation manifest. Ensure vite.config.ts includes deepSpaceDocumentation() from deepspace/documentation."
|
|
3730
|
+
);
|
|
3731
|
+
}
|
|
3732
|
+
return JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
3733
|
+
}
|
|
3567
3734
|
|
|
3568
3735
|
// src/cli/commands/migrate/app-migrations.ts
|
|
3569
3736
|
import {
|
|
3570
3737
|
closeSync,
|
|
3571
3738
|
constants,
|
|
3572
|
-
existsSync as
|
|
3739
|
+
existsSync as existsSync14,
|
|
3573
3740
|
fstatSync,
|
|
3574
3741
|
ftruncateSync,
|
|
3575
3742
|
lstatSync,
|
|
3576
3743
|
openSync,
|
|
3577
|
-
readFileSync as
|
|
3744
|
+
readFileSync as readFileSync16,
|
|
3578
3745
|
writeSync
|
|
3579
3746
|
} from "fs";
|
|
3580
|
-
import { join as
|
|
3747
|
+
import { join as join15 } from "path";
|
|
3581
3748
|
|
|
3582
3749
|
// src/shared/protocol/app-migrations.ts
|
|
3583
3750
|
var APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
@@ -3622,8 +3789,8 @@ var APP_MIGRATION_DEFINITIONS = SOURCE_MIGRATIONS.map(
|
|
|
3622
3789
|
({ id, description }) => ({ id, description })
|
|
3623
3790
|
);
|
|
3624
3791
|
function readAppliedAppMigrations(appDir) {
|
|
3625
|
-
const path =
|
|
3626
|
-
if (!
|
|
3792
|
+
const path = join15(appDir, APP_MIGRATIONS_MANIFEST);
|
|
3793
|
+
if (!existsSync14(path)) return [];
|
|
3627
3794
|
const source = readRegularFile(path);
|
|
3628
3795
|
let parsed;
|
|
3629
3796
|
try {
|
|
@@ -3649,7 +3816,7 @@ function planAppMigrations(appDir) {
|
|
|
3649
3816
|
const edits = [];
|
|
3650
3817
|
for (const repoPath of tracked) {
|
|
3651
3818
|
if (!isSourcePath(repoPath, appPrefix)) continue;
|
|
3652
|
-
const absolutePath =
|
|
3819
|
+
const absolutePath = join15(repo, repoPath);
|
|
3653
3820
|
let stat;
|
|
3654
3821
|
try {
|
|
3655
3822
|
stat = lstatSync(absolutePath);
|
|
@@ -3683,8 +3850,8 @@ function planAppMigrations(appDir) {
|
|
|
3683
3850
|
});
|
|
3684
3851
|
if (pending.length > 0 && blockers.length === 0) {
|
|
3685
3852
|
const manifestPath = `${appPrefix}${APP_MIGRATIONS_MANIFEST}`;
|
|
3686
|
-
const absoluteManifestPath =
|
|
3687
|
-
const before =
|
|
3853
|
+
const absoluteManifestPath = join15(repo, manifestPath);
|
|
3854
|
+
const before = existsSync14(absoluteManifestPath) ? readRegularFile(absoluteManifestPath) : null;
|
|
3688
3855
|
const after = `${JSON.stringify([...appliedIds, ...pending.map(({ id }) => id)], null, 2)}
|
|
3689
3856
|
`;
|
|
3690
3857
|
if (before !== after) edits.push({ path: manifestPath, before, after });
|
|
@@ -3702,13 +3869,13 @@ function applyAppMigrationPlan(appDir, plan) {
|
|
|
3702
3869
|
}
|
|
3703
3870
|
const repo = repoToplevel(appDir);
|
|
3704
3871
|
for (const edit of plan.edits) {
|
|
3705
|
-
const absolutePath =
|
|
3706
|
-
const current =
|
|
3872
|
+
const absolutePath = join15(repo, edit.path);
|
|
3873
|
+
const current = existsSync14(absolutePath) ? readRegularFile(absolutePath) : null;
|
|
3707
3874
|
if (current !== edit.before) {
|
|
3708
3875
|
throw new Error(`Migration input changed after planning: ${edit.path}`);
|
|
3709
3876
|
}
|
|
3710
3877
|
}
|
|
3711
|
-
for (const edit of plan.edits) writeRegularFile(
|
|
3878
|
+
for (const edit of plan.edits) writeRegularFile(join15(repo, edit.path), edit.before, edit.after);
|
|
3712
3879
|
}
|
|
3713
3880
|
function readRegularFile(path) {
|
|
3714
3881
|
const before = lstatSync(path);
|
|
@@ -3722,7 +3889,7 @@ function readRegularFile(path) {
|
|
|
3722
3889
|
if (!opened.isFile() || opened.dev !== after.dev || opened.ino !== after.ino) {
|
|
3723
3890
|
throw new Error(`Migration input changed while opening: ${path}`);
|
|
3724
3891
|
}
|
|
3725
|
-
return
|
|
3892
|
+
return readFileSync16(fd, "utf-8");
|
|
3726
3893
|
} finally {
|
|
3727
3894
|
closeSync(fd);
|
|
3728
3895
|
}
|
|
@@ -3748,7 +3915,7 @@ function writeRegularFile(path, before, after) {
|
|
|
3748
3915
|
if (!opened.isFile() || opened.dev !== currentPath.dev || opened.ino !== currentPath.ino) {
|
|
3749
3916
|
throw new Error(`Migration input changed while opening: ${path}`);
|
|
3750
3917
|
}
|
|
3751
|
-
if (
|
|
3918
|
+
if (readFileSync16(fd, "utf-8") !== before) {
|
|
3752
3919
|
throw new Error(`Migration input changed after planning: ${path}`);
|
|
3753
3920
|
}
|
|
3754
3921
|
ftruncateSync(fd, 0);
|
|
@@ -3859,10 +4026,14 @@ var RESERVED_RUN_WORKER_FIRST = /* @__PURE__ */ new Set([
|
|
|
3859
4026
|
"/ws/*",
|
|
3860
4027
|
"/internal/*",
|
|
3861
4028
|
"/v1/*",
|
|
3862
|
-
"/_deepspace/*"
|
|
4029
|
+
"/_deepspace/*",
|
|
4030
|
+
"/_documentation",
|
|
4031
|
+
"/_documentation/*",
|
|
4032
|
+
"/_documentation-root",
|
|
4033
|
+
"/_documentation-root/*"
|
|
3863
4034
|
]);
|
|
3864
4035
|
async function buildDeployBundle(options) {
|
|
3865
|
-
const { appDir, appName, envName, sharedDevVarsCache,
|
|
4036
|
+
const { appDir, appName, envName, sharedDevVarsCache, output, spinner: spinner2 } = options;
|
|
3866
4037
|
const junk = removeMacosJunk(appDir);
|
|
3867
4038
|
if (junk > 0) p6.log.info(`Removed ${junk} macOS metadata file(s) (._*, .DS_Store)`);
|
|
3868
4039
|
const lintFindings = await lintProjectSchemas(appDir);
|
|
@@ -3875,10 +4046,15 @@ async function buildDeployBundle(options) {
|
|
|
3875
4046
|
preparedWranglerConfig = prepareWranglerEnvConfig(appDir, envName, {
|
|
3876
4047
|
sharedDevVarsCache
|
|
3877
4048
|
});
|
|
4049
|
+
const appDomain = DEEPSPACE_ENV === "staging" ? "spacestest.com" : "app.space";
|
|
3878
4050
|
execSync2("npx vite build", {
|
|
3879
4051
|
cwd: appDir,
|
|
3880
4052
|
stdio: "pipe",
|
|
3881
|
-
env:
|
|
4053
|
+
env: {
|
|
4054
|
+
...wranglerViteEnv(process.env, preparedWranglerConfig),
|
|
4055
|
+
// The Vite plugin's one documentation compile stamps release URLs.
|
|
4056
|
+
DEEPSPACE_DOCUMENTATION_BASE_URL: `https://${appName}.${appDomain}/docs`
|
|
4057
|
+
}
|
|
3882
4058
|
});
|
|
3883
4059
|
} catch (error) {
|
|
3884
4060
|
spinner2.stop("Build failed");
|
|
@@ -3889,19 +4065,19 @@ async function buildDeployBundle(options) {
|
|
|
3889
4065
|
preparedWranglerConfig?.cleanup();
|
|
3890
4066
|
}
|
|
3891
4067
|
spinner2.stop("Built");
|
|
3892
|
-
const deployConfigPath =
|
|
3893
|
-
if (!
|
|
4068
|
+
const deployConfigPath = join16(appDir, ".wrangler", "deploy", "config.json");
|
|
4069
|
+
if (!existsSync15(deployConfigPath)) {
|
|
3894
4070
|
output.die(
|
|
3895
4071
|
"Build output config not found at .wrangler/deploy/config.json",
|
|
3896
4072
|
"build_output_missing"
|
|
3897
4073
|
);
|
|
3898
4074
|
}
|
|
3899
|
-
const deployConfig = JSON.parse(
|
|
4075
|
+
const deployConfig = JSON.parse(readFileSync17(deployConfigPath, "utf-8"));
|
|
3900
4076
|
const outputWranglerPath = resolve10(dirname5(deployConfigPath), deployConfig.configPath);
|
|
3901
|
-
if (!
|
|
4077
|
+
if (!existsSync15(outputWranglerPath)) {
|
|
3902
4078
|
output.die(`Output wrangler.json not found at ${outputWranglerPath}`, "build_output_missing");
|
|
3903
4079
|
}
|
|
3904
|
-
const outputConfig = JSON.parse(
|
|
4080
|
+
const outputConfig = JSON.parse(readFileSync17(outputWranglerPath, "utf-8"));
|
|
3905
4081
|
if (outputConfig.name && outputConfig.name !== appName) {
|
|
3906
4082
|
const envHint = envName ? ` (--env ${envName})` : "";
|
|
3907
4083
|
output.die(
|
|
@@ -3910,18 +4086,25 @@ async function buildDeployBundle(options) {
|
|
|
3910
4086
|
);
|
|
3911
4087
|
}
|
|
3912
4088
|
const workerDir = dirname5(outputWranglerPath);
|
|
3913
|
-
const workerBundlePath =
|
|
4089
|
+
const workerBundlePath = join16(workerDir, outputConfig.main);
|
|
3914
4090
|
const clientDir = outputConfig.assets?.directory ? resolve10(workerDir, outputConfig.assets.directory) : null;
|
|
3915
|
-
if (!
|
|
4091
|
+
if (!existsSync15(workerBundlePath)) {
|
|
3916
4092
|
output.die(`Worker bundle not found at ${workerBundlePath}`, "build_output_missing");
|
|
3917
4093
|
}
|
|
3918
|
-
if (!clientDir || !
|
|
4094
|
+
if (!clientDir || !existsSync15(clientDir)) {
|
|
3919
4095
|
return output.die(`Client assets not found at ${clientDir}`, "build_output_missing");
|
|
3920
4096
|
}
|
|
4097
|
+
try {
|
|
4098
|
+
const manifest = readDocumentationDeployManifest(appDir, clientDir);
|
|
4099
|
+
if (manifest) p6.log.info(`Documentation: ${manifest.pageCount} page(s)`);
|
|
4100
|
+
} catch (error) {
|
|
4101
|
+
output.die(errorMessage(error), "documentation_build_failed");
|
|
4102
|
+
}
|
|
3921
4103
|
spinner2.start("Collecting assets...");
|
|
3922
4104
|
const assets = collectAssets(clientDir);
|
|
4105
|
+
const assetConfig = readDeployAssetConfig(clientDir);
|
|
3923
4106
|
spinner2.stop(`Collected ${assets.length} assets`);
|
|
3924
|
-
const workerJs =
|
|
4107
|
+
const workerJs = readFileSync17(workerBundlePath, "utf-8");
|
|
3925
4108
|
const doBindings = outputConfig.durable_objects?.bindings;
|
|
3926
4109
|
const sqliteClasses = new Set(
|
|
3927
4110
|
outputConfig.migrations?.flatMap((migration) => migration.new_sqlite_classes ?? []) ?? []
|
|
@@ -3946,7 +4129,7 @@ async function buildDeployBundle(options) {
|
|
|
3946
4129
|
`Custom bindings: ${customBindings.map((binding) => `${binding.name} (${binding.type})`).join(", ")}`
|
|
3947
4130
|
);
|
|
3948
4131
|
}
|
|
3949
|
-
const extraRoutes =
|
|
4132
|
+
const extraRoutes = resolveDeployRunWorkerFirst(outputConfig);
|
|
3950
4133
|
let appMigrations = [];
|
|
3951
4134
|
try {
|
|
3952
4135
|
appMigrations = readAppliedAppMigrations(appDir);
|
|
@@ -3955,6 +4138,7 @@ async function buildDeployBundle(options) {
|
|
|
3955
4138
|
}
|
|
3956
4139
|
return {
|
|
3957
4140
|
assets,
|
|
4141
|
+
assetConfig,
|
|
3958
4142
|
workerJs,
|
|
3959
4143
|
appMigrations,
|
|
3960
4144
|
doManifest,
|
|
@@ -3963,8 +4147,7 @@ async function buildDeployBundle(options) {
|
|
|
3963
4147
|
};
|
|
3964
4148
|
}
|
|
3965
4149
|
function extractRunWorkerFirst(config) {
|
|
3966
|
-
const raw = config.assets?.run_worker_first;
|
|
3967
|
-
if (!Array.isArray(raw)) return [];
|
|
4150
|
+
const raw = Array.isArray(config.assets?.run_worker_first) ? config.assets.run_worker_first : [];
|
|
3968
4151
|
const routes = [];
|
|
3969
4152
|
const seen = /* @__PURE__ */ new Set();
|
|
3970
4153
|
for (const entry of raw) {
|
|
@@ -3976,19 +4159,24 @@ function extractRunWorkerFirst(config) {
|
|
|
3976
4159
|
}
|
|
3977
4160
|
return routes;
|
|
3978
4161
|
}
|
|
4162
|
+
function resolveDeployRunWorkerFirst(config) {
|
|
4163
|
+
if (config.assets?.run_worker_first === true) return true;
|
|
4164
|
+
const routes = extractRunWorkerFirst(config);
|
|
4165
|
+
return routes;
|
|
4166
|
+
}
|
|
3979
4167
|
function collectAssets(dir) {
|
|
3980
4168
|
const assets = [];
|
|
3981
4169
|
const walk = (currentDir, prefix) => {
|
|
3982
4170
|
for (const entry of readdirSync4(currentDir, { withFileTypes: true })) {
|
|
3983
|
-
if (entry.name
|
|
3984
|
-
const fullPath =
|
|
4171
|
+
if (isDeployAssetControlFile(entry.name)) continue;
|
|
4172
|
+
const fullPath = join16(currentDir, entry.name);
|
|
3985
4173
|
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
3986
4174
|
if (entry.isDirectory()) {
|
|
3987
4175
|
walk(fullPath, relativePath);
|
|
3988
4176
|
} else {
|
|
3989
4177
|
assets.push({
|
|
3990
4178
|
path: `/${relativePath}`,
|
|
3991
|
-
contentBase64:
|
|
4179
|
+
contentBase64: readFileSync17(fullPath).toString("base64")
|
|
3992
4180
|
});
|
|
3993
4181
|
}
|
|
3994
4182
|
}
|
|
@@ -3996,24 +4184,37 @@ function collectAssets(dir) {
|
|
|
3996
4184
|
walk(dir, "");
|
|
3997
4185
|
return assets;
|
|
3998
4186
|
}
|
|
4187
|
+
function readDeployAssetConfig(clientDir) {
|
|
4188
|
+
const config = {};
|
|
4189
|
+
for (const [file, key] of [["_headers", "_headers"], ["_redirects", "_redirects"]]) {
|
|
4190
|
+
const filePath = join16(clientDir, file);
|
|
4191
|
+
if (!existsSync15(filePath)) continue;
|
|
4192
|
+
const content = readFileSync17(filePath, "utf8");
|
|
4193
|
+
if (content.trim()) config[key] = content;
|
|
4194
|
+
}
|
|
4195
|
+
return config;
|
|
4196
|
+
}
|
|
4197
|
+
function isDeployAssetControlFile(name) {
|
|
4198
|
+
return name === ".assetsignore" || name === "_headers" || name === "_redirects";
|
|
4199
|
+
}
|
|
3999
4200
|
function errorMessage(error) {
|
|
4000
4201
|
return error instanceof Error ? error.message : String(error);
|
|
4001
4202
|
}
|
|
4002
4203
|
|
|
4003
4204
|
// src/cli/commands/deploy/commerce.ts
|
|
4004
4205
|
import * as p7 from "@clack/prompts";
|
|
4005
|
-
import { existsSync as
|
|
4006
|
-
import { join as
|
|
4206
|
+
import { existsSync as existsSync16 } from "fs";
|
|
4207
|
+
import { join as join17 } from "path";
|
|
4007
4208
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
4008
4209
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
4009
4210
|
function errorMessage2(error) {
|
|
4010
4211
|
return error instanceof Error ? error.message : String(error);
|
|
4011
4212
|
}
|
|
4012
4213
|
async function loadDeclaration(appDir, sourceFile, outputFile, exportName) {
|
|
4013
|
-
const sourcePath =
|
|
4014
|
-
if (!
|
|
4214
|
+
const sourcePath = join17(appDir, "src", sourceFile);
|
|
4215
|
+
if (!existsSync16(sourcePath)) return void 0;
|
|
4015
4216
|
const esbuild = await import("esbuild");
|
|
4016
|
-
const outputPath =
|
|
4217
|
+
const outputPath = join17(appDir, ".wrangler", "deploy", outputFile);
|
|
4017
4218
|
await esbuild.build({
|
|
4018
4219
|
entryPoints: [sourcePath],
|
|
4019
4220
|
bundle: true,
|
|
@@ -4106,7 +4307,7 @@ Stripe will keep charging them until you cancel. Cancel at the end of their curr
|
|
|
4106
4307
|
}
|
|
4107
4308
|
}
|
|
4108
4309
|
async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
4109
|
-
if (!
|
|
4310
|
+
if (!existsSync16(join17(appDir, "src", "subscriptions.ts"))) return;
|
|
4110
4311
|
let plans;
|
|
4111
4312
|
try {
|
|
4112
4313
|
plans = await loadDeclaration(
|
|
@@ -4161,7 +4362,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
|
4161
4362
|
}
|
|
4162
4363
|
}
|
|
4163
4364
|
async function syncOneTimeProducts(appDir, appName, token) {
|
|
4164
|
-
if (!
|
|
4365
|
+
if (!existsSync16(join17(appDir, "src", "products.ts"))) return;
|
|
4165
4366
|
let products;
|
|
4166
4367
|
try {
|
|
4167
4368
|
products = await loadDeclaration(
|
|
@@ -4453,10 +4654,22 @@ function repoApi(deployUrl, token, appId) {
|
|
|
4453
4654
|
}
|
|
4454
4655
|
|
|
4455
4656
|
// src/cli/lib/vc-remote.ts
|
|
4456
|
-
import { existsSync as
|
|
4657
|
+
import { existsSync as existsSync17 } from "fs";
|
|
4457
4658
|
import { tmpdir } from "os";
|
|
4458
4659
|
import { isAbsolute as isAbsolute3, relative, resolve as resolve11 } from "path";
|
|
4459
|
-
|
|
4660
|
+
function spaceRemoteName(environment = DEEPSPACE_ENV) {
|
|
4661
|
+
if (environment === "staging") return "space-staging";
|
|
4662
|
+
if (environment === "production") return "space";
|
|
4663
|
+
return "space-invalid";
|
|
4664
|
+
}
|
|
4665
|
+
var SPACE_REMOTE = spaceRemoteName();
|
|
4666
|
+
function spaceTrackingRef(branch, remote = SPACE_REMOTE) {
|
|
4667
|
+
return `refs/remotes/${remote}/${branch}`;
|
|
4668
|
+
}
|
|
4669
|
+
function spacePrivateRef(path, environment = DEEPSPACE_ENV) {
|
|
4670
|
+
const root = environment === "production" ? "refs/deepspace" : `refs/deepspace/${environment}`;
|
|
4671
|
+
return `${root}/${path}`;
|
|
4672
|
+
}
|
|
4460
4673
|
function deployBaseUrl() {
|
|
4461
4674
|
return process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
4462
4675
|
}
|
|
@@ -4493,13 +4706,16 @@ function entryIsTransient(cwd) {
|
|
|
4493
4706
|
return rel === "" || !rel.startsWith("..") && !isAbsolute3(rel);
|
|
4494
4707
|
}
|
|
4495
4708
|
function helperEntryUsable(command) {
|
|
4496
|
-
const match = /^!'([^']*)' '([^']*)' git-credential$/.exec(command);
|
|
4497
|
-
|
|
4709
|
+
const match = /^!(?:DEEPSPACE_ENV=(production|staging) )?'([^']*)' '([^']*)' git-credential$/.exec(command);
|
|
4710
|
+
const expectedEnvironment = DEEPSPACE_ENV === "production" ? void 0 : DEEPSPACE_ENV;
|
|
4711
|
+
if (!match || match[1] !== expectedEnvironment) return false;
|
|
4712
|
+
return existsSync17(match[2]) && existsSync17(match[3]);
|
|
4498
4713
|
}
|
|
4499
|
-
function credentialHelperCommand() {
|
|
4714
|
+
function credentialHelperCommand(environment = DEEPSPACE_ENV) {
|
|
4500
4715
|
const entry = process.argv[1];
|
|
4501
4716
|
if (!entry) throw new Error("CLI entry path is unavailable");
|
|
4502
|
-
|
|
4717
|
+
const environmentPrefix = environment === "staging" ? "DEEPSPACE_ENV=staging " : "";
|
|
4718
|
+
return `!${environmentPrefix}${shQuote(process.execPath)} ${shQuote(entry)} git-credential`;
|
|
4503
4719
|
}
|
|
4504
4720
|
function installCredentialHelper(cwd, url) {
|
|
4505
4721
|
try {
|
|
@@ -4538,22 +4754,22 @@ function installCredentialHelper(cwd, url) {
|
|
|
4538
4754
|
} catch {
|
|
4539
4755
|
}
|
|
4540
4756
|
}
|
|
4541
|
-
function ensureSpaceRemote(cwd, appId) {
|
|
4757
|
+
function ensureSpaceRemote(cwd, appId, remote = SPACE_REMOTE) {
|
|
4542
4758
|
const url = repoUrl(appId);
|
|
4543
|
-
const current = runGit(cwd, ["remote", "get-url",
|
|
4759
|
+
const current = runGit(cwd, ["remote", "get-url", remote], { allowFail: true });
|
|
4544
4760
|
if (current.status !== 0) {
|
|
4545
|
-
runGit(cwd, ["remote", "add",
|
|
4761
|
+
runGit(cwd, ["remote", "add", remote, url]);
|
|
4546
4762
|
} else {
|
|
4547
4763
|
const existing = current.stdout.toString("utf-8").trim();
|
|
4548
4764
|
if (existing !== url) {
|
|
4549
4765
|
const priorApp = appIdFromRepoUrl(existing);
|
|
4550
4766
|
if (priorApp && priorApp !== appId && !process.argv.includes("--json")) {
|
|
4551
4767
|
process.stderr.write(
|
|
4552
|
-
`warning: remote '${
|
|
4768
|
+
`warning: remote '${remote}' was pointed at app ${priorApp}; re-aiming it at ${appId}.
|
|
4553
4769
|
`
|
|
4554
4770
|
);
|
|
4555
4771
|
}
|
|
4556
|
-
runGit(cwd, ["remote", "set-url",
|
|
4772
|
+
runGit(cwd, ["remote", "set-url", remote, url]);
|
|
4557
4773
|
}
|
|
4558
4774
|
}
|
|
4559
4775
|
installCredentialHelper(cwd, url);
|
|
@@ -4628,7 +4844,7 @@ function pushToSpace(cwd, token, refspec, opts = {}) {
|
|
|
4628
4844
|
const result = runGitRemote(
|
|
4629
4845
|
cwd,
|
|
4630
4846
|
token,
|
|
4631
|
-
["push", "--porcelain", ...opts.force ? ["--force"] : [], ...extra, SPACE_REMOTE, refspec],
|
|
4847
|
+
["push", "--porcelain", ...opts.force ? ["--force"] : [], ...extra, opts.remote ?? SPACE_REMOTE, refspec],
|
|
4632
4848
|
{ allowFail: true }
|
|
4633
4849
|
);
|
|
4634
4850
|
const parsed = parsePushPorcelain(result.stdout.toString("utf-8"));
|
|
@@ -4811,7 +5027,8 @@ async function syncDeployRepository(options) {
|
|
|
4811
5027
|
"source_unclaimed"
|
|
4812
5028
|
);
|
|
4813
5029
|
}
|
|
4814
|
-
|
|
5030
|
+
const sourceRemote = spaceRemoteName();
|
|
5031
|
+
ensureSpaceRemote(appDir, appId, sourceRemote);
|
|
4815
5032
|
const branch = currentBranch(appDir);
|
|
4816
5033
|
const workspaceBranchId = workspaceIdFromBranch(branch);
|
|
4817
5034
|
if (!isWorkTreeClean(appDir)) {
|
|
@@ -4855,7 +5072,9 @@ async function syncDeployRepository(options) {
|
|
|
4855
5072
|
);
|
|
4856
5073
|
} else {
|
|
4857
5074
|
const pushResult = await pushWithTransientRetry(
|
|
4858
|
-
() => pushToSpace(appDir, token, `refs/heads/${branch}:refs/heads/${branch}
|
|
5075
|
+
() => pushToSpace(appDir, token, `refs/heads/${branch}:refs/heads/${branch}`, {
|
|
5076
|
+
remote: sourceRemote
|
|
5077
|
+
})
|
|
4859
5078
|
);
|
|
4860
5079
|
if (pushResult.status === "committed" || pushResult.status === "up_to_date") {
|
|
4861
5080
|
recoverable = true;
|
|
@@ -4868,7 +5087,7 @@ async function syncDeployRepository(options) {
|
|
|
4868
5087
|
"vc_push_rejected"
|
|
4869
5088
|
);
|
|
4870
5089
|
} else {
|
|
4871
|
-
const remoteState = classifyRemoteState(appDir, token, branch, tip);
|
|
5090
|
+
const remoteState = classifyRemoteState(appDir, token, branch, tip, sourceRemote);
|
|
4872
5091
|
if (remoteState.strictlyBehind && !ignoreStale) {
|
|
4873
5092
|
let behind = "several";
|
|
4874
5093
|
try {
|
|
@@ -4913,16 +5132,17 @@ async function syncDeployRepository(options) {
|
|
|
4913
5132
|
}
|
|
4914
5133
|
return { commitOid, recoverable, deployKey, source, sourceRevision, baseReleaseId };
|
|
4915
5134
|
}
|
|
4916
|
-
function classifyRemoteState(appDir, token, branch, localTip) {
|
|
5135
|
+
function classifyRemoteState(appDir, token, branch, localTip, remote) {
|
|
4917
5136
|
let remoteTip = null;
|
|
5137
|
+
const remoteRef = `refs/remotes/${remote}/${branch}`;
|
|
4918
5138
|
try {
|
|
4919
5139
|
runGitRemote(appDir, token, [
|
|
4920
5140
|
"fetch",
|
|
4921
5141
|
"--quiet",
|
|
4922
|
-
|
|
4923
|
-
`+refs/heads/${branch}
|
|
5142
|
+
remote,
|
|
5143
|
+
`+refs/heads/${branch}:${remoteRef}`
|
|
4924
5144
|
]);
|
|
4925
|
-
remoteTip = resolveCommit(appDir,
|
|
5145
|
+
remoteTip = resolveCommit(appDir, remoteRef);
|
|
4926
5146
|
return {
|
|
4927
5147
|
remoteTip,
|
|
4928
5148
|
strictlyBehind: remoteTip !== null && remoteTip !== localTip && isAncestor(appDir, localTip, remoteTip)
|
|
@@ -4953,7 +5173,7 @@ async function pushWithTransientRetry(push) {
|
|
|
4953
5173
|
} catch (error) {
|
|
4954
5174
|
const message = errorMessage3(error);
|
|
4955
5175
|
if (/(?:HTTP |error: )(?:429|503)\b/i.test(message) && attempt < backoffMs.length) {
|
|
4956
|
-
await new Promise((
|
|
5176
|
+
await new Promise((resolve18) => setTimeout(resolve18, backoffMs[attempt]));
|
|
4957
5177
|
continue;
|
|
4958
5178
|
}
|
|
4959
5179
|
throw error;
|
|
@@ -5004,7 +5224,9 @@ async function deployBuiltBundle(options) {
|
|
|
5004
5224
|
spinner: spinner2
|
|
5005
5225
|
} = options;
|
|
5006
5226
|
spinner2.start(`Deploying ${appName}...`);
|
|
5007
|
-
if (bundle.extraRoutes
|
|
5227
|
+
if (bundle.extraRoutes === true) {
|
|
5228
|
+
p10.log.info("Worker-first routing: all requests");
|
|
5229
|
+
} else if (bundle.extraRoutes.length) {
|
|
5008
5230
|
p10.log.info(`Custom worker-first routes: ${bundle.extraRoutes.join(", ")}`);
|
|
5009
5231
|
}
|
|
5010
5232
|
const assetGroups = packAssetGroups(bundle.assets, GROUP_BYTES);
|
|
@@ -5074,9 +5296,12 @@ async function deployBuiltBundle(options) {
|
|
|
5074
5296
|
form.append("bindingManifest", JSON.stringify(bundle.customBindings));
|
|
5075
5297
|
}
|
|
5076
5298
|
if (secrets.names.length) form.append("userSecrets", JSON.stringify(secrets.values));
|
|
5077
|
-
if (bundle.extraRoutes.length) {
|
|
5299
|
+
if (bundle.extraRoutes === true || bundle.extraRoutes.length) {
|
|
5078
5300
|
form.append("extraRunWorkerFirst", JSON.stringify(bundle.extraRoutes));
|
|
5079
5301
|
}
|
|
5302
|
+
if (Object.keys(bundle.assetConfig).length) {
|
|
5303
|
+
form.append("assetConfig", JSON.stringify(bundle.assetConfig));
|
|
5304
|
+
}
|
|
5080
5305
|
form.append("name", appName);
|
|
5081
5306
|
if (confirmRename) form.append("confirmRename", "true");
|
|
5082
5307
|
if (shouldSendLineage(repository.commitOid, repository.recoverable)) {
|
|
@@ -5115,11 +5340,6 @@ async function deployBuiltBundle(options) {
|
|
|
5115
5340
|
throw error;
|
|
5116
5341
|
}
|
|
5117
5342
|
let body = await response.json().catch(() => ({}));
|
|
5118
|
-
for (let attempt = 0; response.status === 503 && body.code === "release_finalization_pending" && attempt < 3; attempt++) {
|
|
5119
|
-
await new Promise((resolve19) => setTimeout(resolve19, 250 * 2 ** attempt));
|
|
5120
|
-
response = await postCommit();
|
|
5121
|
-
body = await response.json().catch(() => ({}));
|
|
5122
|
-
}
|
|
5123
5343
|
if (response.status === 409 && body.code === "rename_required" && !confirmRename) {
|
|
5124
5344
|
spinner2.stop("Rename confirmation needed");
|
|
5125
5345
|
if (output.nonInteractive) {
|
|
@@ -5225,7 +5445,7 @@ async function postWithRetry(url, makeInit, {
|
|
|
5225
5445
|
lastError = error;
|
|
5226
5446
|
}
|
|
5227
5447
|
if (attempt < attempts) {
|
|
5228
|
-
await new Promise((
|
|
5448
|
+
await new Promise((resolve18) => setTimeout(resolve18, Math.min(8e3, 500 * 2 ** (attempt - 1))));
|
|
5229
5449
|
}
|
|
5230
5450
|
}
|
|
5231
5451
|
if (lastResponse) return lastResponse;
|
|
@@ -5234,7 +5454,7 @@ async function postWithRetry(url, makeInit, {
|
|
|
5234
5454
|
|
|
5235
5455
|
// src/cli/commands/deploy/secrets.ts
|
|
5236
5456
|
import * as p11 from "@clack/prompts";
|
|
5237
|
-
import { existsSync as
|
|
5457
|
+
import { existsSync as existsSync18, readFileSync as readFileSync18 } from "fs";
|
|
5238
5458
|
function classifyDevVarsSecrets(options) {
|
|
5239
5459
|
const store = new Set(options.storeSecretNames);
|
|
5240
5460
|
const strayKeys = options.handEditedDevVarKeys.filter((key) => !store.has(key));
|
|
@@ -5261,7 +5481,7 @@ async function loadDeploySecrets(options) {
|
|
|
5261
5481
|
generatedSecretsCache: refreshed.rendered,
|
|
5262
5482
|
sharedDevVarsCache: true
|
|
5263
5483
|
});
|
|
5264
|
-
p11.log.info(refreshed.summary);
|
|
5484
|
+
if (refreshed.summary) p11.log.info(refreshed.summary);
|
|
5265
5485
|
}
|
|
5266
5486
|
} catch (error) {
|
|
5267
5487
|
if (error?.status === 403) {
|
|
@@ -5332,8 +5552,8 @@ function prepareDeploySecrets(options) {
|
|
|
5332
5552
|
return { values, names };
|
|
5333
5553
|
}
|
|
5334
5554
|
function readCustomDevVars(path, output) {
|
|
5335
|
-
if (!
|
|
5336
|
-
const section = extractCustomDevVars(stripGeneratedSecretsCache(
|
|
5555
|
+
if (!existsSync18(path)) return {};
|
|
5556
|
+
const section = extractCustomDevVars(stripGeneratedSecretsCache(readFileSync18(path, "utf-8")));
|
|
5337
5557
|
if (!section) return {};
|
|
5338
5558
|
try {
|
|
5339
5559
|
return parseDevVars(section);
|
|
@@ -5502,7 +5722,6 @@ var deploy_default = defineCommand3({
|
|
|
5502
5722
|
appName,
|
|
5503
5723
|
envName,
|
|
5504
5724
|
sharedDevVarsCache: secretsCache.linked !== null,
|
|
5505
|
-
wranglerConfig,
|
|
5506
5725
|
output,
|
|
5507
5726
|
spinner: spinner2
|
|
5508
5727
|
});
|
|
@@ -5608,7 +5827,7 @@ async function waitForAssetsReady(url, timeoutMs) {
|
|
|
5608
5827
|
if (!body.includes("Assets have not yet deployed") && !body.includes("No app configured")) {
|
|
5609
5828
|
streak++;
|
|
5610
5829
|
if (streak >= 3) return true;
|
|
5611
|
-
await new Promise((
|
|
5830
|
+
await new Promise((resolve18) => setTimeout(resolve18, 1500));
|
|
5612
5831
|
continue;
|
|
5613
5832
|
}
|
|
5614
5833
|
streak = 0;
|
|
@@ -5616,7 +5835,7 @@ async function waitForAssetsReady(url, timeoutMs) {
|
|
|
5616
5835
|
streak = 0;
|
|
5617
5836
|
}
|
|
5618
5837
|
const waitMs = Math.min(8e3, 1e3 * 2 ** (attempt - 1));
|
|
5619
|
-
await new Promise((
|
|
5838
|
+
await new Promise((resolve18) => setTimeout(resolve18, waitMs));
|
|
5620
5839
|
}
|
|
5621
5840
|
return false;
|
|
5622
5841
|
}
|
|
@@ -5684,18 +5903,15 @@ async function resolveAppSelector(deployUrl, token, selector) {
|
|
|
5684
5903
|
"app_not_found"
|
|
5685
5904
|
);
|
|
5686
5905
|
}
|
|
5687
|
-
async function resolveAppTarget(deployUrl, token, explicit) {
|
|
5906
|
+
async function resolveAppTarget(deployUrl, token, explicit, options = {}) {
|
|
5907
|
+
assertAppTargetResolvable(explicit, options);
|
|
5688
5908
|
const { app: app2, error } = parseAppArg(explicit);
|
|
5689
5909
|
if (error) throw new InputError(error, "invalid_app");
|
|
5690
5910
|
if (app2 !== void 0) return resolveAppSelector(deployUrl, token, app2);
|
|
5911
|
+
const { wranglerEnv } = parseWranglerEnvArg(options.wranglerEnv);
|
|
5691
5912
|
const appDir = findAppDir();
|
|
5692
|
-
const id = appDir ? readAppId(appDir) : null;
|
|
5693
|
-
if (!id)
|
|
5694
|
-
throw new InputError(
|
|
5695
|
-
"No app id. Run from an app directory whose wrangler.toml carries DEEPSPACE_APP_ID, or pass --app <id or name>.",
|
|
5696
|
-
"not_in_app_repo"
|
|
5697
|
-
);
|
|
5698
|
-
}
|
|
5913
|
+
const id = appDir ? readAppId(appDir, wranglerEnv) : null;
|
|
5914
|
+
if (!id) throw appTargetMissingError(wranglerEnv);
|
|
5699
5915
|
return id;
|
|
5700
5916
|
}
|
|
5701
5917
|
function parseAppArg(raw) {
|
|
@@ -5721,17 +5937,42 @@ function parseAppArg(raw) {
|
|
|
5721
5937
|
}
|
|
5722
5938
|
return { app: selector };
|
|
5723
5939
|
}
|
|
5724
|
-
function
|
|
5940
|
+
function parseWranglerEnvArg(raw) {
|
|
5941
|
+
if (raw === void 0) return {};
|
|
5942
|
+
const wranglerEnv = raw.trim();
|
|
5943
|
+
if (!wranglerEnv) {
|
|
5944
|
+
return {
|
|
5945
|
+
error: "--env was given an empty environment name \u2014 pass a wrangler.toml environment name, or omit --env to use the top-level app."
|
|
5946
|
+
};
|
|
5947
|
+
}
|
|
5948
|
+
return { wranglerEnv };
|
|
5949
|
+
}
|
|
5950
|
+
function appTargetMissingError(wranglerEnv) {
|
|
5951
|
+
if (wranglerEnv) {
|
|
5952
|
+
return new InputError(
|
|
5953
|
+
`No app id for env "${wranglerEnv}" \u2014 wrangler.toml has no [env.${wranglerEnv}] block with its own DEEPSPACE_APP_ID.`,
|
|
5954
|
+
"no_app_id_for_env"
|
|
5955
|
+
);
|
|
5956
|
+
}
|
|
5957
|
+
return new InputError(
|
|
5958
|
+
"No app id. Run from an app directory whose wrangler.toml carries DEEPSPACE_APP_ID, or pass --app <id or name>.",
|
|
5959
|
+
"not_in_app_repo"
|
|
5960
|
+
);
|
|
5961
|
+
}
|
|
5962
|
+
function assertAppTargetResolvable(appArg, options = {}) {
|
|
5725
5963
|
const { app: app2, error } = parseAppArg(appArg);
|
|
5726
5964
|
if (error) throw new InputError(error, "invalid_app");
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
if (
|
|
5965
|
+
const { wranglerEnv, error: envError } = parseWranglerEnvArg(options.wranglerEnv);
|
|
5966
|
+
if (envError) throw new InputError(envError, "invalid_env");
|
|
5967
|
+
if (app2 !== void 0 && wranglerEnv) {
|
|
5730
5968
|
throw new InputError(
|
|
5731
|
-
"
|
|
5732
|
-
"
|
|
5969
|
+
"Pass either --app or --env, not both \u2014 --env reads the [env.<name>] block's own app id, while --app names an app directly.",
|
|
5970
|
+
"ambiguous_target"
|
|
5733
5971
|
);
|
|
5734
5972
|
}
|
|
5973
|
+
if (app2 !== void 0) return;
|
|
5974
|
+
const appDir = findAppDir();
|
|
5975
|
+
if (!appDir || !readAppId(appDir, wranglerEnv)) throw appTargetMissingError(wranglerEnv);
|
|
5735
5976
|
}
|
|
5736
5977
|
async function warnIfPhantomApp(deployUrl, token, appId, explicit) {
|
|
5737
5978
|
if (!explicit || !STRICT_APP_ID_RE.test(explicit.trim())) return;
|
|
@@ -5887,12 +6128,20 @@ var apps_default = defineDeepspaceCommand({
|
|
|
5887
6128
|
if (!apps.length) {
|
|
5888
6129
|
console.log("No apps yet. Create one with `npx create-deepspace <name>` and `deepspace deploy`.");
|
|
5889
6130
|
} else {
|
|
5890
|
-
const
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
6131
|
+
const rows = [...apps].sort(
|
|
6132
|
+
(left, right) => Number(right.status !== "undeployed") - Number(left.status !== "undeployed")
|
|
6133
|
+
);
|
|
6134
|
+
const nameWidth = Math.max(4, ...rows.map((a) => (a.name ?? "\u2014").length));
|
|
6135
|
+
const statusWidth = Math.max(6, ...rows.map((a) => a.status.length));
|
|
6136
|
+
const idWidth = Math.max(6, ...rows.map((a) => a.appId.length));
|
|
6137
|
+
console.log(
|
|
6138
|
+
`${"NAME".padEnd(nameWidth)} ${"STATUS".padEnd(statusWidth)} ${"APP ID".padEnd(idWidth)} URL`
|
|
6139
|
+
);
|
|
6140
|
+
for (const a of rows) {
|
|
5894
6141
|
const url = a.url ?? "(not deployed)";
|
|
5895
|
-
console.log(
|
|
6142
|
+
console.log(
|
|
6143
|
+
`${(a.name ?? "\u2014").padEnd(nameWidth)} ${a.status.padEnd(statusWidth)} ${a.appId.padEnd(idWidth)} ${url}`
|
|
6144
|
+
);
|
|
5896
6145
|
}
|
|
5897
6146
|
}
|
|
5898
6147
|
}
|
|
@@ -6058,28 +6307,10 @@ var logs_default = defineCommand4({
|
|
|
6058
6307
|
}
|
|
6059
6308
|
},
|
|
6060
6309
|
async run({ args }) {
|
|
6310
|
+
const envArg = args.env;
|
|
6311
|
+
assertAppTargetResolvable(args.app, { wranglerEnv: envArg });
|
|
6061
6312
|
const token = await ensureToken();
|
|
6062
|
-
const
|
|
6063
|
-
if (args.app && wranglerEnv) {
|
|
6064
|
-
throw new InputError(
|
|
6065
|
-
"Pass either --app or --env, not both \u2014 --env reads the [env.<name>] block\u2019s own app id, --app names an app directly.",
|
|
6066
|
-
"ambiguous_target"
|
|
6067
|
-
);
|
|
6068
|
-
}
|
|
6069
|
-
let appId;
|
|
6070
|
-
if (!args.app && wranglerEnv) {
|
|
6071
|
-
const appDir = findAppDir();
|
|
6072
|
-
const id = appDir ? readAppId(appDir, wranglerEnv) : null;
|
|
6073
|
-
if (!id) {
|
|
6074
|
-
throw new InputError(
|
|
6075
|
-
`No app id for env "${wranglerEnv}" \u2014 wrangler.toml has no [env.${wranglerEnv}] block with its own DEEPSPACE_APP_ID.`,
|
|
6076
|
-
"no_app_id_for_env"
|
|
6077
|
-
);
|
|
6078
|
-
}
|
|
6079
|
-
appId = id;
|
|
6080
|
-
} else {
|
|
6081
|
-
appId = await resolveAppTarget(DEPLOY_URL6, token, args.app);
|
|
6082
|
-
}
|
|
6313
|
+
const appId = await resolveAppTarget(DEPLOY_URL6, token, args.app, { wranglerEnv: envArg });
|
|
6083
6314
|
if (args.level && !LOG_LEVELS.includes(args.level)) {
|
|
6084
6315
|
throw new InputError(`Invalid --level "${args.level}". Use: ${LOG_LEVELS.join(", ")}`, "invalid_level");
|
|
6085
6316
|
}
|
|
@@ -6248,20 +6479,20 @@ var usage_default = defineDeepspaceCommand({
|
|
|
6248
6479
|
});
|
|
6249
6480
|
|
|
6250
6481
|
// src/cli/commands/add.ts
|
|
6251
|
-
import { existsSync as
|
|
6252
|
-
import { dirname as dirname7, join as
|
|
6482
|
+
import { existsSync as existsSync20, readFileSync as readFileSync20, realpathSync as realpathSync3 } from "fs";
|
|
6483
|
+
import { dirname as dirname7, join as join19, resolve as resolve14 } from "path";
|
|
6253
6484
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
6254
6485
|
|
|
6255
6486
|
// src/cli/commands/feature-installer.ts
|
|
6256
6487
|
import {
|
|
6257
6488
|
closeSync as closeSync2,
|
|
6258
6489
|
copyFileSync,
|
|
6259
|
-
existsSync as
|
|
6490
|
+
existsSync as existsSync19,
|
|
6260
6491
|
fstatSync as fstatSync2,
|
|
6261
6492
|
mkdirSync as mkdirSync6,
|
|
6262
6493
|
mkdtempSync,
|
|
6263
6494
|
openSync as openSync2,
|
|
6264
|
-
readFileSync as
|
|
6495
|
+
readFileSync as readFileSync19,
|
|
6265
6496
|
readSync,
|
|
6266
6497
|
readdirSync as readdirSync5,
|
|
6267
6498
|
rmSync as rmSync4,
|
|
@@ -6269,16 +6500,16 @@ import {
|
|
|
6269
6500
|
} from "fs";
|
|
6270
6501
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
6271
6502
|
import { tmpdir as tmpdir2 } from "os";
|
|
6272
|
-
import { dirname as dirname6, isAbsolute as isAbsolute4, join as
|
|
6503
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, join as join18, resolve as resolve13 } from "path";
|
|
6273
6504
|
import { fileURLToPath } from "url";
|
|
6274
6505
|
var NAV_MARKER = "// \u2500\u2500 Features add nav items below this line \u2500\u2500";
|
|
6275
6506
|
function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
6276
6507
|
let dir = dirname6(fileURLToPath(moduleUrl));
|
|
6277
6508
|
for (; ; ) {
|
|
6278
|
-
const packagePath =
|
|
6279
|
-
if (
|
|
6509
|
+
const packagePath = join18(dir, "package.json");
|
|
6510
|
+
if (existsSync19(packagePath)) {
|
|
6280
6511
|
try {
|
|
6281
|
-
const pkg2 = JSON.parse(
|
|
6512
|
+
const pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
|
|
6282
6513
|
if (pkg2.name === "deepspace") return dir;
|
|
6283
6514
|
} catch {
|
|
6284
6515
|
}
|
|
@@ -6289,13 +6520,13 @@ function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
|
6289
6520
|
}
|
|
6290
6521
|
}
|
|
6291
6522
|
function hasFeatureCatalog(packageRoot = featurePackageRoot()) {
|
|
6292
|
-
return
|
|
6523
|
+
return existsSync19(join18(packageRoot, "features"));
|
|
6293
6524
|
}
|
|
6294
6525
|
function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
6295
|
-
const configPath =
|
|
6296
|
-
if (!
|
|
6526
|
+
const configPath = join18(packageRoot, "features", id, "feature.json");
|
|
6527
|
+
if (!existsSync19(configPath)) return null;
|
|
6297
6528
|
try {
|
|
6298
|
-
const config = JSON.parse(
|
|
6529
|
+
const config = JSON.parse(readFileSync19(configPath, "utf-8"));
|
|
6299
6530
|
if (typeof config.id !== "string" || typeof config.name !== "string" || !Array.isArray(config.files)) {
|
|
6300
6531
|
return null;
|
|
6301
6532
|
}
|
|
@@ -6305,8 +6536,8 @@ function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
|
6305
6536
|
}
|
|
6306
6537
|
}
|
|
6307
6538
|
function listFeatureConfigs(packageRoot = featurePackageRoot()) {
|
|
6308
|
-
const featuresDir =
|
|
6309
|
-
if (!
|
|
6539
|
+
const featuresDir = join18(packageRoot, "features");
|
|
6540
|
+
if (!existsSync19(featuresDir)) return [];
|
|
6310
6541
|
return readdirSync5(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readFeatureConfig(entry.name, packageRoot)).filter((config) => config !== null && !config.internal).sort((a, b) => a.id.localeCompare(b.id));
|
|
6311
6542
|
}
|
|
6312
6543
|
function featureCatalog(packageRoot = featurePackageRoot()) {
|
|
@@ -6360,7 +6591,7 @@ function copyFile(src, dest) {
|
|
|
6360
6591
|
function copyFileWithImportShift(src, dest, levels) {
|
|
6361
6592
|
mkdirSync6(dirname6(dest), { recursive: true });
|
|
6362
6593
|
const prefix = "../".repeat(levels);
|
|
6363
|
-
const shifted =
|
|
6594
|
+
const shifted = readFileSync19(src, "utf-8").replace(
|
|
6364
6595
|
/(from\s+|import\s+|import\()(['"])(\.\.\/)/g,
|
|
6365
6596
|
`$1$2${prefix}$3`
|
|
6366
6597
|
);
|
|
@@ -6373,12 +6604,12 @@ function installFiles(packageRoot, config, targetDir, emit) {
|
|
|
6373
6604
|
const preexisting = [];
|
|
6374
6605
|
const missing = [];
|
|
6375
6606
|
for (const file of config.files) {
|
|
6376
|
-
const srcPath =
|
|
6607
|
+
const srcPath = join18(packageRoot, "features", config.id, file.src);
|
|
6377
6608
|
const resolvedDest = resolveFeatureDestPath(file.dest, config.route);
|
|
6378
|
-
const destPath =
|
|
6379
|
-
const existed =
|
|
6609
|
+
const destPath = join18(targetDir, resolvedDest);
|
|
6610
|
+
const existed = existsSync19(destPath);
|
|
6380
6611
|
if (existed) preexisting.push(resolvedDest);
|
|
6381
|
-
if (!
|
|
6612
|
+
if (!existsSync19(srcPath)) {
|
|
6382
6613
|
emit(` Warning: source not found: ${file.src}`, "stderr");
|
|
6383
6614
|
if (!existed) missing.push(resolvedDest);
|
|
6384
6615
|
continue;
|
|
@@ -6415,11 +6646,11 @@ function unresolvedSchema(schema, reason, emit) {
|
|
|
6415
6646
|
}
|
|
6416
6647
|
function integrateSchema(config, targetDir, emit) {
|
|
6417
6648
|
if (!config.schema) return [];
|
|
6418
|
-
const schemasPath =
|
|
6419
|
-
if (!
|
|
6649
|
+
const schemasPath = join18(targetDir, "src", "schemas.ts");
|
|
6650
|
+
if (!existsSync19(schemasPath)) {
|
|
6420
6651
|
return [unresolvedSchema(config.schema, "src/schemas.ts was not found.", emit)];
|
|
6421
6652
|
}
|
|
6422
|
-
let content =
|
|
6653
|
+
let content = readFileSync19(schemasPath, "utf-8");
|
|
6423
6654
|
const { exportName, importPath, spreadOperator } = config.schema;
|
|
6424
6655
|
const importPattern = new RegExp(`^import\\s+\\{[^}]*\\b${exportName}\\b`, "m");
|
|
6425
6656
|
if (importPattern.test(content)) {
|
|
@@ -6472,11 +6703,11 @@ function unresolvedActions(actions, reason, emit) {
|
|
|
6472
6703
|
}
|
|
6473
6704
|
function integrateActions(config, targetDir, emit) {
|
|
6474
6705
|
if (!config.actions?.length) return [];
|
|
6475
|
-
const actionsPath =
|
|
6476
|
-
if (!
|
|
6706
|
+
const actionsPath = join18(targetDir, "src", "actions", "index.ts");
|
|
6707
|
+
if (!existsSync19(actionsPath)) {
|
|
6477
6708
|
return [unresolvedActions(config.actions, "src/actions/index.ts was not found.", emit)];
|
|
6478
6709
|
}
|
|
6479
|
-
let content =
|
|
6710
|
+
let content = readFileSync19(actionsPath, "utf-8");
|
|
6480
6711
|
const registryPattern = /export const actions:\s*Record<string,\s*ActionHandler(?:<[^>]*>)?>\s*=\s*\{/;
|
|
6481
6712
|
if (!registryPattern.test(content)) {
|
|
6482
6713
|
const reason = "src/actions/index.ts has an unexpected structure.";
|
|
@@ -6519,10 +6750,152 @@ ${registryEntry}`);
|
|
|
6519
6750
|
if (integrated > 0) writeFileSync8(actionsPath, content);
|
|
6520
6751
|
return unresolved;
|
|
6521
6752
|
}
|
|
6753
|
+
function integrateCode(config, targetDir, emit) {
|
|
6754
|
+
const unresolved = [];
|
|
6755
|
+
for (const insertion of config.code ?? []) {
|
|
6756
|
+
const targetPath = join18(targetDir, insertion.file);
|
|
6757
|
+
if (!existsSync19(targetPath)) {
|
|
6758
|
+
unresolved.push({
|
|
6759
|
+
area: "code",
|
|
6760
|
+
reason: `${insertion.file} was not found.`,
|
|
6761
|
+
steps: [`Add ${insertion.insert.trim()} to ${insertion.file}.`]
|
|
6762
|
+
});
|
|
6763
|
+
continue;
|
|
6764
|
+
}
|
|
6765
|
+
const original = readFileSync19(targetPath, "utf-8");
|
|
6766
|
+
if (original.includes(insertion.insert.trim())) {
|
|
6767
|
+
emit(` Integration already present: ${insertion.file}`);
|
|
6768
|
+
continue;
|
|
6769
|
+
}
|
|
6770
|
+
const markerIndex = original.indexOf(insertion.marker);
|
|
6771
|
+
if (markerIndex < 0) {
|
|
6772
|
+
unresolved.push({
|
|
6773
|
+
area: "code",
|
|
6774
|
+
reason: `${insertion.file} does not contain the expected insertion point.`,
|
|
6775
|
+
steps: [
|
|
6776
|
+
...insertion.import ? [`Add ${insertion.import} to ${insertion.file}.`] : [],
|
|
6777
|
+
`Add ${insertion.insert.trim()} to ${insertion.file}.`
|
|
6778
|
+
]
|
|
6779
|
+
});
|
|
6780
|
+
continue;
|
|
6781
|
+
}
|
|
6782
|
+
let content = original;
|
|
6783
|
+
if (insertion.import && !content.includes(insertion.import)) {
|
|
6784
|
+
const importIndex = content.search(/^import\s/m);
|
|
6785
|
+
content = `${content.slice(0, importIndex < 0 ? 0 : importIndex)}${insertion.import}
|
|
6786
|
+
${content.slice(importIndex < 0 ? 0 : importIndex)}`;
|
|
6787
|
+
}
|
|
6788
|
+
const currentMarkerIndex = content.indexOf(insertion.marker);
|
|
6789
|
+
const insertAt = insertion.placement === "before" ? currentMarkerIndex : currentMarkerIndex + insertion.marker.length;
|
|
6790
|
+
const prefix = insertion.placement === "before" ? "" : "\n";
|
|
6791
|
+
const suffix = insertion.placement === "before" ? "\n" : "";
|
|
6792
|
+
content = `${content.slice(0, insertAt)}${prefix}${insertion.insert}${suffix}${content.slice(insertAt)}`;
|
|
6793
|
+
writeFileSync8(targetPath, content);
|
|
6794
|
+
emit(` Integrated: ${insertion.file}`);
|
|
6795
|
+
}
|
|
6796
|
+
return unresolved;
|
|
6797
|
+
}
|
|
6798
|
+
function integrateIgnore(config, targetDir) {
|
|
6799
|
+
if (!config.ignore?.length) return;
|
|
6800
|
+
const targetPath = join18(targetDir, ".gitignore");
|
|
6801
|
+
const current = existsSync19(targetPath) ? readFileSync19(targetPath, "utf-8") : "";
|
|
6802
|
+
const present = new Set(current.split(/\r?\n/).map((line) => line.trim()));
|
|
6803
|
+
const missing = config.ignore.filter((pattern) => !present.has(pattern));
|
|
6804
|
+
if (missing.length === 0) return;
|
|
6805
|
+
const separator = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
6806
|
+
writeFileSync8(targetPath, `${current}${separator}${missing.join("\n")}
|
|
6807
|
+
`);
|
|
6808
|
+
}
|
|
6809
|
+
function integrateWrangler(config, targetDir, emit) {
|
|
6810
|
+
const runWorkerFirst = config.wrangler?.runWorkerFirst === true;
|
|
6811
|
+
const rateLimits = config.wrangler?.rateLimits ?? [];
|
|
6812
|
+
if (!runWorkerFirst && rateLimits.length === 0) return [];
|
|
6813
|
+
const targetPath = join18(targetDir, "wrangler.toml");
|
|
6814
|
+
const steps = [
|
|
6815
|
+
...runWorkerFirst ? ["Set assets.run_worker_first = true in wrangler.toml."] : [],
|
|
6816
|
+
...rateLimits.map((binding) => `Add the ${binding.name} rate-limit binding to wrangler.toml.`)
|
|
6817
|
+
];
|
|
6818
|
+
if (!existsSync19(targetPath)) {
|
|
6819
|
+
return [{ area: "wrangler", reason: "wrangler.toml was not found.", steps }];
|
|
6820
|
+
}
|
|
6821
|
+
let source = readFileSync19(targetPath, "utf-8");
|
|
6822
|
+
if (runWorkerFirst && !/^\[assets\]\s*$/m.test(source)) {
|
|
6823
|
+
return [{ area: "wrangler", reason: "wrangler.toml has no [assets] section.", steps }];
|
|
6824
|
+
}
|
|
6825
|
+
let changed = false;
|
|
6826
|
+
if (runWorkerFirst) {
|
|
6827
|
+
const updated = setRunWorkerFirst(source);
|
|
6828
|
+
changed ||= updated !== source;
|
|
6829
|
+
source = updated;
|
|
6830
|
+
}
|
|
6831
|
+
const scopes = ["", ...wranglerEnvironmentNames(source)];
|
|
6832
|
+
for (const scope of scopes) {
|
|
6833
|
+
for (const binding of rateLimits) {
|
|
6834
|
+
if (hasRateLimitBinding(source, scope, binding.name)) continue;
|
|
6835
|
+
source = `${source.trimEnd()}
|
|
6836
|
+
|
|
6837
|
+
${rateLimitBindingBlock(scope, binding)}
|
|
6838
|
+
`;
|
|
6839
|
+
changed = true;
|
|
6840
|
+
}
|
|
6841
|
+
}
|
|
6842
|
+
if (changed) {
|
|
6843
|
+
writeFileSync8(targetPath, source);
|
|
6844
|
+
emit(" Integrated: wrangler.toml");
|
|
6845
|
+
} else emit(" Integration already present: wrangler.toml");
|
|
6846
|
+
return [];
|
|
6847
|
+
}
|
|
6848
|
+
function setRunWorkerFirst(source) {
|
|
6849
|
+
const headers = [...source.matchAll(/^\[(?:env\.[^\]]+\.)?assets\]\s*$/gm)];
|
|
6850
|
+
for (const header of headers.reverse()) {
|
|
6851
|
+
const start = header.index;
|
|
6852
|
+
const bodyStart = start + header[0].length;
|
|
6853
|
+
const nextHeader = source.slice(bodyStart).search(/^\[\[?[^\]]+\]\]?\s*$/m);
|
|
6854
|
+
const end = nextHeader < 0 ? source.length : bodyStart + nextHeader;
|
|
6855
|
+
const section = source.slice(start, end);
|
|
6856
|
+
if (/^\s*run_worker_first\s*=\s*true(?:\s*#.*)?$/m.test(section)) continue;
|
|
6857
|
+
const setting = /^(\s*)run_worker_first\s*=\s*(?:false|\[(?:[^\]"']|"(?:[^"\\]|\\[\s\S])*"|'[^']*')*\])(\s*(?:#.*)?)$/m;
|
|
6858
|
+
const updated = setting.test(section) ? section.replace(setting, "$1run_worker_first = true$2") : section.replace(/^\[(?:env\.[^\]]+\.)?assets\]\s*$/m, "$&\nrun_worker_first = true");
|
|
6859
|
+
source = source.slice(0, start) + updated + source.slice(end);
|
|
6860
|
+
}
|
|
6861
|
+
return source;
|
|
6862
|
+
}
|
|
6863
|
+
function wranglerEnvironmentNames(source) {
|
|
6864
|
+
return [
|
|
6865
|
+
...new Set(
|
|
6866
|
+
[...source.matchAll(/^\[\[?env\.([^.\]]+)(?:\.|\]\]?\s*$)/gm)].map((match) => match[1])
|
|
6867
|
+
)
|
|
6868
|
+
];
|
|
6869
|
+
}
|
|
6870
|
+
function hasRateLimitBinding(source, scope, name) {
|
|
6871
|
+
const header = scope ? `[[env.${scope}.ratelimits]]` : "[[ratelimits]]";
|
|
6872
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6873
|
+
const namePattern = new RegExp(
|
|
6874
|
+
`^\\s*name\\s*=\\s*(?:"${escaped}"|'${escaped}')\\s*(?:#.*)?$`,
|
|
6875
|
+
"m"
|
|
6876
|
+
);
|
|
6877
|
+
let offset = 0;
|
|
6878
|
+
while ((offset = source.indexOf(header, offset)) >= 0) {
|
|
6879
|
+
const end = source.slice(offset + header.length).search(/^\[/m);
|
|
6880
|
+
const blockEnd = end < 0 ? source.length : offset + header.length + end;
|
|
6881
|
+
if (namePattern.test(source.slice(offset, blockEnd))) return true;
|
|
6882
|
+
offset += header.length;
|
|
6883
|
+
}
|
|
6884
|
+
return false;
|
|
6885
|
+
}
|
|
6886
|
+
function rateLimitBindingBlock(scope, binding) {
|
|
6887
|
+
const header = scope ? `[[env.${scope}.ratelimits]]` : "[[ratelimits]]";
|
|
6888
|
+
return [
|
|
6889
|
+
header,
|
|
6890
|
+
`name = ${JSON.stringify(binding.name)}`,
|
|
6891
|
+
`namespace_id = ${JSON.stringify(binding.namespaceId)}`,
|
|
6892
|
+
`simple = { limit = ${binding.limit}, period = ${binding.period} }`
|
|
6893
|
+
].join("\n");
|
|
6894
|
+
}
|
|
6522
6895
|
function integrateCss(packageRoot, config, targetDir, emit) {
|
|
6523
6896
|
if (!config.css?.length) return [];
|
|
6524
|
-
const stylesPath =
|
|
6525
|
-
if (!
|
|
6897
|
+
const stylesPath = join18(targetDir, "src", "styles.css");
|
|
6898
|
+
if (!existsSync19(stylesPath)) {
|
|
6526
6899
|
emit(" Warning: Cannot integrate CSS \u2014 src/styles.css not found");
|
|
6527
6900
|
return [
|
|
6528
6901
|
{
|
|
@@ -6534,12 +6907,12 @@ function integrateCss(packageRoot, config, targetDir, emit) {
|
|
|
6534
6907
|
}
|
|
6535
6908
|
];
|
|
6536
6909
|
}
|
|
6537
|
-
let styles =
|
|
6910
|
+
let styles = readFileSync19(stylesPath, "utf-8");
|
|
6538
6911
|
let integrated = 0;
|
|
6539
6912
|
const unresolved = [];
|
|
6540
6913
|
for (const cssFile of config.css) {
|
|
6541
|
-
const srcPath =
|
|
6542
|
-
if (!
|
|
6914
|
+
const srcPath = join18(packageRoot, "features", config.id, cssFile);
|
|
6915
|
+
if (!existsSync19(srcPath)) {
|
|
6543
6916
|
emit(` Warning: CSS source not found: ${cssFile}`, "stderr");
|
|
6544
6917
|
unresolved.push({
|
|
6545
6918
|
area: "css",
|
|
@@ -6548,7 +6921,7 @@ function integrateCss(packageRoot, config, targetDir, emit) {
|
|
|
6548
6921
|
});
|
|
6549
6922
|
continue;
|
|
6550
6923
|
}
|
|
6551
|
-
const css =
|
|
6924
|
+
const css = readFileSync19(srcPath, "utf-8");
|
|
6552
6925
|
const fingerprint = css.match(/\.([\w-]+)\s*\{|@keyframes\s+([\w-]+)/);
|
|
6553
6926
|
const marker = fingerprint?.[1] ?? fingerprint?.[2];
|
|
6554
6927
|
if (marker && styles.includes(marker)) {
|
|
@@ -6575,11 +6948,11 @@ function unresolvedRoute(route, config, reason, emit) {
|
|
|
6575
6948
|
}
|
|
6576
6949
|
function integrateRoute(config, targetDir, emit) {
|
|
6577
6950
|
if (!config.route) return [];
|
|
6578
|
-
const navPath =
|
|
6579
|
-
if (!
|
|
6951
|
+
const navPath = join18(targetDir, "src", "nav.ts");
|
|
6952
|
+
if (!existsSync19(navPath)) {
|
|
6580
6953
|
return [unresolvedRoute(config.route, config, "src/nav.ts was not found.", emit)];
|
|
6581
6954
|
}
|
|
6582
|
-
let content =
|
|
6955
|
+
let content = readFileSync19(navPath, "utf-8");
|
|
6583
6956
|
const routePath = config.route.path;
|
|
6584
6957
|
if (content.includes(`'${routePath}'`)) {
|
|
6585
6958
|
emit(` Nav already present: ${routePath}`);
|
|
@@ -6626,10 +6999,10 @@ function runPackageInstall(packageManager, targetDir, outputVisible) {
|
|
|
6626
6999
|
const child = spawnSync6(packageManager, ["install"], { ...options, stdio: "inherit" });
|
|
6627
7000
|
return { error: child.error, status: child.status };
|
|
6628
7001
|
}
|
|
6629
|
-
const captureDir = mkdtempSync(
|
|
7002
|
+
const captureDir = mkdtempSync(join18(tmpdir2(), "deepspace-add-install-"));
|
|
6630
7003
|
let outputFd;
|
|
6631
7004
|
try {
|
|
6632
|
-
outputFd = openSync2(
|
|
7005
|
+
outputFd = openSync2(join18(captureDir, "output.log"), "w+");
|
|
6633
7006
|
const child = spawnSync6(packageManager, ["install"], {
|
|
6634
7007
|
...options,
|
|
6635
7008
|
stdio: ["ignore", outputFd, outputFd]
|
|
@@ -6653,14 +7026,14 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
|
|
|
6653
7026
|
if (declarations.every(({ values }) => !values || Object.keys(values).length === 0)) {
|
|
6654
7027
|
return { added: [], installRan: false, packageManager };
|
|
6655
7028
|
}
|
|
6656
|
-
const packagePath =
|
|
6657
|
-
if (!
|
|
7029
|
+
const packagePath = join18(targetDir, "package.json");
|
|
7030
|
+
if (!existsSync19(packagePath)) {
|
|
6658
7031
|
emit(" Could not patch package.json (file not found)");
|
|
6659
7032
|
return { added: [], installRan: false, packageManager };
|
|
6660
7033
|
}
|
|
6661
7034
|
let pkg2;
|
|
6662
7035
|
try {
|
|
6663
|
-
pkg2 = JSON.parse(
|
|
7036
|
+
pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
|
|
6664
7037
|
} catch (error) {
|
|
6665
7038
|
emit(
|
|
6666
7039
|
` Could not patch package.json (parse error: ${error instanceof Error ? error.message : String(error)})`
|
|
@@ -6720,7 +7093,7 @@ ${child.diagnosticTail}` : "";
|
|
|
6720
7093
|
}
|
|
6721
7094
|
function dependencySnapshot(targetDir) {
|
|
6722
7095
|
try {
|
|
6723
|
-
const pkg2 = JSON.parse(
|
|
7096
|
+
const pkg2 = JSON.parse(readFileSync19(join18(targetDir, "package.json"), "utf-8"));
|
|
6724
7097
|
return {
|
|
6725
7098
|
dependencies: pkg2.dependencies ?? {},
|
|
6726
7099
|
devDependencies: pkg2.devDependencies ?? {}
|
|
@@ -6732,7 +7105,7 @@ function dependencySnapshot(targetDir) {
|
|
|
6732
7105
|
function installedNavFeatures(packageRoot, targetDir) {
|
|
6733
7106
|
return listFeatureConfigs(packageRoot).filter((feature) => feature.category === "nav" && feature.files.length > 0).filter((feature) => {
|
|
6734
7107
|
const signature = resolveFeatureDestPath(feature.files[0].dest, feature.route);
|
|
6735
|
-
return
|
|
7108
|
+
return existsSync19(join18(targetDir, signature));
|
|
6736
7109
|
}).map((feature) => feature.id);
|
|
6737
7110
|
}
|
|
6738
7111
|
function warnNavConflicts(packageRoot, config, targetDir, emit) {
|
|
@@ -6770,6 +7143,7 @@ function installFeature(options) {
|
|
|
6770
7143
|
emit(` ${config.description ?? ""}`);
|
|
6771
7144
|
emit(` Target: ${targetDir}`);
|
|
6772
7145
|
const installedFiles = installFiles(packageRoot, config, targetDir, emit);
|
|
7146
|
+
integrateIgnore(config, targetDir);
|
|
6773
7147
|
emit("");
|
|
6774
7148
|
emit(
|
|
6775
7149
|
`Copied ${installedFiles.copied} file(s)${installedFiles.skipped > 0 ? `, skipped ${installedFiles.skipped} existing` : ""}`
|
|
@@ -6777,6 +7151,8 @@ function installFeature(options) {
|
|
|
6777
7151
|
const unresolvedIntegrations = [
|
|
6778
7152
|
...integrateSchema(config, targetDir, emit),
|
|
6779
7153
|
...integrateActions(config, targetDir, emit),
|
|
7154
|
+
...integrateWrangler(config, targetDir, emit),
|
|
7155
|
+
...integrateCode(config, targetDir, emit),
|
|
6780
7156
|
...integrateCss(packageRoot, config, targetDir, emit),
|
|
6781
7157
|
...integrateRoute(config, targetDir, emit)
|
|
6782
7158
|
];
|
|
@@ -6869,9 +7245,10 @@ var CATEGORY_LABELS = {
|
|
|
6869
7245
|
nav: "Navigation",
|
|
6870
7246
|
layout: "Layouts",
|
|
6871
7247
|
display: "Display",
|
|
6872
|
-
landing: "Landing Page Sections"
|
|
7248
|
+
landing: "Landing Page Sections",
|
|
7249
|
+
publishing: "Publishing"
|
|
6873
7250
|
};
|
|
6874
|
-
var CATEGORY_ORDER = ["assistant", "data", "nav", "layout", "display", "landing"];
|
|
7251
|
+
var CATEGORY_ORDER = ["assistant", "data", "publishing", "nav", "layout", "display", "landing"];
|
|
6875
7252
|
function groupByCategory(features) {
|
|
6876
7253
|
const groups = /* @__PURE__ */ new Map();
|
|
6877
7254
|
for (const feature of features) {
|
|
@@ -6961,11 +7338,11 @@ function renderFeatureInfo(config) {
|
|
|
6961
7338
|
return lines.join("\n");
|
|
6962
7339
|
}
|
|
6963
7340
|
function isDeepSpaceApp(dir) {
|
|
6964
|
-
if (
|
|
6965
|
-
const packagePath =
|
|
6966
|
-
if (!
|
|
7341
|
+
if (existsSync20(join19(dir, "wrangler.toml"))) return true;
|
|
7342
|
+
const packagePath = join19(dir, "package.json");
|
|
7343
|
+
if (!existsSync20(packagePath)) return false;
|
|
6967
7344
|
try {
|
|
6968
|
-
const pkg2 = JSON.parse(
|
|
7345
|
+
const pkg2 = JSON.parse(readFileSync20(packagePath, "utf-8"));
|
|
6969
7346
|
return Boolean(pkg2.dependencies?.deepspace || pkg2.devDependencies?.deepspace);
|
|
6970
7347
|
} catch {
|
|
6971
7348
|
return false;
|
|
@@ -6974,8 +7351,8 @@ function isDeepSpaceApp(dir) {
|
|
|
6974
7351
|
function installedPackageRoot(fromDir) {
|
|
6975
7352
|
let dir = fromDir;
|
|
6976
7353
|
for (; ; ) {
|
|
6977
|
-
const candidate =
|
|
6978
|
-
if (
|
|
7354
|
+
const candidate = join19(dir, "node_modules", "deepspace");
|
|
7355
|
+
if (existsSync20(join19(candidate, "package.json"))) return candidate;
|
|
6979
7356
|
const parent = dirname7(dir);
|
|
6980
7357
|
if (parent === dir) return null;
|
|
6981
7358
|
dir = parent;
|
|
@@ -6992,9 +7369,9 @@ async function targetInstaller(appDir) {
|
|
|
6992
7369
|
if (realpathSync3(installedRoot) === realpathSync3(featurePackageRoot())) {
|
|
6993
7370
|
return { installFeature };
|
|
6994
7371
|
}
|
|
6995
|
-
const entry =
|
|
6996
|
-
const catalog =
|
|
6997
|
-
if (!
|
|
7372
|
+
const entry = join19(installedRoot, "dist", "feature-installer.js");
|
|
7373
|
+
const catalog = join19(installedRoot, "features");
|
|
7374
|
+
if (!existsSync20(entry) || !existsSync20(catalog)) {
|
|
6998
7375
|
throw new Refusal(
|
|
6999
7376
|
`The app's installed deepspace package does not contain the typed feature installer.
|
|
7000
7377
|
Expected: ${entry}
|
|
@@ -7087,7 +7464,7 @@ var add_default = defineDeepspaceCommand({
|
|
|
7087
7464
|
return { data: { features: featureCatalog(root) } };
|
|
7088
7465
|
}
|
|
7089
7466
|
const appDir = resolve14(args.dir ?? ".");
|
|
7090
|
-
if (!
|
|
7467
|
+
if (!existsSync20(appDir)) {
|
|
7091
7468
|
throw new Refusal(
|
|
7092
7469
|
`Target directory not found: ${appDir}
|
|
7093
7470
|
Create a new app first: deepspace app create <name>`,
|
|
@@ -7168,11 +7545,11 @@ function fmtDate(iso) {
|
|
|
7168
7545
|
}
|
|
7169
7546
|
async function confirm4(message) {
|
|
7170
7547
|
process.stdout.write(`${message} [y/N] `);
|
|
7171
|
-
return new Promise((
|
|
7548
|
+
return new Promise((resolve18) => {
|
|
7172
7549
|
process.stdin.setEncoding("utf-8");
|
|
7173
7550
|
process.stdin.once("data", (data) => {
|
|
7174
7551
|
const answer = data.toString().trim().toLowerCase();
|
|
7175
|
-
|
|
7552
|
+
resolve18(answer === "y" || answer === "yes");
|
|
7176
7553
|
});
|
|
7177
7554
|
});
|
|
7178
7555
|
}
|
|
@@ -7499,8 +7876,8 @@ var list3 = defineDeepspaceCommand({
|
|
|
7499
7876
|
}
|
|
7500
7877
|
if (pending.length) {
|
|
7501
7878
|
console.log(`PENDING INVITES ON ${app2}`);
|
|
7502
|
-
for (const
|
|
7503
|
-
console.log(` ${
|
|
7879
|
+
for (const p34 of pending) {
|
|
7880
|
+
console.log(` ${p34.email} (expires ${new Date(p34.expiresAt).toLocaleDateString()})`);
|
|
7504
7881
|
}
|
|
7505
7882
|
}
|
|
7506
7883
|
}
|
|
@@ -7598,7 +7975,7 @@ var cancel3 = defineDeepspaceCommand({
|
|
|
7598
7975
|
`/api/app-collaborators/${encodeURIComponent(app2)}`
|
|
7599
7976
|
);
|
|
7600
7977
|
const target = email.trim().toLowerCase();
|
|
7601
|
-
const found = pending.find((
|
|
7978
|
+
const found = pending.find((p34) => p34.email.toLowerCase() === target);
|
|
7602
7979
|
if (!found) {
|
|
7603
7980
|
throw new Refusal(`${email} has no pending invite on ${app2}`, "no_pending_invite", {
|
|
7604
7981
|
action: cliAction("deepspace", "app", "collaborators", "list", "--app", app2)
|
|
@@ -7748,7 +8125,7 @@ var transfer_default = defineCommand7({
|
|
|
7748
8125
|
import { defineCommand as defineCommand8 } from "citty";
|
|
7749
8126
|
|
|
7750
8127
|
// src/cli/commands/_invoke-impl.ts
|
|
7751
|
-
import { readFileSync as
|
|
8128
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
7752
8129
|
import * as p15 from "@clack/prompts";
|
|
7753
8130
|
var API_URL7 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
7754
8131
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -7789,7 +8166,7 @@ function resolveBody(opts) {
|
|
|
7789
8166
|
if (opts.body != null) {
|
|
7790
8167
|
raw = opts.body;
|
|
7791
8168
|
} else if (opts.bodyFile != null) {
|
|
7792
|
-
raw = opts.bodyFile === "-" ?
|
|
8169
|
+
raw = opts.bodyFile === "-" ? readFileSync21(0, "utf-8") : readFileSync21(opts.bodyFile, "utf-8");
|
|
7793
8170
|
} else {
|
|
7794
8171
|
return "{}";
|
|
7795
8172
|
}
|
|
@@ -8066,226 +8443,9 @@ var integrations_default = defineCommand8({
|
|
|
8066
8443
|
}
|
|
8067
8444
|
});
|
|
8068
8445
|
|
|
8069
|
-
// src/cli/commands/library.ts
|
|
8070
|
-
import { defineCommand as defineCommand9 } from "citty";
|
|
8071
|
-
import { existsSync as existsSync20, readFileSync as readFileSync20 } from "fs";
|
|
8072
|
-
import { join as join19, resolve as resolve15 } from "path";
|
|
8073
|
-
import { parse as parseToml4 } from "smol-toml";
|
|
8074
|
-
import * as p16 from "@clack/prompts";
|
|
8075
|
-
var DEFAULT_LIBRARY_APP = "deepdotspace-site";
|
|
8076
|
-
var str = (v) => typeof v === "string" && v ? v : void 0;
|
|
8077
|
-
var publish = defineDeepspaceCommand({
|
|
8078
|
-
meta: {
|
|
8079
|
-
name: "publish",
|
|
8080
|
-
description: "Publish the current app to the DeepSpace library"
|
|
8081
|
-
},
|
|
8082
|
-
args: {
|
|
8083
|
-
dir: {
|
|
8084
|
-
type: "string",
|
|
8085
|
-
description: "App directory (default: current directory)",
|
|
8086
|
-
required: false
|
|
8087
|
-
},
|
|
8088
|
-
app: {
|
|
8089
|
-
type: "string",
|
|
8090
|
-
description: "App name (default: read from wrangler.toml)",
|
|
8091
|
-
required: false
|
|
8092
|
-
},
|
|
8093
|
-
name: {
|
|
8094
|
-
type: "string",
|
|
8095
|
-
description: "Display name (default: app name)",
|
|
8096
|
-
required: false
|
|
8097
|
-
},
|
|
8098
|
-
description: {
|
|
8099
|
-
type: "string",
|
|
8100
|
-
description: "Short description",
|
|
8101
|
-
required: false
|
|
8102
|
-
},
|
|
8103
|
-
category: {
|
|
8104
|
-
type: "string",
|
|
8105
|
-
description: "Category (e.g. Productivity, Finance, AI). Default: General",
|
|
8106
|
-
required: false
|
|
8107
|
-
},
|
|
8108
|
-
tags: {
|
|
8109
|
-
type: "string",
|
|
8110
|
-
description: "Comma-separated tags",
|
|
8111
|
-
required: false
|
|
8112
|
-
},
|
|
8113
|
-
visibility: {
|
|
8114
|
-
type: "string",
|
|
8115
|
-
description: "'public' (default), 'unlisted', or 'private'",
|
|
8116
|
-
required: false
|
|
8117
|
-
},
|
|
8118
|
-
screenshot: {
|
|
8119
|
-
type: "string",
|
|
8120
|
-
description: "Override the auto-captured preview with a public PNG URL",
|
|
8121
|
-
required: false
|
|
8122
|
-
},
|
|
8123
|
-
"library-app": {
|
|
8124
|
-
type: "string",
|
|
8125
|
-
description: `Target library app subdomain (default: ${DEFAULT_LIBRARY_APP})`,
|
|
8126
|
-
required: false
|
|
8127
|
-
}
|
|
8128
|
-
},
|
|
8129
|
-
async run({ args }) {
|
|
8130
|
-
const json = args.json;
|
|
8131
|
-
const intro6 = (s) => {
|
|
8132
|
-
if (!json) p16.intro(s);
|
|
8133
|
-
};
|
|
8134
|
-
const info2 = (s) => {
|
|
8135
|
-
if (!json) p16.log.info(s);
|
|
8136
|
-
};
|
|
8137
|
-
intro6("Publishing to the DeepSpace library");
|
|
8138
|
-
const appErr = parseAppArg(str(args.app)).error;
|
|
8139
|
-
if (appErr) throw new Refusal(appErr, "invalid_app");
|
|
8140
|
-
let appName = str(args.app)?.trim();
|
|
8141
|
-
if (!appName) {
|
|
8142
|
-
const appDir = resolve15(str(args.dir) ?? ".");
|
|
8143
|
-
const wranglerPath = join19(appDir, "wrangler.toml");
|
|
8144
|
-
if (!existsSync20(wranglerPath)) {
|
|
8145
|
-
throw new Refusal(
|
|
8146
|
-
"No --app provided and no wrangler.toml found. Either pass --app <name> or run from your app's directory.",
|
|
8147
|
-
"not_in_app_repo"
|
|
8148
|
-
);
|
|
8149
|
-
}
|
|
8150
|
-
const cfg = parseToml4(readFileSync20(wranglerPath, "utf-8"));
|
|
8151
|
-
const nameRes = resolveAppName(cfg.name);
|
|
8152
|
-
if (!nameRes.ok) throw new Refusal(`wrangler.toml: ${nameRes.reason}`, "invalid_config");
|
|
8153
|
-
appName = nameRes.name;
|
|
8154
|
-
}
|
|
8155
|
-
info2(`App: ${appName}`);
|
|
8156
|
-
const visibility = str(args.visibility) ?? "public";
|
|
8157
|
-
if (!["public", "private", "unlisted"].includes(visibility)) {
|
|
8158
|
-
throw new Refusal("--visibility must be 'public', 'unlisted', or 'private'.", "invalid_visibility");
|
|
8159
|
-
}
|
|
8160
|
-
const tags = str(args.tags) ? String(args.tags).split(",").map((t) => t.trim()).filter(Boolean) : void 0;
|
|
8161
|
-
const libraryApp = str(args["library-app"]) ?? DEFAULT_LIBRARY_APP;
|
|
8162
|
-
const libraryHost = process.env.DEEPSPACE_LIBRARY_HOST ?? `https://${libraryApp}.app.space`;
|
|
8163
|
-
let token;
|
|
8164
|
-
try {
|
|
8165
|
-
token = await ensureToken();
|
|
8166
|
-
} catch (err) {
|
|
8167
|
-
throw new Refusal(err instanceof Error ? err.message : String(err), "not_authenticated", {
|
|
8168
|
-
action: cliAction("deepspace", "auth", "login")
|
|
8169
|
-
});
|
|
8170
|
-
}
|
|
8171
|
-
const payload = {
|
|
8172
|
-
appName,
|
|
8173
|
-
...args.name ? { name: args.name } : {},
|
|
8174
|
-
...args.description ? { description: args.description } : {},
|
|
8175
|
-
...args.category ? { category: args.category } : {},
|
|
8176
|
-
...tags ? { tags } : {},
|
|
8177
|
-
visibility,
|
|
8178
|
-
...args.screenshot ? { screenshotUrl: args.screenshot } : {}
|
|
8179
|
-
};
|
|
8180
|
-
const url = `${libraryHost}/api/actions/library.publish`;
|
|
8181
|
-
let res;
|
|
8182
|
-
try {
|
|
8183
|
-
res = await fetch(url, {
|
|
8184
|
-
method: "POST",
|
|
8185
|
-
headers: {
|
|
8186
|
-
"Content-Type": "application/json",
|
|
8187
|
-
Authorization: `Bearer ${token}`
|
|
8188
|
-
},
|
|
8189
|
-
body: JSON.stringify(payload)
|
|
8190
|
-
});
|
|
8191
|
-
} catch (err) {
|
|
8192
|
-
throw new Refusal(
|
|
8193
|
-
`Network error reaching ${url}: ${err instanceof Error ? err.message : String(err)}`,
|
|
8194
|
-
"network_error"
|
|
8195
|
-
);
|
|
8196
|
-
}
|
|
8197
|
-
const body = await res.text();
|
|
8198
|
-
let parsed;
|
|
8199
|
-
try {
|
|
8200
|
-
parsed = JSON.parse(body);
|
|
8201
|
-
} catch {
|
|
8202
|
-
throw new Refusal(`Server returned non-JSON (${res.status}): ${body.slice(0, 400)}`, "invalid_response");
|
|
8203
|
-
}
|
|
8204
|
-
if (!parsed.success) {
|
|
8205
|
-
throw new Refusal(parsed.error ?? `Publish failed (HTTP ${res.status}).`, "publish_failed");
|
|
8206
|
-
}
|
|
8207
|
-
if (!json) {
|
|
8208
|
-
p16.log.success(`Published ${appName} as ${parsed.data?.templateHandle} (v${parsed.data?.version})`);
|
|
8209
|
-
if (parsed.data?.url) p16.log.message(parsed.data.url);
|
|
8210
|
-
p16.outro("Done");
|
|
8211
|
-
}
|
|
8212
|
-
return {
|
|
8213
|
-
data: {
|
|
8214
|
-
appName,
|
|
8215
|
-
templateHandle: parsed.data?.templateHandle ?? null,
|
|
8216
|
-
url: parsed.data?.url ?? null,
|
|
8217
|
-
version: parsed.data?.version ?? null,
|
|
8218
|
-
visibility
|
|
8219
|
-
}
|
|
8220
|
-
};
|
|
8221
|
-
}
|
|
8222
|
-
});
|
|
8223
|
-
var unpublish = defineDeepspaceCommand({
|
|
8224
|
-
meta: {
|
|
8225
|
-
name: "unpublish",
|
|
8226
|
-
description: "Remove a library entry (owner or admin only)"
|
|
8227
|
-
},
|
|
8228
|
-
args: {
|
|
8229
|
-
handle: {
|
|
8230
|
-
type: "positional",
|
|
8231
|
-
description: "templateHandle to remove (usually your app name)",
|
|
8232
|
-
required: true
|
|
8233
|
-
},
|
|
8234
|
-
"library-app": {
|
|
8235
|
-
type: "string",
|
|
8236
|
-
description: `Target library app subdomain (default: ${DEFAULT_LIBRARY_APP})`,
|
|
8237
|
-
required: false
|
|
8238
|
-
}
|
|
8239
|
-
},
|
|
8240
|
-
async run({ args }) {
|
|
8241
|
-
const json = args.json;
|
|
8242
|
-
if (!json) p16.intro("Unpublishing library entry");
|
|
8243
|
-
let token;
|
|
8244
|
-
try {
|
|
8245
|
-
token = await ensureToken();
|
|
8246
|
-
} catch (err) {
|
|
8247
|
-
throw new Refusal(err instanceof Error ? err.message : String(err), "not_authenticated", {
|
|
8248
|
-
action: cliAction("deepspace", "auth", "login")
|
|
8249
|
-
});
|
|
8250
|
-
}
|
|
8251
|
-
const libraryApp = str(args["library-app"]) ?? DEFAULT_LIBRARY_APP;
|
|
8252
|
-
const libraryHost = process.env.DEEPSPACE_LIBRARY_HOST ?? `https://${libraryApp}.app.space`;
|
|
8253
|
-
const res = await fetch(`${libraryHost}/api/actions/library.unpublish`, {
|
|
8254
|
-
method: "POST",
|
|
8255
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
8256
|
-
body: JSON.stringify({ templateHandle: String(args.handle) })
|
|
8257
|
-
});
|
|
8258
|
-
const body = await res.text();
|
|
8259
|
-
let parsed;
|
|
8260
|
-
try {
|
|
8261
|
-
parsed = JSON.parse(body);
|
|
8262
|
-
} catch {
|
|
8263
|
-
throw new Refusal(`Server returned non-JSON (${res.status}): ${body.slice(0, 400)}`, "invalid_response");
|
|
8264
|
-
}
|
|
8265
|
-
if (!parsed.success) {
|
|
8266
|
-
throw new Refusal(parsed.error ?? `HTTP ${res.status}`, "unpublish_failed");
|
|
8267
|
-
}
|
|
8268
|
-
if (!json) {
|
|
8269
|
-
p16.log.success(`Unpublished ${parsed.data?.templateHandle}`);
|
|
8270
|
-
p16.outro("Done");
|
|
8271
|
-
}
|
|
8272
|
-
return { data: { templateHandle: parsed.data?.templateHandle ?? String(args.handle) } };
|
|
8273
|
-
}
|
|
8274
|
-
});
|
|
8275
|
-
var library_default = defineCommand9({
|
|
8276
|
-
meta: {
|
|
8277
|
-
name: "library",
|
|
8278
|
-
description: "Manage your DeepSpace library entries"
|
|
8279
|
-
},
|
|
8280
|
-
subCommands: {
|
|
8281
|
-
publish,
|
|
8282
|
-
unpublish
|
|
8283
|
-
}
|
|
8284
|
-
});
|
|
8285
|
-
|
|
8286
8446
|
// src/cli/commands/feedback.ts
|
|
8287
|
-
import * as
|
|
8288
|
-
import { existsSync as existsSync21, readFileSync as
|
|
8447
|
+
import * as p16 from "@clack/prompts";
|
|
8448
|
+
import { existsSync as existsSync21, readFileSync as readFileSync22 } from "fs";
|
|
8289
8449
|
import { dirname as dirname8, join as join20 } from "path";
|
|
8290
8450
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8291
8451
|
import { release } from "os";
|
|
@@ -8314,7 +8474,7 @@ function readCliVersion() {
|
|
|
8314
8474
|
];
|
|
8315
8475
|
for (const path of candidates) {
|
|
8316
8476
|
if (!existsSync21(path)) continue;
|
|
8317
|
-
const pkg2 = JSON.parse(
|
|
8477
|
+
const pkg2 = JSON.parse(readFileSync22(path, "utf-8"));
|
|
8318
8478
|
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
8319
8479
|
}
|
|
8320
8480
|
} catch {
|
|
@@ -8407,21 +8567,21 @@ var feedback_default = defineDeepspaceCommand({
|
|
|
8407
8567
|
);
|
|
8408
8568
|
}
|
|
8409
8569
|
if (interactive && (!args.type || !title || !body)) {
|
|
8410
|
-
|
|
8570
|
+
p16.intro("DeepSpace Feedback");
|
|
8411
8571
|
if (!args.type) {
|
|
8412
|
-
const picked = await
|
|
8572
|
+
const picked = await p16.select({
|
|
8413
8573
|
message: "What kind of feedback is this?",
|
|
8414
8574
|
options: TYPES.map((t) => ({ value: t, label: TYPE_LABELS[t] })),
|
|
8415
8575
|
initialValue: type
|
|
8416
8576
|
});
|
|
8417
|
-
if (
|
|
8418
|
-
|
|
8577
|
+
if (p16.isCancel(picked)) {
|
|
8578
|
+
p16.cancel("Cancelled.");
|
|
8419
8579
|
return { data: { submitted: false, cancelled: true } };
|
|
8420
8580
|
}
|
|
8421
8581
|
type = picked;
|
|
8422
8582
|
}
|
|
8423
8583
|
if (!title) {
|
|
8424
|
-
const t = await
|
|
8584
|
+
const t = await p16.text({
|
|
8425
8585
|
message: "Title",
|
|
8426
8586
|
placeholder: "Short summary of the issue",
|
|
8427
8587
|
validate: (v) => {
|
|
@@ -8431,14 +8591,14 @@ var feedback_default = defineDeepspaceCommand({
|
|
|
8431
8591
|
return void 0;
|
|
8432
8592
|
}
|
|
8433
8593
|
});
|
|
8434
|
-
if (
|
|
8435
|
-
|
|
8594
|
+
if (p16.isCancel(t)) {
|
|
8595
|
+
p16.cancel("Cancelled.");
|
|
8436
8596
|
return { data: { submitted: false, cancelled: true } };
|
|
8437
8597
|
}
|
|
8438
8598
|
title = String(t);
|
|
8439
8599
|
}
|
|
8440
8600
|
if (!body) {
|
|
8441
|
-
const m = await
|
|
8601
|
+
const m = await p16.text({
|
|
8442
8602
|
message: "Details",
|
|
8443
8603
|
placeholder: "What happened? Steps to reproduce, what you expected\u2026",
|
|
8444
8604
|
validate: (v) => {
|
|
@@ -8448,8 +8608,8 @@ var feedback_default = defineDeepspaceCommand({
|
|
|
8448
8608
|
return void 0;
|
|
8449
8609
|
}
|
|
8450
8610
|
});
|
|
8451
|
-
if (
|
|
8452
|
-
|
|
8611
|
+
if (p16.isCancel(m)) {
|
|
8612
|
+
p16.cancel("Cancelled.");
|
|
8453
8613
|
return { data: { submitted: false, cancelled: true } };
|
|
8454
8614
|
}
|
|
8455
8615
|
body = String(m);
|
|
@@ -8467,7 +8627,7 @@ var feedback_default = defineDeepspaceCommand({
|
|
|
8467
8627
|
throw new Refusal(err.message, "invalid_input");
|
|
8468
8628
|
}
|
|
8469
8629
|
if (interactive && !args.yes) {
|
|
8470
|
-
|
|
8630
|
+
p16.note(
|
|
8471
8631
|
`${TYPE_LABELS[payload.type]}
|
|
8472
8632
|
${payload.title}
|
|
8473
8633
|
|
|
@@ -8476,9 +8636,9 @@ ${payload.body}
|
|
|
8476
8636
|
Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payload.platform}` + (payload.appName ? ` \xB7 app ${payload.appName}` : ""),
|
|
8477
8637
|
"Review"
|
|
8478
8638
|
);
|
|
8479
|
-
const ok2 = await
|
|
8480
|
-
if (
|
|
8481
|
-
|
|
8639
|
+
const ok2 = await p16.confirm({ message: "Send this to the DeepSpace team?" });
|
|
8640
|
+
if (p16.isCancel(ok2) || !ok2) {
|
|
8641
|
+
p16.cancel("Not sent.");
|
|
8482
8642
|
return { data: { submitted: false, cancelled: true } };
|
|
8483
8643
|
}
|
|
8484
8644
|
}
|
|
@@ -8498,7 +8658,7 @@ Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payl
|
|
|
8498
8658
|
});
|
|
8499
8659
|
if (!args.json) {
|
|
8500
8660
|
if (interactive) {
|
|
8501
|
-
|
|
8661
|
+
p16.outro(`Thanks! Submitted as ${result.report.id}.`);
|
|
8502
8662
|
} else {
|
|
8503
8663
|
console.log(`Submitted feedback ${result.report.id} (${result.report.type}).`);
|
|
8504
8664
|
}
|
|
@@ -8509,8 +8669,8 @@ Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payl
|
|
|
8509
8669
|
});
|
|
8510
8670
|
|
|
8511
8671
|
// src/cli/commands/secrets.ts
|
|
8512
|
-
import { defineCommand as
|
|
8513
|
-
import { readFileSync as
|
|
8672
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
8673
|
+
import { readFileSync as readFileSync23 } from "fs";
|
|
8514
8674
|
|
|
8515
8675
|
// src/cli/lib/citty-args.ts
|
|
8516
8676
|
function dedupePositionals(bound, rest) {
|
|
@@ -8613,7 +8773,7 @@ function warnIfDebugRoutesEnabled(secrets) {
|
|
|
8613
8773
|
}
|
|
8614
8774
|
var APPLY_HINT = "Run `deepspace deploy` to apply \u2014 secrets take effect at deploy time.";
|
|
8615
8775
|
var LOCAL_DEV_HINT = "Running `deepspace dev start`? Restart it to load the change locally.";
|
|
8616
|
-
var list5 =
|
|
8776
|
+
var list5 = defineCommand9({
|
|
8617
8777
|
meta: { name: "list", description: "List masked secrets in a config" },
|
|
8618
8778
|
args: {
|
|
8619
8779
|
...COMMON_ARGS,
|
|
@@ -8643,7 +8803,7 @@ var list5 = defineCommand10({
|
|
|
8643
8803
|
}
|
|
8644
8804
|
}
|
|
8645
8805
|
});
|
|
8646
|
-
var set =
|
|
8806
|
+
var set = defineCommand9({
|
|
8647
8807
|
meta: { name: "set", description: "Set secrets from KEY=value pairs" },
|
|
8648
8808
|
args: {
|
|
8649
8809
|
...COMMON_ARGS,
|
|
@@ -8692,7 +8852,7 @@ var set = defineCommand10({
|
|
|
8692
8852
|
}
|
|
8693
8853
|
}
|
|
8694
8854
|
});
|
|
8695
|
-
var get =
|
|
8855
|
+
var get = defineCommand9({
|
|
8696
8856
|
meta: { name: "get", description: "Get a secret; pass --plain to print the value" },
|
|
8697
8857
|
args: {
|
|
8698
8858
|
...COMMON_ARGS,
|
|
@@ -8728,7 +8888,7 @@ var get = defineCommand10({
|
|
|
8728
8888
|
}
|
|
8729
8889
|
}
|
|
8730
8890
|
});
|
|
8731
|
-
var del2 =
|
|
8891
|
+
var del2 = defineCommand9({
|
|
8732
8892
|
meta: { name: "delete", description: "Delete one or more secrets" },
|
|
8733
8893
|
args: {
|
|
8734
8894
|
...COMMON_ARGS,
|
|
@@ -8762,7 +8922,7 @@ var del2 = defineCommand10({
|
|
|
8762
8922
|
}
|
|
8763
8923
|
}
|
|
8764
8924
|
});
|
|
8765
|
-
var upload =
|
|
8925
|
+
var upload = defineCommand9({
|
|
8766
8926
|
meta: { name: "upload", description: "Upload secrets from a dotenv or JSON file" },
|
|
8767
8927
|
args: {
|
|
8768
8928
|
...COMMON_ARGS,
|
|
@@ -8780,7 +8940,7 @@ var upload = defineCommand10({
|
|
|
8780
8940
|
async run({ args }) {
|
|
8781
8941
|
try {
|
|
8782
8942
|
const t = await resolveTarget(args);
|
|
8783
|
-
const content = args.file === "-" ?
|
|
8943
|
+
const content = args.file === "-" ? readFileSync23(0, "utf-8") : readFileSync23(args.file, "utf-8");
|
|
8784
8944
|
const secrets = parseSecretsUpload(content);
|
|
8785
8945
|
if (Object.keys(secrets).length === 0)
|
|
8786
8946
|
fail(new InputError("No secrets found in the input.", "empty_input"));
|
|
@@ -8799,7 +8959,7 @@ var upload = defineCommand10({
|
|
|
8799
8959
|
}
|
|
8800
8960
|
}
|
|
8801
8961
|
});
|
|
8802
|
-
var download =
|
|
8962
|
+
var download = defineCommand9({
|
|
8803
8963
|
meta: { name: "download", description: "Download a config\u2019s secrets (dotenv/json/shell)" },
|
|
8804
8964
|
args: {
|
|
8805
8965
|
...COMMON_ARGS,
|
|
@@ -8828,7 +8988,7 @@ var download = defineCommand10({
|
|
|
8828
8988
|
}
|
|
8829
8989
|
}
|
|
8830
8990
|
});
|
|
8831
|
-
var pull =
|
|
8991
|
+
var pull = defineCommand9({
|
|
8832
8992
|
meta: { name: "pull", description: "Refresh the .dev.vars cache from the app store" },
|
|
8833
8993
|
args: { ...COMMON_ARGS },
|
|
8834
8994
|
async run({ args }) {
|
|
@@ -8871,7 +9031,7 @@ var pull = defineCommand10({
|
|
|
8871
9031
|
}
|
|
8872
9032
|
}
|
|
8873
9033
|
});
|
|
8874
|
-
var configsList =
|
|
9034
|
+
var configsList = defineCommand9({
|
|
8875
9035
|
meta: { name: "list", description: "List the app\u2019s configs" },
|
|
8876
9036
|
args: { ...COMMON_ARGS },
|
|
8877
9037
|
async run({ args }) {
|
|
@@ -8892,7 +9052,7 @@ var configsList = defineCommand10({
|
|
|
8892
9052
|
}
|
|
8893
9053
|
}
|
|
8894
9054
|
});
|
|
8895
|
-
var configsCreate =
|
|
9055
|
+
var configsCreate = defineCommand9({
|
|
8896
9056
|
meta: { name: "create", description: "Create a config (optionally copying another)" },
|
|
8897
9057
|
args: {
|
|
8898
9058
|
...COMMON_ARGS,
|
|
@@ -8932,7 +9092,7 @@ var configsCreate = defineCommand10({
|
|
|
8932
9092
|
}
|
|
8933
9093
|
}
|
|
8934
9094
|
});
|
|
8935
|
-
var configsDelete =
|
|
9095
|
+
var configsDelete = defineCommand9({
|
|
8936
9096
|
meta: { name: "delete", description: "Delete a config and all of its secrets" },
|
|
8937
9097
|
args: {
|
|
8938
9098
|
...COMMON_ARGS,
|
|
@@ -8953,11 +9113,11 @@ var configsDelete = defineCommand10({
|
|
|
8953
9113
|
}
|
|
8954
9114
|
}
|
|
8955
9115
|
});
|
|
8956
|
-
var configs =
|
|
9116
|
+
var configs = defineCommand9({
|
|
8957
9117
|
meta: { name: "configs", description: "Manage the app\u2019s secrets configs" },
|
|
8958
9118
|
subCommands: { list: configsList, create: configsCreate, delete: configsDelete }
|
|
8959
9119
|
});
|
|
8960
|
-
var secrets_default =
|
|
9120
|
+
var secrets_default = defineCommand9({
|
|
8961
9121
|
meta: { name: "secrets", description: "Manage the app\u2019s secrets store" },
|
|
8962
9122
|
subCommands: {
|
|
8963
9123
|
list: list5,
|
|
@@ -9034,9 +9194,9 @@ var init_default = defineDeepspaceCommand({
|
|
|
9034
9194
|
});
|
|
9035
9195
|
|
|
9036
9196
|
// src/cli/commands/status.ts
|
|
9037
|
-
import { defineCommand as
|
|
9038
|
-
import * as
|
|
9039
|
-
import { existsSync as existsSync22, readFileSync as
|
|
9197
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
9198
|
+
import * as p17 from "@clack/prompts";
|
|
9199
|
+
import { existsSync as existsSync22, readFileSync as readFileSync24 } from "fs";
|
|
9040
9200
|
function timeAgo(iso) {
|
|
9041
9201
|
const ms = Date.now() - Date.parse(iso);
|
|
9042
9202
|
if (!Number.isFinite(ms) || ms < 0) return iso;
|
|
@@ -9061,7 +9221,15 @@ function statusRemoteFailure(error) {
|
|
|
9061
9221
|
json: { state: "unavailable", error: message, ...code ? { code } : {} }
|
|
9062
9222
|
};
|
|
9063
9223
|
}
|
|
9064
|
-
|
|
9224
|
+
function resolveStatusApp(appDir, wranglerEnv) {
|
|
9225
|
+
if (!wranglerEnv) {
|
|
9226
|
+
return { appName: detectAppName(appDir) ?? "(unnamed)", appId: readAppId(appDir) };
|
|
9227
|
+
}
|
|
9228
|
+
const resolved = resolveAppNameForEnv(readWranglerConfig(appDir), wranglerEnv);
|
|
9229
|
+
if (!resolved.ok) throw new InputError(resolved.reason, "invalid_env");
|
|
9230
|
+
return { appName: resolved.name, appId: readAppId(appDir, wranglerEnv) };
|
|
9231
|
+
}
|
|
9232
|
+
var status_default = defineCommand10({
|
|
9065
9233
|
meta: {
|
|
9066
9234
|
name: "status",
|
|
9067
9235
|
description: "One screenful of current state: session, app, workspace, and live release"
|
|
@@ -9071,9 +9239,17 @@ var status_default = defineCommand11({
|
|
|
9071
9239
|
type: "boolean",
|
|
9072
9240
|
description: "Emit a single-line JSON result for scripts/agents",
|
|
9073
9241
|
default: false
|
|
9242
|
+
},
|
|
9243
|
+
env: {
|
|
9244
|
+
type: "string",
|
|
9245
|
+
alias: "e",
|
|
9246
|
+
description: "wrangler.toml [env.<name>] slot \u2014 reads that environment\u2019s app identity",
|
|
9247
|
+
required: false
|
|
9074
9248
|
}
|
|
9075
9249
|
},
|
|
9076
9250
|
async run({ args }) {
|
|
9251
|
+
const { wranglerEnv, error: envError } = parseWranglerEnvArg(args.env);
|
|
9252
|
+
if (envError) throw new InputError(envError, "invalid_env");
|
|
9077
9253
|
const lines = [];
|
|
9078
9254
|
const facts = {
|
|
9079
9255
|
loggedIn: false,
|
|
@@ -9114,7 +9290,7 @@ var status_default = defineCommand11({
|
|
|
9114
9290
|
let who = "logged in";
|
|
9115
9291
|
try {
|
|
9116
9292
|
const payload = decodeJwtPayload(
|
|
9117
|
-
|
|
9293
|
+
readFileSync24(TOKEN_PATH, "utf-8").trim()
|
|
9118
9294
|
);
|
|
9119
9295
|
if (payload.email) who = payload.email;
|
|
9120
9296
|
userId = payload.sub ?? null;
|
|
@@ -9129,15 +9305,15 @@ var status_default = defineCommand11({
|
|
|
9129
9305
|
lines.push(["App", "not inside a DeepSpace app directory"]);
|
|
9130
9306
|
json.inApp = false;
|
|
9131
9307
|
} else {
|
|
9132
|
-
const appName =
|
|
9133
|
-
const appId = readAppId(appDir);
|
|
9308
|
+
const { appName, appId } = resolveStatusApp(appDir, wranglerEnv);
|
|
9134
9309
|
lines.push([
|
|
9135
9310
|
"App",
|
|
9136
|
-
`${appName}${appId ? ` (${appId})` : " \u2014 no DEEPSPACE_APP_ID; run `deepspace app init`"}`
|
|
9311
|
+
`${appName}${wranglerEnv ? ` [env.${wranglerEnv}]` : ""}${appId ? ` (${appId})` : " \u2014 no DEEPSPACE_APP_ID; run `deepspace app init`"}`
|
|
9137
9312
|
]);
|
|
9138
9313
|
json.inApp = true;
|
|
9139
9314
|
json.appName = appName;
|
|
9140
9315
|
json.appId = appId;
|
|
9316
|
+
if (wranglerEnv) json.wranglerEnv = wranglerEnv;
|
|
9141
9317
|
const deps = installState(appDir);
|
|
9142
9318
|
const depsInstallCommand = installCommand(appDir);
|
|
9143
9319
|
lines.push([
|
|
@@ -9286,12 +9462,12 @@ var status_default = defineCommand11({
|
|
|
9286
9462
|
return;
|
|
9287
9463
|
}
|
|
9288
9464
|
const width = Math.max(...lines.map(([k]) => k.length));
|
|
9289
|
-
for (const [k, v] of lines)
|
|
9465
|
+
for (const [k, v] of lines) p17.log.message(`${k.padEnd(width)} ${v}`);
|
|
9290
9466
|
}
|
|
9291
9467
|
});
|
|
9292
9468
|
|
|
9293
9469
|
// src/cli/commands/push.ts
|
|
9294
|
-
import * as
|
|
9470
|
+
import * as p18 from "@clack/prompts";
|
|
9295
9471
|
function pullRecoveryAction(cwd, appId, branch) {
|
|
9296
9472
|
return {
|
|
9297
9473
|
cwd,
|
|
@@ -9425,7 +9601,7 @@ var push_default = defineDeepspaceCommand({
|
|
|
9425
9601
|
ensureSpaceRemote(appDir, appId);
|
|
9426
9602
|
if (force) {
|
|
9427
9603
|
spinner2?.message(`Checking the cloud ${branch} tip before force-push\u2026`);
|
|
9428
|
-
const lastPushed = resolveCommit(appDir, `
|
|
9604
|
+
const lastPushed = resolveCommit(appDir, spacePrivateRef(`pushed/${branch}`));
|
|
9429
9605
|
let remoteTip = null;
|
|
9430
9606
|
try {
|
|
9431
9607
|
runGitRemote(appDir, token, [
|
|
@@ -9470,12 +9646,12 @@ var push_default = defineDeepspaceCommand({
|
|
|
9470
9646
|
);
|
|
9471
9647
|
const ok2 = result.status === "up_to_date" || result.status === "committed";
|
|
9472
9648
|
if (ok2) {
|
|
9473
|
-
updateRef(appDir, `
|
|
9649
|
+
updateRef(appDir, spacePrivateRef(`pushed/${branch}`), tipOid);
|
|
9474
9650
|
}
|
|
9475
9651
|
const recoverable = isRecoverablePushFailure(result.status);
|
|
9476
9652
|
const rejectReason = result.reason ?? result.summary;
|
|
9477
9653
|
const errorMsg = ok2 ? null : result.status === "ref_conflict" ? `The cloud repo's ${branch} moved while you worked (${rejectReason}). Run \`deepspace pull --app ${shQuote(appId)} --branch ${shQuote(branch)}\` to integrate, then push again.` : result.status === "rejected" ? `The cloud repo rejected ${branch}: ${rejectReason}.` + (OVERSIZED_PUSH_RE.test(rejectReason) ? ` ${oversizedPushFix(appDir)}` : "") : `The cloud repo's ${branch} has commit(s) your local ${branch} doesn't. Run \`deepspace pull --app ${shQuote(appId)} --branch ${shQuote(branch)}\` and merge (or rebase), then push. Avoid --force here \u2014 it's guarded and will refuse rather than silently drop a peer's commits.`;
|
|
9478
|
-
if (!args.json && ok2)
|
|
9654
|
+
if (!args.json && ok2) p18.log.info(`App: ${appId}`);
|
|
9479
9655
|
const action = recoverable ? pullRecoveryAction(pullRecoveryCwd, appId, branch) : void 0;
|
|
9480
9656
|
if (!ok2) {
|
|
9481
9657
|
throw new Refusal(errorMsg ?? `Push did not complete.`, result.status, {
|
|
@@ -9497,7 +9673,7 @@ var push_default = defineDeepspaceCommand({
|
|
|
9497
9673
|
});
|
|
9498
9674
|
|
|
9499
9675
|
// src/cli/commands/pull.ts
|
|
9500
|
-
import * as
|
|
9676
|
+
import * as p19 from "@clack/prompts";
|
|
9501
9677
|
import { realpathSync as realpathSync4 } from "fs";
|
|
9502
9678
|
function targetedVcAction(command, cwd, appId, branch) {
|
|
9503
9679
|
return {
|
|
@@ -9512,7 +9688,7 @@ function workspaceBranchPullRefusal(branch, trunkBranch) {
|
|
|
9512
9688
|
const id = workspaceIdFromBranch(branch);
|
|
9513
9689
|
if (!id) return null;
|
|
9514
9690
|
const trunk = trunkBranch || "main";
|
|
9515
|
-
const merge = `git merge
|
|
9691
|
+
const merge = `git merge ${spaceTrackingRef(shQuote(trunk))}`;
|
|
9516
9692
|
return {
|
|
9517
9693
|
code: "workspace_branch",
|
|
9518
9694
|
trunk,
|
|
@@ -9535,7 +9711,7 @@ function worktreeHoldingBranch(worktrees, branch, selfPath) {
|
|
|
9535
9711
|
}
|
|
9536
9712
|
function divergedMergeAdvice(branch, hasSelectedCheckout) {
|
|
9537
9713
|
const b = shQuote(branch);
|
|
9538
|
-
return hasSelectedCheckout ? `git merge
|
|
9714
|
+
return hasSelectedCheckout ? `git merge ${spaceTrackingRef(b)}` : `git checkout ${b} && git merge ${spaceTrackingRef(b)}`;
|
|
9539
9715
|
}
|
|
9540
9716
|
var pull_default = defineDeepspaceCommand({
|
|
9541
9717
|
meta: {
|
|
@@ -9608,11 +9784,12 @@ var pull_default = defineDeepspaceCommand({
|
|
|
9608
9784
|
const wsRefusal = workspaceBranchPullRefusal(branch, trunkBranch);
|
|
9609
9785
|
if (wsRefusal) {
|
|
9610
9786
|
const trunkRef = `refs/heads/${wsRefusal.trunk}`;
|
|
9787
|
+
const trunkTrackingRef = spaceTrackingRef(wsRefusal.trunk);
|
|
9611
9788
|
runGitRemote(appDir, token, [
|
|
9612
9789
|
"fetch",
|
|
9613
9790
|
"--quiet",
|
|
9614
9791
|
SPACE_REMOTE,
|
|
9615
|
-
`+${trunkRef}
|
|
9792
|
+
`+${trunkRef}:${trunkTrackingRef}`
|
|
9616
9793
|
]);
|
|
9617
9794
|
spinner2?.stop("Workspace branch.");
|
|
9618
9795
|
const { path: branchWorktreePath2 } = selectedBranchCheckout2();
|
|
@@ -9625,7 +9802,7 @@ var pull_default = defineDeepspaceCommand({
|
|
|
9625
9802
|
const step = branchWorktreePath2 ? selectedCheckoutClean2 ? `Merge the fetched trunk in the checkout that owns ${branch} (${branchWorktreePath2}).` : `The checkout that owns ${branch} (${branchWorktreePath2}) is dirty. Commit or stash there, then rerun \`${pullCommand2}\` from that checkout.` : localBranchExists ? canCheckoutHere2 ? `The branch is not checked out. Check it out here, then rerun \`${pullCommand2}\` to receive the merge step.` : `The branch is not checked out and this checkout is dirty. Commit or stash first (or use a clean worktree), check out ${shQuote(branch)}, then rerun \`${pullCommand2}\`.` : `This clone has not attached the workspace. Attach ${workspaceId}, then rerun \`${pullCommand2}\` from its checkout.`;
|
|
9626
9803
|
const action = branchWorktreePath2 && selectedCheckoutClean2 ? {
|
|
9627
9804
|
cwd: branchWorktreePath2,
|
|
9628
|
-
argv: ["git", "merge",
|
|
9805
|
+
argv: ["git", "merge", trunkTrackingRef]
|
|
9629
9806
|
} : canCheckoutHere2 ? { cwd: appDir, argv: ["git", "checkout", branch] } : !localBranchExists ? {
|
|
9630
9807
|
cwd: appDir,
|
|
9631
9808
|
argv: ["deepspace", "workspace", "attach", workspaceId, "--app", appId]
|
|
@@ -9649,17 +9826,18 @@ var pull_default = defineDeepspaceCommand({
|
|
|
9649
9826
|
);
|
|
9650
9827
|
}
|
|
9651
9828
|
spinner2?.message(`Fetching ${branch} from the cloud repo\u2026`);
|
|
9829
|
+
const trackingRef = spaceTrackingRef(branch);
|
|
9652
9830
|
runGitRemote(appDir, token, [
|
|
9653
9831
|
"fetch",
|
|
9654
9832
|
"--quiet",
|
|
9655
9833
|
SPACE_REMOTE,
|
|
9656
|
-
`+${refName}
|
|
9834
|
+
`+${refName}:${trackingRef}`
|
|
9657
9835
|
]);
|
|
9658
|
-
const remoteOid = resolveCommit(appDir,
|
|
9836
|
+
const remoteOid = resolveCommit(appDir, trackingRef);
|
|
9659
9837
|
if (!remoteOid) {
|
|
9660
9838
|
spinner2?.stop("Fetch failed.");
|
|
9661
9839
|
throw new Refusal(
|
|
9662
|
-
`Fetched, but
|
|
9840
|
+
`Fetched, but ${trackingRef} did not materialize \u2014 retry; if it persists, report it with \`deepspace feedback\`.`,
|
|
9663
9841
|
"fetch_incomplete"
|
|
9664
9842
|
);
|
|
9665
9843
|
}
|
|
@@ -9696,8 +9874,8 @@ var pull_default = defineDeepspaceCommand({
|
|
|
9696
9874
|
integration = "fetched_only_diverged";
|
|
9697
9875
|
}
|
|
9698
9876
|
const qBranch = shQuote(branch);
|
|
9699
|
-
const ffRef = `git merge --ff-only
|
|
9700
|
-
const mergeRef = `git merge
|
|
9877
|
+
const ffRef = `git merge --ff-only ${spaceTrackingRef(qBranch)}`;
|
|
9878
|
+
const mergeRef = `git merge ${spaceTrackingRef(qBranch)}`;
|
|
9701
9879
|
const pullCommand = targetedVcCommand("pull", appId, branch);
|
|
9702
9880
|
const pushCommand = targetedVcCommand("push", appId, branch);
|
|
9703
9881
|
const selectedCheckoutClean = branchWorktreePath !== null && isWorkTreeClean(branchWorktreePath);
|
|
@@ -9719,35 +9897,35 @@ var pull_default = defineDeepspaceCommand({
|
|
|
9719
9897
|
codeForState = "diverged";
|
|
9720
9898
|
if (branchWorktreePath && selectedCheckoutClean) {
|
|
9721
9899
|
const mergeAdvice = divergedMergeAdvice(branch, true);
|
|
9722
|
-
line = `Fetched
|
|
9900
|
+
line = `Fetched ${SPACE_REMOTE}/${branch}. ${branch} and the cloud repo diverged \u2014 run \`${mergeAdvice}\` in ${branchWorktreePath} (no network needed), then \`${pushCommand}\`.`;
|
|
9723
9901
|
recoveryAction = {
|
|
9724
9902
|
cwd: branchWorktreePath,
|
|
9725
|
-
argv: ["git", "merge",
|
|
9903
|
+
argv: ["git", "merge", trackingRef]
|
|
9726
9904
|
};
|
|
9727
9905
|
} else if (branchWorktreePath) {
|
|
9728
|
-
line = `Fetched
|
|
9906
|
+
line = `Fetched ${SPACE_REMOTE}/${branch}. ${branch} and the cloud repo diverged, but its checkout (${branchWorktreePath}) is dirty. Commit or stash there, then rerun \`${pullCommand}\` from that checkout.`;
|
|
9729
9907
|
} else if (canCheckoutHere) {
|
|
9730
|
-
line = `Fetched
|
|
9908
|
+
line = `Fetched ${SPACE_REMOTE}/${branch}. ${branch} and the cloud repo diverged, but ${branch} is not checked out. Check it out here, then rerun \`${pullCommand}\` to receive the merge step.`;
|
|
9731
9909
|
recoveryAction = { cwd: appDir, argv: ["git", "checkout", branch] };
|
|
9732
9910
|
} else {
|
|
9733
|
-
line = `Fetched
|
|
9911
|
+
line = `Fetched ${SPACE_REMOTE}/${branch}. ${branch} and the cloud repo diverged, but ${branch} is not checked out and this checkout is dirty. Commit or stash first (or use a clean worktree), check out ${qBranch}, then rerun \`${pullCommand}\`.`;
|
|
9734
9912
|
}
|
|
9735
9913
|
break;
|
|
9736
9914
|
case "fetched_only_dirty":
|
|
9737
9915
|
codeForState = "dirty_worktree";
|
|
9738
|
-
line = `Fetched
|
|
9916
|
+
line = `Fetched ${SPACE_REMOTE}/${branch}, but the worktree is dirty. Either stash \u2192 \`${ffRef}\` \u2192 \`git stash pop\`, or commit first \u2192 \`${mergeRef}\` (or \`git rebase\`).`;
|
|
9739
9917
|
break;
|
|
9740
9918
|
case "fetched_only_unborn":
|
|
9741
9919
|
codeForState = "dirty_worktree";
|
|
9742
|
-
line = `Fetched
|
|
9920
|
+
line = `Fetched ${SPACE_REMOTE}/${branch} and created ${branch}, but the worktree has uncommitted files. Preserve them in a commit or stash (including untracked files), then restore the checkout; do not run reset --hard until \`git status\` confirms nothing worth keeping remains.`;
|
|
9743
9921
|
break;
|
|
9744
9922
|
case "fetched_only_worktree":
|
|
9745
9923
|
codeForState = "branch_in_worktree";
|
|
9746
9924
|
if (branchWorktreePath && selectedCheckoutClean) {
|
|
9747
|
-
line = `Fetched
|
|
9925
|
+
line = `Fetched ${SPACE_REMOTE}/${branch}, but ${branch} is checked out in another worktree (${branchWorktreePath}) \u2014 its ref was left untouched to keep that worktree in sync. Run \`${pullCommand}\` from there.`;
|
|
9748
9926
|
recoveryAction = targetedVcAction("pull", branchWorktreePath, appId, branch);
|
|
9749
9927
|
} else {
|
|
9750
|
-
line = `Fetched
|
|
9928
|
+
line = `Fetched ${SPACE_REMOTE}/${branch}, but its checkout (${branchWorktreePath ?? "a linked worktree"}) is dirty, so the ref and worktree were left untouched. Commit or stash there, then run \`${pullCommand}\` from that checkout.`;
|
|
9751
9929
|
}
|
|
9752
9930
|
break;
|
|
9753
9931
|
default: {
|
|
@@ -9756,8 +9934,8 @@ var pull_default = defineDeepspaceCommand({
|
|
|
9756
9934
|
}
|
|
9757
9935
|
}
|
|
9758
9936
|
const actionRequired = recoveryAction !== void 0;
|
|
9759
|
-
spinner2?.stop(codeForState ? `Fetched
|
|
9760
|
-
if (!args.json)
|
|
9937
|
+
spinner2?.stop(codeForState ? `Fetched ${SPACE_REMOTE}/${branch}.` : line);
|
|
9938
|
+
if (!args.json) p19.log.info(`App: ${appId} \xB7 ${SPACE_REMOTE}/${branch} at ${remoteOid.slice(0, 10)}`);
|
|
9761
9939
|
if (codeForState) {
|
|
9762
9940
|
throw new Refusal(line, codeForState, {
|
|
9763
9941
|
actionRequired,
|
|
@@ -9776,9 +9954,9 @@ var pull_default = defineDeepspaceCommand({
|
|
|
9776
9954
|
});
|
|
9777
9955
|
|
|
9778
9956
|
// src/cli/commands/clone.ts
|
|
9779
|
-
import * as
|
|
9957
|
+
import * as p20 from "@clack/prompts";
|
|
9780
9958
|
import { existsSync as existsSync23, readdirSync as readdirSync6, rmSync as rmSync5 } from "fs";
|
|
9781
|
-
import { resolve as
|
|
9959
|
+
import { resolve as resolve15, basename as basename3, join as join21 } from "path";
|
|
9782
9960
|
function cleanFailedCloneDir(dir) {
|
|
9783
9961
|
let entries;
|
|
9784
9962
|
try {
|
|
@@ -9832,7 +10010,7 @@ var clone_default = defineDeepspaceCommand({
|
|
|
9832
10010
|
async run({ args }) {
|
|
9833
10011
|
assertAppTargetResolvable(String(args.app));
|
|
9834
10012
|
const selector = String(args.app).trim();
|
|
9835
|
-
const dir =
|
|
10013
|
+
const dir = resolve15(String(args.dir ?? selector));
|
|
9836
10014
|
const dirPreexisted = validateCloneTarget(dir);
|
|
9837
10015
|
const spinner2 = args.json ? null : createSpinner();
|
|
9838
10016
|
spinner2?.start(`Resolving ${selector}\u2026`);
|
|
@@ -9878,7 +10056,7 @@ var clone_default = defineDeepspaceCommand({
|
|
|
9878
10056
|
`Cloned ${selector} (${branches.length} ${branches.length === 1 ? "branch" : "branches"}) into ${basename3(dir)}/ at ${head?.slice(0, 10) ?? "(unborn)"}.`
|
|
9879
10057
|
);
|
|
9880
10058
|
if (!args.json && !branch) {
|
|
9881
|
-
|
|
10059
|
+
p20.log.warn("HEAD is detached \u2014 check `git branch -a`.");
|
|
9882
10060
|
}
|
|
9883
10061
|
return {
|
|
9884
10062
|
data: {
|
|
@@ -9897,7 +10075,7 @@ var clone_default = defineDeepspaceCommand({
|
|
|
9897
10075
|
});
|
|
9898
10076
|
|
|
9899
10077
|
// src/cli/commands/releases.ts
|
|
9900
|
-
import * as
|
|
10078
|
+
import * as p21 from "@clack/prompts";
|
|
9901
10079
|
var DEPLOY_URL11 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9902
10080
|
var releases_default = defineDeepspaceCommand({
|
|
9903
10081
|
meta: {
|
|
@@ -9911,6 +10089,12 @@ var releases_default = defineDeepspaceCommand({
|
|
|
9911
10089
|
description: "App id or subdomain name (default: the surrounding app directory)",
|
|
9912
10090
|
required: false
|
|
9913
10091
|
},
|
|
10092
|
+
env: {
|
|
10093
|
+
type: "string",
|
|
10094
|
+
alias: "e",
|
|
10095
|
+
description: "wrangler.toml [env.<name>] slot \u2014 selects that environment's app id",
|
|
10096
|
+
required: false
|
|
10097
|
+
},
|
|
9914
10098
|
limit: {
|
|
9915
10099
|
type: "string",
|
|
9916
10100
|
description: "Max entries (default 20)",
|
|
@@ -9919,18 +10103,20 @@ var releases_default = defineDeepspaceCommand({
|
|
|
9919
10103
|
},
|
|
9920
10104
|
async run({ args }) {
|
|
9921
10105
|
const appArg = args.app;
|
|
10106
|
+
const envArg = args.env;
|
|
9922
10107
|
const { limit, error: limitError } = parseLimitArg(args.limit);
|
|
9923
10108
|
if (limitError) throw new Refusal(limitError, "invalid_limit");
|
|
9924
|
-
assertAppTargetResolvable(appArg);
|
|
10109
|
+
assertAppTargetResolvable(appArg, { wranglerEnv: envArg });
|
|
10110
|
+
const { wranglerEnv } = parseWranglerEnvArg(envArg);
|
|
9925
10111
|
const spinner2 = args.json ? null : createSpinner();
|
|
9926
10112
|
spinner2?.start("Loading release history\u2026");
|
|
9927
10113
|
const token = await ensureToken();
|
|
9928
|
-
const appId = await resolveAppTarget(DEPLOY_URL11, token, appArg);
|
|
10114
|
+
const appId = await resolveAppTarget(DEPLOY_URL11, token, appArg, { wranglerEnv: envArg });
|
|
9929
10115
|
const { releases } = await repoApi(DEPLOY_URL11, token, appId).listReleases(limit);
|
|
9930
10116
|
spinner2?.stop(`Loaded ${releases.length} ${releases.length === 1 ? "release" : "releases"}.`);
|
|
9931
10117
|
if (!args.json) {
|
|
9932
10118
|
if (releases.length === 0) {
|
|
9933
|
-
|
|
10119
|
+
p21.log.info("No releases yet \u2014 the history starts with the next `deepspace deploy`.");
|
|
9934
10120
|
} else {
|
|
9935
10121
|
for (const r of releases) {
|
|
9936
10122
|
const source = r.commitOid ? `commit ${r.commitOid.slice(0, 10)}` : "(no source recorded)";
|
|
@@ -9939,15 +10125,17 @@ var releases_default = defineDeepspaceCommand({
|
|
|
9939
10125
|
`#${r.seq} ${r.id} ${r.kind.padEnd(8)} ${r.createdAt} ${r.actor} ${source} ${rollback}`
|
|
9940
10126
|
);
|
|
9941
10127
|
}
|
|
9942
|
-
|
|
10128
|
+
p21.log.info(
|
|
10129
|
+
`Roll back an available release with: deepspace rollback <release-id>${wranglerEnv ? ` --env ${wranglerEnv}` : ""}`
|
|
10130
|
+
);
|
|
9943
10131
|
}
|
|
9944
10132
|
}
|
|
9945
|
-
return { data: { appId, releases } };
|
|
10133
|
+
return { data: { appId, wranglerEnv: wranglerEnv ?? null, releases } };
|
|
9946
10134
|
}
|
|
9947
10135
|
});
|
|
9948
10136
|
|
|
9949
10137
|
// src/cli/commands/rollback.ts
|
|
9950
|
-
import * as
|
|
10138
|
+
import * as p22 from "@clack/prompts";
|
|
9951
10139
|
var REL_ID_RE = /^rel_[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
9952
10140
|
function unavailableDoGuardRefusal(message) {
|
|
9953
10141
|
return new Refusal(
|
|
@@ -9986,6 +10174,12 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
9986
10174
|
description: "App id or subdomain name (default: the surrounding app directory)",
|
|
9987
10175
|
required: false
|
|
9988
10176
|
},
|
|
10177
|
+
env: {
|
|
10178
|
+
type: "string",
|
|
10179
|
+
alias: "e",
|
|
10180
|
+
description: "wrangler.toml [env.<name>] slot \u2014 selects that environment's app id",
|
|
10181
|
+
required: false
|
|
10182
|
+
},
|
|
9989
10183
|
"allow-do-deletion": {
|
|
9990
10184
|
type: "boolean",
|
|
9991
10185
|
description: "Proceed even though the target release declares fewer Durable Object classes than the current one \u2014 THEIR STORED DATA IS DELETED.",
|
|
@@ -9994,6 +10188,7 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
9994
10188
|
},
|
|
9995
10189
|
async run({ args }) {
|
|
9996
10190
|
const appArg = args.app;
|
|
10191
|
+
const envArg = args.env;
|
|
9997
10192
|
if (args.release !== void 0) {
|
|
9998
10193
|
const r = String(args.release).trim();
|
|
9999
10194
|
if (!r) {
|
|
@@ -10006,11 +10201,12 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
10006
10201
|
throw new Refusal(`Invalid release id: ${r} (expected rel_<ULID>).`, "invalid_release");
|
|
10007
10202
|
}
|
|
10008
10203
|
}
|
|
10009
|
-
assertAppTargetResolvable(appArg);
|
|
10204
|
+
assertAppTargetResolvable(appArg, { wranglerEnv: envArg });
|
|
10205
|
+
const { wranglerEnv } = parseWranglerEnvArg(envArg);
|
|
10010
10206
|
const spinner2 = args.json ? null : createSpinner();
|
|
10011
10207
|
spinner2?.start("Preparing rollback\u2026");
|
|
10012
10208
|
const token = await ensureToken();
|
|
10013
|
-
const appId = await resolveAppTarget(DEPLOY_URL12, token, appArg);
|
|
10209
|
+
const appId = await resolveAppTarget(DEPLOY_URL12, token, appArg, { wranglerEnv: envArg });
|
|
10014
10210
|
const api4 = repoApi(DEPLOY_URL12, token, appId);
|
|
10015
10211
|
let releaseId = args.release ? String(args.release).trim() : "";
|
|
10016
10212
|
if (!releaseId) {
|
|
@@ -10052,18 +10248,18 @@ Re-run with --allow-do-deletion to accept the data loss.`,
|
|
|
10052
10248
|
spinner2?.stop(`Rolled back to ${result.rolledBackTo ?? releaseId}.`);
|
|
10053
10249
|
const doClassGuard = result.doClassGuard === "unverified" ? "unverified" : void 0;
|
|
10054
10250
|
if (!args.json) {
|
|
10055
|
-
if (result.url)
|
|
10251
|
+
if (result.url) p22.log.success(`Live at: ${result.url}`);
|
|
10056
10252
|
if (result.bundleRetained === false) {
|
|
10057
|
-
|
|
10253
|
+
p22.log.warn(
|
|
10058
10254
|
"The rollback is live, but its bundle was concurrently retired and is not available for a future rollback."
|
|
10059
10255
|
);
|
|
10060
10256
|
}
|
|
10061
10257
|
if (doClassGuard === "unverified") {
|
|
10062
|
-
|
|
10258
|
+
p22.log.warn(
|
|
10063
10259
|
"DO-class guard bypassed (--allow-do-deletion): this rollback was NOT checked against the live Durable Object classes \u2014 any class the target doesn't declare has been deleted with its data."
|
|
10064
10260
|
);
|
|
10065
10261
|
}
|
|
10066
|
-
|
|
10262
|
+
p22.log.info(
|
|
10067
10263
|
"This rollback is itself the newest release \u2014 `deepspace releases` shows the full history."
|
|
10068
10264
|
);
|
|
10069
10265
|
}
|
|
@@ -10075,6 +10271,7 @@ Re-run with --allow-do-deletion to accept the data loss.`,
|
|
|
10075
10271
|
bundleRetained: result.bundleRetained ?? null,
|
|
10076
10272
|
url: result.url ?? null,
|
|
10077
10273
|
versionId: result.versionId ?? null,
|
|
10274
|
+
wranglerEnv: wranglerEnv ?? null,
|
|
10078
10275
|
...doClassGuard ? { doClassGuard } : {}
|
|
10079
10276
|
}
|
|
10080
10277
|
};
|
|
@@ -10082,16 +10279,16 @@ Re-run with --allow-do-deletion to accept the data loss.`,
|
|
|
10082
10279
|
});
|
|
10083
10280
|
|
|
10084
10281
|
// src/cli/commands/workspace.ts
|
|
10085
|
-
import { defineCommand as
|
|
10282
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
10086
10283
|
|
|
10087
10284
|
// src/cli/commands/workspace/new.ts
|
|
10088
|
-
import * as
|
|
10089
|
-
import { isAbsolute as isAbsolute6, resolve as
|
|
10285
|
+
import * as p25 from "@clack/prompts";
|
|
10286
|
+
import { isAbsolute as isAbsolute6, resolve as resolve16 } from "path";
|
|
10090
10287
|
|
|
10091
10288
|
// src/cli/commands/workspace/analysis.ts
|
|
10092
|
-
import * as
|
|
10289
|
+
import * as p23 from "@clack/prompts";
|
|
10093
10290
|
var MAX_OVERLAP_PATHS = 20;
|
|
10094
|
-
var PEER_REF_PREFIX = "
|
|
10291
|
+
var PEER_REF_PREFIX = `${spacePrivateRef("peers")}/`;
|
|
10095
10292
|
function peerWorkspaceRef(workspaceId) {
|
|
10096
10293
|
return `${PEER_REF_PREFIX}${workspaceId}`;
|
|
10097
10294
|
}
|
|
@@ -10102,7 +10299,7 @@ function formatCount(count) {
|
|
|
10102
10299
|
function fetchTrunk(appDir, token, head) {
|
|
10103
10300
|
if (!head?.startsWith("refs/heads/")) return null;
|
|
10104
10301
|
const branch = head.slice("refs/heads/".length);
|
|
10105
|
-
const remoteRef =
|
|
10302
|
+
const remoteRef = spaceTrackingRef(branch);
|
|
10106
10303
|
runGitRemote(appDir, token, ["fetch", "--quiet", SPACE_REMOTE, `+${head}:${remoteRef}`]);
|
|
10107
10304
|
const oid = resolveCommit(appDir, remoteRef);
|
|
10108
10305
|
return oid ? { branch, ref: remoteRef, oid } : null;
|
|
@@ -10112,7 +10309,7 @@ function localTrunkOid(appDir, refs) {
|
|
|
10112
10309
|
if (!head?.startsWith("refs/heads/")) return null;
|
|
10113
10310
|
const advertised = refs?.refs.find((ref) => ref.name === head)?.oid ?? null;
|
|
10114
10311
|
if (advertised && resolveCommit(appDir, advertised)) return advertised;
|
|
10115
|
-
return resolveCommit(appDir,
|
|
10312
|
+
return resolveCommit(appDir, spaceTrackingRef(head.slice("refs/heads/".length)));
|
|
10116
10313
|
}
|
|
10117
10314
|
function fetchPeerWorkspaceRefs(appDir, token) {
|
|
10118
10315
|
try {
|
|
@@ -10203,15 +10400,15 @@ function printOverlaps(overlaps, json) {
|
|
|
10203
10400
|
for (const overlap of overlaps) {
|
|
10204
10401
|
const sample = overlap.paths.slice(0, 5).join(", ");
|
|
10205
10402
|
const more = overlap.paths.length - 5 + overlap.morePaths;
|
|
10206
|
-
|
|
10403
|
+
p23.log.warn(
|
|
10207
10404
|
`Overlap: ${overlap.workspaceId} ("${overlap.task}") also modified ${sample}${more > 0 ? ` (+${more} more)` : ""} \u2014 coordinate before landing.`
|
|
10208
10405
|
);
|
|
10209
10406
|
}
|
|
10210
10407
|
}
|
|
10211
10408
|
|
|
10212
10409
|
// src/cli/commands/workspace/local.ts
|
|
10213
|
-
import * as
|
|
10214
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
10410
|
+
import * as p24 from "@clack/prompts";
|
|
10411
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync25, realpathSync as realpathSync5, renameSync as renameSync2, statSync, writeFileSync as writeFileSync9 } from "fs";
|
|
10215
10412
|
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join22, relative as relative2, sep as sep2 } from "path";
|
|
10216
10413
|
var WORKSPACE_MARKER = "deepspace-workspace.json";
|
|
10217
10414
|
function excludeWorktreeDir(appDir, dir) {
|
|
@@ -10269,7 +10466,7 @@ function isManagedWorkspaceWorktree(worktreeRoot, workspaceId) {
|
|
|
10269
10466
|
const markerPath = worktreeMarkerPath(worktreeRoot);
|
|
10270
10467
|
if (!markerPath) return false;
|
|
10271
10468
|
try {
|
|
10272
|
-
const marker = JSON.parse(
|
|
10469
|
+
const marker = JSON.parse(readFileSync25(markerPath, "utf-8"));
|
|
10273
10470
|
return marker.version === 1 && marker.workspaceId === workspaceId && marker.mode === "managed-linked";
|
|
10274
10471
|
} catch {
|
|
10275
10472
|
return false;
|
|
@@ -10371,7 +10568,7 @@ function switchOffWsBranch(mainDir, trunkBranch) {
|
|
|
10371
10568
|
if (resolveCommit(mainDir, `refs/heads/${trunkBranch}`)) {
|
|
10372
10569
|
return runGit(mainDir, ["switch", "--quiet", trunkBranch], { allowFail: true }).status === 0;
|
|
10373
10570
|
}
|
|
10374
|
-
const remote = resolveCommit(mainDir,
|
|
10571
|
+
const remote = resolveCommit(mainDir, spaceTrackingRef(trunkBranch));
|
|
10375
10572
|
if (remote) {
|
|
10376
10573
|
return runGit(mainDir, ["switch", "--quiet", "-c", trunkBranch, remote], { allowFail: true }).status === 0;
|
|
10377
10574
|
}
|
|
@@ -10446,7 +10643,7 @@ function cleanupAction(outcome) {
|
|
|
10446
10643
|
}
|
|
10447
10644
|
function reportCleanupHuman(outcome) {
|
|
10448
10645
|
if (outcome.error) {
|
|
10449
|
-
|
|
10646
|
+
p24.log.warn(
|
|
10450
10647
|
cleanupRefusalMessage({
|
|
10451
10648
|
error: outcome.error,
|
|
10452
10649
|
worktreeDir: outcome.worktreeDir,
|
|
@@ -10459,12 +10656,12 @@ function reportCleanupHuman(outcome) {
|
|
|
10459
10656
|
outcome.worktreeRemoved ? "worktree" : null,
|
|
10460
10657
|
outcome.branchDeleted ? `branch ${outcome.branch}` : null
|
|
10461
10658
|
].filter(Boolean);
|
|
10462
|
-
if (parts.length)
|
|
10659
|
+
if (parts.length) p24.log.success(`Removed ${parts.join(" + ")}.`);
|
|
10463
10660
|
if (outcome.worktreeRetained) {
|
|
10464
|
-
|
|
10661
|
+
p24.log.info(`Retained externally managed worktree: ${outcome.worktreeRetained}`);
|
|
10465
10662
|
}
|
|
10466
10663
|
if (outcome.worktreeRemoved && outcome.mainDir) {
|
|
10467
|
-
|
|
10664
|
+
p24.log.info(`This directory was removed \u2014 run: cd ${shQuote(outcome.mainDir)}`);
|
|
10468
10665
|
}
|
|
10469
10666
|
}
|
|
10470
10667
|
|
|
@@ -10646,7 +10843,7 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10646
10843
|
idempotencyKey: mintIdempotencyKey()
|
|
10647
10844
|
});
|
|
10648
10845
|
const dirInput = typeof args.dir === "string" ? args.dir.trim() : "";
|
|
10649
|
-
const worktreeRoot = dirInput ? isAbsolute6(dirInput) ? dirInput :
|
|
10846
|
+
const worktreeRoot = dirInput ? isAbsolute6(dirInput) ? dirInput : resolve16(appDir, dirInput) : defaultWorkspaceRoot(appDir, id);
|
|
10650
10847
|
const dir = appDirInWorktree(appDir, worktreeRoot);
|
|
10651
10848
|
try {
|
|
10652
10849
|
materializeWorkspaceWorktree(appDir, worktreeRoot, branch, baseOid, id);
|
|
@@ -10661,8 +10858,8 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10661
10858
|
}
|
|
10662
10859
|
spinner2?.stop(`Workspace ${id} created.`);
|
|
10663
10860
|
if (!args.json) {
|
|
10664
|
-
|
|
10665
|
-
|
|
10861
|
+
p25.log.info(`Work in: cd ${dir}`);
|
|
10862
|
+
p25.log.info(
|
|
10666
10863
|
`Then: commit as usual, \`deepspace workspace sync\` to publish, \`deepspace workspace land\` when done.`
|
|
10667
10864
|
);
|
|
10668
10865
|
}
|
|
@@ -10683,9 +10880,9 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10683
10880
|
});
|
|
10684
10881
|
|
|
10685
10882
|
// src/cli/commands/workspace/attach.ts
|
|
10686
|
-
import * as
|
|
10883
|
+
import * as p26 from "@clack/prompts";
|
|
10687
10884
|
import { existsSync as existsSync24, readdirSync as readdirSync7, rmSync as rmSync6 } from "fs";
|
|
10688
|
-
import { isAbsolute as isAbsolute7, resolve as
|
|
10885
|
+
import { isAbsolute as isAbsolute7, resolve as resolve17 } from "path";
|
|
10689
10886
|
function finishedWorkspaceMessage(id, view) {
|
|
10690
10887
|
const workspace = view.workspace;
|
|
10691
10888
|
if (workspace.status === "landed") {
|
|
@@ -10754,11 +10951,12 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10754
10951
|
});
|
|
10755
10952
|
}
|
|
10756
10953
|
ensureSpaceRemote(appDir, appId2);
|
|
10954
|
+
const localWorkspaceRef = spacePrivateRef(`workspaces/${id}`);
|
|
10757
10955
|
runGitRemote(appDir, token2, [
|
|
10758
10956
|
"fetch",
|
|
10759
10957
|
"--quiet",
|
|
10760
10958
|
SPACE_REMOTE,
|
|
10761
|
-
`+${view2.workspace.ref}:${
|
|
10959
|
+
`+${view2.workspace.ref}:${localWorkspaceRef}`
|
|
10762
10960
|
]);
|
|
10763
10961
|
const tip2 = view2.tipOid ?? view2.workspace.baseOid;
|
|
10764
10962
|
if (!resolveCommit(appDir, tip2)) {
|
|
@@ -10769,7 +10967,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10769
10967
|
);
|
|
10770
10968
|
}
|
|
10771
10969
|
const requestedDir = typeof args.dir === "string" ? args.dir.trim() : "";
|
|
10772
|
-
const worktreeRoot = requestedDir ? isAbsolute7(requestedDir) ? requestedDir :
|
|
10970
|
+
const worktreeRoot = requestedDir ? isAbsolute7(requestedDir) ? requestedDir : resolve17(appDir, requestedDir) : defaultWorkspaceRoot(appDir, id);
|
|
10773
10971
|
const dir2 = appDirInWorktree(appDir, worktreeRoot);
|
|
10774
10972
|
if (resolveCommit(appDir, `refs/heads/${branch}`)) {
|
|
10775
10973
|
throw new Refusal(
|
|
@@ -10780,8 +10978,8 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10780
10978
|
materializeWorkspaceWorktree(appDir, worktreeRoot, branch, tip2, id);
|
|
10781
10979
|
spinner2?.stop(`Attached ${id}.`);
|
|
10782
10980
|
if (!args.json) {
|
|
10783
|
-
|
|
10784
|
-
|
|
10981
|
+
p26.log.info(`Task: ${view2.workspace.task}`);
|
|
10982
|
+
p26.log.info(`Work in: cd ${dir2}`);
|
|
10785
10983
|
}
|
|
10786
10984
|
const action2 = { cwd: dir2, argv: [detectPackageManager(dir2), "install"] };
|
|
10787
10985
|
return {
|
|
@@ -10808,7 +11006,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10808
11006
|
extra: { status: view.workspace.status }
|
|
10809
11007
|
});
|
|
10810
11008
|
}
|
|
10811
|
-
const dir =
|
|
11009
|
+
const dir = resolve17(
|
|
10812
11010
|
process.cwd(),
|
|
10813
11011
|
(typeof args.dir === "string" ? args.dir.trim() : "") || `ws-${id.slice(3).toLowerCase()}`
|
|
10814
11012
|
);
|
|
@@ -10827,11 +11025,12 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10827
11025
|
try {
|
|
10828
11026
|
runGit(process.cwd(), ["init", "--quiet", dir]);
|
|
10829
11027
|
ensureSpaceRemote(dir, appId);
|
|
11028
|
+
const localWorkspaceRef = spacePrivateRef(`workspaces/${id}`);
|
|
10830
11029
|
runGitRemote(dir, token, [
|
|
10831
11030
|
"fetch",
|
|
10832
11031
|
"--quiet",
|
|
10833
11032
|
SPACE_REMOTE,
|
|
10834
|
-
`+${view.workspace.ref}:${
|
|
11033
|
+
`+${view.workspace.ref}:${localWorkspaceRef}`
|
|
10835
11034
|
]);
|
|
10836
11035
|
tip = view.tipOid ?? view.workspace.baseOid;
|
|
10837
11036
|
if (!resolveCommit(dir, tip)) {
|
|
@@ -10852,8 +11051,8 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10852
11051
|
spinner2?.stop(`Attached ${id}.`);
|
|
10853
11052
|
const action = { cwd: dir, argv: [detectPackageManager(dir), "install"] };
|
|
10854
11053
|
if (!args.json) {
|
|
10855
|
-
|
|
10856
|
-
|
|
11054
|
+
p26.log.info(`Task: ${view.workspace.task}`);
|
|
11055
|
+
p26.log.info("Pull app secrets after install if this workspace needs them.");
|
|
10857
11056
|
}
|
|
10858
11057
|
return {
|
|
10859
11058
|
data: {
|
|
@@ -10870,7 +11069,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10870
11069
|
});
|
|
10871
11070
|
|
|
10872
11071
|
// src/cli/commands/workspace/sync.ts
|
|
10873
|
-
import * as
|
|
11072
|
+
import * as p27 from "@clack/prompts";
|
|
10874
11073
|
var syncWorkspaceCommand = defineDeepspaceCommand({
|
|
10875
11074
|
meta: {
|
|
10876
11075
|
name: "sync",
|
|
@@ -10931,12 +11130,12 @@ var syncWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10931
11130
|
);
|
|
10932
11131
|
printOverlaps(overlaps, args.json);
|
|
10933
11132
|
if (!args.json && trunkOverlap.length > 0) {
|
|
10934
|
-
|
|
11133
|
+
p27.log.warn(
|
|
10935
11134
|
`Trunk already changed ${trunkOverlap.length} file(s) you also touch \u2014 landed work you haven't merged: ${trunkOverlap.slice(0, 5).join(", ")}${trunkOverlap.length > 5 ? ` (+${trunkOverlap.length - 5} more)` : ""}. Merge trunk before landing (\`deepspace workspace status\` shows the full picture).`
|
|
10936
11135
|
);
|
|
10937
11136
|
}
|
|
10938
11137
|
if (!args.json && dirty.length > 0) {
|
|
10939
|
-
|
|
11138
|
+
p27.log.warn(
|
|
10940
11139
|
`${dirty.length} uncommitted ${dirty.length === 1 ? "file" : "files"} NOT synced \u2014 commit them to this workspace branch (WIP commits are fine \u2014 they land with the merge).`
|
|
10941
11140
|
);
|
|
10942
11141
|
}
|
|
@@ -10956,7 +11155,7 @@ var syncWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10956
11155
|
});
|
|
10957
11156
|
|
|
10958
11157
|
// src/cli/commands/workspace/list.ts
|
|
10959
|
-
import * as
|
|
11158
|
+
import * as p28 from "@clack/prompts";
|
|
10960
11159
|
|
|
10961
11160
|
// src/cli/lib/actor-labels.ts
|
|
10962
11161
|
var API_URL9 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
@@ -11004,7 +11203,7 @@ var listWorkspacesCommand = defineDeepspaceCommand({
|
|
|
11004
11203
|
if (views.length === 0) {
|
|
11005
11204
|
spinner2?.stop("No workspaces found.");
|
|
11006
11205
|
if (!args.json) {
|
|
11007
|
-
|
|
11206
|
+
p28.log.info(
|
|
11008
11207
|
all ? "No workspaces." : 'No active workspaces. Create one with `deepspace workspace new -t "\u2026"`.'
|
|
11009
11208
|
);
|
|
11010
11209
|
}
|
|
@@ -11033,13 +11232,13 @@ var listWorkspacesCommand = defineDeepspaceCommand({
|
|
|
11033
11232
|
);
|
|
11034
11233
|
}
|
|
11035
11234
|
}
|
|
11036
|
-
|
|
11235
|
+
p28.log.info("\u2191 commits ahead of base \xB7 \u2193 trunk commits since base (staleness)");
|
|
11037
11236
|
return { data: { workspaces } };
|
|
11038
11237
|
}
|
|
11039
11238
|
});
|
|
11040
11239
|
|
|
11041
11240
|
// src/cli/commands/workspace/status.ts
|
|
11042
|
-
import * as
|
|
11241
|
+
import * as p29 from "@clack/prompts";
|
|
11043
11242
|
var workspaceStatusCommand = defineDeepspaceCommand({
|
|
11044
11243
|
meta: { name: "status", description: "This workspace: base staleness, overlaps, sync state" },
|
|
11045
11244
|
args: {
|
|
@@ -11099,22 +11298,22 @@ var workspaceStatusCommand = defineDeepspaceCommand({
|
|
|
11099
11298
|
overlaps
|
|
11100
11299
|
};
|
|
11101
11300
|
if (!args.json) {
|
|
11102
|
-
|
|
11103
|
-
|
|
11301
|
+
p29.log.info(`Task: ${workspace.task}`);
|
|
11302
|
+
p29.log.info(
|
|
11104
11303
|
`Base: ${workspace.baseOid.slice(0, 10)} \xB7 ahead ${aheadExact ?? "?"} commit(s) \xB7 trunk moved ${behindExact ?? "?"} commit(s) since your base`
|
|
11105
11304
|
);
|
|
11106
11305
|
if (!attached) {
|
|
11107
|
-
|
|
11306
|
+
p29.log.info(
|
|
11108
11307
|
`Not checked out in this clone \u2014 showing the published state (local HEAD/uncommitted unavailable). Attach it with \`deepspace workspace attach ${id} <dir>\`.`
|
|
11109
11308
|
);
|
|
11110
11309
|
}
|
|
11111
11310
|
if (unsynced)
|
|
11112
|
-
|
|
11311
|
+
p29.log.warn(
|
|
11113
11312
|
`Local HEAD ${headOid?.slice(0, 10)} is not synced (server has ${view.tipOid?.slice(0, 10) ?? "nothing"}) \u2014 run \`deepspace workspace sync\`.`
|
|
11114
11313
|
);
|
|
11115
|
-
if (dirty.length > 0)
|
|
11314
|
+
if (dirty.length > 0) p29.log.warn(`${dirty.length} uncommitted file(s) in the worktree.`);
|
|
11116
11315
|
if (trunkOverlap.length > 0) {
|
|
11117
|
-
|
|
11316
|
+
p29.log.warn(
|
|
11118
11317
|
`Trunk also modified: ${trunkOverlap.slice(0, 5).join(", ")}${trunkOverlap.length > 5 ? ` (+${trunkOverlap.length - 5} more)` : ""} \u2014 expect merge attention at land time.`
|
|
11119
11318
|
);
|
|
11120
11319
|
}
|
|
@@ -11125,17 +11324,17 @@ var workspaceStatusCommand = defineDeepspaceCommand({
|
|
|
11125
11324
|
});
|
|
11126
11325
|
|
|
11127
11326
|
// src/cli/commands/workspace/land.ts
|
|
11128
|
-
import * as
|
|
11327
|
+
import * as p30 from "@clack/prompts";
|
|
11129
11328
|
|
|
11130
11329
|
// src/cli/lib/validation.ts
|
|
11131
11330
|
import { sync as spawnSync7 } from "cross-spawn";
|
|
11132
|
-
import { readFileSync as
|
|
11331
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
11133
11332
|
import { join as join23 } from "path";
|
|
11134
11333
|
var MAX_SUMMARY_CHARS = 4e3;
|
|
11135
11334
|
var MAX_OUTPUT_BUFFER = 64 * 1024 * 1024;
|
|
11136
11335
|
function configuredCommand(appDir) {
|
|
11137
11336
|
try {
|
|
11138
|
-
const pkg2 = JSON.parse(
|
|
11337
|
+
const pkg2 = JSON.parse(readFileSync26(join23(appDir, "package.json"), "utf-8"));
|
|
11139
11338
|
if (pkg2.scripts?.validate) return `${detectPackageManager(appDir)} run --silent validate`;
|
|
11140
11339
|
} catch {
|
|
11141
11340
|
}
|
|
@@ -11266,7 +11465,7 @@ var landWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11266
11465
|
);
|
|
11267
11466
|
}
|
|
11268
11467
|
const intoRef = `refs/heads/${intoBranch}`;
|
|
11269
|
-
const remoteRef =
|
|
11468
|
+
const remoteRef = spaceTrackingRef(intoBranch);
|
|
11270
11469
|
runGitRemote(appDir, token, ["fetch", "--quiet", SPACE_REMOTE, `+${intoRef}:${remoteRef}`], {
|
|
11271
11470
|
allowFail: true
|
|
11272
11471
|
});
|
|
@@ -11411,7 +11610,7 @@ var landWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11411
11610
|
if (!args.json && cleanup && !cleanup.error) {
|
|
11412
11611
|
reportCleanupHuman(cleanup);
|
|
11413
11612
|
} else if (!args.json && retainedWorktree) {
|
|
11414
|
-
|
|
11613
|
+
p30.log.info(`Retained checkout: ${retainedWorktree}`);
|
|
11415
11614
|
}
|
|
11416
11615
|
if (cleanup?.error) {
|
|
11417
11616
|
const action = cleanupAction({
|
|
@@ -11433,7 +11632,7 @@ var landWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11433
11632
|
});
|
|
11434
11633
|
|
|
11435
11634
|
// src/cli/commands/workspace/drop.ts
|
|
11436
|
-
import * as
|
|
11635
|
+
import * as p31 from "@clack/prompts";
|
|
11437
11636
|
function isWorkspaceTipPublished(appDir, localTip, publishedOids) {
|
|
11438
11637
|
return publishedOids.some(
|
|
11439
11638
|
(publishedOid) => publishedOid === localTip || isAncestor(appDir, localTip, publishedOid)
|
|
@@ -11442,9 +11641,9 @@ function isWorkspaceTipPublished(appDir, localTip, publishedOids) {
|
|
|
11442
11641
|
function workspaceUnsyncedRefusal(args) {
|
|
11443
11642
|
const syncArgv = ["deepspace", "workspace", "sync", "--app", args.appId, "--workspace", args.id];
|
|
11444
11643
|
const dropArgv = ["deepspace", "workspace", "drop", args.id, "--app", args.appId];
|
|
11445
|
-
const
|
|
11644
|
+
const publish = args.workspaceDir ? `Publish them first with \`${humanCommand(syncArgv)}\`, then re-run \`${humanCommand(dropArgv)}\`.` : `Recreate a worktree from the branch (\`git worktree add <dir> ${args.branch}\`), publish from there with \`${humanCommand(syncArgv)}\`, then re-run \`${humanCommand(dropArgv)}\`.`;
|
|
11446
11645
|
return new Refusal(
|
|
11447
|
-
`${args.id} has committed work that isn't published yet. ${
|
|
11646
|
+
`${args.id} has committed work that isn't published yet. ${publish} Alternatively, pass \`--keep-worktree\` to drop the cloud workspace while retaining the local branch/worktree for manual disposal. Nothing was dropped.`,
|
|
11448
11647
|
"workspace_unsynced",
|
|
11449
11648
|
{
|
|
11450
11649
|
...args.workspaceDir ? { action: { cwd: args.workspaceDir, argv: syncArgv } } : {},
|
|
@@ -11479,7 +11678,7 @@ var dropWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11479
11678
|
const { view: view2 } = await api5.dropWorkspace(explicitId);
|
|
11480
11679
|
spinner2?.stop();
|
|
11481
11680
|
if (!args.json) {
|
|
11482
|
-
|
|
11681
|
+
p31.log.success(
|
|
11483
11682
|
`Dropped ${explicitId}. Its ref sticks around briefly for undo, then is reaped.`
|
|
11484
11683
|
);
|
|
11485
11684
|
}
|
|
@@ -11531,9 +11730,9 @@ var dropWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11531
11730
|
};
|
|
11532
11731
|
spinner2?.stop();
|
|
11533
11732
|
if (!args.json) {
|
|
11534
|
-
|
|
11733
|
+
p31.log.success(`Dropped ${id}. Its ref sticks around briefly for undo, then is reaped.`);
|
|
11535
11734
|
if (cleanup && !cleanup.error) reportCleanupHuman(cleanup);
|
|
11536
|
-
else if (retainedWorktree)
|
|
11735
|
+
else if (retainedWorktree) p31.log.info(`Retained checkout: ${retainedWorktree}`);
|
|
11537
11736
|
}
|
|
11538
11737
|
if (cleanup?.error) {
|
|
11539
11738
|
const action = cleanupAction({
|
|
@@ -11555,7 +11754,7 @@ var dropWorkspaceCommand = defineDeepspaceCommand({
|
|
|
11555
11754
|
});
|
|
11556
11755
|
|
|
11557
11756
|
// src/cli/commands/workspace.ts
|
|
11558
|
-
var workspace_default =
|
|
11757
|
+
var workspace_default = defineCommand11({
|
|
11559
11758
|
meta: {
|
|
11560
11759
|
name: "workspace",
|
|
11561
11760
|
description: "Durable parallel-agent workspaces (new/attach/sync/list/status/land/drop)"
|
|
@@ -11572,7 +11771,7 @@ var workspace_default = defineCommand12({
|
|
|
11572
11771
|
});
|
|
11573
11772
|
|
|
11574
11773
|
// src/cli/commands/activity.ts
|
|
11575
|
-
import * as
|
|
11774
|
+
import * as p32 from "@clack/prompts";
|
|
11576
11775
|
var FOLLOW_INTERVAL_MS = 5e3;
|
|
11577
11776
|
function landIndex(events) {
|
|
11578
11777
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -11685,7 +11884,7 @@ var activity_default = defineDeepspaceCommand({
|
|
|
11685
11884
|
const lands = landIndex(page.events);
|
|
11686
11885
|
for (const e of page.events) console.log(formatEvent2(e, lands, actors));
|
|
11687
11886
|
if (page.events.length === 0 && !args.follow) {
|
|
11688
|
-
|
|
11887
|
+
p32.log.info(cursor > 0 ? `No activity after #${cursor}.` : "No activity yet.");
|
|
11689
11888
|
}
|
|
11690
11889
|
}
|
|
11691
11890
|
cursor = page.cursor;
|
|
@@ -11695,7 +11894,7 @@ var activity_default = defineDeepspaceCommand({
|
|
|
11695
11894
|
}
|
|
11696
11895
|
if (!args.follow) {
|
|
11697
11896
|
if (page.events.length > 0) {
|
|
11698
|
-
|
|
11897
|
+
p32.log.info(
|
|
11699
11898
|
`Cursor: ${cursor}` + (page.hasMore ? ` \xB7 more available (next page: --since ${cursor})` : ` (resume later with --since ${cursor})`)
|
|
11700
11899
|
);
|
|
11701
11900
|
}
|
|
@@ -11717,7 +11916,7 @@ var activity_default = defineDeepspaceCommand({
|
|
|
11717
11916
|
})
|
|
11718
11917
|
);
|
|
11719
11918
|
} else {
|
|
11720
|
-
|
|
11919
|
+
p32.log.warn(`Activity feed interrupted (${code}); retrying in 5s.`);
|
|
11721
11920
|
}
|
|
11722
11921
|
}
|
|
11723
11922
|
await new Promise((resolveSleep) => setTimeout(resolveSleep, FOLLOW_INTERVAL_MS));
|
|
@@ -11726,7 +11925,7 @@ var activity_default = defineDeepspaceCommand({
|
|
|
11726
11925
|
});
|
|
11727
11926
|
|
|
11728
11927
|
// src/cli/commands/git-credential.ts
|
|
11729
|
-
import { defineCommand as
|
|
11928
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
11730
11929
|
function parseFields(input) {
|
|
11731
11930
|
const fields = /* @__PURE__ */ new Map();
|
|
11732
11931
|
for (const line of input.split("\n")) {
|
|
@@ -11775,7 +11974,7 @@ async function readStdin() {
|
|
|
11775
11974
|
for await (const chunk of process.stdin) data += chunk;
|
|
11776
11975
|
return data;
|
|
11777
11976
|
}
|
|
11778
|
-
var git_credential_default =
|
|
11977
|
+
var git_credential_default = defineCommand12({
|
|
11779
11978
|
meta: {
|
|
11780
11979
|
name: "git-credential",
|
|
11781
11980
|
description: "Git credential helper (installed automatically; not for direct use)"
|
|
@@ -11810,8 +12009,8 @@ var git_credential_default = defineCommand13({
|
|
|
11810
12009
|
});
|
|
11811
12010
|
|
|
11812
12011
|
// src/cli/commands/source.ts
|
|
11813
|
-
import * as
|
|
11814
|
-
var TRANSFER_PREFIX = "
|
|
12012
|
+
import * as p33 from "@clack/prompts";
|
|
12013
|
+
var TRANSFER_PREFIX = spacePrivateRef("source-transfer");
|
|
11815
12014
|
var source_default = defineDeepspaceCommand({
|
|
11816
12015
|
meta: {
|
|
11817
12016
|
name: "source",
|
|
@@ -11863,7 +12062,7 @@ var source_default = defineDeepspaceCommand({
|
|
|
11863
12062
|
const remoteArg = typeof args.remote === "string" ? args.remote.trim() : void 0;
|
|
11864
12063
|
const result = requested === "github" ? await moveToGitHub({ appDir, appId, token, deployUrl, state, remoteArg }) : await moveToDeepSpace({ appDir, appId, token, deployUrl, state, remoteArg });
|
|
11865
12064
|
if (!args.json) {
|
|
11866
|
-
|
|
12065
|
+
p33.log.success(
|
|
11867
12066
|
result.source.provider === "github" ? `Source: GitHub \xB7 ${result.source.repository} (revision ${result.revision})` : `Source: DeepSpace (revision ${result.revision})`
|
|
11868
12067
|
);
|
|
11869
12068
|
}
|
|
@@ -12629,11 +12828,11 @@ function reportMigration(migration, json) {
|
|
|
12629
12828
|
|
|
12630
12829
|
// src/cli/cli.ts
|
|
12631
12830
|
var __dirname = dirname10(fileURLToPath3(import.meta.url));
|
|
12632
|
-
var pkg = JSON.parse(
|
|
12831
|
+
var pkg = JSON.parse(readFileSync27(join24(__dirname, "..", "package.json"), "utf-8"));
|
|
12633
12832
|
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync25("/etc/ssl/certs/ca-certificates.crt")) {
|
|
12634
12833
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
12635
12834
|
}
|
|
12636
|
-
var create2 =
|
|
12835
|
+
var create2 = defineCommand13({
|
|
12637
12836
|
meta: {
|
|
12638
12837
|
name: "create",
|
|
12639
12838
|
description: "Create a new DeepSpace app (runs create-deepspace; all flags are forwarded)"
|
|
@@ -12672,14 +12871,14 @@ var create2 = defineCommand14({
|
|
|
12672
12871
|
}
|
|
12673
12872
|
});
|
|
12674
12873
|
var rename = (cmd, name) => ({ ...cmd, meta: { ...cmd.meta ?? {}, name } });
|
|
12675
|
-
var auth =
|
|
12874
|
+
var auth = defineCommand13({
|
|
12676
12875
|
meta: { name: "auth", description: "Session: login, logout, whoami" },
|
|
12677
12876
|
subCommands: { login: login_default, logout: logout_default, whoami: whoami_default }
|
|
12678
12877
|
});
|
|
12679
|
-
var app =
|
|
12878
|
+
var app = defineCommand13({
|
|
12680
12879
|
meta: {
|
|
12681
12880
|
name: "app",
|
|
12682
|
-
description: "The app itself: create, init, list, source, migrate, undeploy, transfer, collaborators, domain,
|
|
12881
|
+
description: "The app itself: create, init, list, source, migrate, undeploy, transfer, collaborators, domain, usage"
|
|
12683
12882
|
},
|
|
12684
12883
|
subCommands: {
|
|
12685
12884
|
create: create2,
|
|
@@ -12689,17 +12888,16 @@ var app = defineCommand14({
|
|
|
12689
12888
|
transfer: transfer_default,
|
|
12690
12889
|
collaborators: collaborators_default,
|
|
12691
12890
|
domain: domain_default,
|
|
12692
|
-
library: library_default,
|
|
12693
12891
|
usage: usage_default,
|
|
12694
12892
|
source: source_default,
|
|
12695
12893
|
migrate: migrate_default
|
|
12696
12894
|
}
|
|
12697
12895
|
});
|
|
12698
|
-
var devGroup =
|
|
12896
|
+
var devGroup = defineCommand13({
|
|
12699
12897
|
meta: { name: "dev", description: "Local development" },
|
|
12700
12898
|
subCommands: { start: rename(dev_default, "start"), kill: kill_default }
|
|
12701
12899
|
});
|
|
12702
|
-
var testGroup =
|
|
12900
|
+
var testGroup = defineCommand13({
|
|
12703
12901
|
meta: { name: "test", description: "App tests and test tooling" },
|
|
12704
12902
|
subCommands: {
|
|
12705
12903
|
run: rename(test_default, "run"),
|
|
@@ -12707,7 +12905,7 @@ var testGroup = defineCommand14({
|
|
|
12707
12905
|
accounts: rename(test_accounts_default, "accounts")
|
|
12708
12906
|
}
|
|
12709
12907
|
});
|
|
12710
|
-
var main =
|
|
12908
|
+
var main = defineCommand13({
|
|
12711
12909
|
meta: {
|
|
12712
12910
|
name: "deepspace",
|
|
12713
12911
|
version: pkg.version,
|