@devrouter/cli 0.0.74 → 0.0.76
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 +5515 -5436
- package/dist/devrouter.js +186 -19
- package/package.json +1 -1
- package/upgrade-prompts/0.0.75.md +35 -0
- package/upgrade-prompts/0.0.76.md +33 -0
package/dist/devrouter.js
CHANGED
|
@@ -4256,7 +4256,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs7.default.read
|
|
|
4256
4256
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
4257
4257
|
const requiredVersion = config.devrouter?.version;
|
|
4258
4258
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
4259
|
-
const cliVersion = true ? "0.0.
|
|
4259
|
+
const cliVersion = true ? "0.0.76" : "0.0.0-dev";
|
|
4260
4260
|
if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
|
|
4261
4261
|
hasWarnedVersionMismatch = true;
|
|
4262
4262
|
process.stderr.write(
|
|
@@ -5509,7 +5509,7 @@ var init_reliability_contract = __esm({
|
|
|
5509
5509
|
|
|
5510
5510
|
// src/core/reliability-operation-store.ts
|
|
5511
5511
|
function installedCliVersion() {
|
|
5512
|
-
return true ? "0.0.
|
|
5512
|
+
return true ? "0.0.76" : "0.0.0-dev";
|
|
5513
5513
|
}
|
|
5514
5514
|
function compareCliVersions(left, right) {
|
|
5515
5515
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -8413,6 +8413,12 @@ function isWildcard(values) {
|
|
|
8413
8413
|
function sha256(contents) {
|
|
8414
8414
|
return (0, import_node_crypto11.createHash)("sha256").update(contents, "utf-8").digest("hex");
|
|
8415
8415
|
}
|
|
8416
|
+
function safeComposeProject(value) {
|
|
8417
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(value)) {
|
|
8418
|
+
throw new Error(`Docker Compose project '${value}' is not a safe exact identifier.`);
|
|
8419
|
+
}
|
|
8420
|
+
return value;
|
|
8421
|
+
}
|
|
8416
8422
|
function inspectManagedDevcontainerConfig(options) {
|
|
8417
8423
|
const managedRuntime = options.config.managedRuntime;
|
|
8418
8424
|
if (!managedRuntime) {
|
|
@@ -8531,6 +8537,91 @@ function managedComposeEnvironment(workspace) {
|
|
|
8531
8537
|
}
|
|
8532
8538
|
return env;
|
|
8533
8539
|
}
|
|
8540
|
+
function assertReadableComposeFile(file) {
|
|
8541
|
+
try {
|
|
8542
|
+
if (!import_node_fs18.default.statSync(file).isFile()) throw new Error("not a regular file");
|
|
8543
|
+
import_node_fs18.default.accessSync(file, import_node_fs18.default.constants.R_OK);
|
|
8544
|
+
} catch {
|
|
8545
|
+
throw new Error("Managed container Compose configuration contains an unreadable file.");
|
|
8546
|
+
}
|
|
8547
|
+
}
|
|
8548
|
+
function assertManagedContainerConfigUnchanged(options) {
|
|
8549
|
+
if (options.containers.length === 0) {
|
|
8550
|
+
throw new Error("Managed container configuration cannot be verified without containers.");
|
|
8551
|
+
}
|
|
8552
|
+
for (const container of options.containers) {
|
|
8553
|
+
const project = container.labels["com.docker.compose.project"];
|
|
8554
|
+
const service = container.labels["com.docker.compose.service"];
|
|
8555
|
+
const workingDirectory = container.labels["com.docker.compose.project.working_dir"];
|
|
8556
|
+
const recordedConfigFiles = container.labels["com.docker.compose.project.config_files"];
|
|
8557
|
+
const expectedHash = container.labels["com.docker.compose.config-hash"];
|
|
8558
|
+
if (!project || !service || !workingDirectory || !recordedConfigFiles || !expectedHash || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(service) || !/^[a-f0-9]{64}$/.test(expectedHash)) {
|
|
8559
|
+
throw new Error("Managed container is missing valid Compose identity and hash labels.");
|
|
8560
|
+
}
|
|
8561
|
+
const composeProject = safeComposeProject(project);
|
|
8562
|
+
const configFiles2 = recordedConfigFiles.split(",").map((file) => file.trim());
|
|
8563
|
+
if (configFiles2.some((file) => file.length === 0)) {
|
|
8564
|
+
throw new Error("Managed container has an invalid recorded Compose file list.");
|
|
8565
|
+
}
|
|
8566
|
+
if (options.plan.composeFiles.some(
|
|
8567
|
+
(expected) => !configFiles2.some((actual) => sameWorkspacePath(actual, expected))
|
|
8568
|
+
)) {
|
|
8569
|
+
throw new Error("Managed container Compose files do not include the managed plan files.");
|
|
8570
|
+
}
|
|
8571
|
+
for (const file of configFiles2) assertReadableComposeFile(file);
|
|
8572
|
+
const fileArgs = configFiles2.flatMap((file) => ["-f", file]);
|
|
8573
|
+
const result = (() => {
|
|
8574
|
+
try {
|
|
8575
|
+
const composeArgs = [
|
|
8576
|
+
"compose",
|
|
8577
|
+
"--project-name",
|
|
8578
|
+
composeProject,
|
|
8579
|
+
"--project-directory",
|
|
8580
|
+
workingDirectory
|
|
8581
|
+
];
|
|
8582
|
+
const env = managedComposeEnvironment(options.workspace);
|
|
8583
|
+
const rendered = (0, import_node_child_process8.spawnSync)(
|
|
8584
|
+
"docker",
|
|
8585
|
+
[...composeArgs, ...fileArgs, "config", "--format", "json"],
|
|
8586
|
+
{
|
|
8587
|
+
cwd: workingDirectory,
|
|
8588
|
+
encoding: "utf-8",
|
|
8589
|
+
env,
|
|
8590
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
8591
|
+
timeout: COMPOSE_HASH_TIMEOUT_MS,
|
|
8592
|
+
maxBuffer: 1024 * 1024
|
|
8593
|
+
}
|
|
8594
|
+
);
|
|
8595
|
+
if (rendered.status !== 0 || rendered.error || !rendered.stdout?.trim()) {
|
|
8596
|
+
throw new Error("Compose configuration rendering failed.");
|
|
8597
|
+
}
|
|
8598
|
+
return (0, import_node_child_process8.spawnSync)(
|
|
8599
|
+
"docker",
|
|
8600
|
+
[...composeArgs, "-f", "-", "config", "--no-interpolate", "--hash", service],
|
|
8601
|
+
{
|
|
8602
|
+
cwd: workingDirectory,
|
|
8603
|
+
encoding: "utf-8",
|
|
8604
|
+
env,
|
|
8605
|
+
input: rendered.stdout,
|
|
8606
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
8607
|
+
timeout: COMPOSE_HASH_TIMEOUT_MS,
|
|
8608
|
+
maxBuffer: 1024 * 1024
|
|
8609
|
+
}
|
|
8610
|
+
);
|
|
8611
|
+
} catch {
|
|
8612
|
+
throw new Error(`Could not verify managed Compose configuration for service '${service}'.`);
|
|
8613
|
+
}
|
|
8614
|
+
})();
|
|
8615
|
+
if (result.status !== 0 || result.error) {
|
|
8616
|
+
throw new Error(`Could not verify managed Compose configuration for service '${service}'.`);
|
|
8617
|
+
}
|
|
8618
|
+
const output2 = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
8619
|
+
const match = /^(\S+)\s+([a-f0-9]{64})$/.exec(output2);
|
|
8620
|
+
if (!match || match[1] !== service || match[2] !== expectedHash) {
|
|
8621
|
+
throw new Error(`Managed Compose configuration changed for service '${service}'.`);
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8534
8625
|
function stopExactManagedService(containerId, service, options) {
|
|
8535
8626
|
assertSafeContainerId(containerId);
|
|
8536
8627
|
const result = (0, import_node_child_process8.spawnSync)("docker", ["stop", containerId], {
|
|
@@ -8543,7 +8634,7 @@ function stopExactManagedService(containerId, service, options) {
|
|
|
8543
8634
|
throw new Error(`Could not stop exact managed service '${service}' (${containerId}).`);
|
|
8544
8635
|
}
|
|
8545
8636
|
}
|
|
8546
|
-
var import_node_child_process8, import_node_crypto11, import_node_fs18, import_node_path25, import_yaml3, MANAGED_DEVCONTAINER_PATH, MANAGED_DEVCONTAINER_MARKER;
|
|
8637
|
+
var import_node_child_process8, import_node_crypto11, import_node_fs18, import_node_path25, import_yaml3, MANAGED_DEVCONTAINER_PATH, MANAGED_DEVCONTAINER_MARKER, COMPOSE_HASH_TIMEOUT_MS;
|
|
8547
8638
|
var init_devcontainer_profile = __esm({
|
|
8548
8639
|
"src/core/devcontainer-profile.ts"() {
|
|
8549
8640
|
"use strict";
|
|
@@ -8559,6 +8650,7 @@ var init_devcontainer_profile = __esm({
|
|
|
8559
8650
|
init_workspace();
|
|
8560
8651
|
MANAGED_DEVCONTAINER_PATH = ".devcontainer/devcontainer.devrouter.json";
|
|
8561
8652
|
MANAGED_DEVCONTAINER_MARKER = "// devrouter:managed devcontainer profile";
|
|
8653
|
+
COMPOSE_HASH_TIMEOUT_MS = 1e4;
|
|
8562
8654
|
}
|
|
8563
8655
|
});
|
|
8564
8656
|
|
|
@@ -10529,8 +10621,30 @@ function proveInitialManagedDevsyAbsence(repoPath, expectedId) {
|
|
|
10529
10621
|
}
|
|
10530
10622
|
function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
10531
10623
|
if (!import_node_fs25.default.existsSync(import_node_path33.default.join(repoPath, ".devrouter.yml"))) return false;
|
|
10624
|
+
if (!loadRepoConfig(repoPath).managedRuntime) return false;
|
|
10532
10625
|
const linked = isLinkedWorktree(repoPath);
|
|
10533
10626
|
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
10627
|
+
const identity2 = { repoPath, workspace: workspace ?? null, provider: "devsy" };
|
|
10628
|
+
const readAuthority = () => {
|
|
10629
|
+
const record5 = readReliabilityOperation(identity2);
|
|
10630
|
+
const operation = record5?.state.operation;
|
|
10631
|
+
const entries = record5?.state.operationHistory.filter((entry2) => entry2.id === operation?.id);
|
|
10632
|
+
const entry = entries?.[0];
|
|
10633
|
+
if (!record5 || !(0, import_node_util9.isDeepStrictEqual)(record5.identity, identity2) || record5.worker || !operation || operation.kind !== "ensure" || !operation.drained || !["COMPLETED", "INTERRUPTED", "COMPLETION_UNKNOWN"].includes(operation.status) || record5.state.phase !== "stopping" || record5.state.desired !== "stopped-by-user" || entries?.length !== 1 || !entry || entry.kind !== "ensure" || !entry.profile || !entry.drained || entry.status !== operation.status)
|
|
10634
|
+
throw new Error("Initial managed stop requires the drained ensure's recorded profile.");
|
|
10635
|
+
return {
|
|
10636
|
+
identity: record5.identity,
|
|
10637
|
+
fence: reliabilityFence(record5.state),
|
|
10638
|
+
operation,
|
|
10639
|
+
entry
|
|
10640
|
+
};
|
|
10641
|
+
};
|
|
10642
|
+
const authority = readAuthority();
|
|
10643
|
+
const gitCommonDir = linked ? resolveGitCommonDir(repoPath) : void 0;
|
|
10644
|
+
const workspaceEnv = workspace && gitCommonDir ? { token: workspace, gitCommonDir } : void 0;
|
|
10645
|
+
function canonicalProfile(recorded) {
|
|
10646
|
+
return resolveProfile(loadRepoConfig(repoPath), recorded).name;
|
|
10647
|
+
}
|
|
10534
10648
|
const registration2 = () => {
|
|
10535
10649
|
resetWorkspaceRuntimeCaches();
|
|
10536
10650
|
if (resolveWorkspaceRuntimeOrDefault(repoPath) !== "devsy")
|
|
@@ -10540,14 +10654,21 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10540
10654
|
throw new Error("Initial managed stop requires one exact Devsy registration.");
|
|
10541
10655
|
if (linked) {
|
|
10542
10656
|
const record5 = workspace ? readWorkspaceOwnership(repoPath, workspace) : void 0;
|
|
10543
|
-
if (!record5 || record5.devpodId !== devsyId || !sameWorkspacePath(record5.worktreePath, repoPath) || resolveWorktreeWorkspace(repoPath) !== workspace)
|
|
10657
|
+
if (!record5 || record5.devpodId !== devsyId || !sameWorkspacePath(record5.worktreePath, repoPath) || resolveWorktreeWorkspace(repoPath) !== workspace || resolveGitCommonDir(repoPath) !== gitCommonDir || inspectWorkspaceOwnership(record5, listGitWorktrees(repoPath), void 0).ownerStatus !== "present")
|
|
10544
10658
|
throw new Error("Initial managed stop workspace ownership changed.");
|
|
10545
10659
|
}
|
|
10546
10660
|
return owner2.workspace;
|
|
10547
10661
|
};
|
|
10548
|
-
const
|
|
10549
|
-
|
|
10662
|
+
const canonicalName = canonicalProfile(authority.entry.profile);
|
|
10663
|
+
const runtime = loadRuntimeConfig(repoPath, workspace ?? "", canonicalName);
|
|
10664
|
+
if (!runtime.config.managedRuntime || runtime.profile !== canonicalName)
|
|
10665
|
+
throw new Error("Initial managed stop requires the recorded managed profile.");
|
|
10550
10666
|
const owner = registration2();
|
|
10667
|
+
proveLocalDockerSelection(owner);
|
|
10668
|
+
const endpoint = resolveManagedStopEndpoint();
|
|
10669
|
+
if (!supportsManagedStopBaseline(endpoint))
|
|
10670
|
+
throw new Error("Initial managed stop requires a pinned local Docker endpoint.");
|
|
10671
|
+
const daemon = inspectManagedStopDaemon(endpoint);
|
|
10551
10672
|
if (!owner.context || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(owner.context) || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(devsyId))
|
|
10552
10673
|
throw new Error("Initial managed stop requires exact provider context.");
|
|
10553
10674
|
const plan = inspectManagedDevcontainerConfig({
|
|
@@ -10567,27 +10688,53 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10567
10688
|
".docker-compose"
|
|
10568
10689
|
);
|
|
10569
10690
|
const observe = () => {
|
|
10570
|
-
if (!(0, import_node_util9.isDeepStrictEqual)(registration2(), owner) || readManagedRuntimeState(repoPath, workspace))
|
|
10571
|
-
throw new Error("Initial managed stop
|
|
10691
|
+
if (!(0, import_node_util9.isDeepStrictEqual)(readAuthority(), authority) || !(0, import_node_util9.isDeepStrictEqual)(registration2(), owner) || readManagedRuntimeState(repoPath, workspace) || resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
10692
|
+
throw new Error("Initial managed stop authority or runtime state changed.");
|
|
10693
|
+
proveLocalDockerSelection(owner);
|
|
10694
|
+
if (listDevpodWorkspacesRaw({ readLocalWhenMissing: true }).some(
|
|
10695
|
+
(entry) => entry.id === devsyId || entry.source.localFolder !== "" && sameWorkspacePath(entry.source.localFolder, repoPath)
|
|
10696
|
+
))
|
|
10697
|
+
throw new Error("Initial managed stop found conflicting provider ownership.");
|
|
10698
|
+
if (canonicalProfile(authority.entry.profile) !== canonicalName)
|
|
10699
|
+
throw new Error("Initial managed stop configuration changed.");
|
|
10700
|
+
const currentRuntime = loadRuntimeConfig(repoPath, workspace ?? "", canonicalName);
|
|
10701
|
+
const currentPlan = inspectManagedDevcontainerConfig({
|
|
10702
|
+
repoPath,
|
|
10703
|
+
config: currentRuntime.config,
|
|
10704
|
+
profile: currentRuntime.resolvedProfile,
|
|
10705
|
+
linked
|
|
10706
|
+
});
|
|
10707
|
+
if (currentRuntime.profile !== canonicalName || !(0, import_node_util9.isDeepStrictEqual)(currentRuntime.resolvedProfile, runtime.resolvedProfile) || !(0, import_node_util9.isDeepStrictEqual)(currentPlan, plan) || inspectManagedDevcontainerGeneratedConfig(currentPlan).status !== "valid")
|
|
10708
|
+
throw new Error("Initial managed stop configuration changed.");
|
|
10572
10709
|
const matches = inspectWorkspaceContainers().filter(
|
|
10573
10710
|
(c) => sameWorkspacePath(
|
|
10574
10711
|
c.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
10575
10712
|
plan.composeDirectory
|
|
10576
10713
|
)
|
|
10577
10714
|
);
|
|
10578
|
-
if (!matches.length)
|
|
10715
|
+
if (!matches.length)
|
|
10716
|
+
throw new Error("Initial managed stop requires its complete selected population.");
|
|
10579
10717
|
const projects = new Set(matches.map((c) => c.labels["com.docker.compose.project"]));
|
|
10580
10718
|
if (projects.size !== 1 || !matches[0]?.labels["com.docker.compose.project"])
|
|
10581
10719
|
throw new Error("Initial managed stop requires one exact Compose project.");
|
|
10582
10720
|
const population = inspectManagedStopContainers(
|
|
10583
|
-
matches[0].labels["com.docker.compose.project"]
|
|
10721
|
+
matches[0].labels["com.docker.compose.project"],
|
|
10722
|
+
endpoint
|
|
10584
10723
|
);
|
|
10585
10724
|
if (!sameSet(
|
|
10725
|
+
inspectManagedStopWorkspaceIds(endpoint, plan.composeDirectory),
|
|
10726
|
+
population.map((c) => c.id)
|
|
10727
|
+
) || !sameSet(
|
|
10586
10728
|
matches.map((c) => c.id),
|
|
10587
10729
|
population.map((c) => c.id)
|
|
10588
10730
|
))
|
|
10589
10731
|
throw new Error("Initial managed stop population changed.");
|
|
10590
|
-
|
|
10732
|
+
if (!sameSet(
|
|
10733
|
+
population.map((c) => c.labels["com.docker.compose.service"] ?? ""),
|
|
10734
|
+
plan.desiredServices
|
|
10735
|
+
))
|
|
10736
|
+
throw new Error("Initial managed stop requires its complete selected population.");
|
|
10737
|
+
const primary = proveManagedComposePopulation({
|
|
10591
10738
|
plan,
|
|
10592
10739
|
repoPath,
|
|
10593
10740
|
composeProject: matches[0].labels["com.docker.compose.project"],
|
|
@@ -10595,9 +10742,23 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10595
10742
|
featureDirectory,
|
|
10596
10743
|
containers: population
|
|
10597
10744
|
});
|
|
10745
|
+
const uidBytes = Buffer.byteLength(owner.uid ?? "");
|
|
10746
|
+
const runnerId = (uidBytes === 16 || uidBytes === 40 ? owner.uid : void 0) ?? devsyId;
|
|
10747
|
+
if (owner.source.container && owner.source.container !== primary.id || !owner.source.container && inspectManagedStopRunnerId(endpoint, primary.id) !== runnerId)
|
|
10748
|
+
throw new Error("Initial managed stop provider-to-container binding changed.");
|
|
10749
|
+
if (!owner.source.container && !sameSet(inspectProviderRunnerContainers(endpoint, runnerId), [primary.id]))
|
|
10750
|
+
throw new Error("Initial managed stop provider runner population changed.");
|
|
10751
|
+
assertManagedContainerConfigUnchanged({
|
|
10752
|
+
plan,
|
|
10753
|
+
containers: population,
|
|
10754
|
+
workspace: workspaceEnv
|
|
10755
|
+
});
|
|
10756
|
+
if (!(0, import_node_util9.isDeepStrictEqual)(readAuthority(), authority) || !(0, import_node_util9.isDeepStrictEqual)(registration2(), owner) || resolveManagedStopEndpoint() !== endpoint || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
10757
|
+
throw new Error("Initial managed stop authority changed during inspection.");
|
|
10598
10758
|
return population;
|
|
10599
10759
|
};
|
|
10600
10760
|
const initial = observe();
|
|
10761
|
+
const stopped = new Set(initial.filter((c) => !c.state.Running).map((c) => c.id));
|
|
10601
10762
|
const stable = () => {
|
|
10602
10763
|
const current = observe();
|
|
10603
10764
|
if (!sameSet(
|
|
@@ -10605,19 +10766,21 @@ function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
|
10605
10766
|
current.map((c) => c.id)
|
|
10606
10767
|
) || current.some((c) => {
|
|
10607
10768
|
const prior = initial.find((p) => p.id === c.id);
|
|
10608
|
-
return !prior || containerIdentity(prior) !== containerIdentity(c) ||
|
|
10769
|
+
return !prior || containerIdentity(prior) !== containerIdentity(c) || stopped.has(c.id) && c.state.Running;
|
|
10609
10770
|
}))
|
|
10610
10771
|
throw new Error("Initial managed stop population or identity changed.");
|
|
10772
|
+
for (const c of current) if (!c.state.Running) stopped.add(c.id);
|
|
10611
10773
|
return current;
|
|
10612
10774
|
};
|
|
10613
10775
|
stable();
|
|
10614
|
-
if (!initial.length) return false;
|
|
10615
10776
|
for (const container of initial) {
|
|
10616
10777
|
const current = stable().find((c) => c.id === container.id);
|
|
10617
|
-
if (current?.state.Running)
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10778
|
+
if (current?.state.Running) {
|
|
10779
|
+
claimLifecycleEffect();
|
|
10780
|
+
stopPinnedManagedContainer(endpoint, current.id);
|
|
10781
|
+
if (stable().find((c) => c.id === current.id)?.state.Running)
|
|
10782
|
+
throw new Error("Initial managed stop container cessation is not proven.");
|
|
10783
|
+
}
|
|
10621
10784
|
}
|
|
10622
10785
|
if (stable().some((c) => c.state.Running))
|
|
10623
10786
|
throw new Error("Initial managed stop left a workload running.");
|
|
@@ -10837,11 +11000,15 @@ var init_managed_devsy_stop = __esm({
|
|
|
10837
11000
|
init_devcontainer_profile();
|
|
10838
11001
|
init_devpod_environment();
|
|
10839
11002
|
init_devpod_registry();
|
|
11003
|
+
init_devsy_exec_proof();
|
|
10840
11004
|
init_devsy_workspaces();
|
|
10841
11005
|
init_host_routes();
|
|
10842
11006
|
init_managed_compose_population();
|
|
10843
11007
|
init_managed_runtime_state();
|
|
10844
11008
|
init_managed_stop_recovery();
|
|
11009
|
+
init_reliability_context();
|
|
11010
|
+
init_reliability_contract();
|
|
11011
|
+
init_reliability_operation_store();
|
|
10845
11012
|
init_repo_config();
|
|
10846
11013
|
init_route_publication();
|
|
10847
11014
|
init_traefik_route_health();
|
|
@@ -20955,7 +21122,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
20955
21122
|
const config = runtimeConfig.config;
|
|
20956
21123
|
loadedConfig = config;
|
|
20957
21124
|
loadedWorkspace = runtimeConfig.workspace;
|
|
20958
|
-
const cliVersion = true ? "0.0.
|
|
21125
|
+
const cliVersion = true ? "0.0.76" : "0.0.0-dev";
|
|
20959
21126
|
const configVersion = config.devrouter?.version;
|
|
20960
21127
|
if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
|
|
20961
21128
|
addCheck(checks, {
|
|
@@ -26396,7 +26563,7 @@ var init_version = __esm({
|
|
|
26396
26563
|
|
|
26397
26564
|
// src/cli.ts
|
|
26398
26565
|
var import_commander = require("commander");
|
|
26399
|
-
var CLI_VERSION = true ? "0.0.
|
|
26566
|
+
var CLI_VERSION = true ? "0.0.76" : "0.0.0-dev";
|
|
26400
26567
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
26401
26568
|
function withErrorHandling(action2) {
|
|
26402
26569
|
return async (...args) => {
|
package/package.json
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Upgrade to devrouter 0.0.75
|
|
2
|
+
|
|
3
|
+
This patch fixes canonical stop after initial managed Devsy startup is cancelled
|
|
4
|
+
before managed runtime state is saved. Stop uses the exact drained ensure's
|
|
5
|
+
recorded profile and validates complete live ownership and Compose configuration
|
|
6
|
+
hashes. No configuration schema, command, flag or runtime-provider migration is
|
|
7
|
+
required.
|
|
8
|
+
|
|
9
|
+
## Task
|
|
10
|
+
|
|
11
|
+
Use the published 0.0.75 CLI. Update `.devrouter.yml` devrouter.version to 0.0.75
|
|
12
|
+
only when consumer source changes are authorized. Preserve staged work owned by
|
|
13
|
+
another task. Refresh generated agent guidance only with the repository owner's
|
|
14
|
+
authority. No DevPod installation is needed.
|
|
15
|
+
|
|
16
|
+
For an affected exact checkout, use `devrouter stop <path> --json` with the same
|
|
17
|
+
non-secret Compose inputs used at startup. Existing interrupted workspaces can
|
|
18
|
+
recover when the exact operation history and all live configuration and ownership
|
|
19
|
+
proofs remain available. Missing, unreadable, conflicting or changed evidence
|
|
20
|
+
remains a refusal; never fabricate journal state or replace the selected profile
|
|
21
|
+
with the default to bypass it. Stop is non-destructive and retains volumes.
|
|
22
|
+
|
|
23
|
+
## Validation
|
|
24
|
+
|
|
25
|
+
Verify the released CLI revision and exact checkout ownership. Let the checkout's
|
|
26
|
+
runtime owner perform the canonical stop, require provider cessation and route
|
|
27
|
+
removal proof, then verify stopped lifecycle state. Revalidate port conflicts
|
|
28
|
+
before a new ensure and require its own provider, application and route proof.
|
|
29
|
+
A successful stop or source test does not prove successful application startup.
|
|
30
|
+
|
|
31
|
+
## Report
|
|
32
|
+
|
|
33
|
+
Report release version/revision, exact checkout, producing stop result and
|
|
34
|
+
lifecycle state, subsequent ensure evidence or precise refusal, and source/index
|
|
35
|
+
preservation. Separate released source from live consumer acceptance.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Upgrade to devrouter 0.0.76
|
|
2
|
+
|
|
3
|
+
This patch accepts canonical-equivalent historical profile selections during
|
|
4
|
+
initial managed stop and prevents first-transition rollback from contradicting
|
|
5
|
+
captured degraded configuration. No schema, command or flag migration is needed.
|
|
6
|
+
|
|
7
|
+
## Task
|
|
8
|
+
|
|
9
|
+
Use the published 0.0.76 CLI. Update `.devrouter.yml` devrouter.version only when
|
|
10
|
+
consumer source changes are authorized. Preserve another task's staged work.
|
|
11
|
+
Refresh generated agent guidance only with the repository owner's authority.
|
|
12
|
+
No generated runtime configuration or journal rewrite is required or authorized.
|
|
13
|
+
|
|
14
|
+
For an affected exact checkout, its runtime owner uses `devrouter stop <path>
|
|
15
|
+
--json` with the original Compose inputs. Canonical-equivalent selections pass
|
|
16
|
+
the profile check; every ownership and configuration proof still applies. A
|
|
17
|
+
Compose hash refusal must be investigated rather than bypassed. Historical
|
|
18
|
+
contradictory generated configuration remains unproved without writer evidence;
|
|
19
|
+
a valid retained stop baseline can support containment, not automatic resume.
|
|
20
|
+
|
|
21
|
+
## Validation
|
|
22
|
+
|
|
23
|
+
Verify the installed version and exact checkout ownership. Require live provider,
|
|
24
|
+
container and route cessation proof for stop. A later ensure needs its own runtime
|
|
25
|
+
and readiness proof. New compatible failed-first-transition retries retain matching
|
|
26
|
+
configuration; extra resources and manual drift remain refusals. Source tests or a
|
|
27
|
+
passed profile check do not establish full consumer recovery.
|
|
28
|
+
|
|
29
|
+
## Report
|
|
30
|
+
|
|
31
|
+
Report release revision, installed executable, exact checkout, stop/ensure result
|
|
32
|
+
or precise refusal, and source/index preservation. Separate release/install proof
|
|
33
|
+
from live consumer acceptance.
|