@devrouter/cli 0.1.2 → 0.1.3
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 +213 -54
- package/dist/devrouter.js +367 -92
- package/package.json +5 -1
- package/upgrade-prompts/0.1.3.md +55 -0
|
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/core/devpod-exec.ts
|
|
26
|
-
var
|
|
26
|
+
var import_node_child_process30 = require("child_process");
|
|
27
27
|
var import_node_crypto23 = require("crypto");
|
|
28
28
|
|
|
29
29
|
// src/core/devpod-environment.ts
|
|
@@ -1164,7 +1164,11 @@ function listDevpodWorkspacesRaw(options = {}) {
|
|
|
1164
1164
|
}
|
|
1165
1165
|
if (result.error || result.status !== 0) {
|
|
1166
1166
|
const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
1167
|
-
|
|
1167
|
+
const failure = new Error(
|
|
1168
|
+
`devpod list failed: ${details || "devpod is not installed or unavailable"}`
|
|
1169
|
+
);
|
|
1170
|
+
if (result.error) failure.code = result.error.code;
|
|
1171
|
+
throw failure;
|
|
1168
1172
|
}
|
|
1169
1173
|
let parsed;
|
|
1170
1174
|
try {
|
|
@@ -1303,9 +1307,11 @@ function listDevsyWorkspaces() {
|
|
|
1303
1307
|
);
|
|
1304
1308
|
if (result.status !== 0) {
|
|
1305
1309
|
const details = [result.error?.message, result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
1306
|
-
|
|
1310
|
+
const failure = new Error(
|
|
1307
1311
|
`devsy workspace list failed: ${details || "devsy is not installed or unavailable"}`
|
|
1308
1312
|
);
|
|
1313
|
+
if (result.error) failure.code = result.error.code;
|
|
1314
|
+
throw failure;
|
|
1309
1315
|
}
|
|
1310
1316
|
let parsed;
|
|
1311
1317
|
try {
|
|
@@ -1775,25 +1781,23 @@ function readWorkspaceRuntimeConfig() {
|
|
|
1775
1781
|
}
|
|
1776
1782
|
return config;
|
|
1777
1783
|
}
|
|
1784
|
+
function readRegistrySnapshot(runtime, read2, snapshots) {
|
|
1785
|
+
try {
|
|
1786
|
+
return read2();
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
if (error?.code === "ENOENT") return void 0;
|
|
1789
|
+
snapshots.unavailable.push(runtime);
|
|
1790
|
+
snapshots.errors[runtime] = error instanceof Error ? error.message : String(error);
|
|
1791
|
+
return void 0;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1778
1794
|
function getWorkspaceRegistrySnapshots() {
|
|
1779
1795
|
if (cachedSnapshots) return cachedSnapshots;
|
|
1780
1796
|
const snapshots = { unavailable: [], errors: {} };
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
snapshots.unavailable.push("devsy");
|
|
1786
|
-
snapshots.errors.devsy = error instanceof Error ? error.message : String(error);
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
if (isRuntimeInstalled("devpod")) {
|
|
1790
|
-
try {
|
|
1791
|
-
snapshots.devpod = listDevpodWorkspacesRaw();
|
|
1792
|
-
} catch (error) {
|
|
1793
|
-
snapshots.unavailable.push("devpod");
|
|
1794
|
-
snapshots.errors.devpod = error instanceof Error ? error.message : String(error);
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
+
const devsy = readRegistrySnapshot("devsy", listDevsyWorkspaces, snapshots);
|
|
1798
|
+
if (devsy !== void 0) snapshots.devsy = devsy;
|
|
1799
|
+
const devpod = readRegistrySnapshot("devpod", listDevpodWorkspacesRaw, snapshots);
|
|
1800
|
+
if (devpod !== void 0) snapshots.devpod = devpod;
|
|
1797
1801
|
cachedSnapshots = snapshots;
|
|
1798
1802
|
return snapshots;
|
|
1799
1803
|
}
|
|
@@ -1954,7 +1958,7 @@ function selectDevpodWorkspace(workspaces, repoPath) {
|
|
|
1954
1958
|
}
|
|
1955
1959
|
|
|
1956
1960
|
// src/core/devsy-exec.ts
|
|
1957
|
-
var
|
|
1961
|
+
var import_node_child_process29 = require("child_process");
|
|
1958
1962
|
|
|
1959
1963
|
// src/core/devsy-exec-proof.ts
|
|
1960
1964
|
var import_node_fs7 = __toESM(require("fs"));
|
|
@@ -4040,11 +4044,19 @@ function absentRegistrationIdentity(state) {
|
|
|
4040
4044
|
resetWorkspaceRuntimeCaches();
|
|
4041
4045
|
if (resolveWorkspaceRuntimeOrDefault(state.repoPath) !== "devsy")
|
|
4042
4046
|
throw new Error("Stop provider changed.");
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4047
|
+
const linked = isLinkedWorktree(state.repoPath);
|
|
4048
|
+
if (linked !== (state.workspace !== void 0))
|
|
4049
|
+
throw new Error("Absent stop requires the exact workspace identity.");
|
|
4050
|
+
let identity2;
|
|
4051
|
+
if (linked) {
|
|
4052
|
+
const workspace = state.workspace;
|
|
4053
|
+
const record5 = readWorkspaceOwnership(state.repoPath, workspace);
|
|
4054
|
+
if (!record5 || record5.workspace !== workspace || record5.devpodId !== state.devpodId || !sameWorkspacePath(record5.worktreePath, state.repoPath) || resolveWorktreeWorkspace(state.repoPath) !== workspace || inspectWorkspaceOwnership(record5, listGitWorktrees(state.repoPath), void 0).ownerStatus !== "present")
|
|
4055
|
+
throw new Error("Absent stop workspace ownership changed.");
|
|
4056
|
+
identity2 = { kind: "linked", record: record5, gitCommonDir: resolveGitCommonDir(state.repoPath) };
|
|
4057
|
+
} else {
|
|
4058
|
+
identity2 = { kind: "primary", repoPath: state.repoPath, devpodId: state.devpodId };
|
|
4059
|
+
}
|
|
4048
4060
|
for (const entries of [
|
|
4049
4061
|
listDevsyWorkspaces(),
|
|
4050
4062
|
listDevpodWorkspacesRaw({ allowMissingExecutable: true })
|
|
@@ -4054,7 +4066,7 @@ function absentRegistrationIdentity(state) {
|
|
|
4054
4066
|
))
|
|
4055
4067
|
throw new Error("Absent stop requires both provider registrations to remain absent.");
|
|
4056
4068
|
}
|
|
4057
|
-
return
|
|
4069
|
+
return identity2;
|
|
4058
4070
|
}
|
|
4059
4071
|
function replacementObservation(state) {
|
|
4060
4072
|
resetWorkspaceRuntimeCaches();
|
|
@@ -4164,7 +4176,7 @@ function proveManagedStop(state) {
|
|
|
4164
4176
|
const identityKeys = ["context", "providerId", "uid", "sourcePath", "sourceContainer"];
|
|
4165
4177
|
const current = identityOf(owner.workspace);
|
|
4166
4178
|
const changed = identityKeys.filter((key) => baseline[key] !== current[key]);
|
|
4167
|
-
if (changed.length === 0 &&
|
|
4179
|
+
if (changed.length === 0 && inspectManagedStopContainers(baseline.project, baseline.endpoint).length === 0) {
|
|
4168
4180
|
proveAbsentPopulationStop(state, baseline, current);
|
|
4169
4181
|
return { status: "proven-absent", containers: [] };
|
|
4170
4182
|
}
|
|
@@ -5361,7 +5373,7 @@ function retain(session, previous) {
|
|
|
5361
5373
|
|
|
5362
5374
|
// src/core/reliability-operation-store.ts
|
|
5363
5375
|
function installedCliVersion() {
|
|
5364
|
-
return true ? "0.1.
|
|
5376
|
+
return true ? "0.1.3" : "0.0.0-dev";
|
|
5365
5377
|
}
|
|
5366
5378
|
function compareCliVersions(left, right) {
|
|
5367
5379
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -5501,6 +5513,7 @@ function validate2(record5, identity2) {
|
|
|
5501
5513
|
"revision",
|
|
5502
5514
|
"state",
|
|
5503
5515
|
"worker",
|
|
5516
|
+
"stopWorkStarted",
|
|
5504
5517
|
"effectSequence",
|
|
5505
5518
|
"outcome",
|
|
5506
5519
|
"result",
|
|
@@ -5682,6 +5695,9 @@ function validate2(record5, identity2) {
|
|
|
5682
5695
|
throw new Error("Reliability worker identity is invalid.");
|
|
5683
5696
|
}
|
|
5684
5697
|
}
|
|
5698
|
+
if (record5.stopWorkStarted !== void 0 && record5.stopWorkStarted !== null) {
|
|
5699
|
+
if (record5.stopWorkStarted !== true) throw new Error("Invalid stop work marker.");
|
|
5700
|
+
}
|
|
5685
5701
|
}
|
|
5686
5702
|
var ReliabilityJournalError = class extends Error {
|
|
5687
5703
|
constructor(cause, location, message) {
|
|
@@ -7294,7 +7310,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs18.default.rea
|
|
|
7294
7310
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
7295
7311
|
const requiredVersion = config.devrouter?.version;
|
|
7296
7312
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
7297
|
-
const cliVersion = true ? "0.1.
|
|
7313
|
+
const cliVersion = true ? "0.1.3" : "0.0.0-dev";
|
|
7298
7314
|
if (cliVersion !== "0.0.0-dev" && compareSemver2(requiredVersion, cliVersion) > 0) {
|
|
7299
7315
|
hasWarnedVersionMismatch = true;
|
|
7300
7316
|
process.stderr.write(
|
|
@@ -8372,6 +8388,41 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
8372
8388
|
throw new Error("Initial managed stop left a workload running.");
|
|
8373
8389
|
return true;
|
|
8374
8390
|
}
|
|
8391
|
+
function restoreRecordedManagedDevcontainerConfig(repoPath) {
|
|
8392
|
+
const plan = recordedManagedDevcontainerRestorePlan(repoPath);
|
|
8393
|
+
if (!plan) return false;
|
|
8394
|
+
writeManagedDevcontainerConfig(plan);
|
|
8395
|
+
if (inspectManagedDevcontainerGeneratedConfig(plan).status !== "valid") {
|
|
8396
|
+
throw new Error(
|
|
8397
|
+
`Managed Dev Container path '${plan.generatedPath}' could not be restored from the recorded state.`
|
|
8398
|
+
);
|
|
8399
|
+
}
|
|
8400
|
+
return true;
|
|
8401
|
+
}
|
|
8402
|
+
function recordedManagedDevcontainerRestorePlan(repoPath) {
|
|
8403
|
+
try {
|
|
8404
|
+
if (!import_node_fs21.default.existsSync(import_node_path25.default.join(repoPath, ".devrouter.yml"))) return void 0;
|
|
8405
|
+
const linked = isLinkedWorktree(repoPath);
|
|
8406
|
+
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
8407
|
+
const state = readManagedRuntimeState(repoPath, workspace);
|
|
8408
|
+
if (!state) return void 0;
|
|
8409
|
+
const runtime = loadRuntimeConfig(repoPath, workspace ?? "", state.profile);
|
|
8410
|
+
if (!runtime.config.managedRuntime || runtime.profile !== state.profile || runtime.workspace !== workspace)
|
|
8411
|
+
return void 0;
|
|
8412
|
+
const plan = inspectManagedDevcontainerConfig({
|
|
8413
|
+
repoPath,
|
|
8414
|
+
config: runtime.config,
|
|
8415
|
+
profile: runtime.resolvedProfile,
|
|
8416
|
+
linked
|
|
8417
|
+
});
|
|
8418
|
+
if (inspectManagedDevcontainerGeneratedConfig(plan).status !== "missing") return void 0;
|
|
8419
|
+
if (plan.sourceConfigSha256 !== state.sourceConfigSha256 || plan.effectiveConfigSha256 !== state.effectiveConfigSha256)
|
|
8420
|
+
return void 0;
|
|
8421
|
+
return plan;
|
|
8422
|
+
} catch {
|
|
8423
|
+
return void 0;
|
|
8424
|
+
}
|
|
8425
|
+
}
|
|
8375
8426
|
function stopRetainedManagedDevsyWorkspace(options) {
|
|
8376
8427
|
const { repoPath, devsyId } = options;
|
|
8377
8428
|
const linked = isLinkedWorktree(repoPath);
|
|
@@ -10127,6 +10178,7 @@ function inspectExactOwnership(devsyId, worktreePath) {
|
|
|
10127
10178
|
}
|
|
10128
10179
|
function mutateOwnedDevsyWorkspace(action, devsyId, worktreePath) {
|
|
10129
10180
|
return withMutationLock(`Devsy ${action}`, worktreePath, () => {
|
|
10181
|
+
restoreRecordedManagedDevcontainerConfig(worktreePath);
|
|
10130
10182
|
if (action === "stop") {
|
|
10131
10183
|
const stopped = stopRetainedManagedDevsyWorkspace({
|
|
10132
10184
|
repoPath: worktreePath,
|
|
@@ -10647,7 +10699,7 @@ function handleOperationRequest(state, event) {
|
|
|
10647
10699
|
if (state.phase === "stopping" || state.desired === "parked-for-capacity")
|
|
10648
10700
|
return blocked(
|
|
10649
10701
|
state,
|
|
10650
|
-
`phase is '${state.phase}' with desired '${state.desired}'; wait for the running stop to finish.`
|
|
10702
|
+
`phase is '${state.phase}' with desired '${state.desired}'; wait for the running stop to finish, or when no stop worker is running rerun 'devrouter stop <path> --delete', removing the exact provider registration first when the stop cannot prove the recorded population.`
|
|
10651
10703
|
);
|
|
10652
10704
|
if (event.kind === "exec" && (!event.runtimeRunning || state.desired !== "running" && state.intentRevision !== 0))
|
|
10653
10705
|
return blocked(
|
|
@@ -11174,7 +11226,7 @@ function workerGroupAbsent(pid) {
|
|
|
11174
11226
|
}
|
|
11175
11227
|
|
|
11176
11228
|
// src/core/workspace-ensure.ts
|
|
11177
|
-
var
|
|
11229
|
+
var import_node_child_process28 = require("child_process");
|
|
11178
11230
|
var import_node_fs34 = __toESM(require("fs"));
|
|
11179
11231
|
var import_node_path39 = __toESM(require("path"));
|
|
11180
11232
|
|
|
@@ -12082,6 +12134,96 @@ function runManagedProcessAction(options) {
|
|
|
12082
12134
|
return "drifted";
|
|
12083
12135
|
}
|
|
12084
12136
|
|
|
12137
|
+
// src/core/managed-mount-nesting.ts
|
|
12138
|
+
var import_node_child_process23 = require("child_process");
|
|
12139
|
+
var NESTED_MOUNT_REASON = "no configured mount is nested inside another configured mount";
|
|
12140
|
+
var UNREADABLE_MOUNT_REASON = "the container's own mount table could not be read";
|
|
12141
|
+
function normalizeDestination(value) {
|
|
12142
|
+
const trimmed = value.trim().replace(/\/+$/, "");
|
|
12143
|
+
return trimmed === "" ? "/" : trimmed;
|
|
12144
|
+
}
|
|
12145
|
+
function isNestedWithin(destination, candidate) {
|
|
12146
|
+
return candidate === "/" ? destination !== "/" : destination.startsWith(`${candidate}/`);
|
|
12147
|
+
}
|
|
12148
|
+
function unescapeMountField(value) {
|
|
12149
|
+
return value.replace(
|
|
12150
|
+
/\\([0-7]{3})/g,
|
|
12151
|
+
(_match, octal) => String.fromCharCode(Number.parseInt(octal, 8))
|
|
12152
|
+
);
|
|
12153
|
+
}
|
|
12154
|
+
function parseMountinfoDestinations(text4) {
|
|
12155
|
+
const destinations = /* @__PURE__ */ new Set();
|
|
12156
|
+
for (const line of text4.split("\n")) {
|
|
12157
|
+
const fields2 = line.split(" ");
|
|
12158
|
+
if (fields2.length < 5 || !/^\d+$/.test(fields2[0] ?? "")) continue;
|
|
12159
|
+
destinations.add(normalizeDestination(unescapeMountField(fields2[4] ?? "")));
|
|
12160
|
+
}
|
|
12161
|
+
return [...destinations];
|
|
12162
|
+
}
|
|
12163
|
+
function isNestedIndex(destinations, index) {
|
|
12164
|
+
return destinations.some(
|
|
12165
|
+
(candidate, other) => other !== index && isNestedWithin(destinations[index] ?? "", candidate)
|
|
12166
|
+
);
|
|
12167
|
+
}
|
|
12168
|
+
function configuredDestinations(configured) {
|
|
12169
|
+
return configured.map((mount) => normalizeDestination(mount.Destination));
|
|
12170
|
+
}
|
|
12171
|
+
function classifyManagedMountNesting(configured, effectiveDestinations, containerId) {
|
|
12172
|
+
const destinations = configuredDestinations(configured);
|
|
12173
|
+
const nested = configured.filter((_mount, index) => isNestedIndex(destinations, index));
|
|
12174
|
+
const identity2 = containerId ? { container: containerId } : {};
|
|
12175
|
+
if (nested.length === 0) {
|
|
12176
|
+
return { status: "not-applicable", checked: [], unwound: [], reason: NESTED_MOUNT_REASON };
|
|
12177
|
+
}
|
|
12178
|
+
if (effectiveDestinations === null) {
|
|
12179
|
+
return {
|
|
12180
|
+
status: "unverified",
|
|
12181
|
+
checked: [],
|
|
12182
|
+
unwound: [],
|
|
12183
|
+
...identity2,
|
|
12184
|
+
reason: UNREADABLE_MOUNT_REASON
|
|
12185
|
+
};
|
|
12186
|
+
}
|
|
12187
|
+
const effective = new Set(effectiveDestinations.map(normalizeDestination));
|
|
12188
|
+
const checked = [];
|
|
12189
|
+
const unwound = [];
|
|
12190
|
+
for (const mount of nested) {
|
|
12191
|
+
const destination = normalizeDestination(mount.Destination);
|
|
12192
|
+
if (effective.has(destination)) {
|
|
12193
|
+
checked.push(mount.Destination);
|
|
12194
|
+
continue;
|
|
12195
|
+
}
|
|
12196
|
+
const nestedWithin = destinations.filter((candidate) => isNestedWithin(destination, candidate)).sort((a, b) => a.length - b.length)[0];
|
|
12197
|
+
unwound.push({
|
|
12198
|
+
destination: mount.Destination,
|
|
12199
|
+
source: mount.Source,
|
|
12200
|
+
nestedWithin: nestedWithin ?? "/"
|
|
12201
|
+
});
|
|
12202
|
+
}
|
|
12203
|
+
return {
|
|
12204
|
+
status: unwound.length > 0 ? "unwound" : "effective",
|
|
12205
|
+
checked,
|
|
12206
|
+
unwound,
|
|
12207
|
+
...identity2
|
|
12208
|
+
};
|
|
12209
|
+
}
|
|
12210
|
+
function readContainerMountinfo(containerId, options) {
|
|
12211
|
+
const result = (0, import_node_child_process23.spawnSync)("docker", ["exec", containerId, "cat", "/proc/self/mountinfo"], {
|
|
12212
|
+
encoding: "utf-8",
|
|
12213
|
+
timeout: options.timeoutMs ?? 5e3,
|
|
12214
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12215
|
+
...networkDockerOptions()
|
|
12216
|
+
});
|
|
12217
|
+
if (result.status !== 0 || typeof result.stdout !== "string") return null;
|
|
12218
|
+
const destinations = parseMountinfoDestinations(result.stdout);
|
|
12219
|
+
return destinations.length > 0 ? destinations : null;
|
|
12220
|
+
}
|
|
12221
|
+
function observeManagedMountNesting(container, options = {}) {
|
|
12222
|
+
const destinations = configuredDestinations(container.mounts);
|
|
12223
|
+
const effective = destinations.some((_destination, index) => isNestedIndex(destinations, index)) ? readContainerMountinfo(container.id, options) : null;
|
|
12224
|
+
return classifyManagedMountNesting(container.mounts, effective, container.id);
|
|
12225
|
+
}
|
|
12226
|
+
|
|
12085
12227
|
// src/core/managed-runtime-status.ts
|
|
12086
12228
|
function recoveryPath(repoPath) {
|
|
12087
12229
|
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(repoPath)) return repoPath;
|
|
@@ -12362,6 +12504,7 @@ function inspectManagedRuntime(options) {
|
|
|
12362
12504
|
(mount) => mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath)
|
|
12363
12505
|
)?.Destination;
|
|
12364
12506
|
const primaryActive = Boolean(primary?.state.Running);
|
|
12507
|
+
const mountNesting = primary && primaryActive ? observeManagedMountNesting(primary) : void 0;
|
|
12365
12508
|
if (generatedConfigMissing && (state || primaryActive || managedServicesActive)) {
|
|
12366
12509
|
drift.push("managed generated Dev Container configuration is missing");
|
|
12367
12510
|
}
|
|
@@ -12428,7 +12571,8 @@ function inspectManagedRuntime(options) {
|
|
|
12428
12571
|
processStatuses,
|
|
12429
12572
|
activeServices: sortedUnique(activeServices),
|
|
12430
12573
|
activeProcesses: sortedUnique(activeProcesses),
|
|
12431
|
-
drift
|
|
12574
|
+
drift,
|
|
12575
|
+
...mountNesting ? { mountNesting } : {}
|
|
12432
12576
|
};
|
|
12433
12577
|
}
|
|
12434
12578
|
function collectManagedRuntimeStatus(options) {
|
|
@@ -12576,6 +12720,7 @@ function collectManagedRuntimeStatus(options) {
|
|
|
12576
12720
|
baseServiceStatuses: inspection.baseStatuses,
|
|
12577
12721
|
processStatuses: inspection.processStatuses,
|
|
12578
12722
|
drift: sortedUnique(drift),
|
|
12723
|
+
...inspection.mountNesting ? { mountNesting: inspection.mountNesting } : {},
|
|
12579
12724
|
...inspection.plan?.sourceConfigSha256 || state?.sourceConfigSha256 ? { sourceConfigSha256: inspection.plan?.sourceConfigSha256 ?? state?.sourceConfigSha256 } : {},
|
|
12580
12725
|
...inspection.plan?.effectiveConfigSha256 || state?.effectiveConfigSha256 ? {
|
|
12581
12726
|
effectiveConfigSha256: inspection.plan?.effectiveConfigSha256 ?? state?.effectiveConfigSha256
|
|
@@ -12585,19 +12730,19 @@ function collectManagedRuntimeStatus(options) {
|
|
|
12585
12730
|
}
|
|
12586
12731
|
|
|
12587
12732
|
// src/core/network-inventory.ts
|
|
12588
|
-
var
|
|
12733
|
+
var import_node_child_process24 = require("child_process");
|
|
12589
12734
|
var NetworkInventoryError = class extends Error {
|
|
12590
12735
|
};
|
|
12591
12736
|
var MAX_IDS = 4096;
|
|
12592
12737
|
var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
12593
12738
|
var FULL_ID = /^[a-f0-9]{64}$/;
|
|
12594
|
-
var NETWORK_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"driver":{{json .Driver}},"ipam":{{json .IPAM.Config}},"activeEndpoints":{{len .Containers}},"project":{{json (index .Labels "com.docker.compose.project")}},"network":{{json (index .Labels "com.docker.compose.network")}}';
|
|
12739
|
+
var NETWORK_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"driver":{{json .Driver}},"ipam":{{json .IPAM.Config}},"activeEndpoints":{{len .Containers}},"project":{{json (index .Labels "com.docker.compose.project")}},"network":{{json (index .Labels "com.docker.compose.network")}}}';
|
|
12595
12740
|
var CONTAINER_TEMPLATE = '{"id":{{json .Id}},"networks":{{json .NetworkSettings.Networks}}}';
|
|
12596
12741
|
function readDocker(endpoint, args, timeoutMs) {
|
|
12597
12742
|
const env = { ...process.env };
|
|
12598
12743
|
delete env.DOCKER_CONTEXT;
|
|
12599
12744
|
delete env.DOCKER_HOST;
|
|
12600
|
-
const result = (0,
|
|
12745
|
+
const result = (0, import_node_child_process24.spawnSync)("docker", ["--host", endpoint, ...args], {
|
|
12601
12746
|
env,
|
|
12602
12747
|
encoding: "utf-8",
|
|
12603
12748
|
timeout: timeoutMs,
|
|
@@ -12628,7 +12773,14 @@ function id4(value) {
|
|
|
12628
12773
|
}
|
|
12629
12774
|
function rows(output) {
|
|
12630
12775
|
const trimmed = output.trim();
|
|
12631
|
-
return trimmed ? trimmed.split(/\r?\n/).map((line) =>
|
|
12776
|
+
return trimmed ? trimmed.split(/\r?\n/).map((line) => parseRecord(line)) : [];
|
|
12777
|
+
}
|
|
12778
|
+
function parseRecord(line) {
|
|
12779
|
+
try {
|
|
12780
|
+
return JSON.parse(line);
|
|
12781
|
+
} catch {
|
|
12782
|
+
throw new NetworkInventoryError("Docker network inventory contains a malformed record.");
|
|
12783
|
+
}
|
|
12632
12784
|
}
|
|
12633
12785
|
function ids(output) {
|
|
12634
12786
|
const values = output.trim() ? output.trim().split(/\r?\n/).map(id4) : [];
|
|
@@ -12673,7 +12825,7 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
|
|
|
12673
12825
|
return output;
|
|
12674
12826
|
};
|
|
12675
12827
|
const info = record4(
|
|
12676
|
-
|
|
12828
|
+
parseRecord(
|
|
12677
12829
|
read2(["info", "--format", '{"id":{{json .ID}},"pools":{{json .DefaultAddressPools}}}'])
|
|
12678
12830
|
)
|
|
12679
12831
|
);
|
|
@@ -12754,7 +12906,7 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
|
|
|
12754
12906
|
}
|
|
12755
12907
|
|
|
12756
12908
|
// src/core/network-routes.ts
|
|
12757
|
-
var
|
|
12909
|
+
var import_node_child_process25 = require("child_process");
|
|
12758
12910
|
var import_node_net3 = require("net");
|
|
12759
12911
|
|
|
12760
12912
|
// src/core/network-local-daemon.ts
|
|
@@ -12823,7 +12975,7 @@ function parseLinuxNetworkRoutes(output) {
|
|
|
12823
12975
|
function collectNetworkRoutes(options) {
|
|
12824
12976
|
if ((options.platform ?? process.platform) !== "linux" || !hasLocalDockerNetworkNamespace(options.endpoint))
|
|
12825
12977
|
return { status: "unknown", routes: [] };
|
|
12826
|
-
const result = (0,
|
|
12978
|
+
const result = (0, import_node_child_process25.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
|
|
12827
12979
|
encoding: "utf8",
|
|
12828
12980
|
timeout: 5e3,
|
|
12829
12981
|
maxBuffer: 1024 * 1024
|
|
@@ -13672,7 +13824,7 @@ function fingerprintComposeNetworkModel(input) {
|
|
|
13672
13824
|
}
|
|
13673
13825
|
|
|
13674
13826
|
// src/core/network-compose-files.ts
|
|
13675
|
-
var
|
|
13827
|
+
var import_node_child_process26 = require("child_process");
|
|
13676
13828
|
var import_node_crypto20 = require("crypto");
|
|
13677
13829
|
var import_node_fs33 = __toESM(require("fs"));
|
|
13678
13830
|
var import_node_path38 = __toESM(require("path"));
|
|
@@ -13709,7 +13861,7 @@ function inspectNetworkComposeFiles(input) {
|
|
|
13709
13861
|
const env = managedComposeEnvironment(input.workspace);
|
|
13710
13862
|
delete env.DOCKER_CONTEXT;
|
|
13711
13863
|
delete env.DOCKER_HOST;
|
|
13712
|
-
const result = (0,
|
|
13864
|
+
const result = (0, import_node_child_process26.spawnSync)(
|
|
13713
13865
|
"docker",
|
|
13714
13866
|
[
|
|
13715
13867
|
"--host",
|
|
@@ -13744,7 +13896,7 @@ function prepareNetworkComposeFiles(plan, subnet) {
|
|
|
13744
13896
|
const overlay = deriveComposeNetworkOverlay(subnet);
|
|
13745
13897
|
if (overlay.status !== "ready" || !overlay.overlay) throw new Error("Network subnet is invalid.");
|
|
13746
13898
|
const overlayPath = import_node_path38.default.join(plan.composeDirectory, OVERLAY);
|
|
13747
|
-
const ignored = (0,
|
|
13899
|
+
const ignored = (0, import_node_child_process26.spawnSync)("git", [
|
|
13748
13900
|
"-C",
|
|
13749
13901
|
plan.composeDirectory,
|
|
13750
13902
|
"check-ignore",
|
|
@@ -13786,7 +13938,7 @@ ${JSON.stringify(extended.config, null, 2)}
|
|
|
13786
13938
|
}
|
|
13787
13939
|
|
|
13788
13940
|
// src/core/network-connected-route.ts
|
|
13789
|
-
var
|
|
13941
|
+
var import_node_child_process27 = require("child_process");
|
|
13790
13942
|
function bounds(cidr) {
|
|
13791
13943
|
const canonical = parseIPv4Cidr(cidr);
|
|
13792
13944
|
if (!canonical) throw new Error("Invalid connected route.");
|
|
@@ -13811,7 +13963,7 @@ function inspectClaimedBridge(claim2, networkId) {
|
|
|
13811
13963
|
const env = { ...process.env };
|
|
13812
13964
|
delete env.DOCKER_CONTEXT;
|
|
13813
13965
|
delete env.DOCKER_HOST;
|
|
13814
|
-
const result = (0,
|
|
13966
|
+
const result = (0, import_node_child_process27.spawnSync)(
|
|
13815
13967
|
"docker",
|
|
13816
13968
|
[
|
|
13817
13969
|
"--host",
|
|
@@ -13838,7 +13990,7 @@ function inspectClaimedBridge(claim2, networkId) {
|
|
|
13838
13990
|
const [first, last] = bounds(claim2.subnet);
|
|
13839
13991
|
const [address] = bounds(gateway);
|
|
13840
13992
|
if (address <= first || address >= last) throw new Error();
|
|
13841
|
-
const interfaces = (0,
|
|
13993
|
+
const interfaces = (0, import_node_child_process27.spawnSync)("ip", ["-j", "-d", "-4", "address", "show", "dev", bridge], {
|
|
13842
13994
|
encoding: "utf8",
|
|
13843
13995
|
timeout: 5e3,
|
|
13844
13996
|
maxBuffer: 65536
|
|
@@ -14594,7 +14746,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14594
14746
|
target
|
|
14595
14747
|
});
|
|
14596
14748
|
if (target.kind === "linked") {
|
|
14597
|
-
const workspaceEnv = (0,
|
|
14749
|
+
const workspaceEnv = (0, import_node_child_process28.spawnSync)(
|
|
14598
14750
|
"docker",
|
|
14599
14751
|
["exec", appContainer.id, "printenv", "WORKSPACE"],
|
|
14600
14752
|
{ encoding: "utf-8", ...networkDockerOptions() }
|
|
@@ -14604,7 +14756,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14604
14756
|
`Workspace app container must expose WORKSPACE='${target.workspace}' (got '${workspaceEnv.stdout.trim() || "(empty)"}').`
|
|
14605
14757
|
);
|
|
14606
14758
|
}
|
|
14607
|
-
const devrouterWorkspaceEnv = (0,
|
|
14759
|
+
const devrouterWorkspaceEnv = (0, import_node_child_process28.spawnSync)(
|
|
14608
14760
|
"docker",
|
|
14609
14761
|
["exec", appContainer.id, "printenv", "DEVROUTER_WORKSPACE"],
|
|
14610
14762
|
{ encoding: "utf-8", ...networkDockerOptions() }
|
|
@@ -14615,7 +14767,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
14615
14767
|
);
|
|
14616
14768
|
}
|
|
14617
14769
|
}
|
|
14618
|
-
const gitCheck = (0,
|
|
14770
|
+
const gitCheck = (0, import_node_child_process28.spawnSync)(
|
|
14619
14771
|
"docker",
|
|
14620
14772
|
[
|
|
14621
14773
|
"exec",
|
|
@@ -14895,7 +15047,7 @@ function isPrimaryCheckout(repoPath) {
|
|
|
14895
15047
|
}
|
|
14896
15048
|
function openUrls(urls) {
|
|
14897
15049
|
for (const url of urls) {
|
|
14898
|
-
const opened = (0,
|
|
15050
|
+
const opened = (0, import_node_child_process28.spawnSync)("open", [url], { encoding: "utf-8" });
|
|
14899
15051
|
if (opened.status !== 0) {
|
|
14900
15052
|
process.stderr.write(`Warning: could not open '${url}'.
|
|
14901
15053
|
`);
|
|
@@ -15178,7 +15330,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
15178
15330
|
transitionPhase = "service-start";
|
|
15179
15331
|
reportLifecycleProgress("service-start");
|
|
15180
15332
|
claimLifecycleEffect();
|
|
15181
|
-
const result = (0,
|
|
15333
|
+
const result = (0, import_node_child_process28.spawnSync)("docker", ["start", ...stopped.map((entry) => entry.id)], {
|
|
15182
15334
|
...networkDockerOptions(),
|
|
15183
15335
|
encoding: "utf-8",
|
|
15184
15336
|
timeout: DEFAULT_READINESS_TIMEOUT_MS,
|
|
@@ -16122,6 +16274,11 @@ async function executeLifecycleWorker(request, operation) {
|
|
|
16122
16274
|
];
|
|
16123
16275
|
for (const project of stopProjects) inspectManagedStopContainers(project);
|
|
16124
16276
|
}
|
|
16277
|
+
updateReliabilityOperation(request.identity, (record5) => {
|
|
16278
|
+
if (!matchesFence(record5, request.fence))
|
|
16279
|
+
throw new Error("Lifecycle intent changed before stop work began.");
|
|
16280
|
+
record5.stopWorkStarted = true;
|
|
16281
|
+
});
|
|
16125
16282
|
} else {
|
|
16126
16283
|
updateReliabilityOperation(request.identity, (record5) => {
|
|
16127
16284
|
stepRecord(record5, {
|
|
@@ -16233,6 +16390,7 @@ function proveLifecycleStopped() {
|
|
|
16233
16390
|
const settlement = updateReliabilityOperation(request.identity, (record5) => {
|
|
16234
16391
|
if (!matchesFence(record5, request.fence) || record5.worker)
|
|
16235
16392
|
throw new Error("Stop proof was superseded or an earlier worker remains.");
|
|
16393
|
+
delete record5.stopWorkStarted;
|
|
16236
16394
|
if (record5.version === 2) {
|
|
16237
16395
|
if (record5.capacity) record5.capacity.validUntilMs = 0;
|
|
16238
16396
|
return record5.state.environmentId;
|
|
@@ -16271,6 +16429,7 @@ function proveLifecycleStopped() {
|
|
|
16271
16429
|
if (!matchesFence(record5, request.fence) || record5.worker || record5.state.environmentId !== settlement || record5.capacity && record5.capacity.validUntilMs !== 0)
|
|
16272
16430
|
throw new Error("Capacity settlement was superseded before journal confirmation.");
|
|
16273
16431
|
record5.capacity = null;
|
|
16432
|
+
delete record5.stopWorkStarted;
|
|
16274
16433
|
if (record5.enrollment) {
|
|
16275
16434
|
record5.phaseSettlement = null;
|
|
16276
16435
|
record5.activeProfile = null;
|
|
@@ -16324,7 +16483,7 @@ async function devsyExecOutcome(repoPath, command, retainedProof) {
|
|
|
16324
16483
|
const launch = () => {
|
|
16325
16484
|
if (retainedProof) revalidateDevsyExecProof(repoPath, retainedProof);
|
|
16326
16485
|
claimLifecycleEffect();
|
|
16327
|
-
return (0,
|
|
16486
|
+
return (0, import_node_child_process29.spawn)("devsy", args, { stdio: "inherit" });
|
|
16328
16487
|
};
|
|
16329
16488
|
child = retainedProof ? withMutationLock("Execute retained workspace", repoPath, launch) : launch();
|
|
16330
16489
|
} catch (error) {
|
|
@@ -16372,7 +16531,7 @@ function ensureGuidance2(repoPath) {
|
|
|
16372
16531
|
return `Run 'devrouter ensure ${repoPath}' first.`;
|
|
16373
16532
|
}
|
|
16374
16533
|
function assertRunningDevpod(devpodId, repoPath) {
|
|
16375
|
-
const result = (0,
|
|
16534
|
+
const result = (0, import_node_child_process30.spawnSync)("devpod", ["status", devpodId, "--output", "json"], {
|
|
16376
16535
|
encoding: "utf-8"
|
|
16377
16536
|
});
|
|
16378
16537
|
if (result.status !== 0) {
|
|
@@ -16551,7 +16710,7 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
16551
16710
|
let child;
|
|
16552
16711
|
try {
|
|
16553
16712
|
claimLifecycleEffect();
|
|
16554
|
-
child = (0,
|
|
16713
|
+
child = (0, import_node_child_process30.spawn)("devpod", args, { stdio: ["inherit", "inherit", "pipe"] });
|
|
16555
16714
|
} catch (error) {
|
|
16556
16715
|
settled = true;
|
|
16557
16716
|
reject(
|
|
@@ -16608,7 +16767,7 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
16608
16767
|
}
|
|
16609
16768
|
|
|
16610
16769
|
// src/core/workspace-lifecycle.ts
|
|
16611
|
-
var
|
|
16770
|
+
var import_node_child_process31 = require("child_process");
|
|
16612
16771
|
var import_node_fs36 = __toESM(require("fs"));
|
|
16613
16772
|
var import_node_path41 = __toESM(require("path"));
|
|
16614
16773
|
|