@devrouter/cli 0.0.73 → 0.0.75
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/dist/devrouter-lifecycle-worker.js +6623 -6319
- package/dist/devrouter.js +929 -533
- package/package.json +1 -1
- package/upgrade-prompts/0.0.74.md +35 -0
- package/upgrade-prompts/0.0.75.md +35 -0
package/dist/devrouter.js
CHANGED
|
@@ -1177,6 +1177,55 @@ function inspectManagedStopWorkspaceIds(endpoint, composeDirectory) {
|
|
|
1177
1177
|
assertUniqueContainerIds(ids2);
|
|
1178
1178
|
return ids2;
|
|
1179
1179
|
}
|
|
1180
|
+
function assertManagedStopCheckoutAbsent(endpoint, repoPath) {
|
|
1181
|
+
const list2 = () => {
|
|
1182
|
+
const ids3 = parseDockerLines(
|
|
1183
|
+
runManagedStopDocker(["ps", "-a", "--no-trunc", "--format", "{{.ID}}"], endpoint)
|
|
1184
|
+
);
|
|
1185
|
+
if (ids3.length > 256) throw new Error("Managed stop population exceeds its bound.");
|
|
1186
|
+
ids3.forEach(assertFullContainerId);
|
|
1187
|
+
assertUniqueContainerIds(ids3);
|
|
1188
|
+
return ids3;
|
|
1189
|
+
};
|
|
1190
|
+
const ids2 = list2();
|
|
1191
|
+
const seen = [];
|
|
1192
|
+
const labels = [
|
|
1193
|
+
"com.docker.compose.project.working_dir",
|
|
1194
|
+
"devcontainer.local_folder",
|
|
1195
|
+
"vsch.local.folder"
|
|
1196
|
+
];
|
|
1197
|
+
const template = '{"id":{{json .Id}},"labels":{"com.docker.compose.project.working_dir":{{json (index .Config.Labels "com.docker.compose.project.working_dir")}},"devcontainer.local_folder":{{json (index .Config.Labels "devcontainer.local_folder")}},"vsch.local.folder":{{json (index .Config.Labels "vsch.local.folder")}}},"mounts":[{{range $i, $m := .Mounts}}{{if $i}},{{end}}{"Type":{{json $m.Type}},"Source":{{json $m.Source}}}{{end}}]}';
|
|
1198
|
+
if (ids2.length) {
|
|
1199
|
+
for (const line of parseDockerLines(
|
|
1200
|
+
runManagedStopDocker(["inspect", "--format", template, ...ids2], endpoint)
|
|
1201
|
+
)) {
|
|
1202
|
+
const value = parseManagedStopJson(line);
|
|
1203
|
+
if (!isRecord(value) || !isRecord(value.labels) || !Array.isArray(value.mounts))
|
|
1204
|
+
throw new Error("Managed stop checkout inspection is malformed.");
|
|
1205
|
+
assertFullContainerId(value.id);
|
|
1206
|
+
seen.push(value.id);
|
|
1207
|
+
for (const label2 of labels) {
|
|
1208
|
+
const field = value.labels[label2];
|
|
1209
|
+
if (field !== null && typeof field !== "string")
|
|
1210
|
+
throw new Error("Managed stop checkout labels are incomplete.");
|
|
1211
|
+
if (typeof field === "string" && sameWorkspacePath(
|
|
1212
|
+
field,
|
|
1213
|
+
label2 === labels[0] ? import_node_path4.default.join(repoPath, ".devcontainer") : repoPath
|
|
1214
|
+
))
|
|
1215
|
+
throw new Error("Managed stop observed a remaining checkout container.");
|
|
1216
|
+
}
|
|
1217
|
+
for (const mount of value.mounts) {
|
|
1218
|
+
if (!isRecord(mount) || typeof mount.Type !== "string" || typeof mount.Source !== "string")
|
|
1219
|
+
throw new Error("Managed stop checkout mounts are incomplete.");
|
|
1220
|
+
if (mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath))
|
|
1221
|
+
throw new Error("Managed stop observed a remaining checkout mount.");
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
assertUniqueContainerIds(seen);
|
|
1226
|
+
requireSameContainerPopulation(ids2, seen);
|
|
1227
|
+
requireSameContainerPopulation(ids2, list2());
|
|
1228
|
+
}
|
|
1180
1229
|
function hasExactManagedStopMissingObjectError(stderr, id5) {
|
|
1181
1230
|
if (typeof stderr !== "string") return false;
|
|
1182
1231
|
const line = stderr.endsWith("\r\n") ? stderr.slice(0, -2) : stderr.endsWith("\n") ? stderr.slice(0, -1) : stderr;
|
|
@@ -4207,7 +4256,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs7.default.read
|
|
|
4207
4256
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
4208
4257
|
const requiredVersion = config.devrouter?.version;
|
|
4209
4258
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
4210
|
-
const cliVersion = true ? "0.0.
|
|
4259
|
+
const cliVersion = true ? "0.0.75" : "0.0.0-dev";
|
|
4211
4260
|
if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
|
|
4212
4261
|
hasWarnedVersionMismatch = true;
|
|
4213
4262
|
process.stderr.write(
|
|
@@ -5460,7 +5509,7 @@ var init_reliability_contract = __esm({
|
|
|
5460
5509
|
|
|
5461
5510
|
// src/core/reliability-operation-store.ts
|
|
5462
5511
|
function installedCliVersion() {
|
|
5463
|
-
return true ? "0.0.
|
|
5512
|
+
return true ? "0.0.75" : "0.0.0-dev";
|
|
5464
5513
|
}
|
|
5465
5514
|
function compareCliVersions(left, right) {
|
|
5466
5515
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -7671,10 +7720,11 @@ function inspectDevsyRuntimeAbsence(devsyId) {
|
|
|
7671
7720
|
encoding: "utf-8",
|
|
7672
7721
|
timeout: 1e4
|
|
7673
7722
|
});
|
|
7674
|
-
if (
|
|
7723
|
+
if (result.error || result.status === null) return false;
|
|
7724
|
+
if (result.status === 0) {
|
|
7675
7725
|
return parseDevsyRuntimeStatus(result.stdout, devsyId) === "not-found";
|
|
7676
7726
|
}
|
|
7677
|
-
const output2 = [result.
|
|
7727
|
+
const output2 = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
7678
7728
|
return /workspace not found/i.test(output2);
|
|
7679
7729
|
}
|
|
7680
7730
|
function inspectDevsyWorkspaceOwnership(workspaces, devsyId, worktreePath) {
|
|
@@ -8363,6 +8413,12 @@ function isWildcard(values) {
|
|
|
8363
8413
|
function sha256(contents) {
|
|
8364
8414
|
return (0, import_node_crypto11.createHash)("sha256").update(contents, "utf-8").digest("hex");
|
|
8365
8415
|
}
|
|
8416
|
+
function safeComposeProject(value) {
|
|
8417
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(value)) {
|
|
8418
|
+
throw new Error(`Docker Compose project '${value}' is not a safe exact identifier.`);
|
|
8419
|
+
}
|
|
8420
|
+
return value;
|
|
8421
|
+
}
|
|
8366
8422
|
function inspectManagedDevcontainerConfig(options) {
|
|
8367
8423
|
const managedRuntime = options.config.managedRuntime;
|
|
8368
8424
|
if (!managedRuntime) {
|
|
@@ -8481,6 +8537,91 @@ function managedComposeEnvironment(workspace) {
|
|
|
8481
8537
|
}
|
|
8482
8538
|
return env;
|
|
8483
8539
|
}
|
|
8540
|
+
function assertReadableComposeFile(file) {
|
|
8541
|
+
try {
|
|
8542
|
+
if (!import_node_fs18.default.statSync(file).isFile()) throw new Error("not a regular file");
|
|
8543
|
+
import_node_fs18.default.accessSync(file, import_node_fs18.default.constants.R_OK);
|
|
8544
|
+
} catch {
|
|
8545
|
+
throw new Error("Managed container Compose configuration contains an unreadable file.");
|
|
8546
|
+
}
|
|
8547
|
+
}
|
|
8548
|
+
function assertManagedContainerConfigUnchanged(options) {
|
|
8549
|
+
if (options.containers.length === 0) {
|
|
8550
|
+
throw new Error("Managed container configuration cannot be verified without containers.");
|
|
8551
|
+
}
|
|
8552
|
+
for (const container of options.containers) {
|
|
8553
|
+
const project = container.labels["com.docker.compose.project"];
|
|
8554
|
+
const service = container.labels["com.docker.compose.service"];
|
|
8555
|
+
const workingDirectory = container.labels["com.docker.compose.project.working_dir"];
|
|
8556
|
+
const recordedConfigFiles = container.labels["com.docker.compose.project.config_files"];
|
|
8557
|
+
const expectedHash = container.labels["com.docker.compose.config-hash"];
|
|
8558
|
+
if (!project || !service || !workingDirectory || !recordedConfigFiles || !expectedHash || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(service) || !/^[a-f0-9]{64}$/.test(expectedHash)) {
|
|
8559
|
+
throw new Error("Managed container is missing valid Compose identity and hash labels.");
|
|
8560
|
+
}
|
|
8561
|
+
const composeProject = safeComposeProject(project);
|
|
8562
|
+
const configFiles2 = recordedConfigFiles.split(",").map((file) => file.trim());
|
|
8563
|
+
if (configFiles2.some((file) => file.length === 0)) {
|
|
8564
|
+
throw new Error("Managed container has an invalid recorded Compose file list.");
|
|
8565
|
+
}
|
|
8566
|
+
if (options.plan.composeFiles.some(
|
|
8567
|
+
(expected) => !configFiles2.some((actual) => sameWorkspacePath(actual, expected))
|
|
8568
|
+
)) {
|
|
8569
|
+
throw new Error("Managed container Compose files do not include the managed plan files.");
|
|
8570
|
+
}
|
|
8571
|
+
for (const file of configFiles2) assertReadableComposeFile(file);
|
|
8572
|
+
const fileArgs = configFiles2.flatMap((file) => ["-f", file]);
|
|
8573
|
+
const result = (() => {
|
|
8574
|
+
try {
|
|
8575
|
+
const composeArgs = [
|
|
8576
|
+
"compose",
|
|
8577
|
+
"--project-name",
|
|
8578
|
+
composeProject,
|
|
8579
|
+
"--project-directory",
|
|
8580
|
+
workingDirectory
|
|
8581
|
+
];
|
|
8582
|
+
const env = managedComposeEnvironment(options.workspace);
|
|
8583
|
+
const rendered = (0, import_node_child_process8.spawnSync)(
|
|
8584
|
+
"docker",
|
|
8585
|
+
[...composeArgs, ...fileArgs, "config", "--format", "json"],
|
|
8586
|
+
{
|
|
8587
|
+
cwd: workingDirectory,
|
|
8588
|
+
encoding: "utf-8",
|
|
8589
|
+
env,
|
|
8590
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
8591
|
+
timeout: COMPOSE_HASH_TIMEOUT_MS,
|
|
8592
|
+
maxBuffer: 1024 * 1024
|
|
8593
|
+
}
|
|
8594
|
+
);
|
|
8595
|
+
if (rendered.status !== 0 || rendered.error || !rendered.stdout?.trim()) {
|
|
8596
|
+
throw new Error("Compose configuration rendering failed.");
|
|
8597
|
+
}
|
|
8598
|
+
return (0, import_node_child_process8.spawnSync)(
|
|
8599
|
+
"docker",
|
|
8600
|
+
[...composeArgs, "-f", "-", "config", "--no-interpolate", "--hash", service],
|
|
8601
|
+
{
|
|
8602
|
+
cwd: workingDirectory,
|
|
8603
|
+
encoding: "utf-8",
|
|
8604
|
+
env,
|
|
8605
|
+
input: rendered.stdout,
|
|
8606
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
8607
|
+
timeout: COMPOSE_HASH_TIMEOUT_MS,
|
|
8608
|
+
maxBuffer: 1024 * 1024
|
|
8609
|
+
}
|
|
8610
|
+
);
|
|
8611
|
+
} catch {
|
|
8612
|
+
throw new Error(`Could not verify managed Compose configuration for service '${service}'.`);
|
|
8613
|
+
}
|
|
8614
|
+
})();
|
|
8615
|
+
if (result.status !== 0 || result.error) {
|
|
8616
|
+
throw new Error(`Could not verify managed Compose configuration for service '${service}'.`);
|
|
8617
|
+
}
|
|
8618
|
+
const output2 = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
8619
|
+
const match = /^(\S+)\s+([a-f0-9]{64})$/.exec(output2);
|
|
8620
|
+
if (!match || match[1] !== service || match[2] !== expectedHash) {
|
|
8621
|
+
throw new Error(`Managed Compose configuration changed for service '${service}'.`);
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8484
8625
|
function stopExactManagedService(containerId, service, options) {
|
|
8485
8626
|
assertSafeContainerId(containerId);
|
|
8486
8627
|
const result = (0, import_node_child_process8.spawnSync)("docker", ["stop", containerId], {
|
|
@@ -8493,7 +8634,7 @@ function stopExactManagedService(containerId, service, options) {
|
|
|
8493
8634
|
throw new Error(`Could not stop exact managed service '${service}' (${containerId}).`);
|
|
8494
8635
|
}
|
|
8495
8636
|
}
|
|
8496
|
-
var import_node_child_process8, import_node_crypto11, import_node_fs18, import_node_path25, import_yaml3, MANAGED_DEVCONTAINER_PATH, MANAGED_DEVCONTAINER_MARKER;
|
|
8637
|
+
var import_node_child_process8, import_node_crypto11, import_node_fs18, import_node_path25, import_yaml3, MANAGED_DEVCONTAINER_PATH, MANAGED_DEVCONTAINER_MARKER, COMPOSE_HASH_TIMEOUT_MS;
|
|
8497
8638
|
var init_devcontainer_profile = __esm({
|
|
8498
8639
|
"src/core/devcontainer-profile.ts"() {
|
|
8499
8640
|
"use strict";
|
|
@@ -8509,6 +8650,7 @@ var init_devcontainer_profile = __esm({
|
|
|
8509
8650
|
init_workspace();
|
|
8510
8651
|
MANAGED_DEVCONTAINER_PATH = ".devcontainer/devcontainer.devrouter.json";
|
|
8511
8652
|
MANAGED_DEVCONTAINER_MARKER = "// devrouter:managed devcontainer profile";
|
|
8653
|
+
COMPOSE_HASH_TIMEOUT_MS = 1e4;
|
|
8512
8654
|
}
|
|
8513
8655
|
});
|
|
8514
8656
|
|
|
@@ -8517,8 +8659,10 @@ function listDevpodWorkspacesRaw(options = {}) {
|
|
|
8517
8659
|
const result = (0, import_node_child_process9.spawnSync)("devpod", ["list", "--output", "json", "--skip-pro"], {
|
|
8518
8660
|
encoding: "utf-8"
|
|
8519
8661
|
});
|
|
8520
|
-
if (
|
|
8521
|
-
return
|
|
8662
|
+
if (result.error?.code === "ENOENT") {
|
|
8663
|
+
if (options.readLocalWhenMissing) return readLocalDevpodRegistry();
|
|
8664
|
+
if (options.allowMissingExecutable) return [];
|
|
8665
|
+
}
|
|
8522
8666
|
if (result.error || result.status !== 0) {
|
|
8523
8667
|
const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
8524
8668
|
throw new Error(`devpod list failed: ${details || "devpod is not installed or unavailable"}`);
|
|
@@ -8551,11 +8695,110 @@ function listDevpodWorkspacesRaw(options = {}) {
|
|
|
8551
8695
|
return workspace;
|
|
8552
8696
|
});
|
|
8553
8697
|
}
|
|
8554
|
-
|
|
8698
|
+
function devpodRegistryRoot() {
|
|
8699
|
+
return import_node_path26.default.resolve(process.env.DEVPOD_HOME || import_node_path26.default.join(import_node_os3.default.homedir(), ".devpod"));
|
|
8700
|
+
}
|
|
8701
|
+
function readLocalDevpodRegistry() {
|
|
8702
|
+
const fail4 = () => new Error("Local DevPod registry evidence is incomplete, unreadable, or changed.");
|
|
8703
|
+
const stamps = /* @__PURE__ */ new Map();
|
|
8704
|
+
const stamp = (stat, identityOnly = false) => JSON.stringify([
|
|
8705
|
+
stat.dev,
|
|
8706
|
+
stat.ino,
|
|
8707
|
+
stat.mode,
|
|
8708
|
+
stat.uid,
|
|
8709
|
+
...identityOnly ? [] : [stat.size, stat.mtimeMs, stat.ctimeMs]
|
|
8710
|
+
]);
|
|
8711
|
+
const inspect = (file) => {
|
|
8712
|
+
try {
|
|
8713
|
+
return import_node_fs19.default.lstatSync(file);
|
|
8714
|
+
} catch (error) {
|
|
8715
|
+
if (error.code === "ENOENT") return void 0;
|
|
8716
|
+
throw fail4();
|
|
8717
|
+
}
|
|
8718
|
+
};
|
|
8719
|
+
const directory = (dir, optional = false, identityOnly = false) => {
|
|
8720
|
+
const stat = inspect(dir);
|
|
8721
|
+
if (!stat && !optional || stat && !stat.isDirectory()) throw fail4();
|
|
8722
|
+
stamps.set(dir, { value: stat && stamp(stat, identityOnly), identityOnly });
|
|
8723
|
+
return !!stat;
|
|
8724
|
+
};
|
|
8725
|
+
const entries = (dir) => {
|
|
8726
|
+
const names = import_node_fs19.default.readdirSync(dir).sort();
|
|
8727
|
+
if (names.length > 256) throw fail4();
|
|
8728
|
+
return names;
|
|
8729
|
+
};
|
|
8730
|
+
const result = [];
|
|
8731
|
+
try {
|
|
8732
|
+
const root = devpodRegistryRoot();
|
|
8733
|
+
let ancestor = import_node_path26.default.parse(root).root;
|
|
8734
|
+
let present = directory(ancestor, false, ancestor !== root);
|
|
8735
|
+
for (const part of root.slice(ancestor.length).split(import_node_path26.default.sep).filter(Boolean)) {
|
|
8736
|
+
if (!present) break;
|
|
8737
|
+
ancestor = import_node_path26.default.join(ancestor, part);
|
|
8738
|
+
present = directory(ancestor, true, ancestor !== root);
|
|
8739
|
+
}
|
|
8740
|
+
const contexts = import_node_path26.default.join(root, "contexts");
|
|
8741
|
+
if (present && directory(contexts, true)) {
|
|
8742
|
+
for (const context of entries(contexts)) {
|
|
8743
|
+
const contextDir = import_node_path26.default.join(contexts, context);
|
|
8744
|
+
directory(contextDir);
|
|
8745
|
+
const workspaces = import_node_path26.default.join(contextDir, "workspaces");
|
|
8746
|
+
if (!directory(workspaces, true)) continue;
|
|
8747
|
+
for (const id5 of entries(workspaces)) {
|
|
8748
|
+
if (result.length >= 256) throw fail4();
|
|
8749
|
+
const workspace = import_node_path26.default.join(workspaces, id5);
|
|
8750
|
+
directory(workspace);
|
|
8751
|
+
const file = import_node_path26.default.join(workspace, "workspace.json");
|
|
8752
|
+
const stat = inspect(file);
|
|
8753
|
+
if (!stat?.isFile() || stat.size > 1024 * 1024) throw fail4();
|
|
8754
|
+
stamps.set(file, { value: stamp(stat), identityOnly: false });
|
|
8755
|
+
const fd = import_node_fs19.default.openSync(file, import_node_fs19.default.constants.O_RDONLY | import_node_fs19.default.constants.O_NOFOLLOW);
|
|
8756
|
+
let raw;
|
|
8757
|
+
try {
|
|
8758
|
+
if (stamp(import_node_fs19.default.fstatSync(fd)) !== stamp(stat)) throw fail4();
|
|
8759
|
+
const buffer = Buffer.alloc(stat.size + 1);
|
|
8760
|
+
const bytes2 = import_node_fs19.default.readSync(fd, buffer, 0, buffer.length, 0);
|
|
8761
|
+
if (bytes2 !== stat.size || stamp(import_node_fs19.default.fstatSync(fd)) !== stamp(stat)) throw fail4();
|
|
8762
|
+
raw = JSON.parse(buffer.subarray(0, bytes2).toString("utf8"));
|
|
8763
|
+
} finally {
|
|
8764
|
+
import_node_fs19.default.closeSync(fd);
|
|
8765
|
+
}
|
|
8766
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw fail4();
|
|
8767
|
+
const value = raw;
|
|
8768
|
+
if (value.id !== id5 || !value.source || typeof value.source !== "object" || Array.isArray(value.source))
|
|
8769
|
+
throw fail4();
|
|
8770
|
+
const source = value.source;
|
|
8771
|
+
if (source.localFolder !== void 0 && (typeof source.localFolder !== "string" || source.localFolder !== "" && !import_node_path26.default.isAbsolute(source.localFolder)))
|
|
8772
|
+
throw fail4();
|
|
8773
|
+
const remote = ["gitRepository", "image", "container"];
|
|
8774
|
+
if (remote.some((key) => source[key] !== void 0 && typeof source[key] !== "string"))
|
|
8775
|
+
throw fail4();
|
|
8776
|
+
if (!source.localFolder && !remote.some((key) => typeof source[key] === "string" && source[key]))
|
|
8777
|
+
throw fail4();
|
|
8778
|
+
result.push({
|
|
8779
|
+
id: id5,
|
|
8780
|
+
source: { localFolder: source.localFolder ?? "" }
|
|
8781
|
+
});
|
|
8782
|
+
}
|
|
8783
|
+
}
|
|
8784
|
+
}
|
|
8785
|
+
for (const [file, expected] of stamps) {
|
|
8786
|
+
const actual = inspect(file);
|
|
8787
|
+
if ((actual && stamp(actual, expected.identityOnly)) !== expected.value) throw fail4();
|
|
8788
|
+
}
|
|
8789
|
+
return result;
|
|
8790
|
+
} catch {
|
|
8791
|
+
throw fail4();
|
|
8792
|
+
}
|
|
8793
|
+
}
|
|
8794
|
+
var import_node_child_process9, import_node_fs19, import_node_os3, import_node_path26;
|
|
8555
8795
|
var init_devpod_registry = __esm({
|
|
8556
8796
|
"src/core/devpod-registry.ts"() {
|
|
8557
8797
|
"use strict";
|
|
8558
8798
|
import_node_child_process9 = require("child_process");
|
|
8799
|
+
import_node_fs19 = __toESM(require("fs"));
|
|
8800
|
+
import_node_os3 = __toESM(require("os"));
|
|
8801
|
+
import_node_path26 = __toESM(require("path"));
|
|
8559
8802
|
}
|
|
8560
8803
|
});
|
|
8561
8804
|
|
|
@@ -8573,13 +8816,13 @@ function proveManagedComposePopulation(options) {
|
|
|
8573
8816
|
}
|
|
8574
8817
|
services.add(service);
|
|
8575
8818
|
const files = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").map((file) => file.trim());
|
|
8576
|
-
if (files.some((file) => !file || !
|
|
8819
|
+
if (files.some((file) => !file || !import_node_path27.default.isAbsolute(file)) || new Set(files.map((file) => import_node_path27.default.resolve(file))).size !== files.length || files.length < plan.composeFiles.length || plan.composeFiles.some((file, index) => !sameWorkspacePath(file, files[index]))) {
|
|
8577
8820
|
throw new Error("Managed stop Compose file identity changed.");
|
|
8578
8821
|
}
|
|
8579
8822
|
for (const file of files.slice(plan.composeFiles.length)) {
|
|
8580
|
-
if (
|
|
8581
|
-
|
|
8582
|
-
) ||
|
|
8823
|
+
if (import_node_path27.default.dirname(file) !== featureDirectory || !/^docker-compose\.devcontainer\.containerFeatures-[a-zA-Z0-9_-]+\.yml$/.test(
|
|
8824
|
+
import_node_path27.default.basename(file)
|
|
8825
|
+
) || import_node_fs20.default.realpathSync(file) !== import_node_path27.default.join(import_node_fs20.default.realpathSync(providerRoot), import_node_path27.default.relative(providerRoot, file))) {
|
|
8583
8826
|
throw new Error("Managed stop refuses a foreign or escaped provider Compose file.");
|
|
8584
8827
|
}
|
|
8585
8828
|
}
|
|
@@ -8597,12 +8840,12 @@ function proveManagedComposePopulation(options) {
|
|
|
8597
8840
|
}
|
|
8598
8841
|
return primary;
|
|
8599
8842
|
}
|
|
8600
|
-
var
|
|
8843
|
+
var import_node_fs20, import_node_path27;
|
|
8601
8844
|
var init_managed_compose_population = __esm({
|
|
8602
8845
|
"src/core/managed-compose-population.ts"() {
|
|
8603
8846
|
"use strict";
|
|
8604
|
-
|
|
8605
|
-
|
|
8847
|
+
import_node_fs20 = __toESM(require("fs"));
|
|
8848
|
+
import_node_path27 = __toESM(require("path"));
|
|
8606
8849
|
init_workspace();
|
|
8607
8850
|
}
|
|
8608
8851
|
});
|
|
@@ -8638,7 +8881,7 @@ function isRuntimeInstalled(runtime) {
|
|
|
8638
8881
|
function readWorkspaceRuntimeConfig() {
|
|
8639
8882
|
let raw;
|
|
8640
8883
|
try {
|
|
8641
|
-
raw = JSON.parse(
|
|
8884
|
+
raw = JSON.parse(import_node_fs21.default.readFileSync(RUNTIME_CONFIG_FILE, "utf-8"));
|
|
8642
8885
|
} catch {
|
|
8643
8886
|
return {};
|
|
8644
8887
|
}
|
|
@@ -8659,7 +8902,7 @@ function readWorkspaceRuntimeConfig() {
|
|
|
8659
8902
|
function inspectWorkspaceRuntimeConfig() {
|
|
8660
8903
|
let rawText;
|
|
8661
8904
|
try {
|
|
8662
|
-
rawText =
|
|
8905
|
+
rawText = import_node_fs21.default.readFileSync(RUNTIME_CONFIG_FILE, "utf-8");
|
|
8663
8906
|
} catch {
|
|
8664
8907
|
return { exists: false, config: {}, problems: [] };
|
|
8665
8908
|
}
|
|
@@ -8710,7 +8953,7 @@ function writeWorkspaceRuntimeConfig(config) {
|
|
|
8710
8953
|
}
|
|
8711
8954
|
next.devsyInactivityTimeout = timeout;
|
|
8712
8955
|
}
|
|
8713
|
-
|
|
8956
|
+
import_node_fs21.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
8714
8957
|
writeFileAtomically(RUNTIME_CONFIG_FILE, `${JSON.stringify(next, null, 2)}
|
|
8715
8958
|
`);
|
|
8716
8959
|
}
|
|
@@ -8818,20 +9061,20 @@ function resetWorkspaceRuntimeCaches() {
|
|
|
8818
9061
|
cachedRuntime = void 0;
|
|
8819
9062
|
cachedSnapshots = void 0;
|
|
8820
9063
|
}
|
|
8821
|
-
var import_node_child_process10,
|
|
9064
|
+
var import_node_child_process10, import_node_fs21, import_node_path28, SUPPORTED_RUNTIMES2, RUNTIME_CONFIG_FILE, INACTIVITY_TIMEOUT_PATTERN, cachedRuntime, cachedSnapshots, UnsupportedWorkspaceRuntimeError, WorkspaceRuntimeOwnershipError;
|
|
8822
9065
|
var init_workspace_runtime = __esm({
|
|
8823
9066
|
"src/core/workspace-runtime.ts"() {
|
|
8824
9067
|
"use strict";
|
|
8825
9068
|
import_node_child_process10 = require("child_process");
|
|
8826
|
-
|
|
8827
|
-
|
|
9069
|
+
import_node_fs21 = __toESM(require("fs"));
|
|
9070
|
+
import_node_path28 = __toESM(require("path"));
|
|
8828
9071
|
init_atomic_file();
|
|
8829
9072
|
init_devpod_registry();
|
|
8830
9073
|
init_devsy_workspaces();
|
|
8831
9074
|
init_router();
|
|
8832
9075
|
init_workspace();
|
|
8833
9076
|
SUPPORTED_RUNTIMES2 = ["devpod", "devsy"];
|
|
8834
|
-
RUNTIME_CONFIG_FILE =
|
|
9077
|
+
RUNTIME_CONFIG_FILE = import_node_path28.default.join(DEVROUTER_HOME, "workspace-runtime.json");
|
|
8835
9078
|
INACTIVITY_TIMEOUT_PATTERN = /^(?:\d+(?:ms|s|m|h))+$/;
|
|
8836
9079
|
UnsupportedWorkspaceRuntimeError = class extends Error {
|
|
8837
9080
|
};
|
|
@@ -8958,7 +9201,7 @@ function resolveGitCommonDir(repoPath) {
|
|
|
8958
9201
|
if (result.status !== 0 || !output2) {
|
|
8959
9202
|
throw commandError("Could not resolve the Git common directory", repoPath, result.stderr);
|
|
8960
9203
|
}
|
|
8961
|
-
return comparableWorkspacePath(
|
|
9204
|
+
return comparableWorkspacePath(import_node_path29.default.isAbsolute(output2) ? output2 : import_node_path29.default.resolve(repoPath, output2));
|
|
8962
9205
|
}
|
|
8963
9206
|
function resolveGitTopLevel(repoPath) {
|
|
8964
9207
|
const result = (0, import_node_child_process12.spawnSync)("git", ["-C", repoPath, "rev-parse", "--show-toplevel"], {
|
|
@@ -9009,7 +9252,7 @@ function listGitWorktrees(repoPath) {
|
|
|
9009
9252
|
return worktrees;
|
|
9010
9253
|
}
|
|
9011
9254
|
function ownershipDirectory(repoPath) {
|
|
9012
|
-
return
|
|
9255
|
+
return import_node_path29.default.join(resolveGitCommonDir(repoPath), OWNERSHIP_DIR);
|
|
9013
9256
|
}
|
|
9014
9257
|
function validateWorkspace(value, label2) {
|
|
9015
9258
|
if (typeof value !== "string" || wsFromBranch(value) !== value) {
|
|
@@ -9037,7 +9280,7 @@ function validateRecord(value, expectedWorkspace) {
|
|
|
9037
9280
|
`workspace ownership file '${expectedWorkspace}' contains identity '${workspace}'`
|
|
9038
9281
|
);
|
|
9039
9282
|
}
|
|
9040
|
-
if (typeof candidate.worktreePath !== "string" || !
|
|
9283
|
+
if (typeof candidate.worktreePath !== "string" || !import_node_path29.default.isAbsolute(candidate.worktreePath)) {
|
|
9041
9284
|
throw new Error("invalid workspace ownership worktreePath");
|
|
9042
9285
|
}
|
|
9043
9286
|
if (candidate.branch !== null && typeof candidate.branch !== "string") {
|
|
@@ -9055,7 +9298,7 @@ function validateRecord(value, expectedWorkspace) {
|
|
|
9055
9298
|
};
|
|
9056
9299
|
}
|
|
9057
9300
|
function recordPath(repoPath, workspace) {
|
|
9058
|
-
return
|
|
9301
|
+
return import_node_path29.default.join(
|
|
9059
9302
|
ownershipDirectory(repoPath),
|
|
9060
9303
|
`${validateWorkspace(workspace, "workspace")}.json`
|
|
9061
9304
|
);
|
|
@@ -9063,7 +9306,7 @@ function recordPath(repoPath, workspace) {
|
|
|
9063
9306
|
function readRecordFile(filePath, expectedWorkspace) {
|
|
9064
9307
|
let parsed;
|
|
9065
9308
|
try {
|
|
9066
|
-
parsed = JSON.parse(
|
|
9309
|
+
parsed = JSON.parse(import_node_fs22.default.readFileSync(filePath, "utf-8"));
|
|
9067
9310
|
} catch (error) {
|
|
9068
9311
|
if (error instanceof SyntaxError) {
|
|
9069
9312
|
throw new Error(`invalid workspace ownership JSON at '${filePath}'`);
|
|
@@ -9088,7 +9331,7 @@ function listWorkspaceOwnership(repoPath) {
|
|
|
9088
9331
|
function listWorkspaceOwnershipInDirectory(directory) {
|
|
9089
9332
|
let entries;
|
|
9090
9333
|
try {
|
|
9091
|
-
entries =
|
|
9334
|
+
entries = import_node_fs22.default.readdirSync(directory, { withFileTypes: true });
|
|
9092
9335
|
} catch (error) {
|
|
9093
9336
|
if (error.code === "ENOENT") return [];
|
|
9094
9337
|
throw error;
|
|
@@ -9096,14 +9339,14 @@ function listWorkspaceOwnershipInDirectory(directory) {
|
|
|
9096
9339
|
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).sort((left, right) => left.name.localeCompare(right.name)).map((entry) => {
|
|
9097
9340
|
const workspace = entry.name.slice(0, -".json".length);
|
|
9098
9341
|
validateWorkspace(workspace, "filename");
|
|
9099
|
-
return readRecordFile(
|
|
9342
|
+
return readRecordFile(import_node_path29.default.join(directory, entry.name), workspace);
|
|
9100
9343
|
});
|
|
9101
9344
|
}
|
|
9102
9345
|
function writeWorkspaceOwnershipInDirectory(directory, input2) {
|
|
9103
9346
|
const workspace = validateWorkspace(input2.workspace, "workspace");
|
|
9104
9347
|
const devpodId = validateWorkspace(input2.devpodId, "devpodId");
|
|
9105
9348
|
const worktreePath = comparableWorkspacePath(input2.worktreePath);
|
|
9106
|
-
const filePath =
|
|
9349
|
+
const filePath = import_node_path29.default.join(directory, `${workspace}.json`);
|
|
9107
9350
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9108
9351
|
const records = listWorkspaceOwnershipInDirectory(directory);
|
|
9109
9352
|
const existing = records.find((record6) => record6.workspace === workspace);
|
|
@@ -9139,9 +9382,9 @@ function writeWorkspaceOwnershipInDirectory(directory, input2) {
|
|
|
9139
9382
|
return record5;
|
|
9140
9383
|
}
|
|
9141
9384
|
function removeWorkspaceOwnershipInDirectory(directory, workspace) {
|
|
9142
|
-
const filePath =
|
|
9385
|
+
const filePath = import_node_path29.default.join(directory, `${validateWorkspace(workspace, "workspace")}.json`);
|
|
9143
9386
|
try {
|
|
9144
|
-
|
|
9387
|
+
import_node_fs22.default.rmSync(filePath);
|
|
9145
9388
|
return true;
|
|
9146
9389
|
} catch (error) {
|
|
9147
9390
|
if (error.code === "ENOENT") return false;
|
|
@@ -9152,7 +9395,7 @@ function sameOwnershipRecord(left, right) {
|
|
|
9152
9395
|
return left.version === right.version && left.workspace === right.workspace && sameWorkspacePath(left.worktreePath, right.worktreePath) && left.branch === right.branch && left.devpodId === right.devpodId && left.createdAt === right.createdAt && left.updatedAt === right.updatedAt;
|
|
9153
9396
|
}
|
|
9154
9397
|
function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
|
|
9155
|
-
const filePath =
|
|
9398
|
+
const filePath = import_node_path29.default.join(
|
|
9156
9399
|
directory,
|
|
9157
9400
|
`${validateWorkspace(expected.workspace, "workspace")}.json`
|
|
9158
9401
|
);
|
|
@@ -9164,14 +9407,14 @@ function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
|
|
|
9164
9407
|
throw error;
|
|
9165
9408
|
}
|
|
9166
9409
|
if (!sameOwnershipRecord(current, expected)) return "changed";
|
|
9167
|
-
|
|
9410
|
+
import_node_fs22.default.rmSync(filePath);
|
|
9168
9411
|
return "removed";
|
|
9169
9412
|
}
|
|
9170
9413
|
function withWorkspaceOwnershipTransaction(repoPath, operation, options = {}) {
|
|
9171
9414
|
const directory = ownershipDirectory(repoPath);
|
|
9172
|
-
|
|
9415
|
+
import_node_fs22.default.mkdirSync(directory, { recursive: true });
|
|
9173
9416
|
return withFileLockSync(
|
|
9174
|
-
|
|
9417
|
+
import_node_path29.default.join(directory, ".lock"),
|
|
9175
9418
|
{
|
|
9176
9419
|
activity: "workspace ownership transaction",
|
|
9177
9420
|
target: `'${repoPath}'`,
|
|
@@ -9204,7 +9447,7 @@ function persistedWorkspaceOwners(repoPath, worktreePath) {
|
|
|
9204
9447
|
try {
|
|
9205
9448
|
workspace = readPersistedWorkspace(worktree.path);
|
|
9206
9449
|
} catch (error) {
|
|
9207
|
-
if (!
|
|
9450
|
+
if (!import_node_fs22.default.existsSync(worktree.path)) continue;
|
|
9208
9451
|
throw error;
|
|
9209
9452
|
}
|
|
9210
9453
|
if (!workspace) continue;
|
|
@@ -9367,7 +9610,7 @@ function inspectWorkspaceOwnership(record5, worktrees, devpods) {
|
|
|
9367
9610
|
if (worktree?.locked) {
|
|
9368
9611
|
return { ownerStatus: "locked", devpodStatus, worktree };
|
|
9369
9612
|
}
|
|
9370
|
-
if ((!worktree || worktree.prunable) &&
|
|
9613
|
+
if ((!worktree || worktree.prunable) && import_node_fs22.default.existsSync(record5.worktreePath)) {
|
|
9371
9614
|
return { ownerStatus: "conflict", devpodStatus, worktree };
|
|
9372
9615
|
}
|
|
9373
9616
|
if (!worktree || worktree.prunable) {
|
|
@@ -9391,20 +9634,20 @@ function listMissingWorkspaceOwnership(repoPath) {
|
|
|
9391
9634
|
(record5) => inspectWorkspaceOwnership(record5, worktrees, void 0).ownerStatus === "missing"
|
|
9392
9635
|
);
|
|
9393
9636
|
}
|
|
9394
|
-
var import_node_child_process12,
|
|
9637
|
+
var import_node_child_process12, import_node_fs22, import_node_path29, READ_ONLY_GIT_ENV, OWNERSHIP_VERSION, OWNERSHIP_DIR;
|
|
9395
9638
|
var init_workspace_ownership = __esm({
|
|
9396
9639
|
"src/core/workspace-ownership.ts"() {
|
|
9397
9640
|
"use strict";
|
|
9398
9641
|
import_node_child_process12 = require("child_process");
|
|
9399
|
-
|
|
9400
|
-
|
|
9642
|
+
import_node_fs22 = __toESM(require("fs"));
|
|
9643
|
+
import_node_path29 = __toESM(require("path"));
|
|
9401
9644
|
init_atomic_file();
|
|
9402
9645
|
init_devpod_workspaces();
|
|
9403
9646
|
init_file_lock();
|
|
9404
9647
|
init_workspace();
|
|
9405
9648
|
READ_ONLY_GIT_ENV = { ...process.env, GIT_OPTIONAL_LOCKS: "0" };
|
|
9406
9649
|
OWNERSHIP_VERSION = 1;
|
|
9407
|
-
OWNERSHIP_DIR =
|
|
9650
|
+
OWNERSHIP_DIR = import_node_path29.default.join("devrouter", "workspaces");
|
|
9408
9651
|
}
|
|
9409
9652
|
});
|
|
9410
9653
|
|
|
@@ -9653,12 +9896,12 @@ function managedStopRouteReferences(state) {
|
|
|
9653
9896
|
(name) => ["http", "tcp"].map((protocol) => ({ repoPath: state.repoPath, name, protocol }))
|
|
9654
9897
|
);
|
|
9655
9898
|
}
|
|
9656
|
-
var
|
|
9899
|
+
var import_node_os4, import_node_path30, import_node_util8;
|
|
9657
9900
|
var init_managed_stop_recovery = __esm({
|
|
9658
9901
|
"src/core/managed-stop-recovery.ts"() {
|
|
9659
9902
|
"use strict";
|
|
9660
|
-
|
|
9661
|
-
|
|
9903
|
+
import_node_os4 = __toESM(require("os"));
|
|
9904
|
+
import_node_path30 = __toESM(require("path"));
|
|
9662
9905
|
import_node_util8 = require("util");
|
|
9663
9906
|
init_devpod_environment();
|
|
9664
9907
|
init_devpod_registry();
|
|
@@ -9805,8 +10048,8 @@ function tlsSetupCommand(repoPath) {
|
|
|
9805
10048
|
}
|
|
9806
10049
|
function getMkcertRootCAPath(options = {}) {
|
|
9807
10050
|
ensureMkcert();
|
|
9808
|
-
const rootCAPath =
|
|
9809
|
-
if (!
|
|
10051
|
+
const rootCAPath = import_node_path31.default.join(runOrThrow("mkcert", ["-CAROOT"]), "rootCA.pem");
|
|
10052
|
+
if (!import_node_fs23.default.existsSync(rootCAPath)) {
|
|
9810
10053
|
throw new Error(
|
|
9811
10054
|
`mkcert root CA was not found at '${rootCAPath}'. Run: ${tlsSetupCommand(options.repoPath)}`
|
|
9812
10055
|
);
|
|
@@ -9907,11 +10150,11 @@ function compactTLSCertificateHosts(hosts) {
|
|
|
9907
10150
|
);
|
|
9908
10151
|
}
|
|
9909
10152
|
function readCurrentCertificateHosts(options = {}) {
|
|
9910
|
-
if (!
|
|
10153
|
+
if (!import_node_fs23.default.existsSync(CERT_FILE)) {
|
|
9911
10154
|
return [];
|
|
9912
10155
|
}
|
|
9913
10156
|
try {
|
|
9914
|
-
const pem =
|
|
10157
|
+
const pem = import_node_fs23.default.readFileSync(CERT_FILE, "utf-8");
|
|
9915
10158
|
return parseCertificateDnsHosts(pem);
|
|
9916
10159
|
} catch (error) {
|
|
9917
10160
|
if (options.replaceMalformed) {
|
|
@@ -10039,20 +10282,20 @@ Run: ${tlsSetupCommand(options.repoPath)}`
|
|
|
10039
10282
|
);
|
|
10040
10283
|
}
|
|
10041
10284
|
}
|
|
10042
|
-
var import_node_child_process14, import_node_crypto12,
|
|
10285
|
+
var import_node_child_process14, import_node_crypto12, import_node_fs23, import_node_path31, DEFAULT_TLS_CERT_HOSTS, TLS_CERTIFICATE_LOCK_FILE, TLS_CERTIFICATE_LOCK_WAIT_MS, TLS_CERTIFICATE_WRITE_ATTEMPTS;
|
|
10043
10286
|
var init_tls = __esm({
|
|
10044
10287
|
"src/core/tls.ts"() {
|
|
10045
10288
|
"use strict";
|
|
10046
10289
|
import_node_child_process14 = require("child_process");
|
|
10047
10290
|
import_node_crypto12 = require("crypto");
|
|
10048
|
-
|
|
10049
|
-
|
|
10291
|
+
import_node_fs23 = __toESM(require("fs"));
|
|
10292
|
+
import_node_path31 = __toESM(require("path"));
|
|
10050
10293
|
init_docker();
|
|
10051
10294
|
init_file_lock();
|
|
10052
10295
|
init_host_routes();
|
|
10053
10296
|
init_router();
|
|
10054
10297
|
DEFAULT_TLS_CERT_HOSTS = ["localhost", "*.localhost"];
|
|
10055
|
-
TLS_CERTIFICATE_LOCK_FILE =
|
|
10298
|
+
TLS_CERTIFICATE_LOCK_FILE = import_node_path31.default.join(DEVROUTER_HOME, "tls-certificate.lock");
|
|
10056
10299
|
TLS_CERTIFICATE_LOCK_WAIT_MS = 6e4;
|
|
10057
10300
|
TLS_CERTIFICATE_WRITE_ATTEMPTS = 2;
|
|
10058
10301
|
}
|
|
@@ -10133,6 +10376,174 @@ var init_route_publication = __esm({
|
|
|
10133
10376
|
}
|
|
10134
10377
|
});
|
|
10135
10378
|
|
|
10379
|
+
// src/core/traefik-route-health.ts
|
|
10380
|
+
function sleep(ms) {
|
|
10381
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10382
|
+
}
|
|
10383
|
+
function inspectRouterApi(protocol) {
|
|
10384
|
+
const result = (0, import_node_child_process15.spawnSync)(
|
|
10385
|
+
"curl",
|
|
10386
|
+
[
|
|
10387
|
+
"--silent",
|
|
10388
|
+
"--show-error",
|
|
10389
|
+
"--fail",
|
|
10390
|
+
"--max-time",
|
|
10391
|
+
ROUTER_API_REQUEST_TIMEOUT_SECONDS,
|
|
10392
|
+
`${ROUTER_API_BASE}/${protocol}/routers?per_page=${ROUTER_API_PAGE_SIZE}`
|
|
10393
|
+
],
|
|
10394
|
+
{ encoding: "utf-8" }
|
|
10395
|
+
);
|
|
10396
|
+
if (result.status !== 0) {
|
|
10397
|
+
return {
|
|
10398
|
+
ok: false,
|
|
10399
|
+
details: result.stderr?.trim() || `curl exited with status ${result.status ?? "unknown"}`
|
|
10400
|
+
};
|
|
10401
|
+
}
|
|
10402
|
+
let value;
|
|
10403
|
+
try {
|
|
10404
|
+
value = JSON.parse(result.stdout);
|
|
10405
|
+
} catch {
|
|
10406
|
+
return { ok: false, details: `${protocol.toUpperCase()} router API returned invalid JSON` };
|
|
10407
|
+
}
|
|
10408
|
+
if (!Array.isArray(value)) {
|
|
10409
|
+
return { ok: false, details: `${protocol.toUpperCase()} router API returned a non-array` };
|
|
10410
|
+
}
|
|
10411
|
+
if (value.some((item) => !isRecord5(item) || typeof item.name !== "string" || !item.name))
|
|
10412
|
+
return {
|
|
10413
|
+
ok: false,
|
|
10414
|
+
details: `${protocol.toUpperCase()} router API returned malformed entries`
|
|
10415
|
+
};
|
|
10416
|
+
return {
|
|
10417
|
+
ok: true,
|
|
10418
|
+
reachedPageLimit: value.length >= ROUTER_API_PAGE_SIZE,
|
|
10419
|
+
names: new Set(
|
|
10420
|
+
value.flatMap((item) => {
|
|
10421
|
+
if (!item || typeof item !== "object") return [];
|
|
10422
|
+
const name = item.name;
|
|
10423
|
+
return typeof name === "string" ? [name] : [];
|
|
10424
|
+
})
|
|
10425
|
+
)
|
|
10426
|
+
};
|
|
10427
|
+
}
|
|
10428
|
+
function isRecord5(value) {
|
|
10429
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10430
|
+
}
|
|
10431
|
+
function inspectExpectedRoutes(routes, expectation) {
|
|
10432
|
+
const mismatched = [];
|
|
10433
|
+
const failures = [];
|
|
10434
|
+
for (const protocol of ["http", "tcp"]) {
|
|
10435
|
+
const expected = routes.filter((route) => (route.protocol ?? "http") === protocol).map((route) => buildHostRouteRouterName(route.repoPath, route.name));
|
|
10436
|
+
if (expected.length === 0) continue;
|
|
10437
|
+
const result = inspectRouterApi(protocol);
|
|
10438
|
+
if (!result.ok) {
|
|
10439
|
+
failures.push(result.details);
|
|
10440
|
+
mismatched.push(...expected);
|
|
10441
|
+
continue;
|
|
10442
|
+
}
|
|
10443
|
+
const unexpected = expected.filter(
|
|
10444
|
+
(name) => expectation === "loaded" ? !result.names.has(name) : result.names.has(name)
|
|
10445
|
+
);
|
|
10446
|
+
const boundedAbsenceIsUncertain = result.reachedPageLimit && (expectation === "loaded" ? unexpected.length > 0 : unexpected.length === 0);
|
|
10447
|
+
if (boundedAbsenceIsUncertain) {
|
|
10448
|
+
failures.push(
|
|
10449
|
+
`${protocol.toUpperCase()} router API reached the ${ROUTER_API_PAGE_SIZE}-entry safety limit`
|
|
10450
|
+
);
|
|
10451
|
+
}
|
|
10452
|
+
mismatched.push(...unexpected);
|
|
10453
|
+
if (expectation === "removed" && unexpected.length === 0 && result.reachedPageLimit) {
|
|
10454
|
+
mismatched.push(...expected);
|
|
10455
|
+
}
|
|
10456
|
+
}
|
|
10457
|
+
return mismatched.length === 0 ? { ok: true } : {
|
|
10458
|
+
ok: false,
|
|
10459
|
+
routes: mismatched,
|
|
10460
|
+
details: failures.length > 0 ? failures.join("; ") : expectation === "loaded" ? "router names are absent" : "router names are still present"
|
|
10461
|
+
};
|
|
10462
|
+
}
|
|
10463
|
+
async function waitForExpectedRoutes(routes, expectation, timeoutMs, pollIntervalMs) {
|
|
10464
|
+
const deadline = Date.now() + timeoutMs;
|
|
10465
|
+
let result;
|
|
10466
|
+
do {
|
|
10467
|
+
result = inspectExpectedRoutes(routes, expectation);
|
|
10468
|
+
if (result.ok) return result;
|
|
10469
|
+
if (Date.now() < deadline) {
|
|
10470
|
+
await sleep(Math.min(pollIntervalMs, Math.max(1, deadline - Date.now())));
|
|
10471
|
+
}
|
|
10472
|
+
} while (Date.now() < deadline);
|
|
10473
|
+
return result;
|
|
10474
|
+
}
|
|
10475
|
+
async function ensureTraefikRouteExpectation(routes, expectation, options) {
|
|
10476
|
+
if (routes.length === 0) return { restarted: false };
|
|
10477
|
+
const initialTimeoutMs = options.initialTimeoutMs ?? DEFAULT_INITIAL_TIMEOUT_MS;
|
|
10478
|
+
const recoveryTimeoutMs = options.recoveryTimeoutMs ?? DEFAULT_RECOVERY_TIMEOUT_MS;
|
|
10479
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
10480
|
+
const initial = await waitForExpectedRoutes(
|
|
10481
|
+
routes,
|
|
10482
|
+
expectation,
|
|
10483
|
+
initialTimeoutMs,
|
|
10484
|
+
pollIntervalMs
|
|
10485
|
+
);
|
|
10486
|
+
if (initial.ok) return { restarted: false };
|
|
10487
|
+
if (options.allowRestart === false)
|
|
10488
|
+
throw new Error(
|
|
10489
|
+
"Traefik routes did not reload; shared router restart is disabled for this operation."
|
|
10490
|
+
);
|
|
10491
|
+
import_node_fs24.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
10492
|
+
return withFileLock(
|
|
10493
|
+
ROUTER_RELOAD_LOCK_FILE,
|
|
10494
|
+
{
|
|
10495
|
+
activity: "Traefik route reload recovery",
|
|
10496
|
+
waitMs: initialTimeoutMs + recoveryTimeoutMs + 1e4,
|
|
10497
|
+
fair: true,
|
|
10498
|
+
onWait: createStderrWaitReporter("Traefik route reload recovery", "shared router")
|
|
10499
|
+
},
|
|
10500
|
+
async () => {
|
|
10501
|
+
const recheck = await waitForExpectedRoutes(routes, expectation, 0, pollIntervalMs);
|
|
10502
|
+
if (recheck.ok) return { restarted: false };
|
|
10503
|
+
claimLifecycleEffect();
|
|
10504
|
+
restartRouterStack();
|
|
10505
|
+
const recovered = await waitForExpectedRoutes(
|
|
10506
|
+
routes,
|
|
10507
|
+
expectation,
|
|
10508
|
+
recoveryTimeoutMs,
|
|
10509
|
+
pollIntervalMs
|
|
10510
|
+
);
|
|
10511
|
+
if (recovered.ok) return { restarted: true };
|
|
10512
|
+
const action2 = expectation === "loaded" ? "load" : "remove";
|
|
10513
|
+
throw new Error(
|
|
10514
|
+
`Traefik did not ${action2} file-provider routes after one restart: ${recovered.routes.join(", ")} (${recovered.details}). Inspect: devrouter logs --tail 100`
|
|
10515
|
+
);
|
|
10516
|
+
}
|
|
10517
|
+
);
|
|
10518
|
+
}
|
|
10519
|
+
async function ensureTraefikRoutesRemoved(routes, options = {}) {
|
|
10520
|
+
return ensureTraefikRouteExpectation(routes, "removed", options);
|
|
10521
|
+
}
|
|
10522
|
+
function assertTraefikRoutesRemoved(routes) {
|
|
10523
|
+
const result = inspectExpectedRoutes(routes, "removed");
|
|
10524
|
+
if (!result.ok) throw new Error(`Workspace route cessation is not proven: ${result.details}`);
|
|
10525
|
+
}
|
|
10526
|
+
var import_node_child_process15, import_node_fs24, import_node_path32, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, ROUTER_API_REQUEST_TIMEOUT_SECONDS, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
|
|
10527
|
+
var init_traefik_route_health = __esm({
|
|
10528
|
+
"src/core/traefik-route-health.ts"() {
|
|
10529
|
+
"use strict";
|
|
10530
|
+
import_node_child_process15 = require("child_process");
|
|
10531
|
+
import_node_fs24 = __toESM(require("fs"));
|
|
10532
|
+
import_node_path32 = __toESM(require("path"));
|
|
10533
|
+
init_file_lock();
|
|
10534
|
+
init_host_routes();
|
|
10535
|
+
init_reliability_context();
|
|
10536
|
+
init_router();
|
|
10537
|
+
ROUTER_API_BASE = "http://127.0.0.1:8080/api";
|
|
10538
|
+
ROUTER_RELOAD_LOCK_FILE = import_node_path32.default.join(DEVROUTER_HOME, "router-reload.lock");
|
|
10539
|
+
ROUTER_API_REQUEST_TIMEOUT_SECONDS = "2";
|
|
10540
|
+
DEFAULT_INITIAL_TIMEOUT_MS = 3e3;
|
|
10541
|
+
DEFAULT_RECOVERY_TIMEOUT_MS = 1e4;
|
|
10542
|
+
DEFAULT_POLL_INTERVAL_MS = 250;
|
|
10543
|
+
ROUTER_API_PAGE_SIZE = 1e3;
|
|
10544
|
+
}
|
|
10545
|
+
});
|
|
10546
|
+
|
|
10136
10547
|
// src/core/managed-devsy-stop.ts
|
|
10137
10548
|
function sameSet(left, right) {
|
|
10138
10549
|
return JSON.stringify([...left].sort()) === JSON.stringify([...right].sort());
|
|
@@ -10145,10 +10556,92 @@ function containerIdentity(container) {
|
|
|
10145
10556
|
mounts: container.mounts.map((mount) => JSON.stringify(mount)).sort()
|
|
10146
10557
|
});
|
|
10147
10558
|
}
|
|
10559
|
+
function proveInitialManagedDevsyAbsence(repoPath, expectedId) {
|
|
10560
|
+
if (!isLinkedWorktree(repoPath)) return void 0;
|
|
10561
|
+
const workspace = resolveWorktreeWorkspace(repoPath);
|
|
10562
|
+
if (!workspace || readManagedRuntimeState(repoPath, workspace)) return void 0;
|
|
10563
|
+
if (!import_node_fs25.default.existsSync(import_node_path33.default.join(repoPath, ".devrouter.yml"))) return void 0;
|
|
10564
|
+
const runtime = loadRuntimeConfig(repoPath, workspace);
|
|
10565
|
+
if (!runtime.config.managedRuntime) return void 0;
|
|
10566
|
+
const routes = proxyAppsFromConfig(runtime.config).flatMap(
|
|
10567
|
+
(app) => ["http", "tcp"].map((protocol) => ({ repoPath, name: app.name, protocol }))
|
|
10568
|
+
);
|
|
10569
|
+
const record5 = readWorkspaceOwnership(repoPath, workspace);
|
|
10570
|
+
if (!record5 || expectedId !== void 0 && record5.devpodId !== expectedId || record5.workspace !== workspace || !sameWorkspacePath(record5.worktreePath, repoPath))
|
|
10571
|
+
throw new Error("Initial managed stop requires exact linked ownership.");
|
|
10572
|
+
const devsyId = record5.devpodId;
|
|
10573
|
+
const ownership = inspectDevsyWorkspaceOwnership(listDevsyWorkspaces(), devsyId, repoPath);
|
|
10574
|
+
if (ownership.status === "conflict") throw new Error(ownership.reason);
|
|
10575
|
+
if (ownership.status !== "absent") return void 0;
|
|
10576
|
+
const gitCommonDir = resolveGitCommonDir(repoPath);
|
|
10577
|
+
const endpoint = resolveManagedStopEndpoint();
|
|
10578
|
+
if (!supportsManagedStopBaseline(endpoint))
|
|
10579
|
+
throw new Error("Initial managed stop requires a local Docker endpoint.");
|
|
10580
|
+
const daemon = inspectManagedStopDaemon(endpoint);
|
|
10581
|
+
const legacyHome = devpodRegistryRoot();
|
|
10582
|
+
const readLegacy = () => listDevpodWorkspacesRaw({ readLocalWhenMissing: true }).map(({ id: id5, source }) => ({ id: id5, source: { localFolder: source.localFolder } })).sort(
|
|
10583
|
+
(a, b) => a.id.localeCompare(b.id) || a.source.localFolder.localeCompare(b.source.localFolder)
|
|
10584
|
+
);
|
|
10585
|
+
const legacyWorkspaces = readLegacy();
|
|
10586
|
+
const observe = () => {
|
|
10587
|
+
resetWorkspaceRuntimeCaches();
|
|
10588
|
+
if (resolveWorkspaceRuntimeOrDefault(repoPath) !== "devsy" || !isLinkedWorktree(repoPath) || resolveWorktreeWorkspace(repoPath) !== workspace || resolveGitCommonDir(repoPath) !== gitCommonDir || !(0, import_node_util9.isDeepStrictEqual)(readWorkspaceOwnership(repoPath, workspace), record5) || inspectWorkspaceOwnership(record5, listGitWorktrees(repoPath), void 0).ownerStatus !== "present" || readManagedRuntimeState(repoPath, workspace))
|
|
10589
|
+
throw new Error("Initial managed stop ownership or retained state changed.");
|
|
10590
|
+
if (devpodRegistryRoot() !== legacyHome || !(0, import_node_util9.isDeepStrictEqual)(readLegacy(), legacyWorkspaces))
|
|
10591
|
+
throw new Error("Initial managed stop legacy registry evidence changed.");
|
|
10592
|
+
if (inspectDevsyWorkspaceOwnership(listDevsyWorkspaces(), devsyId, repoPath).status !== "absent" || legacyWorkspaces.some(
|
|
10593
|
+
(entry) => entry.id === devsyId || entry.source.localFolder !== "" && sameWorkspacePath(entry.source.localFolder, repoPath)
|
|
10594
|
+
))
|
|
10595
|
+
throw new Error("Initial managed stop requires both provider registrations absent.");
|
|
10596
|
+
if (!inspectDevsyRuntimeAbsence(devsyId))
|
|
10597
|
+
throw new Error("Initial managed stop requires positive runtime not-found.");
|
|
10598
|
+
if (resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
10599
|
+
throw new Error("Initial managed stop Docker identity changed.");
|
|
10600
|
+
if (inspectProviderRunnerContainers(endpoint, devsyId).length)
|
|
10601
|
+
throw new Error("Initial managed stop observed a remaining provider runner.");
|
|
10602
|
+
assertManagedStopCheckoutAbsent(endpoint, repoPath);
|
|
10603
|
+
if (listHostRouteState().some((route) => sameWorkspacePath(route.repoPath, repoPath)))
|
|
10604
|
+
throw new Error("Initial managed stop observed remaining workspace routes.");
|
|
10605
|
+
assertTraefikRoutesRemoved(routes);
|
|
10606
|
+
if (resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
10607
|
+
throw new Error("Initial managed stop Docker identity changed.");
|
|
10608
|
+
};
|
|
10609
|
+
observe();
|
|
10610
|
+
observe();
|
|
10611
|
+
return {
|
|
10612
|
+
workspace,
|
|
10613
|
+
record: record5,
|
|
10614
|
+
gitCommonDir,
|
|
10615
|
+
endpoint,
|
|
10616
|
+
daemon,
|
|
10617
|
+
routes,
|
|
10618
|
+
legacyHome,
|
|
10619
|
+
legacyWorkspaces
|
|
10620
|
+
};
|
|
10621
|
+
}
|
|
10148
10622
|
function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
10149
|
-
if (!
|
|
10623
|
+
if (!import_node_fs25.default.existsSync(import_node_path33.default.join(repoPath, ".devrouter.yml"))) return false;
|
|
10624
|
+
if (!loadRepoConfig(repoPath).managedRuntime) return false;
|
|
10150
10625
|
const linked = isLinkedWorktree(repoPath);
|
|
10151
10626
|
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
10627
|
+
const identity2 = { repoPath, workspace: workspace ?? null, provider: "devsy" };
|
|
10628
|
+
const readAuthority = () => {
|
|
10629
|
+
const record5 = readReliabilityOperation(identity2);
|
|
10630
|
+
const operation = record5?.state.operation;
|
|
10631
|
+
const entries = record5?.state.operationHistory.filter((entry2) => entry2.id === operation?.id);
|
|
10632
|
+
const entry = entries?.[0];
|
|
10633
|
+
if (!record5 || !(0, import_node_util9.isDeepStrictEqual)(record5.identity, identity2) || record5.worker || !operation || operation.kind !== "ensure" || !operation.drained || !["COMPLETED", "INTERRUPTED", "COMPLETION_UNKNOWN"].includes(operation.status) || record5.state.phase !== "stopping" || record5.state.desired !== "stopped-by-user" || entries?.length !== 1 || !entry || entry.kind !== "ensure" || !entry.profile || !entry.drained || entry.status !== operation.status)
|
|
10634
|
+
throw new Error("Initial managed stop requires the drained ensure's recorded profile.");
|
|
10635
|
+
return {
|
|
10636
|
+
identity: record5.identity,
|
|
10637
|
+
fence: reliabilityFence(record5.state),
|
|
10638
|
+
operation,
|
|
10639
|
+
entry
|
|
10640
|
+
};
|
|
10641
|
+
};
|
|
10642
|
+
const authority = readAuthority();
|
|
10643
|
+
const gitCommonDir = linked ? resolveGitCommonDir(repoPath) : void 0;
|
|
10644
|
+
const workspaceEnv = workspace && gitCommonDir ? { token: workspace, gitCommonDir } : void 0;
|
|
10152
10645
|
const registration2 = () => {
|
|
10153
10646
|
resetWorkspaceRuntimeCaches();
|
|
10154
10647
|
if (resolveWorkspaceRuntimeOrDefault(repoPath) !== "devsy")
|
|
@@ -10158,14 +10651,20 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10158
10651
|
throw new Error("Initial managed stop requires one exact Devsy registration.");
|
|
10159
10652
|
if (linked) {
|
|
10160
10653
|
const record5 = workspace ? readWorkspaceOwnership(repoPath, workspace) : void 0;
|
|
10161
|
-
if (!record5 || record5.devpodId !== devsyId || !sameWorkspacePath(record5.worktreePath, repoPath) || resolveWorktreeWorkspace(repoPath) !== workspace)
|
|
10654
|
+
if (!record5 || record5.devpodId !== devsyId || !sameWorkspacePath(record5.worktreePath, repoPath) || resolveWorktreeWorkspace(repoPath) !== workspace || resolveGitCommonDir(repoPath) !== gitCommonDir || inspectWorkspaceOwnership(record5, listGitWorktrees(repoPath), void 0).ownerStatus !== "present")
|
|
10162
10655
|
throw new Error("Initial managed stop workspace ownership changed.");
|
|
10163
10656
|
}
|
|
10164
10657
|
return owner2.workspace;
|
|
10165
10658
|
};
|
|
10166
|
-
const runtime = loadRuntimeConfig(repoPath, workspace ?? "");
|
|
10167
|
-
if (!runtime.config.managedRuntime
|
|
10659
|
+
const runtime = loadRuntimeConfig(repoPath, workspace ?? "", authority.entry.profile);
|
|
10660
|
+
if (!runtime.config.managedRuntime || runtime.profile !== authority.entry.profile)
|
|
10661
|
+
throw new Error("Initial managed stop requires the recorded managed profile.");
|
|
10168
10662
|
const owner = registration2();
|
|
10663
|
+
proveLocalDockerSelection(owner);
|
|
10664
|
+
const endpoint = resolveManagedStopEndpoint();
|
|
10665
|
+
if (!supportsManagedStopBaseline(endpoint))
|
|
10666
|
+
throw new Error("Initial managed stop requires a pinned local Docker endpoint.");
|
|
10667
|
+
const daemon = inspectManagedStopDaemon(endpoint);
|
|
10169
10668
|
if (!owner.context || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(owner.context) || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(devsyId))
|
|
10170
10669
|
throw new Error("Initial managed stop requires exact provider context.");
|
|
10171
10670
|
const plan = inspectManagedDevcontainerConfig({
|
|
@@ -10174,8 +10673,8 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10174
10673
|
profile: runtime.resolvedProfile,
|
|
10175
10674
|
linked
|
|
10176
10675
|
});
|
|
10177
|
-
const providerRoot =
|
|
10178
|
-
const featureDirectory =
|
|
10676
|
+
const providerRoot = import_node_path33.default.resolve(process.env.DEVSY_HOME || import_node_path33.default.join(import_node_os5.default.homedir(), ".devsy"));
|
|
10677
|
+
const featureDirectory = import_node_path33.default.join(
|
|
10179
10678
|
providerRoot,
|
|
10180
10679
|
"contexts",
|
|
10181
10680
|
owner.context,
|
|
@@ -10185,27 +10684,51 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10185
10684
|
".docker-compose"
|
|
10186
10685
|
);
|
|
10187
10686
|
const observe = () => {
|
|
10188
|
-
if (!(0, import_node_util9.isDeepStrictEqual)(registration2(), owner) || readManagedRuntimeState(repoPath, workspace))
|
|
10189
|
-
throw new Error("Initial managed stop
|
|
10687
|
+
if (!(0, import_node_util9.isDeepStrictEqual)(readAuthority(), authority) || !(0, import_node_util9.isDeepStrictEqual)(registration2(), owner) || readManagedRuntimeState(repoPath, workspace) || resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
10688
|
+
throw new Error("Initial managed stop authority or runtime state changed.");
|
|
10689
|
+
proveLocalDockerSelection(owner);
|
|
10690
|
+
if (listDevpodWorkspacesRaw({ readLocalWhenMissing: true }).some(
|
|
10691
|
+
(entry) => entry.id === devsyId || entry.source.localFolder !== "" && sameWorkspacePath(entry.source.localFolder, repoPath)
|
|
10692
|
+
))
|
|
10693
|
+
throw new Error("Initial managed stop found conflicting provider ownership.");
|
|
10694
|
+
const currentRuntime = loadRuntimeConfig(repoPath, workspace ?? "", authority.entry.profile);
|
|
10695
|
+
const currentPlan = inspectManagedDevcontainerConfig({
|
|
10696
|
+
repoPath,
|
|
10697
|
+
config: currentRuntime.config,
|
|
10698
|
+
profile: currentRuntime.resolvedProfile,
|
|
10699
|
+
linked
|
|
10700
|
+
});
|
|
10701
|
+
if (currentRuntime.profile !== authority.entry.profile || !(0, import_node_util9.isDeepStrictEqual)(currentRuntime.resolvedProfile, runtime.resolvedProfile) || !(0, import_node_util9.isDeepStrictEqual)(currentPlan, plan) || inspectManagedDevcontainerGeneratedConfig(currentPlan).status !== "valid")
|
|
10702
|
+
throw new Error("Initial managed stop configuration changed.");
|
|
10190
10703
|
const matches = inspectWorkspaceContainers().filter(
|
|
10191
10704
|
(c) => sameWorkspacePath(
|
|
10192
10705
|
c.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
10193
10706
|
plan.composeDirectory
|
|
10194
10707
|
)
|
|
10195
10708
|
);
|
|
10196
|
-
if (!matches.length)
|
|
10709
|
+
if (!matches.length)
|
|
10710
|
+
throw new Error("Initial managed stop requires its complete selected population.");
|
|
10197
10711
|
const projects = new Set(matches.map((c) => c.labels["com.docker.compose.project"]));
|
|
10198
10712
|
if (projects.size !== 1 || !matches[0]?.labels["com.docker.compose.project"])
|
|
10199
10713
|
throw new Error("Initial managed stop requires one exact Compose project.");
|
|
10200
10714
|
const population = inspectManagedStopContainers(
|
|
10201
|
-
matches[0].labels["com.docker.compose.project"]
|
|
10715
|
+
matches[0].labels["com.docker.compose.project"],
|
|
10716
|
+
endpoint
|
|
10202
10717
|
);
|
|
10203
10718
|
if (!sameSet(
|
|
10719
|
+
inspectManagedStopWorkspaceIds(endpoint, plan.composeDirectory),
|
|
10720
|
+
population.map((c) => c.id)
|
|
10721
|
+
) || !sameSet(
|
|
10204
10722
|
matches.map((c) => c.id),
|
|
10205
10723
|
population.map((c) => c.id)
|
|
10206
10724
|
))
|
|
10207
10725
|
throw new Error("Initial managed stop population changed.");
|
|
10208
|
-
|
|
10726
|
+
if (!sameSet(
|
|
10727
|
+
population.map((c) => c.labels["com.docker.compose.service"] ?? ""),
|
|
10728
|
+
plan.desiredServices
|
|
10729
|
+
))
|
|
10730
|
+
throw new Error("Initial managed stop requires its complete selected population.");
|
|
10731
|
+
const primary = proveManagedComposePopulation({
|
|
10209
10732
|
plan,
|
|
10210
10733
|
repoPath,
|
|
10211
10734
|
composeProject: matches[0].labels["com.docker.compose.project"],
|
|
@@ -10213,9 +10736,23 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10213
10736
|
featureDirectory,
|
|
10214
10737
|
containers: population
|
|
10215
10738
|
});
|
|
10739
|
+
const uidBytes = Buffer.byteLength(owner.uid ?? "");
|
|
10740
|
+
const runnerId = (uidBytes === 16 || uidBytes === 40 ? owner.uid : void 0) ?? devsyId;
|
|
10741
|
+
if (owner.source.container && owner.source.container !== primary.id || !owner.source.container && inspectManagedStopRunnerId(endpoint, primary.id) !== runnerId)
|
|
10742
|
+
throw new Error("Initial managed stop provider-to-container binding changed.");
|
|
10743
|
+
if (!owner.source.container && !sameSet(inspectProviderRunnerContainers(endpoint, runnerId), [primary.id]))
|
|
10744
|
+
throw new Error("Initial managed stop provider runner population changed.");
|
|
10745
|
+
assertManagedContainerConfigUnchanged({
|
|
10746
|
+
plan,
|
|
10747
|
+
containers: population,
|
|
10748
|
+
workspace: workspaceEnv
|
|
10749
|
+
});
|
|
10750
|
+
if (!(0, import_node_util9.isDeepStrictEqual)(readAuthority(), authority) || !(0, import_node_util9.isDeepStrictEqual)(registration2(), owner) || resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
10751
|
+
throw new Error("Initial managed stop authority changed during inspection.");
|
|
10216
10752
|
return population;
|
|
10217
10753
|
};
|
|
10218
10754
|
const initial = observe();
|
|
10755
|
+
const stopped = new Set(initial.filter((c) => !c.state.Running).map((c) => c.id));
|
|
10219
10756
|
const stable = () => {
|
|
10220
10757
|
const current = observe();
|
|
10221
10758
|
if (!sameSet(
|
|
@@ -10223,19 +10760,21 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10223
10760
|
current.map((c) => c.id)
|
|
10224
10761
|
) || current.some((c) => {
|
|
10225
10762
|
const prior = initial.find((p) => p.id === c.id);
|
|
10226
|
-
return !prior || containerIdentity(prior) !== containerIdentity(c) ||
|
|
10763
|
+
return !prior || containerIdentity(prior) !== containerIdentity(c) || stopped.has(c.id) && c.state.Running;
|
|
10227
10764
|
}))
|
|
10228
10765
|
throw new Error("Initial managed stop population or identity changed.");
|
|
10766
|
+
for (const c of current) if (!c.state.Running) stopped.add(c.id);
|
|
10229
10767
|
return current;
|
|
10230
10768
|
};
|
|
10231
10769
|
stable();
|
|
10232
|
-
if (!initial.length) return false;
|
|
10233
10770
|
for (const container of initial) {
|
|
10234
10771
|
const current = stable().find((c) => c.id === container.id);
|
|
10235
|
-
if (current?.state.Running)
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10772
|
+
if (current?.state.Running) {
|
|
10773
|
+
claimLifecycleEffect();
|
|
10774
|
+
stopPinnedManagedContainer(endpoint, current.id);
|
|
10775
|
+
if (stable().find((c) => c.id === current.id)?.state.Running)
|
|
10776
|
+
throw new Error("Initial managed stop container cessation is not proven.");
|
|
10777
|
+
}
|
|
10239
10778
|
}
|
|
10240
10779
|
if (stable().some((c) => c.state.Running))
|
|
10241
10780
|
throw new Error("Initial managed stop left a workload running.");
|
|
@@ -10246,7 +10785,10 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10246
10785
|
const linked = isLinkedWorktree(repoPath);
|
|
10247
10786
|
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
10248
10787
|
const retainedState = readManagedRuntimeState(repoPath, workspace);
|
|
10249
|
-
if (!retainedState)
|
|
10788
|
+
if (!retainedState) {
|
|
10789
|
+
if (proveInitialManagedDevsyAbsence(repoPath, devsyId)) return "proven-absent";
|
|
10790
|
+
return stopInitialManagedDevsyWorkspace(repoPath, devsyId);
|
|
10791
|
+
}
|
|
10250
10792
|
const state = retainedState;
|
|
10251
10793
|
if (state.devpodId !== devsyId || linked && !workspace) {
|
|
10252
10794
|
throw new Error("Managed stop requires the exact retained workspace identity.");
|
|
@@ -10307,7 +10849,7 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10307
10849
|
const owned = inspectWorkspaceContainers().filter(
|
|
10308
10850
|
(container) => sameWorkspacePath(
|
|
10309
10851
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
10310
|
-
|
|
10852
|
+
import_node_path33.default.join(repoPath, ".devcontainer")
|
|
10311
10853
|
)
|
|
10312
10854
|
);
|
|
10313
10855
|
if (owned.length !== 0) {
|
|
@@ -10336,8 +10878,8 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10336
10878
|
if (!context || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(context) || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(devsyId)) {
|
|
10337
10879
|
throw new Error("Managed stop requires a valid exact Devsy context and workspace ID.");
|
|
10338
10880
|
}
|
|
10339
|
-
const devsyRoot =
|
|
10340
|
-
const featureDirectory =
|
|
10881
|
+
const devsyRoot = import_node_path33.default.resolve(process.env.DEVSY_HOME || import_node_path33.default.join(import_node_os5.default.homedir(), ".devsy"));
|
|
10882
|
+
const featureDirectory = import_node_path33.default.join(
|
|
10341
10883
|
devsyRoot,
|
|
10342
10884
|
"contexts",
|
|
10343
10885
|
context,
|
|
@@ -10441,23 +10983,29 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10441
10983
|
if (providerFailed) throw providerError;
|
|
10442
10984
|
return true;
|
|
10443
10985
|
}
|
|
10444
|
-
var
|
|
10986
|
+
var import_node_fs25, import_node_os5, import_node_path33, import_node_util9;
|
|
10445
10987
|
var init_managed_devsy_stop = __esm({
|
|
10446
10988
|
"src/core/managed-devsy-stop.ts"() {
|
|
10447
10989
|
"use strict";
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10990
|
+
import_node_fs25 = __toESM(require("fs"));
|
|
10991
|
+
import_node_os5 = __toESM(require("os"));
|
|
10992
|
+
import_node_path33 = __toESM(require("path"));
|
|
10451
10993
|
import_node_util9 = require("util");
|
|
10452
10994
|
init_devcontainer_profile();
|
|
10453
10995
|
init_devpod_environment();
|
|
10454
10996
|
init_devpod_registry();
|
|
10997
|
+
init_devsy_exec_proof();
|
|
10455
10998
|
init_devsy_workspaces();
|
|
10999
|
+
init_host_routes();
|
|
10456
11000
|
init_managed_compose_population();
|
|
10457
11001
|
init_managed_runtime_state();
|
|
10458
11002
|
init_managed_stop_recovery();
|
|
11003
|
+
init_reliability_context();
|
|
11004
|
+
init_reliability_contract();
|
|
11005
|
+
init_reliability_operation_store();
|
|
10459
11006
|
init_repo_config();
|
|
10460
11007
|
init_route_publication();
|
|
11008
|
+
init_traefik_route_health();
|
|
10461
11009
|
init_workspace();
|
|
10462
11010
|
init_workspace_ownership();
|
|
10463
11011
|
init_workspace_runtime();
|
|
@@ -11003,7 +11551,7 @@ function hasControlCharacter2(value) {
|
|
|
11003
11551
|
function readPolicyFile(filePath) {
|
|
11004
11552
|
let initialStats;
|
|
11005
11553
|
try {
|
|
11006
|
-
initialStats =
|
|
11554
|
+
initialStats = import_node_fs26.default.lstatSync(filePath);
|
|
11007
11555
|
} catch (error) {
|
|
11008
11556
|
if (isErrno(error, "ENOENT")) return { status: "absent" };
|
|
11009
11557
|
return { status: "invalid", error: "network policy could not be read" };
|
|
@@ -11016,16 +11564,16 @@ function readPolicyFile(filePath) {
|
|
|
11016
11564
|
}
|
|
11017
11565
|
let fileDescriptor;
|
|
11018
11566
|
try {
|
|
11019
|
-
fileDescriptor =
|
|
11567
|
+
fileDescriptor = import_node_fs26.default.openSync(
|
|
11020
11568
|
filePath,
|
|
11021
|
-
|
|
11569
|
+
import_node_fs26.default.constants.O_RDONLY | import_node_fs26.default.constants.O_NONBLOCK | (import_node_fs26.default.constants.O_NOFOLLOW ?? 0)
|
|
11022
11570
|
);
|
|
11023
11571
|
} catch (error) {
|
|
11024
11572
|
if (isErrno(error, "ENOENT")) return { status: "absent" };
|
|
11025
11573
|
return { status: "invalid", error: "network policy could not be read" };
|
|
11026
11574
|
}
|
|
11027
11575
|
try {
|
|
11028
|
-
const openedStats =
|
|
11576
|
+
const openedStats = import_node_fs26.default.fstatSync(fileDescriptor);
|
|
11029
11577
|
if (!openedStats.isFile()) {
|
|
11030
11578
|
return { status: "invalid", error: "network policy must be a regular file" };
|
|
11031
11579
|
}
|
|
@@ -11038,12 +11586,12 @@ function readPolicyFile(filePath) {
|
|
|
11038
11586
|
const buffer = Buffer.alloc(openedStats.size);
|
|
11039
11587
|
let offset = 0;
|
|
11040
11588
|
while (offset < buffer.length) {
|
|
11041
|
-
const bytesRead =
|
|
11589
|
+
const bytesRead = import_node_fs26.default.readSync(fileDescriptor, buffer, offset, buffer.length - offset, null);
|
|
11042
11590
|
if (bytesRead === 0)
|
|
11043
11591
|
return { status: "invalid", error: "network policy changed during read" };
|
|
11044
11592
|
offset += bytesRead;
|
|
11045
11593
|
}
|
|
11046
|
-
const finalStats =
|
|
11594
|
+
const finalStats = import_node_fs26.default.fstatSync(fileDescriptor);
|
|
11047
11595
|
if (!finalStats.isFile() || finalStats.size !== openedStats.size) {
|
|
11048
11596
|
return { status: "invalid", error: "network policy changed during read" };
|
|
11049
11597
|
}
|
|
@@ -11052,7 +11600,7 @@ function readPolicyFile(filePath) {
|
|
|
11052
11600
|
return { status: "invalid", error: "network policy could not be read" };
|
|
11053
11601
|
} finally {
|
|
11054
11602
|
try {
|
|
11055
|
-
|
|
11603
|
+
import_node_fs26.default.closeSync(fileDescriptor);
|
|
11056
11604
|
} catch {
|
|
11057
11605
|
}
|
|
11058
11606
|
}
|
|
@@ -11069,16 +11617,16 @@ function assertDisjoint(cidrs, message) {
|
|
|
11069
11617
|
function isErrno(error, code) {
|
|
11070
11618
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
11071
11619
|
}
|
|
11072
|
-
var
|
|
11620
|
+
var import_node_fs26, import_node_path34, NETWORK_POLICY_VERSION, NETWORK_POLICY_FILE, DEFAULT_NETWORK_POLICY_ENDPOINT_RESERVE, NETWORK_POLICY_PREFIX_LENGTHS, MAX_NETWORK_POLICY_FILE_BYTES, MAX_NETWORK_POLICY_ARRAY_ENTRIES, MAX_NETWORK_POLICY_DAEMON_ID_LENGTH, NetworkPolicyValidationError, NETWORK_POLICY_KEYS, PRIVATE_IPV4_RANGES;
|
|
11073
11621
|
var init_network_policy = __esm({
|
|
11074
11622
|
"src/core/network-policy.ts"() {
|
|
11075
11623
|
"use strict";
|
|
11076
|
-
|
|
11077
|
-
|
|
11624
|
+
import_node_fs26 = __toESM(require("fs"));
|
|
11625
|
+
import_node_path34 = __toESM(require("path"));
|
|
11078
11626
|
init_network_capacity();
|
|
11079
11627
|
init_router();
|
|
11080
11628
|
NETWORK_POLICY_VERSION = 1;
|
|
11081
|
-
NETWORK_POLICY_FILE =
|
|
11629
|
+
NETWORK_POLICY_FILE = import_node_path34.default.join(DEVROUTER_HOME, "network-policy.json");
|
|
11082
11630
|
DEFAULT_NETWORK_POLICY_ENDPOINT_RESERVE = 8;
|
|
11083
11631
|
NETWORK_POLICY_PREFIX_LENGTHS = [24, 25, 26];
|
|
11084
11632
|
MAX_NETWORK_POLICY_FILE_BYTES = 64 * 1024;
|
|
@@ -11179,27 +11727,27 @@ function assertExactKeys(value, keys2) {
|
|
|
11179
11727
|
function readDocument(store) {
|
|
11180
11728
|
let descriptor;
|
|
11181
11729
|
try {
|
|
11182
|
-
descriptor =
|
|
11730
|
+
descriptor = import_node_fs27.default.openSync(
|
|
11183
11731
|
store.filePath,
|
|
11184
|
-
|
|
11732
|
+
import_node_fs27.default.constants.O_RDONLY | import_node_fs27.default.constants.O_NONBLOCK | (import_node_fs27.default.constants.O_NOFOLLOW ?? 0)
|
|
11185
11733
|
);
|
|
11186
11734
|
} catch (error) {
|
|
11187
11735
|
if (isErrno2(error, "ENOENT")) return void 0;
|
|
11188
11736
|
throw claimError("invalid-state", "network claim state could not be read");
|
|
11189
11737
|
}
|
|
11190
11738
|
try {
|
|
11191
|
-
const stat =
|
|
11739
|
+
const stat = import_node_fs27.default.fstatSync(descriptor);
|
|
11192
11740
|
if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES || (stat.mode & 63) !== 0) {
|
|
11193
11741
|
throw claimError("invalid-state", "network claim state is not a bounded private file");
|
|
11194
11742
|
}
|
|
11195
11743
|
const buffer = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
|
|
11196
11744
|
let count = 0;
|
|
11197
11745
|
while (count < buffer.length) {
|
|
11198
|
-
const bytesRead =
|
|
11746
|
+
const bytesRead = import_node_fs27.default.readSync(descriptor, buffer, count, buffer.length - count, null);
|
|
11199
11747
|
if (bytesRead === 0) break;
|
|
11200
11748
|
count += bytesRead;
|
|
11201
11749
|
}
|
|
11202
|
-
const finalStat =
|
|
11750
|
+
const finalStat = import_node_fs27.default.fstatSync(descriptor);
|
|
11203
11751
|
if (count > NETWORK_CLAIMS_MAX_FILE_BYTES || finalStat.size !== stat.size) {
|
|
11204
11752
|
throw claimError("invalid-state", "network claim state changed during read");
|
|
11205
11753
|
}
|
|
@@ -11211,7 +11759,7 @@ function readDocument(store) {
|
|
|
11211
11759
|
}
|
|
11212
11760
|
return validateDocument(parsed, store.daemonId);
|
|
11213
11761
|
} finally {
|
|
11214
|
-
|
|
11762
|
+
import_node_fs27.default.closeSync(descriptor);
|
|
11215
11763
|
}
|
|
11216
11764
|
}
|
|
11217
11765
|
function validateDocument(value, daemonId) {
|
|
@@ -11278,16 +11826,16 @@ function createStore(daemonId, options) {
|
|
|
11278
11826
|
return {
|
|
11279
11827
|
root,
|
|
11280
11828
|
daemonId,
|
|
11281
|
-
filePath:
|
|
11282
|
-
lockPath:
|
|
11829
|
+
filePath: import_node_path35.default.join(root, `${key}.json`),
|
|
11830
|
+
lockPath: import_node_path35.default.join(root, `${key}.lock`),
|
|
11283
11831
|
writeAtomic: options.writeAtomic ?? writeFileAtomically
|
|
11284
11832
|
};
|
|
11285
11833
|
}
|
|
11286
11834
|
function validateRoot(root) {
|
|
11287
|
-
if (typeof root !== "string" || !
|
|
11835
|
+
if (typeof root !== "string" || !import_node_path35.default.isAbsolute(root) || root.includes("\0")) {
|
|
11288
11836
|
throw claimError("invalid-input", "network claim root is invalid");
|
|
11289
11837
|
}
|
|
11290
|
-
const resolved =
|
|
11838
|
+
const resolved = import_node_path35.default.resolve(root);
|
|
11291
11839
|
if (resolved !== root) throw claimError("invalid-input", "network claim root is invalid");
|
|
11292
11840
|
return resolved;
|
|
11293
11841
|
}
|
|
@@ -11315,20 +11863,20 @@ function isErrno2(error, code) {
|
|
|
11315
11863
|
function claimError(code, message) {
|
|
11316
11864
|
return new NetworkClaimError(code, message);
|
|
11317
11865
|
}
|
|
11318
|
-
var import_node_crypto13,
|
|
11866
|
+
var import_node_crypto13, import_node_fs27, import_node_path35, NETWORK_CLAIMS_VERSION, NETWORK_CLAIMS_ROOT, NETWORK_CLAIMS_MAX_RECORDS, NETWORK_CLAIMS_MAX_FILE_BYTES, NetworkClaimError, CLAIM_CORE_KEYS, CLAIM_KEYS, FENCE_KEYS, DOCUMENT_KEYS, MAX_CLAIM_TEXT, MAX_ENDPOINT_TEXT;
|
|
11319
11867
|
var init_network_claims = __esm({
|
|
11320
11868
|
"src/core/network-claims.ts"() {
|
|
11321
11869
|
"use strict";
|
|
11322
11870
|
import_node_crypto13 = require("crypto");
|
|
11323
|
-
|
|
11324
|
-
|
|
11871
|
+
import_node_fs27 = __toESM(require("fs"));
|
|
11872
|
+
import_node_path35 = __toESM(require("path"));
|
|
11325
11873
|
init_atomic_file();
|
|
11326
11874
|
init_file_lock();
|
|
11327
11875
|
init_network_capacity();
|
|
11328
11876
|
init_network_policy();
|
|
11329
11877
|
init_router();
|
|
11330
11878
|
NETWORK_CLAIMS_VERSION = 1;
|
|
11331
|
-
NETWORK_CLAIMS_ROOT =
|
|
11879
|
+
NETWORK_CLAIMS_ROOT = import_node_path35.default.join(DEVROUTER_HOME, "networks");
|
|
11332
11880
|
NETWORK_CLAIMS_MAX_RECORDS = 256;
|
|
11333
11881
|
NETWORK_CLAIMS_MAX_FILE_BYTES = 512 * 1024;
|
|
11334
11882
|
NetworkClaimError = class extends Error {
|
|
@@ -11370,14 +11918,14 @@ var init_network_claims = __esm({
|
|
|
11370
11918
|
|
|
11371
11919
|
// src/core/network-claim-lookup.ts
|
|
11372
11920
|
function networkOwnerKey(repoPath) {
|
|
11373
|
-
if (!
|
|
11921
|
+
if (!import_node_path36.default.isAbsolute(repoPath) || import_node_path36.default.resolve(repoPath) !== repoPath)
|
|
11374
11922
|
throw new Error("Invalid network owner path.");
|
|
11375
11923
|
return (0, import_node_crypto14.createHash)("sha256").update(repoPath).digest("hex");
|
|
11376
11924
|
}
|
|
11377
11925
|
function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
11378
11926
|
let names;
|
|
11379
11927
|
try {
|
|
11380
|
-
names =
|
|
11928
|
+
names = import_node_fs28.default.readdirSync(root);
|
|
11381
11929
|
} catch (error) {
|
|
11382
11930
|
if (error.code === "ENOENT") return void 0;
|
|
11383
11931
|
throw new Error("Network claims cannot be inspected; repair is required.");
|
|
@@ -11386,21 +11934,21 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11386
11934
|
if (files.length > 128) throw new Error("Network daemon inventory exceeds its bound.");
|
|
11387
11935
|
let found;
|
|
11388
11936
|
for (const file of files) {
|
|
11389
|
-
const full =
|
|
11390
|
-
const stat =
|
|
11937
|
+
const full = import_node_path36.default.join(root, file);
|
|
11938
|
+
const stat = import_node_fs28.default.lstatSync(full);
|
|
11391
11939
|
if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES)
|
|
11392
11940
|
throw new Error("Invalid retained network claim file.");
|
|
11393
11941
|
let value;
|
|
11394
|
-
const descriptor =
|
|
11942
|
+
const descriptor = import_node_fs28.default.openSync(
|
|
11395
11943
|
full,
|
|
11396
|
-
|
|
11944
|
+
import_node_fs28.default.constants.O_RDONLY | import_node_fs28.default.constants.O_NONBLOCK | import_node_fs28.default.constants.O_NOFOLLOW
|
|
11397
11945
|
);
|
|
11398
11946
|
try {
|
|
11399
|
-
if (!
|
|
11947
|
+
if (!import_node_fs28.default.fstatSync(descriptor).isFile()) throw new Error();
|
|
11400
11948
|
const bytes2 = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
|
|
11401
11949
|
let size = 0;
|
|
11402
11950
|
while (size < bytes2.length) {
|
|
11403
|
-
const count =
|
|
11951
|
+
const count = import_node_fs28.default.readSync(descriptor, bytes2, size, bytes2.length - size, null);
|
|
11404
11952
|
if (!count) break;
|
|
11405
11953
|
size += count;
|
|
11406
11954
|
}
|
|
@@ -11410,7 +11958,7 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11410
11958
|
} catch {
|
|
11411
11959
|
throw new Error("Retained network claim could not be read safely.");
|
|
11412
11960
|
} finally {
|
|
11413
|
-
|
|
11961
|
+
import_node_fs28.default.closeSync(descriptor);
|
|
11414
11962
|
}
|
|
11415
11963
|
if (typeof value.daemonId !== "string" || `${(0, import_node_crypto14.createHash)("sha256").update(value.daemonId).digest("hex")}.json` !== file)
|
|
11416
11964
|
throw new Error("Retained network daemon identity is invalid.");
|
|
@@ -11426,13 +11974,13 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11426
11974
|
}
|
|
11427
11975
|
return found;
|
|
11428
11976
|
}
|
|
11429
|
-
var import_node_crypto14,
|
|
11977
|
+
var import_node_crypto14, import_node_fs28, import_node_path36;
|
|
11430
11978
|
var init_network_claim_lookup = __esm({
|
|
11431
11979
|
"src/core/network-claim-lookup.ts"() {
|
|
11432
11980
|
"use strict";
|
|
11433
11981
|
import_node_crypto14 = require("crypto");
|
|
11434
|
-
|
|
11435
|
-
|
|
11982
|
+
import_node_fs28 = __toESM(require("fs"));
|
|
11983
|
+
import_node_path36 = __toESM(require("path"));
|
|
11436
11984
|
init_network_claims();
|
|
11437
11985
|
}
|
|
11438
11986
|
});
|
|
@@ -11460,7 +12008,7 @@ function qualifyNetworkProviderDefinition(value, provider) {
|
|
|
11460
12008
|
return (0, import_node_crypto15.createHash)("sha256").update(JSON.stringify({ name: definition.name, agent, options, exec })).digest("hex");
|
|
11461
12009
|
}
|
|
11462
12010
|
function read(provider, args) {
|
|
11463
|
-
const result = (0,
|
|
12011
|
+
const result = (0, import_node_child_process16.spawnSync)(provider, args, {
|
|
11464
12012
|
encoding: "utf8",
|
|
11465
12013
|
timeout: 5e3,
|
|
11466
12014
|
maxBuffer: 2 * 1024 * 1024
|
|
@@ -11548,11 +12096,11 @@ function inspectNetworkProviderBinding(input2) {
|
|
|
11548
12096
|
);
|
|
11549
12097
|
}
|
|
11550
12098
|
}
|
|
11551
|
-
var
|
|
12099
|
+
var import_node_child_process16, import_node_crypto15;
|
|
11552
12100
|
var init_network_provider_inspect = __esm({
|
|
11553
12101
|
"src/core/network-provider-inspect.ts"() {
|
|
11554
12102
|
"use strict";
|
|
11555
|
-
|
|
12103
|
+
import_node_child_process16 = require("child_process");
|
|
11556
12104
|
import_node_crypto15 = require("crypto");
|
|
11557
12105
|
init_devpod_environment();
|
|
11558
12106
|
}
|
|
@@ -11591,7 +12139,7 @@ var init_network_provider_effect = __esm({
|
|
|
11591
12139
|
|
|
11592
12140
|
// src/core/devsy-mutation.ts
|
|
11593
12141
|
function withMutationLock(activity, target, operation) {
|
|
11594
|
-
|
|
12142
|
+
import_node_fs29.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
11595
12143
|
return withFileLockSync(
|
|
11596
12144
|
DEVSY_MUTATION_LOCK_FILE,
|
|
11597
12145
|
{
|
|
@@ -11611,7 +12159,7 @@ function runDevsyAction(action2, devsyId, force = false, repoPath) {
|
|
|
11611
12159
|
const args = action2 === "delete" ? ["delete", devsyId, ...force ? ["--force"] : [], "--ignore-not-found"] : ["stop", devsyId];
|
|
11612
12160
|
claimLifecycleEffect();
|
|
11613
12161
|
const binding = repoPath ? networkProviderEffectOptions("devsy", devsyId, repoPath) : { args: [] };
|
|
11614
|
-
const result = (0,
|
|
12162
|
+
const result = (0, import_node_child_process17.spawnSync)("devsy", ["workspace", ...args, ...binding.args], {
|
|
11615
12163
|
encoding: "utf-8",
|
|
11616
12164
|
...binding.env ? { env: binding.env } : {}
|
|
11617
12165
|
});
|
|
@@ -11672,13 +12220,13 @@ function stopOwnedDevsyWorkspace(devsyId, worktreePath) {
|
|
|
11672
12220
|
function deleteOwnedDevsyWorkspace(devsyId, worktreePath) {
|
|
11673
12221
|
return mutateOwnedDevsyWorkspace("delete", devsyId, worktreePath);
|
|
11674
12222
|
}
|
|
11675
|
-
var
|
|
12223
|
+
var import_node_child_process17, import_node_fs29, import_node_path37, DEVSY_MUTATION_LOCK_FILE, DEVSY_MUTATION_WAIT_MS;
|
|
11676
12224
|
var init_devsy_mutation = __esm({
|
|
11677
12225
|
"src/core/devsy-mutation.ts"() {
|
|
11678
12226
|
"use strict";
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
12227
|
+
import_node_child_process17 = require("child_process");
|
|
12228
|
+
import_node_fs29 = __toESM(require("fs"));
|
|
12229
|
+
import_node_path37 = __toESM(require("path"));
|
|
11682
12230
|
init_devsy_agent();
|
|
11683
12231
|
init_devsy_workspaces();
|
|
11684
12232
|
init_file_lock();
|
|
@@ -11687,7 +12235,7 @@ var init_devsy_mutation = __esm({
|
|
|
11687
12235
|
init_network_provider_effect();
|
|
11688
12236
|
init_reliability_context();
|
|
11689
12237
|
init_router();
|
|
11690
|
-
DEVSY_MUTATION_LOCK_FILE =
|
|
12238
|
+
DEVSY_MUTATION_LOCK_FILE = import_node_path37.default.join(DEVROUTER_HOME, "devsy-mutation.lock");
|
|
11691
12239
|
DEVSY_MUTATION_WAIT_MS = 18e5;
|
|
11692
12240
|
}
|
|
11693
12241
|
});
|
|
@@ -12459,10 +13007,10 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
|
|
|
12459
13007
|
throw new Error("Lifecycle invocation was cancelled before dispatch.");
|
|
12460
13008
|
if (process.platform === "win32")
|
|
12461
13009
|
throw new Error("Lifecycle workers require POSIX process-group ownership.");
|
|
12462
|
-
const workerPath =
|
|
12463
|
-
if (!
|
|
13010
|
+
const workerPath = import_node_path38.default.join(__dirname, "devrouter-lifecycle-worker.js");
|
|
13011
|
+
if (!import_node_fs30.default.existsSync(workerPath))
|
|
12464
13012
|
throw new Error("The packaged lifecycle worker is missing; rebuild the CLI.");
|
|
12465
|
-
const child = (0,
|
|
13013
|
+
const child = (0, import_node_child_process18.fork)(workerPath, [], {
|
|
12466
13014
|
detached: true,
|
|
12467
13015
|
stdio: supervision ? ["ignore", "pipe", "pipe", "ipc"] : ["inherit", "inherit", "inherit", "ipc"],
|
|
12468
13016
|
execArgv: []
|
|
@@ -12689,14 +13237,14 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
|
|
|
12689
13237
|
function newLifecycleIds() {
|
|
12690
13238
|
return { requestId: (0, import_node_crypto16.randomUUID)(), operationId: (0, import_node_crypto16.randomUUID)(), workerId: (0, import_node_crypto16.randomUUID)() };
|
|
12691
13239
|
}
|
|
12692
|
-
var
|
|
13240
|
+
var import_node_child_process18, import_node_crypto16, import_node_fs30, import_node_path38, OUTPUT_BUFFER_LIMIT, DEFAULT_OUTPUT_PAGE_BYTES, LifecycleOutput, LifecycleWorkerAdmissionBusyError;
|
|
12693
13241
|
var init_reliability_worker = __esm({
|
|
12694
13242
|
"src/core/reliability-worker.ts"() {
|
|
12695
13243
|
"use strict";
|
|
12696
|
-
|
|
12697
|
-
import_node_crypto16 = require("crypto");
|
|
12698
|
-
|
|
12699
|
-
|
|
13244
|
+
import_node_child_process18 = require("child_process");
|
|
13245
|
+
import_node_crypto16 = require("crypto");
|
|
13246
|
+
import_node_fs30 = __toESM(require("fs"));
|
|
13247
|
+
import_node_path38 = __toESM(require("path"));
|
|
12700
13248
|
init_file_lock();
|
|
12701
13249
|
init_reliability_contract();
|
|
12702
13250
|
init_reliability_model();
|
|
@@ -12811,173 +13359,9 @@ var init_reliability_worker = __esm({
|
|
|
12811
13359
|
}
|
|
12812
13360
|
});
|
|
12813
13361
|
|
|
12814
|
-
// src/core/traefik-route-health.ts
|
|
12815
|
-
function sleep(ms) {
|
|
12816
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12817
|
-
}
|
|
12818
|
-
function inspectRouterApi(protocol) {
|
|
12819
|
-
const result = (0, import_node_child_process18.spawnSync)(
|
|
12820
|
-
"curl",
|
|
12821
|
-
[
|
|
12822
|
-
"--silent",
|
|
12823
|
-
"--show-error",
|
|
12824
|
-
"--fail",
|
|
12825
|
-
"--max-time",
|
|
12826
|
-
ROUTER_API_REQUEST_TIMEOUT_SECONDS,
|
|
12827
|
-
`${ROUTER_API_BASE}/${protocol}/routers?per_page=${ROUTER_API_PAGE_SIZE}`
|
|
12828
|
-
],
|
|
12829
|
-
{ encoding: "utf-8" }
|
|
12830
|
-
);
|
|
12831
|
-
if (result.status !== 0) {
|
|
12832
|
-
return {
|
|
12833
|
-
ok: false,
|
|
12834
|
-
details: result.stderr?.trim() || `curl exited with status ${result.status ?? "unknown"}`
|
|
12835
|
-
};
|
|
12836
|
-
}
|
|
12837
|
-
let value;
|
|
12838
|
-
try {
|
|
12839
|
-
value = JSON.parse(result.stdout);
|
|
12840
|
-
} catch {
|
|
12841
|
-
return { ok: false, details: `${protocol.toUpperCase()} router API returned invalid JSON` };
|
|
12842
|
-
}
|
|
12843
|
-
if (!Array.isArray(value)) {
|
|
12844
|
-
return { ok: false, details: `${protocol.toUpperCase()} router API returned a non-array` };
|
|
12845
|
-
}
|
|
12846
|
-
if (value.some((item) => !isRecord5(item) || typeof item.name !== "string" || !item.name))
|
|
12847
|
-
return {
|
|
12848
|
-
ok: false,
|
|
12849
|
-
details: `${protocol.toUpperCase()} router API returned malformed entries`
|
|
12850
|
-
};
|
|
12851
|
-
return {
|
|
12852
|
-
ok: true,
|
|
12853
|
-
reachedPageLimit: value.length >= ROUTER_API_PAGE_SIZE,
|
|
12854
|
-
names: new Set(
|
|
12855
|
-
value.flatMap((item) => {
|
|
12856
|
-
if (!item || typeof item !== "object") return [];
|
|
12857
|
-
const name = item.name;
|
|
12858
|
-
return typeof name === "string" ? [name] : [];
|
|
12859
|
-
})
|
|
12860
|
-
)
|
|
12861
|
-
};
|
|
12862
|
-
}
|
|
12863
|
-
function isRecord5(value) {
|
|
12864
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12865
|
-
}
|
|
12866
|
-
function inspectExpectedRoutes(routes, expectation) {
|
|
12867
|
-
const mismatched = [];
|
|
12868
|
-
const failures = [];
|
|
12869
|
-
for (const protocol of ["http", "tcp"]) {
|
|
12870
|
-
const expected = routes.filter((route) => (route.protocol ?? "http") === protocol).map((route) => buildHostRouteRouterName(route.repoPath, route.name));
|
|
12871
|
-
if (expected.length === 0) continue;
|
|
12872
|
-
const result = inspectRouterApi(protocol);
|
|
12873
|
-
if (!result.ok) {
|
|
12874
|
-
failures.push(result.details);
|
|
12875
|
-
mismatched.push(...expected);
|
|
12876
|
-
continue;
|
|
12877
|
-
}
|
|
12878
|
-
const unexpected = expected.filter(
|
|
12879
|
-
(name) => expectation === "loaded" ? !result.names.has(name) : result.names.has(name)
|
|
12880
|
-
);
|
|
12881
|
-
const boundedAbsenceIsUncertain = result.reachedPageLimit && (expectation === "loaded" ? unexpected.length > 0 : unexpected.length === 0);
|
|
12882
|
-
if (boundedAbsenceIsUncertain) {
|
|
12883
|
-
failures.push(
|
|
12884
|
-
`${protocol.toUpperCase()} router API reached the ${ROUTER_API_PAGE_SIZE}-entry safety limit`
|
|
12885
|
-
);
|
|
12886
|
-
}
|
|
12887
|
-
mismatched.push(...unexpected);
|
|
12888
|
-
if (expectation === "removed" && unexpected.length === 0 && result.reachedPageLimit) {
|
|
12889
|
-
mismatched.push(...expected);
|
|
12890
|
-
}
|
|
12891
|
-
}
|
|
12892
|
-
return mismatched.length === 0 ? { ok: true } : {
|
|
12893
|
-
ok: false,
|
|
12894
|
-
routes: mismatched,
|
|
12895
|
-
details: failures.length > 0 ? failures.join("; ") : expectation === "loaded" ? "router names are absent" : "router names are still present"
|
|
12896
|
-
};
|
|
12897
|
-
}
|
|
12898
|
-
async function waitForExpectedRoutes(routes, expectation, timeoutMs, pollIntervalMs) {
|
|
12899
|
-
const deadline = Date.now() + timeoutMs;
|
|
12900
|
-
let result;
|
|
12901
|
-
do {
|
|
12902
|
-
result = inspectExpectedRoutes(routes, expectation);
|
|
12903
|
-
if (result.ok) return result;
|
|
12904
|
-
if (Date.now() < deadline) {
|
|
12905
|
-
await sleep(Math.min(pollIntervalMs, Math.max(1, deadline - Date.now())));
|
|
12906
|
-
}
|
|
12907
|
-
} while (Date.now() < deadline);
|
|
12908
|
-
return result;
|
|
12909
|
-
}
|
|
12910
|
-
async function ensureTraefikRouteExpectation(routes, expectation, options) {
|
|
12911
|
-
if (routes.length === 0) return { restarted: false };
|
|
12912
|
-
const initialTimeoutMs = options.initialTimeoutMs ?? DEFAULT_INITIAL_TIMEOUT_MS;
|
|
12913
|
-
const recoveryTimeoutMs = options.recoveryTimeoutMs ?? DEFAULT_RECOVERY_TIMEOUT_MS;
|
|
12914
|
-
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
12915
|
-
const initial = await waitForExpectedRoutes(
|
|
12916
|
-
routes,
|
|
12917
|
-
expectation,
|
|
12918
|
-
initialTimeoutMs,
|
|
12919
|
-
pollIntervalMs
|
|
12920
|
-
);
|
|
12921
|
-
if (initial.ok) return { restarted: false };
|
|
12922
|
-
if (options.allowRestart === false)
|
|
12923
|
-
throw new Error(
|
|
12924
|
-
"Traefik routes did not reload; shared router restart is disabled for this operation."
|
|
12925
|
-
);
|
|
12926
|
-
import_node_fs29.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
12927
|
-
return withFileLock(
|
|
12928
|
-
ROUTER_RELOAD_LOCK_FILE,
|
|
12929
|
-
{
|
|
12930
|
-
activity: "Traefik route reload recovery",
|
|
12931
|
-
waitMs: initialTimeoutMs + recoveryTimeoutMs + 1e4,
|
|
12932
|
-
fair: true,
|
|
12933
|
-
onWait: createStderrWaitReporter("Traefik route reload recovery", "shared router")
|
|
12934
|
-
},
|
|
12935
|
-
async () => {
|
|
12936
|
-
const recheck = await waitForExpectedRoutes(routes, expectation, 0, pollIntervalMs);
|
|
12937
|
-
if (recheck.ok) return { restarted: false };
|
|
12938
|
-
claimLifecycleEffect();
|
|
12939
|
-
restartRouterStack();
|
|
12940
|
-
const recovered = await waitForExpectedRoutes(
|
|
12941
|
-
routes,
|
|
12942
|
-
expectation,
|
|
12943
|
-
recoveryTimeoutMs,
|
|
12944
|
-
pollIntervalMs
|
|
12945
|
-
);
|
|
12946
|
-
if (recovered.ok) return { restarted: true };
|
|
12947
|
-
const action2 = expectation === "loaded" ? "load" : "remove";
|
|
12948
|
-
throw new Error(
|
|
12949
|
-
`Traefik did not ${action2} file-provider routes after one restart: ${recovered.routes.join(", ")} (${recovered.details}). Inspect: devrouter logs --tail 100`
|
|
12950
|
-
);
|
|
12951
|
-
}
|
|
12952
|
-
);
|
|
12953
|
-
}
|
|
12954
|
-
async function ensureTraefikRoutesRemoved(routes, options = {}) {
|
|
12955
|
-
return ensureTraefikRouteExpectation(routes, "removed", options);
|
|
12956
|
-
}
|
|
12957
|
-
var import_node_child_process18, import_node_fs29, import_node_path37, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, ROUTER_API_REQUEST_TIMEOUT_SECONDS, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
|
|
12958
|
-
var init_traefik_route_health = __esm({
|
|
12959
|
-
"src/core/traefik-route-health.ts"() {
|
|
12960
|
-
"use strict";
|
|
12961
|
-
import_node_child_process18 = require("child_process");
|
|
12962
|
-
import_node_fs29 = __toESM(require("fs"));
|
|
12963
|
-
import_node_path37 = __toESM(require("path"));
|
|
12964
|
-
init_file_lock();
|
|
12965
|
-
init_host_routes();
|
|
12966
|
-
init_reliability_context();
|
|
12967
|
-
init_router();
|
|
12968
|
-
ROUTER_API_BASE = "http://127.0.0.1:8080/api";
|
|
12969
|
-
ROUTER_RELOAD_LOCK_FILE = import_node_path37.default.join(DEVROUTER_HOME, "router-reload.lock");
|
|
12970
|
-
ROUTER_API_REQUEST_TIMEOUT_SECONDS = "2";
|
|
12971
|
-
DEFAULT_INITIAL_TIMEOUT_MS = 3e3;
|
|
12972
|
-
DEFAULT_RECOVERY_TIMEOUT_MS = 1e4;
|
|
12973
|
-
DEFAULT_POLL_INTERVAL_MS = 250;
|
|
12974
|
-
ROUTER_API_PAGE_SIZE = 1e3;
|
|
12975
|
-
}
|
|
12976
|
-
});
|
|
12977
|
-
|
|
12978
13362
|
// src/core/devpod-mutation.ts
|
|
12979
13363
|
function withMutationLock2(activity, target, operation) {
|
|
12980
|
-
|
|
13364
|
+
import_node_fs31.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
12981
13365
|
return withFileLockSync(
|
|
12982
13366
|
DEVPOD_MUTATION_LOCK_FILE,
|
|
12983
13367
|
{
|
|
@@ -13067,13 +13451,13 @@ function deleteOwnedDevpodWorkspace(devpodId, worktreePath) {
|
|
|
13067
13451
|
resetWorkspaceRuntimeCaches();
|
|
13068
13452
|
return result;
|
|
13069
13453
|
}
|
|
13070
|
-
var import_node_child_process19,
|
|
13454
|
+
var import_node_child_process19, import_node_fs31, import_node_path39, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS;
|
|
13071
13455
|
var init_devpod_mutation = __esm({
|
|
13072
13456
|
"src/core/devpod-mutation.ts"() {
|
|
13073
13457
|
"use strict";
|
|
13074
13458
|
import_node_child_process19 = require("child_process");
|
|
13075
|
-
|
|
13076
|
-
|
|
13459
|
+
import_node_fs31 = __toESM(require("fs"));
|
|
13460
|
+
import_node_path39 = __toESM(require("path"));
|
|
13077
13461
|
init_devpod_workspaces();
|
|
13078
13462
|
init_devsy_mutation();
|
|
13079
13463
|
init_file_lock();
|
|
@@ -13082,7 +13466,7 @@ var init_devpod_mutation = __esm({
|
|
|
13082
13466
|
init_reliability_context();
|
|
13083
13467
|
init_router();
|
|
13084
13468
|
init_workspace_runtime();
|
|
13085
|
-
DEVPOD_MUTATION_LOCK_FILE =
|
|
13469
|
+
DEVPOD_MUTATION_LOCK_FILE = import_node_path39.default.join(DEVROUTER_HOME, "devpod-mutation.lock");
|
|
13086
13470
|
DEVPOD_MUTATION_WAIT_MS = 18e5;
|
|
13087
13471
|
}
|
|
13088
13472
|
});
|
|
@@ -13288,8 +13672,8 @@ function renderManagedComposeModel(plan, workspace) {
|
|
|
13288
13672
|
}
|
|
13289
13673
|
}
|
|
13290
13674
|
function attributeHolderWorktree(workingDir) {
|
|
13291
|
-
if (
|
|
13292
|
-
return comparableWorkspacePath(
|
|
13675
|
+
if (import_node_path40.default.basename(workingDir) !== ".devcontainer") return void 0;
|
|
13676
|
+
return comparableWorkspacePath(import_node_path40.default.dirname(workingDir));
|
|
13293
13677
|
}
|
|
13294
13678
|
function holderAttribution(holder, repoPath) {
|
|
13295
13679
|
const attribution = {};
|
|
@@ -13321,7 +13705,7 @@ function detectHostPortClaimConflicts(options) {
|
|
|
13321
13705
|
);
|
|
13322
13706
|
if (desired.length === 0) return [];
|
|
13323
13707
|
const holders = listHolderSnapshots();
|
|
13324
|
-
const devcontainerDir =
|
|
13708
|
+
const devcontainerDir = import_node_path40.default.join(options.repoPath, ".devcontainer");
|
|
13325
13709
|
const conflicts = [];
|
|
13326
13710
|
for (const binding of desired) {
|
|
13327
13711
|
for (const holder of holders) {
|
|
@@ -13343,12 +13727,12 @@ function detectHostPortClaimConflicts(options) {
|
|
|
13343
13727
|
}
|
|
13344
13728
|
return conflicts;
|
|
13345
13729
|
}
|
|
13346
|
-
var import_node_child_process20,
|
|
13730
|
+
var import_node_child_process20, import_node_path40, COMPOSE_RENDER_TIMEOUT_MS, COMPOSE_RENDER_MAX_BUFFER, DOCKER_TIMEOUT_MS, DOCKER_MAX_BUFFER, MAX_DESIRED_HOST_PORTS, MAX_RUNNING_HOLDERS, HOST_PORT_INSPECT_TEMPLATE;
|
|
13347
13731
|
var init_host_port_claims = __esm({
|
|
13348
13732
|
"src/core/host-port-claims.ts"() {
|
|
13349
13733
|
"use strict";
|
|
13350
13734
|
import_node_child_process20 = require("child_process");
|
|
13351
|
-
|
|
13735
|
+
import_node_path40 = __toESM(require("path"));
|
|
13352
13736
|
init_devcontainer_profile();
|
|
13353
13737
|
init_network_effect_scope();
|
|
13354
13738
|
init_workspace();
|
|
@@ -13522,20 +13906,20 @@ var init_http_route_probe = __esm({
|
|
|
13522
13906
|
});
|
|
13523
13907
|
|
|
13524
13908
|
// src/core/managed-host-preparation.ts
|
|
13525
|
-
var import_node_child_process22,
|
|
13909
|
+
var import_node_child_process22, import_node_fs32;
|
|
13526
13910
|
var init_managed_host_preparation = __esm({
|
|
13527
13911
|
"src/core/managed-host-preparation.ts"() {
|
|
13528
13912
|
"use strict";
|
|
13529
13913
|
import_node_child_process22 = require("child_process");
|
|
13530
|
-
|
|
13914
|
+
import_node_fs32 = __toESM(require("fs"));
|
|
13531
13915
|
init_repo_config();
|
|
13532
13916
|
}
|
|
13533
13917
|
});
|
|
13534
13918
|
|
|
13535
13919
|
// src/core/managed-post-start.ts
|
|
13536
13920
|
function readRegularFileBytes(filePath) {
|
|
13537
|
-
if (!
|
|
13538
|
-
return
|
|
13921
|
+
if (!import_node_fs33.default.existsSync(filePath) || !import_node_fs33.default.lstatSync(filePath).isFile()) return void 0;
|
|
13922
|
+
return import_node_fs33.default.readFileSync(filePath);
|
|
13539
13923
|
}
|
|
13540
13924
|
function readRegularFile(filePath) {
|
|
13541
13925
|
return readRegularFileBytes(filePath)?.toString("utf-8");
|
|
@@ -13544,11 +13928,11 @@ function adapterFingerprint(adapter) {
|
|
|
13544
13928
|
return (0, import_node_crypto17.createHash)("sha256").update(adapter).digest("hex");
|
|
13545
13929
|
}
|
|
13546
13930
|
function resolveManagedPostStartPlan(repoPath) {
|
|
13547
|
-
const adapterPath =
|
|
13931
|
+
const adapterPath = import_node_path41.default.join(repoPath, MANAGED_ADAPTER_PATH);
|
|
13548
13932
|
const adapterBytes = readRegularFileBytes(adapterPath);
|
|
13549
13933
|
const adapterText = adapterBytes?.toString("utf-8");
|
|
13550
|
-
const dockerfilePath =
|
|
13551
|
-
const devcontainerPath =
|
|
13934
|
+
const dockerfilePath = import_node_path41.default.join(repoPath, ".devcontainer", "Dockerfile");
|
|
13935
|
+
const devcontainerPath = import_node_path41.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
13552
13936
|
const dockerfile = readRegularFile(dockerfilePath) ?? "";
|
|
13553
13937
|
const devcontainer = readRegularFile(devcontainerPath) ?? "";
|
|
13554
13938
|
const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
|
|
@@ -13631,14 +14015,14 @@ function runManagedProcessAction(options) {
|
|
|
13631
14015
|
}
|
|
13632
14016
|
return "drifted";
|
|
13633
14017
|
}
|
|
13634
|
-
var import_node_child_process23, import_node_crypto17,
|
|
14018
|
+
var import_node_child_process23, import_node_crypto17, import_node_fs33, import_node_path41, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH;
|
|
13635
14019
|
var init_managed_post_start = __esm({
|
|
13636
14020
|
"src/core/managed-post-start.ts"() {
|
|
13637
14021
|
"use strict";
|
|
13638
14022
|
import_node_child_process23 = require("child_process");
|
|
13639
14023
|
import_node_crypto17 = require("crypto");
|
|
13640
|
-
|
|
13641
|
-
|
|
14024
|
+
import_node_fs33 = __toESM(require("fs"));
|
|
14025
|
+
import_node_path41 = __toESM(require("path"));
|
|
13642
14026
|
init_devcontainer_config();
|
|
13643
14027
|
init_network_effect_scope();
|
|
13644
14028
|
MANAGED_MARKER = "devrouter:managed devcontainer";
|
|
@@ -14577,26 +14961,26 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
|
14577
14961
|
try {
|
|
14578
14962
|
if (!endpoint.startsWith("unix:///")) return false;
|
|
14579
14963
|
const socket = endpoint.slice("unix://".length);
|
|
14580
|
-
const table =
|
|
14964
|
+
const table = import_node_fs34.default.readFileSync(import_node_path42.default.join(procRoot, "net/unix"), "utf8");
|
|
14581
14965
|
if (Buffer.byteLength(table) > 1024 * 1024) return false;
|
|
14582
14966
|
const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
|
|
14583
14967
|
if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
|
|
14584
14968
|
const inode = `socket:[${rows2[0][6]}]`;
|
|
14585
|
-
const ownNamespace =
|
|
14586
|
-
const pids =
|
|
14969
|
+
const ownNamespace = import_node_fs34.default.readlinkSync(import_node_path42.default.join(procRoot, "self/ns/net"));
|
|
14970
|
+
const pids = import_node_fs34.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
|
|
14587
14971
|
if (pids.length > 4096) return false;
|
|
14588
14972
|
for (const pid of pids) {
|
|
14589
14973
|
if (Date.now() > deadline) return false;
|
|
14590
14974
|
try {
|
|
14591
|
-
const root =
|
|
14592
|
-
if (
|
|
14593
|
-
if (
|
|
14594
|
-
const descriptors =
|
|
14975
|
+
const root = import_node_path42.default.join(procRoot, pid);
|
|
14976
|
+
if (import_node_path42.default.basename(import_node_fs34.default.readlinkSync(import_node_path42.default.join(root, "exe"))) !== "dockerd") continue;
|
|
14977
|
+
if (import_node_fs34.default.readlinkSync(import_node_path42.default.join(root, "ns/net")) !== ownNamespace) continue;
|
|
14978
|
+
const descriptors = import_node_fs34.default.readdirSync(import_node_path42.default.join(root, "fd"));
|
|
14595
14979
|
if (descriptors.length > 4096) continue;
|
|
14596
14980
|
if (descriptors.some((fd) => {
|
|
14597
14981
|
if (Date.now() > deadline) return false;
|
|
14598
14982
|
try {
|
|
14599
|
-
return
|
|
14983
|
+
return import_node_fs34.default.readlinkSync(import_node_path42.default.join(root, "fd", fd)) === inode;
|
|
14600
14984
|
} catch {
|
|
14601
14985
|
return false;
|
|
14602
14986
|
}
|
|
@@ -14610,12 +14994,12 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
|
14610
14994
|
}
|
|
14611
14995
|
return false;
|
|
14612
14996
|
}
|
|
14613
|
-
var
|
|
14997
|
+
var import_node_fs34, import_node_path42;
|
|
14614
14998
|
var init_network_local_daemon = __esm({
|
|
14615
14999
|
"src/core/network-local-daemon.ts"() {
|
|
14616
15000
|
"use strict";
|
|
14617
|
-
|
|
14618
|
-
|
|
15001
|
+
import_node_fs34 = __toESM(require("fs"));
|
|
15002
|
+
import_node_path42 = __toESM(require("path"));
|
|
14619
15003
|
}
|
|
14620
15004
|
});
|
|
14621
15005
|
|
|
@@ -14731,14 +15115,14 @@ var init_network_compose = __esm({
|
|
|
14731
15115
|
});
|
|
14732
15116
|
|
|
14733
15117
|
// src/core/network-compose-files.ts
|
|
14734
|
-
var import_node_child_process26, import_node_crypto19,
|
|
15118
|
+
var import_node_child_process26, import_node_crypto19, import_node_fs35, import_node_path43, import_yaml4, MAX_BYTES3;
|
|
14735
15119
|
var init_network_compose_files = __esm({
|
|
14736
15120
|
"src/core/network-compose-files.ts"() {
|
|
14737
15121
|
"use strict";
|
|
14738
15122
|
import_node_child_process26 = require("child_process");
|
|
14739
15123
|
import_node_crypto19 = require("crypto");
|
|
14740
|
-
|
|
14741
|
-
|
|
15124
|
+
import_node_fs35 = __toESM(require("fs"));
|
|
15125
|
+
import_node_path43 = __toESM(require("path"));
|
|
14742
15126
|
import_yaml4 = __toESM(require("yaml"));
|
|
14743
15127
|
init_atomic_file();
|
|
14744
15128
|
init_devcontainer_profile();
|
|
@@ -14842,13 +15226,13 @@ function resolveLinkedTarget(repoPath) {
|
|
|
14842
15226
|
gitCommonDir: resolveGitCommonDir(repoPath)
|
|
14843
15227
|
};
|
|
14844
15228
|
}
|
|
14845
|
-
var import_node_child_process28,
|
|
15229
|
+
var import_node_child_process28, import_node_fs36, import_node_path44;
|
|
14846
15230
|
var init_workspace_ensure = __esm({
|
|
14847
15231
|
"src/core/workspace-ensure.ts"() {
|
|
14848
15232
|
"use strict";
|
|
14849
15233
|
import_node_child_process28 = require("child_process");
|
|
14850
|
-
|
|
14851
|
-
|
|
15234
|
+
import_node_fs36 = __toESM(require("fs"));
|
|
15235
|
+
import_node_path44 = __toESM(require("path"));
|
|
14852
15236
|
init_devcontainer_profile();
|
|
14853
15237
|
init_devpod_environment();
|
|
14854
15238
|
init_devpod_mutation();
|
|
@@ -14984,7 +15368,7 @@ function prepareManagedLifecycleOperation(input2) {
|
|
|
14984
15368
|
};
|
|
14985
15369
|
});
|
|
14986
15370
|
}
|
|
14987
|
-
function assertCurrentCapacityController(expected, directory =
|
|
15371
|
+
function assertCurrentCapacityController(expected, directory = import_node_path45.default.join(DEVROUTER_HOME, "controller")) {
|
|
14988
15372
|
const current = new ControllerStore(directory).read();
|
|
14989
15373
|
if (!expected || current?.store !== expected.store || current.epoch !== expected.epoch)
|
|
14990
15374
|
throw new Error("Capacity controller incarnation changed.");
|
|
@@ -15036,7 +15420,7 @@ function prepareRecoveryLifecycleOperation(input2) {
|
|
|
15036
15420
|
});
|
|
15037
15421
|
}
|
|
15038
15422
|
function settlePreparedLifecycleCapacity(input2) {
|
|
15039
|
-
const directory = input2.directory ??
|
|
15423
|
+
const directory = input2.directory ?? import_node_path45.default.join(DEVROUTER_HOME, "controller");
|
|
15040
15424
|
const store = new CapacityStore(directory);
|
|
15041
15425
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
15042
15426
|
const snapshot = store.read();
|
|
@@ -15250,7 +15634,7 @@ async function superviseLifecycle(kind, repoPath, options = {}, command) {
|
|
|
15250
15634
|
removeSignals();
|
|
15251
15635
|
}
|
|
15252
15636
|
}
|
|
15253
|
-
function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory =
|
|
15637
|
+
function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory = import_node_path45.default.join(DEVROUTER_HOME, "controller"), controller, admission) {
|
|
15254
15638
|
if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
|
|
15255
15639
|
const capacity = new CapacityStore(directory);
|
|
15256
15640
|
const snapshot = capacity.read();
|
|
@@ -15350,7 +15734,7 @@ function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs,
|
|
|
15350
15734
|
return decision;
|
|
15351
15735
|
}
|
|
15352
15736
|
function capacityPolicyTargets(repoPath) {
|
|
15353
|
-
const policy = readCapacityPolicy(
|
|
15737
|
+
const policy = readCapacityPolicy(import_node_path45.default.join(DEVROUTER_HOME, "controller"));
|
|
15354
15738
|
return policy?.admissions === "enabled" && policy.enrollments.some((entry) => entry.repoPath === repoPath);
|
|
15355
15739
|
}
|
|
15356
15740
|
function controllerWaitSeconds(deadline) {
|
|
@@ -15359,7 +15743,7 @@ function controllerWaitSeconds(deadline) {
|
|
|
15359
15743
|
async function superviseThroughController(input2) {
|
|
15360
15744
|
if (input2.kind === "exec" && !input2.command)
|
|
15361
15745
|
throw new Error("Exec requires a command for controller admission.");
|
|
15362
|
-
const directory =
|
|
15746
|
+
const directory = import_node_path45.default.join(DEVROUTER_HOME, "controller");
|
|
15363
15747
|
const cancellation = { requested: false };
|
|
15364
15748
|
const removeSignals = installLifecycleWaitSignals(cancellation);
|
|
15365
15749
|
const cancelled = () => {
|
|
@@ -15431,7 +15815,7 @@ async function superviseThroughController(input2) {
|
|
|
15431
15815
|
removeSignals();
|
|
15432
15816
|
}
|
|
15433
15817
|
}
|
|
15434
|
-
function renewLifecycleCapacity(request2, policy, samples, controller, directory =
|
|
15818
|
+
function renewLifecycleCapacity(request2, policy, samples, controller, directory = import_node_path45.default.join(DEVROUTER_HOME, "controller"), nowMs = Date.now()) {
|
|
15435
15819
|
return updateReliabilityOperation(request2.identity, (record5) => {
|
|
15436
15820
|
const binding = record5.capacity;
|
|
15437
15821
|
if (!matchesFence(record5, request2.fence) || !binding || binding.operationId !== request2.operationId || binding.workerId !== request2.workerId || binding.controller?.store !== controller.store || binding.controller?.epoch !== controller.epoch)
|
|
@@ -15519,7 +15903,7 @@ function retireQueuedLifecycle(request2, supersededOnly = false) {
|
|
|
15519
15903
|
return true;
|
|
15520
15904
|
});
|
|
15521
15905
|
}
|
|
15522
|
-
function bindLifecycleCapacity(request2, binding, directory =
|
|
15906
|
+
function bindLifecycleCapacity(request2, binding, directory = import_node_path45.default.join(DEVROUTER_HOME, "controller")) {
|
|
15523
15907
|
if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
|
|
15524
15908
|
updateReliabilityOperation(request2.identity, (record5) => {
|
|
15525
15909
|
if (!matchesFence(record5, request2.fence) || record5.worker || record5.state.operation?.id !== request2.operationId || record5.state.operation.status !== "NOT_STARTED" || record5.state.operation.drained)
|
|
@@ -15534,12 +15918,12 @@ function bindLifecycleCapacity(request2, binding, directory = import_node_path44
|
|
|
15534
15918
|
stepRecord(record5, { ...request2.fence, type: "admission", result: "admitted" });
|
|
15535
15919
|
});
|
|
15536
15920
|
}
|
|
15537
|
-
var import_node_crypto21,
|
|
15921
|
+
var import_node_crypto21, import_node_path45, import_node_util10, activeWorker, lockHeld, BUSY_LIFECYCLE_WAIT_MS, BUSY_LIFECYCLE_POLL_MS, CAPACITY_CLIENT_WAIT_MS, BUSY_LIFECYCLE_PROGRESS_MS;
|
|
15538
15922
|
var init_reliability_lifecycle = __esm({
|
|
15539
15923
|
"src/core/reliability-lifecycle.ts"() {
|
|
15540
15924
|
"use strict";
|
|
15541
15925
|
import_node_crypto21 = require("crypto");
|
|
15542
|
-
|
|
15926
|
+
import_node_path45 = __toESM(require("path"));
|
|
15543
15927
|
import_node_util10 = require("util");
|
|
15544
15928
|
init_capacity_accounting();
|
|
15545
15929
|
init_capacity_policy();
|
|
@@ -15552,6 +15936,7 @@ var init_reliability_lifecycle = __esm({
|
|
|
15552
15936
|
init_devsy_mutation();
|
|
15553
15937
|
init_file_lock();
|
|
15554
15938
|
init_host_routes();
|
|
15939
|
+
init_managed_devsy_stop();
|
|
15555
15940
|
init_managed_runtime_state();
|
|
15556
15941
|
init_managed_stop_recovery();
|
|
15557
15942
|
init_reliability_context();
|
|
@@ -16031,7 +16416,7 @@ function createCapacityController(options) {
|
|
|
16031
16416
|
const record5 = readReliabilityOperation(identity2);
|
|
16032
16417
|
if (!record5?.capacity || !record5.preparation && !record5.capacity.execSteady || record5.worker || !record5.state.operation?.drained || !["ensure", "exec"].includes(record5.state.operation.kind))
|
|
16033
16418
|
continue;
|
|
16034
|
-
const bytes2 = readControllerEvidence(
|
|
16419
|
+
const bytes2 = readControllerEvidence(import_node_path46.default.join(enrollment.repoPath, ".devrouter.yml"));
|
|
16035
16420
|
const estimates = loadRepoConfig(enrollment.repoPath, () => bytes2).capacity;
|
|
16036
16421
|
if (!estimates) continue;
|
|
16037
16422
|
settlePreparedLifecycleCapacity({
|
|
@@ -16272,12 +16657,12 @@ function createCapacityController(options) {
|
|
|
16272
16657
|
}
|
|
16273
16658
|
};
|
|
16274
16659
|
}
|
|
16275
|
-
var import_node_crypto22,
|
|
16660
|
+
var import_node_crypto22, import_node_path46, import_node_util12;
|
|
16276
16661
|
var init_capacity_controller = __esm({
|
|
16277
16662
|
"src/core/capacity-controller.ts"() {
|
|
16278
16663
|
"use strict";
|
|
16279
16664
|
import_node_crypto22 = require("crypto");
|
|
16280
|
-
|
|
16665
|
+
import_node_path46 = __toESM(require("path"));
|
|
16281
16666
|
import_node_util12 = require("util");
|
|
16282
16667
|
init_capacity_docker_probe();
|
|
16283
16668
|
init_capacity_enrollment();
|
|
@@ -16637,12 +17022,12 @@ async function containers(project, signal) {
|
|
|
16637
17022
|
}
|
|
16638
17023
|
return values.sort((a, b) => a.id.localeCompare(b.id));
|
|
16639
17024
|
}
|
|
16640
|
-
var import_node_crypto24,
|
|
17025
|
+
var import_node_crypto24, import_node_path47, OBSERVATION_INSPECT_TEMPLATE, collectControllerObservation;
|
|
16641
17026
|
var init_controller_observation = __esm({
|
|
16642
17027
|
"src/core/controller-observation.ts"() {
|
|
16643
17028
|
"use strict";
|
|
16644
17029
|
import_node_crypto24 = require("crypto");
|
|
16645
|
-
|
|
17030
|
+
import_node_path47 = __toESM(require("path"));
|
|
16646
17031
|
init_controller_binding();
|
|
16647
17032
|
init_controller_monitor();
|
|
16648
17033
|
init_controller_probe();
|
|
@@ -16685,14 +17070,14 @@ var init_controller_observation = __esm({
|
|
|
16685
17070
|
)).trim().split("\n");
|
|
16686
17071
|
if (metadata.length !== 2) throw new Error("Observation ownership paths unavailable.");
|
|
16687
17072
|
const [gitDir, commonDir] = metadata;
|
|
16688
|
-
const sourcePath =
|
|
17073
|
+
const sourcePath = import_node_path47.default.join(environment.repoPath, ".devcontainer/devcontainer.json");
|
|
16689
17074
|
const files = [
|
|
16690
|
-
|
|
16691
|
-
|
|
16692
|
-
|
|
17075
|
+
import_node_path47.default.join(commonDir, "devrouter/workspaces", `${environment.workspace}.json`),
|
|
17076
|
+
import_node_path47.default.join(environment.repoPath, ".devrouter.yml"),
|
|
17077
|
+
import_node_path47.default.join(gitDir, "devrouter-workspace"),
|
|
16693
17078
|
managedRuntimeStatePath(environment.repoPath, environment.workspace),
|
|
16694
17079
|
sourcePath,
|
|
16695
|
-
|
|
17080
|
+
import_node_path47.default.join(environment.repoPath, MANAGED_DEVCONTAINER_PATH)
|
|
16696
17081
|
];
|
|
16697
17082
|
const persisted = captureControllerEvidence(files);
|
|
16698
17083
|
if (controllerBindingFingerprint(persisted.contents[0], persisted.contents[1]) !== environment.fingerprint)
|
|
@@ -16717,7 +17102,7 @@ var init_controller_observation = __esm({
|
|
|
16717
17102
|
if (!journal) throw new Error("Observation journal is unavailable.");
|
|
16718
17103
|
const routes = readRoutes().filter((route) => route.repoPath === environment.repoPath);
|
|
16719
17104
|
const before = await containers(state.composeProject, signal);
|
|
16720
|
-
const composeDirectory =
|
|
17105
|
+
const composeDirectory = import_node_path47.default.join(environment.repoPath, ".devcontainer");
|
|
16721
17106
|
const references = typeof source.dockerComposeFile === "string" ? [source.dockerComposeFile] : source.dockerComposeFile;
|
|
16722
17107
|
if (!Array.isArray(references) || !references.length || references.some((value) => typeof value !== "string"))
|
|
16723
17108
|
throw new Error("Observation Compose source is unavailable.");
|
|
@@ -16734,7 +17119,7 @@ var init_controller_observation = __esm({
|
|
|
16734
17119
|
if (!recorded || container.labels["com.docker.compose.project.working_dir"] !== composeDirectory)
|
|
16735
17120
|
throw new Error("Observation Compose ownership is unavailable.");
|
|
16736
17121
|
const files2 = recorded.split(",").map((file) => file.trim());
|
|
16737
|
-
if (files2.some((file) => !
|
|
17122
|
+
if (files2.some((file) => !import_node_path47.default.isAbsolute(file)) || !hasExactComposeIdentity(container, {
|
|
16738
17123
|
repoPath: environment.repoPath,
|
|
16739
17124
|
service: container.labels["com.docker.compose.service"] ?? "",
|
|
16740
17125
|
composeProject: state.composeProject,
|
|
@@ -17099,26 +17484,26 @@ var init_controller_sessions = __esm({
|
|
|
17099
17484
|
|
|
17100
17485
|
// src/core/controller-server.ts
|
|
17101
17486
|
function privateDirectory(directory) {
|
|
17102
|
-
const absolute2 =
|
|
17103
|
-
let current =
|
|
17104
|
-
for (const part of absolute2.slice(current.length).split(
|
|
17105
|
-
current =
|
|
17487
|
+
const absolute2 = import_node_path48.default.resolve(directory);
|
|
17488
|
+
let current = import_node_path48.default.parse(absolute2).root;
|
|
17489
|
+
for (const part of absolute2.slice(current.length).split(import_node_path48.default.sep)) {
|
|
17490
|
+
current = import_node_path48.default.join(current, part);
|
|
17106
17491
|
try {
|
|
17107
|
-
const stat2 =
|
|
17492
|
+
const stat2 = import_node_fs37.default.lstatSync(current);
|
|
17108
17493
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
17109
17494
|
throw new Error("Unsafe controller directory.");
|
|
17110
17495
|
} catch (error) {
|
|
17111
17496
|
if (error.code !== "ENOENT") throw error;
|
|
17112
|
-
|
|
17497
|
+
import_node_fs37.default.mkdirSync(current, { mode: 448 });
|
|
17113
17498
|
}
|
|
17114
17499
|
}
|
|
17115
|
-
const stat =
|
|
17500
|
+
const stat = import_node_fs37.default.lstatSync(absolute2);
|
|
17116
17501
|
if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
17117
17502
|
throw new Error("Controller directory is not private.");
|
|
17118
17503
|
}
|
|
17119
17504
|
function validateOwnedFile(file, socket) {
|
|
17120
17505
|
try {
|
|
17121
|
-
const stat =
|
|
17506
|
+
const stat = import_node_fs37.default.lstatSync(file);
|
|
17122
17507
|
if (stat.uid !== process.getuid?.() || (socket ? !stat.isSocket() : !stat.isFile()) || (stat.mode & 63) !== 0)
|
|
17123
17508
|
throw new Error("Unsafe controller artifact.");
|
|
17124
17509
|
return true;
|
|
@@ -17130,19 +17515,19 @@ function validateOwnedFile(file, socket) {
|
|
|
17130
17515
|
async function runController(options) {
|
|
17131
17516
|
if (options.operations && options.createOperations)
|
|
17132
17517
|
throw new Error("Controller operations have multiple owners.");
|
|
17133
|
-
const socketPath =
|
|
17518
|
+
const socketPath = import_node_path48.default.join(options.directory, "control.sock");
|
|
17134
17519
|
if (Buffer.byteLength(socketPath) > 103) throw new Error("Controller socket path is too long.");
|
|
17135
17520
|
privateDirectory(options.directory);
|
|
17136
|
-
const lockPath =
|
|
17521
|
+
const lockPath = import_node_path48.default.join(options.directory, "owner.lock");
|
|
17137
17522
|
try {
|
|
17138
|
-
const stat =
|
|
17523
|
+
const stat = import_node_fs37.default.lstatSync(lockPath);
|
|
17139
17524
|
if (!stat.isFile() || stat.uid !== process.getuid?.())
|
|
17140
17525
|
throw new Error("Unsafe controller owner lock.");
|
|
17141
17526
|
} catch (error) {
|
|
17142
17527
|
if (error.code !== "ENOENT") throw error;
|
|
17143
17528
|
}
|
|
17144
17529
|
await withFileLock(lockPath, { activity: "controller ownership", waitMs: 0 }, async () => {
|
|
17145
|
-
if (validateOwnedFile(socketPath, true))
|
|
17530
|
+
if (validateOwnedFile(socketPath, true)) import_node_fs37.default.unlinkSync(socketPath);
|
|
17146
17531
|
const sessions = new ControllerSessions(new ControllerStore(options.directory));
|
|
17147
17532
|
const incarnation = sessions.read();
|
|
17148
17533
|
let startupAvailable = true;
|
|
@@ -17531,7 +17916,7 @@ async function runController(options) {
|
|
|
17531
17916
|
resolve();
|
|
17532
17917
|
});
|
|
17533
17918
|
});
|
|
17534
|
-
|
|
17919
|
+
import_node_fs37.default.chmodSync(socketPath, 384);
|
|
17535
17920
|
await new Promise((resolve) => {
|
|
17536
17921
|
finish = resolve;
|
|
17537
17922
|
options.signal.addEventListener("abort", shutdown, { once: true });
|
|
@@ -17550,13 +17935,13 @@ async function runController(options) {
|
|
|
17550
17935
|
}
|
|
17551
17936
|
});
|
|
17552
17937
|
}
|
|
17553
|
-
var
|
|
17938
|
+
var import_node_fs37, import_node_net4, import_node_path48, FRAME_BYTES;
|
|
17554
17939
|
var init_controller_server = __esm({
|
|
17555
17940
|
"src/core/controller-server.ts"() {
|
|
17556
17941
|
"use strict";
|
|
17557
|
-
|
|
17942
|
+
import_node_fs37 = __toESM(require("fs"));
|
|
17558
17943
|
import_node_net4 = __toESM(require("net"));
|
|
17559
|
-
|
|
17944
|
+
import_node_path48 = __toESM(require("path"));
|
|
17560
17945
|
init_controller_monitor();
|
|
17561
17946
|
init_controller_protocol();
|
|
17562
17947
|
init_controller_sessions();
|
|
@@ -17573,7 +17958,7 @@ __export(controller_exports, {
|
|
|
17573
17958
|
runControllerCommand: () => runControllerCommand
|
|
17574
17959
|
});
|
|
17575
17960
|
async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
17576
|
-
const directory =
|
|
17961
|
+
const directory = import_node_path49.default.join(DEVROUTER_HOME, "controller");
|
|
17577
17962
|
try {
|
|
17578
17963
|
if (method === "run") {
|
|
17579
17964
|
const controller = new AbortController();
|
|
@@ -17608,7 +17993,7 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
|
17608
17993
|
if (fields2.timeout !== void 0) fields2.timeout = Number(fields2.timeout);
|
|
17609
17994
|
await controllerRequest(
|
|
17610
17995
|
directory,
|
|
17611
|
-
{ method, ...fields2, ...repo ? { path:
|
|
17996
|
+
{ method, ...fields2, ...repo ? { path: import_node_path49.default.resolve(repo) } : {} },
|
|
17612
17997
|
(value) => process.stdout.write(`${JSON.stringify(value)}
|
|
17613
17998
|
`)
|
|
17614
17999
|
);
|
|
@@ -17623,11 +18008,11 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
|
17623
18008
|
process.exitCode = 1;
|
|
17624
18009
|
}
|
|
17625
18010
|
}
|
|
17626
|
-
var
|
|
18011
|
+
var import_node_path49;
|
|
17627
18012
|
var init_controller = __esm({
|
|
17628
18013
|
"src/commands/controller.ts"() {
|
|
17629
18014
|
"use strict";
|
|
17630
|
-
|
|
18015
|
+
import_node_path49 = __toESM(require("path"));
|
|
17631
18016
|
init_capacity_collector();
|
|
17632
18017
|
init_capacity_controller();
|
|
17633
18018
|
init_capacity_policy();
|
|
@@ -17678,28 +18063,28 @@ function replaceDevrouterSection(content) {
|
|
|
17678
18063
|
return `${content.slice(0, start)}${buildDevrouterSection()}${content.slice(end)}`;
|
|
17679
18064
|
}
|
|
17680
18065
|
function writeRepoFile(repoPath, relPath, content) {
|
|
17681
|
-
const absolutePath = (0,
|
|
17682
|
-
(0,
|
|
17683
|
-
(0,
|
|
18066
|
+
const absolutePath = (0, import_node_path50.join)(repoPath, relPath);
|
|
18067
|
+
(0, import_node_fs38.mkdirSync)((0, import_node_path50.dirname)(absolutePath), { recursive: true });
|
|
18068
|
+
(0, import_node_fs38.writeFileSync)(absolutePath, content, "utf-8");
|
|
17684
18069
|
return absolutePath;
|
|
17685
18070
|
}
|
|
17686
18071
|
function ensureAgentsMdSection(repoPath) {
|
|
17687
|
-
const filePath = (0,
|
|
17688
|
-
if ((0,
|
|
17689
|
-
const content = (0,
|
|
18072
|
+
const filePath = (0, import_node_path50.join)(repoPath, AGENTS_MD);
|
|
18073
|
+
if ((0, import_node_fs38.existsSync)(filePath)) {
|
|
18074
|
+
const content = (0, import_node_fs38.readFileSync)(filePath, "utf-8");
|
|
17690
18075
|
if (content.includes(DEVROUTER_SENTINEL)) {
|
|
17691
18076
|
const updated = replaceDevrouterSection(content);
|
|
17692
18077
|
if (updated === void 0 || updated === content) return { path: filePath, written: false };
|
|
17693
|
-
(0,
|
|
18078
|
+
(0, import_node_fs38.writeFileSync)(filePath, updated, "utf-8");
|
|
17694
18079
|
return { path: filePath, written: true };
|
|
17695
18080
|
}
|
|
17696
|
-
(0,
|
|
18081
|
+
(0, import_node_fs38.writeFileSync)(filePath, `${content.trimEnd()}
|
|
17697
18082
|
|
|
17698
18083
|
${buildDevrouterSection()}
|
|
17699
18084
|
`, "utf-8");
|
|
17700
18085
|
return { path: filePath, written: true };
|
|
17701
18086
|
}
|
|
17702
|
-
(0,
|
|
18087
|
+
(0, import_node_fs38.writeFileSync)(filePath, `# AGENTS.md
|
|
17703
18088
|
|
|
17704
18089
|
${buildDevrouterSection()}
|
|
17705
18090
|
`, "utf-8");
|
|
@@ -17709,12 +18094,12 @@ function ensureSkillFile(repoPath) {
|
|
|
17709
18094
|
const filePath = writeRepoFile(repoPath, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT);
|
|
17710
18095
|
return { path: filePath, written: true };
|
|
17711
18096
|
}
|
|
17712
|
-
var
|
|
18097
|
+
var import_node_fs38, import_node_path50, DEVROUTER_SENTINEL, DEVROUTER_END_SENTINEL, LEGACY_DEVROUTER_SECTION_END, AGENTS_MD, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT;
|
|
17713
18098
|
var init_agents_md = __esm({
|
|
17714
18099
|
"src/core/agents-md.ts"() {
|
|
17715
18100
|
"use strict";
|
|
17716
|
-
|
|
17717
|
-
|
|
18101
|
+
import_node_fs38 = require("fs");
|
|
18102
|
+
import_node_path50 = require("path");
|
|
17718
18103
|
DEVROUTER_SENTINEL = "<!-- devrouter -->";
|
|
17719
18104
|
DEVROUTER_END_SENTINEL = "<!-- /devrouter -->";
|
|
17720
18105
|
LEGACY_DEVROUTER_SECTION_END = "- `devrouter ls`";
|
|
@@ -18439,7 +18824,7 @@ var init_ai_prompt = __esm({
|
|
|
18439
18824
|
},
|
|
18440
18825
|
{
|
|
18441
18826
|
command: "devrouter stop [path] [--delete] [--json]",
|
|
18442
|
-
purpose: "Stop the exact checkout workspace runtime and routes; --delete explicitly deletes the ownership-proven runtime without removing the checkout."
|
|
18827
|
+
purpose: "Stop the exact checkout workspace runtime and routes, or prove absence after pre-registration failure; --delete explicitly deletes the ownership-proven runtime without removing the checkout."
|
|
18443
18828
|
},
|
|
18444
18829
|
{
|
|
18445
18830
|
command: "devrouter exec [path] -- <command...>",
|
|
@@ -18993,11 +19378,11 @@ function extractCurrentVersionFromRepoConfig(repoPath) {
|
|
|
18993
19378
|
};
|
|
18994
19379
|
}
|
|
18995
19380
|
function readPromptDirectory(promptDirPath) {
|
|
18996
|
-
if (!
|
|
19381
|
+
if (!import_node_fs39.default.existsSync(promptDirPath)) {
|
|
18997
19382
|
throw new Error(`Missing ${UPGRADE_PROMPTS_DIR} directory at ${promptDirPath}.`);
|
|
18998
19383
|
}
|
|
18999
19384
|
const releases = [];
|
|
19000
|
-
const files =
|
|
19385
|
+
const files = import_node_fs39.default.readdirSync(promptDirPath, { withFileTypes: true });
|
|
19001
19386
|
for (const file of files) {
|
|
19002
19387
|
if (!file.isFile() || !file.name.endsWith(".md")) {
|
|
19003
19388
|
continue;
|
|
@@ -19007,8 +19392,8 @@ function readPromptDirectory(promptDirPath) {
|
|
|
19007
19392
|
continue;
|
|
19008
19393
|
}
|
|
19009
19394
|
const version = normalizeVersion(basename);
|
|
19010
|
-
const promptPath =
|
|
19011
|
-
const prompt =
|
|
19395
|
+
const promptPath = import_node_path51.default.join(promptDirPath, file.name);
|
|
19396
|
+
const prompt = import_node_fs39.default.readFileSync(promptPath, "utf-8").trim();
|
|
19012
19397
|
releases.push({ version, prompt, promptPath });
|
|
19013
19398
|
}
|
|
19014
19399
|
const deduped = /* @__PURE__ */ new Map();
|
|
@@ -19029,19 +19414,19 @@ function listAvailableUpgradeTargets(currentVersion, releases) {
|
|
|
19029
19414
|
}
|
|
19030
19415
|
function resolvePromptDirectory(explicitPath) {
|
|
19031
19416
|
if (explicitPath) {
|
|
19032
|
-
return
|
|
19417
|
+
return import_node_path51.default.resolve(explicitPath);
|
|
19033
19418
|
}
|
|
19034
|
-
const entryFile = process.argv[1] ?
|
|
19035
|
-
const entryDir =
|
|
19036
|
-
const resolvedEntryDir =
|
|
19419
|
+
const entryFile = process.argv[1] ? import_node_path51.default.resolve(process.argv[1]) : __filename;
|
|
19420
|
+
const entryDir = import_node_path51.default.dirname(entryFile);
|
|
19421
|
+
const resolvedEntryDir = import_node_fs39.default.existsSync(entryFile) ? import_node_path51.default.dirname(import_node_fs39.default.realpathSync(entryFile)) : entryDir;
|
|
19037
19422
|
const candidates = [
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
|
|
19041
|
-
|
|
19042
|
-
|
|
19423
|
+
import_node_path51.default.resolve(resolvedEntryDir, "..", UPGRADE_PROMPTS_DIR),
|
|
19424
|
+
import_node_path51.default.resolve(entryDir, "..", UPGRADE_PROMPTS_DIR),
|
|
19425
|
+
import_node_path51.default.resolve(__dirname, "..", UPGRADE_PROMPTS_DIR),
|
|
19426
|
+
import_node_path51.default.resolve(__dirname, "..", "..", UPGRADE_PROMPTS_DIR),
|
|
19427
|
+
import_node_path51.default.resolve(process.cwd(), UPGRADE_PROMPTS_DIR)
|
|
19043
19428
|
];
|
|
19044
|
-
const existing = candidates.find((candidate) =>
|
|
19429
|
+
const existing = candidates.find((candidate) => import_node_fs39.default.existsSync(candidate));
|
|
19045
19430
|
return existing ?? candidates[0];
|
|
19046
19431
|
}
|
|
19047
19432
|
function loadUpgradeCatalog(options = {}) {
|
|
@@ -19057,12 +19442,12 @@ function loadUpgradeCatalog(options = {}) {
|
|
|
19057
19442
|
releases
|
|
19058
19443
|
};
|
|
19059
19444
|
}
|
|
19060
|
-
var
|
|
19445
|
+
var import_node_fs39, import_node_path51, SEMVER_RE, UPGRADE_PROMPTS_DIR;
|
|
19061
19446
|
var init_upgrade = __esm({
|
|
19062
19447
|
"src/core/upgrade.ts"() {
|
|
19063
19448
|
"use strict";
|
|
19064
|
-
|
|
19065
|
-
|
|
19449
|
+
import_node_fs39 = __toESM(require("fs"));
|
|
19450
|
+
import_node_path51 = __toESM(require("path"));
|
|
19066
19451
|
init_repo_config();
|
|
19067
19452
|
SEMVER_RE = /^v?(\d+)\.(\d+)\.(\d+)$/;
|
|
19068
19453
|
UPGRADE_PROMPTS_DIR = "upgrade-prompts";
|
|
@@ -19205,7 +19590,7 @@ function isLoopbackHost(host) {
|
|
|
19205
19590
|
return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
|
|
19206
19591
|
}
|
|
19207
19592
|
function inspectCompose(composeFile, _workspace) {
|
|
19208
|
-
if (!
|
|
19593
|
+
if (!import_node_fs40.default.existsSync(composeFile)) {
|
|
19209
19594
|
return {
|
|
19210
19595
|
aliases: [],
|
|
19211
19596
|
publishedPorts: [],
|
|
@@ -19214,7 +19599,7 @@ function inspectCompose(composeFile, _workspace) {
|
|
|
19214
19599
|
};
|
|
19215
19600
|
}
|
|
19216
19601
|
try {
|
|
19217
|
-
const raw =
|
|
19602
|
+
const raw = import_node_fs40.default.readFileSync(composeFile, "utf-8");
|
|
19218
19603
|
const parsed = import_yaml5.default.parse(raw);
|
|
19219
19604
|
const root = asRecord(parsed);
|
|
19220
19605
|
const services = asRecord(root?.services);
|
|
@@ -19310,11 +19695,11 @@ function buildHostPortClaimsCheck(repoPath, workspace) {
|
|
|
19310
19695
|
}
|
|
19311
19696
|
}
|
|
19312
19697
|
function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
19313
|
-
const devcontainerDir =
|
|
19314
|
-
if (!
|
|
19698
|
+
const devcontainerDir = import_node_path52.default.join(repoPath, ".devcontainer");
|
|
19699
|
+
if (!import_node_fs40.default.existsSync(devcontainerDir)) {
|
|
19315
19700
|
return [];
|
|
19316
19701
|
}
|
|
19317
|
-
const compose = inspectCompose(
|
|
19702
|
+
const compose = inspectCompose(import_node_path52.default.join(devcontainerDir, "docker-compose.yml"), workspace);
|
|
19318
19703
|
const checks = [];
|
|
19319
19704
|
if (compose.parseError) {
|
|
19320
19705
|
checks.push({
|
|
@@ -19341,9 +19726,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
19341
19726
|
details: compose.publishedPorts.length > 0 ? compose.publishedPorts.join(", ") : void 0,
|
|
19342
19727
|
suggestion: compose.parseError || compose.publishedPorts.length > 0 ? "Remove published ports and route services through devnet aliases with devrouter proxy apps." : void 0
|
|
19343
19728
|
});
|
|
19344
|
-
const dockerfilePath =
|
|
19345
|
-
const dockerfileExists =
|
|
19346
|
-
const dockerfile = dockerfileExists ?
|
|
19729
|
+
const dockerfilePath = import_node_path52.default.join(devcontainerDir, "Dockerfile");
|
|
19730
|
+
const dockerfileExists = import_node_fs40.default.existsSync(dockerfilePath);
|
|
19731
|
+
const dockerfile = dockerfileExists ? import_node_fs40.default.readFileSync(dockerfilePath, "utf-8") : "";
|
|
19347
19732
|
const devrouterArtifacts = ["@devrouter/cli", "devrouter-process"].filter(
|
|
19348
19733
|
(artifact) => dockerfile.includes(artifact)
|
|
19349
19734
|
);
|
|
@@ -19401,12 +19786,12 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
19401
19786
|
});
|
|
19402
19787
|
return checks;
|
|
19403
19788
|
}
|
|
19404
|
-
var
|
|
19789
|
+
var import_node_fs40, import_node_path52, import_yaml5;
|
|
19405
19790
|
var init_devcontainer_diagnostics = __esm({
|
|
19406
19791
|
"src/core/devcontainer-diagnostics.ts"() {
|
|
19407
19792
|
"use strict";
|
|
19408
|
-
|
|
19409
|
-
|
|
19793
|
+
import_node_fs40 = __toESM(require("fs"));
|
|
19794
|
+
import_node_path52 = __toESM(require("path"));
|
|
19410
19795
|
import_yaml5 = __toESM(require("yaml"));
|
|
19411
19796
|
init_devcontainer_profile();
|
|
19412
19797
|
init_host_port_claims();
|
|
@@ -19501,11 +19886,11 @@ function reconcileRouteRunConflict(repoPath, app) {
|
|
|
19501
19886
|
}
|
|
19502
19887
|
}
|
|
19503
19888
|
}
|
|
19504
|
-
var
|
|
19889
|
+
var import_node_fs41;
|
|
19505
19890
|
var init_route_state = __esm({
|
|
19506
19891
|
"src/core/route-state.ts"() {
|
|
19507
19892
|
"use strict";
|
|
19508
|
-
|
|
19893
|
+
import_node_fs41 = __toESM(require("fs"));
|
|
19509
19894
|
init_host_routes();
|
|
19510
19895
|
init_workspace();
|
|
19511
19896
|
}
|
|
@@ -19680,7 +20065,7 @@ function toRepoStatus(repoPath) {
|
|
|
19680
20065
|
const resolvedRepoPath = resolveRepoPath(repoPath);
|
|
19681
20066
|
const configPath = getRepoConfigPath(resolvedRepoPath);
|
|
19682
20067
|
const explicitRepo = typeof repoPath === "string" && repoPath.trim().length > 0;
|
|
19683
|
-
const configExists =
|
|
20068
|
+
const configExists = import_node_fs42.default.existsSync(configPath);
|
|
19684
20069
|
if (!explicitRepo && !configExists) {
|
|
19685
20070
|
return void 0;
|
|
19686
20071
|
}
|
|
@@ -19828,11 +20213,11 @@ async function collectRouterStatus(repoPath) {
|
|
|
19828
20213
|
}
|
|
19829
20214
|
};
|
|
19830
20215
|
}
|
|
19831
|
-
var
|
|
20216
|
+
var import_node_fs42;
|
|
19832
20217
|
var init_status = __esm({
|
|
19833
20218
|
"src/core/status.ts"() {
|
|
19834
20219
|
"use strict";
|
|
19835
|
-
|
|
20220
|
+
import_node_fs42 = __toESM(require("fs"));
|
|
19836
20221
|
init_docker();
|
|
19837
20222
|
init_managed_runtime_state();
|
|
19838
20223
|
init_managed_runtime_status();
|
|
@@ -19906,12 +20291,12 @@ function parseMinimumNodeMajor(value) {
|
|
|
19906
20291
|
return Number(match[1]);
|
|
19907
20292
|
}
|
|
19908
20293
|
function readPackageJson(repoPath) {
|
|
19909
|
-
const packagePath =
|
|
19910
|
-
if (!
|
|
20294
|
+
const packagePath = import_node_path53.default.join(repoPath, "package.json");
|
|
20295
|
+
if (!import_node_fs43.default.existsSync(packagePath)) {
|
|
19911
20296
|
return void 0;
|
|
19912
20297
|
}
|
|
19913
20298
|
try {
|
|
19914
|
-
return JSON.parse(
|
|
20299
|
+
return JSON.parse(import_node_fs43.default.readFileSync(packagePath, "utf-8"));
|
|
19915
20300
|
} catch {
|
|
19916
20301
|
return void 0;
|
|
19917
20302
|
}
|
|
@@ -20095,13 +20480,13 @@ function buildGlobalToolChecksWithoutTelemetry(repoPath) {
|
|
|
20095
20480
|
checks.push(nodeToolchainCheck(repoPath));
|
|
20096
20481
|
return checks;
|
|
20097
20482
|
}
|
|
20098
|
-
var import_node_child_process29,
|
|
20483
|
+
var import_node_child_process29, import_node_fs43, import_node_path53, DEVSY_DISABLE_TELEMETRY;
|
|
20099
20484
|
var init_tool_diagnostics = __esm({
|
|
20100
20485
|
"src/core/tool-diagnostics.ts"() {
|
|
20101
20486
|
"use strict";
|
|
20102
20487
|
import_node_child_process29 = require("child_process");
|
|
20103
|
-
|
|
20104
|
-
|
|
20488
|
+
import_node_fs43 = __toESM(require("fs"));
|
|
20489
|
+
import_node_path53 = __toESM(require("path"));
|
|
20105
20490
|
init_devsy_agent();
|
|
20106
20491
|
init_workspace_runtime();
|
|
20107
20492
|
DEVSY_DISABLE_TELEMETRY = "DEVSY_DISABLE_TELEMETRY";
|
|
@@ -20118,10 +20503,10 @@ function inRepositoryWorkspaceScope(repoPath, worktreePath, livePaths) {
|
|
|
20118
20503
|
if (livePaths.some((candidate) => sameWorkspacePath(candidate, worktreePath))) return true;
|
|
20119
20504
|
const comparableRepo = comparableWorkspacePath(repoPath);
|
|
20120
20505
|
const comparableWorktree = comparableWorkspacePath(worktreePath);
|
|
20121
|
-
const localRoot =
|
|
20506
|
+
const localRoot = import_node_path54.default.join(comparableRepo, "trees") + import_node_path54.default.sep;
|
|
20122
20507
|
if (comparableWorktree.startsWith(localRoot)) return true;
|
|
20123
|
-
const legacyPrefix = `${
|
|
20124
|
-
return
|
|
20508
|
+
const legacyPrefix = `${import_node_path54.default.basename(comparableRepo)}-`;
|
|
20509
|
+
return import_node_path54.default.dirname(comparableWorktree) === import_node_path54.default.dirname(comparableRepo) && import_node_path54.default.basename(comparableWorktree).startsWith(legacyPrefix);
|
|
20125
20510
|
}
|
|
20126
20511
|
function previewActions(devpodStatus, routeCount, includeRecord) {
|
|
20127
20512
|
const actions = [
|
|
@@ -20373,11 +20758,11 @@ function applyWorkspaceGc(plan) {
|
|
|
20373
20758
|
candidates
|
|
20374
20759
|
};
|
|
20375
20760
|
}
|
|
20376
|
-
var
|
|
20761
|
+
var import_node_path54;
|
|
20377
20762
|
var init_workspace_gc = __esm({
|
|
20378
20763
|
"src/core/workspace-gc.ts"() {
|
|
20379
20764
|
"use strict";
|
|
20380
|
-
|
|
20765
|
+
import_node_path54 = __toESM(require("path"));
|
|
20381
20766
|
init_devpod_mutation();
|
|
20382
20767
|
init_devpod_workspaces();
|
|
20383
20768
|
init_host_routes();
|
|
@@ -20447,11 +20832,11 @@ function inspectPostgresCredentials(repoPath, config) {
|
|
|
20447
20832
|
parseErrors.push(`${app.name}: ${composeFile} (${message})`);
|
|
20448
20833
|
continue;
|
|
20449
20834
|
}
|
|
20450
|
-
if (!
|
|
20835
|
+
if (!import_node_fs44.default.existsSync(absolutePath)) {
|
|
20451
20836
|
continue;
|
|
20452
20837
|
}
|
|
20453
20838
|
try {
|
|
20454
|
-
const raw =
|
|
20839
|
+
const raw = import_node_fs44.default.readFileSync(absolutePath, "utf-8");
|
|
20455
20840
|
const parsed = import_yaml6.default.parse(raw);
|
|
20456
20841
|
const root = asRecord2(parsed);
|
|
20457
20842
|
const services = asRecord2(root?.services);
|
|
@@ -20731,7 +21116,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
20731
21116
|
const config = runtimeConfig.config;
|
|
20732
21117
|
loadedConfig = config;
|
|
20733
21118
|
loadedWorkspace = runtimeConfig.workspace;
|
|
20734
|
-
const cliVersion = true ? "0.0.
|
|
21119
|
+
const cliVersion = true ? "0.0.75" : "0.0.0-dev";
|
|
20735
21120
|
const configVersion = config.devrouter?.version;
|
|
20736
21121
|
if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
|
|
20737
21122
|
addCheck(checks, {
|
|
@@ -20761,9 +21146,9 @@ async function buildDoctorReport(options = {}) {
|
|
|
20761
21146
|
(app) => app.docker.composeFiles.map((filePath) => ({
|
|
20762
21147
|
app: app.name,
|
|
20763
21148
|
filePath,
|
|
20764
|
-
absolutePath:
|
|
21149
|
+
absolutePath: import_node_path55.default.resolve(repo.path, filePath)
|
|
20765
21150
|
}))
|
|
20766
|
-
).filter((entry) => !
|
|
21151
|
+
).filter((entry) => !import_node_fs44.default.existsSync(entry.absolutePath));
|
|
20767
21152
|
addCheck(checks, {
|
|
20768
21153
|
id: "repo.compose-files",
|
|
20769
21154
|
level: missingComposeFiles.length === 0 ? "ok" : "error",
|
|
@@ -20806,8 +21191,8 @@ async function buildDoctorReport(options = {}) {
|
|
|
20806
21191
|
const missingHostCwds = config.apps.filter((app) => app.runtime === "host").map((app) => ({
|
|
20807
21192
|
app: app.name,
|
|
20808
21193
|
cwd: app.hostRun.cwd,
|
|
20809
|
-
absolutePath:
|
|
20810
|
-
})).filter((entry) => !
|
|
21194
|
+
absolutePath: import_node_path55.default.resolve(repo.path, app.hostRun.cwd)
|
|
21195
|
+
})).filter((entry) => !import_node_fs44.default.existsSync(entry.absolutePath));
|
|
20811
21196
|
addCheck(checks, {
|
|
20812
21197
|
id: "repo.host-cwd",
|
|
20813
21198
|
level: missingHostCwds.length === 0 ? "ok" : "error",
|
|
@@ -20951,12 +21336,12 @@ async function buildDoctorReport(options = {}) {
|
|
|
20951
21336
|
nextSteps
|
|
20952
21337
|
};
|
|
20953
21338
|
}
|
|
20954
|
-
var
|
|
21339
|
+
var import_node_fs44, import_node_path55, import_yaml6, POSTGRES_DEFAULTS;
|
|
20955
21340
|
var init_doctor = __esm({
|
|
20956
21341
|
"src/core/doctor.ts"() {
|
|
20957
21342
|
"use strict";
|
|
20958
|
-
|
|
20959
|
-
|
|
21343
|
+
import_node_fs44 = __toESM(require("fs"));
|
|
21344
|
+
import_node_path55 = __toESM(require("path"));
|
|
20960
21345
|
import_yaml6 = __toESM(require("yaml"));
|
|
20961
21346
|
init_docker();
|
|
20962
21347
|
init_host_routes();
|
|
@@ -21454,6 +21839,11 @@ async function runStopCommand(options) {
|
|
|
21454
21839
|
return;
|
|
21455
21840
|
}
|
|
21456
21841
|
const label2 = result.kind === "primary" ? "Primary checkout" : `Workspace '${result.workspace}'`;
|
|
21842
|
+
if (result.runtimeAbsent) {
|
|
21843
|
+
process.stdout.write(`${label2} has no runtime or routes; stop is complete.
|
|
21844
|
+
`);
|
|
21845
|
+
return;
|
|
21846
|
+
}
|
|
21457
21847
|
if (!result.stopped && !result.deleted && result.freedRoutes === 0) {
|
|
21458
21848
|
process.stdout.write(`${label2} is already stopped; no routes needed removal.
|
|
21459
21849
|
`);
|
|
@@ -21774,17 +22164,17 @@ function asRecord3(value) {
|
|
|
21774
22164
|
return value;
|
|
21775
22165
|
}
|
|
21776
22166
|
function readJson(filePath) {
|
|
21777
|
-
if (!
|
|
22167
|
+
if (!import_node_fs45.default.existsSync(filePath)) {
|
|
21778
22168
|
return void 0;
|
|
21779
22169
|
}
|
|
21780
22170
|
try {
|
|
21781
|
-
return JSON.parse(
|
|
22171
|
+
return JSON.parse(import_node_fs45.default.readFileSync(filePath, "utf-8"));
|
|
21782
22172
|
} catch {
|
|
21783
22173
|
return void 0;
|
|
21784
22174
|
}
|
|
21785
22175
|
}
|
|
21786
22176
|
function relative(repoPath, filePath) {
|
|
21787
|
-
return
|
|
22177
|
+
return import_node_path56.default.relative(repoPath, filePath) || ".";
|
|
21788
22178
|
}
|
|
21789
22179
|
function redactEnvAssignments(value) {
|
|
21790
22180
|
return value.replace(
|
|
@@ -21825,7 +22215,7 @@ function inspectPackageManager(repoPath, pkg) {
|
|
|
21825
22215
|
["bun.lock", "bun"]
|
|
21826
22216
|
];
|
|
21827
22217
|
for (const [fileName, name] of lockfiles) {
|
|
21828
|
-
if (
|
|
22218
|
+
if (import_node_fs45.default.existsSync(import_node_path56.default.join(repoPath, fileName))) {
|
|
21829
22219
|
return { name, source: fileName };
|
|
21830
22220
|
}
|
|
21831
22221
|
}
|
|
@@ -21840,9 +22230,9 @@ function inspectNode(repoPath, pkg) {
|
|
|
21840
22230
|
if (typeof engines?.node === "string") {
|
|
21841
22231
|
return { version: engines.node, source: "package.json:engines.node" };
|
|
21842
22232
|
}
|
|
21843
|
-
const nvmrc =
|
|
21844
|
-
if (
|
|
21845
|
-
const version =
|
|
22233
|
+
const nvmrc = import_node_path56.default.join(repoPath, ".nvmrc");
|
|
22234
|
+
if (import_node_fs45.default.existsSync(nvmrc)) {
|
|
22235
|
+
const version = import_node_fs45.default.readFileSync(nvmrc, "utf-8").trim();
|
|
21846
22236
|
return { version, source: ".nvmrc" };
|
|
21847
22237
|
}
|
|
21848
22238
|
return void 0;
|
|
@@ -21903,7 +22293,7 @@ function configuredComposeFiles(repoPath) {
|
|
|
21903
22293
|
const files = config.apps.filter(
|
|
21904
22294
|
(app) => app.runtime === "docker"
|
|
21905
22295
|
).flatMap((app) => app.docker.composeFiles).filter(
|
|
21906
|
-
(fileName) => !
|
|
22296
|
+
(fileName) => !import_node_path56.default.isAbsolute(fileName) && !import_node_path56.default.normalize(fileName).startsWith("..")
|
|
21907
22297
|
);
|
|
21908
22298
|
return Array.from(new Set(files));
|
|
21909
22299
|
} catch {
|
|
@@ -21921,7 +22311,7 @@ function composeFiles(repoPath) {
|
|
|
21921
22311
|
...configuredComposeFiles(repoPath)
|
|
21922
22312
|
];
|
|
21923
22313
|
return Array.from(new Set(candidates)).filter(
|
|
21924
|
-
(fileName) =>
|
|
22314
|
+
(fileName) => import_node_fs45.default.existsSync(import_node_path56.default.join(repoPath, fileName))
|
|
21925
22315
|
);
|
|
21926
22316
|
}
|
|
21927
22317
|
function stringArray2(value) {
|
|
@@ -21959,7 +22349,7 @@ function inspectServices(repoPath) {
|
|
|
21959
22349
|
const services = [];
|
|
21960
22350
|
for (const fileName of composeFiles(repoPath)) {
|
|
21961
22351
|
try {
|
|
21962
|
-
const parsed = import_yaml7.default.parse(
|
|
22352
|
+
const parsed = import_yaml7.default.parse(import_node_fs45.default.readFileSync(import_node_path56.default.join(repoPath, fileName), "utf-8"));
|
|
21963
22353
|
const serviceMap = asRecord3(asRecord3(parsed)?.services);
|
|
21964
22354
|
for (const [name, value] of Object.entries(serviceMap ?? {})) {
|
|
21965
22355
|
const service = asRecord3(value);
|
|
@@ -21994,8 +22384,8 @@ function inspectServices(repoPath) {
|
|
|
21994
22384
|
return services;
|
|
21995
22385
|
}
|
|
21996
22386
|
function inspectEnvFiles(repoPath) {
|
|
21997
|
-
const files =
|
|
21998
|
-
const content =
|
|
22387
|
+
const files = import_node_fs45.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
|
|
22388
|
+
const content = import_node_fs45.default.readFileSync(import_node_path56.default.join(repoPath, fileName), "utf-8");
|
|
21999
22389
|
const names = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && line.includes("=")).map((line) => line.split("=")[0]?.trim()).filter((name) => Boolean(name)).sort();
|
|
22000
22390
|
return { path: fileName, names };
|
|
22001
22391
|
});
|
|
@@ -22009,18 +22399,18 @@ function inspectEnvFiles(repoPath) {
|
|
|
22009
22399
|
};
|
|
22010
22400
|
}
|
|
22011
22401
|
function inspectDevcontainer(repoPath) {
|
|
22012
|
-
const dir =
|
|
22013
|
-
if (!
|
|
22402
|
+
const dir = import_node_path56.default.join(repoPath, ".devcontainer");
|
|
22403
|
+
if (!import_node_fs45.default.existsSync(dir)) {
|
|
22014
22404
|
return { exists: false, files: [] };
|
|
22015
22405
|
}
|
|
22016
22406
|
return {
|
|
22017
22407
|
exists: true,
|
|
22018
|
-
files:
|
|
22408
|
+
files: import_node_fs45.default.readdirSync(dir).filter((fileName) => import_node_fs45.default.statSync(import_node_path56.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
|
|
22019
22409
|
};
|
|
22020
22410
|
}
|
|
22021
22411
|
function inspectDevrouter(repoPath) {
|
|
22022
22412
|
const configPath = getRepoConfigPath(repoPath);
|
|
22023
|
-
if (!
|
|
22413
|
+
if (!import_node_fs45.default.existsSync(configPath)) {
|
|
22024
22414
|
return {
|
|
22025
22415
|
exists: false,
|
|
22026
22416
|
configPath,
|
|
@@ -22064,15 +22454,15 @@ function inspectAgentGuidance(repoPath) {
|
|
|
22064
22454
|
["AGENTS.md", "agents"],
|
|
22065
22455
|
["CLAUDE.md", "claude"]
|
|
22066
22456
|
]) {
|
|
22067
|
-
if (
|
|
22457
|
+
if (import_node_fs45.default.existsSync(import_node_path56.default.join(repoPath, fileName))) {
|
|
22068
22458
|
results.push({ path: fileName, kind });
|
|
22069
22459
|
}
|
|
22070
22460
|
}
|
|
22071
|
-
const skillsDir =
|
|
22072
|
-
if (
|
|
22073
|
-
for (const name of
|
|
22074
|
-
const skillPath =
|
|
22075
|
-
if (
|
|
22461
|
+
const skillsDir = import_node_path56.default.join(repoPath, ".agents", "skills");
|
|
22462
|
+
if (import_node_fs45.default.existsSync(skillsDir)) {
|
|
22463
|
+
for (const name of import_node_fs45.default.readdirSync(skillsDir).sort()) {
|
|
22464
|
+
const skillPath = import_node_path56.default.join(skillsDir, name, "SKILL.md");
|
|
22465
|
+
if (import_node_fs45.default.existsSync(skillPath)) {
|
|
22076
22466
|
results.push({ path: relative(repoPath, skillPath), kind: "skill" });
|
|
22077
22467
|
}
|
|
22078
22468
|
}
|
|
@@ -22115,7 +22505,7 @@ function buildIssues(report) {
|
|
|
22115
22505
|
}
|
|
22116
22506
|
function inspectRepo(options = {}) {
|
|
22117
22507
|
const repoPath = resolveRepoPath(options.repo);
|
|
22118
|
-
const pkg = readJson(
|
|
22508
|
+
const pkg = readJson(import_node_path56.default.join(repoPath, "package.json"));
|
|
22119
22509
|
const scripts = inspectScripts(pkg);
|
|
22120
22510
|
const reportWithoutIssues = {
|
|
22121
22511
|
repoPath,
|
|
@@ -22134,12 +22524,12 @@ function inspectRepo(options = {}) {
|
|
|
22134
22524
|
issues: buildIssues(reportWithoutIssues)
|
|
22135
22525
|
};
|
|
22136
22526
|
}
|
|
22137
|
-
var
|
|
22527
|
+
var import_node_fs45, import_node_path56, import_yaml7;
|
|
22138
22528
|
var init_repo_inspect = __esm({
|
|
22139
22529
|
"src/core/repo-inspect.ts"() {
|
|
22140
22530
|
"use strict";
|
|
22141
|
-
|
|
22142
|
-
|
|
22531
|
+
import_node_fs45 = __toESM(require("fs"));
|
|
22532
|
+
import_node_path56 = __toESM(require("path"));
|
|
22143
22533
|
import_yaml7 = __toESM(require("yaml"));
|
|
22144
22534
|
init_repo_config();
|
|
22145
22535
|
}
|
|
@@ -22244,7 +22634,7 @@ function requiredFileChecks(repoPath) {
|
|
|
22244
22634
|
".devcontainer/docker-compose.yml",
|
|
22245
22635
|
".devrouter.yml"
|
|
22246
22636
|
];
|
|
22247
|
-
const missing = required.filter((fileName) => !
|
|
22637
|
+
const missing = required.filter((fileName) => !import_node_fs46.default.existsSync(import_node_path57.default.join(repoPath, fileName)));
|
|
22248
22638
|
return {
|
|
22249
22639
|
id: "repo.devcontainer.verify-files",
|
|
22250
22640
|
level: missing.length === 0 ? "ok" : "error",
|
|
@@ -22469,12 +22859,12 @@ async function verifyDevcontainer(options = {}) {
|
|
|
22469
22859
|
nextSteps: collectNextSteps3(checks)
|
|
22470
22860
|
};
|
|
22471
22861
|
}
|
|
22472
|
-
var
|
|
22862
|
+
var import_node_fs46, import_node_path57;
|
|
22473
22863
|
var init_devcontainer_verify = __esm({
|
|
22474
22864
|
"src/core/devcontainer-verify.ts"() {
|
|
22475
22865
|
"use strict";
|
|
22476
|
-
|
|
22477
|
-
|
|
22866
|
+
import_node_fs46 = __toESM(require("fs"));
|
|
22867
|
+
import_node_path57 = __toESM(require("path"));
|
|
22478
22868
|
init_capabilities();
|
|
22479
22869
|
init_doctor();
|
|
22480
22870
|
init_host_routes();
|
|
@@ -22771,7 +23161,7 @@ function packageManagerIssues(repo) {
|
|
|
22771
23161
|
}
|
|
22772
23162
|
function plannedFiles(repoPath, version) {
|
|
22773
23163
|
const repo = inspectRepo({ repo: repoPath });
|
|
22774
|
-
const projectName = sanitizeProjectName(
|
|
23164
|
+
const projectName = sanitizeProjectName(import_node_path58.default.basename(repo.repoPath));
|
|
22775
23165
|
const nodeMajor = majorVersion(repo.node?.version, "24");
|
|
22776
23166
|
const pnpmVersion = repo.packageManager?.name === "pnpm" && repo.packageManager.version ? repo.packageManager.version : DEFAULT_PNPM_VERSION;
|
|
22777
23167
|
const port = inferPort2(repo);
|
|
@@ -22816,8 +23206,8 @@ function plannedFiles(repoPath, version) {
|
|
|
22816
23206
|
};
|
|
22817
23207
|
}
|
|
22818
23208
|
function classifyFile(repoPath, file) {
|
|
22819
|
-
const absolutePath =
|
|
22820
|
-
if (!
|
|
23209
|
+
const absolutePath = import_node_path58.default.join(repoPath, file.relativePath);
|
|
23210
|
+
if (!import_node_fs47.default.existsSync(absolutePath)) {
|
|
22821
23211
|
return {
|
|
22822
23212
|
path: file.relativePath,
|
|
22823
23213
|
action: "create",
|
|
@@ -22825,7 +23215,7 @@ function classifyFile(repoPath, file) {
|
|
|
22825
23215
|
bytes: Buffer.byteLength(file.content)
|
|
22826
23216
|
};
|
|
22827
23217
|
}
|
|
22828
|
-
const current =
|
|
23218
|
+
const current = import_node_fs47.default.readFileSync(absolutePath, "utf-8");
|
|
22829
23219
|
if (!current.includes(MANAGED_MARKER2)) {
|
|
22830
23220
|
return {
|
|
22831
23221
|
path: file.relativePath,
|
|
@@ -22882,11 +23272,11 @@ function buildPlan(repoPath, dryRun, version) {
|
|
|
22882
23272
|
};
|
|
22883
23273
|
}
|
|
22884
23274
|
function writeFile(repoPath, file) {
|
|
22885
|
-
const absolutePath =
|
|
22886
|
-
|
|
22887
|
-
|
|
23275
|
+
const absolutePath = import_node_path58.default.join(repoPath, file.relativePath);
|
|
23276
|
+
import_node_fs47.default.mkdirSync(import_node_path58.default.dirname(absolutePath), { recursive: true });
|
|
23277
|
+
import_node_fs47.default.writeFileSync(absolutePath, file.content, "utf-8");
|
|
22888
23278
|
if (file.executable) {
|
|
22889
|
-
|
|
23279
|
+
import_node_fs47.default.chmodSync(absolutePath, 493);
|
|
22890
23280
|
}
|
|
22891
23281
|
}
|
|
22892
23282
|
function writeDevcontainer(options = {}) {
|
|
@@ -22924,12 +23314,12 @@ function writeDevcontainer(options = {}) {
|
|
|
22924
23314
|
nextSteps: postWriteNextSteps(repoPath)
|
|
22925
23315
|
};
|
|
22926
23316
|
}
|
|
22927
|
-
var
|
|
23317
|
+
var import_node_fs47, import_node_path58, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
|
|
22928
23318
|
var init_devcontainer_write = __esm({
|
|
22929
23319
|
"src/core/devcontainer-write.ts"() {
|
|
22930
23320
|
"use strict";
|
|
22931
|
-
|
|
22932
|
-
|
|
23321
|
+
import_node_fs47 = __toESM(require("fs"));
|
|
23322
|
+
import_node_path58 = __toESM(require("path"));
|
|
22933
23323
|
init_repo_config();
|
|
22934
23324
|
init_repo_inspect();
|
|
22935
23325
|
MANAGED_MARKER2 = "devrouter:managed devcontainer";
|
|
@@ -23263,13 +23653,13 @@ function buildProfilePlanReport(options) {
|
|
|
23263
23653
|
return { ...report, contractPath, bindings };
|
|
23264
23654
|
}
|
|
23265
23655
|
function loadContract(repoPath, contractPath) {
|
|
23266
|
-
if (
|
|
23656
|
+
if (import_node_path59.default.isAbsolute(contractPath)) {
|
|
23267
23657
|
fail3("profile plan contract path must be relative to the repository.");
|
|
23268
23658
|
}
|
|
23269
23659
|
const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
|
|
23270
23660
|
let metadata;
|
|
23271
23661
|
try {
|
|
23272
|
-
metadata =
|
|
23662
|
+
metadata = import_node_fs48.default.lstatSync(absolutePath);
|
|
23273
23663
|
} catch (error) {
|
|
23274
23664
|
fail3(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
|
|
23275
23665
|
}
|
|
@@ -23279,16 +23669,16 @@ function loadContract(repoPath, contractPath) {
|
|
|
23279
23669
|
if (metadata.size > MAX_CONTRACT_BYTES) {
|
|
23280
23670
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
23281
23671
|
}
|
|
23282
|
-
const realRepo =
|
|
23283
|
-
const realContract =
|
|
23672
|
+
const realRepo = import_node_fs48.default.realpathSync(repoPath);
|
|
23673
|
+
const realContract = import_node_fs48.default.realpathSync(absolutePath);
|
|
23284
23674
|
assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
|
|
23285
|
-
const source =
|
|
23675
|
+
const source = import_node_fs48.default.readFileSync(absolutePath, "utf-8");
|
|
23286
23676
|
if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
|
|
23287
23677
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
23288
23678
|
}
|
|
23289
23679
|
return {
|
|
23290
23680
|
contract: parseProfilePlanContract(source),
|
|
23291
|
-
contractPath:
|
|
23681
|
+
contractPath: import_node_path59.default.relative(repoPath, absolutePath).split(import_node_path59.default.sep).join("/")
|
|
23292
23682
|
};
|
|
23293
23683
|
}
|
|
23294
23684
|
function resolveProfilePlan(options) {
|
|
@@ -23302,12 +23692,12 @@ function resolveProfilePlan(options) {
|
|
|
23302
23692
|
contractPath: loaded.contractPath
|
|
23303
23693
|
});
|
|
23304
23694
|
}
|
|
23305
|
-
var
|
|
23695
|
+
var import_node_fs48, import_node_path59, import_yaml8, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
|
|
23306
23696
|
var init_profile_plan = __esm({
|
|
23307
23697
|
"src/core/profile-plan.ts"() {
|
|
23308
23698
|
"use strict";
|
|
23309
|
-
|
|
23310
|
-
|
|
23699
|
+
import_node_fs48 = __toESM(require("fs"));
|
|
23700
|
+
import_node_path59 = __toESM(require("path"));
|
|
23311
23701
|
import_yaml8 = require("yaml");
|
|
23312
23702
|
init_paths();
|
|
23313
23703
|
init_profile_resolution();
|
|
@@ -23339,7 +23729,7 @@ async function runProfileResolveCommand(options) {
|
|
|
23339
23729
|
async function runProfilePlanCommand(options) {
|
|
23340
23730
|
const report = resolveProfilePlan(options);
|
|
23341
23731
|
if (options.output) {
|
|
23342
|
-
writeFileAtomically(
|
|
23732
|
+
writeFileAtomically(import_node_path60.default.resolve(options.output), `${JSON.stringify(report, null, 2)}
|
|
23343
23733
|
`);
|
|
23344
23734
|
}
|
|
23345
23735
|
if (options.json) {
|
|
@@ -23375,14 +23765,14 @@ function printProfilePlanSummary(report, output2) {
|
|
|
23375
23765
|
process.stdout.write(`Binding ${key}: ${renderValues(report.bindings[key] ?? [])}
|
|
23376
23766
|
`);
|
|
23377
23767
|
}
|
|
23378
|
-
if (output2) process.stdout.write(`Output: ${
|
|
23768
|
+
if (output2) process.stdout.write(`Output: ${import_node_path60.default.resolve(output2)}
|
|
23379
23769
|
`);
|
|
23380
23770
|
}
|
|
23381
|
-
var
|
|
23771
|
+
var import_node_path60;
|
|
23382
23772
|
var init_profile = __esm({
|
|
23383
23773
|
"src/commands/profile.ts"() {
|
|
23384
23774
|
"use strict";
|
|
23385
|
-
|
|
23775
|
+
import_node_path60 = __toESM(require("path"));
|
|
23386
23776
|
init_atomic_file();
|
|
23387
23777
|
init_output();
|
|
23388
23778
|
init_profile_plan();
|
|
@@ -23672,7 +24062,7 @@ function sanitizeRouterId(value) {
|
|
|
23672
24062
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
23673
24063
|
}
|
|
23674
24064
|
function repoHash(repoPath) {
|
|
23675
|
-
return (0, import_node_crypto26.createHash)("sha1").update(
|
|
24065
|
+
return (0, import_node_crypto26.createHash)("sha1").update(import_node_path61.default.resolve(repoPath)).digest("hex").slice(0, 12);
|
|
23676
24066
|
}
|
|
23677
24067
|
function asDockerApp(app) {
|
|
23678
24068
|
return app.runtime === "docker";
|
|
@@ -23751,11 +24141,11 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
|
|
|
23751
24141
|
if (dockerApps.length === 0) {
|
|
23752
24142
|
throw new Error("No docker apps selected to prepare compose overlay.");
|
|
23753
24143
|
}
|
|
23754
|
-
const cachePath =
|
|
23755
|
-
|
|
23756
|
-
const overlayPath =
|
|
24144
|
+
const cachePath = import_node_path61.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
|
|
24145
|
+
import_node_fs49.default.mkdirSync(cachePath, { recursive: true });
|
|
24146
|
+
const overlayPath = import_node_path61.default.join(cachePath, "compose.devrouter.yml");
|
|
23757
24147
|
const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
|
|
23758
|
-
|
|
24148
|
+
import_node_fs49.default.writeFileSync(overlayPath, import_yaml9.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
|
|
23759
24149
|
return {
|
|
23760
24150
|
overlayPath,
|
|
23761
24151
|
composeFiles: ensureComposeFiles(dockerApps),
|
|
@@ -23875,14 +24265,14 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
23875
24265
|
const port = Number(match[1]);
|
|
23876
24266
|
return Number.isInteger(port) && port > 0 ? port : void 0;
|
|
23877
24267
|
}
|
|
23878
|
-
var import_node_child_process33, import_node_crypto26,
|
|
24268
|
+
var import_node_child_process33, import_node_crypto26, import_node_fs49, import_node_path61, import_yaml9;
|
|
23879
24269
|
var init_docker_run = __esm({
|
|
23880
24270
|
"src/core/docker-run.ts"() {
|
|
23881
24271
|
"use strict";
|
|
23882
24272
|
import_node_child_process33 = require("child_process");
|
|
23883
24273
|
import_node_crypto26 = require("crypto");
|
|
23884
|
-
|
|
23885
|
-
|
|
24274
|
+
import_node_fs49 = __toESM(require("fs"));
|
|
24275
|
+
import_node_path61 = __toESM(require("path"));
|
|
23886
24276
|
import_yaml9 = __toESM(require("yaml"));
|
|
23887
24277
|
init_docker_error_guidance();
|
|
23888
24278
|
init_paths();
|
|
@@ -24704,13 +25094,13 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
24704
25094
|
const startedAt = Date.now();
|
|
24705
25095
|
let rootStat;
|
|
24706
25096
|
try {
|
|
24707
|
-
rootStat =
|
|
25097
|
+
rootStat = import_node_fs50.default.lstatSync(worktreePath);
|
|
24708
25098
|
} catch (error) {
|
|
24709
25099
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
24710
25100
|
}
|
|
24711
25101
|
let rootEntries;
|
|
24712
25102
|
try {
|
|
24713
|
-
rootEntries =
|
|
25103
|
+
rootEntries = import_node_fs50.default.readdirSync(worktreePath, { withFileTypes: true });
|
|
24714
25104
|
} catch (error) {
|
|
24715
25105
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
24716
25106
|
}
|
|
@@ -24736,10 +25126,10 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
24736
25126
|
timedOut = true;
|
|
24737
25127
|
break;
|
|
24738
25128
|
}
|
|
24739
|
-
const entryPath =
|
|
25129
|
+
const entryPath = import_node_path62.default.join(dirPath, entry.name);
|
|
24740
25130
|
let entryStat;
|
|
24741
25131
|
try {
|
|
24742
|
-
entryStat =
|
|
25132
|
+
entryStat = import_node_fs50.default.lstatSync(entryPath);
|
|
24743
25133
|
} catch (error) {
|
|
24744
25134
|
if (error?.code === "ENOENT") continue;
|
|
24745
25135
|
unreadableReason = describeIncompleteWalk(error);
|
|
@@ -24749,7 +25139,7 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
24749
25139
|
if (!entryStat.isDirectory()) continue;
|
|
24750
25140
|
let childEntries;
|
|
24751
25141
|
try {
|
|
24752
|
-
childEntries =
|
|
25142
|
+
childEntries = import_node_fs50.default.readdirSync(entryPath, { withFileTypes: true });
|
|
24753
25143
|
} catch (error) {
|
|
24754
25144
|
unreadableReason = describeIncompleteWalk(error);
|
|
24755
25145
|
break;
|
|
@@ -24813,12 +25203,12 @@ function describeError(error, worktreePath) {
|
|
|
24813
25203
|
}
|
|
24814
25204
|
return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
|
|
24815
25205
|
}
|
|
24816
|
-
var
|
|
25206
|
+
var import_node_fs50, import_node_path62, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
|
|
24817
25207
|
var init_workspace_consumption = __esm({
|
|
24818
25208
|
"src/core/workspace-consumption.ts"() {
|
|
24819
25209
|
"use strict";
|
|
24820
|
-
|
|
24821
|
-
|
|
25210
|
+
import_node_fs50 = __toESM(require("fs"));
|
|
25211
|
+
import_node_path62 = __toESM(require("path"));
|
|
24822
25212
|
init_devpod_environment();
|
|
24823
25213
|
DEFAULT_DEADLINE_MS = 1e4;
|
|
24824
25214
|
BLOCK_SIZE_BYTES = 512;
|
|
@@ -24919,7 +25309,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
|
|
|
24919
25309
|
}
|
|
24920
25310
|
function readGitSnapshot(worktree, commandRunner) {
|
|
24921
25311
|
const comparablePath = comparableWorkspacePath(worktree.path);
|
|
24922
|
-
if (worktree.prunable || !
|
|
25312
|
+
if (worktree.prunable || !import_node_fs51.default.existsSync(comparablePath)) {
|
|
24923
25313
|
return { worktree, checkout: "missing", head: null, committerDate: null };
|
|
24924
25314
|
}
|
|
24925
25315
|
const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
|
|
@@ -25444,7 +25834,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
25444
25834
|
containers2 = measureContainersFn(worktreePaths);
|
|
25445
25835
|
} catch (error) {
|
|
25446
25836
|
const reason = `container measurement failed: ${describeCause(error)}`;
|
|
25447
|
-
containers2 = new Map(worktreePaths.map((
|
|
25837
|
+
containers2 = new Map(worktreePaths.map((path63) => [path63, unknownContainers(reason)]));
|
|
25448
25838
|
}
|
|
25449
25839
|
}
|
|
25450
25840
|
const rows2 = records.map((record5) => {
|
|
@@ -25494,12 +25884,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
25494
25884
|
networkCapacity: (dependencies.inspectNetworkCapacity ?? inspectNetworkCapacity)()
|
|
25495
25885
|
};
|
|
25496
25886
|
}
|
|
25497
|
-
var import_node_child_process35,
|
|
25887
|
+
var import_node_child_process35, import_node_fs51, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
|
|
25498
25888
|
var init_workspace_cleanup = __esm({
|
|
25499
25889
|
"src/core/workspace-cleanup.ts"() {
|
|
25500
25890
|
"use strict";
|
|
25501
25891
|
import_node_child_process35 = require("child_process");
|
|
25502
|
-
|
|
25892
|
+
import_node_fs51 = __toESM(require("fs"));
|
|
25503
25893
|
init_devpod_workspaces();
|
|
25504
25894
|
init_host_routes();
|
|
25505
25895
|
init_network_diagnostics();
|
|
@@ -25611,7 +26001,7 @@ function warnMissingWorkspaceOwnership(repoPath) {
|
|
|
25611
26001
|
);
|
|
25612
26002
|
}
|
|
25613
26003
|
function defaultWorktreePath(mainRepo, ws) {
|
|
25614
|
-
return
|
|
26004
|
+
return import_node_path63.default.join(mainRepo, "trees", ws);
|
|
25615
26005
|
}
|
|
25616
26006
|
function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
25617
26007
|
const ignored = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
|
|
@@ -25619,12 +26009,12 @@ function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
|
25619
26009
|
});
|
|
25620
26010
|
if (ignored.status !== 0) {
|
|
25621
26011
|
throw new Error(
|
|
25622
|
-
`Default worktree root '${
|
|
26012
|
+
`Default worktree root '${import_node_path63.default.join(mainRepo, "trees")}' is not ignored. Add 'trees/' to '${import_node_path63.default.join(mainRepo, ".gitignore")}' or use --path.`
|
|
25623
26013
|
);
|
|
25624
26014
|
}
|
|
25625
26015
|
}
|
|
25626
26016
|
function legacyDefaultWorktreePath(mainRepo, ws) {
|
|
25627
|
-
return
|
|
26017
|
+
return import_node_path63.default.join(import_node_path63.default.dirname(mainRepo), `${import_node_path63.default.basename(mainRepo)}-${ws}`);
|
|
25628
26018
|
}
|
|
25629
26019
|
function teardownFallbackPath(mainRepo, workspace) {
|
|
25630
26020
|
const candidates = [
|
|
@@ -25736,7 +26126,7 @@ function assertFullDownPreflight(mainRepo, target) {
|
|
|
25736
26126
|
if (sameWorkspacePath(target.worktreePath, mainRepo)) {
|
|
25737
26127
|
throw new Error("Refusing to remove the primary Git checkout.");
|
|
25738
26128
|
}
|
|
25739
|
-
if (!target.worktree || target.worktree.prunable || !
|
|
26129
|
+
if (!target.worktree || target.worktree.prunable || !import_node_fs52.default.existsSync(target.worktreePath)) return;
|
|
25740
26130
|
if (target.worktree.locked) {
|
|
25741
26131
|
throw new Error(
|
|
25742
26132
|
`Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
|
|
@@ -25776,9 +26166,9 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
25776
26166
|
(candidate) => defaultWorktreePath(mainRepo, candidate)
|
|
25777
26167
|
);
|
|
25778
26168
|
const generatedPath = candidatePaths.find(
|
|
25779
|
-
(candidate) => !
|
|
26169
|
+
(candidate) => !import_node_fs52.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
|
|
25780
26170
|
);
|
|
25781
|
-
const selectedPath = opts.path ?
|
|
26171
|
+
const selectedPath = opts.path ? import_node_path63.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
|
|
25782
26172
|
if (!selectedPath) {
|
|
25783
26173
|
throw new Error(`Could not allocate a collision-safe worktree path for branch '${branch}'.`);
|
|
25784
26174
|
}
|
|
@@ -25787,7 +26177,7 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
25787
26177
|
`Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
|
|
25788
26178
|
);
|
|
25789
26179
|
}
|
|
25790
|
-
if (
|
|
26180
|
+
if (import_node_fs52.default.existsSync(selectedPath)) {
|
|
25791
26181
|
const registered = worktrees.find(
|
|
25792
26182
|
(worktree) => sameWorkspacePath(worktree.path, selectedPath)
|
|
25793
26183
|
);
|
|
@@ -25890,7 +26280,7 @@ async function mutateWorkspaceRuntime(action2, resolved, worktrees, quiet = fals
|
|
|
25890
26280
|
);
|
|
25891
26281
|
}
|
|
25892
26282
|
const retained = action2 === "stop" ? readManagedRuntimeState(resolved.worktreePath, resolved.workspace) : void 0;
|
|
25893
|
-
let absent =
|
|
26283
|
+
let absent = mutation.status === "proven-absent";
|
|
25894
26284
|
const remove = () => {
|
|
25895
26285
|
if (retained?.stopBaseline) {
|
|
25896
26286
|
absent = proveManagedStop(retained).status === "proven-absent";
|
|
@@ -25899,6 +26289,11 @@ async function mutateWorkspaceRuntime(action2, resolved, worktrees, quiet = fals
|
|
|
25899
26289
|
))
|
|
25900
26290
|
throw new Error("Absent stop contains routes outside the retained desired set.");
|
|
25901
26291
|
}
|
|
26292
|
+
if (absent && !retained) {
|
|
26293
|
+
if (listRoutesForWorktreePath(resolved.worktreePath).length)
|
|
26294
|
+
throw new Error("Initial managed stop observed remaining workspace routes.");
|
|
26295
|
+
return [];
|
|
26296
|
+
}
|
|
25902
26297
|
claimLifecycleEffect();
|
|
25903
26298
|
return removeWorkspaceRoutesForWorktree(resolved.workspace, resolved.worktreePath);
|
|
25904
26299
|
};
|
|
@@ -25934,7 +26329,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25934
26329
|
workspace: resolved.workspace,
|
|
25935
26330
|
devpodId: stopped.devpodId,
|
|
25936
26331
|
freedRoutes: stopped.freedRoutes,
|
|
25937
|
-
providerChanged: stopped.stopped
|
|
26332
|
+
providerChanged: stopped.stopped && !stopped.runtimeAbsent,
|
|
26333
|
+
...stopped.runtimeAbsent ? { runtimeAbsent: true } : {}
|
|
25938
26334
|
};
|
|
25939
26335
|
}
|
|
25940
26336
|
const operation = async () => {
|
|
@@ -25944,7 +26340,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25944
26340
|
}
|
|
25945
26341
|
const result = await mutateWorkspaceRuntime("delete", resolved, worktrees, opts.quiet);
|
|
25946
26342
|
if (removeWorktree) {
|
|
25947
|
-
if (resolved.worktree && !resolved.worktree.prunable &&
|
|
26343
|
+
if (resolved.worktree && !resolved.worktree.prunable && import_node_fs52.default.existsSync(resolved.worktreePath)) {
|
|
25948
26344
|
const rm = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
|
|
25949
26345
|
encoding: "utf-8"
|
|
25950
26346
|
});
|
|
@@ -25963,7 +26359,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25963
26359
|
}
|
|
25964
26360
|
return result;
|
|
25965
26361
|
};
|
|
25966
|
-
return resolved.worktree && !resolved.worktree.prunable &&
|
|
26362
|
+
return resolved.worktree && !resolved.worktree.prunable && import_node_fs52.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
|
|
25967
26363
|
}
|
|
25968
26364
|
async function workspaceStop(target, opts = {}) {
|
|
25969
26365
|
return runWorkspaceLifecycle("stop", target, opts);
|
|
@@ -25971,13 +26367,13 @@ async function workspaceStop(target, opts = {}) {
|
|
|
25971
26367
|
async function workspaceDown(target, opts = {}) {
|
|
25972
26368
|
return runWorkspaceLifecycle("down", target, opts);
|
|
25973
26369
|
}
|
|
25974
|
-
var import_node_child_process36,
|
|
26370
|
+
var import_node_child_process36, import_node_fs52, import_node_path63, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
|
|
25975
26371
|
var init_workspace_lifecycle = __esm({
|
|
25976
26372
|
"src/core/workspace-lifecycle.ts"() {
|
|
25977
26373
|
"use strict";
|
|
25978
26374
|
import_node_child_process36 = require("child_process");
|
|
25979
|
-
|
|
25980
|
-
|
|
26375
|
+
import_node_fs52 = __toESM(require("fs"));
|
|
26376
|
+
import_node_path63 = __toESM(require("path"));
|
|
25981
26377
|
init_devpod_mutation();
|
|
25982
26378
|
init_devpod_workspaces();
|
|
25983
26379
|
init_devsy_mutation();
|
|
@@ -26161,7 +26557,7 @@ var init_version = __esm({
|
|
|
26161
26557
|
|
|
26162
26558
|
// src/cli.ts
|
|
26163
26559
|
var import_commander = require("commander");
|
|
26164
|
-
var CLI_VERSION = true ? "0.0.
|
|
26560
|
+
var CLI_VERSION = true ? "0.0.75" : "0.0.0-dev";
|
|
26165
26561
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
26166
26562
|
function withErrorHandling(action2) {
|
|
26167
26563
|
return async (...args) => {
|
|
@@ -26534,10 +26930,10 @@ workspaceCommand2.command("down").description("Delete a workspace's runtime and
|
|
|
26534
26930
|
workspaceCommand2.command("journal").description("Inspect and recover workspace lifecycle journals").command("settle").description(
|
|
26535
26931
|
"Settle an interrupted lifecycle operation as unobservable so ensure and stop can proceed"
|
|
26536
26932
|
).argument("[path]", "Checkout path (defaults to current directory)").option("--json", "Output JSON").action(
|
|
26537
|
-
withErrorHandling(async (
|
|
26933
|
+
withErrorHandling(async (path63, _options, command) => {
|
|
26538
26934
|
const options = command.opts();
|
|
26539
26935
|
const { runWorkspaceJournalSettleCommand: runWorkspaceJournalSettleCommand2 } = await Promise.resolve().then(() => (init_workspace2(), workspace_exports));
|
|
26540
|
-
await runWorkspaceJournalSettleCommand2({ ...
|
|
26936
|
+
await runWorkspaceJournalSettleCommand2({ ...path63 ? { path: path63 } : {}, ...options });
|
|
26541
26937
|
})
|
|
26542
26938
|
);
|
|
26543
26939
|
function parseVersionRequest(argv) {
|