@devrouter/cli 0.5.6 → 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/README.md +5 -2
- package/assets/preview/github-sweep.yml +23 -0
- package/assets/preview/github.yml +35 -0
- package/assets/preview/gitlab-template.yml +34 -0
- package/assets/preview/gitlab.yml +22 -0
- package/assets/preview/installation.json +54 -0
- package/dist/devrouter-lifecycle-worker.js +314 -77
- package/dist/devrouter-preview-gitlab-ingress.js +182 -0
- package/dist/devrouter.js +8992 -1601
- package/package.json +10 -2
- package/upgrade-prompts/0.6.0.md +57 -0
- 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.
|
|
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);
|
|
@@ -7578,6 +7677,9 @@ function resolveRepoPath(repoPath) {
|
|
|
7578
7677
|
function getRepoConfigPath(repoPath) {
|
|
7579
7678
|
return import_node_path23.default.join(resolveRepoPath(repoPath), CONFIG_FILE_NAME);
|
|
7580
7679
|
}
|
|
7680
|
+
function parseRepoConfigSource(source, configPath) {
|
|
7681
|
+
return parseConfig(import_yaml3.default.parse(source) ?? {}, configPath);
|
|
7682
|
+
}
|
|
7581
7683
|
function loadRepoConfig(repoPath, read2 = (file) => import_node_fs19.default.readFileSync(file, "utf-8")) {
|
|
7582
7684
|
const resolvedRepoPath = resolveRepoPath(repoPath);
|
|
7583
7685
|
const configPath = getRepoConfigPath(resolvedRepoPath);
|
|
@@ -7587,11 +7689,10 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs19.default.rea
|
|
|
7587
7689
|
);
|
|
7588
7690
|
}
|
|
7589
7691
|
const raw = read2(configPath);
|
|
7590
|
-
const
|
|
7591
|
-
const config = parseConfig(parsed ?? {}, configPath);
|
|
7692
|
+
const config = parseRepoConfigSource(raw, configPath);
|
|
7592
7693
|
const requiredVersion = config.devrouter?.version;
|
|
7593
7694
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
7594
|
-
const cliVersion = true ? "0.
|
|
7695
|
+
const cliVersion = true ? "0.6.1" : "0.0.0-dev";
|
|
7595
7696
|
if (cliVersion !== "0.0.0-dev" && compareSemver2(requiredVersion, cliVersion) > 0) {
|
|
7596
7697
|
hasWarnedVersionMismatch = true;
|
|
7597
7698
|
process.stderr.write(
|
|
@@ -8634,6 +8735,7 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
8634
8735
|
assertManagedContainerConfigUnchanged({
|
|
8635
8736
|
plan,
|
|
8636
8737
|
containers: population,
|
|
8738
|
+
endpoint,
|
|
8637
8739
|
workspace: workspaceEnv
|
|
8638
8740
|
});
|
|
8639
8741
|
if (!(0, import_node_util5.isDeepStrictEqual)(readAuthority(), authority) || !(0, import_node_util5.isDeepStrictEqual)(registration2(), owner) || resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
@@ -10648,9 +10750,9 @@ var ExecutionOutcomeError = class extends Error {
|
|
|
10648
10750
|
};
|
|
10649
10751
|
|
|
10650
10752
|
// src/core/reliability-lifecycle.ts
|
|
10651
|
-
var
|
|
10652
|
-
var
|
|
10653
|
-
var
|
|
10753
|
+
var import_node_crypto24 = require("crypto");
|
|
10754
|
+
var import_node_path42 = __toESM(require("path"));
|
|
10755
|
+
var import_node_util8 = require("util");
|
|
10654
10756
|
|
|
10655
10757
|
// src/core/capacity-policy.ts
|
|
10656
10758
|
var import_node_fs27 = __toESM(require("fs"));
|
|
@@ -12481,9 +12583,15 @@ function stepReliability(state, event, nowMs) {
|
|
|
12481
12583
|
|
|
12482
12584
|
// src/core/reliability-worker.ts
|
|
12483
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
|
|
12484
12591
|
var import_node_crypto19 = require("crypto");
|
|
12485
12592
|
var import_node_fs30 = __toESM(require("fs"));
|
|
12486
12593
|
var import_node_path35 = __toESM(require("path"));
|
|
12594
|
+
var import_node_util7 = require("util");
|
|
12487
12595
|
|
|
12488
12596
|
// src/core/lifecycle-progress.ts
|
|
12489
12597
|
var ROLES = {
|
|
@@ -12538,6 +12646,89 @@ function reportLifecycleProgress(phase) {
|
|
|
12538
12646
|
sender?.(phase);
|
|
12539
12647
|
}
|
|
12540
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
|
+
|
|
12541
12732
|
// src/core/reliability-worker.ts
|
|
12542
12733
|
var DEFAULT_OUTPUT_PAGE_BYTES = 48 * 1024;
|
|
12543
12734
|
function workerGroupAbsent(pid) {
|
|
@@ -12549,11 +12740,11 @@ function workerGroupAbsent(pid) {
|
|
|
12549
12740
|
if (process.platform !== "linux") return false;
|
|
12550
12741
|
let observedZombie = false;
|
|
12551
12742
|
try {
|
|
12552
|
-
for (const entry of
|
|
12743
|
+
for (const entry of import_node_fs31.default.readdirSync("/proc")) {
|
|
12553
12744
|
if (!/^\d+$/.test(entry)) continue;
|
|
12554
12745
|
let stat;
|
|
12555
12746
|
try {
|
|
12556
|
-
stat =
|
|
12747
|
+
stat = import_node_fs31.default.readFileSync(`/proc/${entry}/stat`, "utf8");
|
|
12557
12748
|
} catch (error) {
|
|
12558
12749
|
if (error.code === "ENOENT") continue;
|
|
12559
12750
|
return false;
|
|
@@ -12574,19 +12765,19 @@ function workerGroupAbsent(pid) {
|
|
|
12574
12765
|
|
|
12575
12766
|
// src/core/workspace-ensure.ts
|
|
12576
12767
|
var import_node_child_process31 = require("child_process");
|
|
12577
|
-
var
|
|
12578
|
-
var
|
|
12768
|
+
var import_node_fs36 = __toESM(require("fs"));
|
|
12769
|
+
var import_node_path41 = __toESM(require("path"));
|
|
12579
12770
|
|
|
12580
12771
|
// src/core/devpod-mutation.ts
|
|
12581
12772
|
var import_node_child_process22 = require("child_process");
|
|
12582
|
-
var
|
|
12583
|
-
var
|
|
12584
|
-
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");
|
|
12585
12776
|
var DEVPOD_MUTATION_WAIT_MS = 18e5;
|
|
12586
12777
|
var DevpodStartPostconditionError = class extends Error {
|
|
12587
12778
|
};
|
|
12588
12779
|
function withMutationLock2(activity, target, operation) {
|
|
12589
|
-
|
|
12780
|
+
import_node_fs32.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
12590
12781
|
return withFileLockSync(
|
|
12591
12782
|
DEVPOD_MUTATION_LOCK_FILE,
|
|
12592
12783
|
{
|
|
@@ -12788,7 +12979,7 @@ async function startDevpodWorkspace(options) {
|
|
|
12788
12979
|
|
|
12789
12980
|
// src/core/host-port-claims.ts
|
|
12790
12981
|
var import_node_child_process23 = require("child_process");
|
|
12791
|
-
var
|
|
12982
|
+
var import_node_path38 = __toESM(require("path"));
|
|
12792
12983
|
var COMPOSE_RENDER_TIMEOUT_MS = 3e4;
|
|
12793
12984
|
var COMPOSE_RENDER_MAX_BUFFER = 8 * 1024 * 1024;
|
|
12794
12985
|
var DOCKER_TIMEOUT_MS = 1e4;
|
|
@@ -12996,8 +13187,8 @@ function renderManagedComposeModel(plan, workspace) {
|
|
|
12996
13187
|
}
|
|
12997
13188
|
}
|
|
12998
13189
|
function attributeHolderWorktree(workingDir) {
|
|
12999
|
-
if (
|
|
13000
|
-
return comparableWorkspacePath(
|
|
13190
|
+
if (import_node_path38.default.basename(workingDir) !== ".devcontainer") return void 0;
|
|
13191
|
+
return comparableWorkspacePath(import_node_path38.default.dirname(workingDir));
|
|
13001
13192
|
}
|
|
13002
13193
|
function holderAttribution(holder, repoPath) {
|
|
13003
13194
|
const attribution = {};
|
|
@@ -13032,7 +13223,7 @@ function detectHostPortClaimConflicts(options) {
|
|
|
13032
13223
|
);
|
|
13033
13224
|
if (desired.length === 0) return [];
|
|
13034
13225
|
const holders = listHolderSnapshots();
|
|
13035
|
-
const devcontainerDir =
|
|
13226
|
+
const devcontainerDir = import_node_path38.default.join(options.repoPath, ".devcontainer");
|
|
13036
13227
|
const conflicts = [];
|
|
13037
13228
|
for (const binding of desired) {
|
|
13038
13229
|
for (const holder of holders) {
|
|
@@ -13193,7 +13384,7 @@ function transportFailure() {
|
|
|
13193
13384
|
|
|
13194
13385
|
// src/core/managed-host-preparation.ts
|
|
13195
13386
|
var import_node_child_process25 = require("child_process");
|
|
13196
|
-
var
|
|
13387
|
+
var import_node_fs33 = __toESM(require("fs"));
|
|
13197
13388
|
var PREPARATION_TIMEOUT_MS = 6e4;
|
|
13198
13389
|
var CONFIG_READ_ERROR = "Managed host preparation could not read .devrouter.yml.";
|
|
13199
13390
|
var CONFIG_CHANGED_ERROR = "Managed host preparation changed .devrouter.yml; refusing to continue.";
|
|
@@ -13236,7 +13427,7 @@ function runPreparationCommand(argv, cwd) {
|
|
|
13236
13427
|
}
|
|
13237
13428
|
function readConfigBytes(configPath) {
|
|
13238
13429
|
try {
|
|
13239
|
-
return
|
|
13430
|
+
return import_node_fs33.default.readFileSync(configPath);
|
|
13240
13431
|
} catch {
|
|
13241
13432
|
return void 0;
|
|
13242
13433
|
}
|
|
@@ -13269,6 +13460,43 @@ async function runManagedHostPreparation(repoPath, config) {
|
|
|
13269
13460
|
}
|
|
13270
13461
|
}
|
|
13271
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
|
+
|
|
13272
13500
|
// src/core/managed-mount-nesting.ts
|
|
13273
13501
|
var import_node_child_process26 = require("child_process");
|
|
13274
13502
|
var NESTED_MOUNT_REASON = "no configured mount is nested inside another configured mount";
|
|
@@ -13421,6 +13649,8 @@ function readReliabilityStatus(options) {
|
|
|
13421
13649
|
return void 0;
|
|
13422
13650
|
}
|
|
13423
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;
|
|
13424
13654
|
const reason2 = reliabilityAttention(record5.state) ?? (record5.state.desired === "running" && refusedManagedStart(record5.result) ? "start-refused" : void 0);
|
|
13425
13655
|
return {
|
|
13426
13656
|
desired: record5.state.desired,
|
|
@@ -13431,6 +13661,14 @@ function readReliabilityStatus(options) {
|
|
|
13431
13661
|
correctiveActionsTaken: record5.state.incident.correctiveActionsTaken,
|
|
13432
13662
|
actionLimit: record5.state.incident.actionLimit
|
|
13433
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
|
+
} : {},
|
|
13434
13672
|
...reason2 ? {
|
|
13435
13673
|
attention: {
|
|
13436
13674
|
reason: reason2,
|
|
@@ -14058,33 +14296,33 @@ var import_node_child_process28 = require("child_process");
|
|
|
14058
14296
|
var import_node_net3 = require("net");
|
|
14059
14297
|
|
|
14060
14298
|
// src/core/network-local-daemon.ts
|
|
14061
|
-
var
|
|
14062
|
-
var
|
|
14299
|
+
var import_node_fs34 = __toESM(require("fs"));
|
|
14300
|
+
var import_node_path39 = __toESM(require("path"));
|
|
14063
14301
|
function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
14064
14302
|
const deadline = Date.now() + 2e3;
|
|
14065
14303
|
try {
|
|
14066
14304
|
if (!endpoint.startsWith("unix:///")) return false;
|
|
14067
14305
|
const socket = endpoint.slice("unix://".length);
|
|
14068
|
-
const table =
|
|
14306
|
+
const table = import_node_fs34.default.readFileSync(import_node_path39.default.join(procRoot, "net/unix"), "utf8");
|
|
14069
14307
|
if (Buffer.byteLength(table) > 1024 * 1024) return false;
|
|
14070
14308
|
const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
|
|
14071
14309
|
if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
|
|
14072
14310
|
const inode = `socket:[${rows2[0][6]}]`;
|
|
14073
|
-
const ownNamespace =
|
|
14074
|
-
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));
|
|
14075
14313
|
if (pids.length > 4096) return false;
|
|
14076
14314
|
for (const pid of pids) {
|
|
14077
14315
|
if (Date.now() > deadline) return false;
|
|
14078
14316
|
try {
|
|
14079
|
-
const root =
|
|
14080
|
-
if (
|
|
14081
|
-
if (
|
|
14082
|
-
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"));
|
|
14083
14321
|
if (descriptors.length > 4096) continue;
|
|
14084
14322
|
if (descriptors.some((fd) => {
|
|
14085
14323
|
if (Date.now() > deadline) return false;
|
|
14086
14324
|
try {
|
|
14087
|
-
return
|
|
14325
|
+
return import_node_fs34.default.readlinkSync(import_node_path39.default.join(root, "fd", fd)) === inode;
|
|
14088
14326
|
} catch {
|
|
14089
14327
|
return false;
|
|
14090
14328
|
}
|
|
@@ -14159,10 +14397,10 @@ function inspectLegacyNetworkCapacity(input) {
|
|
|
14159
14397
|
}
|
|
14160
14398
|
|
|
14161
14399
|
// src/core/network-managed.ts
|
|
14162
|
-
var
|
|
14400
|
+
var import_node_crypto23 = require("crypto");
|
|
14163
14401
|
|
|
14164
14402
|
// src/core/network-compose.ts
|
|
14165
|
-
var
|
|
14403
|
+
var import_node_crypto21 = require("crypto");
|
|
14166
14404
|
var SAFE_ENDPOINT_UPPER_BOUND_SEMANTICS = "full-lifecycle-demand";
|
|
14167
14405
|
function isRecord5(value) {
|
|
14168
14406
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -14968,14 +15206,14 @@ function stableValue(value) {
|
|
|
14968
15206
|
}
|
|
14969
15207
|
function fingerprintComposeNetworkModel(input) {
|
|
14970
15208
|
const projection = normalizedFingerprintProjection(input);
|
|
14971
|
-
return (0,
|
|
15209
|
+
return (0, import_node_crypto21.createHash)("sha256").update(stableValue(projection), "utf8").digest("hex");
|
|
14972
15210
|
}
|
|
14973
15211
|
|
|
14974
15212
|
// src/core/network-compose-files.ts
|
|
14975
15213
|
var import_node_child_process29 = require("child_process");
|
|
14976
|
-
var
|
|
14977
|
-
var
|
|
14978
|
-
var
|
|
15214
|
+
var import_node_crypto22 = require("crypto");
|
|
15215
|
+
var import_node_fs35 = __toESM(require("fs"));
|
|
15216
|
+
var import_node_path40 = __toESM(require("path"));
|
|
14979
15217
|
var import_yaml4 = __toESM(require("yaml"));
|
|
14980
15218
|
var OVERLAY = "docker-compose.devrouter-network.yml";
|
|
14981
15219
|
var MARKER = "# devrouter:managed workspace network";
|
|
@@ -14988,12 +15226,12 @@ function object3(value) {
|
|
|
14988
15226
|
function inspectNetworkComposeFiles(input) {
|
|
14989
15227
|
const authoredNetworks = {};
|
|
14990
15228
|
for (const file of input.plan.composeFiles) {
|
|
14991
|
-
const stat =
|
|
15229
|
+
const stat = import_node_fs35.default.lstatSync(file);
|
|
14992
15230
|
if (!stat.isFile() || stat.size > MAX_BYTES2)
|
|
14993
15231
|
throw new Error("Network Compose source is unavailable or too large.");
|
|
14994
15232
|
let source;
|
|
14995
15233
|
try {
|
|
14996
|
-
source = object3(import_yaml4.default.parse(
|
|
15234
|
+
source = object3(import_yaml4.default.parse(import_node_fs35.default.readFileSync(file, "utf8")));
|
|
14997
15235
|
} catch {
|
|
14998
15236
|
throw new Error("Network Compose source could not be parsed safely.");
|
|
14999
15237
|
}
|
|
@@ -15043,7 +15281,7 @@ function inspectNetworkComposeFiles(input) {
|
|
|
15043
15281
|
function prepareNetworkComposeFiles(plan, subnet) {
|
|
15044
15282
|
const overlay = deriveComposeNetworkOverlay(subnet);
|
|
15045
15283
|
if (overlay.status !== "ready" || !overlay.overlay) throw new Error("Network subnet is invalid.");
|
|
15046
|
-
const overlayPath =
|
|
15284
|
+
const overlayPath = import_node_path40.default.join(plan.composeDirectory, OVERLAY);
|
|
15047
15285
|
const ignored = (0, import_node_child_process29.spawnSync)("git", [
|
|
15048
15286
|
"-C",
|
|
15049
15287
|
plan.composeDirectory,
|
|
@@ -15058,9 +15296,9 @@ function prepareNetworkComposeFiles(plan, subnet) {
|
|
|
15058
15296
|
const bytes2 = `${MARKER}
|
|
15059
15297
|
${import_yaml4.default.stringify(overlay.overlay)}`;
|
|
15060
15298
|
const assertOwnership = () => {
|
|
15061
|
-
if (!
|
|
15062
|
-
const stat =
|
|
15063
|
-
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)
|
|
15064
15302
|
throw new Error("Retained network overlay changed; repair is required.");
|
|
15065
15303
|
};
|
|
15066
15304
|
assertOwnership();
|
|
@@ -15074,7 +15312,7 @@ ${JSON.stringify(extended.config, null, 2)}
|
|
|
15074
15312
|
...plan,
|
|
15075
15313
|
contents,
|
|
15076
15314
|
composeFiles: [...plan.composeFiles, overlayPath],
|
|
15077
|
-
effectiveConfigSha256: (0,
|
|
15315
|
+
effectiveConfigSha256: (0, import_node_crypto22.createHash)("sha256").update(contents).digest("hex")
|
|
15078
15316
|
};
|
|
15079
15317
|
return {
|
|
15080
15318
|
plan: result,
|
|
@@ -15392,7 +15630,7 @@ function createManagedNetworkSession(input) {
|
|
|
15392
15630
|
"Insufficient endpoint headroom; request an allowed larger network before first allocation."
|
|
15393
15631
|
);
|
|
15394
15632
|
const authority = readNetworkOperationAuthority(identity2);
|
|
15395
|
-
const fingerprint = (0,
|
|
15633
|
+
const fingerprint = (0, import_node_crypto23.createHash)("sha256").update(input.plan().sourceConfigSha256).update(fingerprintComposeNetworkModel(model)).digest("hex");
|
|
15396
15634
|
if (!saved) {
|
|
15397
15635
|
if (policy.status !== "valid")
|
|
15398
15636
|
throw new Error("New network allocation requires valid machine policy.");
|
|
@@ -15576,11 +15814,11 @@ var DEFAULT_READINESS_TIMEOUT_MS = 12e4;
|
|
|
15576
15814
|
var POLL_INTERVAL_MS = 1e3;
|
|
15577
15815
|
function assertOverlay(container, repoPath) {
|
|
15578
15816
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
15579
|
-
if (!workingDir || !sameWorkspacePath(workingDir,
|
|
15817
|
+
if (!workingDir || !sameWorkspacePath(workingDir, import_node_path41.default.join(repoPath, ".devcontainer"))) {
|
|
15580
15818
|
throw new Error(`Container '${container.id}' does not belong to the exact worktree.`);
|
|
15581
15819
|
}
|
|
15582
15820
|
const configFiles = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").filter(Boolean);
|
|
15583
|
-
const expectedOverlay =
|
|
15821
|
+
const expectedOverlay = import_node_path41.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
15584
15822
|
if (!configFiles.some((configFile) => sameWorkspacePath(configFile, expectedOverlay))) {
|
|
15585
15823
|
throw new Error(`Container '${container.id}' was not started with ${DEVCONTAINER_OVERLAY}.`);
|
|
15586
15824
|
}
|
|
@@ -15594,7 +15832,7 @@ function assertWorktreeOwnership(container, repoPath) {
|
|
|
15594
15832
|
function isInsideWorktree(candidate, repoPath) {
|
|
15595
15833
|
const normalizedCandidate = comparableWorkspacePath(candidate);
|
|
15596
15834
|
const normalizedRoot = comparableWorkspacePath(repoPath);
|
|
15597
|
-
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}${
|
|
15835
|
+
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}${import_node_path41.default.sep}`);
|
|
15598
15836
|
}
|
|
15599
15837
|
function assertReady(container, label) {
|
|
15600
15838
|
if (!container.state.Running) {
|
|
@@ -15786,7 +16024,7 @@ function hasExactManagedComposeProject(repoPath, state) {
|
|
|
15786
16024
|
}).some((container) => {
|
|
15787
16025
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
15788
16026
|
return Boolean(
|
|
15789
|
-
workingDir && sameWorkspacePath(workingDir,
|
|
16027
|
+
workingDir && sameWorkspacePath(workingDir, import_node_path41.default.join(repoPath, ".devcontainer"))
|
|
15790
16028
|
);
|
|
15791
16029
|
});
|
|
15792
16030
|
} catch {
|
|
@@ -16191,7 +16429,7 @@ function resolvePrimaryTarget(repoPath) {
|
|
|
16191
16429
|
}
|
|
16192
16430
|
function isPrimaryCheckout(repoPath) {
|
|
16193
16431
|
try {
|
|
16194
|
-
return
|
|
16432
|
+
return import_node_fs36.default.statSync(import_node_path41.default.join(repoPath, ".git")).isDirectory();
|
|
16195
16433
|
} catch {
|
|
16196
16434
|
return false;
|
|
16197
16435
|
}
|
|
@@ -16255,8 +16493,8 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16255
16493
|
const target = options.repair ? resolveRepairTarget(repoPath, linked) : linked ? resolveLinkedTarget(repoPath) : resolvePrimaryTarget(repoPath);
|
|
16256
16494
|
let devpodId = target.devpodId;
|
|
16257
16495
|
if (target.kind === "linked") {
|
|
16258
|
-
const overlayPath =
|
|
16259
|
-
if (!
|
|
16496
|
+
const overlayPath = import_node_path41.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
16497
|
+
if (!import_node_fs36.default.existsSync(overlayPath)) {
|
|
16260
16498
|
throw new Error(`Missing required DevPod compose overlay: ${overlayPath}`);
|
|
16261
16499
|
}
|
|
16262
16500
|
}
|
|
@@ -16276,6 +16514,9 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16276
16514
|
if (variant !== "default" && managedPostStart.kind !== "runtime")
|
|
16277
16515
|
throw new Error("Runtime variants require a managed runtime post-start adapter.");
|
|
16278
16516
|
const managedRuntime = runtime.config.managedRuntime;
|
|
16517
|
+
const hostPortDeclarationsSha256 = managedHostPortDeclarationsIdentity(
|
|
16518
|
+
managedRuntime?.devcontainer.hostPorts ?? {}
|
|
16519
|
+
);
|
|
16279
16520
|
if (options.repair && !managedRuntime)
|
|
16280
16521
|
throw new Error("Repair requires managedRuntime configuration.");
|
|
16281
16522
|
previousRoutes = listHostRouteState().filter(
|
|
@@ -16363,7 +16604,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16363
16604
|
}
|
|
16364
16605
|
const apps = proxyAppsFromConfig(runtime.config);
|
|
16365
16606
|
const parsedUpstreams = apps.map((app) => parseUpstream(app.upstream));
|
|
16366
|
-
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";
|
|
16367
16608
|
for (const [index, app] of apps.entries()) {
|
|
16368
16609
|
if (!parsedUpstreams[index].host.startsWith(`${aliasPrefix}-`)) {
|
|
16369
16610
|
const owner = target.kind === "linked" ? "workspace" : "checkout";
|
|
@@ -16600,9 +16841,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16600
16841
|
composeProject: captureProject,
|
|
16601
16842
|
profile: runtime.profile,
|
|
16602
16843
|
variant,
|
|
16603
|
-
hostPortDeclarationsSha256:
|
|
16604
|
-
managedRuntime?.devcontainer.hostPorts ?? {}
|
|
16605
|
-
),
|
|
16844
|
+
...hostPortDeclarationsSha256 !== void 0 ? { hostPortDeclarationsSha256 } : {},
|
|
16606
16845
|
desired: {
|
|
16607
16846
|
apps: apps.map((app) => app.name).sort(),
|
|
16608
16847
|
services: [...capturePlan.desiredProfileServices].sort(),
|
|
@@ -16813,9 +17052,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
16813
17052
|
composeProject: managedComposeProject,
|
|
16814
17053
|
profile: runtime.profile,
|
|
16815
17054
|
variant,
|
|
16816
|
-
hostPortDeclarationsSha256:
|
|
16817
|
-
managedRuntime?.devcontainer.hostPorts ?? {}
|
|
16818
|
-
),
|
|
17055
|
+
...hostPortDeclarationsSha256 !== void 0 ? { hostPortDeclarationsSha256 } : {},
|
|
16819
17056
|
desired: {
|
|
16820
17057
|
apps: apps.map((app) => app.name).sort(),
|
|
16821
17058
|
services: [...managedPlan.desiredProfileServices].sort(),
|
|
@@ -17252,9 +17489,9 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
17252
17489
|
}
|
|
17253
17490
|
const repairLocked = () => {
|
|
17254
17491
|
const runtime = resolveWorkspaceRuntimeOrDefault(repoPath);
|
|
17255
|
-
|
|
17492
|
+
import_node_fs36.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
17256
17493
|
return withFileLock(
|
|
17257
|
-
|
|
17494
|
+
import_node_path41.default.join(DEVROUTER_HOME, `${runtime}-mutation.lock`),
|
|
17258
17495
|
{
|
|
17259
17496
|
activity: "Managed runtime repair",
|
|
17260
17497
|
target: repoPath,
|
|
@@ -17432,7 +17669,7 @@ function preflightRuntimeVariant(repoPath, options) {
|
|
|
17432
17669
|
return variant;
|
|
17433
17670
|
}
|
|
17434
17671
|
function capacityPolicyTargets(repoPath) {
|
|
17435
|
-
const policy = readCapacityPolicy(
|
|
17672
|
+
const policy = readCapacityPolicy(import_node_path42.default.join(DEVROUTER_HOME, "controller"));
|
|
17436
17673
|
return policy?.admissions === "enabled" && policy.enrollments.some((entry) => entry.repoPath === repoPath);
|
|
17437
17674
|
}
|
|
17438
17675
|
function clearActiveLifecycleStartupWitness() {
|
|
@@ -17538,7 +17775,7 @@ async function executeLifecycleWorker(request, operation) {
|
|
|
17538
17775
|
const owned = containers.filter(
|
|
17539
17776
|
(container) => sameWorkspacePath(
|
|
17540
17777
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
17541
|
-
|
|
17778
|
+
import_node_path42.default.join(request.repoPath, ".devcontainer")
|
|
17542
17779
|
)
|
|
17543
17780
|
);
|
|
17544
17781
|
const projects = owned.map((container) => container.labels["com.docker.compose.project"]);
|
|
@@ -17625,7 +17862,7 @@ function proveLifecycleStopped() {
|
|
|
17625
17862
|
throw new Error("Full stop proof requires an explicit stop worker.");
|
|
17626
17863
|
const settle = () => {
|
|
17627
17864
|
claimLifecycleEffect();
|
|
17628
|
-
if (initialStopAbsence && !(0,
|
|
17865
|
+
if (initialStopAbsence && !(0, import_node_util8.isDeepStrictEqual)(
|
|
17629
17866
|
proveInitialManagedDevsyAbsence(request.repoPath, initialStopAbsence.record.devpodId),
|
|
17630
17867
|
initialStopAbsence
|
|
17631
17868
|
))
|
|
@@ -17645,7 +17882,7 @@ function proveLifecycleStopped() {
|
|
|
17645
17882
|
const remaining = inspectWorkspaceContainers().filter(
|
|
17646
17883
|
(container) => sameWorkspacePath(
|
|
17647
17884
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
17648
|
-
|
|
17885
|
+
import_node_path42.default.join(request.repoPath, ".devcontainer")
|
|
17649
17886
|
)
|
|
17650
17887
|
);
|
|
17651
17888
|
if (remaining.some(
|
|
@@ -17675,7 +17912,7 @@ function proveLifecycleStopped() {
|
|
|
17675
17912
|
return void 0;
|
|
17676
17913
|
});
|
|
17677
17914
|
if (settlement) {
|
|
17678
|
-
const capacity = createLifecycleCapacityStore(
|
|
17915
|
+
const capacity = createLifecycleCapacityStore(import_node_path42.default.join(DEVROUTER_HOME, "controller"));
|
|
17679
17916
|
for (let attempt = 0; ; attempt++) {
|
|
17680
17917
|
try {
|
|
17681
17918
|
const state = capacity.inspect();
|
|
@@ -17849,7 +18086,7 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
17849
18086
|
}
|
|
17850
18087
|
assertRunningDevpod(devpod.id, repoPath);
|
|
17851
18088
|
const workspaceDirectory = resolveWorkspaceDirectory(repoPath);
|
|
17852
|
-
const statusMarker = `__DEVROUTER_EXIT_${(0,
|
|
18089
|
+
const statusMarker = `__DEVROUTER_EXIT_${(0, import_node_crypto25.randomUUID)()}__:`;
|
|
17853
18090
|
const statusMarkerBytes = Buffer.from(statusMarker, "ascii");
|
|
17854
18091
|
const literalCommand = command.map(quotePosixArg).join(" ");
|
|
17855
18092
|
const wrappedCommand = `${literalCommand}; __devrouter_status=$?; printf '${statusMarker}%s\\n' "$__devrouter_status" >&2; exit 0`;
|
|
@@ -18039,11 +18276,11 @@ async function devpodExecOutcome(repoPath, command) {
|
|
|
18039
18276
|
|
|
18040
18277
|
// src/core/workspace-lifecycle.ts
|
|
18041
18278
|
var import_node_child_process34 = require("child_process");
|
|
18042
|
-
var
|
|
18043
|
-
var
|
|
18279
|
+
var import_node_fs38 = __toESM(require("fs"));
|
|
18280
|
+
var import_node_path43 = __toESM(require("path"));
|
|
18044
18281
|
|
|
18045
18282
|
// src/core/route-state.ts
|
|
18046
|
-
var
|
|
18283
|
+
var import_node_fs37 = __toESM(require("fs"));
|
|
18047
18284
|
function listRoutesForWorktreePath(worktreePath, routes = listHostRouteState()) {
|
|
18048
18285
|
return routes.filter((route) => sameWorkspacePath(route.repoPath, worktreePath));
|
|
18049
18286
|
}
|