@devrouter/cli 0.0.61 → 0.0.62
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 +326 -191
- package/dist/devrouter.js +275 -111
- package/package.json +1 -1
- package/upgrade-prompts/0.0.62.md +35 -0
package/dist/devrouter.js
CHANGED
|
@@ -2450,7 +2450,7 @@ function loadRepoConfig(repoPath, read = (file) => import_node_fs6.default.readF
|
|
|
2450
2450
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
2451
2451
|
const requiredVersion = config.devrouter?.version;
|
|
2452
2452
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
2453
|
-
const cliVersion = true ? "0.0.
|
|
2453
|
+
const cliVersion = true ? "0.0.62" : "0.0.0-dev";
|
|
2454
2454
|
if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
|
|
2455
2455
|
hasWarnedVersionMismatch = true;
|
|
2456
2456
|
process.stderr.write(
|
|
@@ -4558,6 +4558,19 @@ function parseManagedStopJson(output2) {
|
|
|
4558
4558
|
throw new Error("Managed stop Docker response is malformed.");
|
|
4559
4559
|
}
|
|
4560
4560
|
}
|
|
4561
|
+
function resolveManagedStopEndpoint() {
|
|
4562
|
+
const endpoint = process.env.DOCKER_HOST && !process.env.DOCKER_CONTEXT ? process.env.DOCKER_HOST : parseManagedStopJson(
|
|
4563
|
+
runManagedStopDocker([
|
|
4564
|
+
"context",
|
|
4565
|
+
"inspect",
|
|
4566
|
+
"--format",
|
|
4567
|
+
"{{json .Endpoints.docker.Host}}"
|
|
4568
|
+
])
|
|
4569
|
+
);
|
|
4570
|
+
if (typeof endpoint !== "string") throw new Error("Docker endpoint is unavailable.");
|
|
4571
|
+
supportsManagedStopBaseline(endpoint);
|
|
4572
|
+
return endpoint;
|
|
4573
|
+
}
|
|
4561
4574
|
function inspectManagedStopDaemon(endpoint) {
|
|
4562
4575
|
const value = parseManagedStopJson(
|
|
4563
4576
|
runManagedStopDocker(["info", "--format", "{{json .ID}}"], endpoint)
|
|
@@ -4579,6 +4592,28 @@ function inspectManagedStopRunnerId(endpoint, containerId) {
|
|
|
4579
4592
|
throw new Error("Provider container binding is unavailable.");
|
|
4580
4593
|
return value;
|
|
4581
4594
|
}
|
|
4595
|
+
function inspectProviderRunnerContainers(endpoint, runnerId) {
|
|
4596
|
+
if (!runnerId || runnerId.length > 4096 || /[\r\n\0]/.test(runnerId))
|
|
4597
|
+
throw new Error("Provider runner identity is malformed.");
|
|
4598
|
+
const ids = parseDockerLines(
|
|
4599
|
+
runManagedStopDocker(
|
|
4600
|
+
[
|
|
4601
|
+
"ps",
|
|
4602
|
+
"-a",
|
|
4603
|
+
"--no-trunc",
|
|
4604
|
+
"--filter",
|
|
4605
|
+
`label=dev.containers.id=${runnerId}`,
|
|
4606
|
+
"--format",
|
|
4607
|
+
"{{.ID}}"
|
|
4608
|
+
],
|
|
4609
|
+
endpoint
|
|
4610
|
+
)
|
|
4611
|
+
);
|
|
4612
|
+
if (ids.length > 256) throw new Error("Provider runner population exceeds its bound.");
|
|
4613
|
+
ids.forEach(assertFullContainerId);
|
|
4614
|
+
assertUniqueContainerIds(ids);
|
|
4615
|
+
return ids;
|
|
4616
|
+
}
|
|
4582
4617
|
function inspectManagedStopWorkspaceIds(endpoint, composeDirectory) {
|
|
4583
4618
|
const ids = parseDockerLines(
|
|
4584
4619
|
runManagedStopDocker(
|
|
@@ -7106,6 +7141,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
7106
7141
|
`- When a workspace is active: hosts auto-namespace (\`web.localhost\` \u2192 \`web.<ws>.localhost\`), \`${WORKSPACE_PLACEHOLDER}\` in \`upstream\` is substituted with the token, and the docker \`router\` key is suffixed per workspace. Managed ensure rejects every HTTP/TCP upstream outside that exact alias namespace before mutation. The runtime config is computed in memory only \u2014 the committed \`.devrouter.yml\` is never rewritten.`,
|
|
7107
7142
|
"- TLS: namespaced hosts (`web.<ws>.localhost`) are not covered by the `*.localhost` wildcard; devrouter auto-extends the mkcert cert SANs for active hosts when TLS is enabled.",
|
|
7108
7143
|
"- Lifecycle: after one-time setup, use `devrouter ensure .` for both primary and linked checkouts; never branch on checkout kind or use live verify as startup. Managed consumer images contain no devrouter package/helper: ensure delivers its matching helper at runtime and invokes an exact captured snapshot of the repository-owned post-start adapter. Keep `.devrouter.yml` as the only consumer-side version pin. Use `devrouter stop .` for a non-destructive pause, `devrouter stop . --delete` only for explicit exact-owner cleanup without removing the checkout, and `devrouter exec . -- <command...>` for container commands. Never substitute raw DevPod/Devsy mutations; they bypass the machine-global ownership lock, which serializes provider mutations in a fair arrival-order queue and lets contenders wait up to thirty minutes with throttled stderr progress before failing with the queue position or holder PID and true durations. `workspace up` creates linked worktrees; destructive worktree removal and GC remain ledger-scoped.",
|
|
7144
|
+
"- After a drained interrupted ensure, manual exec can use an exactly proven retained Devsy runtime with the plain local Docker command. Revalidate identity for each tooling command until later preparation reconciles startup; preserve configuration, data and interrupted history. This does not repair absent stop baselines or provide OOM protection.",
|
|
7109
7145
|
"- Ensure reconciles interrupted or never-dispatched ensure after positive worker drainage. Unknown arbitrary exec never replays; use explicit stop to reconcile that uncertainty. Preserve journals and generated configuration if evidence is unavailable. This does not prevent OOM or activate capacity-managed recovery.",
|
|
7110
7146
|
"- Devsy agent readiness: run `devrouter setup --yes --workspace-runtime devsy` once so Devrouter acquires and verifies the pinned agent in its own machine cache. `doctor` reports `ready`, `missing`, `stale`, or `invalid` without network access, and `ensure` fails before the provider queue when readiness is not `ready`. An explicit `DEVSY_AGENT_BINARY` remains authoritative and must match a pinned official asset.",
|
|
7111
7147
|
"- A managed adapter plus `postCreateCommand` requires `waitFor` exactly `postCreateCommand` or `postStartCommand`; managed selective config preserves lifecycle fields and changes only `runServices`.",
|
|
@@ -9634,6 +9670,10 @@ function listDevsyWorkspaces() {
|
|
|
9634
9670
|
...typeof candidate.uid === "string" ? { uid: candidate.uid } : {},
|
|
9635
9671
|
...typeof candidate.context === "string" ? { context: candidate.context } : {}
|
|
9636
9672
|
};
|
|
9673
|
+
const provider = candidate.provider;
|
|
9674
|
+
if (provider && typeof provider.name === "string") workspace.providerName = provider.name;
|
|
9675
|
+
if (typeof provider?.options?.DOCKER_PATH?.value === "string")
|
|
9676
|
+
workspace.dockerPathOption = provider.options.DOCKER_PATH.value;
|
|
9637
9677
|
if ("lastUsed" in candidate) {
|
|
9638
9678
|
if (typeof candidate.lastUsed === "string") {
|
|
9639
9679
|
workspace.lastUsed = candidate.lastUsed;
|
|
@@ -9691,6 +9731,16 @@ function inspectDevsyWorkspaceOwnership(workspaces, devsyId, worktreePath) {
|
|
|
9691
9731
|
}
|
|
9692
9732
|
return exact[0] ? { status: "owned", workspace: exact[0] } : { status: "absent" };
|
|
9693
9733
|
}
|
|
9734
|
+
function selectDevsyWorkspace(workspaces, repoPath) {
|
|
9735
|
+
const matches = workspaces.filter(
|
|
9736
|
+
(workspace) => sameWorkspacePath(workspace.source.localFolder, repoPath)
|
|
9737
|
+
);
|
|
9738
|
+
if (matches.length > 1) {
|
|
9739
|
+
const ids = matches.map((match) => match.id).join(", ");
|
|
9740
|
+
throw new Error(`Multiple Devsy workspaces reference '${repoPath}': ${ids}`);
|
|
9741
|
+
}
|
|
9742
|
+
return matches[0];
|
|
9743
|
+
}
|
|
9694
9744
|
var import_node_child_process13;
|
|
9695
9745
|
var init_devsy_workspaces = __esm({
|
|
9696
9746
|
"src/core/devsy-workspaces.ts"() {
|
|
@@ -12018,7 +12068,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
12018
12068
|
const config = runtimeConfig.config;
|
|
12019
12069
|
loadedConfig = config;
|
|
12020
12070
|
loadedWorkspace = runtimeConfig.workspace;
|
|
12021
|
-
const cliVersion = true ? "0.0.
|
|
12071
|
+
const cliVersion = true ? "0.0.62" : "0.0.0-dev";
|
|
12022
12072
|
const configVersion = config.devrouter?.version;
|
|
12023
12073
|
if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
|
|
12024
12074
|
addCheck(checks, {
|
|
@@ -12651,6 +12701,96 @@ var init_up = __esm({
|
|
|
12651
12701
|
}
|
|
12652
12702
|
});
|
|
12653
12703
|
|
|
12704
|
+
// src/core/devsy-exec-proof.ts
|
|
12705
|
+
function proveLocalDockerSelection(workspace) {
|
|
12706
|
+
const name = workspace.providerName;
|
|
12707
|
+
const context = workspace.context;
|
|
12708
|
+
if (!name || !context || !/^[a-z0-9][a-z0-9-]{0,31}$/.test(name) || !/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(context))
|
|
12709
|
+
throw new Error("Retained exec provider configuration identity is unavailable.");
|
|
12710
|
+
const file = import_node_path37.default.join(
|
|
12711
|
+
process.env.DEVSY_HOME || import_node_path37.default.join(import_node_os4.default.homedir(), ".devsy"),
|
|
12712
|
+
"contexts",
|
|
12713
|
+
context,
|
|
12714
|
+
"providers",
|
|
12715
|
+
name,
|
|
12716
|
+
"provider.json"
|
|
12717
|
+
);
|
|
12718
|
+
try {
|
|
12719
|
+
if (import_node_fs30.default.statSync(file).size > 1024 * 1024) throw new Error();
|
|
12720
|
+
const config = JSON.parse(import_node_fs30.default.readFileSync(file, "utf8"));
|
|
12721
|
+
const driver = config.agent?.driver ?? "docker";
|
|
12722
|
+
const configuredPath = config.agent?.docker?.path ?? "";
|
|
12723
|
+
if (typeof configuredPath !== "string") throw new Error();
|
|
12724
|
+
const command = configuredPath === "${DOCKER_PATH}" || configuredPath === "$DOCKER_PATH" ? workspace.dockerPathOption || process.env.DOCKER_PATH || "docker" : configuredPath || "docker";
|
|
12725
|
+
if (config.name !== name || driver !== "docker" || command !== "docker") throw new Error();
|
|
12726
|
+
} catch {
|
|
12727
|
+
throw new Error("Retained exec requires a provider configured for the local Docker command.");
|
|
12728
|
+
}
|
|
12729
|
+
return name;
|
|
12730
|
+
}
|
|
12731
|
+
function captureDevsyExecProof(repoPath) {
|
|
12732
|
+
repoPath = comparableWorkspacePath(repoPath);
|
|
12733
|
+
const registry = listDevsyWorkspaces();
|
|
12734
|
+
const workspace = selectDevsyWorkspace(registry, repoPath);
|
|
12735
|
+
if (!workspace?.uid || !workspace.context || workspace.source.container || inspectDevsyWorkspaceOwnership(registry, workspace.id, repoPath).status !== "owned" || inspectDevsyRuntimeStatus(workspace.id) !== "running")
|
|
12736
|
+
throw new Error("Retained exec requires one exact running Devsy registration.");
|
|
12737
|
+
const providerName = proveLocalDockerSelection(workspace);
|
|
12738
|
+
const endpoint = resolveManagedStopEndpoint();
|
|
12739
|
+
if (!supportsManagedStopBaseline(endpoint))
|
|
12740
|
+
throw new Error("Retained exec requires an exact local Docker endpoint.");
|
|
12741
|
+
const daemon = inspectManagedStopDaemon(endpoint);
|
|
12742
|
+
const candidates = workspaceAppContainers(inspectWorkspaceContainers(), repoPath).filter(
|
|
12743
|
+
(container) => container.state.Running
|
|
12744
|
+
);
|
|
12745
|
+
if (candidates.length !== 1)
|
|
12746
|
+
throw new Error("Retained exec requires one exact running primary container.");
|
|
12747
|
+
const candidate = candidates[0];
|
|
12748
|
+
const project = candidate.labels["com.docker.compose.project"];
|
|
12749
|
+
if (!project) throw new Error("Retained exec primary has no Compose identity.");
|
|
12750
|
+
const pinned = workspaceAppContainers(
|
|
12751
|
+
inspectManagedStopContainers(project, endpoint),
|
|
12752
|
+
repoPath
|
|
12753
|
+
).filter((container) => container.state.Running);
|
|
12754
|
+
if (pinned.length !== 1 || pinned[0].id !== candidate.id)
|
|
12755
|
+
throw new Error("Retained exec primary changed during inspection.");
|
|
12756
|
+
const mounts = pinned[0].mounts.filter(
|
|
12757
|
+
(mount) => mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath)
|
|
12758
|
+
);
|
|
12759
|
+
if (mounts.length !== 1 || !mounts[0].Destination.startsWith("/"))
|
|
12760
|
+
throw new Error("Retained exec requires one exact source mount.");
|
|
12761
|
+
const uidBytes = Buffer.byteLength(workspace.uid);
|
|
12762
|
+
const runner = uidBytes === 16 || uidBytes === 40 ? workspace.uid : workspace.id;
|
|
12763
|
+
const runnerContainers = inspectProviderRunnerContainers(endpoint, runner);
|
|
12764
|
+
if (runnerContainers.length !== 1 || runnerContainers[0] !== candidate.id)
|
|
12765
|
+
throw new Error("Retained exec runner does not select the exact primary container.");
|
|
12766
|
+
if (inspectManagedStopRunnerId(endpoint, candidate.id) !== runner || inspectManagedStopDaemon(endpoint) !== daemon)
|
|
12767
|
+
throw new Error("Retained exec provider or Docker identity changed.");
|
|
12768
|
+
return {
|
|
12769
|
+
repoPath,
|
|
12770
|
+
id: workspace.id,
|
|
12771
|
+
uid: workspace.uid,
|
|
12772
|
+
context: workspace.context,
|
|
12773
|
+
providerName,
|
|
12774
|
+
endpoint,
|
|
12775
|
+
daemon,
|
|
12776
|
+
containerId: candidate.id,
|
|
12777
|
+
workspacePath: mounts[0].Destination
|
|
12778
|
+
};
|
|
12779
|
+
}
|
|
12780
|
+
var import_node_fs30, import_node_os4, import_node_path37, import_node_util3;
|
|
12781
|
+
var init_devsy_exec_proof = __esm({
|
|
12782
|
+
"src/core/devsy-exec-proof.ts"() {
|
|
12783
|
+
"use strict";
|
|
12784
|
+
import_node_fs30 = __toESM(require("fs"));
|
|
12785
|
+
import_node_os4 = __toESM(require("os"));
|
|
12786
|
+
import_node_path37 = __toESM(require("path"));
|
|
12787
|
+
import_node_util3 = require("util");
|
|
12788
|
+
init_devpod_environment();
|
|
12789
|
+
init_devsy_workspaces();
|
|
12790
|
+
init_workspace();
|
|
12791
|
+
}
|
|
12792
|
+
});
|
|
12793
|
+
|
|
12654
12794
|
// src/core/reliability-model.ts
|
|
12655
12795
|
function record3(value) {
|
|
12656
12796
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -12680,7 +12820,7 @@ function assertReliabilityEvent(value) {
|
|
|
12680
12820
|
valid = oneOf2(value.mode, ["start", "attach"]) && isReliabilityId(value.key) && isReliabilityId(value.operationId) && isReliabilityProfile(value.profile) && isConsumer(value.consumer);
|
|
12681
12821
|
break;
|
|
12682
12822
|
case "operation-request":
|
|
12683
|
-
valid = oneOf2(value.kind, ["ensure", "exec"]) && isReliabilityId(value.key) && isReliabilityId(value.operationId) && isReliabilityProfile(value.profile) && isConsumer(value.consumer) && typeof value.runtimeRunning === "boolean";
|
|
12823
|
+
valid = oneOf2(value.kind, ["ensure", "exec"]) && isReliabilityId(value.key) && isReliabilityId(value.operationId) && isReliabilityProfile(value.profile) && isConsumer(value.consumer) && typeof value.runtimeRunning === "boolean" && (value.recoverInterruptedEnsure === void 0 || typeof value.recoverInterruptedEnsure === "boolean");
|
|
12684
12824
|
break;
|
|
12685
12825
|
case "drained":
|
|
12686
12826
|
valid = isReliabilityId(value.operationId);
|
|
@@ -12863,6 +13003,10 @@ function handleRequest(state, event) {
|
|
|
12863
13003
|
});
|
|
12864
13004
|
return transition(state, "accepted");
|
|
12865
13005
|
}
|
|
13006
|
+
function canExecAfterInterruptedEnsure(state) {
|
|
13007
|
+
const latestEnsure = [...state.operationHistory].reverse().find((operation) => operation.kind === "ensure");
|
|
13008
|
+
return state.executionPolicy === "manual" && state.desired === "running" && latestEnsure?.status === "INTERRUPTED" && latestEnsure.drained && state.operation?.drained === true && (state.operation.kind === "ensure" && state.phase === "recovering" || state.operation.kind === "exec" && ["COMPLETED", "NOT_LAUNCHED", "NOT_STARTED"].includes(state.operation.status));
|
|
13009
|
+
}
|
|
12866
13010
|
function handleOperationRequest(state, event) {
|
|
12867
13011
|
if (state.executionPolicy !== "manual") return unchanged(state, "blocked");
|
|
12868
13012
|
const previous = state.operationHistory.find((entry) => entry.key === event.key);
|
|
@@ -12873,9 +13017,11 @@ function handleOperationRequest(state, event) {
|
|
|
12873
13017
|
);
|
|
12874
13018
|
if (state.operationHistory.some((entry) => entry.id === event.operationId))
|
|
12875
13019
|
return unchanged(state, "conflict");
|
|
13020
|
+
if (event.kind === "exec" && canExecAfterInterruptedEnsure(state) && event.recoverInterruptedEnsure !== true)
|
|
13021
|
+
return unchanged(state, "blocked");
|
|
12876
13022
|
const fullyStopped = state.stopProof.workloadsStopped && state.stopProof.routesRemoved;
|
|
12877
13023
|
const reconcileEnsure = event.kind === "ensure" && state.operation?.kind === "ensure" && ["NOT_STARTED", "INTERRUPTED"].includes(state.operation.status);
|
|
12878
|
-
if (state.operation && (!state.operation.drained || !["COMPLETED", "NOT_LAUNCHED", "NOT_STARTED"].includes(state.operation.status) && !fullyStopped && !reconcileEnsure))
|
|
13024
|
+
if (state.operation && (!state.operation.drained || !["COMPLETED", "NOT_LAUNCHED", "NOT_STARTED"].includes(state.operation.status) && !fullyStopped && !reconcileEnsure && !(event.kind === "exec" && event.recoverInterruptedEnsure === true && canExecAfterInterruptedEnsure(state))))
|
|
12879
13025
|
return unchanged(state, "blocked");
|
|
12880
13026
|
if (state.phase === "stopping" || state.desired === "parked-for-capacity")
|
|
12881
13027
|
return unchanged(state, "blocked");
|
|
@@ -12886,8 +13032,9 @@ function handleOperationRequest(state, event) {
|
|
|
12886
13032
|
if (rollover) {
|
|
12887
13033
|
if (state.operation && (!state.operation.drained || !["COMPLETED", "NOT_LAUNCHED"].includes(state.operation.status)))
|
|
12888
13034
|
return unchanged(state, "blocked");
|
|
13035
|
+
const latestEnsureId = [...state.operationHistory].reverse().find((entry) => entry.kind === "ensure")?.id;
|
|
12889
13036
|
retired = state.operationHistory.findIndex(
|
|
12890
|
-
(entry) => entry.id !== state.operation?.id && entry.drained && ["COMPLETED", "NOT_LAUNCHED"].includes(entry.status)
|
|
13037
|
+
(entry) => entry.id !== state.operation?.id && entry.id !== latestEnsureId && entry.drained && ["COMPLETED", "NOT_LAUNCHED"].includes(entry.status)
|
|
12891
13038
|
);
|
|
12892
13039
|
if (retired === -1) return unchanged(state, "blocked");
|
|
12893
13040
|
}
|
|
@@ -13289,8 +13436,8 @@ function signalOwnedGroup(child, signal) {
|
|
|
13289
13436
|
async function runLifecycleWorker(request) {
|
|
13290
13437
|
if (process.platform === "win32")
|
|
13291
13438
|
throw new Error("Lifecycle workers require POSIX process-group ownership.");
|
|
13292
|
-
const workerPath =
|
|
13293
|
-
if (!
|
|
13439
|
+
const workerPath = import_node_path38.default.join(__dirname, "devrouter-lifecycle-worker.js");
|
|
13440
|
+
if (!import_node_fs31.default.existsSync(workerPath))
|
|
13294
13441
|
throw new Error("The packaged lifecycle worker is missing; rebuild the CLI.");
|
|
13295
13442
|
const child = (0, import_node_child_process21.fork)(workerPath, [], {
|
|
13296
13443
|
detached: true,
|
|
@@ -13448,14 +13595,14 @@ async function runLifecycleWorker(request) {
|
|
|
13448
13595
|
function newLifecycleIds() {
|
|
13449
13596
|
return { requestId: (0, import_node_crypto16.randomUUID)(), operationId: (0, import_node_crypto16.randomUUID)(), workerId: (0, import_node_crypto16.randomUUID)() };
|
|
13450
13597
|
}
|
|
13451
|
-
var import_node_child_process21, import_node_crypto16,
|
|
13598
|
+
var import_node_child_process21, import_node_crypto16, import_node_fs31, import_node_path38;
|
|
13452
13599
|
var init_reliability_worker = __esm({
|
|
13453
13600
|
"src/core/reliability-worker.ts"() {
|
|
13454
13601
|
"use strict";
|
|
13455
13602
|
import_node_child_process21 = require("child_process");
|
|
13456
13603
|
import_node_crypto16 = require("crypto");
|
|
13457
|
-
|
|
13458
|
-
|
|
13604
|
+
import_node_fs31 = __toESM(require("fs"));
|
|
13605
|
+
import_node_path38 = __toESM(require("path"));
|
|
13459
13606
|
init_file_lock();
|
|
13460
13607
|
init_reliability_contract();
|
|
13461
13608
|
init_reliability_model();
|
|
@@ -13464,12 +13611,12 @@ var init_reliability_worker = __esm({
|
|
|
13464
13611
|
});
|
|
13465
13612
|
|
|
13466
13613
|
// src/core/managed-host-preparation.ts
|
|
13467
|
-
var import_node_child_process22,
|
|
13614
|
+
var import_node_child_process22, import_node_fs32;
|
|
13468
13615
|
var init_managed_host_preparation = __esm({
|
|
13469
13616
|
"src/core/managed-host-preparation.ts"() {
|
|
13470
13617
|
"use strict";
|
|
13471
13618
|
import_node_child_process22 = require("child_process");
|
|
13472
|
-
|
|
13619
|
+
import_node_fs32 = __toESM(require("fs"));
|
|
13473
13620
|
init_repo_config();
|
|
13474
13621
|
}
|
|
13475
13622
|
});
|
|
@@ -13578,7 +13725,7 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
|
|
|
13578
13725
|
throw new Error(
|
|
13579
13726
|
"Traefik routes did not reload; shared router restart is disabled for this operation."
|
|
13580
13727
|
);
|
|
13581
|
-
|
|
13728
|
+
import_node_fs33.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
13582
13729
|
return withFileLock(
|
|
13583
13730
|
ROUTER_RELOAD_LOCK_FILE,
|
|
13584
13731
|
{
|
|
@@ -13609,19 +13756,19 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
|
|
|
13609
13756
|
async function ensureTraefikRoutesRemoved(routes, options = {}) {
|
|
13610
13757
|
return ensureTraefikRouteExpectation(routes, "removed", options);
|
|
13611
13758
|
}
|
|
13612
|
-
var import_node_child_process23,
|
|
13759
|
+
var import_node_child_process23, import_node_fs33, import_node_path39, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, ROUTER_API_REQUEST_TIMEOUT_SECONDS, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
|
|
13613
13760
|
var init_traefik_route_health = __esm({
|
|
13614
13761
|
"src/core/traefik-route-health.ts"() {
|
|
13615
13762
|
"use strict";
|
|
13616
13763
|
import_node_child_process23 = require("child_process");
|
|
13617
|
-
|
|
13618
|
-
|
|
13764
|
+
import_node_fs33 = __toESM(require("fs"));
|
|
13765
|
+
import_node_path39 = __toESM(require("path"));
|
|
13619
13766
|
init_file_lock();
|
|
13620
13767
|
init_host_routes();
|
|
13621
13768
|
init_reliability_context();
|
|
13622
13769
|
init_router();
|
|
13623
13770
|
ROUTER_API_BASE = "http://127.0.0.1:8080/api";
|
|
13624
|
-
ROUTER_RELOAD_LOCK_FILE =
|
|
13771
|
+
ROUTER_RELOAD_LOCK_FILE = import_node_path39.default.join(DEVROUTER_HOME, "router-reload.lock");
|
|
13625
13772
|
ROUTER_API_REQUEST_TIMEOUT_SECONDS = "2";
|
|
13626
13773
|
DEFAULT_INITIAL_TIMEOUT_MS = 3e3;
|
|
13627
13774
|
DEFAULT_RECOVERY_TIMEOUT_MS = 1e4;
|
|
@@ -13660,13 +13807,13 @@ function resolveLinkedTarget(repoPath) {
|
|
|
13660
13807
|
gitCommonDir: resolveGitCommonDir(repoPath)
|
|
13661
13808
|
};
|
|
13662
13809
|
}
|
|
13663
|
-
var import_node_child_process24,
|
|
13810
|
+
var import_node_child_process24, import_node_fs34, import_node_path40;
|
|
13664
13811
|
var init_workspace_ensure = __esm({
|
|
13665
13812
|
"src/core/workspace-ensure.ts"() {
|
|
13666
13813
|
"use strict";
|
|
13667
13814
|
import_node_child_process24 = require("child_process");
|
|
13668
|
-
|
|
13669
|
-
|
|
13815
|
+
import_node_fs34 = __toESM(require("fs"));
|
|
13816
|
+
import_node_path40 = __toESM(require("path"));
|
|
13670
13817
|
init_devcontainer_profile();
|
|
13671
13818
|
init_devpod_environment();
|
|
13672
13819
|
init_devpod_mutation();
|
|
@@ -13753,8 +13900,22 @@ async function superviseLifecycle(kind, repoPath, options = {}, command) {
|
|
|
13753
13900
|
const ids = newLifecycleIds();
|
|
13754
13901
|
let fence;
|
|
13755
13902
|
const runtimeRunning = kind === "exec" ? Boolean(resolveRunningWorkspaceContainer(repoPath)) : false;
|
|
13903
|
+
const previous = kind === "exec" && identity2.provider === "devsy" ? updateReliabilityOperation(identity2, (record4) => {
|
|
13904
|
+
reconcileDrained(record4);
|
|
13905
|
+
return record4;
|
|
13906
|
+
}) : void 0;
|
|
13907
|
+
let retainedExecProof;
|
|
13908
|
+
if (previous && !previous.worker && canExecAfterInterruptedEnsure(previous.state)) {
|
|
13909
|
+
retainedExecProof = withMutationLock(
|
|
13910
|
+
"Prove retained exec",
|
|
13911
|
+
repoPath,
|
|
13912
|
+
() => captureDevsyExecProof(repoPath)
|
|
13913
|
+
);
|
|
13914
|
+
}
|
|
13756
13915
|
updateReliabilityOperation(identity2, (record4) => {
|
|
13757
13916
|
reconcileDrained(record4);
|
|
13917
|
+
if (retainedExecProof && record4.state.operation?.id !== previous?.state.operation?.id)
|
|
13918
|
+
throw new Error("Lifecycle operation changed during retained exec proof.");
|
|
13758
13919
|
if (kind === "stop") {
|
|
13759
13920
|
stepRecord(record4, { ...reliabilityFence(record4.state), type: "stop" });
|
|
13760
13921
|
} else {
|
|
@@ -13771,7 +13932,8 @@ async function superviseLifecycle(kind, repoPath, options = {}, command) {
|
|
|
13771
13932
|
operationId: ids.operationId,
|
|
13772
13933
|
profile: options.profile ?? record4.state.profile ?? "full",
|
|
13773
13934
|
consumer: { id: "manual-cli", requiredCapabilities: [], pinned: false },
|
|
13774
|
-
runtimeRunning
|
|
13935
|
+
runtimeRunning,
|
|
13936
|
+
...retainedExecProof ? { recoverInterruptedEnsure: true } : {}
|
|
13775
13937
|
});
|
|
13776
13938
|
}
|
|
13777
13939
|
fence = reliabilityFence(record4.state);
|
|
@@ -13783,15 +13945,17 @@ async function superviseLifecycle(kind, repoPath, options = {}, command) {
|
|
|
13783
13945
|
...ids,
|
|
13784
13946
|
fence,
|
|
13785
13947
|
options,
|
|
13786
|
-
...command ? { command } : {}
|
|
13948
|
+
...command ? { command } : {},
|
|
13949
|
+
...retainedExecProof ? { retainedExecProof } : {}
|
|
13787
13950
|
});
|
|
13788
13951
|
}
|
|
13789
|
-
var
|
|
13952
|
+
var import_node_path41, activeWorker, lockHeld;
|
|
13790
13953
|
var init_reliability_lifecycle = __esm({
|
|
13791
13954
|
"src/core/reliability-lifecycle.ts"() {
|
|
13792
13955
|
"use strict";
|
|
13793
|
-
|
|
13956
|
+
import_node_path41 = __toESM(require("path"));
|
|
13794
13957
|
init_devpod_environment();
|
|
13958
|
+
init_devsy_exec_proof();
|
|
13795
13959
|
init_devsy_mutation();
|
|
13796
13960
|
init_file_lock();
|
|
13797
13961
|
init_host_routes();
|
|
@@ -14200,17 +14364,17 @@ function asRecord3(value) {
|
|
|
14200
14364
|
return value;
|
|
14201
14365
|
}
|
|
14202
14366
|
function readJson(filePath) {
|
|
14203
|
-
if (!
|
|
14367
|
+
if (!import_node_fs35.default.existsSync(filePath)) {
|
|
14204
14368
|
return void 0;
|
|
14205
14369
|
}
|
|
14206
14370
|
try {
|
|
14207
|
-
return JSON.parse(
|
|
14371
|
+
return JSON.parse(import_node_fs35.default.readFileSync(filePath, "utf-8"));
|
|
14208
14372
|
} catch {
|
|
14209
14373
|
return void 0;
|
|
14210
14374
|
}
|
|
14211
14375
|
}
|
|
14212
14376
|
function relative(repoPath, filePath) {
|
|
14213
|
-
return
|
|
14377
|
+
return import_node_path42.default.relative(repoPath, filePath) || ".";
|
|
14214
14378
|
}
|
|
14215
14379
|
function redactEnvAssignments(value) {
|
|
14216
14380
|
return value.replace(
|
|
@@ -14251,7 +14415,7 @@ function inspectPackageManager(repoPath, pkg) {
|
|
|
14251
14415
|
["bun.lock", "bun"]
|
|
14252
14416
|
];
|
|
14253
14417
|
for (const [fileName, name] of lockfiles) {
|
|
14254
|
-
if (
|
|
14418
|
+
if (import_node_fs35.default.existsSync(import_node_path42.default.join(repoPath, fileName))) {
|
|
14255
14419
|
return { name, source: fileName };
|
|
14256
14420
|
}
|
|
14257
14421
|
}
|
|
@@ -14266,9 +14430,9 @@ function inspectNode(repoPath, pkg) {
|
|
|
14266
14430
|
if (typeof engines?.node === "string") {
|
|
14267
14431
|
return { version: engines.node, source: "package.json:engines.node" };
|
|
14268
14432
|
}
|
|
14269
|
-
const nvmrc =
|
|
14270
|
-
if (
|
|
14271
|
-
const version =
|
|
14433
|
+
const nvmrc = import_node_path42.default.join(repoPath, ".nvmrc");
|
|
14434
|
+
if (import_node_fs35.default.existsSync(nvmrc)) {
|
|
14435
|
+
const version = import_node_fs35.default.readFileSync(nvmrc, "utf-8").trim();
|
|
14272
14436
|
return { version, source: ".nvmrc" };
|
|
14273
14437
|
}
|
|
14274
14438
|
return void 0;
|
|
@@ -14329,7 +14493,7 @@ function configuredComposeFiles(repoPath) {
|
|
|
14329
14493
|
const files = config.apps.filter(
|
|
14330
14494
|
(app) => app.runtime === "docker"
|
|
14331
14495
|
).flatMap((app) => app.docker.composeFiles).filter(
|
|
14332
|
-
(fileName) => !
|
|
14496
|
+
(fileName) => !import_node_path42.default.isAbsolute(fileName) && !import_node_path42.default.normalize(fileName).startsWith("..")
|
|
14333
14497
|
);
|
|
14334
14498
|
return Array.from(new Set(files));
|
|
14335
14499
|
} catch {
|
|
@@ -14347,7 +14511,7 @@ function composeFiles(repoPath) {
|
|
|
14347
14511
|
...configuredComposeFiles(repoPath)
|
|
14348
14512
|
];
|
|
14349
14513
|
return Array.from(new Set(candidates)).filter(
|
|
14350
|
-
(fileName) =>
|
|
14514
|
+
(fileName) => import_node_fs35.default.existsSync(import_node_path42.default.join(repoPath, fileName))
|
|
14351
14515
|
);
|
|
14352
14516
|
}
|
|
14353
14517
|
function stringArray2(value) {
|
|
@@ -14385,7 +14549,7 @@ function inspectServices(repoPath) {
|
|
|
14385
14549
|
const services = [];
|
|
14386
14550
|
for (const fileName of composeFiles(repoPath)) {
|
|
14387
14551
|
try {
|
|
14388
|
-
const parsed = import_yaml6.default.parse(
|
|
14552
|
+
const parsed = import_yaml6.default.parse(import_node_fs35.default.readFileSync(import_node_path42.default.join(repoPath, fileName), "utf-8"));
|
|
14389
14553
|
const serviceMap = asRecord3(asRecord3(parsed)?.services);
|
|
14390
14554
|
for (const [name, value] of Object.entries(serviceMap ?? {})) {
|
|
14391
14555
|
const service = asRecord3(value);
|
|
@@ -14420,8 +14584,8 @@ function inspectServices(repoPath) {
|
|
|
14420
14584
|
return services;
|
|
14421
14585
|
}
|
|
14422
14586
|
function inspectEnvFiles(repoPath) {
|
|
14423
|
-
const files =
|
|
14424
|
-
const content =
|
|
14587
|
+
const files = import_node_fs35.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
|
|
14588
|
+
const content = import_node_fs35.default.readFileSync(import_node_path42.default.join(repoPath, fileName), "utf-8");
|
|
14425
14589
|
const names = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && line.includes("=")).map((line) => line.split("=")[0]?.trim()).filter((name) => Boolean(name)).sort();
|
|
14426
14590
|
return { path: fileName, names };
|
|
14427
14591
|
});
|
|
@@ -14435,18 +14599,18 @@ function inspectEnvFiles(repoPath) {
|
|
|
14435
14599
|
};
|
|
14436
14600
|
}
|
|
14437
14601
|
function inspectDevcontainer(repoPath) {
|
|
14438
|
-
const dir =
|
|
14439
|
-
if (!
|
|
14602
|
+
const dir = import_node_path42.default.join(repoPath, ".devcontainer");
|
|
14603
|
+
if (!import_node_fs35.default.existsSync(dir)) {
|
|
14440
14604
|
return { exists: false, files: [] };
|
|
14441
14605
|
}
|
|
14442
14606
|
return {
|
|
14443
14607
|
exists: true,
|
|
14444
|
-
files:
|
|
14608
|
+
files: import_node_fs35.default.readdirSync(dir).filter((fileName) => import_node_fs35.default.statSync(import_node_path42.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
|
|
14445
14609
|
};
|
|
14446
14610
|
}
|
|
14447
14611
|
function inspectDevrouter(repoPath) {
|
|
14448
14612
|
const configPath = getRepoConfigPath(repoPath);
|
|
14449
|
-
if (!
|
|
14613
|
+
if (!import_node_fs35.default.existsSync(configPath)) {
|
|
14450
14614
|
return {
|
|
14451
14615
|
exists: false,
|
|
14452
14616
|
configPath,
|
|
@@ -14490,15 +14654,15 @@ function inspectAgentGuidance(repoPath) {
|
|
|
14490
14654
|
["AGENTS.md", "agents"],
|
|
14491
14655
|
["CLAUDE.md", "claude"]
|
|
14492
14656
|
]) {
|
|
14493
|
-
if (
|
|
14657
|
+
if (import_node_fs35.default.existsSync(import_node_path42.default.join(repoPath, fileName))) {
|
|
14494
14658
|
results.push({ path: fileName, kind });
|
|
14495
14659
|
}
|
|
14496
14660
|
}
|
|
14497
|
-
const skillsDir =
|
|
14498
|
-
if (
|
|
14499
|
-
for (const name of
|
|
14500
|
-
const skillPath =
|
|
14501
|
-
if (
|
|
14661
|
+
const skillsDir = import_node_path42.default.join(repoPath, ".agents", "skills");
|
|
14662
|
+
if (import_node_fs35.default.existsSync(skillsDir)) {
|
|
14663
|
+
for (const name of import_node_fs35.default.readdirSync(skillsDir).sort()) {
|
|
14664
|
+
const skillPath = import_node_path42.default.join(skillsDir, name, "SKILL.md");
|
|
14665
|
+
if (import_node_fs35.default.existsSync(skillPath)) {
|
|
14502
14666
|
results.push({ path: relative(repoPath, skillPath), kind: "skill" });
|
|
14503
14667
|
}
|
|
14504
14668
|
}
|
|
@@ -14541,7 +14705,7 @@ function buildIssues(report) {
|
|
|
14541
14705
|
}
|
|
14542
14706
|
function inspectRepo(options = {}) {
|
|
14543
14707
|
const repoPath = resolveRepoPath(options.repo);
|
|
14544
|
-
const pkg = readJson(
|
|
14708
|
+
const pkg = readJson(import_node_path42.default.join(repoPath, "package.json"));
|
|
14545
14709
|
const scripts = inspectScripts(pkg);
|
|
14546
14710
|
const reportWithoutIssues = {
|
|
14547
14711
|
repoPath,
|
|
@@ -14560,12 +14724,12 @@ function inspectRepo(options = {}) {
|
|
|
14560
14724
|
issues: buildIssues(reportWithoutIssues)
|
|
14561
14725
|
};
|
|
14562
14726
|
}
|
|
14563
|
-
var
|
|
14727
|
+
var import_node_fs35, import_node_path42, import_yaml6;
|
|
14564
14728
|
var init_repo_inspect = __esm({
|
|
14565
14729
|
"src/core/repo-inspect.ts"() {
|
|
14566
14730
|
"use strict";
|
|
14567
|
-
|
|
14568
|
-
|
|
14731
|
+
import_node_fs35 = __toESM(require("fs"));
|
|
14732
|
+
import_node_path42 = __toESM(require("path"));
|
|
14569
14733
|
import_yaml6 = __toESM(require("yaml"));
|
|
14570
14734
|
init_repo_config();
|
|
14571
14735
|
}
|
|
@@ -14670,7 +14834,7 @@ function requiredFileChecks(repoPath) {
|
|
|
14670
14834
|
".devcontainer/docker-compose.yml",
|
|
14671
14835
|
".devrouter.yml"
|
|
14672
14836
|
];
|
|
14673
|
-
const missing = required.filter((fileName) => !
|
|
14837
|
+
const missing = required.filter((fileName) => !import_node_fs36.default.existsSync(import_node_path43.default.join(repoPath, fileName)));
|
|
14674
14838
|
return {
|
|
14675
14839
|
id: "repo.devcontainer.verify-files",
|
|
14676
14840
|
level: missing.length === 0 ? "ok" : "error",
|
|
@@ -14895,12 +15059,12 @@ async function verifyDevcontainer(options = {}) {
|
|
|
14895
15059
|
nextSteps: collectNextSteps3(checks)
|
|
14896
15060
|
};
|
|
14897
15061
|
}
|
|
14898
|
-
var
|
|
15062
|
+
var import_node_fs36, import_node_path43;
|
|
14899
15063
|
var init_devcontainer_verify = __esm({
|
|
14900
15064
|
"src/core/devcontainer-verify.ts"() {
|
|
14901
15065
|
"use strict";
|
|
14902
|
-
|
|
14903
|
-
|
|
15066
|
+
import_node_fs36 = __toESM(require("fs"));
|
|
15067
|
+
import_node_path43 = __toESM(require("path"));
|
|
14904
15068
|
init_capabilities();
|
|
14905
15069
|
init_doctor();
|
|
14906
15070
|
init_host_routes();
|
|
@@ -15197,7 +15361,7 @@ function packageManagerIssues(repo) {
|
|
|
15197
15361
|
}
|
|
15198
15362
|
function plannedFiles(repoPath, version) {
|
|
15199
15363
|
const repo = inspectRepo({ repo: repoPath });
|
|
15200
|
-
const projectName = sanitizeProjectName(
|
|
15364
|
+
const projectName = sanitizeProjectName(import_node_path44.default.basename(repo.repoPath));
|
|
15201
15365
|
const nodeMajor = majorVersion(repo.node?.version, "24");
|
|
15202
15366
|
const pnpmVersion = repo.packageManager?.name === "pnpm" && repo.packageManager.version ? repo.packageManager.version : DEFAULT_PNPM_VERSION;
|
|
15203
15367
|
const port = inferPort2(repo);
|
|
@@ -15242,8 +15406,8 @@ function plannedFiles(repoPath, version) {
|
|
|
15242
15406
|
};
|
|
15243
15407
|
}
|
|
15244
15408
|
function classifyFile(repoPath, file) {
|
|
15245
|
-
const absolutePath =
|
|
15246
|
-
if (!
|
|
15409
|
+
const absolutePath = import_node_path44.default.join(repoPath, file.relativePath);
|
|
15410
|
+
if (!import_node_fs37.default.existsSync(absolutePath)) {
|
|
15247
15411
|
return {
|
|
15248
15412
|
path: file.relativePath,
|
|
15249
15413
|
action: "create",
|
|
@@ -15251,7 +15415,7 @@ function classifyFile(repoPath, file) {
|
|
|
15251
15415
|
bytes: Buffer.byteLength(file.content)
|
|
15252
15416
|
};
|
|
15253
15417
|
}
|
|
15254
|
-
const current =
|
|
15418
|
+
const current = import_node_fs37.default.readFileSync(absolutePath, "utf-8");
|
|
15255
15419
|
if (!current.includes(MANAGED_MARKER2)) {
|
|
15256
15420
|
return {
|
|
15257
15421
|
path: file.relativePath,
|
|
@@ -15308,11 +15472,11 @@ function buildPlan(repoPath, dryRun, version) {
|
|
|
15308
15472
|
};
|
|
15309
15473
|
}
|
|
15310
15474
|
function writeFile(repoPath, file) {
|
|
15311
|
-
const absolutePath =
|
|
15312
|
-
|
|
15313
|
-
|
|
15475
|
+
const absolutePath = import_node_path44.default.join(repoPath, file.relativePath);
|
|
15476
|
+
import_node_fs37.default.mkdirSync(import_node_path44.default.dirname(absolutePath), { recursive: true });
|
|
15477
|
+
import_node_fs37.default.writeFileSync(absolutePath, file.content, "utf-8");
|
|
15314
15478
|
if (file.executable) {
|
|
15315
|
-
|
|
15479
|
+
import_node_fs37.default.chmodSync(absolutePath, 493);
|
|
15316
15480
|
}
|
|
15317
15481
|
}
|
|
15318
15482
|
function writeDevcontainer(options = {}) {
|
|
@@ -15350,12 +15514,12 @@ function writeDevcontainer(options = {}) {
|
|
|
15350
15514
|
nextSteps: postWriteNextSteps(repoPath)
|
|
15351
15515
|
};
|
|
15352
15516
|
}
|
|
15353
|
-
var
|
|
15517
|
+
var import_node_fs37, import_node_path44, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
|
|
15354
15518
|
var init_devcontainer_write = __esm({
|
|
15355
15519
|
"src/core/devcontainer-write.ts"() {
|
|
15356
15520
|
"use strict";
|
|
15357
|
-
|
|
15358
|
-
|
|
15521
|
+
import_node_fs37 = __toESM(require("fs"));
|
|
15522
|
+
import_node_path44 = __toESM(require("path"));
|
|
15359
15523
|
init_repo_config();
|
|
15360
15524
|
init_repo_inspect();
|
|
15361
15525
|
MANAGED_MARKER2 = "devrouter:managed devcontainer";
|
|
@@ -15689,13 +15853,13 @@ function buildProfilePlanReport(options) {
|
|
|
15689
15853
|
return { ...report, contractPath, bindings };
|
|
15690
15854
|
}
|
|
15691
15855
|
function loadContract(repoPath, contractPath) {
|
|
15692
|
-
if (
|
|
15856
|
+
if (import_node_path45.default.isAbsolute(contractPath)) {
|
|
15693
15857
|
fail3("profile plan contract path must be relative to the repository.");
|
|
15694
15858
|
}
|
|
15695
15859
|
const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
|
|
15696
15860
|
let metadata;
|
|
15697
15861
|
try {
|
|
15698
|
-
metadata =
|
|
15862
|
+
metadata = import_node_fs38.default.lstatSync(absolutePath);
|
|
15699
15863
|
} catch (error) {
|
|
15700
15864
|
fail3(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
|
|
15701
15865
|
}
|
|
@@ -15705,16 +15869,16 @@ function loadContract(repoPath, contractPath) {
|
|
|
15705
15869
|
if (metadata.size > MAX_CONTRACT_BYTES) {
|
|
15706
15870
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
15707
15871
|
}
|
|
15708
|
-
const realRepo =
|
|
15709
|
-
const realContract =
|
|
15872
|
+
const realRepo = import_node_fs38.default.realpathSync(repoPath);
|
|
15873
|
+
const realContract = import_node_fs38.default.realpathSync(absolutePath);
|
|
15710
15874
|
assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
|
|
15711
|
-
const source =
|
|
15875
|
+
const source = import_node_fs38.default.readFileSync(absolutePath, "utf-8");
|
|
15712
15876
|
if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
|
|
15713
15877
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
15714
15878
|
}
|
|
15715
15879
|
return {
|
|
15716
15880
|
contract: parseProfilePlanContract(source),
|
|
15717
|
-
contractPath:
|
|
15881
|
+
contractPath: import_node_path45.default.relative(repoPath, absolutePath).split(import_node_path45.default.sep).join("/")
|
|
15718
15882
|
};
|
|
15719
15883
|
}
|
|
15720
15884
|
function resolveProfilePlan(options) {
|
|
@@ -15728,12 +15892,12 @@ function resolveProfilePlan(options) {
|
|
|
15728
15892
|
contractPath: loaded.contractPath
|
|
15729
15893
|
});
|
|
15730
15894
|
}
|
|
15731
|
-
var
|
|
15895
|
+
var import_node_fs38, import_node_path45, import_yaml7, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
|
|
15732
15896
|
var init_profile_plan = __esm({
|
|
15733
15897
|
"src/core/profile-plan.ts"() {
|
|
15734
15898
|
"use strict";
|
|
15735
|
-
|
|
15736
|
-
|
|
15899
|
+
import_node_fs38 = __toESM(require("fs"));
|
|
15900
|
+
import_node_path45 = __toESM(require("path"));
|
|
15737
15901
|
import_yaml7 = require("yaml");
|
|
15738
15902
|
init_paths();
|
|
15739
15903
|
init_profile_resolution();
|
|
@@ -15765,7 +15929,7 @@ async function runProfileResolveCommand(options) {
|
|
|
15765
15929
|
async function runProfilePlanCommand(options) {
|
|
15766
15930
|
const report = resolveProfilePlan(options);
|
|
15767
15931
|
if (options.output) {
|
|
15768
|
-
writeFileAtomically(
|
|
15932
|
+
writeFileAtomically(import_node_path46.default.resolve(options.output), `${JSON.stringify(report, null, 2)}
|
|
15769
15933
|
`);
|
|
15770
15934
|
}
|
|
15771
15935
|
if (options.json) {
|
|
@@ -15801,14 +15965,14 @@ function printProfilePlanSummary(report, output2) {
|
|
|
15801
15965
|
process.stdout.write(`Binding ${key}: ${renderValues(report.bindings[key] ?? [])}
|
|
15802
15966
|
`);
|
|
15803
15967
|
}
|
|
15804
|
-
if (output2) process.stdout.write(`Output: ${
|
|
15968
|
+
if (output2) process.stdout.write(`Output: ${import_node_path46.default.resolve(output2)}
|
|
15805
15969
|
`);
|
|
15806
15970
|
}
|
|
15807
|
-
var
|
|
15971
|
+
var import_node_path46;
|
|
15808
15972
|
var init_profile = __esm({
|
|
15809
15973
|
"src/commands/profile.ts"() {
|
|
15810
15974
|
"use strict";
|
|
15811
|
-
|
|
15975
|
+
import_node_path46 = __toESM(require("path"));
|
|
15812
15976
|
init_atomic_file();
|
|
15813
15977
|
init_output();
|
|
15814
15978
|
init_profile_plan();
|
|
@@ -16098,7 +16262,7 @@ function sanitizeRouterId(value) {
|
|
|
16098
16262
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
16099
16263
|
}
|
|
16100
16264
|
function repoHash(repoPath) {
|
|
16101
|
-
return (0, import_node_crypto17.createHash)("sha1").update(
|
|
16265
|
+
return (0, import_node_crypto17.createHash)("sha1").update(import_node_path47.default.resolve(repoPath)).digest("hex").slice(0, 12);
|
|
16102
16266
|
}
|
|
16103
16267
|
function asDockerApp(app) {
|
|
16104
16268
|
return app.runtime === "docker";
|
|
@@ -16177,11 +16341,11 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
|
|
|
16177
16341
|
if (dockerApps.length === 0) {
|
|
16178
16342
|
throw new Error("No docker apps selected to prepare compose overlay.");
|
|
16179
16343
|
}
|
|
16180
|
-
const cachePath =
|
|
16181
|
-
|
|
16182
|
-
const overlayPath =
|
|
16344
|
+
const cachePath = import_node_path47.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
|
|
16345
|
+
import_node_fs39.default.mkdirSync(cachePath, { recursive: true });
|
|
16346
|
+
const overlayPath = import_node_path47.default.join(cachePath, "compose.devrouter.yml");
|
|
16183
16347
|
const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
|
|
16184
|
-
|
|
16348
|
+
import_node_fs39.default.writeFileSync(overlayPath, import_yaml8.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
|
|
16185
16349
|
return {
|
|
16186
16350
|
overlayPath,
|
|
16187
16351
|
composeFiles: ensureComposeFiles(dockerApps),
|
|
@@ -16301,14 +16465,14 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
16301
16465
|
const port = Number(match[1]);
|
|
16302
16466
|
return Number.isInteger(port) && port > 0 ? port : void 0;
|
|
16303
16467
|
}
|
|
16304
|
-
var import_node_child_process27, import_node_crypto17,
|
|
16468
|
+
var import_node_child_process27, import_node_crypto17, import_node_fs39, import_node_path47, import_yaml8;
|
|
16305
16469
|
var init_docker_run = __esm({
|
|
16306
16470
|
"src/core/docker-run.ts"() {
|
|
16307
16471
|
"use strict";
|
|
16308
16472
|
import_node_child_process27 = require("child_process");
|
|
16309
16473
|
import_node_crypto17 = require("crypto");
|
|
16310
|
-
|
|
16311
|
-
|
|
16474
|
+
import_node_fs39 = __toESM(require("fs"));
|
|
16475
|
+
import_node_path47 = __toESM(require("path"));
|
|
16312
16476
|
import_yaml8 = __toESM(require("yaml"));
|
|
16313
16477
|
init_docker_error_guidance();
|
|
16314
16478
|
init_paths();
|
|
@@ -17130,13 +17294,13 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
17130
17294
|
const startedAt = Date.now();
|
|
17131
17295
|
let rootStat;
|
|
17132
17296
|
try {
|
|
17133
|
-
rootStat =
|
|
17297
|
+
rootStat = import_node_fs40.default.lstatSync(worktreePath);
|
|
17134
17298
|
} catch (error) {
|
|
17135
17299
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
17136
17300
|
}
|
|
17137
17301
|
let rootEntries;
|
|
17138
17302
|
try {
|
|
17139
|
-
rootEntries =
|
|
17303
|
+
rootEntries = import_node_fs40.default.readdirSync(worktreePath, { withFileTypes: true });
|
|
17140
17304
|
} catch (error) {
|
|
17141
17305
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
17142
17306
|
}
|
|
@@ -17162,10 +17326,10 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
17162
17326
|
timedOut = true;
|
|
17163
17327
|
break;
|
|
17164
17328
|
}
|
|
17165
|
-
const entryPath =
|
|
17329
|
+
const entryPath = import_node_path48.default.join(dirPath, entry.name);
|
|
17166
17330
|
let entryStat;
|
|
17167
17331
|
try {
|
|
17168
|
-
entryStat =
|
|
17332
|
+
entryStat = import_node_fs40.default.lstatSync(entryPath);
|
|
17169
17333
|
} catch (error) {
|
|
17170
17334
|
if (error?.code === "ENOENT") continue;
|
|
17171
17335
|
unreadableReason = describeIncompleteWalk(error);
|
|
@@ -17175,7 +17339,7 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
17175
17339
|
if (!entryStat.isDirectory()) continue;
|
|
17176
17340
|
let childEntries;
|
|
17177
17341
|
try {
|
|
17178
|
-
childEntries =
|
|
17342
|
+
childEntries = import_node_fs40.default.readdirSync(entryPath, { withFileTypes: true });
|
|
17179
17343
|
} catch (error) {
|
|
17180
17344
|
unreadableReason = describeIncompleteWalk(error);
|
|
17181
17345
|
break;
|
|
@@ -17239,12 +17403,12 @@ function describeError(error, worktreePath) {
|
|
|
17239
17403
|
}
|
|
17240
17404
|
return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
|
|
17241
17405
|
}
|
|
17242
|
-
var
|
|
17406
|
+
var import_node_fs40, import_node_path48, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
|
|
17243
17407
|
var init_workspace_consumption = __esm({
|
|
17244
17408
|
"src/core/workspace-consumption.ts"() {
|
|
17245
17409
|
"use strict";
|
|
17246
|
-
|
|
17247
|
-
|
|
17410
|
+
import_node_fs40 = __toESM(require("fs"));
|
|
17411
|
+
import_node_path48 = __toESM(require("path"));
|
|
17248
17412
|
init_devpod_environment();
|
|
17249
17413
|
DEFAULT_DEADLINE_MS = 1e4;
|
|
17250
17414
|
BLOCK_SIZE_BYTES = 512;
|
|
@@ -17345,7 +17509,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
|
|
|
17345
17509
|
}
|
|
17346
17510
|
function readGitSnapshot(worktree, commandRunner) {
|
|
17347
17511
|
const comparablePath = comparableWorkspacePath(worktree.path);
|
|
17348
|
-
if (worktree.prunable || !
|
|
17512
|
+
if (worktree.prunable || !import_node_fs41.default.existsSync(comparablePath)) {
|
|
17349
17513
|
return { worktree, checkout: "missing", head: null, committerDate: null };
|
|
17350
17514
|
}
|
|
17351
17515
|
const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
|
|
@@ -17870,7 +18034,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
17870
18034
|
containers2 = measureContainersFn(worktreePaths);
|
|
17871
18035
|
} catch (error) {
|
|
17872
18036
|
const reason = `container measurement failed: ${describeCause(error)}`;
|
|
17873
|
-
containers2 = new Map(worktreePaths.map((
|
|
18037
|
+
containers2 = new Map(worktreePaths.map((path49) => [path49, unknownContainers(reason)]));
|
|
17874
18038
|
}
|
|
17875
18039
|
}
|
|
17876
18040
|
const rows = records.map((record4) => {
|
|
@@ -17919,12 +18083,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
17919
18083
|
workspaces: rows
|
|
17920
18084
|
};
|
|
17921
18085
|
}
|
|
17922
|
-
var import_node_child_process29,
|
|
18086
|
+
var import_node_child_process29, import_node_fs41, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
|
|
17923
18087
|
var init_workspace_cleanup = __esm({
|
|
17924
18088
|
"src/core/workspace-cleanup.ts"() {
|
|
17925
18089
|
"use strict";
|
|
17926
18090
|
import_node_child_process29 = require("child_process");
|
|
17927
|
-
|
|
18091
|
+
import_node_fs41 = __toESM(require("fs"));
|
|
17928
18092
|
init_devpod_workspaces();
|
|
17929
18093
|
init_host_routes();
|
|
17930
18094
|
init_repo_config();
|
|
@@ -17971,7 +18135,7 @@ function warnMissingWorkspaceOwnership(repoPath) {
|
|
|
17971
18135
|
);
|
|
17972
18136
|
}
|
|
17973
18137
|
function defaultWorktreePath(mainRepo, ws) {
|
|
17974
|
-
return
|
|
18138
|
+
return import_node_path49.default.join(mainRepo, "trees", ws);
|
|
17975
18139
|
}
|
|
17976
18140
|
function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
17977
18141
|
const ignored = (0, import_node_child_process30.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
|
|
@@ -17979,12 +18143,12 @@ function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
|
17979
18143
|
});
|
|
17980
18144
|
if (ignored.status !== 0) {
|
|
17981
18145
|
throw new Error(
|
|
17982
|
-
`Default worktree root '${
|
|
18146
|
+
`Default worktree root '${import_node_path49.default.join(mainRepo, "trees")}' is not ignored. Add 'trees/' to '${import_node_path49.default.join(mainRepo, ".gitignore")}' or use --path.`
|
|
17983
18147
|
);
|
|
17984
18148
|
}
|
|
17985
18149
|
}
|
|
17986
18150
|
function legacyDefaultWorktreePath(mainRepo, ws) {
|
|
17987
|
-
return
|
|
18151
|
+
return import_node_path49.default.join(import_node_path49.default.dirname(mainRepo), `${import_node_path49.default.basename(mainRepo)}-${ws}`);
|
|
17988
18152
|
}
|
|
17989
18153
|
function teardownFallbackPath(mainRepo, workspace) {
|
|
17990
18154
|
const candidates = [
|
|
@@ -18096,7 +18260,7 @@ function assertFullDownPreflight(mainRepo, target) {
|
|
|
18096
18260
|
if (sameWorkspacePath(target.worktreePath, mainRepo)) {
|
|
18097
18261
|
throw new Error("Refusing to remove the primary Git checkout.");
|
|
18098
18262
|
}
|
|
18099
|
-
if (!target.worktree || target.worktree.prunable || !
|
|
18263
|
+
if (!target.worktree || target.worktree.prunable || !import_node_fs42.default.existsSync(target.worktreePath)) return;
|
|
18100
18264
|
if (target.worktree.locked) {
|
|
18101
18265
|
throw new Error(
|
|
18102
18266
|
`Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
|
|
@@ -18136,9 +18300,9 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
18136
18300
|
(candidate) => defaultWorktreePath(mainRepo, candidate)
|
|
18137
18301
|
);
|
|
18138
18302
|
const generatedPath = candidatePaths.find(
|
|
18139
|
-
(candidate) => !
|
|
18303
|
+
(candidate) => !import_node_fs42.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
|
|
18140
18304
|
);
|
|
18141
|
-
const selectedPath = opts.path ?
|
|
18305
|
+
const selectedPath = opts.path ? import_node_path49.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
|
|
18142
18306
|
if (!selectedPath) {
|
|
18143
18307
|
throw new Error(`Could not allocate a collision-safe worktree path for branch '${branch}'.`);
|
|
18144
18308
|
}
|
|
@@ -18147,7 +18311,7 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
18147
18311
|
`Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
|
|
18148
18312
|
);
|
|
18149
18313
|
}
|
|
18150
|
-
if (
|
|
18314
|
+
if (import_node_fs42.default.existsSync(selectedPath)) {
|
|
18151
18315
|
const registered = worktrees.find(
|
|
18152
18316
|
(worktree) => sameWorkspacePath(worktree.path, selectedPath)
|
|
18153
18317
|
);
|
|
@@ -18287,7 +18451,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
18287
18451
|
}
|
|
18288
18452
|
const result = await mutateWorkspaceRuntime("delete", resolved, worktrees, opts.quiet);
|
|
18289
18453
|
if (removeWorktree) {
|
|
18290
|
-
if (resolved.worktree && !resolved.worktree.prunable &&
|
|
18454
|
+
if (resolved.worktree && !resolved.worktree.prunable && import_node_fs42.default.existsSync(resolved.worktreePath)) {
|
|
18291
18455
|
const rm = (0, import_node_child_process30.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
|
|
18292
18456
|
encoding: "utf-8"
|
|
18293
18457
|
});
|
|
@@ -18306,7 +18470,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
18306
18470
|
}
|
|
18307
18471
|
return result;
|
|
18308
18472
|
};
|
|
18309
|
-
return resolved.worktree && !resolved.worktree.prunable &&
|
|
18473
|
+
return resolved.worktree && !resolved.worktree.prunable && import_node_fs42.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
|
|
18310
18474
|
}
|
|
18311
18475
|
async function workspaceStop(target, opts = {}) {
|
|
18312
18476
|
return runWorkspaceLifecycle("stop", target, opts);
|
|
@@ -18314,13 +18478,13 @@ async function workspaceStop(target, opts = {}) {
|
|
|
18314
18478
|
async function workspaceDown(target, opts = {}) {
|
|
18315
18479
|
return runWorkspaceLifecycle("down", target, opts);
|
|
18316
18480
|
}
|
|
18317
|
-
var import_node_child_process30,
|
|
18481
|
+
var import_node_child_process30, import_node_fs42, import_node_path49, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
|
|
18318
18482
|
var init_workspace_lifecycle = __esm({
|
|
18319
18483
|
"src/core/workspace-lifecycle.ts"() {
|
|
18320
18484
|
"use strict";
|
|
18321
18485
|
import_node_child_process30 = require("child_process");
|
|
18322
|
-
|
|
18323
|
-
|
|
18486
|
+
import_node_fs42 = __toESM(require("fs"));
|
|
18487
|
+
import_node_path49 = __toESM(require("path"));
|
|
18324
18488
|
init_devpod_mutation();
|
|
18325
18489
|
init_devpod_workspaces();
|
|
18326
18490
|
init_managed_runtime_state();
|
|
@@ -18479,7 +18643,7 @@ var init_version = __esm({
|
|
|
18479
18643
|
|
|
18480
18644
|
// src/cli.ts
|
|
18481
18645
|
var import_commander = require("commander");
|
|
18482
|
-
var CLI_VERSION = true ? "0.0.
|
|
18646
|
+
var CLI_VERSION = true ? "0.0.62" : "0.0.0-dev";
|
|
18483
18647
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
18484
18648
|
function withErrorHandling(action2) {
|
|
18485
18649
|
return async (...args) => {
|