@devrouter/cli 0.0.73 → 0.0.74
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 +5199 -4964
- package/dist/devrouter.js +753 -518
- package/package.json +1 -1
- package/upgrade-prompts/0.0.74.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.74" : "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.74" : "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) {
|
|
@@ -8517,8 +8567,10 @@ function listDevpodWorkspacesRaw(options = {}) {
|
|
|
8517
8567
|
const result = (0, import_node_child_process9.spawnSync)("devpod", ["list", "--output", "json", "--skip-pro"], {
|
|
8518
8568
|
encoding: "utf-8"
|
|
8519
8569
|
});
|
|
8520
|
-
if (
|
|
8521
|
-
return
|
|
8570
|
+
if (result.error?.code === "ENOENT") {
|
|
8571
|
+
if (options.readLocalWhenMissing) return readLocalDevpodRegistry();
|
|
8572
|
+
if (options.allowMissingExecutable) return [];
|
|
8573
|
+
}
|
|
8522
8574
|
if (result.error || result.status !== 0) {
|
|
8523
8575
|
const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
8524
8576
|
throw new Error(`devpod list failed: ${details || "devpod is not installed or unavailable"}`);
|
|
@@ -8551,11 +8603,110 @@ function listDevpodWorkspacesRaw(options = {}) {
|
|
|
8551
8603
|
return workspace;
|
|
8552
8604
|
});
|
|
8553
8605
|
}
|
|
8554
|
-
|
|
8606
|
+
function devpodRegistryRoot() {
|
|
8607
|
+
return import_node_path26.default.resolve(process.env.DEVPOD_HOME || import_node_path26.default.join(import_node_os3.default.homedir(), ".devpod"));
|
|
8608
|
+
}
|
|
8609
|
+
function readLocalDevpodRegistry() {
|
|
8610
|
+
const fail4 = () => new Error("Local DevPod registry evidence is incomplete, unreadable, or changed.");
|
|
8611
|
+
const stamps = /* @__PURE__ */ new Map();
|
|
8612
|
+
const stamp = (stat, identityOnly = false) => JSON.stringify([
|
|
8613
|
+
stat.dev,
|
|
8614
|
+
stat.ino,
|
|
8615
|
+
stat.mode,
|
|
8616
|
+
stat.uid,
|
|
8617
|
+
...identityOnly ? [] : [stat.size, stat.mtimeMs, stat.ctimeMs]
|
|
8618
|
+
]);
|
|
8619
|
+
const inspect = (file) => {
|
|
8620
|
+
try {
|
|
8621
|
+
return import_node_fs19.default.lstatSync(file);
|
|
8622
|
+
} catch (error) {
|
|
8623
|
+
if (error.code === "ENOENT") return void 0;
|
|
8624
|
+
throw fail4();
|
|
8625
|
+
}
|
|
8626
|
+
};
|
|
8627
|
+
const directory = (dir, optional = false, identityOnly = false) => {
|
|
8628
|
+
const stat = inspect(dir);
|
|
8629
|
+
if (!stat && !optional || stat && !stat.isDirectory()) throw fail4();
|
|
8630
|
+
stamps.set(dir, { value: stat && stamp(stat, identityOnly), identityOnly });
|
|
8631
|
+
return !!stat;
|
|
8632
|
+
};
|
|
8633
|
+
const entries = (dir) => {
|
|
8634
|
+
const names = import_node_fs19.default.readdirSync(dir).sort();
|
|
8635
|
+
if (names.length > 256) throw fail4();
|
|
8636
|
+
return names;
|
|
8637
|
+
};
|
|
8638
|
+
const result = [];
|
|
8639
|
+
try {
|
|
8640
|
+
const root = devpodRegistryRoot();
|
|
8641
|
+
let ancestor = import_node_path26.default.parse(root).root;
|
|
8642
|
+
let present = directory(ancestor, false, ancestor !== root);
|
|
8643
|
+
for (const part of root.slice(ancestor.length).split(import_node_path26.default.sep).filter(Boolean)) {
|
|
8644
|
+
if (!present) break;
|
|
8645
|
+
ancestor = import_node_path26.default.join(ancestor, part);
|
|
8646
|
+
present = directory(ancestor, true, ancestor !== root);
|
|
8647
|
+
}
|
|
8648
|
+
const contexts = import_node_path26.default.join(root, "contexts");
|
|
8649
|
+
if (present && directory(contexts, true)) {
|
|
8650
|
+
for (const context of entries(contexts)) {
|
|
8651
|
+
const contextDir = import_node_path26.default.join(contexts, context);
|
|
8652
|
+
directory(contextDir);
|
|
8653
|
+
const workspaces = import_node_path26.default.join(contextDir, "workspaces");
|
|
8654
|
+
if (!directory(workspaces, true)) continue;
|
|
8655
|
+
for (const id5 of entries(workspaces)) {
|
|
8656
|
+
if (result.length >= 256) throw fail4();
|
|
8657
|
+
const workspace = import_node_path26.default.join(workspaces, id5);
|
|
8658
|
+
directory(workspace);
|
|
8659
|
+
const file = import_node_path26.default.join(workspace, "workspace.json");
|
|
8660
|
+
const stat = inspect(file);
|
|
8661
|
+
if (!stat?.isFile() || stat.size > 1024 * 1024) throw fail4();
|
|
8662
|
+
stamps.set(file, { value: stamp(stat), identityOnly: false });
|
|
8663
|
+
const fd = import_node_fs19.default.openSync(file, import_node_fs19.default.constants.O_RDONLY | import_node_fs19.default.constants.O_NOFOLLOW);
|
|
8664
|
+
let raw;
|
|
8665
|
+
try {
|
|
8666
|
+
if (stamp(import_node_fs19.default.fstatSync(fd)) !== stamp(stat)) throw fail4();
|
|
8667
|
+
const buffer = Buffer.alloc(stat.size + 1);
|
|
8668
|
+
const bytes2 = import_node_fs19.default.readSync(fd, buffer, 0, buffer.length, 0);
|
|
8669
|
+
if (bytes2 !== stat.size || stamp(import_node_fs19.default.fstatSync(fd)) !== stamp(stat)) throw fail4();
|
|
8670
|
+
raw = JSON.parse(buffer.subarray(0, bytes2).toString("utf8"));
|
|
8671
|
+
} finally {
|
|
8672
|
+
import_node_fs19.default.closeSync(fd);
|
|
8673
|
+
}
|
|
8674
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw fail4();
|
|
8675
|
+
const value = raw;
|
|
8676
|
+
if (value.id !== id5 || !value.source || typeof value.source !== "object" || Array.isArray(value.source))
|
|
8677
|
+
throw fail4();
|
|
8678
|
+
const source = value.source;
|
|
8679
|
+
if (source.localFolder !== void 0 && (typeof source.localFolder !== "string" || source.localFolder !== "" && !import_node_path26.default.isAbsolute(source.localFolder)))
|
|
8680
|
+
throw fail4();
|
|
8681
|
+
const remote = ["gitRepository", "image", "container"];
|
|
8682
|
+
if (remote.some((key) => source[key] !== void 0 && typeof source[key] !== "string"))
|
|
8683
|
+
throw fail4();
|
|
8684
|
+
if (!source.localFolder && !remote.some((key) => typeof source[key] === "string" && source[key]))
|
|
8685
|
+
throw fail4();
|
|
8686
|
+
result.push({
|
|
8687
|
+
id: id5,
|
|
8688
|
+
source: { localFolder: source.localFolder ?? "" }
|
|
8689
|
+
});
|
|
8690
|
+
}
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
for (const [file, expected] of stamps) {
|
|
8694
|
+
const actual = inspect(file);
|
|
8695
|
+
if ((actual && stamp(actual, expected.identityOnly)) !== expected.value) throw fail4();
|
|
8696
|
+
}
|
|
8697
|
+
return result;
|
|
8698
|
+
} catch {
|
|
8699
|
+
throw fail4();
|
|
8700
|
+
}
|
|
8701
|
+
}
|
|
8702
|
+
var import_node_child_process9, import_node_fs19, import_node_os3, import_node_path26;
|
|
8555
8703
|
var init_devpod_registry = __esm({
|
|
8556
8704
|
"src/core/devpod-registry.ts"() {
|
|
8557
8705
|
"use strict";
|
|
8558
8706
|
import_node_child_process9 = require("child_process");
|
|
8707
|
+
import_node_fs19 = __toESM(require("fs"));
|
|
8708
|
+
import_node_os3 = __toESM(require("os"));
|
|
8709
|
+
import_node_path26 = __toESM(require("path"));
|
|
8559
8710
|
}
|
|
8560
8711
|
});
|
|
8561
8712
|
|
|
@@ -8573,13 +8724,13 @@ function proveManagedComposePopulation(options) {
|
|
|
8573
8724
|
}
|
|
8574
8725
|
services.add(service);
|
|
8575
8726
|
const files = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").map((file) => file.trim());
|
|
8576
|
-
if (files.some((file) => !file || !
|
|
8727
|
+
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
8728
|
throw new Error("Managed stop Compose file identity changed.");
|
|
8578
8729
|
}
|
|
8579
8730
|
for (const file of files.slice(plan.composeFiles.length)) {
|
|
8580
|
-
if (
|
|
8581
|
-
|
|
8582
|
-
) ||
|
|
8731
|
+
if (import_node_path27.default.dirname(file) !== featureDirectory || !/^docker-compose\.devcontainer\.containerFeatures-[a-zA-Z0-9_-]+\.yml$/.test(
|
|
8732
|
+
import_node_path27.default.basename(file)
|
|
8733
|
+
) || 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
8734
|
throw new Error("Managed stop refuses a foreign or escaped provider Compose file.");
|
|
8584
8735
|
}
|
|
8585
8736
|
}
|
|
@@ -8597,12 +8748,12 @@ function proveManagedComposePopulation(options) {
|
|
|
8597
8748
|
}
|
|
8598
8749
|
return primary;
|
|
8599
8750
|
}
|
|
8600
|
-
var
|
|
8751
|
+
var import_node_fs20, import_node_path27;
|
|
8601
8752
|
var init_managed_compose_population = __esm({
|
|
8602
8753
|
"src/core/managed-compose-population.ts"() {
|
|
8603
8754
|
"use strict";
|
|
8604
|
-
|
|
8605
|
-
|
|
8755
|
+
import_node_fs20 = __toESM(require("fs"));
|
|
8756
|
+
import_node_path27 = __toESM(require("path"));
|
|
8606
8757
|
init_workspace();
|
|
8607
8758
|
}
|
|
8608
8759
|
});
|
|
@@ -8638,7 +8789,7 @@ function isRuntimeInstalled(runtime) {
|
|
|
8638
8789
|
function readWorkspaceRuntimeConfig() {
|
|
8639
8790
|
let raw;
|
|
8640
8791
|
try {
|
|
8641
|
-
raw = JSON.parse(
|
|
8792
|
+
raw = JSON.parse(import_node_fs21.default.readFileSync(RUNTIME_CONFIG_FILE, "utf-8"));
|
|
8642
8793
|
} catch {
|
|
8643
8794
|
return {};
|
|
8644
8795
|
}
|
|
@@ -8659,7 +8810,7 @@ function readWorkspaceRuntimeConfig() {
|
|
|
8659
8810
|
function inspectWorkspaceRuntimeConfig() {
|
|
8660
8811
|
let rawText;
|
|
8661
8812
|
try {
|
|
8662
|
-
rawText =
|
|
8813
|
+
rawText = import_node_fs21.default.readFileSync(RUNTIME_CONFIG_FILE, "utf-8");
|
|
8663
8814
|
} catch {
|
|
8664
8815
|
return { exists: false, config: {}, problems: [] };
|
|
8665
8816
|
}
|
|
@@ -8710,7 +8861,7 @@ function writeWorkspaceRuntimeConfig(config) {
|
|
|
8710
8861
|
}
|
|
8711
8862
|
next.devsyInactivityTimeout = timeout;
|
|
8712
8863
|
}
|
|
8713
|
-
|
|
8864
|
+
import_node_fs21.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
8714
8865
|
writeFileAtomically(RUNTIME_CONFIG_FILE, `${JSON.stringify(next, null, 2)}
|
|
8715
8866
|
`);
|
|
8716
8867
|
}
|
|
@@ -8818,20 +8969,20 @@ function resetWorkspaceRuntimeCaches() {
|
|
|
8818
8969
|
cachedRuntime = void 0;
|
|
8819
8970
|
cachedSnapshots = void 0;
|
|
8820
8971
|
}
|
|
8821
|
-
var import_node_child_process10,
|
|
8972
|
+
var import_node_child_process10, import_node_fs21, import_node_path28, SUPPORTED_RUNTIMES2, RUNTIME_CONFIG_FILE, INACTIVITY_TIMEOUT_PATTERN, cachedRuntime, cachedSnapshots, UnsupportedWorkspaceRuntimeError, WorkspaceRuntimeOwnershipError;
|
|
8822
8973
|
var init_workspace_runtime = __esm({
|
|
8823
8974
|
"src/core/workspace-runtime.ts"() {
|
|
8824
8975
|
"use strict";
|
|
8825
8976
|
import_node_child_process10 = require("child_process");
|
|
8826
|
-
|
|
8827
|
-
|
|
8977
|
+
import_node_fs21 = __toESM(require("fs"));
|
|
8978
|
+
import_node_path28 = __toESM(require("path"));
|
|
8828
8979
|
init_atomic_file();
|
|
8829
8980
|
init_devpod_registry();
|
|
8830
8981
|
init_devsy_workspaces();
|
|
8831
8982
|
init_router();
|
|
8832
8983
|
init_workspace();
|
|
8833
8984
|
SUPPORTED_RUNTIMES2 = ["devpod", "devsy"];
|
|
8834
|
-
RUNTIME_CONFIG_FILE =
|
|
8985
|
+
RUNTIME_CONFIG_FILE = import_node_path28.default.join(DEVROUTER_HOME, "workspace-runtime.json");
|
|
8835
8986
|
INACTIVITY_TIMEOUT_PATTERN = /^(?:\d+(?:ms|s|m|h))+$/;
|
|
8836
8987
|
UnsupportedWorkspaceRuntimeError = class extends Error {
|
|
8837
8988
|
};
|
|
@@ -8958,7 +9109,7 @@ function resolveGitCommonDir(repoPath) {
|
|
|
8958
9109
|
if (result.status !== 0 || !output2) {
|
|
8959
9110
|
throw commandError("Could not resolve the Git common directory", repoPath, result.stderr);
|
|
8960
9111
|
}
|
|
8961
|
-
return comparableWorkspacePath(
|
|
9112
|
+
return comparableWorkspacePath(import_node_path29.default.isAbsolute(output2) ? output2 : import_node_path29.default.resolve(repoPath, output2));
|
|
8962
9113
|
}
|
|
8963
9114
|
function resolveGitTopLevel(repoPath) {
|
|
8964
9115
|
const result = (0, import_node_child_process12.spawnSync)("git", ["-C", repoPath, "rev-parse", "--show-toplevel"], {
|
|
@@ -9009,7 +9160,7 @@ function listGitWorktrees(repoPath) {
|
|
|
9009
9160
|
return worktrees;
|
|
9010
9161
|
}
|
|
9011
9162
|
function ownershipDirectory(repoPath) {
|
|
9012
|
-
return
|
|
9163
|
+
return import_node_path29.default.join(resolveGitCommonDir(repoPath), OWNERSHIP_DIR);
|
|
9013
9164
|
}
|
|
9014
9165
|
function validateWorkspace(value, label2) {
|
|
9015
9166
|
if (typeof value !== "string" || wsFromBranch(value) !== value) {
|
|
@@ -9037,7 +9188,7 @@ function validateRecord(value, expectedWorkspace) {
|
|
|
9037
9188
|
`workspace ownership file '${expectedWorkspace}' contains identity '${workspace}'`
|
|
9038
9189
|
);
|
|
9039
9190
|
}
|
|
9040
|
-
if (typeof candidate.worktreePath !== "string" || !
|
|
9191
|
+
if (typeof candidate.worktreePath !== "string" || !import_node_path29.default.isAbsolute(candidate.worktreePath)) {
|
|
9041
9192
|
throw new Error("invalid workspace ownership worktreePath");
|
|
9042
9193
|
}
|
|
9043
9194
|
if (candidate.branch !== null && typeof candidate.branch !== "string") {
|
|
@@ -9055,7 +9206,7 @@ function validateRecord(value, expectedWorkspace) {
|
|
|
9055
9206
|
};
|
|
9056
9207
|
}
|
|
9057
9208
|
function recordPath(repoPath, workspace) {
|
|
9058
|
-
return
|
|
9209
|
+
return import_node_path29.default.join(
|
|
9059
9210
|
ownershipDirectory(repoPath),
|
|
9060
9211
|
`${validateWorkspace(workspace, "workspace")}.json`
|
|
9061
9212
|
);
|
|
@@ -9063,7 +9214,7 @@ function recordPath(repoPath, workspace) {
|
|
|
9063
9214
|
function readRecordFile(filePath, expectedWorkspace) {
|
|
9064
9215
|
let parsed;
|
|
9065
9216
|
try {
|
|
9066
|
-
parsed = JSON.parse(
|
|
9217
|
+
parsed = JSON.parse(import_node_fs22.default.readFileSync(filePath, "utf-8"));
|
|
9067
9218
|
} catch (error) {
|
|
9068
9219
|
if (error instanceof SyntaxError) {
|
|
9069
9220
|
throw new Error(`invalid workspace ownership JSON at '${filePath}'`);
|
|
@@ -9088,7 +9239,7 @@ function listWorkspaceOwnership(repoPath) {
|
|
|
9088
9239
|
function listWorkspaceOwnershipInDirectory(directory) {
|
|
9089
9240
|
let entries;
|
|
9090
9241
|
try {
|
|
9091
|
-
entries =
|
|
9242
|
+
entries = import_node_fs22.default.readdirSync(directory, { withFileTypes: true });
|
|
9092
9243
|
} catch (error) {
|
|
9093
9244
|
if (error.code === "ENOENT") return [];
|
|
9094
9245
|
throw error;
|
|
@@ -9096,14 +9247,14 @@ function listWorkspaceOwnershipInDirectory(directory) {
|
|
|
9096
9247
|
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).sort((left, right) => left.name.localeCompare(right.name)).map((entry) => {
|
|
9097
9248
|
const workspace = entry.name.slice(0, -".json".length);
|
|
9098
9249
|
validateWorkspace(workspace, "filename");
|
|
9099
|
-
return readRecordFile(
|
|
9250
|
+
return readRecordFile(import_node_path29.default.join(directory, entry.name), workspace);
|
|
9100
9251
|
});
|
|
9101
9252
|
}
|
|
9102
9253
|
function writeWorkspaceOwnershipInDirectory(directory, input2) {
|
|
9103
9254
|
const workspace = validateWorkspace(input2.workspace, "workspace");
|
|
9104
9255
|
const devpodId = validateWorkspace(input2.devpodId, "devpodId");
|
|
9105
9256
|
const worktreePath = comparableWorkspacePath(input2.worktreePath);
|
|
9106
|
-
const filePath =
|
|
9257
|
+
const filePath = import_node_path29.default.join(directory, `${workspace}.json`);
|
|
9107
9258
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9108
9259
|
const records = listWorkspaceOwnershipInDirectory(directory);
|
|
9109
9260
|
const existing = records.find((record6) => record6.workspace === workspace);
|
|
@@ -9139,9 +9290,9 @@ function writeWorkspaceOwnershipInDirectory(directory, input2) {
|
|
|
9139
9290
|
return record5;
|
|
9140
9291
|
}
|
|
9141
9292
|
function removeWorkspaceOwnershipInDirectory(directory, workspace) {
|
|
9142
|
-
const filePath =
|
|
9293
|
+
const filePath = import_node_path29.default.join(directory, `${validateWorkspace(workspace, "workspace")}.json`);
|
|
9143
9294
|
try {
|
|
9144
|
-
|
|
9295
|
+
import_node_fs22.default.rmSync(filePath);
|
|
9145
9296
|
return true;
|
|
9146
9297
|
} catch (error) {
|
|
9147
9298
|
if (error.code === "ENOENT") return false;
|
|
@@ -9152,7 +9303,7 @@ function sameOwnershipRecord(left, right) {
|
|
|
9152
9303
|
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
9304
|
}
|
|
9154
9305
|
function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
|
|
9155
|
-
const filePath =
|
|
9306
|
+
const filePath = import_node_path29.default.join(
|
|
9156
9307
|
directory,
|
|
9157
9308
|
`${validateWorkspace(expected.workspace, "workspace")}.json`
|
|
9158
9309
|
);
|
|
@@ -9164,14 +9315,14 @@ function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
|
|
|
9164
9315
|
throw error;
|
|
9165
9316
|
}
|
|
9166
9317
|
if (!sameOwnershipRecord(current, expected)) return "changed";
|
|
9167
|
-
|
|
9318
|
+
import_node_fs22.default.rmSync(filePath);
|
|
9168
9319
|
return "removed";
|
|
9169
9320
|
}
|
|
9170
9321
|
function withWorkspaceOwnershipTransaction(repoPath, operation, options = {}) {
|
|
9171
9322
|
const directory = ownershipDirectory(repoPath);
|
|
9172
|
-
|
|
9323
|
+
import_node_fs22.default.mkdirSync(directory, { recursive: true });
|
|
9173
9324
|
return withFileLockSync(
|
|
9174
|
-
|
|
9325
|
+
import_node_path29.default.join(directory, ".lock"),
|
|
9175
9326
|
{
|
|
9176
9327
|
activity: "workspace ownership transaction",
|
|
9177
9328
|
target: `'${repoPath}'`,
|
|
@@ -9204,7 +9355,7 @@ function persistedWorkspaceOwners(repoPath, worktreePath) {
|
|
|
9204
9355
|
try {
|
|
9205
9356
|
workspace = readPersistedWorkspace(worktree.path);
|
|
9206
9357
|
} catch (error) {
|
|
9207
|
-
if (!
|
|
9358
|
+
if (!import_node_fs22.default.existsSync(worktree.path)) continue;
|
|
9208
9359
|
throw error;
|
|
9209
9360
|
}
|
|
9210
9361
|
if (!workspace) continue;
|
|
@@ -9367,7 +9518,7 @@ function inspectWorkspaceOwnership(record5, worktrees, devpods) {
|
|
|
9367
9518
|
if (worktree?.locked) {
|
|
9368
9519
|
return { ownerStatus: "locked", devpodStatus, worktree };
|
|
9369
9520
|
}
|
|
9370
|
-
if ((!worktree || worktree.prunable) &&
|
|
9521
|
+
if ((!worktree || worktree.prunable) && import_node_fs22.default.existsSync(record5.worktreePath)) {
|
|
9371
9522
|
return { ownerStatus: "conflict", devpodStatus, worktree };
|
|
9372
9523
|
}
|
|
9373
9524
|
if (!worktree || worktree.prunable) {
|
|
@@ -9391,20 +9542,20 @@ function listMissingWorkspaceOwnership(repoPath) {
|
|
|
9391
9542
|
(record5) => inspectWorkspaceOwnership(record5, worktrees, void 0).ownerStatus === "missing"
|
|
9392
9543
|
);
|
|
9393
9544
|
}
|
|
9394
|
-
var import_node_child_process12,
|
|
9545
|
+
var import_node_child_process12, import_node_fs22, import_node_path29, READ_ONLY_GIT_ENV, OWNERSHIP_VERSION, OWNERSHIP_DIR;
|
|
9395
9546
|
var init_workspace_ownership = __esm({
|
|
9396
9547
|
"src/core/workspace-ownership.ts"() {
|
|
9397
9548
|
"use strict";
|
|
9398
9549
|
import_node_child_process12 = require("child_process");
|
|
9399
|
-
|
|
9400
|
-
|
|
9550
|
+
import_node_fs22 = __toESM(require("fs"));
|
|
9551
|
+
import_node_path29 = __toESM(require("path"));
|
|
9401
9552
|
init_atomic_file();
|
|
9402
9553
|
init_devpod_workspaces();
|
|
9403
9554
|
init_file_lock();
|
|
9404
9555
|
init_workspace();
|
|
9405
9556
|
READ_ONLY_GIT_ENV = { ...process.env, GIT_OPTIONAL_LOCKS: "0" };
|
|
9406
9557
|
OWNERSHIP_VERSION = 1;
|
|
9407
|
-
OWNERSHIP_DIR =
|
|
9558
|
+
OWNERSHIP_DIR = import_node_path29.default.join("devrouter", "workspaces");
|
|
9408
9559
|
}
|
|
9409
9560
|
});
|
|
9410
9561
|
|
|
@@ -9653,12 +9804,12 @@ function managedStopRouteReferences(state) {
|
|
|
9653
9804
|
(name) => ["http", "tcp"].map((protocol) => ({ repoPath: state.repoPath, name, protocol }))
|
|
9654
9805
|
);
|
|
9655
9806
|
}
|
|
9656
|
-
var
|
|
9807
|
+
var import_node_os4, import_node_path30, import_node_util8;
|
|
9657
9808
|
var init_managed_stop_recovery = __esm({
|
|
9658
9809
|
"src/core/managed-stop-recovery.ts"() {
|
|
9659
9810
|
"use strict";
|
|
9660
|
-
|
|
9661
|
-
|
|
9811
|
+
import_node_os4 = __toESM(require("os"));
|
|
9812
|
+
import_node_path30 = __toESM(require("path"));
|
|
9662
9813
|
import_node_util8 = require("util");
|
|
9663
9814
|
init_devpod_environment();
|
|
9664
9815
|
init_devpod_registry();
|
|
@@ -9805,8 +9956,8 @@ function tlsSetupCommand(repoPath) {
|
|
|
9805
9956
|
}
|
|
9806
9957
|
function getMkcertRootCAPath(options = {}) {
|
|
9807
9958
|
ensureMkcert();
|
|
9808
|
-
const rootCAPath =
|
|
9809
|
-
if (!
|
|
9959
|
+
const rootCAPath = import_node_path31.default.join(runOrThrow("mkcert", ["-CAROOT"]), "rootCA.pem");
|
|
9960
|
+
if (!import_node_fs23.default.existsSync(rootCAPath)) {
|
|
9810
9961
|
throw new Error(
|
|
9811
9962
|
`mkcert root CA was not found at '${rootCAPath}'. Run: ${tlsSetupCommand(options.repoPath)}`
|
|
9812
9963
|
);
|
|
@@ -9907,11 +10058,11 @@ function compactTLSCertificateHosts(hosts) {
|
|
|
9907
10058
|
);
|
|
9908
10059
|
}
|
|
9909
10060
|
function readCurrentCertificateHosts(options = {}) {
|
|
9910
|
-
if (!
|
|
10061
|
+
if (!import_node_fs23.default.existsSync(CERT_FILE)) {
|
|
9911
10062
|
return [];
|
|
9912
10063
|
}
|
|
9913
10064
|
try {
|
|
9914
|
-
const pem =
|
|
10065
|
+
const pem = import_node_fs23.default.readFileSync(CERT_FILE, "utf-8");
|
|
9915
10066
|
return parseCertificateDnsHosts(pem);
|
|
9916
10067
|
} catch (error) {
|
|
9917
10068
|
if (options.replaceMalformed) {
|
|
@@ -10039,20 +10190,20 @@ Run: ${tlsSetupCommand(options.repoPath)}`
|
|
|
10039
10190
|
);
|
|
10040
10191
|
}
|
|
10041
10192
|
}
|
|
10042
|
-
var import_node_child_process14, import_node_crypto12,
|
|
10193
|
+
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
10194
|
var init_tls = __esm({
|
|
10044
10195
|
"src/core/tls.ts"() {
|
|
10045
10196
|
"use strict";
|
|
10046
10197
|
import_node_child_process14 = require("child_process");
|
|
10047
10198
|
import_node_crypto12 = require("crypto");
|
|
10048
|
-
|
|
10049
|
-
|
|
10199
|
+
import_node_fs23 = __toESM(require("fs"));
|
|
10200
|
+
import_node_path31 = __toESM(require("path"));
|
|
10050
10201
|
init_docker();
|
|
10051
10202
|
init_file_lock();
|
|
10052
10203
|
init_host_routes();
|
|
10053
10204
|
init_router();
|
|
10054
10205
|
DEFAULT_TLS_CERT_HOSTS = ["localhost", "*.localhost"];
|
|
10055
|
-
TLS_CERTIFICATE_LOCK_FILE =
|
|
10206
|
+
TLS_CERTIFICATE_LOCK_FILE = import_node_path31.default.join(DEVROUTER_HOME, "tls-certificate.lock");
|
|
10056
10207
|
TLS_CERTIFICATE_LOCK_WAIT_MS = 6e4;
|
|
10057
10208
|
TLS_CERTIFICATE_WRITE_ATTEMPTS = 2;
|
|
10058
10209
|
}
|
|
@@ -10133,6 +10284,174 @@ var init_route_publication = __esm({
|
|
|
10133
10284
|
}
|
|
10134
10285
|
});
|
|
10135
10286
|
|
|
10287
|
+
// src/core/traefik-route-health.ts
|
|
10288
|
+
function sleep(ms) {
|
|
10289
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10290
|
+
}
|
|
10291
|
+
function inspectRouterApi(protocol) {
|
|
10292
|
+
const result = (0, import_node_child_process15.spawnSync)(
|
|
10293
|
+
"curl",
|
|
10294
|
+
[
|
|
10295
|
+
"--silent",
|
|
10296
|
+
"--show-error",
|
|
10297
|
+
"--fail",
|
|
10298
|
+
"--max-time",
|
|
10299
|
+
ROUTER_API_REQUEST_TIMEOUT_SECONDS,
|
|
10300
|
+
`${ROUTER_API_BASE}/${protocol}/routers?per_page=${ROUTER_API_PAGE_SIZE}`
|
|
10301
|
+
],
|
|
10302
|
+
{ encoding: "utf-8" }
|
|
10303
|
+
);
|
|
10304
|
+
if (result.status !== 0) {
|
|
10305
|
+
return {
|
|
10306
|
+
ok: false,
|
|
10307
|
+
details: result.stderr?.trim() || `curl exited with status ${result.status ?? "unknown"}`
|
|
10308
|
+
};
|
|
10309
|
+
}
|
|
10310
|
+
let value;
|
|
10311
|
+
try {
|
|
10312
|
+
value = JSON.parse(result.stdout);
|
|
10313
|
+
} catch {
|
|
10314
|
+
return { ok: false, details: `${protocol.toUpperCase()} router API returned invalid JSON` };
|
|
10315
|
+
}
|
|
10316
|
+
if (!Array.isArray(value)) {
|
|
10317
|
+
return { ok: false, details: `${protocol.toUpperCase()} router API returned a non-array` };
|
|
10318
|
+
}
|
|
10319
|
+
if (value.some((item) => !isRecord5(item) || typeof item.name !== "string" || !item.name))
|
|
10320
|
+
return {
|
|
10321
|
+
ok: false,
|
|
10322
|
+
details: `${protocol.toUpperCase()} router API returned malformed entries`
|
|
10323
|
+
};
|
|
10324
|
+
return {
|
|
10325
|
+
ok: true,
|
|
10326
|
+
reachedPageLimit: value.length >= ROUTER_API_PAGE_SIZE,
|
|
10327
|
+
names: new Set(
|
|
10328
|
+
value.flatMap((item) => {
|
|
10329
|
+
if (!item || typeof item !== "object") return [];
|
|
10330
|
+
const name = item.name;
|
|
10331
|
+
return typeof name === "string" ? [name] : [];
|
|
10332
|
+
})
|
|
10333
|
+
)
|
|
10334
|
+
};
|
|
10335
|
+
}
|
|
10336
|
+
function isRecord5(value) {
|
|
10337
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10338
|
+
}
|
|
10339
|
+
function inspectExpectedRoutes(routes, expectation) {
|
|
10340
|
+
const mismatched = [];
|
|
10341
|
+
const failures = [];
|
|
10342
|
+
for (const protocol of ["http", "tcp"]) {
|
|
10343
|
+
const expected = routes.filter((route) => (route.protocol ?? "http") === protocol).map((route) => buildHostRouteRouterName(route.repoPath, route.name));
|
|
10344
|
+
if (expected.length === 0) continue;
|
|
10345
|
+
const result = inspectRouterApi(protocol);
|
|
10346
|
+
if (!result.ok) {
|
|
10347
|
+
failures.push(result.details);
|
|
10348
|
+
mismatched.push(...expected);
|
|
10349
|
+
continue;
|
|
10350
|
+
}
|
|
10351
|
+
const unexpected = expected.filter(
|
|
10352
|
+
(name) => expectation === "loaded" ? !result.names.has(name) : result.names.has(name)
|
|
10353
|
+
);
|
|
10354
|
+
const boundedAbsenceIsUncertain = result.reachedPageLimit && (expectation === "loaded" ? unexpected.length > 0 : unexpected.length === 0);
|
|
10355
|
+
if (boundedAbsenceIsUncertain) {
|
|
10356
|
+
failures.push(
|
|
10357
|
+
`${protocol.toUpperCase()} router API reached the ${ROUTER_API_PAGE_SIZE}-entry safety limit`
|
|
10358
|
+
);
|
|
10359
|
+
}
|
|
10360
|
+
mismatched.push(...unexpected);
|
|
10361
|
+
if (expectation === "removed" && unexpected.length === 0 && result.reachedPageLimit) {
|
|
10362
|
+
mismatched.push(...expected);
|
|
10363
|
+
}
|
|
10364
|
+
}
|
|
10365
|
+
return mismatched.length === 0 ? { ok: true } : {
|
|
10366
|
+
ok: false,
|
|
10367
|
+
routes: mismatched,
|
|
10368
|
+
details: failures.length > 0 ? failures.join("; ") : expectation === "loaded" ? "router names are absent" : "router names are still present"
|
|
10369
|
+
};
|
|
10370
|
+
}
|
|
10371
|
+
async function waitForExpectedRoutes(routes, expectation, timeoutMs, pollIntervalMs) {
|
|
10372
|
+
const deadline = Date.now() + timeoutMs;
|
|
10373
|
+
let result;
|
|
10374
|
+
do {
|
|
10375
|
+
result = inspectExpectedRoutes(routes, expectation);
|
|
10376
|
+
if (result.ok) return result;
|
|
10377
|
+
if (Date.now() < deadline) {
|
|
10378
|
+
await sleep(Math.min(pollIntervalMs, Math.max(1, deadline - Date.now())));
|
|
10379
|
+
}
|
|
10380
|
+
} while (Date.now() < deadline);
|
|
10381
|
+
return result;
|
|
10382
|
+
}
|
|
10383
|
+
async function ensureTraefikRouteExpectation(routes, expectation, options) {
|
|
10384
|
+
if (routes.length === 0) return { restarted: false };
|
|
10385
|
+
const initialTimeoutMs = options.initialTimeoutMs ?? DEFAULT_INITIAL_TIMEOUT_MS;
|
|
10386
|
+
const recoveryTimeoutMs = options.recoveryTimeoutMs ?? DEFAULT_RECOVERY_TIMEOUT_MS;
|
|
10387
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
10388
|
+
const initial = await waitForExpectedRoutes(
|
|
10389
|
+
routes,
|
|
10390
|
+
expectation,
|
|
10391
|
+
initialTimeoutMs,
|
|
10392
|
+
pollIntervalMs
|
|
10393
|
+
);
|
|
10394
|
+
if (initial.ok) return { restarted: false };
|
|
10395
|
+
if (options.allowRestart === false)
|
|
10396
|
+
throw new Error(
|
|
10397
|
+
"Traefik routes did not reload; shared router restart is disabled for this operation."
|
|
10398
|
+
);
|
|
10399
|
+
import_node_fs24.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
10400
|
+
return withFileLock(
|
|
10401
|
+
ROUTER_RELOAD_LOCK_FILE,
|
|
10402
|
+
{
|
|
10403
|
+
activity: "Traefik route reload recovery",
|
|
10404
|
+
waitMs: initialTimeoutMs + recoveryTimeoutMs + 1e4,
|
|
10405
|
+
fair: true,
|
|
10406
|
+
onWait: createStderrWaitReporter("Traefik route reload recovery", "shared router")
|
|
10407
|
+
},
|
|
10408
|
+
async () => {
|
|
10409
|
+
const recheck = await waitForExpectedRoutes(routes, expectation, 0, pollIntervalMs);
|
|
10410
|
+
if (recheck.ok) return { restarted: false };
|
|
10411
|
+
claimLifecycleEffect();
|
|
10412
|
+
restartRouterStack();
|
|
10413
|
+
const recovered = await waitForExpectedRoutes(
|
|
10414
|
+
routes,
|
|
10415
|
+
expectation,
|
|
10416
|
+
recoveryTimeoutMs,
|
|
10417
|
+
pollIntervalMs
|
|
10418
|
+
);
|
|
10419
|
+
if (recovered.ok) return { restarted: true };
|
|
10420
|
+
const action2 = expectation === "loaded" ? "load" : "remove";
|
|
10421
|
+
throw new Error(
|
|
10422
|
+
`Traefik did not ${action2} file-provider routes after one restart: ${recovered.routes.join(", ")} (${recovered.details}). Inspect: devrouter logs --tail 100`
|
|
10423
|
+
);
|
|
10424
|
+
}
|
|
10425
|
+
);
|
|
10426
|
+
}
|
|
10427
|
+
async function ensureTraefikRoutesRemoved(routes, options = {}) {
|
|
10428
|
+
return ensureTraefikRouteExpectation(routes, "removed", options);
|
|
10429
|
+
}
|
|
10430
|
+
function assertTraefikRoutesRemoved(routes) {
|
|
10431
|
+
const result = inspectExpectedRoutes(routes, "removed");
|
|
10432
|
+
if (!result.ok) throw new Error(`Workspace route cessation is not proven: ${result.details}`);
|
|
10433
|
+
}
|
|
10434
|
+
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;
|
|
10435
|
+
var init_traefik_route_health = __esm({
|
|
10436
|
+
"src/core/traefik-route-health.ts"() {
|
|
10437
|
+
"use strict";
|
|
10438
|
+
import_node_child_process15 = require("child_process");
|
|
10439
|
+
import_node_fs24 = __toESM(require("fs"));
|
|
10440
|
+
import_node_path32 = __toESM(require("path"));
|
|
10441
|
+
init_file_lock();
|
|
10442
|
+
init_host_routes();
|
|
10443
|
+
init_reliability_context();
|
|
10444
|
+
init_router();
|
|
10445
|
+
ROUTER_API_BASE = "http://127.0.0.1:8080/api";
|
|
10446
|
+
ROUTER_RELOAD_LOCK_FILE = import_node_path32.default.join(DEVROUTER_HOME, "router-reload.lock");
|
|
10447
|
+
ROUTER_API_REQUEST_TIMEOUT_SECONDS = "2";
|
|
10448
|
+
DEFAULT_INITIAL_TIMEOUT_MS = 3e3;
|
|
10449
|
+
DEFAULT_RECOVERY_TIMEOUT_MS = 1e4;
|
|
10450
|
+
DEFAULT_POLL_INTERVAL_MS = 250;
|
|
10451
|
+
ROUTER_API_PAGE_SIZE = 1e3;
|
|
10452
|
+
}
|
|
10453
|
+
});
|
|
10454
|
+
|
|
10136
10455
|
// src/core/managed-devsy-stop.ts
|
|
10137
10456
|
function sameSet(left, right) {
|
|
10138
10457
|
return JSON.stringify([...left].sort()) === JSON.stringify([...right].sort());
|
|
@@ -10145,8 +10464,71 @@ function containerIdentity(container) {
|
|
|
10145
10464
|
mounts: container.mounts.map((mount) => JSON.stringify(mount)).sort()
|
|
10146
10465
|
});
|
|
10147
10466
|
}
|
|
10467
|
+
function proveInitialManagedDevsyAbsence(repoPath, expectedId) {
|
|
10468
|
+
if (!isLinkedWorktree(repoPath)) return void 0;
|
|
10469
|
+
const workspace = resolveWorktreeWorkspace(repoPath);
|
|
10470
|
+
if (!workspace || readManagedRuntimeState(repoPath, workspace)) return void 0;
|
|
10471
|
+
if (!import_node_fs25.default.existsSync(import_node_path33.default.join(repoPath, ".devrouter.yml"))) return void 0;
|
|
10472
|
+
const runtime = loadRuntimeConfig(repoPath, workspace);
|
|
10473
|
+
if (!runtime.config.managedRuntime) return void 0;
|
|
10474
|
+
const routes = proxyAppsFromConfig(runtime.config).flatMap(
|
|
10475
|
+
(app) => ["http", "tcp"].map((protocol) => ({ repoPath, name: app.name, protocol }))
|
|
10476
|
+
);
|
|
10477
|
+
const record5 = readWorkspaceOwnership(repoPath, workspace);
|
|
10478
|
+
if (!record5 || expectedId !== void 0 && record5.devpodId !== expectedId || record5.workspace !== workspace || !sameWorkspacePath(record5.worktreePath, repoPath))
|
|
10479
|
+
throw new Error("Initial managed stop requires exact linked ownership.");
|
|
10480
|
+
const devsyId = record5.devpodId;
|
|
10481
|
+
const ownership = inspectDevsyWorkspaceOwnership(listDevsyWorkspaces(), devsyId, repoPath);
|
|
10482
|
+
if (ownership.status === "conflict") throw new Error(ownership.reason);
|
|
10483
|
+
if (ownership.status !== "absent") return void 0;
|
|
10484
|
+
const gitCommonDir = resolveGitCommonDir(repoPath);
|
|
10485
|
+
const endpoint = resolveManagedStopEndpoint();
|
|
10486
|
+
if (!supportsManagedStopBaseline(endpoint))
|
|
10487
|
+
throw new Error("Initial managed stop requires a local Docker endpoint.");
|
|
10488
|
+
const daemon = inspectManagedStopDaemon(endpoint);
|
|
10489
|
+
const legacyHome = devpodRegistryRoot();
|
|
10490
|
+
const readLegacy = () => listDevpodWorkspacesRaw({ readLocalWhenMissing: true }).map(({ id: id5, source }) => ({ id: id5, source: { localFolder: source.localFolder } })).sort(
|
|
10491
|
+
(a, b) => a.id.localeCompare(b.id) || a.source.localFolder.localeCompare(b.source.localFolder)
|
|
10492
|
+
);
|
|
10493
|
+
const legacyWorkspaces = readLegacy();
|
|
10494
|
+
const observe = () => {
|
|
10495
|
+
resetWorkspaceRuntimeCaches();
|
|
10496
|
+
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))
|
|
10497
|
+
throw new Error("Initial managed stop ownership or retained state changed.");
|
|
10498
|
+
if (devpodRegistryRoot() !== legacyHome || !(0, import_node_util9.isDeepStrictEqual)(readLegacy(), legacyWorkspaces))
|
|
10499
|
+
throw new Error("Initial managed stop legacy registry evidence changed.");
|
|
10500
|
+
if (inspectDevsyWorkspaceOwnership(listDevsyWorkspaces(), devsyId, repoPath).status !== "absent" || legacyWorkspaces.some(
|
|
10501
|
+
(entry) => entry.id === devsyId || entry.source.localFolder !== "" && sameWorkspacePath(entry.source.localFolder, repoPath)
|
|
10502
|
+
))
|
|
10503
|
+
throw new Error("Initial managed stop requires both provider registrations absent.");
|
|
10504
|
+
if (!inspectDevsyRuntimeAbsence(devsyId))
|
|
10505
|
+
throw new Error("Initial managed stop requires positive runtime not-found.");
|
|
10506
|
+
if (resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
10507
|
+
throw new Error("Initial managed stop Docker identity changed.");
|
|
10508
|
+
if (inspectProviderRunnerContainers(endpoint, devsyId).length)
|
|
10509
|
+
throw new Error("Initial managed stop observed a remaining provider runner.");
|
|
10510
|
+
assertManagedStopCheckoutAbsent(endpoint, repoPath);
|
|
10511
|
+
if (listHostRouteState().some((route) => sameWorkspacePath(route.repoPath, repoPath)))
|
|
10512
|
+
throw new Error("Initial managed stop observed remaining workspace routes.");
|
|
10513
|
+
assertTraefikRoutesRemoved(routes);
|
|
10514
|
+
if (resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
10515
|
+
throw new Error("Initial managed stop Docker identity changed.");
|
|
10516
|
+
};
|
|
10517
|
+
observe();
|
|
10518
|
+
observe();
|
|
10519
|
+
return {
|
|
10520
|
+
workspace,
|
|
10521
|
+
record: record5,
|
|
10522
|
+
gitCommonDir,
|
|
10523
|
+
endpoint,
|
|
10524
|
+
daemon,
|
|
10525
|
+
routes,
|
|
10526
|
+
legacyHome,
|
|
10527
|
+
legacyWorkspaces
|
|
10528
|
+
};
|
|
10529
|
+
}
|
|
10148
10530
|
function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
10149
|
-
if (!
|
|
10531
|
+
if (!import_node_fs25.default.existsSync(import_node_path33.default.join(repoPath, ".devrouter.yml"))) return false;
|
|
10150
10532
|
const linked = isLinkedWorktree(repoPath);
|
|
10151
10533
|
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
10152
10534
|
const registration2 = () => {
|
|
@@ -10174,8 +10556,8 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10174
10556
|
profile: runtime.resolvedProfile,
|
|
10175
10557
|
linked
|
|
10176
10558
|
});
|
|
10177
|
-
const providerRoot =
|
|
10178
|
-
const featureDirectory =
|
|
10559
|
+
const providerRoot = import_node_path33.default.resolve(process.env.DEVSY_HOME || import_node_path33.default.join(import_node_os5.default.homedir(), ".devsy"));
|
|
10560
|
+
const featureDirectory = import_node_path33.default.join(
|
|
10179
10561
|
providerRoot,
|
|
10180
10562
|
"contexts",
|
|
10181
10563
|
owner.context,
|
|
@@ -10246,7 +10628,10 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10246
10628
|
const linked = isLinkedWorktree(repoPath);
|
|
10247
10629
|
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
10248
10630
|
const retainedState = readManagedRuntimeState(repoPath, workspace);
|
|
10249
|
-
if (!retainedState)
|
|
10631
|
+
if (!retainedState) {
|
|
10632
|
+
if (proveInitialManagedDevsyAbsence(repoPath, devsyId)) return "proven-absent";
|
|
10633
|
+
return stopInitialManagedDevsyWorkspace(repoPath, devsyId);
|
|
10634
|
+
}
|
|
10250
10635
|
const state = retainedState;
|
|
10251
10636
|
if (state.devpodId !== devsyId || linked && !workspace) {
|
|
10252
10637
|
throw new Error("Managed stop requires the exact retained workspace identity.");
|
|
@@ -10307,7 +10692,7 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10307
10692
|
const owned = inspectWorkspaceContainers().filter(
|
|
10308
10693
|
(container) => sameWorkspacePath(
|
|
10309
10694
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
10310
|
-
|
|
10695
|
+
import_node_path33.default.join(repoPath, ".devcontainer")
|
|
10311
10696
|
)
|
|
10312
10697
|
);
|
|
10313
10698
|
if (owned.length !== 0) {
|
|
@@ -10336,8 +10721,8 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10336
10721
|
if (!context || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(context) || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(devsyId)) {
|
|
10337
10722
|
throw new Error("Managed stop requires a valid exact Devsy context and workspace ID.");
|
|
10338
10723
|
}
|
|
10339
|
-
const devsyRoot =
|
|
10340
|
-
const featureDirectory =
|
|
10724
|
+
const devsyRoot = import_node_path33.default.resolve(process.env.DEVSY_HOME || import_node_path33.default.join(import_node_os5.default.homedir(), ".devsy"));
|
|
10725
|
+
const featureDirectory = import_node_path33.default.join(
|
|
10341
10726
|
devsyRoot,
|
|
10342
10727
|
"contexts",
|
|
10343
10728
|
context,
|
|
@@ -10441,23 +10826,25 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10441
10826
|
if (providerFailed) throw providerError;
|
|
10442
10827
|
return true;
|
|
10443
10828
|
}
|
|
10444
|
-
var
|
|
10829
|
+
var import_node_fs25, import_node_os5, import_node_path33, import_node_util9;
|
|
10445
10830
|
var init_managed_devsy_stop = __esm({
|
|
10446
10831
|
"src/core/managed-devsy-stop.ts"() {
|
|
10447
10832
|
"use strict";
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10833
|
+
import_node_fs25 = __toESM(require("fs"));
|
|
10834
|
+
import_node_os5 = __toESM(require("os"));
|
|
10835
|
+
import_node_path33 = __toESM(require("path"));
|
|
10451
10836
|
import_node_util9 = require("util");
|
|
10452
10837
|
init_devcontainer_profile();
|
|
10453
10838
|
init_devpod_environment();
|
|
10454
10839
|
init_devpod_registry();
|
|
10455
10840
|
init_devsy_workspaces();
|
|
10841
|
+
init_host_routes();
|
|
10456
10842
|
init_managed_compose_population();
|
|
10457
10843
|
init_managed_runtime_state();
|
|
10458
10844
|
init_managed_stop_recovery();
|
|
10459
10845
|
init_repo_config();
|
|
10460
10846
|
init_route_publication();
|
|
10847
|
+
init_traefik_route_health();
|
|
10461
10848
|
init_workspace();
|
|
10462
10849
|
init_workspace_ownership();
|
|
10463
10850
|
init_workspace_runtime();
|
|
@@ -11003,7 +11390,7 @@ function hasControlCharacter2(value) {
|
|
|
11003
11390
|
function readPolicyFile(filePath) {
|
|
11004
11391
|
let initialStats;
|
|
11005
11392
|
try {
|
|
11006
|
-
initialStats =
|
|
11393
|
+
initialStats = import_node_fs26.default.lstatSync(filePath);
|
|
11007
11394
|
} catch (error) {
|
|
11008
11395
|
if (isErrno(error, "ENOENT")) return { status: "absent" };
|
|
11009
11396
|
return { status: "invalid", error: "network policy could not be read" };
|
|
@@ -11016,16 +11403,16 @@ function readPolicyFile(filePath) {
|
|
|
11016
11403
|
}
|
|
11017
11404
|
let fileDescriptor;
|
|
11018
11405
|
try {
|
|
11019
|
-
fileDescriptor =
|
|
11406
|
+
fileDescriptor = import_node_fs26.default.openSync(
|
|
11020
11407
|
filePath,
|
|
11021
|
-
|
|
11408
|
+
import_node_fs26.default.constants.O_RDONLY | import_node_fs26.default.constants.O_NONBLOCK | (import_node_fs26.default.constants.O_NOFOLLOW ?? 0)
|
|
11022
11409
|
);
|
|
11023
11410
|
} catch (error) {
|
|
11024
11411
|
if (isErrno(error, "ENOENT")) return { status: "absent" };
|
|
11025
11412
|
return { status: "invalid", error: "network policy could not be read" };
|
|
11026
11413
|
}
|
|
11027
11414
|
try {
|
|
11028
|
-
const openedStats =
|
|
11415
|
+
const openedStats = import_node_fs26.default.fstatSync(fileDescriptor);
|
|
11029
11416
|
if (!openedStats.isFile()) {
|
|
11030
11417
|
return { status: "invalid", error: "network policy must be a regular file" };
|
|
11031
11418
|
}
|
|
@@ -11038,12 +11425,12 @@ function readPolicyFile(filePath) {
|
|
|
11038
11425
|
const buffer = Buffer.alloc(openedStats.size);
|
|
11039
11426
|
let offset = 0;
|
|
11040
11427
|
while (offset < buffer.length) {
|
|
11041
|
-
const bytesRead =
|
|
11428
|
+
const bytesRead = import_node_fs26.default.readSync(fileDescriptor, buffer, offset, buffer.length - offset, null);
|
|
11042
11429
|
if (bytesRead === 0)
|
|
11043
11430
|
return { status: "invalid", error: "network policy changed during read" };
|
|
11044
11431
|
offset += bytesRead;
|
|
11045
11432
|
}
|
|
11046
|
-
const finalStats =
|
|
11433
|
+
const finalStats = import_node_fs26.default.fstatSync(fileDescriptor);
|
|
11047
11434
|
if (!finalStats.isFile() || finalStats.size !== openedStats.size) {
|
|
11048
11435
|
return { status: "invalid", error: "network policy changed during read" };
|
|
11049
11436
|
}
|
|
@@ -11052,7 +11439,7 @@ function readPolicyFile(filePath) {
|
|
|
11052
11439
|
return { status: "invalid", error: "network policy could not be read" };
|
|
11053
11440
|
} finally {
|
|
11054
11441
|
try {
|
|
11055
|
-
|
|
11442
|
+
import_node_fs26.default.closeSync(fileDescriptor);
|
|
11056
11443
|
} catch {
|
|
11057
11444
|
}
|
|
11058
11445
|
}
|
|
@@ -11069,16 +11456,16 @@ function assertDisjoint(cidrs, message) {
|
|
|
11069
11456
|
function isErrno(error, code) {
|
|
11070
11457
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
11071
11458
|
}
|
|
11072
|
-
var
|
|
11459
|
+
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
11460
|
var init_network_policy = __esm({
|
|
11074
11461
|
"src/core/network-policy.ts"() {
|
|
11075
11462
|
"use strict";
|
|
11076
|
-
|
|
11077
|
-
|
|
11463
|
+
import_node_fs26 = __toESM(require("fs"));
|
|
11464
|
+
import_node_path34 = __toESM(require("path"));
|
|
11078
11465
|
init_network_capacity();
|
|
11079
11466
|
init_router();
|
|
11080
11467
|
NETWORK_POLICY_VERSION = 1;
|
|
11081
|
-
NETWORK_POLICY_FILE =
|
|
11468
|
+
NETWORK_POLICY_FILE = import_node_path34.default.join(DEVROUTER_HOME, "network-policy.json");
|
|
11082
11469
|
DEFAULT_NETWORK_POLICY_ENDPOINT_RESERVE = 8;
|
|
11083
11470
|
NETWORK_POLICY_PREFIX_LENGTHS = [24, 25, 26];
|
|
11084
11471
|
MAX_NETWORK_POLICY_FILE_BYTES = 64 * 1024;
|
|
@@ -11179,27 +11566,27 @@ function assertExactKeys(value, keys2) {
|
|
|
11179
11566
|
function readDocument(store) {
|
|
11180
11567
|
let descriptor;
|
|
11181
11568
|
try {
|
|
11182
|
-
descriptor =
|
|
11569
|
+
descriptor = import_node_fs27.default.openSync(
|
|
11183
11570
|
store.filePath,
|
|
11184
|
-
|
|
11571
|
+
import_node_fs27.default.constants.O_RDONLY | import_node_fs27.default.constants.O_NONBLOCK | (import_node_fs27.default.constants.O_NOFOLLOW ?? 0)
|
|
11185
11572
|
);
|
|
11186
11573
|
} catch (error) {
|
|
11187
11574
|
if (isErrno2(error, "ENOENT")) return void 0;
|
|
11188
11575
|
throw claimError("invalid-state", "network claim state could not be read");
|
|
11189
11576
|
}
|
|
11190
11577
|
try {
|
|
11191
|
-
const stat =
|
|
11578
|
+
const stat = import_node_fs27.default.fstatSync(descriptor);
|
|
11192
11579
|
if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES || (stat.mode & 63) !== 0) {
|
|
11193
11580
|
throw claimError("invalid-state", "network claim state is not a bounded private file");
|
|
11194
11581
|
}
|
|
11195
11582
|
const buffer = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
|
|
11196
11583
|
let count = 0;
|
|
11197
11584
|
while (count < buffer.length) {
|
|
11198
|
-
const bytesRead =
|
|
11585
|
+
const bytesRead = import_node_fs27.default.readSync(descriptor, buffer, count, buffer.length - count, null);
|
|
11199
11586
|
if (bytesRead === 0) break;
|
|
11200
11587
|
count += bytesRead;
|
|
11201
11588
|
}
|
|
11202
|
-
const finalStat =
|
|
11589
|
+
const finalStat = import_node_fs27.default.fstatSync(descriptor);
|
|
11203
11590
|
if (count > NETWORK_CLAIMS_MAX_FILE_BYTES || finalStat.size !== stat.size) {
|
|
11204
11591
|
throw claimError("invalid-state", "network claim state changed during read");
|
|
11205
11592
|
}
|
|
@@ -11211,7 +11598,7 @@ function readDocument(store) {
|
|
|
11211
11598
|
}
|
|
11212
11599
|
return validateDocument(parsed, store.daemonId);
|
|
11213
11600
|
} finally {
|
|
11214
|
-
|
|
11601
|
+
import_node_fs27.default.closeSync(descriptor);
|
|
11215
11602
|
}
|
|
11216
11603
|
}
|
|
11217
11604
|
function validateDocument(value, daemonId) {
|
|
@@ -11278,16 +11665,16 @@ function createStore(daemonId, options) {
|
|
|
11278
11665
|
return {
|
|
11279
11666
|
root,
|
|
11280
11667
|
daemonId,
|
|
11281
|
-
filePath:
|
|
11282
|
-
lockPath:
|
|
11668
|
+
filePath: import_node_path35.default.join(root, `${key}.json`),
|
|
11669
|
+
lockPath: import_node_path35.default.join(root, `${key}.lock`),
|
|
11283
11670
|
writeAtomic: options.writeAtomic ?? writeFileAtomically
|
|
11284
11671
|
};
|
|
11285
11672
|
}
|
|
11286
11673
|
function validateRoot(root) {
|
|
11287
|
-
if (typeof root !== "string" || !
|
|
11674
|
+
if (typeof root !== "string" || !import_node_path35.default.isAbsolute(root) || root.includes("\0")) {
|
|
11288
11675
|
throw claimError("invalid-input", "network claim root is invalid");
|
|
11289
11676
|
}
|
|
11290
|
-
const resolved =
|
|
11677
|
+
const resolved = import_node_path35.default.resolve(root);
|
|
11291
11678
|
if (resolved !== root) throw claimError("invalid-input", "network claim root is invalid");
|
|
11292
11679
|
return resolved;
|
|
11293
11680
|
}
|
|
@@ -11315,20 +11702,20 @@ function isErrno2(error, code) {
|
|
|
11315
11702
|
function claimError(code, message) {
|
|
11316
11703
|
return new NetworkClaimError(code, message);
|
|
11317
11704
|
}
|
|
11318
|
-
var import_node_crypto13,
|
|
11705
|
+
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
11706
|
var init_network_claims = __esm({
|
|
11320
11707
|
"src/core/network-claims.ts"() {
|
|
11321
11708
|
"use strict";
|
|
11322
11709
|
import_node_crypto13 = require("crypto");
|
|
11323
|
-
|
|
11324
|
-
|
|
11710
|
+
import_node_fs27 = __toESM(require("fs"));
|
|
11711
|
+
import_node_path35 = __toESM(require("path"));
|
|
11325
11712
|
init_atomic_file();
|
|
11326
11713
|
init_file_lock();
|
|
11327
11714
|
init_network_capacity();
|
|
11328
11715
|
init_network_policy();
|
|
11329
11716
|
init_router();
|
|
11330
11717
|
NETWORK_CLAIMS_VERSION = 1;
|
|
11331
|
-
NETWORK_CLAIMS_ROOT =
|
|
11718
|
+
NETWORK_CLAIMS_ROOT = import_node_path35.default.join(DEVROUTER_HOME, "networks");
|
|
11332
11719
|
NETWORK_CLAIMS_MAX_RECORDS = 256;
|
|
11333
11720
|
NETWORK_CLAIMS_MAX_FILE_BYTES = 512 * 1024;
|
|
11334
11721
|
NetworkClaimError = class extends Error {
|
|
@@ -11370,14 +11757,14 @@ var init_network_claims = __esm({
|
|
|
11370
11757
|
|
|
11371
11758
|
// src/core/network-claim-lookup.ts
|
|
11372
11759
|
function networkOwnerKey(repoPath) {
|
|
11373
|
-
if (!
|
|
11760
|
+
if (!import_node_path36.default.isAbsolute(repoPath) || import_node_path36.default.resolve(repoPath) !== repoPath)
|
|
11374
11761
|
throw new Error("Invalid network owner path.");
|
|
11375
11762
|
return (0, import_node_crypto14.createHash)("sha256").update(repoPath).digest("hex");
|
|
11376
11763
|
}
|
|
11377
11764
|
function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
11378
11765
|
let names;
|
|
11379
11766
|
try {
|
|
11380
|
-
names =
|
|
11767
|
+
names = import_node_fs28.default.readdirSync(root);
|
|
11381
11768
|
} catch (error) {
|
|
11382
11769
|
if (error.code === "ENOENT") return void 0;
|
|
11383
11770
|
throw new Error("Network claims cannot be inspected; repair is required.");
|
|
@@ -11386,21 +11773,21 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11386
11773
|
if (files.length > 128) throw new Error("Network daemon inventory exceeds its bound.");
|
|
11387
11774
|
let found;
|
|
11388
11775
|
for (const file of files) {
|
|
11389
|
-
const full =
|
|
11390
|
-
const stat =
|
|
11776
|
+
const full = import_node_path36.default.join(root, file);
|
|
11777
|
+
const stat = import_node_fs28.default.lstatSync(full);
|
|
11391
11778
|
if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES)
|
|
11392
11779
|
throw new Error("Invalid retained network claim file.");
|
|
11393
11780
|
let value;
|
|
11394
|
-
const descriptor =
|
|
11781
|
+
const descriptor = import_node_fs28.default.openSync(
|
|
11395
11782
|
full,
|
|
11396
|
-
|
|
11783
|
+
import_node_fs28.default.constants.O_RDONLY | import_node_fs28.default.constants.O_NONBLOCK | import_node_fs28.default.constants.O_NOFOLLOW
|
|
11397
11784
|
);
|
|
11398
11785
|
try {
|
|
11399
|
-
if (!
|
|
11786
|
+
if (!import_node_fs28.default.fstatSync(descriptor).isFile()) throw new Error();
|
|
11400
11787
|
const bytes2 = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
|
|
11401
11788
|
let size = 0;
|
|
11402
11789
|
while (size < bytes2.length) {
|
|
11403
|
-
const count =
|
|
11790
|
+
const count = import_node_fs28.default.readSync(descriptor, bytes2, size, bytes2.length - size, null);
|
|
11404
11791
|
if (!count) break;
|
|
11405
11792
|
size += count;
|
|
11406
11793
|
}
|
|
@@ -11410,7 +11797,7 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11410
11797
|
} catch {
|
|
11411
11798
|
throw new Error("Retained network claim could not be read safely.");
|
|
11412
11799
|
} finally {
|
|
11413
|
-
|
|
11800
|
+
import_node_fs28.default.closeSync(descriptor);
|
|
11414
11801
|
}
|
|
11415
11802
|
if (typeof value.daemonId !== "string" || `${(0, import_node_crypto14.createHash)("sha256").update(value.daemonId).digest("hex")}.json` !== file)
|
|
11416
11803
|
throw new Error("Retained network daemon identity is invalid.");
|
|
@@ -11426,13 +11813,13 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11426
11813
|
}
|
|
11427
11814
|
return found;
|
|
11428
11815
|
}
|
|
11429
|
-
var import_node_crypto14,
|
|
11816
|
+
var import_node_crypto14, import_node_fs28, import_node_path36;
|
|
11430
11817
|
var init_network_claim_lookup = __esm({
|
|
11431
11818
|
"src/core/network-claim-lookup.ts"() {
|
|
11432
11819
|
"use strict";
|
|
11433
11820
|
import_node_crypto14 = require("crypto");
|
|
11434
|
-
|
|
11435
|
-
|
|
11821
|
+
import_node_fs28 = __toESM(require("fs"));
|
|
11822
|
+
import_node_path36 = __toESM(require("path"));
|
|
11436
11823
|
init_network_claims();
|
|
11437
11824
|
}
|
|
11438
11825
|
});
|
|
@@ -11460,7 +11847,7 @@ function qualifyNetworkProviderDefinition(value, provider) {
|
|
|
11460
11847
|
return (0, import_node_crypto15.createHash)("sha256").update(JSON.stringify({ name: definition.name, agent, options, exec })).digest("hex");
|
|
11461
11848
|
}
|
|
11462
11849
|
function read(provider, args) {
|
|
11463
|
-
const result = (0,
|
|
11850
|
+
const result = (0, import_node_child_process16.spawnSync)(provider, args, {
|
|
11464
11851
|
encoding: "utf8",
|
|
11465
11852
|
timeout: 5e3,
|
|
11466
11853
|
maxBuffer: 2 * 1024 * 1024
|
|
@@ -11548,11 +11935,11 @@ function inspectNetworkProviderBinding(input2) {
|
|
|
11548
11935
|
);
|
|
11549
11936
|
}
|
|
11550
11937
|
}
|
|
11551
|
-
var
|
|
11938
|
+
var import_node_child_process16, import_node_crypto15;
|
|
11552
11939
|
var init_network_provider_inspect = __esm({
|
|
11553
11940
|
"src/core/network-provider-inspect.ts"() {
|
|
11554
11941
|
"use strict";
|
|
11555
|
-
|
|
11942
|
+
import_node_child_process16 = require("child_process");
|
|
11556
11943
|
import_node_crypto15 = require("crypto");
|
|
11557
11944
|
init_devpod_environment();
|
|
11558
11945
|
}
|
|
@@ -11591,7 +11978,7 @@ var init_network_provider_effect = __esm({
|
|
|
11591
11978
|
|
|
11592
11979
|
// src/core/devsy-mutation.ts
|
|
11593
11980
|
function withMutationLock(activity, target, operation) {
|
|
11594
|
-
|
|
11981
|
+
import_node_fs29.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
11595
11982
|
return withFileLockSync(
|
|
11596
11983
|
DEVSY_MUTATION_LOCK_FILE,
|
|
11597
11984
|
{
|
|
@@ -11611,7 +11998,7 @@ function runDevsyAction(action2, devsyId, force = false, repoPath) {
|
|
|
11611
11998
|
const args = action2 === "delete" ? ["delete", devsyId, ...force ? ["--force"] : [], "--ignore-not-found"] : ["stop", devsyId];
|
|
11612
11999
|
claimLifecycleEffect();
|
|
11613
12000
|
const binding = repoPath ? networkProviderEffectOptions("devsy", devsyId, repoPath) : { args: [] };
|
|
11614
|
-
const result = (0,
|
|
12001
|
+
const result = (0, import_node_child_process17.spawnSync)("devsy", ["workspace", ...args, ...binding.args], {
|
|
11615
12002
|
encoding: "utf-8",
|
|
11616
12003
|
...binding.env ? { env: binding.env } : {}
|
|
11617
12004
|
});
|
|
@@ -11672,13 +12059,13 @@ function stopOwnedDevsyWorkspace(devsyId, worktreePath) {
|
|
|
11672
12059
|
function deleteOwnedDevsyWorkspace(devsyId, worktreePath) {
|
|
11673
12060
|
return mutateOwnedDevsyWorkspace("delete", devsyId, worktreePath);
|
|
11674
12061
|
}
|
|
11675
|
-
var
|
|
12062
|
+
var import_node_child_process17, import_node_fs29, import_node_path37, DEVSY_MUTATION_LOCK_FILE, DEVSY_MUTATION_WAIT_MS;
|
|
11676
12063
|
var init_devsy_mutation = __esm({
|
|
11677
12064
|
"src/core/devsy-mutation.ts"() {
|
|
11678
12065
|
"use strict";
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
12066
|
+
import_node_child_process17 = require("child_process");
|
|
12067
|
+
import_node_fs29 = __toESM(require("fs"));
|
|
12068
|
+
import_node_path37 = __toESM(require("path"));
|
|
11682
12069
|
init_devsy_agent();
|
|
11683
12070
|
init_devsy_workspaces();
|
|
11684
12071
|
init_file_lock();
|
|
@@ -11687,7 +12074,7 @@ var init_devsy_mutation = __esm({
|
|
|
11687
12074
|
init_network_provider_effect();
|
|
11688
12075
|
init_reliability_context();
|
|
11689
12076
|
init_router();
|
|
11690
|
-
DEVSY_MUTATION_LOCK_FILE =
|
|
12077
|
+
DEVSY_MUTATION_LOCK_FILE = import_node_path37.default.join(DEVROUTER_HOME, "devsy-mutation.lock");
|
|
11691
12078
|
DEVSY_MUTATION_WAIT_MS = 18e5;
|
|
11692
12079
|
}
|
|
11693
12080
|
});
|
|
@@ -12459,10 +12846,10 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
|
|
|
12459
12846
|
throw new Error("Lifecycle invocation was cancelled before dispatch.");
|
|
12460
12847
|
if (process.platform === "win32")
|
|
12461
12848
|
throw new Error("Lifecycle workers require POSIX process-group ownership.");
|
|
12462
|
-
const workerPath =
|
|
12463
|
-
if (!
|
|
12849
|
+
const workerPath = import_node_path38.default.join(__dirname, "devrouter-lifecycle-worker.js");
|
|
12850
|
+
if (!import_node_fs30.default.existsSync(workerPath))
|
|
12464
12851
|
throw new Error("The packaged lifecycle worker is missing; rebuild the CLI.");
|
|
12465
|
-
const child = (0,
|
|
12852
|
+
const child = (0, import_node_child_process18.fork)(workerPath, [], {
|
|
12466
12853
|
detached: true,
|
|
12467
12854
|
stdio: supervision ? ["ignore", "pipe", "pipe", "ipc"] : ["inherit", "inherit", "inherit", "ipc"],
|
|
12468
12855
|
execArgv: []
|
|
@@ -12689,14 +13076,14 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
|
|
|
12689
13076
|
function newLifecycleIds() {
|
|
12690
13077
|
return { requestId: (0, import_node_crypto16.randomUUID)(), operationId: (0, import_node_crypto16.randomUUID)(), workerId: (0, import_node_crypto16.randomUUID)() };
|
|
12691
13078
|
}
|
|
12692
|
-
var
|
|
13079
|
+
var import_node_child_process18, import_node_crypto16, import_node_fs30, import_node_path38, OUTPUT_BUFFER_LIMIT, DEFAULT_OUTPUT_PAGE_BYTES, LifecycleOutput, LifecycleWorkerAdmissionBusyError;
|
|
12693
13080
|
var init_reliability_worker = __esm({
|
|
12694
13081
|
"src/core/reliability-worker.ts"() {
|
|
12695
13082
|
"use strict";
|
|
12696
|
-
|
|
12697
|
-
import_node_crypto16 = require("crypto");
|
|
12698
|
-
|
|
12699
|
-
|
|
13083
|
+
import_node_child_process18 = require("child_process");
|
|
13084
|
+
import_node_crypto16 = require("crypto");
|
|
13085
|
+
import_node_fs30 = __toESM(require("fs"));
|
|
13086
|
+
import_node_path38 = __toESM(require("path"));
|
|
12700
13087
|
init_file_lock();
|
|
12701
13088
|
init_reliability_contract();
|
|
12702
13089
|
init_reliability_model();
|
|
@@ -12811,173 +13198,9 @@ var init_reliability_worker = __esm({
|
|
|
12811
13198
|
}
|
|
12812
13199
|
});
|
|
12813
13200
|
|
|
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
13201
|
// src/core/devpod-mutation.ts
|
|
12979
13202
|
function withMutationLock2(activity, target, operation) {
|
|
12980
|
-
|
|
13203
|
+
import_node_fs31.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
12981
13204
|
return withFileLockSync(
|
|
12982
13205
|
DEVPOD_MUTATION_LOCK_FILE,
|
|
12983
13206
|
{
|
|
@@ -13067,13 +13290,13 @@ function deleteOwnedDevpodWorkspace(devpodId, worktreePath) {
|
|
|
13067
13290
|
resetWorkspaceRuntimeCaches();
|
|
13068
13291
|
return result;
|
|
13069
13292
|
}
|
|
13070
|
-
var import_node_child_process19,
|
|
13293
|
+
var import_node_child_process19, import_node_fs31, import_node_path39, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS;
|
|
13071
13294
|
var init_devpod_mutation = __esm({
|
|
13072
13295
|
"src/core/devpod-mutation.ts"() {
|
|
13073
13296
|
"use strict";
|
|
13074
13297
|
import_node_child_process19 = require("child_process");
|
|
13075
|
-
|
|
13076
|
-
|
|
13298
|
+
import_node_fs31 = __toESM(require("fs"));
|
|
13299
|
+
import_node_path39 = __toESM(require("path"));
|
|
13077
13300
|
init_devpod_workspaces();
|
|
13078
13301
|
init_devsy_mutation();
|
|
13079
13302
|
init_file_lock();
|
|
@@ -13082,7 +13305,7 @@ var init_devpod_mutation = __esm({
|
|
|
13082
13305
|
init_reliability_context();
|
|
13083
13306
|
init_router();
|
|
13084
13307
|
init_workspace_runtime();
|
|
13085
|
-
DEVPOD_MUTATION_LOCK_FILE =
|
|
13308
|
+
DEVPOD_MUTATION_LOCK_FILE = import_node_path39.default.join(DEVROUTER_HOME, "devpod-mutation.lock");
|
|
13086
13309
|
DEVPOD_MUTATION_WAIT_MS = 18e5;
|
|
13087
13310
|
}
|
|
13088
13311
|
});
|
|
@@ -13288,8 +13511,8 @@ function renderManagedComposeModel(plan, workspace) {
|
|
|
13288
13511
|
}
|
|
13289
13512
|
}
|
|
13290
13513
|
function attributeHolderWorktree(workingDir) {
|
|
13291
|
-
if (
|
|
13292
|
-
return comparableWorkspacePath(
|
|
13514
|
+
if (import_node_path40.default.basename(workingDir) !== ".devcontainer") return void 0;
|
|
13515
|
+
return comparableWorkspacePath(import_node_path40.default.dirname(workingDir));
|
|
13293
13516
|
}
|
|
13294
13517
|
function holderAttribution(holder, repoPath) {
|
|
13295
13518
|
const attribution = {};
|
|
@@ -13321,7 +13544,7 @@ function detectHostPortClaimConflicts(options) {
|
|
|
13321
13544
|
);
|
|
13322
13545
|
if (desired.length === 0) return [];
|
|
13323
13546
|
const holders = listHolderSnapshots();
|
|
13324
|
-
const devcontainerDir =
|
|
13547
|
+
const devcontainerDir = import_node_path40.default.join(options.repoPath, ".devcontainer");
|
|
13325
13548
|
const conflicts = [];
|
|
13326
13549
|
for (const binding of desired) {
|
|
13327
13550
|
for (const holder of holders) {
|
|
@@ -13343,12 +13566,12 @@ function detectHostPortClaimConflicts(options) {
|
|
|
13343
13566
|
}
|
|
13344
13567
|
return conflicts;
|
|
13345
13568
|
}
|
|
13346
|
-
var import_node_child_process20,
|
|
13569
|
+
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
13570
|
var init_host_port_claims = __esm({
|
|
13348
13571
|
"src/core/host-port-claims.ts"() {
|
|
13349
13572
|
"use strict";
|
|
13350
13573
|
import_node_child_process20 = require("child_process");
|
|
13351
|
-
|
|
13574
|
+
import_node_path40 = __toESM(require("path"));
|
|
13352
13575
|
init_devcontainer_profile();
|
|
13353
13576
|
init_network_effect_scope();
|
|
13354
13577
|
init_workspace();
|
|
@@ -13522,20 +13745,20 @@ var init_http_route_probe = __esm({
|
|
|
13522
13745
|
});
|
|
13523
13746
|
|
|
13524
13747
|
// src/core/managed-host-preparation.ts
|
|
13525
|
-
var import_node_child_process22,
|
|
13748
|
+
var import_node_child_process22, import_node_fs32;
|
|
13526
13749
|
var init_managed_host_preparation = __esm({
|
|
13527
13750
|
"src/core/managed-host-preparation.ts"() {
|
|
13528
13751
|
"use strict";
|
|
13529
13752
|
import_node_child_process22 = require("child_process");
|
|
13530
|
-
|
|
13753
|
+
import_node_fs32 = __toESM(require("fs"));
|
|
13531
13754
|
init_repo_config();
|
|
13532
13755
|
}
|
|
13533
13756
|
});
|
|
13534
13757
|
|
|
13535
13758
|
// src/core/managed-post-start.ts
|
|
13536
13759
|
function readRegularFileBytes(filePath) {
|
|
13537
|
-
if (!
|
|
13538
|
-
return
|
|
13760
|
+
if (!import_node_fs33.default.existsSync(filePath) || !import_node_fs33.default.lstatSync(filePath).isFile()) return void 0;
|
|
13761
|
+
return import_node_fs33.default.readFileSync(filePath);
|
|
13539
13762
|
}
|
|
13540
13763
|
function readRegularFile(filePath) {
|
|
13541
13764
|
return readRegularFileBytes(filePath)?.toString("utf-8");
|
|
@@ -13544,11 +13767,11 @@ function adapterFingerprint(adapter) {
|
|
|
13544
13767
|
return (0, import_node_crypto17.createHash)("sha256").update(adapter).digest("hex");
|
|
13545
13768
|
}
|
|
13546
13769
|
function resolveManagedPostStartPlan(repoPath) {
|
|
13547
|
-
const adapterPath =
|
|
13770
|
+
const adapterPath = import_node_path41.default.join(repoPath, MANAGED_ADAPTER_PATH);
|
|
13548
13771
|
const adapterBytes = readRegularFileBytes(adapterPath);
|
|
13549
13772
|
const adapterText = adapterBytes?.toString("utf-8");
|
|
13550
|
-
const dockerfilePath =
|
|
13551
|
-
const devcontainerPath =
|
|
13773
|
+
const dockerfilePath = import_node_path41.default.join(repoPath, ".devcontainer", "Dockerfile");
|
|
13774
|
+
const devcontainerPath = import_node_path41.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
13552
13775
|
const dockerfile = readRegularFile(dockerfilePath) ?? "";
|
|
13553
13776
|
const devcontainer = readRegularFile(devcontainerPath) ?? "";
|
|
13554
13777
|
const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
|
|
@@ -13631,14 +13854,14 @@ function runManagedProcessAction(options) {
|
|
|
13631
13854
|
}
|
|
13632
13855
|
return "drifted";
|
|
13633
13856
|
}
|
|
13634
|
-
var import_node_child_process23, import_node_crypto17,
|
|
13857
|
+
var import_node_child_process23, import_node_crypto17, import_node_fs33, import_node_path41, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH;
|
|
13635
13858
|
var init_managed_post_start = __esm({
|
|
13636
13859
|
"src/core/managed-post-start.ts"() {
|
|
13637
13860
|
"use strict";
|
|
13638
13861
|
import_node_child_process23 = require("child_process");
|
|
13639
13862
|
import_node_crypto17 = require("crypto");
|
|
13640
|
-
|
|
13641
|
-
|
|
13863
|
+
import_node_fs33 = __toESM(require("fs"));
|
|
13864
|
+
import_node_path41 = __toESM(require("path"));
|
|
13642
13865
|
init_devcontainer_config();
|
|
13643
13866
|
init_network_effect_scope();
|
|
13644
13867
|
MANAGED_MARKER = "devrouter:managed devcontainer";
|
|
@@ -14577,26 +14800,26 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
|
14577
14800
|
try {
|
|
14578
14801
|
if (!endpoint.startsWith("unix:///")) return false;
|
|
14579
14802
|
const socket = endpoint.slice("unix://".length);
|
|
14580
|
-
const table =
|
|
14803
|
+
const table = import_node_fs34.default.readFileSync(import_node_path42.default.join(procRoot, "net/unix"), "utf8");
|
|
14581
14804
|
if (Buffer.byteLength(table) > 1024 * 1024) return false;
|
|
14582
14805
|
const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
|
|
14583
14806
|
if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
|
|
14584
14807
|
const inode = `socket:[${rows2[0][6]}]`;
|
|
14585
|
-
const ownNamespace =
|
|
14586
|
-
const pids =
|
|
14808
|
+
const ownNamespace = import_node_fs34.default.readlinkSync(import_node_path42.default.join(procRoot, "self/ns/net"));
|
|
14809
|
+
const pids = import_node_fs34.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
|
|
14587
14810
|
if (pids.length > 4096) return false;
|
|
14588
14811
|
for (const pid of pids) {
|
|
14589
14812
|
if (Date.now() > deadline) return false;
|
|
14590
14813
|
try {
|
|
14591
|
-
const root =
|
|
14592
|
-
if (
|
|
14593
|
-
if (
|
|
14594
|
-
const descriptors =
|
|
14814
|
+
const root = import_node_path42.default.join(procRoot, pid);
|
|
14815
|
+
if (import_node_path42.default.basename(import_node_fs34.default.readlinkSync(import_node_path42.default.join(root, "exe"))) !== "dockerd") continue;
|
|
14816
|
+
if (import_node_fs34.default.readlinkSync(import_node_path42.default.join(root, "ns/net")) !== ownNamespace) continue;
|
|
14817
|
+
const descriptors = import_node_fs34.default.readdirSync(import_node_path42.default.join(root, "fd"));
|
|
14595
14818
|
if (descriptors.length > 4096) continue;
|
|
14596
14819
|
if (descriptors.some((fd) => {
|
|
14597
14820
|
if (Date.now() > deadline) return false;
|
|
14598
14821
|
try {
|
|
14599
|
-
return
|
|
14822
|
+
return import_node_fs34.default.readlinkSync(import_node_path42.default.join(root, "fd", fd)) === inode;
|
|
14600
14823
|
} catch {
|
|
14601
14824
|
return false;
|
|
14602
14825
|
}
|
|
@@ -14610,12 +14833,12 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
|
14610
14833
|
}
|
|
14611
14834
|
return false;
|
|
14612
14835
|
}
|
|
14613
|
-
var
|
|
14836
|
+
var import_node_fs34, import_node_path42;
|
|
14614
14837
|
var init_network_local_daemon = __esm({
|
|
14615
14838
|
"src/core/network-local-daemon.ts"() {
|
|
14616
14839
|
"use strict";
|
|
14617
|
-
|
|
14618
|
-
|
|
14840
|
+
import_node_fs34 = __toESM(require("fs"));
|
|
14841
|
+
import_node_path42 = __toESM(require("path"));
|
|
14619
14842
|
}
|
|
14620
14843
|
});
|
|
14621
14844
|
|
|
@@ -14731,14 +14954,14 @@ var init_network_compose = __esm({
|
|
|
14731
14954
|
});
|
|
14732
14955
|
|
|
14733
14956
|
// src/core/network-compose-files.ts
|
|
14734
|
-
var import_node_child_process26, import_node_crypto19,
|
|
14957
|
+
var import_node_child_process26, import_node_crypto19, import_node_fs35, import_node_path43, import_yaml4, MAX_BYTES3;
|
|
14735
14958
|
var init_network_compose_files = __esm({
|
|
14736
14959
|
"src/core/network-compose-files.ts"() {
|
|
14737
14960
|
"use strict";
|
|
14738
14961
|
import_node_child_process26 = require("child_process");
|
|
14739
14962
|
import_node_crypto19 = require("crypto");
|
|
14740
|
-
|
|
14741
|
-
|
|
14963
|
+
import_node_fs35 = __toESM(require("fs"));
|
|
14964
|
+
import_node_path43 = __toESM(require("path"));
|
|
14742
14965
|
import_yaml4 = __toESM(require("yaml"));
|
|
14743
14966
|
init_atomic_file();
|
|
14744
14967
|
init_devcontainer_profile();
|
|
@@ -14842,13 +15065,13 @@ function resolveLinkedTarget(repoPath) {
|
|
|
14842
15065
|
gitCommonDir: resolveGitCommonDir(repoPath)
|
|
14843
15066
|
};
|
|
14844
15067
|
}
|
|
14845
|
-
var import_node_child_process28,
|
|
15068
|
+
var import_node_child_process28, import_node_fs36, import_node_path44;
|
|
14846
15069
|
var init_workspace_ensure = __esm({
|
|
14847
15070
|
"src/core/workspace-ensure.ts"() {
|
|
14848
15071
|
"use strict";
|
|
14849
15072
|
import_node_child_process28 = require("child_process");
|
|
14850
|
-
|
|
14851
|
-
|
|
15073
|
+
import_node_fs36 = __toESM(require("fs"));
|
|
15074
|
+
import_node_path44 = __toESM(require("path"));
|
|
14852
15075
|
init_devcontainer_profile();
|
|
14853
15076
|
init_devpod_environment();
|
|
14854
15077
|
init_devpod_mutation();
|
|
@@ -14984,7 +15207,7 @@ function prepareManagedLifecycleOperation(input2) {
|
|
|
14984
15207
|
};
|
|
14985
15208
|
});
|
|
14986
15209
|
}
|
|
14987
|
-
function assertCurrentCapacityController(expected, directory =
|
|
15210
|
+
function assertCurrentCapacityController(expected, directory = import_node_path45.default.join(DEVROUTER_HOME, "controller")) {
|
|
14988
15211
|
const current = new ControllerStore(directory).read();
|
|
14989
15212
|
if (!expected || current?.store !== expected.store || current.epoch !== expected.epoch)
|
|
14990
15213
|
throw new Error("Capacity controller incarnation changed.");
|
|
@@ -15036,7 +15259,7 @@ function prepareRecoveryLifecycleOperation(input2) {
|
|
|
15036
15259
|
});
|
|
15037
15260
|
}
|
|
15038
15261
|
function settlePreparedLifecycleCapacity(input2) {
|
|
15039
|
-
const directory = input2.directory ??
|
|
15262
|
+
const directory = input2.directory ?? import_node_path45.default.join(DEVROUTER_HOME, "controller");
|
|
15040
15263
|
const store = new CapacityStore(directory);
|
|
15041
15264
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
15042
15265
|
const snapshot = store.read();
|
|
@@ -15250,7 +15473,7 @@ async function superviseLifecycle(kind, repoPath, options = {}, command) {
|
|
|
15250
15473
|
removeSignals();
|
|
15251
15474
|
}
|
|
15252
15475
|
}
|
|
15253
|
-
function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory =
|
|
15476
|
+
function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory = import_node_path45.default.join(DEVROUTER_HOME, "controller"), controller, admission) {
|
|
15254
15477
|
if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
|
|
15255
15478
|
const capacity = new CapacityStore(directory);
|
|
15256
15479
|
const snapshot = capacity.read();
|
|
@@ -15350,7 +15573,7 @@ function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs,
|
|
|
15350
15573
|
return decision;
|
|
15351
15574
|
}
|
|
15352
15575
|
function capacityPolicyTargets(repoPath) {
|
|
15353
|
-
const policy = readCapacityPolicy(
|
|
15576
|
+
const policy = readCapacityPolicy(import_node_path45.default.join(DEVROUTER_HOME, "controller"));
|
|
15354
15577
|
return policy?.admissions === "enabled" && policy.enrollments.some((entry) => entry.repoPath === repoPath);
|
|
15355
15578
|
}
|
|
15356
15579
|
function controllerWaitSeconds(deadline) {
|
|
@@ -15359,7 +15582,7 @@ function controllerWaitSeconds(deadline) {
|
|
|
15359
15582
|
async function superviseThroughController(input2) {
|
|
15360
15583
|
if (input2.kind === "exec" && !input2.command)
|
|
15361
15584
|
throw new Error("Exec requires a command for controller admission.");
|
|
15362
|
-
const directory =
|
|
15585
|
+
const directory = import_node_path45.default.join(DEVROUTER_HOME, "controller");
|
|
15363
15586
|
const cancellation = { requested: false };
|
|
15364
15587
|
const removeSignals = installLifecycleWaitSignals(cancellation);
|
|
15365
15588
|
const cancelled = () => {
|
|
@@ -15431,7 +15654,7 @@ async function superviseThroughController(input2) {
|
|
|
15431
15654
|
removeSignals();
|
|
15432
15655
|
}
|
|
15433
15656
|
}
|
|
15434
|
-
function renewLifecycleCapacity(request2, policy, samples, controller, directory =
|
|
15657
|
+
function renewLifecycleCapacity(request2, policy, samples, controller, directory = import_node_path45.default.join(DEVROUTER_HOME, "controller"), nowMs = Date.now()) {
|
|
15435
15658
|
return updateReliabilityOperation(request2.identity, (record5) => {
|
|
15436
15659
|
const binding = record5.capacity;
|
|
15437
15660
|
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 +15742,7 @@ function retireQueuedLifecycle(request2, supersededOnly = false) {
|
|
|
15519
15742
|
return true;
|
|
15520
15743
|
});
|
|
15521
15744
|
}
|
|
15522
|
-
function bindLifecycleCapacity(request2, binding, directory =
|
|
15745
|
+
function bindLifecycleCapacity(request2, binding, directory = import_node_path45.default.join(DEVROUTER_HOME, "controller")) {
|
|
15523
15746
|
if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
|
|
15524
15747
|
updateReliabilityOperation(request2.identity, (record5) => {
|
|
15525
15748
|
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 +15757,12 @@ function bindLifecycleCapacity(request2, binding, directory = import_node_path44
|
|
|
15534
15757
|
stepRecord(record5, { ...request2.fence, type: "admission", result: "admitted" });
|
|
15535
15758
|
});
|
|
15536
15759
|
}
|
|
15537
|
-
var import_node_crypto21,
|
|
15760
|
+
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
15761
|
var init_reliability_lifecycle = __esm({
|
|
15539
15762
|
"src/core/reliability-lifecycle.ts"() {
|
|
15540
15763
|
"use strict";
|
|
15541
15764
|
import_node_crypto21 = require("crypto");
|
|
15542
|
-
|
|
15765
|
+
import_node_path45 = __toESM(require("path"));
|
|
15543
15766
|
import_node_util10 = require("util");
|
|
15544
15767
|
init_capacity_accounting();
|
|
15545
15768
|
init_capacity_policy();
|
|
@@ -15552,6 +15775,7 @@ var init_reliability_lifecycle = __esm({
|
|
|
15552
15775
|
init_devsy_mutation();
|
|
15553
15776
|
init_file_lock();
|
|
15554
15777
|
init_host_routes();
|
|
15778
|
+
init_managed_devsy_stop();
|
|
15555
15779
|
init_managed_runtime_state();
|
|
15556
15780
|
init_managed_stop_recovery();
|
|
15557
15781
|
init_reliability_context();
|
|
@@ -16031,7 +16255,7 @@ function createCapacityController(options) {
|
|
|
16031
16255
|
const record5 = readReliabilityOperation(identity2);
|
|
16032
16256
|
if (!record5?.capacity || !record5.preparation && !record5.capacity.execSteady || record5.worker || !record5.state.operation?.drained || !["ensure", "exec"].includes(record5.state.operation.kind))
|
|
16033
16257
|
continue;
|
|
16034
|
-
const bytes2 = readControllerEvidence(
|
|
16258
|
+
const bytes2 = readControllerEvidence(import_node_path46.default.join(enrollment.repoPath, ".devrouter.yml"));
|
|
16035
16259
|
const estimates = loadRepoConfig(enrollment.repoPath, () => bytes2).capacity;
|
|
16036
16260
|
if (!estimates) continue;
|
|
16037
16261
|
settlePreparedLifecycleCapacity({
|
|
@@ -16272,12 +16496,12 @@ function createCapacityController(options) {
|
|
|
16272
16496
|
}
|
|
16273
16497
|
};
|
|
16274
16498
|
}
|
|
16275
|
-
var import_node_crypto22,
|
|
16499
|
+
var import_node_crypto22, import_node_path46, import_node_util12;
|
|
16276
16500
|
var init_capacity_controller = __esm({
|
|
16277
16501
|
"src/core/capacity-controller.ts"() {
|
|
16278
16502
|
"use strict";
|
|
16279
16503
|
import_node_crypto22 = require("crypto");
|
|
16280
|
-
|
|
16504
|
+
import_node_path46 = __toESM(require("path"));
|
|
16281
16505
|
import_node_util12 = require("util");
|
|
16282
16506
|
init_capacity_docker_probe();
|
|
16283
16507
|
init_capacity_enrollment();
|
|
@@ -16637,12 +16861,12 @@ async function containers(project, signal) {
|
|
|
16637
16861
|
}
|
|
16638
16862
|
return values.sort((a, b) => a.id.localeCompare(b.id));
|
|
16639
16863
|
}
|
|
16640
|
-
var import_node_crypto24,
|
|
16864
|
+
var import_node_crypto24, import_node_path47, OBSERVATION_INSPECT_TEMPLATE, collectControllerObservation;
|
|
16641
16865
|
var init_controller_observation = __esm({
|
|
16642
16866
|
"src/core/controller-observation.ts"() {
|
|
16643
16867
|
"use strict";
|
|
16644
16868
|
import_node_crypto24 = require("crypto");
|
|
16645
|
-
|
|
16869
|
+
import_node_path47 = __toESM(require("path"));
|
|
16646
16870
|
init_controller_binding();
|
|
16647
16871
|
init_controller_monitor();
|
|
16648
16872
|
init_controller_probe();
|
|
@@ -16685,14 +16909,14 @@ var init_controller_observation = __esm({
|
|
|
16685
16909
|
)).trim().split("\n");
|
|
16686
16910
|
if (metadata.length !== 2) throw new Error("Observation ownership paths unavailable.");
|
|
16687
16911
|
const [gitDir, commonDir] = metadata;
|
|
16688
|
-
const sourcePath =
|
|
16912
|
+
const sourcePath = import_node_path47.default.join(environment.repoPath, ".devcontainer/devcontainer.json");
|
|
16689
16913
|
const files = [
|
|
16690
|
-
|
|
16691
|
-
|
|
16692
|
-
|
|
16914
|
+
import_node_path47.default.join(commonDir, "devrouter/workspaces", `${environment.workspace}.json`),
|
|
16915
|
+
import_node_path47.default.join(environment.repoPath, ".devrouter.yml"),
|
|
16916
|
+
import_node_path47.default.join(gitDir, "devrouter-workspace"),
|
|
16693
16917
|
managedRuntimeStatePath(environment.repoPath, environment.workspace),
|
|
16694
16918
|
sourcePath,
|
|
16695
|
-
|
|
16919
|
+
import_node_path47.default.join(environment.repoPath, MANAGED_DEVCONTAINER_PATH)
|
|
16696
16920
|
];
|
|
16697
16921
|
const persisted = captureControllerEvidence(files);
|
|
16698
16922
|
if (controllerBindingFingerprint(persisted.contents[0], persisted.contents[1]) !== environment.fingerprint)
|
|
@@ -16717,7 +16941,7 @@ var init_controller_observation = __esm({
|
|
|
16717
16941
|
if (!journal) throw new Error("Observation journal is unavailable.");
|
|
16718
16942
|
const routes = readRoutes().filter((route) => route.repoPath === environment.repoPath);
|
|
16719
16943
|
const before = await containers(state.composeProject, signal);
|
|
16720
|
-
const composeDirectory =
|
|
16944
|
+
const composeDirectory = import_node_path47.default.join(environment.repoPath, ".devcontainer");
|
|
16721
16945
|
const references = typeof source.dockerComposeFile === "string" ? [source.dockerComposeFile] : source.dockerComposeFile;
|
|
16722
16946
|
if (!Array.isArray(references) || !references.length || references.some((value) => typeof value !== "string"))
|
|
16723
16947
|
throw new Error("Observation Compose source is unavailable.");
|
|
@@ -16734,7 +16958,7 @@ var init_controller_observation = __esm({
|
|
|
16734
16958
|
if (!recorded || container.labels["com.docker.compose.project.working_dir"] !== composeDirectory)
|
|
16735
16959
|
throw new Error("Observation Compose ownership is unavailable.");
|
|
16736
16960
|
const files2 = recorded.split(",").map((file) => file.trim());
|
|
16737
|
-
if (files2.some((file) => !
|
|
16961
|
+
if (files2.some((file) => !import_node_path47.default.isAbsolute(file)) || !hasExactComposeIdentity(container, {
|
|
16738
16962
|
repoPath: environment.repoPath,
|
|
16739
16963
|
service: container.labels["com.docker.compose.service"] ?? "",
|
|
16740
16964
|
composeProject: state.composeProject,
|
|
@@ -17099,26 +17323,26 @@ var init_controller_sessions = __esm({
|
|
|
17099
17323
|
|
|
17100
17324
|
// src/core/controller-server.ts
|
|
17101
17325
|
function privateDirectory(directory) {
|
|
17102
|
-
const absolute2 =
|
|
17103
|
-
let current =
|
|
17104
|
-
for (const part of absolute2.slice(current.length).split(
|
|
17105
|
-
current =
|
|
17326
|
+
const absolute2 = import_node_path48.default.resolve(directory);
|
|
17327
|
+
let current = import_node_path48.default.parse(absolute2).root;
|
|
17328
|
+
for (const part of absolute2.slice(current.length).split(import_node_path48.default.sep)) {
|
|
17329
|
+
current = import_node_path48.default.join(current, part);
|
|
17106
17330
|
try {
|
|
17107
|
-
const stat2 =
|
|
17331
|
+
const stat2 = import_node_fs37.default.lstatSync(current);
|
|
17108
17332
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
17109
17333
|
throw new Error("Unsafe controller directory.");
|
|
17110
17334
|
} catch (error) {
|
|
17111
17335
|
if (error.code !== "ENOENT") throw error;
|
|
17112
|
-
|
|
17336
|
+
import_node_fs37.default.mkdirSync(current, { mode: 448 });
|
|
17113
17337
|
}
|
|
17114
17338
|
}
|
|
17115
|
-
const stat =
|
|
17339
|
+
const stat = import_node_fs37.default.lstatSync(absolute2);
|
|
17116
17340
|
if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
17117
17341
|
throw new Error("Controller directory is not private.");
|
|
17118
17342
|
}
|
|
17119
17343
|
function validateOwnedFile(file, socket) {
|
|
17120
17344
|
try {
|
|
17121
|
-
const stat =
|
|
17345
|
+
const stat = import_node_fs37.default.lstatSync(file);
|
|
17122
17346
|
if (stat.uid !== process.getuid?.() || (socket ? !stat.isSocket() : !stat.isFile()) || (stat.mode & 63) !== 0)
|
|
17123
17347
|
throw new Error("Unsafe controller artifact.");
|
|
17124
17348
|
return true;
|
|
@@ -17130,19 +17354,19 @@ function validateOwnedFile(file, socket) {
|
|
|
17130
17354
|
async function runController(options) {
|
|
17131
17355
|
if (options.operations && options.createOperations)
|
|
17132
17356
|
throw new Error("Controller operations have multiple owners.");
|
|
17133
|
-
const socketPath =
|
|
17357
|
+
const socketPath = import_node_path48.default.join(options.directory, "control.sock");
|
|
17134
17358
|
if (Buffer.byteLength(socketPath) > 103) throw new Error("Controller socket path is too long.");
|
|
17135
17359
|
privateDirectory(options.directory);
|
|
17136
|
-
const lockPath =
|
|
17360
|
+
const lockPath = import_node_path48.default.join(options.directory, "owner.lock");
|
|
17137
17361
|
try {
|
|
17138
|
-
const stat =
|
|
17362
|
+
const stat = import_node_fs37.default.lstatSync(lockPath);
|
|
17139
17363
|
if (!stat.isFile() || stat.uid !== process.getuid?.())
|
|
17140
17364
|
throw new Error("Unsafe controller owner lock.");
|
|
17141
17365
|
} catch (error) {
|
|
17142
17366
|
if (error.code !== "ENOENT") throw error;
|
|
17143
17367
|
}
|
|
17144
17368
|
await withFileLock(lockPath, { activity: "controller ownership", waitMs: 0 }, async () => {
|
|
17145
|
-
if (validateOwnedFile(socketPath, true))
|
|
17369
|
+
if (validateOwnedFile(socketPath, true)) import_node_fs37.default.unlinkSync(socketPath);
|
|
17146
17370
|
const sessions = new ControllerSessions(new ControllerStore(options.directory));
|
|
17147
17371
|
const incarnation = sessions.read();
|
|
17148
17372
|
let startupAvailable = true;
|
|
@@ -17531,7 +17755,7 @@ async function runController(options) {
|
|
|
17531
17755
|
resolve();
|
|
17532
17756
|
});
|
|
17533
17757
|
});
|
|
17534
|
-
|
|
17758
|
+
import_node_fs37.default.chmodSync(socketPath, 384);
|
|
17535
17759
|
await new Promise((resolve) => {
|
|
17536
17760
|
finish = resolve;
|
|
17537
17761
|
options.signal.addEventListener("abort", shutdown, { once: true });
|
|
@@ -17550,13 +17774,13 @@ async function runController(options) {
|
|
|
17550
17774
|
}
|
|
17551
17775
|
});
|
|
17552
17776
|
}
|
|
17553
|
-
var
|
|
17777
|
+
var import_node_fs37, import_node_net4, import_node_path48, FRAME_BYTES;
|
|
17554
17778
|
var init_controller_server = __esm({
|
|
17555
17779
|
"src/core/controller-server.ts"() {
|
|
17556
17780
|
"use strict";
|
|
17557
|
-
|
|
17781
|
+
import_node_fs37 = __toESM(require("fs"));
|
|
17558
17782
|
import_node_net4 = __toESM(require("net"));
|
|
17559
|
-
|
|
17783
|
+
import_node_path48 = __toESM(require("path"));
|
|
17560
17784
|
init_controller_monitor();
|
|
17561
17785
|
init_controller_protocol();
|
|
17562
17786
|
init_controller_sessions();
|
|
@@ -17573,7 +17797,7 @@ __export(controller_exports, {
|
|
|
17573
17797
|
runControllerCommand: () => runControllerCommand
|
|
17574
17798
|
});
|
|
17575
17799
|
async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
17576
|
-
const directory =
|
|
17800
|
+
const directory = import_node_path49.default.join(DEVROUTER_HOME, "controller");
|
|
17577
17801
|
try {
|
|
17578
17802
|
if (method === "run") {
|
|
17579
17803
|
const controller = new AbortController();
|
|
@@ -17608,7 +17832,7 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
|
17608
17832
|
if (fields2.timeout !== void 0) fields2.timeout = Number(fields2.timeout);
|
|
17609
17833
|
await controllerRequest(
|
|
17610
17834
|
directory,
|
|
17611
|
-
{ method, ...fields2, ...repo ? { path:
|
|
17835
|
+
{ method, ...fields2, ...repo ? { path: import_node_path49.default.resolve(repo) } : {} },
|
|
17612
17836
|
(value) => process.stdout.write(`${JSON.stringify(value)}
|
|
17613
17837
|
`)
|
|
17614
17838
|
);
|
|
@@ -17623,11 +17847,11 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
|
17623
17847
|
process.exitCode = 1;
|
|
17624
17848
|
}
|
|
17625
17849
|
}
|
|
17626
|
-
var
|
|
17850
|
+
var import_node_path49;
|
|
17627
17851
|
var init_controller = __esm({
|
|
17628
17852
|
"src/commands/controller.ts"() {
|
|
17629
17853
|
"use strict";
|
|
17630
|
-
|
|
17854
|
+
import_node_path49 = __toESM(require("path"));
|
|
17631
17855
|
init_capacity_collector();
|
|
17632
17856
|
init_capacity_controller();
|
|
17633
17857
|
init_capacity_policy();
|
|
@@ -17678,28 +17902,28 @@ function replaceDevrouterSection(content) {
|
|
|
17678
17902
|
return `${content.slice(0, start)}${buildDevrouterSection()}${content.slice(end)}`;
|
|
17679
17903
|
}
|
|
17680
17904
|
function writeRepoFile(repoPath, relPath, content) {
|
|
17681
|
-
const absolutePath = (0,
|
|
17682
|
-
(0,
|
|
17683
|
-
(0,
|
|
17905
|
+
const absolutePath = (0, import_node_path50.join)(repoPath, relPath);
|
|
17906
|
+
(0, import_node_fs38.mkdirSync)((0, import_node_path50.dirname)(absolutePath), { recursive: true });
|
|
17907
|
+
(0, import_node_fs38.writeFileSync)(absolutePath, content, "utf-8");
|
|
17684
17908
|
return absolutePath;
|
|
17685
17909
|
}
|
|
17686
17910
|
function ensureAgentsMdSection(repoPath) {
|
|
17687
|
-
const filePath = (0,
|
|
17688
|
-
if ((0,
|
|
17689
|
-
const content = (0,
|
|
17911
|
+
const filePath = (0, import_node_path50.join)(repoPath, AGENTS_MD);
|
|
17912
|
+
if ((0, import_node_fs38.existsSync)(filePath)) {
|
|
17913
|
+
const content = (0, import_node_fs38.readFileSync)(filePath, "utf-8");
|
|
17690
17914
|
if (content.includes(DEVROUTER_SENTINEL)) {
|
|
17691
17915
|
const updated = replaceDevrouterSection(content);
|
|
17692
17916
|
if (updated === void 0 || updated === content) return { path: filePath, written: false };
|
|
17693
|
-
(0,
|
|
17917
|
+
(0, import_node_fs38.writeFileSync)(filePath, updated, "utf-8");
|
|
17694
17918
|
return { path: filePath, written: true };
|
|
17695
17919
|
}
|
|
17696
|
-
(0,
|
|
17920
|
+
(0, import_node_fs38.writeFileSync)(filePath, `${content.trimEnd()}
|
|
17697
17921
|
|
|
17698
17922
|
${buildDevrouterSection()}
|
|
17699
17923
|
`, "utf-8");
|
|
17700
17924
|
return { path: filePath, written: true };
|
|
17701
17925
|
}
|
|
17702
|
-
(0,
|
|
17926
|
+
(0, import_node_fs38.writeFileSync)(filePath, `# AGENTS.md
|
|
17703
17927
|
|
|
17704
17928
|
${buildDevrouterSection()}
|
|
17705
17929
|
`, "utf-8");
|
|
@@ -17709,12 +17933,12 @@ function ensureSkillFile(repoPath) {
|
|
|
17709
17933
|
const filePath = writeRepoFile(repoPath, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT);
|
|
17710
17934
|
return { path: filePath, written: true };
|
|
17711
17935
|
}
|
|
17712
|
-
var
|
|
17936
|
+
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
17937
|
var init_agents_md = __esm({
|
|
17714
17938
|
"src/core/agents-md.ts"() {
|
|
17715
17939
|
"use strict";
|
|
17716
|
-
|
|
17717
|
-
|
|
17940
|
+
import_node_fs38 = require("fs");
|
|
17941
|
+
import_node_path50 = require("path");
|
|
17718
17942
|
DEVROUTER_SENTINEL = "<!-- devrouter -->";
|
|
17719
17943
|
DEVROUTER_END_SENTINEL = "<!-- /devrouter -->";
|
|
17720
17944
|
LEGACY_DEVROUTER_SECTION_END = "- `devrouter ls`";
|
|
@@ -18439,7 +18663,7 @@ var init_ai_prompt = __esm({
|
|
|
18439
18663
|
},
|
|
18440
18664
|
{
|
|
18441
18665
|
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."
|
|
18666
|
+
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
18667
|
},
|
|
18444
18668
|
{
|
|
18445
18669
|
command: "devrouter exec [path] -- <command...>",
|
|
@@ -18993,11 +19217,11 @@ function extractCurrentVersionFromRepoConfig(repoPath) {
|
|
|
18993
19217
|
};
|
|
18994
19218
|
}
|
|
18995
19219
|
function readPromptDirectory(promptDirPath) {
|
|
18996
|
-
if (!
|
|
19220
|
+
if (!import_node_fs39.default.existsSync(promptDirPath)) {
|
|
18997
19221
|
throw new Error(`Missing ${UPGRADE_PROMPTS_DIR} directory at ${promptDirPath}.`);
|
|
18998
19222
|
}
|
|
18999
19223
|
const releases = [];
|
|
19000
|
-
const files =
|
|
19224
|
+
const files = import_node_fs39.default.readdirSync(promptDirPath, { withFileTypes: true });
|
|
19001
19225
|
for (const file of files) {
|
|
19002
19226
|
if (!file.isFile() || !file.name.endsWith(".md")) {
|
|
19003
19227
|
continue;
|
|
@@ -19007,8 +19231,8 @@ function readPromptDirectory(promptDirPath) {
|
|
|
19007
19231
|
continue;
|
|
19008
19232
|
}
|
|
19009
19233
|
const version = normalizeVersion(basename);
|
|
19010
|
-
const promptPath =
|
|
19011
|
-
const prompt =
|
|
19234
|
+
const promptPath = import_node_path51.default.join(promptDirPath, file.name);
|
|
19235
|
+
const prompt = import_node_fs39.default.readFileSync(promptPath, "utf-8").trim();
|
|
19012
19236
|
releases.push({ version, prompt, promptPath });
|
|
19013
19237
|
}
|
|
19014
19238
|
const deduped = /* @__PURE__ */ new Map();
|
|
@@ -19029,19 +19253,19 @@ function listAvailableUpgradeTargets(currentVersion, releases) {
|
|
|
19029
19253
|
}
|
|
19030
19254
|
function resolvePromptDirectory(explicitPath) {
|
|
19031
19255
|
if (explicitPath) {
|
|
19032
|
-
return
|
|
19256
|
+
return import_node_path51.default.resolve(explicitPath);
|
|
19033
19257
|
}
|
|
19034
|
-
const entryFile = process.argv[1] ?
|
|
19035
|
-
const entryDir =
|
|
19036
|
-
const resolvedEntryDir =
|
|
19258
|
+
const entryFile = process.argv[1] ? import_node_path51.default.resolve(process.argv[1]) : __filename;
|
|
19259
|
+
const entryDir = import_node_path51.default.dirname(entryFile);
|
|
19260
|
+
const resolvedEntryDir = import_node_fs39.default.existsSync(entryFile) ? import_node_path51.default.dirname(import_node_fs39.default.realpathSync(entryFile)) : entryDir;
|
|
19037
19261
|
const candidates = [
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
|
|
19041
|
-
|
|
19042
|
-
|
|
19262
|
+
import_node_path51.default.resolve(resolvedEntryDir, "..", UPGRADE_PROMPTS_DIR),
|
|
19263
|
+
import_node_path51.default.resolve(entryDir, "..", UPGRADE_PROMPTS_DIR),
|
|
19264
|
+
import_node_path51.default.resolve(__dirname, "..", UPGRADE_PROMPTS_DIR),
|
|
19265
|
+
import_node_path51.default.resolve(__dirname, "..", "..", UPGRADE_PROMPTS_DIR),
|
|
19266
|
+
import_node_path51.default.resolve(process.cwd(), UPGRADE_PROMPTS_DIR)
|
|
19043
19267
|
];
|
|
19044
|
-
const existing = candidates.find((candidate) =>
|
|
19268
|
+
const existing = candidates.find((candidate) => import_node_fs39.default.existsSync(candidate));
|
|
19045
19269
|
return existing ?? candidates[0];
|
|
19046
19270
|
}
|
|
19047
19271
|
function loadUpgradeCatalog(options = {}) {
|
|
@@ -19057,12 +19281,12 @@ function loadUpgradeCatalog(options = {}) {
|
|
|
19057
19281
|
releases
|
|
19058
19282
|
};
|
|
19059
19283
|
}
|
|
19060
|
-
var
|
|
19284
|
+
var import_node_fs39, import_node_path51, SEMVER_RE, UPGRADE_PROMPTS_DIR;
|
|
19061
19285
|
var init_upgrade = __esm({
|
|
19062
19286
|
"src/core/upgrade.ts"() {
|
|
19063
19287
|
"use strict";
|
|
19064
|
-
|
|
19065
|
-
|
|
19288
|
+
import_node_fs39 = __toESM(require("fs"));
|
|
19289
|
+
import_node_path51 = __toESM(require("path"));
|
|
19066
19290
|
init_repo_config();
|
|
19067
19291
|
SEMVER_RE = /^v?(\d+)\.(\d+)\.(\d+)$/;
|
|
19068
19292
|
UPGRADE_PROMPTS_DIR = "upgrade-prompts";
|
|
@@ -19205,7 +19429,7 @@ function isLoopbackHost(host) {
|
|
|
19205
19429
|
return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
|
|
19206
19430
|
}
|
|
19207
19431
|
function inspectCompose(composeFile, _workspace) {
|
|
19208
|
-
if (!
|
|
19432
|
+
if (!import_node_fs40.default.existsSync(composeFile)) {
|
|
19209
19433
|
return {
|
|
19210
19434
|
aliases: [],
|
|
19211
19435
|
publishedPorts: [],
|
|
@@ -19214,7 +19438,7 @@ function inspectCompose(composeFile, _workspace) {
|
|
|
19214
19438
|
};
|
|
19215
19439
|
}
|
|
19216
19440
|
try {
|
|
19217
|
-
const raw =
|
|
19441
|
+
const raw = import_node_fs40.default.readFileSync(composeFile, "utf-8");
|
|
19218
19442
|
const parsed = import_yaml5.default.parse(raw);
|
|
19219
19443
|
const root = asRecord(parsed);
|
|
19220
19444
|
const services = asRecord(root?.services);
|
|
@@ -19310,11 +19534,11 @@ function buildHostPortClaimsCheck(repoPath, workspace) {
|
|
|
19310
19534
|
}
|
|
19311
19535
|
}
|
|
19312
19536
|
function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
19313
|
-
const devcontainerDir =
|
|
19314
|
-
if (!
|
|
19537
|
+
const devcontainerDir = import_node_path52.default.join(repoPath, ".devcontainer");
|
|
19538
|
+
if (!import_node_fs40.default.existsSync(devcontainerDir)) {
|
|
19315
19539
|
return [];
|
|
19316
19540
|
}
|
|
19317
|
-
const compose = inspectCompose(
|
|
19541
|
+
const compose = inspectCompose(import_node_path52.default.join(devcontainerDir, "docker-compose.yml"), workspace);
|
|
19318
19542
|
const checks = [];
|
|
19319
19543
|
if (compose.parseError) {
|
|
19320
19544
|
checks.push({
|
|
@@ -19341,9 +19565,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
19341
19565
|
details: compose.publishedPorts.length > 0 ? compose.publishedPorts.join(", ") : void 0,
|
|
19342
19566
|
suggestion: compose.parseError || compose.publishedPorts.length > 0 ? "Remove published ports and route services through devnet aliases with devrouter proxy apps." : void 0
|
|
19343
19567
|
});
|
|
19344
|
-
const dockerfilePath =
|
|
19345
|
-
const dockerfileExists =
|
|
19346
|
-
const dockerfile = dockerfileExists ?
|
|
19568
|
+
const dockerfilePath = import_node_path52.default.join(devcontainerDir, "Dockerfile");
|
|
19569
|
+
const dockerfileExists = import_node_fs40.default.existsSync(dockerfilePath);
|
|
19570
|
+
const dockerfile = dockerfileExists ? import_node_fs40.default.readFileSync(dockerfilePath, "utf-8") : "";
|
|
19347
19571
|
const devrouterArtifacts = ["@devrouter/cli", "devrouter-process"].filter(
|
|
19348
19572
|
(artifact) => dockerfile.includes(artifact)
|
|
19349
19573
|
);
|
|
@@ -19401,12 +19625,12 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
19401
19625
|
});
|
|
19402
19626
|
return checks;
|
|
19403
19627
|
}
|
|
19404
|
-
var
|
|
19628
|
+
var import_node_fs40, import_node_path52, import_yaml5;
|
|
19405
19629
|
var init_devcontainer_diagnostics = __esm({
|
|
19406
19630
|
"src/core/devcontainer-diagnostics.ts"() {
|
|
19407
19631
|
"use strict";
|
|
19408
|
-
|
|
19409
|
-
|
|
19632
|
+
import_node_fs40 = __toESM(require("fs"));
|
|
19633
|
+
import_node_path52 = __toESM(require("path"));
|
|
19410
19634
|
import_yaml5 = __toESM(require("yaml"));
|
|
19411
19635
|
init_devcontainer_profile();
|
|
19412
19636
|
init_host_port_claims();
|
|
@@ -19501,11 +19725,11 @@ function reconcileRouteRunConflict(repoPath, app) {
|
|
|
19501
19725
|
}
|
|
19502
19726
|
}
|
|
19503
19727
|
}
|
|
19504
|
-
var
|
|
19728
|
+
var import_node_fs41;
|
|
19505
19729
|
var init_route_state = __esm({
|
|
19506
19730
|
"src/core/route-state.ts"() {
|
|
19507
19731
|
"use strict";
|
|
19508
|
-
|
|
19732
|
+
import_node_fs41 = __toESM(require("fs"));
|
|
19509
19733
|
init_host_routes();
|
|
19510
19734
|
init_workspace();
|
|
19511
19735
|
}
|
|
@@ -19680,7 +19904,7 @@ function toRepoStatus(repoPath) {
|
|
|
19680
19904
|
const resolvedRepoPath = resolveRepoPath(repoPath);
|
|
19681
19905
|
const configPath = getRepoConfigPath(resolvedRepoPath);
|
|
19682
19906
|
const explicitRepo = typeof repoPath === "string" && repoPath.trim().length > 0;
|
|
19683
|
-
const configExists =
|
|
19907
|
+
const configExists = import_node_fs42.default.existsSync(configPath);
|
|
19684
19908
|
if (!explicitRepo && !configExists) {
|
|
19685
19909
|
return void 0;
|
|
19686
19910
|
}
|
|
@@ -19828,11 +20052,11 @@ async function collectRouterStatus(repoPath) {
|
|
|
19828
20052
|
}
|
|
19829
20053
|
};
|
|
19830
20054
|
}
|
|
19831
|
-
var
|
|
20055
|
+
var import_node_fs42;
|
|
19832
20056
|
var init_status = __esm({
|
|
19833
20057
|
"src/core/status.ts"() {
|
|
19834
20058
|
"use strict";
|
|
19835
|
-
|
|
20059
|
+
import_node_fs42 = __toESM(require("fs"));
|
|
19836
20060
|
init_docker();
|
|
19837
20061
|
init_managed_runtime_state();
|
|
19838
20062
|
init_managed_runtime_status();
|
|
@@ -19906,12 +20130,12 @@ function parseMinimumNodeMajor(value) {
|
|
|
19906
20130
|
return Number(match[1]);
|
|
19907
20131
|
}
|
|
19908
20132
|
function readPackageJson(repoPath) {
|
|
19909
|
-
const packagePath =
|
|
19910
|
-
if (!
|
|
20133
|
+
const packagePath = import_node_path53.default.join(repoPath, "package.json");
|
|
20134
|
+
if (!import_node_fs43.default.existsSync(packagePath)) {
|
|
19911
20135
|
return void 0;
|
|
19912
20136
|
}
|
|
19913
20137
|
try {
|
|
19914
|
-
return JSON.parse(
|
|
20138
|
+
return JSON.parse(import_node_fs43.default.readFileSync(packagePath, "utf-8"));
|
|
19915
20139
|
} catch {
|
|
19916
20140
|
return void 0;
|
|
19917
20141
|
}
|
|
@@ -20095,13 +20319,13 @@ function buildGlobalToolChecksWithoutTelemetry(repoPath) {
|
|
|
20095
20319
|
checks.push(nodeToolchainCheck(repoPath));
|
|
20096
20320
|
return checks;
|
|
20097
20321
|
}
|
|
20098
|
-
var import_node_child_process29,
|
|
20322
|
+
var import_node_child_process29, import_node_fs43, import_node_path53, DEVSY_DISABLE_TELEMETRY;
|
|
20099
20323
|
var init_tool_diagnostics = __esm({
|
|
20100
20324
|
"src/core/tool-diagnostics.ts"() {
|
|
20101
20325
|
"use strict";
|
|
20102
20326
|
import_node_child_process29 = require("child_process");
|
|
20103
|
-
|
|
20104
|
-
|
|
20327
|
+
import_node_fs43 = __toESM(require("fs"));
|
|
20328
|
+
import_node_path53 = __toESM(require("path"));
|
|
20105
20329
|
init_devsy_agent();
|
|
20106
20330
|
init_workspace_runtime();
|
|
20107
20331
|
DEVSY_DISABLE_TELEMETRY = "DEVSY_DISABLE_TELEMETRY";
|
|
@@ -20118,10 +20342,10 @@ function inRepositoryWorkspaceScope(repoPath, worktreePath, livePaths) {
|
|
|
20118
20342
|
if (livePaths.some((candidate) => sameWorkspacePath(candidate, worktreePath))) return true;
|
|
20119
20343
|
const comparableRepo = comparableWorkspacePath(repoPath);
|
|
20120
20344
|
const comparableWorktree = comparableWorkspacePath(worktreePath);
|
|
20121
|
-
const localRoot =
|
|
20345
|
+
const localRoot = import_node_path54.default.join(comparableRepo, "trees") + import_node_path54.default.sep;
|
|
20122
20346
|
if (comparableWorktree.startsWith(localRoot)) return true;
|
|
20123
|
-
const legacyPrefix = `${
|
|
20124
|
-
return
|
|
20347
|
+
const legacyPrefix = `${import_node_path54.default.basename(comparableRepo)}-`;
|
|
20348
|
+
return import_node_path54.default.dirname(comparableWorktree) === import_node_path54.default.dirname(comparableRepo) && import_node_path54.default.basename(comparableWorktree).startsWith(legacyPrefix);
|
|
20125
20349
|
}
|
|
20126
20350
|
function previewActions(devpodStatus, routeCount, includeRecord) {
|
|
20127
20351
|
const actions = [
|
|
@@ -20373,11 +20597,11 @@ function applyWorkspaceGc(plan) {
|
|
|
20373
20597
|
candidates
|
|
20374
20598
|
};
|
|
20375
20599
|
}
|
|
20376
|
-
var
|
|
20600
|
+
var import_node_path54;
|
|
20377
20601
|
var init_workspace_gc = __esm({
|
|
20378
20602
|
"src/core/workspace-gc.ts"() {
|
|
20379
20603
|
"use strict";
|
|
20380
|
-
|
|
20604
|
+
import_node_path54 = __toESM(require("path"));
|
|
20381
20605
|
init_devpod_mutation();
|
|
20382
20606
|
init_devpod_workspaces();
|
|
20383
20607
|
init_host_routes();
|
|
@@ -20447,11 +20671,11 @@ function inspectPostgresCredentials(repoPath, config) {
|
|
|
20447
20671
|
parseErrors.push(`${app.name}: ${composeFile} (${message})`);
|
|
20448
20672
|
continue;
|
|
20449
20673
|
}
|
|
20450
|
-
if (!
|
|
20674
|
+
if (!import_node_fs44.default.existsSync(absolutePath)) {
|
|
20451
20675
|
continue;
|
|
20452
20676
|
}
|
|
20453
20677
|
try {
|
|
20454
|
-
const raw =
|
|
20678
|
+
const raw = import_node_fs44.default.readFileSync(absolutePath, "utf-8");
|
|
20455
20679
|
const parsed = import_yaml6.default.parse(raw);
|
|
20456
20680
|
const root = asRecord2(parsed);
|
|
20457
20681
|
const services = asRecord2(root?.services);
|
|
@@ -20731,7 +20955,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
20731
20955
|
const config = runtimeConfig.config;
|
|
20732
20956
|
loadedConfig = config;
|
|
20733
20957
|
loadedWorkspace = runtimeConfig.workspace;
|
|
20734
|
-
const cliVersion = true ? "0.0.
|
|
20958
|
+
const cliVersion = true ? "0.0.74" : "0.0.0-dev";
|
|
20735
20959
|
const configVersion = config.devrouter?.version;
|
|
20736
20960
|
if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
|
|
20737
20961
|
addCheck(checks, {
|
|
@@ -20761,9 +20985,9 @@ async function buildDoctorReport(options = {}) {
|
|
|
20761
20985
|
(app) => app.docker.composeFiles.map((filePath) => ({
|
|
20762
20986
|
app: app.name,
|
|
20763
20987
|
filePath,
|
|
20764
|
-
absolutePath:
|
|
20988
|
+
absolutePath: import_node_path55.default.resolve(repo.path, filePath)
|
|
20765
20989
|
}))
|
|
20766
|
-
).filter((entry) => !
|
|
20990
|
+
).filter((entry) => !import_node_fs44.default.existsSync(entry.absolutePath));
|
|
20767
20991
|
addCheck(checks, {
|
|
20768
20992
|
id: "repo.compose-files",
|
|
20769
20993
|
level: missingComposeFiles.length === 0 ? "ok" : "error",
|
|
@@ -20806,8 +21030,8 @@ async function buildDoctorReport(options = {}) {
|
|
|
20806
21030
|
const missingHostCwds = config.apps.filter((app) => app.runtime === "host").map((app) => ({
|
|
20807
21031
|
app: app.name,
|
|
20808
21032
|
cwd: app.hostRun.cwd,
|
|
20809
|
-
absolutePath:
|
|
20810
|
-
})).filter((entry) => !
|
|
21033
|
+
absolutePath: import_node_path55.default.resolve(repo.path, app.hostRun.cwd)
|
|
21034
|
+
})).filter((entry) => !import_node_fs44.default.existsSync(entry.absolutePath));
|
|
20811
21035
|
addCheck(checks, {
|
|
20812
21036
|
id: "repo.host-cwd",
|
|
20813
21037
|
level: missingHostCwds.length === 0 ? "ok" : "error",
|
|
@@ -20951,12 +21175,12 @@ async function buildDoctorReport(options = {}) {
|
|
|
20951
21175
|
nextSteps
|
|
20952
21176
|
};
|
|
20953
21177
|
}
|
|
20954
|
-
var
|
|
21178
|
+
var import_node_fs44, import_node_path55, import_yaml6, POSTGRES_DEFAULTS;
|
|
20955
21179
|
var init_doctor = __esm({
|
|
20956
21180
|
"src/core/doctor.ts"() {
|
|
20957
21181
|
"use strict";
|
|
20958
|
-
|
|
20959
|
-
|
|
21182
|
+
import_node_fs44 = __toESM(require("fs"));
|
|
21183
|
+
import_node_path55 = __toESM(require("path"));
|
|
20960
21184
|
import_yaml6 = __toESM(require("yaml"));
|
|
20961
21185
|
init_docker();
|
|
20962
21186
|
init_host_routes();
|
|
@@ -21454,6 +21678,11 @@ async function runStopCommand(options) {
|
|
|
21454
21678
|
return;
|
|
21455
21679
|
}
|
|
21456
21680
|
const label2 = result.kind === "primary" ? "Primary checkout" : `Workspace '${result.workspace}'`;
|
|
21681
|
+
if (result.runtimeAbsent) {
|
|
21682
|
+
process.stdout.write(`${label2} has no runtime or routes; stop is complete.
|
|
21683
|
+
`);
|
|
21684
|
+
return;
|
|
21685
|
+
}
|
|
21457
21686
|
if (!result.stopped && !result.deleted && result.freedRoutes === 0) {
|
|
21458
21687
|
process.stdout.write(`${label2} is already stopped; no routes needed removal.
|
|
21459
21688
|
`);
|
|
@@ -21774,17 +22003,17 @@ function asRecord3(value) {
|
|
|
21774
22003
|
return value;
|
|
21775
22004
|
}
|
|
21776
22005
|
function readJson(filePath) {
|
|
21777
|
-
if (!
|
|
22006
|
+
if (!import_node_fs45.default.existsSync(filePath)) {
|
|
21778
22007
|
return void 0;
|
|
21779
22008
|
}
|
|
21780
22009
|
try {
|
|
21781
|
-
return JSON.parse(
|
|
22010
|
+
return JSON.parse(import_node_fs45.default.readFileSync(filePath, "utf-8"));
|
|
21782
22011
|
} catch {
|
|
21783
22012
|
return void 0;
|
|
21784
22013
|
}
|
|
21785
22014
|
}
|
|
21786
22015
|
function relative(repoPath, filePath) {
|
|
21787
|
-
return
|
|
22016
|
+
return import_node_path56.default.relative(repoPath, filePath) || ".";
|
|
21788
22017
|
}
|
|
21789
22018
|
function redactEnvAssignments(value) {
|
|
21790
22019
|
return value.replace(
|
|
@@ -21825,7 +22054,7 @@ function inspectPackageManager(repoPath, pkg) {
|
|
|
21825
22054
|
["bun.lock", "bun"]
|
|
21826
22055
|
];
|
|
21827
22056
|
for (const [fileName, name] of lockfiles) {
|
|
21828
|
-
if (
|
|
22057
|
+
if (import_node_fs45.default.existsSync(import_node_path56.default.join(repoPath, fileName))) {
|
|
21829
22058
|
return { name, source: fileName };
|
|
21830
22059
|
}
|
|
21831
22060
|
}
|
|
@@ -21840,9 +22069,9 @@ function inspectNode(repoPath, pkg) {
|
|
|
21840
22069
|
if (typeof engines?.node === "string") {
|
|
21841
22070
|
return { version: engines.node, source: "package.json:engines.node" };
|
|
21842
22071
|
}
|
|
21843
|
-
const nvmrc =
|
|
21844
|
-
if (
|
|
21845
|
-
const version =
|
|
22072
|
+
const nvmrc = import_node_path56.default.join(repoPath, ".nvmrc");
|
|
22073
|
+
if (import_node_fs45.default.existsSync(nvmrc)) {
|
|
22074
|
+
const version = import_node_fs45.default.readFileSync(nvmrc, "utf-8").trim();
|
|
21846
22075
|
return { version, source: ".nvmrc" };
|
|
21847
22076
|
}
|
|
21848
22077
|
return void 0;
|
|
@@ -21903,7 +22132,7 @@ function configuredComposeFiles(repoPath) {
|
|
|
21903
22132
|
const files = config.apps.filter(
|
|
21904
22133
|
(app) => app.runtime === "docker"
|
|
21905
22134
|
).flatMap((app) => app.docker.composeFiles).filter(
|
|
21906
|
-
(fileName) => !
|
|
22135
|
+
(fileName) => !import_node_path56.default.isAbsolute(fileName) && !import_node_path56.default.normalize(fileName).startsWith("..")
|
|
21907
22136
|
);
|
|
21908
22137
|
return Array.from(new Set(files));
|
|
21909
22138
|
} catch {
|
|
@@ -21921,7 +22150,7 @@ function composeFiles(repoPath) {
|
|
|
21921
22150
|
...configuredComposeFiles(repoPath)
|
|
21922
22151
|
];
|
|
21923
22152
|
return Array.from(new Set(candidates)).filter(
|
|
21924
|
-
(fileName) =>
|
|
22153
|
+
(fileName) => import_node_fs45.default.existsSync(import_node_path56.default.join(repoPath, fileName))
|
|
21925
22154
|
);
|
|
21926
22155
|
}
|
|
21927
22156
|
function stringArray2(value) {
|
|
@@ -21959,7 +22188,7 @@ function inspectServices(repoPath) {
|
|
|
21959
22188
|
const services = [];
|
|
21960
22189
|
for (const fileName of composeFiles(repoPath)) {
|
|
21961
22190
|
try {
|
|
21962
|
-
const parsed = import_yaml7.default.parse(
|
|
22191
|
+
const parsed = import_yaml7.default.parse(import_node_fs45.default.readFileSync(import_node_path56.default.join(repoPath, fileName), "utf-8"));
|
|
21963
22192
|
const serviceMap = asRecord3(asRecord3(parsed)?.services);
|
|
21964
22193
|
for (const [name, value] of Object.entries(serviceMap ?? {})) {
|
|
21965
22194
|
const service = asRecord3(value);
|
|
@@ -21994,8 +22223,8 @@ function inspectServices(repoPath) {
|
|
|
21994
22223
|
return services;
|
|
21995
22224
|
}
|
|
21996
22225
|
function inspectEnvFiles(repoPath) {
|
|
21997
|
-
const files =
|
|
21998
|
-
const content =
|
|
22226
|
+
const files = import_node_fs45.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
|
|
22227
|
+
const content = import_node_fs45.default.readFileSync(import_node_path56.default.join(repoPath, fileName), "utf-8");
|
|
21999
22228
|
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
22229
|
return { path: fileName, names };
|
|
22001
22230
|
});
|
|
@@ -22009,18 +22238,18 @@ function inspectEnvFiles(repoPath) {
|
|
|
22009
22238
|
};
|
|
22010
22239
|
}
|
|
22011
22240
|
function inspectDevcontainer(repoPath) {
|
|
22012
|
-
const dir =
|
|
22013
|
-
if (!
|
|
22241
|
+
const dir = import_node_path56.default.join(repoPath, ".devcontainer");
|
|
22242
|
+
if (!import_node_fs45.default.existsSync(dir)) {
|
|
22014
22243
|
return { exists: false, files: [] };
|
|
22015
22244
|
}
|
|
22016
22245
|
return {
|
|
22017
22246
|
exists: true,
|
|
22018
|
-
files:
|
|
22247
|
+
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
22248
|
};
|
|
22020
22249
|
}
|
|
22021
22250
|
function inspectDevrouter(repoPath) {
|
|
22022
22251
|
const configPath = getRepoConfigPath(repoPath);
|
|
22023
|
-
if (!
|
|
22252
|
+
if (!import_node_fs45.default.existsSync(configPath)) {
|
|
22024
22253
|
return {
|
|
22025
22254
|
exists: false,
|
|
22026
22255
|
configPath,
|
|
@@ -22064,15 +22293,15 @@ function inspectAgentGuidance(repoPath) {
|
|
|
22064
22293
|
["AGENTS.md", "agents"],
|
|
22065
22294
|
["CLAUDE.md", "claude"]
|
|
22066
22295
|
]) {
|
|
22067
|
-
if (
|
|
22296
|
+
if (import_node_fs45.default.existsSync(import_node_path56.default.join(repoPath, fileName))) {
|
|
22068
22297
|
results.push({ path: fileName, kind });
|
|
22069
22298
|
}
|
|
22070
22299
|
}
|
|
22071
|
-
const skillsDir =
|
|
22072
|
-
if (
|
|
22073
|
-
for (const name of
|
|
22074
|
-
const skillPath =
|
|
22075
|
-
if (
|
|
22300
|
+
const skillsDir = import_node_path56.default.join(repoPath, ".agents", "skills");
|
|
22301
|
+
if (import_node_fs45.default.existsSync(skillsDir)) {
|
|
22302
|
+
for (const name of import_node_fs45.default.readdirSync(skillsDir).sort()) {
|
|
22303
|
+
const skillPath = import_node_path56.default.join(skillsDir, name, "SKILL.md");
|
|
22304
|
+
if (import_node_fs45.default.existsSync(skillPath)) {
|
|
22076
22305
|
results.push({ path: relative(repoPath, skillPath), kind: "skill" });
|
|
22077
22306
|
}
|
|
22078
22307
|
}
|
|
@@ -22115,7 +22344,7 @@ function buildIssues(report) {
|
|
|
22115
22344
|
}
|
|
22116
22345
|
function inspectRepo(options = {}) {
|
|
22117
22346
|
const repoPath = resolveRepoPath(options.repo);
|
|
22118
|
-
const pkg = readJson(
|
|
22347
|
+
const pkg = readJson(import_node_path56.default.join(repoPath, "package.json"));
|
|
22119
22348
|
const scripts = inspectScripts(pkg);
|
|
22120
22349
|
const reportWithoutIssues = {
|
|
22121
22350
|
repoPath,
|
|
@@ -22134,12 +22363,12 @@ function inspectRepo(options = {}) {
|
|
|
22134
22363
|
issues: buildIssues(reportWithoutIssues)
|
|
22135
22364
|
};
|
|
22136
22365
|
}
|
|
22137
|
-
var
|
|
22366
|
+
var import_node_fs45, import_node_path56, import_yaml7;
|
|
22138
22367
|
var init_repo_inspect = __esm({
|
|
22139
22368
|
"src/core/repo-inspect.ts"() {
|
|
22140
22369
|
"use strict";
|
|
22141
|
-
|
|
22142
|
-
|
|
22370
|
+
import_node_fs45 = __toESM(require("fs"));
|
|
22371
|
+
import_node_path56 = __toESM(require("path"));
|
|
22143
22372
|
import_yaml7 = __toESM(require("yaml"));
|
|
22144
22373
|
init_repo_config();
|
|
22145
22374
|
}
|
|
@@ -22244,7 +22473,7 @@ function requiredFileChecks(repoPath) {
|
|
|
22244
22473
|
".devcontainer/docker-compose.yml",
|
|
22245
22474
|
".devrouter.yml"
|
|
22246
22475
|
];
|
|
22247
|
-
const missing = required.filter((fileName) => !
|
|
22476
|
+
const missing = required.filter((fileName) => !import_node_fs46.default.existsSync(import_node_path57.default.join(repoPath, fileName)));
|
|
22248
22477
|
return {
|
|
22249
22478
|
id: "repo.devcontainer.verify-files",
|
|
22250
22479
|
level: missing.length === 0 ? "ok" : "error",
|
|
@@ -22469,12 +22698,12 @@ async function verifyDevcontainer(options = {}) {
|
|
|
22469
22698
|
nextSteps: collectNextSteps3(checks)
|
|
22470
22699
|
};
|
|
22471
22700
|
}
|
|
22472
|
-
var
|
|
22701
|
+
var import_node_fs46, import_node_path57;
|
|
22473
22702
|
var init_devcontainer_verify = __esm({
|
|
22474
22703
|
"src/core/devcontainer-verify.ts"() {
|
|
22475
22704
|
"use strict";
|
|
22476
|
-
|
|
22477
|
-
|
|
22705
|
+
import_node_fs46 = __toESM(require("fs"));
|
|
22706
|
+
import_node_path57 = __toESM(require("path"));
|
|
22478
22707
|
init_capabilities();
|
|
22479
22708
|
init_doctor();
|
|
22480
22709
|
init_host_routes();
|
|
@@ -22771,7 +23000,7 @@ function packageManagerIssues(repo) {
|
|
|
22771
23000
|
}
|
|
22772
23001
|
function plannedFiles(repoPath, version) {
|
|
22773
23002
|
const repo = inspectRepo({ repo: repoPath });
|
|
22774
|
-
const projectName = sanitizeProjectName(
|
|
23003
|
+
const projectName = sanitizeProjectName(import_node_path58.default.basename(repo.repoPath));
|
|
22775
23004
|
const nodeMajor = majorVersion(repo.node?.version, "24");
|
|
22776
23005
|
const pnpmVersion = repo.packageManager?.name === "pnpm" && repo.packageManager.version ? repo.packageManager.version : DEFAULT_PNPM_VERSION;
|
|
22777
23006
|
const port = inferPort2(repo);
|
|
@@ -22816,8 +23045,8 @@ function plannedFiles(repoPath, version) {
|
|
|
22816
23045
|
};
|
|
22817
23046
|
}
|
|
22818
23047
|
function classifyFile(repoPath, file) {
|
|
22819
|
-
const absolutePath =
|
|
22820
|
-
if (!
|
|
23048
|
+
const absolutePath = import_node_path58.default.join(repoPath, file.relativePath);
|
|
23049
|
+
if (!import_node_fs47.default.existsSync(absolutePath)) {
|
|
22821
23050
|
return {
|
|
22822
23051
|
path: file.relativePath,
|
|
22823
23052
|
action: "create",
|
|
@@ -22825,7 +23054,7 @@ function classifyFile(repoPath, file) {
|
|
|
22825
23054
|
bytes: Buffer.byteLength(file.content)
|
|
22826
23055
|
};
|
|
22827
23056
|
}
|
|
22828
|
-
const current =
|
|
23057
|
+
const current = import_node_fs47.default.readFileSync(absolutePath, "utf-8");
|
|
22829
23058
|
if (!current.includes(MANAGED_MARKER2)) {
|
|
22830
23059
|
return {
|
|
22831
23060
|
path: file.relativePath,
|
|
@@ -22882,11 +23111,11 @@ function buildPlan(repoPath, dryRun, version) {
|
|
|
22882
23111
|
};
|
|
22883
23112
|
}
|
|
22884
23113
|
function writeFile(repoPath, file) {
|
|
22885
|
-
const absolutePath =
|
|
22886
|
-
|
|
22887
|
-
|
|
23114
|
+
const absolutePath = import_node_path58.default.join(repoPath, file.relativePath);
|
|
23115
|
+
import_node_fs47.default.mkdirSync(import_node_path58.default.dirname(absolutePath), { recursive: true });
|
|
23116
|
+
import_node_fs47.default.writeFileSync(absolutePath, file.content, "utf-8");
|
|
22888
23117
|
if (file.executable) {
|
|
22889
|
-
|
|
23118
|
+
import_node_fs47.default.chmodSync(absolutePath, 493);
|
|
22890
23119
|
}
|
|
22891
23120
|
}
|
|
22892
23121
|
function writeDevcontainer(options = {}) {
|
|
@@ -22924,12 +23153,12 @@ function writeDevcontainer(options = {}) {
|
|
|
22924
23153
|
nextSteps: postWriteNextSteps(repoPath)
|
|
22925
23154
|
};
|
|
22926
23155
|
}
|
|
22927
|
-
var
|
|
23156
|
+
var import_node_fs47, import_node_path58, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
|
|
22928
23157
|
var init_devcontainer_write = __esm({
|
|
22929
23158
|
"src/core/devcontainer-write.ts"() {
|
|
22930
23159
|
"use strict";
|
|
22931
|
-
|
|
22932
|
-
|
|
23160
|
+
import_node_fs47 = __toESM(require("fs"));
|
|
23161
|
+
import_node_path58 = __toESM(require("path"));
|
|
22933
23162
|
init_repo_config();
|
|
22934
23163
|
init_repo_inspect();
|
|
22935
23164
|
MANAGED_MARKER2 = "devrouter:managed devcontainer";
|
|
@@ -23263,13 +23492,13 @@ function buildProfilePlanReport(options) {
|
|
|
23263
23492
|
return { ...report, contractPath, bindings };
|
|
23264
23493
|
}
|
|
23265
23494
|
function loadContract(repoPath, contractPath) {
|
|
23266
|
-
if (
|
|
23495
|
+
if (import_node_path59.default.isAbsolute(contractPath)) {
|
|
23267
23496
|
fail3("profile plan contract path must be relative to the repository.");
|
|
23268
23497
|
}
|
|
23269
23498
|
const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
|
|
23270
23499
|
let metadata;
|
|
23271
23500
|
try {
|
|
23272
|
-
metadata =
|
|
23501
|
+
metadata = import_node_fs48.default.lstatSync(absolutePath);
|
|
23273
23502
|
} catch (error) {
|
|
23274
23503
|
fail3(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
|
|
23275
23504
|
}
|
|
@@ -23279,16 +23508,16 @@ function loadContract(repoPath, contractPath) {
|
|
|
23279
23508
|
if (metadata.size > MAX_CONTRACT_BYTES) {
|
|
23280
23509
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
23281
23510
|
}
|
|
23282
|
-
const realRepo =
|
|
23283
|
-
const realContract =
|
|
23511
|
+
const realRepo = import_node_fs48.default.realpathSync(repoPath);
|
|
23512
|
+
const realContract = import_node_fs48.default.realpathSync(absolutePath);
|
|
23284
23513
|
assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
|
|
23285
|
-
const source =
|
|
23514
|
+
const source = import_node_fs48.default.readFileSync(absolutePath, "utf-8");
|
|
23286
23515
|
if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
|
|
23287
23516
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
23288
23517
|
}
|
|
23289
23518
|
return {
|
|
23290
23519
|
contract: parseProfilePlanContract(source),
|
|
23291
|
-
contractPath:
|
|
23520
|
+
contractPath: import_node_path59.default.relative(repoPath, absolutePath).split(import_node_path59.default.sep).join("/")
|
|
23292
23521
|
};
|
|
23293
23522
|
}
|
|
23294
23523
|
function resolveProfilePlan(options) {
|
|
@@ -23302,12 +23531,12 @@ function resolveProfilePlan(options) {
|
|
|
23302
23531
|
contractPath: loaded.contractPath
|
|
23303
23532
|
});
|
|
23304
23533
|
}
|
|
23305
|
-
var
|
|
23534
|
+
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
23535
|
var init_profile_plan = __esm({
|
|
23307
23536
|
"src/core/profile-plan.ts"() {
|
|
23308
23537
|
"use strict";
|
|
23309
|
-
|
|
23310
|
-
|
|
23538
|
+
import_node_fs48 = __toESM(require("fs"));
|
|
23539
|
+
import_node_path59 = __toESM(require("path"));
|
|
23311
23540
|
import_yaml8 = require("yaml");
|
|
23312
23541
|
init_paths();
|
|
23313
23542
|
init_profile_resolution();
|
|
@@ -23339,7 +23568,7 @@ async function runProfileResolveCommand(options) {
|
|
|
23339
23568
|
async function runProfilePlanCommand(options) {
|
|
23340
23569
|
const report = resolveProfilePlan(options);
|
|
23341
23570
|
if (options.output) {
|
|
23342
|
-
writeFileAtomically(
|
|
23571
|
+
writeFileAtomically(import_node_path60.default.resolve(options.output), `${JSON.stringify(report, null, 2)}
|
|
23343
23572
|
`);
|
|
23344
23573
|
}
|
|
23345
23574
|
if (options.json) {
|
|
@@ -23375,14 +23604,14 @@ function printProfilePlanSummary(report, output2) {
|
|
|
23375
23604
|
process.stdout.write(`Binding ${key}: ${renderValues(report.bindings[key] ?? [])}
|
|
23376
23605
|
`);
|
|
23377
23606
|
}
|
|
23378
|
-
if (output2) process.stdout.write(`Output: ${
|
|
23607
|
+
if (output2) process.stdout.write(`Output: ${import_node_path60.default.resolve(output2)}
|
|
23379
23608
|
`);
|
|
23380
23609
|
}
|
|
23381
|
-
var
|
|
23610
|
+
var import_node_path60;
|
|
23382
23611
|
var init_profile = __esm({
|
|
23383
23612
|
"src/commands/profile.ts"() {
|
|
23384
23613
|
"use strict";
|
|
23385
|
-
|
|
23614
|
+
import_node_path60 = __toESM(require("path"));
|
|
23386
23615
|
init_atomic_file();
|
|
23387
23616
|
init_output();
|
|
23388
23617
|
init_profile_plan();
|
|
@@ -23672,7 +23901,7 @@ function sanitizeRouterId(value) {
|
|
|
23672
23901
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
23673
23902
|
}
|
|
23674
23903
|
function repoHash(repoPath) {
|
|
23675
|
-
return (0, import_node_crypto26.createHash)("sha1").update(
|
|
23904
|
+
return (0, import_node_crypto26.createHash)("sha1").update(import_node_path61.default.resolve(repoPath)).digest("hex").slice(0, 12);
|
|
23676
23905
|
}
|
|
23677
23906
|
function asDockerApp(app) {
|
|
23678
23907
|
return app.runtime === "docker";
|
|
@@ -23751,11 +23980,11 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
|
|
|
23751
23980
|
if (dockerApps.length === 0) {
|
|
23752
23981
|
throw new Error("No docker apps selected to prepare compose overlay.");
|
|
23753
23982
|
}
|
|
23754
|
-
const cachePath =
|
|
23755
|
-
|
|
23756
|
-
const overlayPath =
|
|
23983
|
+
const cachePath = import_node_path61.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
|
|
23984
|
+
import_node_fs49.default.mkdirSync(cachePath, { recursive: true });
|
|
23985
|
+
const overlayPath = import_node_path61.default.join(cachePath, "compose.devrouter.yml");
|
|
23757
23986
|
const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
|
|
23758
|
-
|
|
23987
|
+
import_node_fs49.default.writeFileSync(overlayPath, import_yaml9.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
|
|
23759
23988
|
return {
|
|
23760
23989
|
overlayPath,
|
|
23761
23990
|
composeFiles: ensureComposeFiles(dockerApps),
|
|
@@ -23875,14 +24104,14 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
23875
24104
|
const port = Number(match[1]);
|
|
23876
24105
|
return Number.isInteger(port) && port > 0 ? port : void 0;
|
|
23877
24106
|
}
|
|
23878
|
-
var import_node_child_process33, import_node_crypto26,
|
|
24107
|
+
var import_node_child_process33, import_node_crypto26, import_node_fs49, import_node_path61, import_yaml9;
|
|
23879
24108
|
var init_docker_run = __esm({
|
|
23880
24109
|
"src/core/docker-run.ts"() {
|
|
23881
24110
|
"use strict";
|
|
23882
24111
|
import_node_child_process33 = require("child_process");
|
|
23883
24112
|
import_node_crypto26 = require("crypto");
|
|
23884
|
-
|
|
23885
|
-
|
|
24113
|
+
import_node_fs49 = __toESM(require("fs"));
|
|
24114
|
+
import_node_path61 = __toESM(require("path"));
|
|
23886
24115
|
import_yaml9 = __toESM(require("yaml"));
|
|
23887
24116
|
init_docker_error_guidance();
|
|
23888
24117
|
init_paths();
|
|
@@ -24704,13 +24933,13 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
24704
24933
|
const startedAt = Date.now();
|
|
24705
24934
|
let rootStat;
|
|
24706
24935
|
try {
|
|
24707
|
-
rootStat =
|
|
24936
|
+
rootStat = import_node_fs50.default.lstatSync(worktreePath);
|
|
24708
24937
|
} catch (error) {
|
|
24709
24938
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
24710
24939
|
}
|
|
24711
24940
|
let rootEntries;
|
|
24712
24941
|
try {
|
|
24713
|
-
rootEntries =
|
|
24942
|
+
rootEntries = import_node_fs50.default.readdirSync(worktreePath, { withFileTypes: true });
|
|
24714
24943
|
} catch (error) {
|
|
24715
24944
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
24716
24945
|
}
|
|
@@ -24736,10 +24965,10 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
24736
24965
|
timedOut = true;
|
|
24737
24966
|
break;
|
|
24738
24967
|
}
|
|
24739
|
-
const entryPath =
|
|
24968
|
+
const entryPath = import_node_path62.default.join(dirPath, entry.name);
|
|
24740
24969
|
let entryStat;
|
|
24741
24970
|
try {
|
|
24742
|
-
entryStat =
|
|
24971
|
+
entryStat = import_node_fs50.default.lstatSync(entryPath);
|
|
24743
24972
|
} catch (error) {
|
|
24744
24973
|
if (error?.code === "ENOENT") continue;
|
|
24745
24974
|
unreadableReason = describeIncompleteWalk(error);
|
|
@@ -24749,7 +24978,7 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
24749
24978
|
if (!entryStat.isDirectory()) continue;
|
|
24750
24979
|
let childEntries;
|
|
24751
24980
|
try {
|
|
24752
|
-
childEntries =
|
|
24981
|
+
childEntries = import_node_fs50.default.readdirSync(entryPath, { withFileTypes: true });
|
|
24753
24982
|
} catch (error) {
|
|
24754
24983
|
unreadableReason = describeIncompleteWalk(error);
|
|
24755
24984
|
break;
|
|
@@ -24813,12 +25042,12 @@ function describeError(error, worktreePath) {
|
|
|
24813
25042
|
}
|
|
24814
25043
|
return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
|
|
24815
25044
|
}
|
|
24816
|
-
var
|
|
25045
|
+
var import_node_fs50, import_node_path62, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
|
|
24817
25046
|
var init_workspace_consumption = __esm({
|
|
24818
25047
|
"src/core/workspace-consumption.ts"() {
|
|
24819
25048
|
"use strict";
|
|
24820
|
-
|
|
24821
|
-
|
|
25049
|
+
import_node_fs50 = __toESM(require("fs"));
|
|
25050
|
+
import_node_path62 = __toESM(require("path"));
|
|
24822
25051
|
init_devpod_environment();
|
|
24823
25052
|
DEFAULT_DEADLINE_MS = 1e4;
|
|
24824
25053
|
BLOCK_SIZE_BYTES = 512;
|
|
@@ -24919,7 +25148,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
|
|
|
24919
25148
|
}
|
|
24920
25149
|
function readGitSnapshot(worktree, commandRunner) {
|
|
24921
25150
|
const comparablePath = comparableWorkspacePath(worktree.path);
|
|
24922
|
-
if (worktree.prunable || !
|
|
25151
|
+
if (worktree.prunable || !import_node_fs51.default.existsSync(comparablePath)) {
|
|
24923
25152
|
return { worktree, checkout: "missing", head: null, committerDate: null };
|
|
24924
25153
|
}
|
|
24925
25154
|
const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
|
|
@@ -25444,7 +25673,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
25444
25673
|
containers2 = measureContainersFn(worktreePaths);
|
|
25445
25674
|
} catch (error) {
|
|
25446
25675
|
const reason = `container measurement failed: ${describeCause(error)}`;
|
|
25447
|
-
containers2 = new Map(worktreePaths.map((
|
|
25676
|
+
containers2 = new Map(worktreePaths.map((path63) => [path63, unknownContainers(reason)]));
|
|
25448
25677
|
}
|
|
25449
25678
|
}
|
|
25450
25679
|
const rows2 = records.map((record5) => {
|
|
@@ -25494,12 +25723,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
25494
25723
|
networkCapacity: (dependencies.inspectNetworkCapacity ?? inspectNetworkCapacity)()
|
|
25495
25724
|
};
|
|
25496
25725
|
}
|
|
25497
|
-
var import_node_child_process35,
|
|
25726
|
+
var import_node_child_process35, import_node_fs51, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
|
|
25498
25727
|
var init_workspace_cleanup = __esm({
|
|
25499
25728
|
"src/core/workspace-cleanup.ts"() {
|
|
25500
25729
|
"use strict";
|
|
25501
25730
|
import_node_child_process35 = require("child_process");
|
|
25502
|
-
|
|
25731
|
+
import_node_fs51 = __toESM(require("fs"));
|
|
25503
25732
|
init_devpod_workspaces();
|
|
25504
25733
|
init_host_routes();
|
|
25505
25734
|
init_network_diagnostics();
|
|
@@ -25611,7 +25840,7 @@ function warnMissingWorkspaceOwnership(repoPath) {
|
|
|
25611
25840
|
);
|
|
25612
25841
|
}
|
|
25613
25842
|
function defaultWorktreePath(mainRepo, ws) {
|
|
25614
|
-
return
|
|
25843
|
+
return import_node_path63.default.join(mainRepo, "trees", ws);
|
|
25615
25844
|
}
|
|
25616
25845
|
function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
25617
25846
|
const ignored = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
|
|
@@ -25619,12 +25848,12 @@ function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
|
25619
25848
|
});
|
|
25620
25849
|
if (ignored.status !== 0) {
|
|
25621
25850
|
throw new Error(
|
|
25622
|
-
`Default worktree root '${
|
|
25851
|
+
`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
25852
|
);
|
|
25624
25853
|
}
|
|
25625
25854
|
}
|
|
25626
25855
|
function legacyDefaultWorktreePath(mainRepo, ws) {
|
|
25627
|
-
return
|
|
25856
|
+
return import_node_path63.default.join(import_node_path63.default.dirname(mainRepo), `${import_node_path63.default.basename(mainRepo)}-${ws}`);
|
|
25628
25857
|
}
|
|
25629
25858
|
function teardownFallbackPath(mainRepo, workspace) {
|
|
25630
25859
|
const candidates = [
|
|
@@ -25736,7 +25965,7 @@ function assertFullDownPreflight(mainRepo, target) {
|
|
|
25736
25965
|
if (sameWorkspacePath(target.worktreePath, mainRepo)) {
|
|
25737
25966
|
throw new Error("Refusing to remove the primary Git checkout.");
|
|
25738
25967
|
}
|
|
25739
|
-
if (!target.worktree || target.worktree.prunable || !
|
|
25968
|
+
if (!target.worktree || target.worktree.prunable || !import_node_fs52.default.existsSync(target.worktreePath)) return;
|
|
25740
25969
|
if (target.worktree.locked) {
|
|
25741
25970
|
throw new Error(
|
|
25742
25971
|
`Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
|
|
@@ -25776,9 +26005,9 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
25776
26005
|
(candidate) => defaultWorktreePath(mainRepo, candidate)
|
|
25777
26006
|
);
|
|
25778
26007
|
const generatedPath = candidatePaths.find(
|
|
25779
|
-
(candidate) => !
|
|
26008
|
+
(candidate) => !import_node_fs52.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
|
|
25780
26009
|
);
|
|
25781
|
-
const selectedPath = opts.path ?
|
|
26010
|
+
const selectedPath = opts.path ? import_node_path63.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
|
|
25782
26011
|
if (!selectedPath) {
|
|
25783
26012
|
throw new Error(`Could not allocate a collision-safe worktree path for branch '${branch}'.`);
|
|
25784
26013
|
}
|
|
@@ -25787,7 +26016,7 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
25787
26016
|
`Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
|
|
25788
26017
|
);
|
|
25789
26018
|
}
|
|
25790
|
-
if (
|
|
26019
|
+
if (import_node_fs52.default.existsSync(selectedPath)) {
|
|
25791
26020
|
const registered = worktrees.find(
|
|
25792
26021
|
(worktree) => sameWorkspacePath(worktree.path, selectedPath)
|
|
25793
26022
|
);
|
|
@@ -25890,7 +26119,7 @@ async function mutateWorkspaceRuntime(action2, resolved, worktrees, quiet = fals
|
|
|
25890
26119
|
);
|
|
25891
26120
|
}
|
|
25892
26121
|
const retained = action2 === "stop" ? readManagedRuntimeState(resolved.worktreePath, resolved.workspace) : void 0;
|
|
25893
|
-
let absent =
|
|
26122
|
+
let absent = mutation.status === "proven-absent";
|
|
25894
26123
|
const remove = () => {
|
|
25895
26124
|
if (retained?.stopBaseline) {
|
|
25896
26125
|
absent = proveManagedStop(retained).status === "proven-absent";
|
|
@@ -25899,6 +26128,11 @@ async function mutateWorkspaceRuntime(action2, resolved, worktrees, quiet = fals
|
|
|
25899
26128
|
))
|
|
25900
26129
|
throw new Error("Absent stop contains routes outside the retained desired set.");
|
|
25901
26130
|
}
|
|
26131
|
+
if (absent && !retained) {
|
|
26132
|
+
if (listRoutesForWorktreePath(resolved.worktreePath).length)
|
|
26133
|
+
throw new Error("Initial managed stop observed remaining workspace routes.");
|
|
26134
|
+
return [];
|
|
26135
|
+
}
|
|
25902
26136
|
claimLifecycleEffect();
|
|
25903
26137
|
return removeWorkspaceRoutesForWorktree(resolved.workspace, resolved.worktreePath);
|
|
25904
26138
|
};
|
|
@@ -25934,7 +26168,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25934
26168
|
workspace: resolved.workspace,
|
|
25935
26169
|
devpodId: stopped.devpodId,
|
|
25936
26170
|
freedRoutes: stopped.freedRoutes,
|
|
25937
|
-
providerChanged: stopped.stopped
|
|
26171
|
+
providerChanged: stopped.stopped && !stopped.runtimeAbsent,
|
|
26172
|
+
...stopped.runtimeAbsent ? { runtimeAbsent: true } : {}
|
|
25938
26173
|
};
|
|
25939
26174
|
}
|
|
25940
26175
|
const operation = async () => {
|
|
@@ -25944,7 +26179,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25944
26179
|
}
|
|
25945
26180
|
const result = await mutateWorkspaceRuntime("delete", resolved, worktrees, opts.quiet);
|
|
25946
26181
|
if (removeWorktree) {
|
|
25947
|
-
if (resolved.worktree && !resolved.worktree.prunable &&
|
|
26182
|
+
if (resolved.worktree && !resolved.worktree.prunable && import_node_fs52.default.existsSync(resolved.worktreePath)) {
|
|
25948
26183
|
const rm = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
|
|
25949
26184
|
encoding: "utf-8"
|
|
25950
26185
|
});
|
|
@@ -25963,7 +26198,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25963
26198
|
}
|
|
25964
26199
|
return result;
|
|
25965
26200
|
};
|
|
25966
|
-
return resolved.worktree && !resolved.worktree.prunable &&
|
|
26201
|
+
return resolved.worktree && !resolved.worktree.prunable && import_node_fs52.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
|
|
25967
26202
|
}
|
|
25968
26203
|
async function workspaceStop(target, opts = {}) {
|
|
25969
26204
|
return runWorkspaceLifecycle("stop", target, opts);
|
|
@@ -25971,13 +26206,13 @@ async function workspaceStop(target, opts = {}) {
|
|
|
25971
26206
|
async function workspaceDown(target, opts = {}) {
|
|
25972
26207
|
return runWorkspaceLifecycle("down", target, opts);
|
|
25973
26208
|
}
|
|
25974
|
-
var import_node_child_process36,
|
|
26209
|
+
var import_node_child_process36, import_node_fs52, import_node_path63, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
|
|
25975
26210
|
var init_workspace_lifecycle = __esm({
|
|
25976
26211
|
"src/core/workspace-lifecycle.ts"() {
|
|
25977
26212
|
"use strict";
|
|
25978
26213
|
import_node_child_process36 = require("child_process");
|
|
25979
|
-
|
|
25980
|
-
|
|
26214
|
+
import_node_fs52 = __toESM(require("fs"));
|
|
26215
|
+
import_node_path63 = __toESM(require("path"));
|
|
25981
26216
|
init_devpod_mutation();
|
|
25982
26217
|
init_devpod_workspaces();
|
|
25983
26218
|
init_devsy_mutation();
|
|
@@ -26161,7 +26396,7 @@ var init_version = __esm({
|
|
|
26161
26396
|
|
|
26162
26397
|
// src/cli.ts
|
|
26163
26398
|
var import_commander = require("commander");
|
|
26164
|
-
var CLI_VERSION = true ? "0.0.
|
|
26399
|
+
var CLI_VERSION = true ? "0.0.74" : "0.0.0-dev";
|
|
26165
26400
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
26166
26401
|
function withErrorHandling(action2) {
|
|
26167
26402
|
return async (...args) => {
|
|
@@ -26534,10 +26769,10 @@ workspaceCommand2.command("down").description("Delete a workspace's runtime and
|
|
|
26534
26769
|
workspaceCommand2.command("journal").description("Inspect and recover workspace lifecycle journals").command("settle").description(
|
|
26535
26770
|
"Settle an interrupted lifecycle operation as unobservable so ensure and stop can proceed"
|
|
26536
26771
|
).argument("[path]", "Checkout path (defaults to current directory)").option("--json", "Output JSON").action(
|
|
26537
|
-
withErrorHandling(async (
|
|
26772
|
+
withErrorHandling(async (path63, _options, command) => {
|
|
26538
26773
|
const options = command.opts();
|
|
26539
26774
|
const { runWorkspaceJournalSettleCommand: runWorkspaceJournalSettleCommand2 } = await Promise.resolve().then(() => (init_workspace2(), workspace_exports));
|
|
26540
|
-
await runWorkspaceJournalSettleCommand2({ ...
|
|
26775
|
+
await runWorkspaceJournalSettleCommand2({ ...path63 ? { path: path63 } : {}, ...options });
|
|
26541
26776
|
})
|
|
26542
26777
|
);
|
|
26543
26778
|
function parseVersionRequest(argv) {
|