@devrouter/cli 0.1.2 → 0.2.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/README.md +5 -0
- package/dist/devrouter-lifecycle-worker.js +438 -218
- package/dist/devrouter.js +2785 -755
- package/package.json +6 -1
- package/upgrade-prompts/0.1.3.md +55 -0
- package/upgrade-prompts/0.2.0.md +81 -0
|
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/core/devpod-exec.ts
|
|
26
|
-
var
|
|
26
|
+
var import_node_child_process32 = require("child_process");
|
|
27
27
|
var import_node_crypto23 = require("crypto");
|
|
28
28
|
|
|
29
29
|
// src/core/devpod-environment.ts
|
|
@@ -1164,7 +1164,11 @@ function listDevpodWorkspacesRaw(options = {}) {
|
|
|
1164
1164
|
}
|
|
1165
1165
|
if (result.error || result.status !== 0) {
|
|
1166
1166
|
const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
1167
|
-
|
|
1167
|
+
const failure = new Error(
|
|
1168
|
+
`devpod list failed: ${details || "devpod is not installed or unavailable"}`
|
|
1169
|
+
);
|
|
1170
|
+
if (result.error) failure.code = result.error.code;
|
|
1171
|
+
throw failure;
|
|
1168
1172
|
}
|
|
1169
1173
|
let parsed;
|
|
1170
1174
|
try {
|
|
@@ -1303,9 +1307,11 @@ function listDevsyWorkspaces() {
|
|
|
1303
1307
|
);
|
|
1304
1308
|
if (result.status !== 0) {
|
|
1305
1309
|
const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
1306
|
-
|
|
1310
|
+
const failure = new Error(
|
|
1307
1311
|
`devsy workspace list failed: ${details || "devsy is not installed or unavailable"}`
|
|
1308
1312
|
);
|
|
1313
|
+
if (result.error) failure.code = result.error.code;
|
|
1314
|
+
throw failure;
|
|
1309
1315
|
}
|
|
1310
1316
|
let parsed;
|
|
1311
1317
|
try {
|
|
@@ -1775,25 +1781,23 @@ function readWorkspaceRuntimeConfig() {
|
|
|
1775
1781
|
}
|
|
1776
1782
|
return config;
|
|
1777
1783
|
}
|
|
1784
|
+
function readRegistrySnapshot(runtime, read2, snapshots) {
|
|
1785
|
+
try {
|
|
1786
|
+
return read2();
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
if (error?.code === "ENOENT") return void 0;
|
|
1789
|
+
snapshots.unavailable.push(runtime);
|
|
1790
|
+
snapshots.errors[runtime] = error instanceof Error ? error.message : String(error);
|
|
1791
|
+
return void 0;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1778
1794
|
function getWorkspaceRegistrySnapshots() {
|
|
1779
1795
|
if (cachedSnapshots) return cachedSnapshots;
|
|
1780
1796
|
const snapshots = { unavailable: [], errors: {} };
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
snapshots.unavailable.push("devsy");
|
|
1786
|
-
snapshots.errors.devsy = error instanceof Error ? error.message : String(error);
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
if (isRuntimeInstalled("devpod")) {
|
|
1790
|
-
try {
|
|
1791
|
-
snapshots.devpod = listDevpodWorkspacesRaw();
|
|
1792
|
-
} catch (error) {
|
|
1793
|
-
snapshots.unavailable.push("devpod");
|
|
1794
|
-
snapshots.errors.devpod = error instanceof Error ? error.message : String(error);
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
+
const devsy = readRegistrySnapshot("devsy", listDevsyWorkspaces, snapshots);
|
|
1798
|
+
if (devsy !== void 0) snapshots.devsy = devsy;
|
|
1799
|
+
const devpod = readRegistrySnapshot("devpod", listDevpodWorkspacesRaw, snapshots);
|
|
1800
|
+
if (devpod !== void 0) snapshots.devpod = devpod;
|
|
1797
1801
|
cachedSnapshots = snapshots;
|
|
1798
1802
|
return snapshots;
|
|
1799
1803
|
}
|
|
@@ -1954,7 +1958,7 @@ function selectDevpodWorkspace(workspaces, repoPath) {
|
|
|
1954
1958
|
}
|
|
1955
1959
|
|
|
1956
1960
|
// src/core/devsy-exec.ts
|
|
1957
|
-
var
|
|
1961
|
+
var import_node_child_process31 = require("child_process");
|
|
1958
1962
|
|
|
1959
1963
|
// src/core/devsy-exec-proof.ts
|
|
1960
1964
|
var import_node_fs7 = __toESM(require("fs"));
|
|
@@ -2042,9 +2046,9 @@ function revalidateDevsyExecProof(repoPath, proof) {
|
|
|
2042
2046
|
}
|
|
2043
2047
|
|
|
2044
2048
|
// src/core/devsy-mutation.ts
|
|
2045
|
-
var
|
|
2046
|
-
var
|
|
2047
|
-
var
|
|
2049
|
+
var import_node_child_process18 = require("child_process");
|
|
2050
|
+
var import_node_fs26 = __toESM(require("fs"));
|
|
2051
|
+
var import_node_path30 = __toESM(require("path"));
|
|
2048
2052
|
|
|
2049
2053
|
// src/core/devsy-agent.ts
|
|
2050
2054
|
var import_node_child_process9 = require("child_process");
|
|
@@ -2320,9 +2324,9 @@ function requireReadyDevsyAgent(options = {}) {
|
|
|
2320
2324
|
}
|
|
2321
2325
|
|
|
2322
2326
|
// src/core/managed-devsy-stop.ts
|
|
2323
|
-
var
|
|
2327
|
+
var import_node_fs22 = __toESM(require("fs"));
|
|
2324
2328
|
var import_node_os5 = __toESM(require("os"));
|
|
2325
|
-
var
|
|
2329
|
+
var import_node_path26 = __toESM(require("path"));
|
|
2326
2330
|
var import_node_util5 = require("util");
|
|
2327
2331
|
|
|
2328
2332
|
// src/core/devcontainer-profile.ts
|
|
@@ -4040,11 +4044,19 @@ function absentRegistrationIdentity(state) {
|
|
|
4040
4044
|
resetWorkspaceRuntimeCaches();
|
|
4041
4045
|
if (resolveWorkspaceRuntimeOrDefault(state.repoPath) !== "devsy")
|
|
4042
4046
|
throw new Error("Stop provider changed.");
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4047
|
+
const linked = isLinkedWorktree(state.repoPath);
|
|
4048
|
+
if (linked !== (state.workspace !== void 0))
|
|
4049
|
+
throw new Error("Absent stop requires the exact workspace identity.");
|
|
4050
|
+
let identity2;
|
|
4051
|
+
if (linked) {
|
|
4052
|
+
const workspace = state.workspace;
|
|
4053
|
+
const record5 = readWorkspaceOwnership(state.repoPath, workspace);
|
|
4054
|
+
if (!record5 || record5.workspace !== workspace || record5.devpodId !== state.devpodId || !sameWorkspacePath(record5.worktreePath, state.repoPath) || resolveWorktreeWorkspace(state.repoPath) !== workspace || inspectWorkspaceOwnership(record5, listGitWorktrees(state.repoPath), void 0).ownerStatus !== "present")
|
|
4055
|
+
throw new Error("Absent stop workspace ownership changed.");
|
|
4056
|
+
identity2 = { kind: "linked", record: record5, gitCommonDir: resolveGitCommonDir(state.repoPath) };
|
|
4057
|
+
} else {
|
|
4058
|
+
identity2 = { kind: "primary", repoPath: state.repoPath, devpodId: state.devpodId };
|
|
4059
|
+
}
|
|
4048
4060
|
for (const entries of [
|
|
4049
4061
|
listDevsyWorkspaces(),
|
|
4050
4062
|
listDevpodWorkspacesRaw({ allowMissingExecutable: true })
|
|
@@ -4054,7 +4066,7 @@ function absentRegistrationIdentity(state) {
|
|
|
4054
4066
|
))
|
|
4055
4067
|
throw new Error("Absent stop requires both provider registrations to remain absent.");
|
|
4056
4068
|
}
|
|
4057
|
-
return
|
|
4069
|
+
return identity2;
|
|
4058
4070
|
}
|
|
4059
4071
|
function replacementObservation(state) {
|
|
4060
4072
|
resetWorkspaceRuntimeCaches();
|
|
@@ -4164,7 +4176,7 @@ function proveManagedStop(state) {
|
|
|
4164
4176
|
const identityKeys = ["context", "providerId", "uid", "sourcePath", "sourceContainer"];
|
|
4165
4177
|
const current = identityOf(owner.workspace);
|
|
4166
4178
|
const changed = identityKeys.filter((key) => baseline[key] !== current[key]);
|
|
4167
|
-
if (changed.length === 0 &&
|
|
4179
|
+
if (changed.length === 0 && inspectManagedStopContainers(baseline.project, baseline.endpoint).length === 0) {
|
|
4168
4180
|
proveAbsentPopulationStop(state, baseline, current);
|
|
4169
4181
|
return { status: "proven-absent", containers: [] };
|
|
4170
4182
|
}
|
|
@@ -5361,7 +5373,7 @@ function retain(session, previous) {
|
|
|
5361
5373
|
|
|
5362
5374
|
// src/core/reliability-operation-store.ts
|
|
5363
5375
|
function installedCliVersion() {
|
|
5364
|
-
return true ? "0.
|
|
5376
|
+
return true ? "0.2.0" : "0.0.0-dev";
|
|
5365
5377
|
}
|
|
5366
5378
|
function compareCliVersions(left, right) {
|
|
5367
5379
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -5501,6 +5513,7 @@ function validate2(record5, identity2) {
|
|
|
5501
5513
|
"revision",
|
|
5502
5514
|
"state",
|
|
5503
5515
|
"worker",
|
|
5516
|
+
"stopWorkStarted",
|
|
5504
5517
|
"effectSequence",
|
|
5505
5518
|
"outcome",
|
|
5506
5519
|
"result",
|
|
@@ -5682,6 +5695,9 @@ function validate2(record5, identity2) {
|
|
|
5682
5695
|
throw new Error("Reliability worker identity is invalid.");
|
|
5683
5696
|
}
|
|
5684
5697
|
}
|
|
5698
|
+
if (record5.stopWorkStarted !== void 0 && record5.stopWorkStarted !== null) {
|
|
5699
|
+
if (record5.stopWorkStarted !== true) throw new Error("Invalid stop work marker.");
|
|
5700
|
+
}
|
|
5685
5701
|
}
|
|
5686
5702
|
var ReliabilityJournalError = class extends Error {
|
|
5687
5703
|
constructor(cause, location, message) {
|
|
@@ -6018,8 +6034,8 @@ function clearStartupWitness(identity2, fence) {
|
|
|
6018
6034
|
|
|
6019
6035
|
// src/core/repo-config.ts
|
|
6020
6036
|
var import_node_crypto10 = require("crypto");
|
|
6021
|
-
var
|
|
6022
|
-
var
|
|
6037
|
+
var import_node_fs19 = __toESM(require("fs"));
|
|
6038
|
+
var import_node_path23 = __toESM(require("path"));
|
|
6023
6039
|
var import_yaml3 = __toESM(require("yaml"));
|
|
6024
6040
|
|
|
6025
6041
|
// src/core/capabilities.ts
|
|
@@ -6044,6 +6060,44 @@ function formatSupportedTcpProtocols() {
|
|
|
6044
6060
|
return SUPPORTED_TCP_PROTOCOLS.join(", ");
|
|
6045
6061
|
}
|
|
6046
6062
|
|
|
6063
|
+
// src/core/worktree-safety.ts
|
|
6064
|
+
var import_node_child_process13 = require("child_process");
|
|
6065
|
+
var import_node_fs18 = __toESM(require("fs"));
|
|
6066
|
+
var import_node_path22 = __toESM(require("path"));
|
|
6067
|
+
|
|
6068
|
+
// src/core/forge-changes.ts
|
|
6069
|
+
var import_node_child_process12 = require("child_process");
|
|
6070
|
+
|
|
6071
|
+
// src/core/worktree-safety.ts
|
|
6072
|
+
var WORKTREE_LOCAL_SAFETY_CODES = [
|
|
6073
|
+
"git-error",
|
|
6074
|
+
"detached",
|
|
6075
|
+
"index-lock",
|
|
6076
|
+
"operation-in-progress",
|
|
6077
|
+
"submodule",
|
|
6078
|
+
"dirty",
|
|
6079
|
+
"untracked",
|
|
6080
|
+
"ignored-state"
|
|
6081
|
+
];
|
|
6082
|
+
var WORKTREE_SAFETY_CODES = [
|
|
6083
|
+
...WORKTREE_LOCAL_SAFETY_CODES,
|
|
6084
|
+
"primary",
|
|
6085
|
+
"locked",
|
|
6086
|
+
"runtime-present",
|
|
6087
|
+
"forge-not-checked",
|
|
6088
|
+
"forge-unavailable",
|
|
6089
|
+
"no-change",
|
|
6090
|
+
"change-open",
|
|
6091
|
+
"change-closed",
|
|
6092
|
+
"head-beyond-merge",
|
|
6093
|
+
"active"
|
|
6094
|
+
];
|
|
6095
|
+
var DISPOSABLE_OVERRIDE_FILE = import_node_path22.default.join("info", "worktree-reclaim-disposable");
|
|
6096
|
+
function isValidWorktreePattern(pattern) {
|
|
6097
|
+
if (!pattern || pattern.startsWith("/") || pattern.includes("\\")) return false;
|
|
6098
|
+
return pattern.split("/").every((segment) => segment && segment !== "." && segment !== "..");
|
|
6099
|
+
}
|
|
6100
|
+
|
|
6047
6101
|
// src/core/repo-config.ts
|
|
6048
6102
|
function compareSemver2(a, b) {
|
|
6049
6103
|
const parse2 = (v) => {
|
|
@@ -6878,6 +6932,7 @@ function parseConfig(raw, configPath) {
|
|
|
6878
6932
|
"managedRuntime",
|
|
6879
6933
|
"profiles",
|
|
6880
6934
|
"capacity",
|
|
6935
|
+
"worktrees",
|
|
6881
6936
|
"apps"
|
|
6882
6937
|
],
|
|
6883
6938
|
configPath
|
|
@@ -6952,6 +7007,7 @@ function parseConfig(raw, configPath) {
|
|
|
6952
7007
|
const managedRuntime = parseManagedRuntime(root.managedRuntime, configPath);
|
|
6953
7008
|
const profiles = parseProfiles(root.profiles, configPath, apps, managedRuntime);
|
|
6954
7009
|
const capacity = parseCapacityEstimates(root.capacity, configPath, profiles);
|
|
7010
|
+
const worktrees = parseWorktreesConfig(root.worktrees, configPath);
|
|
6955
7011
|
return {
|
|
6956
7012
|
version: 1,
|
|
6957
7013
|
...devrouter ? { devrouter } : {},
|
|
@@ -6960,9 +7016,30 @@ function parseConfig(raw, configPath) {
|
|
|
6960
7016
|
...managedRuntime ? { managedRuntime } : {},
|
|
6961
7017
|
...profiles ? { profiles } : {},
|
|
6962
7018
|
...capacity ? { capacity } : {},
|
|
7019
|
+
...worktrees ? { worktrees } : {},
|
|
6963
7020
|
apps
|
|
6964
7021
|
};
|
|
6965
7022
|
}
|
|
7023
|
+
function parseWorktreesConfig(value, configPath) {
|
|
7024
|
+
if (value === void 0) return void 0;
|
|
7025
|
+
const raw = ensureObject(value, `${configPath}.worktrees`);
|
|
7026
|
+
ensureAllowedKeys(raw, ["disposable", "trim"], `${configPath}.worktrees`);
|
|
7027
|
+
const result = {};
|
|
7028
|
+
for (const key of ["disposable", "trim"]) {
|
|
7029
|
+
if (raw[key] === void 0) continue;
|
|
7030
|
+
const label = `${configPath}.worktrees.${key}`;
|
|
7031
|
+
if (!Array.isArray(raw[key])) throw new Error(`${label} must be an array of patterns.`);
|
|
7032
|
+
result[key] = raw[key].map((pattern, index) => {
|
|
7033
|
+
if (typeof pattern !== "string" || !isValidWorktreePattern(pattern)) {
|
|
7034
|
+
throw new Error(
|
|
7035
|
+
`${label}[${index}] must be a worktree-relative pattern without '..' or a leading '/'.`
|
|
7036
|
+
);
|
|
7037
|
+
}
|
|
7038
|
+
return pattern;
|
|
7039
|
+
});
|
|
7040
|
+
}
|
|
7041
|
+
return result;
|
|
7042
|
+
}
|
|
6966
7043
|
var PROFILE_NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
6967
7044
|
var MAX_PROFILES = 32;
|
|
6968
7045
|
function parseProfiles(value, configPath, apps, managedRuntime) {
|
|
@@ -7276,15 +7353,15 @@ function applyProfile(config, profile) {
|
|
|
7276
7353
|
return next;
|
|
7277
7354
|
}
|
|
7278
7355
|
function resolveRepoPath(repoPath) {
|
|
7279
|
-
return
|
|
7356
|
+
return import_node_path23.default.resolve(repoPath ?? process.cwd());
|
|
7280
7357
|
}
|
|
7281
7358
|
function getRepoConfigPath(repoPath) {
|
|
7282
|
-
return
|
|
7359
|
+
return import_node_path23.default.join(resolveRepoPath(repoPath), CONFIG_FILE_NAME);
|
|
7283
7360
|
}
|
|
7284
|
-
function loadRepoConfig(repoPath, read2 = (file) =>
|
|
7361
|
+
function loadRepoConfig(repoPath, read2 = (file) => import_node_fs19.default.readFileSync(file, "utf-8")) {
|
|
7285
7362
|
const resolvedRepoPath = resolveRepoPath(repoPath);
|
|
7286
7363
|
const configPath = getRepoConfigPath(resolvedRepoPath);
|
|
7287
|
-
if (!
|
|
7364
|
+
if (!import_node_fs19.default.existsSync(configPath)) {
|
|
7288
7365
|
throw new Error(
|
|
7289
7366
|
`Missing ${CONFIG_FILE_NAME} in ${resolvedRepoPath}. Run 'dev repo init --repo ${resolvedRepoPath}' first.`
|
|
7290
7367
|
);
|
|
@@ -7294,7 +7371,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs18.default.rea
|
|
|
7294
7371
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
7295
7372
|
const requiredVersion = config.devrouter?.version;
|
|
7296
7373
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
7297
|
-
const cliVersion = true ? "0.
|
|
7374
|
+
const cliVersion = true ? "0.2.0" : "0.0.0-dev";
|
|
7298
7375
|
if (cliVersion !== "0.0.0-dev" && compareSemver2(requiredVersion, cliVersion) > 0) {
|
|
7299
7376
|
hasWarnedVersionMismatch = true;
|
|
7300
7377
|
process.stderr.write(
|
|
@@ -7314,7 +7391,7 @@ function namespaceHost(host, workspace) {
|
|
|
7314
7391
|
return `${base}.${workspace}${suffix}`;
|
|
7315
7392
|
}
|
|
7316
7393
|
function applyWorkspace(config, workspace, repoPath) {
|
|
7317
|
-
const defaultToken = wsFromBranch(config.project?.name ??
|
|
7394
|
+
const defaultToken = wsFromBranch(config.project?.name ?? import_node_path23.default.basename(import_node_path23.default.resolve(repoPath))) ?? "app";
|
|
7318
7395
|
const substitutionToken = workspace ?? defaultToken;
|
|
7319
7396
|
const next = structuredClone(config);
|
|
7320
7397
|
for (const app of next.apps) {
|
|
@@ -7387,7 +7464,7 @@ function resolveAppDependencies(config, app) {
|
|
|
7387
7464
|
}
|
|
7388
7465
|
|
|
7389
7466
|
// src/core/docker.ts
|
|
7390
|
-
var
|
|
7467
|
+
var import_node_child_process14 = require("child_process");
|
|
7391
7468
|
var dockerodeConstructorPromise = null;
|
|
7392
7469
|
async function getDockerodeConstructor() {
|
|
7393
7470
|
if (!dockerodeConstructorPromise) {
|
|
@@ -7399,7 +7476,7 @@ async function getDockerodeConstructor() {
|
|
|
7399
7476
|
return dockerodeConstructorPromise;
|
|
7400
7477
|
}
|
|
7401
7478
|
function runDockerContextCommand(args) {
|
|
7402
|
-
const result = (0,
|
|
7479
|
+
const result = (0, import_node_child_process14.spawnSync)("docker", ["context", ...args], {
|
|
7403
7480
|
encoding: "utf-8"
|
|
7404
7481
|
});
|
|
7405
7482
|
if (result.status !== 0) {
|
|
@@ -7412,7 +7489,7 @@ function getCurrentDockerContext() {
|
|
|
7412
7489
|
return runDockerContextCommand(["show"]);
|
|
7413
7490
|
}
|
|
7414
7491
|
function getDockerHostFromContext(context) {
|
|
7415
|
-
const result = (0,
|
|
7492
|
+
const result = (0, import_node_child_process14.spawnSync)(
|
|
7416
7493
|
"docker",
|
|
7417
7494
|
["context", "inspect", context, "--format", "{{ .Endpoints.docker.Host }}"],
|
|
7418
7495
|
{ encoding: "utf-8" }
|
|
@@ -7472,16 +7549,16 @@ async function ensureNetwork(name) {
|
|
|
7472
7549
|
}
|
|
7473
7550
|
|
|
7474
7551
|
// src/core/tls.ts
|
|
7475
|
-
var
|
|
7552
|
+
var import_node_child_process15 = require("child_process");
|
|
7476
7553
|
var import_node_crypto11 = require("crypto");
|
|
7477
|
-
var
|
|
7478
|
-
var
|
|
7554
|
+
var import_node_fs20 = __toESM(require("fs"));
|
|
7555
|
+
var import_node_path24 = __toESM(require("path"));
|
|
7479
7556
|
var DEFAULT_TLS_CERT_HOSTS = ["localhost", "*.localhost"];
|
|
7480
|
-
var TLS_CERTIFICATE_LOCK_FILE =
|
|
7557
|
+
var TLS_CERTIFICATE_LOCK_FILE = import_node_path24.default.join(DEVROUTER_HOME, "tls-certificate.lock");
|
|
7481
7558
|
var TLS_CERTIFICATE_LOCK_WAIT_MS = 6e4;
|
|
7482
7559
|
var TLS_CERTIFICATE_WRITE_ATTEMPTS = 2;
|
|
7483
7560
|
function runOrThrow(command, args) {
|
|
7484
|
-
const result = (0,
|
|
7561
|
+
const result = (0, import_node_child_process15.spawnSync)(command, args, {
|
|
7485
7562
|
encoding: "utf-8"
|
|
7486
7563
|
});
|
|
7487
7564
|
if (result.status !== 0) {
|
|
@@ -7491,7 +7568,7 @@ function runOrThrow(command, args) {
|
|
|
7491
7568
|
return result.stdout.trim();
|
|
7492
7569
|
}
|
|
7493
7570
|
function commandExists(command) {
|
|
7494
|
-
const result = (0,
|
|
7571
|
+
const result = (0, import_node_child_process15.spawnSync)("sh", ["-c", `command -v ${command}`], { encoding: "utf-8" });
|
|
7495
7572
|
return result.status === 0;
|
|
7496
7573
|
}
|
|
7497
7574
|
function ensureMkcert() {
|
|
@@ -7509,8 +7586,8 @@ function tlsSetupCommand(repoPath) {
|
|
|
7509
7586
|
}
|
|
7510
7587
|
function getMkcertRootCAPath(options = {}) {
|
|
7511
7588
|
ensureMkcert();
|
|
7512
|
-
const rootCAPath =
|
|
7513
|
-
if (!
|
|
7589
|
+
const rootCAPath = import_node_path24.default.join(runOrThrow("mkcert", ["-CAROOT"]), "rootCA.pem");
|
|
7590
|
+
if (!import_node_fs20.default.existsSync(rootCAPath)) {
|
|
7514
7591
|
throw new Error(
|
|
7515
7592
|
`mkcert root CA was not found at '${rootCAPath}'. Run: ${tlsSetupCommand(options.repoPath)}`
|
|
7516
7593
|
);
|
|
@@ -7611,11 +7688,11 @@ function compactTLSCertificateHosts(hosts) {
|
|
|
7611
7688
|
);
|
|
7612
7689
|
}
|
|
7613
7690
|
function readCurrentCertificateHosts(options = {}) {
|
|
7614
|
-
if (!
|
|
7691
|
+
if (!import_node_fs20.default.existsSync(CERT_FILE)) {
|
|
7615
7692
|
return [];
|
|
7616
7693
|
}
|
|
7617
7694
|
try {
|
|
7618
|
-
const pem =
|
|
7695
|
+
const pem = import_node_fs20.default.readFileSync(CERT_FILE, "utf-8");
|
|
7619
7696
|
return parseCertificateDnsHosts(pem);
|
|
7620
7697
|
} catch (error) {
|
|
7621
7698
|
if (options.replaceMalformed) {
|
|
@@ -7807,11 +7884,11 @@ async function replacePublishedProxyRoutes(repoPath, config, workspace, options
|
|
|
7807
7884
|
}
|
|
7808
7885
|
|
|
7809
7886
|
// src/core/traefik-route-health.ts
|
|
7810
|
-
var
|
|
7811
|
-
var
|
|
7812
|
-
var
|
|
7887
|
+
var import_node_child_process16 = require("child_process");
|
|
7888
|
+
var import_node_fs21 = __toESM(require("fs"));
|
|
7889
|
+
var import_node_path25 = __toESM(require("path"));
|
|
7813
7890
|
var ROUTER_API_BASE = "http://127.0.0.1:8080/api";
|
|
7814
|
-
var ROUTER_RELOAD_LOCK_FILE =
|
|
7891
|
+
var ROUTER_RELOAD_LOCK_FILE = import_node_path25.default.join(DEVROUTER_HOME, "router-reload.lock");
|
|
7815
7892
|
var ROUTER_API_REQUEST_TIMEOUT_SECONDS = "2";
|
|
7816
7893
|
var DEFAULT_INITIAL_TIMEOUT_MS = 3e3;
|
|
7817
7894
|
var DEFAULT_RECOVERY_TIMEOUT_MS = 1e4;
|
|
@@ -7821,7 +7898,7 @@ function sleep(ms) {
|
|
|
7821
7898
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7822
7899
|
}
|
|
7823
7900
|
function inspectRouterApi(protocol) {
|
|
7824
|
-
const result = (0,
|
|
7901
|
+
const result = (0, import_node_child_process16.spawnSync)(
|
|
7825
7902
|
"curl",
|
|
7826
7903
|
[
|
|
7827
7904
|
"--silent",
|
|
@@ -7869,7 +7946,7 @@ function isRecord3(value) {
|
|
|
7869
7946
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7870
7947
|
}
|
|
7871
7948
|
function inspectNamedRouterApi(protocol, resource, name) {
|
|
7872
|
-
const result = (0,
|
|
7949
|
+
const result = (0, import_node_child_process16.spawnSync)(
|
|
7873
7950
|
"curl",
|
|
7874
7951
|
[
|
|
7875
7952
|
"--silent",
|
|
@@ -8080,7 +8157,7 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
|
|
|
8080
8157
|
throw new Error(
|
|
8081
8158
|
"Traefik routes did not reload; shared router restart is disabled for this operation."
|
|
8082
8159
|
);
|
|
8083
|
-
|
|
8160
|
+
import_node_fs21.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
8084
8161
|
return withFileLock(
|
|
8085
8162
|
ROUTER_RELOAD_LOCK_FILE,
|
|
8086
8163
|
{
|
|
@@ -8146,7 +8223,7 @@ function proveInitialManagedDevsyAbsence(repoPath, expectedId) {
|
|
|
8146
8223
|
if (!isLinkedWorktree(repoPath)) return void 0;
|
|
8147
8224
|
const workspace = resolveWorktreeWorkspace(repoPath);
|
|
8148
8225
|
if (!workspace || readManagedRuntimeState(repoPath, workspace)) return void 0;
|
|
8149
|
-
if (!
|
|
8226
|
+
if (!import_node_fs22.default.existsSync(import_node_path26.default.join(repoPath, ".devrouter.yml"))) return void 0;
|
|
8150
8227
|
const runtime = loadRuntimeConfig(repoPath, workspace);
|
|
8151
8228
|
if (!runtime.config.managedRuntime) return void 0;
|
|
8152
8229
|
const routes = proxyAppsFromConfig(runtime.config).flatMap(
|
|
@@ -8206,7 +8283,7 @@ function proveInitialManagedDevsyAbsence(repoPath, expectedId) {
|
|
|
8206
8283
|
};
|
|
8207
8284
|
}
|
|
8208
8285
|
function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
8209
|
-
if (!
|
|
8286
|
+
if (!import_node_fs22.default.existsSync(import_node_path26.default.join(repoPath, ".devrouter.yml"))) return false;
|
|
8210
8287
|
if (!loadRepoConfig(repoPath).managedRuntime) return false;
|
|
8211
8288
|
const linked = isLinkedWorktree(repoPath);
|
|
8212
8289
|
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
@@ -8263,8 +8340,8 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
8263
8340
|
profile: runtime.resolvedProfile,
|
|
8264
8341
|
linked
|
|
8265
8342
|
});
|
|
8266
|
-
const providerRoot =
|
|
8267
|
-
const featureDirectory =
|
|
8343
|
+
const providerRoot = import_node_path26.default.resolve(process.env.DEVSY_HOME || import_node_path26.default.join(import_node_os5.default.homedir(), ".devsy"));
|
|
8344
|
+
const featureDirectory = import_node_path26.default.join(
|
|
8268
8345
|
providerRoot,
|
|
8269
8346
|
"contexts",
|
|
8270
8347
|
owner.context,
|
|
@@ -8372,6 +8449,41 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
8372
8449
|
throw new Error("Initial managed stop left a workload running.");
|
|
8373
8450
|
return true;
|
|
8374
8451
|
}
|
|
8452
|
+
function restoreRecordedManagedDevcontainerConfig(repoPath) {
|
|
8453
|
+
const plan = recordedManagedDevcontainerRestorePlan(repoPath);
|
|
8454
|
+
if (!plan) return false;
|
|
8455
|
+
writeManagedDevcontainerConfig(plan);
|
|
8456
|
+
if (inspectManagedDevcontainerGeneratedConfig(plan).status !== "valid") {
|
|
8457
|
+
throw new Error(
|
|
8458
|
+
`Managed Dev Container path '${plan.generatedPath}' could not be restored from the recorded state.`
|
|
8459
|
+
);
|
|
8460
|
+
}
|
|
8461
|
+
return true;
|
|
8462
|
+
}
|
|
8463
|
+
function recordedManagedDevcontainerRestorePlan(repoPath) {
|
|
8464
|
+
try {
|
|
8465
|
+
if (!import_node_fs22.default.existsSync(import_node_path26.default.join(repoPath, ".devrouter.yml"))) return void 0;
|
|
8466
|
+
const linked = isLinkedWorktree(repoPath);
|
|
8467
|
+
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
8468
|
+
const state = readManagedRuntimeState(repoPath, workspace);
|
|
8469
|
+
if (!state) return void 0;
|
|
8470
|
+
const runtime = loadRuntimeConfig(repoPath, workspace ?? "", state.profile);
|
|
8471
|
+
if (!runtime.config.managedRuntime || runtime.profile !== state.profile || runtime.workspace !== workspace)
|
|
8472
|
+
return void 0;
|
|
8473
|
+
const plan = inspectManagedDevcontainerConfig({
|
|
8474
|
+
repoPath,
|
|
8475
|
+
config: runtime.config,
|
|
8476
|
+
profile: runtime.resolvedProfile,
|
|
8477
|
+
linked
|
|
8478
|
+
});
|
|
8479
|
+
if (inspectManagedDevcontainerGeneratedConfig(plan).status !== "missing") return void 0;
|
|
8480
|
+
if (plan.sourceConfigSha256 !== state.sourceConfigSha256 || plan.effectiveConfigSha256 !== state.effectiveConfigSha256)
|
|
8481
|
+
return void 0;
|
|
8482
|
+
return plan;
|
|
8483
|
+
} catch {
|
|
8484
|
+
return void 0;
|
|
8485
|
+
}
|
|
8486
|
+
}
|
|
8375
8487
|
function stopRetainedManagedDevsyWorkspace(options) {
|
|
8376
8488
|
const { repoPath, devsyId } = options;
|
|
8377
8489
|
const linked = isLinkedWorktree(repoPath);
|
|
@@ -8441,7 +8553,7 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
8441
8553
|
const owned = inspectWorkspaceContainers().filter(
|
|
8442
8554
|
(container) => sameWorkspacePath(
|
|
8443
8555
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
8444
|
-
|
|
8556
|
+
import_node_path26.default.join(repoPath, ".devcontainer")
|
|
8445
8557
|
)
|
|
8446
8558
|
);
|
|
8447
8559
|
if (owned.length !== 0) {
|
|
@@ -8470,8 +8582,8 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
8470
8582
|
if (!context || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(context) || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(devsyId)) {
|
|
8471
8583
|
throw new Error("Managed stop requires a valid exact Devsy context and workspace ID.");
|
|
8472
8584
|
}
|
|
8473
|
-
const devsyRoot =
|
|
8474
|
-
const featureDirectory =
|
|
8585
|
+
const devsyRoot = import_node_path26.default.resolve(process.env.DEVSY_HOME || import_node_path26.default.join(import_node_os5.default.homedir(), ".devsy"));
|
|
8586
|
+
const featureDirectory = import_node_path26.default.join(
|
|
8475
8587
|
devsyRoot,
|
|
8476
8588
|
"contexts",
|
|
8477
8589
|
context,
|
|
@@ -8610,13 +8722,13 @@ function networkProviderEnvironment(binding, source) {
|
|
|
8610
8722
|
|
|
8611
8723
|
// src/core/network-claim-lookup.ts
|
|
8612
8724
|
var import_node_crypto13 = require("crypto");
|
|
8613
|
-
var
|
|
8614
|
-
var
|
|
8725
|
+
var import_node_fs25 = __toESM(require("fs"));
|
|
8726
|
+
var import_node_path29 = __toESM(require("path"));
|
|
8615
8727
|
|
|
8616
8728
|
// src/core/network-claims.ts
|
|
8617
8729
|
var import_node_crypto12 = require("crypto");
|
|
8618
|
-
var
|
|
8619
|
-
var
|
|
8730
|
+
var import_node_fs24 = __toESM(require("fs"));
|
|
8731
|
+
var import_node_path28 = __toESM(require("path"));
|
|
8620
8732
|
|
|
8621
8733
|
// src/core/network-capacity.ts
|
|
8622
8734
|
var import_node_net = require("net");
|
|
@@ -8957,10 +9069,10 @@ function overlaps(left, right) {
|
|
|
8957
9069
|
}
|
|
8958
9070
|
|
|
8959
9071
|
// src/core/network-policy.ts
|
|
8960
|
-
var
|
|
8961
|
-
var
|
|
9072
|
+
var import_node_fs23 = __toESM(require("fs"));
|
|
9073
|
+
var import_node_path27 = __toESM(require("path"));
|
|
8962
9074
|
var NETWORK_POLICY_VERSION = 1;
|
|
8963
|
-
var NETWORK_POLICY_FILE =
|
|
9075
|
+
var NETWORK_POLICY_FILE = import_node_path27.default.join(DEVROUTER_HOME, "network-policy.json");
|
|
8964
9076
|
var DEFAULT_NETWORK_POLICY_ENDPOINT_RESERVE = 8;
|
|
8965
9077
|
var NETWORK_POLICY_PREFIX_LENGTHS = [24, 25, 26];
|
|
8966
9078
|
var MAX_NETWORK_POLICY_FILE_BYTES = 64 * 1024;
|
|
@@ -9153,7 +9265,7 @@ function hasControlCharacter(value) {
|
|
|
9153
9265
|
function readPolicyFile(filePath) {
|
|
9154
9266
|
let initialStats;
|
|
9155
9267
|
try {
|
|
9156
|
-
initialStats =
|
|
9268
|
+
initialStats = import_node_fs23.default.lstatSync(filePath);
|
|
9157
9269
|
} catch (error) {
|
|
9158
9270
|
if (isErrno(error, "ENOENT")) return { status: "absent" };
|
|
9159
9271
|
return { status: "invalid", error: "network policy could not be read" };
|
|
@@ -9166,16 +9278,16 @@ function readPolicyFile(filePath) {
|
|
|
9166
9278
|
}
|
|
9167
9279
|
let fileDescriptor;
|
|
9168
9280
|
try {
|
|
9169
|
-
fileDescriptor =
|
|
9281
|
+
fileDescriptor = import_node_fs23.default.openSync(
|
|
9170
9282
|
filePath,
|
|
9171
|
-
|
|
9283
|
+
import_node_fs23.default.constants.O_RDONLY | import_node_fs23.default.constants.O_NONBLOCK | (import_node_fs23.default.constants.O_NOFOLLOW ?? 0)
|
|
9172
9284
|
);
|
|
9173
9285
|
} catch (error) {
|
|
9174
9286
|
if (isErrno(error, "ENOENT")) return { status: "absent" };
|
|
9175
9287
|
return { status: "invalid", error: "network policy could not be read" };
|
|
9176
9288
|
}
|
|
9177
9289
|
try {
|
|
9178
|
-
const openedStats =
|
|
9290
|
+
const openedStats = import_node_fs23.default.fstatSync(fileDescriptor);
|
|
9179
9291
|
if (!openedStats.isFile()) {
|
|
9180
9292
|
return { status: "invalid", error: "network policy must be a regular file" };
|
|
9181
9293
|
}
|
|
@@ -9188,12 +9300,12 @@ function readPolicyFile(filePath) {
|
|
|
9188
9300
|
const buffer = Buffer.alloc(openedStats.size);
|
|
9189
9301
|
let offset = 0;
|
|
9190
9302
|
while (offset < buffer.length) {
|
|
9191
|
-
const bytesRead =
|
|
9303
|
+
const bytesRead = import_node_fs23.default.readSync(fileDescriptor, buffer, offset, buffer.length - offset, null);
|
|
9192
9304
|
if (bytesRead === 0)
|
|
9193
9305
|
return { status: "invalid", error: "network policy changed during read" };
|
|
9194
9306
|
offset += bytesRead;
|
|
9195
9307
|
}
|
|
9196
|
-
const finalStats =
|
|
9308
|
+
const finalStats = import_node_fs23.default.fstatSync(fileDescriptor);
|
|
9197
9309
|
if (!finalStats.isFile() || finalStats.size !== openedStats.size) {
|
|
9198
9310
|
return { status: "invalid", error: "network policy changed during read" };
|
|
9199
9311
|
}
|
|
@@ -9202,7 +9314,7 @@ function readPolicyFile(filePath) {
|
|
|
9202
9314
|
return { status: "invalid", error: "network policy could not be read" };
|
|
9203
9315
|
} finally {
|
|
9204
9316
|
try {
|
|
9205
|
-
|
|
9317
|
+
import_node_fs23.default.closeSync(fileDescriptor);
|
|
9206
9318
|
} catch {
|
|
9207
9319
|
}
|
|
9208
9320
|
}
|
|
@@ -9222,7 +9334,7 @@ function isErrno(error, code) {
|
|
|
9222
9334
|
|
|
9223
9335
|
// src/core/network-claims.ts
|
|
9224
9336
|
var NETWORK_CLAIMS_VERSION = 1;
|
|
9225
|
-
var NETWORK_CLAIMS_ROOT =
|
|
9337
|
+
var NETWORK_CLAIMS_ROOT = import_node_path28.default.join(DEVROUTER_HOME, "networks");
|
|
9226
9338
|
var NETWORK_CLAIMS_MAX_RECORDS = 256;
|
|
9227
9339
|
var NETWORK_CLAIMS_MAX_FILE_BYTES = 512 * 1024;
|
|
9228
9340
|
var NETWORK_CLAIMS_DEFAULT_PREFIX = 26;
|
|
@@ -9628,27 +9740,27 @@ function assertExactKeys(value, keys2) {
|
|
|
9628
9740
|
function readDocument(store) {
|
|
9629
9741
|
let descriptor;
|
|
9630
9742
|
try {
|
|
9631
|
-
descriptor =
|
|
9743
|
+
descriptor = import_node_fs24.default.openSync(
|
|
9632
9744
|
store.filePath,
|
|
9633
|
-
|
|
9745
|
+
import_node_fs24.default.constants.O_RDONLY | import_node_fs24.default.constants.O_NONBLOCK | (import_node_fs24.default.constants.O_NOFOLLOW ?? 0)
|
|
9634
9746
|
);
|
|
9635
9747
|
} catch (error) {
|
|
9636
9748
|
if (isErrno2(error, "ENOENT")) return void 0;
|
|
9637
9749
|
throw claimError("invalid-state", "network claim state could not be read");
|
|
9638
9750
|
}
|
|
9639
9751
|
try {
|
|
9640
|
-
const stat =
|
|
9752
|
+
const stat = import_node_fs24.default.fstatSync(descriptor);
|
|
9641
9753
|
if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES || (stat.mode & 63) !== 0) {
|
|
9642
9754
|
throw claimError("invalid-state", "network claim state is not a bounded private file");
|
|
9643
9755
|
}
|
|
9644
9756
|
const buffer = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
|
|
9645
9757
|
let count = 0;
|
|
9646
9758
|
while (count < buffer.length) {
|
|
9647
|
-
const bytesRead =
|
|
9759
|
+
const bytesRead = import_node_fs24.default.readSync(descriptor, buffer, count, buffer.length - count, null);
|
|
9648
9760
|
if (bytesRead === 0) break;
|
|
9649
9761
|
count += bytesRead;
|
|
9650
9762
|
}
|
|
9651
|
-
const finalStat =
|
|
9763
|
+
const finalStat = import_node_fs24.default.fstatSync(descriptor);
|
|
9652
9764
|
if (count > NETWORK_CLAIMS_MAX_FILE_BYTES || finalStat.size !== stat.size) {
|
|
9653
9765
|
throw claimError("invalid-state", "network claim state changed during read");
|
|
9654
9766
|
}
|
|
@@ -9660,7 +9772,7 @@ function readDocument(store) {
|
|
|
9660
9772
|
}
|
|
9661
9773
|
return validateDocument(parsed, store.daemonId);
|
|
9662
9774
|
} finally {
|
|
9663
|
-
|
|
9775
|
+
import_node_fs24.default.closeSync(descriptor);
|
|
9664
9776
|
}
|
|
9665
9777
|
}
|
|
9666
9778
|
function validateDocument(value, daemonId) {
|
|
@@ -9764,7 +9876,7 @@ function withStoreLock(daemonId, options, operation) {
|
|
|
9764
9876
|
if (!Number.isSafeInteger(waitMs) || waitMs < 0 || waitMs > MAX_LOCK_WAIT_MS) {
|
|
9765
9877
|
throw claimError("invalid-input", "network claim lock wait is invalid");
|
|
9766
9878
|
}
|
|
9767
|
-
|
|
9879
|
+
import_node_fs24.default.mkdirSync(store.root, { recursive: true, mode: 448 });
|
|
9768
9880
|
assertDirectory(store.root);
|
|
9769
9881
|
return withFileLockSync(
|
|
9770
9882
|
store.lockPath,
|
|
@@ -9779,23 +9891,23 @@ function createStore(daemonId, options) {
|
|
|
9779
9891
|
return {
|
|
9780
9892
|
root,
|
|
9781
9893
|
daemonId,
|
|
9782
|
-
filePath:
|
|
9783
|
-
lockPath:
|
|
9894
|
+
filePath: import_node_path28.default.join(root, `${key}.json`),
|
|
9895
|
+
lockPath: import_node_path28.default.join(root, `${key}.lock`),
|
|
9784
9896
|
writeAtomic: options.writeAtomic ?? writeFileAtomically
|
|
9785
9897
|
};
|
|
9786
9898
|
}
|
|
9787
9899
|
function validateRoot(root) {
|
|
9788
|
-
if (typeof root !== "string" || !
|
|
9900
|
+
if (typeof root !== "string" || !import_node_path28.default.isAbsolute(root) || root.includes("\0")) {
|
|
9789
9901
|
throw claimError("invalid-input", "network claim root is invalid");
|
|
9790
9902
|
}
|
|
9791
|
-
const resolved =
|
|
9903
|
+
const resolved = import_node_path28.default.resolve(root);
|
|
9792
9904
|
if (resolved !== root) throw claimError("invalid-input", "network claim root is invalid");
|
|
9793
9905
|
return resolved;
|
|
9794
9906
|
}
|
|
9795
9907
|
function assertDirectory(directory) {
|
|
9796
9908
|
let stat;
|
|
9797
9909
|
try {
|
|
9798
|
-
stat =
|
|
9910
|
+
stat = import_node_fs24.default.lstatSync(directory);
|
|
9799
9911
|
} catch {
|
|
9800
9912
|
throw claimError("invalid-state", "network claim root could not be read");
|
|
9801
9913
|
}
|
|
@@ -9830,14 +9942,14 @@ function claimError(code, message) {
|
|
|
9830
9942
|
|
|
9831
9943
|
// src/core/network-claim-lookup.ts
|
|
9832
9944
|
function networkOwnerKey(repoPath) {
|
|
9833
|
-
if (!
|
|
9945
|
+
if (!import_node_path29.default.isAbsolute(repoPath) || import_node_path29.default.resolve(repoPath) !== repoPath)
|
|
9834
9946
|
throw new Error("Invalid network owner path.");
|
|
9835
9947
|
return (0, import_node_crypto13.createHash)("sha256").update(repoPath).digest("hex");
|
|
9836
9948
|
}
|
|
9837
9949
|
function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
9838
9950
|
let names;
|
|
9839
9951
|
try {
|
|
9840
|
-
names =
|
|
9952
|
+
names = import_node_fs25.default.readdirSync(root);
|
|
9841
9953
|
} catch (error) {
|
|
9842
9954
|
if (error.code === "ENOENT") return void 0;
|
|
9843
9955
|
throw new Error("Network claims cannot be inspected; repair is required.");
|
|
@@ -9846,21 +9958,21 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
9846
9958
|
if (files.length > 128) throw new Error("Network daemon inventory exceeds its bound.");
|
|
9847
9959
|
let found;
|
|
9848
9960
|
for (const file of files) {
|
|
9849
|
-
const full =
|
|
9850
|
-
const stat =
|
|
9961
|
+
const full = import_node_path29.default.join(root, file);
|
|
9962
|
+
const stat = import_node_fs25.default.lstatSync(full);
|
|
9851
9963
|
if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES)
|
|
9852
9964
|
throw new Error("Invalid retained network claim file.");
|
|
9853
9965
|
let value;
|
|
9854
|
-
const descriptor =
|
|
9966
|
+
const descriptor = import_node_fs25.default.openSync(
|
|
9855
9967
|
full,
|
|
9856
|
-
|
|
9968
|
+
import_node_fs25.default.constants.O_RDONLY | import_node_fs25.default.constants.O_NONBLOCK | import_node_fs25.default.constants.O_NOFOLLOW
|
|
9857
9969
|
);
|
|
9858
9970
|
try {
|
|
9859
|
-
if (!
|
|
9971
|
+
if (!import_node_fs25.default.fstatSync(descriptor).isFile()) throw new Error();
|
|
9860
9972
|
const bytes2 = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
|
|
9861
9973
|
let size = 0;
|
|
9862
9974
|
while (size < bytes2.length) {
|
|
9863
|
-
const count =
|
|
9975
|
+
const count = import_node_fs25.default.readSync(descriptor, bytes2, size, bytes2.length - size, null);
|
|
9864
9976
|
if (!count) break;
|
|
9865
9977
|
size += count;
|
|
9866
9978
|
}
|
|
@@ -9870,7 +9982,7 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
9870
9982
|
} catch {
|
|
9871
9983
|
throw new Error("Retained network claim could not be read safely.");
|
|
9872
9984
|
} finally {
|
|
9873
|
-
|
|
9985
|
+
import_node_fs25.default.closeSync(descriptor);
|
|
9874
9986
|
}
|
|
9875
9987
|
if (typeof value.daemonId !== "string" || `${(0, import_node_crypto13.createHash)("sha256").update(value.daemonId).digest("hex")}.json` !== file)
|
|
9876
9988
|
throw new Error("Retained network daemon identity is invalid.");
|
|
@@ -9888,7 +10000,7 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
9888
10000
|
}
|
|
9889
10001
|
|
|
9890
10002
|
// src/core/network-provider-inspect.ts
|
|
9891
|
-
var
|
|
10003
|
+
var import_node_child_process17 = require("child_process");
|
|
9892
10004
|
var import_node_crypto14 = require("crypto");
|
|
9893
10005
|
function object2(value) {
|
|
9894
10006
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -9912,7 +10024,7 @@ function qualifyNetworkProviderDefinition(value, provider) {
|
|
|
9912
10024
|
return (0, import_node_crypto14.createHash)("sha256").update(JSON.stringify({ name: definition.name, agent, options, exec })).digest("hex");
|
|
9913
10025
|
}
|
|
9914
10026
|
function read(provider, args) {
|
|
9915
|
-
const result = (0,
|
|
10027
|
+
const result = (0, import_node_child_process17.spawnSync)(provider, args, {
|
|
9916
10028
|
encoding: "utf8",
|
|
9917
10029
|
timeout: 5e3,
|
|
9918
10030
|
maxBuffer: 2 * 1024 * 1024
|
|
@@ -10026,7 +10138,7 @@ function networkProviderEffectOptions(provider, providerId, repoPath) {
|
|
|
10026
10138
|
}
|
|
10027
10139
|
|
|
10028
10140
|
// src/core/devsy-mutation.ts
|
|
10029
|
-
var DEVSY_MUTATION_LOCK_FILE =
|
|
10141
|
+
var DEVSY_MUTATION_LOCK_FILE = import_node_path30.default.join(DEVROUTER_HOME, "devsy-mutation.lock");
|
|
10030
10142
|
var DEVSY_MUTATION_WAIT_MS = 18e5;
|
|
10031
10143
|
var DevsyStartPostconditionError = class extends Error {
|
|
10032
10144
|
};
|
|
@@ -10041,7 +10153,7 @@ function failedStartMayHaveAttached(devsyId, repoPath) {
|
|
|
10041
10153
|
}
|
|
10042
10154
|
}
|
|
10043
10155
|
function withMutationLock(activity, target, operation) {
|
|
10044
|
-
|
|
10156
|
+
import_node_fs26.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
10045
10157
|
return withFileLockSync(
|
|
10046
10158
|
DEVSY_MUTATION_LOCK_FILE,
|
|
10047
10159
|
{
|
|
@@ -10055,7 +10167,7 @@ function withMutationLock(activity, target, operation) {
|
|
|
10055
10167
|
);
|
|
10056
10168
|
}
|
|
10057
10169
|
function withMutationLockAsync(activity, target, operation) {
|
|
10058
|
-
|
|
10170
|
+
import_node_fs26.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
10059
10171
|
return withFileLock(
|
|
10060
10172
|
DEVSY_MUTATION_LOCK_FILE,
|
|
10061
10173
|
{
|
|
@@ -10076,7 +10188,7 @@ var DEVSY_STDERR_TAIL_BYTES = 8192;
|
|
|
10076
10188
|
function runDevsyUp(args, env, quiet) {
|
|
10077
10189
|
return new Promise((resolve) => {
|
|
10078
10190
|
claimLifecycleEffect();
|
|
10079
|
-
const child = (0,
|
|
10191
|
+
const child = (0, import_node_child_process18.spawn)("devsy", args, {
|
|
10080
10192
|
stdio: ["inherit", quiet ? 2 : "inherit", "pipe"],
|
|
10081
10193
|
env
|
|
10082
10194
|
});
|
|
@@ -10110,7 +10222,7 @@ function runDevsyAction(action, devsyId, force = false, repoPath) {
|
|
|
10110
10222
|
const args = action === "delete" ? ["delete", devsyId, ...force ? ["--force"] : [], "--ignore-not-found"] : ["stop", devsyId];
|
|
10111
10223
|
claimLifecycleEffect();
|
|
10112
10224
|
const binding = repoPath ? networkProviderEffectOptions("devsy", devsyId, repoPath) : { args: [] };
|
|
10113
|
-
const result = (0,
|
|
10225
|
+
const result = (0, import_node_child_process18.spawnSync)("devsy", ["workspace", ...args, ...binding.args], {
|
|
10114
10226
|
encoding: "utf-8",
|
|
10115
10227
|
...binding.env ? { env: binding.env } : {}
|
|
10116
10228
|
});
|
|
@@ -10127,6 +10239,7 @@ function inspectExactOwnership(devsyId, worktreePath) {
|
|
|
10127
10239
|
}
|
|
10128
10240
|
function mutateOwnedDevsyWorkspace(action, devsyId, worktreePath) {
|
|
10129
10241
|
return withMutationLock(`Devsy ${action}`, worktreePath, () => {
|
|
10242
|
+
restoreRecordedManagedDevcontainerConfig(worktreePath);
|
|
10130
10243
|
if (action === "stop") {
|
|
10131
10244
|
const stopped = stopRetainedManagedDevsyWorkspace({
|
|
10132
10245
|
repoPath: worktreePath,
|
|
@@ -10293,21 +10406,21 @@ var ExecutionOutcomeError = class extends Error {
|
|
|
10293
10406
|
|
|
10294
10407
|
// src/core/reliability-lifecycle.ts
|
|
10295
10408
|
var import_node_crypto22 = require("crypto");
|
|
10296
|
-
var
|
|
10409
|
+
var import_node_path41 = __toESM(require("path"));
|
|
10297
10410
|
var import_node_util7 = require("util");
|
|
10298
10411
|
|
|
10299
10412
|
// src/core/capacity-policy.ts
|
|
10300
|
-
var
|
|
10301
|
-
var
|
|
10413
|
+
var import_node_fs27 = __toESM(require("fs"));
|
|
10414
|
+
var import_node_path31 = __toESM(require("path"));
|
|
10302
10415
|
|
|
10303
10416
|
// src/core/controller-client.ts
|
|
10304
10417
|
var import_node_crypto15 = require("crypto");
|
|
10305
|
-
var
|
|
10418
|
+
var import_node_fs28 = __toESM(require("fs"));
|
|
10306
10419
|
var import_node_net2 = __toESM(require("net"));
|
|
10307
|
-
var
|
|
10420
|
+
var import_node_path33 = __toESM(require("path"));
|
|
10308
10421
|
|
|
10309
10422
|
// src/core/controller-protocol.ts
|
|
10310
|
-
var
|
|
10423
|
+
var import_node_path32 = __toESM(require("path"));
|
|
10311
10424
|
var CONTROLLER_FRAME_BYTES = 64 * 1024;
|
|
10312
10425
|
var CONTROLLER_MAX_OPERATION_REQUEST_BYTES = 32 * 1024;
|
|
10313
10426
|
var CONTROLLER_PROFILE_TOKEN_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -10647,7 +10760,7 @@ function handleOperationRequest(state, event) {
|
|
|
10647
10760
|
if (state.phase === "stopping" || state.desired === "parked-for-capacity")
|
|
10648
10761
|
return blocked(
|
|
10649
10762
|
state,
|
|
10650
|
-
`phase is '${state.phase}' with desired '${state.desired}'; wait for the running stop to finish.`
|
|
10763
|
+
`phase is '${state.phase}' with desired '${state.desired}'; wait for the running stop to finish, or when no stop worker is running rerun 'devrouter stop <path> --delete', removing the exact provider registration first when the stop cannot prove the recorded population.`
|
|
10651
10764
|
);
|
|
10652
10765
|
if (event.kind === "exec" && (!event.runtimeRunning || state.desired !== "running" && state.intentRevision !== 0))
|
|
10653
10766
|
return blocked(
|
|
@@ -11104,10 +11217,10 @@ function stepReliability(state, event, nowMs) {
|
|
|
11104
11217
|
}
|
|
11105
11218
|
|
|
11106
11219
|
// src/core/reliability-worker.ts
|
|
11107
|
-
var
|
|
11220
|
+
var import_node_child_process19 = require("child_process");
|
|
11108
11221
|
var import_node_crypto17 = require("crypto");
|
|
11109
|
-
var
|
|
11110
|
-
var
|
|
11222
|
+
var import_node_fs29 = __toESM(require("fs"));
|
|
11223
|
+
var import_node_path34 = __toESM(require("path"));
|
|
11111
11224
|
|
|
11112
11225
|
// src/core/lifecycle-progress.ts
|
|
11113
11226
|
var ROLES = {
|
|
@@ -11174,20 +11287,20 @@ function workerGroupAbsent(pid) {
|
|
|
11174
11287
|
}
|
|
11175
11288
|
|
|
11176
11289
|
// src/core/workspace-ensure.ts
|
|
11177
|
-
var
|
|
11178
|
-
var
|
|
11179
|
-
var
|
|
11290
|
+
var import_node_child_process30 = require("child_process");
|
|
11291
|
+
var import_node_fs35 = __toESM(require("fs"));
|
|
11292
|
+
var import_node_path40 = __toESM(require("path"));
|
|
11180
11293
|
|
|
11181
11294
|
// src/core/devpod-mutation.ts
|
|
11182
|
-
var
|
|
11183
|
-
var
|
|
11184
|
-
var
|
|
11185
|
-
var DEVPOD_MUTATION_LOCK_FILE =
|
|
11295
|
+
var import_node_child_process20 = require("child_process");
|
|
11296
|
+
var import_node_fs30 = __toESM(require("fs"));
|
|
11297
|
+
var import_node_path35 = __toESM(require("path"));
|
|
11298
|
+
var DEVPOD_MUTATION_LOCK_FILE = import_node_path35.default.join(DEVROUTER_HOME, "devpod-mutation.lock");
|
|
11186
11299
|
var DEVPOD_MUTATION_WAIT_MS = 18e5;
|
|
11187
11300
|
var DevpodStartPostconditionError = class extends Error {
|
|
11188
11301
|
};
|
|
11189
11302
|
function withMutationLock2(activity, target, operation) {
|
|
11190
|
-
|
|
11303
|
+
import_node_fs30.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
11191
11304
|
return withFileLockSync(
|
|
11192
11305
|
DEVPOD_MUTATION_LOCK_FILE,
|
|
11193
11306
|
{
|
|
@@ -11207,7 +11320,7 @@ function runDevpodAction(action, devpodId, force = false, repoPath) {
|
|
|
11207
11320
|
const args = action === "delete" ? [action, devpodId, ...force ? ["--force"] : [], "--ignore-not-found"] : [action, devpodId];
|
|
11208
11321
|
claimLifecycleEffect();
|
|
11209
11322
|
const binding = repoPath ? networkProviderEffectOptions("devpod", devpodId, repoPath) : { args: [] };
|
|
11210
|
-
const result = (0,
|
|
11323
|
+
const result = (0, import_node_child_process20.spawnSync)("devpod", [...args, ...binding.args], {
|
|
11211
11324
|
encoding: "utf-8",
|
|
11212
11325
|
...binding.env ? { env: binding.env } : {}
|
|
11213
11326
|
});
|
|
@@ -11349,7 +11462,7 @@ async function startDevpodWorkspace(options) {
|
|
|
11349
11462
|
}
|
|
11350
11463
|
claimLifecycleEffect();
|
|
11351
11464
|
network?.beforeDispatch?.();
|
|
11352
|
-
const result = (0,
|
|
11465
|
+
const result = (0, import_node_child_process20.spawnSync)("devpod", args, {
|
|
11353
11466
|
stdio: options.quiet ? ["inherit", 2, "inherit"] : "inherit",
|
|
11354
11467
|
env
|
|
11355
11468
|
});
|
|
@@ -11389,8 +11502,8 @@ async function startDevpodWorkspace(options) {
|
|
|
11389
11502
|
}
|
|
11390
11503
|
|
|
11391
11504
|
// src/core/host-port-claims.ts
|
|
11392
|
-
var
|
|
11393
|
-
var
|
|
11505
|
+
var import_node_child_process21 = require("child_process");
|
|
11506
|
+
var import_node_path36 = __toESM(require("path"));
|
|
11394
11507
|
var COMPOSE_RENDER_TIMEOUT_MS = 3e4;
|
|
11395
11508
|
var COMPOSE_RENDER_MAX_BUFFER = 8 * 1024 * 1024;
|
|
11396
11509
|
var DOCKER_TIMEOUT_MS = 1e4;
|
|
@@ -11493,7 +11606,7 @@ function hostPortBindingsConflict(desired, holder) {
|
|
|
11493
11606
|
}
|
|
11494
11607
|
var HOST_PORT_INSPECT_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"labels":{"com.docker.compose.project":{{json (index .Config.Labels "com.docker.compose.project")}},"com.docker.compose.project.working_dir":{{json (index .Config.Labels "com.docker.compose.project.working_dir")}}},"ports":{{json .NetworkSettings.Ports}}}';
|
|
11495
11608
|
function runDocker(args) {
|
|
11496
|
-
const result = (0,
|
|
11609
|
+
const result = (0, import_node_child_process21.spawnSync)("docker", args, {
|
|
11497
11610
|
...networkDockerOptions(),
|
|
11498
11611
|
encoding: "utf-8",
|
|
11499
11612
|
timeout: DOCKER_TIMEOUT_MS,
|
|
@@ -11563,7 +11676,7 @@ function listHolderSnapshots() {
|
|
|
11563
11676
|
}
|
|
11564
11677
|
function renderManagedComposeModel(plan, workspace) {
|
|
11565
11678
|
const fileArgs = plan.composeFiles.flatMap((file) => ["-f", file]);
|
|
11566
|
-
const result = (0,
|
|
11679
|
+
const result = (0, import_node_child_process21.spawnSync)(
|
|
11567
11680
|
"docker",
|
|
11568
11681
|
[
|
|
11569
11682
|
"compose",
|
|
@@ -11598,8 +11711,8 @@ function renderManagedComposeModel(plan, workspace) {
|
|
|
11598
11711
|
}
|
|
11599
11712
|
}
|
|
11600
11713
|
function attributeHolderWorktree(workingDir) {
|
|
11601
|
-
if (
|
|
11602
|
-
return comparableWorkspacePath(
|
|
11714
|
+
if (import_node_path36.default.basename(workingDir) !== ".devcontainer") return void 0;
|
|
11715
|
+
return comparableWorkspacePath(import_node_path36.default.dirname(workingDir));
|
|
11603
11716
|
}
|
|
11604
11717
|
function holderAttribution(holder, repoPath) {
|
|
11605
11718
|
const attribution = {};
|
|
@@ -11634,7 +11747,7 @@ function detectHostPortClaimConflicts(options) {
|
|
|
11634
11747
|
);
|
|
11635
11748
|
if (desired.length === 0) return [];
|
|
11636
11749
|
const holders = listHolderSnapshots();
|
|
11637
|
-
const devcontainerDir =
|
|
11750
|
+
const devcontainerDir = import_node_path36.default.join(options.repoPath, ".devcontainer");
|
|
11638
11751
|
const conflicts = [];
|
|
11639
11752
|
for (const binding of desired) {
|
|
11640
11753
|
for (const holder of holders) {
|
|
@@ -11658,7 +11771,7 @@ function detectHostPortClaimConflicts(options) {
|
|
|
11658
11771
|
}
|
|
11659
11772
|
|
|
11660
11773
|
// src/core/http-route-probe.ts
|
|
11661
|
-
var
|
|
11774
|
+
var import_node_child_process22 = require("child_process");
|
|
11662
11775
|
var DEFAULT_MAX_TIME_SECONDS = 5;
|
|
11663
11776
|
var PARENT_TIMEOUT_PADDING_MS = 250;
|
|
11664
11777
|
var CONTRACT_MAX_BUFFER = 1024;
|
|
@@ -11691,7 +11804,7 @@ function probeHttpRoute(host, options = {}) {
|
|
|
11691
11804
|
args.push("--cacert", CERT_FILE);
|
|
11692
11805
|
}
|
|
11693
11806
|
args.push(url);
|
|
11694
|
-
const result = (0,
|
|
11807
|
+
const result = (0, import_node_child_process22.spawnSync)("curl", args, { encoding: "utf-8" });
|
|
11695
11808
|
const stdout = result.stdout?.trim() ?? "";
|
|
11696
11809
|
const status = /^\d{3}$/.test(stdout) ? Number(stdout) : void 0;
|
|
11697
11810
|
const ok = result.status === 0 && status !== void 0 && status >= 100 && status < 500;
|
|
@@ -11710,7 +11823,7 @@ function probeHttpReadinessSync(host, readiness, maxTimeSeconds, repoPath) {
|
|
|
11710
11823
|
}
|
|
11711
11824
|
let result;
|
|
11712
11825
|
try {
|
|
11713
|
-
result = (0,
|
|
11826
|
+
result = (0, import_node_child_process22.spawnSync)("curl", args, {
|
|
11714
11827
|
encoding: "utf-8",
|
|
11715
11828
|
maxBuffer: CONTRACT_MAX_BUFFER,
|
|
11716
11829
|
timeout: Math.ceil(parentTimeoutMs)
|
|
@@ -11794,8 +11907,8 @@ function transportFailure() {
|
|
|
11794
11907
|
}
|
|
11795
11908
|
|
|
11796
11909
|
// src/core/managed-host-preparation.ts
|
|
11797
|
-
var
|
|
11798
|
-
var
|
|
11910
|
+
var import_node_child_process23 = require("child_process");
|
|
11911
|
+
var import_node_fs31 = __toESM(require("fs"));
|
|
11799
11912
|
var PREPARATION_TIMEOUT_MS = 6e4;
|
|
11800
11913
|
var CONFIG_READ_ERROR = "Managed host preparation could not read .devrouter.yml.";
|
|
11801
11914
|
var CONFIG_CHANGED_ERROR = "Managed host preparation changed .devrouter.yml; refusing to continue.";
|
|
@@ -11806,7 +11919,7 @@ function runPreparationCommand(argv, cwd) {
|
|
|
11806
11919
|
return new Promise((resolve) => {
|
|
11807
11920
|
let child;
|
|
11808
11921
|
try {
|
|
11809
|
-
child = (0,
|
|
11922
|
+
child = (0, import_node_child_process23.spawn)(argv[0], argv.slice(1), {
|
|
11810
11923
|
cwd,
|
|
11811
11924
|
detached: true,
|
|
11812
11925
|
shell: false,
|
|
@@ -11838,7 +11951,7 @@ function runPreparationCommand(argv, cwd) {
|
|
|
11838
11951
|
}
|
|
11839
11952
|
function readConfigBytes(configPath) {
|
|
11840
11953
|
try {
|
|
11841
|
-
return
|
|
11954
|
+
return import_node_fs31.default.readFileSync(configPath);
|
|
11842
11955
|
} catch {
|
|
11843
11956
|
return void 0;
|
|
11844
11957
|
}
|
|
@@ -11872,10 +11985,10 @@ async function runManagedHostPreparation(repoPath, config) {
|
|
|
11872
11985
|
}
|
|
11873
11986
|
|
|
11874
11987
|
// src/core/managed-post-start.ts
|
|
11875
|
-
var
|
|
11988
|
+
var import_node_child_process24 = require("child_process");
|
|
11876
11989
|
var import_node_crypto18 = require("crypto");
|
|
11877
|
-
var
|
|
11878
|
-
var
|
|
11990
|
+
var import_node_fs32 = __toESM(require("fs"));
|
|
11991
|
+
var import_node_path37 = __toESM(require("path"));
|
|
11879
11992
|
var MANAGED_MARKER = "devrouter:managed devcontainer";
|
|
11880
11993
|
var MANAGED_ADAPTER_PATH = ".devcontainer/post-start.sh";
|
|
11881
11994
|
var RUNTIME_HELPER_PATH = "/tmp/devrouter/bin/devrouter-process";
|
|
@@ -11888,7 +12001,7 @@ function renderDeliveryScript(targetPath) {
|
|
|
11888
12001
|
if (!/^\/tmp\/devrouter\/bin\/[a-zA-Z0-9._-]+$/.test(targetPath)) {
|
|
11889
12002
|
throw new Error(`Unsafe managed runtime delivery path: ${targetPath}`);
|
|
11890
12003
|
}
|
|
11891
|
-
const temporaryPrefix =
|
|
12004
|
+
const temporaryPrefix = import_node_path37.default.posix.basename(targetPath);
|
|
11892
12005
|
return `set -eu
|
|
11893
12006
|
umask 077
|
|
11894
12007
|
runtime_root=/tmp/devrouter
|
|
@@ -11912,7 +12025,7 @@ function commandFailure3(result) {
|
|
|
11912
12025
|
return [result.error?.message, result.stderr, result.stdout].filter(Boolean).map((value) => String(value).trim()).filter(Boolean).join("\n");
|
|
11913
12026
|
}
|
|
11914
12027
|
function deliverRuntimeFile(containerId, targetPath, contents, label) {
|
|
11915
|
-
const result = (0,
|
|
12028
|
+
const result = (0, import_node_child_process24.spawnSync)(
|
|
11916
12029
|
"docker",
|
|
11917
12030
|
["exec", "-i", containerId, "sh", "-c", renderDeliveryScript(targetPath)],
|
|
11918
12031
|
{ input: contents, encoding: "utf-8", ...networkDockerOptions() }
|
|
@@ -11923,8 +12036,8 @@ function deliverRuntimeFile(containerId, targetPath, contents, label) {
|
|
|
11923
12036
|
}
|
|
11924
12037
|
}
|
|
11925
12038
|
function readRegularFileBytes(filePath) {
|
|
11926
|
-
if (!
|
|
11927
|
-
return
|
|
12039
|
+
if (!import_node_fs32.default.existsSync(filePath) || !import_node_fs32.default.lstatSync(filePath).isFile()) return void 0;
|
|
12040
|
+
return import_node_fs32.default.readFileSync(filePath);
|
|
11928
12041
|
}
|
|
11929
12042
|
function readRegularFile(filePath) {
|
|
11930
12043
|
return readRegularFileBytes(filePath)?.toString("utf-8");
|
|
@@ -11934,21 +12047,21 @@ function adapterFingerprint(adapter) {
|
|
|
11934
12047
|
}
|
|
11935
12048
|
function resolveProcessHelperPath() {
|
|
11936
12049
|
const candidates = [
|
|
11937
|
-
|
|
11938
|
-
|
|
12050
|
+
import_node_path37.default.resolve(__dirname, "..", "bin", "devrouter-process"),
|
|
12051
|
+
import_node_path37.default.resolve(__dirname, "..", "..", "bin", "devrouter-process")
|
|
11939
12052
|
];
|
|
11940
|
-
const helperPath = candidates.find((candidate) =>
|
|
12053
|
+
const helperPath = candidates.find((candidate) => import_node_fs32.default.existsSync(candidate));
|
|
11941
12054
|
if (!helperPath) {
|
|
11942
12055
|
throw new Error("Could not locate the packaged devrouter-process helper.");
|
|
11943
12056
|
}
|
|
11944
12057
|
return helperPath;
|
|
11945
12058
|
}
|
|
11946
12059
|
function resolveManagedPostStartPlan(repoPath) {
|
|
11947
|
-
const adapterPath =
|
|
12060
|
+
const adapterPath = import_node_path37.default.join(repoPath, MANAGED_ADAPTER_PATH);
|
|
11948
12061
|
const adapterBytes = readRegularFileBytes(adapterPath);
|
|
11949
12062
|
const adapterText = adapterBytes?.toString("utf-8");
|
|
11950
|
-
const dockerfilePath =
|
|
11951
|
-
const devcontainerPath =
|
|
12063
|
+
const dockerfilePath = import_node_path37.default.join(repoPath, ".devcontainer", "Dockerfile");
|
|
12064
|
+
const devcontainerPath = import_node_path37.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
11952
12065
|
const dockerfile = readRegularFile(dockerfilePath) ?? "";
|
|
11953
12066
|
const devcontainer = readRegularFile(devcontainerPath) ?? "";
|
|
11954
12067
|
const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
|
|
@@ -11989,7 +12102,7 @@ function resolveManagedPostStartPlan(repoPath) {
|
|
|
11989
12102
|
}
|
|
11990
12103
|
function runManagedPostStart(options) {
|
|
11991
12104
|
if (options.plan.kind !== "runtime") return;
|
|
11992
|
-
const helper =
|
|
12105
|
+
const helper = import_node_fs32.default.readFileSync(resolveProcessHelperPath());
|
|
11993
12106
|
deliverRuntimeFile(
|
|
11994
12107
|
options.container.id,
|
|
11995
12108
|
RUNTIME_HELPER_PATH,
|
|
@@ -12003,7 +12116,7 @@ function runManagedPostStart(options) {
|
|
|
12003
12116
|
options.plan.adapterContents,
|
|
12004
12117
|
"the managed post-start adapter"
|
|
12005
12118
|
);
|
|
12006
|
-
const started2 = (0,
|
|
12119
|
+
const started2 = (0, import_node_child_process24.spawnSync)(
|
|
12007
12120
|
"docker",
|
|
12008
12121
|
[
|
|
12009
12122
|
"exec",
|
|
@@ -12044,7 +12157,7 @@ function assertSafeProcessName(name) {
|
|
|
12044
12157
|
}
|
|
12045
12158
|
function runManagedProcessAction(options) {
|
|
12046
12159
|
assertSafeProcessName(options.name);
|
|
12047
|
-
const result = (0,
|
|
12160
|
+
const result = (0, import_node_child_process24.spawnSync)(
|
|
12048
12161
|
"docker",
|
|
12049
12162
|
[
|
|
12050
12163
|
"exec",
|
|
@@ -12082,6 +12195,96 @@ function runManagedProcessAction(options) {
|
|
|
12082
12195
|
return "drifted";
|
|
12083
12196
|
}
|
|
12084
12197
|
|
|
12198
|
+
// src/core/managed-mount-nesting.ts
|
|
12199
|
+
var import_node_child_process25 = require("child_process");
|
|
12200
|
+
var NESTED_MOUNT_REASON = "no configured mount is nested inside another configured mount";
|
|
12201
|
+
var UNREADABLE_MOUNT_REASON = "the container's own mount table could not be read";
|
|
12202
|
+
function normalizeDestination(value) {
|
|
12203
|
+
const trimmed = value.trim().replace(/\/+$/, "");
|
|
12204
|
+
return trimmed === "" ? "/" : trimmed;
|
|
12205
|
+
}
|
|
12206
|
+
function isNestedWithin(destination, candidate) {
|
|
12207
|
+
return candidate === "/" ? destination !== "/" : destination.startsWith(`${candidate}/`);
|
|
12208
|
+
}
|
|
12209
|
+
function unescapeMountField(value) {
|
|
12210
|
+
return value.replace(
|
|
12211
|
+
/\\([0-7]{3})/g,
|
|
12212
|
+
(_match, octal) => String.fromCharCode(Number.parseInt(octal, 8))
|
|
12213
|
+
);
|
|
12214
|
+
}
|
|
12215
|
+
function parseMountinfoDestinations(text4) {
|
|
12216
|
+
const destinations = /* @__PURE__ */ new Set();
|
|
12217
|
+
for (const line of text4.split("\n")) {
|
|
12218
|
+
const fields2 = line.split(" ");
|
|
12219
|
+
if (fields2.length < 5 || !/^\d+$/.test(fields2[0] ?? "")) continue;
|
|
12220
|
+
destinations.add(normalizeDestination(unescapeMountField(fields2[4] ?? "")));
|
|
12221
|
+
}
|
|
12222
|
+
return [...destinations];
|
|
12223
|
+
}
|
|
12224
|
+
function isNestedIndex(destinations, index) {
|
|
12225
|
+
return destinations.some(
|
|
12226
|
+
(candidate, other) => other !== index && isNestedWithin(destinations[index] ?? "", candidate)
|
|
12227
|
+
);
|
|
12228
|
+
}
|
|
12229
|
+
function configuredDestinations(configured) {
|
|
12230
|
+
return configured.map((mount) => normalizeDestination(mount.Destination));
|
|
12231
|
+
}
|
|
12232
|
+
function classifyManagedMountNesting(configured, effectiveDestinations, containerId) {
|
|
12233
|
+
const destinations = configuredDestinations(configured);
|
|
12234
|
+
const nested = configured.filter((_mount, index) => isNestedIndex(destinations, index));
|
|
12235
|
+
const identity2 = containerId ? { container: containerId } : {};
|
|
12236
|
+
if (nested.length === 0) {
|
|
12237
|
+
return { status: "not-applicable", checked: [], unwound: [], reason: NESTED_MOUNT_REASON };
|
|
12238
|
+
}
|
|
12239
|
+
if (effectiveDestinations === null) {
|
|
12240
|
+
return {
|
|
12241
|
+
status: "unverified",
|
|
12242
|
+
checked: [],
|
|
12243
|
+
unwound: [],
|
|
12244
|
+
...identity2,
|
|
12245
|
+
reason: UNREADABLE_MOUNT_REASON
|
|
12246
|
+
};
|
|
12247
|
+
}
|
|
12248
|
+
const effective = new Set(effectiveDestinations.map(normalizeDestination));
|
|
12249
|
+
const checked = [];
|
|
12250
|
+
const unwound = [];
|
|
12251
|
+
for (const mount of nested) {
|
|
12252
|
+
const destination = normalizeDestination(mount.Destination);
|
|
12253
|
+
if (effective.has(destination)) {
|
|
12254
|
+
checked.push(mount.Destination);
|
|
12255
|
+
continue;
|
|
12256
|
+
}
|
|
12257
|
+
const nestedWithin = destinations.filter((candidate) => isNestedWithin(destination, candidate)).sort((a, b) => a.length - b.length)[0];
|
|
12258
|
+
unwound.push({
|
|
12259
|
+
destination: mount.Destination,
|
|
12260
|
+
source: mount.Source,
|
|
12261
|
+
nestedWithin: nestedWithin ?? "/"
|
|
12262
|
+
});
|
|
12263
|
+
}
|
|
12264
|
+
return {
|
|
12265
|
+
status: unwound.length > 0 ? "unwound" : "effective",
|
|
12266
|
+
checked,
|
|
12267
|
+
unwound,
|
|
12268
|
+
...identity2
|
|
12269
|
+
};
|
|
12270
|
+
}
|
|
12271
|
+
function readContainerMountinfo(containerId, options) {
|
|
12272
|
+
const result = (0, import_node_child_process25.spawnSync)("docker", ["exec", containerId, "cat", "/proc/self/mountinfo"], {
|
|
12273
|
+
encoding: "utf-8",
|
|
12274
|
+
timeout: options.timeoutMs ?? 5e3,
|
|
12275
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12276
|
+
...networkDockerOptions()
|
|
12277
|
+
});
|
|
12278
|
+
if (result.status !== 0 || typeof result.stdout !== "string") return null;
|
|
12279
|
+
const destinations = parseMountinfoDestinations(result.stdout);
|
|
12280
|
+
return destinations.length > 0 ? destinations : null;
|
|
12281
|
+
}
|
|
12282
|
+
function observeManagedMountNesting(container, options = {}) {
|
|
12283
|
+
const destinations = configuredDestinations(container.mounts);
|
|
12284
|
+
const effective = destinations.some((_destination, index) => isNestedIndex(destinations, index)) ? readContainerMountinfo(container.id, options) : null;
|
|
12285
|
+
return classifyManagedMountNesting(container.mounts, effective, container.id);
|
|
12286
|
+
}
|
|
12287
|
+
|
|
12085
12288
|
// src/core/managed-runtime-status.ts
|
|
12086
12289
|
function recoveryPath(repoPath) {
|
|
12087
12290
|
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(repoPath)) return repoPath;
|
|
@@ -12362,6 +12565,7 @@ function inspectManagedRuntime(options) {
|
|
|
12362
12565
|
(mount) => mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath)
|
|
12363
12566
|
)?.Destination;
|
|
12364
12567
|
const primaryActive = Boolean(primary?.state.Running);
|
|
12568
|
+
const mountNesting = primary && primaryActive ? observeManagedMountNesting(primary) : void 0;
|
|
12365
12569
|
if (generatedConfigMissing && (state || primaryActive || managedServicesActive)) {
|
|
12366
12570
|
drift.push("managed generated Dev Container configuration is missing");
|
|
12367
12571
|
}
|
|
@@ -12428,7 +12632,8 @@ function inspectManagedRuntime(options) {
|
|
|
12428
12632
|
processStatuses,
|
|
12429
12633
|
activeServices: sortedUnique(activeServices),
|
|
12430
12634
|
activeProcesses: sortedUnique(activeProcesses),
|
|
12431
|
-
drift
|
|
12635
|
+
drift,
|
|
12636
|
+
...mountNesting ? { mountNesting } : {}
|
|
12432
12637
|
};
|
|
12433
12638
|
}
|
|
12434
12639
|
function collectManagedRuntimeStatus(options) {
|
|
@@ -12576,6 +12781,7 @@ function collectManagedRuntimeStatus(options) {
|
|
|
12576
12781
|
baseServiceStatuses: inspection.baseStatuses,
|
|
12577
12782
|
processStatuses: inspection.processStatuses,
|
|
12578
12783
|
drift: sortedUnique(drift),
|
|
12784
|
+
...inspection.mountNesting ? { mountNesting: inspection.mountNesting } : {},
|
|
12579
12785
|
...inspection.plan?.sourceConfigSha256 || state?.sourceConfigSha256 ? { sourceConfigSha256: inspection.plan?.sourceConfigSha256 ?? state?.sourceConfigSha256 } : {},
|
|
12580
12786
|
...inspection.plan?.effectiveConfigSha256 || state?.effectiveConfigSha256 ? {
|
|
12581
12787
|
effectiveConfigSha256: inspection.plan?.effectiveConfigSha256 ?? state?.effectiveConfigSha256
|
|
@@ -12585,19 +12791,19 @@ function collectManagedRuntimeStatus(options) {
|
|
|
12585
12791
|
}
|
|
12586
12792
|
|
|
12587
12793
|
// src/core/network-inventory.ts
|
|
12588
|
-
var
|
|
12794
|
+
var import_node_child_process26 = require("child_process");
|
|
12589
12795
|
var NetworkInventoryError = class extends Error {
|
|
12590
12796
|
};
|
|
12591
12797
|
var MAX_IDS = 4096;
|
|
12592
12798
|
var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
12593
12799
|
var FULL_ID = /^[a-f0-9]{64}$/;
|
|
12594
|
-
var NETWORK_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"driver":{{json .Driver}},"ipam":{{json .IPAM.Config}},"activeEndpoints":{{len .Containers}},"project":{{json (index .Labels "com.docker.compose.project")}},"network":{{json (index .Labels "com.docker.compose.network")}}';
|
|
12800
|
+
var NETWORK_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"driver":{{json .Driver}},"ipam":{{json .IPAM.Config}},"activeEndpoints":{{len .Containers}},"project":{{json (index .Labels "com.docker.compose.project")}},"network":{{json (index .Labels "com.docker.compose.network")}}}';
|
|
12595
12801
|
var CONTAINER_TEMPLATE = '{"id":{{json .Id}},"networks":{{json .NetworkSettings.Networks}}}';
|
|
12596
12802
|
function readDocker(endpoint, args, timeoutMs) {
|
|
12597
12803
|
const env = { ...process.env };
|
|
12598
12804
|
delete env.DOCKER_CONTEXT;
|
|
12599
12805
|
delete env.DOCKER_HOST;
|
|
12600
|
-
const result = (0,
|
|
12806
|
+
const result = (0, import_node_child_process26.spawnSync)("docker", ["--host", endpoint, ...args], {
|
|
12601
12807
|
env,
|
|
12602
12808
|
encoding: "utf-8",
|
|
12603
12809
|
timeout: timeoutMs,
|
|
@@ -12628,7 +12834,14 @@ function id4(value) {
|
|
|
12628
12834
|
}
|
|
12629
12835
|
function rows(output) {
|
|
12630
12836
|
const trimmed = output.trim();
|
|
12631
|
-
return trimmed ? trimmed.split(/\r?\n/).map((line) =>
|
|
12837
|
+
return trimmed ? trimmed.split(/\r?\n/).map((line) => parseRecord(line)) : [];
|
|
12838
|
+
}
|
|
12839
|
+
function parseRecord(line) {
|
|
12840
|
+
try {
|
|
12841
|
+
return JSON.parse(line);
|
|
12842
|
+
} catch {
|
|
12843
|
+
throw new NetworkInventoryError("Docker network inventory contains a malformed record.");
|
|
12844
|
+
}
|
|
12632
12845
|
}
|
|
12633
12846
|
function ids(output) {
|
|
12634
12847
|
const values = output.trim() ? output.trim().split(/\r?\n/).map(id4) : [];
|
|
@@ -12673,7 +12886,7 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
|
|
|
12673
12886
|
return output;
|
|
12674
12887
|
};
|
|
12675
12888
|
const info = record4(
|
|
12676
|
-
|
|
12889
|
+
parseRecord(
|
|
12677
12890
|
read2(["info", "--format", '{"id":{{json .ID}},"pools":{{json .DefaultAddressPools}}}'])
|
|
12678
12891
|
)
|
|
12679
12892
|
);
|
|
@@ -12754,37 +12967,37 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
|
|
|
12754
12967
|
}
|
|
12755
12968
|
|
|
12756
12969
|
// src/core/network-routes.ts
|
|
12757
|
-
var
|
|
12970
|
+
var import_node_child_process27 = require("child_process");
|
|
12758
12971
|
var import_node_net3 = require("net");
|
|
12759
12972
|
|
|
12760
12973
|
// src/core/network-local-daemon.ts
|
|
12761
|
-
var
|
|
12762
|
-
var
|
|
12974
|
+
var import_node_fs33 = __toESM(require("fs"));
|
|
12975
|
+
var import_node_path38 = __toESM(require("path"));
|
|
12763
12976
|
function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
12764
12977
|
const deadline = Date.now() + 2e3;
|
|
12765
12978
|
try {
|
|
12766
12979
|
if (!endpoint.startsWith("unix:///")) return false;
|
|
12767
12980
|
const socket = endpoint.slice("unix://".length);
|
|
12768
|
-
const table =
|
|
12981
|
+
const table = import_node_fs33.default.readFileSync(import_node_path38.default.join(procRoot, "net/unix"), "utf8");
|
|
12769
12982
|
if (Buffer.byteLength(table) > 1024 * 1024) return false;
|
|
12770
12983
|
const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
|
|
12771
12984
|
if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
|
|
12772
12985
|
const inode = `socket:[${rows2[0][6]}]`;
|
|
12773
|
-
const ownNamespace =
|
|
12774
|
-
const pids =
|
|
12986
|
+
const ownNamespace = import_node_fs33.default.readlinkSync(import_node_path38.default.join(procRoot, "self/ns/net"));
|
|
12987
|
+
const pids = import_node_fs33.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
|
|
12775
12988
|
if (pids.length > 4096) return false;
|
|
12776
12989
|
for (const pid of pids) {
|
|
12777
12990
|
if (Date.now() > deadline) return false;
|
|
12778
12991
|
try {
|
|
12779
|
-
const root =
|
|
12780
|
-
if (
|
|
12781
|
-
if (
|
|
12782
|
-
const descriptors =
|
|
12992
|
+
const root = import_node_path38.default.join(procRoot, pid);
|
|
12993
|
+
if (import_node_path38.default.basename(import_node_fs33.default.readlinkSync(import_node_path38.default.join(root, "exe"))) !== "dockerd") continue;
|
|
12994
|
+
if (import_node_fs33.default.readlinkSync(import_node_path38.default.join(root, "ns/net")) !== ownNamespace) continue;
|
|
12995
|
+
const descriptors = import_node_fs33.default.readdirSync(import_node_path38.default.join(root, "fd"));
|
|
12783
12996
|
if (descriptors.length > 4096) continue;
|
|
12784
12997
|
if (descriptors.some((fd) => {
|
|
12785
12998
|
if (Date.now() > deadline) return false;
|
|
12786
12999
|
try {
|
|
12787
|
-
return
|
|
13000
|
+
return import_node_fs33.default.readlinkSync(import_node_path38.default.join(root, "fd", fd)) === inode;
|
|
12788
13001
|
} catch {
|
|
12789
13002
|
return false;
|
|
12790
13003
|
}
|
|
@@ -12823,7 +13036,7 @@ function parseLinuxNetworkRoutes(output) {
|
|
|
12823
13036
|
function collectNetworkRoutes(options) {
|
|
12824
13037
|
if ((options.platform ?? process.platform) !== "linux" || !hasLocalDockerNetworkNamespace(options.endpoint))
|
|
12825
13038
|
return { status: "unknown", routes: [] };
|
|
12826
|
-
const result = (0,
|
|
13039
|
+
const result = (0, import_node_child_process27.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
|
|
12827
13040
|
encoding: "utf8",
|
|
12828
13041
|
timeout: 5e3,
|
|
12829
13042
|
maxBuffer: 1024 * 1024
|
|
@@ -13672,10 +13885,10 @@ function fingerprintComposeNetworkModel(input) {
|
|
|
13672
13885
|
}
|
|
13673
13886
|
|
|
13674
13887
|
// src/core/network-compose-files.ts
|
|
13675
|
-
var
|
|
13888
|
+
var import_node_child_process28 = require("child_process");
|
|
13676
13889
|
var import_node_crypto20 = require("crypto");
|
|
13677
|
-
var
|
|
13678
|
-
var
|
|
13890
|
+
var import_node_fs34 = __toESM(require("fs"));
|
|
13891
|
+
var import_node_path39 = __toESM(require("path"));
|
|
13679
13892
|
var import_yaml4 = __toESM(require("yaml"));
|
|
13680
13893
|
var OVERLAY = "docker-compose.devrouter-network.yml";
|
|
13681
13894
|
var MARKER = "# devrouter:managed workspace network";
|
|
@@ -13688,12 +13901,12 @@ function object3(value) {
|
|
|
13688
13901
|
function inspectNetworkComposeFiles(input) {
|
|
13689
13902
|
const authoredNetworks = {};
|
|
13690
13903
|
for (const file of input.plan.composeFiles) {
|
|
13691
|
-
const stat =
|
|
13904
|
+
const stat = import_node_fs34.default.lstatSync(file);
|
|
13692
13905
|
if (!stat.isFile() || stat.size > MAX_BYTES2)
|
|
13693
13906
|
throw new Error("Network Compose source is unavailable or too large.");
|
|
13694
13907
|
let source;
|
|
13695
13908
|
try {
|
|
13696
|
-
source = object3(import_yaml4.default.parse(
|
|
13909
|
+
source = object3(import_yaml4.default.parse(import_node_fs34.default.readFileSync(file, "utf8")));
|
|
13697
13910
|
} catch {
|
|
13698
13911
|
throw new Error("Network Compose source could not be parsed safely.");
|
|
13699
13912
|
}
|
|
@@ -13709,7 +13922,7 @@ function inspectNetworkComposeFiles(input) {
|
|
|
13709
13922
|
const env = managedComposeEnvironment(input.workspace);
|
|
13710
13923
|
delete env.DOCKER_CONTEXT;
|
|
13711
13924
|
delete env.DOCKER_HOST;
|
|
13712
|
-
const result = (0,
|
|
13925
|
+
const result = (0, import_node_child_process28.spawnSync)(
|
|
13713
13926
|
"docker",
|
|
13714
13927
|
[
|
|
13715
13928
|
"--host",
|
|
@@ -13743,8 +13956,8 @@ function inspectNetworkComposeFiles(input) {
|
|
|
13743
13956
|
function prepareNetworkComposeFiles(plan, subnet) {
|
|
13744
13957
|
const overlay = deriveComposeNetworkOverlay(subnet);
|
|
13745
13958
|
if (overlay.status !== "ready" || !overlay.overlay) throw new Error("Network subnet is invalid.");
|
|
13746
|
-
const overlayPath =
|
|
13747
|
-
const ignored = (0,
|
|
13959
|
+
const overlayPath = import_node_path39.default.join(plan.composeDirectory, OVERLAY);
|
|
13960
|
+
const ignored = (0, import_node_child_process28.spawnSync)("git", [
|
|
13748
13961
|
"-C",
|
|
13749
13962
|
plan.composeDirectory,
|
|
13750
13963
|
"check-ignore",
|
|
@@ -13758,9 +13971,9 @@ function prepareNetworkComposeFiles(plan, subnet) {
|
|
|
13758
13971
|
const bytes2 = `${MARKER}
|
|
13759
13972
|
${import_yaml4.default.stringify(overlay.overlay)}`;
|
|
13760
13973
|
const assertOwnership = () => {
|
|
13761
|
-
if (!
|
|
13762
|
-
const stat =
|
|
13763
|
-
if (!stat.isFile() || stat.size > MAX_BYTES2 ||
|
|
13974
|
+
if (!import_node_fs34.default.existsSync(overlayPath)) return;
|
|
13975
|
+
const stat = import_node_fs34.default.lstatSync(overlayPath);
|
|
13976
|
+
if (!stat.isFile() || stat.size > MAX_BYTES2 || import_node_fs34.default.readFileSync(overlayPath, "utf8") !== bytes2)
|
|
13764
13977
|
throw new Error("Retained network overlay changed; repair is required.");
|
|
13765
13978
|
};
|
|
13766
13979
|
assertOwnership();
|
|
@@ -13786,7 +13999,7 @@ ${JSON.stringify(extended.config, null, 2)}
|
|
|
13786
13999
|
}
|
|
13787
14000
|
|
|
13788
14001
|
// src/core/network-connected-route.ts
|
|
13789
|
-
var
|
|
14002
|
+
var import_node_child_process29 = require("child_process");
|
|
13790
14003
|
function bounds(cidr) {
|
|
13791
14004
|
const canonical = parseIPv4Cidr(cidr);
|
|
13792
14005
|
if (!canonical) throw new Error("Invalid connected route.");
|
|
@@ -13811,7 +14024,7 @@ function inspectClaimedBridge(claim2, networkId) {
|
|
|
13811
14024
|
const env = { ...process.env };
|
|
13812
14025
|
delete env.DOCKER_CONTEXT;
|
|
13813
14026
|
delete env.DOCKER_HOST;
|
|
13814
|
-
const result = (0,
|
|
14027
|
+
const result = (0, import_node_child_process29.spawnSync)(
|
|
13815
14028
|
"docker",
|
|
13816
14029
|
[
|
|
13817
14030
|
"--host",
|
|
@@ -13838,7 +14051,7 @@ function inspectClaimedBridge(claim2, networkId) {
|
|
|
13838
14051
|
const [first, last] = bounds(claim2.subnet);
|
|
13839
14052
|
const [address] = bounds(gateway);
|
|
13840
14053
|
if (address <= first || address >= last) throw new Error();
|
|
13841
|
-
const interfaces = (0,
|
|
14054
|
+
const interfaces = (0, import_node_child_process29.spawnSync)("ip", ["-j", "-d", "-4", "address", "show", "dev", bridge], {
|
|
13842
14055
|
encoding: "utf8",
|
|
13843
14056
|
timeout: 5e3,
|
|
13844
14057
|
maxBuffer: 65536
|
|
@@ -14276,11 +14489,11 @@ var DEFAULT_READINESS_TIMEOUT_MS = 12e4;
|
|
|
14276
14489
|
var POLL_INTERVAL_MS = 1e3;
|
|
14277
14490
|
function assertOverlay(container, repoPath) {
|
|
14278
14491
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
14279
|
-
if (!workingDir || !sameWorkspacePath(workingDir,
|
|
14492
|
+
if (!workingDir || !sameWorkspacePath(workingDir, import_node_path40.default.join(repoPath, ".devcontainer"))) {
|
|
14280
14493
|
throw new Error(`Container '${container.id}' does not belong to the exact worktree.`);
|
|
14281
14494
|
}
|
|
14282
14495
|
const configFiles = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").filter(Boolean);
|
|
14283
|
-
const expectedOverlay =
|
|
14496
|
+
const expectedOverlay = import_node_path40.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
14284
14497
|
if (!configFiles.some((configFile) => sameWorkspacePath(configFile, expectedOverlay))) {
|
|
14285
14498
|
throw new Error(`Container '${container.id}' was not started with ${DEVCONTAINER_OVERLAY}.`);
|
|
14286
14499
|
}
|
|
@@ -14294,7 +14507,7 @@ function assertWorktreeOwnership(container, repoPath) {
|
|
|
14294
14507
|
function isInsideWorktree(candidate, repoPath) {
|
|
14295
14508
|
const normalizedCandidate = comparableWorkspacePath(candidate);
|
|
14296
14509
|
const normalizedRoot = comparableWorkspacePath(repoPath);
|
|
14297
|
-
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}${
|
|
14510
|
+
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}${import_node_path40.default.sep}`);
|
|
14298
14511
|
}
|
|
14299
14512
|
function assertReady(container, label) {
|
|
14300
14513
|
if (!container.state.Running) {
|
|
@@ -14486,7 +14699,7 @@ function hasExactManagedComposeProject(repoPath, state) {
|
|
|
14486
14699
|
}).some((container) => {
|
|
14487
14700
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
14488
14701
|
return Boolean(
|
|
14489
|
-
workingDir && sameWorkspacePath(workingDir,
|
|
14702
|
+
workingDir && sameWorkspacePath(workingDir, import_node_path40.default.join(repoPath, ".devcontainer"))
|
|
14490
14703
|
);
|
|
14491
14704
|
});
|
|
14492
14705
|
} catch {
|
|
@@ -14594,7 +14807,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14594
14807
|
target
|
|
14595
14808
|
});
|
|
14596
14809
|
if (target.kind === "linked") {
|
|
14597
|
-
const workspaceEnv = (0,
|
|
14810
|
+
const workspaceEnv = (0, import_node_child_process30.spawnSync)(
|
|
14598
14811
|
"docker",
|
|
14599
14812
|
["exec", appContainer.id, "printenv", "WORKSPACE"],
|
|
14600
14813
|
{ encoding: "utf-8", ...networkDockerOptions() }
|
|
@@ -14604,7 +14817,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14604
14817
|
`Workspace app container must expose WORKSPACE='${target.workspace}' (got '${workspaceEnv.stdout.trim() || "(empty)"}').`
|
|
14605
14818
|
);
|
|
14606
14819
|
}
|
|
14607
|
-
const devrouterWorkspaceEnv = (0,
|
|
14820
|
+
const devrouterWorkspaceEnv = (0, import_node_child_process30.spawnSync)(
|
|
14608
14821
|
"docker",
|
|
14609
14822
|
["exec", appContainer.id, "printenv", "DEVROUTER_WORKSPACE"],
|
|
14610
14823
|
{ encoding: "utf-8", ...networkDockerOptions() }
|
|
@@ -14615,7 +14828,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14615
14828
|
);
|
|
14616
14829
|
}
|
|
14617
14830
|
}
|
|
14618
|
-
const gitCheck = (0,
|
|
14831
|
+
const gitCheck = (0, import_node_child_process30.spawnSync)(
|
|
14619
14832
|
"docker",
|
|
14620
14833
|
[
|
|
14621
14834
|
"exec",
|
|
@@ -14888,14 +15101,14 @@ function resolvePrimaryTarget(repoPath) {
|
|
|
14888
15101
|
}
|
|
14889
15102
|
function isPrimaryCheckout(repoPath) {
|
|
14890
15103
|
try {
|
|
14891
|
-
return
|
|
15104
|
+
return import_node_fs35.default.statSync(import_node_path40.default.join(repoPath, ".git")).isDirectory();
|
|
14892
15105
|
} catch {
|
|
14893
15106
|
return false;
|
|
14894
15107
|
}
|
|
14895
15108
|
}
|
|
14896
15109
|
function openUrls(urls) {
|
|
14897
15110
|
for (const url of urls) {
|
|
14898
|
-
const opened = (0,
|
|
15111
|
+
const opened = (0, import_node_child_process30.spawnSync)("open", [url], { encoding: "utf-8" });
|
|
14899
15112
|
if (opened.status !== 0) {
|
|
14900
15113
|
process.stderr.write(`Warning: could not open '${url}'.
|
|
14901
15114
|
`);
|
|
@@ -14952,8 +15165,8 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
14952
15165
|
const target = options.repair ? resolveRepairTarget(repoPath, linked) : linked ? resolveLinkedTarget(repoPath) : resolvePrimaryTarget(repoPath);
|
|
14953
15166
|
let devpodId = target.devpodId;
|
|
14954
15167
|
if (target.kind === "linked") {
|
|
14955
|
-
const overlayPath =
|
|
14956
|
-
if (!
|
|
15168
|
+
const overlayPath = import_node_path40.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
15169
|
+
if (!import_node_fs35.default.existsSync(overlayPath)) {
|
|
14957
15170
|
throw new Error(`Missing required DevPod compose overlay: ${overlayPath}`);
|
|
14958
15171
|
}
|
|
14959
15172
|
}
|
|
@@ -15053,7 +15266,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
15053
15266
|
}
|
|
15054
15267
|
const apps = proxyAppsFromConfig(runtime.config);
|
|
15055
15268
|
const parsedUpstreams = apps.map((app) => parseUpstream(app.upstream));
|
|
15056
|
-
const aliasPrefix = target.kind === "linked" ? target.workspace : wsFromBranch(runtime.config.project?.name ??
|
|
15269
|
+
const aliasPrefix = target.kind === "linked" ? target.workspace : wsFromBranch(runtime.config.project?.name ?? import_node_path40.default.basename(repoPath)) ?? "app";
|
|
15057
15270
|
for (const [index, app] of apps.entries()) {
|
|
15058
15271
|
if (!parsedUpstreams[index].host.startsWith(`${aliasPrefix}-`)) {
|
|
15059
15272
|
const owner = target.kind === "linked" ? "workspace" : "checkout";
|
|
@@ -15178,7 +15391,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
15178
15391
|
transitionPhase = "service-start";
|
|
15179
15392
|
reportLifecycleProgress("service-start");
|
|
15180
15393
|
claimLifecycleEffect();
|
|
15181
|
-
const result = (0,
|
|
15394
|
+
const result = (0, import_node_child_process30.spawnSync)("docker", ["start", ...stopped.map((entry) => entry.id)], {
|
|
15182
15395
|
...networkDockerOptions(),
|
|
15183
15396
|
encoding: "utf-8",
|
|
15184
15397
|
timeout: DEFAULT_READINESS_TIMEOUT_MS,
|
|
@@ -15895,9 +16108,9 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
15895
16108
|
}
|
|
15896
16109
|
const repairLocked = () => {
|
|
15897
16110
|
const runtime = resolveWorkspaceRuntimeOrDefault(repoPath);
|
|
15898
|
-
|
|
16111
|
+
import_node_fs35.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
15899
16112
|
return withFileLock(
|
|
15900
|
-
|
|
16113
|
+
import_node_path40.default.join(DEVROUTER_HOME, `${runtime}-mutation.lock`),
|
|
15901
16114
|
{
|
|
15902
16115
|
activity: "Managed runtime repair",
|
|
15903
16116
|
target: repoPath,
|
|
@@ -16111,7 +16324,7 @@ async function executeLifecycleWorker(request, operation) {
|
|
|
16111
16324
|
const owned = containers.filter(
|
|
16112
16325
|
(container) => sameWorkspacePath(
|
|
16113
16326
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
16114
|
-
|
|
16327
|
+
import_node_path41.default.join(request.repoPath, ".devcontainer")
|
|
16115
16328
|
)
|
|
16116
16329
|
);
|
|
16117
16330
|
const projects = owned.map((container) => container.labels["com.docker.compose.project"]);
|
|
@@ -16122,6 +16335,11 @@ async function executeLifecycleWorker(request, operation) {
|
|
|
16122
16335
|
];
|
|
16123
16336
|
for (const project of stopProjects) inspectManagedStopContainers(project);
|
|
16124
16337
|
}
|
|
16338
|
+
updateReliabilityOperation(request.identity, (record5) => {
|
|
16339
|
+
if (!matchesFence(record5, request.fence))
|
|
16340
|
+
throw new Error("Lifecycle intent changed before stop work began.");
|
|
16341
|
+
record5.stopWorkStarted = true;
|
|
16342
|
+
});
|
|
16125
16343
|
} else {
|
|
16126
16344
|
updateReliabilityOperation(request.identity, (record5) => {
|
|
16127
16345
|
stepRecord(record5, {
|
|
@@ -16217,7 +16435,7 @@ function proveLifecycleStopped() {
|
|
|
16217
16435
|
const remaining = inspectWorkspaceContainers().filter(
|
|
16218
16436
|
(container) => sameWorkspacePath(
|
|
16219
16437
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
16220
|
-
|
|
16438
|
+
import_node_path41.default.join(request.repoPath, ".devcontainer")
|
|
16221
16439
|
)
|
|
16222
16440
|
);
|
|
16223
16441
|
if (remaining.some(
|
|
@@ -16233,6 +16451,7 @@ function proveLifecycleStopped() {
|
|
|
16233
16451
|
const settlement = updateReliabilityOperation(request.identity, (record5) => {
|
|
16234
16452
|
if (!matchesFence(record5, request.fence) || record5.worker)
|
|
16235
16453
|
throw new Error("Stop proof was superseded or an earlier worker remains.");
|
|
16454
|
+
delete record5.stopWorkStarted;
|
|
16236
16455
|
if (record5.version === 2) {
|
|
16237
16456
|
if (record5.capacity) record5.capacity.validUntilMs = 0;
|
|
16238
16457
|
return record5.state.environmentId;
|
|
@@ -16246,7 +16465,7 @@ function proveLifecycleStopped() {
|
|
|
16246
16465
|
return void 0;
|
|
16247
16466
|
});
|
|
16248
16467
|
if (settlement) {
|
|
16249
|
-
const capacity = createLifecycleCapacityStore(
|
|
16468
|
+
const capacity = createLifecycleCapacityStore(import_node_path41.default.join(DEVROUTER_HOME, "controller"));
|
|
16250
16469
|
for (let attempt = 0; ; attempt++) {
|
|
16251
16470
|
try {
|
|
16252
16471
|
const state = capacity.inspect();
|
|
@@ -16271,6 +16490,7 @@ function proveLifecycleStopped() {
|
|
|
16271
16490
|
if (!matchesFence(record5, request.fence) || record5.worker || record5.state.environmentId !== settlement || record5.capacity && record5.capacity.validUntilMs !== 0)
|
|
16272
16491
|
throw new Error("Capacity settlement was superseded before journal confirmation.");
|
|
16273
16492
|
record5.capacity = null;
|
|
16493
|
+
delete record5.stopWorkStarted;
|
|
16274
16494
|
if (record5.enrollment) {
|
|
16275
16495
|
record5.phaseSettlement = null;
|
|
16276
16496
|
record5.activeProfile = null;
|
|
@@ -16324,7 +16544,7 @@ async function devsyExecOutcome(repoPath, command, retainedProof) {
|
|
|
16324
16544
|
const launch = () => {
|
|
16325
16545
|
if (retainedProof) revalidateDevsyExecProof(repoPath, retainedProof);
|
|
16326
16546
|
claimLifecycleEffect();
|
|
16327
|
-
return (0,
|
|
16547
|
+
return (0, import_node_child_process31.spawn)("devsy", args, { stdio: "inherit" });
|
|
16328
16548
|
};
|
|
16329
16549
|
child = retainedProof ? withMutationLock("Execute retained workspace", repoPath, launch) : launch();
|
|
16330
16550
|
} catch (error) {
|
|
@@ -16372,7 +16592,7 @@ function ensureGuidance2(repoPath) {
|
|
|
16372
16592
|
return `Run 'devrouter ensure ${repoPath}' first.`;
|
|
16373
16593
|
}
|
|
16374
16594
|
function assertRunningDevpod(devpodId, repoPath) {
|
|
16375
|
-
const result = (0,
|
|
16595
|
+
const result = (0, import_node_child_process32.spawnSync)("devpod", ["status", devpodId, "--output", "json"], {
|
|
16376
16596
|
encoding: "utf-8"
|
|
16377
16597
|
});
|
|
16378
16598
|
if (result.status !== 0) {
|
|
@@ -16551,7 +16771,7 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
16551
16771
|
let child;
|
|
16552
16772
|
try {
|
|
16553
16773
|
claimLifecycleEffect();
|
|
16554
|
-
child = (0,
|
|
16774
|
+
child = (0, import_node_child_process32.spawn)("devpod", args, { stdio: ["inherit", "inherit", "pipe"] });
|
|
16555
16775
|
} catch (error) {
|
|
16556
16776
|
settled = true;
|
|
16557
16777
|
reject(
|
|
@@ -16608,12 +16828,12 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
16608
16828
|
}
|
|
16609
16829
|
|
|
16610
16830
|
// src/core/workspace-lifecycle.ts
|
|
16611
|
-
var
|
|
16612
|
-
var
|
|
16613
|
-
var
|
|
16831
|
+
var import_node_child_process33 = require("child_process");
|
|
16832
|
+
var import_node_fs37 = __toESM(require("fs"));
|
|
16833
|
+
var import_node_path42 = __toESM(require("path"));
|
|
16614
16834
|
|
|
16615
16835
|
// src/core/route-state.ts
|
|
16616
|
-
var
|
|
16836
|
+
var import_node_fs36 = __toESM(require("fs"));
|
|
16617
16837
|
function listRoutesForWorktreePath(worktreePath, routes = listHostRouteState()) {
|
|
16618
16838
|
return routes.filter((route) => sameWorkspacePath(route.repoPath, worktreePath));
|
|
16619
16839
|
}
|