deepspace 0.8.0 → 0.8.2
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 +13 -0
- package/dist/cli.js +496 -408
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/dist/testing.d.ts +0 -115
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
4
|
import { defineCommand as defineCommand14, runMain, runCommand } from "citty";
|
|
5
5
|
import { sync as spawnSync8 } from "cross-spawn";
|
|
6
|
-
import { readFileSync as
|
|
7
|
-
import { dirname as
|
|
6
|
+
import { readFileSync as readFileSync25, existsSync as existsSync24 } from "fs";
|
|
7
|
+
import { dirname as dirname10, join as join23 } from "path";
|
|
8
8
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9
9
|
|
|
10
10
|
// src/cli/lib/spinner.ts
|
|
@@ -836,7 +836,7 @@ ${line}
|
|
|
836
836
|
}
|
|
837
837
|
|
|
838
838
|
// src/cli/commands/dev.ts
|
|
839
|
-
import { resolve as
|
|
839
|
+
import { resolve as resolve6, basename as basename2, join as join12 } from "path";
|
|
840
840
|
import spawn from "cross-spawn";
|
|
841
841
|
|
|
842
842
|
// src/cli/lib/app-context.ts
|
|
@@ -874,10 +874,231 @@ function findChildApps(dir) {
|
|
|
874
874
|
}
|
|
875
875
|
|
|
876
876
|
// src/cli/lib/launch-config.ts
|
|
877
|
-
import {
|
|
878
|
-
|
|
877
|
+
import {
|
|
878
|
+
existsSync as existsSync5,
|
|
879
|
+
readFileSync as readFileSync7,
|
|
880
|
+
realpathSync,
|
|
881
|
+
writeFileSync as writeFileSync4,
|
|
882
|
+
mkdirSync as mkdirSync4,
|
|
883
|
+
renameSync,
|
|
884
|
+
unlinkSync
|
|
885
|
+
} from "fs";
|
|
886
|
+
import { join as join6, resolve as resolve4, dirname as dirname3, basename, isAbsolute as isAbsolute2, sep } from "path";
|
|
887
|
+
|
|
888
|
+
// src/cli/lib/git/repository.ts
|
|
889
|
+
import { appendFileSync, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
890
|
+
import { dirname as dirname2, join as join5, resolve as resolve3 } from "path";
|
|
891
|
+
|
|
892
|
+
// src/cli/lib/git/process.ts
|
|
893
|
+
import { sync as spawnSync } from "cross-spawn";
|
|
894
|
+
var MAX_GIT_BUFFER = 512 * 1024 * 1024;
|
|
895
|
+
var GitError = class extends Error {
|
|
896
|
+
constructor(message, code) {
|
|
897
|
+
super(message);
|
|
898
|
+
this.code = code;
|
|
899
|
+
this.name = "GitError";
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
function runGit(cwd, args, opts = {}) {
|
|
903
|
+
const result = spawnSync("git", args, {
|
|
904
|
+
cwd,
|
|
905
|
+
input: opts.input,
|
|
906
|
+
maxBuffer: MAX_GIT_BUFFER,
|
|
907
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
908
|
+
env: {
|
|
909
|
+
...process.env,
|
|
910
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
911
|
+
LC_ALL: "C",
|
|
912
|
+
...opts.env ?? {}
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
if (result.error) {
|
|
916
|
+
const code = result.error.code;
|
|
917
|
+
if (code === "ENOENT") {
|
|
918
|
+
throw new GitError(
|
|
919
|
+
"git is not installed or not on PATH \u2014 install git and retry.",
|
|
920
|
+
"git_not_installed"
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
throw new GitError(`git ${args[0]} failed: ${result.error.message}`);
|
|
924
|
+
}
|
|
925
|
+
const status3 = result.status ?? 1;
|
|
926
|
+
if (status3 !== 0 && !opts.allowFail) {
|
|
927
|
+
const stderr = result.stderr?.toString("utf-8").trim() ?? "";
|
|
928
|
+
throw new GitError(`git ${args.join(" ")} exited ${status3}${stderr ? `: ${stderr}` : ""}`);
|
|
929
|
+
}
|
|
930
|
+
return {
|
|
931
|
+
stdout: result.stdout ?? Buffer.alloc(0),
|
|
932
|
+
stderr: result.stderr ?? Buffer.alloc(0),
|
|
933
|
+
status: status3
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
function gitLine(cwd, args) {
|
|
937
|
+
return runGit(cwd, args).stdout.toString("utf-8").trim();
|
|
938
|
+
}
|
|
939
|
+
function splitNulFields(buffer) {
|
|
940
|
+
const fields = [];
|
|
941
|
+
let start = 0;
|
|
942
|
+
for (let index = 0; index < buffer.length; index++) {
|
|
943
|
+
if (buffer[index] === 0) {
|
|
944
|
+
fields.push(buffer.toString("utf-8", start, index));
|
|
945
|
+
start = index + 1;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (start < buffer.length) fields.push(buffer.toString("utf-8", start));
|
|
949
|
+
return fields;
|
|
950
|
+
}
|
|
951
|
+
function parseGitVersion(output) {
|
|
952
|
+
const match = /(\d+)\.(\d+)/.exec(output);
|
|
953
|
+
return match ? [Number(match[1]), Number(match[2])] : null;
|
|
954
|
+
}
|
|
955
|
+
function gitMeetsFloor(version) {
|
|
956
|
+
if (!version) return true;
|
|
957
|
+
const [major, minor] = version;
|
|
958
|
+
return major > 2 || major === 2 && minor >= 29;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// src/cli/lib/git/repository.ts
|
|
962
|
+
function isPlausibleBranchName(name) {
|
|
963
|
+
if (!name || name === "HEAD" || name === "@") return false;
|
|
964
|
+
if (name.startsWith("/") || name.startsWith("-") || name.startsWith(".")) return false;
|
|
965
|
+
if (name.endsWith("/") || name.endsWith(".") || name.endsWith(".lock")) return false;
|
|
966
|
+
if (name.includes("/.") || name.includes(".lock/")) return false;
|
|
967
|
+
return !/(\.\.|\/\/|@\{|[\x00-\x20\x7f~^:?*[\\])/.test(name);
|
|
968
|
+
}
|
|
969
|
+
function assertSyncableRepo(cwd) {
|
|
970
|
+
const inside = runGit(cwd, ["rev-parse", "--is-inside-work-tree"], { allowFail: true });
|
|
971
|
+
if (inside.status !== 0 || inside.stdout.toString().trim() !== "true") {
|
|
972
|
+
throw new InputError(
|
|
973
|
+
`${cwd} is not a git repository. DeepSpace apps are scaffolded as git repos \u2014 run \`git init\` first.`,
|
|
974
|
+
"not_git_repo"
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
const versionOutput = runGit(cwd, ["--version"], { allowFail: true }).stdout.toString("utf-8").trim();
|
|
978
|
+
if (!gitMeetsFloor(parseGitVersion(versionOutput))) {
|
|
979
|
+
throw gitTooOld(versionOutput);
|
|
980
|
+
}
|
|
981
|
+
const formatResult = runGit(cwd, ["rev-parse", "--show-object-format"], { allowFail: true });
|
|
982
|
+
const format = formatResult.stdout.toString("utf-8").trim();
|
|
983
|
+
if (formatResult.status !== 0 || format.startsWith("--")) throw gitTooOld(versionOutput);
|
|
984
|
+
if (format !== "sha1") {
|
|
985
|
+
throw new InputError(
|
|
986
|
+
`This repo uses object format "${format}" \u2014 DeepSpace version control supports sha1 repos only (git's default) for now.`,
|
|
987
|
+
"unsupported_object_format"
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
if (gitLine(cwd, ["rev-parse", "--is-shallow-repository"]) === "true") {
|
|
991
|
+
throw new InputError(
|
|
992
|
+
"This is a shallow clone. Run `git fetch --unshallow` before pushing to DeepSpace.",
|
|
993
|
+
"shallow_repo"
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function gitTooOld(versionOutput) {
|
|
998
|
+
return new InputError(
|
|
999
|
+
`DeepSpace version control needs git \u2265 2.29 \u2014 found ${versionOutput || "an unknown git version"}. Upgrade git and retry.`,
|
|
1000
|
+
"git_too_old"
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
function repoToplevel(cwd) {
|
|
1004
|
+
return gitLine(cwd, ["rev-parse", "--show-toplevel"]);
|
|
1005
|
+
}
|
|
1006
|
+
function currentBranch(cwd) {
|
|
1007
|
+
const result = runGit(cwd, ["symbolic-ref", "--short", "-q", "HEAD"], { allowFail: true });
|
|
1008
|
+
const name = result.stdout.toString("utf-8").trim();
|
|
1009
|
+
return result.status === 0 && name ? name : null;
|
|
1010
|
+
}
|
|
1011
|
+
function resolveCommit(cwd, ref) {
|
|
1012
|
+
const result = runGit(cwd, ["rev-parse", "-q", "--verify", `${ref}^{commit}`], {
|
|
1013
|
+
allowFail: true
|
|
1014
|
+
});
|
|
1015
|
+
const oid = result.stdout.toString("utf-8").trim();
|
|
1016
|
+
return result.status === 0 && /^[0-9a-f]{40}$/.test(oid) ? oid : null;
|
|
1017
|
+
}
|
|
1018
|
+
function isWorkTreeClean(cwd) {
|
|
1019
|
+
return gitLine(cwd, ["status", "--porcelain"]) === "";
|
|
1020
|
+
}
|
|
1021
|
+
function updateRef(cwd, ref, oid) {
|
|
1022
|
+
runGit(cwd, ["update-ref", ref, oid]);
|
|
1023
|
+
}
|
|
1024
|
+
function isAncestor(cwd, ancestor, descendant) {
|
|
1025
|
+
return runGit(cwd, ["merge-base", "--is-ancestor", ancestor, descendant], { allowFail: true }).status === 0;
|
|
1026
|
+
}
|
|
1027
|
+
function mergeBase(cwd, left, right) {
|
|
1028
|
+
const result = runGit(cwd, ["merge-base", left, right], { allowFail: true });
|
|
1029
|
+
if (result.status !== 0) return null;
|
|
1030
|
+
const oid = result.stdout.toString("utf-8").trim();
|
|
1031
|
+
return /^[0-9a-f]{40}$/.test(oid) ? oid : null;
|
|
1032
|
+
}
|
|
1033
|
+
function fastForwardCurrentBranch(cwd, oid) {
|
|
1034
|
+
runGit(cwd, ["merge", "--ff-only", oid]);
|
|
1035
|
+
}
|
|
1036
|
+
function checkoutHead(cwd) {
|
|
1037
|
+
runGit(cwd, ["reset", "--hard", "--quiet"]);
|
|
1038
|
+
}
|
|
1039
|
+
function switchToNewBranch(cwd, name, oid) {
|
|
1040
|
+
runGit(cwd, ["switch", "--quiet", "-c", name, oid]);
|
|
1041
|
+
}
|
|
1042
|
+
function diffNameOnly(cwd, base, head) {
|
|
1043
|
+
return splitNulFields(
|
|
1044
|
+
runGit(cwd, [
|
|
1045
|
+
"-c",
|
|
1046
|
+
"core.quotePath=false",
|
|
1047
|
+
"diff",
|
|
1048
|
+
"--name-only",
|
|
1049
|
+
"--no-renames",
|
|
1050
|
+
"-z",
|
|
1051
|
+
base,
|
|
1052
|
+
head
|
|
1053
|
+
]).stdout
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
function revListCount(cwd, exclude, include) {
|
|
1057
|
+
const count = Number(gitLine(cwd, ["rev-list", "--count", `${exclude}..${include}`]));
|
|
1058
|
+
return Number.isFinite(count) ? count : 0;
|
|
1059
|
+
}
|
|
1060
|
+
function addWorktreeNewBranch(cwd, dir, branch, oid) {
|
|
1061
|
+
runGit(cwd, ["worktree", "add", "--quiet", "-b", branch, dir, oid]);
|
|
1062
|
+
}
|
|
1063
|
+
function listWorktrees(cwd) {
|
|
1064
|
+
const result = runGit(cwd, ["worktree", "list", "--porcelain"], { allowFail: true });
|
|
1065
|
+
if (result.status !== 0) return [];
|
|
1066
|
+
const worktrees = [];
|
|
1067
|
+
let current = null;
|
|
1068
|
+
for (const line of result.stdout.toString("utf-8").split("\n")) {
|
|
1069
|
+
if (line.startsWith("worktree ")) {
|
|
1070
|
+
current = { path: line.slice("worktree ".length), branch: null };
|
|
1071
|
+
worktrees.push(current);
|
|
1072
|
+
} else if (line.startsWith("branch ") && current) {
|
|
1073
|
+
const ref = line.slice("branch ".length);
|
|
1074
|
+
current.branch = ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
return worktrees;
|
|
1078
|
+
}
|
|
1079
|
+
function ensureLocalExclude(cwd, pattern) {
|
|
1080
|
+
const commonDir = resolve3(cwd, gitLine(cwd, ["rev-parse", "--git-common-dir"]));
|
|
1081
|
+
const excludePath = join5(commonDir, "info", "exclude");
|
|
1082
|
+
let current = "";
|
|
1083
|
+
try {
|
|
1084
|
+
current = readFileSync6(excludePath, "utf-8");
|
|
1085
|
+
} catch {
|
|
1086
|
+
}
|
|
1087
|
+
if (current.split("\n").some((line) => line.trim() === pattern)) return;
|
|
1088
|
+
try {
|
|
1089
|
+
mkdirSync3(dirname2(excludePath), { recursive: true });
|
|
1090
|
+
appendFileSync(
|
|
1091
|
+
excludePath,
|
|
1092
|
+
`${current.endsWith("\n") || current === "" ? "" : "\n"}${pattern}
|
|
1093
|
+
`
|
|
1094
|
+
);
|
|
1095
|
+
} catch {
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// src/cli/lib/launch-config.ts
|
|
879
1100
|
function writeLaunchFile(launchPath, config) {
|
|
880
|
-
|
|
1101
|
+
mkdirSync4(dirname3(launchPath), { recursive: true });
|
|
881
1102
|
const tmpPath = `${launchPath}.${process.pid}.tmp`;
|
|
882
1103
|
writeFileSync4(tmpPath, JSON.stringify(config, null, 2) + "\n");
|
|
883
1104
|
try {
|
|
@@ -893,14 +1114,14 @@ function writeLaunchFile(launchPath, config) {
|
|
|
893
1114
|
function readLaunchFile(launchPath) {
|
|
894
1115
|
const empty = { version: "0.0.1", configurations: [] };
|
|
895
1116
|
if (!existsSync5(launchPath)) return empty;
|
|
896
|
-
const raw =
|
|
1117
|
+
const raw = readFileSync7(launchPath, "utf-8").trim();
|
|
897
1118
|
if (!raw) return empty;
|
|
898
1119
|
const parsed = JSON.parse(raw);
|
|
899
1120
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.configurations)) return null;
|
|
900
1121
|
return parsed;
|
|
901
1122
|
}
|
|
902
1123
|
function writeLaunchConfigIfMissing(appDir, appName, port, opts = {}) {
|
|
903
|
-
const launchPath =
|
|
1124
|
+
const launchPath = join6(appDir, ".claude", "launch.json");
|
|
904
1125
|
try {
|
|
905
1126
|
const config = readLaunchFile(launchPath);
|
|
906
1127
|
if (!config) return;
|
|
@@ -941,7 +1162,7 @@ function writeLaunchConfigIfMissing(appDir, appName, port, opts = {}) {
|
|
|
941
1162
|
}
|
|
942
1163
|
function resolveAppLaunchPort(appDir) {
|
|
943
1164
|
try {
|
|
944
|
-
const config = readLaunchFile(
|
|
1165
|
+
const config = readLaunchFile(join6(appDir, ".claude", "launch.json"));
|
|
945
1166
|
if (!config) return null;
|
|
946
1167
|
const appName = detectAppName(appDir) ?? basename(appDir);
|
|
947
1168
|
const entry = config.configurations.find((c) => c?.name === appName) ?? (config.configurations.length === 1 ? config.configurations[0] : void 0);
|
|
@@ -952,32 +1173,55 @@ function resolveAppLaunchPort(appDir) {
|
|
|
952
1173
|
}
|
|
953
1174
|
}
|
|
954
1175
|
function detectClaudeWorktree(appDir) {
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1176
|
+
try {
|
|
1177
|
+
const worktreeRoot = canonicalPath(repoToplevel(appDir));
|
|
1178
|
+
const registered = new Set(
|
|
1179
|
+
listWorktrees(appDir).map((worktree) => canonicalPath(worktree.path))
|
|
1180
|
+
);
|
|
1181
|
+
if (!registered.has(worktreeRoot)) return null;
|
|
1182
|
+
const worktreesDir = dirname3(worktreeRoot);
|
|
1183
|
+
const claudeDir = dirname3(worktreesDir);
|
|
1184
|
+
if (basename(worktreesDir) !== "worktrees" || basename(claudeDir) !== ".claude") return null;
|
|
1185
|
+
const ownerRoot = canonicalPath(dirname3(claudeDir));
|
|
1186
|
+
if (!registered.has(ownerRoot)) return null;
|
|
1187
|
+
return { mainRepoRoot: ownerRoot, worktreeName: basename(worktreeRoot) };
|
|
1188
|
+
} catch {
|
|
1189
|
+
return null;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
function canonicalPath(path) {
|
|
1193
|
+
try {
|
|
1194
|
+
return realpathSync(path);
|
|
1195
|
+
} catch {
|
|
1196
|
+
return resolve4(path);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
function detectLinkedWorktree(appDir) {
|
|
1200
|
+
try {
|
|
1201
|
+
const worktreeRoot = canonicalPath(repoToplevel(appDir));
|
|
1202
|
+
const registered = listWorktrees(appDir).map((worktree) => canonicalPath(worktree.path));
|
|
1203
|
+
const currentIndex = registered.indexOf(worktreeRoot);
|
|
1204
|
+
if (currentIndex <= 0 || !registered[0]) return null;
|
|
1205
|
+
return { worktreeRoot, mainRepoRoot: registered[0] };
|
|
1206
|
+
} catch {
|
|
1207
|
+
return null;
|
|
963
1208
|
}
|
|
964
|
-
return null;
|
|
965
1209
|
}
|
|
966
1210
|
var WORKTREE_ENTRY_PREFIX = "wt-";
|
|
967
1211
|
var WORKTREE_PORT_MIN = 5180;
|
|
968
|
-
var WORKTREE_PORT_RANGE =
|
|
969
|
-
function deriveWorktreePort(
|
|
1212
|
+
var WORKTREE_PORT_RANGE = 1e3;
|
|
1213
|
+
function deriveWorktreePort(worktreeKey) {
|
|
970
1214
|
let hash = 0;
|
|
971
|
-
for (const ch of
|
|
1215
|
+
for (const ch of worktreeKey) hash = hash * 31 + ch.charCodeAt(0) >>> 0;
|
|
972
1216
|
return WORKTREE_PORT_MIN + hash % WORKTREE_PORT_RANGE;
|
|
973
1217
|
}
|
|
974
1218
|
function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
975
|
-
const launchPath =
|
|
1219
|
+
const launchPath = join6(mainRepoRoot, ".claude", "launch.json");
|
|
976
1220
|
const entryName = WORKTREE_ENTRY_PREFIX + worktreeName;
|
|
977
1221
|
try {
|
|
978
1222
|
const config = readLaunchFile(launchPath);
|
|
979
1223
|
if (!config) return null;
|
|
980
|
-
const worktreesRoot =
|
|
1224
|
+
const worktreesRoot = join6(mainRepoRoot, ".claude", "worktrees") + sep;
|
|
981
1225
|
config.configurations = config.configurations.filter((c) => {
|
|
982
1226
|
if (c?.name === entryName) return false;
|
|
983
1227
|
const isStaleWorktreeEntry = typeof c?.name === "string" && c.name.startsWith(WORKTREE_ENTRY_PREFIX) && typeof c.cwd === "string" && isAbsolute2(c.cwd) && c.cwd.startsWith(worktreesRoot) && !existsSync5(c.cwd);
|
|
@@ -1001,7 +1245,7 @@ function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
|
1001
1245
|
// the Claude Code desktop launch.json reference alongside `port`) and
|
|
1002
1246
|
// verified end-to-end: with it set, the preview server builds from the
|
|
1003
1247
|
// worktree instead of the session's main repo.
|
|
1004
|
-
cwd:
|
|
1248
|
+
cwd: resolve4(appDir)
|
|
1005
1249
|
});
|
|
1006
1250
|
writeLaunchFile(launchPath, config);
|
|
1007
1251
|
return { entryName, port };
|
|
@@ -1010,22 +1254,25 @@ function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
|
1010
1254
|
}
|
|
1011
1255
|
}
|
|
1012
1256
|
function resolveWorktreePort(appDir) {
|
|
1013
|
-
const
|
|
1014
|
-
if (!
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1257
|
+
const linked = detectLinkedWorktree(appDir);
|
|
1258
|
+
if (!linked) return null;
|
|
1259
|
+
const claude = detectClaudeWorktree(appDir);
|
|
1260
|
+
if (claude) {
|
|
1261
|
+
try {
|
|
1262
|
+
const config = readLaunchFile(join6(claude.mainRepoRoot, ".claude", "launch.json"));
|
|
1263
|
+
const entry = config?.configurations.find(
|
|
1264
|
+
(c) => c?.name === WORKTREE_ENTRY_PREFIX + claude.worktreeName
|
|
1265
|
+
);
|
|
1266
|
+
const port = Number(entry?.port);
|
|
1267
|
+
if (Number.isInteger(port) && port > 0) return port;
|
|
1268
|
+
} catch {
|
|
1269
|
+
}
|
|
1023
1270
|
}
|
|
1024
|
-
return deriveWorktreePort(
|
|
1271
|
+
return deriveWorktreePort(linked.worktreeRoot);
|
|
1025
1272
|
}
|
|
1026
1273
|
|
|
1027
1274
|
// src/cli/lib/dev-vars.ts
|
|
1028
|
-
import { existsSync as existsSync7, readFileSync as
|
|
1275
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
|
|
1029
1276
|
|
|
1030
1277
|
// src/cli/lib/app-tokens.ts
|
|
1031
1278
|
async function fetchPublicKey(authUrl) {
|
|
@@ -1542,8 +1789,8 @@ function writeSecretFileSync(path, content) {
|
|
|
1542
1789
|
}
|
|
1543
1790
|
|
|
1544
1791
|
// src/cli/lib/wrangler-env.ts
|
|
1545
|
-
import { existsSync as existsSync6, readFileSync as
|
|
1546
|
-
import { join as
|
|
1792
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
1793
|
+
import { join as join7 } from "path";
|
|
1547
1794
|
import { parse as parseToml3, stringify as stringifyToml } from "smol-toml";
|
|
1548
1795
|
|
|
1549
1796
|
// src/server/rooms/app-name.ts
|
|
@@ -1643,11 +1890,11 @@ function resolveAppNameForEnv(config, envName) {
|
|
|
1643
1890
|
return resolveAppName(envBlock.name);
|
|
1644
1891
|
}
|
|
1645
1892
|
function devVarsPathFor(appDir, envName, opts = {}) {
|
|
1646
|
-
if (opts.sharedDevVarsCache || !envName) return
|
|
1647
|
-
return
|
|
1893
|
+
if (opts.sharedDevVarsCache || !envName) return join7(appDir, ".dev.vars");
|
|
1894
|
+
return join7(appDir, `.dev.vars.${envName}`);
|
|
1648
1895
|
}
|
|
1649
1896
|
function legacyEnvDevVarsPathFor(appDir, envName) {
|
|
1650
|
-
return envName ?
|
|
1897
|
+
return envName ? join7(appDir, `.dev.vars.${envName}`) : void 0;
|
|
1651
1898
|
}
|
|
1652
1899
|
function warnIfLegacyEnvDevVarsExists(appDir, envName, warn = console.warn) {
|
|
1653
1900
|
const legacyPath = legacyEnvDevVarsPathFor(appDir, envName);
|
|
@@ -1666,7 +1913,7 @@ function prepareWranglerEnvConfig(appDir, envName, opts = {}) {
|
|
|
1666
1913
|
const resolved = resolveAppNameForEnv(config, envName);
|
|
1667
1914
|
if (!resolved.ok) throw new Error(resolved.reason);
|
|
1668
1915
|
const flattened = flattenWranglerEnvConfig(config, envName);
|
|
1669
|
-
const configPath =
|
|
1916
|
+
const configPath = join7(
|
|
1670
1917
|
appDir,
|
|
1671
1918
|
`.wrangler.deepspace.${safeGeneratedConfigSegment(envName)}.${process.pid}.${++generatedConfigSeq}.toml`
|
|
1672
1919
|
);
|
|
@@ -1727,8 +1974,8 @@ var WranglerConfigError = class extends Error {
|
|
|
1727
1974
|
}
|
|
1728
1975
|
};
|
|
1729
1976
|
function readWranglerConfig(appDir) {
|
|
1730
|
-
const path =
|
|
1731
|
-
const raw =
|
|
1977
|
+
const path = join7(appDir, "wrangler.toml");
|
|
1978
|
+
const raw = readFileSync8(path, "utf-8");
|
|
1732
1979
|
try {
|
|
1733
1980
|
return parseToml3(raw);
|
|
1734
1981
|
} catch (err) {
|
|
@@ -1736,7 +1983,7 @@ function readWranglerConfig(appDir) {
|
|
|
1736
1983
|
}
|
|
1737
1984
|
}
|
|
1738
1985
|
function hasWranglerConfig(appDir) {
|
|
1739
|
-
return existsSync6(
|
|
1986
|
+
return existsSync6(join7(appDir, "wrangler.toml"));
|
|
1740
1987
|
}
|
|
1741
1988
|
|
|
1742
1989
|
// src/cli/lib/dev-vars.ts
|
|
@@ -1901,7 +2148,7 @@ async function writeDevVars(appDir, ownerId, callerJwt, wranglerEnv, opts = {})
|
|
|
1901
2148
|
const devVarsPath = devVarsPathFor(appDir, wranglerEnv, {
|
|
1902
2149
|
sharedDevVarsCache: useSharedDevVarsCache
|
|
1903
2150
|
});
|
|
1904
|
-
const existing = existsSync7(devVarsPath) ?
|
|
2151
|
+
const existing = existsSync7(devVarsPath) ? readFileSync9(devVarsPath, "utf-8") : "";
|
|
1905
2152
|
const custom = opts.generatedSecretsCache ?? extractCustomDevVars(stripGeneratedSecretsCache(existing).trimEnd());
|
|
1906
2153
|
let body;
|
|
1907
2154
|
if (!custom) {
|
|
@@ -1923,22 +2170,22 @@ ${custom}
|
|
|
1923
2170
|
}
|
|
1924
2171
|
|
|
1925
2172
|
// src/cli/lib/install-status.ts
|
|
1926
|
-
import { existsSync as existsSync9, readFileSync as
|
|
1927
|
-
import { dirname as
|
|
2173
|
+
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
|
|
2174
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
1928
2175
|
|
|
1929
2176
|
// src/cli/lib/package-manager.ts
|
|
1930
|
-
import { existsSync as existsSync8, readFileSync as
|
|
1931
|
-
import { join as
|
|
2177
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
|
|
2178
|
+
import { join as join8 } from "path";
|
|
1932
2179
|
function detectPackageManager(appDir) {
|
|
1933
2180
|
try {
|
|
1934
|
-
const pkg2 = JSON.parse(
|
|
2181
|
+
const pkg2 = JSON.parse(readFileSync10(join8(appDir, "package.json"), "utf-8"));
|
|
1935
2182
|
const name = typeof pkg2.packageManager === "string" ? pkg2.packageManager.split("@")[0] : "";
|
|
1936
2183
|
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
|
|
1937
2184
|
} catch {
|
|
1938
2185
|
}
|
|
1939
|
-
if (existsSync8(
|
|
1940
|
-
if (existsSync8(
|
|
1941
|
-
if (existsSync8(
|
|
2186
|
+
if (existsSync8(join8(appDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
2187
|
+
if (existsSync8(join8(appDir, "yarn.lock"))) return "yarn";
|
|
2188
|
+
if (existsSync8(join8(appDir, "bun.lock")) || existsSync8(join8(appDir, "bun.lockb"))) return "bun";
|
|
1942
2189
|
return "npm";
|
|
1943
2190
|
}
|
|
1944
2191
|
function installCommand(appDir) {
|
|
@@ -1948,10 +2195,10 @@ function installCommand(appDir) {
|
|
|
1948
2195
|
// src/cli/lib/install-status.ts
|
|
1949
2196
|
function installState(appDir) {
|
|
1950
2197
|
if (resolvesDeepspace(appDir)) return "ready";
|
|
1951
|
-
const sentinelDir =
|
|
1952
|
-
if (existsSync9(
|
|
1953
|
-
if (existsSync9(
|
|
1954
|
-
if (!existsSync9(
|
|
2198
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
2199
|
+
if (existsSync9(join9(sentinelDir, "install.err"))) return "failed";
|
|
2200
|
+
if (existsSync9(join9(sentinelDir, "install.started"))) {
|
|
2201
|
+
if (!existsSync9(join9(sentinelDir, "install.done")) && installWorkerAlive(sentinelDir)) {
|
|
1955
2202
|
return "installing";
|
|
1956
2203
|
}
|
|
1957
2204
|
return "failed";
|
|
@@ -1963,16 +2210,16 @@ function tailHint(logPath, platform = process.platform) {
|
|
|
1963
2210
|
}
|
|
1964
2211
|
function ensureInstallReady(appDir) {
|
|
1965
2212
|
if (resolvesDeepspace(appDir)) return;
|
|
1966
|
-
const sentinelDir =
|
|
1967
|
-
const errPath =
|
|
1968
|
-
const startedPath =
|
|
1969
|
-
const donePath =
|
|
1970
|
-
const logPath =
|
|
2213
|
+
const sentinelDir = join9(appDir, ".deepspace");
|
|
2214
|
+
const errPath = join9(sentinelDir, "install.err");
|
|
2215
|
+
const startedPath = join9(sentinelDir, "install.started");
|
|
2216
|
+
const donePath = join9(sentinelDir, "install.done");
|
|
2217
|
+
const logPath = join9(sentinelDir, "install.log");
|
|
1971
2218
|
if (existsSync9(errPath)) {
|
|
1972
|
-
const detail =
|
|
2219
|
+
const detail = readFileSync11(errPath, "utf-8").trimEnd();
|
|
1973
2220
|
const log28 = existsSync9(logPath) ? ` Full log: ${logPath}.` : "";
|
|
1974
2221
|
throw new Refusal(
|
|
1975
|
-
`
|
|
2222
|
+
`Dependency install failed: ${detail}.${log28} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
1976
2223
|
"install_failed",
|
|
1977
2224
|
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
1978
2225
|
);
|
|
@@ -1981,14 +2228,14 @@ function ensureInstallReady(appDir) {
|
|
|
1981
2228
|
if (!installWorkerAlive(sentinelDir)) {
|
|
1982
2229
|
const log28 = existsSync9(logPath) ? ` See what happened: ${logPath}.` : "";
|
|
1983
2230
|
throw new Refusal(
|
|
1984
|
-
`The
|
|
2231
|
+
`The dependency install is no longer running and never finished.${log28} Run \`npm install\` (or \`bun install\`) manually, then retry.`,
|
|
1985
2232
|
"install_failed",
|
|
1986
2233
|
{ action: { cwd: appDir, argv: [detectPackageManager(appDir), "install"] } }
|
|
1987
2234
|
);
|
|
1988
2235
|
}
|
|
1989
2236
|
const tail = existsSync9(logPath) ? ` Tail progress: ${tailHint(logPath)}.` : "";
|
|
1990
2237
|
throw new Refusal(
|
|
1991
|
-
`Dependencies are still installing
|
|
2238
|
+
`Dependencies are still installing.${tail} Retry once it finishes.`,
|
|
1992
2239
|
"install_in_progress"
|
|
1993
2240
|
);
|
|
1994
2241
|
}
|
|
@@ -2003,16 +2250,16 @@ function ensureInstallReady(appDir) {
|
|
|
2003
2250
|
function resolvesDeepspace(fromDir) {
|
|
2004
2251
|
let dir = fromDir;
|
|
2005
2252
|
for (; ; ) {
|
|
2006
|
-
if (existsSync9(
|
|
2007
|
-
const parent =
|
|
2253
|
+
if (existsSync9(join9(dir, "node_modules", "deepspace", "package.json"))) return true;
|
|
2254
|
+
const parent = dirname4(dir);
|
|
2008
2255
|
if (parent === dir) return false;
|
|
2009
2256
|
dir = parent;
|
|
2010
2257
|
}
|
|
2011
2258
|
}
|
|
2012
2259
|
function installWorkerAlive(sentinelDir) {
|
|
2013
|
-
const pidPath =
|
|
2260
|
+
const pidPath = join9(sentinelDir, "install.pid");
|
|
2014
2261
|
if (!existsSync9(pidPath)) return false;
|
|
2015
|
-
const pid = Number(
|
|
2262
|
+
const pid = Number(readFileSync11(pidPath, "utf-8").trim());
|
|
2016
2263
|
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
2017
2264
|
try {
|
|
2018
2265
|
process.kill(pid, 0);
|
|
@@ -2026,7 +2273,7 @@ function installWorkerAlive(sentinelDir) {
|
|
|
2026
2273
|
import { execFileSync } from "child_process";
|
|
2027
2274
|
import { existsSync as existsSync10 } from "fs";
|
|
2028
2275
|
import * as nodeModule from "module";
|
|
2029
|
-
import { resolve as
|
|
2276
|
+
import { resolve as resolve5 } from "path";
|
|
2030
2277
|
function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.registerHooks === "function") {
|
|
2031
2278
|
if (hasRegisterHooks) return;
|
|
2032
2279
|
throw new Refusal(
|
|
@@ -2037,7 +2284,7 @@ function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.regi
|
|
|
2037
2284
|
var DLL_NOT_FOUND = -1073741515;
|
|
2038
2285
|
function preflightWindowsWorkerd(appDir) {
|
|
2039
2286
|
if (process.platform !== "win32") return;
|
|
2040
|
-
const workerdExe =
|
|
2287
|
+
const workerdExe = resolve5(
|
|
2041
2288
|
appDir,
|
|
2042
2289
|
"node_modules",
|
|
2043
2290
|
"@cloudflare",
|
|
@@ -2061,7 +2308,7 @@ function preflightWindowsWorkerd(appDir) {
|
|
|
2061
2308
|
|
|
2062
2309
|
// src/cli/lib/macos-junk.ts
|
|
2063
2310
|
import { readdirSync as readdirSync2, rmSync as rmSync3 } from "fs";
|
|
2064
|
-
import { join as
|
|
2311
|
+
import { join as join10 } from "path";
|
|
2065
2312
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".wrangler", ".vite"]);
|
|
2066
2313
|
function removeMacosJunk(dir) {
|
|
2067
2314
|
let removed = 0;
|
|
@@ -2072,7 +2319,7 @@ function removeMacosJunk(dir) {
|
|
|
2072
2319
|
return removed;
|
|
2073
2320
|
}
|
|
2074
2321
|
for (const entry of entries) {
|
|
2075
|
-
const path =
|
|
2322
|
+
const path = join10(dir, entry.name);
|
|
2076
2323
|
if (entry.isDirectory()) {
|
|
2077
2324
|
if (!SKIP_DIRS.has(entry.name)) removed += removeMacosJunk(path);
|
|
2078
2325
|
continue;
|
|
@@ -2090,7 +2337,7 @@ function removeMacosJunk(dir) {
|
|
|
2090
2337
|
|
|
2091
2338
|
// src/cli/lib/schema-lint.ts
|
|
2092
2339
|
import { existsSync as existsSync11 } from "fs";
|
|
2093
|
-
import { join as
|
|
2340
|
+
import { join as join11 } from "path";
|
|
2094
2341
|
import { pathToFileURL } from "url";
|
|
2095
2342
|
|
|
2096
2343
|
// src/server/schemas/registry.ts
|
|
@@ -2146,12 +2393,12 @@ function lintSchema(schema) {
|
|
|
2146
2393
|
|
|
2147
2394
|
// src/cli/lib/schema-lint.ts
|
|
2148
2395
|
async function lintProjectSchemas(appDir) {
|
|
2149
|
-
const schemasPath =
|
|
2396
|
+
const schemasPath = join11(appDir, "src", "schemas.ts");
|
|
2150
2397
|
if (!existsSync11(schemasPath)) return null;
|
|
2151
2398
|
let schemas;
|
|
2152
2399
|
try {
|
|
2153
2400
|
const esbuild = await import("esbuild");
|
|
2154
|
-
const outFile =
|
|
2401
|
+
const outFile = join11(appDir, ".wrangler", "deploy", "schemas.lint.bundle.mjs");
|
|
2155
2402
|
await esbuild.build({
|
|
2156
2403
|
entryPoints: [schemasPath],
|
|
2157
2404
|
bundle: true,
|
|
@@ -2256,7 +2503,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2256
2503
|
};
|
|
2257
2504
|
preflightNodeVersion("dev start");
|
|
2258
2505
|
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
2259
|
-
const start =
|
|
2506
|
+
const start = resolve6(args.dir ?? ".");
|
|
2260
2507
|
const appDir = findAppDir(start);
|
|
2261
2508
|
if (!appDir) {
|
|
2262
2509
|
throw new Refusal(noAppDirMessage(start, findChildApps(start)), "no_app_dir");
|
|
@@ -2264,8 +2511,10 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2264
2511
|
const junk = removeMacosJunk(appDir);
|
|
2265
2512
|
if (junk > 0) say(`Removed ${junk} macOS metadata file(s) (._*, .DS_Store)`);
|
|
2266
2513
|
const worktree = detectClaudeWorktree(appDir);
|
|
2514
|
+
const linkedWorktree = detectLinkedWorktree(appDir);
|
|
2267
2515
|
const explicitPort = Boolean(args.port);
|
|
2268
|
-
|
|
2516
|
+
const configuredPort = explicitPort || Boolean(process.env.DEEPSPACE_PORT);
|
|
2517
|
+
let port = linkedWorktree && !configuredPort ? deriveWorktreePort(linkedWorktree.worktreeRoot) : resolvePort(args.port);
|
|
2269
2518
|
if (worktree) {
|
|
2270
2519
|
const upserted = upsertWorktreeLaunchConfig(
|
|
2271
2520
|
worktree.mainRepoRoot,
|
|
@@ -2273,7 +2522,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2273
2522
|
appDir,
|
|
2274
2523
|
{
|
|
2275
2524
|
port,
|
|
2276
|
-
probePort: !
|
|
2525
|
+
probePort: !configuredPort,
|
|
2277
2526
|
extraArgs: [...wranglerEnv ? ["--env", wranglerEnv] : []]
|
|
2278
2527
|
}
|
|
2279
2528
|
);
|
|
@@ -2284,12 +2533,7 @@ var dev_default = defineDeepspaceCommand({
|
|
|
2284
2533
|
);
|
|
2285
2534
|
} else if (!args.json) {
|
|
2286
2535
|
console.warn(
|
|
2287
|
-
`Warning: could not update ${
|
|
2288
|
-
);
|
|
2289
|
-
}
|
|
2290
|
-
if (!explicitPort && process.env.DEEPSPACE_PORT) {
|
|
2291
|
-
say(
|
|
2292
|
-
`Ignoring DEEPSPACE_PORT=${process.env.DEEPSPACE_PORT} inside a worktree \u2014 using per-worktree port ${port}. Pass --port to override.`
|
|
2536
|
+
`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.`
|
|
2293
2537
|
);
|
|
2294
2538
|
}
|
|
2295
2539
|
}
|
|
@@ -2388,15 +2632,15 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
2388
2632
|
});
|
|
2389
2633
|
|
|
2390
2634
|
// src/cli/commands/kill.ts
|
|
2391
|
-
import { spawnSync } from "child_process";
|
|
2392
|
-
import { readFileSync as
|
|
2635
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
2636
|
+
import { readFileSync as readFileSync12, readdirSync as readdirSync3, readlinkSync } from "fs";
|
|
2393
2637
|
import { setTimeout as wait } from "timers/promises";
|
|
2394
2638
|
var SIGTERM_GRACE_MS = 1500;
|
|
2395
2639
|
var IS_WIN = process.platform === "win32";
|
|
2396
2640
|
function pickKillPort(opts) {
|
|
2397
2641
|
if (opts.explicit != null) return opts.explicit;
|
|
2398
|
-
if (opts.worktree != null) return opts.worktree;
|
|
2399
2642
|
if (opts.env != null) return opts.env;
|
|
2643
|
+
if (opts.worktree != null) return opts.worktree;
|
|
2400
2644
|
return opts.appLaunch ?? DEFAULT_PORT;
|
|
2401
2645
|
}
|
|
2402
2646
|
var kill_default = defineDeepspaceCommand({
|
|
@@ -2523,7 +2767,7 @@ function listenerPids(port) {
|
|
|
2523
2767
|
}
|
|
2524
2768
|
function runLsof(port) {
|
|
2525
2769
|
try {
|
|
2526
|
-
const r =
|
|
2770
|
+
const r = spawnSync2("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], {
|
|
2527
2771
|
encoding: "utf-8"
|
|
2528
2772
|
});
|
|
2529
2773
|
if (r.error) return { pids: [], ran: false };
|
|
@@ -2540,7 +2784,7 @@ function listenerPidsViaProc(port) {
|
|
|
2540
2784
|
for (const f of ["/proc/net/tcp", "/proc/net/tcp6"]) {
|
|
2541
2785
|
let data;
|
|
2542
2786
|
try {
|
|
2543
|
-
data =
|
|
2787
|
+
data = readFileSync12(f, "utf-8");
|
|
2544
2788
|
} catch {
|
|
2545
2789
|
continue;
|
|
2546
2790
|
}
|
|
@@ -2608,16 +2852,19 @@ function sweepByName(names) {
|
|
|
2608
2852
|
}
|
|
2609
2853
|
function runPgrep(pgrepArgs) {
|
|
2610
2854
|
try {
|
|
2611
|
-
const r =
|
|
2855
|
+
const r = spawnSync2("pgrep", pgrepArgs, { encoding: "utf-8" });
|
|
2612
2856
|
if (r.error) return { pids: [], ran: false };
|
|
2613
|
-
return {
|
|
2857
|
+
return {
|
|
2858
|
+
pids: r.status === 0 ? parsePidLines(r.stdout) : [],
|
|
2859
|
+
ran: r.status === 0 || r.status === 1
|
|
2860
|
+
};
|
|
2614
2861
|
} catch {
|
|
2615
2862
|
return { pids: [], ran: false };
|
|
2616
2863
|
}
|
|
2617
2864
|
}
|
|
2618
2865
|
function run(cmd) {
|
|
2619
2866
|
try {
|
|
2620
|
-
const r =
|
|
2867
|
+
const r = spawnSync2(cmd[0], cmd.slice(1), { encoding: "utf-8" });
|
|
2621
2868
|
if (r.status !== 0) return [];
|
|
2622
2869
|
return parsePidLines(r.stdout);
|
|
2623
2870
|
} catch {
|
|
@@ -2630,7 +2877,7 @@ function runPowershell(script) {
|
|
|
2630
2877
|
function runPowershellChecked(script) {
|
|
2631
2878
|
const exe = pwshOrPowershell();
|
|
2632
2879
|
try {
|
|
2633
|
-
const r =
|
|
2880
|
+
const r = spawnSync2(exe, ["-NoProfile", "-NonInteractive", "-Command", script], {
|
|
2634
2881
|
encoding: "utf-8"
|
|
2635
2882
|
});
|
|
2636
2883
|
if (r.error) return { pids: [], ran: false };
|
|
@@ -2640,7 +2887,7 @@ function runPowershellChecked(script) {
|
|
|
2640
2887
|
}
|
|
2641
2888
|
}
|
|
2642
2889
|
function pwshOrPowershell() {
|
|
2643
|
-
const probe =
|
|
2890
|
+
const probe = spawnSync2("pwsh", ["-NoProfile", "-Command", "$PSVersionTable.PSVersion.Major"], {
|
|
2644
2891
|
encoding: "utf-8"
|
|
2645
2892
|
});
|
|
2646
2893
|
if (probe.status === 0) return "pwsh";
|
|
@@ -2667,12 +2914,12 @@ function isAlive(pid) {
|
|
|
2667
2914
|
}
|
|
2668
2915
|
|
|
2669
2916
|
// src/cli/commands/test.ts
|
|
2670
|
-
import { resolve as
|
|
2671
|
-
import { sync as
|
|
2917
|
+
import { resolve as resolve7 } from "path";
|
|
2918
|
+
import { sync as spawnSync4 } from "cross-spawn";
|
|
2672
2919
|
|
|
2673
2920
|
// src/cli/lib/playwright.ts
|
|
2674
2921
|
import { execSync } from "child_process";
|
|
2675
|
-
import { sync as
|
|
2922
|
+
import { sync as spawnSync3 } from "cross-spawn";
|
|
2676
2923
|
function ensurePlaywright(appDir) {
|
|
2677
2924
|
try {
|
|
2678
2925
|
execSync("npx playwright --version", { cwd: appDir, stdio: "pipe" });
|
|
@@ -2683,7 +2930,7 @@ function ensurePlaywright(appDir) {
|
|
|
2683
2930
|
const isLinux = process.platform === "linux";
|
|
2684
2931
|
const isRoot = isLinux && typeof process.getuid === "function" && process.getuid() === 0;
|
|
2685
2932
|
const args = isRoot ? ["install", "--with-deps", "chromium"] : ["install", "chromium"];
|
|
2686
|
-
const result =
|
|
2933
|
+
const result = spawnSync3("npx", ["playwright", ...args], {
|
|
2687
2934
|
cwd: appDir,
|
|
2688
2935
|
stdio: "inherit"
|
|
2689
2936
|
});
|
|
@@ -2727,18 +2974,14 @@ var test_default = defineDeepspaceCommand({
|
|
|
2727
2974
|
preflightNodeVersion("test run");
|
|
2728
2975
|
const suite = args.suite ?? "default";
|
|
2729
2976
|
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
2730
|
-
const start =
|
|
2977
|
+
const start = resolve7(".");
|
|
2731
2978
|
const appDir = findAppDir(start);
|
|
2732
2979
|
if (!appDir) {
|
|
2733
2980
|
throw new Refusal(noAppDirMessage(start, findChildApps(start)), "no_app_dir");
|
|
2734
2981
|
}
|
|
2735
|
-
const
|
|
2982
|
+
const configuredPort = Boolean(args.port) || Boolean(process.env.DEEPSPACE_PORT);
|
|
2983
|
+
const worktreePort = configuredPort ? null : resolveWorktreePort(appDir);
|
|
2736
2984
|
const port = worktreePort ?? resolvePort(args.port);
|
|
2737
|
-
if (worktreePort && process.env.DEEPSPACE_PORT) {
|
|
2738
|
-
say(
|
|
2739
|
-
`Ignoring DEEPSPACE_PORT=${process.env.DEEPSPACE_PORT} inside a worktree \u2014 targeting per-worktree port ${port}. Pass --port to override.`
|
|
2740
|
-
);
|
|
2741
|
-
}
|
|
2742
2985
|
ensureInstallReady(appDir);
|
|
2743
2986
|
let token;
|
|
2744
2987
|
let ownerId;
|
|
@@ -2846,7 +3089,7 @@ function runPlaywright(appDir, testFiles, port, wranglerEnv, sharedDevVarsCache
|
|
|
2846
3089
|
return 1;
|
|
2847
3090
|
}
|
|
2848
3091
|
try {
|
|
2849
|
-
const result =
|
|
3092
|
+
const result = spawnSync4(
|
|
2850
3093
|
"npx",
|
|
2851
3094
|
["playwright", "test", "--config", "tests/playwright.config.ts", ...testFiles],
|
|
2852
3095
|
{
|
|
@@ -2861,7 +3104,7 @@ function runPlaywright(appDir, testFiles, port, wranglerEnv, sharedDevVarsCache
|
|
|
2861
3104
|
}
|
|
2862
3105
|
}
|
|
2863
3106
|
function runVitest(appDir) {
|
|
2864
|
-
const result =
|
|
3107
|
+
const result = spawnSync4("npx", ["vitest", "run", "--passWithNoTests"], {
|
|
2865
3108
|
cwd: appDir,
|
|
2866
3109
|
stdio: "inherit"
|
|
2867
3110
|
});
|
|
@@ -2869,8 +3112,8 @@ function runVitest(appDir) {
|
|
|
2869
3112
|
}
|
|
2870
3113
|
|
|
2871
3114
|
// src/cli/commands/screenshot.ts
|
|
2872
|
-
import { sync as
|
|
2873
|
-
import { resolve as
|
|
3115
|
+
import { sync as spawnSync5 } from "cross-spawn";
|
|
3116
|
+
import { resolve as resolve8 } from "path";
|
|
2874
3117
|
var screenshot_default = defineDeepspaceCommand({
|
|
2875
3118
|
meta: {
|
|
2876
3119
|
name: "screenshot",
|
|
@@ -2899,7 +3142,7 @@ var screenshot_default = defineDeepspaceCommand({
|
|
|
2899
3142
|
}
|
|
2900
3143
|
},
|
|
2901
3144
|
async run({ args }) {
|
|
2902
|
-
const appDir =
|
|
3145
|
+
const appDir = resolve8(".");
|
|
2903
3146
|
ensurePlaywright(appDir);
|
|
2904
3147
|
const url = String(args.url);
|
|
2905
3148
|
const output = String(args.output);
|
|
@@ -2908,7 +3151,7 @@ var screenshot_default = defineDeepspaceCommand({
|
|
|
2908
3151
|
if (args["wait-for-timeout"]) {
|
|
2909
3152
|
playwrightArgs.push("--wait-for-timeout", String(args["wait-for-timeout"]));
|
|
2910
3153
|
}
|
|
2911
|
-
const result =
|
|
3154
|
+
const result = spawnSync5("npx", playwrightArgs, {
|
|
2912
3155
|
cwd: appDir,
|
|
2913
3156
|
stdio: "inherit"
|
|
2914
3157
|
});
|
|
@@ -2925,27 +3168,27 @@ var screenshot_default = defineDeepspaceCommand({
|
|
|
2925
3168
|
// src/cli/commands/test-accounts.ts
|
|
2926
3169
|
import { defineCommand as defineCommand2 } from "citty";
|
|
2927
3170
|
import * as p5 from "@clack/prompts";
|
|
2928
|
-
import { readFileSync as
|
|
2929
|
-
import { join as
|
|
3171
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
|
|
3172
|
+
import { join as join13 } from "path";
|
|
2930
3173
|
import { homedir as homedir3 } from "os";
|
|
2931
3174
|
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
2932
3175
|
var AUTH_URL4 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
2933
|
-
var DIR2 =
|
|
2934
|
-
var ACCOUNTS_PATH =
|
|
3176
|
+
var DIR2 = join13(homedir3(), ".deepspace");
|
|
3177
|
+
var ACCOUNTS_PATH = join13(DIR2, "test-accounts.json");
|
|
2935
3178
|
function loadAccounts() {
|
|
2936
3179
|
if (!existsSync12(ACCOUNTS_PATH)) return [];
|
|
2937
3180
|
try {
|
|
2938
|
-
return JSON.parse(
|
|
3181
|
+
return JSON.parse(readFileSync13(ACCOUNTS_PATH, "utf-8"));
|
|
2939
3182
|
} catch {
|
|
2940
3183
|
return [];
|
|
2941
3184
|
}
|
|
2942
3185
|
}
|
|
2943
3186
|
function saveAccounts(accounts) {
|
|
2944
|
-
|
|
3187
|
+
mkdirSync5(DIR2, { recursive: true });
|
|
2945
3188
|
writeFileSync7(ACCOUNTS_PATH, JSON.stringify(accounts, null, 2), { mode: 384 });
|
|
2946
3189
|
}
|
|
2947
3190
|
function sessionCookie() {
|
|
2948
|
-
const token =
|
|
3191
|
+
const token = readFileSync13(SESSION_PATH, "utf-8").trim();
|
|
2949
3192
|
return `${SESSION_COOKIE2}=${encodeURIComponent(token)}`;
|
|
2950
3193
|
}
|
|
2951
3194
|
async function fetchRemoteAccounts() {
|
|
@@ -3261,8 +3504,8 @@ import { resolve as resolve11 } from "path";
|
|
|
3261
3504
|
// src/cli/commands/deploy/build.ts
|
|
3262
3505
|
import * as p6 from "@clack/prompts";
|
|
3263
3506
|
import { execSync as execSync2 } from "child_process";
|
|
3264
|
-
import { existsSync as existsSync13, readFileSync as
|
|
3265
|
-
import { dirname as
|
|
3507
|
+
import { existsSync as existsSync13, readFileSync as readFileSync14, readdirSync as readdirSync4 } from "fs";
|
|
3508
|
+
import { dirname as dirname5, join as join14, resolve as resolve9 } from "path";
|
|
3266
3509
|
var RESERVED_RUN_WORKER_FIRST = /* @__PURE__ */ new Set([
|
|
3267
3510
|
"/api/*",
|
|
3268
3511
|
"/ws/*",
|
|
@@ -3298,19 +3541,19 @@ async function buildDeployBundle(options) {
|
|
|
3298
3541
|
preparedWranglerConfig?.cleanup();
|
|
3299
3542
|
}
|
|
3300
3543
|
spinner2.stop("Built");
|
|
3301
|
-
const deployConfigPath =
|
|
3544
|
+
const deployConfigPath = join14(appDir, ".wrangler", "deploy", "config.json");
|
|
3302
3545
|
if (!existsSync13(deployConfigPath)) {
|
|
3303
3546
|
output.die(
|
|
3304
3547
|
"Build output config not found at .wrangler/deploy/config.json",
|
|
3305
3548
|
"build_output_missing"
|
|
3306
3549
|
);
|
|
3307
3550
|
}
|
|
3308
|
-
const deployConfig = JSON.parse(
|
|
3309
|
-
const outputWranglerPath =
|
|
3551
|
+
const deployConfig = JSON.parse(readFileSync14(deployConfigPath, "utf-8"));
|
|
3552
|
+
const outputWranglerPath = resolve9(dirname5(deployConfigPath), deployConfig.configPath);
|
|
3310
3553
|
if (!existsSync13(outputWranglerPath)) {
|
|
3311
3554
|
output.die(`Output wrangler.json not found at ${outputWranglerPath}`, "build_output_missing");
|
|
3312
3555
|
}
|
|
3313
|
-
const outputConfig = JSON.parse(
|
|
3556
|
+
const outputConfig = JSON.parse(readFileSync14(outputWranglerPath, "utf-8"));
|
|
3314
3557
|
if (outputConfig.name && outputConfig.name !== appName) {
|
|
3315
3558
|
const envHint = envName ? ` (--env ${envName})` : "";
|
|
3316
3559
|
output.die(
|
|
@@ -3318,9 +3561,9 @@ async function buildDeployBundle(options) {
|
|
|
3318
3561
|
"build_output_mismatch"
|
|
3319
3562
|
);
|
|
3320
3563
|
}
|
|
3321
|
-
const workerDir =
|
|
3322
|
-
const workerBundlePath =
|
|
3323
|
-
const clientDir = outputConfig.assets?.directory ?
|
|
3564
|
+
const workerDir = dirname5(outputWranglerPath);
|
|
3565
|
+
const workerBundlePath = join14(workerDir, outputConfig.main);
|
|
3566
|
+
const clientDir = outputConfig.assets?.directory ? resolve9(workerDir, outputConfig.assets.directory) : null;
|
|
3324
3567
|
if (!existsSync13(workerBundlePath)) {
|
|
3325
3568
|
output.die(`Worker bundle not found at ${workerBundlePath}`, "build_output_missing");
|
|
3326
3569
|
}
|
|
@@ -3330,7 +3573,7 @@ async function buildDeployBundle(options) {
|
|
|
3330
3573
|
spinner2.start("Collecting assets...");
|
|
3331
3574
|
const assets = collectAssets(clientDir);
|
|
3332
3575
|
spinner2.stop(`Collected ${assets.length} assets`);
|
|
3333
|
-
const workerJs =
|
|
3576
|
+
const workerJs = readFileSync14(workerBundlePath, "utf-8");
|
|
3334
3577
|
const doBindings = outputConfig.durable_objects?.bindings;
|
|
3335
3578
|
const sqliteClasses = new Set(
|
|
3336
3579
|
outputConfig.migrations?.flatMap((migration) => migration.new_sqlite_classes ?? []) ?? []
|
|
@@ -3383,14 +3626,14 @@ function collectAssets(dir) {
|
|
|
3383
3626
|
const walk = (currentDir, prefix) => {
|
|
3384
3627
|
for (const entry of readdirSync4(currentDir, { withFileTypes: true })) {
|
|
3385
3628
|
if (entry.name === ".assetsignore") continue;
|
|
3386
|
-
const fullPath =
|
|
3629
|
+
const fullPath = join14(currentDir, entry.name);
|
|
3387
3630
|
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
3388
3631
|
if (entry.isDirectory()) {
|
|
3389
3632
|
walk(fullPath, relativePath);
|
|
3390
3633
|
} else {
|
|
3391
3634
|
assets.push({
|
|
3392
3635
|
path: `/${relativePath}`,
|
|
3393
|
-
contentBase64:
|
|
3636
|
+
contentBase64: readFileSync14(fullPath).toString("base64")
|
|
3394
3637
|
});
|
|
3395
3638
|
}
|
|
3396
3639
|
}
|
|
@@ -3405,17 +3648,17 @@ function errorMessage(error) {
|
|
|
3405
3648
|
// src/cli/commands/deploy/commerce.ts
|
|
3406
3649
|
import * as p7 from "@clack/prompts";
|
|
3407
3650
|
import { existsSync as existsSync14 } from "fs";
|
|
3408
|
-
import { join as
|
|
3651
|
+
import { join as join15 } from "path";
|
|
3409
3652
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3410
3653
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
3411
3654
|
function errorMessage2(error) {
|
|
3412
3655
|
return error instanceof Error ? error.message : String(error);
|
|
3413
3656
|
}
|
|
3414
3657
|
async function loadDeclaration(appDir, sourceFile, outputFile, exportName) {
|
|
3415
|
-
const sourcePath =
|
|
3658
|
+
const sourcePath = join15(appDir, "src", sourceFile);
|
|
3416
3659
|
if (!existsSync14(sourcePath)) return void 0;
|
|
3417
3660
|
const esbuild = await import("esbuild");
|
|
3418
|
-
const outputPath =
|
|
3661
|
+
const outputPath = join15(appDir, ".wrangler", "deploy", outputFile);
|
|
3419
3662
|
await esbuild.build({
|
|
3420
3663
|
entryPoints: [sourcePath],
|
|
3421
3664
|
bundle: true,
|
|
@@ -3508,7 +3751,7 @@ Stripe will keep charging them until you cancel. Cancel at the end of their curr
|
|
|
3508
3751
|
}
|
|
3509
3752
|
}
|
|
3510
3753
|
async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
3511
|
-
if (!existsSync14(
|
|
3754
|
+
if (!existsSync14(join15(appDir, "src", "subscriptions.ts"))) return;
|
|
3512
3755
|
let plans;
|
|
3513
3756
|
try {
|
|
3514
3757
|
plans = await loadDeclaration(
|
|
@@ -3563,7 +3806,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
|
3563
3806
|
}
|
|
3564
3807
|
}
|
|
3565
3808
|
async function syncOneTimeProducts(appDir, appName, token) {
|
|
3566
|
-
if (!existsSync14(
|
|
3809
|
+
if (!existsSync14(join15(appDir, "src", "products.ts"))) return;
|
|
3567
3810
|
let products;
|
|
3568
3811
|
try {
|
|
3569
3812
|
products = await loadDeclaration(
|
|
@@ -3669,217 +3912,6 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
3669
3912
|
// src/cli/commands/deploy/repository.ts
|
|
3670
3913
|
import * as p9 from "@clack/prompts";
|
|
3671
3914
|
|
|
3672
|
-
// src/cli/lib/git/repository.ts
|
|
3673
|
-
import { appendFileSync, mkdirSync as mkdirSync5, readFileSync as readFileSync14 } from "fs";
|
|
3674
|
-
import { dirname as dirname5, join as join15, resolve as resolve9 } from "path";
|
|
3675
|
-
|
|
3676
|
-
// src/cli/lib/git/process.ts
|
|
3677
|
-
import { sync as spawnSync5 } from "cross-spawn";
|
|
3678
|
-
var MAX_GIT_BUFFER = 512 * 1024 * 1024;
|
|
3679
|
-
var GitError = class extends Error {
|
|
3680
|
-
constructor(message, code) {
|
|
3681
|
-
super(message);
|
|
3682
|
-
this.code = code;
|
|
3683
|
-
this.name = "GitError";
|
|
3684
|
-
}
|
|
3685
|
-
};
|
|
3686
|
-
function runGit(cwd, args, opts = {}) {
|
|
3687
|
-
const result = spawnSync5("git", args, {
|
|
3688
|
-
cwd,
|
|
3689
|
-
input: opts.input,
|
|
3690
|
-
maxBuffer: MAX_GIT_BUFFER,
|
|
3691
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
3692
|
-
env: {
|
|
3693
|
-
...process.env,
|
|
3694
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
3695
|
-
LC_ALL: "C",
|
|
3696
|
-
...opts.env ?? {}
|
|
3697
|
-
}
|
|
3698
|
-
});
|
|
3699
|
-
if (result.error) {
|
|
3700
|
-
const code = result.error.code;
|
|
3701
|
-
if (code === "ENOENT") {
|
|
3702
|
-
throw new GitError(
|
|
3703
|
-
"git is not installed or not on PATH \u2014 install git and retry.",
|
|
3704
|
-
"git_not_installed"
|
|
3705
|
-
);
|
|
3706
|
-
}
|
|
3707
|
-
throw new GitError(`git ${args[0]} failed: ${result.error.message}`);
|
|
3708
|
-
}
|
|
3709
|
-
const status3 = result.status ?? 1;
|
|
3710
|
-
if (status3 !== 0 && !opts.allowFail) {
|
|
3711
|
-
const stderr = result.stderr?.toString("utf-8").trim() ?? "";
|
|
3712
|
-
throw new GitError(`git ${args.join(" ")} exited ${status3}${stderr ? `: ${stderr}` : ""}`);
|
|
3713
|
-
}
|
|
3714
|
-
return {
|
|
3715
|
-
stdout: result.stdout ?? Buffer.alloc(0),
|
|
3716
|
-
stderr: result.stderr ?? Buffer.alloc(0),
|
|
3717
|
-
status: status3
|
|
3718
|
-
};
|
|
3719
|
-
}
|
|
3720
|
-
function gitLine(cwd, args) {
|
|
3721
|
-
return runGit(cwd, args).stdout.toString("utf-8").trim();
|
|
3722
|
-
}
|
|
3723
|
-
function splitNulFields(buffer) {
|
|
3724
|
-
const fields = [];
|
|
3725
|
-
let start = 0;
|
|
3726
|
-
for (let index = 0; index < buffer.length; index++) {
|
|
3727
|
-
if (buffer[index] === 0) {
|
|
3728
|
-
fields.push(buffer.toString("utf-8", start, index));
|
|
3729
|
-
start = index + 1;
|
|
3730
|
-
}
|
|
3731
|
-
}
|
|
3732
|
-
if (start < buffer.length) fields.push(buffer.toString("utf-8", start));
|
|
3733
|
-
return fields;
|
|
3734
|
-
}
|
|
3735
|
-
function parseGitVersion(output) {
|
|
3736
|
-
const match = /(\d+)\.(\d+)/.exec(output);
|
|
3737
|
-
return match ? [Number(match[1]), Number(match[2])] : null;
|
|
3738
|
-
}
|
|
3739
|
-
function gitMeetsFloor(version) {
|
|
3740
|
-
if (!version) return true;
|
|
3741
|
-
const [major, minor] = version;
|
|
3742
|
-
return major > 2 || major === 2 && minor >= 29;
|
|
3743
|
-
}
|
|
3744
|
-
|
|
3745
|
-
// src/cli/lib/git/repository.ts
|
|
3746
|
-
function isPlausibleBranchName(name) {
|
|
3747
|
-
if (!name || name === "HEAD" || name === "@") return false;
|
|
3748
|
-
if (name.startsWith("/") || name.startsWith("-") || name.startsWith(".")) return false;
|
|
3749
|
-
if (name.endsWith("/") || name.endsWith(".") || name.endsWith(".lock")) return false;
|
|
3750
|
-
if (name.includes("/.") || name.includes(".lock/")) return false;
|
|
3751
|
-
return !/(\.\.|\/\/|@\{|[\x00-\x20\x7f~^:?*[\\])/.test(name);
|
|
3752
|
-
}
|
|
3753
|
-
function assertSyncableRepo(cwd) {
|
|
3754
|
-
const inside = runGit(cwd, ["rev-parse", "--is-inside-work-tree"], { allowFail: true });
|
|
3755
|
-
if (inside.status !== 0 || inside.stdout.toString().trim() !== "true") {
|
|
3756
|
-
throw new InputError(
|
|
3757
|
-
`${cwd} is not a git repository. DeepSpace apps are scaffolded as git repos \u2014 run \`git init\` first.`,
|
|
3758
|
-
"not_git_repo"
|
|
3759
|
-
);
|
|
3760
|
-
}
|
|
3761
|
-
const versionOutput = runGit(cwd, ["--version"], { allowFail: true }).stdout.toString("utf-8").trim();
|
|
3762
|
-
if (!gitMeetsFloor(parseGitVersion(versionOutput))) {
|
|
3763
|
-
throw gitTooOld(versionOutput);
|
|
3764
|
-
}
|
|
3765
|
-
const formatResult = runGit(cwd, ["rev-parse", "--show-object-format"], { allowFail: true });
|
|
3766
|
-
const format = formatResult.stdout.toString("utf-8").trim();
|
|
3767
|
-
if (formatResult.status !== 0 || format.startsWith("--")) throw gitTooOld(versionOutput);
|
|
3768
|
-
if (format !== "sha1") {
|
|
3769
|
-
throw new InputError(
|
|
3770
|
-
`This repo uses object format "${format}" \u2014 DeepSpace version control supports sha1 repos only (git's default) for now.`,
|
|
3771
|
-
"unsupported_object_format"
|
|
3772
|
-
);
|
|
3773
|
-
}
|
|
3774
|
-
if (gitLine(cwd, ["rev-parse", "--is-shallow-repository"]) === "true") {
|
|
3775
|
-
throw new InputError(
|
|
3776
|
-
"This is a shallow clone. Run `git fetch --unshallow` before pushing to DeepSpace.",
|
|
3777
|
-
"shallow_repo"
|
|
3778
|
-
);
|
|
3779
|
-
}
|
|
3780
|
-
}
|
|
3781
|
-
function gitTooOld(versionOutput) {
|
|
3782
|
-
return new InputError(
|
|
3783
|
-
`DeepSpace version control needs git \u2265 2.29 \u2014 found ${versionOutput || "an unknown git version"}. Upgrade git and retry.`,
|
|
3784
|
-
"git_too_old"
|
|
3785
|
-
);
|
|
3786
|
-
}
|
|
3787
|
-
function repoToplevel(cwd) {
|
|
3788
|
-
return gitLine(cwd, ["rev-parse", "--show-toplevel"]);
|
|
3789
|
-
}
|
|
3790
|
-
function currentBranch(cwd) {
|
|
3791
|
-
const result = runGit(cwd, ["symbolic-ref", "--short", "-q", "HEAD"], { allowFail: true });
|
|
3792
|
-
const name = result.stdout.toString("utf-8").trim();
|
|
3793
|
-
return result.status === 0 && name ? name : null;
|
|
3794
|
-
}
|
|
3795
|
-
function resolveCommit(cwd, ref) {
|
|
3796
|
-
const result = runGit(cwd, ["rev-parse", "-q", "--verify", `${ref}^{commit}`], {
|
|
3797
|
-
allowFail: true
|
|
3798
|
-
});
|
|
3799
|
-
const oid = result.stdout.toString("utf-8").trim();
|
|
3800
|
-
return result.status === 0 && /^[0-9a-f]{40}$/.test(oid) ? oid : null;
|
|
3801
|
-
}
|
|
3802
|
-
function isWorkTreeClean(cwd) {
|
|
3803
|
-
return gitLine(cwd, ["status", "--porcelain"]) === "";
|
|
3804
|
-
}
|
|
3805
|
-
function updateRef(cwd, ref, oid) {
|
|
3806
|
-
runGit(cwd, ["update-ref", ref, oid]);
|
|
3807
|
-
}
|
|
3808
|
-
function isAncestor(cwd, ancestor, descendant) {
|
|
3809
|
-
return runGit(cwd, ["merge-base", "--is-ancestor", ancestor, descendant], { allowFail: true }).status === 0;
|
|
3810
|
-
}
|
|
3811
|
-
function mergeBase(cwd, left, right) {
|
|
3812
|
-
const result = runGit(cwd, ["merge-base", left, right], { allowFail: true });
|
|
3813
|
-
if (result.status !== 0) return null;
|
|
3814
|
-
const oid = result.stdout.toString("utf-8").trim();
|
|
3815
|
-
return /^[0-9a-f]{40}$/.test(oid) ? oid : null;
|
|
3816
|
-
}
|
|
3817
|
-
function fastForwardCurrentBranch(cwd, oid) {
|
|
3818
|
-
runGit(cwd, ["merge", "--ff-only", oid]);
|
|
3819
|
-
}
|
|
3820
|
-
function checkoutHead(cwd) {
|
|
3821
|
-
runGit(cwd, ["reset", "--hard", "--quiet"]);
|
|
3822
|
-
}
|
|
3823
|
-
function switchToNewBranch(cwd, name, oid) {
|
|
3824
|
-
runGit(cwd, ["switch", "--quiet", "-c", name, oid]);
|
|
3825
|
-
}
|
|
3826
|
-
function diffNameOnly(cwd, base, head) {
|
|
3827
|
-
return splitNulFields(
|
|
3828
|
-
runGit(cwd, [
|
|
3829
|
-
"-c",
|
|
3830
|
-
"core.quotePath=false",
|
|
3831
|
-
"diff",
|
|
3832
|
-
"--name-only",
|
|
3833
|
-
"--no-renames",
|
|
3834
|
-
"-z",
|
|
3835
|
-
base,
|
|
3836
|
-
head
|
|
3837
|
-
]).stdout
|
|
3838
|
-
);
|
|
3839
|
-
}
|
|
3840
|
-
function revListCount(cwd, exclude, include) {
|
|
3841
|
-
const count = Number(gitLine(cwd, ["rev-list", "--count", `${exclude}..${include}`]));
|
|
3842
|
-
return Number.isFinite(count) ? count : 0;
|
|
3843
|
-
}
|
|
3844
|
-
function addWorktreeNewBranch(cwd, dir, branch, oid) {
|
|
3845
|
-
runGit(cwd, ["worktree", "add", "--quiet", "-b", branch, dir, oid]);
|
|
3846
|
-
}
|
|
3847
|
-
function listWorktrees(cwd) {
|
|
3848
|
-
const result = runGit(cwd, ["worktree", "list", "--porcelain"], { allowFail: true });
|
|
3849
|
-
if (result.status !== 0) return [];
|
|
3850
|
-
const worktrees = [];
|
|
3851
|
-
let current = null;
|
|
3852
|
-
for (const line of result.stdout.toString("utf-8").split("\n")) {
|
|
3853
|
-
if (line.startsWith("worktree ")) {
|
|
3854
|
-
current = { path: line.slice("worktree ".length), branch: null };
|
|
3855
|
-
worktrees.push(current);
|
|
3856
|
-
} else if (line.startsWith("branch ") && current) {
|
|
3857
|
-
const ref = line.slice("branch ".length);
|
|
3858
|
-
current.branch = ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref;
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
return worktrees;
|
|
3862
|
-
}
|
|
3863
|
-
function ensureLocalExclude(cwd, pattern) {
|
|
3864
|
-
const commonDir = resolve9(cwd, gitLine(cwd, ["rev-parse", "--git-common-dir"]));
|
|
3865
|
-
const excludePath = join15(commonDir, "info", "exclude");
|
|
3866
|
-
let current = "";
|
|
3867
|
-
try {
|
|
3868
|
-
current = readFileSync14(excludePath, "utf-8");
|
|
3869
|
-
} catch {
|
|
3870
|
-
}
|
|
3871
|
-
if (current.split("\n").some((line) => line.trim() === pattern)) return;
|
|
3872
|
-
try {
|
|
3873
|
-
mkdirSync5(dirname5(excludePath), { recursive: true });
|
|
3874
|
-
appendFileSync(
|
|
3875
|
-
excludePath,
|
|
3876
|
-
`${current.endsWith("\n") || current === "" ? "" : "\n"}${pattern}
|
|
3877
|
-
`
|
|
3878
|
-
);
|
|
3879
|
-
} catch {
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
3882
|
-
|
|
3883
3915
|
// src/cli/lib/git/safety.ts
|
|
3884
3916
|
var SECRET_FILE_RE = /(^|\/)(\.dev\.vars(\.[^/]*)?|\.env(\.[^/]*)?|\.npmrc|\.envrc|\.mcp\.json)$/i;
|
|
3885
3917
|
var SECRET_TEMPLATE_RE = /\.(example|sample|template)$/i;
|
|
@@ -4110,7 +4142,15 @@ function installCredentialHelper(cwd, url) {
|
|
|
4110
4142
|
const key = `credential.${origin.protocol}//${origin.host}.helper`;
|
|
4111
4143
|
const helper = credentialHelperCommand();
|
|
4112
4144
|
const wanted = ["", helper];
|
|
4113
|
-
const
|
|
4145
|
+
const transient = entryIsTransient(cwd);
|
|
4146
|
+
const scope = transient ? "--worktree" : "--global";
|
|
4147
|
+
if (transient) {
|
|
4148
|
+
const enabled = runGit(cwd, ["config", "--local", "extensions.worktreeConfig", "true"], {
|
|
4149
|
+
allowFail: true
|
|
4150
|
+
});
|
|
4151
|
+
if (enabled.status !== 0) return;
|
|
4152
|
+
runGit(cwd, ["config", "--local", "--unset-all", key], { allowFail: true });
|
|
4153
|
+
}
|
|
4114
4154
|
const current = runGit(cwd, ["config", scope, "--get-all", key], { allowFail: true });
|
|
4115
4155
|
if (current.status === 0) {
|
|
4116
4156
|
const existing = current.stdout.toString("utf-8").split("\n").filter((_, index, lines) => index < lines.length - 1);
|
|
@@ -4306,8 +4346,9 @@ async function syncDeployRepository(options) {
|
|
|
4306
4346
|
output.die(refusal.error, refusal.code);
|
|
4307
4347
|
}
|
|
4308
4348
|
if (!branch) {
|
|
4309
|
-
|
|
4310
|
-
|
|
4349
|
+
const refusal = detachedHeadRefusal();
|
|
4350
|
+
output.die(refusal.error, refusal.code);
|
|
4351
|
+
throw new Error(refusal.error);
|
|
4311
4352
|
}
|
|
4312
4353
|
const tip = resolveCommit(appDir, `refs/heads/${branch}`);
|
|
4313
4354
|
if (!tip) return { commitOid, recoverable, deployKey };
|
|
@@ -4453,6 +4494,12 @@ function dirtyWorktreeRefusal(branch) {
|
|
|
4453
4494
|
error: "The worktree has uncommitted changes, and a deploy records the commit it ships. " + (workspaceId ? `Commit them to this workspace branch (${branch}) \u2014 WIP commits are fine \u2014 then redeploy. ` : 'Commit them (to a workspace branch if this is work in progress: `deepspace workspace new -t "\u2026"`), then redeploy. ') + "Or pass --no-push to deploy without version-control sync \u2014 the release then records no source lineage."
|
|
4454
4495
|
};
|
|
4455
4496
|
}
|
|
4497
|
+
function detachedHeadRefusal() {
|
|
4498
|
+
return {
|
|
4499
|
+
code: "detached_head",
|
|
4500
|
+
error: "HEAD is detached, so DeepSpace cannot publish this commit to a recoverable branch. Create or switch to a branch before deploying, or pass `--no-push` to explicitly deploy without source lineage."
|
|
4501
|
+
};
|
|
4502
|
+
}
|
|
4456
4503
|
|
|
4457
4504
|
// src/cli/commands/deploy/request.ts
|
|
4458
4505
|
var CLOUDFLARE_DEPLOY_ERROR_HINT = "Deployment reached the DeepSpace deploy worker, but Cloudflare's deployment control-plane API failed. This is often caused by a Cloudflare Dashboard/API incident or Workers for Platforms entitlement outage. Already deployed apps should keep serving; wait for Cloudflare to recover and retry.";
|
|
@@ -5694,7 +5741,7 @@ var usage_default = defineDeepspaceCommand({
|
|
|
5694
5741
|
});
|
|
5695
5742
|
|
|
5696
5743
|
// src/cli/commands/add.ts
|
|
5697
|
-
import { existsSync as existsSync18, readFileSync as readFileSync17, realpathSync } from "fs";
|
|
5744
|
+
import { existsSync as existsSync18, readFileSync as readFileSync17, realpathSync as realpathSync2 } from "fs";
|
|
5698
5745
|
import { dirname as dirname7, join as join17, resolve as resolve13 } from "path";
|
|
5699
5746
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
5700
5747
|
|
|
@@ -6435,7 +6482,7 @@ async function targetInstaller(appDir) {
|
|
|
6435
6482
|
"feature_installer_missing"
|
|
6436
6483
|
);
|
|
6437
6484
|
}
|
|
6438
|
-
if (
|
|
6485
|
+
if (realpathSync2(installedRoot) === realpathSync2(featurePackageRoot())) {
|
|
6439
6486
|
return { installFeature };
|
|
6440
6487
|
}
|
|
6441
6488
|
const entry = join17(installedRoot, "dist", "feature-installer.js");
|
|
@@ -8588,7 +8635,7 @@ var status_default = defineCommand11({
|
|
|
8588
8635
|
const depsInstallCommand = installCommand(appDir);
|
|
8589
8636
|
lines.push([
|
|
8590
8637
|
"Deps",
|
|
8591
|
-
deps === "ready" ? "installed" : deps === "installing" ? "
|
|
8638
|
+
deps === "ready" ? "installed" : deps === "installing" ? "dependency installation is still running" : `${deps} \u2014 run ${depsInstallCommand}`
|
|
8592
8639
|
]);
|
|
8593
8640
|
json.deps = deps;
|
|
8594
8641
|
let branch = null;
|
|
@@ -8918,7 +8965,7 @@ var push_default = defineDeepspaceCommand({
|
|
|
8918
8965
|
|
|
8919
8966
|
// src/cli/commands/pull.ts
|
|
8920
8967
|
import * as p20 from "@clack/prompts";
|
|
8921
|
-
import { realpathSync as
|
|
8968
|
+
import { realpathSync as realpathSync3 } from "fs";
|
|
8922
8969
|
function targetedVcAction(command, cwd, appId, branch) {
|
|
8923
8970
|
return {
|
|
8924
8971
|
cwd,
|
|
@@ -8942,7 +8989,7 @@ function workspaceBranchPullRefusal(branch, trunkBranch) {
|
|
|
8942
8989
|
function worktreeHoldingBranch(worktrees, branch, selfPath) {
|
|
8943
8990
|
const real = (pth) => {
|
|
8944
8991
|
try {
|
|
8945
|
-
return
|
|
8992
|
+
return realpathSync3(pth);
|
|
8946
8993
|
} catch {
|
|
8947
8994
|
return pth;
|
|
8948
8995
|
}
|
|
@@ -9506,7 +9553,7 @@ import { defineCommand as defineCommand12 } from "citty";
|
|
|
9506
9553
|
|
|
9507
9554
|
// src/cli/commands/workspace/new.ts
|
|
9508
9555
|
import * as p26 from "@clack/prompts";
|
|
9509
|
-
import { isAbsolute as isAbsolute6,
|
|
9556
|
+
import { isAbsolute as isAbsolute6, resolve as resolve16 } from "path";
|
|
9510
9557
|
|
|
9511
9558
|
// src/cli/commands/workspace/analysis.ts
|
|
9512
9559
|
import * as p24 from "@clack/prompts";
|
|
@@ -9631,44 +9678,75 @@ function printOverlaps(overlaps, json) {
|
|
|
9631
9678
|
|
|
9632
9679
|
// src/cli/commands/workspace/local.ts
|
|
9633
9680
|
import * as p25 from "@clack/prompts";
|
|
9634
|
-
import {
|
|
9635
|
-
import { isAbsolute as isAbsolute5, join as join21, relative as relative2, sep as sep2 } from "path";
|
|
9681
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync23, realpathSync as realpathSync4, renameSync as renameSync2, statSync, writeFileSync as writeFileSync9 } from "fs";
|
|
9682
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join21, relative as relative2, sep as sep2 } from "path";
|
|
9683
|
+
var WORKSPACE_MARKER = "deepspace-workspace.json";
|
|
9636
9684
|
function excludeWorktreeDir(appDir, dir) {
|
|
9637
9685
|
try {
|
|
9638
9686
|
ensureLocalExclude(appDir, ".deepspace/");
|
|
9639
|
-
const realDir =
|
|
9640
|
-
const rel = relative2(
|
|
9687
|
+
const realDir = realpathSync4(dir);
|
|
9688
|
+
const rel = relative2(realpathSync4(repoToplevel(appDir)), realDir);
|
|
9641
9689
|
if (!rel || rel === ".." || rel.startsWith(`..${sep2}`) || isAbsolute5(rel)) return;
|
|
9642
9690
|
const pattern = rel.split(sep2).join("/");
|
|
9643
9691
|
if (pattern === ".deepspace" || pattern.startsWith(".deepspace/")) return;
|
|
9644
|
-
const relApp = relative2(
|
|
9692
|
+
const relApp = relative2(realpathSync4(appDir), realDir).split(sep2).join("/");
|
|
9645
9693
|
if (relApp === ".deepspace" || relApp.startsWith(".deepspace/")) return;
|
|
9646
9694
|
ensureLocalExclude(appDir, `/${pattern}/`);
|
|
9647
9695
|
} catch {
|
|
9648
9696
|
}
|
|
9649
9697
|
}
|
|
9650
|
-
function
|
|
9651
|
-
const linked = [];
|
|
9698
|
+
function worktreeMarkerPath(worktreeRoot) {
|
|
9652
9699
|
try {
|
|
9653
|
-
const
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9700
|
+
const result = runGit(worktreeRoot, ["rev-parse", "--absolute-git-dir"], { allowFail: true });
|
|
9701
|
+
if (result.status !== 0) return null;
|
|
9702
|
+
const gitDir = result.stdout.toString("utf-8").trim();
|
|
9703
|
+
return isAbsolute5(gitDir) ? join21(gitDir, WORKSPACE_MARKER) : null;
|
|
9704
|
+
} catch {
|
|
9705
|
+
return null;
|
|
9706
|
+
}
|
|
9707
|
+
}
|
|
9708
|
+
function markManagedWorkspace(worktreeRoot, workspaceId) {
|
|
9709
|
+
const markerPath = worktreeMarkerPath(worktreeRoot);
|
|
9710
|
+
if (!markerPath) return false;
|
|
9711
|
+
const marker = { version: 1, workspaceId, mode: "managed-linked" };
|
|
9712
|
+
const tempPath = `${markerPath}.${process.pid}.tmp`;
|
|
9713
|
+
try {
|
|
9714
|
+
mkdirSync7(dirname9(markerPath), { recursive: true });
|
|
9715
|
+
writeFileSync9(tempPath, JSON.stringify(marker) + "\n");
|
|
9716
|
+
renameSync2(tempPath, markerPath);
|
|
9717
|
+
return true;
|
|
9718
|
+
} catch {
|
|
9719
|
+
return false;
|
|
9720
|
+
}
|
|
9721
|
+
}
|
|
9722
|
+
function isManagedWorkspaceWorktree(worktreeRoot, workspaceId) {
|
|
9723
|
+
const markerPath = worktreeMarkerPath(worktreeRoot);
|
|
9724
|
+
if (!markerPath) return false;
|
|
9725
|
+
try {
|
|
9726
|
+
const marker = JSON.parse(readFileSync23(markerPath, "utf-8"));
|
|
9727
|
+
return marker.version === 1 && marker.workspaceId === workspaceId && marker.mode === "managed-linked";
|
|
9659
9728
|
} catch {
|
|
9729
|
+
return false;
|
|
9660
9730
|
}
|
|
9661
|
-
return linked;
|
|
9662
9731
|
}
|
|
9663
|
-
function
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
return
|
|
9732
|
+
function defaultWorkspaceRoot(appDir, workspaceId) {
|
|
9733
|
+
const primary = listWorktrees(appDir)[0];
|
|
9734
|
+
if (!primary) throw new Error("Git did not report a primary worktree");
|
|
9735
|
+
return join21(primary.path, ".deepspace", "ws", workspaceId.slice(3).toLowerCase());
|
|
9736
|
+
}
|
|
9737
|
+
function appDirInWorktree(appDir, worktreeRoot) {
|
|
9738
|
+
const appRelative = relative2(realpathSync4(repoToplevel(appDir)), realpathSync4(appDir));
|
|
9739
|
+
return appRelative ? join21(worktreeRoot, appRelative) : worktreeRoot;
|
|
9740
|
+
}
|
|
9741
|
+
function materializeWorkspaceWorktree(appDir, worktreeRoot, branch, startPoint, workspaceId) {
|
|
9742
|
+
addWorktreeNewBranch(appDir, worktreeRoot, branch, startPoint);
|
|
9743
|
+
excludeWorktreeDir(appDir, worktreeRoot);
|
|
9744
|
+
markManagedWorkspace(worktreeRoot, workspaceId);
|
|
9667
9745
|
}
|
|
9668
9746
|
function inOwnLinkedWorktree(appDir, id) {
|
|
9669
9747
|
if (workspaceIdFromBranch(currentBranch(appDir)) !== id) return false;
|
|
9670
9748
|
try {
|
|
9671
|
-
return statSync(join21(appDir, ".git")).isFile();
|
|
9749
|
+
return statSync(join21(repoToplevel(appDir), ".git")).isFile();
|
|
9672
9750
|
} catch {
|
|
9673
9751
|
return false;
|
|
9674
9752
|
}
|
|
@@ -9676,6 +9754,7 @@ function inOwnLinkedWorktree(appDir, id) {
|
|
|
9676
9754
|
function cleanupJson(outcome) {
|
|
9677
9755
|
return {
|
|
9678
9756
|
worktreeRemoved: outcome.worktreeRemoved,
|
|
9757
|
+
worktreeRetained: outcome.worktreeRetained,
|
|
9679
9758
|
branchDeleted: outcome.branchDeleted,
|
|
9680
9759
|
...outcome.mainDir ? { mainDir: outcome.mainDir } : {},
|
|
9681
9760
|
...outcome.error ? { error: outcome.error } : {}
|
|
@@ -9703,6 +9782,7 @@ function cleanupWorkspaceLocal(appDir, id, trunkBranch) {
|
|
|
9703
9782
|
const branch = workspaceBranchName(id);
|
|
9704
9783
|
const out = {
|
|
9705
9784
|
worktreeRemoved: null,
|
|
9785
|
+
worktreeRetained: null,
|
|
9706
9786
|
branchDeleted: null,
|
|
9707
9787
|
worktreeDir: null,
|
|
9708
9788
|
branch
|
|
@@ -9713,9 +9793,13 @@ function cleanupWorkspaceLocal(appDir, id, trunkBranch) {
|
|
|
9713
9793
|
const linked = worktrees.find((worktree, index) => index > 0 && worktree.branch === branch);
|
|
9714
9794
|
if (linked) {
|
|
9715
9795
|
out.worktreeDir = linked.path;
|
|
9796
|
+
if (!isManagedWorkspaceWorktree(linked.path, id)) {
|
|
9797
|
+
out.worktreeRetained = linked.path;
|
|
9798
|
+
return out;
|
|
9799
|
+
}
|
|
9716
9800
|
let inside = false;
|
|
9717
9801
|
try {
|
|
9718
|
-
inside =
|
|
9802
|
+
inside = realpathSync4(repoToplevel(appDir)) === realpathSync4(linked.path);
|
|
9719
9803
|
} catch {
|
|
9720
9804
|
inside = false;
|
|
9721
9805
|
}
|
|
@@ -9769,6 +9853,9 @@ function reportCleanupHuman(outcome) {
|
|
|
9769
9853
|
outcome.branchDeleted ? `branch ${outcome.branch}` : null
|
|
9770
9854
|
].filter(Boolean);
|
|
9771
9855
|
if (parts.length) p25.log.success(`Removed ${parts.join(" + ")}.`);
|
|
9856
|
+
if (outcome.worktreeRetained) {
|
|
9857
|
+
p25.log.info(`Retained externally managed worktree: ${outcome.worktreeRetained}`);
|
|
9858
|
+
}
|
|
9772
9859
|
if (outcome.worktreeRemoved && outcome.mainDir) {
|
|
9773
9860
|
p25.log.info(`This directory was removed \u2014 run: cd ${shQuote(outcome.mainDir)}`);
|
|
9774
9861
|
}
|
|
@@ -9952,11 +10039,10 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
9952
10039
|
idempotencyKey: mintIdempotencyKey()
|
|
9953
10040
|
});
|
|
9954
10041
|
const dirInput = typeof args.dir === "string" ? args.dir.trim() : "";
|
|
9955
|
-
const
|
|
9956
|
-
const dir =
|
|
9957
|
-
let provisioned = [];
|
|
10042
|
+
const worktreeRoot = dirInput ? isAbsolute6(dirInput) ? dirInput : resolve16(appDir, dirInput) : defaultWorkspaceRoot(appDir, id);
|
|
10043
|
+
const dir = appDirInWorktree(appDir, worktreeRoot);
|
|
9958
10044
|
try {
|
|
9959
|
-
|
|
10045
|
+
materializeWorkspaceWorktree(appDir, worktreeRoot, branch, baseOid, id);
|
|
9960
10046
|
} catch (error) {
|
|
9961
10047
|
spinner2?.stop("Worktree failed.");
|
|
9962
10048
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -9968,13 +10054,12 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
9968
10054
|
}
|
|
9969
10055
|
spinner2?.stop(`Workspace ${id} created.`);
|
|
9970
10056
|
if (!args.json) {
|
|
9971
|
-
if (provisioned.length > 0)
|
|
9972
|
-
p26.log.info(`Linked from the main checkout: ${provisioned.join(", ")}`);
|
|
9973
10057
|
p26.log.info(`Work in: cd ${dir}`);
|
|
9974
10058
|
p26.log.info(
|
|
9975
10059
|
`Then: commit as usual, \`deepspace workspace sync\` to publish, \`deepspace workspace land\` when done.`
|
|
9976
10060
|
);
|
|
9977
10061
|
}
|
|
10062
|
+
const action = { cwd: dir, argv: [detectPackageManager(dir), "install"] };
|
|
9978
10063
|
return {
|
|
9979
10064
|
data: {
|
|
9980
10065
|
workspaceId: id,
|
|
@@ -9983,16 +10068,17 @@ var newWorkspaceCommand = defineDeepspaceCommand({
|
|
|
9983
10068
|
baseOid,
|
|
9984
10069
|
branch,
|
|
9985
10070
|
dir,
|
|
9986
|
-
|
|
9987
|
-
}
|
|
10071
|
+
worktreeRoot
|
|
10072
|
+
},
|
|
10073
|
+
action
|
|
9988
10074
|
};
|
|
9989
10075
|
}
|
|
9990
10076
|
});
|
|
9991
10077
|
|
|
9992
10078
|
// src/cli/commands/workspace/attach.ts
|
|
9993
10079
|
import * as p27 from "@clack/prompts";
|
|
9994
|
-
import { existsSync as
|
|
9995
|
-
import { isAbsolute as isAbsolute7,
|
|
10080
|
+
import { existsSync as existsSync23, readdirSync as readdirSync7, rmSync as rmSync6 } from "fs";
|
|
10081
|
+
import { isAbsolute as isAbsolute7, resolve as resolve17 } from "path";
|
|
9996
10082
|
function finishedWorkspaceMessage(id, view) {
|
|
9997
10083
|
const workspace = view.workspace;
|
|
9998
10084
|
if (workspace.status === "landed") {
|
|
@@ -10001,7 +10087,7 @@ function finishedWorkspaceMessage(id, view) {
|
|
|
10001
10087
|
return `Workspace ${id} is ${workspace.status} \u2014 its ref is retained briefly (fetch ${workspace.ref} to inspect), but attach only works on active workspaces.`;
|
|
10002
10088
|
}
|
|
10003
10089
|
function cleanFailedFreshAttachDir(dir) {
|
|
10004
|
-
if (!
|
|
10090
|
+
if (!existsSync23(dir)) return [];
|
|
10005
10091
|
let entries;
|
|
10006
10092
|
try {
|
|
10007
10093
|
entries = readdirSync7(dir);
|
|
@@ -10076,32 +10162,32 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10076
10162
|
);
|
|
10077
10163
|
}
|
|
10078
10164
|
const requestedDir = typeof args.dir === "string" ? args.dir.trim() : "";
|
|
10079
|
-
const
|
|
10080
|
-
const dir2 =
|
|
10165
|
+
const worktreeRoot = requestedDir ? isAbsolute7(requestedDir) ? requestedDir : resolve17(appDir, requestedDir) : defaultWorkspaceRoot(appDir, id);
|
|
10166
|
+
const dir2 = appDirInWorktree(appDir, worktreeRoot);
|
|
10081
10167
|
if (resolveCommit(appDir, `refs/heads/${branch}`)) {
|
|
10082
10168
|
throw new Refusal(
|
|
10083
10169
|
`Branch ${branch} already exists locally (this workspace has a worktree here). Find it with \`git worktree list\` and continue there.`,
|
|
10084
10170
|
"branch_exists"
|
|
10085
10171
|
);
|
|
10086
10172
|
}
|
|
10087
|
-
|
|
10173
|
+
materializeWorkspaceWorktree(appDir, worktreeRoot, branch, tip2, id);
|
|
10088
10174
|
spinner2?.stop(`Attached ${id}.`);
|
|
10089
10175
|
if (!args.json) {
|
|
10090
|
-
if (provisioned.length > 0)
|
|
10091
|
-
p27.log.info(`Linked from the main checkout: ${provisioned.join(", ")}`);
|
|
10092
10176
|
p27.log.info(`Task: ${view2.workspace.task}`);
|
|
10093
10177
|
p27.log.info(`Work in: cd ${dir2}`);
|
|
10094
10178
|
}
|
|
10179
|
+
const action2 = { cwd: dir2, argv: [detectPackageManager(dir2), "install"] };
|
|
10095
10180
|
return {
|
|
10096
10181
|
data: {
|
|
10097
10182
|
workspaceId: id,
|
|
10098
10183
|
mode: "worktree",
|
|
10099
10184
|
dir: dir2,
|
|
10185
|
+
worktreeRoot,
|
|
10100
10186
|
branch,
|
|
10101
10187
|
tipOid: tip2,
|
|
10102
|
-
task: view2.workspace.task
|
|
10103
|
-
|
|
10104
|
-
|
|
10188
|
+
task: view2.workspace.task
|
|
10189
|
+
},
|
|
10190
|
+
action: action2
|
|
10105
10191
|
};
|
|
10106
10192
|
}
|
|
10107
10193
|
const token = await ensureToken();
|
|
@@ -10119,7 +10205,7 @@ var attachWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10119
10205
|
process.cwd(),
|
|
10120
10206
|
(typeof args.dir === "string" ? args.dir.trim() : "") || `ws-${id.slice(3).toLowerCase()}`
|
|
10121
10207
|
);
|
|
10122
|
-
if (
|
|
10208
|
+
if (existsSync23(dir)) {
|
|
10123
10209
|
spinner2?.stop("Target exists.");
|
|
10124
10210
|
throw new Refusal(`${dir} already exists \u2014 pass a different directory.`, "dir_exists");
|
|
10125
10211
|
}
|
|
@@ -10436,13 +10522,13 @@ import * as p31 from "@clack/prompts";
|
|
|
10436
10522
|
|
|
10437
10523
|
// src/cli/lib/validation.ts
|
|
10438
10524
|
import { sync as spawnSync7 } from "cross-spawn";
|
|
10439
|
-
import { readFileSync as
|
|
10440
|
-
import { join as
|
|
10525
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
10526
|
+
import { join as join22 } from "path";
|
|
10441
10527
|
var MAX_SUMMARY_CHARS = 4e3;
|
|
10442
10528
|
var MAX_OUTPUT_BUFFER = 64 * 1024 * 1024;
|
|
10443
10529
|
function configuredCommand(appDir) {
|
|
10444
10530
|
try {
|
|
10445
|
-
const pkg2 = JSON.parse(
|
|
10531
|
+
const pkg2 = JSON.parse(readFileSync24(join22(appDir, "package.json"), "utf-8"));
|
|
10446
10532
|
if (pkg2.scripts?.validate) return `${detectPackageManager(appDir)} run --silent validate`;
|
|
10447
10533
|
} catch {
|
|
10448
10534
|
}
|
|
@@ -10706,7 +10792,7 @@ var landWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10706
10792
|
spinner2?.stop(`Landed ${id} into ${intoBranch} at ${landedOid.slice(0, 10)}.`);
|
|
10707
10793
|
const cleanup = keepWorktree ? null : cleanupWorkspaceLocal(appDir, id, intoBranch);
|
|
10708
10794
|
const inOwnWorktree = !cleanup && inOwnLinkedWorktree(appDir, id);
|
|
10709
|
-
const retainedWorktree = keepWorktree || inOwnWorktree ? appDir : null;
|
|
10795
|
+
const retainedWorktree = cleanup?.worktreeRetained ?? (keepWorktree || inOwnWorktree ? appDir : null);
|
|
10710
10796
|
const data = {
|
|
10711
10797
|
workspaceId: id,
|
|
10712
10798
|
into: intoBranch,
|
|
@@ -10776,7 +10862,9 @@ var dropWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10776
10862
|
}
|
|
10777
10863
|
const { appDir, api: api4 } = await resolveTarget2(appArg);
|
|
10778
10864
|
const id = inferWorkspaceId(appDir, explicitId);
|
|
10779
|
-
|
|
10865
|
+
const ownLinkedWorktree = inOwnLinkedWorktree(appDir, id);
|
|
10866
|
+
const cleanupOwnsCheckout = !ownLinkedWorktree || isManagedWorkspaceWorktree(appDir, id);
|
|
10867
|
+
if (!keepWorktree && cleanupOwnsCheckout && !isWorkTreeClean(appDir)) {
|
|
10780
10868
|
const dropArgv = ["deepspace", "workspace", "drop", ...explicitId ? [explicitId] : []];
|
|
10781
10869
|
const dropNext = humanCommand(dropArgv);
|
|
10782
10870
|
throw new Refusal(
|
|
@@ -10788,8 +10876,8 @@ var dropWorkspaceCommand = defineDeepspaceCommand({
|
|
|
10788
10876
|
const { view } = await api4.dropWorkspace(id);
|
|
10789
10877
|
spinner2?.message(`Cleaning up ${id} locally\u2026`);
|
|
10790
10878
|
const cleanup = keepWorktree ? null : cleanupWorkspaceLocal(appDir, id, null);
|
|
10791
|
-
const inOwnWorktree = !cleanup &&
|
|
10792
|
-
const retainedWorktree = keepWorktree || inOwnWorktree ? appDir : null;
|
|
10879
|
+
const inOwnWorktree = !cleanup && ownLinkedWorktree;
|
|
10880
|
+
const retainedWorktree = cleanup?.worktreeRetained ?? (keepWorktree || inOwnWorktree ? appDir : null);
|
|
10793
10881
|
const data = {
|
|
10794
10882
|
workspaceId: id,
|
|
10795
10883
|
status: view.workspace.status,
|
|
@@ -11077,9 +11165,9 @@ var git_credential_default = defineCommand13({
|
|
|
11077
11165
|
});
|
|
11078
11166
|
|
|
11079
11167
|
// src/cli/cli.ts
|
|
11080
|
-
var __dirname =
|
|
11081
|
-
var pkg = JSON.parse(
|
|
11082
|
-
if (process.platform === "linux" && !process.env.SSL_CERT_FILE &&
|
|
11168
|
+
var __dirname = dirname10(fileURLToPath3(import.meta.url));
|
|
11169
|
+
var pkg = JSON.parse(readFileSync25(join23(__dirname, "..", "package.json"), "utf-8"));
|
|
11170
|
+
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync24("/etc/ssl/certs/ca-certificates.crt")) {
|
|
11083
11171
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
11084
11172
|
}
|
|
11085
11173
|
var create2 = defineCommand14({
|