deepspace 0.25.0 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +7 -6
- package/dist/{chunk-2GUGZLAX.js → chunk-YBXIUDZJ.js} +2 -2
- package/dist/{chunk-2GUGZLAX.js.map → chunk-YBXIUDZJ.js.map} +1 -1
- package/dist/cli.js +1036 -966
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.js +1 -1
- package/dist/documentation-default-renderer.cjs +1 -1
- package/dist/documentation-react.js +1 -1
- package/dist/documentation-runtime.js +1 -1
- package/dist/documentation-server-core.js +1 -1
- package/dist/documentation.js +1 -1
- package/dist/documentation.js.map +1 -1
- package/dist/index.d.ts +26 -434
- package/dist/index.js +99 -864
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +4 -41
- package/dist/schema.js +2 -242
- package/dist/schema.js.map +1 -1
- package/dist/server.d.ts +6 -162
- package/dist/server.js +13 -281
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +4 -160
- package/dist/worker.js +15 -281
- package/dist/worker.js.map +1 -1
- package/features/messaging/feature.json +3 -43
- package/features/messaging/src/ChatPage.tsx +12 -16
- package/features/messaging/src/components/chat/ChatHeader.tsx +5 -13
- package/features/messaging/src/components/chat/MessageItem.tsx +7 -9
- package/features/messaging/src/components/chat/MessageList.tsx +57 -40
- package/features/messaging/src/components/chat/ThreadPanel.tsx +25 -11
- package/features/messaging/src/components/chat/UserProfilePopover.tsx +10 -29
- package/features/messaging/src/hooks/useChatChannel.ts +18 -23
- package/features/messaging/src/messaging-schema.ts +0 -2
- package/features/search-bar/feature.json +9 -20
- package/features/search-bar/src/InlineSearch.tsx +101 -0
- package/features/search-bar/src/__tests__/search-model.test.tsx +38 -0
- package/features/search-bar/src/search-model.ts +2 -149
- package/package.json +1 -1
- package/features/messaging/src/ChatMultiPage.tsx +0 -298
- package/features/messaging/src/components/chat-multi/AddMemberModal.tsx +0 -188
- package/features/messaging/src/components/chat-multi/BrowseGroupsModal.tsx +0 -163
- package/features/messaging/src/components/chat-multi/ChannelListItem.tsx +0 -147
- package/features/messaging/src/components/chat-multi/ChannelSettingsPanel.tsx +0 -344
- package/features/messaging/src/components/chat-multi/ChannelSidebar.tsx +0 -460
- package/features/messaging/src/components/chat-multi/CreateGroupModal.tsx +0 -190
- package/features/messaging/src/components/chat-multi/InvitationList.tsx +0 -149
- package/features/messaging/src/components/chat-multi/NewMessageModal.tsx +0 -159
- package/features/messaging/src/hooks/useMultiChannel.ts +0 -142
- package/features/search-bar/src/SearchOverlay.tsx +0 -566
- package/features/search-bar/src/__tests__/search-model.test.ts +0 -40
- package/features/sidebar/feature.json +0 -19
- package/features/sidebar/src/AppSidebar.tsx +0 -558
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
runCommand,
|
|
8
8
|
renderUsage
|
|
9
9
|
} from "citty";
|
|
10
|
-
import { sync as
|
|
10
|
+
import { sync as spawnSync9 } from "cross-spawn";
|
|
11
11
|
import { readFileSync as readFileSync27, existsSync as existsSync30 } from "fs";
|
|
12
12
|
import { dirname as dirname13, join as join29 } from "path";
|
|
13
13
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -46,8 +46,12 @@ function displayLines(value) {
|
|
|
46
46
|
|
|
47
47
|
// src/cli/lib/spinner.ts
|
|
48
48
|
var activeSpinner = null;
|
|
49
|
+
var plainProgress = false;
|
|
50
|
+
function setPlainProgress(enabled) {
|
|
51
|
+
plainProgress = enabled;
|
|
52
|
+
}
|
|
49
53
|
function createSpinner() {
|
|
50
|
-
const impl = process.stdout.isTTY ? p.spinner() : {
|
|
54
|
+
const impl = !plainProgress && process.stdout.isTTY ? p.spinner() : {
|
|
51
55
|
start: (msg) => {
|
|
52
56
|
if (msg) console.log(msg);
|
|
53
57
|
},
|
|
@@ -366,7 +370,9 @@ function errorCode(err) {
|
|
|
366
370
|
return void 0;
|
|
367
371
|
}
|
|
368
372
|
var API_ERROR_HINTS = {
|
|
369
|
-
|
|
373
|
+
// Starts where the server sentence ("Only the app owner can do this.")
|
|
374
|
+
// stops, so the two read as one message instead of a stutter.
|
|
375
|
+
not_app_owner: "Collaborators can deploy and manage secrets, but ownership verbs (collaborators, transfer, undeploy) stay with the owner. Ask them to run it, have them transfer the app (`deepspace app transfer offer`), or fork your checkout as your own app (`deepspace app init --new-id`).",
|
|
370
376
|
app_not_found: "App not found on the service this command asked. Check the app id \u2014 the DEEPSPACE_APP_ID value in wrangler.toml, usually `app_\u2026` (a legacy app's id is its name); list your apps with `deepspace app list`. If you are overriding DEEPSPACE_DEPLOY_URL (staging), note that account/collaborator commands go to the PLATFORM API \u2014 an app registered only on a staging deploy service is unknown there. (`domain` commands take the deployed app *name* instead.)",
|
|
371
377
|
not_app_owner_or_collaborator: "You must be the app owner or a collaborator to do this.",
|
|
372
378
|
test_account_cannot_be_collaborator: "Test accounts cannot be added as collaborators. Use a real DeepSpace account.",
|
|
@@ -591,13 +597,16 @@ function findUnknownCommand(argv, rootCommands) {
|
|
|
591
597
|
const exactAll = commandPaths(table).filter((path) => path.at(-1) === token);
|
|
592
598
|
const exactHit = exactAll.length === 1 ? exactAll[0] : null;
|
|
593
599
|
const split = splitTokenPath(token, table);
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
const
|
|
600
|
+
const rootExactAll = exactHit || split ? [] : commandPaths(rootCommands).filter((path) => path.at(-1) === token);
|
|
601
|
+
const rootExact = rootExactAll.length === 1 ? rootExactAll[0] : null;
|
|
602
|
+
const relativeSuggestion = exactHit ?? split ?? (rootExact ? null : closestCommandPath(token, table));
|
|
603
|
+
const suggestionPath = relativeSuggestion ? [...accepted, ...relativeSuggestion] : rootExact;
|
|
604
|
+
if (!suggestionPath) return null;
|
|
605
|
+
const guessed = suggestionPath.at(-1) ?? "";
|
|
597
606
|
return {
|
|
598
607
|
attemptedPath: ["deepspace", ...accepted, token],
|
|
599
608
|
helpPath: ["deepspace", ...accepted],
|
|
600
|
-
suggestion: ["deepspace", ...
|
|
609
|
+
suggestion: ["deepspace", ...suggestionPath],
|
|
601
610
|
executable: split !== null && !DESTRUCTIVE_VERBS.has(guessed),
|
|
602
611
|
// POSITIONALS ONLY. The remainder exists so the corrected verb keeps
|
|
603
612
|
// the arguments it needs; carrying flags too meant `auth logn --email
|
|
@@ -1120,6 +1129,10 @@ var logout_default = defineDeepspaceCommand({
|
|
|
1120
1129
|
}
|
|
1121
1130
|
});
|
|
1122
1131
|
|
|
1132
|
+
// src/cli/lib/app-registration.ts
|
|
1133
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, rmSync as rmSync3, statSync } from "fs";
|
|
1134
|
+
import { join as join4 } from "path";
|
|
1135
|
+
|
|
1123
1136
|
// src/cli/lib/app-identity.ts
|
|
1124
1137
|
import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
1125
1138
|
import { join as join3, resolve as resolve2 } from "path";
|
|
@@ -1459,55 +1472,9 @@ ${line}
|
|
|
1459
1472
|
writeFileSync3(wranglerPath, src);
|
|
1460
1473
|
}
|
|
1461
1474
|
|
|
1462
|
-
// src/cli/commands/dev.ts
|
|
1463
|
-
import { resolve as resolve8, basename as basename2, join as join11 } from "path";
|
|
1464
|
-
import spawn2 from "cross-spawn";
|
|
1465
|
-
|
|
1466
|
-
// src/cli/lib/app-context.ts
|
|
1467
|
-
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
1468
|
-
import { dirname as dirname3, join as join4, resolve as resolve3 } from "path";
|
|
1469
|
-
function detectAppName(cwd = process.cwd()) {
|
|
1470
|
-
const appDir = resolve3(cwd);
|
|
1471
|
-
if (!existsSync5(join4(appDir, "wrangler.toml"))) return null;
|
|
1472
|
-
const { name } = readWranglerConfig(appDir);
|
|
1473
|
-
return typeof name === "string" && name.length > 0 ? name : null;
|
|
1474
|
-
}
|
|
1475
|
-
function findAppDir(start = process.cwd()) {
|
|
1476
|
-
let dir = resolve3(start);
|
|
1477
|
-
for (; ; ) {
|
|
1478
|
-
if (existsSync5(join4(dir, "wrangler.toml"))) return dir;
|
|
1479
|
-
const parent = dirname3(dir);
|
|
1480
|
-
if (parent === dir) return null;
|
|
1481
|
-
dir = parent;
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
function findChildApps(dir) {
|
|
1485
|
-
try {
|
|
1486
|
-
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync5(join4(dir, entry.name, "wrangler.toml"))).map((entry) => entry.name).sort();
|
|
1487
|
-
} catch {
|
|
1488
|
-
return [];
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
// src/cli/lib/launch-config.ts
|
|
1493
|
-
import {
|
|
1494
|
-
existsSync as existsSync8,
|
|
1495
|
-
readFileSync as readFileSync7,
|
|
1496
|
-
realpathSync as realpathSync2,
|
|
1497
|
-
writeFileSync as writeFileSync4,
|
|
1498
|
-
mkdirSync as mkdirSync4,
|
|
1499
|
-
renameSync,
|
|
1500
|
-
unlinkSync
|
|
1501
|
-
} from "fs";
|
|
1502
|
-
import { join as join6, resolve as resolve5, dirname as dirname5, basename, isAbsolute as isAbsolute2, sep } from "path";
|
|
1503
|
-
|
|
1504
|
-
// src/cli/lib/git/repository.ts
|
|
1505
|
-
import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
1506
|
-
import { dirname as dirname4, join as join5, resolve as resolve4 } from "path";
|
|
1507
|
-
|
|
1508
1475
|
// src/cli/lib/git/process.ts
|
|
1509
1476
|
import { sync as spawnSync } from "cross-spawn";
|
|
1510
|
-
import { existsSync as
|
|
1477
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1511
1478
|
var MAX_GIT_BUFFER = 512 * 1024 * 1024;
|
|
1512
1479
|
var GIT_TIMEOUT_MS = 3e5;
|
|
1513
1480
|
var GitError = class extends Error {
|
|
@@ -1545,7 +1512,7 @@ function runGit(cwd, args, opts = {}) {
|
|
|
1545
1512
|
if (result.error) {
|
|
1546
1513
|
const code = result.error.code;
|
|
1547
1514
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
1548
|
-
if (code === "ENOTDIR" || !
|
|
1515
|
+
if (code === "ENOTDIR" || !existsSync5(cwd)) {
|
|
1549
1516
|
throw new GitError(
|
|
1550
1517
|
`The directory this command runs in is not there: ${cwd}. If it was a workspace worktree, prune it (\`git worktree prune\`) and re-attach.`,
|
|
1551
1518
|
"worktree_missing"
|
|
@@ -1594,7 +1561,323 @@ function gitMeetsFloor(version) {
|
|
|
1594
1561
|
return major > 2 || major === 2 && minor >= 29;
|
|
1595
1562
|
}
|
|
1596
1563
|
|
|
1564
|
+
// src/cli/lib/vc-remote.ts
|
|
1565
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1566
|
+
import { tmpdir } from "os";
|
|
1567
|
+
import { isAbsolute as isAbsolute2, relative, resolve as resolve3 } from "path";
|
|
1568
|
+
function spaceRemoteName(environment = DEEPSPACE_ENV) {
|
|
1569
|
+
if (environment === "staging") return "space-staging";
|
|
1570
|
+
if (environment === "production") return "space";
|
|
1571
|
+
return "space-invalid";
|
|
1572
|
+
}
|
|
1573
|
+
var SPACE_REMOTE = spaceRemoteName();
|
|
1574
|
+
function spaceTrackingRef(branch, remote = SPACE_REMOTE) {
|
|
1575
|
+
return `refs/remotes/${remote}/${branch}`;
|
|
1576
|
+
}
|
|
1577
|
+
function spacePrivateRef(path, environment = DEEPSPACE_ENV) {
|
|
1578
|
+
const root = environment === "production" ? "refs/deepspace" : `refs/deepspace/${environment}`;
|
|
1579
|
+
return `${root}/${path}`;
|
|
1580
|
+
}
|
|
1581
|
+
function deployBaseUrl() {
|
|
1582
|
+
return process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
1583
|
+
}
|
|
1584
|
+
function repoUrl(appId, base = deployBaseUrl()) {
|
|
1585
|
+
return `${base.replace(/\/+$/, "")}/api/repo/${encodeURIComponent(appId)}`;
|
|
1586
|
+
}
|
|
1587
|
+
function gitAuthEnv(token, base = deployBaseUrl()) {
|
|
1588
|
+
const existing = Number(process.env.GIT_CONFIG_COUNT ?? "0");
|
|
1589
|
+
const index = Number.isInteger(existing) && existing > 0 ? existing : 0;
|
|
1590
|
+
return {
|
|
1591
|
+
GIT_CONFIG_COUNT: String(index + 1),
|
|
1592
|
+
[`GIT_CONFIG_KEY_${index}`]: `http.${base.replace(/\/+$/, "")}.extraHeader`,
|
|
1593
|
+
[`GIT_CONFIG_VALUE_${index}`]: `Authorization: Bearer ${token}`
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
function runGitRemote(cwd, token, args, opts = {}) {
|
|
1597
|
+
return runGit(cwd, args, { ...opts, env: { ...gitAuthEnv(token), ...opts.env ?? {} } });
|
|
1598
|
+
}
|
|
1599
|
+
function entryIsTransient(cwd) {
|
|
1600
|
+
const entry = process.argv[1];
|
|
1601
|
+
if (!entry) return true;
|
|
1602
|
+
if (/[\\/]_npx[\\/]/.test(entry) || entry.startsWith(tmpdir())) return true;
|
|
1603
|
+
const rel = relative(resolve3(cwd), resolve3(entry));
|
|
1604
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
|
|
1605
|
+
}
|
|
1606
|
+
function helperEntryUsable(command) {
|
|
1607
|
+
const match = /^!(?:DEEPSPACE_ENV=(production|staging) )?'([^']*)' '([^']*)' git-credential$/.exec(command);
|
|
1608
|
+
const expectedEnvironment = DEEPSPACE_ENV === "production" ? void 0 : DEEPSPACE_ENV;
|
|
1609
|
+
if (!match || match[1] !== expectedEnvironment) return false;
|
|
1610
|
+
return existsSync6(match[2]) && existsSync6(match[3]);
|
|
1611
|
+
}
|
|
1612
|
+
function credentialHelperCommand(environment = DEEPSPACE_ENV) {
|
|
1613
|
+
const entry = process.argv[1];
|
|
1614
|
+
if (!entry) throw new Error("CLI entry path is unavailable");
|
|
1615
|
+
const environmentPrefix = environment === "staging" ? "DEEPSPACE_ENV=staging " : "";
|
|
1616
|
+
return `!${environmentPrefix}${shQuote(process.execPath)} ${shQuote(entry)} git-credential`;
|
|
1617
|
+
}
|
|
1618
|
+
function installCredentialHelper(cwd, url) {
|
|
1619
|
+
try {
|
|
1620
|
+
const origin = new URL(url);
|
|
1621
|
+
const key = `credential.${origin.protocol}//${origin.host}.helper`;
|
|
1622
|
+
const helper = credentialHelperCommand();
|
|
1623
|
+
const wanted = ["", helper];
|
|
1624
|
+
const transient = entryIsTransient(cwd);
|
|
1625
|
+
const scope = transient ? "--worktree" : "--global";
|
|
1626
|
+
if (transient) {
|
|
1627
|
+
const enabled = runGit(cwd, ["config", "--local", "extensions.worktreeConfig", "true"], {
|
|
1628
|
+
allowFail: true
|
|
1629
|
+
});
|
|
1630
|
+
if (enabled.status !== 0) return;
|
|
1631
|
+
runGit(cwd, ["config", "--local", "--unset-all", key], { allowFail: true });
|
|
1632
|
+
}
|
|
1633
|
+
const current = runGit(cwd, ["config", scope, "--get-all", key], { allowFail: true });
|
|
1634
|
+
if (current.status === 0) {
|
|
1635
|
+
const existing = current.stdout.toString("utf-8").split("\n").filter((_, index, lines) => index < lines.length - 1);
|
|
1636
|
+
if (existing.length === wanted.length && existing.every((value, i) => value === wanted[i])) {
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
if (existing.length === wanted.length && existing[0] === "" && helperEntryUsable(existing[1])) {
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
runGit(cwd, ["config", scope, "--unset-all", key], { allowFail: true });
|
|
1643
|
+
}
|
|
1644
|
+
runGit(cwd, ["config", scope, "--add", key, ""]);
|
|
1645
|
+
runGit(cwd, ["config", scope, "--add", key, helper]);
|
|
1646
|
+
if (scope === "--global" && !process.argv.includes("--json")) {
|
|
1647
|
+
process.stderr.write(
|
|
1648
|
+
`note: added a git credential helper for ${origin.host} to your global git config so plain \`git clone\`/\`git push\` to DeepSpace authenticate. Undo with: git config --global --unset-all ${key}
|
|
1649
|
+
`
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
} catch {
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
function ensureSpaceRemote(cwd, appId, remote = SPACE_REMOTE, token) {
|
|
1656
|
+
const url = repoUrl(appId);
|
|
1657
|
+
const current = runGit(cwd, ["remote", "get-url", remote], { allowFail: true });
|
|
1658
|
+
const existing = current.status === 0 ? current.stdout.toString("utf-8").trim() : null;
|
|
1659
|
+
if (appIdFromRepoUrl(existing ?? "") !== appId) {
|
|
1660
|
+
const owned = declaredAppIds(cwd);
|
|
1661
|
+
if (owned.size > 0 && !owned.has(appId)) {
|
|
1662
|
+
throw new InputError(
|
|
1663
|
+
`This checkout declares ${[...owned].join(", ")}, but this command targets ${appId}. Pointing its \`${remote}\` remote at another app would send every later plain \`git push ${remote}\` to the wrong repo. Work on ${appId} from its own checkout (\`deepspace clone ${appId}\` into a new directory), or fork THIS checkout into a brand-new app of your own with \`deepspace app init --new-id\`.`,
|
|
1664
|
+
"app_checkout_mismatch"
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
if (token) ensureGitIdentity(cwd, token);
|
|
1669
|
+
if (existing === null) {
|
|
1670
|
+
runGit(cwd, ["remote", "add", remote, url]);
|
|
1671
|
+
} else if (existing !== url) {
|
|
1672
|
+
const priorApp = appIdFromRepoUrl(existing);
|
|
1673
|
+
if (priorApp && priorApp !== appId && !process.argv.includes("--json")) {
|
|
1674
|
+
process.stderr.write(
|
|
1675
|
+
`warning: remote '${remote}' was pointed at app ${priorApp}; re-aiming it at ${appId}.
|
|
1676
|
+
`
|
|
1677
|
+
);
|
|
1678
|
+
}
|
|
1679
|
+
runGit(cwd, ["remote", "set-url", remote, url]);
|
|
1680
|
+
}
|
|
1681
|
+
installCredentialHelper(cwd, url);
|
|
1682
|
+
return url;
|
|
1683
|
+
}
|
|
1684
|
+
function removeSpaceRemote(cwd, remote = SPACE_REMOTE) {
|
|
1685
|
+
const current = runGit(cwd, ["remote", "get-url", remote], { allowFail: true });
|
|
1686
|
+
if (current.status !== 0) return false;
|
|
1687
|
+
runGit(cwd, ["remote", "remove", remote]);
|
|
1688
|
+
return true;
|
|
1689
|
+
}
|
|
1690
|
+
function ensureGitIdentity(cwd, token) {
|
|
1691
|
+
try {
|
|
1692
|
+
const configured = (key) => runGit(cwd, ["config", "--get", key], { allowFail: true }).stdout.toString("utf-8").trim() !== "";
|
|
1693
|
+
const missingName = !configured("user.name");
|
|
1694
|
+
const missingEmail = !configured("user.email");
|
|
1695
|
+
if (!missingName && !missingEmail) return;
|
|
1696
|
+
if (runGit(cwd, ["config", "--get", "--type=bool", "user.useConfigOnly"], { allowFail: true }).stdout.toString("utf-8").trim() === "true") {
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
const payload = decodeJwtPayload(token);
|
|
1700
|
+
if (!payload.email) return;
|
|
1701
|
+
if (missingName) {
|
|
1702
|
+
runGit(cwd, ["config", "--local", "user.name", payload.name || payload.email], {
|
|
1703
|
+
allowFail: true
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
if (missingEmail) {
|
|
1707
|
+
runGit(cwd, ["config", "--local", "user.email", payload.email], { allowFail: true });
|
|
1708
|
+
}
|
|
1709
|
+
const wrote = [...missingEmail ? ["user.email"] : [], ...missingName ? ["user.name"] : []];
|
|
1710
|
+
const dir = /^[A-Za-z0-9_./:@-]+$/.test(cwd) ? cwd : shQuote(cwd);
|
|
1711
|
+
const undo = wrote.map((key) => `git -C ${dir} config --local --unset ${key}`).join(" && ");
|
|
1712
|
+
process.stderr.write(
|
|
1713
|
+
`Git identity for this checkout set from your DeepSpace login (${payload.email}).
|
|
1714
|
+
Undo with: ${undo}
|
|
1715
|
+
`
|
|
1716
|
+
);
|
|
1717
|
+
} catch {
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
function appIdFromRepoUrl(url) {
|
|
1721
|
+
const match = /\/api\/repo\/([^/]+)\/?$/.exec(url);
|
|
1722
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// src/cli/lib/app-registration.ts
|
|
1726
|
+
var DEPLOY_URL = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
1727
|
+
async function mintAppIdentity(appDir, token, opts = {}) {
|
|
1728
|
+
const { appId } = await apiFetch(DEPLOY_URL, token, "/api/apps/mint", {
|
|
1729
|
+
method: "POST"
|
|
1730
|
+
});
|
|
1731
|
+
try {
|
|
1732
|
+
writeAppId(appDir, appId, { wranglerEnv: opts.wranglerEnv, force: Boolean(opts.force) });
|
|
1733
|
+
} catch (error) {
|
|
1734
|
+
throw new Error(
|
|
1735
|
+
`Registered ${appId}, but writing it to wrangler.toml failed: ${error instanceof Error ? error.message : String(error)} \u2014 add DEEPSPACE_APP_ID = "${appId}" ${opts.wranglerEnv ? `to [env.${opts.wranglerEnv}.vars] ` : ""}by hand.`
|
|
1736
|
+
);
|
|
1737
|
+
}
|
|
1738
|
+
const committedScaffold = opts.commitScaffold === true && commitScaffoldIfUnborn(appDir, token);
|
|
1739
|
+
return { appId, committedScaffold };
|
|
1740
|
+
}
|
|
1741
|
+
function canHealAppRegistration(appDir, wranglerEnv) {
|
|
1742
|
+
if (!hasWranglerConfig(appDir)) return false;
|
|
1743
|
+
try {
|
|
1744
|
+
const cfg = readWranglerConfig(appDir);
|
|
1745
|
+
if (wranglerEnv && cfg.env?.[wranglerEnv] === void 0) return false;
|
|
1746
|
+
if (readAppIdVar(cfg, wranglerEnv) === void 0) return false;
|
|
1747
|
+
} catch {
|
|
1748
|
+
return false;
|
|
1749
|
+
}
|
|
1750
|
+
try {
|
|
1751
|
+
const committed = runGit(appDir, ["show", "HEAD:./wrangler.toml"], { allowFail: true });
|
|
1752
|
+
if (committed.status === 0 && committed.stdout.toString("utf-8").includes("__APP_ID__")) {
|
|
1753
|
+
return false;
|
|
1754
|
+
}
|
|
1755
|
+
} catch {
|
|
1756
|
+
}
|
|
1757
|
+
return true;
|
|
1758
|
+
}
|
|
1759
|
+
async function ensureAppRegistered(appDir, token, wranglerEnv, opts = {}) {
|
|
1760
|
+
const existing = readAppId(appDir, wranglerEnv);
|
|
1761
|
+
if (existing) return { appId: existing, minted: false, committedScaffold: false };
|
|
1762
|
+
if (!canHealAppRegistration(appDir, wranglerEnv)) return null;
|
|
1763
|
+
const release2 = acquireMintLock(appDir);
|
|
1764
|
+
if (!release2) {
|
|
1765
|
+
const winner = await waitForMintedId(appDir, wranglerEnv);
|
|
1766
|
+
if (winner) return { appId: winner, minted: false, committedScaffold: false };
|
|
1767
|
+
}
|
|
1768
|
+
try {
|
|
1769
|
+
const raced = readAppId(appDir, wranglerEnv);
|
|
1770
|
+
if (raced) return { appId: raced, minted: false, committedScaffold: false };
|
|
1771
|
+
const { appId, committedScaffold } = await mintAppIdentity(appDir, token, {
|
|
1772
|
+
wranglerEnv,
|
|
1773
|
+
commitScaffold: opts.commitScaffold
|
|
1774
|
+
});
|
|
1775
|
+
process.stderr.write(
|
|
1776
|
+
`Registered ${appId}${wranglerEnv ? ` (env: ${wranglerEnv})` : ""} on ${DEEPSPACE_ENV} to your account \u2014 apps register on first use` + (committedScaffold ? "; initial scaffold commit created" : "") + ".\n"
|
|
1777
|
+
);
|
|
1778
|
+
return { appId, minted: true, committedScaffold };
|
|
1779
|
+
} finally {
|
|
1780
|
+
release2?.();
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
function acquireMintLock(appDir) {
|
|
1784
|
+
const lockDir = join4(appDir, ".deepspace", "register.lock");
|
|
1785
|
+
mkdirSync3(join4(appDir, ".deepspace"), { recursive: true });
|
|
1786
|
+
try {
|
|
1787
|
+
mkdirSync3(lockDir);
|
|
1788
|
+
} catch {
|
|
1789
|
+
try {
|
|
1790
|
+
const age = Date.now() - statSync(lockDir).mtimeMs;
|
|
1791
|
+
if (age < 2 * 60 * 1e3) return null;
|
|
1792
|
+
rmSync3(lockDir, { recursive: true, force: true });
|
|
1793
|
+
mkdirSync3(lockDir);
|
|
1794
|
+
} catch {
|
|
1795
|
+
return null;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
return () => rmSync3(lockDir, { recursive: true, force: true });
|
|
1799
|
+
}
|
|
1800
|
+
async function waitForMintedId(appDir, wranglerEnv) {
|
|
1801
|
+
const deadline = Date.now() + 3e4;
|
|
1802
|
+
while (Date.now() < deadline) {
|
|
1803
|
+
const id = readAppId(appDir, wranglerEnv);
|
|
1804
|
+
if (id) return id;
|
|
1805
|
+
if (!existsSync7(join4(appDir, ".deepspace", "register.lock"))) {
|
|
1806
|
+
return readAppId(appDir, wranglerEnv);
|
|
1807
|
+
}
|
|
1808
|
+
await new Promise((resolve23) => setTimeout(resolve23, 250));
|
|
1809
|
+
}
|
|
1810
|
+
return null;
|
|
1811
|
+
}
|
|
1812
|
+
function wranglerConfigUncommitted(appDir) {
|
|
1813
|
+
if (!existsSync7(join4(appDir, ".git"))) return false;
|
|
1814
|
+
const dryRun = runGit(appDir, ["commit", "--dry-run", "-m", "x", "--", "wrangler.toml"], {
|
|
1815
|
+
allowFail: true
|
|
1816
|
+
});
|
|
1817
|
+
return dryRun.status === 0;
|
|
1818
|
+
}
|
|
1819
|
+
function commitScaffoldIfUnborn(appDir, token) {
|
|
1820
|
+
try {
|
|
1821
|
+
if (!existsSync7(join4(appDir, ".git"))) return false;
|
|
1822
|
+
if (runGit(appDir, ["rev-parse", "--verify", "HEAD"], { allowFail: true }).status === 0) {
|
|
1823
|
+
return false;
|
|
1824
|
+
}
|
|
1825
|
+
ensureGitIdentity(appDir, token);
|
|
1826
|
+
if (runGit(appDir, ["add", "-A"], { allowFail: true }).status !== 0) return false;
|
|
1827
|
+
const commit = runGit(appDir, ["commit", "-m", "Initial DeepSpace scaffold", "--no-verify"], {
|
|
1828
|
+
allowFail: true
|
|
1829
|
+
});
|
|
1830
|
+
return commit.status === 0;
|
|
1831
|
+
} catch {
|
|
1832
|
+
return false;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
// src/cli/commands/dev.ts
|
|
1837
|
+
import { resolve as resolve9, basename as basename2, join as join12 } from "path";
|
|
1838
|
+
import spawn2 from "cross-spawn";
|
|
1839
|
+
|
|
1840
|
+
// src/cli/lib/app-context.ts
|
|
1841
|
+
import { existsSync as existsSync8, readdirSync } from "fs";
|
|
1842
|
+
import { dirname as dirname3, join as join5, resolve as resolve4 } from "path";
|
|
1843
|
+
function detectAppName(cwd = process.cwd()) {
|
|
1844
|
+
const appDir = resolve4(cwd);
|
|
1845
|
+
if (!existsSync8(join5(appDir, "wrangler.toml"))) return null;
|
|
1846
|
+
const { name } = readWranglerConfig(appDir);
|
|
1847
|
+
return typeof name === "string" && name.length > 0 ? name : null;
|
|
1848
|
+
}
|
|
1849
|
+
function findAppDir(start = process.cwd()) {
|
|
1850
|
+
let dir = resolve4(start);
|
|
1851
|
+
for (; ; ) {
|
|
1852
|
+
if (existsSync8(join5(dir, "wrangler.toml"))) return dir;
|
|
1853
|
+
const parent = dirname3(dir);
|
|
1854
|
+
if (parent === dir) return null;
|
|
1855
|
+
dir = parent;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
function findChildApps(dir) {
|
|
1859
|
+
try {
|
|
1860
|
+
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync8(join5(dir, entry.name, "wrangler.toml"))).map((entry) => entry.name).sort();
|
|
1861
|
+
} catch {
|
|
1862
|
+
return [];
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// src/cli/lib/launch-config.ts
|
|
1867
|
+
import {
|
|
1868
|
+
existsSync as existsSync10,
|
|
1869
|
+
readFileSync as readFileSync7,
|
|
1870
|
+
realpathSync as realpathSync2,
|
|
1871
|
+
writeFileSync as writeFileSync4,
|
|
1872
|
+
mkdirSync as mkdirSync5,
|
|
1873
|
+
renameSync,
|
|
1874
|
+
unlinkSync
|
|
1875
|
+
} from "fs";
|
|
1876
|
+
import { join as join7, resolve as resolve6, dirname as dirname5, basename, isAbsolute as isAbsolute3, sep } from "path";
|
|
1877
|
+
|
|
1597
1878
|
// src/cli/lib/git/repository.ts
|
|
1879
|
+
import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync6 } from "fs";
|
|
1880
|
+
import { dirname as dirname4, join as join6, resolve as resolve5 } from "path";
|
|
1598
1881
|
function isPlausibleBranchName(name) {
|
|
1599
1882
|
if (!name || name === "HEAD" || name === "@") return false;
|
|
1600
1883
|
if (name.startsWith("/") || name.startsWith("-") || name.startsWith(".")) return false;
|
|
@@ -1644,7 +1927,7 @@ function assertNoOperationInProgress(cwd) {
|
|
|
1644
1927
|
["REVERT_HEAD", "revert", "git revert --continue", "git revert --abort"],
|
|
1645
1928
|
["rebase-merge", "rebase", "git rebase --continue", "git rebase --abort"],
|
|
1646
1929
|
["rebase-apply", "rebase", "git rebase --continue", "git rebase --abort"]
|
|
1647
|
-
].find(([marker]) =>
|
|
1930
|
+
].find(([marker]) => existsSync9(join6(resolve5(cwd, gitDir), marker)));
|
|
1648
1931
|
if (!inProgress) return;
|
|
1649
1932
|
const [, kind, finish, abort] = inProgress;
|
|
1650
1933
|
throw new InputError(
|
|
@@ -1658,13 +1941,13 @@ function repoToplevel(cwd) {
|
|
|
1658
1941
|
function interruptedGitOperation(cwd) {
|
|
1659
1942
|
const gitDir = runGit(cwd, ["rev-parse", "--git-path", "HEAD"], { allowFail: true });
|
|
1660
1943
|
if (gitDir.status !== 0) return null;
|
|
1661
|
-
const dir = dirname4(
|
|
1662
|
-
if (
|
|
1663
|
-
if (
|
|
1664
|
-
if (
|
|
1665
|
-
if (
|
|
1666
|
-
if (
|
|
1667
|
-
if (
|
|
1944
|
+
const dir = dirname4(resolve5(cwd, gitDir.stdout.toString("utf-8").trim()));
|
|
1945
|
+
if (existsSync9(join6(dir, "rebase-apply", "applying"))) return "am";
|
|
1946
|
+
if (existsSync9(join6(dir, "rebase-merge")) || existsSync9(join6(dir, "rebase-apply"))) return "rebase";
|
|
1947
|
+
if (existsSync9(join6(dir, "MERGE_HEAD"))) return "merge";
|
|
1948
|
+
if (existsSync9(join6(dir, "CHERRY_PICK_HEAD"))) return "cherry-pick";
|
|
1949
|
+
if (existsSync9(join6(dir, "REVERT_HEAD"))) return "revert";
|
|
1950
|
+
if (existsSync9(join6(dir, "BISECT_LOG"))) return "bisect";
|
|
1668
1951
|
return null;
|
|
1669
1952
|
}
|
|
1670
1953
|
function currentBranch(cwd) {
|
|
@@ -1741,11 +2024,11 @@ function listWorktrees(cwd) {
|
|
|
1741
2024
|
current.branch = ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref;
|
|
1742
2025
|
}
|
|
1743
2026
|
}
|
|
1744
|
-
return worktrees.filter((worktree) =>
|
|
2027
|
+
return worktrees.filter((worktree) => existsSync9(worktree.path));
|
|
1745
2028
|
}
|
|
1746
2029
|
function ensureLocalExclude(cwd, pattern) {
|
|
1747
|
-
const commonDir =
|
|
1748
|
-
const excludePath =
|
|
2030
|
+
const commonDir = resolve5(cwd, gitLine(cwd, ["rev-parse", "--git-common-dir"]));
|
|
2031
|
+
const excludePath = join6(commonDir, "info", "exclude");
|
|
1749
2032
|
let current = "";
|
|
1750
2033
|
try {
|
|
1751
2034
|
current = readFileSync6(excludePath, "utf-8");
|
|
@@ -1753,7 +2036,7 @@ function ensureLocalExclude(cwd, pattern) {
|
|
|
1753
2036
|
}
|
|
1754
2037
|
if (current.split("\n").some((line) => line.trim() === pattern)) return;
|
|
1755
2038
|
try {
|
|
1756
|
-
|
|
2039
|
+
mkdirSync4(dirname4(excludePath), { recursive: true });
|
|
1757
2040
|
appendFileSync(
|
|
1758
2041
|
excludePath,
|
|
1759
2042
|
`${current.endsWith("\n") || current === "" ? "" : "\n"}${pattern}
|
|
@@ -1765,7 +2048,7 @@ function ensureLocalExclude(cwd, pattern) {
|
|
|
1765
2048
|
|
|
1766
2049
|
// src/cli/lib/launch-config.ts
|
|
1767
2050
|
function writeLaunchFile(launchPath, config) {
|
|
1768
|
-
|
|
2051
|
+
mkdirSync5(dirname5(launchPath), { recursive: true });
|
|
1769
2052
|
const tmpPath = `${launchPath}.${process.pid}.tmp`;
|
|
1770
2053
|
writeFileSync4(tmpPath, JSON.stringify(config, null, 2) + "\n");
|
|
1771
2054
|
try {
|
|
@@ -1780,7 +2063,7 @@ function writeLaunchFile(launchPath, config) {
|
|
|
1780
2063
|
}
|
|
1781
2064
|
function readLaunchFile(launchPath) {
|
|
1782
2065
|
const empty = { version: "0.0.1", configurations: [] };
|
|
1783
|
-
if (!
|
|
2066
|
+
if (!existsSync10(launchPath)) return empty;
|
|
1784
2067
|
const raw = readFileSync7(launchPath, "utf-8").trim();
|
|
1785
2068
|
if (!raw) return empty;
|
|
1786
2069
|
const parsed = JSON.parse(raw);
|
|
@@ -1788,7 +2071,7 @@ function readLaunchFile(launchPath) {
|
|
|
1788
2071
|
return parsed;
|
|
1789
2072
|
}
|
|
1790
2073
|
function writeLaunchConfigIfMissing(appDir, appName, port, opts = {}) {
|
|
1791
|
-
const launchPath =
|
|
2074
|
+
const launchPath = join7(appDir, ".claude", "launch.json");
|
|
1792
2075
|
try {
|
|
1793
2076
|
const config = readLaunchFile(launchPath);
|
|
1794
2077
|
if (!config) return;
|
|
@@ -1829,7 +2112,7 @@ function writeLaunchConfigIfMissing(appDir, appName, port, opts = {}) {
|
|
|
1829
2112
|
}
|
|
1830
2113
|
function resolveAppLaunchPort(appDir) {
|
|
1831
2114
|
try {
|
|
1832
|
-
const config = readLaunchFile(
|
|
2115
|
+
const config = readLaunchFile(join7(appDir, ".claude", "launch.json"));
|
|
1833
2116
|
if (!config) return null;
|
|
1834
2117
|
const appName = detectAppName(appDir) ?? basename(appDir);
|
|
1835
2118
|
const entry = config.configurations.find((c) => c?.name === appName) ?? (config.configurations.length === 1 ? config.configurations[0] : void 0);
|
|
@@ -1860,7 +2143,7 @@ function canonicalPath(path) {
|
|
|
1860
2143
|
try {
|
|
1861
2144
|
return realpathSync2(path);
|
|
1862
2145
|
} catch {
|
|
1863
|
-
return
|
|
2146
|
+
return resolve6(path);
|
|
1864
2147
|
}
|
|
1865
2148
|
}
|
|
1866
2149
|
function detectLinkedWorktree(appDir) {
|
|
@@ -1883,21 +2166,21 @@ function deriveWorktreePort(worktreeKey) {
|
|
|
1883
2166
|
return WORKTREE_PORT_MIN + hash % WORKTREE_PORT_RANGE;
|
|
1884
2167
|
}
|
|
1885
2168
|
function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
1886
|
-
const launchPath =
|
|
2169
|
+
const launchPath = join7(mainRepoRoot, ".claude", "launch.json");
|
|
1887
2170
|
const entryName = WORKTREE_ENTRY_PREFIX + worktreeName;
|
|
1888
2171
|
try {
|
|
1889
2172
|
const config = readLaunchFile(launchPath);
|
|
1890
2173
|
if (!config) return null;
|
|
1891
|
-
const worktreesRoot =
|
|
2174
|
+
const worktreesRoot = join7(mainRepoRoot, ".claude", "worktrees") + sep;
|
|
1892
2175
|
config.configurations = config.configurations.filter((c) => {
|
|
1893
2176
|
if (c?.name === entryName) return false;
|
|
1894
|
-
const isStaleWorktreeEntry = typeof c?.name === "string" && c.name.startsWith(WORKTREE_ENTRY_PREFIX) && typeof c.cwd === "string" &&
|
|
2177
|
+
const isStaleWorktreeEntry = typeof c?.name === "string" && c.name.startsWith(WORKTREE_ENTRY_PREFIX) && typeof c.cwd === "string" && isAbsolute3(c.cwd) && c.cwd.startsWith(worktreesRoot) && !existsSync10(c.cwd);
|
|
1895
2178
|
return !isStaleWorktreeEntry;
|
|
1896
2179
|
});
|
|
1897
2180
|
let port = opts.port;
|
|
1898
2181
|
if (opts.probePort) {
|
|
1899
2182
|
const taken = new Set(
|
|
1900
|
-
config.configurations.map((c) => Number(c?.port)).filter((
|
|
2183
|
+
config.configurations.map((c) => Number(c?.port)).filter((p35) => Number.isInteger(p35) && p35 > 0)
|
|
1901
2184
|
);
|
|
1902
2185
|
for (let i = 0; i < WORKTREE_PORT_RANGE && taken.has(port); i++) {
|
|
1903
2186
|
port = WORKTREE_PORT_MIN + (port - WORKTREE_PORT_MIN + 1) % WORKTREE_PORT_RANGE;
|
|
@@ -1912,7 +2195,7 @@ function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
|
1912
2195
|
// the Claude Code desktop launch.json reference alongside `port`) and
|
|
1913
2196
|
// verified end-to-end: with it set, the preview server builds from the
|
|
1914
2197
|
// worktree instead of the session's main repo.
|
|
1915
|
-
cwd:
|
|
2198
|
+
cwd: resolve6(appDir)
|
|
1916
2199
|
});
|
|
1917
2200
|
writeLaunchFile(launchPath, config);
|
|
1918
2201
|
return { entryName, port };
|
|
@@ -1926,7 +2209,7 @@ function resolveWorktreePort(appDir) {
|
|
|
1926
2209
|
const claude = detectClaudeWorktree(appDir);
|
|
1927
2210
|
if (claude) {
|
|
1928
2211
|
try {
|
|
1929
|
-
const config = readLaunchFile(
|
|
2212
|
+
const config = readLaunchFile(join7(claude.mainRepoRoot, ".claude", "launch.json"));
|
|
1930
2213
|
const entry = config?.configurations.find(
|
|
1931
2214
|
(c) => c?.name === WORKTREE_ENTRY_PREFIX + claude.worktreeName
|
|
1932
2215
|
);
|
|
@@ -1987,14 +2270,6 @@ async function getAppSource(deployUrl, token, appId) {
|
|
|
1987
2270
|
throw error;
|
|
1988
2271
|
}
|
|
1989
2272
|
}
|
|
1990
|
-
function setAppSource(deployUrl, token, appId, input) {
|
|
1991
|
-
return apiFetch(
|
|
1992
|
-
deployUrl,
|
|
1993
|
-
token,
|
|
1994
|
-
`/api/apps/${encodeURIComponent(appId)}/source`,
|
|
1995
|
-
{ method: "POST", body: JSON.stringify(input) }
|
|
1996
|
-
);
|
|
1997
|
-
}
|
|
1998
2273
|
|
|
1999
2274
|
// src/cli/lib/repo-api.ts
|
|
2000
2275
|
function isWorkspaceRef(ref) {
|
|
@@ -2002,10 +2277,15 @@ function isWorkspaceRef(ref) {
|
|
|
2002
2277
|
return ref.startsWith(prefix) && isWorkspaceId(ref.slice(prefix.length));
|
|
2003
2278
|
}
|
|
2004
2279
|
function releaseSourceLabel(release2) {
|
|
2005
|
-
|
|
2006
|
-
if (release2.
|
|
2007
|
-
if (release2.source?.provider === "
|
|
2008
|
-
|
|
2280
|
+
const dirty = release2.sourceDirty === true ? " (dirty worktree)" : "";
|
|
2281
|
+
if (release2.commitOid) return `commit ${release2.commitOid.slice(0, 10)}${dirty}`;
|
|
2282
|
+
if (release2.source?.provider === "github") {
|
|
2283
|
+
return `GitHub \xB7 ${release2.source.repository}${dirty ? ", dirty worktree" : ""}`;
|
|
2284
|
+
}
|
|
2285
|
+
if (release2.source?.provider === "deepspace") {
|
|
2286
|
+
return `DeepSpace source, no commit recorded${dirty}`;
|
|
2287
|
+
}
|
|
2288
|
+
return `no source recorded${dirty}`;
|
|
2009
2289
|
}
|
|
2010
2290
|
function mintIdempotencyKey() {
|
|
2011
2291
|
return randomUUID();
|
|
@@ -2186,6 +2466,11 @@ async function resolveAppTarget(deployUrl, token, explicit, options = {}) {
|
|
|
2186
2466
|
if (app2 !== void 0) return resolveAppSelector(deployUrl, token, app2);
|
|
2187
2467
|
const { wranglerEnv } = parseWranglerEnvArg(options.wranglerEnv);
|
|
2188
2468
|
const appDir = findAppDir();
|
|
2469
|
+
if (options.register === true) {
|
|
2470
|
+
const ensured = appDir ? await ensureAppRegistered(appDir, token, wranglerEnv) : null;
|
|
2471
|
+
if (!ensured) throw appTargetMissingError(appDir, wranglerEnv);
|
|
2472
|
+
return ensured.appId;
|
|
2473
|
+
}
|
|
2189
2474
|
const id = appDir ? readAppId(appDir, wranglerEnv) : null;
|
|
2190
2475
|
if (!id) throw appTargetMissingError(appDir, wranglerEnv);
|
|
2191
2476
|
return id;
|
|
@@ -2255,7 +2540,9 @@ function assertAppTargetResolvable(appArg, options = {}) {
|
|
|
2255
2540
|
}
|
|
2256
2541
|
if (app2 !== void 0) return;
|
|
2257
2542
|
const appDir = findAppDir();
|
|
2258
|
-
if (
|
|
2543
|
+
if (appDir && readAppId(appDir, wranglerEnv)) return;
|
|
2544
|
+
if (options.register === true && appDir && canHealAppRegistration(appDir, wranglerEnv)) return;
|
|
2545
|
+
throw appTargetMissingError(appDir, wranglerEnv);
|
|
2259
2546
|
}
|
|
2260
2547
|
async function warnIfPhantomApp(deployUrl, token, appId, explicit) {
|
|
2261
2548
|
if (!explicit || !STRICT_APP_ID_RE.test(explicit.trim())) return;
|
|
@@ -2357,22 +2644,34 @@ async function writeDevVars(appDir, ownerId, callerJwt, wranglerEnv, opts = {})
|
|
|
2357
2644
|
}
|
|
2358
2645
|
|
|
2359
2646
|
// src/cli/lib/install-status.ts
|
|
2360
|
-
import {
|
|
2361
|
-
|
|
2647
|
+
import {
|
|
2648
|
+
appendFileSync as appendFileSync2,
|
|
2649
|
+
closeSync,
|
|
2650
|
+
existsSync as existsSync12,
|
|
2651
|
+
mkdirSync as mkdirSync6,
|
|
2652
|
+
openSync,
|
|
2653
|
+
readFileSync as readFileSync9,
|
|
2654
|
+
realpathSync as realpathSync3,
|
|
2655
|
+
rmSync as rmSync4,
|
|
2656
|
+
statSync as statSync2,
|
|
2657
|
+
writeFileSync as writeFileSync5
|
|
2658
|
+
} from "fs";
|
|
2659
|
+
import { dirname as dirname6, join as join9, resolve as resolve7 } from "path";
|
|
2660
|
+
import { sync as spawnSync2 } from "cross-spawn";
|
|
2362
2661
|
|
|
2363
2662
|
// src/cli/lib/package-manager.ts
|
|
2364
|
-
import { existsSync as
|
|
2365
|
-
import { join as
|
|
2663
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
2664
|
+
import { join as join8 } from "path";
|
|
2366
2665
|
function detectPackageManager(appDir) {
|
|
2367
2666
|
try {
|
|
2368
|
-
const pkg2 = JSON.parse(readFileSync8(
|
|
2667
|
+
const pkg2 = JSON.parse(readFileSync8(join8(appDir, "package.json"), "utf-8"));
|
|
2369
2668
|
const name = typeof pkg2.packageManager === "string" ? pkg2.packageManager.split("@")[0] : "";
|
|
2370
2669
|
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
|
|
2371
2670
|
} catch {
|
|
2372
2671
|
}
|
|
2373
|
-
if (
|
|
2374
|
-
if (
|
|
2375
|
-
if (
|
|
2672
|
+
if (existsSync11(join8(appDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
2673
|
+
if (existsSync11(join8(appDir, "yarn.lock"))) return "yarn";
|
|
2674
|
+
if (existsSync11(join8(appDir, "bun.lock")) || existsSync11(join8(appDir, "bun.lockb"))) return "bun";
|
|
2376
2675
|
return "npm";
|
|
2377
2676
|
}
|
|
2378
2677
|
function installCommand(appDir) {
|
|
@@ -2382,10 +2681,10 @@ function installCommand(appDir) {
|
|
|
2382
2681
|
// src/cli/lib/install-status.ts
|
|
2383
2682
|
function installState(appDir) {
|
|
2384
2683
|
if (resolvesDeepspace(appDir)) return "ready";
|
|
2385
|
-
const sentinelDir =
|
|
2386
|
-
if (
|
|
2387
|
-
if (
|
|
2388
|
-
if (!
|
|
2684
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
2685
|
+
if (existsSync12(join9(sentinelDir, "install.err"))) return "failed";
|
|
2686
|
+
if (existsSync12(join9(sentinelDir, "install.started"))) {
|
|
2687
|
+
if (!existsSync12(join9(sentinelDir, "install.done")) && installWorkerAlive(sentinelDir)) {
|
|
2389
2688
|
return "installing";
|
|
2390
2689
|
}
|
|
2391
2690
|
return "failed";
|
|
@@ -2397,42 +2696,108 @@ function tailHint(logPath, platform = process.platform) {
|
|
|
2397
2696
|
}
|
|
2398
2697
|
function ensureInstallReady(appDir) {
|
|
2399
2698
|
if (resolvesDeepspace(appDir)) return;
|
|
2400
|
-
const sentinelDir =
|
|
2401
|
-
const
|
|
2402
|
-
const
|
|
2403
|
-
const
|
|
2404
|
-
|
|
2405
|
-
if (existsSync10(errPath)) {
|
|
2406
|
-
const detail = readFileSync9(errPath, "utf-8").trimEnd();
|
|
2407
|
-
const log29 = existsSync10(logPath) ? ` Full log: ${logPath}.` : "";
|
|
2699
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
2700
|
+
const startedPath = join9(sentinelDir, "install.started");
|
|
2701
|
+
const donePath = join9(sentinelDir, "install.done");
|
|
2702
|
+
const logPath = join9(sentinelDir, "install.log");
|
|
2703
|
+
if (process.env.DEEPSPACE_NO_INSTALL) {
|
|
2408
2704
|
throw new Refusal(
|
|
2409
|
-
|
|
2410
|
-
"
|
|
2705
|
+
"Dependencies not installed, and DEEPSPACE_NO_INSTALL is set \u2014 run the install yourself, then retry.",
|
|
2706
|
+
"deps_missing",
|
|
2411
2707
|
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
2412
2708
|
);
|
|
2413
2709
|
}
|
|
2414
|
-
if (
|
|
2415
|
-
|
|
2416
|
-
const log29 = existsSync10(logPath) ? ` See what happened: ${logPath}.` : "";
|
|
2417
|
-
throw new Refusal(
|
|
2418
|
-
`The dependency install is no longer running and never finished.${log29} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
2419
|
-
"install_failed",
|
|
2420
|
-
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
2421
|
-
);
|
|
2422
|
-
}
|
|
2423
|
-
const tail = existsSync10(logPath) ? ` Tail progress: ${tailHint(logPath)}.` : "";
|
|
2710
|
+
if (existsSync12(startedPath) && !existsSync12(donePath) && installWorkerAlive(sentinelDir) && sentinelAgeMs(startedPath) < 30 * 60 * 1e3) {
|
|
2711
|
+
const tail = existsSync12(logPath) ? ` Tail progress: ${tailHint(logPath)}.` : "";
|
|
2424
2712
|
throw new Refusal(
|
|
2425
|
-
`Dependencies are still installing.${tail} Retry once it finishes.`,
|
|
2713
|
+
`Dependencies are still installing.${tail} Retry once it finishes (or remove ${sentinelDir}/install.started if no install is actually running).`,
|
|
2426
2714
|
"install_in_progress"
|
|
2427
2715
|
);
|
|
2428
2716
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
);
|
|
2717
|
+
healMissingInstall(appDir);
|
|
2718
|
+
}
|
|
2719
|
+
function healMissingInstall(appDir) {
|
|
2720
|
+
const manager = detectPackageManager(appDir);
|
|
2721
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
2722
|
+
const logPath = join9(sentinelDir, "install.log");
|
|
2723
|
+
const errPath = join9(sentinelDir, "install.err");
|
|
2724
|
+
const previousFailure = existsSync12(errPath) ? readFileSync9(errPath, "utf-8").trimEnd() : null;
|
|
2725
|
+
process.stderr.write(
|
|
2726
|
+
`Installing dependencies (${manager} install) \u2014 first use installs them. ${previousFailure ? `Retrying after: ${previousFailure}. ` : ""}Watch: ${tailHint(logPath)}
|
|
2727
|
+
`
|
|
2728
|
+
);
|
|
2729
|
+
if (process.platform === "win32") {
|
|
2730
|
+
for (const ext of [".cmd", ".bat", ".exe", ".com", ".ps1"]) {
|
|
2731
|
+
if (existsSync12(join9(appDir, `${manager}${ext}`))) {
|
|
2732
|
+
throw new Refusal(
|
|
2733
|
+
`Refusing to install: ${appDir} contains ${manager}${ext}, which Windows would execute instead of the real ${manager}. Remove it, or run the install yourself from a shell you trust.`,
|
|
2734
|
+
"install_failed"
|
|
2735
|
+
);
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
mkdirSync6(sentinelDir, { recursive: true });
|
|
2740
|
+
ensureSentinelsIgnored(appDir);
|
|
2741
|
+
rmSync4(errPath, { force: true });
|
|
2742
|
+
rmSync4(join9(sentinelDir, "install.done"), { force: true });
|
|
2743
|
+
writeFileSync5(join9(sentinelDir, "install.started"), (/* @__PURE__ */ new Date()).toISOString());
|
|
2744
|
+
writeFileSync5(join9(sentinelDir, "install.pid"), String(process.pid));
|
|
2745
|
+
const logFd = openSync(logPath, "w");
|
|
2746
|
+
let result;
|
|
2747
|
+
try {
|
|
2748
|
+
result = spawnSync2(manager, ["install"], {
|
|
2749
|
+
cwd: appDir,
|
|
2750
|
+
stdio: ["ignore", logFd, logFd],
|
|
2751
|
+
// Bounded (repo rule: no task over 5 minutes): a hung registry must
|
|
2752
|
+
// surface as a fast failure, never a stall. The log names the cure.
|
|
2753
|
+
timeout: 5 * 60 * 1e3
|
|
2754
|
+
});
|
|
2755
|
+
} finally {
|
|
2756
|
+
closeSync(logFd);
|
|
2757
|
+
}
|
|
2758
|
+
if (result.status === 0 && !result.error && resolvesDeepspace(appDir)) {
|
|
2759
|
+
writeFileSync5(join9(sentinelDir, "install.done"), (/* @__PURE__ */ new Date()).toISOString());
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
const detail = result.error ? result.error.code === "ETIMEDOUT" ? `${manager} install timed out after 5 minutes` : result.error.message : result.status === 0 ? `${manager} install completed but the \`deepspace\` package still does not resolve \u2014 is this directory the app (its package.json must depend on deepspace)?` : result.status === null ? `${manager} install was killed (${result.signal ?? "signal"})` : `${manager} install exited ${result.status}`;
|
|
2763
|
+
writeFileSync5(errPath, detail);
|
|
2764
|
+
let logTail = "";
|
|
2765
|
+
try {
|
|
2766
|
+
const log28 = readFileSync9(logPath, "utf-8");
|
|
2767
|
+
const tail = log28.slice(-1500).trimEnd();
|
|
2768
|
+
if (tail) logTail = `
|
|
2769
|
+
--- install log (tail) ---
|
|
2770
|
+
${tail}`;
|
|
2771
|
+
} catch {
|
|
2772
|
+
}
|
|
2773
|
+
throw new Refusal(
|
|
2774
|
+
`Dependency install failed: ${detail}. Full log: ${logPath}. It will be retried on the next command, or run \`${manager} install\` yourself.${logTail}`,
|
|
2775
|
+
"install_failed",
|
|
2776
|
+
{ action: { cwd: appDir, argv: [manager, "install"] } }
|
|
2777
|
+
);
|
|
2778
|
+
}
|
|
2779
|
+
function sentinelAgeMs(path) {
|
|
2780
|
+
try {
|
|
2781
|
+
return Date.now() - statSync2(path).mtimeMs;
|
|
2782
|
+
} catch {
|
|
2783
|
+
return Number.POSITIVE_INFINITY;
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
function ensureSentinelsIgnored(appDir) {
|
|
2787
|
+
try {
|
|
2788
|
+
const check = spawnSync2("git", ["-C", appDir, "check-ignore", "-q", ".deepspace"], {
|
|
2789
|
+
stdio: "ignore"
|
|
2790
|
+
});
|
|
2791
|
+
if (check.status === 0 || check.error) return;
|
|
2792
|
+
const excludePath = spawnSync2("git", ["-C", appDir, "rev-parse", "--git-path", "info/exclude"], {
|
|
2793
|
+
encoding: "utf-8"
|
|
2794
|
+
});
|
|
2795
|
+
if (excludePath.status !== 0) return;
|
|
2796
|
+
const target2 = resolve7(appDir, excludePath.stdout.trim());
|
|
2797
|
+
mkdirSync6(dirname6(target2), { recursive: true });
|
|
2798
|
+
appendFileSync2(target2, "\n# deepspace CLI state (added by first-use install)\n.deepspace/\n");
|
|
2799
|
+
} catch {
|
|
2800
|
+
}
|
|
2436
2801
|
}
|
|
2437
2802
|
function resolvesDeepspace(fromDir) {
|
|
2438
2803
|
let dir = canonicalPath2(fromDir);
|
|
@@ -2442,7 +2807,7 @@ function resolvesDeepspace(fromDir) {
|
|
|
2442
2807
|
} catch {
|
|
2443
2808
|
}
|
|
2444
2809
|
for (; ; ) {
|
|
2445
|
-
if (
|
|
2810
|
+
if (existsSync12(join9(dir, "node_modules", "deepspace", "package.json"))) return true;
|
|
2446
2811
|
if (dir === checkoutRoot) return false;
|
|
2447
2812
|
const parent = dirname6(dir);
|
|
2448
2813
|
if (parent === dir) return false;
|
|
@@ -2453,13 +2818,15 @@ function canonicalPath2(path) {
|
|
|
2453
2818
|
try {
|
|
2454
2819
|
return realpathSync3(path);
|
|
2455
2820
|
} catch {
|
|
2456
|
-
return
|
|
2821
|
+
return resolve7(path);
|
|
2457
2822
|
}
|
|
2458
2823
|
}
|
|
2459
2824
|
function installWorkerAlive(sentinelDir) {
|
|
2460
|
-
const pidPath =
|
|
2461
|
-
if (!
|
|
2462
|
-
|
|
2825
|
+
const pidPath = join9(sentinelDir, "install.pid");
|
|
2826
|
+
if (!existsSync12(pidPath)) return false;
|
|
2827
|
+
const pid = Number(readFileSync9(pidPath, "utf-8").trim());
|
|
2828
|
+
if (pid === process.pid) return false;
|
|
2829
|
+
return processAlive(pid);
|
|
2463
2830
|
}
|
|
2464
2831
|
function processAlive(pid) {
|
|
2465
2832
|
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
@@ -2473,9 +2840,9 @@ function processAlive(pid) {
|
|
|
2473
2840
|
|
|
2474
2841
|
// src/cli/lib/preflight.ts
|
|
2475
2842
|
import { execFileSync } from "child_process";
|
|
2476
|
-
import { existsSync as
|
|
2843
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2477
2844
|
import * as nodeModule from "module";
|
|
2478
|
-
import { resolve as
|
|
2845
|
+
import { resolve as resolve8 } from "path";
|
|
2479
2846
|
function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.registerHooks === "function", version = process.versions.node) {
|
|
2480
2847
|
const [major, minor] = version.split(".").map(Number);
|
|
2481
2848
|
const supportedLine = major === 22 && minor >= 15 || major === 24 || major === 26;
|
|
@@ -2488,7 +2855,7 @@ function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.regi
|
|
|
2488
2855
|
var DLL_NOT_FOUND = -1073741515;
|
|
2489
2856
|
function preflightWindowsWorkerd(appDir) {
|
|
2490
2857
|
if (process.platform !== "win32") return;
|
|
2491
|
-
const workerdExe =
|
|
2858
|
+
const workerdExe = resolve8(
|
|
2492
2859
|
appDir,
|
|
2493
2860
|
"node_modules",
|
|
2494
2861
|
"@cloudflare",
|
|
@@ -2496,7 +2863,7 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
2496
2863
|
"bin",
|
|
2497
2864
|
"workerd.exe"
|
|
2498
2865
|
);
|
|
2499
|
-
if (!
|
|
2866
|
+
if (!existsSync13(workerdExe)) return;
|
|
2500
2867
|
try {
|
|
2501
2868
|
execFileSync(workerdExe, ["--help"], { stdio: "pipe", timeout: 1e4 });
|
|
2502
2869
|
return;
|
|
@@ -2511,8 +2878,8 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
2511
2878
|
}
|
|
2512
2879
|
|
|
2513
2880
|
// src/cli/lib/macos-junk.ts
|
|
2514
|
-
import { readdirSync as readdirSync2, rmSync as
|
|
2515
|
-
import { join as
|
|
2881
|
+
import { readdirSync as readdirSync2, rmSync as rmSync5 } from "fs";
|
|
2882
|
+
import { join as join10 } from "path";
|
|
2516
2883
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".wrangler", ".vite"]);
|
|
2517
2884
|
function removeMacosJunk(dir) {
|
|
2518
2885
|
let removed = 0;
|
|
@@ -2523,14 +2890,14 @@ function removeMacosJunk(dir) {
|
|
|
2523
2890
|
return removed;
|
|
2524
2891
|
}
|
|
2525
2892
|
for (const entry of entries) {
|
|
2526
|
-
const path =
|
|
2893
|
+
const path = join10(dir, entry.name);
|
|
2527
2894
|
if (entry.isDirectory()) {
|
|
2528
2895
|
if (!SKIP_DIRS.has(entry.name)) removed += removeMacosJunk(path);
|
|
2529
2896
|
continue;
|
|
2530
2897
|
}
|
|
2531
2898
|
if (entry.name === ".DS_Store" || entry.name.startsWith("._")) {
|
|
2532
2899
|
try {
|
|
2533
|
-
|
|
2900
|
+
rmSync5(path, { force: true });
|
|
2534
2901
|
removed++;
|
|
2535
2902
|
} catch {
|
|
2536
2903
|
}
|
|
@@ -3105,8 +3472,8 @@ async function refreshSecretsCache(deployUrl, token, appId, wranglerEnv, selecte
|
|
|
3105
3472
|
}
|
|
3106
3473
|
|
|
3107
3474
|
// src/cli/lib/schema-lint.ts
|
|
3108
|
-
import { existsSync as
|
|
3109
|
-
import { join as
|
|
3475
|
+
import { existsSync as existsSync14 } from "fs";
|
|
3476
|
+
import { join as join11 } from "path";
|
|
3110
3477
|
import { pathToFileURL } from "url";
|
|
3111
3478
|
|
|
3112
3479
|
// src/server/schemas/registry.ts
|
|
@@ -3139,7 +3506,7 @@ function lintSchema(schema) {
|
|
|
3139
3506
|
const offendingRoles = Object.entries(schema.permissions).filter(([, perms]) => perms.read === true).map(([role]) => role);
|
|
3140
3507
|
if (offendingRoles.length > 0) {
|
|
3141
3508
|
const usesAware = Object.values(schema.permissions).some(
|
|
3142
|
-
(
|
|
3509
|
+
(p35) => typeof p35.read === "string" && VISIBILITY_AWARE.includes(p35.read)
|
|
3143
3510
|
);
|
|
3144
3511
|
if (!usesAware) {
|
|
3145
3512
|
warnings.push(
|
|
@@ -3175,7 +3542,7 @@ function lintSchemaSet(schemas) {
|
|
|
3175
3542
|
const warnings = [];
|
|
3176
3543
|
if (!schemas.some((schema) => schema.name === "team_members")) {
|
|
3177
3544
|
for (const schema of schemas) {
|
|
3178
|
-
const teamRoles = Object.entries(schema.permissions).filter(([,
|
|
3545
|
+
const teamRoles = Object.entries(schema.permissions).filter(([, p35]) => [p35.read, p35.update, p35.delete].includes("team")).map(([role]) => role);
|
|
3179
3546
|
if (teamRoles.length > 0) {
|
|
3180
3547
|
warnings.push(
|
|
3181
3548
|
`[${schema.name}] role(s) ${teamRoles.join(", ")} use the 'team' permission level, but no 'team_members' collection is registered. 'team' resolves membership from team_members (teamId, userId, status), so without it those roles are denied everything \u2014 silently. Register a team_members schema or use another level.`
|
|
@@ -3188,12 +3555,12 @@ function lintSchemaSet(schemas) {
|
|
|
3188
3555
|
|
|
3189
3556
|
// src/cli/lib/schema-lint.ts
|
|
3190
3557
|
async function lintProjectSchemas(appDir) {
|
|
3191
|
-
const schemasPath =
|
|
3192
|
-
if (!
|
|
3558
|
+
const schemasPath = join11(appDir, "src", "schemas.ts");
|
|
3559
|
+
if (!existsSync14(schemasPath)) return null;
|
|
3193
3560
|
let schemas;
|
|
3194
3561
|
try {
|
|
3195
3562
|
const esbuild = await import("esbuild");
|
|
3196
|
-
const outFile =
|
|
3563
|
+
const outFile = join11(appDir, ".wrangler", "deploy", "schemas.lint.bundle.mjs");
|
|
3197
3564
|
await esbuild.build({
|
|
3198
3565
|
entryPoints: [schemasPath],
|
|
3199
3566
|
bundle: true,
|
|
@@ -3242,8 +3609,33 @@ function formatSchemaLintFindings(findings) {
|
|
|
3242
3609
|
}
|
|
3243
3610
|
|
|
3244
3611
|
// src/cli/lib/port.ts
|
|
3245
|
-
import {
|
|
3612
|
+
import { Socket } from "net";
|
|
3246
3613
|
var DEFAULT_PORT = 5173;
|
|
3614
|
+
async function ensurePortFree(port, host, waitMs = 15e3) {
|
|
3615
|
+
if (!await isPortListening(port, host)) return;
|
|
3616
|
+
if (!await isHttpResponding(port)) {
|
|
3617
|
+
const deadline = Date.now() + waitMs;
|
|
3618
|
+
while (Date.now() < deadline) {
|
|
3619
|
+
await new Promise((resolve23) => setTimeout(resolve23, 500));
|
|
3620
|
+
if (!await isPortListening(port, host)) return;
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
const killArgv = port === DEFAULT_PORT ? ["deepspace", "dev", "kill"] : ["deepspace", "dev", "kill", "--port", String(port)];
|
|
3624
|
+
throw new Refusal(
|
|
3625
|
+
`Port ${port} is already in use.
|
|
3626
|
+
Free it with \`${killArgv.join(" ")}\`, or use another port: \`--port <other>\`.`,
|
|
3627
|
+
"port_in_use",
|
|
3628
|
+
{ extra: { port } }
|
|
3629
|
+
);
|
|
3630
|
+
}
|
|
3631
|
+
async function isHttpResponding(port, timeoutMs = 2e3) {
|
|
3632
|
+
try {
|
|
3633
|
+
await fetch(`http://127.0.0.1:${port}/`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
3634
|
+
return true;
|
|
3635
|
+
} catch {
|
|
3636
|
+
return false;
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3247
3639
|
function isPortListening(port, host, timeoutMs = 500) {
|
|
3248
3640
|
const target2 = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
|
|
3249
3641
|
return new Promise((resolve23) => {
|
|
@@ -3280,7 +3672,7 @@ function resolvePort(arg) {
|
|
|
3280
3672
|
// src/cli/commands/dev.ts
|
|
3281
3673
|
var DEFAULT_DEV_HOST = "0.0.0.0";
|
|
3282
3674
|
var READINESS_TIMEOUT_MS = 12e4;
|
|
3283
|
-
var
|
|
3675
|
+
var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
3284
3676
|
function noAppDirRefusal(start) {
|
|
3285
3677
|
const children = findChildApps(start);
|
|
3286
3678
|
const lines = [`No wrangler.toml found at or above ${start}.`];
|
|
@@ -3330,7 +3722,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
3330
3722
|
preflightNodeVersion("dev start");
|
|
3331
3723
|
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
3332
3724
|
const host = typeof args.host === "string" && args.host.trim() ? args.host.trim() : DEFAULT_DEV_HOST;
|
|
3333
|
-
const start =
|
|
3725
|
+
const start = resolve9(args.dir ?? ".");
|
|
3334
3726
|
const appDir = findAppDir(start);
|
|
3335
3727
|
if (!appDir) {
|
|
3336
3728
|
throw noAppDirRefusal(start);
|
|
@@ -3360,18 +3752,13 @@ var dev_default = defineDeepspaceCommand({
|
|
|
3360
3752
|
);
|
|
3361
3753
|
} else if (!args.json) {
|
|
3362
3754
|
console.warn(
|
|
3363
|
-
`Warning: could not update ${
|
|
3755
|
+
`Warning: could not update ${join12(worktree.mainRepoRoot, ".claude", "launch.json")} (malformed or unwritable) \u2014 the Claude preview tool may serve the main repo's code instead of this worktree.`
|
|
3364
3756
|
);
|
|
3365
3757
|
}
|
|
3366
3758
|
}
|
|
3367
3759
|
writeLaunchConfigIfMissing(appDir, detectAppName(appDir) ?? basename2(appDir), port, {
|
|
3368
3760
|
updatePort: explicitPort
|
|
3369
3761
|
});
|
|
3370
|
-
ensureInstallReady(appDir);
|
|
3371
|
-
const lintFindings = await lintProjectSchemas(appDir);
|
|
3372
|
-
if (lintFindings && lintFindings.length > 0 && !args.json) {
|
|
3373
|
-
for (const line of formatSchemaLintFindings(lintFindings)) console.warn(line);
|
|
3374
|
-
}
|
|
3375
3762
|
let token;
|
|
3376
3763
|
try {
|
|
3377
3764
|
token = await ensureToken();
|
|
@@ -3394,11 +3781,24 @@ var dev_default = defineDeepspaceCommand({
|
|
|
3394
3781
|
if (wranglerEnv) say(`Wrangler env: ${wranglerEnv}`);
|
|
3395
3782
|
say(`Port: ${port}`);
|
|
3396
3783
|
if (host !== DEFAULT_DEV_HOST) say(`Host: ${host}`);
|
|
3784
|
+
ensureInstallReady(appDir);
|
|
3785
|
+
const lintFindings = await lintProjectSchemas(appDir);
|
|
3786
|
+
if (lintFindings && lintFindings.length > 0 && !args.json) {
|
|
3787
|
+
for (const line of formatSchemaLintFindings(lintFindings)) console.warn(line);
|
|
3788
|
+
}
|
|
3397
3789
|
let generatedSecretsCache;
|
|
3398
|
-
|
|
3790
|
+
let appIdForSecrets = null;
|
|
3791
|
+
try {
|
|
3792
|
+
appIdForSecrets = (await ensureAppRegistered(appDir, token, wranglerEnv))?.appId ?? null;
|
|
3793
|
+
} catch (registrationError) {
|
|
3794
|
+
process.stderr.write(
|
|
3795
|
+
`warning: could not register the app (${registrationError instanceof Error ? registrationError.message.split("\n")[0] : String(registrationError)}) \u2014 continuing without app secrets.
|
|
3796
|
+
`
|
|
3797
|
+
);
|
|
3798
|
+
}
|
|
3399
3799
|
if (appIdForSecrets) {
|
|
3400
3800
|
try {
|
|
3401
|
-
const refreshed = await refreshSecretsCache(
|
|
3801
|
+
const refreshed = await refreshSecretsCache(DEPLOY_URL2, token, appIdForSecrets, wranglerEnv);
|
|
3402
3802
|
generatedSecretsCache = refreshed.rendered;
|
|
3403
3803
|
if (refreshed.summary) say(refreshed.summary);
|
|
3404
3804
|
} catch (err) {
|
|
@@ -3413,16 +3813,11 @@ var dev_default = defineDeepspaceCommand({
|
|
|
3413
3813
|
});
|
|
3414
3814
|
const wranglerConfig = prepareWranglerEnvConfig(appDir, wranglerEnv);
|
|
3415
3815
|
preflightWindowsWorkerd(appDir);
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3816
|
+
try {
|
|
3817
|
+
await ensurePortFree(port, host);
|
|
3818
|
+
} catch (err) {
|
|
3419
3819
|
wranglerConfig.cleanup();
|
|
3420
|
-
throw
|
|
3421
|
-
`Port ${port} is already in use.
|
|
3422
|
-
Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --port <other>\`.`,
|
|
3423
|
-
"port_in_use",
|
|
3424
|
-
{ extra: { port } }
|
|
3425
|
-
);
|
|
3820
|
+
throw err;
|
|
3426
3821
|
}
|
|
3427
3822
|
const url = `http://${host === DEFAULT_DEV_HOST ? "localhost" : host}:${port}`;
|
|
3428
3823
|
say("Starting dev server...\n");
|
|
@@ -3481,7 +3876,7 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
3481
3876
|
});
|
|
3482
3877
|
|
|
3483
3878
|
// src/cli/commands/kill.ts
|
|
3484
|
-
import { spawnSync as
|
|
3879
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
3485
3880
|
import { readFileSync as readFileSync10, readdirSync as readdirSync3, readlinkSync } from "fs";
|
|
3486
3881
|
import { setTimeout as wait } from "timers/promises";
|
|
3487
3882
|
var SIGTERM_GRACE_MS = 1500;
|
|
@@ -3616,7 +4011,7 @@ function listenerPids(port) {
|
|
|
3616
4011
|
}
|
|
3617
4012
|
function runLsof(port) {
|
|
3618
4013
|
try {
|
|
3619
|
-
const r =
|
|
4014
|
+
const r = spawnSync3("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], {
|
|
3620
4015
|
encoding: "utf-8"
|
|
3621
4016
|
});
|
|
3622
4017
|
if (r.error) return { pids: [], ran: false };
|
|
@@ -3701,7 +4096,7 @@ function sweepByName(names) {
|
|
|
3701
4096
|
}
|
|
3702
4097
|
function runPgrep(pgrepArgs) {
|
|
3703
4098
|
try {
|
|
3704
|
-
const r =
|
|
4099
|
+
const r = spawnSync3("pgrep", pgrepArgs, { encoding: "utf-8" });
|
|
3705
4100
|
if (r.error) return { pids: [], ran: false };
|
|
3706
4101
|
return {
|
|
3707
4102
|
pids: r.status === 0 ? parsePidLines(r.stdout) : [],
|
|
@@ -3713,7 +4108,7 @@ function runPgrep(pgrepArgs) {
|
|
|
3713
4108
|
}
|
|
3714
4109
|
function run(cmd) {
|
|
3715
4110
|
try {
|
|
3716
|
-
const r =
|
|
4111
|
+
const r = spawnSync3(cmd[0], cmd.slice(1), { encoding: "utf-8" });
|
|
3717
4112
|
if (r.status !== 0) return [];
|
|
3718
4113
|
return parsePidLines(r.stdout);
|
|
3719
4114
|
} catch {
|
|
@@ -3734,7 +4129,7 @@ function readPowershellPids(stdout) {
|
|
|
3734
4129
|
function runPowershellChecked(script) {
|
|
3735
4130
|
const exe = pwshOrPowershell();
|
|
3736
4131
|
try {
|
|
3737
|
-
const r =
|
|
4132
|
+
const r = spawnSync3(exe, ["-NoProfile", "-NonInteractive", "-Command", withSentinel(script)], {
|
|
3738
4133
|
encoding: "utf-8"
|
|
3739
4134
|
});
|
|
3740
4135
|
if (r.error) return { pids: [], ran: false };
|
|
@@ -3744,7 +4139,7 @@ function runPowershellChecked(script) {
|
|
|
3744
4139
|
}
|
|
3745
4140
|
}
|
|
3746
4141
|
function pwshOrPowershell() {
|
|
3747
|
-
const probe =
|
|
4142
|
+
const probe = spawnSync3("pwsh", ["-NoProfile", "-Command", "$PSVersionTable.PSVersion.Major"], {
|
|
3748
4143
|
encoding: "utf-8"
|
|
3749
4144
|
});
|
|
3750
4145
|
if (probe.status === 0) return "pwsh";
|
|
@@ -3772,12 +4167,12 @@ function isAlive(pid) {
|
|
|
3772
4167
|
|
|
3773
4168
|
// src/cli/commands/test.ts
|
|
3774
4169
|
import { readdirSync as readdirSync4 } from "fs";
|
|
3775
|
-
import { join as
|
|
3776
|
-
import { sync as
|
|
4170
|
+
import { join as join14, relative as relative2, resolve as resolve10, sep as sep2 } from "path";
|
|
4171
|
+
import { sync as spawnSync5 } from "cross-spawn";
|
|
3777
4172
|
|
|
3778
4173
|
// src/cli/lib/playwright.ts
|
|
3779
4174
|
import { execSync } from "child_process";
|
|
3780
|
-
import { sync as
|
|
4175
|
+
import { sync as spawnSync4 } from "cross-spawn";
|
|
3781
4176
|
var childStdoutToStderr = false;
|
|
3782
4177
|
function routeChildStdoutToStderr(enabled) {
|
|
3783
4178
|
childStdoutToStderr = enabled;
|
|
@@ -3800,7 +4195,7 @@ function ensurePlaywright(appDir) {
|
|
|
3800
4195
|
"Installing Chromium and its system libraries \u2014 this runs `apt-get` as root and can take several minutes on a cold machine."
|
|
3801
4196
|
);
|
|
3802
4197
|
}
|
|
3803
|
-
const result =
|
|
4198
|
+
const result = spawnSync4("npx", ["playwright", ...args], {
|
|
3804
4199
|
cwd: appDir,
|
|
3805
4200
|
stdio: childStdio()
|
|
3806
4201
|
});
|
|
@@ -3816,10 +4211,10 @@ function ensurePlaywright(appDir) {
|
|
|
3816
4211
|
import { readFileSync as readFileSync12 } from "fs";
|
|
3817
4212
|
|
|
3818
4213
|
// src/testing/accounts.ts
|
|
3819
|
-
import { existsSync as
|
|
4214
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11 } from "fs";
|
|
3820
4215
|
import { homedir as homedir2 } from "os";
|
|
3821
|
-
import { join as
|
|
3822
|
-
var TEST_ACCOUNTS_PATH =
|
|
4216
|
+
import { join as join13 } from "path";
|
|
4217
|
+
var TEST_ACCOUNTS_PATH = join13(homedir2(), ".deepspace", "test-accounts.json");
|
|
3823
4218
|
var PRODUCTION_AUTH_ORIGIN = "https://auth.deep.space";
|
|
3824
4219
|
var STAGING_AUTH_ORIGIN = "https://auth.deepspacesites.com";
|
|
3825
4220
|
function currentTestAccountScope(env = process.env) {
|
|
@@ -3884,7 +4279,7 @@ function removeTestAccounts(ids, emails = [], scope = currentTestAccountScope())
|
|
|
3884
4279
|
});
|
|
3885
4280
|
}
|
|
3886
4281
|
function readTestAccountStore() {
|
|
3887
|
-
if (!
|
|
4282
|
+
if (!existsSync15(TEST_ACCOUNTS_PATH)) return { version: 2, scopes: {} };
|
|
3888
4283
|
try {
|
|
3889
4284
|
const raw = JSON.parse(readFileSync11(TEST_ACCOUNTS_PATH, "utf-8"));
|
|
3890
4285
|
if (Array.isArray(raw)) return { version: 2, scopes: {} };
|
|
@@ -3909,7 +4304,7 @@ function validTestAccounts(value) {
|
|
|
3909
4304
|
);
|
|
3910
4305
|
}
|
|
3911
4306
|
function saveTestAccountStore(store) {
|
|
3912
|
-
|
|
4307
|
+
mkdirSync7(join13(homedir2(), ".deepspace"), { recursive: true });
|
|
3913
4308
|
writeSecretFileSync(TEST_ACCOUNTS_PATH, JSON.stringify(store, null, 2));
|
|
3914
4309
|
}
|
|
3915
4310
|
|
|
@@ -3987,7 +4382,7 @@ async function syncTestAccountStore() {
|
|
|
3987
4382
|
}
|
|
3988
4383
|
|
|
3989
4384
|
// src/cli/commands/test.ts
|
|
3990
|
-
var
|
|
4385
|
+
var DEPLOY_URL3 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
3991
4386
|
var PLAYWRIGHT_OUTPUT_DIR = ".deepspace/test-results";
|
|
3992
4387
|
var DEFAULT_SUITE_SPECS = ["tests/smoke.spec.ts", "tests/api.spec.ts"];
|
|
3993
4388
|
function specsSkippedByDefaultSuite(appDir) {
|
|
@@ -4001,14 +4396,14 @@ function specsSkippedByDefaultSuite(appDir) {
|
|
|
4001
4396
|
}
|
|
4002
4397
|
for (const entry of entries) {
|
|
4003
4398
|
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
4004
|
-
const full =
|
|
4399
|
+
const full = join14(dir, entry.name);
|
|
4005
4400
|
if (entry.isDirectory()) walk(full);
|
|
4006
4401
|
else if (entry.name.endsWith(".spec.ts")) {
|
|
4007
|
-
found.push(
|
|
4402
|
+
found.push(relative2(appDir, full).split(sep2).join("/"));
|
|
4008
4403
|
}
|
|
4009
4404
|
}
|
|
4010
4405
|
};
|
|
4011
|
-
walk(
|
|
4406
|
+
walk(join14(appDir, "tests"));
|
|
4012
4407
|
return found.filter((spec) => !DEFAULT_SUITE_SPECS.includes(spec)).sort();
|
|
4013
4408
|
}
|
|
4014
4409
|
function playwrightTestArgs(testFiles, flags = {}) {
|
|
@@ -4077,7 +4472,7 @@ var test_default = defineDeepspaceCommand({
|
|
|
4077
4472
|
project: typeof args.project === "string" && args.project ? args.project : void 0,
|
|
4078
4473
|
headed: Boolean(args.headed)
|
|
4079
4474
|
};
|
|
4080
|
-
const start =
|
|
4475
|
+
const start = resolve10(".");
|
|
4081
4476
|
const appDir = findAppDir(start);
|
|
4082
4477
|
if (!appDir) {
|
|
4083
4478
|
throw noAppDirRefusal(start);
|
|
@@ -4085,7 +4480,7 @@ var test_default = defineDeepspaceCommand({
|
|
|
4085
4480
|
const configuredPort = Boolean(args.port) || Boolean(process.env.DEEPSPACE_PORT);
|
|
4086
4481
|
const worktreePort = configuredPort ? null : resolveWorktreePort(appDir);
|
|
4087
4482
|
const port = worktreePort ?? resolvePort(args.port);
|
|
4088
|
-
|
|
4483
|
+
if (suite !== "unit") await ensurePortFree(port, "0.0.0.0");
|
|
4089
4484
|
let token;
|
|
4090
4485
|
let ownerId;
|
|
4091
4486
|
try {
|
|
@@ -4099,11 +4494,20 @@ var test_default = defineDeepspaceCommand({
|
|
|
4099
4494
|
{ action: cliAction("deepspace", "auth", "login") }
|
|
4100
4495
|
);
|
|
4101
4496
|
}
|
|
4497
|
+
ensureInstallReady(appDir);
|
|
4102
4498
|
let generatedSecretsCache;
|
|
4103
|
-
|
|
4499
|
+
let appIdForSecrets = null;
|
|
4500
|
+
try {
|
|
4501
|
+
appIdForSecrets = (await ensureAppRegistered(appDir, token, wranglerEnv))?.appId ?? null;
|
|
4502
|
+
} catch (registrationError) {
|
|
4503
|
+
process.stderr.write(
|
|
4504
|
+
`warning: could not register the app (${registrationError instanceof Error ? registrationError.message.split("\n")[0] : String(registrationError)}) \u2014 continuing without app secrets.
|
|
4505
|
+
`
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4104
4508
|
if (appIdForSecrets) {
|
|
4105
4509
|
try {
|
|
4106
|
-
const refreshed = await refreshSecretsCache(
|
|
4510
|
+
const refreshed = await refreshSecretsCache(DEPLOY_URL3, token, appIdForSecrets, wranglerEnv);
|
|
4107
4511
|
generatedSecretsCache = refreshed.rendered;
|
|
4108
4512
|
if (refreshed.summary) say(refreshed.summary);
|
|
4109
4513
|
} catch (err) {
|
|
@@ -4144,7 +4548,9 @@ var test_default = defineDeepspaceCommand({
|
|
|
4144
4548
|
break;
|
|
4145
4549
|
case "unit":
|
|
4146
4550
|
if (forwarded.grep || forwarded.project || forwarded.headed) {
|
|
4147
|
-
console.error(
|
|
4551
|
+
console.error(
|
|
4552
|
+
"note: --grep/--project/--headed apply to Playwright suites and are ignored by `unit`"
|
|
4553
|
+
);
|
|
4148
4554
|
}
|
|
4149
4555
|
exitCode = runVitest(appDir, wranglerEnv);
|
|
4150
4556
|
break;
|
|
@@ -4189,7 +4595,7 @@ function runSuite(appDir, argv, wranglerEnv, extraEnv = {}) {
|
|
|
4189
4595
|
return 1;
|
|
4190
4596
|
}
|
|
4191
4597
|
try {
|
|
4192
|
-
const result =
|
|
4598
|
+
const result = spawnSync5("npx", argv, {
|
|
4193
4599
|
cwd: appDir,
|
|
4194
4600
|
// Under --json the child's stdout is routed to our stderr: the live
|
|
4195
4601
|
// suite output stays visible and stdout stays a single JSON line.
|
|
@@ -4212,8 +4618,20 @@ function runVitest(appDir, wranglerEnv) {
|
|
|
4212
4618
|
}
|
|
4213
4619
|
|
|
4214
4620
|
// src/cli/commands/screenshot.ts
|
|
4215
|
-
import { sync as
|
|
4216
|
-
import { resolve as
|
|
4621
|
+
import { sync as spawnSync6 } from "cross-spawn";
|
|
4622
|
+
import { resolve as resolve11 } from "path";
|
|
4623
|
+
var DEFAULT_SCREENSHOT_WAIT_MS = 1e3;
|
|
4624
|
+
function normalizeWaitTimeout(value) {
|
|
4625
|
+
if (value === void 0) return DEFAULT_SCREENSHOT_WAIT_MS;
|
|
4626
|
+
const text2 = String(value).trim();
|
|
4627
|
+
if (!/^\d+$/.test(text2) || !Number.isSafeInteger(Number(text2))) {
|
|
4628
|
+
throw new Refusal(
|
|
4629
|
+
"Screenshot wait timeout must be a non-negative whole number of milliseconds.",
|
|
4630
|
+
"invalid_wait_timeout"
|
|
4631
|
+
);
|
|
4632
|
+
}
|
|
4633
|
+
return Number(text2);
|
|
4634
|
+
}
|
|
4217
4635
|
function normalizeViewportSize(value) {
|
|
4218
4636
|
if (value === void 0) return void 0;
|
|
4219
4637
|
const match = /^(\d+)\s*[x,]\s*(\d+)$/i.exec(String(value).trim());
|
|
@@ -4255,25 +4673,32 @@ var screenshot_default = defineDeepspaceCommand({
|
|
|
4255
4673
|
},
|
|
4256
4674
|
"wait-for-timeout": {
|
|
4257
4675
|
type: "string",
|
|
4258
|
-
description:
|
|
4676
|
+
description: `Milliseconds to settle before capture (default ${DEFAULT_SCREENSHOT_WAIT_MS}, 0 disables)`,
|
|
4677
|
+
required: false
|
|
4678
|
+
},
|
|
4679
|
+
"wait-for-selector": {
|
|
4680
|
+
type: "string",
|
|
4681
|
+
description: "Visible selector to await before capture (off by default)",
|
|
4259
4682
|
required: false
|
|
4260
4683
|
}
|
|
4261
4684
|
},
|
|
4262
4685
|
async run({ args }) {
|
|
4686
|
+
routeChildStdoutToStderr(args.json === true);
|
|
4263
4687
|
const viewport = normalizeViewportSize(args.viewport);
|
|
4264
|
-
const
|
|
4688
|
+
const waitForTimeout = normalizeWaitTimeout(args["wait-for-timeout"]);
|
|
4689
|
+
const appDir = resolve11(".");
|
|
4265
4690
|
ensurePlaywright(appDir);
|
|
4266
4691
|
const url = String(args.url);
|
|
4267
4692
|
const output = String(args.output);
|
|
4268
4693
|
const playwrightArgs = ["playwright", "screenshot", url, output];
|
|
4694
|
+
const waitForSelector = args["wait-for-selector"] === void 0 ? "" : String(args["wait-for-selector"]);
|
|
4269
4695
|
if (args["full-page"]) playwrightArgs.push("--full-page");
|
|
4270
4696
|
if (viewport) playwrightArgs.push("--viewport-size", viewport);
|
|
4271
|
-
if (
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
const result = spawnSync5("npx", playwrightArgs, {
|
|
4697
|
+
if (waitForSelector) playwrightArgs.push("--wait-for-selector", waitForSelector);
|
|
4698
|
+
if (waitForTimeout !== 0) playwrightArgs.push("--wait-for-timeout", String(waitForTimeout));
|
|
4699
|
+
const result = spawnSync6("npx", playwrightArgs, {
|
|
4275
4700
|
cwd: appDir,
|
|
4276
|
-
stdio:
|
|
4701
|
+
stdio: childStdio()
|
|
4277
4702
|
});
|
|
4278
4703
|
if (result.status !== 0) {
|
|
4279
4704
|
throw new Refusal(
|
|
@@ -4282,7 +4707,14 @@ var screenshot_default = defineDeepspaceCommand({
|
|
|
4282
4707
|
);
|
|
4283
4708
|
}
|
|
4284
4709
|
return {
|
|
4285
|
-
data: {
|
|
4710
|
+
data: {
|
|
4711
|
+
url,
|
|
4712
|
+
output,
|
|
4713
|
+
fullPage: Boolean(args["full-page"]),
|
|
4714
|
+
viewport: viewport ?? null,
|
|
4715
|
+
waitForSelector: waitForSelector || null,
|
|
4716
|
+
waitForTimeout
|
|
4717
|
+
}
|
|
4286
4718
|
};
|
|
4287
4719
|
}
|
|
4288
4720
|
});
|
|
@@ -4303,8 +4735,13 @@ var create = defineDeepspaceCommand({
|
|
|
4303
4735
|
},
|
|
4304
4736
|
password: {
|
|
4305
4737
|
type: "string",
|
|
4306
|
-
description: "Password (min 8 characters)",
|
|
4307
|
-
required:
|
|
4738
|
+
description: "Password (min 8 characters). Prefer --password-stdin \u2014 argv is visible in `ps` and shell history",
|
|
4739
|
+
required: false
|
|
4740
|
+
},
|
|
4741
|
+
"password-stdin": {
|
|
4742
|
+
type: "boolean",
|
|
4743
|
+
description: "Read the password from stdin (one trailing newline stripped)",
|
|
4744
|
+
default: false
|
|
4308
4745
|
},
|
|
4309
4746
|
name: {
|
|
4310
4747
|
type: "string",
|
|
@@ -4320,7 +4757,26 @@ var create = defineDeepspaceCommand({
|
|
|
4320
4757
|
async run({ args }) {
|
|
4321
4758
|
await ensureToken();
|
|
4322
4759
|
const email = args.email;
|
|
4323
|
-
|
|
4760
|
+
if (args["password-stdin"] && args.password !== void 0) {
|
|
4761
|
+
throw new Refusal("Pass either --password or --password-stdin, not both.", "invalid_flags");
|
|
4762
|
+
}
|
|
4763
|
+
let password;
|
|
4764
|
+
if (args["password-stdin"]) {
|
|
4765
|
+
if (process.stdin.isTTY) {
|
|
4766
|
+
throw new Refusal(
|
|
4767
|
+
'--password-stdin expects the password piped on stdin, e.g. `printf %s "$PW" | deepspace test accounts create --email bot@deepspace.test --password-stdin`.',
|
|
4768
|
+
"password_stdin_needs_pipe"
|
|
4769
|
+
);
|
|
4770
|
+
}
|
|
4771
|
+
password = (await readStreamText(process.stdin, MAX_STDIN_BYTES)).replace(/\r?\n$/, "");
|
|
4772
|
+
} else if (typeof args.password === "string") {
|
|
4773
|
+
password = args.password;
|
|
4774
|
+
} else {
|
|
4775
|
+
throw new Refusal(
|
|
4776
|
+
"A password is required: pipe it with --password-stdin (preferred), or pass --password.",
|
|
4777
|
+
"missing_password"
|
|
4778
|
+
);
|
|
4779
|
+
}
|
|
4324
4780
|
const name = args.name ?? email.split("@")[0];
|
|
4325
4781
|
const label = args.label;
|
|
4326
4782
|
let account;
|
|
@@ -4688,7 +5144,7 @@ var test_accounts_default = defineCommand2({
|
|
|
4688
5144
|
// src/cli/commands/deploy.ts
|
|
4689
5145
|
import * as p12 from "@clack/prompts";
|
|
4690
5146
|
import { defineCommand as defineCommand3 } from "citty";
|
|
4691
|
-
import { join as
|
|
5147
|
+
import { join as join21, resolve as resolve15 } from "path";
|
|
4692
5148
|
|
|
4693
5149
|
// src/cli/lib/edge-propagation.ts
|
|
4694
5150
|
import { request as httpRequest } from "http";
|
|
@@ -4852,11 +5308,11 @@ function describeFilesFailure(status3, body) {
|
|
|
4852
5308
|
import * as p6 from "@clack/prompts";
|
|
4853
5309
|
import { execSync as execSync2 } from "child_process";
|
|
4854
5310
|
import { createHash as createHash2 } from "crypto";
|
|
4855
|
-
import { existsSync as
|
|
4856
|
-
import { dirname as dirname7, isAbsolute as
|
|
5311
|
+
import { existsSync as existsSync19, readFileSync as readFileSync15, readdirSync as readdirSync6 } from "fs";
|
|
5312
|
+
import { dirname as dirname7, isAbsolute as isAbsolute4, join as join18, relative as relative3, resolve as resolve14, sep as sep3 } from "path";
|
|
4857
5313
|
|
|
4858
5314
|
// src/build/app-id.ts
|
|
4859
|
-
import { resolve as
|
|
5315
|
+
import { resolve as resolve12 } from "path";
|
|
4860
5316
|
var APP_ID_ADOPTION_EDITS = {
|
|
4861
5317
|
"vite.config.ts": `import { deepspaceBuild } from 'deepspace/build' and add
|
|
4862
5318
|
deepspaceBuild({ appDir: fileURLToPath(new URL('.', import.meta.url)) }) to plugins`,
|
|
@@ -4870,11 +5326,11 @@ var APP_ID_ADOPTION_STEPS = `Point the client at the build-injected id \u2014 th
|
|
|
4870
5326
|
`).join("") + APP_ID_ADOPTION_TOGETHER;
|
|
4871
5327
|
|
|
4872
5328
|
// src/build/plugin.ts
|
|
4873
|
-
import { existsSync as
|
|
4874
|
-
import { join as
|
|
5329
|
+
import { existsSync as existsSync16, lstatSync, readdirSync as readdirSync5, unlinkSync as unlinkSync2 } from "fs";
|
|
5330
|
+
import { join as join15, resolve as resolve13 } from "path";
|
|
4875
5331
|
function removeBuildDevVars(workerDir) {
|
|
4876
|
-
const path =
|
|
4877
|
-
if (!
|
|
5332
|
+
const path = join15(workerDir, ".dev.vars");
|
|
5333
|
+
if (!existsSync16(path)) return false;
|
|
4878
5334
|
if (!lstatSync(path).isFile()) {
|
|
4879
5335
|
throw new Error(`Refusing unsafe build secret path: ${path}`);
|
|
4880
5336
|
}
|
|
@@ -4883,12 +5339,12 @@ function removeBuildDevVars(workerDir) {
|
|
|
4883
5339
|
}
|
|
4884
5340
|
|
|
4885
5341
|
// src/documentation/deploy.ts
|
|
4886
|
-
import { existsSync as
|
|
4887
|
-
import { join as
|
|
5342
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
5343
|
+
import { join as join16 } from "path";
|
|
4888
5344
|
function readDocumentationDeployManifest(appDir, clientDir) {
|
|
4889
|
-
if (!
|
|
4890
|
-
const manifestPath =
|
|
4891
|
-
if (!
|
|
5345
|
+
if (!existsSync17(join16(appDir, "documentation.json"))) return void 0;
|
|
5346
|
+
const manifestPath = join16(clientDir, "_documentation", "manifest.json");
|
|
5347
|
+
if (!existsSync17(manifestPath)) {
|
|
4892
5348
|
throw new Error(
|
|
4893
5349
|
"documentation.json exists but the build produced no documentation manifest. Ensure vite.config.ts includes deepSpaceDocumentation() from deepspace/documentation."
|
|
4894
5350
|
);
|
|
@@ -4897,8 +5353,8 @@ function readDocumentationDeployManifest(appDir, clientDir) {
|
|
|
4897
5353
|
}
|
|
4898
5354
|
|
|
4899
5355
|
// src/cli/commands/update/app-migrations.ts
|
|
4900
|
-
import { existsSync as
|
|
4901
|
-
import { join as
|
|
5356
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
|
|
5357
|
+
import { join as join17 } from "path";
|
|
4902
5358
|
|
|
4903
5359
|
// src/shared/protocol/app-migrations.ts
|
|
4904
5360
|
var APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
@@ -4972,8 +5428,8 @@ var APP_MIGRATION_DEFINITIONS = APP_MIGRATION_GUIDANCE.map(({ id, description })
|
|
|
4972
5428
|
description
|
|
4973
5429
|
}));
|
|
4974
5430
|
function readAppliedAppMigrations(appDir) {
|
|
4975
|
-
const path =
|
|
4976
|
-
if (!
|
|
5431
|
+
const path = join17(appDir, APP_MIGRATIONS_MANIFEST);
|
|
5432
|
+
if (!existsSync18(path)) return [];
|
|
4977
5433
|
let parsed;
|
|
4978
5434
|
try {
|
|
4979
5435
|
parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
@@ -5035,16 +5491,16 @@ async function buildDeployBundle(options) {
|
|
|
5035
5491
|
preparedWranglerConfig?.cleanup();
|
|
5036
5492
|
}
|
|
5037
5493
|
spinner2.stop(`Built (${Math.round((Date.now() - buildStarted) / 1e3)}s)`);
|
|
5038
|
-
const deployConfigPath =
|
|
5039
|
-
if (!
|
|
5494
|
+
const deployConfigPath = join18(appDir, ".wrangler", "deploy", "config.json");
|
|
5495
|
+
if (!existsSync19(deployConfigPath)) {
|
|
5040
5496
|
output.die(
|
|
5041
5497
|
"Build output config not found at .wrangler/deploy/config.json",
|
|
5042
5498
|
"build_output_missing"
|
|
5043
5499
|
);
|
|
5044
5500
|
}
|
|
5045
5501
|
const deployConfig = JSON.parse(readFileSync15(deployConfigPath, "utf-8"));
|
|
5046
|
-
const outputWranglerPath =
|
|
5047
|
-
if (!
|
|
5502
|
+
const outputWranglerPath = resolve14(dirname7(deployConfigPath), deployConfig.configPath);
|
|
5503
|
+
if (!existsSync19(outputWranglerPath)) {
|
|
5048
5504
|
output.die(`Output wrangler.json not found at ${outputWranglerPath}`, "build_output_missing");
|
|
5049
5505
|
}
|
|
5050
5506
|
const outputConfig = JSON.parse(readFileSync15(outputWranglerPath, "utf-8"));
|
|
@@ -5061,12 +5517,12 @@ async function buildDeployBundle(options) {
|
|
|
5061
5517
|
} catch (error) {
|
|
5062
5518
|
output.die(errorMessage(error), "build_output_unsafe");
|
|
5063
5519
|
}
|
|
5064
|
-
const workerBundlePath =
|
|
5065
|
-
const clientDir = outputConfig.assets?.directory ?
|
|
5066
|
-
if (!
|
|
5520
|
+
const workerBundlePath = join18(workerDir, outputConfig.main);
|
|
5521
|
+
const clientDir = outputConfig.assets?.directory ? resolve14(workerDir, outputConfig.assets.directory) : null;
|
|
5522
|
+
if (!existsSync19(workerBundlePath)) {
|
|
5067
5523
|
output.die(`Worker bundle not found at ${workerBundlePath}`, "build_output_missing");
|
|
5068
5524
|
}
|
|
5069
|
-
if (!clientDir || !
|
|
5525
|
+
if (!clientDir || !existsSync19(clientDir)) {
|
|
5070
5526
|
return output.die(`Client assets not found at ${clientDir}`, "build_output_missing");
|
|
5071
5527
|
}
|
|
5072
5528
|
try {
|
|
@@ -5134,16 +5590,16 @@ async function buildDeployBundle(options) {
|
|
|
5134
5590
|
}
|
|
5135
5591
|
var WORKER_ES_MODULE = /\.(?:js|mjs)$/i;
|
|
5136
5592
|
function collectWorkerBundle(workerDir, mainPath, clientDir) {
|
|
5137
|
-
const root =
|
|
5138
|
-
const mainAbsolute =
|
|
5139
|
-
const excludedClient = clientDir ?
|
|
5593
|
+
const root = resolve14(workerDir);
|
|
5594
|
+
const mainAbsolute = resolve14(mainPath);
|
|
5595
|
+
const excludedClient = clientDir ? resolve14(clientDir) : null;
|
|
5140
5596
|
const main2 = moduleName(root, mainAbsolute);
|
|
5141
5597
|
const modules = [];
|
|
5142
5598
|
const walk = (directory) => {
|
|
5143
5599
|
for (const entry of readdirSync6(directory, { withFileTypes: true }).sort(
|
|
5144
5600
|
(a, b) => a.name.localeCompare(b.name)
|
|
5145
5601
|
)) {
|
|
5146
|
-
const fullPath =
|
|
5602
|
+
const fullPath = join18(directory, entry.name);
|
|
5147
5603
|
if (excludedClient && isWithin(excludedClient, fullPath)) continue;
|
|
5148
5604
|
if (entry.isDirectory()) {
|
|
5149
5605
|
walk(fullPath);
|
|
@@ -5159,15 +5615,15 @@ function collectWorkerBundle(workerDir, mainPath, clientDir) {
|
|
|
5159
5615
|
return { main: main2, modules: [mainModule, ...modules] };
|
|
5160
5616
|
}
|
|
5161
5617
|
function moduleName(root, path) {
|
|
5162
|
-
const name =
|
|
5163
|
-
if (!name ||
|
|
5618
|
+
const name = relative3(root, path);
|
|
5619
|
+
if (!name || isAbsolute4(name) || name === ".." || name.startsWith(`..${sep3}`)) {
|
|
5164
5620
|
throw new Error(`Worker module is outside the generated output directory: ${path}`);
|
|
5165
5621
|
}
|
|
5166
5622
|
return name.split(sep3).join("/");
|
|
5167
5623
|
}
|
|
5168
5624
|
function isWithin(root, path) {
|
|
5169
|
-
const name =
|
|
5170
|
-
return name === "" || !
|
|
5625
|
+
const name = relative3(root, path);
|
|
5626
|
+
return name === "" || !isAbsolute4(name) && name !== ".." && !name.startsWith(`..${sep3}`);
|
|
5171
5627
|
}
|
|
5172
5628
|
function extractRunWorkerFirst(config) {
|
|
5173
5629
|
const raw = Array.isArray(config.assets?.run_worker_first) ? config.assets.run_worker_first : [];
|
|
@@ -5229,7 +5685,7 @@ function collectAssets(dir) {
|
|
|
5229
5685
|
const walk = (currentDir, prefix) => {
|
|
5230
5686
|
for (const entry of readdirSync6(currentDir, { withFileTypes: true })) {
|
|
5231
5687
|
if (isDeployAssetControlFile(entry.name)) continue;
|
|
5232
|
-
const fullPath =
|
|
5688
|
+
const fullPath = join18(currentDir, entry.name);
|
|
5233
5689
|
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
5234
5690
|
if (entry.isDirectory()) {
|
|
5235
5691
|
walk(fullPath, relativePath);
|
|
@@ -5253,8 +5709,8 @@ function readDeployAssetConfig(clientDir) {
|
|
|
5253
5709
|
["_headers", "_headers"],
|
|
5254
5710
|
["_redirects", "_redirects"]
|
|
5255
5711
|
]) {
|
|
5256
|
-
const filePath =
|
|
5257
|
-
if (!
|
|
5712
|
+
const filePath = join18(clientDir, file);
|
|
5713
|
+
if (!existsSync19(filePath)) continue;
|
|
5258
5714
|
const content = readFileSync15(filePath, "utf8");
|
|
5259
5715
|
if (content.trim()) config[key] = content;
|
|
5260
5716
|
}
|
|
@@ -5269,8 +5725,8 @@ function errorMessage(error) {
|
|
|
5269
5725
|
|
|
5270
5726
|
// src/cli/commands/deploy/commerce.ts
|
|
5271
5727
|
import * as p7 from "@clack/prompts";
|
|
5272
|
-
import { existsSync as
|
|
5273
|
-
import { join as
|
|
5728
|
+
import { existsSync as existsSync20 } from "fs";
|
|
5729
|
+
import { join as join19 } from "path";
|
|
5274
5730
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5275
5731
|
|
|
5276
5732
|
// src/shared/api-error.ts
|
|
@@ -5322,10 +5778,10 @@ function errorMessage2(error) {
|
|
|
5322
5778
|
return error instanceof Error ? error.message : String(error);
|
|
5323
5779
|
}
|
|
5324
5780
|
async function loadDeclaration(appDir, sourceFile, outputFile, exportName) {
|
|
5325
|
-
const sourcePath =
|
|
5326
|
-
if (!
|
|
5781
|
+
const sourcePath = join19(appDir, "src", sourceFile);
|
|
5782
|
+
if (!existsSync20(sourcePath)) return void 0;
|
|
5327
5783
|
const esbuild = await import("esbuild");
|
|
5328
|
-
const outputPath =
|
|
5784
|
+
const outputPath = join19(appDir, ".wrangler", "deploy", outputFile);
|
|
5329
5785
|
await esbuild.build({
|
|
5330
5786
|
entryPoints: [sourcePath],
|
|
5331
5787
|
bundle: true,
|
|
@@ -5418,7 +5874,7 @@ Stripe will keep charging them until you cancel. Cancel at the end of their curr
|
|
|
5418
5874
|
}
|
|
5419
5875
|
}
|
|
5420
5876
|
async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
5421
|
-
if (!
|
|
5877
|
+
if (!existsSync20(join19(appDir, "src", "subscriptions.ts"))) return;
|
|
5422
5878
|
let plans;
|
|
5423
5879
|
try {
|
|
5424
5880
|
plans = await loadDeclaration(
|
|
@@ -5473,7 +5929,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
|
5473
5929
|
}
|
|
5474
5930
|
}
|
|
5475
5931
|
async function syncOneTimeProducts(appDir, appName, token) {
|
|
5476
|
-
if (!
|
|
5932
|
+
if (!existsSync20(join19(appDir, "src", "products.ts"))) return;
|
|
5477
5933
|
let products;
|
|
5478
5934
|
try {
|
|
5479
5935
|
products = await loadDeclaration(
|
|
@@ -5578,184 +6034,13 @@ function createDeployOutput(json) {
|
|
|
5578
6034
|
};
|
|
5579
6035
|
}
|
|
5580
6036
|
|
|
5581
|
-
// src/cli/commands/deploy/request.ts
|
|
5582
|
-
import * as p10 from "@clack/prompts";
|
|
5583
|
-
import { readFileSync as readFileSync16 } from "fs";
|
|
5584
|
-
|
|
5585
|
-
// src/cli/commands/deploy/repository.ts
|
|
5586
|
-
import * as p9 from "@clack/prompts";
|
|
5587
|
-
|
|
5588
|
-
// src/cli/lib/vc-remote.ts
|
|
5589
|
-
import { existsSync as existsSync19 } from "fs";
|
|
5590
|
-
import { tmpdir } from "os";
|
|
5591
|
-
import { isAbsolute as isAbsolute4, relative as relative3, resolve as resolve14 } from "path";
|
|
5592
|
-
function spaceRemoteName(environment = DEEPSPACE_ENV) {
|
|
5593
|
-
if (environment === "staging") return "space-staging";
|
|
5594
|
-
if (environment === "production") return "space";
|
|
5595
|
-
return "space-invalid";
|
|
5596
|
-
}
|
|
5597
|
-
var SPACE_REMOTE = spaceRemoteName();
|
|
5598
|
-
function spaceTrackingRef(branch, remote = SPACE_REMOTE) {
|
|
5599
|
-
return `refs/remotes/${remote}/${branch}`;
|
|
5600
|
-
}
|
|
5601
|
-
function spacePrivateRef(path, environment = DEEPSPACE_ENV) {
|
|
5602
|
-
const root = environment === "production" ? "refs/deepspace" : `refs/deepspace/${environment}`;
|
|
5603
|
-
return `${root}/${path}`;
|
|
5604
|
-
}
|
|
5605
|
-
function deployBaseUrl() {
|
|
5606
|
-
return process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
5607
|
-
}
|
|
5608
|
-
function repoUrl(appId, base = deployBaseUrl()) {
|
|
5609
|
-
return `${base.replace(/\/+$/, "")}/api/repo/${encodeURIComponent(appId)}`;
|
|
5610
|
-
}
|
|
5611
|
-
function gitAuthEnv(token, base = deployBaseUrl()) {
|
|
5612
|
-
const existing = Number(process.env.GIT_CONFIG_COUNT ?? "0");
|
|
5613
|
-
const index = Number.isInteger(existing) && existing > 0 ? existing : 0;
|
|
5614
|
-
return {
|
|
5615
|
-
GIT_CONFIG_COUNT: String(index + 1),
|
|
5616
|
-
[`GIT_CONFIG_KEY_${index}`]: `http.${base.replace(/\/+$/, "")}.extraHeader`,
|
|
5617
|
-
[`GIT_CONFIG_VALUE_${index}`]: `Authorization: Bearer ${token}`
|
|
5618
|
-
};
|
|
5619
|
-
}
|
|
5620
|
-
function gitSourceImportEnv(token, revision, base = deployBaseUrl()) {
|
|
5621
|
-
const env = gitAuthEnv(token, base);
|
|
5622
|
-
const index = Number(env.GIT_CONFIG_COUNT);
|
|
5623
|
-
return {
|
|
5624
|
-
...env,
|
|
5625
|
-
GIT_CONFIG_COUNT: String(index + 1),
|
|
5626
|
-
[`GIT_CONFIG_KEY_${index}`]: `http.${base.replace(/\/+$/, "")}.extraHeader`,
|
|
5627
|
-
[`GIT_CONFIG_VALUE_${index}`]: `X-DeepSpace-Source-Revision: ${revision}`
|
|
5628
|
-
};
|
|
5629
|
-
}
|
|
5630
|
-
function runGitRemote(cwd, token, args, opts = {}) {
|
|
5631
|
-
return runGit(cwd, args, { ...opts, env: { ...gitAuthEnv(token), ...opts.env ?? {} } });
|
|
5632
|
-
}
|
|
5633
|
-
function entryIsTransient(cwd) {
|
|
5634
|
-
const entry = process.argv[1];
|
|
5635
|
-
if (!entry) return true;
|
|
5636
|
-
if (/[\\/]_npx[\\/]/.test(entry) || entry.startsWith(tmpdir())) return true;
|
|
5637
|
-
const rel = relative3(resolve14(cwd), resolve14(entry));
|
|
5638
|
-
return rel === "" || !rel.startsWith("..") && !isAbsolute4(rel);
|
|
5639
|
-
}
|
|
5640
|
-
function helperEntryUsable(command) {
|
|
5641
|
-
const match = /^!(?:DEEPSPACE_ENV=(production|staging) )?'([^']*)' '([^']*)' git-credential$/.exec(command);
|
|
5642
|
-
const expectedEnvironment = DEEPSPACE_ENV === "production" ? void 0 : DEEPSPACE_ENV;
|
|
5643
|
-
if (!match || match[1] !== expectedEnvironment) return false;
|
|
5644
|
-
return existsSync19(match[2]) && existsSync19(match[3]);
|
|
5645
|
-
}
|
|
5646
|
-
function credentialHelperCommand(environment = DEEPSPACE_ENV) {
|
|
5647
|
-
const entry = process.argv[1];
|
|
5648
|
-
if (!entry) throw new Error("CLI entry path is unavailable");
|
|
5649
|
-
const environmentPrefix = environment === "staging" ? "DEEPSPACE_ENV=staging " : "";
|
|
5650
|
-
return `!${environmentPrefix}${shQuote(process.execPath)} ${shQuote(entry)} git-credential`;
|
|
5651
|
-
}
|
|
5652
|
-
function installCredentialHelper(cwd, url) {
|
|
5653
|
-
try {
|
|
5654
|
-
const origin = new URL(url);
|
|
5655
|
-
const key = `credential.${origin.protocol}//${origin.host}.helper`;
|
|
5656
|
-
const helper = credentialHelperCommand();
|
|
5657
|
-
const wanted = ["", helper];
|
|
5658
|
-
const transient = entryIsTransient(cwd);
|
|
5659
|
-
const scope = transient ? "--worktree" : "--global";
|
|
5660
|
-
if (transient) {
|
|
5661
|
-
const enabled = runGit(cwd, ["config", "--local", "extensions.worktreeConfig", "true"], {
|
|
5662
|
-
allowFail: true
|
|
5663
|
-
});
|
|
5664
|
-
if (enabled.status !== 0) return;
|
|
5665
|
-
runGit(cwd, ["config", "--local", "--unset-all", key], { allowFail: true });
|
|
5666
|
-
}
|
|
5667
|
-
const current = runGit(cwd, ["config", scope, "--get-all", key], { allowFail: true });
|
|
5668
|
-
if (current.status === 0) {
|
|
5669
|
-
const existing = current.stdout.toString("utf-8").split("\n").filter((_, index, lines) => index < lines.length - 1);
|
|
5670
|
-
if (existing.length === wanted.length && existing.every((value, i) => value === wanted[i])) {
|
|
5671
|
-
return;
|
|
5672
|
-
}
|
|
5673
|
-
if (existing.length === wanted.length && existing[0] === "" && helperEntryUsable(existing[1])) {
|
|
5674
|
-
return;
|
|
5675
|
-
}
|
|
5676
|
-
runGit(cwd, ["config", scope, "--unset-all", key], { allowFail: true });
|
|
5677
|
-
}
|
|
5678
|
-
runGit(cwd, ["config", scope, "--add", key, ""]);
|
|
5679
|
-
runGit(cwd, ["config", scope, "--add", key, helper]);
|
|
5680
|
-
if (scope === "--global" && !process.argv.includes("--json")) {
|
|
5681
|
-
process.stderr.write(
|
|
5682
|
-
`note: added a git credential helper for ${origin.host} to your global git config so plain \`git clone\`/\`git push\` to DeepSpace authenticate. Undo with: git config --global --unset-all ${key}
|
|
5683
|
-
`
|
|
5684
|
-
);
|
|
5685
|
-
}
|
|
5686
|
-
} catch {
|
|
5687
|
-
}
|
|
5688
|
-
}
|
|
5689
|
-
function ensureSpaceRemote(cwd, appId, remote = SPACE_REMOTE, token) {
|
|
5690
|
-
const url = repoUrl(appId);
|
|
5691
|
-
const current = runGit(cwd, ["remote", "get-url", remote], { allowFail: true });
|
|
5692
|
-
const existing = current.status === 0 ? current.stdout.toString("utf-8").trim() : null;
|
|
5693
|
-
if (appIdFromRepoUrl(existing ?? "") !== appId) {
|
|
5694
|
-
const owned = declaredAppIds(cwd);
|
|
5695
|
-
if (owned.size > 0 && !owned.has(appId)) {
|
|
5696
|
-
throw new InputError(
|
|
5697
|
-
`This checkout declares ${[...owned].join(", ")}, but this command targets ${appId}. Pointing its \`${remote}\` remote at another app would send every later plain \`git push ${remote}\` to the wrong repo. Work on ${appId} from its own checkout (\`deepspace clone ${appId}\` into a new directory), or fork THIS checkout into a brand-new app of your own with \`deepspace app init --new-id\`.`,
|
|
5698
|
-
"app_checkout_mismatch"
|
|
5699
|
-
);
|
|
5700
|
-
}
|
|
5701
|
-
}
|
|
5702
|
-
if (token) ensureGitIdentity(cwd, token);
|
|
5703
|
-
if (existing === null) {
|
|
5704
|
-
runGit(cwd, ["remote", "add", remote, url]);
|
|
5705
|
-
} else if (existing !== url) {
|
|
5706
|
-
const priorApp = appIdFromRepoUrl(existing);
|
|
5707
|
-
if (priorApp && priorApp !== appId && !process.argv.includes("--json")) {
|
|
5708
|
-
process.stderr.write(
|
|
5709
|
-
`warning: remote '${remote}' was pointed at app ${priorApp}; re-aiming it at ${appId}.
|
|
5710
|
-
`
|
|
5711
|
-
);
|
|
5712
|
-
}
|
|
5713
|
-
runGit(cwd, ["remote", "set-url", remote, url]);
|
|
5714
|
-
}
|
|
5715
|
-
installCredentialHelper(cwd, url);
|
|
5716
|
-
return url;
|
|
5717
|
-
}
|
|
5718
|
-
function removeSpaceRemote(cwd, remote = SPACE_REMOTE) {
|
|
5719
|
-
const current = runGit(cwd, ["remote", "get-url", remote], { allowFail: true });
|
|
5720
|
-
if (current.status !== 0) return false;
|
|
5721
|
-
runGit(cwd, ["remote", "remove", remote]);
|
|
5722
|
-
return true;
|
|
5723
|
-
}
|
|
5724
|
-
function ensureGitIdentity(cwd, token) {
|
|
5725
|
-
try {
|
|
5726
|
-
const configured = (key) => runGit(cwd, ["config", "--get", key], { allowFail: true }).stdout.toString("utf-8").trim() !== "";
|
|
5727
|
-
const missingName = !configured("user.name");
|
|
5728
|
-
const missingEmail = !configured("user.email");
|
|
5729
|
-
if (!missingName && !missingEmail) return;
|
|
5730
|
-
if (runGit(cwd, ["config", "--get", "--type=bool", "user.useConfigOnly"], { allowFail: true }).stdout.toString("utf-8").trim() === "true") {
|
|
5731
|
-
return;
|
|
5732
|
-
}
|
|
5733
|
-
const payload = decodeJwtPayload(token);
|
|
5734
|
-
if (!payload.email) return;
|
|
5735
|
-
if (missingName) {
|
|
5736
|
-
runGit(cwd, ["config", "--local", "user.name", payload.name || payload.email], {
|
|
5737
|
-
allowFail: true
|
|
5738
|
-
});
|
|
5739
|
-
}
|
|
5740
|
-
if (missingEmail) {
|
|
5741
|
-
runGit(cwd, ["config", "--local", "user.email", payload.email], { allowFail: true });
|
|
5742
|
-
}
|
|
5743
|
-
const wrote = [...missingEmail ? ["user.email"] : [], ...missingName ? ["user.name"] : []];
|
|
5744
|
-
const dir = /^[A-Za-z0-9_./:@-]+$/.test(cwd) ? cwd : shQuote(cwd);
|
|
5745
|
-
const undo = wrote.map((key) => `git -C ${dir} config --local --unset ${key}`).join(" && ");
|
|
5746
|
-
process.stderr.write(
|
|
5747
|
-
`Git identity for this checkout set from your DeepSpace login (${payload.email}).
|
|
5748
|
-
Undo with: ${undo}
|
|
5749
|
-
`
|
|
5750
|
-
);
|
|
5751
|
-
} catch {
|
|
5752
|
-
}
|
|
5753
|
-
}
|
|
5754
|
-
function appIdFromRepoUrl(url) {
|
|
5755
|
-
const match = /\/api\/repo\/([^/]+)\/?$/.exec(url);
|
|
5756
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
5757
|
-
}
|
|
5758
|
-
|
|
6037
|
+
// src/cli/commands/deploy/request.ts
|
|
6038
|
+
import * as p10 from "@clack/prompts";
|
|
6039
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
6040
|
+
|
|
6041
|
+
// src/cli/commands/deploy/repository.ts
|
|
6042
|
+
import * as p9 from "@clack/prompts";
|
|
6043
|
+
|
|
5759
6044
|
// src/cli/lib/git/safety.ts
|
|
5760
6045
|
var SECRET_FILE_RE = /(^|\/)(\.dev\.vars(\.[^/]*)?|\.env(\.[^/]*)?|\.npmrc|\.envrc|\.mcp\.json)$/i;
|
|
5761
6046
|
var SECRET_TEMPLATE_RE = /\.(example|sample|template)$/i;
|
|
@@ -6196,41 +6481,6 @@ function selectGitHubRemote(cwd, options = {}) {
|
|
|
6196
6481
|
const matching = repository ? remotes.filter((remote) => remote.repository === repository) : remotes;
|
|
6197
6482
|
return matching.find((remote) => remote.name === "origin") ?? (matching.length === 1 ? matching[0] : null);
|
|
6198
6483
|
}
|
|
6199
|
-
function remotePublicRefs(cwd, remote) {
|
|
6200
|
-
let output;
|
|
6201
|
-
try {
|
|
6202
|
-
output = runGit(cwd, ["ls-remote", "--refs", remote]).stdout.toString("utf-8");
|
|
6203
|
-
} catch (error) {
|
|
6204
|
-
if (!isGitCredentialFailure(error)) throw error;
|
|
6205
|
-
throw new GitError(
|
|
6206
|
-
"Git could not authenticate the remote. In a container, forward an SSH agent or use an ephemeral Git credential helper, then retry. Do not put tokens in remote URLs.",
|
|
6207
|
-
"github_credentials_required"
|
|
6208
|
-
);
|
|
6209
|
-
}
|
|
6210
|
-
return output.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
6211
|
-
const [oid, name] = line.split(/\s+/, 2);
|
|
6212
|
-
return { oid, name };
|
|
6213
|
-
}).filter(
|
|
6214
|
-
(ref) => /^[0-9a-f]{40}$/.test(ref.oid) && (ref.name.startsWith("refs/heads/") || ref.name.startsWith("refs/tags/"))
|
|
6215
|
-
);
|
|
6216
|
-
}
|
|
6217
|
-
function isGitCredentialFailure(error) {
|
|
6218
|
-
if (!(error instanceof Error)) return false;
|
|
6219
|
-
return /authentication failed|could not read username|permission denied \(publickey\)|repository not found|terminal prompts disabled/i.test(
|
|
6220
|
-
error.message
|
|
6221
|
-
);
|
|
6222
|
-
}
|
|
6223
|
-
function localTransferRefs(cwd, prefix, target2) {
|
|
6224
|
-
const output = runGit(cwd, [
|
|
6225
|
-
"for-each-ref",
|
|
6226
|
-
"--format=%(refname) %(objectname)",
|
|
6227
|
-
prefix
|
|
6228
|
-
]).stdout.toString("utf-8");
|
|
6229
|
-
return output.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
6230
|
-
const [name, oid] = line.split(/\s+/, 2);
|
|
6231
|
-
return { name: `${target2}/${name.slice(`${prefix}/`.length)}`, oid };
|
|
6232
|
-
}).filter((ref) => /^[0-9a-f]{40}$/.test(ref.oid));
|
|
6233
|
-
}
|
|
6234
6484
|
|
|
6235
6485
|
// src/cli/commands/deploy/repository.ts
|
|
6236
6486
|
function describeWorktree(appDir) {
|
|
@@ -6240,17 +6490,27 @@ function describeWorktree(appDir) {
|
|
|
6240
6490
|
return { branch: null, dirty: null };
|
|
6241
6491
|
}
|
|
6242
6492
|
}
|
|
6493
|
+
function externalGitSource(appDir, source) {
|
|
6494
|
+
if (source?.provider === "github") return true;
|
|
6495
|
+
if (source !== null) return false;
|
|
6496
|
+
try {
|
|
6497
|
+
return listGitHubRemotes(appDir).length > 0;
|
|
6498
|
+
} catch {
|
|
6499
|
+
return false;
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6502
|
+
function observedGitHubRepository(appDir) {
|
|
6503
|
+
try {
|
|
6504
|
+
return selectGitHubRemote(appDir)?.repository ?? listGitHubRemotes(appDir)[0]?.repository ?? null;
|
|
6505
|
+
} catch {
|
|
6506
|
+
return null;
|
|
6507
|
+
}
|
|
6508
|
+
}
|
|
6243
6509
|
function preflightDeployRepository(options) {
|
|
6244
6510
|
const { appDir, push, source } = options;
|
|
6245
|
-
if (!push || source
|
|
6511
|
+
if (!push || externalGitSource(appDir, source)) return null;
|
|
6246
6512
|
try {
|
|
6247
6513
|
assertSyncableRepo(appDir);
|
|
6248
|
-
if (!source && listGitHubRemotes(appDir).length > 0) {
|
|
6249
|
-
return {
|
|
6250
|
-
code: "source_unclaimed",
|
|
6251
|
-
error: "This app has a GitHub remote but no claimed source. Choose once with `deepspace app source github` (manual GitHub ownership) or `deepspace app source deepspace` (packaged DeepSpace source), then deploy again."
|
|
6252
|
-
};
|
|
6253
|
-
}
|
|
6254
6514
|
assertNoOperationInProgress(appDir);
|
|
6255
6515
|
const branch = currentBranch(appDir);
|
|
6256
6516
|
if (!isWorkTreeClean(appDir)) return dirtyWorktreeRefusal(branch, appDir);
|
|
@@ -6267,9 +6527,18 @@ async function syncDeployRepository(options) {
|
|
|
6267
6527
|
const deployKey = mintIdempotencyKey();
|
|
6268
6528
|
let source = sourceState.source;
|
|
6269
6529
|
let sourceRevision = sourceState.revision;
|
|
6270
|
-
const baseReleaseId = null;
|
|
6271
6530
|
const worktree = describeWorktree(appDir);
|
|
6272
|
-
|
|
6531
|
+
const external = externalGitSource(appDir, source);
|
|
6532
|
+
if (external) {
|
|
6533
|
+
if (sourceState.source?.provider === "github") {
|
|
6534
|
+
try {
|
|
6535
|
+
if (removeSpaceRemote(appDir)) {
|
|
6536
|
+
p9.log.info(`Removed the stale \`space\` git remote \u2014 GitHub owns this app's source.`);
|
|
6537
|
+
}
|
|
6538
|
+
} catch {
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6541
|
+
const observedRepository = source === null ? observedGitHubRepository(appDir) : null;
|
|
6273
6542
|
const where = `GitHub source: shipping the working tree of ${worktree.branch ?? "(detached HEAD)"}`;
|
|
6274
6543
|
if (worktree.dirty) {
|
|
6275
6544
|
p9.log.warn(
|
|
@@ -6278,7 +6547,7 @@ async function syncDeployRepository(options) {
|
|
|
6278
6547
|
} else {
|
|
6279
6548
|
p9.log.info(`${where} (clean). GitHub-source releases record no commit lineage.`);
|
|
6280
6549
|
}
|
|
6281
|
-
return { commitOid, recoverable, deployKey, source, sourceRevision,
|
|
6550
|
+
return { commitOid, recoverable, deployKey, source, sourceRevision, observedRepository, ...worktree };
|
|
6282
6551
|
}
|
|
6283
6552
|
if (!push) {
|
|
6284
6553
|
try {
|
|
@@ -6286,7 +6555,12 @@ async function syncDeployRepository(options) {
|
|
|
6286
6555
|
commitOid = resolveCommit(appDir, "HEAD");
|
|
6287
6556
|
} catch {
|
|
6288
6557
|
}
|
|
6289
|
-
|
|
6558
|
+
if (worktree.dirty) {
|
|
6559
|
+
p9.log.warn(
|
|
6560
|
+
`--no-push: shipping the working tree WITH uncommitted changes \u2014 the recorded commit ${commitOid ? commitOid.slice(0, 10) : ""} is not exactly what went live.`
|
|
6561
|
+
);
|
|
6562
|
+
}
|
|
6563
|
+
return { commitOid, recoverable, deployKey, source, sourceRevision, observedRepository: null, ...worktree };
|
|
6290
6564
|
}
|
|
6291
6565
|
try {
|
|
6292
6566
|
const preflight = preflightDeployRepository({ appDir, push, source });
|
|
@@ -6302,7 +6576,7 @@ async function syncDeployRepository(options) {
|
|
|
6302
6576
|
}
|
|
6303
6577
|
const tip = resolveCommit(appDir, `refs/heads/${branch}`);
|
|
6304
6578
|
if (!tip) {
|
|
6305
|
-
return { commitOid, recoverable, deployKey, source, sourceRevision,
|
|
6579
|
+
return { commitOid, recoverable, deployKey, source, sourceRevision, observedRepository: null, ...worktree };
|
|
6306
6580
|
}
|
|
6307
6581
|
const secretFiles = workspaceBranchId ? [] : trackedSecretFiles(appDir, tip);
|
|
6308
6582
|
if (workspaceBranchId) {
|
|
@@ -6331,6 +6605,11 @@ async function syncDeployRepository(options) {
|
|
|
6331
6605
|
`Skipping cloud-repo push \u2014 the branch tracks secret file(s): ${secretFiles.join(", ")}. ` + secretRecoverySentence(secretFiles, "`deepspace push`") + ` Without the push this deploy carries no source lineage: if the live release has one, the server refuses it as stale (--ignore-stale replaces it anyway), and this release's source isn't recoverable until the push works.`
|
|
6332
6606
|
);
|
|
6333
6607
|
} else {
|
|
6608
|
+
if (sourceState.source === null) {
|
|
6609
|
+
process.stderr.write(
|
|
6610
|
+
"This app's source is unclaimed \u2014 this deploy's push claims DeepSpace source for it, permanently.\n"
|
|
6611
|
+
);
|
|
6612
|
+
}
|
|
6334
6613
|
const pushResult = await pushWithTransientRetry(
|
|
6335
6614
|
() => pushToSpace(appDir, token, `refs/heads/${branch}:refs/heads/${branch}`, {
|
|
6336
6615
|
remote: sourceRemote
|
|
@@ -6391,7 +6670,7 @@ async function syncDeployRepository(options) {
|
|
|
6391
6670
|
const failure = deployRepositoryFailure(error, appDir);
|
|
6392
6671
|
output.die(failure.error, failure.code);
|
|
6393
6672
|
}
|
|
6394
|
-
return { commitOid, recoverable, deployKey, source, sourceRevision,
|
|
6673
|
+
return { commitOid, recoverable, deployKey, source, sourceRevision, observedRepository: null, ...worktree };
|
|
6395
6674
|
}
|
|
6396
6675
|
function classifyRemoteState(appDir, token, branch, localTip, remote) {
|
|
6397
6676
|
let remoteTip = null;
|
|
@@ -6617,7 +6896,10 @@ async function deployBuiltBundle(options) {
|
|
|
6617
6896
|
form.append("sourceRepository", repository.source.repository);
|
|
6618
6897
|
}
|
|
6619
6898
|
}
|
|
6620
|
-
if (repository.
|
|
6899
|
+
if (repository.dirty !== null) form.append("sourceDirty", repository.dirty ? "true" : "false");
|
|
6900
|
+
if (repository.observedRepository) {
|
|
6901
|
+
form.append("observedRepository", repository.observedRepository);
|
|
6902
|
+
}
|
|
6621
6903
|
if (ignoreStale) form.append("ignoreStale", "true");
|
|
6622
6904
|
form.append("deployKey", repository.deployKey);
|
|
6623
6905
|
return form;
|
|
@@ -6951,21 +7233,21 @@ function errorMessage5(error) {
|
|
|
6951
7233
|
|
|
6952
7234
|
// src/cli/commands/deploy/lock.ts
|
|
6953
7235
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6954
|
-
import { mkdirSync as
|
|
6955
|
-
import { dirname as dirname8, join as
|
|
7236
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
7237
|
+
import { dirname as dirname8, join as join20 } from "path";
|
|
6956
7238
|
function deployLockPath(appDir) {
|
|
6957
|
-
return
|
|
7239
|
+
return join20(appDir, ".deepspace", "deploy.lock");
|
|
6958
7240
|
}
|
|
6959
7241
|
function acquireDeployLock(appDir) {
|
|
6960
7242
|
const path = deployLockPath(appDir);
|
|
6961
|
-
|
|
7243
|
+
mkdirSync8(dirname8(path), { recursive: true });
|
|
6962
7244
|
const record = {
|
|
6963
7245
|
pid: process.pid,
|
|
6964
7246
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6965
7247
|
token: randomUUID2()
|
|
6966
7248
|
};
|
|
6967
7249
|
try {
|
|
6968
|
-
|
|
7250
|
+
writeFileSync6(path, JSON.stringify(record), { flag: "wx" });
|
|
6969
7251
|
} catch (err) {
|
|
6970
7252
|
if (err.code !== "EEXIST") throw err;
|
|
6971
7253
|
const holder = readLock(path);
|
|
@@ -6981,7 +7263,7 @@ function acquireDeployLock(appDir) {
|
|
|
6981
7263
|
);
|
|
6982
7264
|
}
|
|
6983
7265
|
return () => {
|
|
6984
|
-
if (readLock(path)?.token === record.token)
|
|
7266
|
+
if (readLock(path)?.token === record.token) rmSync6(path, { force: true });
|
|
6985
7267
|
};
|
|
6986
7268
|
}
|
|
6987
7269
|
function readLock(path) {
|
|
@@ -6994,7 +7276,7 @@ function readLock(path) {
|
|
|
6994
7276
|
}
|
|
6995
7277
|
|
|
6996
7278
|
// src/cli/commands/deploy.ts
|
|
6997
|
-
var
|
|
7279
|
+
var DEPLOY_URL4 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
6998
7280
|
var DEPLOY_TOKEN_MIN_VALIDITY_MS = 10 * 60 * 1e3;
|
|
6999
7281
|
var POST_DEPLOY_TOKEN_MIN_VALIDITY_MS = 2 * 60 * 1e3;
|
|
7000
7282
|
var deploy_default = defineCommand3({
|
|
@@ -7047,6 +7329,7 @@ var deploy_default = defineCommand3({
|
|
|
7047
7329
|
},
|
|
7048
7330
|
async run({ args }) {
|
|
7049
7331
|
const output = createDeployOutput(args.json === true);
|
|
7332
|
+
setPlainProgress(output.json);
|
|
7050
7333
|
try {
|
|
7051
7334
|
await runDeploy(args, output);
|
|
7052
7335
|
} catch (error) {
|
|
@@ -7067,15 +7350,7 @@ async function runDeploy(args, output) {
|
|
|
7067
7350
|
if (selectorRefusal) output.die(selectorRefusal.error, selectorRefusal.code);
|
|
7068
7351
|
const envName = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
7069
7352
|
if (!hasWranglerConfig(appDir)) {
|
|
7070
|
-
output.die(noWranglerConfigMessage(
|
|
7071
|
-
}
|
|
7072
|
-
try {
|
|
7073
|
-
ensureInstallReady(appDir);
|
|
7074
|
-
} catch (error) {
|
|
7075
|
-
const refusal = error;
|
|
7076
|
-
output.die(refusal.message ?? String(error), refusal.code ?? "deps_missing", {
|
|
7077
|
-
action: refusal.action
|
|
7078
|
-
});
|
|
7353
|
+
output.die(noWranglerConfigMessage(join21(appDir, "wrangler.toml")), "not_in_app_repo");
|
|
7079
7354
|
}
|
|
7080
7355
|
const releaseLock = acquireDeployLock(appDir);
|
|
7081
7356
|
try {
|
|
@@ -7099,24 +7374,30 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
7099
7374
|
);
|
|
7100
7375
|
}
|
|
7101
7376
|
const appName = nameResult.name;
|
|
7102
|
-
|
|
7377
|
+
let appId = readAppId(appDir, envName);
|
|
7103
7378
|
p12.log.info(envName ? `App: ${appName} (env: ${envName})` : `App: ${appName}`);
|
|
7104
7379
|
const { token, ownerId } = await authenticate(appDir, output);
|
|
7380
|
+
try {
|
|
7381
|
+
ensureInstallReady(appDir);
|
|
7382
|
+
} catch (error) {
|
|
7383
|
+
const refusal = error;
|
|
7384
|
+
return output.die(refusal.message ?? String(error), refusal.code ?? "install_failed", {
|
|
7385
|
+
action: refusal.action
|
|
7386
|
+
});
|
|
7387
|
+
}
|
|
7105
7388
|
if (!appId) {
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
actionRequired: true
|
|
7115
|
-
}
|
|
7116
|
-
);
|
|
7389
|
+
const ensured = await ensureAppRegistered(appDir, token, envName, { commitScaffold: true });
|
|
7390
|
+
if (!ensured) {
|
|
7391
|
+
return output.die(
|
|
7392
|
+
envName ? `No app id for [env.${envName}] \u2014 declare the [env.${envName}] block with its own DEEPSPACE_APP_ID entry (the \`__APP_ID__\` placeholder is enough), or run \`deepspace app init --env ${envName}\`.` : "wrangler.toml declares no DEEPSPACE_APP_ID \u2014 this does not look like a DeepSpace app. Run `deepspace app init` to register it as one.",
|
|
7393
|
+
envName ? "no_app_id_for_env" : "app_not_initialized"
|
|
7394
|
+
);
|
|
7395
|
+
}
|
|
7396
|
+
appId = ensured.appId;
|
|
7117
7397
|
}
|
|
7398
|
+
commitScaffoldIfUnborn(appDir, token);
|
|
7118
7399
|
p12.log.info(`Id: ${appId}`);
|
|
7119
|
-
const sourceState = await getAppSource(
|
|
7400
|
+
const sourceState = await getAppSource(DEPLOY_URL4, token, appId).catch((error) => {
|
|
7120
7401
|
if (errorCode(error) === "forbidden") {
|
|
7121
7402
|
output.die(forbiddenDeployMessage(appId, token), "forbidden");
|
|
7122
7403
|
}
|
|
@@ -7129,7 +7410,7 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
7129
7410
|
);
|
|
7130
7411
|
}
|
|
7131
7412
|
const secretsCache = await loadDeploySecrets({
|
|
7132
|
-
deployUrl:
|
|
7413
|
+
deployUrl: DEPLOY_URL4,
|
|
7133
7414
|
appDir,
|
|
7134
7415
|
appId,
|
|
7135
7416
|
envName,
|
|
@@ -7173,7 +7454,7 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
7173
7454
|
}
|
|
7174
7455
|
const { token: deployToken } = await authenticate(appDir, output, DEPLOY_TOKEN_MIN_VALIDITY_MS);
|
|
7175
7456
|
const repository = await syncDeployRepository({
|
|
7176
|
-
deployUrl:
|
|
7457
|
+
deployUrl: DEPLOY_URL4,
|
|
7177
7458
|
appDir,
|
|
7178
7459
|
appId,
|
|
7179
7460
|
token: deployToken,
|
|
@@ -7189,7 +7470,7 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
7189
7470
|
output
|
|
7190
7471
|
});
|
|
7191
7472
|
const body = await deployBuiltBundle({
|
|
7192
|
-
deployUrl:
|
|
7473
|
+
deployUrl: DEPLOY_URL4,
|
|
7193
7474
|
appDir,
|
|
7194
7475
|
appId,
|
|
7195
7476
|
appName,
|
|
@@ -7342,7 +7623,7 @@ async function authenticate(appDir, output, minimumValidityMs) {
|
|
|
7342
7623
|
|
|
7343
7624
|
// src/cli/commands/undeploy.ts
|
|
7344
7625
|
import * as p13 from "@clack/prompts";
|
|
7345
|
-
var
|
|
7626
|
+
var DEPLOY_URL5 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
7346
7627
|
var undeploy_default = defineDeepspaceCommand({
|
|
7347
7628
|
meta: {
|
|
7348
7629
|
name: "undeploy",
|
|
@@ -7361,7 +7642,8 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
7361
7642
|
},
|
|
7362
7643
|
yes: {
|
|
7363
7644
|
type: "boolean",
|
|
7364
|
-
|
|
7645
|
+
alias: "y",
|
|
7646
|
+
description: "Skip the confirmation (required for --json / non-interactive)",
|
|
7365
7647
|
default: false
|
|
7366
7648
|
}
|
|
7367
7649
|
},
|
|
@@ -7379,7 +7661,7 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
7379
7661
|
let label;
|
|
7380
7662
|
const positional = typeof args.name === "string" && args.name.trim() ? args.name.trim() : void 0;
|
|
7381
7663
|
if (positional) {
|
|
7382
|
-
appId = await resolveAppSelector(
|
|
7664
|
+
appId = await resolveAppSelector(DEPLOY_URL5, token, positional);
|
|
7383
7665
|
label = positional;
|
|
7384
7666
|
} else if (hasWranglerConfig(process.cwd())) {
|
|
7385
7667
|
appId = readAppId(process.cwd(), envName) ?? void 0;
|
|
@@ -7393,6 +7675,13 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
7393
7675
|
);
|
|
7394
7676
|
}
|
|
7395
7677
|
const target2 = label && label !== appId ? `${label} (${appId})` : appId;
|
|
7678
|
+
if (args.yes !== true && (args.json === true || !process.stdin.isTTY)) {
|
|
7679
|
+
throw new Refusal(
|
|
7680
|
+
`Undeploying ${target2} takes its URL offline immediately and destroys its live data \u2014 records, messages, canvas state, cron history \u2014 with the worker (secrets and the registration stay). Re-run with --yes to confirm.`,
|
|
7681
|
+
"confirmation_required",
|
|
7682
|
+
{ extra: { appId } }
|
|
7683
|
+
);
|
|
7684
|
+
}
|
|
7396
7685
|
if (!args.json && !args.yes && process.stdin.isTTY) {
|
|
7397
7686
|
p13.intro(`Undeploying ${target2}`);
|
|
7398
7687
|
const confirmed = await p13.confirm({
|
|
@@ -7407,7 +7696,7 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
7407
7696
|
}
|
|
7408
7697
|
const s = args.json ? null : createSpinner();
|
|
7409
7698
|
s?.start("Removing...");
|
|
7410
|
-
const res = await fetch(`${
|
|
7699
|
+
const res = await fetch(`${DEPLOY_URL5}/api/deploy/${appId}`, {
|
|
7411
7700
|
method: "DELETE",
|
|
7412
7701
|
headers: { Authorization: `Bearer ${token}` }
|
|
7413
7702
|
});
|
|
@@ -7492,7 +7781,7 @@ var whoami_default = defineDeepspaceCommand({
|
|
|
7492
7781
|
});
|
|
7493
7782
|
|
|
7494
7783
|
// src/cli/commands/apps.ts
|
|
7495
|
-
var
|
|
7784
|
+
var DEPLOY_URL6 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
7496
7785
|
var apps_default = defineDeepspaceCommand({
|
|
7497
7786
|
meta: {
|
|
7498
7787
|
name: "apps",
|
|
@@ -7500,7 +7789,7 @@ var apps_default = defineDeepspaceCommand({
|
|
|
7500
7789
|
},
|
|
7501
7790
|
async run({ args }) {
|
|
7502
7791
|
const token = await ensureToken();
|
|
7503
|
-
const { apps, pendingTransfers } = await listAppsPage(
|
|
7792
|
+
const { apps, pendingTransfers } = await listAppsPage(DEPLOY_URL6, token);
|
|
7504
7793
|
if (!args.json) {
|
|
7505
7794
|
if (!apps.length && !pendingTransfers.length) {
|
|
7506
7795
|
console.log("No apps yet. Create one with `npx create-deepspace <name>` and `deepspace deploy`.");
|
|
@@ -7558,7 +7847,7 @@ import { setTimeout as delay } from "timers/promises";
|
|
|
7558
7847
|
var LOG_LEVELS = ["debug", "log", "info", "warn", "error"];
|
|
7559
7848
|
|
|
7560
7849
|
// src/cli/commands/logs.ts
|
|
7561
|
-
var
|
|
7850
|
+
var DEPLOY_URL7 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
7562
7851
|
var RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
7563
7852
|
var DEFAULT_WINDOW_MS = 15 * 60 * 1e3;
|
|
7564
7853
|
var MIN_POLL_MS = 2500;
|
|
@@ -7719,7 +8008,7 @@ var logs_default = defineCommand4({
|
|
|
7719
8008
|
const envArg = args.env;
|
|
7720
8009
|
assertAppTargetResolvable(args.app, { wranglerEnv: envArg });
|
|
7721
8010
|
const token = await ensureToken();
|
|
7722
|
-
const appId = await resolveAppTarget(
|
|
8011
|
+
const appId = await resolveAppTarget(DEPLOY_URL7, token, args.app, { wranglerEnv: envArg });
|
|
7723
8012
|
if (args.level && !LOG_LEVELS.includes(args.level)) {
|
|
7724
8013
|
throw new InputError(
|
|
7725
8014
|
`Invalid --level "${args.level}". Use: ${LOG_LEVELS.join(", ")}`,
|
|
@@ -7738,7 +8027,7 @@ var logs_default = defineCommand4({
|
|
|
7738
8027
|
}
|
|
7739
8028
|
const initialSince = args.since ? parseSince(args.since) : Date.now() - DEFAULT_WINDOW_MS;
|
|
7740
8029
|
const windowLabel = args.since?.trim() || "15m";
|
|
7741
|
-
const registryRow = (await listApps(
|
|
8030
|
+
const registryRow = (await listApps(DEPLOY_URL7, token)).find((app2) => app2.appId === appId);
|
|
7742
8031
|
if (registryRow && registryRow.deployedAt === null) {
|
|
7743
8032
|
const appDir = args.app === void 0 ? findAppDir() : null;
|
|
7744
8033
|
throw new Refusal(
|
|
@@ -7760,7 +8049,7 @@ var logs_default = defineCommand4({
|
|
|
7760
8049
|
if (args.level) params.set("level", args.level);
|
|
7761
8050
|
if (args.search) params.set("search", args.search);
|
|
7762
8051
|
const path = `/api/apps/${appId}/logs?${params}`;
|
|
7763
|
-
return signal ? apiFetch(
|
|
8052
|
+
return signal ? apiFetch(DEPLOY_URL7, token2, path, { signal }) : apiFetchReadWithRetry(DEPLOY_URL7, token2, path);
|
|
7764
8053
|
};
|
|
7765
8054
|
const print = (events) => {
|
|
7766
8055
|
for (const e of events) {
|
|
@@ -7911,6 +8200,8 @@ function formatDate(iso) {
|
|
|
7911
8200
|
function renderSummary(summary) {
|
|
7912
8201
|
const { credits, usageByIntegration } = summary;
|
|
7913
8202
|
const lines = [];
|
|
8203
|
+
lines.push("Account usage (all apps)");
|
|
8204
|
+
lines.push("");
|
|
7914
8205
|
const renews = credits.subscription.renewsAt ? ` \xB7 renews ${formatDate(credits.subscription.renewsAt)}` : "";
|
|
7915
8206
|
lines.push(`Plan: ${credits.subscription.tier}${renews}`);
|
|
7916
8207
|
lines.push(
|
|
@@ -7939,7 +8230,9 @@ function renderSummary(summary) {
|
|
|
7939
8230
|
const nameWidth = Math.max(11, ...usageByIntegration.map((u) => u.name.length));
|
|
7940
8231
|
lines.push(`${"INTEGRATION".padEnd(nameWidth)} CALLS COST`);
|
|
7941
8232
|
for (const u of usageByIntegration) {
|
|
7942
|
-
lines.push(
|
|
8233
|
+
lines.push(
|
|
8234
|
+
`${u.name.padEnd(nameWidth)} ${String(u.count).padEnd(5)} ${formatUsd(u.totalCost)}`
|
|
8235
|
+
);
|
|
7943
8236
|
}
|
|
7944
8237
|
}
|
|
7945
8238
|
lines.push("");
|
|
@@ -7949,46 +8242,46 @@ function renderSummary(summary) {
|
|
|
7949
8242
|
var usage_default = defineDeepspaceCommand({
|
|
7950
8243
|
meta: {
|
|
7951
8244
|
name: "usage",
|
|
7952
|
-
description: "Show
|
|
8245
|
+
description: "Show account-wide credits and integration spend (all apps)"
|
|
7953
8246
|
},
|
|
7954
8247
|
async run({ args }) {
|
|
7955
8248
|
const token = await ensureToken();
|
|
7956
8249
|
const summary = await apiFetch(API_URL3, token, "/api/usage/summary");
|
|
7957
8250
|
if (!args.json) console.log(renderSummary(summary));
|
|
7958
|
-
return { data: { ...summary } };
|
|
8251
|
+
return { data: { scope: "account", ...summary } };
|
|
7959
8252
|
}
|
|
7960
8253
|
});
|
|
7961
8254
|
|
|
7962
8255
|
// src/cli/commands/add.ts
|
|
7963
|
-
import { existsSync as
|
|
7964
|
-
import { dirname as dirname10, join as
|
|
8256
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, realpathSync as realpathSync4 } from "fs";
|
|
8257
|
+
import { dirname as dirname10, join as join23, resolve as resolve17 } from "path";
|
|
7965
8258
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
7966
8259
|
|
|
7967
8260
|
// src/cli/commands/feature-installer.ts
|
|
7968
8261
|
import {
|
|
7969
|
-
closeSync,
|
|
8262
|
+
closeSync as closeSync2,
|
|
7970
8263
|
copyFileSync,
|
|
7971
|
-
existsSync as
|
|
8264
|
+
existsSync as existsSync21,
|
|
7972
8265
|
fstatSync,
|
|
7973
|
-
mkdirSync as
|
|
8266
|
+
mkdirSync as mkdirSync9,
|
|
7974
8267
|
mkdtempSync,
|
|
7975
|
-
openSync,
|
|
8268
|
+
openSync as openSync2,
|
|
7976
8269
|
readFileSync as readFileSync18,
|
|
7977
8270
|
readSync,
|
|
7978
8271
|
readdirSync as readdirSync7,
|
|
7979
|
-
rmSync as
|
|
7980
|
-
writeFileSync as
|
|
8272
|
+
rmSync as rmSync7,
|
|
8273
|
+
writeFileSync as writeFileSync7
|
|
7981
8274
|
} from "fs";
|
|
7982
|
-
import { spawnSync as
|
|
8275
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
7983
8276
|
import { tmpdir as tmpdir2 } from "os";
|
|
7984
|
-
import { dirname as dirname9, isAbsolute as isAbsolute5, join as
|
|
8277
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join22, resolve as resolve16 } from "path";
|
|
7985
8278
|
import { fileURLToPath } from "url";
|
|
7986
8279
|
var NAV_MARKER = "// \u2500\u2500 Features add nav items below this line \u2500\u2500";
|
|
7987
8280
|
function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
7988
8281
|
let dir = dirname9(fileURLToPath(moduleUrl));
|
|
7989
8282
|
for (; ; ) {
|
|
7990
|
-
const packagePath =
|
|
7991
|
-
if (
|
|
8283
|
+
const packagePath = join22(dir, "package.json");
|
|
8284
|
+
if (existsSync21(packagePath)) {
|
|
7992
8285
|
try {
|
|
7993
8286
|
const pkg2 = JSON.parse(readFileSync18(packagePath, "utf-8"));
|
|
7994
8287
|
if (pkg2.name === "deepspace") return dir;
|
|
@@ -8001,11 +8294,11 @@ function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
|
8001
8294
|
}
|
|
8002
8295
|
}
|
|
8003
8296
|
function hasFeatureCatalog(packageRoot = featurePackageRoot()) {
|
|
8004
|
-
return
|
|
8297
|
+
return existsSync21(join22(packageRoot, "features"));
|
|
8005
8298
|
}
|
|
8006
8299
|
function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
8007
|
-
const configPath =
|
|
8008
|
-
if (!
|
|
8300
|
+
const configPath = join22(packageRoot, "features", id, "feature.json");
|
|
8301
|
+
if (!existsSync21(configPath)) return null;
|
|
8009
8302
|
try {
|
|
8010
8303
|
const config = JSON.parse(readFileSync18(configPath, "utf-8"));
|
|
8011
8304
|
if (typeof config.id !== "string" || typeof config.name !== "string" || !Array.isArray(config.files)) {
|
|
@@ -8017,8 +8310,8 @@ function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
|
8017
8310
|
}
|
|
8018
8311
|
}
|
|
8019
8312
|
function listFeatureConfigs(packageRoot = featurePackageRoot()) {
|
|
8020
|
-
const featuresDir =
|
|
8021
|
-
if (!
|
|
8313
|
+
const featuresDir = join22(packageRoot, "features");
|
|
8314
|
+
if (!existsSync21(featuresDir)) return [];
|
|
8022
8315
|
return readdirSync7(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));
|
|
8023
8316
|
}
|
|
8024
8317
|
function featureCatalog(packageRoot = featurePackageRoot()) {
|
|
@@ -8066,17 +8359,17 @@ function resolveFeatureDestPath(fileDest, route) {
|
|
|
8066
8359
|
return route?.protected === false ? `${appPages}${rest}` : `${appPages}(protected)/${rest}`;
|
|
8067
8360
|
}
|
|
8068
8361
|
function copyFile(src, dest) {
|
|
8069
|
-
|
|
8362
|
+
mkdirSync9(dirname9(dest), { recursive: true });
|
|
8070
8363
|
copyFileSync(src, dest);
|
|
8071
8364
|
}
|
|
8072
8365
|
function copyFileWithImportShift(src, dest, levels) {
|
|
8073
|
-
|
|
8366
|
+
mkdirSync9(dirname9(dest), { recursive: true });
|
|
8074
8367
|
const prefix = "../".repeat(levels);
|
|
8075
8368
|
const shifted = readFileSync18(src, "utf-8").replace(
|
|
8076
8369
|
/(from\s+|import\s+|import\()(['"])(\.\.\/)/g,
|
|
8077
8370
|
`$1$2${prefix}$3`
|
|
8078
8371
|
);
|
|
8079
|
-
|
|
8372
|
+
writeFileSync7(dest, shifted);
|
|
8080
8373
|
}
|
|
8081
8374
|
function installFiles(packageRoot, config, targetDir, emit) {
|
|
8082
8375
|
let copied = 0;
|
|
@@ -8085,12 +8378,12 @@ function installFiles(packageRoot, config, targetDir, emit) {
|
|
|
8085
8378
|
const preexisting = [];
|
|
8086
8379
|
const missing = [];
|
|
8087
8380
|
for (const file of config.files) {
|
|
8088
|
-
const srcPath =
|
|
8381
|
+
const srcPath = join22(packageRoot, "features", config.id, file.src);
|
|
8089
8382
|
const resolvedDest = resolveFeatureDestPath(file.dest, config.route);
|
|
8090
|
-
const destPath =
|
|
8091
|
-
const existed =
|
|
8383
|
+
const destPath = join22(targetDir, resolvedDest);
|
|
8384
|
+
const existed = existsSync21(destPath);
|
|
8092
8385
|
if (existed) preexisting.push(resolvedDest);
|
|
8093
|
-
if (!
|
|
8386
|
+
if (!existsSync21(srcPath)) {
|
|
8094
8387
|
emit(` Warning: source not found: ${file.src}`, "stderr");
|
|
8095
8388
|
if (!existed) missing.push(resolvedDest);
|
|
8096
8389
|
continue;
|
|
@@ -8127,8 +8420,8 @@ function unresolvedSchema(schema, reason, emit) {
|
|
|
8127
8420
|
}
|
|
8128
8421
|
function integrateSchema(config, targetDir, emit) {
|
|
8129
8422
|
if (!config.schema) return [];
|
|
8130
|
-
const schemasPath =
|
|
8131
|
-
if (!
|
|
8423
|
+
const schemasPath = join22(targetDir, "src", "schemas.ts");
|
|
8424
|
+
if (!existsSync21(schemasPath)) {
|
|
8132
8425
|
return [unresolvedSchema(config.schema, "src/schemas.ts was not found.", emit)];
|
|
8133
8426
|
}
|
|
8134
8427
|
let content = readFileSync18(schemasPath, "utf-8");
|
|
@@ -8162,7 +8455,7 @@ export const schemas`
|
|
|
8162
8455
|
return [unresolvedSchema(config.schema, reason, emit)];
|
|
8163
8456
|
}
|
|
8164
8457
|
content = withEntry;
|
|
8165
|
-
|
|
8458
|
+
writeFileSync7(schemasPath, content);
|
|
8166
8459
|
emit(` Schema integrated: ${exportName} -> schemas.ts`);
|
|
8167
8460
|
return [];
|
|
8168
8461
|
}
|
|
@@ -8184,8 +8477,8 @@ function unresolvedActions(actions, reason, emit) {
|
|
|
8184
8477
|
}
|
|
8185
8478
|
function integrateActions(config, targetDir, emit) {
|
|
8186
8479
|
if (!config.actions?.length) return [];
|
|
8187
|
-
const actionsPath =
|
|
8188
|
-
if (!
|
|
8480
|
+
const actionsPath = join22(targetDir, "src", "actions", "index.ts");
|
|
8481
|
+
if (!existsSync21(actionsPath)) {
|
|
8189
8482
|
return [unresolvedActions(config.actions, "src/actions/index.ts was not found.", emit)];
|
|
8190
8483
|
}
|
|
8191
8484
|
let content = readFileSync18(actionsPath, "utf-8");
|
|
@@ -8228,14 +8521,14 @@ ${registryEntry}`);
|
|
|
8228
8521
|
integrated++;
|
|
8229
8522
|
emit(` Action integrated: ${action.registerAs} (${action.exportName})`);
|
|
8230
8523
|
}
|
|
8231
|
-
if (integrated > 0)
|
|
8524
|
+
if (integrated > 0) writeFileSync7(actionsPath, content);
|
|
8232
8525
|
return unresolved;
|
|
8233
8526
|
}
|
|
8234
8527
|
function integrateCode(config, targetDir, emit) {
|
|
8235
8528
|
const unresolved = [];
|
|
8236
8529
|
for (const insertion of config.code ?? []) {
|
|
8237
|
-
const targetPath =
|
|
8238
|
-
if (!
|
|
8530
|
+
const targetPath = join22(targetDir, insertion.file);
|
|
8531
|
+
if (!existsSync21(targetPath)) {
|
|
8239
8532
|
unresolved.push({
|
|
8240
8533
|
area: "code",
|
|
8241
8534
|
reason: `${insertion.file} was not found.`,
|
|
@@ -8273,32 +8566,32 @@ ${content.slice(
|
|
|
8273
8566
|
const prefix = insertion.placement === "before" ? "" : "\n";
|
|
8274
8567
|
const suffix = insertion.placement === "before" ? "\n" : "";
|
|
8275
8568
|
content = `${content.slice(0, insertAt)}${prefix}${insertion.insert}${suffix}${content.slice(insertAt)}`;
|
|
8276
|
-
|
|
8569
|
+
writeFileSync7(targetPath, content);
|
|
8277
8570
|
emit(` Integrated: ${insertion.file}`);
|
|
8278
8571
|
}
|
|
8279
8572
|
return unresolved;
|
|
8280
8573
|
}
|
|
8281
8574
|
function integrateIgnore(config, targetDir) {
|
|
8282
8575
|
if (!config.ignore?.length) return;
|
|
8283
|
-
const targetPath =
|
|
8284
|
-
const current =
|
|
8576
|
+
const targetPath = join22(targetDir, ".gitignore");
|
|
8577
|
+
const current = existsSync21(targetPath) ? readFileSync18(targetPath, "utf-8") : "";
|
|
8285
8578
|
const present = new Set(current.split(/\r?\n/).map((line) => line.trim()));
|
|
8286
8579
|
const missing = config.ignore.filter((pattern) => !present.has(pattern));
|
|
8287
8580
|
if (missing.length === 0) return;
|
|
8288
8581
|
const separator = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
8289
|
-
|
|
8582
|
+
writeFileSync7(targetPath, `${current}${separator}${missing.join("\n")}
|
|
8290
8583
|
`);
|
|
8291
8584
|
}
|
|
8292
8585
|
function integrateWrangler(config, targetDir, emit) {
|
|
8293
8586
|
const runWorkerFirst = config.wrangler?.runWorkerFirst === true;
|
|
8294
8587
|
const rateLimits = config.wrangler?.rateLimits ?? [];
|
|
8295
8588
|
if (!runWorkerFirst && rateLimits.length === 0) return [];
|
|
8296
|
-
const targetPath =
|
|
8589
|
+
const targetPath = join22(targetDir, "wrangler.toml");
|
|
8297
8590
|
const steps = [
|
|
8298
8591
|
...runWorkerFirst ? ["Set assets.run_worker_first = true in wrangler.toml."] : [],
|
|
8299
8592
|
...rateLimits.map((binding) => `Add the ${binding.name} rate-limit binding to wrangler.toml.`)
|
|
8300
8593
|
];
|
|
8301
|
-
if (!
|
|
8594
|
+
if (!existsSync21(targetPath)) {
|
|
8302
8595
|
return [{ area: "wrangler", reason: "wrangler.toml was not found.", steps }];
|
|
8303
8596
|
}
|
|
8304
8597
|
let source = readFileSync18(targetPath, "utf-8");
|
|
@@ -8323,7 +8616,7 @@ ${rateLimitBindingBlock(scope, binding)}
|
|
|
8323
8616
|
}
|
|
8324
8617
|
}
|
|
8325
8618
|
if (changed) {
|
|
8326
|
-
|
|
8619
|
+
writeFileSync7(targetPath, source);
|
|
8327
8620
|
emit(" Integrated: wrangler.toml");
|
|
8328
8621
|
} else emit(" Integration already present: wrangler.toml");
|
|
8329
8622
|
return [];
|
|
@@ -8377,8 +8670,8 @@ function rateLimitBindingBlock(scope, binding) {
|
|
|
8377
8670
|
}
|
|
8378
8671
|
function integrateCss(packageRoot, config, targetDir, emit) {
|
|
8379
8672
|
if (!config.css?.length) return [];
|
|
8380
|
-
const stylesPath =
|
|
8381
|
-
if (!
|
|
8673
|
+
const stylesPath = join22(targetDir, "src", "styles.css");
|
|
8674
|
+
if (!existsSync21(stylesPath)) {
|
|
8382
8675
|
emit(" Warning: Cannot integrate CSS \u2014 src/styles.css not found");
|
|
8383
8676
|
return [
|
|
8384
8677
|
{
|
|
@@ -8394,8 +8687,8 @@ function integrateCss(packageRoot, config, targetDir, emit) {
|
|
|
8394
8687
|
let integrated = 0;
|
|
8395
8688
|
const unresolved = [];
|
|
8396
8689
|
for (const cssFile of config.css) {
|
|
8397
|
-
const srcPath =
|
|
8398
|
-
if (!
|
|
8690
|
+
const srcPath = join22(packageRoot, "features", config.id, cssFile);
|
|
8691
|
+
if (!existsSync21(srcPath)) {
|
|
8399
8692
|
emit(` Warning: CSS source not found: ${cssFile}`, "stderr");
|
|
8400
8693
|
unresolved.push({
|
|
8401
8694
|
area: "css",
|
|
@@ -8416,7 +8709,7 @@ ${css}`;
|
|
|
8416
8709
|
integrated++;
|
|
8417
8710
|
emit(` CSS integrated: ${cssFile} -> styles.css`);
|
|
8418
8711
|
}
|
|
8419
|
-
if (integrated > 0)
|
|
8712
|
+
if (integrated > 0) writeFileSync7(stylesPath, styles);
|
|
8420
8713
|
return unresolved;
|
|
8421
8714
|
}
|
|
8422
8715
|
function unresolvedRoute(route, config, reason, emit) {
|
|
@@ -8438,8 +8731,8 @@ function routeRolesSuffix(route) {
|
|
|
8438
8731
|
}
|
|
8439
8732
|
function integrateRoute(config, targetDir, emit) {
|
|
8440
8733
|
if (!config.route) return [];
|
|
8441
|
-
const navPath =
|
|
8442
|
-
if (!
|
|
8734
|
+
const navPath = join22(targetDir, "src", "nav.ts");
|
|
8735
|
+
if (!existsSync21(navPath)) {
|
|
8443
8736
|
return [unresolvedRoute(config.route, config, "src/nav.ts was not found.", emit)];
|
|
8444
8737
|
}
|
|
8445
8738
|
let content = readFileSync18(navPath, "utf-8");
|
|
@@ -8463,7 +8756,7 @@ function integrateRoute(config, targetDir, emit) {
|
|
|
8463
8756
|
const entry = ` { path: '${routePath}', label: '${config.name}'${roles} },`;
|
|
8464
8757
|
content = content.replace(NAV_MARKER, `${NAV_MARKER}
|
|
8465
8758
|
${entry}`);
|
|
8466
|
-
|
|
8759
|
+
writeFileSync7(navPath, content);
|
|
8467
8760
|
emit(` Nav wired: ${routePath} (${config.name})`);
|
|
8468
8761
|
emit(" Route: automatic (file-based routing via src/pages/)");
|
|
8469
8762
|
return [];
|
|
@@ -8486,14 +8779,14 @@ function runPackageInstall(packageManager, targetDir, outputVisible) {
|
|
|
8486
8779
|
shell: process.platform === "win32"
|
|
8487
8780
|
};
|
|
8488
8781
|
if (outputVisible) {
|
|
8489
|
-
const child =
|
|
8782
|
+
const child = spawnSync7(packageManager, ["install"], { ...options, stdio: "inherit" });
|
|
8490
8783
|
return { error: child.error, status: child.status };
|
|
8491
8784
|
}
|
|
8492
|
-
const captureDir = mkdtempSync(
|
|
8785
|
+
const captureDir = mkdtempSync(join22(tmpdir2(), "deepspace-add-install-"));
|
|
8493
8786
|
let outputFd;
|
|
8494
8787
|
try {
|
|
8495
|
-
outputFd =
|
|
8496
|
-
const child =
|
|
8788
|
+
outputFd = openSync2(join22(captureDir, "output.log"), "w+");
|
|
8789
|
+
const child = spawnSync7(packageManager, ["install"], {
|
|
8497
8790
|
...options,
|
|
8498
8791
|
stdio: ["ignore", outputFd, outputFd]
|
|
8499
8792
|
});
|
|
@@ -8503,8 +8796,8 @@ function runPackageInstall(packageManager, targetDir, outputVisible) {
|
|
|
8503
8796
|
diagnosticTail: readDiagnosticTail(outputFd)
|
|
8504
8797
|
};
|
|
8505
8798
|
} finally {
|
|
8506
|
-
if (outputFd !== void 0)
|
|
8507
|
-
|
|
8799
|
+
if (outputFd !== void 0) closeSync2(outputFd);
|
|
8800
|
+
rmSync7(captureDir, { recursive: true, force: true });
|
|
8508
8801
|
}
|
|
8509
8802
|
}
|
|
8510
8803
|
function integrateDependencies(config, targetDir, runInstall, outputVisible, emit) {
|
|
@@ -8516,8 +8809,8 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
|
|
|
8516
8809
|
if (declarations.every(({ values }) => !values || Object.keys(values).length === 0)) {
|
|
8517
8810
|
return { added: [], installRan: false, packageManager };
|
|
8518
8811
|
}
|
|
8519
|
-
const packagePath =
|
|
8520
|
-
if (!
|
|
8812
|
+
const packagePath = join22(targetDir, "package.json");
|
|
8813
|
+
if (!existsSync21(packagePath)) {
|
|
8521
8814
|
emit(" Could not patch package.json (file not found)");
|
|
8522
8815
|
return { added: [], installRan: false, packageManager };
|
|
8523
8816
|
}
|
|
@@ -8547,7 +8840,7 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
|
|
|
8547
8840
|
if (added.length === 0) {
|
|
8548
8841
|
emit(" Dependencies already present");
|
|
8549
8842
|
} else {
|
|
8550
|
-
|
|
8843
|
+
writeFileSync7(packagePath, `${JSON.stringify(pkg2, null, 2)}
|
|
8551
8844
|
`, "utf-8");
|
|
8552
8845
|
emit(` Added to package.json: ${added.map(({ name }) => name).join(", ")}`);
|
|
8553
8846
|
if (skipped.length) emit(` Already present (kept your version): ${skipped.join(", ")}`);
|
|
@@ -8583,7 +8876,7 @@ ${child.diagnosticTail}` : "";
|
|
|
8583
8876
|
}
|
|
8584
8877
|
function dependencySnapshot(targetDir) {
|
|
8585
8878
|
try {
|
|
8586
|
-
const pkg2 = JSON.parse(readFileSync18(
|
|
8879
|
+
const pkg2 = JSON.parse(readFileSync18(join22(targetDir, "package.json"), "utf-8"));
|
|
8587
8880
|
return {
|
|
8588
8881
|
dependencies: pkg2.dependencies ?? {},
|
|
8589
8882
|
devDependencies: pkg2.devDependencies ?? {}
|
|
@@ -8595,7 +8888,7 @@ function dependencySnapshot(targetDir) {
|
|
|
8595
8888
|
function installedNavFeatures(packageRoot, targetDir) {
|
|
8596
8889
|
return listFeatureConfigs(packageRoot).filter((feature) => feature.category === "nav" && feature.files.length > 0).filter((feature) => {
|
|
8597
8890
|
const signature = resolveFeatureDestPath(feature.files[0].dest, feature.route);
|
|
8598
|
-
return
|
|
8891
|
+
return existsSync21(join22(targetDir, signature));
|
|
8599
8892
|
}).map((feature) => feature.id);
|
|
8600
8893
|
}
|
|
8601
8894
|
function warnNavConflicts(packageRoot, config, targetDir, emit) {
|
|
@@ -8828,9 +9121,9 @@ function renderFeatureInfo(config) {
|
|
|
8828
9121
|
return lines.join("\n");
|
|
8829
9122
|
}
|
|
8830
9123
|
function isDeepSpaceApp(dir) {
|
|
8831
|
-
if (
|
|
8832
|
-
const packagePath =
|
|
8833
|
-
if (!
|
|
9124
|
+
if (existsSync22(join23(dir, "wrangler.toml"))) return true;
|
|
9125
|
+
const packagePath = join23(dir, "package.json");
|
|
9126
|
+
if (!existsSync22(packagePath)) return false;
|
|
8834
9127
|
try {
|
|
8835
9128
|
const pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
|
|
8836
9129
|
return Boolean(pkg2.dependencies?.deepspace || pkg2.devDependencies?.deepspace);
|
|
@@ -8841,8 +9134,8 @@ function isDeepSpaceApp(dir) {
|
|
|
8841
9134
|
function installedPackageRoot(fromDir) {
|
|
8842
9135
|
let dir = fromDir;
|
|
8843
9136
|
for (; ; ) {
|
|
8844
|
-
const candidate =
|
|
8845
|
-
if (
|
|
9137
|
+
const candidate = join23(dir, "node_modules", "deepspace");
|
|
9138
|
+
if (existsSync22(join23(candidate, "package.json"))) return candidate;
|
|
8846
9139
|
const parent = dirname10(dir);
|
|
8847
9140
|
if (parent === dir) return null;
|
|
8848
9141
|
dir = parent;
|
|
@@ -8859,9 +9152,9 @@ async function targetInstaller(appDir) {
|
|
|
8859
9152
|
if (realpathSync4(installedRoot) === realpathSync4(featurePackageRoot())) {
|
|
8860
9153
|
return { installFeature };
|
|
8861
9154
|
}
|
|
8862
|
-
const entry =
|
|
8863
|
-
const catalog =
|
|
8864
|
-
if (!
|
|
9155
|
+
const entry = join23(installedRoot, "dist", "feature-installer.js");
|
|
9156
|
+
const catalog = join23(installedRoot, "features");
|
|
9157
|
+
if (!existsSync22(entry) || !existsSync22(catalog)) {
|
|
8865
9158
|
throw new Refusal(
|
|
8866
9159
|
`The app's installed deepspace package does not contain the typed feature installer.
|
|
8867
9160
|
Expected: ${entry}
|
|
@@ -8954,7 +9247,7 @@ var add_default = defineDeepspaceCommand({
|
|
|
8954
9247
|
return { data: { features: featureCatalog(root) } };
|
|
8955
9248
|
}
|
|
8956
9249
|
const appDir = resolve17(args.dir ?? ".");
|
|
8957
|
-
if (!
|
|
9250
|
+
if (!existsSync22(appDir)) {
|
|
8958
9251
|
throw new Refusal(
|
|
8959
9252
|
`Target directory not found: ${appDir}
|
|
8960
9253
|
Create a new app first: deepspace app create <name>`,
|
|
@@ -9012,7 +9305,7 @@ Create one with: deepspace app create <name>`,
|
|
|
9012
9305
|
// src/cli/commands/domain.ts
|
|
9013
9306
|
import { defineCommand as defineCommand5 } from "citty";
|
|
9014
9307
|
var API_URL4 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
9015
|
-
var
|
|
9308
|
+
var DEPLOY_URL8 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9016
9309
|
var api = (token, path, init) => apiFetch(API_URL4, token, path, init);
|
|
9017
9310
|
function scrubInternal(value) {
|
|
9018
9311
|
if (Array.isArray(value)) return value.map(scrubInternal);
|
|
@@ -9093,7 +9386,7 @@ var buy = defineDeepspaceCommand({
|
|
|
9093
9386
|
async run({ args }) {
|
|
9094
9387
|
const domain = String(args.domain);
|
|
9095
9388
|
const token = await ensureToken();
|
|
9096
|
-
const appId = await resolveAppTarget(
|
|
9389
|
+
const appId = await resolveAppTarget(DEPLOY_URL8, token, args.app);
|
|
9097
9390
|
const priceCheck = await api(token, "/api/domains/check", {
|
|
9098
9391
|
method: "POST",
|
|
9099
9392
|
body: JSON.stringify({ domains: [domain] })
|
|
@@ -9246,7 +9539,7 @@ var attach = defineDeepspaceCommand({
|
|
|
9246
9539
|
const domain = String(args.domain);
|
|
9247
9540
|
const token = await ensureToken();
|
|
9248
9541
|
const targetAppId = await resolveAppTarget(
|
|
9249
|
-
|
|
9542
|
+
DEPLOY_URL8,
|
|
9250
9543
|
token,
|
|
9251
9544
|
args.app
|
|
9252
9545
|
);
|
|
@@ -9330,10 +9623,10 @@ var domain_default = defineCommand5({
|
|
|
9330
9623
|
// src/cli/commands/app-files.ts
|
|
9331
9624
|
import { defineCommand as defineCommand6 } from "citty";
|
|
9332
9625
|
import { basename as basename4, resolve as resolve18 } from "path";
|
|
9333
|
-
import { existsSync as
|
|
9626
|
+
import { existsSync as existsSync23, statSync as statSync4 } from "fs";
|
|
9334
9627
|
|
|
9335
9628
|
// src/cli/lib/app-files-api.ts
|
|
9336
|
-
import { createWriteStream, openAsBlob, statSync } from "fs";
|
|
9629
|
+
import { createWriteStream, openAsBlob, statSync as statSync3 } from "fs";
|
|
9337
9630
|
import { basename as basename3, extname } from "path";
|
|
9338
9631
|
import { Readable } from "stream";
|
|
9339
9632
|
import { pipeline } from "stream/promises";
|
|
@@ -9443,7 +9736,7 @@ Content-Type: ${contentType}\r
|
|
|
9443
9736
|
}
|
|
9444
9737
|
async function uploadAppFile(baseUrl, token, appId, localPath, key, onProgress) {
|
|
9445
9738
|
const path = filesPath(appId, `/upload?key=${encodeKeyPath(key)}`);
|
|
9446
|
-
const size =
|
|
9739
|
+
const size = statSync3(localPath).size;
|
|
9447
9740
|
if (size > MAX_APP_FILE_BYTES) {
|
|
9448
9741
|
throw new ApiError(oversizeMessage(size), 0, "too_large", path);
|
|
9449
9742
|
}
|
|
@@ -9561,11 +9854,11 @@ async function downloadAppFile(baseUrl, token, appId, key, destination) {
|
|
|
9561
9854
|
}
|
|
9562
9855
|
|
|
9563
9856
|
// src/cli/commands/app-files.ts
|
|
9564
|
-
var
|
|
9857
|
+
var DEPLOY_URL9 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9565
9858
|
var PLATFORM_URL = process.env.DEEPSPACE_PLATFORM_URL ?? PLATFORM_URLS.platform;
|
|
9566
9859
|
async function target(app2) {
|
|
9567
9860
|
const token = await ensureToken();
|
|
9568
|
-
const appId = await resolveAppTarget(
|
|
9861
|
+
const appId = await resolveAppTarget(DEPLOY_URL9, token, app2);
|
|
9569
9862
|
return { token, appId };
|
|
9570
9863
|
}
|
|
9571
9864
|
var MAX_LIST_LIMIT = 1e3;
|
|
@@ -9609,7 +9902,7 @@ var put = defineDeepspaceCommand({
|
|
|
9609
9902
|
},
|
|
9610
9903
|
async run({ args }) {
|
|
9611
9904
|
const localPath = resolve18(String(args.file));
|
|
9612
|
-
if (!
|
|
9905
|
+
if (!existsSync23(localPath) || !statSync4(localPath).isFile()) {
|
|
9613
9906
|
throw new Refusal(`No such file: ${args.file}`, "file_not_found");
|
|
9614
9907
|
}
|
|
9615
9908
|
const key = requireKey(args.key ?? basename4(localPath));
|
|
@@ -9752,7 +10045,7 @@ function isTestAccountEmail(email) {
|
|
|
9752
10045
|
|
|
9753
10046
|
// src/cli/commands/collaborators.ts
|
|
9754
10047
|
var API_URL5 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
9755
|
-
var
|
|
10048
|
+
var DEPLOY_URL10 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9756
10049
|
var COLLABORATOR_GRANTS = ["deploy", "secrets:read", "secrets:write"];
|
|
9757
10050
|
var COLLABORATOR_GRANT_SENTENCE = `grants ${COLLABORATOR_GRANTS.join(", ")} \u2014 they can deploy the app AND read and change every app secret in plaintext; they cannot undeploy or transfer it`;
|
|
9758
10051
|
function api2(token, path, init) {
|
|
@@ -9765,7 +10058,7 @@ var list4 = defineDeepspaceCommand({
|
|
|
9765
10058
|
},
|
|
9766
10059
|
async run({ args }) {
|
|
9767
10060
|
const token = await ensureToken();
|
|
9768
|
-
const app2 = await resolveAppTarget(
|
|
10061
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
9769
10062
|
const { collaborators, pending = [] } = await api2(token, `/api/app-collaborators/${encodeURIComponent(app2)}`);
|
|
9770
10063
|
if (!collaborators.length && !pending.length) {
|
|
9771
10064
|
if (!args.json) {
|
|
@@ -9782,8 +10075,8 @@ var list4 = defineDeepspaceCommand({
|
|
|
9782
10075
|
}
|
|
9783
10076
|
if (pending.length) {
|
|
9784
10077
|
console.log(`PENDING INVITES ON ${app2}`);
|
|
9785
|
-
for (const
|
|
9786
|
-
console.log(` ${
|
|
10078
|
+
for (const p35 of pending) {
|
|
10079
|
+
console.log(` ${p35.email} (expires ${new Date(p35.expiresAt).toLocaleDateString()})`);
|
|
9787
10080
|
}
|
|
9788
10081
|
}
|
|
9789
10082
|
}
|
|
@@ -9806,7 +10099,7 @@ var add = defineDeepspaceCommand({
|
|
|
9806
10099
|
async run({ args }) {
|
|
9807
10100
|
const email = String(args.email);
|
|
9808
10101
|
const token = await ensureToken();
|
|
9809
|
-
const app2 = await resolveAppTarget(
|
|
10102
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
9810
10103
|
if (isTestAccountEmail(email)) {
|
|
9811
10104
|
throw new Refusal(
|
|
9812
10105
|
"Test accounts (@deepspace.test) cannot be added as collaborators. Use a real DeepSpace account.",
|
|
@@ -9853,7 +10146,7 @@ var remove = defineDeepspaceCommand({
|
|
|
9853
10146
|
async run({ args }) {
|
|
9854
10147
|
const email = String(args.email);
|
|
9855
10148
|
const token = await ensureToken();
|
|
9856
|
-
const app2 = await resolveAppTarget(
|
|
10149
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
9857
10150
|
const { collaborators } = await api2(
|
|
9858
10151
|
token,
|
|
9859
10152
|
`/api/app-collaborators/${encodeURIComponent(app2)}`
|
|
@@ -9883,13 +10176,13 @@ var cancel3 = defineDeepspaceCommand({
|
|
|
9883
10176
|
async run({ args }) {
|
|
9884
10177
|
const email = String(args.email);
|
|
9885
10178
|
const token = await ensureToken();
|
|
9886
|
-
const app2 = await resolveAppTarget(
|
|
10179
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
9887
10180
|
const { pending = [] } = await api2(
|
|
9888
10181
|
token,
|
|
9889
10182
|
`/api/app-collaborators/${encodeURIComponent(app2)}`
|
|
9890
10183
|
);
|
|
9891
10184
|
const target2 = email.trim().toLowerCase();
|
|
9892
|
-
const found = pending.find((
|
|
10185
|
+
const found = pending.find((p35) => p35.email.toLowerCase() === target2);
|
|
9893
10186
|
if (!found) {
|
|
9894
10187
|
throw new Refusal(`${email} has no pending invite on ${app2}`, "no_pending_invite", {
|
|
9895
10188
|
action: cliAction("deepspace", "app", "collaborators", "list", "--app", app2)
|
|
@@ -9963,7 +10256,7 @@ var collaborators_default = defineCommand7({
|
|
|
9963
10256
|
import { defineCommand as defineCommand8 } from "citty";
|
|
9964
10257
|
import * as p14 from "@clack/prompts";
|
|
9965
10258
|
var API_URL6 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
9966
|
-
var
|
|
10259
|
+
var DEPLOY_URL11 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9967
10260
|
var offer = defineDeepspaceCommand({
|
|
9968
10261
|
meta: { name: "offer", description: "Offer this app to another DeepSpace user" },
|
|
9969
10262
|
args: {
|
|
@@ -9977,7 +10270,7 @@ var offer = defineDeepspaceCommand({
|
|
|
9977
10270
|
async run({ args }) {
|
|
9978
10271
|
const email = String(args.email);
|
|
9979
10272
|
const token = await ensureToken();
|
|
9980
|
-
const app2 = await resolveAppTarget(
|
|
10273
|
+
const app2 = await resolveAppTarget(DEPLOY_URL11, token, args.app);
|
|
9981
10274
|
const pending = await apiFetch(
|
|
9982
10275
|
API_URL6,
|
|
9983
10276
|
token,
|
|
@@ -10033,7 +10326,7 @@ var status2 = defineDeepspaceCommand({
|
|
|
10033
10326
|
},
|
|
10034
10327
|
async run({ args }) {
|
|
10035
10328
|
const token = await ensureToken();
|
|
10036
|
-
const app2 = await resolveAppTarget(
|
|
10329
|
+
const app2 = await resolveAppTarget(DEPLOY_URL11, token, args.app);
|
|
10037
10330
|
const { transfer } = await apiFetch(API_URL6, token, `/api/app-transfers/${encodeURIComponent(app2)}`);
|
|
10038
10331
|
if (!transfer) {
|
|
10039
10332
|
if (!args.json) console.log(`No pending transfer for ${app2}.`);
|
|
@@ -10062,7 +10355,7 @@ var accept2 = defineDeepspaceCommand({
|
|
|
10062
10355
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10063
10356
|
throw new Refusal(msg, msg.startsWith("No app id") ? "not_in_app_repo" : "invalid_app");
|
|
10064
10357
|
}
|
|
10065
|
-
const { replayed, acceptedAt, inheritedCollaborators } = await apiFetch(
|
|
10358
|
+
const { replayed, acceptedAt, inheritedCollaborators } = await apiFetch(DEPLOY_URL11, token, `/api/apps/${encodeURIComponent(app2)}/transfer/accept`, {
|
|
10066
10359
|
method: "POST"
|
|
10067
10360
|
});
|
|
10068
10361
|
if (!args.json) {
|
|
@@ -10097,7 +10390,7 @@ var cancel4 = defineDeepspaceCommand({
|
|
|
10097
10390
|
},
|
|
10098
10391
|
async run({ args }) {
|
|
10099
10392
|
const token = await ensureToken();
|
|
10100
|
-
const app2 = await resolveAppTarget(
|
|
10393
|
+
const app2 = await resolveAppTarget(DEPLOY_URL11, token, args.app);
|
|
10101
10394
|
await apiFetch(API_URL6, token, `/api/app-transfers/${encodeURIComponent(app2)}`, { method: "DELETE" });
|
|
10102
10395
|
if (!args.json) console.log(`\u2713 Transfer offer for ${app2} cancelled.`);
|
|
10103
10396
|
return { data: { app: app2, cancelled: true } };
|
|
@@ -10491,8 +10784,8 @@ var integrations_default = defineCommand9({
|
|
|
10491
10784
|
|
|
10492
10785
|
// src/cli/commands/feedback.ts
|
|
10493
10786
|
import * as p16 from "@clack/prompts";
|
|
10494
|
-
import { existsSync as
|
|
10495
|
-
import { dirname as dirname11, join as
|
|
10787
|
+
import { existsSync as existsSync24, readFileSync as readFileSync21 } from "fs";
|
|
10788
|
+
import { dirname as dirname11, join as join24 } from "path";
|
|
10496
10789
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10497
10790
|
import { release } from "os";
|
|
10498
10791
|
var API_URL8 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
@@ -10515,11 +10808,11 @@ function readCliVersion() {
|
|
|
10515
10808
|
try {
|
|
10516
10809
|
const here = dirname11(fileURLToPath2(import.meta.url));
|
|
10517
10810
|
const candidates = [
|
|
10518
|
-
|
|
10519
|
-
|
|
10811
|
+
join24(here, "..", "package.json"),
|
|
10812
|
+
join24(here, "..", "..", "..", "package.json")
|
|
10520
10813
|
];
|
|
10521
10814
|
for (const path of candidates) {
|
|
10522
|
-
if (!
|
|
10815
|
+
if (!existsSync24(path)) continue;
|
|
10523
10816
|
const pkg2 = JSON.parse(readFileSync21(path, "utf-8"));
|
|
10524
10817
|
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
10525
10818
|
}
|
|
@@ -10750,7 +11043,7 @@ function parseCursorArg(raw) {
|
|
|
10750
11043
|
}
|
|
10751
11044
|
|
|
10752
11045
|
// src/cli/commands/secrets.ts
|
|
10753
|
-
var
|
|
11046
|
+
var DEPLOY_URL12 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
10754
11047
|
var COMMON_ARGS = {
|
|
10755
11048
|
app: {
|
|
10756
11049
|
type: "string",
|
|
@@ -10776,12 +11069,13 @@ var COMMON_ARGS = {
|
|
|
10776
11069
|
default: false
|
|
10777
11070
|
}
|
|
10778
11071
|
};
|
|
10779
|
-
async function resolveTarget(args) {
|
|
10780
|
-
|
|
11072
|
+
async function resolveTarget(args, opts = {}) {
|
|
11073
|
+
const register = opts.register === true;
|
|
11074
|
+
assertAppTargetResolvable(args.app, { wranglerEnv: args.env, register });
|
|
10781
11075
|
const { wranglerEnv } = parseWranglerEnvArg(args.env);
|
|
10782
11076
|
const configName = validateConfigName(args.config?.trim() || defaultConfigNameForEnv(wranglerEnv));
|
|
10783
11077
|
const token = await ensureToken();
|
|
10784
|
-
const appId = await resolveAppTarget(
|
|
11078
|
+
const appId = await resolveAppTarget(DEPLOY_URL12, token, args.app, { wranglerEnv, register });
|
|
10785
11079
|
return { appId, configName, token };
|
|
10786
11080
|
}
|
|
10787
11081
|
function ok(json, data, human) {
|
|
@@ -10810,7 +11104,7 @@ var list6 = defineCommand10({
|
|
|
10810
11104
|
},
|
|
10811
11105
|
async run({ args }) {
|
|
10812
11106
|
const t = await resolveTarget(args);
|
|
10813
|
-
const { secrets } = await listSecrets(
|
|
11107
|
+
const { secrets } = await listSecrets(DEPLOY_URL12, t.token, t.appId, t.configName);
|
|
10814
11108
|
if (args.json) {
|
|
10815
11109
|
console.log(JSON.stringify({ ok: true, appId: t.appId, config: t.configName, secrets }));
|
|
10816
11110
|
return;
|
|
@@ -10831,7 +11125,7 @@ var set = defineCommand10({
|
|
|
10831
11125
|
secret: { type: "positional", description: "KEY=value (repeatable)", required: true }
|
|
10832
11126
|
},
|
|
10833
11127
|
async run({ args }) {
|
|
10834
|
-
const t = await resolveTarget(args);
|
|
11128
|
+
const t = await resolveTarget(args, { register: true });
|
|
10835
11129
|
const pairs = dedupePositionals(args.secret, args._);
|
|
10836
11130
|
const secrets = {};
|
|
10837
11131
|
const dupes = [];
|
|
@@ -10851,9 +11145,9 @@ var set = defineCommand10({
|
|
|
10851
11145
|
}
|
|
10852
11146
|
if (Object.keys(secrets).length === 1) {
|
|
10853
11147
|
const [[k, v]] = Object.entries(secrets);
|
|
10854
|
-
await setSecret(
|
|
11148
|
+
await setSecret(DEPLOY_URL12, t.token, t.appId, t.configName, k, v);
|
|
10855
11149
|
} else {
|
|
10856
|
-
await uploadSecrets(
|
|
11150
|
+
await uploadSecrets(DEPLOY_URL12, t.token, t.appId, t.configName, secrets, false);
|
|
10857
11151
|
}
|
|
10858
11152
|
const n = Object.keys(secrets).length;
|
|
10859
11153
|
ok(
|
|
@@ -10886,11 +11180,11 @@ var get2 = defineCommand10({
|
|
|
10886
11180
|
const t = await resolveTarget(args);
|
|
10887
11181
|
const key = validateSecretName(args.key);
|
|
10888
11182
|
if (args.plain) {
|
|
10889
|
-
const { value } = await getSecretPlain(
|
|
11183
|
+
const { value } = await getSecretPlain(DEPLOY_URL12, t.token, t.appId, t.configName, key);
|
|
10890
11184
|
process.stdout.write(process.stdout.isTTY ? value + "\n" : value);
|
|
10891
11185
|
return;
|
|
10892
11186
|
}
|
|
10893
|
-
const { secrets } = await listSecrets(
|
|
11187
|
+
const { secrets } = await listSecrets(DEPLOY_URL12, t.token, t.appId, t.configName);
|
|
10894
11188
|
const item = secrets.find((s) => s.key === key);
|
|
10895
11189
|
if (!item)
|
|
10896
11190
|
throw new InputError(`Secret "${key}" not found in ${t.configName}`, "secret_not_found");
|
|
@@ -10921,7 +11215,7 @@ var del2 = defineCommand10({
|
|
|
10921
11215
|
for (const key of keys) {
|
|
10922
11216
|
const name = validateSecretName(key);
|
|
10923
11217
|
try {
|
|
10924
|
-
const result = await deleteSecret(
|
|
11218
|
+
const result = await deleteSecret(DEPLOY_URL12, t.token, t.appId, t.configName, name);
|
|
10925
11219
|
if (result.deleted === false) absent++;
|
|
10926
11220
|
else deleted++;
|
|
10927
11221
|
} catch (err) {
|
|
@@ -10959,11 +11253,11 @@ var upload = defineCommand10({
|
|
|
10959
11253
|
}
|
|
10960
11254
|
},
|
|
10961
11255
|
async run({ args }) {
|
|
10962
|
-
const t = await resolveTarget(args);
|
|
11256
|
+
const t = await resolveTarget(args, { register: true });
|
|
10963
11257
|
const secrets = parseSecretsUpload(await readUploadSource(String(args.file)));
|
|
10964
11258
|
if (Object.keys(secrets).length === 0)
|
|
10965
11259
|
throw new InputError("No secrets found in the input.", "empty_input");
|
|
10966
|
-
await uploadSecrets(
|
|
11260
|
+
await uploadSecrets(DEPLOY_URL12, t.token, t.appId, t.configName, secrets, args.replace);
|
|
10967
11261
|
const uploaded = Object.keys(secrets);
|
|
10968
11262
|
ok(
|
|
10969
11263
|
args.json === true,
|
|
@@ -11038,7 +11332,7 @@ var download = defineCommand10({
|
|
|
11038
11332
|
"invalid_format"
|
|
11039
11333
|
);
|
|
11040
11334
|
}
|
|
11041
|
-
const { secrets } = await fetchSecretsValues(
|
|
11335
|
+
const { secrets } = await fetchSecretsValues(DEPLOY_URL12, t.token, t.appId, t.configName);
|
|
11042
11336
|
process.stdout.write(formatSecretsDownload(secrets, format));
|
|
11043
11337
|
}
|
|
11044
11338
|
});
|
|
@@ -11053,10 +11347,10 @@ var pull = defineCommand10({
|
|
|
11053
11347
|
"Run from a DeepSpace app directory (one containing wrangler.toml).",
|
|
11054
11348
|
"not_in_app_repo"
|
|
11055
11349
|
);
|
|
11056
|
-
const t = await resolveTarget(args);
|
|
11350
|
+
const t = await resolveTarget(args, { register: true });
|
|
11057
11351
|
const ownerId = decodeJwtPayload(t.token).sub;
|
|
11058
11352
|
const refreshed = await refreshSecretsCache(
|
|
11059
|
-
|
|
11353
|
+
DEPLOY_URL12,
|
|
11060
11354
|
t.token,
|
|
11061
11355
|
t.appId,
|
|
11062
11356
|
wranglerEnv,
|
|
@@ -11081,7 +11375,7 @@ var configsList = defineCommand10({
|
|
|
11081
11375
|
args: { ...COMMON_ARGS },
|
|
11082
11376
|
async run({ args }) {
|
|
11083
11377
|
const t = await resolveTarget(args);
|
|
11084
|
-
const { configs: configs2 } = await listConfigs(
|
|
11378
|
+
const { configs: configs2 } = await listConfigs(DEPLOY_URL12, t.token, t.appId);
|
|
11085
11379
|
ok(args.json === true, { appId: t.appId, configs: configs2 }, () => {
|
|
11086
11380
|
if (configs2.length === 0) {
|
|
11087
11381
|
console.log("No configs yet \u2014 the first `secrets set` creates one.");
|
|
@@ -11101,10 +11395,10 @@ var configsCreate = defineCommand10({
|
|
|
11101
11395
|
"copy-from": { type: "string", description: "Copy secrets from this config", required: false }
|
|
11102
11396
|
},
|
|
11103
11397
|
async run({ args }) {
|
|
11104
|
-
const t = await resolveTarget(args);
|
|
11398
|
+
const t = await resolveTarget(args, { register: true });
|
|
11105
11399
|
const name = validateConfigName(args.name);
|
|
11106
11400
|
const copyFrom = args["copy-from"] || void 0;
|
|
11107
|
-
const { configs: configs2 } = await listConfigs(
|
|
11401
|
+
const { configs: configs2 } = await listConfigs(DEPLOY_URL12, t.token, t.appId);
|
|
11108
11402
|
if (configs2.some((c) => c.name === name)) {
|
|
11109
11403
|
if (copyFrom) {
|
|
11110
11404
|
throw new InputError(
|
|
@@ -11119,7 +11413,7 @@ var configsCreate = defineCommand10({
|
|
|
11119
11413
|
);
|
|
11120
11414
|
return;
|
|
11121
11415
|
}
|
|
11122
|
-
await createConfig(
|
|
11416
|
+
await createConfig(DEPLOY_URL12, t.token, t.appId, name, copyFrom);
|
|
11123
11417
|
ok(
|
|
11124
11418
|
args.json === true,
|
|
11125
11419
|
{ appId: t.appId, config: name, created: true, ...copyFrom ? { copyFrom } : {} },
|
|
@@ -11151,7 +11445,7 @@ var configsDelete = defineCommand10({
|
|
|
11151
11445
|
let secrets = [];
|
|
11152
11446
|
let counted = true;
|
|
11153
11447
|
try {
|
|
11154
|
-
secrets = (await listSecrets(
|
|
11448
|
+
secrets = (await listSecrets(DEPLOY_URL12, t.token, t.appId, name)).secrets;
|
|
11155
11449
|
} catch (error) {
|
|
11156
11450
|
if (args.yes !== true) throw error;
|
|
11157
11451
|
counted = false;
|
|
@@ -11167,7 +11461,7 @@ var configsDelete = defineCommand10({
|
|
|
11167
11461
|
throw new InputError("Cancelled.", "confirmation_required");
|
|
11168
11462
|
}
|
|
11169
11463
|
}
|
|
11170
|
-
await deleteConfig(
|
|
11464
|
+
await deleteConfig(DEPLOY_URL12, t.token, t.appId, name);
|
|
11171
11465
|
ok(
|
|
11172
11466
|
args.json === true,
|
|
11173
11467
|
{ appId: t.appId, config: name, deleted: true, secretsDeleted: counted ? count : null },
|
|
@@ -11196,32 +11490,7 @@ var secrets_default = defineCommand10({
|
|
|
11196
11490
|
});
|
|
11197
11491
|
|
|
11198
11492
|
// src/cli/commands/init.ts
|
|
11199
|
-
|
|
11200
|
-
import { join as join24 } from "path";
|
|
11201
|
-
var DEPLOY_URL12 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
11202
|
-
function wranglerConfigUncommitted(appDir) {
|
|
11203
|
-
if (!existsSync24(join24(appDir, ".git"))) return false;
|
|
11204
|
-
const dryRun = runGit(appDir, ["commit", "--dry-run", "-m", "x", "--", "wrangler.toml"], {
|
|
11205
|
-
allowFail: true
|
|
11206
|
-
});
|
|
11207
|
-
return dryRun.status === 0;
|
|
11208
|
-
}
|
|
11209
|
-
function commitScaffoldIfUnborn(appDir, token) {
|
|
11210
|
-
try {
|
|
11211
|
-
if (!existsSync24(join24(appDir, ".git"))) return false;
|
|
11212
|
-
if (runGit(appDir, ["rev-parse", "--verify", "HEAD"], { allowFail: true }).status === 0) {
|
|
11213
|
-
return false;
|
|
11214
|
-
}
|
|
11215
|
-
ensureGitIdentity(appDir, token);
|
|
11216
|
-
if (runGit(appDir, ["add", "-A"], { allowFail: true }).status !== 0) return false;
|
|
11217
|
-
const commit = runGit(appDir, ["commit", "-m", "Initial DeepSpace scaffold", "--no-verify"], {
|
|
11218
|
-
allowFail: true
|
|
11219
|
-
});
|
|
11220
|
-
return commit.status === 0;
|
|
11221
|
-
} catch {
|
|
11222
|
-
return false;
|
|
11223
|
-
}
|
|
11224
|
-
}
|
|
11493
|
+
var DEPLOY_URL13 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
11225
11494
|
var init_default = defineDeepspaceCommand({
|
|
11226
11495
|
meta: {
|
|
11227
11496
|
name: "init",
|
|
@@ -11262,7 +11531,7 @@ var init_default = defineDeepspaceCommand({
|
|
|
11262
11531
|
const token2 = await ensureToken();
|
|
11263
11532
|
let state;
|
|
11264
11533
|
try {
|
|
11265
|
-
state = await getAppSource(
|
|
11534
|
+
state = await getAppSource(DEPLOY_URL13, token2, existing);
|
|
11266
11535
|
} catch (error) {
|
|
11267
11536
|
if (error instanceof ApiError && error.code === "forbidden") {
|
|
11268
11537
|
throw new Refusal(
|
|
@@ -11287,15 +11556,17 @@ var init_default = defineDeepspaceCommand({
|
|
|
11287
11556
|
}
|
|
11288
11557
|
);
|
|
11289
11558
|
}
|
|
11559
|
+
const healedScaffold = commitScaffoldIfUnborn(appDir, token2);
|
|
11290
11560
|
if (!args.json) {
|
|
11291
11561
|
console.log(
|
|
11292
|
-
`Already initialized: ${existing}${envName ? ` (env: ${envName})` : ""} \u2014 registered on ${DEEPSPACE_ENV}`
|
|
11562
|
+
`Already initialized: ${existing}${envName ? ` (env: ${envName})` : ""} \u2014 registered on ${DEEPSPACE_ENV}` + (healedScaffold ? " \u2014 initial scaffold commit created." : "")
|
|
11293
11563
|
);
|
|
11294
11564
|
console.log(`App dir: ${appDir}`);
|
|
11295
11565
|
}
|
|
11296
11566
|
return {
|
|
11297
11567
|
data: {
|
|
11298
11568
|
status: "already_initialized",
|
|
11569
|
+
...healedScaffold ? { committedScaffold: true } : {},
|
|
11299
11570
|
appId: existing,
|
|
11300
11571
|
appDir,
|
|
11301
11572
|
env: DEEPSPACE_ENV,
|
|
@@ -11304,11 +11575,11 @@ var init_default = defineDeepspaceCommand({
|
|
|
11304
11575
|
};
|
|
11305
11576
|
}
|
|
11306
11577
|
const token = await ensureToken();
|
|
11307
|
-
const { appId } = await
|
|
11308
|
-
|
|
11578
|
+
const { appId, committedScaffold } = await mintAppIdentity(appDir, token, {
|
|
11579
|
+
wranglerEnv: envName,
|
|
11580
|
+
force: Boolean(args["new-id"]),
|
|
11581
|
+
commitScaffold: true
|
|
11309
11582
|
});
|
|
11310
|
-
writeAppId(appDir, appId, { wranglerEnv: envName, force: Boolean(args["new-id"]) });
|
|
11311
|
-
const committedScaffold = commitScaffoldIfUnborn(appDir, token);
|
|
11312
11583
|
if (!args.json) {
|
|
11313
11584
|
const envSuffix = envName ? ` (env: ${envName})` : "";
|
|
11314
11585
|
const commitNote = committedScaffold ? " \u2014 initial scaffold commit created." : " \u2014 commit wrangler.toml.";
|
|
@@ -11484,7 +11755,7 @@ var status_default = defineCommand11({
|
|
|
11484
11755
|
const { appName, appId, appIdError } = resolveStatusApp(appDir, wranglerEnv);
|
|
11485
11756
|
lines.push([
|
|
11486
11757
|
"App",
|
|
11487
|
-
`${appName}${wranglerEnv ? ` [env.${wranglerEnv}]` : ""}${appId ? ` (${appId})` : appIdError ? ` \u2014 ${appIdError}` : " \u2014 no DEEPSPACE_APP_ID;
|
|
11758
|
+
`${appName}${wranglerEnv ? ` [env.${wranglerEnv}]` : ""}${appId ? ` (${appId})` : appIdError ? ` \u2014 ${appIdError}` : " \u2014 no DEEPSPACE_APP_ID yet; it registers on first use (deploy, secrets, dev\u2026)"}`
|
|
11488
11759
|
]);
|
|
11489
11760
|
json.inApp = true;
|
|
11490
11761
|
json.appName = appName;
|
|
@@ -11495,7 +11766,7 @@ var status_default = defineCommand11({
|
|
|
11495
11766
|
const depsInstallCommand = installCommand(appDir);
|
|
11496
11767
|
lines.push([
|
|
11497
11768
|
"Deps",
|
|
11498
|
-
deps === "ready" ? "installed" : deps === "installing" ? "dependency installation is still running" :
|
|
11769
|
+
deps === "ready" ? "installed" : deps === "installing" ? "dependency installation is still running" : deps === "missing" ? "missing \u2014 any command that needs them installs on first use" : `failed \u2014 retried automatically by the next dev/test/deploy (or run ${depsInstallCommand})`
|
|
11499
11770
|
]);
|
|
11500
11771
|
json.deps = deps;
|
|
11501
11772
|
let branch = null;
|
|
@@ -11573,7 +11844,10 @@ var status_default = defineCommand11({
|
|
|
11573
11844
|
createdAt: rel.createdAt,
|
|
11574
11845
|
byYou,
|
|
11575
11846
|
source: rel.source,
|
|
11576
|
-
sourceRevision: rel.sourceRevision
|
|
11847
|
+
sourceRevision: rel.sourceRevision,
|
|
11848
|
+
// The human line can say "dirty worktree"; the machine mirror
|
|
11849
|
+
// must be able to say the same.
|
|
11850
|
+
sourceDirty: rel.sourceDirty ?? null
|
|
11577
11851
|
};
|
|
11578
11852
|
if (!entry) {
|
|
11579
11853
|
lines.push(["Live", `${release2} \xB7 serving state not visible from your account`]);
|
|
@@ -11604,8 +11878,13 @@ var status_default = defineCommand11({
|
|
|
11604
11878
|
]);
|
|
11605
11879
|
json.source = { ...source, revision: sourceResult.value.revision };
|
|
11606
11880
|
} else {
|
|
11607
|
-
|
|
11881
|
+
const github = externalGitSource(appDir, null);
|
|
11882
|
+
lines.push([
|
|
11883
|
+
"Source",
|
|
11884
|
+
`unclaimed \xB7 ${github ? "next deploy infers GitHub from this checkout\u2019s remote" : "next deploy syncs to DeepSpace and claims it, permanently"}`
|
|
11885
|
+
]);
|
|
11608
11886
|
json.source = null;
|
|
11887
|
+
json.sourceInference = github ? "github" : "deepspace";
|
|
11609
11888
|
}
|
|
11610
11889
|
const [refsResult, wsResult, releaseResult, appsResult] = await Promise.all([
|
|
11611
11890
|
githubSource ? Promise.resolve({ ok: true, value: null }) : observeRemote(api4.getRefs()),
|
|
@@ -11775,9 +12054,9 @@ var push_default = defineDeepspaceCommand({
|
|
|
11775
12054
|
const spinner2 = args.json ? null : createSpinner();
|
|
11776
12055
|
spinner2?.start(`Checking ${branch} before push\u2026`);
|
|
11777
12056
|
const resolveTarget3 = async () => {
|
|
11778
|
-
assertAppTargetResolvable(appArg);
|
|
12057
|
+
assertAppTargetResolvable(appArg, { register: true });
|
|
11779
12058
|
const token2 = await ensureToken();
|
|
11780
|
-
const appId2 = await resolveAppTarget(deployBaseUrl(), token2, appArg);
|
|
12059
|
+
const appId2 = await resolveAppTarget(deployBaseUrl(), token2, appArg, { register: true });
|
|
11781
12060
|
await warnIfPhantomApp(deployBaseUrl(), token2, appId2, appArg?.trim() || void 0);
|
|
11782
12061
|
return { token: token2, appId: appId2 };
|
|
11783
12062
|
};
|
|
@@ -11850,6 +12129,12 @@ var push_default = defineDeepspaceCommand({
|
|
|
11850
12129
|
if (sourceState.source?.provider === "github") {
|
|
11851
12130
|
throw githubSourceRefusal(appId, sourceState.source.repository);
|
|
11852
12131
|
}
|
|
12132
|
+
if (sourceState.source === null) {
|
|
12133
|
+
process.stderr.write(
|
|
12134
|
+
`This app's source is unclaimed \u2014 this push claims DeepSpace source for it, permanently (a checkout that deploys from GitHub instead should not run \`deepspace push\`).
|
|
12135
|
+
`
|
|
12136
|
+
);
|
|
12137
|
+
}
|
|
11853
12138
|
const pullRecoveryCwd = selectedBranchCheckout(appDir, branch) ?? appDir;
|
|
11854
12139
|
ensureSpaceRemote(appDir, appId, void 0, token);
|
|
11855
12140
|
if (force) {
|
|
@@ -11939,7 +12224,21 @@ var push_default = defineDeepspaceCommand({
|
|
|
11939
12224
|
extra: { status: result.status, appId, branch, oid: tipOid }
|
|
11940
12225
|
});
|
|
11941
12226
|
}
|
|
11942
|
-
|
|
12227
|
+
const uncommitted = appDir ? statusFiles(appDir) : [];
|
|
12228
|
+
if (!args.json && uncommitted.length > 0) {
|
|
12229
|
+
p19.log.warn(
|
|
12230
|
+
`${uncommitted.length} uncommitted file${uncommitted.length === 1 ? "" : "s"} NOT pushed \u2014 push publishes committed history only. Commit and push again to publish them.`
|
|
12231
|
+
);
|
|
12232
|
+
}
|
|
12233
|
+
return {
|
|
12234
|
+
data: {
|
|
12235
|
+
status: result.status,
|
|
12236
|
+
appId,
|
|
12237
|
+
branch,
|
|
12238
|
+
oid: tipOid,
|
|
12239
|
+
...uncommitted.length > 0 ? { uncommitted } : {}
|
|
12240
|
+
}
|
|
12241
|
+
};
|
|
11943
12242
|
} catch (err) {
|
|
11944
12243
|
if (err instanceof Refusal) throw err;
|
|
11945
12244
|
const transportFailure = classifyPushTransportFailure(err, appDir ?? void 0);
|
|
@@ -12236,7 +12535,7 @@ var pull_default = defineDeepspaceCommand({
|
|
|
12236
12535
|
|
|
12237
12536
|
// src/cli/commands/clone.ts
|
|
12238
12537
|
import * as p21 from "@clack/prompts";
|
|
12239
|
-
import { existsSync as existsSync26, readdirSync as readdirSync8, rmSync as
|
|
12538
|
+
import { existsSync as existsSync26, readdirSync as readdirSync8, rmSync as rmSync8 } from "fs";
|
|
12240
12539
|
import { resolve as resolve19, basename as basename5, join as join25 } from "path";
|
|
12241
12540
|
function cleanFailedCloneDir(dir) {
|
|
12242
12541
|
let entries;
|
|
@@ -12247,7 +12546,7 @@ function cleanFailedCloneDir(dir) {
|
|
|
12247
12546
|
}
|
|
12248
12547
|
if (entries.length === 1 && entries[0] === ".git") {
|
|
12249
12548
|
try {
|
|
12250
|
-
|
|
12549
|
+
rmSync8(join25(dir, ".git"), { recursive: true, force: true });
|
|
12251
12550
|
return [];
|
|
12252
12551
|
} catch {
|
|
12253
12552
|
return entries;
|
|
@@ -12378,7 +12677,7 @@ async function actorLabels(token, appId) {
|
|
|
12378
12677
|
}
|
|
12379
12678
|
|
|
12380
12679
|
// src/cli/commands/releases.ts
|
|
12381
|
-
var
|
|
12680
|
+
var DEPLOY_URL14 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
12382
12681
|
var releases_default = defineDeepspaceCommand({
|
|
12383
12682
|
meta: {
|
|
12384
12683
|
name: "releases",
|
|
@@ -12413,8 +12712,8 @@ var releases_default = defineDeepspaceCommand({
|
|
|
12413
12712
|
const spinner2 = args.json ? null : createSpinner();
|
|
12414
12713
|
spinner2?.start("Loading release history\u2026");
|
|
12415
12714
|
const token = await ensureToken();
|
|
12416
|
-
const appId = await resolveAppTarget(
|
|
12417
|
-
const { releases } = await repoApi(
|
|
12715
|
+
const appId = await resolveAppTarget(DEPLOY_URL14, token, appArg, { wranglerEnv: envArg });
|
|
12716
|
+
const { releases } = await repoApi(DEPLOY_URL14, token, appId).listReleases(limit);
|
|
12418
12717
|
spinner2?.stop(`Loaded ${releases.length} ${releases.length === 1 ? "release" : "releases"}.`);
|
|
12419
12718
|
if (!args.json) {
|
|
12420
12719
|
if (releases.length === 0) {
|
|
@@ -12422,7 +12721,7 @@ var releases_default = defineDeepspaceCommand({
|
|
|
12422
12721
|
} else {
|
|
12423
12722
|
const actors = await actorLabels(token, appId);
|
|
12424
12723
|
for (const r of releases) {
|
|
12425
|
-
const source = releaseSourceLabel(r);
|
|
12724
|
+
const source = displayText(releaseSourceLabel(r));
|
|
12426
12725
|
const rollback = r.rollbackAvailable ? "rollback available" : "rollback unavailable";
|
|
12427
12726
|
console.log(
|
|
12428
12727
|
`#${r.seq} ${r.id} ${r.kind.padEnd(8)} ${r.createdAt} ${actors.get(r.actor) ?? r.actor} ${source} ${rollback}`
|
|
@@ -12447,7 +12746,7 @@ Retry in a moment, or re-run with --allow-do-deletion to accept the risk.`,
|
|
|
12447
12746
|
"do_guard_unavailable"
|
|
12448
12747
|
);
|
|
12449
12748
|
}
|
|
12450
|
-
var
|
|
12749
|
+
var DEPLOY_URL15 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
12451
12750
|
function pickPreviousRelease(releases) {
|
|
12452
12751
|
if (releases.length === 0) {
|
|
12453
12752
|
return { error: "No releases recorded yet \u2014 nothing to roll back to.", code: "no_releases" };
|
|
@@ -12509,8 +12808,8 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
12509
12808
|
const spinner2 = args.json ? null : createSpinner();
|
|
12510
12809
|
spinner2?.start("Preparing rollback\u2026");
|
|
12511
12810
|
const token = await ensureToken();
|
|
12512
|
-
const appId = await resolveAppTarget(
|
|
12513
|
-
const api4 = repoApi(
|
|
12811
|
+
const appId = await resolveAppTarget(DEPLOY_URL15, token, appArg, { wranglerEnv: envArg });
|
|
12812
|
+
const api4 = repoApi(DEPLOY_URL15, token, appId);
|
|
12514
12813
|
let releaseId = args.release ? String(args.release).trim() : "";
|
|
12515
12814
|
if (!releaseId) {
|
|
12516
12815
|
const { releases } = await api4.listReleases(2);
|
|
@@ -12521,7 +12820,7 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
12521
12820
|
spinner2?.message(`Rolling back to ${releaseId}\u2026`);
|
|
12522
12821
|
let result;
|
|
12523
12822
|
try {
|
|
12524
|
-
result = await apiFetch(
|
|
12823
|
+
result = await apiFetch(DEPLOY_URL15, token, `/api/deploy/${appId}/rollback`, {
|
|
12525
12824
|
method: "POST",
|
|
12526
12825
|
body: JSON.stringify({
|
|
12527
12826
|
releaseId,
|
|
@@ -12733,12 +13032,12 @@ function printOverlaps(overlaps, json) {
|
|
|
12733
13032
|
import * as p25 from "@clack/prompts";
|
|
12734
13033
|
import {
|
|
12735
13034
|
existsSync as existsSync27,
|
|
12736
|
-
mkdirSync as
|
|
13035
|
+
mkdirSync as mkdirSync10,
|
|
12737
13036
|
readFileSync as readFileSync24,
|
|
12738
13037
|
realpathSync as realpathSync6,
|
|
12739
13038
|
renameSync as renameSync2,
|
|
12740
|
-
statSync as
|
|
12741
|
-
writeFileSync as
|
|
13039
|
+
statSync as statSync5,
|
|
13040
|
+
writeFileSync as writeFileSync8
|
|
12742
13041
|
} from "fs";
|
|
12743
13042
|
import { dirname as dirname12, isAbsolute as isAbsolute6, join as join26, relative as relative4, sep as sep4 } from "path";
|
|
12744
13043
|
var WORKSPACE_MARKER = "deepspace-workspace.json";
|
|
@@ -12772,8 +13071,8 @@ function markManagedWorkspace(worktreeRoot, workspaceId) {
|
|
|
12772
13071
|
const marker = { version: 1, workspaceId, mode: "managed-linked" };
|
|
12773
13072
|
const tempPath = `${markerPath}.${process.pid}.tmp`;
|
|
12774
13073
|
try {
|
|
12775
|
-
|
|
12776
|
-
|
|
13074
|
+
mkdirSync10(dirname12(markerPath), { recursive: true });
|
|
13075
|
+
writeFileSync8(tempPath, JSON.stringify(marker) + "\n");
|
|
12777
13076
|
renameSync2(tempPath, markerPath);
|
|
12778
13077
|
return true;
|
|
12779
13078
|
} catch {
|
|
@@ -12840,7 +13139,7 @@ function rematerializeWorkspaceWorktree(appDir, worktreeRoot, branch, workspaceI
|
|
|
12840
13139
|
function inOwnLinkedWorktree(appDir, id) {
|
|
12841
13140
|
if (workspaceIdFromBranch(currentBranch(appDir)) !== id) return false;
|
|
12842
13141
|
try {
|
|
12843
|
-
return
|
|
13142
|
+
return statSync5(join26(repoToplevel(appDir), ".git")).isFile();
|
|
12844
13143
|
} catch {
|
|
12845
13144
|
return false;
|
|
12846
13145
|
}
|
|
@@ -13286,14 +13585,14 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
13286
13585
|
|
|
13287
13586
|
// src/cli/commands/workspace/attach.ts
|
|
13288
13587
|
import * as p27 from "@clack/prompts";
|
|
13289
|
-
import { existsSync as existsSync28, readdirSync as readdirSync9, realpathSync as realpathSync7, rmSync as
|
|
13588
|
+
import { existsSync as existsSync28, readdirSync as readdirSync9, realpathSync as realpathSync7, rmSync as rmSync9 } from "fs";
|
|
13290
13589
|
import { isAbsolute as isAbsolute8, resolve as resolve21 } from "path";
|
|
13291
13590
|
function samePath(a, b) {
|
|
13292
|
-
const real = (
|
|
13591
|
+
const real = (p35) => {
|
|
13293
13592
|
try {
|
|
13294
|
-
return realpathSync7(
|
|
13593
|
+
return realpathSync7(p35);
|
|
13295
13594
|
} catch {
|
|
13296
|
-
return resolve21(
|
|
13595
|
+
return resolve21(p35);
|
|
13297
13596
|
}
|
|
13298
13597
|
};
|
|
13299
13598
|
return real(a) === real(b);
|
|
@@ -13317,7 +13616,7 @@ function cleanFailedFreshAttachDir(dir) {
|
|
|
13317
13616
|
return entries;
|
|
13318
13617
|
}
|
|
13319
13618
|
try {
|
|
13320
|
-
|
|
13619
|
+
rmSync9(dir, { recursive: true, force: true });
|
|
13321
13620
|
return [];
|
|
13322
13621
|
} catch {
|
|
13323
13622
|
return entries.length > 0 ? entries : ["<cleanup-failed>"];
|
|
@@ -13851,7 +14150,7 @@ var workspaceStatusCommand = defineDeepspaceCommand({
|
|
|
13851
14150
|
import * as p31 from "@clack/prompts";
|
|
13852
14151
|
|
|
13853
14152
|
// src/cli/lib/validation.ts
|
|
13854
|
-
import { sync as
|
|
14153
|
+
import { sync as spawnSync8 } from "cross-spawn";
|
|
13855
14154
|
import { readFileSync as readFileSync25 } from "fs";
|
|
13856
14155
|
import { join as join27 } from "path";
|
|
13857
14156
|
var MAX_SUMMARY_CHARS = 4e3;
|
|
@@ -13869,7 +14168,7 @@ function resolveValidationCommand(appDir, explicit) {
|
|
|
13869
14168
|
}
|
|
13870
14169
|
function runValidationCommand(appDir, command) {
|
|
13871
14170
|
const started = Date.now();
|
|
13872
|
-
const res =
|
|
14171
|
+
const res = spawnSync8(command, [], {
|
|
13873
14172
|
cwd: appDir,
|
|
13874
14173
|
shell: true,
|
|
13875
14174
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -14537,7 +14836,8 @@ function formatEvent2(e, lands, actors) {
|
|
|
14537
14836
|
case "release.rollback":
|
|
14538
14837
|
detail = `#${Number(s.seq ?? 0)} ${releaseSourceLabel({
|
|
14539
14838
|
commitOid: typeof s.commitOid === "string" ? s.commitOid : null,
|
|
14540
|
-
source: s.sourceProvider === "github" && typeof s.sourceRepository === "string" ? { provider: "github", repository: s.sourceRepository } : s.sourceProvider === "deepspace" ? { provider: "deepspace" } : null
|
|
14839
|
+
source: s.sourceProvider === "github" && typeof s.sourceRepository === "string" ? { provider: "github", repository: s.sourceRepository } : s.sourceProvider === "deepspace" ? { provider: "deepspace" } : null,
|
|
14840
|
+
sourceDirty: typeof s.sourceDirty === "boolean" ? s.sourceDirty : null
|
|
14541
14841
|
})}`;
|
|
14542
14842
|
break;
|
|
14543
14843
|
// Unknown kinds still print their kind + actor — never crash the feed.
|
|
@@ -14731,17 +15031,15 @@ var git_credential_default = defineCommand13({
|
|
|
14731
15031
|
});
|
|
14732
15032
|
|
|
14733
15033
|
// src/cli/commands/source.ts
|
|
14734
|
-
import * as p34 from "@clack/prompts";
|
|
14735
|
-
var TRANSFER_PREFIX = spacePrivateRef("source-transfer");
|
|
14736
15034
|
var source_default = defineDeepspaceCommand({
|
|
14737
15035
|
meta: {
|
|
14738
15036
|
name: "source",
|
|
14739
|
-
description: "Show
|
|
15037
|
+
description: "Show the app\u2019s source authority (inferred from use, never declared)"
|
|
14740
15038
|
},
|
|
14741
15039
|
args: {
|
|
14742
15040
|
provider: {
|
|
14743
15041
|
type: "positional",
|
|
14744
|
-
description: "
|
|
15042
|
+
description: "No longer accepted \u2014 source is inferred, not declared",
|
|
14745
15043
|
required: false
|
|
14746
15044
|
},
|
|
14747
15045
|
app: {
|
|
@@ -14749,71 +15047,41 @@ var source_default = defineDeepspaceCommand({
|
|
|
14749
15047
|
alias: "a",
|
|
14750
15048
|
description: "App id or subdomain name (default: current app)",
|
|
14751
15049
|
required: false
|
|
14752
|
-
},
|
|
14753
|
-
remote: {
|
|
14754
|
-
type: "string",
|
|
14755
|
-
description: "GitHub remote name when it cannot be inferred (default: origin)",
|
|
14756
|
-
required: false
|
|
14757
15050
|
}
|
|
14758
15051
|
},
|
|
14759
15052
|
async run({ args }) {
|
|
14760
|
-
|
|
14761
|
-
if (requested && requested !== "github" && requested !== "deepspace") {
|
|
14762
|
-
throw new Refusal("Source must be `github` or `deepspace`.", "invalid_source");
|
|
14763
|
-
}
|
|
14764
|
-
const token = await ensureToken();
|
|
14765
|
-
const deployUrl = deployBaseUrl();
|
|
14766
|
-
const appId = await resolveAppTarget(
|
|
14767
|
-
deployUrl,
|
|
14768
|
-
token,
|
|
14769
|
-
typeof args.app === "string" ? args.app : void 0
|
|
14770
|
-
);
|
|
14771
|
-
const state = await getAppSource(deployUrl, token, appId);
|
|
14772
|
-
if (!requested) {
|
|
14773
|
-
reportSource(appId, state, args.json);
|
|
14774
|
-
return { data: sourceData(state) };
|
|
14775
|
-
}
|
|
14776
|
-
const appDir = findAppDir();
|
|
14777
|
-
if (!appDir) {
|
|
15053
|
+
if (typeof args.provider === "string" && args.provider.trim()) {
|
|
14778
15054
|
throw new Refusal(
|
|
14779
|
-
"
|
|
14780
|
-
"
|
|
15055
|
+
"Source is no longer declared. A checkout with a GitHub remote deploys as GitHub automatically, and an app becomes DeepSpace-source by publishing to it (`deepspace push`) \u2014 permanently. This command only reports.",
|
|
15056
|
+
"source_inferred"
|
|
14781
15057
|
);
|
|
14782
15058
|
}
|
|
14783
|
-
const
|
|
14784
|
-
|
|
14785
|
-
|
|
14786
|
-
|
|
14787
|
-
|
|
14788
|
-
|
|
14789
|
-
|
|
14790
|
-
|
|
14791
|
-
|
|
14792
|
-
spaceRemote === "removed" ? `Removed the \`${SPACE_REMOTE}\` git remote \u2014 DeepSpace no longer owns this repo.` : spaceRemote === "absent" ? `No \`${SPACE_REMOTE}\` git remote to remove \u2014 source operations go through GitHub.` : spaceRemote === "present" ? `The \`${SPACE_REMOTE}\` git remote points at this app's cloud repo.` : `The local \`${SPACE_REMOTE}\` git remote still needs reconciliation.`
|
|
14793
|
-
);
|
|
14794
|
-
if (reconciliation.localReconciliation) {
|
|
14795
|
-
p34.log.warn(reconciliation.localReconciliation.message);
|
|
15059
|
+
const appArg = typeof args.app === "string" ? args.app : void 0;
|
|
15060
|
+
if (appArg === void 0) {
|
|
15061
|
+
const appDir = findAppDir();
|
|
15062
|
+
if (appDir && !readAppId(appDir)) {
|
|
15063
|
+
if (!args.json) {
|
|
15064
|
+
console.log("App: not registered yet \u2014 it registers on first use (deploy, secrets, dev\u2026)");
|
|
15065
|
+
console.log("Source: unclaimed \u2014 a checkout with a GitHub remote deploys as GitHub; the first `deepspace push` claims DeepSpace source permanently.");
|
|
15066
|
+
}
|
|
15067
|
+
return { data: { appId: null, source: null, revision: 0, registered: false } };
|
|
14796
15068
|
}
|
|
14797
15069
|
}
|
|
15070
|
+
const token = await ensureToken();
|
|
15071
|
+
const deployUrl = deployBaseUrl();
|
|
15072
|
+
const appId = await resolveAppTarget(deployUrl, token, appArg);
|
|
15073
|
+
const state = await getAppSource(deployUrl, token, appId);
|
|
15074
|
+
reportSource(appId, state, args.json);
|
|
14798
15075
|
return {
|
|
14799
15076
|
data: {
|
|
14800
|
-
appId,
|
|
14801
|
-
source:
|
|
14802
|
-
revision:
|
|
14803
|
-
|
|
14804
|
-
...reconciliation.localReconciliation ? { localReconciliation: reconciliation.localReconciliation } : {}
|
|
15077
|
+
appId: state.appId,
|
|
15078
|
+
source: state.source,
|
|
15079
|
+
revision: state.revision,
|
|
15080
|
+
registered: state.registered
|
|
14805
15081
|
}
|
|
14806
15082
|
};
|
|
14807
15083
|
}
|
|
14808
15084
|
});
|
|
14809
|
-
function sourceData(state) {
|
|
14810
|
-
return {
|
|
14811
|
-
appId: state.appId,
|
|
14812
|
-
source: state.source,
|
|
14813
|
-
revision: state.revision,
|
|
14814
|
-
registered: state.registered
|
|
14815
|
-
};
|
|
14816
|
-
}
|
|
14817
15085
|
function reportSource(appId, state, json) {
|
|
14818
15086
|
if (json) return;
|
|
14819
15087
|
console.log(`App: ${appId}`);
|
|
@@ -14822,220 +15090,22 @@ function reportSource(appId, state, json) {
|
|
|
14822
15090
|
} else if (state.source?.provider === "deepspace") {
|
|
14823
15091
|
console.log("Source: DeepSpace");
|
|
14824
15092
|
} else {
|
|
14825
|
-
console.log(
|
|
14826
|
-
|
|
14827
|
-
console.log(`Revision: ${state.revision}`);
|
|
14828
|
-
}
|
|
14829
|
-
async function moveToGitHub(input) {
|
|
14830
|
-
const { appDir, appId, token, deployUrl, state, remoteArg } = input;
|
|
14831
|
-
const selected = requireGitHubRemote(appDir, remoteArg);
|
|
14832
|
-
const target2 = { provider: "github", repository: selected.repository };
|
|
14833
|
-
if (sameSource(state.source, target2)) return { source: target2, revision: state.revision };
|
|
14834
|
-
if (state.source?.provider === "deepspace") {
|
|
14835
|
-
assertSyncableRepo(appDir);
|
|
14836
|
-
const api4 = repoApi(deployUrl, token, appId);
|
|
14837
|
-
const { views } = await api4.listWorkspaces();
|
|
14838
|
-
if (views.length > 0) {
|
|
14839
|
-
throw new Refusal(
|
|
14840
|
-
"Land or drop every active DeepSpace workspace before moving source to GitHub.",
|
|
14841
|
-
"active_workspaces"
|
|
14842
|
-
);
|
|
14843
|
-
}
|
|
14844
|
-
ensureSpaceRemote(appDir, appId);
|
|
14845
|
-
runGitRemote(appDir, token, [
|
|
14846
|
-
"fetch",
|
|
14847
|
-
"--quiet",
|
|
14848
|
-
"--prune",
|
|
14849
|
-
SPACE_REMOTE,
|
|
14850
|
-
`+refs/heads/*:${TRANSFER_PREFIX}/heads/*`,
|
|
14851
|
-
`+refs/tags/*:${TRANSFER_PREFIX}/tags/*`
|
|
14852
|
-
]);
|
|
14853
|
-
const { refs } = await api4.getRefs().then((value) => {
|
|
14854
|
-
if (!value)
|
|
14855
|
-
throw new Refusal("The DeepSpace source repository is unavailable.", "source_unavailable");
|
|
14856
|
-
return value;
|
|
14857
|
-
});
|
|
14858
|
-
const wanted = refs.filter(isPublicRef).map(({ name, oid }) => ({ name, oid }));
|
|
14859
|
-
const fetched = transferRefs(appDir);
|
|
14860
|
-
if (!sameRefs(fetched, wanted)) {
|
|
14861
|
-
throw new Refusal(
|
|
14862
|
-
"DeepSpace branches or tags changed while they were being fetched. Retry the transfer from fresh refs.",
|
|
14863
|
-
"source_changed"
|
|
14864
|
-
);
|
|
14865
|
-
}
|
|
14866
|
-
const latest = await api4.latestRelease().then((value) => value.release);
|
|
14867
|
-
requireReleaseReachable(appDir, latest, wanted);
|
|
14868
|
-
const remote = new Map(
|
|
14869
|
-
remotePublicRefs(appDir, selected.name).map((ref) => [ref.name, ref.oid])
|
|
14870
|
-
);
|
|
14871
|
-
const missing = wanted.filter((ref) => remote.get(ref.name) !== ref.oid);
|
|
14872
|
-
if (missing.length > 0) {
|
|
14873
|
-
const refspecs = missing.map((ref) => transferRefspec(ref.name));
|
|
14874
|
-
throw new Refusal(
|
|
14875
|
-
`GitHub ${selected.repository} is missing ${missing.length} authoritative branch/tag ref(s). Push them manually, then rerun this command.`,
|
|
14876
|
-
"github_push_required",
|
|
14877
|
-
{
|
|
14878
|
-
actionRequired: true,
|
|
14879
|
-
action: { cwd: appDir, argv: ["git", "push", "--atomic", selected.name, ...refspecs] },
|
|
14880
|
-
extra: {
|
|
14881
|
-
appId,
|
|
14882
|
-
repository: selected.repository,
|
|
14883
|
-
missing: missing.map((ref) => ref.name)
|
|
14884
|
-
}
|
|
14885
|
-
}
|
|
14886
|
-
);
|
|
14887
|
-
}
|
|
14888
|
-
return setAppSource(deployUrl, token, appId, {
|
|
14889
|
-
source: target2,
|
|
14890
|
-
expectedRevision: state.revision,
|
|
14891
|
-
refs: wanted,
|
|
14892
|
-
...releaseExpectation(latest)
|
|
14893
|
-
});
|
|
14894
|
-
}
|
|
14895
|
-
remotePublicRefs(appDir, selected.name);
|
|
14896
|
-
return setAppSource(deployUrl, token, appId, {
|
|
14897
|
-
source: target2,
|
|
14898
|
-
expectedRevision: state.revision
|
|
14899
|
-
});
|
|
14900
|
-
}
|
|
14901
|
-
async function moveToDeepSpace(input) {
|
|
14902
|
-
const { appDir, appId, token, deployUrl, state, remoteArg } = input;
|
|
14903
|
-
const target2 = { provider: "deepspace" };
|
|
14904
|
-
if (sameSource(state.source, target2)) return { source: target2, revision: state.revision };
|
|
14905
|
-
assertSyncableRepo(appDir);
|
|
14906
|
-
if (state.source?.provider !== "github") {
|
|
14907
|
-
return setAppSource(deployUrl, token, appId, {
|
|
14908
|
-
source: target2,
|
|
14909
|
-
expectedRevision: state.revision
|
|
14910
|
-
});
|
|
14911
|
-
}
|
|
14912
|
-
const selected = requireGitHubRemote(appDir, remoteArg, state.source.repository);
|
|
14913
|
-
fetchGitHubTransferRefs(appDir, selected.name);
|
|
14914
|
-
const refs = transferRefs(appDir);
|
|
14915
|
-
if (refs.length === 0) {
|
|
14916
|
-
throw new Refusal("GitHub has no branches or tags to import.", "source_empty");
|
|
14917
|
-
}
|
|
14918
|
-
const latest = await repoApi(deployUrl, token, appId).latestRelease().then((value) => value.release);
|
|
14919
|
-
requireReleaseReachable(appDir, latest, refs);
|
|
14920
|
-
const pushed = runGit(
|
|
14921
|
-
appDir,
|
|
14922
|
-
[
|
|
14923
|
-
"push",
|
|
14924
|
-
"--force",
|
|
14925
|
-
"--atomic",
|
|
14926
|
-
"--prune",
|
|
14927
|
-
repoUrl(appId, deployUrl),
|
|
14928
|
-
`${TRANSFER_PREFIX}/heads/*:refs/heads/*`,
|
|
14929
|
-
`${TRANSFER_PREFIX}/tags/*:refs/tags/*`
|
|
14930
|
-
],
|
|
14931
|
-
{ allowFail: true, env: gitSourceImportEnv(token, state.revision, deployUrl) }
|
|
14932
|
-
);
|
|
14933
|
-
if (pushed.status !== 0) {
|
|
14934
|
-
throw new Refusal(
|
|
14935
|
-
`DeepSpace source import failed: ${pushed.stderr.toString("utf-8").trim() || "git push failed"}`,
|
|
14936
|
-
"source_import_failed"
|
|
14937
|
-
);
|
|
14938
|
-
}
|
|
14939
|
-
const latestGitHubRefs = remotePublicRefs(appDir, selected.name);
|
|
14940
|
-
if (!sameRefs(latestGitHubRefs, refs)) {
|
|
14941
|
-
throw new Refusal(
|
|
14942
|
-
"GitHub branches or tags changed while they were being imported. Retry the transfer from fresh refs.",
|
|
14943
|
-
"source_changed"
|
|
14944
|
-
);
|
|
14945
|
-
}
|
|
14946
|
-
return setAppSource(deployUrl, token, appId, {
|
|
14947
|
-
source: target2,
|
|
14948
|
-
expectedRevision: state.revision,
|
|
14949
|
-
refs,
|
|
14950
|
-
...releaseExpectation(latest)
|
|
14951
|
-
});
|
|
14952
|
-
}
|
|
14953
|
-
function reconcileSpaceRemote(appDir, appId, source) {
|
|
14954
|
-
try {
|
|
14955
|
-
if (source.provider === "github") {
|
|
14956
|
-
return { spaceRemote: removeSpaceRemote(appDir) ? "removed" : "absent" };
|
|
14957
|
-
}
|
|
14958
|
-
ensureSpaceRemote(appDir, appId);
|
|
14959
|
-
return { spaceRemote: "present" };
|
|
14960
|
-
} catch (error) {
|
|
14961
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
14962
|
-
return {
|
|
14963
|
-
spaceRemote: "repair_required",
|
|
14964
|
-
localReconciliation: {
|
|
14965
|
-
required: true,
|
|
14966
|
-
code: "git_remote_reconciliation_failed",
|
|
14967
|
-
message: `Source authority is ${source.provider}, but the local \`${SPACE_REMOTE}\` remote could not be reconciled: ${detail}`
|
|
14968
|
-
}
|
|
14969
|
-
};
|
|
14970
|
-
}
|
|
14971
|
-
}
|
|
14972
|
-
function requireGitHubRemote(appDir, remoteArg, repository) {
|
|
14973
|
-
const selected = selectGitHubRemote(appDir, { name: remoteArg, repository });
|
|
14974
|
-
if (selected) return selected;
|
|
14975
|
-
throw new Refusal(
|
|
14976
|
-
remoteArg ? `Remote "${remoteArg}" is not a GitHub repository${repository ? ` for ${repository}` : ""}.` : repository ? `No local GitHub remote points at ${repository}. Add one or pass --remote <name>.` : "No unambiguous GitHub remote was found. Add `origin` or pass --remote <name>.",
|
|
14977
|
-
"github_remote_required"
|
|
14978
|
-
);
|
|
14979
|
-
}
|
|
14980
|
-
function fetchGitHubTransferRefs(appDir, remote) {
|
|
14981
|
-
runGit(appDir, [
|
|
14982
|
-
"fetch",
|
|
14983
|
-
"--quiet",
|
|
14984
|
-
"--prune",
|
|
14985
|
-
remote,
|
|
14986
|
-
`+refs/heads/*:${TRANSFER_PREFIX}/heads/*`,
|
|
14987
|
-
`+refs/tags/*:${TRANSFER_PREFIX}/tags/*`
|
|
14988
|
-
]);
|
|
14989
|
-
}
|
|
14990
|
-
function transferRefs(appDir) {
|
|
14991
|
-
return localTransferRefs(appDir, `${TRANSFER_PREFIX}/heads`, "refs/heads").concat(
|
|
14992
|
-
localTransferRefs(appDir, `${TRANSFER_PREFIX}/tags`, "refs/tags")
|
|
14993
|
-
);
|
|
14994
|
-
}
|
|
14995
|
-
function transferRefspec(ref) {
|
|
14996
|
-
if (ref.startsWith("refs/heads/")) {
|
|
14997
|
-
return `${TRANSFER_PREFIX}/heads/${ref.slice("refs/heads/".length)}:${ref}`;
|
|
14998
|
-
}
|
|
14999
|
-
return `${TRANSFER_PREFIX}/tags/${ref.slice("refs/tags/".length)}:${ref}`;
|
|
15000
|
-
}
|
|
15001
|
-
function isPublicRef(ref) {
|
|
15002
|
-
return ref.name.startsWith("refs/heads/") || ref.name.startsWith("refs/tags/");
|
|
15003
|
-
}
|
|
15004
|
-
function requireReleaseReachable(appDir, release2, refs) {
|
|
15005
|
-
if (!release2?.commitOid) return;
|
|
15006
|
-
const known = resolveCommit(appDir, release2.commitOid) !== null;
|
|
15007
|
-
const reachable = known && refs.some((ref) => isAncestor(appDir, release2.commitOid, ref.oid));
|
|
15008
|
-
if (!reachable) {
|
|
15009
|
-
throw new Refusal(
|
|
15010
|
-
`Live release #${release2.seq} (${release2.commitOid.slice(0, 10)}) is not reachable from a public branch or tag. Redeploy a public branch before changing source.`,
|
|
15011
|
-
"release_not_public"
|
|
15093
|
+
console.log(
|
|
15094
|
+
"Source: unclaimed \u2014 a checkout with a GitHub remote deploys as GitHub; the first `deepspace push` claims DeepSpace source permanently."
|
|
15012
15095
|
);
|
|
15013
15096
|
}
|
|
15014
|
-
}
|
|
15015
|
-
function releaseExpectation(release2) {
|
|
15016
|
-
return {
|
|
15017
|
-
expectedReleaseId: release2?.id ?? null,
|
|
15018
|
-
expectedReleaseCommitOid: release2?.commitOid ?? null
|
|
15019
|
-
};
|
|
15020
|
-
}
|
|
15021
|
-
function sameSource(left, right) {
|
|
15022
|
-
return left?.provider === right.provider && (left.provider !== "github" || right.provider === "github" && left.repository === right.repository);
|
|
15023
|
-
}
|
|
15024
|
-
function sameRefs(left, right) {
|
|
15025
|
-
if (left.length !== right.length) return false;
|
|
15026
|
-
const expected = new Map(right.map((ref) => [ref.name, ref.oid]));
|
|
15027
|
-
return left.every((ref) => expected.get(ref.name) === ref.oid);
|
|
15097
|
+
console.log(`Revision: ${state.revision}`);
|
|
15028
15098
|
}
|
|
15029
15099
|
|
|
15030
15100
|
// src/cli/commands/update.ts
|
|
15031
|
-
import * as
|
|
15101
|
+
import * as p34 from "@clack/prompts";
|
|
15032
15102
|
import { existsSync as existsSync29, readFileSync as readFileSync26 } from "fs";
|
|
15033
15103
|
import { join as join28, resolve as resolve22 } from "path";
|
|
15034
15104
|
|
|
15035
15105
|
// package.json
|
|
15036
15106
|
var package_default = {
|
|
15037
15107
|
name: "deepspace",
|
|
15038
|
-
version: "0.
|
|
15108
|
+
version: "0.26.1",
|
|
15039
15109
|
engines: {
|
|
15040
15110
|
node: ">=22.15.0 <23 || >=24 <25 || >=26 <27"
|
|
15041
15111
|
},
|
|
@@ -15290,7 +15360,7 @@ var update_default = defineDeepspaceCommand({
|
|
|
15290
15360
|
} : null;
|
|
15291
15361
|
if (versionGuidance) {
|
|
15292
15362
|
const steps2 = [versionGuidance.step];
|
|
15293
|
-
if (!args.json)
|
|
15363
|
+
if (!args.json) p34.log.warn(versionGuidance.step);
|
|
15294
15364
|
return {
|
|
15295
15365
|
data: {
|
|
15296
15366
|
ready: false,
|
|
@@ -15326,9 +15396,9 @@ var update_default = defineDeepspaceCommand({
|
|
|
15326
15396
|
];
|
|
15327
15397
|
const status3 = unverifiedSdk ? "dependency_unverified" : steps.length === 0 ? "aligned" : "guidance_available";
|
|
15328
15398
|
if (!args.json) {
|
|
15329
|
-
|
|
15330
|
-
for (const step of steps)
|
|
15331
|
-
if (steps.length === 0)
|
|
15399
|
+
p34.log.info(`App dependency: ${currentSpec}; guidance owner: deepspace CLI ${targetVersion}`);
|
|
15400
|
+
for (const step of steps) p34.log.message(`\u2022 ${step}`);
|
|
15401
|
+
if (steps.length === 0) p34.log.success("This app is already aligned with this CLI version.");
|
|
15332
15402
|
}
|
|
15333
15403
|
const data = {
|
|
15334
15404
|
ready: status3 === "aligned",
|
|
@@ -15394,7 +15464,7 @@ var create2 = defineCommand14({
|
|
|
15394
15464
|
}
|
|
15395
15465
|
const scaffolder = `create-deepspace@${pkg.version}`;
|
|
15396
15466
|
console.log(`Running: npx ${scaffolder} ${rawArgs2.join(" ")}`.trimEnd() + "\n");
|
|
15397
|
-
const res =
|
|
15467
|
+
const res = spawnSync9("npx", ["-y", scaffolder, ...rawArgs2], { stdio: "inherit" });
|
|
15398
15468
|
if (res.error) {
|
|
15399
15469
|
const err = res.error;
|
|
15400
15470
|
console.error(
|