deepspace 0.25.0 → 0.26.0
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 +6 -0
- package/README.md +7 -6
- package/dist/{chunk-2GUGZLAX.js → chunk-LPXWXF6R.js} +2 -2
- package/dist/{chunk-2GUGZLAX.js.map → chunk-LPXWXF6R.js.map} +1 -1
- package/dist/cli.js +984 -949
- 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/package.json +1 -1
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";
|
|
@@ -366,7 +366,9 @@ function errorCode(err) {
|
|
|
366
366
|
return void 0;
|
|
367
367
|
}
|
|
368
368
|
var API_ERROR_HINTS = {
|
|
369
|
-
|
|
369
|
+
// Starts where the server sentence ("Only the app owner can do this.")
|
|
370
|
+
// stops, so the two read as one message instead of a stutter.
|
|
371
|
+
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
372
|
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
373
|
not_app_owner_or_collaborator: "You must be the app owner or a collaborator to do this.",
|
|
372
374
|
test_account_cannot_be_collaborator: "Test accounts cannot be added as collaborators. Use a real DeepSpace account.",
|
|
@@ -591,13 +593,16 @@ function findUnknownCommand(argv, rootCommands) {
|
|
|
591
593
|
const exactAll = commandPaths(table).filter((path) => path.at(-1) === token);
|
|
592
594
|
const exactHit = exactAll.length === 1 ? exactAll[0] : null;
|
|
593
595
|
const split = splitTokenPath(token, table);
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
const
|
|
596
|
+
const rootExactAll = exactHit || split ? [] : commandPaths(rootCommands).filter((path) => path.at(-1) === token);
|
|
597
|
+
const rootExact = rootExactAll.length === 1 ? rootExactAll[0] : null;
|
|
598
|
+
const relativeSuggestion = exactHit ?? split ?? (rootExact ? null : closestCommandPath(token, table));
|
|
599
|
+
const suggestionPath = relativeSuggestion ? [...accepted, ...relativeSuggestion] : rootExact;
|
|
600
|
+
if (!suggestionPath) return null;
|
|
601
|
+
const guessed = suggestionPath.at(-1) ?? "";
|
|
597
602
|
return {
|
|
598
603
|
attemptedPath: ["deepspace", ...accepted, token],
|
|
599
604
|
helpPath: ["deepspace", ...accepted],
|
|
600
|
-
suggestion: ["deepspace", ...
|
|
605
|
+
suggestion: ["deepspace", ...suggestionPath],
|
|
601
606
|
executable: split !== null && !DESTRUCTIVE_VERBS.has(guessed),
|
|
602
607
|
// POSITIONALS ONLY. The remainder exists so the corrected verb keeps
|
|
603
608
|
// the arguments it needs; carrying flags too meant `auth logn --email
|
|
@@ -1120,6 +1125,10 @@ var logout_default = defineDeepspaceCommand({
|
|
|
1120
1125
|
}
|
|
1121
1126
|
});
|
|
1122
1127
|
|
|
1128
|
+
// src/cli/lib/app-registration.ts
|
|
1129
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, rmSync as rmSync3, statSync } from "fs";
|
|
1130
|
+
import { join as join4 } from "path";
|
|
1131
|
+
|
|
1123
1132
|
// src/cli/lib/app-identity.ts
|
|
1124
1133
|
import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
1125
1134
|
import { join as join3, resolve as resolve2 } from "path";
|
|
@@ -1459,55 +1468,9 @@ ${line}
|
|
|
1459
1468
|
writeFileSync3(wranglerPath, src);
|
|
1460
1469
|
}
|
|
1461
1470
|
|
|
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
1471
|
// src/cli/lib/git/process.ts
|
|
1509
1472
|
import { sync as spawnSync } from "cross-spawn";
|
|
1510
|
-
import { existsSync as
|
|
1473
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1511
1474
|
var MAX_GIT_BUFFER = 512 * 1024 * 1024;
|
|
1512
1475
|
var GIT_TIMEOUT_MS = 3e5;
|
|
1513
1476
|
var GitError = class extends Error {
|
|
@@ -1545,7 +1508,7 @@ function runGit(cwd, args, opts = {}) {
|
|
|
1545
1508
|
if (result.error) {
|
|
1546
1509
|
const code = result.error.code;
|
|
1547
1510
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
1548
|
-
if (code === "ENOTDIR" || !
|
|
1511
|
+
if (code === "ENOTDIR" || !existsSync5(cwd)) {
|
|
1549
1512
|
throw new GitError(
|
|
1550
1513
|
`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
1514
|
"worktree_missing"
|
|
@@ -1594,7 +1557,323 @@ function gitMeetsFloor(version) {
|
|
|
1594
1557
|
return major > 2 || major === 2 && minor >= 29;
|
|
1595
1558
|
}
|
|
1596
1559
|
|
|
1560
|
+
// src/cli/lib/vc-remote.ts
|
|
1561
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1562
|
+
import { tmpdir } from "os";
|
|
1563
|
+
import { isAbsolute as isAbsolute2, relative, resolve as resolve3 } from "path";
|
|
1564
|
+
function spaceRemoteName(environment = DEEPSPACE_ENV) {
|
|
1565
|
+
if (environment === "staging") return "space-staging";
|
|
1566
|
+
if (environment === "production") return "space";
|
|
1567
|
+
return "space-invalid";
|
|
1568
|
+
}
|
|
1569
|
+
var SPACE_REMOTE = spaceRemoteName();
|
|
1570
|
+
function spaceTrackingRef(branch, remote = SPACE_REMOTE) {
|
|
1571
|
+
return `refs/remotes/${remote}/${branch}`;
|
|
1572
|
+
}
|
|
1573
|
+
function spacePrivateRef(path, environment = DEEPSPACE_ENV) {
|
|
1574
|
+
const root = environment === "production" ? "refs/deepspace" : `refs/deepspace/${environment}`;
|
|
1575
|
+
return `${root}/${path}`;
|
|
1576
|
+
}
|
|
1577
|
+
function deployBaseUrl() {
|
|
1578
|
+
return process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
1579
|
+
}
|
|
1580
|
+
function repoUrl(appId, base = deployBaseUrl()) {
|
|
1581
|
+
return `${base.replace(/\/+$/, "")}/api/repo/${encodeURIComponent(appId)}`;
|
|
1582
|
+
}
|
|
1583
|
+
function gitAuthEnv(token, base = deployBaseUrl()) {
|
|
1584
|
+
const existing = Number(process.env.GIT_CONFIG_COUNT ?? "0");
|
|
1585
|
+
const index = Number.isInteger(existing) && existing > 0 ? existing : 0;
|
|
1586
|
+
return {
|
|
1587
|
+
GIT_CONFIG_COUNT: String(index + 1),
|
|
1588
|
+
[`GIT_CONFIG_KEY_${index}`]: `http.${base.replace(/\/+$/, "")}.extraHeader`,
|
|
1589
|
+
[`GIT_CONFIG_VALUE_${index}`]: `Authorization: Bearer ${token}`
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
function runGitRemote(cwd, token, args, opts = {}) {
|
|
1593
|
+
return runGit(cwd, args, { ...opts, env: { ...gitAuthEnv(token), ...opts.env ?? {} } });
|
|
1594
|
+
}
|
|
1595
|
+
function entryIsTransient(cwd) {
|
|
1596
|
+
const entry = process.argv[1];
|
|
1597
|
+
if (!entry) return true;
|
|
1598
|
+
if (/[\\/]_npx[\\/]/.test(entry) || entry.startsWith(tmpdir())) return true;
|
|
1599
|
+
const rel = relative(resolve3(cwd), resolve3(entry));
|
|
1600
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
|
|
1601
|
+
}
|
|
1602
|
+
function helperEntryUsable(command) {
|
|
1603
|
+
const match = /^!(?:DEEPSPACE_ENV=(production|staging) )?'([^']*)' '([^']*)' git-credential$/.exec(command);
|
|
1604
|
+
const expectedEnvironment = DEEPSPACE_ENV === "production" ? void 0 : DEEPSPACE_ENV;
|
|
1605
|
+
if (!match || match[1] !== expectedEnvironment) return false;
|
|
1606
|
+
return existsSync6(match[2]) && existsSync6(match[3]);
|
|
1607
|
+
}
|
|
1608
|
+
function credentialHelperCommand(environment = DEEPSPACE_ENV) {
|
|
1609
|
+
const entry = process.argv[1];
|
|
1610
|
+
if (!entry) throw new Error("CLI entry path is unavailable");
|
|
1611
|
+
const environmentPrefix = environment === "staging" ? "DEEPSPACE_ENV=staging " : "";
|
|
1612
|
+
return `!${environmentPrefix}${shQuote(process.execPath)} ${shQuote(entry)} git-credential`;
|
|
1613
|
+
}
|
|
1614
|
+
function installCredentialHelper(cwd, url) {
|
|
1615
|
+
try {
|
|
1616
|
+
const origin = new URL(url);
|
|
1617
|
+
const key = `credential.${origin.protocol}//${origin.host}.helper`;
|
|
1618
|
+
const helper = credentialHelperCommand();
|
|
1619
|
+
const wanted = ["", helper];
|
|
1620
|
+
const transient = entryIsTransient(cwd);
|
|
1621
|
+
const scope = transient ? "--worktree" : "--global";
|
|
1622
|
+
if (transient) {
|
|
1623
|
+
const enabled = runGit(cwd, ["config", "--local", "extensions.worktreeConfig", "true"], {
|
|
1624
|
+
allowFail: true
|
|
1625
|
+
});
|
|
1626
|
+
if (enabled.status !== 0) return;
|
|
1627
|
+
runGit(cwd, ["config", "--local", "--unset-all", key], { allowFail: true });
|
|
1628
|
+
}
|
|
1629
|
+
const current = runGit(cwd, ["config", scope, "--get-all", key], { allowFail: true });
|
|
1630
|
+
if (current.status === 0) {
|
|
1631
|
+
const existing = current.stdout.toString("utf-8").split("\n").filter((_, index, lines) => index < lines.length - 1);
|
|
1632
|
+
if (existing.length === wanted.length && existing.every((value, i) => value === wanted[i])) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
if (existing.length === wanted.length && existing[0] === "" && helperEntryUsable(existing[1])) {
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
runGit(cwd, ["config", scope, "--unset-all", key], { allowFail: true });
|
|
1639
|
+
}
|
|
1640
|
+
runGit(cwd, ["config", scope, "--add", key, ""]);
|
|
1641
|
+
runGit(cwd, ["config", scope, "--add", key, helper]);
|
|
1642
|
+
if (scope === "--global" && !process.argv.includes("--json")) {
|
|
1643
|
+
process.stderr.write(
|
|
1644
|
+
`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}
|
|
1645
|
+
`
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
} catch {
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
function ensureSpaceRemote(cwd, appId, remote = SPACE_REMOTE, token) {
|
|
1652
|
+
const url = repoUrl(appId);
|
|
1653
|
+
const current = runGit(cwd, ["remote", "get-url", remote], { allowFail: true });
|
|
1654
|
+
const existing = current.status === 0 ? current.stdout.toString("utf-8").trim() : null;
|
|
1655
|
+
if (appIdFromRepoUrl(existing ?? "") !== appId) {
|
|
1656
|
+
const owned = declaredAppIds(cwd);
|
|
1657
|
+
if (owned.size > 0 && !owned.has(appId)) {
|
|
1658
|
+
throw new InputError(
|
|
1659
|
+
`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\`.`,
|
|
1660
|
+
"app_checkout_mismatch"
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (token) ensureGitIdentity(cwd, token);
|
|
1665
|
+
if (existing === null) {
|
|
1666
|
+
runGit(cwd, ["remote", "add", remote, url]);
|
|
1667
|
+
} else if (existing !== url) {
|
|
1668
|
+
const priorApp = appIdFromRepoUrl(existing);
|
|
1669
|
+
if (priorApp && priorApp !== appId && !process.argv.includes("--json")) {
|
|
1670
|
+
process.stderr.write(
|
|
1671
|
+
`warning: remote '${remote}' was pointed at app ${priorApp}; re-aiming it at ${appId}.
|
|
1672
|
+
`
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
runGit(cwd, ["remote", "set-url", remote, url]);
|
|
1676
|
+
}
|
|
1677
|
+
installCredentialHelper(cwd, url);
|
|
1678
|
+
return url;
|
|
1679
|
+
}
|
|
1680
|
+
function removeSpaceRemote(cwd, remote = SPACE_REMOTE) {
|
|
1681
|
+
const current = runGit(cwd, ["remote", "get-url", remote], { allowFail: true });
|
|
1682
|
+
if (current.status !== 0) return false;
|
|
1683
|
+
runGit(cwd, ["remote", "remove", remote]);
|
|
1684
|
+
return true;
|
|
1685
|
+
}
|
|
1686
|
+
function ensureGitIdentity(cwd, token) {
|
|
1687
|
+
try {
|
|
1688
|
+
const configured = (key) => runGit(cwd, ["config", "--get", key], { allowFail: true }).stdout.toString("utf-8").trim() !== "";
|
|
1689
|
+
const missingName = !configured("user.name");
|
|
1690
|
+
const missingEmail = !configured("user.email");
|
|
1691
|
+
if (!missingName && !missingEmail) return;
|
|
1692
|
+
if (runGit(cwd, ["config", "--get", "--type=bool", "user.useConfigOnly"], { allowFail: true }).stdout.toString("utf-8").trim() === "true") {
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
const payload = decodeJwtPayload(token);
|
|
1696
|
+
if (!payload.email) return;
|
|
1697
|
+
if (missingName) {
|
|
1698
|
+
runGit(cwd, ["config", "--local", "user.name", payload.name || payload.email], {
|
|
1699
|
+
allowFail: true
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
if (missingEmail) {
|
|
1703
|
+
runGit(cwd, ["config", "--local", "user.email", payload.email], { allowFail: true });
|
|
1704
|
+
}
|
|
1705
|
+
const wrote = [...missingEmail ? ["user.email"] : [], ...missingName ? ["user.name"] : []];
|
|
1706
|
+
const dir = /^[A-Za-z0-9_./:@-]+$/.test(cwd) ? cwd : shQuote(cwd);
|
|
1707
|
+
const undo = wrote.map((key) => `git -C ${dir} config --local --unset ${key}`).join(" && ");
|
|
1708
|
+
process.stderr.write(
|
|
1709
|
+
`Git identity for this checkout set from your DeepSpace login (${payload.email}).
|
|
1710
|
+
Undo with: ${undo}
|
|
1711
|
+
`
|
|
1712
|
+
);
|
|
1713
|
+
} catch {
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
function appIdFromRepoUrl(url) {
|
|
1717
|
+
const match = /\/api\/repo\/([^/]+)\/?$/.exec(url);
|
|
1718
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// src/cli/lib/app-registration.ts
|
|
1722
|
+
var DEPLOY_URL = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
1723
|
+
async function mintAppIdentity(appDir, token, opts = {}) {
|
|
1724
|
+
const { appId } = await apiFetch(DEPLOY_URL, token, "/api/apps/mint", {
|
|
1725
|
+
method: "POST"
|
|
1726
|
+
});
|
|
1727
|
+
try {
|
|
1728
|
+
writeAppId(appDir, appId, { wranglerEnv: opts.wranglerEnv, force: Boolean(opts.force) });
|
|
1729
|
+
} catch (error) {
|
|
1730
|
+
throw new Error(
|
|
1731
|
+
`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.`
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
const committedScaffold = opts.commitScaffold === true && commitScaffoldIfUnborn(appDir, token);
|
|
1735
|
+
return { appId, committedScaffold };
|
|
1736
|
+
}
|
|
1737
|
+
function canHealAppRegistration(appDir, wranglerEnv) {
|
|
1738
|
+
if (!hasWranglerConfig(appDir)) return false;
|
|
1739
|
+
try {
|
|
1740
|
+
const cfg = readWranglerConfig(appDir);
|
|
1741
|
+
if (wranglerEnv && cfg.env?.[wranglerEnv] === void 0) return false;
|
|
1742
|
+
if (readAppIdVar(cfg, wranglerEnv) === void 0) return false;
|
|
1743
|
+
} catch {
|
|
1744
|
+
return false;
|
|
1745
|
+
}
|
|
1746
|
+
try {
|
|
1747
|
+
const committed = runGit(appDir, ["show", "HEAD:./wrangler.toml"], { allowFail: true });
|
|
1748
|
+
if (committed.status === 0 && committed.stdout.toString("utf-8").includes("__APP_ID__")) {
|
|
1749
|
+
return false;
|
|
1750
|
+
}
|
|
1751
|
+
} catch {
|
|
1752
|
+
}
|
|
1753
|
+
return true;
|
|
1754
|
+
}
|
|
1755
|
+
async function ensureAppRegistered(appDir, token, wranglerEnv, opts = {}) {
|
|
1756
|
+
const existing = readAppId(appDir, wranglerEnv);
|
|
1757
|
+
if (existing) return { appId: existing, minted: false, committedScaffold: false };
|
|
1758
|
+
if (!canHealAppRegistration(appDir, wranglerEnv)) return null;
|
|
1759
|
+
const release2 = acquireMintLock(appDir);
|
|
1760
|
+
if (!release2) {
|
|
1761
|
+
const winner = await waitForMintedId(appDir, wranglerEnv);
|
|
1762
|
+
if (winner) return { appId: winner, minted: false, committedScaffold: false };
|
|
1763
|
+
}
|
|
1764
|
+
try {
|
|
1765
|
+
const raced = readAppId(appDir, wranglerEnv);
|
|
1766
|
+
if (raced) return { appId: raced, minted: false, committedScaffold: false };
|
|
1767
|
+
const { appId, committedScaffold } = await mintAppIdentity(appDir, token, {
|
|
1768
|
+
wranglerEnv,
|
|
1769
|
+
commitScaffold: opts.commitScaffold
|
|
1770
|
+
});
|
|
1771
|
+
process.stderr.write(
|
|
1772
|
+
`Registered ${appId}${wranglerEnv ? ` (env: ${wranglerEnv})` : ""} on ${DEEPSPACE_ENV} to your account \u2014 apps register on first use` + (committedScaffold ? "; initial scaffold commit created" : "") + ".\n"
|
|
1773
|
+
);
|
|
1774
|
+
return { appId, minted: true, committedScaffold };
|
|
1775
|
+
} finally {
|
|
1776
|
+
release2?.();
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
function acquireMintLock(appDir) {
|
|
1780
|
+
const lockDir = join4(appDir, ".deepspace", "register.lock");
|
|
1781
|
+
mkdirSync3(join4(appDir, ".deepspace"), { recursive: true });
|
|
1782
|
+
try {
|
|
1783
|
+
mkdirSync3(lockDir);
|
|
1784
|
+
} catch {
|
|
1785
|
+
try {
|
|
1786
|
+
const age = Date.now() - statSync(lockDir).mtimeMs;
|
|
1787
|
+
if (age < 2 * 60 * 1e3) return null;
|
|
1788
|
+
rmSync3(lockDir, { recursive: true, force: true });
|
|
1789
|
+
mkdirSync3(lockDir);
|
|
1790
|
+
} catch {
|
|
1791
|
+
return null;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
return () => rmSync3(lockDir, { recursive: true, force: true });
|
|
1795
|
+
}
|
|
1796
|
+
async function waitForMintedId(appDir, wranglerEnv) {
|
|
1797
|
+
const deadline = Date.now() + 3e4;
|
|
1798
|
+
while (Date.now() < deadline) {
|
|
1799
|
+
const id = readAppId(appDir, wranglerEnv);
|
|
1800
|
+
if (id) return id;
|
|
1801
|
+
if (!existsSync7(join4(appDir, ".deepspace", "register.lock"))) {
|
|
1802
|
+
return readAppId(appDir, wranglerEnv);
|
|
1803
|
+
}
|
|
1804
|
+
await new Promise((resolve23) => setTimeout(resolve23, 250));
|
|
1805
|
+
}
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
function wranglerConfigUncommitted(appDir) {
|
|
1809
|
+
if (!existsSync7(join4(appDir, ".git"))) return false;
|
|
1810
|
+
const dryRun = runGit(appDir, ["commit", "--dry-run", "-m", "x", "--", "wrangler.toml"], {
|
|
1811
|
+
allowFail: true
|
|
1812
|
+
});
|
|
1813
|
+
return dryRun.status === 0;
|
|
1814
|
+
}
|
|
1815
|
+
function commitScaffoldIfUnborn(appDir, token) {
|
|
1816
|
+
try {
|
|
1817
|
+
if (!existsSync7(join4(appDir, ".git"))) return false;
|
|
1818
|
+
if (runGit(appDir, ["rev-parse", "--verify", "HEAD"], { allowFail: true }).status === 0) {
|
|
1819
|
+
return false;
|
|
1820
|
+
}
|
|
1821
|
+
ensureGitIdentity(appDir, token);
|
|
1822
|
+
if (runGit(appDir, ["add", "-A"], { allowFail: true }).status !== 0) return false;
|
|
1823
|
+
const commit = runGit(appDir, ["commit", "-m", "Initial DeepSpace scaffold", "--no-verify"], {
|
|
1824
|
+
allowFail: true
|
|
1825
|
+
});
|
|
1826
|
+
return commit.status === 0;
|
|
1827
|
+
} catch {
|
|
1828
|
+
return false;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// src/cli/commands/dev.ts
|
|
1833
|
+
import { resolve as resolve9, basename as basename2, join as join12 } from "path";
|
|
1834
|
+
import spawn2 from "cross-spawn";
|
|
1835
|
+
|
|
1836
|
+
// src/cli/lib/app-context.ts
|
|
1837
|
+
import { existsSync as existsSync8, readdirSync } from "fs";
|
|
1838
|
+
import { dirname as dirname3, join as join5, resolve as resolve4 } from "path";
|
|
1839
|
+
function detectAppName(cwd = process.cwd()) {
|
|
1840
|
+
const appDir = resolve4(cwd);
|
|
1841
|
+
if (!existsSync8(join5(appDir, "wrangler.toml"))) return null;
|
|
1842
|
+
const { name } = readWranglerConfig(appDir);
|
|
1843
|
+
return typeof name === "string" && name.length > 0 ? name : null;
|
|
1844
|
+
}
|
|
1845
|
+
function findAppDir(start = process.cwd()) {
|
|
1846
|
+
let dir = resolve4(start);
|
|
1847
|
+
for (; ; ) {
|
|
1848
|
+
if (existsSync8(join5(dir, "wrangler.toml"))) return dir;
|
|
1849
|
+
const parent = dirname3(dir);
|
|
1850
|
+
if (parent === dir) return null;
|
|
1851
|
+
dir = parent;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
function findChildApps(dir) {
|
|
1855
|
+
try {
|
|
1856
|
+
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync8(join5(dir, entry.name, "wrangler.toml"))).map((entry) => entry.name).sort();
|
|
1857
|
+
} catch {
|
|
1858
|
+
return [];
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
// src/cli/lib/launch-config.ts
|
|
1863
|
+
import {
|
|
1864
|
+
existsSync as existsSync10,
|
|
1865
|
+
readFileSync as readFileSync7,
|
|
1866
|
+
realpathSync as realpathSync2,
|
|
1867
|
+
writeFileSync as writeFileSync4,
|
|
1868
|
+
mkdirSync as mkdirSync5,
|
|
1869
|
+
renameSync,
|
|
1870
|
+
unlinkSync
|
|
1871
|
+
} from "fs";
|
|
1872
|
+
import { join as join7, resolve as resolve6, dirname as dirname5, basename, isAbsolute as isAbsolute3, sep } from "path";
|
|
1873
|
+
|
|
1597
1874
|
// src/cli/lib/git/repository.ts
|
|
1875
|
+
import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync6 } from "fs";
|
|
1876
|
+
import { dirname as dirname4, join as join6, resolve as resolve5 } from "path";
|
|
1598
1877
|
function isPlausibleBranchName(name) {
|
|
1599
1878
|
if (!name || name === "HEAD" || name === "@") return false;
|
|
1600
1879
|
if (name.startsWith("/") || name.startsWith("-") || name.startsWith(".")) return false;
|
|
@@ -1644,7 +1923,7 @@ function assertNoOperationInProgress(cwd) {
|
|
|
1644
1923
|
["REVERT_HEAD", "revert", "git revert --continue", "git revert --abort"],
|
|
1645
1924
|
["rebase-merge", "rebase", "git rebase --continue", "git rebase --abort"],
|
|
1646
1925
|
["rebase-apply", "rebase", "git rebase --continue", "git rebase --abort"]
|
|
1647
|
-
].find(([marker]) =>
|
|
1926
|
+
].find(([marker]) => existsSync9(join6(resolve5(cwd, gitDir), marker)));
|
|
1648
1927
|
if (!inProgress) return;
|
|
1649
1928
|
const [, kind, finish, abort] = inProgress;
|
|
1650
1929
|
throw new InputError(
|
|
@@ -1658,13 +1937,13 @@ function repoToplevel(cwd) {
|
|
|
1658
1937
|
function interruptedGitOperation(cwd) {
|
|
1659
1938
|
const gitDir = runGit(cwd, ["rev-parse", "--git-path", "HEAD"], { allowFail: true });
|
|
1660
1939
|
if (gitDir.status !== 0) return null;
|
|
1661
|
-
const dir = dirname4(
|
|
1662
|
-
if (
|
|
1663
|
-
if (
|
|
1664
|
-
if (
|
|
1665
|
-
if (
|
|
1666
|
-
if (
|
|
1667
|
-
if (
|
|
1940
|
+
const dir = dirname4(resolve5(cwd, gitDir.stdout.toString("utf-8").trim()));
|
|
1941
|
+
if (existsSync9(join6(dir, "rebase-apply", "applying"))) return "am";
|
|
1942
|
+
if (existsSync9(join6(dir, "rebase-merge")) || existsSync9(join6(dir, "rebase-apply"))) return "rebase";
|
|
1943
|
+
if (existsSync9(join6(dir, "MERGE_HEAD"))) return "merge";
|
|
1944
|
+
if (existsSync9(join6(dir, "CHERRY_PICK_HEAD"))) return "cherry-pick";
|
|
1945
|
+
if (existsSync9(join6(dir, "REVERT_HEAD"))) return "revert";
|
|
1946
|
+
if (existsSync9(join6(dir, "BISECT_LOG"))) return "bisect";
|
|
1668
1947
|
return null;
|
|
1669
1948
|
}
|
|
1670
1949
|
function currentBranch(cwd) {
|
|
@@ -1741,11 +2020,11 @@ function listWorktrees(cwd) {
|
|
|
1741
2020
|
current.branch = ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref;
|
|
1742
2021
|
}
|
|
1743
2022
|
}
|
|
1744
|
-
return worktrees.filter((worktree) =>
|
|
2023
|
+
return worktrees.filter((worktree) => existsSync9(worktree.path));
|
|
1745
2024
|
}
|
|
1746
2025
|
function ensureLocalExclude(cwd, pattern) {
|
|
1747
|
-
const commonDir =
|
|
1748
|
-
const excludePath =
|
|
2026
|
+
const commonDir = resolve5(cwd, gitLine(cwd, ["rev-parse", "--git-common-dir"]));
|
|
2027
|
+
const excludePath = join6(commonDir, "info", "exclude");
|
|
1749
2028
|
let current = "";
|
|
1750
2029
|
try {
|
|
1751
2030
|
current = readFileSync6(excludePath, "utf-8");
|
|
@@ -1753,7 +2032,7 @@ function ensureLocalExclude(cwd, pattern) {
|
|
|
1753
2032
|
}
|
|
1754
2033
|
if (current.split("\n").some((line) => line.trim() === pattern)) return;
|
|
1755
2034
|
try {
|
|
1756
|
-
|
|
2035
|
+
mkdirSync4(dirname4(excludePath), { recursive: true });
|
|
1757
2036
|
appendFileSync(
|
|
1758
2037
|
excludePath,
|
|
1759
2038
|
`${current.endsWith("\n") || current === "" ? "" : "\n"}${pattern}
|
|
@@ -1765,7 +2044,7 @@ function ensureLocalExclude(cwd, pattern) {
|
|
|
1765
2044
|
|
|
1766
2045
|
// src/cli/lib/launch-config.ts
|
|
1767
2046
|
function writeLaunchFile(launchPath, config) {
|
|
1768
|
-
|
|
2047
|
+
mkdirSync5(dirname5(launchPath), { recursive: true });
|
|
1769
2048
|
const tmpPath = `${launchPath}.${process.pid}.tmp`;
|
|
1770
2049
|
writeFileSync4(tmpPath, JSON.stringify(config, null, 2) + "\n");
|
|
1771
2050
|
try {
|
|
@@ -1780,7 +2059,7 @@ function writeLaunchFile(launchPath, config) {
|
|
|
1780
2059
|
}
|
|
1781
2060
|
function readLaunchFile(launchPath) {
|
|
1782
2061
|
const empty = { version: "0.0.1", configurations: [] };
|
|
1783
|
-
if (!
|
|
2062
|
+
if (!existsSync10(launchPath)) return empty;
|
|
1784
2063
|
const raw = readFileSync7(launchPath, "utf-8").trim();
|
|
1785
2064
|
if (!raw) return empty;
|
|
1786
2065
|
const parsed = JSON.parse(raw);
|
|
@@ -1788,7 +2067,7 @@ function readLaunchFile(launchPath) {
|
|
|
1788
2067
|
return parsed;
|
|
1789
2068
|
}
|
|
1790
2069
|
function writeLaunchConfigIfMissing(appDir, appName, port, opts = {}) {
|
|
1791
|
-
const launchPath =
|
|
2070
|
+
const launchPath = join7(appDir, ".claude", "launch.json");
|
|
1792
2071
|
try {
|
|
1793
2072
|
const config = readLaunchFile(launchPath);
|
|
1794
2073
|
if (!config) return;
|
|
@@ -1829,7 +2108,7 @@ function writeLaunchConfigIfMissing(appDir, appName, port, opts = {}) {
|
|
|
1829
2108
|
}
|
|
1830
2109
|
function resolveAppLaunchPort(appDir) {
|
|
1831
2110
|
try {
|
|
1832
|
-
const config = readLaunchFile(
|
|
2111
|
+
const config = readLaunchFile(join7(appDir, ".claude", "launch.json"));
|
|
1833
2112
|
if (!config) return null;
|
|
1834
2113
|
const appName = detectAppName(appDir) ?? basename(appDir);
|
|
1835
2114
|
const entry = config.configurations.find((c) => c?.name === appName) ?? (config.configurations.length === 1 ? config.configurations[0] : void 0);
|
|
@@ -1860,7 +2139,7 @@ function canonicalPath(path) {
|
|
|
1860
2139
|
try {
|
|
1861
2140
|
return realpathSync2(path);
|
|
1862
2141
|
} catch {
|
|
1863
|
-
return
|
|
2142
|
+
return resolve6(path);
|
|
1864
2143
|
}
|
|
1865
2144
|
}
|
|
1866
2145
|
function detectLinkedWorktree(appDir) {
|
|
@@ -1883,21 +2162,21 @@ function deriveWorktreePort(worktreeKey) {
|
|
|
1883
2162
|
return WORKTREE_PORT_MIN + hash % WORKTREE_PORT_RANGE;
|
|
1884
2163
|
}
|
|
1885
2164
|
function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
1886
|
-
const launchPath =
|
|
2165
|
+
const launchPath = join7(mainRepoRoot, ".claude", "launch.json");
|
|
1887
2166
|
const entryName = WORKTREE_ENTRY_PREFIX + worktreeName;
|
|
1888
2167
|
try {
|
|
1889
2168
|
const config = readLaunchFile(launchPath);
|
|
1890
2169
|
if (!config) return null;
|
|
1891
|
-
const worktreesRoot =
|
|
2170
|
+
const worktreesRoot = join7(mainRepoRoot, ".claude", "worktrees") + sep;
|
|
1892
2171
|
config.configurations = config.configurations.filter((c) => {
|
|
1893
2172
|
if (c?.name === entryName) return false;
|
|
1894
|
-
const isStaleWorktreeEntry = typeof c?.name === "string" && c.name.startsWith(WORKTREE_ENTRY_PREFIX) && typeof c.cwd === "string" &&
|
|
2173
|
+
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
2174
|
return !isStaleWorktreeEntry;
|
|
1896
2175
|
});
|
|
1897
2176
|
let port = opts.port;
|
|
1898
2177
|
if (opts.probePort) {
|
|
1899
2178
|
const taken = new Set(
|
|
1900
|
-
config.configurations.map((c) => Number(c?.port)).filter((
|
|
2179
|
+
config.configurations.map((c) => Number(c?.port)).filter((p35) => Number.isInteger(p35) && p35 > 0)
|
|
1901
2180
|
);
|
|
1902
2181
|
for (let i = 0; i < WORKTREE_PORT_RANGE && taken.has(port); i++) {
|
|
1903
2182
|
port = WORKTREE_PORT_MIN + (port - WORKTREE_PORT_MIN + 1) % WORKTREE_PORT_RANGE;
|
|
@@ -1912,7 +2191,7 @@ function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
|
1912
2191
|
// the Claude Code desktop launch.json reference alongside `port`) and
|
|
1913
2192
|
// verified end-to-end: with it set, the preview server builds from the
|
|
1914
2193
|
// worktree instead of the session's main repo.
|
|
1915
|
-
cwd:
|
|
2194
|
+
cwd: resolve6(appDir)
|
|
1916
2195
|
});
|
|
1917
2196
|
writeLaunchFile(launchPath, config);
|
|
1918
2197
|
return { entryName, port };
|
|
@@ -1926,7 +2205,7 @@ function resolveWorktreePort(appDir) {
|
|
|
1926
2205
|
const claude = detectClaudeWorktree(appDir);
|
|
1927
2206
|
if (claude) {
|
|
1928
2207
|
try {
|
|
1929
|
-
const config = readLaunchFile(
|
|
2208
|
+
const config = readLaunchFile(join7(claude.mainRepoRoot, ".claude", "launch.json"));
|
|
1930
2209
|
const entry = config?.configurations.find(
|
|
1931
2210
|
(c) => c?.name === WORKTREE_ENTRY_PREFIX + claude.worktreeName
|
|
1932
2211
|
);
|
|
@@ -1987,14 +2266,6 @@ async function getAppSource(deployUrl, token, appId) {
|
|
|
1987
2266
|
throw error;
|
|
1988
2267
|
}
|
|
1989
2268
|
}
|
|
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
2269
|
|
|
1999
2270
|
// src/cli/lib/repo-api.ts
|
|
2000
2271
|
function isWorkspaceRef(ref) {
|
|
@@ -2002,10 +2273,15 @@ function isWorkspaceRef(ref) {
|
|
|
2002
2273
|
return ref.startsWith(prefix) && isWorkspaceId(ref.slice(prefix.length));
|
|
2003
2274
|
}
|
|
2004
2275
|
function releaseSourceLabel(release2) {
|
|
2005
|
-
|
|
2006
|
-
if (release2.
|
|
2007
|
-
if (release2.source?.provider === "
|
|
2008
|
-
|
|
2276
|
+
const dirty = release2.sourceDirty === true ? " (dirty worktree)" : "";
|
|
2277
|
+
if (release2.commitOid) return `commit ${release2.commitOid.slice(0, 10)}${dirty}`;
|
|
2278
|
+
if (release2.source?.provider === "github") {
|
|
2279
|
+
return `GitHub \xB7 ${release2.source.repository}${dirty ? ", dirty worktree" : ""}`;
|
|
2280
|
+
}
|
|
2281
|
+
if (release2.source?.provider === "deepspace") {
|
|
2282
|
+
return `DeepSpace source, no commit recorded${dirty}`;
|
|
2283
|
+
}
|
|
2284
|
+
return `no source recorded${dirty}`;
|
|
2009
2285
|
}
|
|
2010
2286
|
function mintIdempotencyKey() {
|
|
2011
2287
|
return randomUUID();
|
|
@@ -2186,6 +2462,11 @@ async function resolveAppTarget(deployUrl, token, explicit, options = {}) {
|
|
|
2186
2462
|
if (app2 !== void 0) return resolveAppSelector(deployUrl, token, app2);
|
|
2187
2463
|
const { wranglerEnv } = parseWranglerEnvArg(options.wranglerEnv);
|
|
2188
2464
|
const appDir = findAppDir();
|
|
2465
|
+
if (options.register === true) {
|
|
2466
|
+
const ensured = appDir ? await ensureAppRegistered(appDir, token, wranglerEnv) : null;
|
|
2467
|
+
if (!ensured) throw appTargetMissingError(appDir, wranglerEnv);
|
|
2468
|
+
return ensured.appId;
|
|
2469
|
+
}
|
|
2189
2470
|
const id = appDir ? readAppId(appDir, wranglerEnv) : null;
|
|
2190
2471
|
if (!id) throw appTargetMissingError(appDir, wranglerEnv);
|
|
2191
2472
|
return id;
|
|
@@ -2255,7 +2536,9 @@ function assertAppTargetResolvable(appArg, options = {}) {
|
|
|
2255
2536
|
}
|
|
2256
2537
|
if (app2 !== void 0) return;
|
|
2257
2538
|
const appDir = findAppDir();
|
|
2258
|
-
if (
|
|
2539
|
+
if (appDir && readAppId(appDir, wranglerEnv)) return;
|
|
2540
|
+
if (options.register === true && appDir && canHealAppRegistration(appDir, wranglerEnv)) return;
|
|
2541
|
+
throw appTargetMissingError(appDir, wranglerEnv);
|
|
2259
2542
|
}
|
|
2260
2543
|
async function warnIfPhantomApp(deployUrl, token, appId, explicit) {
|
|
2261
2544
|
if (!explicit || !STRICT_APP_ID_RE.test(explicit.trim())) return;
|
|
@@ -2357,22 +2640,34 @@ async function writeDevVars(appDir, ownerId, callerJwt, wranglerEnv, opts = {})
|
|
|
2357
2640
|
}
|
|
2358
2641
|
|
|
2359
2642
|
// src/cli/lib/install-status.ts
|
|
2360
|
-
import {
|
|
2361
|
-
|
|
2643
|
+
import {
|
|
2644
|
+
appendFileSync as appendFileSync2,
|
|
2645
|
+
closeSync,
|
|
2646
|
+
existsSync as existsSync12,
|
|
2647
|
+
mkdirSync as mkdirSync6,
|
|
2648
|
+
openSync,
|
|
2649
|
+
readFileSync as readFileSync9,
|
|
2650
|
+
realpathSync as realpathSync3,
|
|
2651
|
+
rmSync as rmSync4,
|
|
2652
|
+
statSync as statSync2,
|
|
2653
|
+
writeFileSync as writeFileSync5
|
|
2654
|
+
} from "fs";
|
|
2655
|
+
import { dirname as dirname6, join as join9, resolve as resolve7 } from "path";
|
|
2656
|
+
import { sync as spawnSync2 } from "cross-spawn";
|
|
2362
2657
|
|
|
2363
2658
|
// src/cli/lib/package-manager.ts
|
|
2364
|
-
import { existsSync as
|
|
2365
|
-
import { join as
|
|
2659
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
2660
|
+
import { join as join8 } from "path";
|
|
2366
2661
|
function detectPackageManager(appDir) {
|
|
2367
2662
|
try {
|
|
2368
|
-
const pkg2 = JSON.parse(readFileSync8(
|
|
2663
|
+
const pkg2 = JSON.parse(readFileSync8(join8(appDir, "package.json"), "utf-8"));
|
|
2369
2664
|
const name = typeof pkg2.packageManager === "string" ? pkg2.packageManager.split("@")[0] : "";
|
|
2370
2665
|
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
|
|
2371
2666
|
} catch {
|
|
2372
2667
|
}
|
|
2373
|
-
if (
|
|
2374
|
-
if (
|
|
2375
|
-
if (
|
|
2668
|
+
if (existsSync11(join8(appDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
2669
|
+
if (existsSync11(join8(appDir, "yarn.lock"))) return "yarn";
|
|
2670
|
+
if (existsSync11(join8(appDir, "bun.lock")) || existsSync11(join8(appDir, "bun.lockb"))) return "bun";
|
|
2376
2671
|
return "npm";
|
|
2377
2672
|
}
|
|
2378
2673
|
function installCommand(appDir) {
|
|
@@ -2382,10 +2677,10 @@ function installCommand(appDir) {
|
|
|
2382
2677
|
// src/cli/lib/install-status.ts
|
|
2383
2678
|
function installState(appDir) {
|
|
2384
2679
|
if (resolvesDeepspace(appDir)) return "ready";
|
|
2385
|
-
const sentinelDir =
|
|
2386
|
-
if (
|
|
2387
|
-
if (
|
|
2388
|
-
if (!
|
|
2680
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
2681
|
+
if (existsSync12(join9(sentinelDir, "install.err"))) return "failed";
|
|
2682
|
+
if (existsSync12(join9(sentinelDir, "install.started"))) {
|
|
2683
|
+
if (!existsSync12(join9(sentinelDir, "install.done")) && installWorkerAlive(sentinelDir)) {
|
|
2389
2684
|
return "installing";
|
|
2390
2685
|
}
|
|
2391
2686
|
return "failed";
|
|
@@ -2397,43 +2692,109 @@ function tailHint(logPath, platform = process.platform) {
|
|
|
2397
2692
|
}
|
|
2398
2693
|
function ensureInstallReady(appDir) {
|
|
2399
2694
|
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}.` : "";
|
|
2695
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
2696
|
+
const startedPath = join9(sentinelDir, "install.started");
|
|
2697
|
+
const donePath = join9(sentinelDir, "install.done");
|
|
2698
|
+
const logPath = join9(sentinelDir, "install.log");
|
|
2699
|
+
if (process.env.DEEPSPACE_NO_INSTALL) {
|
|
2408
2700
|
throw new Refusal(
|
|
2409
|
-
|
|
2410
|
-
"
|
|
2701
|
+
"Dependencies not installed, and DEEPSPACE_NO_INSTALL is set \u2014 run the install yourself, then retry.",
|
|
2702
|
+
"deps_missing",
|
|
2411
2703
|
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
2412
2704
|
);
|
|
2413
2705
|
}
|
|
2414
|
-
if (
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2706
|
+
if (existsSync12(startedPath) && !existsSync12(donePath) && installWorkerAlive(sentinelDir) && sentinelAgeMs(startedPath) < 30 * 60 * 1e3) {
|
|
2707
|
+
const tail = existsSync12(logPath) ? ` Tail progress: ${tailHint(logPath)}.` : "";
|
|
2708
|
+
throw new Refusal(
|
|
2709
|
+
`Dependencies are still installing.${tail} Retry once it finishes (or remove ${sentinelDir}/install.started if no install is actually running).`,
|
|
2710
|
+
"install_in_progress"
|
|
2711
|
+
);
|
|
2712
|
+
}
|
|
2713
|
+
healMissingInstall(appDir);
|
|
2714
|
+
}
|
|
2715
|
+
function healMissingInstall(appDir) {
|
|
2716
|
+
const manager = detectPackageManager(appDir);
|
|
2717
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
2718
|
+
const logPath = join9(sentinelDir, "install.log");
|
|
2719
|
+
const errPath = join9(sentinelDir, "install.err");
|
|
2720
|
+
const previousFailure = existsSync12(errPath) ? readFileSync9(errPath, "utf-8").trimEnd() : null;
|
|
2721
|
+
process.stderr.write(
|
|
2722
|
+
`Installing dependencies (${manager} install) \u2014 first use installs them. ${previousFailure ? `Retrying after: ${previousFailure}. ` : ""}Watch: ${tailHint(logPath)}
|
|
2723
|
+
`
|
|
2724
|
+
);
|
|
2725
|
+
if (process.platform === "win32") {
|
|
2726
|
+
for (const ext of [".cmd", ".bat", ".exe", ".com", ".ps1"]) {
|
|
2727
|
+
if (existsSync12(join9(appDir, `${manager}${ext}`))) {
|
|
2728
|
+
throw new Refusal(
|
|
2729
|
+
`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.`,
|
|
2730
|
+
"install_failed"
|
|
2731
|
+
);
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
mkdirSync6(sentinelDir, { recursive: true });
|
|
2736
|
+
ensureSentinelsIgnored(appDir);
|
|
2737
|
+
rmSync4(errPath, { force: true });
|
|
2738
|
+
rmSync4(join9(sentinelDir, "install.done"), { force: true });
|
|
2739
|
+
writeFileSync5(join9(sentinelDir, "install.started"), (/* @__PURE__ */ new Date()).toISOString());
|
|
2740
|
+
writeFileSync5(join9(sentinelDir, "install.pid"), String(process.pid));
|
|
2741
|
+
const logFd = openSync(logPath, "w");
|
|
2742
|
+
let result;
|
|
2743
|
+
try {
|
|
2744
|
+
result = spawnSync2(manager, ["install"], {
|
|
2745
|
+
cwd: appDir,
|
|
2746
|
+
stdio: ["ignore", logFd, logFd],
|
|
2747
|
+
// Bounded (repo rule: no task over 5 minutes): a hung registry must
|
|
2748
|
+
// surface as a fast failure, never a stall. The log names the cure.
|
|
2749
|
+
timeout: 5 * 60 * 1e3
|
|
2750
|
+
});
|
|
2751
|
+
} finally {
|
|
2752
|
+
closeSync(logFd);
|
|
2753
|
+
}
|
|
2754
|
+
if (result.status === 0 && !result.error && resolvesDeepspace(appDir)) {
|
|
2755
|
+
writeFileSync5(join9(sentinelDir, "install.done"), (/* @__PURE__ */ new Date()).toISOString());
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
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}`;
|
|
2759
|
+
writeFileSync5(errPath, detail);
|
|
2760
|
+
let logTail = "";
|
|
2761
|
+
try {
|
|
2762
|
+
const log28 = readFileSync9(logPath, "utf-8");
|
|
2763
|
+
const tail = log28.slice(-1500).trimEnd();
|
|
2764
|
+
if (tail) logTail = `
|
|
2765
|
+
--- install log (tail) ---
|
|
2766
|
+
${tail}`;
|
|
2767
|
+
} catch {
|
|
2428
2768
|
}
|
|
2429
2769
|
throw new Refusal(
|
|
2430
|
-
|
|
2431
|
-
"
|
|
2432
|
-
{
|
|
2433
|
-
action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] }
|
|
2434
|
-
}
|
|
2770
|
+
`Dependency install failed: ${detail}. Full log: ${logPath}. It will be retried on the next command, or run \`${manager} install\` yourself.${logTail}`,
|
|
2771
|
+
"install_failed",
|
|
2772
|
+
{ action: { cwd: appDir, argv: [manager, "install"] } }
|
|
2435
2773
|
);
|
|
2436
2774
|
}
|
|
2775
|
+
function sentinelAgeMs(path) {
|
|
2776
|
+
try {
|
|
2777
|
+
return Date.now() - statSync2(path).mtimeMs;
|
|
2778
|
+
} catch {
|
|
2779
|
+
return Number.POSITIVE_INFINITY;
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
function ensureSentinelsIgnored(appDir) {
|
|
2783
|
+
try {
|
|
2784
|
+
const check = spawnSync2("git", ["-C", appDir, "check-ignore", "-q", ".deepspace"], {
|
|
2785
|
+
stdio: "ignore"
|
|
2786
|
+
});
|
|
2787
|
+
if (check.status === 0 || check.error) return;
|
|
2788
|
+
const excludePath = spawnSync2("git", ["-C", appDir, "rev-parse", "--git-path", "info/exclude"], {
|
|
2789
|
+
encoding: "utf-8"
|
|
2790
|
+
});
|
|
2791
|
+
if (excludePath.status !== 0) return;
|
|
2792
|
+
const target2 = resolve7(appDir, excludePath.stdout.trim());
|
|
2793
|
+
mkdirSync6(dirname6(target2), { recursive: true });
|
|
2794
|
+
appendFileSync2(target2, "\n# deepspace CLI state (added by first-use install)\n.deepspace/\n");
|
|
2795
|
+
} catch {
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2437
2798
|
function resolvesDeepspace(fromDir) {
|
|
2438
2799
|
let dir = canonicalPath2(fromDir);
|
|
2439
2800
|
let checkoutRoot = null;
|
|
@@ -2442,7 +2803,7 @@ function resolvesDeepspace(fromDir) {
|
|
|
2442
2803
|
} catch {
|
|
2443
2804
|
}
|
|
2444
2805
|
for (; ; ) {
|
|
2445
|
-
if (
|
|
2806
|
+
if (existsSync12(join9(dir, "node_modules", "deepspace", "package.json"))) return true;
|
|
2446
2807
|
if (dir === checkoutRoot) return false;
|
|
2447
2808
|
const parent = dirname6(dir);
|
|
2448
2809
|
if (parent === dir) return false;
|
|
@@ -2453,13 +2814,15 @@ function canonicalPath2(path) {
|
|
|
2453
2814
|
try {
|
|
2454
2815
|
return realpathSync3(path);
|
|
2455
2816
|
} catch {
|
|
2456
|
-
return
|
|
2817
|
+
return resolve7(path);
|
|
2457
2818
|
}
|
|
2458
2819
|
}
|
|
2459
2820
|
function installWorkerAlive(sentinelDir) {
|
|
2460
|
-
const pidPath =
|
|
2461
|
-
if (!
|
|
2462
|
-
|
|
2821
|
+
const pidPath = join9(sentinelDir, "install.pid");
|
|
2822
|
+
if (!existsSync12(pidPath)) return false;
|
|
2823
|
+
const pid = Number(readFileSync9(pidPath, "utf-8").trim());
|
|
2824
|
+
if (pid === process.pid) return false;
|
|
2825
|
+
return processAlive(pid);
|
|
2463
2826
|
}
|
|
2464
2827
|
function processAlive(pid) {
|
|
2465
2828
|
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
@@ -2473,9 +2836,9 @@ function processAlive(pid) {
|
|
|
2473
2836
|
|
|
2474
2837
|
// src/cli/lib/preflight.ts
|
|
2475
2838
|
import { execFileSync } from "child_process";
|
|
2476
|
-
import { existsSync as
|
|
2839
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2477
2840
|
import * as nodeModule from "module";
|
|
2478
|
-
import { resolve as
|
|
2841
|
+
import { resolve as resolve8 } from "path";
|
|
2479
2842
|
function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.registerHooks === "function", version = process.versions.node) {
|
|
2480
2843
|
const [major, minor] = version.split(".").map(Number);
|
|
2481
2844
|
const supportedLine = major === 22 && minor >= 15 || major === 24 || major === 26;
|
|
@@ -2488,7 +2851,7 @@ function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.regi
|
|
|
2488
2851
|
var DLL_NOT_FOUND = -1073741515;
|
|
2489
2852
|
function preflightWindowsWorkerd(appDir) {
|
|
2490
2853
|
if (process.platform !== "win32") return;
|
|
2491
|
-
const workerdExe =
|
|
2854
|
+
const workerdExe = resolve8(
|
|
2492
2855
|
appDir,
|
|
2493
2856
|
"node_modules",
|
|
2494
2857
|
"@cloudflare",
|
|
@@ -2496,7 +2859,7 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
2496
2859
|
"bin",
|
|
2497
2860
|
"workerd.exe"
|
|
2498
2861
|
);
|
|
2499
|
-
if (!
|
|
2862
|
+
if (!existsSync13(workerdExe)) return;
|
|
2500
2863
|
try {
|
|
2501
2864
|
execFileSync(workerdExe, ["--help"], { stdio: "pipe", timeout: 1e4 });
|
|
2502
2865
|
return;
|
|
@@ -2511,8 +2874,8 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
2511
2874
|
}
|
|
2512
2875
|
|
|
2513
2876
|
// src/cli/lib/macos-junk.ts
|
|
2514
|
-
import { readdirSync as readdirSync2, rmSync as
|
|
2515
|
-
import { join as
|
|
2877
|
+
import { readdirSync as readdirSync2, rmSync as rmSync5 } from "fs";
|
|
2878
|
+
import { join as join10 } from "path";
|
|
2516
2879
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".wrangler", ".vite"]);
|
|
2517
2880
|
function removeMacosJunk(dir) {
|
|
2518
2881
|
let removed = 0;
|
|
@@ -2523,14 +2886,14 @@ function removeMacosJunk(dir) {
|
|
|
2523
2886
|
return removed;
|
|
2524
2887
|
}
|
|
2525
2888
|
for (const entry of entries) {
|
|
2526
|
-
const path =
|
|
2889
|
+
const path = join10(dir, entry.name);
|
|
2527
2890
|
if (entry.isDirectory()) {
|
|
2528
2891
|
if (!SKIP_DIRS.has(entry.name)) removed += removeMacosJunk(path);
|
|
2529
2892
|
continue;
|
|
2530
2893
|
}
|
|
2531
2894
|
if (entry.name === ".DS_Store" || entry.name.startsWith("._")) {
|
|
2532
2895
|
try {
|
|
2533
|
-
|
|
2896
|
+
rmSync5(path, { force: true });
|
|
2534
2897
|
removed++;
|
|
2535
2898
|
} catch {
|
|
2536
2899
|
}
|
|
@@ -3105,8 +3468,8 @@ async function refreshSecretsCache(deployUrl, token, appId, wranglerEnv, selecte
|
|
|
3105
3468
|
}
|
|
3106
3469
|
|
|
3107
3470
|
// src/cli/lib/schema-lint.ts
|
|
3108
|
-
import { existsSync as
|
|
3109
|
-
import { join as
|
|
3471
|
+
import { existsSync as existsSync14 } from "fs";
|
|
3472
|
+
import { join as join11 } from "path";
|
|
3110
3473
|
import { pathToFileURL } from "url";
|
|
3111
3474
|
|
|
3112
3475
|
// src/server/schemas/registry.ts
|
|
@@ -3139,7 +3502,7 @@ function lintSchema(schema) {
|
|
|
3139
3502
|
const offendingRoles = Object.entries(schema.permissions).filter(([, perms]) => perms.read === true).map(([role]) => role);
|
|
3140
3503
|
if (offendingRoles.length > 0) {
|
|
3141
3504
|
const usesAware = Object.values(schema.permissions).some(
|
|
3142
|
-
(
|
|
3505
|
+
(p35) => typeof p35.read === "string" && VISIBILITY_AWARE.includes(p35.read)
|
|
3143
3506
|
);
|
|
3144
3507
|
if (!usesAware) {
|
|
3145
3508
|
warnings.push(
|
|
@@ -3175,7 +3538,7 @@ function lintSchemaSet(schemas) {
|
|
|
3175
3538
|
const warnings = [];
|
|
3176
3539
|
if (!schemas.some((schema) => schema.name === "team_members")) {
|
|
3177
3540
|
for (const schema of schemas) {
|
|
3178
|
-
const teamRoles = Object.entries(schema.permissions).filter(([,
|
|
3541
|
+
const teamRoles = Object.entries(schema.permissions).filter(([, p35]) => [p35.read, p35.update, p35.delete].includes("team")).map(([role]) => role);
|
|
3179
3542
|
if (teamRoles.length > 0) {
|
|
3180
3543
|
warnings.push(
|
|
3181
3544
|
`[${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 +3551,12 @@ function lintSchemaSet(schemas) {
|
|
|
3188
3551
|
|
|
3189
3552
|
// src/cli/lib/schema-lint.ts
|
|
3190
3553
|
async function lintProjectSchemas(appDir) {
|
|
3191
|
-
const schemasPath =
|
|
3192
|
-
if (!
|
|
3554
|
+
const schemasPath = join11(appDir, "src", "schemas.ts");
|
|
3555
|
+
if (!existsSync14(schemasPath)) return null;
|
|
3193
3556
|
let schemas;
|
|
3194
3557
|
try {
|
|
3195
3558
|
const esbuild = await import("esbuild");
|
|
3196
|
-
const outFile =
|
|
3559
|
+
const outFile = join11(appDir, ".wrangler", "deploy", "schemas.lint.bundle.mjs");
|
|
3197
3560
|
await esbuild.build({
|
|
3198
3561
|
entryPoints: [schemasPath],
|
|
3199
3562
|
bundle: true,
|
|
@@ -3267,6 +3630,28 @@ async function waitForPortListening(port, host, timeoutMs, signal) {
|
|
|
3267
3630
|
}
|
|
3268
3631
|
return false;
|
|
3269
3632
|
}
|
|
3633
|
+
async function assertReusableDevServer(port, waitMs = 15e3) {
|
|
3634
|
+
if (!await isPortListening(port, "127.0.0.1")) return;
|
|
3635
|
+
if (await isHttpResponding(port)) return;
|
|
3636
|
+
const deadline = Date.now() + waitMs;
|
|
3637
|
+
while (Date.now() < deadline) {
|
|
3638
|
+
if (!await isPortListening(port, "127.0.0.1")) return;
|
|
3639
|
+
await new Promise((resolve23) => setTimeout(resolve23, 500));
|
|
3640
|
+
}
|
|
3641
|
+
throw new Refusal(
|
|
3642
|
+
`Port ${port} is held by a server that no longer answers HTTP \u2014 likely a previous run's dev server still shutting down. Waited ${Math.round(waitMs / 1e3)}s for it to exit. Stop it (\`deepspace dev kill\`) or pass --port <other>, then re-run.`,
|
|
3643
|
+
"port_in_use",
|
|
3644
|
+
{ extra: { port } }
|
|
3645
|
+
);
|
|
3646
|
+
}
|
|
3647
|
+
async function isHttpResponding(port, timeoutMs = 2e3) {
|
|
3648
|
+
try {
|
|
3649
|
+
await fetch(`http://127.0.0.1:${port}/`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
3650
|
+
return true;
|
|
3651
|
+
} catch {
|
|
3652
|
+
return false;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3270
3655
|
function resolvePort(arg) {
|
|
3271
3656
|
const raw = arg ?? process.env.DEEPSPACE_PORT;
|
|
3272
3657
|
if (!raw) return DEFAULT_PORT;
|
|
@@ -3280,7 +3665,7 @@ function resolvePort(arg) {
|
|
|
3280
3665
|
// src/cli/commands/dev.ts
|
|
3281
3666
|
var DEFAULT_DEV_HOST = "0.0.0.0";
|
|
3282
3667
|
var READINESS_TIMEOUT_MS = 12e4;
|
|
3283
|
-
var
|
|
3668
|
+
var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
3284
3669
|
function noAppDirRefusal(start) {
|
|
3285
3670
|
const children = findChildApps(start);
|
|
3286
3671
|
const lines = [`No wrangler.toml found at or above ${start}.`];
|
|
@@ -3330,7 +3715,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
3330
3715
|
preflightNodeVersion("dev start");
|
|
3331
3716
|
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
3332
3717
|
const host = typeof args.host === "string" && args.host.trim() ? args.host.trim() : DEFAULT_DEV_HOST;
|
|
3333
|
-
const start =
|
|
3718
|
+
const start = resolve9(args.dir ?? ".");
|
|
3334
3719
|
const appDir = findAppDir(start);
|
|
3335
3720
|
if (!appDir) {
|
|
3336
3721
|
throw noAppDirRefusal(start);
|
|
@@ -3360,18 +3745,13 @@ var dev_default = defineDeepspaceCommand({
|
|
|
3360
3745
|
);
|
|
3361
3746
|
} else if (!args.json) {
|
|
3362
3747
|
console.warn(
|
|
3363
|
-
`Warning: could not update ${
|
|
3748
|
+
`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
3749
|
);
|
|
3365
3750
|
}
|
|
3366
3751
|
}
|
|
3367
3752
|
writeLaunchConfigIfMissing(appDir, detectAppName(appDir) ?? basename2(appDir), port, {
|
|
3368
3753
|
updatePort: explicitPort
|
|
3369
3754
|
});
|
|
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
3755
|
let token;
|
|
3376
3756
|
try {
|
|
3377
3757
|
token = await ensureToken();
|
|
@@ -3394,11 +3774,24 @@ var dev_default = defineDeepspaceCommand({
|
|
|
3394
3774
|
if (wranglerEnv) say(`Wrangler env: ${wranglerEnv}`);
|
|
3395
3775
|
say(`Port: ${port}`);
|
|
3396
3776
|
if (host !== DEFAULT_DEV_HOST) say(`Host: ${host}`);
|
|
3777
|
+
ensureInstallReady(appDir);
|
|
3778
|
+
const lintFindings = await lintProjectSchemas(appDir);
|
|
3779
|
+
if (lintFindings && lintFindings.length > 0 && !args.json) {
|
|
3780
|
+
for (const line of formatSchemaLintFindings(lintFindings)) console.warn(line);
|
|
3781
|
+
}
|
|
3397
3782
|
let generatedSecretsCache;
|
|
3398
|
-
|
|
3783
|
+
let appIdForSecrets = null;
|
|
3784
|
+
try {
|
|
3785
|
+
appIdForSecrets = (await ensureAppRegistered(appDir, token, wranglerEnv))?.appId ?? null;
|
|
3786
|
+
} catch (registrationError) {
|
|
3787
|
+
process.stderr.write(
|
|
3788
|
+
`warning: could not register the app (${registrationError instanceof Error ? registrationError.message.split("\n")[0] : String(registrationError)}) \u2014 continuing without app secrets.
|
|
3789
|
+
`
|
|
3790
|
+
);
|
|
3791
|
+
}
|
|
3399
3792
|
if (appIdForSecrets) {
|
|
3400
3793
|
try {
|
|
3401
|
-
const refreshed = await refreshSecretsCache(
|
|
3794
|
+
const refreshed = await refreshSecretsCache(DEPLOY_URL2, token, appIdForSecrets, wranglerEnv);
|
|
3402
3795
|
generatedSecretsCache = refreshed.rendered;
|
|
3403
3796
|
if (refreshed.summary) say(refreshed.summary);
|
|
3404
3797
|
} catch (err) {
|
|
@@ -3481,7 +3874,7 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
3481
3874
|
});
|
|
3482
3875
|
|
|
3483
3876
|
// src/cli/commands/kill.ts
|
|
3484
|
-
import { spawnSync as
|
|
3877
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
3485
3878
|
import { readFileSync as readFileSync10, readdirSync as readdirSync3, readlinkSync } from "fs";
|
|
3486
3879
|
import { setTimeout as wait } from "timers/promises";
|
|
3487
3880
|
var SIGTERM_GRACE_MS = 1500;
|
|
@@ -3616,7 +4009,7 @@ function listenerPids(port) {
|
|
|
3616
4009
|
}
|
|
3617
4010
|
function runLsof(port) {
|
|
3618
4011
|
try {
|
|
3619
|
-
const r =
|
|
4012
|
+
const r = spawnSync3("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], {
|
|
3620
4013
|
encoding: "utf-8"
|
|
3621
4014
|
});
|
|
3622
4015
|
if (r.error) return { pids: [], ran: false };
|
|
@@ -3701,7 +4094,7 @@ function sweepByName(names) {
|
|
|
3701
4094
|
}
|
|
3702
4095
|
function runPgrep(pgrepArgs) {
|
|
3703
4096
|
try {
|
|
3704
|
-
const r =
|
|
4097
|
+
const r = spawnSync3("pgrep", pgrepArgs, { encoding: "utf-8" });
|
|
3705
4098
|
if (r.error) return { pids: [], ran: false };
|
|
3706
4099
|
return {
|
|
3707
4100
|
pids: r.status === 0 ? parsePidLines(r.stdout) : [],
|
|
@@ -3713,7 +4106,7 @@ function runPgrep(pgrepArgs) {
|
|
|
3713
4106
|
}
|
|
3714
4107
|
function run(cmd) {
|
|
3715
4108
|
try {
|
|
3716
|
-
const r =
|
|
4109
|
+
const r = spawnSync3(cmd[0], cmd.slice(1), { encoding: "utf-8" });
|
|
3717
4110
|
if (r.status !== 0) return [];
|
|
3718
4111
|
return parsePidLines(r.stdout);
|
|
3719
4112
|
} catch {
|
|
@@ -3734,7 +4127,7 @@ function readPowershellPids(stdout) {
|
|
|
3734
4127
|
function runPowershellChecked(script) {
|
|
3735
4128
|
const exe = pwshOrPowershell();
|
|
3736
4129
|
try {
|
|
3737
|
-
const r =
|
|
4130
|
+
const r = spawnSync3(exe, ["-NoProfile", "-NonInteractive", "-Command", withSentinel(script)], {
|
|
3738
4131
|
encoding: "utf-8"
|
|
3739
4132
|
});
|
|
3740
4133
|
if (r.error) return { pids: [], ran: false };
|
|
@@ -3744,7 +4137,7 @@ function runPowershellChecked(script) {
|
|
|
3744
4137
|
}
|
|
3745
4138
|
}
|
|
3746
4139
|
function pwshOrPowershell() {
|
|
3747
|
-
const probe =
|
|
4140
|
+
const probe = spawnSync3("pwsh", ["-NoProfile", "-Command", "$PSVersionTable.PSVersion.Major"], {
|
|
3748
4141
|
encoding: "utf-8"
|
|
3749
4142
|
});
|
|
3750
4143
|
if (probe.status === 0) return "pwsh";
|
|
@@ -3772,12 +4165,12 @@ function isAlive(pid) {
|
|
|
3772
4165
|
|
|
3773
4166
|
// src/cli/commands/test.ts
|
|
3774
4167
|
import { readdirSync as readdirSync4 } from "fs";
|
|
3775
|
-
import { join as
|
|
3776
|
-
import { sync as
|
|
4168
|
+
import { join as join14, relative as relative2, resolve as resolve10, sep as sep2 } from "path";
|
|
4169
|
+
import { sync as spawnSync5 } from "cross-spawn";
|
|
3777
4170
|
|
|
3778
4171
|
// src/cli/lib/playwright.ts
|
|
3779
4172
|
import { execSync } from "child_process";
|
|
3780
|
-
import { sync as
|
|
4173
|
+
import { sync as spawnSync4 } from "cross-spawn";
|
|
3781
4174
|
var childStdoutToStderr = false;
|
|
3782
4175
|
function routeChildStdoutToStderr(enabled) {
|
|
3783
4176
|
childStdoutToStderr = enabled;
|
|
@@ -3800,7 +4193,7 @@ function ensurePlaywright(appDir) {
|
|
|
3800
4193
|
"Installing Chromium and its system libraries \u2014 this runs `apt-get` as root and can take several minutes on a cold machine."
|
|
3801
4194
|
);
|
|
3802
4195
|
}
|
|
3803
|
-
const result =
|
|
4196
|
+
const result = spawnSync4("npx", ["playwright", ...args], {
|
|
3804
4197
|
cwd: appDir,
|
|
3805
4198
|
stdio: childStdio()
|
|
3806
4199
|
});
|
|
@@ -3816,10 +4209,10 @@ function ensurePlaywright(appDir) {
|
|
|
3816
4209
|
import { readFileSync as readFileSync12 } from "fs";
|
|
3817
4210
|
|
|
3818
4211
|
// src/testing/accounts.ts
|
|
3819
|
-
import { existsSync as
|
|
4212
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11 } from "fs";
|
|
3820
4213
|
import { homedir as homedir2 } from "os";
|
|
3821
|
-
import { join as
|
|
3822
|
-
var TEST_ACCOUNTS_PATH =
|
|
4214
|
+
import { join as join13 } from "path";
|
|
4215
|
+
var TEST_ACCOUNTS_PATH = join13(homedir2(), ".deepspace", "test-accounts.json");
|
|
3823
4216
|
var PRODUCTION_AUTH_ORIGIN = "https://auth.deep.space";
|
|
3824
4217
|
var STAGING_AUTH_ORIGIN = "https://auth.deepspacesites.com";
|
|
3825
4218
|
function currentTestAccountScope(env = process.env) {
|
|
@@ -3884,7 +4277,7 @@ function removeTestAccounts(ids, emails = [], scope = currentTestAccountScope())
|
|
|
3884
4277
|
});
|
|
3885
4278
|
}
|
|
3886
4279
|
function readTestAccountStore() {
|
|
3887
|
-
if (!
|
|
4280
|
+
if (!existsSync15(TEST_ACCOUNTS_PATH)) return { version: 2, scopes: {} };
|
|
3888
4281
|
try {
|
|
3889
4282
|
const raw = JSON.parse(readFileSync11(TEST_ACCOUNTS_PATH, "utf-8"));
|
|
3890
4283
|
if (Array.isArray(raw)) return { version: 2, scopes: {} };
|
|
@@ -3909,7 +4302,7 @@ function validTestAccounts(value) {
|
|
|
3909
4302
|
);
|
|
3910
4303
|
}
|
|
3911
4304
|
function saveTestAccountStore(store) {
|
|
3912
|
-
|
|
4305
|
+
mkdirSync7(join13(homedir2(), ".deepspace"), { recursive: true });
|
|
3913
4306
|
writeSecretFileSync(TEST_ACCOUNTS_PATH, JSON.stringify(store, null, 2));
|
|
3914
4307
|
}
|
|
3915
4308
|
|
|
@@ -3987,7 +4380,7 @@ async function syncTestAccountStore() {
|
|
|
3987
4380
|
}
|
|
3988
4381
|
|
|
3989
4382
|
// src/cli/commands/test.ts
|
|
3990
|
-
var
|
|
4383
|
+
var DEPLOY_URL3 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
3991
4384
|
var PLAYWRIGHT_OUTPUT_DIR = ".deepspace/test-results";
|
|
3992
4385
|
var DEFAULT_SUITE_SPECS = ["tests/smoke.spec.ts", "tests/api.spec.ts"];
|
|
3993
4386
|
function specsSkippedByDefaultSuite(appDir) {
|
|
@@ -4001,14 +4394,14 @@ function specsSkippedByDefaultSuite(appDir) {
|
|
|
4001
4394
|
}
|
|
4002
4395
|
for (const entry of entries) {
|
|
4003
4396
|
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
4004
|
-
const full =
|
|
4397
|
+
const full = join14(dir, entry.name);
|
|
4005
4398
|
if (entry.isDirectory()) walk(full);
|
|
4006
4399
|
else if (entry.name.endsWith(".spec.ts")) {
|
|
4007
|
-
found.push(
|
|
4400
|
+
found.push(relative2(appDir, full).split(sep2).join("/"));
|
|
4008
4401
|
}
|
|
4009
4402
|
}
|
|
4010
4403
|
};
|
|
4011
|
-
walk(
|
|
4404
|
+
walk(join14(appDir, "tests"));
|
|
4012
4405
|
return found.filter((spec) => !DEFAULT_SUITE_SPECS.includes(spec)).sort();
|
|
4013
4406
|
}
|
|
4014
4407
|
function playwrightTestArgs(testFiles, flags = {}) {
|
|
@@ -4077,7 +4470,7 @@ var test_default = defineDeepspaceCommand({
|
|
|
4077
4470
|
project: typeof args.project === "string" && args.project ? args.project : void 0,
|
|
4078
4471
|
headed: Boolean(args.headed)
|
|
4079
4472
|
};
|
|
4080
|
-
const start =
|
|
4473
|
+
const start = resolve10(".");
|
|
4081
4474
|
const appDir = findAppDir(start);
|
|
4082
4475
|
if (!appDir) {
|
|
4083
4476
|
throw noAppDirRefusal(start);
|
|
@@ -4085,7 +4478,6 @@ var test_default = defineDeepspaceCommand({
|
|
|
4085
4478
|
const configuredPort = Boolean(args.port) || Boolean(process.env.DEEPSPACE_PORT);
|
|
4086
4479
|
const worktreePort = configuredPort ? null : resolveWorktreePort(appDir);
|
|
4087
4480
|
const port = worktreePort ?? resolvePort(args.port);
|
|
4088
|
-
ensureInstallReady(appDir);
|
|
4089
4481
|
let token;
|
|
4090
4482
|
let ownerId;
|
|
4091
4483
|
try {
|
|
@@ -4099,11 +4491,20 @@ var test_default = defineDeepspaceCommand({
|
|
|
4099
4491
|
{ action: cliAction("deepspace", "auth", "login") }
|
|
4100
4492
|
);
|
|
4101
4493
|
}
|
|
4494
|
+
ensureInstallReady(appDir);
|
|
4102
4495
|
let generatedSecretsCache;
|
|
4103
|
-
|
|
4496
|
+
let appIdForSecrets = null;
|
|
4497
|
+
try {
|
|
4498
|
+
appIdForSecrets = (await ensureAppRegistered(appDir, token, wranglerEnv))?.appId ?? null;
|
|
4499
|
+
} catch (registrationError) {
|
|
4500
|
+
process.stderr.write(
|
|
4501
|
+
`warning: could not register the app (${registrationError instanceof Error ? registrationError.message.split("\n")[0] : String(registrationError)}) \u2014 continuing without app secrets.
|
|
4502
|
+
`
|
|
4503
|
+
);
|
|
4504
|
+
}
|
|
4104
4505
|
if (appIdForSecrets) {
|
|
4105
4506
|
try {
|
|
4106
|
-
const refreshed = await refreshSecretsCache(
|
|
4507
|
+
const refreshed = await refreshSecretsCache(DEPLOY_URL3, token, appIdForSecrets, wranglerEnv);
|
|
4107
4508
|
generatedSecretsCache = refreshed.rendered;
|
|
4108
4509
|
if (refreshed.summary) say(refreshed.summary);
|
|
4109
4510
|
} catch (err) {
|
|
@@ -4129,6 +4530,7 @@ var test_default = defineDeepspaceCommand({
|
|
|
4129
4530
|
if (suite !== "unit") {
|
|
4130
4531
|
preflightWindowsWorkerd(appDir);
|
|
4131
4532
|
ensurePlaywright(appDir);
|
|
4533
|
+
await assertReusableDevServer(port);
|
|
4132
4534
|
}
|
|
4133
4535
|
let exitCode = 0;
|
|
4134
4536
|
let skippedSpecs = [];
|
|
@@ -4189,7 +4591,7 @@ function runSuite(appDir, argv, wranglerEnv, extraEnv = {}) {
|
|
|
4189
4591
|
return 1;
|
|
4190
4592
|
}
|
|
4191
4593
|
try {
|
|
4192
|
-
const result =
|
|
4594
|
+
const result = spawnSync5("npx", argv, {
|
|
4193
4595
|
cwd: appDir,
|
|
4194
4596
|
// Under --json the child's stdout is routed to our stderr: the live
|
|
4195
4597
|
// suite output stays visible and stdout stays a single JSON line.
|
|
@@ -4212,8 +4614,8 @@ function runVitest(appDir, wranglerEnv) {
|
|
|
4212
4614
|
}
|
|
4213
4615
|
|
|
4214
4616
|
// src/cli/commands/screenshot.ts
|
|
4215
|
-
import { sync as
|
|
4216
|
-
import { resolve as
|
|
4617
|
+
import { sync as spawnSync6 } from "cross-spawn";
|
|
4618
|
+
import { resolve as resolve11 } from "path";
|
|
4217
4619
|
function normalizeViewportSize(value) {
|
|
4218
4620
|
if (value === void 0) return void 0;
|
|
4219
4621
|
const match = /^(\d+)\s*[x,]\s*(\d+)$/i.exec(String(value).trim());
|
|
@@ -4261,7 +4663,7 @@ var screenshot_default = defineDeepspaceCommand({
|
|
|
4261
4663
|
},
|
|
4262
4664
|
async run({ args }) {
|
|
4263
4665
|
const viewport = normalizeViewportSize(args.viewport);
|
|
4264
|
-
const appDir =
|
|
4666
|
+
const appDir = resolve11(".");
|
|
4265
4667
|
ensurePlaywright(appDir);
|
|
4266
4668
|
const url = String(args.url);
|
|
4267
4669
|
const output = String(args.output);
|
|
@@ -4271,7 +4673,7 @@ var screenshot_default = defineDeepspaceCommand({
|
|
|
4271
4673
|
if (args["wait-for-timeout"] !== void 0) {
|
|
4272
4674
|
playwrightArgs.push("--wait-for-timeout", String(args["wait-for-timeout"]));
|
|
4273
4675
|
}
|
|
4274
|
-
const result =
|
|
4676
|
+
const result = spawnSync6("npx", playwrightArgs, {
|
|
4275
4677
|
cwd: appDir,
|
|
4276
4678
|
stdio: "inherit"
|
|
4277
4679
|
});
|
|
@@ -4303,8 +4705,13 @@ var create = defineDeepspaceCommand({
|
|
|
4303
4705
|
},
|
|
4304
4706
|
password: {
|
|
4305
4707
|
type: "string",
|
|
4306
|
-
description: "Password (min 8 characters)",
|
|
4307
|
-
required:
|
|
4708
|
+
description: "Password (min 8 characters). Prefer --password-stdin \u2014 argv is visible in `ps` and shell history",
|
|
4709
|
+
required: false
|
|
4710
|
+
},
|
|
4711
|
+
"password-stdin": {
|
|
4712
|
+
type: "boolean",
|
|
4713
|
+
description: "Read the password from stdin (one trailing newline stripped)",
|
|
4714
|
+
default: false
|
|
4308
4715
|
},
|
|
4309
4716
|
name: {
|
|
4310
4717
|
type: "string",
|
|
@@ -4320,7 +4727,26 @@ var create = defineDeepspaceCommand({
|
|
|
4320
4727
|
async run({ args }) {
|
|
4321
4728
|
await ensureToken();
|
|
4322
4729
|
const email = args.email;
|
|
4323
|
-
|
|
4730
|
+
if (args["password-stdin"] && args.password !== void 0) {
|
|
4731
|
+
throw new Refusal("Pass either --password or --password-stdin, not both.", "invalid_flags");
|
|
4732
|
+
}
|
|
4733
|
+
let password;
|
|
4734
|
+
if (args["password-stdin"]) {
|
|
4735
|
+
if (process.stdin.isTTY) {
|
|
4736
|
+
throw new Refusal(
|
|
4737
|
+
'--password-stdin expects the password piped on stdin, e.g. `printf %s "$PW" | deepspace test accounts create --email bot@deepspace.test --password-stdin`.',
|
|
4738
|
+
"password_stdin_needs_pipe"
|
|
4739
|
+
);
|
|
4740
|
+
}
|
|
4741
|
+
password = (await readStreamText(process.stdin, MAX_STDIN_BYTES)).replace(/\r?\n$/, "");
|
|
4742
|
+
} else if (typeof args.password === "string") {
|
|
4743
|
+
password = args.password;
|
|
4744
|
+
} else {
|
|
4745
|
+
throw new Refusal(
|
|
4746
|
+
"A password is required: pipe it with --password-stdin (preferred), or pass --password.",
|
|
4747
|
+
"missing_password"
|
|
4748
|
+
);
|
|
4749
|
+
}
|
|
4324
4750
|
const name = args.name ?? email.split("@")[0];
|
|
4325
4751
|
const label = args.label;
|
|
4326
4752
|
let account;
|
|
@@ -4688,7 +5114,7 @@ var test_accounts_default = defineCommand2({
|
|
|
4688
5114
|
// src/cli/commands/deploy.ts
|
|
4689
5115
|
import * as p12 from "@clack/prompts";
|
|
4690
5116
|
import { defineCommand as defineCommand3 } from "citty";
|
|
4691
|
-
import { join as
|
|
5117
|
+
import { join as join21, resolve as resolve15 } from "path";
|
|
4692
5118
|
|
|
4693
5119
|
// src/cli/lib/edge-propagation.ts
|
|
4694
5120
|
import { request as httpRequest } from "http";
|
|
@@ -4852,11 +5278,11 @@ function describeFilesFailure(status3, body) {
|
|
|
4852
5278
|
import * as p6 from "@clack/prompts";
|
|
4853
5279
|
import { execSync as execSync2 } from "child_process";
|
|
4854
5280
|
import { createHash as createHash2 } from "crypto";
|
|
4855
|
-
import { existsSync as
|
|
4856
|
-
import { dirname as dirname7, isAbsolute as
|
|
5281
|
+
import { existsSync as existsSync19, readFileSync as readFileSync15, readdirSync as readdirSync6 } from "fs";
|
|
5282
|
+
import { dirname as dirname7, isAbsolute as isAbsolute4, join as join18, relative as relative3, resolve as resolve14, sep as sep3 } from "path";
|
|
4857
5283
|
|
|
4858
5284
|
// src/build/app-id.ts
|
|
4859
|
-
import { resolve as
|
|
5285
|
+
import { resolve as resolve12 } from "path";
|
|
4860
5286
|
var APP_ID_ADOPTION_EDITS = {
|
|
4861
5287
|
"vite.config.ts": `import { deepspaceBuild } from 'deepspace/build' and add
|
|
4862
5288
|
deepspaceBuild({ appDir: fileURLToPath(new URL('.', import.meta.url)) }) to plugins`,
|
|
@@ -4870,11 +5296,11 @@ var APP_ID_ADOPTION_STEPS = `Point the client at the build-injected id \u2014 th
|
|
|
4870
5296
|
`).join("") + APP_ID_ADOPTION_TOGETHER;
|
|
4871
5297
|
|
|
4872
5298
|
// src/build/plugin.ts
|
|
4873
|
-
import { existsSync as
|
|
4874
|
-
import { join as
|
|
5299
|
+
import { existsSync as existsSync16, lstatSync, readdirSync as readdirSync5, unlinkSync as unlinkSync2 } from "fs";
|
|
5300
|
+
import { join as join15, resolve as resolve13 } from "path";
|
|
4875
5301
|
function removeBuildDevVars(workerDir) {
|
|
4876
|
-
const path =
|
|
4877
|
-
if (!
|
|
5302
|
+
const path = join15(workerDir, ".dev.vars");
|
|
5303
|
+
if (!existsSync16(path)) return false;
|
|
4878
5304
|
if (!lstatSync(path).isFile()) {
|
|
4879
5305
|
throw new Error(`Refusing unsafe build secret path: ${path}`);
|
|
4880
5306
|
}
|
|
@@ -4883,12 +5309,12 @@ function removeBuildDevVars(workerDir) {
|
|
|
4883
5309
|
}
|
|
4884
5310
|
|
|
4885
5311
|
// src/documentation/deploy.ts
|
|
4886
|
-
import { existsSync as
|
|
4887
|
-
import { join as
|
|
5312
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
5313
|
+
import { join as join16 } from "path";
|
|
4888
5314
|
function readDocumentationDeployManifest(appDir, clientDir) {
|
|
4889
|
-
if (!
|
|
4890
|
-
const manifestPath =
|
|
4891
|
-
if (!
|
|
5315
|
+
if (!existsSync17(join16(appDir, "documentation.json"))) return void 0;
|
|
5316
|
+
const manifestPath = join16(clientDir, "_documentation", "manifest.json");
|
|
5317
|
+
if (!existsSync17(manifestPath)) {
|
|
4892
5318
|
throw new Error(
|
|
4893
5319
|
"documentation.json exists but the build produced no documentation manifest. Ensure vite.config.ts includes deepSpaceDocumentation() from deepspace/documentation."
|
|
4894
5320
|
);
|
|
@@ -4897,8 +5323,8 @@ function readDocumentationDeployManifest(appDir, clientDir) {
|
|
|
4897
5323
|
}
|
|
4898
5324
|
|
|
4899
5325
|
// src/cli/commands/update/app-migrations.ts
|
|
4900
|
-
import { existsSync as
|
|
4901
|
-
import { join as
|
|
5326
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
|
|
5327
|
+
import { join as join17 } from "path";
|
|
4902
5328
|
|
|
4903
5329
|
// src/shared/protocol/app-migrations.ts
|
|
4904
5330
|
var APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
@@ -4972,8 +5398,8 @@ var APP_MIGRATION_DEFINITIONS = APP_MIGRATION_GUIDANCE.map(({ id, description })
|
|
|
4972
5398
|
description
|
|
4973
5399
|
}));
|
|
4974
5400
|
function readAppliedAppMigrations(appDir) {
|
|
4975
|
-
const path =
|
|
4976
|
-
if (!
|
|
5401
|
+
const path = join17(appDir, APP_MIGRATIONS_MANIFEST);
|
|
5402
|
+
if (!existsSync18(path)) return [];
|
|
4977
5403
|
let parsed;
|
|
4978
5404
|
try {
|
|
4979
5405
|
parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
@@ -5035,16 +5461,16 @@ async function buildDeployBundle(options) {
|
|
|
5035
5461
|
preparedWranglerConfig?.cleanup();
|
|
5036
5462
|
}
|
|
5037
5463
|
spinner2.stop(`Built (${Math.round((Date.now() - buildStarted) / 1e3)}s)`);
|
|
5038
|
-
const deployConfigPath =
|
|
5039
|
-
if (!
|
|
5464
|
+
const deployConfigPath = join18(appDir, ".wrangler", "deploy", "config.json");
|
|
5465
|
+
if (!existsSync19(deployConfigPath)) {
|
|
5040
5466
|
output.die(
|
|
5041
5467
|
"Build output config not found at .wrangler/deploy/config.json",
|
|
5042
5468
|
"build_output_missing"
|
|
5043
5469
|
);
|
|
5044
5470
|
}
|
|
5045
5471
|
const deployConfig = JSON.parse(readFileSync15(deployConfigPath, "utf-8"));
|
|
5046
|
-
const outputWranglerPath =
|
|
5047
|
-
if (!
|
|
5472
|
+
const outputWranglerPath = resolve14(dirname7(deployConfigPath), deployConfig.configPath);
|
|
5473
|
+
if (!existsSync19(outputWranglerPath)) {
|
|
5048
5474
|
output.die(`Output wrangler.json not found at ${outputWranglerPath}`, "build_output_missing");
|
|
5049
5475
|
}
|
|
5050
5476
|
const outputConfig = JSON.parse(readFileSync15(outputWranglerPath, "utf-8"));
|
|
@@ -5061,12 +5487,12 @@ async function buildDeployBundle(options) {
|
|
|
5061
5487
|
} catch (error) {
|
|
5062
5488
|
output.die(errorMessage(error), "build_output_unsafe");
|
|
5063
5489
|
}
|
|
5064
|
-
const workerBundlePath =
|
|
5065
|
-
const clientDir = outputConfig.assets?.directory ?
|
|
5066
|
-
if (!
|
|
5490
|
+
const workerBundlePath = join18(workerDir, outputConfig.main);
|
|
5491
|
+
const clientDir = outputConfig.assets?.directory ? resolve14(workerDir, outputConfig.assets.directory) : null;
|
|
5492
|
+
if (!existsSync19(workerBundlePath)) {
|
|
5067
5493
|
output.die(`Worker bundle not found at ${workerBundlePath}`, "build_output_missing");
|
|
5068
5494
|
}
|
|
5069
|
-
if (!clientDir || !
|
|
5495
|
+
if (!clientDir || !existsSync19(clientDir)) {
|
|
5070
5496
|
return output.die(`Client assets not found at ${clientDir}`, "build_output_missing");
|
|
5071
5497
|
}
|
|
5072
5498
|
try {
|
|
@@ -5134,16 +5560,16 @@ async function buildDeployBundle(options) {
|
|
|
5134
5560
|
}
|
|
5135
5561
|
var WORKER_ES_MODULE = /\.(?:js|mjs)$/i;
|
|
5136
5562
|
function collectWorkerBundle(workerDir, mainPath, clientDir) {
|
|
5137
|
-
const root =
|
|
5138
|
-
const mainAbsolute =
|
|
5139
|
-
const excludedClient = clientDir ?
|
|
5563
|
+
const root = resolve14(workerDir);
|
|
5564
|
+
const mainAbsolute = resolve14(mainPath);
|
|
5565
|
+
const excludedClient = clientDir ? resolve14(clientDir) : null;
|
|
5140
5566
|
const main2 = moduleName(root, mainAbsolute);
|
|
5141
5567
|
const modules = [];
|
|
5142
5568
|
const walk = (directory) => {
|
|
5143
5569
|
for (const entry of readdirSync6(directory, { withFileTypes: true }).sort(
|
|
5144
5570
|
(a, b) => a.name.localeCompare(b.name)
|
|
5145
5571
|
)) {
|
|
5146
|
-
const fullPath =
|
|
5572
|
+
const fullPath = join18(directory, entry.name);
|
|
5147
5573
|
if (excludedClient && isWithin(excludedClient, fullPath)) continue;
|
|
5148
5574
|
if (entry.isDirectory()) {
|
|
5149
5575
|
walk(fullPath);
|
|
@@ -5159,15 +5585,15 @@ function collectWorkerBundle(workerDir, mainPath, clientDir) {
|
|
|
5159
5585
|
return { main: main2, modules: [mainModule, ...modules] };
|
|
5160
5586
|
}
|
|
5161
5587
|
function moduleName(root, path) {
|
|
5162
|
-
const name =
|
|
5163
|
-
if (!name ||
|
|
5588
|
+
const name = relative3(root, path);
|
|
5589
|
+
if (!name || isAbsolute4(name) || name === ".." || name.startsWith(`..${sep3}`)) {
|
|
5164
5590
|
throw new Error(`Worker module is outside the generated output directory: ${path}`);
|
|
5165
5591
|
}
|
|
5166
5592
|
return name.split(sep3).join("/");
|
|
5167
5593
|
}
|
|
5168
5594
|
function isWithin(root, path) {
|
|
5169
|
-
const name =
|
|
5170
|
-
return name === "" || !
|
|
5595
|
+
const name = relative3(root, path);
|
|
5596
|
+
return name === "" || !isAbsolute4(name) && name !== ".." && !name.startsWith(`..${sep3}`);
|
|
5171
5597
|
}
|
|
5172
5598
|
function extractRunWorkerFirst(config) {
|
|
5173
5599
|
const raw = Array.isArray(config.assets?.run_worker_first) ? config.assets.run_worker_first : [];
|
|
@@ -5229,7 +5655,7 @@ function collectAssets(dir) {
|
|
|
5229
5655
|
const walk = (currentDir, prefix) => {
|
|
5230
5656
|
for (const entry of readdirSync6(currentDir, { withFileTypes: true })) {
|
|
5231
5657
|
if (isDeployAssetControlFile(entry.name)) continue;
|
|
5232
|
-
const fullPath =
|
|
5658
|
+
const fullPath = join18(currentDir, entry.name);
|
|
5233
5659
|
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
5234
5660
|
if (entry.isDirectory()) {
|
|
5235
5661
|
walk(fullPath, relativePath);
|
|
@@ -5253,8 +5679,8 @@ function readDeployAssetConfig(clientDir) {
|
|
|
5253
5679
|
["_headers", "_headers"],
|
|
5254
5680
|
["_redirects", "_redirects"]
|
|
5255
5681
|
]) {
|
|
5256
|
-
const filePath =
|
|
5257
|
-
if (!
|
|
5682
|
+
const filePath = join18(clientDir, file);
|
|
5683
|
+
if (!existsSync19(filePath)) continue;
|
|
5258
5684
|
const content = readFileSync15(filePath, "utf8");
|
|
5259
5685
|
if (content.trim()) config[key] = content;
|
|
5260
5686
|
}
|
|
@@ -5269,8 +5695,8 @@ function errorMessage(error) {
|
|
|
5269
5695
|
|
|
5270
5696
|
// src/cli/commands/deploy/commerce.ts
|
|
5271
5697
|
import * as p7 from "@clack/prompts";
|
|
5272
|
-
import { existsSync as
|
|
5273
|
-
import { join as
|
|
5698
|
+
import { existsSync as existsSync20 } from "fs";
|
|
5699
|
+
import { join as join19 } from "path";
|
|
5274
5700
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5275
5701
|
|
|
5276
5702
|
// src/shared/api-error.ts
|
|
@@ -5322,10 +5748,10 @@ function errorMessage2(error) {
|
|
|
5322
5748
|
return error instanceof Error ? error.message : String(error);
|
|
5323
5749
|
}
|
|
5324
5750
|
async function loadDeclaration(appDir, sourceFile, outputFile, exportName) {
|
|
5325
|
-
const sourcePath =
|
|
5326
|
-
if (!
|
|
5751
|
+
const sourcePath = join19(appDir, "src", sourceFile);
|
|
5752
|
+
if (!existsSync20(sourcePath)) return void 0;
|
|
5327
5753
|
const esbuild = await import("esbuild");
|
|
5328
|
-
const outputPath =
|
|
5754
|
+
const outputPath = join19(appDir, ".wrangler", "deploy", outputFile);
|
|
5329
5755
|
await esbuild.build({
|
|
5330
5756
|
entryPoints: [sourcePath],
|
|
5331
5757
|
bundle: true,
|
|
@@ -5418,7 +5844,7 @@ Stripe will keep charging them until you cancel. Cancel at the end of their curr
|
|
|
5418
5844
|
}
|
|
5419
5845
|
}
|
|
5420
5846
|
async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
5421
|
-
if (!
|
|
5847
|
+
if (!existsSync20(join19(appDir, "src", "subscriptions.ts"))) return;
|
|
5422
5848
|
let plans;
|
|
5423
5849
|
try {
|
|
5424
5850
|
plans = await loadDeclaration(
|
|
@@ -5473,7 +5899,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
|
5473
5899
|
}
|
|
5474
5900
|
}
|
|
5475
5901
|
async function syncOneTimeProducts(appDir, appName, token) {
|
|
5476
|
-
if (!
|
|
5902
|
+
if (!existsSync20(join19(appDir, "src", "products.ts"))) return;
|
|
5477
5903
|
let products;
|
|
5478
5904
|
try {
|
|
5479
5905
|
products = await loadDeclaration(
|
|
@@ -5573,189 +5999,18 @@ function createDeployOutput(json) {
|
|
|
5573
5999
|
else console.error(withSlug(message, code));
|
|
5574
6000
|
if (action) printAction(action);
|
|
5575
6001
|
}
|
|
5576
|
-
throw new CliExit(opts.actionRequired ? 2 : 1);
|
|
5577
|
-
}
|
|
5578
|
-
};
|
|
5579
|
-
}
|
|
5580
|
-
|
|
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;
|
|
6002
|
+
throw new CliExit(opts.actionRequired ? 2 : 1);
|
|
6003
|
+
}
|
|
6004
|
+
};
|
|
5757
6005
|
}
|
|
5758
6006
|
|
|
6007
|
+
// src/cli/commands/deploy/request.ts
|
|
6008
|
+
import * as p10 from "@clack/prompts";
|
|
6009
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
6010
|
+
|
|
6011
|
+
// src/cli/commands/deploy/repository.ts
|
|
6012
|
+
import * as p9 from "@clack/prompts";
|
|
6013
|
+
|
|
5759
6014
|
// src/cli/lib/git/safety.ts
|
|
5760
6015
|
var SECRET_FILE_RE = /(^|\/)(\.dev\.vars(\.[^/]*)?|\.env(\.[^/]*)?|\.npmrc|\.envrc|\.mcp\.json)$/i;
|
|
5761
6016
|
var SECRET_TEMPLATE_RE = /\.(example|sample|template)$/i;
|
|
@@ -6196,41 +6451,6 @@ function selectGitHubRemote(cwd, options = {}) {
|
|
|
6196
6451
|
const matching = repository ? remotes.filter((remote) => remote.repository === repository) : remotes;
|
|
6197
6452
|
return matching.find((remote) => remote.name === "origin") ?? (matching.length === 1 ? matching[0] : null);
|
|
6198
6453
|
}
|
|
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
6454
|
|
|
6235
6455
|
// src/cli/commands/deploy/repository.ts
|
|
6236
6456
|
function describeWorktree(appDir) {
|
|
@@ -6240,17 +6460,27 @@ function describeWorktree(appDir) {
|
|
|
6240
6460
|
return { branch: null, dirty: null };
|
|
6241
6461
|
}
|
|
6242
6462
|
}
|
|
6463
|
+
function externalGitSource(appDir, source) {
|
|
6464
|
+
if (source?.provider === "github") return true;
|
|
6465
|
+
if (source !== null) return false;
|
|
6466
|
+
try {
|
|
6467
|
+
return listGitHubRemotes(appDir).length > 0;
|
|
6468
|
+
} catch {
|
|
6469
|
+
return false;
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
function observedGitHubRepository(appDir) {
|
|
6473
|
+
try {
|
|
6474
|
+
return selectGitHubRemote(appDir)?.repository ?? listGitHubRemotes(appDir)[0]?.repository ?? null;
|
|
6475
|
+
} catch {
|
|
6476
|
+
return null;
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6243
6479
|
function preflightDeployRepository(options) {
|
|
6244
6480
|
const { appDir, push, source } = options;
|
|
6245
|
-
if (!push || source
|
|
6481
|
+
if (!push || externalGitSource(appDir, source)) return null;
|
|
6246
6482
|
try {
|
|
6247
6483
|
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
6484
|
assertNoOperationInProgress(appDir);
|
|
6255
6485
|
const branch = currentBranch(appDir);
|
|
6256
6486
|
if (!isWorkTreeClean(appDir)) return dirtyWorktreeRefusal(branch, appDir);
|
|
@@ -6267,9 +6497,18 @@ async function syncDeployRepository(options) {
|
|
|
6267
6497
|
const deployKey = mintIdempotencyKey();
|
|
6268
6498
|
let source = sourceState.source;
|
|
6269
6499
|
let sourceRevision = sourceState.revision;
|
|
6270
|
-
const baseReleaseId = null;
|
|
6271
6500
|
const worktree = describeWorktree(appDir);
|
|
6272
|
-
|
|
6501
|
+
const external = externalGitSource(appDir, source);
|
|
6502
|
+
if (external) {
|
|
6503
|
+
if (sourceState.source?.provider === "github") {
|
|
6504
|
+
try {
|
|
6505
|
+
if (removeSpaceRemote(appDir)) {
|
|
6506
|
+
p9.log.info(`Removed the stale \`space\` git remote \u2014 GitHub owns this app's source.`);
|
|
6507
|
+
}
|
|
6508
|
+
} catch {
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
const observedRepository = source === null ? observedGitHubRepository(appDir) : null;
|
|
6273
6512
|
const where = `GitHub source: shipping the working tree of ${worktree.branch ?? "(detached HEAD)"}`;
|
|
6274
6513
|
if (worktree.dirty) {
|
|
6275
6514
|
p9.log.warn(
|
|
@@ -6278,7 +6517,7 @@ async function syncDeployRepository(options) {
|
|
|
6278
6517
|
} else {
|
|
6279
6518
|
p9.log.info(`${where} (clean). GitHub-source releases record no commit lineage.`);
|
|
6280
6519
|
}
|
|
6281
|
-
return { commitOid, recoverable, deployKey, source, sourceRevision,
|
|
6520
|
+
return { commitOid, recoverable, deployKey, source, sourceRevision, observedRepository, ...worktree };
|
|
6282
6521
|
}
|
|
6283
6522
|
if (!push) {
|
|
6284
6523
|
try {
|
|
@@ -6286,7 +6525,12 @@ async function syncDeployRepository(options) {
|
|
|
6286
6525
|
commitOid = resolveCommit(appDir, "HEAD");
|
|
6287
6526
|
} catch {
|
|
6288
6527
|
}
|
|
6289
|
-
|
|
6528
|
+
if (worktree.dirty) {
|
|
6529
|
+
p9.log.warn(
|
|
6530
|
+
`--no-push: shipping the working tree WITH uncommitted changes \u2014 the recorded commit ${commitOid ? commitOid.slice(0, 10) : ""} is not exactly what went live.`
|
|
6531
|
+
);
|
|
6532
|
+
}
|
|
6533
|
+
return { commitOid, recoverable, deployKey, source, sourceRevision, observedRepository: null, ...worktree };
|
|
6290
6534
|
}
|
|
6291
6535
|
try {
|
|
6292
6536
|
const preflight = preflightDeployRepository({ appDir, push, source });
|
|
@@ -6302,7 +6546,7 @@ async function syncDeployRepository(options) {
|
|
|
6302
6546
|
}
|
|
6303
6547
|
const tip = resolveCommit(appDir, `refs/heads/${branch}`);
|
|
6304
6548
|
if (!tip) {
|
|
6305
|
-
return { commitOid, recoverable, deployKey, source, sourceRevision,
|
|
6549
|
+
return { commitOid, recoverable, deployKey, source, sourceRevision, observedRepository: null, ...worktree };
|
|
6306
6550
|
}
|
|
6307
6551
|
const secretFiles = workspaceBranchId ? [] : trackedSecretFiles(appDir, tip);
|
|
6308
6552
|
if (workspaceBranchId) {
|
|
@@ -6331,6 +6575,11 @@ async function syncDeployRepository(options) {
|
|
|
6331
6575
|
`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
6576
|
);
|
|
6333
6577
|
} else {
|
|
6578
|
+
if (sourceState.source === null) {
|
|
6579
|
+
process.stderr.write(
|
|
6580
|
+
"This app's source is unclaimed \u2014 this deploy's push claims DeepSpace source for it, permanently.\n"
|
|
6581
|
+
);
|
|
6582
|
+
}
|
|
6334
6583
|
const pushResult = await pushWithTransientRetry(
|
|
6335
6584
|
() => pushToSpace(appDir, token, `refs/heads/${branch}:refs/heads/${branch}`, {
|
|
6336
6585
|
remote: sourceRemote
|
|
@@ -6391,7 +6640,7 @@ async function syncDeployRepository(options) {
|
|
|
6391
6640
|
const failure = deployRepositoryFailure(error, appDir);
|
|
6392
6641
|
output.die(failure.error, failure.code);
|
|
6393
6642
|
}
|
|
6394
|
-
return { commitOid, recoverable, deployKey, source, sourceRevision,
|
|
6643
|
+
return { commitOid, recoverable, deployKey, source, sourceRevision, observedRepository: null, ...worktree };
|
|
6395
6644
|
}
|
|
6396
6645
|
function classifyRemoteState(appDir, token, branch, localTip, remote) {
|
|
6397
6646
|
let remoteTip = null;
|
|
@@ -6617,7 +6866,10 @@ async function deployBuiltBundle(options) {
|
|
|
6617
6866
|
form.append("sourceRepository", repository.source.repository);
|
|
6618
6867
|
}
|
|
6619
6868
|
}
|
|
6620
|
-
if (repository.
|
|
6869
|
+
if (repository.dirty !== null) form.append("sourceDirty", repository.dirty ? "true" : "false");
|
|
6870
|
+
if (repository.observedRepository) {
|
|
6871
|
+
form.append("observedRepository", repository.observedRepository);
|
|
6872
|
+
}
|
|
6621
6873
|
if (ignoreStale) form.append("ignoreStale", "true");
|
|
6622
6874
|
form.append("deployKey", repository.deployKey);
|
|
6623
6875
|
return form;
|
|
@@ -6951,21 +7203,21 @@ function errorMessage5(error) {
|
|
|
6951
7203
|
|
|
6952
7204
|
// src/cli/commands/deploy/lock.ts
|
|
6953
7205
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6954
|
-
import { mkdirSync as
|
|
6955
|
-
import { dirname as dirname8, join as
|
|
7206
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
7207
|
+
import { dirname as dirname8, join as join20 } from "path";
|
|
6956
7208
|
function deployLockPath(appDir) {
|
|
6957
|
-
return
|
|
7209
|
+
return join20(appDir, ".deepspace", "deploy.lock");
|
|
6958
7210
|
}
|
|
6959
7211
|
function acquireDeployLock(appDir) {
|
|
6960
7212
|
const path = deployLockPath(appDir);
|
|
6961
|
-
|
|
7213
|
+
mkdirSync8(dirname8(path), { recursive: true });
|
|
6962
7214
|
const record = {
|
|
6963
7215
|
pid: process.pid,
|
|
6964
7216
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6965
7217
|
token: randomUUID2()
|
|
6966
7218
|
};
|
|
6967
7219
|
try {
|
|
6968
|
-
|
|
7220
|
+
writeFileSync6(path, JSON.stringify(record), { flag: "wx" });
|
|
6969
7221
|
} catch (err) {
|
|
6970
7222
|
if (err.code !== "EEXIST") throw err;
|
|
6971
7223
|
const holder = readLock(path);
|
|
@@ -6981,7 +7233,7 @@ function acquireDeployLock(appDir) {
|
|
|
6981
7233
|
);
|
|
6982
7234
|
}
|
|
6983
7235
|
return () => {
|
|
6984
|
-
if (readLock(path)?.token === record.token)
|
|
7236
|
+
if (readLock(path)?.token === record.token) rmSync6(path, { force: true });
|
|
6985
7237
|
};
|
|
6986
7238
|
}
|
|
6987
7239
|
function readLock(path) {
|
|
@@ -6994,7 +7246,7 @@ function readLock(path) {
|
|
|
6994
7246
|
}
|
|
6995
7247
|
|
|
6996
7248
|
// src/cli/commands/deploy.ts
|
|
6997
|
-
var
|
|
7249
|
+
var DEPLOY_URL4 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
6998
7250
|
var DEPLOY_TOKEN_MIN_VALIDITY_MS = 10 * 60 * 1e3;
|
|
6999
7251
|
var POST_DEPLOY_TOKEN_MIN_VALIDITY_MS = 2 * 60 * 1e3;
|
|
7000
7252
|
var deploy_default = defineCommand3({
|
|
@@ -7067,15 +7319,7 @@ async function runDeploy(args, output) {
|
|
|
7067
7319
|
if (selectorRefusal) output.die(selectorRefusal.error, selectorRefusal.code);
|
|
7068
7320
|
const envName = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
7069
7321
|
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
|
-
});
|
|
7322
|
+
output.die(noWranglerConfigMessage(join21(appDir, "wrangler.toml")), "not_in_app_repo");
|
|
7079
7323
|
}
|
|
7080
7324
|
const releaseLock = acquireDeployLock(appDir);
|
|
7081
7325
|
try {
|
|
@@ -7099,24 +7343,30 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
7099
7343
|
);
|
|
7100
7344
|
}
|
|
7101
7345
|
const appName = nameResult.name;
|
|
7102
|
-
|
|
7346
|
+
let appId = readAppId(appDir, envName);
|
|
7103
7347
|
p12.log.info(envName ? `App: ${appName} (env: ${envName})` : `App: ${appName}`);
|
|
7104
7348
|
const { token, ownerId } = await authenticate(appDir, output);
|
|
7349
|
+
try {
|
|
7350
|
+
ensureInstallReady(appDir);
|
|
7351
|
+
} catch (error) {
|
|
7352
|
+
const refusal = error;
|
|
7353
|
+
return output.die(refusal.message ?? String(error), refusal.code ?? "install_failed", {
|
|
7354
|
+
action: refusal.action
|
|
7355
|
+
});
|
|
7356
|
+
}
|
|
7105
7357
|
if (!appId) {
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
actionRequired: true
|
|
7115
|
-
}
|
|
7116
|
-
);
|
|
7358
|
+
const ensured = await ensureAppRegistered(appDir, token, envName, { commitScaffold: true });
|
|
7359
|
+
if (!ensured) {
|
|
7360
|
+
return output.die(
|
|
7361
|
+
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.",
|
|
7362
|
+
envName ? "no_app_id_for_env" : "app_not_initialized"
|
|
7363
|
+
);
|
|
7364
|
+
}
|
|
7365
|
+
appId = ensured.appId;
|
|
7117
7366
|
}
|
|
7367
|
+
commitScaffoldIfUnborn(appDir, token);
|
|
7118
7368
|
p12.log.info(`Id: ${appId}`);
|
|
7119
|
-
const sourceState = await getAppSource(
|
|
7369
|
+
const sourceState = await getAppSource(DEPLOY_URL4, token, appId).catch((error) => {
|
|
7120
7370
|
if (errorCode(error) === "forbidden") {
|
|
7121
7371
|
output.die(forbiddenDeployMessage(appId, token), "forbidden");
|
|
7122
7372
|
}
|
|
@@ -7129,7 +7379,7 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
7129
7379
|
);
|
|
7130
7380
|
}
|
|
7131
7381
|
const secretsCache = await loadDeploySecrets({
|
|
7132
|
-
deployUrl:
|
|
7382
|
+
deployUrl: DEPLOY_URL4,
|
|
7133
7383
|
appDir,
|
|
7134
7384
|
appId,
|
|
7135
7385
|
envName,
|
|
@@ -7173,7 +7423,7 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
7173
7423
|
}
|
|
7174
7424
|
const { token: deployToken } = await authenticate(appDir, output, DEPLOY_TOKEN_MIN_VALIDITY_MS);
|
|
7175
7425
|
const repository = await syncDeployRepository({
|
|
7176
|
-
deployUrl:
|
|
7426
|
+
deployUrl: DEPLOY_URL4,
|
|
7177
7427
|
appDir,
|
|
7178
7428
|
appId,
|
|
7179
7429
|
token: deployToken,
|
|
@@ -7189,7 +7439,7 @@ async function runLockedDeploy(args, output, appDir, envName) {
|
|
|
7189
7439
|
output
|
|
7190
7440
|
});
|
|
7191
7441
|
const body = await deployBuiltBundle({
|
|
7192
|
-
deployUrl:
|
|
7442
|
+
deployUrl: DEPLOY_URL4,
|
|
7193
7443
|
appDir,
|
|
7194
7444
|
appId,
|
|
7195
7445
|
appName,
|
|
@@ -7342,7 +7592,7 @@ async function authenticate(appDir, output, minimumValidityMs) {
|
|
|
7342
7592
|
|
|
7343
7593
|
// src/cli/commands/undeploy.ts
|
|
7344
7594
|
import * as p13 from "@clack/prompts";
|
|
7345
|
-
var
|
|
7595
|
+
var DEPLOY_URL5 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
7346
7596
|
var undeploy_default = defineDeepspaceCommand({
|
|
7347
7597
|
meta: {
|
|
7348
7598
|
name: "undeploy",
|
|
@@ -7361,7 +7611,8 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
7361
7611
|
},
|
|
7362
7612
|
yes: {
|
|
7363
7613
|
type: "boolean",
|
|
7364
|
-
|
|
7614
|
+
alias: "y",
|
|
7615
|
+
description: "Skip the confirmation (required for --json / non-interactive)",
|
|
7365
7616
|
default: false
|
|
7366
7617
|
}
|
|
7367
7618
|
},
|
|
@@ -7379,7 +7630,7 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
7379
7630
|
let label;
|
|
7380
7631
|
const positional = typeof args.name === "string" && args.name.trim() ? args.name.trim() : void 0;
|
|
7381
7632
|
if (positional) {
|
|
7382
|
-
appId = await resolveAppSelector(
|
|
7633
|
+
appId = await resolveAppSelector(DEPLOY_URL5, token, positional);
|
|
7383
7634
|
label = positional;
|
|
7384
7635
|
} else if (hasWranglerConfig(process.cwd())) {
|
|
7385
7636
|
appId = readAppId(process.cwd(), envName) ?? void 0;
|
|
@@ -7393,6 +7644,13 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
7393
7644
|
);
|
|
7394
7645
|
}
|
|
7395
7646
|
const target2 = label && label !== appId ? `${label} (${appId})` : appId;
|
|
7647
|
+
if (args.yes !== true && (args.json === true || !process.stdin.isTTY)) {
|
|
7648
|
+
throw new Refusal(
|
|
7649
|
+
`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.`,
|
|
7650
|
+
"confirmation_required",
|
|
7651
|
+
{ extra: { appId } }
|
|
7652
|
+
);
|
|
7653
|
+
}
|
|
7396
7654
|
if (!args.json && !args.yes && process.stdin.isTTY) {
|
|
7397
7655
|
p13.intro(`Undeploying ${target2}`);
|
|
7398
7656
|
const confirmed = await p13.confirm({
|
|
@@ -7407,7 +7665,7 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
7407
7665
|
}
|
|
7408
7666
|
const s = args.json ? null : createSpinner();
|
|
7409
7667
|
s?.start("Removing...");
|
|
7410
|
-
const res = await fetch(`${
|
|
7668
|
+
const res = await fetch(`${DEPLOY_URL5}/api/deploy/${appId}`, {
|
|
7411
7669
|
method: "DELETE",
|
|
7412
7670
|
headers: { Authorization: `Bearer ${token}` }
|
|
7413
7671
|
});
|
|
@@ -7492,7 +7750,7 @@ var whoami_default = defineDeepspaceCommand({
|
|
|
7492
7750
|
});
|
|
7493
7751
|
|
|
7494
7752
|
// src/cli/commands/apps.ts
|
|
7495
|
-
var
|
|
7753
|
+
var DEPLOY_URL6 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
7496
7754
|
var apps_default = defineDeepspaceCommand({
|
|
7497
7755
|
meta: {
|
|
7498
7756
|
name: "apps",
|
|
@@ -7500,7 +7758,7 @@ var apps_default = defineDeepspaceCommand({
|
|
|
7500
7758
|
},
|
|
7501
7759
|
async run({ args }) {
|
|
7502
7760
|
const token = await ensureToken();
|
|
7503
|
-
const { apps, pendingTransfers } = await listAppsPage(
|
|
7761
|
+
const { apps, pendingTransfers } = await listAppsPage(DEPLOY_URL6, token);
|
|
7504
7762
|
if (!args.json) {
|
|
7505
7763
|
if (!apps.length && !pendingTransfers.length) {
|
|
7506
7764
|
console.log("No apps yet. Create one with `npx create-deepspace <name>` and `deepspace deploy`.");
|
|
@@ -7558,7 +7816,7 @@ import { setTimeout as delay } from "timers/promises";
|
|
|
7558
7816
|
var LOG_LEVELS = ["debug", "log", "info", "warn", "error"];
|
|
7559
7817
|
|
|
7560
7818
|
// src/cli/commands/logs.ts
|
|
7561
|
-
var
|
|
7819
|
+
var DEPLOY_URL7 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
7562
7820
|
var RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
7563
7821
|
var DEFAULT_WINDOW_MS = 15 * 60 * 1e3;
|
|
7564
7822
|
var MIN_POLL_MS = 2500;
|
|
@@ -7719,7 +7977,7 @@ var logs_default = defineCommand4({
|
|
|
7719
7977
|
const envArg = args.env;
|
|
7720
7978
|
assertAppTargetResolvable(args.app, { wranglerEnv: envArg });
|
|
7721
7979
|
const token = await ensureToken();
|
|
7722
|
-
const appId = await resolveAppTarget(
|
|
7980
|
+
const appId = await resolveAppTarget(DEPLOY_URL7, token, args.app, { wranglerEnv: envArg });
|
|
7723
7981
|
if (args.level && !LOG_LEVELS.includes(args.level)) {
|
|
7724
7982
|
throw new InputError(
|
|
7725
7983
|
`Invalid --level "${args.level}". Use: ${LOG_LEVELS.join(", ")}`,
|
|
@@ -7738,7 +7996,7 @@ var logs_default = defineCommand4({
|
|
|
7738
7996
|
}
|
|
7739
7997
|
const initialSince = args.since ? parseSince(args.since) : Date.now() - DEFAULT_WINDOW_MS;
|
|
7740
7998
|
const windowLabel = args.since?.trim() || "15m";
|
|
7741
|
-
const registryRow = (await listApps(
|
|
7999
|
+
const registryRow = (await listApps(DEPLOY_URL7, token)).find((app2) => app2.appId === appId);
|
|
7742
8000
|
if (registryRow && registryRow.deployedAt === null) {
|
|
7743
8001
|
const appDir = args.app === void 0 ? findAppDir() : null;
|
|
7744
8002
|
throw new Refusal(
|
|
@@ -7760,7 +8018,7 @@ var logs_default = defineCommand4({
|
|
|
7760
8018
|
if (args.level) params.set("level", args.level);
|
|
7761
8019
|
if (args.search) params.set("search", args.search);
|
|
7762
8020
|
const path = `/api/apps/${appId}/logs?${params}`;
|
|
7763
|
-
return signal ? apiFetch(
|
|
8021
|
+
return signal ? apiFetch(DEPLOY_URL7, token2, path, { signal }) : apiFetchReadWithRetry(DEPLOY_URL7, token2, path);
|
|
7764
8022
|
};
|
|
7765
8023
|
const print = (events) => {
|
|
7766
8024
|
for (const e of events) {
|
|
@@ -7960,35 +8218,35 @@ var usage_default = defineDeepspaceCommand({
|
|
|
7960
8218
|
});
|
|
7961
8219
|
|
|
7962
8220
|
// src/cli/commands/add.ts
|
|
7963
|
-
import { existsSync as
|
|
7964
|
-
import { dirname as dirname10, join as
|
|
8221
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, realpathSync as realpathSync4 } from "fs";
|
|
8222
|
+
import { dirname as dirname10, join as join23, resolve as resolve17 } from "path";
|
|
7965
8223
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
7966
8224
|
|
|
7967
8225
|
// src/cli/commands/feature-installer.ts
|
|
7968
8226
|
import {
|
|
7969
|
-
closeSync,
|
|
8227
|
+
closeSync as closeSync2,
|
|
7970
8228
|
copyFileSync,
|
|
7971
|
-
existsSync as
|
|
8229
|
+
existsSync as existsSync21,
|
|
7972
8230
|
fstatSync,
|
|
7973
|
-
mkdirSync as
|
|
8231
|
+
mkdirSync as mkdirSync9,
|
|
7974
8232
|
mkdtempSync,
|
|
7975
|
-
openSync,
|
|
8233
|
+
openSync as openSync2,
|
|
7976
8234
|
readFileSync as readFileSync18,
|
|
7977
8235
|
readSync,
|
|
7978
8236
|
readdirSync as readdirSync7,
|
|
7979
|
-
rmSync as
|
|
7980
|
-
writeFileSync as
|
|
8237
|
+
rmSync as rmSync7,
|
|
8238
|
+
writeFileSync as writeFileSync7
|
|
7981
8239
|
} from "fs";
|
|
7982
|
-
import { spawnSync as
|
|
8240
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
7983
8241
|
import { tmpdir as tmpdir2 } from "os";
|
|
7984
|
-
import { dirname as dirname9, isAbsolute as isAbsolute5, join as
|
|
8242
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join22, resolve as resolve16 } from "path";
|
|
7985
8243
|
import { fileURLToPath } from "url";
|
|
7986
8244
|
var NAV_MARKER = "// \u2500\u2500 Features add nav items below this line \u2500\u2500";
|
|
7987
8245
|
function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
7988
8246
|
let dir = dirname9(fileURLToPath(moduleUrl));
|
|
7989
8247
|
for (; ; ) {
|
|
7990
|
-
const packagePath =
|
|
7991
|
-
if (
|
|
8248
|
+
const packagePath = join22(dir, "package.json");
|
|
8249
|
+
if (existsSync21(packagePath)) {
|
|
7992
8250
|
try {
|
|
7993
8251
|
const pkg2 = JSON.parse(readFileSync18(packagePath, "utf-8"));
|
|
7994
8252
|
if (pkg2.name === "deepspace") return dir;
|
|
@@ -8001,11 +8259,11 @@ function featurePackageRoot(moduleUrl = import.meta.url) {
|
|
|
8001
8259
|
}
|
|
8002
8260
|
}
|
|
8003
8261
|
function hasFeatureCatalog(packageRoot = featurePackageRoot()) {
|
|
8004
|
-
return
|
|
8262
|
+
return existsSync21(join22(packageRoot, "features"));
|
|
8005
8263
|
}
|
|
8006
8264
|
function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
8007
|
-
const configPath =
|
|
8008
|
-
if (!
|
|
8265
|
+
const configPath = join22(packageRoot, "features", id, "feature.json");
|
|
8266
|
+
if (!existsSync21(configPath)) return null;
|
|
8009
8267
|
try {
|
|
8010
8268
|
const config = JSON.parse(readFileSync18(configPath, "utf-8"));
|
|
8011
8269
|
if (typeof config.id !== "string" || typeof config.name !== "string" || !Array.isArray(config.files)) {
|
|
@@ -8017,8 +8275,8 @@ function readFeatureConfig(id, packageRoot = featurePackageRoot()) {
|
|
|
8017
8275
|
}
|
|
8018
8276
|
}
|
|
8019
8277
|
function listFeatureConfigs(packageRoot = featurePackageRoot()) {
|
|
8020
|
-
const featuresDir =
|
|
8021
|
-
if (!
|
|
8278
|
+
const featuresDir = join22(packageRoot, "features");
|
|
8279
|
+
if (!existsSync21(featuresDir)) return [];
|
|
8022
8280
|
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
8281
|
}
|
|
8024
8282
|
function featureCatalog(packageRoot = featurePackageRoot()) {
|
|
@@ -8066,17 +8324,17 @@ function resolveFeatureDestPath(fileDest, route) {
|
|
|
8066
8324
|
return route?.protected === false ? `${appPages}${rest}` : `${appPages}(protected)/${rest}`;
|
|
8067
8325
|
}
|
|
8068
8326
|
function copyFile(src, dest) {
|
|
8069
|
-
|
|
8327
|
+
mkdirSync9(dirname9(dest), { recursive: true });
|
|
8070
8328
|
copyFileSync(src, dest);
|
|
8071
8329
|
}
|
|
8072
8330
|
function copyFileWithImportShift(src, dest, levels) {
|
|
8073
|
-
|
|
8331
|
+
mkdirSync9(dirname9(dest), { recursive: true });
|
|
8074
8332
|
const prefix = "../".repeat(levels);
|
|
8075
8333
|
const shifted = readFileSync18(src, "utf-8").replace(
|
|
8076
8334
|
/(from\s+|import\s+|import\()(['"])(\.\.\/)/g,
|
|
8077
8335
|
`$1$2${prefix}$3`
|
|
8078
8336
|
);
|
|
8079
|
-
|
|
8337
|
+
writeFileSync7(dest, shifted);
|
|
8080
8338
|
}
|
|
8081
8339
|
function installFiles(packageRoot, config, targetDir, emit) {
|
|
8082
8340
|
let copied = 0;
|
|
@@ -8085,12 +8343,12 @@ function installFiles(packageRoot, config, targetDir, emit) {
|
|
|
8085
8343
|
const preexisting = [];
|
|
8086
8344
|
const missing = [];
|
|
8087
8345
|
for (const file of config.files) {
|
|
8088
|
-
const srcPath =
|
|
8346
|
+
const srcPath = join22(packageRoot, "features", config.id, file.src);
|
|
8089
8347
|
const resolvedDest = resolveFeatureDestPath(file.dest, config.route);
|
|
8090
|
-
const destPath =
|
|
8091
|
-
const existed =
|
|
8348
|
+
const destPath = join22(targetDir, resolvedDest);
|
|
8349
|
+
const existed = existsSync21(destPath);
|
|
8092
8350
|
if (existed) preexisting.push(resolvedDest);
|
|
8093
|
-
if (!
|
|
8351
|
+
if (!existsSync21(srcPath)) {
|
|
8094
8352
|
emit(` Warning: source not found: ${file.src}`, "stderr");
|
|
8095
8353
|
if (!existed) missing.push(resolvedDest);
|
|
8096
8354
|
continue;
|
|
@@ -8127,8 +8385,8 @@ function unresolvedSchema(schema, reason, emit) {
|
|
|
8127
8385
|
}
|
|
8128
8386
|
function integrateSchema(config, targetDir, emit) {
|
|
8129
8387
|
if (!config.schema) return [];
|
|
8130
|
-
const schemasPath =
|
|
8131
|
-
if (!
|
|
8388
|
+
const schemasPath = join22(targetDir, "src", "schemas.ts");
|
|
8389
|
+
if (!existsSync21(schemasPath)) {
|
|
8132
8390
|
return [unresolvedSchema(config.schema, "src/schemas.ts was not found.", emit)];
|
|
8133
8391
|
}
|
|
8134
8392
|
let content = readFileSync18(schemasPath, "utf-8");
|
|
@@ -8162,7 +8420,7 @@ export const schemas`
|
|
|
8162
8420
|
return [unresolvedSchema(config.schema, reason, emit)];
|
|
8163
8421
|
}
|
|
8164
8422
|
content = withEntry;
|
|
8165
|
-
|
|
8423
|
+
writeFileSync7(schemasPath, content);
|
|
8166
8424
|
emit(` Schema integrated: ${exportName} -> schemas.ts`);
|
|
8167
8425
|
return [];
|
|
8168
8426
|
}
|
|
@@ -8184,8 +8442,8 @@ function unresolvedActions(actions, reason, emit) {
|
|
|
8184
8442
|
}
|
|
8185
8443
|
function integrateActions(config, targetDir, emit) {
|
|
8186
8444
|
if (!config.actions?.length) return [];
|
|
8187
|
-
const actionsPath =
|
|
8188
|
-
if (!
|
|
8445
|
+
const actionsPath = join22(targetDir, "src", "actions", "index.ts");
|
|
8446
|
+
if (!existsSync21(actionsPath)) {
|
|
8189
8447
|
return [unresolvedActions(config.actions, "src/actions/index.ts was not found.", emit)];
|
|
8190
8448
|
}
|
|
8191
8449
|
let content = readFileSync18(actionsPath, "utf-8");
|
|
@@ -8228,14 +8486,14 @@ ${registryEntry}`);
|
|
|
8228
8486
|
integrated++;
|
|
8229
8487
|
emit(` Action integrated: ${action.registerAs} (${action.exportName})`);
|
|
8230
8488
|
}
|
|
8231
|
-
if (integrated > 0)
|
|
8489
|
+
if (integrated > 0) writeFileSync7(actionsPath, content);
|
|
8232
8490
|
return unresolved;
|
|
8233
8491
|
}
|
|
8234
8492
|
function integrateCode(config, targetDir, emit) {
|
|
8235
8493
|
const unresolved = [];
|
|
8236
8494
|
for (const insertion of config.code ?? []) {
|
|
8237
|
-
const targetPath =
|
|
8238
|
-
if (!
|
|
8495
|
+
const targetPath = join22(targetDir, insertion.file);
|
|
8496
|
+
if (!existsSync21(targetPath)) {
|
|
8239
8497
|
unresolved.push({
|
|
8240
8498
|
area: "code",
|
|
8241
8499
|
reason: `${insertion.file} was not found.`,
|
|
@@ -8273,32 +8531,32 @@ ${content.slice(
|
|
|
8273
8531
|
const prefix = insertion.placement === "before" ? "" : "\n";
|
|
8274
8532
|
const suffix = insertion.placement === "before" ? "\n" : "";
|
|
8275
8533
|
content = `${content.slice(0, insertAt)}${prefix}${insertion.insert}${suffix}${content.slice(insertAt)}`;
|
|
8276
|
-
|
|
8534
|
+
writeFileSync7(targetPath, content);
|
|
8277
8535
|
emit(` Integrated: ${insertion.file}`);
|
|
8278
8536
|
}
|
|
8279
8537
|
return unresolved;
|
|
8280
8538
|
}
|
|
8281
8539
|
function integrateIgnore(config, targetDir) {
|
|
8282
8540
|
if (!config.ignore?.length) return;
|
|
8283
|
-
const targetPath =
|
|
8284
|
-
const current =
|
|
8541
|
+
const targetPath = join22(targetDir, ".gitignore");
|
|
8542
|
+
const current = existsSync21(targetPath) ? readFileSync18(targetPath, "utf-8") : "";
|
|
8285
8543
|
const present = new Set(current.split(/\r?\n/).map((line) => line.trim()));
|
|
8286
8544
|
const missing = config.ignore.filter((pattern) => !present.has(pattern));
|
|
8287
8545
|
if (missing.length === 0) return;
|
|
8288
8546
|
const separator = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
8289
|
-
|
|
8547
|
+
writeFileSync7(targetPath, `${current}${separator}${missing.join("\n")}
|
|
8290
8548
|
`);
|
|
8291
8549
|
}
|
|
8292
8550
|
function integrateWrangler(config, targetDir, emit) {
|
|
8293
8551
|
const runWorkerFirst = config.wrangler?.runWorkerFirst === true;
|
|
8294
8552
|
const rateLimits = config.wrangler?.rateLimits ?? [];
|
|
8295
8553
|
if (!runWorkerFirst && rateLimits.length === 0) return [];
|
|
8296
|
-
const targetPath =
|
|
8554
|
+
const targetPath = join22(targetDir, "wrangler.toml");
|
|
8297
8555
|
const steps = [
|
|
8298
8556
|
...runWorkerFirst ? ["Set assets.run_worker_first = true in wrangler.toml."] : [],
|
|
8299
8557
|
...rateLimits.map((binding) => `Add the ${binding.name} rate-limit binding to wrangler.toml.`)
|
|
8300
8558
|
];
|
|
8301
|
-
if (!
|
|
8559
|
+
if (!existsSync21(targetPath)) {
|
|
8302
8560
|
return [{ area: "wrangler", reason: "wrangler.toml was not found.", steps }];
|
|
8303
8561
|
}
|
|
8304
8562
|
let source = readFileSync18(targetPath, "utf-8");
|
|
@@ -8323,7 +8581,7 @@ ${rateLimitBindingBlock(scope, binding)}
|
|
|
8323
8581
|
}
|
|
8324
8582
|
}
|
|
8325
8583
|
if (changed) {
|
|
8326
|
-
|
|
8584
|
+
writeFileSync7(targetPath, source);
|
|
8327
8585
|
emit(" Integrated: wrangler.toml");
|
|
8328
8586
|
} else emit(" Integration already present: wrangler.toml");
|
|
8329
8587
|
return [];
|
|
@@ -8377,8 +8635,8 @@ function rateLimitBindingBlock(scope, binding) {
|
|
|
8377
8635
|
}
|
|
8378
8636
|
function integrateCss(packageRoot, config, targetDir, emit) {
|
|
8379
8637
|
if (!config.css?.length) return [];
|
|
8380
|
-
const stylesPath =
|
|
8381
|
-
if (!
|
|
8638
|
+
const stylesPath = join22(targetDir, "src", "styles.css");
|
|
8639
|
+
if (!existsSync21(stylesPath)) {
|
|
8382
8640
|
emit(" Warning: Cannot integrate CSS \u2014 src/styles.css not found");
|
|
8383
8641
|
return [
|
|
8384
8642
|
{
|
|
@@ -8394,8 +8652,8 @@ function integrateCss(packageRoot, config, targetDir, emit) {
|
|
|
8394
8652
|
let integrated = 0;
|
|
8395
8653
|
const unresolved = [];
|
|
8396
8654
|
for (const cssFile of config.css) {
|
|
8397
|
-
const srcPath =
|
|
8398
|
-
if (!
|
|
8655
|
+
const srcPath = join22(packageRoot, "features", config.id, cssFile);
|
|
8656
|
+
if (!existsSync21(srcPath)) {
|
|
8399
8657
|
emit(` Warning: CSS source not found: ${cssFile}`, "stderr");
|
|
8400
8658
|
unresolved.push({
|
|
8401
8659
|
area: "css",
|
|
@@ -8416,7 +8674,7 @@ ${css}`;
|
|
|
8416
8674
|
integrated++;
|
|
8417
8675
|
emit(` CSS integrated: ${cssFile} -> styles.css`);
|
|
8418
8676
|
}
|
|
8419
|
-
if (integrated > 0)
|
|
8677
|
+
if (integrated > 0) writeFileSync7(stylesPath, styles);
|
|
8420
8678
|
return unresolved;
|
|
8421
8679
|
}
|
|
8422
8680
|
function unresolvedRoute(route, config, reason, emit) {
|
|
@@ -8438,8 +8696,8 @@ function routeRolesSuffix(route) {
|
|
|
8438
8696
|
}
|
|
8439
8697
|
function integrateRoute(config, targetDir, emit) {
|
|
8440
8698
|
if (!config.route) return [];
|
|
8441
|
-
const navPath =
|
|
8442
|
-
if (!
|
|
8699
|
+
const navPath = join22(targetDir, "src", "nav.ts");
|
|
8700
|
+
if (!existsSync21(navPath)) {
|
|
8443
8701
|
return [unresolvedRoute(config.route, config, "src/nav.ts was not found.", emit)];
|
|
8444
8702
|
}
|
|
8445
8703
|
let content = readFileSync18(navPath, "utf-8");
|
|
@@ -8463,7 +8721,7 @@ function integrateRoute(config, targetDir, emit) {
|
|
|
8463
8721
|
const entry = ` { path: '${routePath}', label: '${config.name}'${roles} },`;
|
|
8464
8722
|
content = content.replace(NAV_MARKER, `${NAV_MARKER}
|
|
8465
8723
|
${entry}`);
|
|
8466
|
-
|
|
8724
|
+
writeFileSync7(navPath, content);
|
|
8467
8725
|
emit(` Nav wired: ${routePath} (${config.name})`);
|
|
8468
8726
|
emit(" Route: automatic (file-based routing via src/pages/)");
|
|
8469
8727
|
return [];
|
|
@@ -8486,14 +8744,14 @@ function runPackageInstall(packageManager, targetDir, outputVisible) {
|
|
|
8486
8744
|
shell: process.platform === "win32"
|
|
8487
8745
|
};
|
|
8488
8746
|
if (outputVisible) {
|
|
8489
|
-
const child =
|
|
8747
|
+
const child = spawnSync7(packageManager, ["install"], { ...options, stdio: "inherit" });
|
|
8490
8748
|
return { error: child.error, status: child.status };
|
|
8491
8749
|
}
|
|
8492
|
-
const captureDir = mkdtempSync(
|
|
8750
|
+
const captureDir = mkdtempSync(join22(tmpdir2(), "deepspace-add-install-"));
|
|
8493
8751
|
let outputFd;
|
|
8494
8752
|
try {
|
|
8495
|
-
outputFd =
|
|
8496
|
-
const child =
|
|
8753
|
+
outputFd = openSync2(join22(captureDir, "output.log"), "w+");
|
|
8754
|
+
const child = spawnSync7(packageManager, ["install"], {
|
|
8497
8755
|
...options,
|
|
8498
8756
|
stdio: ["ignore", outputFd, outputFd]
|
|
8499
8757
|
});
|
|
@@ -8503,8 +8761,8 @@ function runPackageInstall(packageManager, targetDir, outputVisible) {
|
|
|
8503
8761
|
diagnosticTail: readDiagnosticTail(outputFd)
|
|
8504
8762
|
};
|
|
8505
8763
|
} finally {
|
|
8506
|
-
if (outputFd !== void 0)
|
|
8507
|
-
|
|
8764
|
+
if (outputFd !== void 0) closeSync2(outputFd);
|
|
8765
|
+
rmSync7(captureDir, { recursive: true, force: true });
|
|
8508
8766
|
}
|
|
8509
8767
|
}
|
|
8510
8768
|
function integrateDependencies(config, targetDir, runInstall, outputVisible, emit) {
|
|
@@ -8516,8 +8774,8 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
|
|
|
8516
8774
|
if (declarations.every(({ values }) => !values || Object.keys(values).length === 0)) {
|
|
8517
8775
|
return { added: [], installRan: false, packageManager };
|
|
8518
8776
|
}
|
|
8519
|
-
const packagePath =
|
|
8520
|
-
if (!
|
|
8777
|
+
const packagePath = join22(targetDir, "package.json");
|
|
8778
|
+
if (!existsSync21(packagePath)) {
|
|
8521
8779
|
emit(" Could not patch package.json (file not found)");
|
|
8522
8780
|
return { added: [], installRan: false, packageManager };
|
|
8523
8781
|
}
|
|
@@ -8547,7 +8805,7 @@ function integrateDependencies(config, targetDir, runInstall, outputVisible, emi
|
|
|
8547
8805
|
if (added.length === 0) {
|
|
8548
8806
|
emit(" Dependencies already present");
|
|
8549
8807
|
} else {
|
|
8550
|
-
|
|
8808
|
+
writeFileSync7(packagePath, `${JSON.stringify(pkg2, null, 2)}
|
|
8551
8809
|
`, "utf-8");
|
|
8552
8810
|
emit(` Added to package.json: ${added.map(({ name }) => name).join(", ")}`);
|
|
8553
8811
|
if (skipped.length) emit(` Already present (kept your version): ${skipped.join(", ")}`);
|
|
@@ -8583,7 +8841,7 @@ ${child.diagnosticTail}` : "";
|
|
|
8583
8841
|
}
|
|
8584
8842
|
function dependencySnapshot(targetDir) {
|
|
8585
8843
|
try {
|
|
8586
|
-
const pkg2 = JSON.parse(readFileSync18(
|
|
8844
|
+
const pkg2 = JSON.parse(readFileSync18(join22(targetDir, "package.json"), "utf-8"));
|
|
8587
8845
|
return {
|
|
8588
8846
|
dependencies: pkg2.dependencies ?? {},
|
|
8589
8847
|
devDependencies: pkg2.devDependencies ?? {}
|
|
@@ -8595,7 +8853,7 @@ function dependencySnapshot(targetDir) {
|
|
|
8595
8853
|
function installedNavFeatures(packageRoot, targetDir) {
|
|
8596
8854
|
return listFeatureConfigs(packageRoot).filter((feature) => feature.category === "nav" && feature.files.length > 0).filter((feature) => {
|
|
8597
8855
|
const signature = resolveFeatureDestPath(feature.files[0].dest, feature.route);
|
|
8598
|
-
return
|
|
8856
|
+
return existsSync21(join22(targetDir, signature));
|
|
8599
8857
|
}).map((feature) => feature.id);
|
|
8600
8858
|
}
|
|
8601
8859
|
function warnNavConflicts(packageRoot, config, targetDir, emit) {
|
|
@@ -8828,9 +9086,9 @@ function renderFeatureInfo(config) {
|
|
|
8828
9086
|
return lines.join("\n");
|
|
8829
9087
|
}
|
|
8830
9088
|
function isDeepSpaceApp(dir) {
|
|
8831
|
-
if (
|
|
8832
|
-
const packagePath =
|
|
8833
|
-
if (!
|
|
9089
|
+
if (existsSync22(join23(dir, "wrangler.toml"))) return true;
|
|
9090
|
+
const packagePath = join23(dir, "package.json");
|
|
9091
|
+
if (!existsSync22(packagePath)) return false;
|
|
8834
9092
|
try {
|
|
8835
9093
|
const pkg2 = JSON.parse(readFileSync19(packagePath, "utf-8"));
|
|
8836
9094
|
return Boolean(pkg2.dependencies?.deepspace || pkg2.devDependencies?.deepspace);
|
|
@@ -8841,8 +9099,8 @@ function isDeepSpaceApp(dir) {
|
|
|
8841
9099
|
function installedPackageRoot(fromDir) {
|
|
8842
9100
|
let dir = fromDir;
|
|
8843
9101
|
for (; ; ) {
|
|
8844
|
-
const candidate =
|
|
8845
|
-
if (
|
|
9102
|
+
const candidate = join23(dir, "node_modules", "deepspace");
|
|
9103
|
+
if (existsSync22(join23(candidate, "package.json"))) return candidate;
|
|
8846
9104
|
const parent = dirname10(dir);
|
|
8847
9105
|
if (parent === dir) return null;
|
|
8848
9106
|
dir = parent;
|
|
@@ -8859,9 +9117,9 @@ async function targetInstaller(appDir) {
|
|
|
8859
9117
|
if (realpathSync4(installedRoot) === realpathSync4(featurePackageRoot())) {
|
|
8860
9118
|
return { installFeature };
|
|
8861
9119
|
}
|
|
8862
|
-
const entry =
|
|
8863
|
-
const catalog =
|
|
8864
|
-
if (!
|
|
9120
|
+
const entry = join23(installedRoot, "dist", "feature-installer.js");
|
|
9121
|
+
const catalog = join23(installedRoot, "features");
|
|
9122
|
+
if (!existsSync22(entry) || !existsSync22(catalog)) {
|
|
8865
9123
|
throw new Refusal(
|
|
8866
9124
|
`The app's installed deepspace package does not contain the typed feature installer.
|
|
8867
9125
|
Expected: ${entry}
|
|
@@ -8954,7 +9212,7 @@ var add_default = defineDeepspaceCommand({
|
|
|
8954
9212
|
return { data: { features: featureCatalog(root) } };
|
|
8955
9213
|
}
|
|
8956
9214
|
const appDir = resolve17(args.dir ?? ".");
|
|
8957
|
-
if (!
|
|
9215
|
+
if (!existsSync22(appDir)) {
|
|
8958
9216
|
throw new Refusal(
|
|
8959
9217
|
`Target directory not found: ${appDir}
|
|
8960
9218
|
Create a new app first: deepspace app create <name>`,
|
|
@@ -9012,7 +9270,7 @@ Create one with: deepspace app create <name>`,
|
|
|
9012
9270
|
// src/cli/commands/domain.ts
|
|
9013
9271
|
import { defineCommand as defineCommand5 } from "citty";
|
|
9014
9272
|
var API_URL4 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
9015
|
-
var
|
|
9273
|
+
var DEPLOY_URL8 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9016
9274
|
var api = (token, path, init) => apiFetch(API_URL4, token, path, init);
|
|
9017
9275
|
function scrubInternal(value) {
|
|
9018
9276
|
if (Array.isArray(value)) return value.map(scrubInternal);
|
|
@@ -9093,7 +9351,7 @@ var buy = defineDeepspaceCommand({
|
|
|
9093
9351
|
async run({ args }) {
|
|
9094
9352
|
const domain = String(args.domain);
|
|
9095
9353
|
const token = await ensureToken();
|
|
9096
|
-
const appId = await resolveAppTarget(
|
|
9354
|
+
const appId = await resolveAppTarget(DEPLOY_URL8, token, args.app);
|
|
9097
9355
|
const priceCheck = await api(token, "/api/domains/check", {
|
|
9098
9356
|
method: "POST",
|
|
9099
9357
|
body: JSON.stringify({ domains: [domain] })
|
|
@@ -9246,7 +9504,7 @@ var attach = defineDeepspaceCommand({
|
|
|
9246
9504
|
const domain = String(args.domain);
|
|
9247
9505
|
const token = await ensureToken();
|
|
9248
9506
|
const targetAppId = await resolveAppTarget(
|
|
9249
|
-
|
|
9507
|
+
DEPLOY_URL8,
|
|
9250
9508
|
token,
|
|
9251
9509
|
args.app
|
|
9252
9510
|
);
|
|
@@ -9330,10 +9588,10 @@ var domain_default = defineCommand5({
|
|
|
9330
9588
|
// src/cli/commands/app-files.ts
|
|
9331
9589
|
import { defineCommand as defineCommand6 } from "citty";
|
|
9332
9590
|
import { basename as basename4, resolve as resolve18 } from "path";
|
|
9333
|
-
import { existsSync as
|
|
9591
|
+
import { existsSync as existsSync23, statSync as statSync4 } from "fs";
|
|
9334
9592
|
|
|
9335
9593
|
// src/cli/lib/app-files-api.ts
|
|
9336
|
-
import { createWriteStream, openAsBlob, statSync } from "fs";
|
|
9594
|
+
import { createWriteStream, openAsBlob, statSync as statSync3 } from "fs";
|
|
9337
9595
|
import { basename as basename3, extname } from "path";
|
|
9338
9596
|
import { Readable } from "stream";
|
|
9339
9597
|
import { pipeline } from "stream/promises";
|
|
@@ -9443,7 +9701,7 @@ Content-Type: ${contentType}\r
|
|
|
9443
9701
|
}
|
|
9444
9702
|
async function uploadAppFile(baseUrl, token, appId, localPath, key, onProgress) {
|
|
9445
9703
|
const path = filesPath(appId, `/upload?key=${encodeKeyPath(key)}`);
|
|
9446
|
-
const size =
|
|
9704
|
+
const size = statSync3(localPath).size;
|
|
9447
9705
|
if (size > MAX_APP_FILE_BYTES) {
|
|
9448
9706
|
throw new ApiError(oversizeMessage(size), 0, "too_large", path);
|
|
9449
9707
|
}
|
|
@@ -9561,11 +9819,11 @@ async function downloadAppFile(baseUrl, token, appId, key, destination) {
|
|
|
9561
9819
|
}
|
|
9562
9820
|
|
|
9563
9821
|
// src/cli/commands/app-files.ts
|
|
9564
|
-
var
|
|
9822
|
+
var DEPLOY_URL9 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9565
9823
|
var PLATFORM_URL = process.env.DEEPSPACE_PLATFORM_URL ?? PLATFORM_URLS.platform;
|
|
9566
9824
|
async function target(app2) {
|
|
9567
9825
|
const token = await ensureToken();
|
|
9568
|
-
const appId = await resolveAppTarget(
|
|
9826
|
+
const appId = await resolveAppTarget(DEPLOY_URL9, token, app2);
|
|
9569
9827
|
return { token, appId };
|
|
9570
9828
|
}
|
|
9571
9829
|
var MAX_LIST_LIMIT = 1e3;
|
|
@@ -9609,7 +9867,7 @@ var put = defineDeepspaceCommand({
|
|
|
9609
9867
|
},
|
|
9610
9868
|
async run({ args }) {
|
|
9611
9869
|
const localPath = resolve18(String(args.file));
|
|
9612
|
-
if (!
|
|
9870
|
+
if (!existsSync23(localPath) || !statSync4(localPath).isFile()) {
|
|
9613
9871
|
throw new Refusal(`No such file: ${args.file}`, "file_not_found");
|
|
9614
9872
|
}
|
|
9615
9873
|
const key = requireKey(args.key ?? basename4(localPath));
|
|
@@ -9752,7 +10010,7 @@ function isTestAccountEmail(email) {
|
|
|
9752
10010
|
|
|
9753
10011
|
// src/cli/commands/collaborators.ts
|
|
9754
10012
|
var API_URL5 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
9755
|
-
var
|
|
10013
|
+
var DEPLOY_URL10 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9756
10014
|
var COLLABORATOR_GRANTS = ["deploy", "secrets:read", "secrets:write"];
|
|
9757
10015
|
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
10016
|
function api2(token, path, init) {
|
|
@@ -9765,7 +10023,7 @@ var list4 = defineDeepspaceCommand({
|
|
|
9765
10023
|
},
|
|
9766
10024
|
async run({ args }) {
|
|
9767
10025
|
const token = await ensureToken();
|
|
9768
|
-
const app2 = await resolveAppTarget(
|
|
10026
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
9769
10027
|
const { collaborators, pending = [] } = await api2(token, `/api/app-collaborators/${encodeURIComponent(app2)}`);
|
|
9770
10028
|
if (!collaborators.length && !pending.length) {
|
|
9771
10029
|
if (!args.json) {
|
|
@@ -9782,8 +10040,8 @@ var list4 = defineDeepspaceCommand({
|
|
|
9782
10040
|
}
|
|
9783
10041
|
if (pending.length) {
|
|
9784
10042
|
console.log(`PENDING INVITES ON ${app2}`);
|
|
9785
|
-
for (const
|
|
9786
|
-
console.log(` ${
|
|
10043
|
+
for (const p35 of pending) {
|
|
10044
|
+
console.log(` ${p35.email} (expires ${new Date(p35.expiresAt).toLocaleDateString()})`);
|
|
9787
10045
|
}
|
|
9788
10046
|
}
|
|
9789
10047
|
}
|
|
@@ -9806,7 +10064,7 @@ var add = defineDeepspaceCommand({
|
|
|
9806
10064
|
async run({ args }) {
|
|
9807
10065
|
const email = String(args.email);
|
|
9808
10066
|
const token = await ensureToken();
|
|
9809
|
-
const app2 = await resolveAppTarget(
|
|
10067
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
9810
10068
|
if (isTestAccountEmail(email)) {
|
|
9811
10069
|
throw new Refusal(
|
|
9812
10070
|
"Test accounts (@deepspace.test) cannot be added as collaborators. Use a real DeepSpace account.",
|
|
@@ -9853,7 +10111,7 @@ var remove = defineDeepspaceCommand({
|
|
|
9853
10111
|
async run({ args }) {
|
|
9854
10112
|
const email = String(args.email);
|
|
9855
10113
|
const token = await ensureToken();
|
|
9856
|
-
const app2 = await resolveAppTarget(
|
|
10114
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
9857
10115
|
const { collaborators } = await api2(
|
|
9858
10116
|
token,
|
|
9859
10117
|
`/api/app-collaborators/${encodeURIComponent(app2)}`
|
|
@@ -9883,13 +10141,13 @@ var cancel3 = defineDeepspaceCommand({
|
|
|
9883
10141
|
async run({ args }) {
|
|
9884
10142
|
const email = String(args.email);
|
|
9885
10143
|
const token = await ensureToken();
|
|
9886
|
-
const app2 = await resolveAppTarget(
|
|
10144
|
+
const app2 = await resolveAppTarget(DEPLOY_URL10, token, args.app);
|
|
9887
10145
|
const { pending = [] } = await api2(
|
|
9888
10146
|
token,
|
|
9889
10147
|
`/api/app-collaborators/${encodeURIComponent(app2)}`
|
|
9890
10148
|
);
|
|
9891
10149
|
const target2 = email.trim().toLowerCase();
|
|
9892
|
-
const found = pending.find((
|
|
10150
|
+
const found = pending.find((p35) => p35.email.toLowerCase() === target2);
|
|
9893
10151
|
if (!found) {
|
|
9894
10152
|
throw new Refusal(`${email} has no pending invite on ${app2}`, "no_pending_invite", {
|
|
9895
10153
|
action: cliAction("deepspace", "app", "collaborators", "list", "--app", app2)
|
|
@@ -9963,7 +10221,7 @@ var collaborators_default = defineCommand7({
|
|
|
9963
10221
|
import { defineCommand as defineCommand8 } from "citty";
|
|
9964
10222
|
import * as p14 from "@clack/prompts";
|
|
9965
10223
|
var API_URL6 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
9966
|
-
var
|
|
10224
|
+
var DEPLOY_URL11 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
9967
10225
|
var offer = defineDeepspaceCommand({
|
|
9968
10226
|
meta: { name: "offer", description: "Offer this app to another DeepSpace user" },
|
|
9969
10227
|
args: {
|
|
@@ -9977,7 +10235,7 @@ var offer = defineDeepspaceCommand({
|
|
|
9977
10235
|
async run({ args }) {
|
|
9978
10236
|
const email = String(args.email);
|
|
9979
10237
|
const token = await ensureToken();
|
|
9980
|
-
const app2 = await resolveAppTarget(
|
|
10238
|
+
const app2 = await resolveAppTarget(DEPLOY_URL11, token, args.app);
|
|
9981
10239
|
const pending = await apiFetch(
|
|
9982
10240
|
API_URL6,
|
|
9983
10241
|
token,
|
|
@@ -10033,7 +10291,7 @@ var status2 = defineDeepspaceCommand({
|
|
|
10033
10291
|
},
|
|
10034
10292
|
async run({ args }) {
|
|
10035
10293
|
const token = await ensureToken();
|
|
10036
|
-
const app2 = await resolveAppTarget(
|
|
10294
|
+
const app2 = await resolveAppTarget(DEPLOY_URL11, token, args.app);
|
|
10037
10295
|
const { transfer } = await apiFetch(API_URL6, token, `/api/app-transfers/${encodeURIComponent(app2)}`);
|
|
10038
10296
|
if (!transfer) {
|
|
10039
10297
|
if (!args.json) console.log(`No pending transfer for ${app2}.`);
|
|
@@ -10062,7 +10320,7 @@ var accept2 = defineDeepspaceCommand({
|
|
|
10062
10320
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10063
10321
|
throw new Refusal(msg, msg.startsWith("No app id") ? "not_in_app_repo" : "invalid_app");
|
|
10064
10322
|
}
|
|
10065
|
-
const { replayed, acceptedAt, inheritedCollaborators } = await apiFetch(
|
|
10323
|
+
const { replayed, acceptedAt, inheritedCollaborators } = await apiFetch(DEPLOY_URL11, token, `/api/apps/${encodeURIComponent(app2)}/transfer/accept`, {
|
|
10066
10324
|
method: "POST"
|
|
10067
10325
|
});
|
|
10068
10326
|
if (!args.json) {
|
|
@@ -10097,7 +10355,7 @@ var cancel4 = defineDeepspaceCommand({
|
|
|
10097
10355
|
},
|
|
10098
10356
|
async run({ args }) {
|
|
10099
10357
|
const token = await ensureToken();
|
|
10100
|
-
const app2 = await resolveAppTarget(
|
|
10358
|
+
const app2 = await resolveAppTarget(DEPLOY_URL11, token, args.app);
|
|
10101
10359
|
await apiFetch(API_URL6, token, `/api/app-transfers/${encodeURIComponent(app2)}`, { method: "DELETE" });
|
|
10102
10360
|
if (!args.json) console.log(`\u2713 Transfer offer for ${app2} cancelled.`);
|
|
10103
10361
|
return { data: { app: app2, cancelled: true } };
|
|
@@ -10491,8 +10749,8 @@ var integrations_default = defineCommand9({
|
|
|
10491
10749
|
|
|
10492
10750
|
// src/cli/commands/feedback.ts
|
|
10493
10751
|
import * as p16 from "@clack/prompts";
|
|
10494
|
-
import { existsSync as
|
|
10495
|
-
import { dirname as dirname11, join as
|
|
10752
|
+
import { existsSync as existsSync24, readFileSync as readFileSync21 } from "fs";
|
|
10753
|
+
import { dirname as dirname11, join as join24 } from "path";
|
|
10496
10754
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10497
10755
|
import { release } from "os";
|
|
10498
10756
|
var API_URL8 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
@@ -10515,11 +10773,11 @@ function readCliVersion() {
|
|
|
10515
10773
|
try {
|
|
10516
10774
|
const here = dirname11(fileURLToPath2(import.meta.url));
|
|
10517
10775
|
const candidates = [
|
|
10518
|
-
|
|
10519
|
-
|
|
10776
|
+
join24(here, "..", "package.json"),
|
|
10777
|
+
join24(here, "..", "..", "..", "package.json")
|
|
10520
10778
|
];
|
|
10521
10779
|
for (const path of candidates) {
|
|
10522
|
-
if (!
|
|
10780
|
+
if (!existsSync24(path)) continue;
|
|
10523
10781
|
const pkg2 = JSON.parse(readFileSync21(path, "utf-8"));
|
|
10524
10782
|
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
10525
10783
|
}
|
|
@@ -10750,7 +11008,7 @@ function parseCursorArg(raw) {
|
|
|
10750
11008
|
}
|
|
10751
11009
|
|
|
10752
11010
|
// src/cli/commands/secrets.ts
|
|
10753
|
-
var
|
|
11011
|
+
var DEPLOY_URL12 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
10754
11012
|
var COMMON_ARGS = {
|
|
10755
11013
|
app: {
|
|
10756
11014
|
type: "string",
|
|
@@ -10776,12 +11034,13 @@ var COMMON_ARGS = {
|
|
|
10776
11034
|
default: false
|
|
10777
11035
|
}
|
|
10778
11036
|
};
|
|
10779
|
-
async function resolveTarget(args) {
|
|
10780
|
-
|
|
11037
|
+
async function resolveTarget(args, opts = {}) {
|
|
11038
|
+
const register = opts.register === true;
|
|
11039
|
+
assertAppTargetResolvable(args.app, { wranglerEnv: args.env, register });
|
|
10781
11040
|
const { wranglerEnv } = parseWranglerEnvArg(args.env);
|
|
10782
11041
|
const configName = validateConfigName(args.config?.trim() || defaultConfigNameForEnv(wranglerEnv));
|
|
10783
11042
|
const token = await ensureToken();
|
|
10784
|
-
const appId = await resolveAppTarget(
|
|
11043
|
+
const appId = await resolveAppTarget(DEPLOY_URL12, token, args.app, { wranglerEnv, register });
|
|
10785
11044
|
return { appId, configName, token };
|
|
10786
11045
|
}
|
|
10787
11046
|
function ok(json, data, human) {
|
|
@@ -10810,7 +11069,7 @@ var list6 = defineCommand10({
|
|
|
10810
11069
|
},
|
|
10811
11070
|
async run({ args }) {
|
|
10812
11071
|
const t = await resolveTarget(args);
|
|
10813
|
-
const { secrets } = await listSecrets(
|
|
11072
|
+
const { secrets } = await listSecrets(DEPLOY_URL12, t.token, t.appId, t.configName);
|
|
10814
11073
|
if (args.json) {
|
|
10815
11074
|
console.log(JSON.stringify({ ok: true, appId: t.appId, config: t.configName, secrets }));
|
|
10816
11075
|
return;
|
|
@@ -10831,7 +11090,7 @@ var set = defineCommand10({
|
|
|
10831
11090
|
secret: { type: "positional", description: "KEY=value (repeatable)", required: true }
|
|
10832
11091
|
},
|
|
10833
11092
|
async run({ args }) {
|
|
10834
|
-
const t = await resolveTarget(args);
|
|
11093
|
+
const t = await resolveTarget(args, { register: true });
|
|
10835
11094
|
const pairs = dedupePositionals(args.secret, args._);
|
|
10836
11095
|
const secrets = {};
|
|
10837
11096
|
const dupes = [];
|
|
@@ -10851,9 +11110,9 @@ var set = defineCommand10({
|
|
|
10851
11110
|
}
|
|
10852
11111
|
if (Object.keys(secrets).length === 1) {
|
|
10853
11112
|
const [[k, v]] = Object.entries(secrets);
|
|
10854
|
-
await setSecret(
|
|
11113
|
+
await setSecret(DEPLOY_URL12, t.token, t.appId, t.configName, k, v);
|
|
10855
11114
|
} else {
|
|
10856
|
-
await uploadSecrets(
|
|
11115
|
+
await uploadSecrets(DEPLOY_URL12, t.token, t.appId, t.configName, secrets, false);
|
|
10857
11116
|
}
|
|
10858
11117
|
const n = Object.keys(secrets).length;
|
|
10859
11118
|
ok(
|
|
@@ -10886,11 +11145,11 @@ var get2 = defineCommand10({
|
|
|
10886
11145
|
const t = await resolveTarget(args);
|
|
10887
11146
|
const key = validateSecretName(args.key);
|
|
10888
11147
|
if (args.plain) {
|
|
10889
|
-
const { value } = await getSecretPlain(
|
|
11148
|
+
const { value } = await getSecretPlain(DEPLOY_URL12, t.token, t.appId, t.configName, key);
|
|
10890
11149
|
process.stdout.write(process.stdout.isTTY ? value + "\n" : value);
|
|
10891
11150
|
return;
|
|
10892
11151
|
}
|
|
10893
|
-
const { secrets } = await listSecrets(
|
|
11152
|
+
const { secrets } = await listSecrets(DEPLOY_URL12, t.token, t.appId, t.configName);
|
|
10894
11153
|
const item = secrets.find((s) => s.key === key);
|
|
10895
11154
|
if (!item)
|
|
10896
11155
|
throw new InputError(`Secret "${key}" not found in ${t.configName}`, "secret_not_found");
|
|
@@ -10921,7 +11180,7 @@ var del2 = defineCommand10({
|
|
|
10921
11180
|
for (const key of keys) {
|
|
10922
11181
|
const name = validateSecretName(key);
|
|
10923
11182
|
try {
|
|
10924
|
-
const result = await deleteSecret(
|
|
11183
|
+
const result = await deleteSecret(DEPLOY_URL12, t.token, t.appId, t.configName, name);
|
|
10925
11184
|
if (result.deleted === false) absent++;
|
|
10926
11185
|
else deleted++;
|
|
10927
11186
|
} catch (err) {
|
|
@@ -10959,11 +11218,11 @@ var upload = defineCommand10({
|
|
|
10959
11218
|
}
|
|
10960
11219
|
},
|
|
10961
11220
|
async run({ args }) {
|
|
10962
|
-
const t = await resolveTarget(args);
|
|
11221
|
+
const t = await resolveTarget(args, { register: true });
|
|
10963
11222
|
const secrets = parseSecretsUpload(await readUploadSource(String(args.file)));
|
|
10964
11223
|
if (Object.keys(secrets).length === 0)
|
|
10965
11224
|
throw new InputError("No secrets found in the input.", "empty_input");
|
|
10966
|
-
await uploadSecrets(
|
|
11225
|
+
await uploadSecrets(DEPLOY_URL12, t.token, t.appId, t.configName, secrets, args.replace);
|
|
10967
11226
|
const uploaded = Object.keys(secrets);
|
|
10968
11227
|
ok(
|
|
10969
11228
|
args.json === true,
|
|
@@ -11038,7 +11297,7 @@ var download = defineCommand10({
|
|
|
11038
11297
|
"invalid_format"
|
|
11039
11298
|
);
|
|
11040
11299
|
}
|
|
11041
|
-
const { secrets } = await fetchSecretsValues(
|
|
11300
|
+
const { secrets } = await fetchSecretsValues(DEPLOY_URL12, t.token, t.appId, t.configName);
|
|
11042
11301
|
process.stdout.write(formatSecretsDownload(secrets, format));
|
|
11043
11302
|
}
|
|
11044
11303
|
});
|
|
@@ -11053,10 +11312,10 @@ var pull = defineCommand10({
|
|
|
11053
11312
|
"Run from a DeepSpace app directory (one containing wrangler.toml).",
|
|
11054
11313
|
"not_in_app_repo"
|
|
11055
11314
|
);
|
|
11056
|
-
const t = await resolveTarget(args);
|
|
11315
|
+
const t = await resolveTarget(args, { register: true });
|
|
11057
11316
|
const ownerId = decodeJwtPayload(t.token).sub;
|
|
11058
11317
|
const refreshed = await refreshSecretsCache(
|
|
11059
|
-
|
|
11318
|
+
DEPLOY_URL12,
|
|
11060
11319
|
t.token,
|
|
11061
11320
|
t.appId,
|
|
11062
11321
|
wranglerEnv,
|
|
@@ -11081,7 +11340,7 @@ var configsList = defineCommand10({
|
|
|
11081
11340
|
args: { ...COMMON_ARGS },
|
|
11082
11341
|
async run({ args }) {
|
|
11083
11342
|
const t = await resolveTarget(args);
|
|
11084
|
-
const { configs: configs2 } = await listConfigs(
|
|
11343
|
+
const { configs: configs2 } = await listConfigs(DEPLOY_URL12, t.token, t.appId);
|
|
11085
11344
|
ok(args.json === true, { appId: t.appId, configs: configs2 }, () => {
|
|
11086
11345
|
if (configs2.length === 0) {
|
|
11087
11346
|
console.log("No configs yet \u2014 the first `secrets set` creates one.");
|
|
@@ -11101,10 +11360,10 @@ var configsCreate = defineCommand10({
|
|
|
11101
11360
|
"copy-from": { type: "string", description: "Copy secrets from this config", required: false }
|
|
11102
11361
|
},
|
|
11103
11362
|
async run({ args }) {
|
|
11104
|
-
const t = await resolveTarget(args);
|
|
11363
|
+
const t = await resolveTarget(args, { register: true });
|
|
11105
11364
|
const name = validateConfigName(args.name);
|
|
11106
11365
|
const copyFrom = args["copy-from"] || void 0;
|
|
11107
|
-
const { configs: configs2 } = await listConfigs(
|
|
11366
|
+
const { configs: configs2 } = await listConfigs(DEPLOY_URL12, t.token, t.appId);
|
|
11108
11367
|
if (configs2.some((c) => c.name === name)) {
|
|
11109
11368
|
if (copyFrom) {
|
|
11110
11369
|
throw new InputError(
|
|
@@ -11119,7 +11378,7 @@ var configsCreate = defineCommand10({
|
|
|
11119
11378
|
);
|
|
11120
11379
|
return;
|
|
11121
11380
|
}
|
|
11122
|
-
await createConfig(
|
|
11381
|
+
await createConfig(DEPLOY_URL12, t.token, t.appId, name, copyFrom);
|
|
11123
11382
|
ok(
|
|
11124
11383
|
args.json === true,
|
|
11125
11384
|
{ appId: t.appId, config: name, created: true, ...copyFrom ? { copyFrom } : {} },
|
|
@@ -11151,7 +11410,7 @@ var configsDelete = defineCommand10({
|
|
|
11151
11410
|
let secrets = [];
|
|
11152
11411
|
let counted = true;
|
|
11153
11412
|
try {
|
|
11154
|
-
secrets = (await listSecrets(
|
|
11413
|
+
secrets = (await listSecrets(DEPLOY_URL12, t.token, t.appId, name)).secrets;
|
|
11155
11414
|
} catch (error) {
|
|
11156
11415
|
if (args.yes !== true) throw error;
|
|
11157
11416
|
counted = false;
|
|
@@ -11167,7 +11426,7 @@ var configsDelete = defineCommand10({
|
|
|
11167
11426
|
throw new InputError("Cancelled.", "confirmation_required");
|
|
11168
11427
|
}
|
|
11169
11428
|
}
|
|
11170
|
-
await deleteConfig(
|
|
11429
|
+
await deleteConfig(DEPLOY_URL12, t.token, t.appId, name);
|
|
11171
11430
|
ok(
|
|
11172
11431
|
args.json === true,
|
|
11173
11432
|
{ appId: t.appId, config: name, deleted: true, secretsDeleted: counted ? count : null },
|
|
@@ -11196,32 +11455,7 @@ var secrets_default = defineCommand10({
|
|
|
11196
11455
|
});
|
|
11197
11456
|
|
|
11198
11457
|
// 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
|
-
}
|
|
11458
|
+
var DEPLOY_URL13 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
11225
11459
|
var init_default = defineDeepspaceCommand({
|
|
11226
11460
|
meta: {
|
|
11227
11461
|
name: "init",
|
|
@@ -11262,7 +11496,7 @@ var init_default = defineDeepspaceCommand({
|
|
|
11262
11496
|
const token2 = await ensureToken();
|
|
11263
11497
|
let state;
|
|
11264
11498
|
try {
|
|
11265
|
-
state = await getAppSource(
|
|
11499
|
+
state = await getAppSource(DEPLOY_URL13, token2, existing);
|
|
11266
11500
|
} catch (error) {
|
|
11267
11501
|
if (error instanceof ApiError && error.code === "forbidden") {
|
|
11268
11502
|
throw new Refusal(
|
|
@@ -11287,15 +11521,17 @@ var init_default = defineDeepspaceCommand({
|
|
|
11287
11521
|
}
|
|
11288
11522
|
);
|
|
11289
11523
|
}
|
|
11524
|
+
const healedScaffold = commitScaffoldIfUnborn(appDir, token2);
|
|
11290
11525
|
if (!args.json) {
|
|
11291
11526
|
console.log(
|
|
11292
|
-
`Already initialized: ${existing}${envName ? ` (env: ${envName})` : ""} \u2014 registered on ${DEEPSPACE_ENV}`
|
|
11527
|
+
`Already initialized: ${existing}${envName ? ` (env: ${envName})` : ""} \u2014 registered on ${DEEPSPACE_ENV}` + (healedScaffold ? " \u2014 initial scaffold commit created." : "")
|
|
11293
11528
|
);
|
|
11294
11529
|
console.log(`App dir: ${appDir}`);
|
|
11295
11530
|
}
|
|
11296
11531
|
return {
|
|
11297
11532
|
data: {
|
|
11298
11533
|
status: "already_initialized",
|
|
11534
|
+
...healedScaffold ? { committedScaffold: true } : {},
|
|
11299
11535
|
appId: existing,
|
|
11300
11536
|
appDir,
|
|
11301
11537
|
env: DEEPSPACE_ENV,
|
|
@@ -11304,11 +11540,11 @@ var init_default = defineDeepspaceCommand({
|
|
|
11304
11540
|
};
|
|
11305
11541
|
}
|
|
11306
11542
|
const token = await ensureToken();
|
|
11307
|
-
const { appId } = await
|
|
11308
|
-
|
|
11543
|
+
const { appId, committedScaffold } = await mintAppIdentity(appDir, token, {
|
|
11544
|
+
wranglerEnv: envName,
|
|
11545
|
+
force: Boolean(args["new-id"]),
|
|
11546
|
+
commitScaffold: true
|
|
11309
11547
|
});
|
|
11310
|
-
writeAppId(appDir, appId, { wranglerEnv: envName, force: Boolean(args["new-id"]) });
|
|
11311
|
-
const committedScaffold = commitScaffoldIfUnborn(appDir, token);
|
|
11312
11548
|
if (!args.json) {
|
|
11313
11549
|
const envSuffix = envName ? ` (env: ${envName})` : "";
|
|
11314
11550
|
const commitNote = committedScaffold ? " \u2014 initial scaffold commit created." : " \u2014 commit wrangler.toml.";
|
|
@@ -11484,7 +11720,7 @@ var status_default = defineCommand11({
|
|
|
11484
11720
|
const { appName, appId, appIdError } = resolveStatusApp(appDir, wranglerEnv);
|
|
11485
11721
|
lines.push([
|
|
11486
11722
|
"App",
|
|
11487
|
-
`${appName}${wranglerEnv ? ` [env.${wranglerEnv}]` : ""}${appId ? ` (${appId})` : appIdError ? ` \u2014 ${appIdError}` : " \u2014 no DEEPSPACE_APP_ID;
|
|
11723
|
+
`${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
11724
|
]);
|
|
11489
11725
|
json.inApp = true;
|
|
11490
11726
|
json.appName = appName;
|
|
@@ -11495,7 +11731,7 @@ var status_default = defineCommand11({
|
|
|
11495
11731
|
const depsInstallCommand = installCommand(appDir);
|
|
11496
11732
|
lines.push([
|
|
11497
11733
|
"Deps",
|
|
11498
|
-
deps === "ready" ? "installed" : deps === "installing" ? "dependency installation is still running" :
|
|
11734
|
+
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
11735
|
]);
|
|
11500
11736
|
json.deps = deps;
|
|
11501
11737
|
let branch = null;
|
|
@@ -11573,7 +11809,10 @@ var status_default = defineCommand11({
|
|
|
11573
11809
|
createdAt: rel.createdAt,
|
|
11574
11810
|
byYou,
|
|
11575
11811
|
source: rel.source,
|
|
11576
|
-
sourceRevision: rel.sourceRevision
|
|
11812
|
+
sourceRevision: rel.sourceRevision,
|
|
11813
|
+
// The human line can say "dirty worktree"; the machine mirror
|
|
11814
|
+
// must be able to say the same.
|
|
11815
|
+
sourceDirty: rel.sourceDirty ?? null
|
|
11577
11816
|
};
|
|
11578
11817
|
if (!entry) {
|
|
11579
11818
|
lines.push(["Live", `${release2} \xB7 serving state not visible from your account`]);
|
|
@@ -11604,8 +11843,13 @@ var status_default = defineCommand11({
|
|
|
11604
11843
|
]);
|
|
11605
11844
|
json.source = { ...source, revision: sourceResult.value.revision };
|
|
11606
11845
|
} else {
|
|
11607
|
-
|
|
11846
|
+
const github = externalGitSource(appDir, null);
|
|
11847
|
+
lines.push([
|
|
11848
|
+
"Source",
|
|
11849
|
+
`unclaimed \xB7 ${github ? "next deploy infers GitHub from this checkout\u2019s remote" : "next deploy syncs to DeepSpace and claims it, permanently"}`
|
|
11850
|
+
]);
|
|
11608
11851
|
json.source = null;
|
|
11852
|
+
json.sourceInference = github ? "github" : "deepspace";
|
|
11609
11853
|
}
|
|
11610
11854
|
const [refsResult, wsResult, releaseResult, appsResult] = await Promise.all([
|
|
11611
11855
|
githubSource ? Promise.resolve({ ok: true, value: null }) : observeRemote(api4.getRefs()),
|
|
@@ -11775,9 +12019,9 @@ var push_default = defineDeepspaceCommand({
|
|
|
11775
12019
|
const spinner2 = args.json ? null : createSpinner();
|
|
11776
12020
|
spinner2?.start(`Checking ${branch} before push\u2026`);
|
|
11777
12021
|
const resolveTarget3 = async () => {
|
|
11778
|
-
assertAppTargetResolvable(appArg);
|
|
12022
|
+
assertAppTargetResolvable(appArg, { register: true });
|
|
11779
12023
|
const token2 = await ensureToken();
|
|
11780
|
-
const appId2 = await resolveAppTarget(deployBaseUrl(), token2, appArg);
|
|
12024
|
+
const appId2 = await resolveAppTarget(deployBaseUrl(), token2, appArg, { register: true });
|
|
11781
12025
|
await warnIfPhantomApp(deployBaseUrl(), token2, appId2, appArg?.trim() || void 0);
|
|
11782
12026
|
return { token: token2, appId: appId2 };
|
|
11783
12027
|
};
|
|
@@ -11850,6 +12094,12 @@ var push_default = defineDeepspaceCommand({
|
|
|
11850
12094
|
if (sourceState.source?.provider === "github") {
|
|
11851
12095
|
throw githubSourceRefusal(appId, sourceState.source.repository);
|
|
11852
12096
|
}
|
|
12097
|
+
if (sourceState.source === null) {
|
|
12098
|
+
process.stderr.write(
|
|
12099
|
+
`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\`).
|
|
12100
|
+
`
|
|
12101
|
+
);
|
|
12102
|
+
}
|
|
11853
12103
|
const pullRecoveryCwd = selectedBranchCheckout(appDir, branch) ?? appDir;
|
|
11854
12104
|
ensureSpaceRemote(appDir, appId, void 0, token);
|
|
11855
12105
|
if (force) {
|
|
@@ -11939,7 +12189,21 @@ var push_default = defineDeepspaceCommand({
|
|
|
11939
12189
|
extra: { status: result.status, appId, branch, oid: tipOid }
|
|
11940
12190
|
});
|
|
11941
12191
|
}
|
|
11942
|
-
|
|
12192
|
+
const uncommitted = appDir ? statusFiles(appDir) : [];
|
|
12193
|
+
if (!args.json && uncommitted.length > 0) {
|
|
12194
|
+
p19.log.warn(
|
|
12195
|
+
`${uncommitted.length} uncommitted file${uncommitted.length === 1 ? "" : "s"} NOT pushed \u2014 push publishes committed history only. Commit and push again to publish them.`
|
|
12196
|
+
);
|
|
12197
|
+
}
|
|
12198
|
+
return {
|
|
12199
|
+
data: {
|
|
12200
|
+
status: result.status,
|
|
12201
|
+
appId,
|
|
12202
|
+
branch,
|
|
12203
|
+
oid: tipOid,
|
|
12204
|
+
...uncommitted.length > 0 ? { uncommitted } : {}
|
|
12205
|
+
}
|
|
12206
|
+
};
|
|
11943
12207
|
} catch (err) {
|
|
11944
12208
|
if (err instanceof Refusal) throw err;
|
|
11945
12209
|
const transportFailure = classifyPushTransportFailure(err, appDir ?? void 0);
|
|
@@ -12236,7 +12500,7 @@ var pull_default = defineDeepspaceCommand({
|
|
|
12236
12500
|
|
|
12237
12501
|
// src/cli/commands/clone.ts
|
|
12238
12502
|
import * as p21 from "@clack/prompts";
|
|
12239
|
-
import { existsSync as existsSync26, readdirSync as readdirSync8, rmSync as
|
|
12503
|
+
import { existsSync as existsSync26, readdirSync as readdirSync8, rmSync as rmSync8 } from "fs";
|
|
12240
12504
|
import { resolve as resolve19, basename as basename5, join as join25 } from "path";
|
|
12241
12505
|
function cleanFailedCloneDir(dir) {
|
|
12242
12506
|
let entries;
|
|
@@ -12247,7 +12511,7 @@ function cleanFailedCloneDir(dir) {
|
|
|
12247
12511
|
}
|
|
12248
12512
|
if (entries.length === 1 && entries[0] === ".git") {
|
|
12249
12513
|
try {
|
|
12250
|
-
|
|
12514
|
+
rmSync8(join25(dir, ".git"), { recursive: true, force: true });
|
|
12251
12515
|
return [];
|
|
12252
12516
|
} catch {
|
|
12253
12517
|
return entries;
|
|
@@ -12378,7 +12642,7 @@ async function actorLabels(token, appId) {
|
|
|
12378
12642
|
}
|
|
12379
12643
|
|
|
12380
12644
|
// src/cli/commands/releases.ts
|
|
12381
|
-
var
|
|
12645
|
+
var DEPLOY_URL14 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
12382
12646
|
var releases_default = defineDeepspaceCommand({
|
|
12383
12647
|
meta: {
|
|
12384
12648
|
name: "releases",
|
|
@@ -12413,8 +12677,8 @@ var releases_default = defineDeepspaceCommand({
|
|
|
12413
12677
|
const spinner2 = args.json ? null : createSpinner();
|
|
12414
12678
|
spinner2?.start("Loading release history\u2026");
|
|
12415
12679
|
const token = await ensureToken();
|
|
12416
|
-
const appId = await resolveAppTarget(
|
|
12417
|
-
const { releases } = await repoApi(
|
|
12680
|
+
const appId = await resolveAppTarget(DEPLOY_URL14, token, appArg, { wranglerEnv: envArg });
|
|
12681
|
+
const { releases } = await repoApi(DEPLOY_URL14, token, appId).listReleases(limit);
|
|
12418
12682
|
spinner2?.stop(`Loaded ${releases.length} ${releases.length === 1 ? "release" : "releases"}.`);
|
|
12419
12683
|
if (!args.json) {
|
|
12420
12684
|
if (releases.length === 0) {
|
|
@@ -12422,7 +12686,7 @@ var releases_default = defineDeepspaceCommand({
|
|
|
12422
12686
|
} else {
|
|
12423
12687
|
const actors = await actorLabels(token, appId);
|
|
12424
12688
|
for (const r of releases) {
|
|
12425
|
-
const source = releaseSourceLabel(r);
|
|
12689
|
+
const source = displayText(releaseSourceLabel(r));
|
|
12426
12690
|
const rollback = r.rollbackAvailable ? "rollback available" : "rollback unavailable";
|
|
12427
12691
|
console.log(
|
|
12428
12692
|
`#${r.seq} ${r.id} ${r.kind.padEnd(8)} ${r.createdAt} ${actors.get(r.actor) ?? r.actor} ${source} ${rollback}`
|
|
@@ -12447,7 +12711,7 @@ Retry in a moment, or re-run with --allow-do-deletion to accept the risk.`,
|
|
|
12447
12711
|
"do_guard_unavailable"
|
|
12448
12712
|
);
|
|
12449
12713
|
}
|
|
12450
|
-
var
|
|
12714
|
+
var DEPLOY_URL15 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
12451
12715
|
function pickPreviousRelease(releases) {
|
|
12452
12716
|
if (releases.length === 0) {
|
|
12453
12717
|
return { error: "No releases recorded yet \u2014 nothing to roll back to.", code: "no_releases" };
|
|
@@ -12509,8 +12773,8 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
12509
12773
|
const spinner2 = args.json ? null : createSpinner();
|
|
12510
12774
|
spinner2?.start("Preparing rollback\u2026");
|
|
12511
12775
|
const token = await ensureToken();
|
|
12512
|
-
const appId = await resolveAppTarget(
|
|
12513
|
-
const api4 = repoApi(
|
|
12776
|
+
const appId = await resolveAppTarget(DEPLOY_URL15, token, appArg, { wranglerEnv: envArg });
|
|
12777
|
+
const api4 = repoApi(DEPLOY_URL15, token, appId);
|
|
12514
12778
|
let releaseId = args.release ? String(args.release).trim() : "";
|
|
12515
12779
|
if (!releaseId) {
|
|
12516
12780
|
const { releases } = await api4.listReleases(2);
|
|
@@ -12521,7 +12785,7 @@ var rollback_default = defineDeepspaceCommand({
|
|
|
12521
12785
|
spinner2?.message(`Rolling back to ${releaseId}\u2026`);
|
|
12522
12786
|
let result;
|
|
12523
12787
|
try {
|
|
12524
|
-
result = await apiFetch(
|
|
12788
|
+
result = await apiFetch(DEPLOY_URL15, token, `/api/deploy/${appId}/rollback`, {
|
|
12525
12789
|
method: "POST",
|
|
12526
12790
|
body: JSON.stringify({
|
|
12527
12791
|
releaseId,
|
|
@@ -12733,12 +12997,12 @@ function printOverlaps(overlaps, json) {
|
|
|
12733
12997
|
import * as p25 from "@clack/prompts";
|
|
12734
12998
|
import {
|
|
12735
12999
|
existsSync as existsSync27,
|
|
12736
|
-
mkdirSync as
|
|
13000
|
+
mkdirSync as mkdirSync10,
|
|
12737
13001
|
readFileSync as readFileSync24,
|
|
12738
13002
|
realpathSync as realpathSync6,
|
|
12739
13003
|
renameSync as renameSync2,
|
|
12740
|
-
statSync as
|
|
12741
|
-
writeFileSync as
|
|
13004
|
+
statSync as statSync5,
|
|
13005
|
+
writeFileSync as writeFileSync8
|
|
12742
13006
|
} from "fs";
|
|
12743
13007
|
import { dirname as dirname12, isAbsolute as isAbsolute6, join as join26, relative as relative4, sep as sep4 } from "path";
|
|
12744
13008
|
var WORKSPACE_MARKER = "deepspace-workspace.json";
|
|
@@ -12772,8 +13036,8 @@ function markManagedWorkspace(worktreeRoot, workspaceId) {
|
|
|
12772
13036
|
const marker = { version: 1, workspaceId, mode: "managed-linked" };
|
|
12773
13037
|
const tempPath = `${markerPath}.${process.pid}.tmp`;
|
|
12774
13038
|
try {
|
|
12775
|
-
|
|
12776
|
-
|
|
13039
|
+
mkdirSync10(dirname12(markerPath), { recursive: true });
|
|
13040
|
+
writeFileSync8(tempPath, JSON.stringify(marker) + "\n");
|
|
12777
13041
|
renameSync2(tempPath, markerPath);
|
|
12778
13042
|
return true;
|
|
12779
13043
|
} catch {
|
|
@@ -12840,7 +13104,7 @@ function rematerializeWorkspaceWorktree(appDir, worktreeRoot, branch, workspaceI
|
|
|
12840
13104
|
function inOwnLinkedWorktree(appDir, id) {
|
|
12841
13105
|
if (workspaceIdFromBranch(currentBranch(appDir)) !== id) return false;
|
|
12842
13106
|
try {
|
|
12843
|
-
return
|
|
13107
|
+
return statSync5(join26(repoToplevel(appDir), ".git")).isFile();
|
|
12844
13108
|
} catch {
|
|
12845
13109
|
return false;
|
|
12846
13110
|
}
|
|
@@ -13286,14 +13550,14 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
13286
13550
|
|
|
13287
13551
|
// src/cli/commands/workspace/attach.ts
|
|
13288
13552
|
import * as p27 from "@clack/prompts";
|
|
13289
|
-
import { existsSync as existsSync28, readdirSync as readdirSync9, realpathSync as realpathSync7, rmSync as
|
|
13553
|
+
import { existsSync as existsSync28, readdirSync as readdirSync9, realpathSync as realpathSync7, rmSync as rmSync9 } from "fs";
|
|
13290
13554
|
import { isAbsolute as isAbsolute8, resolve as resolve21 } from "path";
|
|
13291
13555
|
function samePath(a, b) {
|
|
13292
|
-
const real = (
|
|
13556
|
+
const real = (p35) => {
|
|
13293
13557
|
try {
|
|
13294
|
-
return realpathSync7(
|
|
13558
|
+
return realpathSync7(p35);
|
|
13295
13559
|
} catch {
|
|
13296
|
-
return resolve21(
|
|
13560
|
+
return resolve21(p35);
|
|
13297
13561
|
}
|
|
13298
13562
|
};
|
|
13299
13563
|
return real(a) === real(b);
|
|
@@ -13317,7 +13581,7 @@ function cleanFailedFreshAttachDir(dir) {
|
|
|
13317
13581
|
return entries;
|
|
13318
13582
|
}
|
|
13319
13583
|
try {
|
|
13320
|
-
|
|
13584
|
+
rmSync9(dir, { recursive: true, force: true });
|
|
13321
13585
|
return [];
|
|
13322
13586
|
} catch {
|
|
13323
13587
|
return entries.length > 0 ? entries : ["<cleanup-failed>"];
|
|
@@ -13851,7 +14115,7 @@ var workspaceStatusCommand = defineDeepspaceCommand({
|
|
|
13851
14115
|
import * as p31 from "@clack/prompts";
|
|
13852
14116
|
|
|
13853
14117
|
// src/cli/lib/validation.ts
|
|
13854
|
-
import { sync as
|
|
14118
|
+
import { sync as spawnSync8 } from "cross-spawn";
|
|
13855
14119
|
import { readFileSync as readFileSync25 } from "fs";
|
|
13856
14120
|
import { join as join27 } from "path";
|
|
13857
14121
|
var MAX_SUMMARY_CHARS = 4e3;
|
|
@@ -13869,7 +14133,7 @@ function resolveValidationCommand(appDir, explicit) {
|
|
|
13869
14133
|
}
|
|
13870
14134
|
function runValidationCommand(appDir, command) {
|
|
13871
14135
|
const started = Date.now();
|
|
13872
|
-
const res =
|
|
14136
|
+
const res = spawnSync8(command, [], {
|
|
13873
14137
|
cwd: appDir,
|
|
13874
14138
|
shell: true,
|
|
13875
14139
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -14537,7 +14801,8 @@ function formatEvent2(e, lands, actors) {
|
|
|
14537
14801
|
case "release.rollback":
|
|
14538
14802
|
detail = `#${Number(s.seq ?? 0)} ${releaseSourceLabel({
|
|
14539
14803
|
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
|
|
14804
|
+
source: s.sourceProvider === "github" && typeof s.sourceRepository === "string" ? { provider: "github", repository: s.sourceRepository } : s.sourceProvider === "deepspace" ? { provider: "deepspace" } : null,
|
|
14805
|
+
sourceDirty: typeof s.sourceDirty === "boolean" ? s.sourceDirty : null
|
|
14541
14806
|
})}`;
|
|
14542
14807
|
break;
|
|
14543
14808
|
// Unknown kinds still print their kind + actor — never crash the feed.
|
|
@@ -14731,17 +14996,15 @@ var git_credential_default = defineCommand13({
|
|
|
14731
14996
|
});
|
|
14732
14997
|
|
|
14733
14998
|
// src/cli/commands/source.ts
|
|
14734
|
-
import * as p34 from "@clack/prompts";
|
|
14735
|
-
var TRANSFER_PREFIX = spacePrivateRef("source-transfer");
|
|
14736
14999
|
var source_default = defineDeepspaceCommand({
|
|
14737
15000
|
meta: {
|
|
14738
15001
|
name: "source",
|
|
14739
|
-
description: "Show
|
|
15002
|
+
description: "Show the app\u2019s source authority (inferred from use, never declared)"
|
|
14740
15003
|
},
|
|
14741
15004
|
args: {
|
|
14742
15005
|
provider: {
|
|
14743
15006
|
type: "positional",
|
|
14744
|
-
description: "
|
|
15007
|
+
description: "No longer accepted \u2014 source is inferred, not declared",
|
|
14745
15008
|
required: false
|
|
14746
15009
|
},
|
|
14747
15010
|
app: {
|
|
@@ -14749,71 +15012,41 @@ var source_default = defineDeepspaceCommand({
|
|
|
14749
15012
|
alias: "a",
|
|
14750
15013
|
description: "App id or subdomain name (default: current app)",
|
|
14751
15014
|
required: false
|
|
14752
|
-
},
|
|
14753
|
-
remote: {
|
|
14754
|
-
type: "string",
|
|
14755
|
-
description: "GitHub remote name when it cannot be inferred (default: origin)",
|
|
14756
|
-
required: false
|
|
14757
15015
|
}
|
|
14758
15016
|
},
|
|
14759
15017
|
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) {
|
|
15018
|
+
if (typeof args.provider === "string" && args.provider.trim()) {
|
|
14778
15019
|
throw new Refusal(
|
|
14779
|
-
"
|
|
14780
|
-
"
|
|
15020
|
+
"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.",
|
|
15021
|
+
"source_inferred"
|
|
14781
15022
|
);
|
|
14782
15023
|
}
|
|
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);
|
|
15024
|
+
const appArg = typeof args.app === "string" ? args.app : void 0;
|
|
15025
|
+
if (appArg === void 0) {
|
|
15026
|
+
const appDir = findAppDir();
|
|
15027
|
+
if (appDir && !readAppId(appDir)) {
|
|
15028
|
+
if (!args.json) {
|
|
15029
|
+
console.log("App: not registered yet \u2014 it registers on first use (deploy, secrets, dev\u2026)");
|
|
15030
|
+
console.log("Source: unclaimed \u2014 a checkout with a GitHub remote deploys as GitHub; the first `deepspace push` claims DeepSpace source permanently.");
|
|
15031
|
+
}
|
|
15032
|
+
return { data: { appId: null, source: null, revision: 0, registered: false } };
|
|
14796
15033
|
}
|
|
14797
15034
|
}
|
|
15035
|
+
const token = await ensureToken();
|
|
15036
|
+
const deployUrl = deployBaseUrl();
|
|
15037
|
+
const appId = await resolveAppTarget(deployUrl, token, appArg);
|
|
15038
|
+
const state = await getAppSource(deployUrl, token, appId);
|
|
15039
|
+
reportSource(appId, state, args.json);
|
|
14798
15040
|
return {
|
|
14799
15041
|
data: {
|
|
14800
|
-
appId,
|
|
14801
|
-
source:
|
|
14802
|
-
revision:
|
|
14803
|
-
|
|
14804
|
-
...reconciliation.localReconciliation ? { localReconciliation: reconciliation.localReconciliation } : {}
|
|
15042
|
+
appId: state.appId,
|
|
15043
|
+
source: state.source,
|
|
15044
|
+
revision: state.revision,
|
|
15045
|
+
registered: state.registered
|
|
14805
15046
|
}
|
|
14806
15047
|
};
|
|
14807
15048
|
}
|
|
14808
15049
|
});
|
|
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
15050
|
function reportSource(appId, state, json) {
|
|
14818
15051
|
if (json) return;
|
|
14819
15052
|
console.log(`App: ${appId}`);
|
|
@@ -14822,220 +15055,22 @@ function reportSource(appId, state, json) {
|
|
|
14822
15055
|
} else if (state.source?.provider === "deepspace") {
|
|
14823
15056
|
console.log("Source: DeepSpace");
|
|
14824
15057
|
} 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"
|
|
15058
|
+
console.log(
|
|
15059
|
+
"Source: unclaimed \u2014 a checkout with a GitHub remote deploys as GitHub; the first `deepspace push` claims DeepSpace source permanently."
|
|
15012
15060
|
);
|
|
15013
15061
|
}
|
|
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);
|
|
15062
|
+
console.log(`Revision: ${state.revision}`);
|
|
15028
15063
|
}
|
|
15029
15064
|
|
|
15030
15065
|
// src/cli/commands/update.ts
|
|
15031
|
-
import * as
|
|
15066
|
+
import * as p34 from "@clack/prompts";
|
|
15032
15067
|
import { existsSync as existsSync29, readFileSync as readFileSync26 } from "fs";
|
|
15033
15068
|
import { join as join28, resolve as resolve22 } from "path";
|
|
15034
15069
|
|
|
15035
15070
|
// package.json
|
|
15036
15071
|
var package_default = {
|
|
15037
15072
|
name: "deepspace",
|
|
15038
|
-
version: "0.
|
|
15073
|
+
version: "0.26.0",
|
|
15039
15074
|
engines: {
|
|
15040
15075
|
node: ">=22.15.0 <23 || >=24 <25 || >=26 <27"
|
|
15041
15076
|
},
|
|
@@ -15290,7 +15325,7 @@ var update_default = defineDeepspaceCommand({
|
|
|
15290
15325
|
} : null;
|
|
15291
15326
|
if (versionGuidance) {
|
|
15292
15327
|
const steps2 = [versionGuidance.step];
|
|
15293
|
-
if (!args.json)
|
|
15328
|
+
if (!args.json) p34.log.warn(versionGuidance.step);
|
|
15294
15329
|
return {
|
|
15295
15330
|
data: {
|
|
15296
15331
|
ready: false,
|
|
@@ -15326,9 +15361,9 @@ var update_default = defineDeepspaceCommand({
|
|
|
15326
15361
|
];
|
|
15327
15362
|
const status3 = unverifiedSdk ? "dependency_unverified" : steps.length === 0 ? "aligned" : "guidance_available";
|
|
15328
15363
|
if (!args.json) {
|
|
15329
|
-
|
|
15330
|
-
for (const step of steps)
|
|
15331
|
-
if (steps.length === 0)
|
|
15364
|
+
p34.log.info(`App dependency: ${currentSpec}; guidance owner: deepspace CLI ${targetVersion}`);
|
|
15365
|
+
for (const step of steps) p34.log.message(`\u2022 ${step}`);
|
|
15366
|
+
if (steps.length === 0) p34.log.success("This app is already aligned with this CLI version.");
|
|
15332
15367
|
}
|
|
15333
15368
|
const data = {
|
|
15334
15369
|
ready: status3 === "aligned",
|
|
@@ -15394,7 +15429,7 @@ var create2 = defineCommand14({
|
|
|
15394
15429
|
}
|
|
15395
15430
|
const scaffolder = `create-deepspace@${pkg.version}`;
|
|
15396
15431
|
console.log(`Running: npx ${scaffolder} ${rawArgs2.join(" ")}`.trimEnd() + "\n");
|
|
15397
|
-
const res =
|
|
15432
|
+
const res = spawnSync9("npx", ["-y", scaffolder, ...rawArgs2], { stdio: "inherit" });
|
|
15398
15433
|
if (res.error) {
|
|
15399
15434
|
const err = res.error;
|
|
15400
15435
|
console.error(
|