@devrouter/cli 0.6.0 → 0.6.1
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 +310 -75
- package/dist/devrouter.js +1995 -726
- package/package.json +1 -1
- package/upgrade-prompts/0.6.1.md +51 -0
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
|
|
25
25
|
// src/core/devpod-exec.ts
|
|
26
26
|
var import_node_child_process33 = require("child_process");
|
|
27
|
-
var
|
|
27
|
+
var import_node_crypto25 = require("crypto");
|
|
28
28
|
|
|
29
29
|
// src/core/devpod-environment.ts
|
|
30
30
|
var import_node_child_process3 = require("child_process");
|
|
@@ -2695,6 +2695,72 @@ function assertReadableComposeFile(file) {
|
|
|
2695
2695
|
throw new Error("Managed container Compose configuration contains an unreadable file.");
|
|
2696
2696
|
}
|
|
2697
2697
|
}
|
|
2698
|
+
function resolveManagedServiceNetworkNamespace(options) {
|
|
2699
|
+
const { plan, containers, container, serviceConfig, env } = options;
|
|
2700
|
+
const service = container.labels["com.docker.compose.service"];
|
|
2701
|
+
const targetService = String(serviceConfig.network_mode).slice("service:".length);
|
|
2702
|
+
const target = containers.filter(
|
|
2703
|
+
(item) => item.labels["com.docker.compose.service"] === targetService
|
|
2704
|
+
);
|
|
2705
|
+
const primary = containers.filter(
|
|
2706
|
+
(item) => item.labels["com.docker.compose.service"] === plan.primaryService
|
|
2707
|
+
);
|
|
2708
|
+
const repoPath = import_node_path12.default.dirname(plan.composeDirectory);
|
|
2709
|
+
const owned = (item) => /^[a-f0-9]{64}$/.test(item.id) && containers.filter((candidate) => candidate.id === item.id).length === 1 && containers.filter(
|
|
2710
|
+
(candidate) => candidate.labels["com.docker.compose.service"] === item.labels["com.docker.compose.service"]
|
|
2711
|
+
).length === 1 && plan.desiredServices.includes(item.labels["com.docker.compose.service"] ?? "") && hasExactComposeIdentity(item, {
|
|
2712
|
+
repoPath,
|
|
2713
|
+
service: item.labels["com.docker.compose.service"] ?? "",
|
|
2714
|
+
composeProject: container.labels["com.docker.compose.project"],
|
|
2715
|
+
composeFiles: plan.composeFiles
|
|
2716
|
+
});
|
|
2717
|
+
if (targetService === service || target.length !== 1 || primary.length !== 1 || !owned(container) || !owned(target[0]) || !owned(primary[0]) || primary[0].mounts.filter(
|
|
2718
|
+
(mount) => mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath)
|
|
2719
|
+
).length !== 1) {
|
|
2720
|
+
throw new Error("Managed service network namespace ownership is unavailable.");
|
|
2721
|
+
}
|
|
2722
|
+
const namespace = `container:${target[0].id}`;
|
|
2723
|
+
const ids2 = [container.id, target[0].id];
|
|
2724
|
+
const inspect = () => {
|
|
2725
|
+
const result = (0, import_node_child_process10.spawnSync)(
|
|
2726
|
+
"docker",
|
|
2727
|
+
[
|
|
2728
|
+
"inspect",
|
|
2729
|
+
"--type",
|
|
2730
|
+
"container",
|
|
2731
|
+
"--format",
|
|
2732
|
+
'{"id":{{json .Id}},"networkMode":{{json .HostConfig.NetworkMode}}}',
|
|
2733
|
+
...ids2
|
|
2734
|
+
],
|
|
2735
|
+
{
|
|
2736
|
+
env,
|
|
2737
|
+
encoding: "utf-8",
|
|
2738
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2739
|
+
timeout: COMPOSE_HASH_TIMEOUT_MS,
|
|
2740
|
+
maxBuffer: 1024 * 1024
|
|
2741
|
+
}
|
|
2742
|
+
);
|
|
2743
|
+
if (result.status !== 0 || result.error || !result.stdout?.trim()) {
|
|
2744
|
+
throw new Error("Managed service network namespace inspection failed.");
|
|
2745
|
+
}
|
|
2746
|
+
const rows2 = result.stdout.trim().split(/\r?\n/).map((line) => JSON.parse(line));
|
|
2747
|
+
if (rows2.length !== ids2.length || ids2.some(
|
|
2748
|
+
(id5) => rows2.filter(
|
|
2749
|
+
(row) => isObject2(row) && row.id === id5 && typeof row.networkMode === "string"
|
|
2750
|
+
).length !== 1
|
|
2751
|
+
) || !rows2.some((row) => isObject2(row) && row.id === container.id && row.networkMode === namespace)) {
|
|
2752
|
+
throw new Error("Managed service network namespace identity changed.");
|
|
2753
|
+
}
|
|
2754
|
+
return ids2.map((id5) => rows2.find((row) => isObject2(row) && row.id === id5));
|
|
2755
|
+
};
|
|
2756
|
+
const before = JSON.stringify(inspect());
|
|
2757
|
+
serviceConfig.network_mode = namespace;
|
|
2758
|
+
return () => {
|
|
2759
|
+
if (JSON.stringify(inspect()) !== before) {
|
|
2760
|
+
throw new Error("Managed service network namespace changed during configuration inspection.");
|
|
2761
|
+
}
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2698
2764
|
function assertManagedContainerConfigUnchanged(options) {
|
|
2699
2765
|
if (options.containers.length === 0) {
|
|
2700
2766
|
throw new Error("Managed container configuration cannot be verified without containers.");
|
|
@@ -2730,6 +2796,10 @@ function assertManagedContainerConfigUnchanged(options) {
|
|
|
2730
2796
|
workingDirectory
|
|
2731
2797
|
];
|
|
2732
2798
|
const env = managedComposeEnvironment(options.workspace);
|
|
2799
|
+
if (options.endpoint) {
|
|
2800
|
+
env.DOCKER_HOST = options.endpoint;
|
|
2801
|
+
delete env.DOCKER_CONTEXT;
|
|
2802
|
+
}
|
|
2733
2803
|
const rendered = (0, import_node_child_process10.spawnSync)(
|
|
2734
2804
|
"docker",
|
|
2735
2805
|
[...composeArgs, ...fileArgs, "config", "--format", "json"],
|
|
@@ -2745,19 +2815,38 @@ function assertManagedContainerConfigUnchanged(options) {
|
|
|
2745
2815
|
if (rendered.status !== 0 || rendered.error || !rendered.stdout?.trim()) {
|
|
2746
2816
|
throw new Error("Compose configuration rendering failed.");
|
|
2747
2817
|
}
|
|
2748
|
-
|
|
2818
|
+
const hash = (input2) => (0, import_node_child_process10.spawnSync)(
|
|
2749
2819
|
"docker",
|
|
2750
2820
|
[...composeArgs, "-f", "-", "config", "--no-interpolate", "--hash", service],
|
|
2751
2821
|
{
|
|
2752
2822
|
cwd: workingDirectory,
|
|
2753
2823
|
encoding: "utf-8",
|
|
2754
2824
|
env,
|
|
2755
|
-
input:
|
|
2825
|
+
input: input2,
|
|
2756
2826
|
stdio: ["pipe", "pipe", "ignore"],
|
|
2757
2827
|
timeout: COMPOSE_HASH_TIMEOUT_MS,
|
|
2758
2828
|
maxBuffer: 1024 * 1024
|
|
2759
2829
|
}
|
|
2760
2830
|
);
|
|
2831
|
+
const input = rendered.stdout.replaceAll("$$", "$");
|
|
2832
|
+
const result2 = hash(input);
|
|
2833
|
+
const match2 = typeof result2.stdout === "string" ? /^(\S+)\s+([a-f0-9]{64})$/.exec(result2.stdout.trim()) : null;
|
|
2834
|
+
if (result2.status !== 0 || result2.error || !match2 || match2[1] !== service || match2[2] === expectedHash)
|
|
2835
|
+
return result2;
|
|
2836
|
+
const model = JSON.parse(input);
|
|
2837
|
+
const serviceConfig = isObject2(model) && isObject2(model.services) ? model.services[service] : void 0;
|
|
2838
|
+
if (!isObject2(serviceConfig) || typeof serviceConfig.network_mode !== "string" || !serviceConfig.network_mode.startsWith("service:"))
|
|
2839
|
+
return result2;
|
|
2840
|
+
const revalidate = resolveManagedServiceNetworkNamespace({
|
|
2841
|
+
plan: options.plan,
|
|
2842
|
+
containers: options.containers,
|
|
2843
|
+
container,
|
|
2844
|
+
serviceConfig,
|
|
2845
|
+
env
|
|
2846
|
+
});
|
|
2847
|
+
const resolved = hash(JSON.stringify(model));
|
|
2848
|
+
revalidate();
|
|
2849
|
+
return resolved;
|
|
2761
2850
|
} catch {
|
|
2762
2851
|
throw new Error(`Could not verify managed Compose configuration for service '${service}'.`);
|
|
2763
2852
|
}
|
|
@@ -4083,11 +4172,20 @@ function recoverManagedStopBaseline(state, readPlan, beforePersist) {
|
|
|
4083
4172
|
);
|
|
4084
4173
|
if (currentOwner.status !== "owned") throw new Error("Stop recovery owner is unavailable.");
|
|
4085
4174
|
proveLocalDockerSelection(currentOwner.workspace);
|
|
4086
|
-
assertManagedStopContainersAbsent(
|
|
4087
|
-
baseline.endpoint,
|
|
4088
|
-
baseline.containers.map((container) => container.id)
|
|
4089
|
-
);
|
|
4090
4175
|
const containers = inspectManagedStopContainers(baseline.project, baseline.endpoint);
|
|
4176
|
+
const missingIds = [];
|
|
4177
|
+
for (const recorded of baseline.containers) {
|
|
4178
|
+
const retained = containers.find((container) => container.id === recorded.id);
|
|
4179
|
+
if (!retained) {
|
|
4180
|
+
missingIds.push(recorded.id);
|
|
4181
|
+
} else if (!(0, import_node_util3.isDeepStrictEqual)(
|
|
4182
|
+
{ ...recorded, mounts: orderedMounts(recorded.mounts) },
|
|
4183
|
+
identity(retained)
|
|
4184
|
+
)) {
|
|
4185
|
+
throw new Error("Stop recovery retained container identity changed.");
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
if (missingIds.length > 0) assertManagedStopContainersAbsent(baseline.endpoint, missingIds);
|
|
4091
4189
|
if (!(0, import_node_util3.isDeepStrictEqual)(
|
|
4092
4190
|
containers.map((container) => container.labels["com.docker.compose.service"]).sort(),
|
|
4093
4191
|
[...plan.desiredServices].sort()
|
|
@@ -4107,6 +4205,7 @@ function recoverManagedStopBaseline(state, readPlan, beforePersist) {
|
|
|
4107
4205
|
assertManagedContainerConfigUnchanged({
|
|
4108
4206
|
plan,
|
|
4109
4207
|
containers,
|
|
4208
|
+
endpoint: baseline.endpoint,
|
|
4110
4209
|
workspace: state.workspace ? {
|
|
4111
4210
|
token: state.workspace,
|
|
4112
4211
|
gitCommonDir: resolveGitCommonDir(state.repoPath)
|
|
@@ -5490,7 +5589,7 @@ function retain(session, previous) {
|
|
|
5490
5589
|
|
|
5491
5590
|
// src/core/reliability-operation-store.ts
|
|
5492
5591
|
function installedCliVersion() {
|
|
5493
|
-
return true ? "0.6.
|
|
5592
|
+
return true ? "0.6.1" : "0.0.0-dev";
|
|
5494
5593
|
}
|
|
5495
5594
|
function compareCliVersions(left, right) {
|
|
5496
5595
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -7593,7 +7692,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs19.default.rea
|
|
|
7593
7692
|
const config = parseRepoConfigSource(raw, configPath);
|
|
7594
7693
|
const requiredVersion = config.devrouter?.version;
|
|
7595
7694
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
7596
|
-
const cliVersion = true ? "0.6.
|
|
7695
|
+
const cliVersion = true ? "0.6.1" : "0.0.0-dev";
|
|
7597
7696
|
if (cliVersion !== "0.0.0-dev" && compareSemver2(requiredVersion, cliVersion) > 0) {
|
|
7598
7697
|
hasWarnedVersionMismatch = true;
|
|
7599
7698
|
process.stderr.write(
|
|
@@ -8636,6 +8735,7 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
8636
8735
|
assertManagedContainerConfigUnchanged({
|
|
8637
8736
|
plan,
|
|
8638
8737
|
containers: population,
|
|
8738
|
+
endpoint,
|
|
8639
8739
|
workspace: workspaceEnv
|
|
8640
8740
|
});
|
|
8641
8741
|
if (!(0, import_node_util5.isDeepStrictEqual)(readAuthority(), authority) || !(0, import_node_util5.isDeepStrictEqual)(registration2(), owner) || resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
@@ -10650,9 +10750,9 @@ var ExecutionOutcomeError = class extends Error {
|
|
|
10650
10750
|
};
|
|
10651
10751
|
|
|
10652
10752
|
// src/core/reliability-lifecycle.ts
|
|
10653
|
-
var
|
|
10654
|
-
var
|
|
10655
|
-
var
|
|
10753
|
+
var import_node_crypto24 = require("crypto");
|
|
10754
|
+
var import_node_path42 = __toESM(require("path"));
|
|
10755
|
+
var import_node_util8 = require("util");
|
|
10656
10756
|
|
|
10657
10757
|
// src/core/capacity-policy.ts
|
|
10658
10758
|
var import_node_fs27 = __toESM(require("fs"));
|
|
@@ -12483,9 +12583,15 @@ function stepReliability(state, event, nowMs) {
|
|
|
12483
12583
|
|
|
12484
12584
|
// src/core/reliability-worker.ts
|
|
12485
12585
|
var import_node_child_process21 = require("child_process");
|
|
12586
|
+
var import_node_crypto20 = require("crypto");
|
|
12587
|
+
var import_node_fs31 = __toESM(require("fs"));
|
|
12588
|
+
var import_node_path36 = __toESM(require("path"));
|
|
12589
|
+
|
|
12590
|
+
// src/core/lifecycle-diagnostics.ts
|
|
12486
12591
|
var import_node_crypto19 = require("crypto");
|
|
12487
12592
|
var import_node_fs30 = __toESM(require("fs"));
|
|
12488
12593
|
var import_node_path35 = __toESM(require("path"));
|
|
12594
|
+
var import_node_util7 = require("util");
|
|
12489
12595
|
|
|
12490
12596
|
// src/core/lifecycle-progress.ts
|
|
12491
12597
|
var ROLES = {
|
|
@@ -12540,6 +12646,89 @@ function reportLifecycleProgress(phase) {
|
|
|
12540
12646
|
sender?.(phase);
|
|
12541
12647
|
}
|
|
12542
12648
|
|
|
12649
|
+
// src/core/lifecycle-diagnostics.ts
|
|
12650
|
+
var MAX_TAIL_BYTES = 32768;
|
|
12651
|
+
var MAX_RECEIPT_BYTES = 65536;
|
|
12652
|
+
function privateDirectory(directory) {
|
|
12653
|
+
const stat = import_node_fs30.default.lstatSync(directory);
|
|
12654
|
+
return stat.isDirectory() && stat.uid === process.getuid?.() && (stat.mode & 63) === 0;
|
|
12655
|
+
}
|
|
12656
|
+
function lifecycleDiagnosticsPath(identity2) {
|
|
12657
|
+
return import_node_path35.default.join(
|
|
12658
|
+
DEVROUTER_HOME,
|
|
12659
|
+
"lifecycle-diagnostics",
|
|
12660
|
+
`${(0, import_node_crypto19.createHash)("sha256").update(identity2.repoPath).digest("hex")}.json`
|
|
12661
|
+
);
|
|
12662
|
+
}
|
|
12663
|
+
function readReceipt(identity2) {
|
|
12664
|
+
let descriptor;
|
|
12665
|
+
try {
|
|
12666
|
+
const file = lifecycleDiagnosticsPath(identity2);
|
|
12667
|
+
if (!privateDirectory(import_node_path35.default.dirname(file))) return void 0;
|
|
12668
|
+
descriptor = import_node_fs30.default.openSync(
|
|
12669
|
+
file,
|
|
12670
|
+
import_node_fs30.default.constants.O_RDONLY | import_node_fs30.default.constants.O_NOFOLLOW | import_node_fs30.default.constants.O_NONBLOCK
|
|
12671
|
+
);
|
|
12672
|
+
const stat = import_node_fs30.default.fstatSync(descriptor);
|
|
12673
|
+
if (!stat.isFile() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0 || stat.size > MAX_RECEIPT_BYTES)
|
|
12674
|
+
return void 0;
|
|
12675
|
+
const buffer = Buffer.alloc(MAX_RECEIPT_BYTES + 1);
|
|
12676
|
+
const size = import_node_fs30.default.readSync(descriptor, buffer, 0, buffer.length, 0);
|
|
12677
|
+
if (size > MAX_RECEIPT_BYTES) return void 0;
|
|
12678
|
+
const receipt = JSON.parse(buffer.subarray(0, size).toString("utf8"));
|
|
12679
|
+
const timestamp = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
12680
|
+
const optionalTime = (value) => value === null || timestamp(value);
|
|
12681
|
+
if (receipt.version !== 1 || !(0, import_node_util7.isDeepStrictEqual)(receipt.identity, identity2) || typeof receipt.operationId !== "string" || typeof receipt.workerId !== "string" || !timestamp(receipt.startedAtMs) || !timestamp(receipt.updatedAtMs) || receipt.updatedAtMs < receipt.startedAtMs || !(receipt.phase === null || isLifecycleProgressPhase(receipt.phase)) || !optionalTime(receipt.phaseAtMs) || !optionalTime(receipt.lastOutputAtMs) || !optionalTime(receipt.lastProgressAtMs) || !(receipt.buildPackage === null || typeof receipt.buildPackage === "string" && receipt.buildPackage.length <= 128 && /^(?:@[a-zA-Z0-9_.-]+\/)?[a-zA-Z0-9_.-]+$/.test(receipt.buildPackage)) || typeof receipt.cancellationRequested !== "boolean" || typeof receipt.closed !== "boolean" || !Array.isArray(receipt.output) || receipt.output.length > 64)
|
|
12682
|
+
return void 0;
|
|
12683
|
+
let bytes2 = 0;
|
|
12684
|
+
for (const chunk of receipt.output) {
|
|
12685
|
+
if (!chunk || !["stdout", "stderr"].includes(chunk.stream) || typeof chunk.data !== "string" || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(chunk.data))
|
|
12686
|
+
return void 0;
|
|
12687
|
+
bytes2 += Buffer.byteLength(chunk.data, "base64");
|
|
12688
|
+
}
|
|
12689
|
+
return bytes2 <= MAX_TAIL_BYTES ? receipt : void 0;
|
|
12690
|
+
} catch {
|
|
12691
|
+
return void 0;
|
|
12692
|
+
} finally {
|
|
12693
|
+
if (descriptor !== void 0) import_node_fs30.default.closeSync(descriptor);
|
|
12694
|
+
}
|
|
12695
|
+
}
|
|
12696
|
+
function readLifecycleDiagnostics(identity2, now = Date.now()) {
|
|
12697
|
+
try {
|
|
12698
|
+
const before = readReliabilityOperation(identity2);
|
|
12699
|
+
const receipt = readReceipt(identity2);
|
|
12700
|
+
const after = readReliabilityOperation(identity2);
|
|
12701
|
+
if (!before || !after || !receipt || before.revision !== after.revision || !(0, import_node_util7.isDeepStrictEqual)(before, after) || !(0, import_node_util7.isDeepStrictEqual)(receipt.fence, reliabilityFence(after.state)) || after.state.operation?.id !== receipt.operationId || after.state.operation.kind !== "ensure" || after.worker !== null && after.worker.id !== receipt.workerId || !receipt.closed && !after.worker || now < receipt.updatedAtMs || [receipt.phaseAtMs, receipt.lastOutputAtMs, receipt.lastProgressAtMs].some(
|
|
12702
|
+
(time) => time !== null && (time < receipt.startedAtMs || time > receipt.updatedAtMs)
|
|
12703
|
+
))
|
|
12704
|
+
return void 0;
|
|
12705
|
+
const age = (time) => time === null ? null : now - time;
|
|
12706
|
+
const metadata = {
|
|
12707
|
+
stage: receipt.phase ?? "unknown",
|
|
12708
|
+
elapsedMs: Math.max(0, (receipt.closed ? receipt.updatedAtMs : now) - receipt.startedAtMs),
|
|
12709
|
+
stageAgeMs: age(receipt.phaseAtMs),
|
|
12710
|
+
outputAgeMs: age(receipt.lastOutputAtMs),
|
|
12711
|
+
progressAgeMs: age(receipt.lastProgressAtMs),
|
|
12712
|
+
evidence: now - receipt.updatedAtMs < 3e4 ? "recent" : "stale",
|
|
12713
|
+
buildPackage: receipt.buildPackage,
|
|
12714
|
+
liveness: "unknown",
|
|
12715
|
+
executionDeadlineAtMs: null,
|
|
12716
|
+
handshakeTimeoutMs: 3e4,
|
|
12717
|
+
cancellationGraceMs: 2e3,
|
|
12718
|
+
cancellationRequested: receipt.cancellationRequested,
|
|
12719
|
+
workerDrained: after.state.operation.drained
|
|
12720
|
+
};
|
|
12721
|
+
return {
|
|
12722
|
+
operationId: receipt.operationId,
|
|
12723
|
+
revision: after.revision,
|
|
12724
|
+
metadata,
|
|
12725
|
+
output: receipt.output
|
|
12726
|
+
};
|
|
12727
|
+
} catch {
|
|
12728
|
+
return void 0;
|
|
12729
|
+
}
|
|
12730
|
+
}
|
|
12731
|
+
|
|
12543
12732
|
// src/core/reliability-worker.ts
|
|
12544
12733
|
var DEFAULT_OUTPUT_PAGE_BYTES = 48 * 1024;
|
|
12545
12734
|
function workerGroupAbsent(pid) {
|
|
@@ -12551,11 +12740,11 @@ function workerGroupAbsent(pid) {
|
|
|
12551
12740
|
if (process.platform !== "linux") return false;
|
|
12552
12741
|
let observedZombie = false;
|
|
12553
12742
|
try {
|
|
12554
|
-
for (const entry of
|
|
12743
|
+
for (const entry of import_node_fs31.default.readdirSync("/proc")) {
|
|
12555
12744
|
if (!/^\d+$/.test(entry)) continue;
|
|
12556
12745
|
let stat;
|
|
12557
12746
|
try {
|
|
12558
|
-
stat =
|
|
12747
|
+
stat = import_node_fs31.default.readFileSync(`/proc/${entry}/stat`, "utf8");
|
|
12559
12748
|
} catch (error) {
|
|
12560
12749
|
if (error.code === "ENOENT") continue;
|
|
12561
12750
|
return false;
|
|
@@ -12576,19 +12765,19 @@ function workerGroupAbsent(pid) {
|
|
|
12576
12765
|
|
|
12577
12766
|
// src/core/workspace-ensure.ts
|
|
12578
12767
|
var import_node_child_process31 = require("child_process");
|
|
12579
|
-
var
|
|
12580
|
-
var
|
|
12768
|
+
var import_node_fs36 = __toESM(require("fs"));
|
|
12769
|
+
var import_node_path41 = __toESM(require("path"));
|
|
12581
12770
|
|
|
12582
12771
|
// src/core/devpod-mutation.ts
|
|
12583
12772
|
var import_node_child_process22 = require("child_process");
|
|
12584
|
-
var
|
|
12585
|
-
var
|
|
12586
|
-
var DEVPOD_MUTATION_LOCK_FILE =
|
|
12773
|
+
var import_node_fs32 = __toESM(require("fs"));
|
|
12774
|
+
var import_node_path37 = __toESM(require("path"));
|
|
12775
|
+
var DEVPOD_MUTATION_LOCK_FILE = import_node_path37.default.join(DEVROUTER_HOME, "devpod-mutation.lock");
|
|
12587
12776
|
var DEVPOD_MUTATION_WAIT_MS = 18e5;
|
|
12588
12777
|
var DevpodStartPostconditionError = class extends Error {
|
|
12589
12778
|
};
|
|
12590
12779
|
function withMutationLock2(activity, target, operation) {
|
|
12591
|
-
|
|
12780
|
+
import_node_fs32.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
12592
12781
|
return withFileLockSync(
|
|
12593
12782
|
DEVPOD_MUTATION_LOCK_FILE,
|
|
12594
12783
|
{
|
|
@@ -12790,7 +12979,7 @@ async function startDevpodWorkspace(options) {
|
|
|
12790
12979
|
|
|
12791
12980
|
// src/core/host-port-claims.ts
|
|
12792
12981
|
var import_node_child_process23 = require("child_process");
|
|
12793
|
-
var
|
|
12982
|
+
var import_node_path38 = __toESM(require("path"));
|
|
12794
12983
|
var COMPOSE_RENDER_TIMEOUT_MS = 3e4;
|
|
12795
12984
|
var COMPOSE_RENDER_MAX_BUFFER = 8 * 1024 * 1024;
|
|
12796
12985
|
var DOCKER_TIMEOUT_MS = 1e4;
|
|
@@ -12998,8 +13187,8 @@ function renderManagedComposeModel(plan, workspace) {
|
|
|
12998
13187
|
}
|
|
12999
13188
|
}
|
|
13000
13189
|
function attributeHolderWorktree(workingDir) {
|
|
13001
|
-
if (
|
|
13002
|
-
return comparableWorkspacePath(
|
|
13190
|
+
if (import_node_path38.default.basename(workingDir) !== ".devcontainer") return void 0;
|
|
13191
|
+
return comparableWorkspacePath(import_node_path38.default.dirname(workingDir));
|
|
13003
13192
|
}
|
|
13004
13193
|
function holderAttribution(holder, repoPath) {
|
|
13005
13194
|
const attribution = {};
|
|
@@ -13034,7 +13223,7 @@ function detectHostPortClaimConflicts(options) {
|
|
|
13034
13223
|
);
|
|
13035
13224
|
if (desired.length === 0) return [];
|
|
13036
13225
|
const holders = listHolderSnapshots();
|
|
13037
|
-
const devcontainerDir =
|
|
13226
|
+
const devcontainerDir = import_node_path38.default.join(options.repoPath, ".devcontainer");
|
|
13038
13227
|
const conflicts = [];
|
|
13039
13228
|
for (const binding of desired) {
|
|
13040
13229
|
for (const holder of holders) {
|
|
@@ -13195,7 +13384,7 @@ function transportFailure() {
|
|
|
13195
13384
|
|
|
13196
13385
|
// src/core/managed-host-preparation.ts
|
|
13197
13386
|
var import_node_child_process25 = require("child_process");
|
|
13198
|
-
var
|
|
13387
|
+
var import_node_fs33 = __toESM(require("fs"));
|
|
13199
13388
|
var PREPARATION_TIMEOUT_MS = 6e4;
|
|
13200
13389
|
var CONFIG_READ_ERROR = "Managed host preparation could not read .devrouter.yml.";
|
|
13201
13390
|
var CONFIG_CHANGED_ERROR = "Managed host preparation changed .devrouter.yml; refusing to continue.";
|
|
@@ -13238,7 +13427,7 @@ function runPreparationCommand(argv, cwd) {
|
|
|
13238
13427
|
}
|
|
13239
13428
|
function readConfigBytes(configPath) {
|
|
13240
13429
|
try {
|
|
13241
|
-
return
|
|
13430
|
+
return import_node_fs33.default.readFileSync(configPath);
|
|
13242
13431
|
} catch {
|
|
13243
13432
|
return void 0;
|
|
13244
13433
|
}
|
|
@@ -13271,6 +13460,43 @@ async function runManagedHostPreparation(repoPath, config) {
|
|
|
13271
13460
|
}
|
|
13272
13461
|
}
|
|
13273
13462
|
|
|
13463
|
+
// src/core/lifecycle-operation-status.ts
|
|
13464
|
+
function projectLifecycleOperation(operation) {
|
|
13465
|
+
const result = {
|
|
13466
|
+
operationId: operation.id,
|
|
13467
|
+
phase: "terminal",
|
|
13468
|
+
outcome: null,
|
|
13469
|
+
reason: null,
|
|
13470
|
+
exitCode: null
|
|
13471
|
+
};
|
|
13472
|
+
switch (operation.status) {
|
|
13473
|
+
case "NOT_STARTED":
|
|
13474
|
+
if (operation.drained) result.outcome = "NOT_STARTED";
|
|
13475
|
+
else result.phase = "queued";
|
|
13476
|
+
break;
|
|
13477
|
+
case "DISPATCH_PENDING":
|
|
13478
|
+
case "DISPATCH_RECORDED":
|
|
13479
|
+
case "RUNNING":
|
|
13480
|
+
if (operation.drained) {
|
|
13481
|
+
result.outcome = "COMPLETION_UNKNOWN";
|
|
13482
|
+
result.reason = "worker-drained-without-result";
|
|
13483
|
+
} else result.phase = operation.status === "RUNNING" ? "running" : "dispatching";
|
|
13484
|
+
break;
|
|
13485
|
+
case "COMPLETED":
|
|
13486
|
+
result.outcome = "COMPLETED";
|
|
13487
|
+
result.exitCode = operation.exitCode;
|
|
13488
|
+
break;
|
|
13489
|
+
case "NOT_LAUNCHED":
|
|
13490
|
+
result.outcome = "NOT_STARTED";
|
|
13491
|
+
break;
|
|
13492
|
+
case "INTERRUPTED":
|
|
13493
|
+
case "COMPLETION_UNKNOWN":
|
|
13494
|
+
result.outcome = operation.status;
|
|
13495
|
+
break;
|
|
13496
|
+
}
|
|
13497
|
+
return result;
|
|
13498
|
+
}
|
|
13499
|
+
|
|
13274
13500
|
// src/core/managed-mount-nesting.ts
|
|
13275
13501
|
var import_node_child_process26 = require("child_process");
|
|
13276
13502
|
var NESTED_MOUNT_REASON = "no configured mount is nested inside another configured mount";
|
|
@@ -13423,6 +13649,8 @@ function readReliabilityStatus(options) {
|
|
|
13423
13649
|
return void 0;
|
|
13424
13650
|
}
|
|
13425
13651
|
if (!record5) return void 0;
|
|
13652
|
+
const diagnosticReceipt = record5.state.operation ? readLifecycleDiagnostics(record5.identity) : void 0;
|
|
13653
|
+
const diagnostics = diagnosticReceipt?.operationId === record5.state.operation?.id && diagnosticReceipt?.revision === record5.revision ? diagnosticReceipt?.metadata : void 0;
|
|
13426
13654
|
const reason2 = reliabilityAttention(record5.state) ?? (record5.state.desired === "running" && refusedManagedStart(record5.result) ? "start-refused" : void 0);
|
|
13427
13655
|
return {
|
|
13428
13656
|
desired: record5.state.desired,
|
|
@@ -13433,6 +13661,14 @@ function readReliabilityStatus(options) {
|
|
|
13433
13661
|
correctiveActionsTaken: record5.state.incident.correctiveActionsTaken,
|
|
13434
13662
|
actionLimit: record5.state.incident.actionLimit
|
|
13435
13663
|
} : null,
|
|
13664
|
+
...record5.state.operation ? {
|
|
13665
|
+
operation: {
|
|
13666
|
+
...projectLifecycleOperation(record5.state.operation),
|
|
13667
|
+
kind: record5.state.operation.kind,
|
|
13668
|
+
drained: record5.state.operation.drained,
|
|
13669
|
+
...diagnostics ? { diagnostics } : {}
|
|
13670
|
+
}
|
|
13671
|
+
} : {},
|
|
13436
13672
|
...reason2 ? {
|
|
13437
13673
|
attention: {
|
|
13438
13674
|
reason: reason2,
|
|
@@ -14060,33 +14296,33 @@ var import_node_child_process28 = require("child_process");
|
|
|
14060
14296
|
var import_node_net3 = require("net");
|
|
14061
14297
|
|
|
14062
14298
|
// src/core/network-local-daemon.ts
|
|
14063
|
-
var
|
|
14064
|
-
var
|
|
14299
|
+
var import_node_fs34 = __toESM(require("fs"));
|
|
14300
|
+
var import_node_path39 = __toESM(require("path"));
|
|
14065
14301
|
function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
14066
14302
|
const deadline = Date.now() + 2e3;
|
|
14067
14303
|
try {
|
|
14068
14304
|
if (!endpoint.startsWith("unix:///")) return false;
|
|
14069
14305
|
const socket = endpoint.slice("unix://".length);
|
|
14070
|
-
const table =
|
|
14306
|
+
const table = import_node_fs34.default.readFileSync(import_node_path39.default.join(procRoot, "net/unix"), "utf8");
|
|
14071
14307
|
if (Buffer.byteLength(table) > 1024 * 1024) return false;
|
|
14072
14308
|
const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
|
|
14073
14309
|
if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
|
|
14074
14310
|
const inode = `socket:[${rows2[0][6]}]`;
|
|
14075
|
-
const ownNamespace =
|
|
14076
|
-
const pids =
|
|
14311
|
+
const ownNamespace = import_node_fs34.default.readlinkSync(import_node_path39.default.join(procRoot, "self/ns/net"));
|
|
14312
|
+
const pids = import_node_fs34.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
|
|
14077
14313
|
if (pids.length > 4096) return false;
|
|
14078
14314
|
for (const pid of pids) {
|
|
14079
14315
|
if (Date.now() > deadline) return false;
|
|
14080
14316
|
try {
|
|
14081
|
-
const root =
|
|
14082
|
-
if (
|
|
14083
|
-
if (
|
|
14084
|
-
const descriptors =
|
|
14317
|
+
const root = import_node_path39.default.join(procRoot, pid);
|
|
14318
|
+
if (import_node_path39.default.basename(import_node_fs34.default.readlinkSync(import_node_path39.default.join(root, "exe"))) !== "dockerd") continue;
|
|
14319
|
+
if (import_node_fs34.default.readlinkSync(import_node_path39.default.join(root, "ns/net")) !== ownNamespace) continue;
|
|
14320
|
+
const descriptors = import_node_fs34.default.readdirSync(import_node_path39.default.join(root, "fd"));
|
|
14085
14321
|
if (descriptors.length > 4096) continue;
|
|
14086
14322
|
if (descriptors.some((fd) => {
|
|
14087
14323
|
if (Date.now() > deadline) return false;
|
|
14088
14324
|
try {
|
|
14089
|
-
return
|
|
14325
|
+
return import_node_fs34.default.readlinkSync(import_node_path39.default.join(root, "fd", fd)) === inode;
|
|
14090
14326
|
} catch {
|
|
14091
14327
|
return false;
|
|
14092
14328
|
}
|
|
@@ -14161,10 +14397,10 @@ function inspectLegacyNetworkCapacity(input) {
|
|
|
14161
14397
|
}
|
|
14162
14398
|
|
|
14163
14399
|
// src/core/network-managed.ts
|
|
14164
|
-
var
|
|
14400
|
+
var import_node_crypto23 = require("crypto");
|
|
14165
14401
|
|
|
14166
14402
|
// src/core/network-compose.ts
|
|
14167
|
-
var
|
|
14403
|
+
var import_node_crypto21 = require("crypto");
|
|
14168
14404
|
var SAFE_ENDPOINT_UPPER_BOUND_SEMANTICS = "full-lifecycle-demand";
|
|
14169
14405
|
function isRecord5(value) {
|
|
14170
14406
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -14970,14 +15206,14 @@ function stableValue(value) {
|
|
|
14970
15206
|
}
|
|
14971
15207
|
function fingerprintComposeNetworkModel(input) {
|
|
14972
15208
|
const projection = normalizedFingerprintProjection(input);
|
|
14973
|
-
return (0,
|
|
15209
|
+
return (0, import_node_crypto21.createHash)("sha256").update(stableValue(projection), "utf8").digest("hex");
|
|
14974
15210
|
}
|
|
14975
15211
|
|
|
14976
15212
|
// src/core/network-compose-files.ts
|
|
14977
15213
|
var import_node_child_process29 = require("child_process");
|
|
14978
|
-
var
|
|
14979
|
-
var
|
|
14980
|
-
var
|
|
15214
|
+
var import_node_crypto22 = require("crypto");
|
|
15215
|
+
var import_node_fs35 = __toESM(require("fs"));
|
|
15216
|
+
var import_node_path40 = __toESM(require("path"));
|
|
14981
15217
|
var import_yaml4 = __toESM(require("yaml"));
|
|
14982
15218
|
var OVERLAY = "docker-compose.devrouter-network.yml";
|
|
14983
15219
|
var MARKER = "# devrouter:managed workspace network";
|
|
@@ -14990,12 +15226,12 @@ function object3(value) {
|
|
|
14990
15226
|
function inspectNetworkComposeFiles(input) {
|
|
14991
15227
|
const authoredNetworks = {};
|
|
14992
15228
|
for (const file of input.plan.composeFiles) {
|
|
14993
|
-
const stat =
|
|
15229
|
+
const stat = import_node_fs35.default.lstatSync(file);
|
|
14994
15230
|
if (!stat.isFile() || stat.size > MAX_BYTES2)
|
|
14995
15231
|
throw new Error("Network Compose source is unavailable or too large.");
|
|
14996
15232
|
let source;
|
|
14997
15233
|
try {
|
|
14998
|
-
source = object3(import_yaml4.default.parse(
|
|
15234
|
+
source = object3(import_yaml4.default.parse(import_node_fs35.default.readFileSync(file, "utf8")));
|
|
14999
15235
|
} catch {
|
|
15000
15236
|
throw new Error("Network Compose source could not be parsed safely.");
|
|
15001
15237
|
}
|
|
@@ -15045,7 +15281,7 @@ function inspectNetworkComposeFiles(input) {
|
|
|
15045
15281
|
function prepareNetworkComposeFiles(plan, subnet) {
|
|
15046
15282
|
const overlay = deriveComposeNetworkOverlay(subnet);
|
|
15047
15283
|
if (overlay.status !== "ready" || !overlay.overlay) throw new Error("Network subnet is invalid.");
|
|
15048
|
-
const overlayPath =
|
|
15284
|
+
const overlayPath = import_node_path40.default.join(plan.composeDirectory, OVERLAY);
|
|
15049
15285
|
const ignored = (0, import_node_child_process29.spawnSync)("git", [
|
|
15050
15286
|
"-C",
|
|
15051
15287
|
plan.composeDirectory,
|
|
@@ -15060,9 +15296,9 @@ function prepareNetworkComposeFiles(plan, subnet) {
|
|
|
15060
15296
|
const bytes2 = `${MARKER}
|
|
15061
15297
|
${import_yaml4.default.stringify(overlay.overlay)}`;
|
|
15062
15298
|
const assertOwnership = () => {
|
|
15063
|
-
if (!
|
|
15064
|
-
const stat =
|
|
15065
|
-
if (!stat.isFile() || stat.size > MAX_BYTES2 ||
|
|
15299
|
+
if (!import_node_fs35.default.existsSync(overlayPath)) return;
|
|
15300
|
+
const stat = import_node_fs35.default.lstatSync(overlayPath);
|
|
15301
|
+
if (!stat.isFile() || stat.size > MAX_BYTES2 || import_node_fs35.default.readFileSync(overlayPath, "utf8") !== bytes2)
|
|
15066
15302
|
throw new Error("Retained network overlay changed; repair is required.");
|
|
15067
15303
|
};
|
|
15068
15304
|
assertOwnership();
|
|
@@ -15076,7 +15312,7 @@ ${JSON.stringify(extended.config, null, 2)}
|
|
|
15076
15312
|
...plan,
|
|
15077
15313
|
contents,
|
|
15078
15314
|
composeFiles: [...plan.composeFiles, overlayPath],
|
|
15079
|
-
effectiveConfigSha256: (0,
|
|
15315
|
+
effectiveConfigSha256: (0, import_node_crypto22.createHash)("sha256").update(contents).digest("hex")
|
|
15080
15316
|
};
|
|
15081
15317
|
return {
|
|
15082
15318
|
plan: result,
|
|
@@ -15394,7 +15630,7 @@ function createManagedNetworkSession(input) {
|
|
|
15394
15630
|
"Insufficient endpoint headroom; request an allowed larger network before first allocation."
|
|
15395
15631
|
);
|
|
15396
15632
|
const authority = readNetworkOperationAuthority(identity2);
|
|
15397
|
-
const fingerprint = (0,
|
|
15633
|
+
const fingerprint = (0, import_node_crypto23.createHash)("sha256").update(input.plan().sourceConfigSha256).update(fingerprintComposeNetworkModel(model)).digest("hex");
|
|
15398
15634
|
if (!saved) {
|
|
15399
15635
|
if (policy.status !== "valid")
|
|
15400
15636
|
throw new Error("New network allocation requires valid machine policy.");
|
|
@@ -15578,11 +15814,11 @@ var DEFAULT_READINESS_TIMEOUT_MS = 12e4;
|
|
|
15578
15814
|
var POLL_INTERVAL_MS = 1e3;
|
|
15579
15815
|
function assertOverlay(container, repoPath) {
|
|
15580
15816
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
15581
|
-
if (!workingDir || !sameWorkspacePath(workingDir,
|
|
15817
|
+
if (!workingDir || !sameWorkspacePath(workingDir, import_node_path41.default.join(repoPath, ".devcontainer"))) {
|
|
15582
15818
|
throw new Error(`Container '${container.id}' does not belong to the exact worktree.`);
|
|
15583
15819
|
}
|
|
15584
15820
|
const configFiles = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").filter(Boolean);
|
|
15585
|
-
const expectedOverlay =
|
|
15821
|
+
const expectedOverlay = import_node_path41.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
15586
15822
|
if (!configFiles.some((configFile) => sameWorkspacePath(configFile, expectedOverlay))) {
|
|
15587
15823
|
throw new Error(`Container '${container.id}' was not started with ${DEVCONTAINER_OVERLAY}.`);
|
|
15588
15824
|
}
|
|
@@ -15596,7 +15832,7 @@ function assertWorktreeOwnership(container, repoPath) {
|
|
|
15596
15832
|
function isInsideWorktree(candidate, repoPath) {
|
|
15597
15833
|
const normalizedCandidate = comparableWorkspacePath(candidate);
|
|
15598
15834
|
const normalizedRoot = comparableWorkspacePath(repoPath);
|
|
15599
|
-
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}${
|
|
15835
|
+
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}${import_node_path41.default.sep}`);
|
|
15600
15836
|
}
|
|
15601
15837
|
function assertReady(container, label) {
|
|
15602
15838
|
if (!container.state.Running) {
|
|
@@ -15788,7 +16024,7 @@ function hasExactManagedComposeProject(repoPath, state) {
|
|
|
15788
16024
|
}).some((container) => {
|
|
15789
16025
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
15790
16026
|
return Boolean(
|
|
15791
|
-
workingDir && sameWorkspacePath(workingDir,
|
|
16027
|
+
workingDir && sameWorkspacePath(workingDir, import_node_path41.default.join(repoPath, ".devcontainer"))
|
|
15792
16028
|
);
|
|
15793
16029
|
});
|
|
15794
16030
|
} catch {
|
|
@@ -16193,7 +16429,7 @@ function resolvePrimaryTarget(repoPath) {
|
|
|
16193
16429
|
}
|
|
16194
16430
|
function isPrimaryCheckout(repoPath) {
|
|
16195
16431
|
try {
|
|
16196
|
-
return
|
|
16432
|
+
return import_node_fs36.default.statSync(import_node_path41.default.join(repoPath, ".git")).isDirectory();
|
|
16197
16433
|
} catch {
|
|
16198
16434
|
return false;
|
|
16199
16435
|
}
|
|
@@ -16257,8 +16493,8 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16257
16493
|
const target = options.repair ? resolveRepairTarget(repoPath, linked) : linked ? resolveLinkedTarget(repoPath) : resolvePrimaryTarget(repoPath);
|
|
16258
16494
|
let devpodId = target.devpodId;
|
|
16259
16495
|
if (target.kind === "linked") {
|
|
16260
|
-
const overlayPath =
|
|
16261
|
-
if (!
|
|
16496
|
+
const overlayPath = import_node_path41.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
16497
|
+
if (!import_node_fs36.default.existsSync(overlayPath)) {
|
|
16262
16498
|
throw new Error(`Missing required DevPod compose overlay: ${overlayPath}`);
|
|
16263
16499
|
}
|
|
16264
16500
|
}
|
|
@@ -16278,6 +16514,9 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16278
16514
|
if (variant !== "default" && managedPostStart.kind !== "runtime")
|
|
16279
16515
|
throw new Error("Runtime variants require a managed runtime post-start adapter.");
|
|
16280
16516
|
const managedRuntime = runtime.config.managedRuntime;
|
|
16517
|
+
const hostPortDeclarationsSha256 = managedHostPortDeclarationsIdentity(
|
|
16518
|
+
managedRuntime?.devcontainer.hostPorts ?? {}
|
|
16519
|
+
);
|
|
16281
16520
|
if (options.repair && !managedRuntime)
|
|
16282
16521
|
throw new Error("Repair requires managedRuntime configuration.");
|
|
16283
16522
|
previousRoutes = listHostRouteState().filter(
|
|
@@ -16365,7 +16604,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16365
16604
|
}
|
|
16366
16605
|
const apps = proxyAppsFromConfig(runtime.config);
|
|
16367
16606
|
const parsedUpstreams = apps.map((app) => parseUpstream(app.upstream));
|
|
16368
|
-
const aliasPrefix = target.kind === "linked" ? target.workspace : wsFromBranch(runtime.config.project?.name ??
|
|
16607
|
+
const aliasPrefix = target.kind === "linked" ? target.workspace : wsFromBranch(runtime.config.project?.name ?? import_node_path41.default.basename(repoPath)) ?? "app";
|
|
16369
16608
|
for (const [index, app] of apps.entries()) {
|
|
16370
16609
|
if (!parsedUpstreams[index].host.startsWith(`${aliasPrefix}-`)) {
|
|
16371
16610
|
const owner = target.kind === "linked" ? "workspace" : "checkout";
|
|
@@ -16602,9 +16841,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16602
16841
|
composeProject: captureProject,
|
|
16603
16842
|
profile: runtime.profile,
|
|
16604
16843
|
variant,
|
|
16605
|
-
hostPortDeclarationsSha256:
|
|
16606
|
-
managedRuntime?.devcontainer.hostPorts ?? {}
|
|
16607
|
-
),
|
|
16844
|
+
...hostPortDeclarationsSha256 !== void 0 ? { hostPortDeclarationsSha256 } : {},
|
|
16608
16845
|
desired: {
|
|
16609
16846
|
apps: apps.map((app) => app.name).sort(),
|
|
16610
16847
|
services: [...capturePlan.desiredProfileServices].sort(),
|
|
@@ -16815,9 +17052,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16815
17052
|
composeProject: managedComposeProject,
|
|
16816
17053
|
profile: runtime.profile,
|
|
16817
17054
|
variant,
|
|
16818
|
-
hostPortDeclarationsSha256:
|
|
16819
|
-
managedRuntime?.devcontainer.hostPorts ?? {}
|
|
16820
|
-
),
|
|
17055
|
+
...hostPortDeclarationsSha256 !== void 0 ? { hostPortDeclarationsSha256 } : {},
|
|
16821
17056
|
desired: {
|
|
16822
17057
|
apps: apps.map((app) => app.name).sort(),
|
|
16823
17058
|
services: [...managedPlan.desiredProfileServices].sort(),
|
|
@@ -17254,9 +17489,9 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
17254
17489
|
}
|
|
17255
17490
|
const repairLocked = () => {
|
|
17256
17491
|
const runtime = resolveWorkspaceRuntimeOrDefault(repoPath);
|
|
17257
|
-
|
|
17492
|
+
import_node_fs36.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
17258
17493
|
return withFileLock(
|
|
17259
|
-
|
|
17494
|
+
import_node_path41.default.join(DEVROUTER_HOME, `${runtime}-mutation.lock`),
|
|
17260
17495
|
{
|
|
17261
17496
|
activity: "Managed runtime repair",
|
|
17262
17497
|
target: repoPath,
|
|
@@ -17434,7 +17669,7 @@ function preflightRuntimeVariant(repoPath, options) {
|
|
|
17434
17669
|
return variant;
|
|
17435
17670
|
}
|
|
17436
17671
|
function capacityPolicyTargets(repoPath) {
|
|
17437
|
-
const policy = readCapacityPolicy(
|
|
17672
|
+
const policy = readCapacityPolicy(import_node_path42.default.join(DEVROUTER_HOME, "controller"));
|
|
17438
17673
|
return policy?.admissions === "enabled" && policy.enrollments.some((entry) => entry.repoPath === repoPath);
|
|
17439
17674
|
}
|
|
17440
17675
|
function clearActiveLifecycleStartupWitness() {
|
|
@@ -17540,7 +17775,7 @@ async function executeLifecycleWorker(request, operation) {
|
|
|
17540
17775
|
const owned = containers.filter(
|
|
17541
17776
|
(container) => sameWorkspacePath(
|
|
17542
17777
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
17543
|
-
|
|
17778
|
+
import_node_path42.default.join(request.repoPath, ".devcontainer")
|
|
17544
17779
|
)
|
|
17545
17780
|
);
|
|
17546
17781
|
const projects = owned.map((container) => container.labels["com.docker.compose.project"]);
|
|
@@ -17627,7 +17862,7 @@ function proveLifecycleStopped() {
|
|
|
17627
17862
|
throw new Error("Full stop proof requires an explicit stop worker.");
|
|
17628
17863
|
const settle = () => {
|
|
17629
17864
|
claimLifecycleEffect();
|
|
17630
|
-
if (initialStopAbsence && !(0,
|
|
17865
|
+
if (initialStopAbsence && !(0, import_node_util8.isDeepStrictEqual)(
|
|
17631
17866
|
proveInitialManagedDevsyAbsence(request.repoPath, initialStopAbsence.record.devpodId),
|
|
17632
17867
|
initialStopAbsence
|
|
17633
17868
|
))
|
|
@@ -17647,7 +17882,7 @@ function proveLifecycleStopped() {
|
|
|
17647
17882
|
const remaining = inspectWorkspaceContainers().filter(
|
|
17648
17883
|
(container) => sameWorkspacePath(
|
|
17649
17884
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
17650
|
-
|
|
17885
|
+
import_node_path42.default.join(request.repoPath, ".devcontainer")
|
|
17651
17886
|
)
|
|
17652
17887
|
);
|
|
17653
17888
|
if (remaining.some(
|
|
@@ -17677,7 +17912,7 @@ function proveLifecycleStopped() {
|
|
|
17677
17912
|
return void 0;
|
|
17678
17913
|
});
|
|
17679
17914
|
if (settlement) {
|
|
17680
|
-
const capacity = createLifecycleCapacityStore(
|
|
17915
|
+
const capacity = createLifecycleCapacityStore(import_node_path42.default.join(DEVROUTER_HOME, "controller"));
|
|
17681
17916
|
for (let attempt = 0; ; attempt++) {
|
|
17682
17917
|
try {
|
|
17683
17918
|
const state = capacity.inspect();
|
|
@@ -17851,7 +18086,7 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
17851
18086
|
}
|
|
17852
18087
|
assertRunningDevpod(devpod.id, repoPath);
|
|
17853
18088
|
const workspaceDirectory = resolveWorkspaceDirectory(repoPath);
|
|
17854
|
-
const statusMarker = `__DEVROUTER_EXIT_${(0,
|
|
18089
|
+
const statusMarker = `__DEVROUTER_EXIT_${(0, import_node_crypto25.randomUUID)()}__:`;
|
|
17855
18090
|
const statusMarkerBytes = Buffer.from(statusMarker, "ascii");
|
|
17856
18091
|
const literalCommand = command.map(quotePosixArg).join(" ");
|
|
17857
18092
|
const wrappedCommand = `${literalCommand}; __devrouter_status=$?; printf '${statusMarker}%s\\n' "$__devrouter_status" >&2; exit 0`;
|
|
@@ -18041,11 +18276,11 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
18041
18276
|
|
|
18042
18277
|
// src/core/workspace-lifecycle.ts
|
|
18043
18278
|
var import_node_child_process34 = require("child_process");
|
|
18044
|
-
var
|
|
18045
|
-
var
|
|
18279
|
+
var import_node_fs38 = __toESM(require("fs"));
|
|
18280
|
+
var import_node_path43 = __toESM(require("path"));
|
|
18046
18281
|
|
|
18047
18282
|
// src/core/route-state.ts
|
|
18048
|
-
var
|
|
18283
|
+
var import_node_fs37 = __toESM(require("fs"));
|
|
18049
18284
|
function listRoutesForWorktreePath(worktreePath, routes = listHostRouteState()) {
|
|
18050
18285
|
return routes.filter((route) => sameWorkspacePath(route.repoPath, worktreePath));
|
|
18051
18286
|
}
|