@devrouter/cli 0.0.46 → 0.0.49
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 +11 -0
- package/dist/devrouter.js +1079 -337
- package/package.json +1 -1
- package/upgrade-prompts/0.0.48.md +44 -0
- package/upgrade-prompts/0.0.49.md +26 -0
package/dist/devrouter.js
CHANGED
|
@@ -44,6 +44,7 @@ function buildDevrouterSection() {
|
|
|
44
44
|
"",
|
|
45
45
|
"Quick validation sequence:",
|
|
46
46
|
"- Managed devcontainer consumer images contain no devrouter package or helper; `devrouter ensure` delivers the matching helper at runtime.",
|
|
47
|
+
"- Devsy runtime: run `devrouter setup --yes --workspace-runtime devsy` once; `doctor` then reports verified agent readiness without network access.",
|
|
47
48
|
"- `devrouter up`",
|
|
48
49
|
"- `devrouter tls install` (required when repo defines tcp/postgres apps)",
|
|
49
50
|
"- `devrouter app ls --repo .`",
|
|
@@ -322,7 +323,7 @@ Run several worktrees of one repo in parallel without host/route collisions. A *
|
|
|
322
323
|
- **When active**: hosts auto-namespace (\`web.localhost\` \u2192 \`web.<ws>.localhost\`), \`\${WORKSPACE}\` in \`upstream\` is substituted with the token, and the docker \`router\` key is suffixed per workspace. Managed \`ensure\` rejects every HTTP/TCP proxy upstream outside that exact alias namespace before it mutates DevPod or routes. The runtime config is computed in memory only \u2014 the committed \`.devrouter.yml\` is never rewritten.
|
|
323
324
|
- **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.
|
|
324
325
|
- **devcontainer integration**: managed scaffolds list the base compose file, then \`\${localEnv:DEVCONTAINER_COMPOSE_OVERLAY:docker-compose.default.yml}\`; custom repositories may keep another default overlay. Selecting \`.devcontainer/docker-compose.devrouter.yml\` for linked worktrees must pass \`WORKSPACE\` and \`DEVROUTER_WORKSPACE\` across the combined base/overlay config and bind-mount \`\${DEVROUTER_GIT_COMMON_DIR}\` to the same absolute app-container path. The app exposes \`\${WORKSPACE}-app\`; the proxy uses \`upstream: \${WORKSPACE}-app:<port>\`.
|
|
325
|
-
- **Lifecycle**: after one-time \`setup\`, use \`ensure .\` for both primary and linked checkouts; never branch manually on checkout kind or use live verify as startup. \`stop .\` is non-destructive; \`stop . --delete\` is explicit exact-owner cleanup without worktree removal; and \`exec . -- <command...>\` runs one-shot commands only in the exact running workspace runtime (DevPod or Devsy). Never substitute raw \`devpod up\`, \`stop\`, \`delete\`, or the Devsy equivalents: they bypass devrouter's 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. Runtime selection is path-aware: \`DEVROUTER_WORKSPACE_RUNTIME=devpod|devsy\` forces one runtime, an exact-path registry owner wins next, then the machine preference from \`devrouter setup --workspace-runtime\`, then installed-CLI auto-detection. \`workspace up\` creates linked worktrees; destructive worktree removal and GC remain ledger-scoped. Dirty or locked full down fails before side effects.
|
|
326
|
+
- **Lifecycle**: after one-time \`setup\`, use \`ensure .\` for both primary and linked checkouts; never branch manually on checkout kind or use live verify as startup. \`stop .\` is non-destructive; \`stop . --delete\` is explicit exact-owner cleanup without worktree removal; and \`exec . -- <command...>\` runs one-shot commands only in the exact running workspace runtime (DevPod or Devsy). Never substitute raw \`devpod up\`, \`stop\`, \`delete\`, or the Devsy equivalents: they bypass devrouter's 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. Runtime selection is path-aware: \`DEVROUTER_WORKSPACE_RUNTIME=devpod|devsy\` forces one runtime, an exact-path registry owner wins next, then the machine preference from \`devrouter setup --workspace-runtime\`, then installed-CLI auto-detection. \`workspace up\` creates linked worktrees; destructive worktree removal and GC remain ledger-scoped. Dirty or locked full down fails before side effects. For Devsy, 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.
|
|
326
327
|
- **Managed process identity**: \`ensure\` executes an exact captured adapter snapshot. Default reuse includes command argv, workspace, and adapter SHA-256. Set \`DEVROUTER_PROCESS_FINGERPRINT_ENV\` only to comma-separated non-secret environment names whose values affect runtime identity; secret-like names are rejected and raw values are never persisted.
|
|
327
328
|
- **Route state**: the versioned Traefik dynamic file is authoritative for both metadata and rendering. JSON is a compatibility mirror; valid headerless generations migrate automatically, while corrupt canonical metadata fails closed.
|
|
328
329
|
- **Cleanup**: \`workspace cleanup --repo . --inactive-for 30d --json\` is a report-only, no-\`--yes\` report for managed linked workspaces. It joins ownership (\`present|missing|locked|conflict\`), workspace runtime registration, runtime state (\`running|stopped|busy|not-found|absent|unknown\`), checkout, route, advisory activity, and integration evidence without mutating the workspace runtime, routes, ownership, Git, Docker, applications, worktrees, or branches. Local DevPod/Devsy list/status checks always run; \`--check-merged\` alone enables read-only origin and matching GitHub/GitLab checks. Treat \`not-found\` as stale runtime after Docker pruning; busy, unavailable, or conflicting evidence suppresses destructive suggestions. Explicit \`gc\`/\`down\` can remove exact stale registration only after expected-ID \`NotFound\` proof and ownership revalidation. GC never removes Git worktrees, branches, or prune state. Git has no worktree-removal hook.
|
|
@@ -376,8 +377,9 @@ Run several worktrees of one repo in parallel without host/route collisions. A *
|
|
|
376
377
|
- \`devrouter init [--write-agents] [--write-skill]\`: print AI onboarding prompt (non-mutating by default)
|
|
377
378
|
- \`devrouter -V [--repo .]\`: show installed CLI version, local repo version, and next upgrade target
|
|
378
379
|
- \`devrouter upgrade [version] [--repo .]\`: list upgrade targets or print target Agent Adaptation Prompt
|
|
379
|
-
- \`devrouter setup --yes [--repo .] [--json]\`: first-run machine setup plus structured diagnostics
|
|
380
|
+
- \`devrouter setup --yes [--repo .] [--json] [--workspace-runtime <devpod|devsy>]\`: first-run machine setup plus structured diagnostics; explicit Devsy selection acquires its verified agent
|
|
380
381
|
- \`devrouter ensure [path] [--profile <name>] [--open] [--json]\`: canonical startup/reconciliation for primary and linked checkouts; a managed profile selects its independent resource dimensions
|
|
382
|
+
- \`devrouter profile resolve --repo <path> [--profile <selection>] [--json]\`: resolve exact profile resources for automation without starting or inspecting a runtime
|
|
381
383
|
- \`devrouter stop [path] [--delete] [--json]\`: stop the exact workspace runtime and remove exact routes; \`--delete\` explicitly deletes its ownership-proven data without removing the checkout
|
|
382
384
|
- \`devrouter exec [path] -- <command...>\`: literal one-shot command inside the exact running workspace runtime
|
|
383
385
|
- \`devrouter up\` / \`devrouter down\`: start/stop shared Traefik router
|
|
@@ -411,6 +413,9 @@ Run several worktrees of one repo in parallel without host/route collisions. A *
|
|
|
411
413
|
|
|
412
414
|
For devcontainer onboarding:
|
|
413
415
|
|
|
416
|
+
When Devsy owns the workspace, first run
|
|
417
|
+
\`devrouter setup --repo . --yes --workspace-runtime devsy --json\`.
|
|
418
|
+
|
|
414
419
|
1. \`devrouter setup --repo . --yes --json\`
|
|
415
420
|
2. \`devrouter doctor --repo . --json\`
|
|
416
421
|
3. \`devrouter repo inspect --repo . --json\`
|
|
@@ -720,6 +725,9 @@ function runDockerCompose(args) {
|
|
|
720
725
|
function startRouterStack() {
|
|
721
726
|
runDockerCompose(["up", "-d"]);
|
|
722
727
|
}
|
|
728
|
+
function restartRouterStack() {
|
|
729
|
+
runDockerCompose(["restart", "traefik"]);
|
|
730
|
+
}
|
|
723
731
|
function stopRouterStack() {
|
|
724
732
|
runDockerCompose(["down"]);
|
|
725
733
|
}
|
|
@@ -1406,6 +1414,12 @@ function hostRouteFromInput(input2, existing, now) {
|
|
|
1406
1414
|
function sanitizeKey(value) {
|
|
1407
1415
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1408
1416
|
}
|
|
1417
|
+
function buildHostRouteRouterKey(routeId) {
|
|
1418
|
+
return `host-${sanitizeKey(routeId)}`;
|
|
1419
|
+
}
|
|
1420
|
+
function buildHostRouteRouterName(repoPath, name) {
|
|
1421
|
+
return `${buildHostRouteRouterKey(buildHostRouteId(repoPath, name))}@file`;
|
|
1422
|
+
}
|
|
1409
1423
|
function tcpTlsOptionName(tcpProtocol) {
|
|
1410
1424
|
return `devrouter-tcp-${tcpProtocol}`;
|
|
1411
1425
|
}
|
|
@@ -1416,7 +1430,7 @@ function buildHostRoutesDocument(routes, tlsEnabled) {
|
|
|
1416
1430
|
const tcpServices = {};
|
|
1417
1431
|
const tlsOptions = {};
|
|
1418
1432
|
for (const route of routes) {
|
|
1419
|
-
const key =
|
|
1433
|
+
const key = buildHostRouteRouterKey(route.id);
|
|
1420
1434
|
if (route.protocol === "tcp") {
|
|
1421
1435
|
const alpn = route.tcpProtocol ? TCP_TLS_ALPN_PROTOCOLS[route.tcpProtocol] : void 0;
|
|
1422
1436
|
let tls = {};
|
|
@@ -2654,7 +2668,7 @@ function loadRepoConfig(repoPath) {
|
|
|
2654
2668
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
2655
2669
|
const requiredVersion = config.devrouter?.version;
|
|
2656
2670
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
2657
|
-
const cliVersion = true ? "0.0.
|
|
2671
|
+
const cliVersion = true ? "0.0.49" : "0.0.0-dev";
|
|
2658
2672
|
if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
|
|
2659
2673
|
hasWarnedVersionMismatch = true;
|
|
2660
2674
|
process.stderr.write(
|
|
@@ -3185,6 +3199,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
3185
3199
|
`- 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.`,
|
|
3186
3200
|
"- 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.",
|
|
3187
3201
|
"- 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.",
|
|
3202
|
+
"- 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.",
|
|
3188
3203
|
"- A managed adapter plus `postCreateCommand` requires `waitFor` exactly `postCreateCommand` or `postStartCommand`; managed selective config preserves lifecycle fields and changes only `runServices`.",
|
|
3189
3204
|
"- Managed process reuse fingerprints command argv, workspace identity, and the exact adapter snapshot. If a non-secret runtime value also affects reuse, set `DEVROUTER_PROCESS_FINGERPRINT_ENV` to its comma-separated environment names; secret-like names are rejected and raw values are never persisted.",
|
|
3190
3205
|
"- Owner status is `present`, `missing`, `locked`, or `conflict`. Dirty or locked full down fails before side effects. `workspace gc` is a dry run; only `--yes` deletes exact eligible missing resources and records, never Git worktrees, branches, or prune state.",
|
|
@@ -3216,7 +3231,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
3216
3231
|
"- Warning: Do not run migration/seed until env probe confirms expected DB variables and values.",
|
|
3217
3232
|
"",
|
|
3218
3233
|
"Required workflow:",
|
|
3219
|
-
"1) Run `devrouter setup --yes --json` for devrouter-owned machine state
|
|
3234
|
+
"1) Run `devrouter setup --yes --json` for devrouter-owned machine state. When selecting Devsy, use `devrouter setup --yes --workspace-runtime devsy --json` to acquire its verified agent. Use `devrouter doctor --repo <REPO_PATH> --json` to diagnose missing prerequisites without mutation.",
|
|
3220
3235
|
"2) Run `devrouter repo inspect --repo <REPO_PATH> --json` before editing files.",
|
|
3221
3236
|
"3) For the supported Node/pnpm/Postgres devcontainer shape, run `devrouter repo devcontainer write --repo <REPO_PATH> --dry-run --json`, review the plan, then run `devrouter repo devcontainer write --repo <REPO_PATH> --yes`.",
|
|
3222
3237
|
"4) For unsupported shapes or custom existing files, make minimal manual edits and explain the assumptions.",
|
|
@@ -3226,6 +3241,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
3226
3241
|
"",
|
|
3227
3242
|
"Validation commands to run/report for the devcontainer path:",
|
|
3228
3243
|
"- devrouter setup --yes --json",
|
|
3244
|
+
"- Devsy runtime setup: devrouter setup --yes --workspace-runtime devsy --json",
|
|
3229
3245
|
"- devrouter doctor --repo <REPO_PATH> --json",
|
|
3230
3246
|
"- devrouter repo inspect --repo <REPO_PATH> --json",
|
|
3231
3247
|
"- devrouter repo devcontainer write --repo <REPO_PATH> --dry-run --json",
|
|
@@ -3353,6 +3369,10 @@ var init_ai_prompt = __esm({
|
|
|
3353
3369
|
command: "devrouter ensure [path] [--profile <name>] [--open] [--json]",
|
|
3354
3370
|
purpose: "Canonical startup and proof for an exact primary or linked checkout; atomically publishes routes after readiness."
|
|
3355
3371
|
},
|
|
3372
|
+
{
|
|
3373
|
+
command: "devrouter profile resolve --repo <path> [--profile <selection>] [--json]",
|
|
3374
|
+
purpose: "Resolve exact profile resources without starting or inspecting a runtime; fail closed on invalid selections or unknown report schemas."
|
|
3375
|
+
},
|
|
3356
3376
|
{
|
|
3357
3377
|
command: "devrouter stop [path] [--delete] [--json]",
|
|
3358
3378
|
purpose: "Stop the exact checkout workspace runtime and routes; --delete explicitly deletes the ownership-proven runtime without removing the checkout."
|
|
@@ -4055,6 +4075,384 @@ var init_upgrade2 = __esm({
|
|
|
4055
4075
|
}
|
|
4056
4076
|
});
|
|
4057
4077
|
|
|
4078
|
+
// src/core/devsy-agent.ts
|
|
4079
|
+
function parseVersion(output2) {
|
|
4080
|
+
return output2?.match(
|
|
4081
|
+
/\bv?(\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)\b/
|
|
4082
|
+
)?.[1];
|
|
4083
|
+
}
|
|
4084
|
+
function devsyAgentRepairSuggestion(inspection) {
|
|
4085
|
+
if (inspection.state === "stale") {
|
|
4086
|
+
return `Install Devsy ${SUPPORTED_DEVSY_VERSION} for a supported host, then run: ${DEVSY_AGENT_SETUP_COMMAND}`;
|
|
4087
|
+
}
|
|
4088
|
+
return inspection.source === "explicit" ? `Fix or unset DEVSY_AGENT_BINARY, then run: ${DEVSY_AGENT_SETUP_COMMAND}` : `Run: ${DEVSY_AGENT_SETUP_COMMAND}`;
|
|
4089
|
+
}
|
|
4090
|
+
function installedVersionOutput() {
|
|
4091
|
+
const result = (0, import_node_child_process4.spawnSync)("devsy", ["--version"], { encoding: "utf-8" });
|
|
4092
|
+
if (result.error || result.status !== 0) return void 0;
|
|
4093
|
+
return [result.stdout, result.stderr].filter(Boolean).join("\n").trim() || void 0;
|
|
4094
|
+
}
|
|
4095
|
+
function nativeAssetName(platform, arch) {
|
|
4096
|
+
if (platform !== "darwin") return void 0;
|
|
4097
|
+
if (arch === "arm64") return "devsy-linux-arm64";
|
|
4098
|
+
if (arch === "x64") return "devsy-linux-amd64";
|
|
4099
|
+
return void 0;
|
|
4100
|
+
}
|
|
4101
|
+
function managedBinaryPath(cacheRoot, asset) {
|
|
4102
|
+
return import_node_path9.default.join(cacheRoot, `v${SUPPORTED_DEVSY_VERSION}`, asset.name);
|
|
4103
|
+
}
|
|
4104
|
+
function hashOpenFile(file) {
|
|
4105
|
+
const digest = (0, import_node_crypto4.createHash)("sha256");
|
|
4106
|
+
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
4107
|
+
let position = 0;
|
|
4108
|
+
for (; ; ) {
|
|
4109
|
+
const bytesRead = import_node_fs9.default.readSync(file, buffer, 0, buffer.length, position);
|
|
4110
|
+
if (bytesRead === 0) break;
|
|
4111
|
+
digest.update(buffer.subarray(0, bytesRead));
|
|
4112
|
+
position += bytesRead;
|
|
4113
|
+
}
|
|
4114
|
+
return digest.digest("hex");
|
|
4115
|
+
}
|
|
4116
|
+
function inspectBinary(binaryPath, assets, missingState) {
|
|
4117
|
+
let file;
|
|
4118
|
+
try {
|
|
4119
|
+
file = import_node_fs9.default.openSync(binaryPath, import_node_fs9.default.constants.O_RDONLY | import_node_fs9.default.constants.O_NOFOLLOW);
|
|
4120
|
+
const stat = import_node_fs9.default.fstatSync(file);
|
|
4121
|
+
if (!stat.isFile()) {
|
|
4122
|
+
return { state: "invalid", reason: "the selected source is not a regular file" };
|
|
4123
|
+
}
|
|
4124
|
+
const sizeMatches = assets.filter((asset2) => asset2.size === stat.size);
|
|
4125
|
+
if (sizeMatches.length === 0) {
|
|
4126
|
+
return { state: "invalid", reason: "the selected source has an unexpected size" };
|
|
4127
|
+
}
|
|
4128
|
+
const sha2562 = hashOpenFile(file);
|
|
4129
|
+
const asset = sizeMatches.find((candidate) => candidate.sha256 === sha2562);
|
|
4130
|
+
return asset ? { state: "ready", reason: "the selected source matches the pinned manifest", asset } : { state: "invalid", reason: "the selected source has an unexpected digest" };
|
|
4131
|
+
} catch (error) {
|
|
4132
|
+
const code = error.code;
|
|
4133
|
+
return code === "ENOENT" ? { state: missingState, reason: "the selected source is missing" } : { state: "invalid", reason: "the selected source is not a readable regular file" };
|
|
4134
|
+
} finally {
|
|
4135
|
+
if (file !== void 0) import_node_fs9.default.closeSync(file);
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
function resolvedOptions(options) {
|
|
4139
|
+
return {
|
|
4140
|
+
env: options.env ?? process.env,
|
|
4141
|
+
platform: options.platform ?? process.platform,
|
|
4142
|
+
arch: options.arch ?? process.arch,
|
|
4143
|
+
versionOutput: options.versionOutput ?? installedVersionOutput(),
|
|
4144
|
+
cacheRoot: options.cacheRoot ?? import_node_path9.default.join(CACHE_DIR, "devsy", "agents"),
|
|
4145
|
+
lockPath: options.lockPath ?? DEVSY_AGENT_CACHE_LOCK,
|
|
4146
|
+
assets: options.assets ?? DEVSY_AGENT_ASSETS,
|
|
4147
|
+
nativeAssetName: options.nativeAssetName
|
|
4148
|
+
};
|
|
4149
|
+
}
|
|
4150
|
+
function inspectDevsyAgent(options = {}) {
|
|
4151
|
+
const resolved = resolvedOptions(options);
|
|
4152
|
+
const installedVersion = parseVersion(resolved.versionOutput);
|
|
4153
|
+
const explicitPath = resolved.env.DEVSY_AGENT_BINARY?.trim();
|
|
4154
|
+
const source = explicitPath ? "explicit" : "managed";
|
|
4155
|
+
if (installedVersion !== SUPPORTED_DEVSY_VERSION) {
|
|
4156
|
+
return {
|
|
4157
|
+
state: "stale",
|
|
4158
|
+
source,
|
|
4159
|
+
reason: installedVersion ? `installed Devsy ${installedVersion} is not supported by this Devrouter release` : "the supported Devsy version is not available",
|
|
4160
|
+
installedVersion
|
|
4161
|
+
};
|
|
4162
|
+
}
|
|
4163
|
+
if (explicitPath) {
|
|
4164
|
+
return {
|
|
4165
|
+
...inspectBinary(explicitPath, resolved.assets, "invalid"),
|
|
4166
|
+
source,
|
|
4167
|
+
binaryPath: explicitPath,
|
|
4168
|
+
installedVersion
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
const expectedName = resolved.nativeAssetName ?? nativeAssetName(resolved.platform, resolved.arch);
|
|
4172
|
+
const asset = resolved.assets.find((candidate) => candidate.name === expectedName);
|
|
4173
|
+
if (!asset) {
|
|
4174
|
+
return {
|
|
4175
|
+
state: "stale",
|
|
4176
|
+
source,
|
|
4177
|
+
reason: `this Devrouter release has no pinned Devsy agent for ${resolved.platform}/${resolved.arch}`,
|
|
4178
|
+
installedVersion
|
|
4179
|
+
};
|
|
4180
|
+
}
|
|
4181
|
+
const binaryPath = managedBinaryPath(resolved.cacheRoot, asset);
|
|
4182
|
+
return {
|
|
4183
|
+
...inspectBinary(binaryPath, [asset], "missing"),
|
|
4184
|
+
source,
|
|
4185
|
+
binaryPath,
|
|
4186
|
+
asset,
|
|
4187
|
+
installedVersion
|
|
4188
|
+
};
|
|
4189
|
+
}
|
|
4190
|
+
async function writeAll(handle, chunk, position) {
|
|
4191
|
+
let offset = 0;
|
|
4192
|
+
while (offset < chunk.byteLength) {
|
|
4193
|
+
const { bytesWritten } = await handle.write(
|
|
4194
|
+
chunk,
|
|
4195
|
+
offset,
|
|
4196
|
+
chunk.byteLength - offset,
|
|
4197
|
+
position + offset
|
|
4198
|
+
);
|
|
4199
|
+
if (bytesWritten === 0) throw new Error("Devsy agent download stopped making progress");
|
|
4200
|
+
offset += bytesWritten;
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
function appendBoundedTail(current, chunk) {
|
|
4204
|
+
if (chunk.length >= GITHUB_CLI_STDERR_TAIL_BYTES) {
|
|
4205
|
+
return Buffer.from(chunk.subarray(chunk.length - GITHUB_CLI_STDERR_TAIL_BYTES));
|
|
4206
|
+
}
|
|
4207
|
+
const combined = Buffer.concat([current, chunk]);
|
|
4208
|
+
return combined.length > GITHUB_CLI_STDERR_TAIL_BYTES ? combined.subarray(combined.length - GITHUB_CLI_STDERR_TAIL_BYTES) : combined;
|
|
4209
|
+
}
|
|
4210
|
+
function errorMessage(error) {
|
|
4211
|
+
return error instanceof Error ? error.message : String(error);
|
|
4212
|
+
}
|
|
4213
|
+
async function downloadWithGitHubCli(asset, writeChunk) {
|
|
4214
|
+
const child = (0, import_node_child_process4.spawn)(
|
|
4215
|
+
"gh",
|
|
4216
|
+
[
|
|
4217
|
+
"api",
|
|
4218
|
+
`repos/devsy-org/devsy/releases/assets/${asset.githubAssetId}`,
|
|
4219
|
+
"-H",
|
|
4220
|
+
"Accept: application/octet-stream"
|
|
4221
|
+
],
|
|
4222
|
+
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
4223
|
+
);
|
|
4224
|
+
if (!child.stdout || !child.stderr) {
|
|
4225
|
+
child.kill();
|
|
4226
|
+
throw new Error("GitHub CLI fallback did not create output pipes");
|
|
4227
|
+
}
|
|
4228
|
+
let stderrTail = Buffer.alloc(0);
|
|
4229
|
+
child.stderr.on("data", (chunk) => {
|
|
4230
|
+
const value = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
4231
|
+
stderrTail = appendBoundedTail(stderrTail, value);
|
|
4232
|
+
});
|
|
4233
|
+
let spawnError;
|
|
4234
|
+
const completion = new Promise((resolve) => {
|
|
4235
|
+
child.once("error", (error) => {
|
|
4236
|
+
spawnError = error;
|
|
4237
|
+
});
|
|
4238
|
+
child.once("close", (status) => {
|
|
4239
|
+
resolve(status);
|
|
4240
|
+
});
|
|
4241
|
+
});
|
|
4242
|
+
try {
|
|
4243
|
+
for await (const chunk of child.stdout) {
|
|
4244
|
+
await writeChunk(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4245
|
+
}
|
|
4246
|
+
const status = await completion;
|
|
4247
|
+
if (spawnError) throw spawnError;
|
|
4248
|
+
if (status !== 0) {
|
|
4249
|
+
const details = stderrTail.toString("utf-8").trim();
|
|
4250
|
+
throw new Error(
|
|
4251
|
+
`gh api exited with status ${status ?? "unknown"}${details ? `: ${details}` : ""}`
|
|
4252
|
+
);
|
|
4253
|
+
}
|
|
4254
|
+
} catch (error) {
|
|
4255
|
+
if (child.exitCode === null) child.kill();
|
|
4256
|
+
await completion.catch(() => void 0);
|
|
4257
|
+
throw error;
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
function fsyncDirectory2(directory) {
|
|
4261
|
+
const handle = import_node_fs9.default.openSync(directory, "r");
|
|
4262
|
+
try {
|
|
4263
|
+
import_node_fs9.default.fsyncSync(handle);
|
|
4264
|
+
} finally {
|
|
4265
|
+
import_node_fs9.default.closeSync(handle);
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
async function downloadAndPublish(asset, binaryPath, fetcher, githubCliDownloader) {
|
|
4269
|
+
const directory = import_node_path9.default.dirname(binaryPath);
|
|
4270
|
+
import_node_fs9.default.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
4271
|
+
const temporaryPath = import_node_path9.default.join(
|
|
4272
|
+
directory,
|
|
4273
|
+
`.${import_node_path9.default.basename(binaryPath)}.${process.pid}.${(0, import_node_crypto4.randomUUID)()}.tmp`
|
|
4274
|
+
);
|
|
4275
|
+
let handle;
|
|
4276
|
+
try {
|
|
4277
|
+
let response;
|
|
4278
|
+
let directFailure;
|
|
4279
|
+
try {
|
|
4280
|
+
response = await fetcher(asset.url);
|
|
4281
|
+
} catch (error) {
|
|
4282
|
+
directFailure = error;
|
|
4283
|
+
}
|
|
4284
|
+
handle = await import_node_fs9.default.promises.open(temporaryPath, "wx", 384);
|
|
4285
|
+
const digest = (0, import_node_crypto4.createHash)("sha256");
|
|
4286
|
+
let size = 0;
|
|
4287
|
+
const writeChunk = async (chunk) => {
|
|
4288
|
+
size += chunk.byteLength;
|
|
4289
|
+
if (size > asset.size) {
|
|
4290
|
+
throw new Error("Devsy agent download exceeded the pinned size");
|
|
4291
|
+
}
|
|
4292
|
+
digest.update(chunk);
|
|
4293
|
+
await writeAll(handle, chunk, size - chunk.byteLength);
|
|
4294
|
+
};
|
|
4295
|
+
let transport;
|
|
4296
|
+
if (response) {
|
|
4297
|
+
if (!response.ok) {
|
|
4298
|
+
throw new Error(`Devsy agent download failed with HTTP ${response.status}`);
|
|
4299
|
+
}
|
|
4300
|
+
if (!response.body) throw new Error("Devsy agent download returned no response body");
|
|
4301
|
+
const reader = response.body.getReader();
|
|
4302
|
+
try {
|
|
4303
|
+
for (; ; ) {
|
|
4304
|
+
const { done, value } = await reader.read();
|
|
4305
|
+
if (done) break;
|
|
4306
|
+
await writeChunk(value);
|
|
4307
|
+
}
|
|
4308
|
+
} catch (error) {
|
|
4309
|
+
await reader.cancel().catch(() => void 0);
|
|
4310
|
+
throw error;
|
|
4311
|
+
}
|
|
4312
|
+
transport = "https";
|
|
4313
|
+
} else {
|
|
4314
|
+
try {
|
|
4315
|
+
await githubCliDownloader(asset, writeChunk);
|
|
4316
|
+
} catch (fallbackError) {
|
|
4317
|
+
throw new Error(
|
|
4318
|
+
`Devsy agent direct HTTPS download failed: ${errorMessage(directFailure)}; GitHub CLI fallback failed: ${errorMessage(fallbackError)}. Restore direct GitHub release access or install and authenticate gh.`
|
|
4319
|
+
);
|
|
4320
|
+
}
|
|
4321
|
+
transport = "github-cli";
|
|
4322
|
+
}
|
|
4323
|
+
if (size !== asset.size) throw new Error("Devsy agent download has an unexpected size");
|
|
4324
|
+
if (digest.digest("hex") !== asset.sha256) {
|
|
4325
|
+
throw new Error("Devsy agent download has an unexpected digest");
|
|
4326
|
+
}
|
|
4327
|
+
await handle.sync();
|
|
4328
|
+
await handle.chmod(448);
|
|
4329
|
+
await handle.sync();
|
|
4330
|
+
await handle.close();
|
|
4331
|
+
handle = void 0;
|
|
4332
|
+
await import_node_fs9.default.promises.rename(temporaryPath, binaryPath);
|
|
4333
|
+
fsyncDirectory2(directory);
|
|
4334
|
+
return transport;
|
|
4335
|
+
} finally {
|
|
4336
|
+
if (handle) await handle.close();
|
|
4337
|
+
await import_node_fs9.default.promises.rm(temporaryPath, { force: true });
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
async function prepareDevsyAgent(options = {}) {
|
|
4341
|
+
const resolved = resolvedOptions(options);
|
|
4342
|
+
const inspectOptions = resolved;
|
|
4343
|
+
const before = inspectDevsyAgent(inspectOptions);
|
|
4344
|
+
if (before.state === "ready" && before.binaryPath && before.asset) {
|
|
4345
|
+
return {
|
|
4346
|
+
binaryPath: before.binaryPath,
|
|
4347
|
+
source: before.source,
|
|
4348
|
+
asset: before.asset,
|
|
4349
|
+
changed: false,
|
|
4350
|
+
transport: "existing"
|
|
4351
|
+
};
|
|
4352
|
+
}
|
|
4353
|
+
if (before.source === "explicit" || before.state === "stale" || !before.binaryPath || !before.asset) {
|
|
4354
|
+
throw new DevsyAgentReadinessError(before);
|
|
4355
|
+
}
|
|
4356
|
+
const lock = options.withLock ?? ((operation) => {
|
|
4357
|
+
import_node_fs9.default.mkdirSync(import_node_path9.default.dirname(resolved.lockPath), { recursive: true });
|
|
4358
|
+
return withFileLock(
|
|
4359
|
+
resolved.lockPath,
|
|
4360
|
+
{
|
|
4361
|
+
activity: "Devsy agent acquisition",
|
|
4362
|
+
waitMs: DEVSY_AGENT_LOCK_WAIT_MS,
|
|
4363
|
+
fair: true,
|
|
4364
|
+
onWait: createStderrWaitReporter("Devsy agent acquisition", "the managed cache")
|
|
4365
|
+
},
|
|
4366
|
+
operation
|
|
4367
|
+
);
|
|
4368
|
+
});
|
|
4369
|
+
return lock(async () => {
|
|
4370
|
+
const current = inspectDevsyAgent(inspectOptions);
|
|
4371
|
+
if (current.state === "ready" && current.binaryPath && current.asset) {
|
|
4372
|
+
return {
|
|
4373
|
+
binaryPath: current.binaryPath,
|
|
4374
|
+
source: current.source,
|
|
4375
|
+
asset: current.asset,
|
|
4376
|
+
changed: false,
|
|
4377
|
+
transport: "existing"
|
|
4378
|
+
};
|
|
4379
|
+
}
|
|
4380
|
+
if (current.source === "explicit" || current.state === "stale" || !current.binaryPath || !current.asset) {
|
|
4381
|
+
throw new DevsyAgentReadinessError(current);
|
|
4382
|
+
}
|
|
4383
|
+
const transport = await downloadAndPublish(
|
|
4384
|
+
current.asset,
|
|
4385
|
+
current.binaryPath,
|
|
4386
|
+
options.fetcher ?? fetch,
|
|
4387
|
+
options.githubCliDownloader ?? downloadWithGitHubCli
|
|
4388
|
+
);
|
|
4389
|
+
const published = inspectDevsyAgent(inspectOptions);
|
|
4390
|
+
if (published.state !== "ready" || !published.binaryPath || !published.asset) {
|
|
4391
|
+
throw new DevsyAgentReadinessError(published);
|
|
4392
|
+
}
|
|
4393
|
+
return {
|
|
4394
|
+
binaryPath: published.binaryPath,
|
|
4395
|
+
source: published.source,
|
|
4396
|
+
asset: published.asset,
|
|
4397
|
+
changed: true,
|
|
4398
|
+
transport
|
|
4399
|
+
};
|
|
4400
|
+
});
|
|
4401
|
+
}
|
|
4402
|
+
function requireReadyDevsyAgent(options = {}) {
|
|
4403
|
+
const inspection = inspectDevsyAgent(options);
|
|
4404
|
+
if (inspection.state !== "ready" || !inspection.binaryPath || !inspection.asset) {
|
|
4405
|
+
throw new DevsyAgentReadinessError(inspection);
|
|
4406
|
+
}
|
|
4407
|
+
return {
|
|
4408
|
+
binaryPath: inspection.binaryPath,
|
|
4409
|
+
source: inspection.source,
|
|
4410
|
+
asset: inspection.asset,
|
|
4411
|
+
changed: false,
|
|
4412
|
+
transport: "existing"
|
|
4413
|
+
};
|
|
4414
|
+
}
|
|
4415
|
+
var import_node_child_process4, import_node_crypto4, import_node_fs9, import_node_path9, SUPPORTED_DEVSY_VERSION, DEVSY_AGENT_SETUP_COMMAND, DEVSY_AGENT_ASSETS, DevsyAgentReadinessError, DEVSY_AGENT_CACHE_LOCK, DEVSY_AGENT_LOCK_WAIT_MS, GITHUB_CLI_STDERR_TAIL_BYTES;
|
|
4416
|
+
var init_devsy_agent = __esm({
|
|
4417
|
+
"src/core/devsy-agent.ts"() {
|
|
4418
|
+
"use strict";
|
|
4419
|
+
import_node_child_process4 = require("child_process");
|
|
4420
|
+
import_node_crypto4 = require("crypto");
|
|
4421
|
+
import_node_fs9 = __toESM(require("fs"));
|
|
4422
|
+
import_node_path9 = __toESM(require("path"));
|
|
4423
|
+
init_file_lock();
|
|
4424
|
+
init_router();
|
|
4425
|
+
SUPPORTED_DEVSY_VERSION = "1.16.2";
|
|
4426
|
+
DEVSY_AGENT_SETUP_COMMAND = "devrouter setup --yes --workspace-runtime devsy";
|
|
4427
|
+
DEVSY_AGENT_ASSETS = [
|
|
4428
|
+
{
|
|
4429
|
+
githubAssetId: 529830010,
|
|
4430
|
+
name: "devsy-linux-arm64",
|
|
4431
|
+
size: 124518562,
|
|
4432
|
+
sha256: "31060b96486b5398f2aa3ee0875b2555782a2db0954a799d387be38ed4b4990d",
|
|
4433
|
+
url: "https://github.com/devsy-org/devsy/releases/download/v1.16.2/devsy-linux-arm64"
|
|
4434
|
+
},
|
|
4435
|
+
{
|
|
4436
|
+
githubAssetId: 529830011,
|
|
4437
|
+
name: "devsy-linux-amd64",
|
|
4438
|
+
size: 133505186,
|
|
4439
|
+
sha256: "4983c52a3536c5a91d1b5f356a1c3428778ebf3f896d9897f60bce3978abc839",
|
|
4440
|
+
url: "https://github.com/devsy-org/devsy/releases/download/v1.16.2/devsy-linux-amd64"
|
|
4441
|
+
}
|
|
4442
|
+
];
|
|
4443
|
+
DevsyAgentReadinessError = class extends Error {
|
|
4444
|
+
constructor(inspection) {
|
|
4445
|
+
super(`Devsy agent is ${inspection.state}: ${inspection.reason}`);
|
|
4446
|
+
this.inspection = inspection;
|
|
4447
|
+
this.name = "DevsyAgentReadinessError";
|
|
4448
|
+
}
|
|
4449
|
+
};
|
|
4450
|
+
DEVSY_AGENT_CACHE_LOCK = import_node_path9.default.join(DEVROUTER_HOME, "devsy-agent-cache.lock");
|
|
4451
|
+
DEVSY_AGENT_LOCK_WAIT_MS = 18e5;
|
|
4452
|
+
GITHUB_CLI_STDERR_TAIL_BYTES = 4096;
|
|
4453
|
+
}
|
|
4454
|
+
});
|
|
4455
|
+
|
|
4058
4456
|
// src/core/docker.ts
|
|
4059
4457
|
async function getDockerodeConstructor() {
|
|
4060
4458
|
if (!dockerodeConstructorPromise) {
|
|
@@ -4066,7 +4464,7 @@ async function getDockerodeConstructor() {
|
|
|
4066
4464
|
return dockerodeConstructorPromise;
|
|
4067
4465
|
}
|
|
4068
4466
|
function runDockerContextCommand(args) {
|
|
4069
|
-
const result = (0,
|
|
4467
|
+
const result = (0, import_node_child_process5.spawnSync)("docker", ["context", ...args], {
|
|
4070
4468
|
encoding: "utf-8"
|
|
4071
4469
|
});
|
|
4072
4470
|
if (result.status !== 0) {
|
|
@@ -4079,7 +4477,7 @@ function getCurrentDockerContext() {
|
|
|
4079
4477
|
return runDockerContextCommand(["show"]);
|
|
4080
4478
|
}
|
|
4081
4479
|
function getDockerHostFromContext(context) {
|
|
4082
|
-
const result = (0,
|
|
4480
|
+
const result = (0, import_node_child_process5.spawnSync)(
|
|
4083
4481
|
"docker",
|
|
4084
4482
|
["context", "inspect", context, "--format", "{{ .Endpoints.docker.Host }}"],
|
|
4085
4483
|
{ encoding: "utf-8" }
|
|
@@ -4147,11 +4545,11 @@ async function networkExists(name) {
|
|
|
4147
4545
|
return false;
|
|
4148
4546
|
}
|
|
4149
4547
|
}
|
|
4150
|
-
var
|
|
4548
|
+
var import_node_child_process5, dockerodeConstructorPromise;
|
|
4151
4549
|
var init_docker = __esm({
|
|
4152
4550
|
"src/core/docker.ts"() {
|
|
4153
4551
|
"use strict";
|
|
4154
|
-
|
|
4552
|
+
import_node_child_process5 = require("child_process");
|
|
4155
4553
|
dockerodeConstructorPromise = null;
|
|
4156
4554
|
}
|
|
4157
4555
|
});
|
|
@@ -4178,11 +4576,11 @@ function parseDevcontainerConfig(contents, sourcePath) {
|
|
|
4178
4576
|
return parsed;
|
|
4179
4577
|
}
|
|
4180
4578
|
function readDevcontainerConfig(repoPath) {
|
|
4181
|
-
const sourcePath =
|
|
4182
|
-
if (!
|
|
4579
|
+
const sourcePath = import_node_path10.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
4580
|
+
if (!import_node_fs10.default.existsSync(sourcePath) || !import_node_fs10.default.lstatSync(sourcePath).isFile()) {
|
|
4183
4581
|
throw new Error(`Managed Dev Container source config does not exist: ${sourcePath}`);
|
|
4184
4582
|
}
|
|
4185
|
-
const sourceContents =
|
|
4583
|
+
const sourceContents = import_node_fs10.default.readFileSync(sourcePath, "utf-8");
|
|
4186
4584
|
return {
|
|
4187
4585
|
sourcePath,
|
|
4188
4586
|
sourceContents,
|
|
@@ -4198,12 +4596,12 @@ function assertManagedDevcontainerLifecycle(repoPath) {
|
|
|
4198
4596
|
`Managed Dev Container source '${sourcePath}' defines postCreateCommand, but waitFor ${actual}. Set waitFor to 'postCreateCommand' or 'postStartCommand' before retrying devrouter ensure.`
|
|
4199
4597
|
);
|
|
4200
4598
|
}
|
|
4201
|
-
var
|
|
4599
|
+
var import_node_fs10, import_node_path10, import_jsonc_parser;
|
|
4202
4600
|
var init_devcontainer_config = __esm({
|
|
4203
4601
|
"src/core/devcontainer-config.ts"() {
|
|
4204
4602
|
"use strict";
|
|
4205
|
-
|
|
4206
|
-
|
|
4603
|
+
import_node_fs10 = __toESM(require("fs"));
|
|
4604
|
+
import_node_path10 = __toESM(require("path"));
|
|
4207
4605
|
import_jsonc_parser = require("jsonc-parser");
|
|
4208
4606
|
}
|
|
4209
4607
|
});
|
|
@@ -4213,7 +4611,7 @@ function renderDeliveryScript(targetPath) {
|
|
|
4213
4611
|
if (!/^\/tmp\/devrouter\/bin\/[a-zA-Z0-9._-]+$/.test(targetPath)) {
|
|
4214
4612
|
throw new Error(`Unsafe managed runtime delivery path: ${targetPath}`);
|
|
4215
4613
|
}
|
|
4216
|
-
const temporaryPrefix =
|
|
4614
|
+
const temporaryPrefix = import_node_path11.default.posix.basename(targetPath);
|
|
4217
4615
|
return `set -eu
|
|
4218
4616
|
umask 077
|
|
4219
4617
|
runtime_root=/tmp/devrouter
|
|
@@ -4237,7 +4635,7 @@ function commandFailure(result) {
|
|
|
4237
4635
|
return [result.error?.message, result.stderr, result.stdout].filter(Boolean).map((value) => String(value).trim()).filter(Boolean).join("\n");
|
|
4238
4636
|
}
|
|
4239
4637
|
function deliverRuntimeFile(containerId, targetPath, contents, label) {
|
|
4240
|
-
const result = (0,
|
|
4638
|
+
const result = (0, import_node_child_process6.spawnSync)(
|
|
4241
4639
|
"docker",
|
|
4242
4640
|
["exec", "-i", containerId, "sh", "-c", renderDeliveryScript(targetPath)],
|
|
4243
4641
|
{ input: contents, encoding: "utf-8" }
|
|
@@ -4248,32 +4646,32 @@ function deliverRuntimeFile(containerId, targetPath, contents, label) {
|
|
|
4248
4646
|
}
|
|
4249
4647
|
}
|
|
4250
4648
|
function readRegularFileBytes(filePath) {
|
|
4251
|
-
if (!
|
|
4252
|
-
return
|
|
4649
|
+
if (!import_node_fs11.default.existsSync(filePath) || !import_node_fs11.default.lstatSync(filePath).isFile()) return void 0;
|
|
4650
|
+
return import_node_fs11.default.readFileSync(filePath);
|
|
4253
4651
|
}
|
|
4254
4652
|
function readRegularFile(filePath) {
|
|
4255
4653
|
return readRegularFileBytes(filePath)?.toString("utf-8");
|
|
4256
4654
|
}
|
|
4257
4655
|
function adapterFingerprint(adapter) {
|
|
4258
|
-
return (0,
|
|
4656
|
+
return (0, import_node_crypto5.createHash)("sha256").update(adapter).digest("hex");
|
|
4259
4657
|
}
|
|
4260
4658
|
function resolveProcessHelperPath() {
|
|
4261
4659
|
const candidates = [
|
|
4262
|
-
|
|
4263
|
-
|
|
4660
|
+
import_node_path11.default.resolve(__dirname, "..", "bin", "devrouter-process"),
|
|
4661
|
+
import_node_path11.default.resolve(__dirname, "..", "..", "bin", "devrouter-process")
|
|
4264
4662
|
];
|
|
4265
|
-
const helperPath = candidates.find((candidate) =>
|
|
4663
|
+
const helperPath = candidates.find((candidate) => import_node_fs11.default.existsSync(candidate));
|
|
4266
4664
|
if (!helperPath) {
|
|
4267
4665
|
throw new Error("Could not locate the packaged devrouter-process helper.");
|
|
4268
4666
|
}
|
|
4269
4667
|
return helperPath;
|
|
4270
4668
|
}
|
|
4271
4669
|
function resolveManagedPostStartPlan(repoPath) {
|
|
4272
|
-
const adapterPath =
|
|
4670
|
+
const adapterPath = import_node_path11.default.join(repoPath, MANAGED_ADAPTER_PATH);
|
|
4273
4671
|
const adapterBytes = readRegularFileBytes(adapterPath);
|
|
4274
4672
|
const adapterText = adapterBytes?.toString("utf-8");
|
|
4275
|
-
const dockerfilePath =
|
|
4276
|
-
const devcontainerPath =
|
|
4673
|
+
const dockerfilePath = import_node_path11.default.join(repoPath, ".devcontainer", "Dockerfile");
|
|
4674
|
+
const devcontainerPath = import_node_path11.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
4277
4675
|
const dockerfile = readRegularFile(dockerfilePath) ?? "";
|
|
4278
4676
|
const devcontainer = readRegularFile(devcontainerPath) ?? "";
|
|
4279
4677
|
const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
|
|
@@ -4314,7 +4712,7 @@ function resolveManagedPostStartPlan(repoPath) {
|
|
|
4314
4712
|
}
|
|
4315
4713
|
function runManagedPostStart(options) {
|
|
4316
4714
|
if (options.plan.kind !== "runtime") return;
|
|
4317
|
-
const helper =
|
|
4715
|
+
const helper = import_node_fs11.default.readFileSync(resolveProcessHelperPath());
|
|
4318
4716
|
deliverRuntimeFile(
|
|
4319
4717
|
options.container.id,
|
|
4320
4718
|
RUNTIME_HELPER_PATH,
|
|
@@ -4328,7 +4726,7 @@ function runManagedPostStart(options) {
|
|
|
4328
4726
|
options.plan.adapterContents,
|
|
4329
4727
|
"the managed post-start adapter"
|
|
4330
4728
|
);
|
|
4331
|
-
const started = (0,
|
|
4729
|
+
const started = (0, import_node_child_process6.spawnSync)(
|
|
4332
4730
|
"docker",
|
|
4333
4731
|
[
|
|
4334
4732
|
"exec",
|
|
@@ -4366,7 +4764,7 @@ function assertSafeProcessName(name) {
|
|
|
4366
4764
|
}
|
|
4367
4765
|
function runManagedProcessAction(options) {
|
|
4368
4766
|
assertSafeProcessName(options.name);
|
|
4369
|
-
const result = (0,
|
|
4767
|
+
const result = (0, import_node_child_process6.spawnSync)(
|
|
4370
4768
|
"docker",
|
|
4371
4769
|
[
|
|
4372
4770
|
"exec",
|
|
@@ -4402,14 +4800,14 @@ function runManagedProcessAction(options) {
|
|
|
4402
4800
|
}
|
|
4403
4801
|
return "drifted";
|
|
4404
4802
|
}
|
|
4405
|
-
var
|
|
4803
|
+
var import_node_child_process6, import_node_crypto5, import_node_fs11, import_node_path11, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH, ADAPTER_WRAPPER;
|
|
4406
4804
|
var init_managed_post_start = __esm({
|
|
4407
4805
|
"src/core/managed-post-start.ts"() {
|
|
4408
4806
|
"use strict";
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4807
|
+
import_node_child_process6 = require("child_process");
|
|
4808
|
+
import_node_crypto5 = require("crypto");
|
|
4809
|
+
import_node_fs11 = __toESM(require("fs"));
|
|
4810
|
+
import_node_path11 = __toESM(require("path"));
|
|
4413
4811
|
init_devcontainer_config();
|
|
4414
4812
|
MANAGED_MARKER = "devrouter:managed devcontainer";
|
|
4415
4813
|
MANAGED_ADAPTER_PATH = ".devcontainer/post-start.sh";
|
|
@@ -4476,7 +4874,7 @@ function isLoopbackHost(host) {
|
|
|
4476
4874
|
return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
|
|
4477
4875
|
}
|
|
4478
4876
|
function inspectCompose(composeFile, _workspace) {
|
|
4479
|
-
if (!
|
|
4877
|
+
if (!import_node_fs12.default.existsSync(composeFile)) {
|
|
4480
4878
|
return {
|
|
4481
4879
|
aliases: [],
|
|
4482
4880
|
publishedPorts: [],
|
|
@@ -4485,7 +4883,7 @@ function inspectCompose(composeFile, _workspace) {
|
|
|
4485
4883
|
};
|
|
4486
4884
|
}
|
|
4487
4885
|
try {
|
|
4488
|
-
const raw =
|
|
4886
|
+
const raw = import_node_fs12.default.readFileSync(composeFile, "utf-8");
|
|
4489
4887
|
const parsed = import_yaml3.default.parse(raw);
|
|
4490
4888
|
const root = asRecord(parsed);
|
|
4491
4889
|
const services = asRecord(root?.services);
|
|
@@ -4542,11 +4940,11 @@ function routedProxyApps(config) {
|
|
|
4542
4940
|
);
|
|
4543
4941
|
}
|
|
4544
4942
|
function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
4545
|
-
const devcontainerDir =
|
|
4546
|
-
if (!
|
|
4943
|
+
const devcontainerDir = import_node_path12.default.join(repoPath, ".devcontainer");
|
|
4944
|
+
if (!import_node_fs12.default.existsSync(devcontainerDir)) {
|
|
4547
4945
|
return [];
|
|
4548
4946
|
}
|
|
4549
|
-
const compose = inspectCompose(
|
|
4947
|
+
const compose = inspectCompose(import_node_path12.default.join(devcontainerDir, "docker-compose.yml"), workspace);
|
|
4550
4948
|
const checks = [];
|
|
4551
4949
|
if (compose.parseError) {
|
|
4552
4950
|
checks.push({
|
|
@@ -4573,9 +4971,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
4573
4971
|
details: compose.publishedPorts.length > 0 ? compose.publishedPorts.join(", ") : void 0,
|
|
4574
4972
|
suggestion: compose.parseError || compose.publishedPorts.length > 0 ? "Remove published ports and route services through devnet aliases with devrouter proxy apps." : void 0
|
|
4575
4973
|
});
|
|
4576
|
-
const dockerfilePath =
|
|
4577
|
-
const dockerfileExists =
|
|
4578
|
-
const dockerfile = dockerfileExists ?
|
|
4974
|
+
const dockerfilePath = import_node_path12.default.join(devcontainerDir, "Dockerfile");
|
|
4975
|
+
const dockerfileExists = import_node_fs12.default.existsSync(dockerfilePath);
|
|
4976
|
+
const dockerfile = dockerfileExists ? import_node_fs12.default.readFileSync(dockerfilePath, "utf-8") : "";
|
|
4579
4977
|
const devrouterArtifacts = ["@devrouter/cli", "devrouter-process"].filter(
|
|
4580
4978
|
(artifact) => dockerfile.includes(artifact)
|
|
4581
4979
|
);
|
|
@@ -4630,12 +5028,12 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
4630
5028
|
});
|
|
4631
5029
|
return checks;
|
|
4632
5030
|
}
|
|
4633
|
-
var
|
|
5031
|
+
var import_node_fs12, import_node_path12, import_yaml3;
|
|
4634
5032
|
var init_devcontainer_diagnostics = __esm({
|
|
4635
5033
|
"src/core/devcontainer-diagnostics.ts"() {
|
|
4636
5034
|
"use strict";
|
|
4637
|
-
|
|
4638
|
-
|
|
5035
|
+
import_node_fs12 = __toESM(require("fs"));
|
|
5036
|
+
import_node_path12 = __toESM(require("path"));
|
|
4639
5037
|
import_yaml3 = __toESM(require("yaml"));
|
|
4640
5038
|
init_managed_post_start();
|
|
4641
5039
|
}
|
|
@@ -4643,18 +5041,18 @@ var init_devcontainer_diagnostics = __esm({
|
|
|
4643
5041
|
|
|
4644
5042
|
// src/core/paths.ts
|
|
4645
5043
|
function assertPathWithinRepo(filePath, repoRoot, label) {
|
|
4646
|
-
const resolvedRoot =
|
|
4647
|
-
const resolved =
|
|
4648
|
-
if (resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot +
|
|
5044
|
+
const resolvedRoot = import_node_path13.default.resolve(repoRoot);
|
|
5045
|
+
const resolved = import_node_path13.default.resolve(repoRoot, filePath);
|
|
5046
|
+
if (resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot + import_node_path13.default.sep)) {
|
|
4649
5047
|
throw new Error(`${label} path '${filePath}' escapes the repository root.`);
|
|
4650
5048
|
}
|
|
4651
5049
|
return resolved;
|
|
4652
5050
|
}
|
|
4653
|
-
var
|
|
5051
|
+
var import_node_path13;
|
|
4654
5052
|
var init_paths = __esm({
|
|
4655
5053
|
"src/core/paths.ts"() {
|
|
4656
5054
|
"use strict";
|
|
4657
|
-
|
|
5055
|
+
import_node_path13 = __toESM(require("path"));
|
|
4658
5056
|
}
|
|
4659
5057
|
});
|
|
4660
5058
|
|
|
@@ -4739,11 +5137,11 @@ function reconcileRouteRunConflict(repoPath, app) {
|
|
|
4739
5137
|
}
|
|
4740
5138
|
}
|
|
4741
5139
|
}
|
|
4742
|
-
var
|
|
5140
|
+
var import_node_fs13;
|
|
4743
5141
|
var init_route_state = __esm({
|
|
4744
5142
|
"src/core/route-state.ts"() {
|
|
4745
5143
|
"use strict";
|
|
4746
|
-
|
|
5144
|
+
import_node_fs13 = __toESM(require("fs"));
|
|
4747
5145
|
init_host_routes();
|
|
4748
5146
|
init_workspace();
|
|
4749
5147
|
}
|
|
@@ -4909,10 +5307,10 @@ var init_routes = __esm({
|
|
|
4909
5307
|
|
|
4910
5308
|
// src/core/managed-runtime-state.ts
|
|
4911
5309
|
function stateKey(repoPath, workspace) {
|
|
4912
|
-
return (0,
|
|
5310
|
+
return (0, import_node_crypto6.createHash)("sha256").update(`${repoPath}\0${workspace ?? ""}`, "utf-8").digest("hex");
|
|
4913
5311
|
}
|
|
4914
5312
|
function managedRuntimeStatePath(repoPath, workspace) {
|
|
4915
|
-
return
|
|
5313
|
+
return import_node_path14.default.join(DEVROUTER_HOME, "managed-runtime", `${stateKey(repoPath, workspace)}.json`);
|
|
4916
5314
|
}
|
|
4917
5315
|
function isStringArray(value) {
|
|
4918
5316
|
return Array.isArray(value) && value.every((item) => typeof item === "string") && new Set(value).size === value.length;
|
|
@@ -4975,10 +5373,10 @@ function validateState(value, repoPath, workspace) {
|
|
|
4975
5373
|
}
|
|
4976
5374
|
function readManagedRuntimeState(repoPath, workspace) {
|
|
4977
5375
|
const statePath = managedRuntimeStatePath(repoPath, workspace);
|
|
4978
|
-
if (!
|
|
5376
|
+
if (!import_node_fs14.default.existsSync(statePath)) return void 0;
|
|
4979
5377
|
let parsed;
|
|
4980
5378
|
try {
|
|
4981
|
-
parsed = JSON.parse(
|
|
5379
|
+
parsed = JSON.parse(import_node_fs14.default.readFileSync(statePath, "utf-8"));
|
|
4982
5380
|
} catch (error) {
|
|
4983
5381
|
throw new Error(`Could not parse managed runtime state: ${String(error)}`);
|
|
4984
5382
|
}
|
|
@@ -5000,13 +5398,13 @@ function markManagedRuntimeDegraded(state, transitionPhase) {
|
|
|
5000
5398
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5001
5399
|
});
|
|
5002
5400
|
}
|
|
5003
|
-
var
|
|
5401
|
+
var import_node_crypto6, import_node_fs14, import_node_path14;
|
|
5004
5402
|
var init_managed_runtime_state = __esm({
|
|
5005
5403
|
"src/core/managed-runtime-state.ts"() {
|
|
5006
5404
|
"use strict";
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5405
|
+
import_node_crypto6 = require("crypto");
|
|
5406
|
+
import_node_fs14 = __toESM(require("fs"));
|
|
5407
|
+
import_node_path14 = __toESM(require("path"));
|
|
5010
5408
|
init_atomic_file();
|
|
5011
5409
|
init_router();
|
|
5012
5410
|
}
|
|
@@ -5057,14 +5455,14 @@ function resolveComposeFiles(source, repoPath, linked) {
|
|
|
5057
5455
|
"managedRuntime requires .devcontainer/devcontainer.json to define dockerComposeFile."
|
|
5058
5456
|
);
|
|
5059
5457
|
}
|
|
5060
|
-
const directory =
|
|
5458
|
+
const directory = import_node_path15.default.join(repoPath, ".devcontainer");
|
|
5061
5459
|
const files = references.map((reference) => {
|
|
5062
5460
|
const resolved2 = assertPathWithinRepo(
|
|
5063
5461
|
resolveComposeReference(reference, linked),
|
|
5064
5462
|
directory,
|
|
5065
5463
|
"dockerComposeFile"
|
|
5066
5464
|
);
|
|
5067
|
-
if (!
|
|
5465
|
+
if (!import_node_fs15.default.existsSync(resolved2) || !import_node_fs15.default.lstatSync(resolved2).isFile()) {
|
|
5068
5466
|
throw new Error(`Managed Dev Container compose file does not exist: ${resolved2}`);
|
|
5069
5467
|
}
|
|
5070
5468
|
return resolved2;
|
|
@@ -5072,7 +5470,7 @@ function resolveComposeFiles(source, repoPath, linked) {
|
|
|
5072
5470
|
for (const file of files) {
|
|
5073
5471
|
let parsed;
|
|
5074
5472
|
try {
|
|
5075
|
-
parsed = import_yaml4.default.parse(
|
|
5473
|
+
parsed = import_yaml4.default.parse(import_node_fs15.default.readFileSync(file, "utf-8"));
|
|
5076
5474
|
} catch (error) {
|
|
5077
5475
|
throw new Error(
|
|
5078
5476
|
`Could not parse managed Dev Container compose file '${file}': ${String(error)}`
|
|
@@ -5083,7 +5481,7 @@ function resolveComposeFiles(source, repoPath, linked) {
|
|
|
5083
5481
|
}
|
|
5084
5482
|
}
|
|
5085
5483
|
const fileArgs = files.flatMap((file) => ["-f", file]);
|
|
5086
|
-
const resolved = (0,
|
|
5484
|
+
const resolved = (0, import_node_child_process7.spawnSync)(
|
|
5087
5485
|
"docker",
|
|
5088
5486
|
[
|
|
5089
5487
|
"compose",
|
|
@@ -5109,7 +5507,7 @@ function resolveComposeFiles(source, repoPath, linked) {
|
|
|
5109
5507
|
return { directory, files, services: services.sort() };
|
|
5110
5508
|
}
|
|
5111
5509
|
function assertIgnoredGeneratedPath(repoPath, generatedPath) {
|
|
5112
|
-
const ignored = (0,
|
|
5510
|
+
const ignored = (0, import_node_child_process7.spawnSync)(
|
|
5113
5511
|
"git",
|
|
5114
5512
|
["-C", repoPath, "check-ignore", "--quiet", "--no-index", "--", generatedPath],
|
|
5115
5513
|
{ encoding: "utf-8" }
|
|
@@ -5121,12 +5519,12 @@ function assertIgnoredGeneratedPath(repoPath, generatedPath) {
|
|
|
5121
5519
|
}
|
|
5122
5520
|
}
|
|
5123
5521
|
function assertGeneratedPathOwnership(generatedPath) {
|
|
5124
|
-
if (!
|
|
5125
|
-
const stat =
|
|
5522
|
+
if (!import_node_fs15.default.existsSync(generatedPath)) return;
|
|
5523
|
+
const stat = import_node_fs15.default.lstatSync(generatedPath);
|
|
5126
5524
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
5127
5525
|
throw new Error(`Managed Dev Container path '${generatedPath}' is not a regular file.`);
|
|
5128
5526
|
}
|
|
5129
|
-
const firstLine2 =
|
|
5527
|
+
const firstLine2 = import_node_fs15.default.readFileSync(generatedPath, "utf-8").split(/\r?\n/, 1)[0];
|
|
5130
5528
|
if (firstLine2 !== MANAGED_DEVCONTAINER_MARKER) {
|
|
5131
5529
|
throw new Error(
|
|
5132
5530
|
`Managed Dev Container path '${generatedPath}' exists without the devrouter ownership marker.`
|
|
@@ -5137,7 +5535,7 @@ function isWildcard(values) {
|
|
|
5137
5535
|
return values?.length === 1 && values[0] === "*";
|
|
5138
5536
|
}
|
|
5139
5537
|
function sha256(contents) {
|
|
5140
|
-
return (0,
|
|
5538
|
+
return (0, import_node_crypto7.createHash)("sha256").update(contents, "utf-8").digest("hex");
|
|
5141
5539
|
}
|
|
5142
5540
|
function safeComposeProject(value) {
|
|
5143
5541
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(value)) {
|
|
@@ -5202,7 +5600,7 @@ function inspectManagedDevcontainerConfig(options) {
|
|
|
5202
5600
|
const contents = `${MANAGED_DEVCONTAINER_MARKER}
|
|
5203
5601
|
${JSON.stringify(effective, null, 2)}
|
|
5204
5602
|
`;
|
|
5205
|
-
const generatedPath =
|
|
5603
|
+
const generatedPath = import_node_path15.default.join(options.repoPath, MANAGED_DEVCONTAINER_PATH);
|
|
5206
5604
|
assertIgnoredGeneratedPath(options.repoPath, generatedPath);
|
|
5207
5605
|
assertGeneratedPathOwnership(generatedPath);
|
|
5208
5606
|
return {
|
|
@@ -5229,13 +5627,13 @@ function writeManagedDevcontainerConfig(plan) {
|
|
|
5229
5627
|
function inspectManagedDevcontainerGeneratedConfig(plan) {
|
|
5230
5628
|
let stat;
|
|
5231
5629
|
try {
|
|
5232
|
-
stat =
|
|
5630
|
+
stat = import_node_fs15.default.lstatSync(plan.generatedPath);
|
|
5233
5631
|
} catch (error) {
|
|
5234
5632
|
if (error.code === "ENOENT") return { status: "missing" };
|
|
5235
5633
|
throw error;
|
|
5236
5634
|
}
|
|
5237
5635
|
if (!stat.isFile() || stat.isSymbolicLink()) return { status: "foreign" };
|
|
5238
|
-
const contents =
|
|
5636
|
+
const contents = import_node_fs15.default.readFileSync(plan.generatedPath, "utf-8");
|
|
5239
5637
|
if (!contents.startsWith(`${MANAGED_DEVCONTAINER_MARKER}
|
|
5240
5638
|
`)) {
|
|
5241
5639
|
return { status: "foreign" };
|
|
@@ -5247,17 +5645,17 @@ function inspectManagedDevcontainerGeneratedConfig(plan) {
|
|
|
5247
5645
|
};
|
|
5248
5646
|
}
|
|
5249
5647
|
function removeManagedDevcontainerConfig(plan) {
|
|
5250
|
-
if (!
|
|
5251
|
-
const stat =
|
|
5648
|
+
if (!import_node_fs15.default.existsSync(plan.generatedPath)) return;
|
|
5649
|
+
const stat = import_node_fs15.default.lstatSync(plan.generatedPath);
|
|
5252
5650
|
if (!stat.isFile()) {
|
|
5253
5651
|
throw new Error(`Managed Dev Container path '${plan.generatedPath}' is not a regular file.`);
|
|
5254
5652
|
}
|
|
5255
|
-
const contents =
|
|
5653
|
+
const contents = import_node_fs15.default.readFileSync(plan.generatedPath, "utf-8");
|
|
5256
5654
|
if (!contents.startsWith(`${MANAGED_DEVCONTAINER_MARKER}
|
|
5257
5655
|
`)) {
|
|
5258
5656
|
throw new Error(`Managed Dev Container path '${plan.generatedPath}' is not devrouter-owned.`);
|
|
5259
5657
|
}
|
|
5260
|
-
|
|
5658
|
+
import_node_fs15.default.unlinkSync(plan.generatedPath);
|
|
5261
5659
|
}
|
|
5262
5660
|
function assertSafeContainerId(containerId) {
|
|
5263
5661
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(containerId)) {
|
|
@@ -5284,7 +5682,7 @@ function startExactManagedServices(options) {
|
|
|
5284
5682
|
delete env.DEVROUTER_GIT_COMMON_DIR;
|
|
5285
5683
|
delete env.DEVCONTAINER_COMPOSE_OVERLAY;
|
|
5286
5684
|
}
|
|
5287
|
-
const result = (0,
|
|
5685
|
+
const result = (0, import_node_child_process7.spawnSync)(
|
|
5288
5686
|
"docker",
|
|
5289
5687
|
[
|
|
5290
5688
|
"compose",
|
|
@@ -5314,7 +5712,7 @@ function startExactManagedServices(options) {
|
|
|
5314
5712
|
}
|
|
5315
5713
|
function stopExactManagedService(containerId, service) {
|
|
5316
5714
|
assertSafeContainerId(containerId);
|
|
5317
|
-
const result = (0,
|
|
5715
|
+
const result = (0, import_node_child_process7.spawnSync)("docker", ["stop", containerId], {
|
|
5318
5716
|
encoding: "utf-8",
|
|
5319
5717
|
stdio: "inherit"
|
|
5320
5718
|
});
|
|
@@ -5322,14 +5720,14 @@ function stopExactManagedService(containerId, service) {
|
|
|
5322
5720
|
throw new Error(`Could not stop exact managed service '${service}' (${containerId}).`);
|
|
5323
5721
|
}
|
|
5324
5722
|
}
|
|
5325
|
-
var
|
|
5723
|
+
var import_node_child_process7, import_node_crypto7, import_node_fs15, import_node_path15, import_yaml4, MANAGED_DEVCONTAINER_PATH, MANAGED_DEVCONTAINER_MARKER;
|
|
5326
5724
|
var init_devcontainer_profile = __esm({
|
|
5327
5725
|
"src/core/devcontainer-profile.ts"() {
|
|
5328
5726
|
"use strict";
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5727
|
+
import_node_child_process7 = require("child_process");
|
|
5728
|
+
import_node_crypto7 = require("crypto");
|
|
5729
|
+
import_node_fs15 = __toESM(require("fs"));
|
|
5730
|
+
import_node_path15 = __toESM(require("path"));
|
|
5333
5731
|
import_yaml4 = __toESM(require("yaml"));
|
|
5334
5732
|
init_atomic_file();
|
|
5335
5733
|
init_devcontainer_config();
|
|
@@ -5343,7 +5741,7 @@ var init_devcontainer_profile = __esm({
|
|
|
5343
5741
|
function inspectWorkspaceContainers(options) {
|
|
5344
5742
|
let ids = options?.ids;
|
|
5345
5743
|
if (!ids) {
|
|
5346
|
-
const listed = (0,
|
|
5744
|
+
const listed = (0, import_node_child_process8.spawnSync)(
|
|
5347
5745
|
"docker",
|
|
5348
5746
|
[
|
|
5349
5747
|
"ps",
|
|
@@ -5363,7 +5761,7 @@ function inspectWorkspaceContainers(options) {
|
|
|
5363
5761
|
}
|
|
5364
5762
|
if (ids.length === 0) return [];
|
|
5365
5763
|
const template = options?.withSize ? SIZE_INSPECT_TEMPLATE : SAFE_INSPECT_TEMPLATE;
|
|
5366
|
-
const inspected = (0,
|
|
5764
|
+
const inspected = (0, import_node_child_process8.spawnSync)(
|
|
5367
5765
|
"docker",
|
|
5368
5766
|
["inspect", ...options?.withSize ? ["--size"] : [], "--format", template, ...ids],
|
|
5369
5767
|
{ encoding: "utf-8" }
|
|
@@ -5378,7 +5776,7 @@ function inspectWorkspaceContainers(options) {
|
|
|
5378
5776
|
function workspaceAppContainers(containers, repoPath) {
|
|
5379
5777
|
return containers.filter((container) => {
|
|
5380
5778
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
5381
|
-
return Boolean(workingDir && sameWorkspacePath(workingDir,
|
|
5779
|
+
return Boolean(workingDir && sameWorkspacePath(workingDir, import_node_path16.default.join(repoPath, ".devcontainer"))) && container.mounts.some(
|
|
5382
5780
|
(mount) => mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath)
|
|
5383
5781
|
);
|
|
5384
5782
|
});
|
|
@@ -5386,7 +5784,7 @@ function workspaceAppContainers(containers, repoPath) {
|
|
|
5386
5784
|
function hasExactComposeIdentity(container, options) {
|
|
5387
5785
|
if (options.composeProject !== void 0 && container.labels["com.docker.compose.project"] !== options.composeProject || container.labels["com.docker.compose.service"] !== options.service || !sameWorkspacePath(
|
|
5388
5786
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
5389
|
-
|
|
5787
|
+
import_node_path16.default.join(options.repoPath, ".devcontainer")
|
|
5390
5788
|
)) {
|
|
5391
5789
|
return false;
|
|
5392
5790
|
}
|
|
@@ -5423,12 +5821,12 @@ function resolveRunningWorkspaceContainer(repoPath) {
|
|
|
5423
5821
|
}
|
|
5424
5822
|
return { id: container.id, workspacePath: repoMount.Destination };
|
|
5425
5823
|
}
|
|
5426
|
-
var
|
|
5824
|
+
var import_node_child_process8, import_node_path16, SAFE_INSPECT_TEMPLATE, SIZE_INSPECT_TEMPLATE;
|
|
5427
5825
|
var init_devpod_environment = __esm({
|
|
5428
5826
|
"src/core/devpod-environment.ts"() {
|
|
5429
5827
|
"use strict";
|
|
5430
|
-
|
|
5431
|
-
|
|
5828
|
+
import_node_child_process8 = require("child_process");
|
|
5829
|
+
import_node_path16 = __toESM(require("path"));
|
|
5432
5830
|
init_workspace();
|
|
5433
5831
|
SAFE_INSPECT_TEMPLATE = '{"id":{{json .Id}},"state":{"Running":{{json .State.Running}},"Health":{{with (index .State "Health")}}{"Status":{{json .Status}}}{{else}}null{{end}}},"labels":{"com.docker.compose.project":{{json (index .Config.Labels "com.docker.compose.project")}},"com.docker.compose.service":{{json (index .Config.Labels "com.docker.compose.service")}},"com.docker.compose.project.working_dir":{{json (index .Config.Labels "com.docker.compose.project.working_dir")}},"com.docker.compose.project.config_files":{{json (index .Config.Labels "com.docker.compose.project.config_files")}}},"mounts":{{json .Mounts}},"networks":{{json .NetworkSettings.Networks}}}';
|
|
5434
5832
|
SIZE_INSPECT_TEMPLATE = SAFE_INSPECT_TEMPLATE.replace(
|
|
@@ -5882,7 +6280,7 @@ function toRepoStatus(repoPath) {
|
|
|
5882
6280
|
const resolvedRepoPath = resolveRepoPath(repoPath);
|
|
5883
6281
|
const configPath = getRepoConfigPath(resolvedRepoPath);
|
|
5884
6282
|
const explicitRepo = typeof repoPath === "string" && repoPath.trim().length > 0;
|
|
5885
|
-
const configExists =
|
|
6283
|
+
const configExists = import_node_fs16.default.existsSync(configPath);
|
|
5886
6284
|
if (!explicitRepo && !configExists) {
|
|
5887
6285
|
return void 0;
|
|
5888
6286
|
}
|
|
@@ -6030,11 +6428,11 @@ async function collectRouterStatus(repoPath) {
|
|
|
6030
6428
|
}
|
|
6031
6429
|
};
|
|
6032
6430
|
}
|
|
6033
|
-
var
|
|
6431
|
+
var import_node_fs16;
|
|
6034
6432
|
var init_status = __esm({
|
|
6035
6433
|
"src/core/status.ts"() {
|
|
6036
6434
|
"use strict";
|
|
6037
|
-
|
|
6435
|
+
import_node_fs16 = __toESM(require("fs"));
|
|
6038
6436
|
init_docker();
|
|
6039
6437
|
init_managed_runtime_state();
|
|
6040
6438
|
init_managed_runtime_status();
|
|
@@ -6045,7 +6443,7 @@ var init_status = __esm({
|
|
|
6045
6443
|
|
|
6046
6444
|
// src/core/tls.ts
|
|
6047
6445
|
function runOrThrow(command, args) {
|
|
6048
|
-
const result = (0,
|
|
6446
|
+
const result = (0, import_node_child_process9.spawnSync)(command, args, {
|
|
6049
6447
|
encoding: "utf-8"
|
|
6050
6448
|
});
|
|
6051
6449
|
if (result.status !== 0) {
|
|
@@ -6055,7 +6453,7 @@ function runOrThrow(command, args) {
|
|
|
6055
6453
|
return result.stdout.trim();
|
|
6056
6454
|
}
|
|
6057
6455
|
function commandExists(command) {
|
|
6058
|
-
const result = (0,
|
|
6456
|
+
const result = (0, import_node_child_process9.spawnSync)("sh", ["-c", `command -v ${command}`], { encoding: "utf-8" });
|
|
6059
6457
|
return result.status === 0;
|
|
6060
6458
|
}
|
|
6061
6459
|
function ensureMkcert() {
|
|
@@ -6073,8 +6471,8 @@ function tlsSetupCommand(repoPath) {
|
|
|
6073
6471
|
}
|
|
6074
6472
|
function getMkcertRootCAPath(options = {}) {
|
|
6075
6473
|
ensureMkcert();
|
|
6076
|
-
const rootCAPath =
|
|
6077
|
-
if (!
|
|
6474
|
+
const rootCAPath = import_node_path17.default.join(runOrThrow("mkcert", ["-CAROOT"]), "rootCA.pem");
|
|
6475
|
+
if (!import_node_fs17.default.existsSync(rootCAPath)) {
|
|
6078
6476
|
throw new Error(
|
|
6079
6477
|
`mkcert root CA was not found at '${rootCAPath}'. Run: ${tlsSetupCommand(options.repoPath)}`
|
|
6080
6478
|
);
|
|
@@ -6107,7 +6505,7 @@ function parseDnsHostsFromSubjectAltName(subjectAltName) {
|
|
|
6107
6505
|
return normalizeUniqueHosts(names);
|
|
6108
6506
|
}
|
|
6109
6507
|
function parseCertificateDnsHosts(pem) {
|
|
6110
|
-
const certificate = new
|
|
6508
|
+
const certificate = new import_node_crypto8.X509Certificate(pem);
|
|
6111
6509
|
const subjectAltName = certificate.subjectAltName ?? "";
|
|
6112
6510
|
if (subjectAltName.length === 0) {
|
|
6113
6511
|
return [];
|
|
@@ -6175,11 +6573,11 @@ function compactTLSCertificateHosts(hosts) {
|
|
|
6175
6573
|
);
|
|
6176
6574
|
}
|
|
6177
6575
|
function readCurrentCertificateHosts(options = {}) {
|
|
6178
|
-
if (!
|
|
6576
|
+
if (!import_node_fs17.default.existsSync(CERT_FILE)) {
|
|
6179
6577
|
return [];
|
|
6180
6578
|
}
|
|
6181
6579
|
try {
|
|
6182
|
-
const pem =
|
|
6580
|
+
const pem = import_node_fs17.default.readFileSync(CERT_FILE, "utf-8");
|
|
6183
6581
|
return parseCertificateDnsHosts(pem);
|
|
6184
6582
|
} catch (error) {
|
|
6185
6583
|
if (options.replaceMalformed) {
|
|
@@ -6307,20 +6705,20 @@ Run: ${tlsSetupCommand(options.repoPath)}`
|
|
|
6307
6705
|
);
|
|
6308
6706
|
}
|
|
6309
6707
|
}
|
|
6310
|
-
var
|
|
6708
|
+
var import_node_child_process9, import_node_crypto8, import_node_fs17, import_node_path17, DEFAULT_TLS_CERT_HOSTS, TLS_CERTIFICATE_LOCK_FILE, TLS_CERTIFICATE_LOCK_WAIT_MS, TLS_CERTIFICATE_WRITE_ATTEMPTS;
|
|
6311
6709
|
var init_tls = __esm({
|
|
6312
6710
|
"src/core/tls.ts"() {
|
|
6313
6711
|
"use strict";
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6712
|
+
import_node_child_process9 = require("child_process");
|
|
6713
|
+
import_node_crypto8 = require("crypto");
|
|
6714
|
+
import_node_fs17 = __toESM(require("fs"));
|
|
6715
|
+
import_node_path17 = __toESM(require("path"));
|
|
6318
6716
|
init_docker();
|
|
6319
6717
|
init_file_lock();
|
|
6320
6718
|
init_host_routes();
|
|
6321
6719
|
init_router();
|
|
6322
6720
|
DEFAULT_TLS_CERT_HOSTS = ["localhost", "*.localhost"];
|
|
6323
|
-
TLS_CERTIFICATE_LOCK_FILE =
|
|
6721
|
+
TLS_CERTIFICATE_LOCK_FILE = import_node_path17.default.join(DEVROUTER_HOME, "tls-certificate.lock");
|
|
6324
6722
|
TLS_CERTIFICATE_LOCK_WAIT_MS = 6e4;
|
|
6325
6723
|
TLS_CERTIFICATE_WRITE_ATTEMPTS = 2;
|
|
6326
6724
|
}
|
|
@@ -6328,7 +6726,7 @@ var init_tls = __esm({
|
|
|
6328
6726
|
|
|
6329
6727
|
// src/core/devpod-registry.ts
|
|
6330
6728
|
function listDevpodWorkspacesRaw() {
|
|
6331
|
-
const result = (0,
|
|
6729
|
+
const result = (0, import_node_child_process10.spawnSync)("devpod", ["list", "--output", "json", "--skip-pro"], {
|
|
6332
6730
|
encoding: "utf-8"
|
|
6333
6731
|
});
|
|
6334
6732
|
if (result.status !== 0) {
|
|
@@ -6363,17 +6761,17 @@ function listDevpodWorkspacesRaw() {
|
|
|
6363
6761
|
return workspace;
|
|
6364
6762
|
});
|
|
6365
6763
|
}
|
|
6366
|
-
var
|
|
6764
|
+
var import_node_child_process10;
|
|
6367
6765
|
var init_devpod_registry = __esm({
|
|
6368
6766
|
"src/core/devpod-registry.ts"() {
|
|
6369
6767
|
"use strict";
|
|
6370
|
-
|
|
6768
|
+
import_node_child_process10 = require("child_process");
|
|
6371
6769
|
}
|
|
6372
6770
|
});
|
|
6373
6771
|
|
|
6374
6772
|
// src/core/devsy-workspaces.ts
|
|
6375
6773
|
function listDevsyWorkspaces() {
|
|
6376
|
-
const result = (0,
|
|
6774
|
+
const result = (0, import_node_child_process11.spawnSync)(
|
|
6377
6775
|
"devsy",
|
|
6378
6776
|
["workspace", "list", "--result-format", "json", "--skip-pro"],
|
|
6379
6777
|
{
|
|
@@ -6439,7 +6837,7 @@ function parseDevsyRuntimeStatus(output2, expectedId) {
|
|
|
6439
6837
|
}
|
|
6440
6838
|
}
|
|
6441
6839
|
function inspectDevsyRuntimeStatus(devsyId) {
|
|
6442
|
-
const result = (0,
|
|
6840
|
+
const result = (0, import_node_child_process11.spawnSync)("devsy", ["workspace", "status", devsyId, "--result-format", "json"], {
|
|
6443
6841
|
encoding: "utf-8",
|
|
6444
6842
|
timeout: 1e4
|
|
6445
6843
|
});
|
|
@@ -6472,11 +6870,11 @@ function selectDevsyWorkspace(workspaces, repoPath) {
|
|
|
6472
6870
|
}
|
|
6473
6871
|
return matches[0];
|
|
6474
6872
|
}
|
|
6475
|
-
var
|
|
6873
|
+
var import_node_child_process11;
|
|
6476
6874
|
var init_devsy_workspaces = __esm({
|
|
6477
6875
|
"src/core/devsy-workspaces.ts"() {
|
|
6478
6876
|
"use strict";
|
|
6479
|
-
|
|
6877
|
+
import_node_child_process11 = require("child_process");
|
|
6480
6878
|
init_workspace();
|
|
6481
6879
|
}
|
|
6482
6880
|
});
|
|
@@ -6493,13 +6891,13 @@ function parseWorkspaceRuntime(value) {
|
|
|
6493
6891
|
}
|
|
6494
6892
|
function isRuntimeInstalled(runtime) {
|
|
6495
6893
|
const probeArgs = runtime === "devsy" ? ["--version"] : ["version"];
|
|
6496
|
-
const probe = (0,
|
|
6894
|
+
const probe = (0, import_node_child_process12.spawnSync)(runtime, probeArgs, { encoding: "utf-8" });
|
|
6497
6895
|
return probe.status === 0 && !probe.error;
|
|
6498
6896
|
}
|
|
6499
6897
|
function readWorkspaceRuntimeConfig() {
|
|
6500
6898
|
let raw;
|
|
6501
6899
|
try {
|
|
6502
|
-
raw = JSON.parse(
|
|
6900
|
+
raw = JSON.parse(import_node_fs18.default.readFileSync(RUNTIME_CONFIG_FILE, "utf-8"));
|
|
6503
6901
|
} catch {
|
|
6504
6902
|
return {};
|
|
6505
6903
|
}
|
|
@@ -6520,7 +6918,7 @@ function readWorkspaceRuntimeConfig() {
|
|
|
6520
6918
|
function inspectWorkspaceRuntimeConfig() {
|
|
6521
6919
|
let rawText;
|
|
6522
6920
|
try {
|
|
6523
|
-
rawText =
|
|
6921
|
+
rawText = import_node_fs18.default.readFileSync(RUNTIME_CONFIG_FILE, "utf-8");
|
|
6524
6922
|
} catch {
|
|
6525
6923
|
return { exists: false, config: {}, problems: [] };
|
|
6526
6924
|
}
|
|
@@ -6571,7 +6969,7 @@ function writeWorkspaceRuntimeConfig(config) {
|
|
|
6571
6969
|
}
|
|
6572
6970
|
next.devsyInactivityTimeout = timeout;
|
|
6573
6971
|
}
|
|
6574
|
-
|
|
6972
|
+
import_node_fs18.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
6575
6973
|
writeFileAtomically(RUNTIME_CONFIG_FILE, `${JSON.stringify(next, null, 2)}
|
|
6576
6974
|
`);
|
|
6577
6975
|
}
|
|
@@ -6679,20 +7077,20 @@ function resetWorkspaceRuntimeCaches() {
|
|
|
6679
7077
|
cachedRuntime = void 0;
|
|
6680
7078
|
cachedSnapshots = void 0;
|
|
6681
7079
|
}
|
|
6682
|
-
var
|
|
7080
|
+
var import_node_child_process12, import_node_fs18, import_node_path18, SUPPORTED_RUNTIMES2, RUNTIME_CONFIG_FILE, INACTIVITY_TIMEOUT_PATTERN, cachedRuntime, cachedSnapshots, UnsupportedWorkspaceRuntimeError, WorkspaceRuntimeOwnershipError;
|
|
6683
7081
|
var init_workspace_runtime = __esm({
|
|
6684
7082
|
"src/core/workspace-runtime.ts"() {
|
|
6685
7083
|
"use strict";
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
7084
|
+
import_node_child_process12 = require("child_process");
|
|
7085
|
+
import_node_fs18 = __toESM(require("fs"));
|
|
7086
|
+
import_node_path18 = __toESM(require("path"));
|
|
6689
7087
|
init_atomic_file();
|
|
6690
7088
|
init_devpod_registry();
|
|
6691
7089
|
init_devsy_workspaces();
|
|
6692
7090
|
init_router();
|
|
6693
7091
|
init_workspace();
|
|
6694
7092
|
SUPPORTED_RUNTIMES2 = ["devpod", "devsy"];
|
|
6695
|
-
RUNTIME_CONFIG_FILE =
|
|
7093
|
+
RUNTIME_CONFIG_FILE = import_node_path18.default.join(DEVROUTER_HOME, "workspace-runtime.json");
|
|
6696
7094
|
INACTIVITY_TIMEOUT_PATTERN = /^(?:\d+(?:ms|s|m|h))+$/;
|
|
6697
7095
|
UnsupportedWorkspaceRuntimeError = class extends Error {
|
|
6698
7096
|
};
|
|
@@ -6713,7 +7111,7 @@ function outputFromResult(result) {
|
|
|
6713
7111
|
return output2.length > 0 ? output2 : void 0;
|
|
6714
7112
|
}
|
|
6715
7113
|
function runTool(command, args = []) {
|
|
6716
|
-
const result = (0,
|
|
7114
|
+
const result = (0, import_node_child_process13.spawnSync)(command, args, {
|
|
6717
7115
|
encoding: "utf-8"
|
|
6718
7116
|
});
|
|
6719
7117
|
if (result.error) {
|
|
@@ -6770,12 +7168,12 @@ function parseMinimumNodeMajor(value) {
|
|
|
6770
7168
|
return Number(match[1]);
|
|
6771
7169
|
}
|
|
6772
7170
|
function readPackageJson(repoPath) {
|
|
6773
|
-
const packagePath =
|
|
6774
|
-
if (!
|
|
7171
|
+
const packagePath = import_node_path19.default.join(repoPath, "package.json");
|
|
7172
|
+
if (!import_node_fs19.default.existsSync(packagePath)) {
|
|
6775
7173
|
return void 0;
|
|
6776
7174
|
}
|
|
6777
7175
|
try {
|
|
6778
|
-
return JSON.parse(
|
|
7176
|
+
return JSON.parse(import_node_fs19.default.readFileSync(packagePath, "utf-8"));
|
|
6779
7177
|
} catch {
|
|
6780
7178
|
return void 0;
|
|
6781
7179
|
}
|
|
@@ -6840,7 +7238,44 @@ function nodeToolchainCheck(repoPath) {
|
|
|
6840
7238
|
details: details.join(", ")
|
|
6841
7239
|
};
|
|
6842
7240
|
}
|
|
7241
|
+
function devsyAgentCheck(inspection) {
|
|
7242
|
+
const details = [
|
|
7243
|
+
`state=${inspection.state}`,
|
|
7244
|
+
`source=${inspection.source}`,
|
|
7245
|
+
...inspection.installedVersion ? [`version=${inspection.installedVersion}`] : [],
|
|
7246
|
+
...inspection.asset ? [`asset=${inspection.asset.name}`] : []
|
|
7247
|
+
].join(", ");
|
|
7248
|
+
if (inspection.state === "ready") {
|
|
7249
|
+
return {
|
|
7250
|
+
id: "global.devsy-agent",
|
|
7251
|
+
level: "ok",
|
|
7252
|
+
summary: "Devsy agent source is ready.",
|
|
7253
|
+
details
|
|
7254
|
+
};
|
|
7255
|
+
}
|
|
7256
|
+
const summary = inspection.state === "missing" ? "Managed Devsy agent source is missing." : inspection.state === "stale" ? "Devsy agent source is stale for this Devrouter release." : "Devsy agent source is invalid.";
|
|
7257
|
+
return {
|
|
7258
|
+
id: "global.devsy-agent",
|
|
7259
|
+
level: "error",
|
|
7260
|
+
summary,
|
|
7261
|
+
details: `${details}, ${inspection.reason}`,
|
|
7262
|
+
suggestion: devsyAgentRepairSuggestion(inspection)
|
|
7263
|
+
};
|
|
7264
|
+
}
|
|
6843
7265
|
function buildGlobalToolChecks(repoPath) {
|
|
7266
|
+
const previous = process.env[DEVSY_DISABLE_TELEMETRY];
|
|
7267
|
+
process.env[DEVSY_DISABLE_TELEMETRY] = "true";
|
|
7268
|
+
try {
|
|
7269
|
+
return buildGlobalToolChecksWithoutTelemetry(repoPath);
|
|
7270
|
+
} finally {
|
|
7271
|
+
if (previous === void 0) {
|
|
7272
|
+
delete process.env[DEVSY_DISABLE_TELEMETRY];
|
|
7273
|
+
} else {
|
|
7274
|
+
process.env[DEVSY_DISABLE_TELEMETRY] = previous;
|
|
7275
|
+
}
|
|
7276
|
+
}
|
|
7277
|
+
}
|
|
7278
|
+
function buildGlobalToolChecksWithoutTelemetry(repoPath) {
|
|
6844
7279
|
const checks = [];
|
|
6845
7280
|
const compose = runTool("docker", ["compose", "version"]);
|
|
6846
7281
|
checks.push({
|
|
@@ -6894,6 +7329,13 @@ function buildGlobalToolChecks(repoPath) {
|
|
|
6894
7329
|
].filter(Boolean).join(", "),
|
|
6895
7330
|
suggestion: runtimeTool.ok ? void 0 : workspaceRuntime === "devsy" ? "Install Devsy for devcontainer workspace flows: brew install devsy-org/homebrew-tap/devsy" : "Install DevPod for devcontainer workspace flows: brew install devpod"
|
|
6896
7331
|
});
|
|
7332
|
+
if (workspaceRuntime === "devsy") {
|
|
7333
|
+
checks.push(
|
|
7334
|
+
devsyAgentCheck(
|
|
7335
|
+
inspectDevsyAgent({ versionOutput: runtimeTool.ok ? runtimeTool.output : "" })
|
|
7336
|
+
)
|
|
7337
|
+
);
|
|
7338
|
+
}
|
|
6897
7339
|
const configProblems = [...configInspection.problems];
|
|
6898
7340
|
const machineResolvedDevpod = workspaceRuntime === "devpod" && (runtimeResolution.source === "auto-detect" || runtimeResolution.source === "default");
|
|
6899
7341
|
if (machineConfig.devsyInactivityTimeout && machineResolvedDevpod) {
|
|
@@ -6915,14 +7357,16 @@ function buildGlobalToolChecks(repoPath) {
|
|
|
6915
7357
|
checks.push(nodeToolchainCheck(repoPath));
|
|
6916
7358
|
return checks;
|
|
6917
7359
|
}
|
|
6918
|
-
var
|
|
7360
|
+
var import_node_child_process13, import_node_fs19, import_node_path19, DEVSY_DISABLE_TELEMETRY;
|
|
6919
7361
|
var init_tool_diagnostics = __esm({
|
|
6920
7362
|
"src/core/tool-diagnostics.ts"() {
|
|
6921
7363
|
"use strict";
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
7364
|
+
import_node_child_process13 = require("child_process");
|
|
7365
|
+
import_node_fs19 = __toESM(require("fs"));
|
|
7366
|
+
import_node_path19 = __toESM(require("path"));
|
|
7367
|
+
init_devsy_agent();
|
|
6925
7368
|
init_workspace_runtime();
|
|
7369
|
+
DEVSY_DISABLE_TELEMETRY = "DEVSY_DISABLE_TELEMETRY";
|
|
6926
7370
|
}
|
|
6927
7371
|
});
|
|
6928
7372
|
|
|
@@ -6991,7 +7435,7 @@ function inspectDevpodRuntimeStatus(devpodId, repoPath) {
|
|
|
6991
7435
|
if (resolveWorkspaceRuntimeOrDefault(repoPath) === "devsy") {
|
|
6992
7436
|
return inspectDevsyRuntimeStatus(devpodId);
|
|
6993
7437
|
}
|
|
6994
|
-
const result = (0,
|
|
7438
|
+
const result = (0, import_node_child_process14.spawnSync)("devpod", ["status", devpodId, "--output", "json", "--timeout", "5s"], {
|
|
6995
7439
|
encoding: "utf-8"
|
|
6996
7440
|
});
|
|
6997
7441
|
if (result.status !== 0 || result.error) return "unknown";
|
|
@@ -7024,11 +7468,11 @@ function selectDevpodWorkspace(workspaces, repoPath) {
|
|
|
7024
7468
|
}
|
|
7025
7469
|
return matches[0];
|
|
7026
7470
|
}
|
|
7027
|
-
var
|
|
7471
|
+
var import_node_child_process14;
|
|
7028
7472
|
var init_devpod_workspaces = __esm({
|
|
7029
7473
|
"src/core/devpod-workspaces.ts"() {
|
|
7030
7474
|
"use strict";
|
|
7031
|
-
|
|
7475
|
+
import_node_child_process14 = require("child_process");
|
|
7032
7476
|
init_devpod_registry();
|
|
7033
7477
|
init_devsy_workspaces();
|
|
7034
7478
|
init_workspace();
|
|
@@ -7048,7 +7492,7 @@ function failedStartMayHaveAttached(devsyId, repoPath) {
|
|
|
7048
7492
|
}
|
|
7049
7493
|
}
|
|
7050
7494
|
function withMutationLock(activity, target, operation) {
|
|
7051
|
-
|
|
7495
|
+
import_node_fs20.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
7052
7496
|
return withFileLockSync(
|
|
7053
7497
|
DEVSY_MUTATION_LOCK_FILE,
|
|
7054
7498
|
{
|
|
@@ -7062,7 +7506,7 @@ function withMutationLock(activity, target, operation) {
|
|
|
7062
7506
|
);
|
|
7063
7507
|
}
|
|
7064
7508
|
function withMutationLockAsync(activity, target, operation) {
|
|
7065
|
-
|
|
7509
|
+
import_node_fs20.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
7066
7510
|
return withFileLock(
|
|
7067
7511
|
DEVSY_MUTATION_LOCK_FILE,
|
|
7068
7512
|
{
|
|
@@ -7080,7 +7524,7 @@ function commandFailure2(result) {
|
|
|
7080
7524
|
}
|
|
7081
7525
|
function runDevsyUp(args, env, quiet) {
|
|
7082
7526
|
return new Promise((resolve) => {
|
|
7083
|
-
const child = (0,
|
|
7527
|
+
const child = (0, import_node_child_process15.spawn)("devsy", args, {
|
|
7084
7528
|
stdio: ["inherit", quiet ? 2 : "inherit", "pipe"],
|
|
7085
7529
|
env
|
|
7086
7530
|
});
|
|
@@ -7112,7 +7556,7 @@ function runDevsyUp(args, env, quiet) {
|
|
|
7112
7556
|
}
|
|
7113
7557
|
function runDevsyAction(action2, devsyId, force = false) {
|
|
7114
7558
|
const args = action2 === "delete" ? ["delete", devsyId, ...force ? ["--force"] : [], "--ignore-not-found"] : ["stop", devsyId];
|
|
7115
|
-
const result = (0,
|
|
7559
|
+
const result = (0, import_node_child_process15.spawnSync)("devsy", ["workspace", ...args], { encoding: "utf-8" });
|
|
7116
7560
|
if (result.status !== 0) {
|
|
7117
7561
|
throw new Error(
|
|
7118
7562
|
`devsy workspace ${action2}${force ? " --force" : ""} failed for '${devsyId}': ${commandFailure2(result) || "unknown error"}`
|
|
@@ -7172,8 +7616,16 @@ function assertDevsyTarget(devsyId, repoPath) {
|
|
|
7172
7616
|
}
|
|
7173
7617
|
return id;
|
|
7174
7618
|
}
|
|
7175
|
-
function startDevsyWorkspace(options) {
|
|
7619
|
+
async function startDevsyWorkspace(options) {
|
|
7176
7620
|
const activity = options.recreate ? "Devsy recreate" : "Devsy start";
|
|
7621
|
+
let agent;
|
|
7622
|
+
try {
|
|
7623
|
+
agent = requireReadyDevsyAgent();
|
|
7624
|
+
} catch (error) {
|
|
7625
|
+
if (!(error instanceof DevsyAgentReadinessError)) throw error;
|
|
7626
|
+
const repair = devsyAgentRepairSuggestion(error.inspection);
|
|
7627
|
+
throw new Error(`${error.message}. ${repair}`);
|
|
7628
|
+
}
|
|
7177
7629
|
return withMutationLockAsync(activity, options.repoPath, async () => {
|
|
7178
7630
|
let devsyId = assertDevsyTarget(options.devsyId, options.repoPath);
|
|
7179
7631
|
if (devsyId && options.recreate) {
|
|
@@ -7203,6 +7655,7 @@ function startDevsyWorkspace(options) {
|
|
|
7203
7655
|
}
|
|
7204
7656
|
if (options.recreate) args.push("--recreate");
|
|
7205
7657
|
const env = { ...process.env };
|
|
7658
|
+
env.DEVSY_AGENT_BINARY = agent.binaryPath;
|
|
7206
7659
|
if (options.workspace) {
|
|
7207
7660
|
env.WORKSPACE = options.workspace.token;
|
|
7208
7661
|
env.DEVROUTER_WORKSPACE = options.workspace.token;
|
|
@@ -7219,7 +7672,7 @@ function startDevsyWorkspace(options) {
|
|
|
7219
7672
|
let message = `devsy workspace up failed for '${devsyId ?? options.repoPath}'.`;
|
|
7220
7673
|
if (result.error?.message) message += ` ${result.error.message}`;
|
|
7221
7674
|
if (AGENT_ACQUISITION_RE.test(result.stderrTail)) {
|
|
7222
|
-
message +=
|
|
7675
|
+
message += ` Devsy rejected the verified agent source. Run: ${DEVSY_AGENT_SETUP_COMMAND}`;
|
|
7223
7676
|
}
|
|
7224
7677
|
if (failedStartMayHaveAttached(devsyId, options.repoPath)) {
|
|
7225
7678
|
throw new DevsyStartPostconditionError(message);
|
|
@@ -7246,17 +7699,18 @@ function startDevsyWorkspace(options) {
|
|
|
7246
7699
|
}
|
|
7247
7700
|
});
|
|
7248
7701
|
}
|
|
7249
|
-
var
|
|
7702
|
+
var import_node_child_process15, import_node_fs20, import_node_path20, DEVSY_MUTATION_LOCK_FILE, DEVSY_MUTATION_WAIT_MS, DevsyStartPostconditionError, AGENT_ACQUISITION_RE, DEVSY_STDERR_TAIL_BYTES;
|
|
7250
7703
|
var init_devsy_mutation = __esm({
|
|
7251
7704
|
"src/core/devsy-mutation.ts"() {
|
|
7252
7705
|
"use strict";
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7706
|
+
import_node_child_process15 = require("child_process");
|
|
7707
|
+
import_node_fs20 = __toESM(require("fs"));
|
|
7708
|
+
import_node_path20 = __toESM(require("path"));
|
|
7709
|
+
init_devsy_agent();
|
|
7256
7710
|
init_devsy_workspaces();
|
|
7257
7711
|
init_file_lock();
|
|
7258
7712
|
init_router();
|
|
7259
|
-
DEVSY_MUTATION_LOCK_FILE =
|
|
7713
|
+
DEVSY_MUTATION_LOCK_FILE = import_node_path20.default.join(DEVROUTER_HOME, "devsy-mutation.lock");
|
|
7260
7714
|
DEVSY_MUTATION_WAIT_MS = 18e5;
|
|
7261
7715
|
DevsyStartPostconditionError = class extends Error {
|
|
7262
7716
|
};
|
|
@@ -7267,7 +7721,7 @@ var init_devsy_mutation = __esm({
|
|
|
7267
7721
|
|
|
7268
7722
|
// src/core/devpod-mutation.ts
|
|
7269
7723
|
function withMutationLock2(activity, target, operation) {
|
|
7270
|
-
|
|
7724
|
+
import_node_fs21.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
7271
7725
|
return withFileLockSync(
|
|
7272
7726
|
DEVPOD_MUTATION_LOCK_FILE,
|
|
7273
7727
|
{
|
|
@@ -7285,7 +7739,7 @@ function commandFailure3(result) {
|
|
|
7285
7739
|
}
|
|
7286
7740
|
function runDevpodAction(action2, devpodId, force = false) {
|
|
7287
7741
|
const args = action2 === "delete" ? [action2, devpodId, ...force ? ["--force"] : [], "--ignore-not-found"] : [action2, devpodId];
|
|
7288
|
-
const result = (0,
|
|
7742
|
+
const result = (0, import_node_child_process16.spawnSync)("devpod", args, { encoding: "utf-8" });
|
|
7289
7743
|
if (result.status !== 0) {
|
|
7290
7744
|
throw new Error(
|
|
7291
7745
|
`devpod ${action2}${force ? " --force" : ""} failed for '${devpodId}': ${commandFailure3(result) || "unknown error"}`
|
|
@@ -7413,7 +7867,7 @@ async function startDevpodWorkspace(options) {
|
|
|
7413
7867
|
delete env.DEVROUTER_GIT_COMMON_DIR;
|
|
7414
7868
|
delete env.DEVCONTAINER_COMPOSE_OVERLAY;
|
|
7415
7869
|
}
|
|
7416
|
-
const result = (0,
|
|
7870
|
+
const result = (0, import_node_child_process16.spawnSync)("devpod", args, {
|
|
7417
7871
|
stdio: options.quiet ? ["inherit", 2, "inherit"] : "inherit",
|
|
7418
7872
|
env
|
|
7419
7873
|
});
|
|
@@ -7441,19 +7895,19 @@ async function startDevpodWorkspace(options) {
|
|
|
7441
7895
|
}
|
|
7442
7896
|
});
|
|
7443
7897
|
}
|
|
7444
|
-
var
|
|
7898
|
+
var import_node_child_process16, import_node_fs21, import_node_path21, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS, DevpodStartPostconditionError;
|
|
7445
7899
|
var init_devpod_mutation = __esm({
|
|
7446
7900
|
"src/core/devpod-mutation.ts"() {
|
|
7447
7901
|
"use strict";
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7902
|
+
import_node_child_process16 = require("child_process");
|
|
7903
|
+
import_node_fs21 = __toESM(require("fs"));
|
|
7904
|
+
import_node_path21 = __toESM(require("path"));
|
|
7451
7905
|
init_devpod_workspaces();
|
|
7452
7906
|
init_devsy_mutation();
|
|
7453
7907
|
init_file_lock();
|
|
7454
7908
|
init_router();
|
|
7455
7909
|
init_workspace_runtime();
|
|
7456
|
-
DEVPOD_MUTATION_LOCK_FILE =
|
|
7910
|
+
DEVPOD_MUTATION_LOCK_FILE = import_node_path21.default.join(DEVROUTER_HOME, "devpod-mutation.lock");
|
|
7457
7911
|
DEVPOD_MUTATION_WAIT_MS = 18e5;
|
|
7458
7912
|
DevpodStartPostconditionError = class extends Error {
|
|
7459
7913
|
};
|
|
@@ -7467,7 +7921,7 @@ function commandError(command, repoPath, stderr) {
|
|
|
7467
7921
|
);
|
|
7468
7922
|
}
|
|
7469
7923
|
function resolveGitCommonDir(repoPath) {
|
|
7470
|
-
const result = (0,
|
|
7924
|
+
const result = (0, import_node_child_process17.spawnSync)("git", ["-C", repoPath, "rev-parse", "--git-common-dir"], {
|
|
7471
7925
|
encoding: "utf-8",
|
|
7472
7926
|
env: READ_ONLY_GIT_ENV
|
|
7473
7927
|
});
|
|
@@ -7475,10 +7929,10 @@ function resolveGitCommonDir(repoPath) {
|
|
|
7475
7929
|
if (result.status !== 0 || !output2) {
|
|
7476
7930
|
throw commandError("Could not resolve the Git common directory", repoPath, result.stderr);
|
|
7477
7931
|
}
|
|
7478
|
-
return comparableWorkspacePath(
|
|
7932
|
+
return comparableWorkspacePath(import_node_path22.default.isAbsolute(output2) ? output2 : import_node_path22.default.resolve(repoPath, output2));
|
|
7479
7933
|
}
|
|
7480
7934
|
function resolveGitTopLevel(repoPath) {
|
|
7481
|
-
const result = (0,
|
|
7935
|
+
const result = (0, import_node_child_process17.spawnSync)("git", ["-C", repoPath, "rev-parse", "--show-toplevel"], {
|
|
7482
7936
|
encoding: "utf-8",
|
|
7483
7937
|
env: READ_ONLY_GIT_ENV
|
|
7484
7938
|
});
|
|
@@ -7489,7 +7943,7 @@ function resolveGitTopLevel(repoPath) {
|
|
|
7489
7943
|
return comparableWorkspacePath(output2);
|
|
7490
7944
|
}
|
|
7491
7945
|
function listGitWorktrees(repoPath) {
|
|
7492
|
-
const result = (0,
|
|
7946
|
+
const result = (0, import_node_child_process17.spawnSync)("git", ["-C", repoPath, "worktree", "list", "--porcelain"], {
|
|
7493
7947
|
encoding: "utf-8",
|
|
7494
7948
|
env: READ_ONLY_GIT_ENV
|
|
7495
7949
|
});
|
|
@@ -7526,7 +7980,7 @@ function listGitWorktrees(repoPath) {
|
|
|
7526
7980
|
return worktrees;
|
|
7527
7981
|
}
|
|
7528
7982
|
function ownershipDirectory(repoPath) {
|
|
7529
|
-
return
|
|
7983
|
+
return import_node_path22.default.join(resolveGitCommonDir(repoPath), OWNERSHIP_DIR);
|
|
7530
7984
|
}
|
|
7531
7985
|
function validateWorkspace(value, label) {
|
|
7532
7986
|
if (typeof value !== "string" || wsFromBranch(value) !== value) {
|
|
@@ -7554,7 +8008,7 @@ function validateRecord(value, expectedWorkspace) {
|
|
|
7554
8008
|
`workspace ownership file '${expectedWorkspace}' contains identity '${workspace}'`
|
|
7555
8009
|
);
|
|
7556
8010
|
}
|
|
7557
|
-
if (typeof candidate.worktreePath !== "string" || !
|
|
8011
|
+
if (typeof candidate.worktreePath !== "string" || !import_node_path22.default.isAbsolute(candidate.worktreePath)) {
|
|
7558
8012
|
throw new Error("invalid workspace ownership worktreePath");
|
|
7559
8013
|
}
|
|
7560
8014
|
if (candidate.branch !== null && typeof candidate.branch !== "string") {
|
|
@@ -7574,7 +8028,7 @@ function validateRecord(value, expectedWorkspace) {
|
|
|
7574
8028
|
function readRecordFile(filePath, expectedWorkspace) {
|
|
7575
8029
|
let parsed;
|
|
7576
8030
|
try {
|
|
7577
|
-
parsed = JSON.parse(
|
|
8031
|
+
parsed = JSON.parse(import_node_fs22.default.readFileSync(filePath, "utf-8"));
|
|
7578
8032
|
} catch (error) {
|
|
7579
8033
|
if (error instanceof SyntaxError) {
|
|
7580
8034
|
throw new Error(`invalid workspace ownership JSON at '${filePath}'`);
|
|
@@ -7590,7 +8044,7 @@ function listWorkspaceOwnership(repoPath) {
|
|
|
7590
8044
|
function listWorkspaceOwnershipInDirectory(directory) {
|
|
7591
8045
|
let entries;
|
|
7592
8046
|
try {
|
|
7593
|
-
entries =
|
|
8047
|
+
entries = import_node_fs22.default.readdirSync(directory, { withFileTypes: true });
|
|
7594
8048
|
} catch (error) {
|
|
7595
8049
|
if (error.code === "ENOENT") return [];
|
|
7596
8050
|
throw error;
|
|
@@ -7598,14 +8052,14 @@ function listWorkspaceOwnershipInDirectory(directory) {
|
|
|
7598
8052
|
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).sort((left, right) => left.name.localeCompare(right.name)).map((entry) => {
|
|
7599
8053
|
const workspace = entry.name.slice(0, -".json".length);
|
|
7600
8054
|
validateWorkspace(workspace, "filename");
|
|
7601
|
-
return readRecordFile(
|
|
8055
|
+
return readRecordFile(import_node_path22.default.join(directory, entry.name), workspace);
|
|
7602
8056
|
});
|
|
7603
8057
|
}
|
|
7604
8058
|
function writeWorkspaceOwnershipInDirectory(directory, input2) {
|
|
7605
8059
|
const workspace = validateWorkspace(input2.workspace, "workspace");
|
|
7606
8060
|
const devpodId = validateWorkspace(input2.devpodId, "devpodId");
|
|
7607
8061
|
const worktreePath = comparableWorkspacePath(input2.worktreePath);
|
|
7608
|
-
const filePath =
|
|
8062
|
+
const filePath = import_node_path22.default.join(directory, `${workspace}.json`);
|
|
7609
8063
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7610
8064
|
const records = listWorkspaceOwnershipInDirectory(directory);
|
|
7611
8065
|
const existing = records.find((record2) => record2.workspace === workspace);
|
|
@@ -7641,9 +8095,9 @@ function writeWorkspaceOwnershipInDirectory(directory, input2) {
|
|
|
7641
8095
|
return record;
|
|
7642
8096
|
}
|
|
7643
8097
|
function removeWorkspaceOwnershipInDirectory(directory, workspace) {
|
|
7644
|
-
const filePath =
|
|
8098
|
+
const filePath = import_node_path22.default.join(directory, `${validateWorkspace(workspace, "workspace")}.json`);
|
|
7645
8099
|
try {
|
|
7646
|
-
|
|
8100
|
+
import_node_fs22.default.rmSync(filePath);
|
|
7647
8101
|
return true;
|
|
7648
8102
|
} catch (error) {
|
|
7649
8103
|
if (error.code === "ENOENT") return false;
|
|
@@ -7654,7 +8108,7 @@ function sameOwnershipRecord(left, right) {
|
|
|
7654
8108
|
return left.version === right.version && left.workspace === right.workspace && sameWorkspacePath(left.worktreePath, right.worktreePath) && left.branch === right.branch && left.devpodId === right.devpodId && left.createdAt === right.createdAt && left.updatedAt === right.updatedAt;
|
|
7655
8109
|
}
|
|
7656
8110
|
function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
|
|
7657
|
-
const filePath =
|
|
8111
|
+
const filePath = import_node_path22.default.join(
|
|
7658
8112
|
directory,
|
|
7659
8113
|
`${validateWorkspace(expected.workspace, "workspace")}.json`
|
|
7660
8114
|
);
|
|
@@ -7666,14 +8120,14 @@ function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
|
|
|
7666
8120
|
throw error;
|
|
7667
8121
|
}
|
|
7668
8122
|
if (!sameOwnershipRecord(current, expected)) return "changed";
|
|
7669
|
-
|
|
8123
|
+
import_node_fs22.default.rmSync(filePath);
|
|
7670
8124
|
return "removed";
|
|
7671
8125
|
}
|
|
7672
8126
|
function withWorkspaceOwnershipTransaction(repoPath, operation, options = {}) {
|
|
7673
8127
|
const directory = ownershipDirectory(repoPath);
|
|
7674
|
-
|
|
8128
|
+
import_node_fs22.default.mkdirSync(directory, { recursive: true });
|
|
7675
8129
|
return withFileLockSync(
|
|
7676
|
-
|
|
8130
|
+
import_node_path22.default.join(directory, ".lock"),
|
|
7677
8131
|
{
|
|
7678
8132
|
activity: "workspace ownership transaction",
|
|
7679
8133
|
target: `'${repoPath}'`,
|
|
@@ -7706,7 +8160,7 @@ function persistedWorkspaceOwners(repoPath, worktreePath) {
|
|
|
7706
8160
|
try {
|
|
7707
8161
|
workspace = readPersistedWorkspace(worktree.path);
|
|
7708
8162
|
} catch (error) {
|
|
7709
|
-
if (!
|
|
8163
|
+
if (!import_node_fs22.default.existsSync(worktree.path)) continue;
|
|
7710
8164
|
throw error;
|
|
7711
8165
|
}
|
|
7712
8166
|
if (!workspace) continue;
|
|
@@ -7869,7 +8323,7 @@ function inspectWorkspaceOwnership(record, worktrees, devpods) {
|
|
|
7869
8323
|
if (worktree?.locked) {
|
|
7870
8324
|
return { ownerStatus: "locked", devpodStatus, worktree };
|
|
7871
8325
|
}
|
|
7872
|
-
if ((!worktree || worktree.prunable) &&
|
|
8326
|
+
if ((!worktree || worktree.prunable) && import_node_fs22.default.existsSync(record.worktreePath)) {
|
|
7873
8327
|
return { ownerStatus: "conflict", devpodStatus, worktree };
|
|
7874
8328
|
}
|
|
7875
8329
|
if (!worktree || worktree.prunable) {
|
|
@@ -7893,20 +8347,20 @@ function listMissingWorkspaceOwnership(repoPath) {
|
|
|
7893
8347
|
(record) => inspectWorkspaceOwnership(record, worktrees, void 0).ownerStatus === "missing"
|
|
7894
8348
|
);
|
|
7895
8349
|
}
|
|
7896
|
-
var
|
|
8350
|
+
var import_node_child_process17, import_node_fs22, import_node_path22, READ_ONLY_GIT_ENV, OWNERSHIP_VERSION, OWNERSHIP_DIR;
|
|
7897
8351
|
var init_workspace_ownership = __esm({
|
|
7898
8352
|
"src/core/workspace-ownership.ts"() {
|
|
7899
8353
|
"use strict";
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
8354
|
+
import_node_child_process17 = require("child_process");
|
|
8355
|
+
import_node_fs22 = __toESM(require("fs"));
|
|
8356
|
+
import_node_path22 = __toESM(require("path"));
|
|
7903
8357
|
init_atomic_file();
|
|
7904
8358
|
init_devpod_workspaces();
|
|
7905
8359
|
init_file_lock();
|
|
7906
8360
|
init_workspace();
|
|
7907
8361
|
READ_ONLY_GIT_ENV = { ...process.env, GIT_OPTIONAL_LOCKS: "0" };
|
|
7908
8362
|
OWNERSHIP_VERSION = 1;
|
|
7909
|
-
OWNERSHIP_DIR =
|
|
8363
|
+
OWNERSHIP_DIR = import_node_path22.default.join("devrouter", "workspaces");
|
|
7910
8364
|
}
|
|
7911
8365
|
});
|
|
7912
8366
|
|
|
@@ -7920,10 +8374,10 @@ function inRepositoryWorkspaceScope(repoPath, worktreePath, livePaths) {
|
|
|
7920
8374
|
if (livePaths.some((candidate) => sameWorkspacePath(candidate, worktreePath))) return true;
|
|
7921
8375
|
const comparableRepo = comparableWorkspacePath(repoPath);
|
|
7922
8376
|
const comparableWorktree = comparableWorkspacePath(worktreePath);
|
|
7923
|
-
const localRoot =
|
|
8377
|
+
const localRoot = import_node_path23.default.join(comparableRepo, "trees") + import_node_path23.default.sep;
|
|
7924
8378
|
if (comparableWorktree.startsWith(localRoot)) return true;
|
|
7925
|
-
const legacyPrefix = `${
|
|
7926
|
-
return
|
|
8379
|
+
const legacyPrefix = `${import_node_path23.default.basename(comparableRepo)}-`;
|
|
8380
|
+
return import_node_path23.default.dirname(comparableWorktree) === import_node_path23.default.dirname(comparableRepo) && import_node_path23.default.basename(comparableWorktree).startsWith(legacyPrefix);
|
|
7927
8381
|
}
|
|
7928
8382
|
function previewActions(devpodStatus, routeCount, includeRecord) {
|
|
7929
8383
|
const actions = [
|
|
@@ -8175,11 +8629,11 @@ function applyWorkspaceGc(plan) {
|
|
|
8175
8629
|
candidates
|
|
8176
8630
|
};
|
|
8177
8631
|
}
|
|
8178
|
-
var
|
|
8632
|
+
var import_node_path23;
|
|
8179
8633
|
var init_workspace_gc = __esm({
|
|
8180
8634
|
"src/core/workspace-gc.ts"() {
|
|
8181
8635
|
"use strict";
|
|
8182
|
-
|
|
8636
|
+
import_node_path23 = __toESM(require("path"));
|
|
8183
8637
|
init_devpod_mutation();
|
|
8184
8638
|
init_devpod_workspaces();
|
|
8185
8639
|
init_host_routes();
|
|
@@ -8249,11 +8703,11 @@ function inspectPostgresCredentials(repoPath, config) {
|
|
|
8249
8703
|
parseErrors.push(`${app.name}: ${composeFile} (${message})`);
|
|
8250
8704
|
continue;
|
|
8251
8705
|
}
|
|
8252
|
-
if (!
|
|
8706
|
+
if (!import_node_fs23.default.existsSync(absolutePath)) {
|
|
8253
8707
|
continue;
|
|
8254
8708
|
}
|
|
8255
8709
|
try {
|
|
8256
|
-
const raw =
|
|
8710
|
+
const raw = import_node_fs23.default.readFileSync(absolutePath, "utf-8");
|
|
8257
8711
|
const parsed = import_yaml5.default.parse(raw);
|
|
8258
8712
|
const root = asRecord2(parsed);
|
|
8259
8713
|
const services = asRecord2(root?.services);
|
|
@@ -8532,7 +8986,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
8532
8986
|
const config = runtimeConfig.config;
|
|
8533
8987
|
loadedConfig = config;
|
|
8534
8988
|
loadedWorkspace = runtimeConfig.workspace;
|
|
8535
|
-
const cliVersion = true ? "0.0.
|
|
8989
|
+
const cliVersion = true ? "0.0.49" : "0.0.0-dev";
|
|
8536
8990
|
const configVersion = config.devrouter?.version;
|
|
8537
8991
|
if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
|
|
8538
8992
|
addCheck(checks, {
|
|
@@ -8562,9 +9016,9 @@ async function buildDoctorReport(options = {}) {
|
|
|
8562
9016
|
(app) => app.docker.composeFiles.map((filePath) => ({
|
|
8563
9017
|
app: app.name,
|
|
8564
9018
|
filePath,
|
|
8565
|
-
absolutePath:
|
|
9019
|
+
absolutePath: import_node_path24.default.resolve(repo.path, filePath)
|
|
8566
9020
|
}))
|
|
8567
|
-
).filter((entry) => !
|
|
9021
|
+
).filter((entry) => !import_node_fs23.default.existsSync(entry.absolutePath));
|
|
8568
9022
|
addCheck(checks, {
|
|
8569
9023
|
id: "repo.compose-files",
|
|
8570
9024
|
level: missingComposeFiles.length === 0 ? "ok" : "error",
|
|
@@ -8607,8 +9061,8 @@ async function buildDoctorReport(options = {}) {
|
|
|
8607
9061
|
const missingHostCwds = config.apps.filter((app) => app.runtime === "host").map((app) => ({
|
|
8608
9062
|
app: app.name,
|
|
8609
9063
|
cwd: app.hostRun.cwd,
|
|
8610
|
-
absolutePath:
|
|
8611
|
-
})).filter((entry) => !
|
|
9064
|
+
absolutePath: import_node_path24.default.resolve(repo.path, app.hostRun.cwd)
|
|
9065
|
+
})).filter((entry) => !import_node_fs23.default.existsSync(entry.absolutePath));
|
|
8612
9066
|
addCheck(checks, {
|
|
8613
9067
|
id: "repo.host-cwd",
|
|
8614
9068
|
level: missingHostCwds.length === 0 ? "ok" : "error",
|
|
@@ -8752,12 +9206,12 @@ async function buildDoctorReport(options = {}) {
|
|
|
8752
9206
|
nextSteps
|
|
8753
9207
|
};
|
|
8754
9208
|
}
|
|
8755
|
-
var
|
|
9209
|
+
var import_node_fs23, import_node_path24, import_yaml5, POSTGRES_DEFAULTS;
|
|
8756
9210
|
var init_doctor = __esm({
|
|
8757
9211
|
"src/core/doctor.ts"() {
|
|
8758
9212
|
"use strict";
|
|
8759
|
-
|
|
8760
|
-
|
|
9213
|
+
import_node_fs23 = __toESM(require("fs"));
|
|
9214
|
+
import_node_path24 = __toESM(require("path"));
|
|
8761
9215
|
import_yaml5 = __toESM(require("yaml"));
|
|
8762
9216
|
init_docker();
|
|
8763
9217
|
init_host_routes();
|
|
@@ -8864,11 +9318,13 @@ async function runSetup(options = {}) {
|
|
|
8864
9318
|
})
|
|
8865
9319
|
);
|
|
8866
9320
|
}
|
|
9321
|
+
let requestedWorkspaceRuntime;
|
|
8867
9322
|
if (options.workspaceRuntime !== void 0 || options.devsyInactivityTimeout !== void 0) {
|
|
8868
9323
|
try {
|
|
8869
9324
|
const existing = readWorkspaceRuntimeConfig();
|
|
9325
|
+
requestedWorkspaceRuntime = options.workspaceRuntime ? parseWorkspaceRuntime(options.workspaceRuntime) : void 0;
|
|
8870
9326
|
const next = {
|
|
8871
|
-
runtime:
|
|
9327
|
+
runtime: requestedWorkspaceRuntime ?? existing.runtime,
|
|
8872
9328
|
devsyInactivityTimeout: options.devsyInactivityTimeout ?? existing.devsyInactivityTimeout
|
|
8873
9329
|
};
|
|
8874
9330
|
writeWorkspaceRuntimeConfig(next);
|
|
@@ -8890,6 +9346,28 @@ async function runSetup(options = {}) {
|
|
|
8890
9346
|
);
|
|
8891
9347
|
}
|
|
8892
9348
|
}
|
|
9349
|
+
if (requestedWorkspaceRuntime === "devsy") {
|
|
9350
|
+
try {
|
|
9351
|
+
const prepared = await prepareDevsyAgent();
|
|
9352
|
+
actions.push(
|
|
9353
|
+
action(prepared.changed ? "performed" : "skipped", {
|
|
9354
|
+
id: "global.devsy-agent",
|
|
9355
|
+
summary: prepared.changed ? `Downloaded and verified ${prepared.asset.name} for Devsy.` : `Verified the ${prepared.source} Devsy agent source.`,
|
|
9356
|
+
details: `version=${SUPPORTED_DEVSY_VERSION}, asset=${prepared.asset.name}, source=${prepared.source}, transport=${prepared.transport}`
|
|
9357
|
+
})
|
|
9358
|
+
);
|
|
9359
|
+
} catch (error) {
|
|
9360
|
+
const details = error instanceof DevsyAgentReadinessError ? `state=${error.inspection.state}, source=${error.inspection.source}, ${error.inspection.reason}` : error instanceof Error ? error.message : String(error);
|
|
9361
|
+
actions.push(
|
|
9362
|
+
action("failed", {
|
|
9363
|
+
id: "global.devsy-agent",
|
|
9364
|
+
summary: "Failed to prepare a verified Devsy agent source.",
|
|
9365
|
+
details,
|
|
9366
|
+
suggestion: error instanceof DevsyAgentReadinessError ? devsyAgentRepairSuggestion(error.inspection) : `Run: ${DEVSY_AGENT_SETUP_COMMAND}`
|
|
9367
|
+
})
|
|
9368
|
+
);
|
|
9369
|
+
}
|
|
9370
|
+
}
|
|
8893
9371
|
try {
|
|
8894
9372
|
const existed = await networkExists(DEVNET_NAME);
|
|
8895
9373
|
await ensureNetwork(DEVNET_NAME);
|
|
@@ -8979,6 +9457,7 @@ async function runSetup(options = {}) {
|
|
|
8979
9457
|
var init_setup = __esm({
|
|
8980
9458
|
"src/core/setup.ts"() {
|
|
8981
9459
|
"use strict";
|
|
9460
|
+
init_devsy_agent();
|
|
8982
9461
|
init_docker();
|
|
8983
9462
|
init_doctor();
|
|
8984
9463
|
init_repo_config();
|
|
@@ -9047,12 +9526,12 @@ function parseSsPortListeners(stdout, port) {
|
|
|
9047
9526
|
return listeners;
|
|
9048
9527
|
}
|
|
9049
9528
|
function findPortListeners(port) {
|
|
9050
|
-
const result = (0,
|
|
9529
|
+
const result = (0, import_node_child_process18.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
|
|
9051
9530
|
encoding: "utf-8"
|
|
9052
9531
|
});
|
|
9053
9532
|
if (result.error && result.error.code === "ENOENT") {
|
|
9054
9533
|
if (process.platform === "linux") {
|
|
9055
|
-
const ssResult = (0,
|
|
9534
|
+
const ssResult = (0, import_node_child_process18.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
|
|
9056
9535
|
if (ssResult.status === 0 && ssResult.stdout) {
|
|
9057
9536
|
return parseSsPortListeners(ssResult.stdout, port);
|
|
9058
9537
|
}
|
|
@@ -9081,11 +9560,11 @@ function findPortListeners(port) {
|
|
|
9081
9560
|
};
|
|
9082
9561
|
});
|
|
9083
9562
|
}
|
|
9084
|
-
var
|
|
9563
|
+
var import_node_child_process18;
|
|
9085
9564
|
var init_ports = __esm({
|
|
9086
9565
|
"src/util/ports.ts"() {
|
|
9087
9566
|
"use strict";
|
|
9088
|
-
|
|
9567
|
+
import_node_child_process18 = require("child_process");
|
|
9089
9568
|
}
|
|
9090
9569
|
});
|
|
9091
9570
|
|
|
@@ -9162,18 +9641,18 @@ function probeHttpRoute(host, options = {}) {
|
|
|
9162
9641
|
args.push("--cacert", CERT_FILE);
|
|
9163
9642
|
}
|
|
9164
9643
|
args.push(url);
|
|
9165
|
-
const result = (0,
|
|
9644
|
+
const result = (0, import_node_child_process19.spawnSync)("curl", args, { encoding: "utf-8" });
|
|
9166
9645
|
const stdout = result.stdout?.trim() ?? "";
|
|
9167
9646
|
const status = /^\d{3}$/.test(stdout) ? Number(stdout) : void 0;
|
|
9168
9647
|
const ok = result.status === 0 && status !== void 0 && status >= 100 && status < 500;
|
|
9169
9648
|
const details = ok ? `HTTP ${status}` : [status === void 0 ? void 0 : `HTTP ${status}`, result.stderr?.trim()].filter(Boolean).join(": ") || `curl exited with status ${result.status ?? "unknown"}`;
|
|
9170
9649
|
return { ok, status, details };
|
|
9171
9650
|
}
|
|
9172
|
-
var
|
|
9651
|
+
var import_node_child_process19;
|
|
9173
9652
|
var init_http_route_probe = __esm({
|
|
9174
9653
|
"src/core/http-route-probe.ts"() {
|
|
9175
9654
|
"use strict";
|
|
9176
|
-
|
|
9655
|
+
import_node_child_process19 = require("child_process");
|
|
9177
9656
|
init_router();
|
|
9178
9657
|
init_tls();
|
|
9179
9658
|
}
|
|
@@ -9249,14 +9728,143 @@ var init_route_publication = __esm({
|
|
|
9249
9728
|
}
|
|
9250
9729
|
});
|
|
9251
9730
|
|
|
9731
|
+
// src/core/traefik-route-health.ts
|
|
9732
|
+
function sleep(ms) {
|
|
9733
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9734
|
+
}
|
|
9735
|
+
function inspectRouterApi(protocol) {
|
|
9736
|
+
const result = (0, import_node_child_process20.spawnSync)(
|
|
9737
|
+
"curl",
|
|
9738
|
+
[
|
|
9739
|
+
"--silent",
|
|
9740
|
+
"--show-error",
|
|
9741
|
+
"--fail",
|
|
9742
|
+
"--max-time",
|
|
9743
|
+
"2",
|
|
9744
|
+
`${ROUTER_API_BASE}/${protocol}/routers?per_page=${ROUTER_API_PAGE_SIZE}`
|
|
9745
|
+
],
|
|
9746
|
+
{ encoding: "utf-8" }
|
|
9747
|
+
);
|
|
9748
|
+
if (result.status !== 0) {
|
|
9749
|
+
return {
|
|
9750
|
+
ok: false,
|
|
9751
|
+
details: result.stderr?.trim() || `curl exited with status ${result.status ?? "unknown"}`
|
|
9752
|
+
};
|
|
9753
|
+
}
|
|
9754
|
+
let value;
|
|
9755
|
+
try {
|
|
9756
|
+
value = JSON.parse(result.stdout);
|
|
9757
|
+
} catch {
|
|
9758
|
+
return { ok: false, details: `${protocol.toUpperCase()} router API returned invalid JSON` };
|
|
9759
|
+
}
|
|
9760
|
+
if (!Array.isArray(value)) {
|
|
9761
|
+
return { ok: false, details: `${protocol.toUpperCase()} router API returned a non-array` };
|
|
9762
|
+
}
|
|
9763
|
+
return {
|
|
9764
|
+
ok: true,
|
|
9765
|
+
reachedPageLimit: value.length >= ROUTER_API_PAGE_SIZE,
|
|
9766
|
+
names: new Set(
|
|
9767
|
+
value.flatMap((item) => {
|
|
9768
|
+
if (!item || typeof item !== "object") return [];
|
|
9769
|
+
const name = item.name;
|
|
9770
|
+
return typeof name === "string" ? [name] : [];
|
|
9771
|
+
})
|
|
9772
|
+
)
|
|
9773
|
+
};
|
|
9774
|
+
}
|
|
9775
|
+
function inspectExpectedRoutes(routes) {
|
|
9776
|
+
const missing = [];
|
|
9777
|
+
const failures = [];
|
|
9778
|
+
for (const protocol of ["http", "tcp"]) {
|
|
9779
|
+
const expected = routes.filter((route) => (route.protocol ?? "http") === protocol).map((route) => buildHostRouteRouterName(route.repoPath, route.name));
|
|
9780
|
+
if (expected.length === 0) continue;
|
|
9781
|
+
const result = inspectRouterApi(protocol);
|
|
9782
|
+
if (!result.ok) {
|
|
9783
|
+
failures.push(result.details);
|
|
9784
|
+
missing.push(...expected);
|
|
9785
|
+
continue;
|
|
9786
|
+
}
|
|
9787
|
+
const absent = expected.filter((name) => !result.names.has(name));
|
|
9788
|
+
if (absent.length > 0 && result.reachedPageLimit) {
|
|
9789
|
+
failures.push(
|
|
9790
|
+
`${protocol.toUpperCase()} router API reached the ${ROUTER_API_PAGE_SIZE}-entry safety limit`
|
|
9791
|
+
);
|
|
9792
|
+
}
|
|
9793
|
+
missing.push(...absent);
|
|
9794
|
+
}
|
|
9795
|
+
return missing.length === 0 ? { ok: true } : {
|
|
9796
|
+
ok: false,
|
|
9797
|
+
missing,
|
|
9798
|
+
details: failures.length > 0 ? failures.join("; ") : "router names are absent"
|
|
9799
|
+
};
|
|
9800
|
+
}
|
|
9801
|
+
async function waitForExpectedRoutes(routes, timeoutMs, pollIntervalMs) {
|
|
9802
|
+
const deadline = Date.now() + timeoutMs;
|
|
9803
|
+
let result;
|
|
9804
|
+
do {
|
|
9805
|
+
result = inspectExpectedRoutes(routes);
|
|
9806
|
+
if (result.ok) return result;
|
|
9807
|
+
if (Date.now() < deadline) {
|
|
9808
|
+
await sleep(Math.min(pollIntervalMs, Math.max(1, deadline - Date.now())));
|
|
9809
|
+
}
|
|
9810
|
+
} while (Date.now() < deadline);
|
|
9811
|
+
return result;
|
|
9812
|
+
}
|
|
9813
|
+
async function ensureTraefikRoutesLoaded(routes, options = {}) {
|
|
9814
|
+
if (routes.length === 0) return { restarted: false };
|
|
9815
|
+
const initialTimeoutMs = options.initialTimeoutMs ?? DEFAULT_INITIAL_TIMEOUT_MS;
|
|
9816
|
+
const recoveryTimeoutMs = options.recoveryTimeoutMs ?? DEFAULT_RECOVERY_TIMEOUT_MS;
|
|
9817
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
9818
|
+
const initial = await waitForExpectedRoutes(routes, initialTimeoutMs, pollIntervalMs);
|
|
9819
|
+
if (initial.ok) return { restarted: false };
|
|
9820
|
+
import_node_fs24.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
9821
|
+
return withFileLock(
|
|
9822
|
+
ROUTER_RELOAD_LOCK_FILE,
|
|
9823
|
+
{
|
|
9824
|
+
activity: "Traefik route reload recovery",
|
|
9825
|
+
waitMs: initialTimeoutMs + recoveryTimeoutMs + 1e4,
|
|
9826
|
+
fair: true,
|
|
9827
|
+
onWait: createStderrWaitReporter("Traefik route reload recovery", "shared router")
|
|
9828
|
+
},
|
|
9829
|
+
async () => {
|
|
9830
|
+
const recheck = await waitForExpectedRoutes(routes, 0, pollIntervalMs);
|
|
9831
|
+
if (recheck.ok) return { restarted: false };
|
|
9832
|
+
restartRouterStack();
|
|
9833
|
+
const recovered = await waitForExpectedRoutes(routes, recoveryTimeoutMs, pollIntervalMs);
|
|
9834
|
+
if (recovered.ok) return { restarted: true };
|
|
9835
|
+
throw new Error(
|
|
9836
|
+
`Traefik did not load file-provider routes after one restart: ${recovered.missing.join(", ")} (${recovered.details}). Inspect: devrouter logs --tail 100`
|
|
9837
|
+
);
|
|
9838
|
+
}
|
|
9839
|
+
);
|
|
9840
|
+
}
|
|
9841
|
+
var import_node_child_process20, import_node_fs24, import_node_path25, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
|
|
9842
|
+
var init_traefik_route_health = __esm({
|
|
9843
|
+
"src/core/traefik-route-health.ts"() {
|
|
9844
|
+
"use strict";
|
|
9845
|
+
import_node_child_process20 = require("child_process");
|
|
9846
|
+
import_node_fs24 = __toESM(require("fs"));
|
|
9847
|
+
import_node_path25 = __toESM(require("path"));
|
|
9848
|
+
init_file_lock();
|
|
9849
|
+
init_host_routes();
|
|
9850
|
+
init_router();
|
|
9851
|
+
ROUTER_API_BASE = "http://127.0.0.1:8080/api";
|
|
9852
|
+
ROUTER_RELOAD_LOCK_FILE = import_node_path25.default.join(DEVROUTER_HOME, "router-reload.lock");
|
|
9853
|
+
DEFAULT_INITIAL_TIMEOUT_MS = 3e3;
|
|
9854
|
+
DEFAULT_RECOVERY_TIMEOUT_MS = 1e4;
|
|
9855
|
+
DEFAULT_POLL_INTERVAL_MS = 250;
|
|
9856
|
+
ROUTER_API_PAGE_SIZE = 1e3;
|
|
9857
|
+
}
|
|
9858
|
+
});
|
|
9859
|
+
|
|
9252
9860
|
// src/core/workspace-ensure.ts
|
|
9253
9861
|
function assertOverlay(container, repoPath) {
|
|
9254
9862
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
9255
|
-
if (!workingDir || !sameWorkspacePath(workingDir,
|
|
9863
|
+
if (!workingDir || !sameWorkspacePath(workingDir, import_node_path26.default.join(repoPath, ".devcontainer"))) {
|
|
9256
9864
|
throw new Error(`Container '${container.id}' does not belong to the exact worktree.`);
|
|
9257
9865
|
}
|
|
9258
9866
|
const configFiles = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").filter(Boolean);
|
|
9259
|
-
const expectedOverlay =
|
|
9867
|
+
const expectedOverlay = import_node_path26.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
9260
9868
|
if (!configFiles.some((configFile) => sameWorkspacePath(configFile, expectedOverlay))) {
|
|
9261
9869
|
throw new Error(`Container '${container.id}' was not started with ${DEVCONTAINER_OVERLAY}.`);
|
|
9262
9870
|
}
|
|
@@ -9330,7 +9938,7 @@ async function waitForManagedServices(plan, repoPath, composeProject, expectedSe
|
|
|
9330
9938
|
} catch (error) {
|
|
9331
9939
|
lastError = error;
|
|
9332
9940
|
}
|
|
9333
|
-
if (Date.now() < deadline) await
|
|
9941
|
+
if (Date.now() < deadline) await sleep2(POLL_INTERVAL_MS);
|
|
9334
9942
|
} while (Date.now() < deadline);
|
|
9335
9943
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
9336
9944
|
}
|
|
@@ -9439,7 +10047,7 @@ function hasExactManagedComposeProject(repoPath, state) {
|
|
|
9439
10047
|
}).some((container) => {
|
|
9440
10048
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
9441
10049
|
return Boolean(
|
|
9442
|
-
workingDir && sameWorkspacePath(workingDir,
|
|
10050
|
+
workingDir && sameWorkspacePath(workingDir, import_node_path26.default.join(repoPath, ".devcontainer"))
|
|
9443
10051
|
);
|
|
9444
10052
|
});
|
|
9445
10053
|
} catch {
|
|
@@ -9533,7 +10141,7 @@ function validateWorkspaceContainers(containers, options) {
|
|
|
9533
10141
|
}
|
|
9534
10142
|
return { id: appContainer.id, workspacePath: repoMount.Destination };
|
|
9535
10143
|
}
|
|
9536
|
-
function
|
|
10144
|
+
function sleep2(ms) {
|
|
9537
10145
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9538
10146
|
}
|
|
9539
10147
|
async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeoutMs) {
|
|
@@ -9547,7 +10155,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
9547
10155
|
target
|
|
9548
10156
|
});
|
|
9549
10157
|
if (target.kind === "linked") {
|
|
9550
|
-
const workspaceEnv = (0,
|
|
10158
|
+
const workspaceEnv = (0, import_node_child_process21.spawnSync)(
|
|
9551
10159
|
"docker",
|
|
9552
10160
|
["exec", appContainer.id, "printenv", "WORKSPACE"],
|
|
9553
10161
|
{ encoding: "utf-8" }
|
|
@@ -9557,7 +10165,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
9557
10165
|
`Workspace app container must expose WORKSPACE='${target.workspace}' (got '${workspaceEnv.stdout.trim() || "(empty)"}').`
|
|
9558
10166
|
);
|
|
9559
10167
|
}
|
|
9560
|
-
const devrouterWorkspaceEnv = (0,
|
|
10168
|
+
const devrouterWorkspaceEnv = (0, import_node_child_process21.spawnSync)(
|
|
9561
10169
|
"docker",
|
|
9562
10170
|
["exec", appContainer.id, "printenv", "DEVROUTER_WORKSPACE"],
|
|
9563
10171
|
{ encoding: "utf-8" }
|
|
@@ -9568,7 +10176,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
9568
10176
|
);
|
|
9569
10177
|
}
|
|
9570
10178
|
}
|
|
9571
|
-
const gitCheck = (0,
|
|
10179
|
+
const gitCheck = (0, import_node_child_process21.spawnSync)(
|
|
9572
10180
|
"docker",
|
|
9573
10181
|
[
|
|
9574
10182
|
"exec",
|
|
@@ -9589,7 +10197,7 @@ async function waitForContainerPreflight(repoPath, target, upstreamHosts, timeou
|
|
|
9589
10197
|
lastError = error;
|
|
9590
10198
|
}
|
|
9591
10199
|
if (Date.now() < deadline) {
|
|
9592
|
-
await
|
|
10200
|
+
await sleep2(POLL_INTERVAL_MS);
|
|
9593
10201
|
}
|
|
9594
10202
|
} while (Date.now() < deadline);
|
|
9595
10203
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
@@ -9614,7 +10222,7 @@ async function waitForHttpRoutes(repoPath, apps, timeoutMs) {
|
|
|
9614
10222
|
return;
|
|
9615
10223
|
}
|
|
9616
10224
|
if (Date.now() < deadline) {
|
|
9617
|
-
await
|
|
10225
|
+
await sleep2(POLL_INTERVAL_MS);
|
|
9618
10226
|
}
|
|
9619
10227
|
} while (Date.now() < deadline);
|
|
9620
10228
|
throw new Error(
|
|
@@ -9660,14 +10268,14 @@ function resolvePrimaryTarget(repoPath) {
|
|
|
9660
10268
|
}
|
|
9661
10269
|
function isPrimaryCheckout(repoPath) {
|
|
9662
10270
|
try {
|
|
9663
|
-
return
|
|
10271
|
+
return import_node_fs25.default.statSync(import_node_path26.default.join(repoPath, ".git")).isDirectory();
|
|
9664
10272
|
} catch {
|
|
9665
10273
|
return false;
|
|
9666
10274
|
}
|
|
9667
10275
|
}
|
|
9668
10276
|
function openUrls(urls) {
|
|
9669
10277
|
for (const url of urls) {
|
|
9670
|
-
const opened = (0,
|
|
10278
|
+
const opened = (0, import_node_child_process21.spawnSync)("open", [url], { encoding: "utf-8" });
|
|
9671
10279
|
if (opened.status !== 0) {
|
|
9672
10280
|
process.stderr.write(`Warning: could not open '${url}'.
|
|
9673
10281
|
`);
|
|
@@ -9706,12 +10314,16 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
9706
10314
|
let managedConfigWritten = false;
|
|
9707
10315
|
let managedWorkspaceEnv;
|
|
9708
10316
|
let firstTransitionBaseline;
|
|
10317
|
+
const routeLoadOptions = {
|
|
10318
|
+
initialTimeoutMs: Math.min(options.httpTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS, 3e3),
|
|
10319
|
+
recoveryTimeoutMs: Math.min(options.httpTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS, 1e4)
|
|
10320
|
+
};
|
|
9709
10321
|
try {
|
|
9710
10322
|
const target = linked ? resolveLinkedTarget(repoPath) : resolvePrimaryTarget(repoPath);
|
|
9711
10323
|
let devpodId = target.devpodId;
|
|
9712
10324
|
if (target.kind === "linked") {
|
|
9713
|
-
const overlayPath =
|
|
9714
|
-
if (!
|
|
10325
|
+
const overlayPath = import_node_path26.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
10326
|
+
if (!import_node_fs25.default.existsSync(overlayPath)) {
|
|
9715
10327
|
throw new Error(`Missing required DevPod compose overlay: ${overlayPath}`);
|
|
9716
10328
|
}
|
|
9717
10329
|
}
|
|
@@ -9781,7 +10393,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
9781
10393
|
}
|
|
9782
10394
|
const apps = proxyAppsFromConfig(runtime.config);
|
|
9783
10395
|
const parsedUpstreams = apps.map((app) => parseUpstream(app.upstream));
|
|
9784
|
-
const aliasPrefix = target.kind === "linked" ? target.workspace : wsFromBranch(runtime.config.project?.name ??
|
|
10396
|
+
const aliasPrefix = target.kind === "linked" ? target.workspace : wsFromBranch(runtime.config.project?.name ?? import_node_path26.default.basename(repoPath)) ?? "app";
|
|
9785
10397
|
for (const [index, app] of apps.entries()) {
|
|
9786
10398
|
if (!parsedUpstreams[index].host.startsWith(`${aliasPrefix}-`)) {
|
|
9787
10399
|
const owner = target.kind === "linked" ? "workspace" : "checkout";
|
|
@@ -9945,6 +10557,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
9945
10557
|
target.workspace
|
|
9946
10558
|
);
|
|
9947
10559
|
candidateRoutesPublished = true;
|
|
10560
|
+
await ensureTraefikRoutesLoaded(publication.routes, routeLoadOptions);
|
|
9948
10561
|
try {
|
|
9949
10562
|
await waitForHttpRoutes(
|
|
9950
10563
|
repoPath,
|
|
@@ -9968,6 +10581,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
9968
10581
|
});
|
|
9969
10582
|
recreated = true;
|
|
9970
10583
|
replaceHostRoutesForRepo(repoPath, publication.routes);
|
|
10584
|
+
await ensureTraefikRoutesLoaded(publication.routes, routeLoadOptions);
|
|
9971
10585
|
await waitForHttpRoutes(
|
|
9972
10586
|
repoPath,
|
|
9973
10587
|
apps,
|
|
@@ -10141,7 +10755,9 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
10141
10755
|
}
|
|
10142
10756
|
if (candidateRoutesPublished) {
|
|
10143
10757
|
try {
|
|
10144
|
-
|
|
10758
|
+
const rollbackRoutes = previousRoutes.map(routeInputFromState);
|
|
10759
|
+
replaceHostRoutesForRepo(repoPath, rollbackRoutes);
|
|
10760
|
+
await ensureTraefikRoutesLoaded(rollbackRoutes, routeLoadOptions);
|
|
10145
10761
|
} catch (rollbackError) {
|
|
10146
10762
|
rollbackErrors.push(
|
|
10147
10763
|
`routes: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`
|
|
@@ -10184,7 +10800,9 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
10184
10800
|
}
|
|
10185
10801
|
if (candidateRoutesPublished) {
|
|
10186
10802
|
try {
|
|
10187
|
-
|
|
10803
|
+
const rollbackRoutes = previousRoutes.map(routeInputFromState);
|
|
10804
|
+
replaceHostRoutesForRepo(repoPath, rollbackRoutes);
|
|
10805
|
+
await ensureTraefikRoutesLoaded(rollbackRoutes, routeLoadOptions);
|
|
10188
10806
|
} catch (rollbackError) {
|
|
10189
10807
|
rollbackErrors.push(
|
|
10190
10808
|
`routes: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`
|
|
@@ -10213,13 +10831,13 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
10213
10831
|
}
|
|
10214
10832
|
});
|
|
10215
10833
|
}
|
|
10216
|
-
var
|
|
10834
|
+
var import_node_child_process21, import_node_fs25, import_node_path26, DEVCONTAINER_OVERLAY, DEFAULT_READINESS_TIMEOUT_MS, POLL_INTERVAL_MS;
|
|
10217
10835
|
var init_workspace_ensure = __esm({
|
|
10218
10836
|
"src/core/workspace-ensure.ts"() {
|
|
10219
10837
|
"use strict";
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10838
|
+
import_node_child_process21 = require("child_process");
|
|
10839
|
+
import_node_fs25 = __toESM(require("fs"));
|
|
10840
|
+
import_node_path26 = __toESM(require("path"));
|
|
10223
10841
|
init_devcontainer_profile();
|
|
10224
10842
|
init_devpod_environment();
|
|
10225
10843
|
init_devpod_mutation();
|
|
@@ -10232,6 +10850,7 @@ var init_workspace_ensure = __esm({
|
|
|
10232
10850
|
init_repo_config();
|
|
10233
10851
|
init_route_publication();
|
|
10234
10852
|
init_router();
|
|
10853
|
+
init_traefik_route_health();
|
|
10235
10854
|
init_workspace();
|
|
10236
10855
|
init_workspace_ownership();
|
|
10237
10856
|
init_workspace_runtime();
|
|
@@ -10305,20 +10924,20 @@ function warnMissingWorkspaceOwnership(repoPath) {
|
|
|
10305
10924
|
);
|
|
10306
10925
|
}
|
|
10307
10926
|
function defaultWorktreePath(mainRepo, ws) {
|
|
10308
|
-
return
|
|
10927
|
+
return import_node_path27.default.join(mainRepo, "trees", ws);
|
|
10309
10928
|
}
|
|
10310
10929
|
function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
10311
|
-
const ignored = (0,
|
|
10930
|
+
const ignored = (0, import_node_child_process22.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
|
|
10312
10931
|
encoding: "utf-8"
|
|
10313
10932
|
});
|
|
10314
10933
|
if (ignored.status !== 0) {
|
|
10315
10934
|
throw new Error(
|
|
10316
|
-
`Default worktree root '${
|
|
10935
|
+
`Default worktree root '${import_node_path27.default.join(mainRepo, "trees")}' is not ignored. Add 'trees/' to '${import_node_path27.default.join(mainRepo, ".gitignore")}' or use --path.`
|
|
10317
10936
|
);
|
|
10318
10937
|
}
|
|
10319
10938
|
}
|
|
10320
10939
|
function legacyDefaultWorktreePath(mainRepo, ws) {
|
|
10321
|
-
return
|
|
10940
|
+
return import_node_path27.default.join(import_node_path27.default.dirname(mainRepo), `${import_node_path27.default.basename(mainRepo)}-${ws}`);
|
|
10322
10941
|
}
|
|
10323
10942
|
function teardownFallbackPath(mainRepo, workspace) {
|
|
10324
10943
|
const candidates = [
|
|
@@ -10430,13 +11049,13 @@ function assertFullDownPreflight(mainRepo, target) {
|
|
|
10430
11049
|
if (sameWorkspacePath(target.worktreePath, mainRepo)) {
|
|
10431
11050
|
throw new Error("Refusing to remove the primary Git checkout.");
|
|
10432
11051
|
}
|
|
10433
|
-
if (!target.worktree || target.worktree.prunable || !
|
|
11052
|
+
if (!target.worktree || target.worktree.prunable || !import_node_fs26.default.existsSync(target.worktreePath)) return;
|
|
10434
11053
|
if (target.worktree.locked) {
|
|
10435
11054
|
throw new Error(
|
|
10436
11055
|
`Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
|
|
10437
11056
|
);
|
|
10438
11057
|
}
|
|
10439
|
-
const status = (0,
|
|
11058
|
+
const status = (0, import_node_child_process22.spawnSync)(
|
|
10440
11059
|
"git",
|
|
10441
11060
|
["-C", target.worktreePath, "status", "--porcelain", "--untracked-files=normal"],
|
|
10442
11061
|
{ encoding: "utf-8" }
|
|
@@ -10470,9 +11089,9 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
10470
11089
|
(candidate) => defaultWorktreePath(mainRepo, candidate)
|
|
10471
11090
|
);
|
|
10472
11091
|
const generatedPath = candidatePaths.find(
|
|
10473
|
-
(candidate) => !
|
|
11092
|
+
(candidate) => !import_node_fs26.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
|
|
10474
11093
|
);
|
|
10475
|
-
const selectedPath = opts.path ?
|
|
11094
|
+
const selectedPath = opts.path ? import_node_path27.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
|
|
10476
11095
|
if (!selectedPath) {
|
|
10477
11096
|
throw new Error(`Could not allocate a collision-safe worktree path for branch '${branch}'.`);
|
|
10478
11097
|
}
|
|
@@ -10481,7 +11100,7 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
10481
11100
|
`Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
|
|
10482
11101
|
);
|
|
10483
11102
|
}
|
|
10484
|
-
if (
|
|
11103
|
+
if (import_node_fs26.default.existsSync(selectedPath)) {
|
|
10485
11104
|
const registered = worktrees.find(
|
|
10486
11105
|
(worktree) => sameWorkspacePath(worktree.path, selectedPath)
|
|
10487
11106
|
);
|
|
@@ -10500,11 +11119,11 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
10500
11119
|
return selectedPath;
|
|
10501
11120
|
}
|
|
10502
11121
|
if (!opts.path) assertDefaultWorktreeRootIgnored(mainRepo);
|
|
10503
|
-
const add = (0,
|
|
11122
|
+
const add = (0, import_node_child_process22.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
|
|
10504
11123
|
encoding: "utf-8"
|
|
10505
11124
|
});
|
|
10506
11125
|
if (add.status !== 0) {
|
|
10507
|
-
const addNew = (0,
|
|
11126
|
+
const addNew = (0, import_node_child_process22.spawnSync)(
|
|
10508
11127
|
"git",
|
|
10509
11128
|
["-C", mainRepo, "worktree", "add", "-b", branch, selectedPath],
|
|
10510
11129
|
{ encoding: "utf-8" }
|
|
@@ -10606,8 +11225,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
10606
11225
|
opts.quiet
|
|
10607
11226
|
);
|
|
10608
11227
|
if (removeWorktree) {
|
|
10609
|
-
if (resolved.worktree && !resolved.worktree.prunable &&
|
|
10610
|
-
const rm = (0,
|
|
11228
|
+
if (resolved.worktree && !resolved.worktree.prunable && import_node_fs26.default.existsSync(resolved.worktreePath)) {
|
|
11229
|
+
const rm = (0, import_node_child_process22.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
|
|
10611
11230
|
encoding: "utf-8"
|
|
10612
11231
|
});
|
|
10613
11232
|
if (rm.status !== 0) {
|
|
@@ -10625,7 +11244,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
10625
11244
|
}
|
|
10626
11245
|
return result;
|
|
10627
11246
|
};
|
|
10628
|
-
return resolved.worktree && !resolved.worktree.prunable &&
|
|
11247
|
+
return resolved.worktree && !resolved.worktree.prunable && import_node_fs26.default.existsSync(resolved.worktreePath) ? withWorkspaceLifecycleLock(resolved.worktreePath, operation) : operation();
|
|
10629
11248
|
}
|
|
10630
11249
|
async function mutateWorkspaceOwnedPath(action2, worktreePath, opts = {}) {
|
|
10631
11250
|
const mainRepo = resolveRepoPath(opts.repoPath);
|
|
@@ -10660,13 +11279,13 @@ async function workspaceStop(target, opts = {}) {
|
|
|
10660
11279
|
async function workspaceDown(target, opts = {}) {
|
|
10661
11280
|
return runWorkspaceLifecycle("down", target, opts);
|
|
10662
11281
|
}
|
|
10663
|
-
var
|
|
11282
|
+
var import_node_child_process22, import_node_fs26, import_node_path27, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
|
|
10664
11283
|
var init_workspace_lifecycle = __esm({
|
|
10665
11284
|
"src/core/workspace-lifecycle.ts"() {
|
|
10666
11285
|
"use strict";
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
11286
|
+
import_node_child_process22 = require("child_process");
|
|
11287
|
+
import_node_fs26 = __toESM(require("fs"));
|
|
11288
|
+
import_node_path27 = __toESM(require("path"));
|
|
10670
11289
|
init_devpod_mutation();
|
|
10671
11290
|
init_devpod_workspaces();
|
|
10672
11291
|
init_repo_config();
|
|
@@ -10795,17 +11414,17 @@ async function devsyExec(repoPath, command) {
|
|
|
10795
11414
|
}
|
|
10796
11415
|
const args = ["workspace", "exec", "--result-format", "plain", workspace.id, "--", ...command];
|
|
10797
11416
|
return new Promise((resolve, reject) => {
|
|
10798
|
-
const child = (0,
|
|
11417
|
+
const child = (0, import_node_child_process23.spawn)("devsy", args, { stdio: "inherit" });
|
|
10799
11418
|
child.once("error", (error) => reject(new Error(`devsy exec failed: ${error.message}`)));
|
|
10800
11419
|
child.once("close", (code) => resolve(code ?? 1));
|
|
10801
11420
|
});
|
|
10802
11421
|
});
|
|
10803
11422
|
}
|
|
10804
|
-
var
|
|
11423
|
+
var import_node_child_process23;
|
|
10805
11424
|
var init_devsy_exec = __esm({
|
|
10806
11425
|
"src/core/devsy-exec.ts"() {
|
|
10807
11426
|
"use strict";
|
|
10808
|
-
|
|
11427
|
+
import_node_child_process23 = require("child_process");
|
|
10809
11428
|
init_devsy_workspaces();
|
|
10810
11429
|
init_workspace();
|
|
10811
11430
|
}
|
|
@@ -10827,7 +11446,7 @@ function ensureGuidance2(repoPath) {
|
|
|
10827
11446
|
return `Run 'devrouter ensure ${repoPath}' first.`;
|
|
10828
11447
|
}
|
|
10829
11448
|
function assertRunningDevpod(devpodId, repoPath) {
|
|
10830
|
-
const result = (0,
|
|
11449
|
+
const result = (0, import_node_child_process24.spawnSync)("devpod", ["status", devpodId, "--output", "json"], {
|
|
10831
11450
|
encoding: "utf-8"
|
|
10832
11451
|
});
|
|
10833
11452
|
if (result.status !== 0) {
|
|
@@ -10868,7 +11487,7 @@ async function devpodExec(repoPath, command) {
|
|
|
10868
11487
|
}
|
|
10869
11488
|
assertRunningDevpod(devpod.id, repoPath);
|
|
10870
11489
|
const workspaceDirectory = resolveWorkspaceDirectory(repoPath);
|
|
10871
|
-
const statusMarker = `__DEVROUTER_EXIT_${(0,
|
|
11490
|
+
const statusMarker = `__DEVROUTER_EXIT_${(0, import_node_crypto9.randomUUID)()}__:`;
|
|
10872
11491
|
const statusMarkerBytes = Buffer.from(statusMarker, "ascii");
|
|
10873
11492
|
const literalCommand = command.map(quotePosixArg).join(" ");
|
|
10874
11493
|
const wrappedCommand = `${literalCommand}; __devrouter_status=$?; printf '${statusMarker}%s\\n' "$__devrouter_status" >&2; exit 0`;
|
|
@@ -10886,7 +11505,7 @@ async function devpodExec(repoPath, command) {
|
|
|
10886
11505
|
wrappedCommand
|
|
10887
11506
|
];
|
|
10888
11507
|
return new Promise((resolve, reject) => {
|
|
10889
|
-
const child = (0,
|
|
11508
|
+
const child = (0, import_node_child_process24.spawn)("devpod", args, { stdio: ["inherit", "inherit", "pipe"] });
|
|
10890
11509
|
let pending = Buffer.alloc(0);
|
|
10891
11510
|
let statusBytes = Buffer.alloc(0);
|
|
10892
11511
|
let readingStatus = false;
|
|
@@ -10945,12 +11564,12 @@ async function devpodExec(repoPath, command) {
|
|
|
10945
11564
|
});
|
|
10946
11565
|
});
|
|
10947
11566
|
}
|
|
10948
|
-
var
|
|
11567
|
+
var import_node_child_process24, import_node_crypto9, DEVPOD_MISSING_EXIT_STATUS_DIAGNOSTIC;
|
|
10949
11568
|
var init_devpod_exec = __esm({
|
|
10950
11569
|
"src/core/devpod-exec.ts"() {
|
|
10951
11570
|
"use strict";
|
|
10952
|
-
|
|
10953
|
-
|
|
11571
|
+
import_node_child_process24 = require("child_process");
|
|
11572
|
+
import_node_crypto9 = require("crypto");
|
|
10954
11573
|
init_devpod_environment();
|
|
10955
11574
|
init_devpod_workspaces();
|
|
10956
11575
|
init_devsy_exec();
|
|
@@ -11136,7 +11755,7 @@ async function runOpenCommand(name) {
|
|
|
11136
11755
|
);
|
|
11137
11756
|
return;
|
|
11138
11757
|
}
|
|
11139
|
-
const result = (0,
|
|
11758
|
+
const result = (0, import_node_child_process25.spawnSync)("open", [url], { encoding: "utf-8" });
|
|
11140
11759
|
if (result.status !== 0) {
|
|
11141
11760
|
const details = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
11142
11761
|
throw new Error(`Unable to open '${url}': ${details || "unknown error"}`);
|
|
@@ -11144,11 +11763,11 @@ async function runOpenCommand(name) {
|
|
|
11144
11763
|
process.stdout.write(`Opened ${url}
|
|
11145
11764
|
`);
|
|
11146
11765
|
}
|
|
11147
|
-
var
|
|
11766
|
+
var import_node_child_process25;
|
|
11148
11767
|
var init_open = __esm({
|
|
11149
11768
|
"src/commands/open.ts"() {
|
|
11150
11769
|
"use strict";
|
|
11151
|
-
|
|
11770
|
+
import_node_child_process25 = require("child_process");
|
|
11152
11771
|
init_docker();
|
|
11153
11772
|
init_host_routes();
|
|
11154
11773
|
init_repo_config();
|
|
@@ -11171,7 +11790,7 @@ async function runLogsCommand(options) {
|
|
|
11171
11790
|
const args = ["logs", "--tail", tail, ROUTER_CONTAINER_NAME];
|
|
11172
11791
|
if (options.follow) {
|
|
11173
11792
|
args.splice(1, 0, "-f");
|
|
11174
|
-
const child = (0,
|
|
11793
|
+
const child = (0, import_node_child_process26.spawn)("docker", args, { stdio: "inherit" });
|
|
11175
11794
|
const onSignal = () => {
|
|
11176
11795
|
child.kill("SIGTERM");
|
|
11177
11796
|
};
|
|
@@ -11185,17 +11804,17 @@ async function runLogsCommand(options) {
|
|
|
11185
11804
|
});
|
|
11186
11805
|
});
|
|
11187
11806
|
} else {
|
|
11188
|
-
const result = (0,
|
|
11807
|
+
const result = (0, import_node_child_process26.spawnSync)("docker", args, { stdio: "inherit" });
|
|
11189
11808
|
if (result.status !== 0) {
|
|
11190
11809
|
throw new Error("Failed to retrieve router logs.");
|
|
11191
11810
|
}
|
|
11192
11811
|
}
|
|
11193
11812
|
}
|
|
11194
|
-
var
|
|
11813
|
+
var import_node_child_process26;
|
|
11195
11814
|
var init_logs = __esm({
|
|
11196
11815
|
"src/commands/logs.ts"() {
|
|
11197
11816
|
"use strict";
|
|
11198
|
-
|
|
11817
|
+
import_node_child_process26 = require("child_process");
|
|
11199
11818
|
init_docker();
|
|
11200
11819
|
init_router();
|
|
11201
11820
|
}
|
|
@@ -11235,17 +11854,17 @@ function asRecord3(value) {
|
|
|
11235
11854
|
return value;
|
|
11236
11855
|
}
|
|
11237
11856
|
function readJson(filePath) {
|
|
11238
|
-
if (!
|
|
11857
|
+
if (!import_node_fs27.default.existsSync(filePath)) {
|
|
11239
11858
|
return void 0;
|
|
11240
11859
|
}
|
|
11241
11860
|
try {
|
|
11242
|
-
return JSON.parse(
|
|
11861
|
+
return JSON.parse(import_node_fs27.default.readFileSync(filePath, "utf-8"));
|
|
11243
11862
|
} catch {
|
|
11244
11863
|
return void 0;
|
|
11245
11864
|
}
|
|
11246
11865
|
}
|
|
11247
11866
|
function relative(repoPath, filePath) {
|
|
11248
|
-
return
|
|
11867
|
+
return import_node_path28.default.relative(repoPath, filePath) || ".";
|
|
11249
11868
|
}
|
|
11250
11869
|
function redactEnvAssignments(value) {
|
|
11251
11870
|
return value.replace(
|
|
@@ -11286,7 +11905,7 @@ function inspectPackageManager(repoPath, pkg) {
|
|
|
11286
11905
|
["bun.lock", "bun"]
|
|
11287
11906
|
];
|
|
11288
11907
|
for (const [fileName, name] of lockfiles) {
|
|
11289
|
-
if (
|
|
11908
|
+
if (import_node_fs27.default.existsSync(import_node_path28.default.join(repoPath, fileName))) {
|
|
11290
11909
|
return { name, source: fileName };
|
|
11291
11910
|
}
|
|
11292
11911
|
}
|
|
@@ -11301,9 +11920,9 @@ function inspectNode(repoPath, pkg) {
|
|
|
11301
11920
|
if (typeof engines?.node === "string") {
|
|
11302
11921
|
return { version: engines.node, source: "package.json:engines.node" };
|
|
11303
11922
|
}
|
|
11304
|
-
const nvmrc =
|
|
11305
|
-
if (
|
|
11306
|
-
const version =
|
|
11923
|
+
const nvmrc = import_node_path28.default.join(repoPath, ".nvmrc");
|
|
11924
|
+
if (import_node_fs27.default.existsSync(nvmrc)) {
|
|
11925
|
+
const version = import_node_fs27.default.readFileSync(nvmrc, "utf-8").trim();
|
|
11307
11926
|
return { version, source: ".nvmrc" };
|
|
11308
11927
|
}
|
|
11309
11928
|
return void 0;
|
|
@@ -11364,7 +11983,7 @@ function configuredComposeFiles(repoPath) {
|
|
|
11364
11983
|
const files = config.apps.filter(
|
|
11365
11984
|
(app) => app.runtime === "docker"
|
|
11366
11985
|
).flatMap((app) => app.docker.composeFiles).filter(
|
|
11367
|
-
(fileName) => !
|
|
11986
|
+
(fileName) => !import_node_path28.default.isAbsolute(fileName) && !import_node_path28.default.normalize(fileName).startsWith("..")
|
|
11368
11987
|
);
|
|
11369
11988
|
return Array.from(new Set(files));
|
|
11370
11989
|
} catch {
|
|
@@ -11382,7 +12001,7 @@ function composeFiles(repoPath) {
|
|
|
11382
12001
|
...configuredComposeFiles(repoPath)
|
|
11383
12002
|
];
|
|
11384
12003
|
return Array.from(new Set(candidates)).filter(
|
|
11385
|
-
(fileName) =>
|
|
12004
|
+
(fileName) => import_node_fs27.default.existsSync(import_node_path28.default.join(repoPath, fileName))
|
|
11386
12005
|
);
|
|
11387
12006
|
}
|
|
11388
12007
|
function stringArray2(value) {
|
|
@@ -11420,7 +12039,7 @@ function inspectServices(repoPath) {
|
|
|
11420
12039
|
const services = [];
|
|
11421
12040
|
for (const fileName of composeFiles(repoPath)) {
|
|
11422
12041
|
try {
|
|
11423
|
-
const parsed = import_yaml6.default.parse(
|
|
12042
|
+
const parsed = import_yaml6.default.parse(import_node_fs27.default.readFileSync(import_node_path28.default.join(repoPath, fileName), "utf-8"));
|
|
11424
12043
|
const serviceMap = asRecord3(asRecord3(parsed)?.services);
|
|
11425
12044
|
for (const [name, value] of Object.entries(serviceMap ?? {})) {
|
|
11426
12045
|
const service = asRecord3(value);
|
|
@@ -11455,8 +12074,8 @@ function inspectServices(repoPath) {
|
|
|
11455
12074
|
return services;
|
|
11456
12075
|
}
|
|
11457
12076
|
function inspectEnvFiles(repoPath) {
|
|
11458
|
-
const files =
|
|
11459
|
-
const content =
|
|
12077
|
+
const files = import_node_fs27.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
|
|
12078
|
+
const content = import_node_fs27.default.readFileSync(import_node_path28.default.join(repoPath, fileName), "utf-8");
|
|
11460
12079
|
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();
|
|
11461
12080
|
return { path: fileName, names };
|
|
11462
12081
|
});
|
|
@@ -11470,18 +12089,18 @@ function inspectEnvFiles(repoPath) {
|
|
|
11470
12089
|
};
|
|
11471
12090
|
}
|
|
11472
12091
|
function inspectDevcontainer(repoPath) {
|
|
11473
|
-
const dir =
|
|
11474
|
-
if (!
|
|
12092
|
+
const dir = import_node_path28.default.join(repoPath, ".devcontainer");
|
|
12093
|
+
if (!import_node_fs27.default.existsSync(dir)) {
|
|
11475
12094
|
return { exists: false, files: [] };
|
|
11476
12095
|
}
|
|
11477
12096
|
return {
|
|
11478
12097
|
exists: true,
|
|
11479
|
-
files:
|
|
12098
|
+
files: import_node_fs27.default.readdirSync(dir).filter((fileName) => import_node_fs27.default.statSync(import_node_path28.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
|
|
11480
12099
|
};
|
|
11481
12100
|
}
|
|
11482
12101
|
function inspectDevrouter(repoPath) {
|
|
11483
12102
|
const configPath = getRepoConfigPath(repoPath);
|
|
11484
|
-
if (!
|
|
12103
|
+
if (!import_node_fs27.default.existsSync(configPath)) {
|
|
11485
12104
|
return {
|
|
11486
12105
|
exists: false,
|
|
11487
12106
|
configPath,
|
|
@@ -11525,15 +12144,15 @@ function inspectAgentGuidance(repoPath) {
|
|
|
11525
12144
|
["AGENTS.md", "agents"],
|
|
11526
12145
|
["CLAUDE.md", "claude"]
|
|
11527
12146
|
]) {
|
|
11528
|
-
if (
|
|
12147
|
+
if (import_node_fs27.default.existsSync(import_node_path28.default.join(repoPath, fileName))) {
|
|
11529
12148
|
results.push({ path: fileName, kind });
|
|
11530
12149
|
}
|
|
11531
12150
|
}
|
|
11532
|
-
const skillsDir =
|
|
11533
|
-
if (
|
|
11534
|
-
for (const name of
|
|
11535
|
-
const skillPath =
|
|
11536
|
-
if (
|
|
12151
|
+
const skillsDir = import_node_path28.default.join(repoPath, ".agents", "skills");
|
|
12152
|
+
if (import_node_fs27.default.existsSync(skillsDir)) {
|
|
12153
|
+
for (const name of import_node_fs27.default.readdirSync(skillsDir).sort()) {
|
|
12154
|
+
const skillPath = import_node_path28.default.join(skillsDir, name, "SKILL.md");
|
|
12155
|
+
if (import_node_fs27.default.existsSync(skillPath)) {
|
|
11537
12156
|
results.push({ path: relative(repoPath, skillPath), kind: "skill" });
|
|
11538
12157
|
}
|
|
11539
12158
|
}
|
|
@@ -11576,7 +12195,7 @@ function buildIssues(report) {
|
|
|
11576
12195
|
}
|
|
11577
12196
|
function inspectRepo(options = {}) {
|
|
11578
12197
|
const repoPath = resolveRepoPath(options.repo);
|
|
11579
|
-
const pkg = readJson(
|
|
12198
|
+
const pkg = readJson(import_node_path28.default.join(repoPath, "package.json"));
|
|
11580
12199
|
const scripts = inspectScripts(pkg);
|
|
11581
12200
|
const reportWithoutIssues = {
|
|
11582
12201
|
repoPath,
|
|
@@ -11595,12 +12214,12 @@ function inspectRepo(options = {}) {
|
|
|
11595
12214
|
issues: buildIssues(reportWithoutIssues)
|
|
11596
12215
|
};
|
|
11597
12216
|
}
|
|
11598
|
-
var
|
|
12217
|
+
var import_node_fs27, import_node_path28, import_yaml6;
|
|
11599
12218
|
var init_repo_inspect = __esm({
|
|
11600
12219
|
"src/core/repo-inspect.ts"() {
|
|
11601
12220
|
"use strict";
|
|
11602
|
-
|
|
11603
|
-
|
|
12221
|
+
import_node_fs27 = __toESM(require("fs"));
|
|
12222
|
+
import_node_path28 = __toESM(require("path"));
|
|
11604
12223
|
import_yaml6 = __toESM(require("yaml"));
|
|
11605
12224
|
init_repo_config();
|
|
11606
12225
|
}
|
|
@@ -11705,7 +12324,7 @@ function requiredFileChecks(repoPath) {
|
|
|
11705
12324
|
".devcontainer/docker-compose.yml",
|
|
11706
12325
|
".devrouter.yml"
|
|
11707
12326
|
];
|
|
11708
|
-
const missing = required.filter((fileName) => !
|
|
12327
|
+
const missing = required.filter((fileName) => !import_node_fs28.default.existsSync(import_node_path29.default.join(repoPath, fileName)));
|
|
11709
12328
|
return {
|
|
11710
12329
|
id: "repo.devcontainer.verify-files",
|
|
11711
12330
|
level: missing.length === 0 ? "ok" : "error",
|
|
@@ -11924,12 +12543,12 @@ async function verifyDevcontainer(options = {}) {
|
|
|
11924
12543
|
nextSteps: collectNextSteps3(checks)
|
|
11925
12544
|
};
|
|
11926
12545
|
}
|
|
11927
|
-
var
|
|
12546
|
+
var import_node_fs28, import_node_path29;
|
|
11928
12547
|
var init_devcontainer_verify = __esm({
|
|
11929
12548
|
"src/core/devcontainer-verify.ts"() {
|
|
11930
12549
|
"use strict";
|
|
11931
|
-
|
|
11932
|
-
|
|
12550
|
+
import_node_fs28 = __toESM(require("fs"));
|
|
12551
|
+
import_node_path29 = __toESM(require("path"));
|
|
11933
12552
|
init_capabilities();
|
|
11934
12553
|
init_doctor();
|
|
11935
12554
|
init_host_routes();
|
|
@@ -12226,7 +12845,7 @@ function packageManagerIssues(repo) {
|
|
|
12226
12845
|
}
|
|
12227
12846
|
function plannedFiles(repoPath, version) {
|
|
12228
12847
|
const repo = inspectRepo({ repo: repoPath });
|
|
12229
|
-
const projectName = sanitizeProjectName(
|
|
12848
|
+
const projectName = sanitizeProjectName(import_node_path30.default.basename(repo.repoPath));
|
|
12230
12849
|
const nodeMajor = majorVersion(repo.node?.version, "24");
|
|
12231
12850
|
const pnpmVersion = repo.packageManager?.name === "pnpm" && repo.packageManager.version ? repo.packageManager.version : DEFAULT_PNPM_VERSION;
|
|
12232
12851
|
const port = inferPort2(repo);
|
|
@@ -12271,8 +12890,8 @@ function plannedFiles(repoPath, version) {
|
|
|
12271
12890
|
};
|
|
12272
12891
|
}
|
|
12273
12892
|
function classifyFile(repoPath, file) {
|
|
12274
|
-
const absolutePath =
|
|
12275
|
-
if (!
|
|
12893
|
+
const absolutePath = import_node_path30.default.join(repoPath, file.relativePath);
|
|
12894
|
+
if (!import_node_fs29.default.existsSync(absolutePath)) {
|
|
12276
12895
|
return {
|
|
12277
12896
|
path: file.relativePath,
|
|
12278
12897
|
action: "create",
|
|
@@ -12280,7 +12899,7 @@ function classifyFile(repoPath, file) {
|
|
|
12280
12899
|
bytes: Buffer.byteLength(file.content)
|
|
12281
12900
|
};
|
|
12282
12901
|
}
|
|
12283
|
-
const current =
|
|
12902
|
+
const current = import_node_fs29.default.readFileSync(absolutePath, "utf-8");
|
|
12284
12903
|
if (!current.includes(MANAGED_MARKER2)) {
|
|
12285
12904
|
return {
|
|
12286
12905
|
path: file.relativePath,
|
|
@@ -12337,11 +12956,11 @@ function buildPlan(repoPath, dryRun, version) {
|
|
|
12337
12956
|
};
|
|
12338
12957
|
}
|
|
12339
12958
|
function writeFile(repoPath, file) {
|
|
12340
|
-
const absolutePath =
|
|
12341
|
-
|
|
12342
|
-
|
|
12959
|
+
const absolutePath = import_node_path30.default.join(repoPath, file.relativePath);
|
|
12960
|
+
import_node_fs29.default.mkdirSync(import_node_path30.default.dirname(absolutePath), { recursive: true });
|
|
12961
|
+
import_node_fs29.default.writeFileSync(absolutePath, file.content, "utf-8");
|
|
12343
12962
|
if (file.executable) {
|
|
12344
|
-
|
|
12963
|
+
import_node_fs29.default.chmodSync(absolutePath, 493);
|
|
12345
12964
|
}
|
|
12346
12965
|
}
|
|
12347
12966
|
function writeDevcontainer(options = {}) {
|
|
@@ -12379,12 +12998,12 @@ function writeDevcontainer(options = {}) {
|
|
|
12379
12998
|
nextSteps: postWriteNextSteps(repoPath)
|
|
12380
12999
|
};
|
|
12381
13000
|
}
|
|
12382
|
-
var
|
|
13001
|
+
var import_node_fs29, import_node_path30, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
|
|
12383
13002
|
var init_devcontainer_write = __esm({
|
|
12384
13003
|
"src/core/devcontainer-write.ts"() {
|
|
12385
13004
|
"use strict";
|
|
12386
|
-
|
|
12387
|
-
|
|
13005
|
+
import_node_fs29 = __toESM(require("fs"));
|
|
13006
|
+
import_node_path30 = __toESM(require("path"));
|
|
12388
13007
|
init_repo_config();
|
|
12389
13008
|
init_repo_inspect();
|
|
12390
13009
|
MANAGED_MARKER2 = "devrouter:managed devcontainer";
|
|
@@ -12493,6 +13112,119 @@ var init_repo_devcontainer = __esm({
|
|
|
12493
13112
|
}
|
|
12494
13113
|
});
|
|
12495
13114
|
|
|
13115
|
+
// src/core/profile-resolution.ts
|
|
13116
|
+
function sortedUnique2(values) {
|
|
13117
|
+
return Array.from(new Set(values)).sort();
|
|
13118
|
+
}
|
|
13119
|
+
function expandsAll(values) {
|
|
13120
|
+
return values?.length === 1 && values[0] === "*";
|
|
13121
|
+
}
|
|
13122
|
+
function selectedManagedResources(profile, selected, registry) {
|
|
13123
|
+
if (!profile || expandsAll(selected)) return sortedUnique2(registry);
|
|
13124
|
+
return sortedUnique2(selected ?? []);
|
|
13125
|
+
}
|
|
13126
|
+
function selectedReadiness(config, profileName, profile, routedApps) {
|
|
13127
|
+
const selectedNames = profileName.split(",");
|
|
13128
|
+
if (selectedNames.length === 1) {
|
|
13129
|
+
return sortedUnique2(
|
|
13130
|
+
profile?.readiness ?? routedApps.filter((app) => app.protocol === "http").map((app) => app.name)
|
|
13131
|
+
);
|
|
13132
|
+
}
|
|
13133
|
+
const readiness = /* @__PURE__ */ new Set();
|
|
13134
|
+
for (const selectedName of selectedNames) {
|
|
13135
|
+
const selected = resolveProfile(config, selectedName);
|
|
13136
|
+
const selectedApps = applyProfile(config, selected.profile).apps.filter(
|
|
13137
|
+
(app) => app.kind !== "dependency"
|
|
13138
|
+
);
|
|
13139
|
+
const selectedTargets = selected.profile?.readiness ?? selectedApps.filter((app) => app.protocol === "http").map((app) => app.name);
|
|
13140
|
+
for (const target of selectedTargets) readiness.add(target);
|
|
13141
|
+
}
|
|
13142
|
+
return sortedUnique2(readiness);
|
|
13143
|
+
}
|
|
13144
|
+
function buildProfileResolutionReport(config, repoPath, profileOverride) {
|
|
13145
|
+
const resolved = resolveProfile(config, profileOverride);
|
|
13146
|
+
const filtered = applyProfile(config, resolved.profile);
|
|
13147
|
+
const routedApps = filtered.apps.filter((app) => app.kind !== "dependency");
|
|
13148
|
+
const dependencies = filtered.apps.filter((app) => app.kind === "dependency");
|
|
13149
|
+
const readiness = selectedReadiness(config, resolved.name, resolved.profile, routedApps);
|
|
13150
|
+
const managedRuntime = config.managedRuntime;
|
|
13151
|
+
const baseServices = sortedUnique2(managedRuntime?.devcontainer.baseServices ?? []);
|
|
13152
|
+
const profileServices = managedRuntime ? selectedManagedResources(
|
|
13153
|
+
resolved.profile,
|
|
13154
|
+
resolved.profile?.devcontainerServices,
|
|
13155
|
+
managedRuntime.devcontainer.profileServices
|
|
13156
|
+
) : [];
|
|
13157
|
+
const processes = managedRuntime ? selectedManagedResources(
|
|
13158
|
+
resolved.profile,
|
|
13159
|
+
resolved.profile?.processes,
|
|
13160
|
+
managedRuntime.processes
|
|
13161
|
+
) : [];
|
|
13162
|
+
return {
|
|
13163
|
+
schemaVersion: 1,
|
|
13164
|
+
repoPath,
|
|
13165
|
+
profile: resolved.name,
|
|
13166
|
+
apps: sortedUnique2(routedApps.map((app) => app.name)),
|
|
13167
|
+
dependencies: sortedUnique2(dependencies.map((app) => app.name)),
|
|
13168
|
+
readiness: sortedUnique2(readiness),
|
|
13169
|
+
managedRuntime: {
|
|
13170
|
+
baseServices,
|
|
13171
|
+
profileServices,
|
|
13172
|
+
services: sortedUnique2([...baseServices, ...profileServices]),
|
|
13173
|
+
processes
|
|
13174
|
+
}
|
|
13175
|
+
};
|
|
13176
|
+
}
|
|
13177
|
+
function resolveProfileReport(options) {
|
|
13178
|
+
const repoPath = resolveRepoPath(options.repo);
|
|
13179
|
+
return buildProfileResolutionReport(loadRepoConfig(repoPath), repoPath, options.profile);
|
|
13180
|
+
}
|
|
13181
|
+
var init_profile_resolution = __esm({
|
|
13182
|
+
"src/core/profile-resolution.ts"() {
|
|
13183
|
+
"use strict";
|
|
13184
|
+
init_repo_config();
|
|
13185
|
+
}
|
|
13186
|
+
});
|
|
13187
|
+
|
|
13188
|
+
// src/commands/profile.ts
|
|
13189
|
+
var profile_exports = {};
|
|
13190
|
+
__export(profile_exports, {
|
|
13191
|
+
runProfileResolveCommand: () => runProfileResolveCommand
|
|
13192
|
+
});
|
|
13193
|
+
async function runProfileResolveCommand(options) {
|
|
13194
|
+
const report = resolveProfileReport(options);
|
|
13195
|
+
if (options.json) {
|
|
13196
|
+
printJSON(report);
|
|
13197
|
+
return;
|
|
13198
|
+
}
|
|
13199
|
+
printProfileResolutionSummary(report);
|
|
13200
|
+
}
|
|
13201
|
+
function renderValues(values) {
|
|
13202
|
+
return values.join(", ") || "-";
|
|
13203
|
+
}
|
|
13204
|
+
function printProfileResolutionSummary(report) {
|
|
13205
|
+
process.stdout.write(`Repo: ${report.repoPath}
|
|
13206
|
+
`);
|
|
13207
|
+
process.stdout.write(`Profile: ${report.profile}
|
|
13208
|
+
`);
|
|
13209
|
+
process.stdout.write(`Apps: ${renderValues(report.apps)}
|
|
13210
|
+
`);
|
|
13211
|
+
process.stdout.write(`Dependencies: ${renderValues(report.dependencies)}
|
|
13212
|
+
`);
|
|
13213
|
+
process.stdout.write(`Readiness: ${renderValues(report.readiness)}
|
|
13214
|
+
`);
|
|
13215
|
+
process.stdout.write(`Managed services: ${renderValues(report.managedRuntime.services)}
|
|
13216
|
+
`);
|
|
13217
|
+
process.stdout.write(`Managed processes: ${renderValues(report.managedRuntime.processes)}
|
|
13218
|
+
`);
|
|
13219
|
+
}
|
|
13220
|
+
var init_profile = __esm({
|
|
13221
|
+
"src/commands/profile.ts"() {
|
|
13222
|
+
"use strict";
|
|
13223
|
+
init_output();
|
|
13224
|
+
init_profile_resolution();
|
|
13225
|
+
}
|
|
13226
|
+
});
|
|
13227
|
+
|
|
12496
13228
|
// src/commands/app-add.ts
|
|
12497
13229
|
var app_add_exports = {};
|
|
12498
13230
|
__export(app_add_exports, {
|
|
@@ -12775,7 +13507,7 @@ function sanitizeRouterId(value) {
|
|
|
12775
13507
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
12776
13508
|
}
|
|
12777
13509
|
function repoHash(repoPath) {
|
|
12778
|
-
return (0,
|
|
13510
|
+
return (0, import_node_crypto10.createHash)("sha1").update(import_node_path31.default.resolve(repoPath)).digest("hex").slice(0, 12);
|
|
12779
13511
|
}
|
|
12780
13512
|
function asDockerApp(app) {
|
|
12781
13513
|
return app.runtime === "docker";
|
|
@@ -12854,11 +13586,11 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
|
|
|
12854
13586
|
if (dockerApps.length === 0) {
|
|
12855
13587
|
throw new Error("No docker apps selected to prepare compose overlay.");
|
|
12856
13588
|
}
|
|
12857
|
-
const cachePath =
|
|
12858
|
-
|
|
12859
|
-
const overlayPath =
|
|
13589
|
+
const cachePath = import_node_path31.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
|
|
13590
|
+
import_node_fs30.default.mkdirSync(cachePath, { recursive: true });
|
|
13591
|
+
const overlayPath = import_node_path31.default.join(cachePath, "compose.devrouter.yml");
|
|
12860
13592
|
const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
|
|
12861
|
-
|
|
13593
|
+
import_node_fs30.default.writeFileSync(overlayPath, import_yaml7.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
|
|
12862
13594
|
return {
|
|
12863
13595
|
overlayPath,
|
|
12864
13596
|
composeFiles: ensureComposeFiles(dockerApps),
|
|
@@ -12872,7 +13604,7 @@ function runDockerComposeUp(repoPath, composeFiles2, overlayPath, services) {
|
|
|
12872
13604
|
fileArgs.push("-f", resolved);
|
|
12873
13605
|
}
|
|
12874
13606
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "up", "-d", "--wait", ...services];
|
|
12875
|
-
const result = (0,
|
|
13607
|
+
const result = (0, import_node_child_process27.spawnSync)("docker", args, {
|
|
12876
13608
|
encoding: "utf-8",
|
|
12877
13609
|
cwd: repoPath
|
|
12878
13610
|
});
|
|
@@ -12900,7 +13632,7 @@ function queryRunningComposeServices(repoPath, composeFiles2, overlayPath, servi
|
|
|
12900
13632
|
"--services",
|
|
12901
13633
|
...services
|
|
12902
13634
|
];
|
|
12903
|
-
const result = (0,
|
|
13635
|
+
const result = (0, import_node_child_process27.spawnSync)("docker", args, {
|
|
12904
13636
|
encoding: "utf-8",
|
|
12905
13637
|
cwd: repoPath
|
|
12906
13638
|
});
|
|
@@ -12926,7 +13658,7 @@ function runDockerComposeStop(repoPath, composeFiles2, overlayPath, services) {
|
|
|
12926
13658
|
fileArgs.push("-f", resolved);
|
|
12927
13659
|
}
|
|
12928
13660
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "stop", ...services];
|
|
12929
|
-
const result = (0,
|
|
13661
|
+
const result = (0, import_node_child_process27.spawnSync)("docker", args, {
|
|
12930
13662
|
encoding: "utf-8",
|
|
12931
13663
|
cwd: repoPath
|
|
12932
13664
|
});
|
|
@@ -12952,7 +13684,7 @@ function runDockerComposeLogs(repoPath, composeFiles2, overlayPath, services, ta
|
|
|
12952
13684
|
String(tail),
|
|
12953
13685
|
...services
|
|
12954
13686
|
];
|
|
12955
|
-
(0,
|
|
13687
|
+
(0, import_node_child_process27.spawnSync)("docker", args, {
|
|
12956
13688
|
stdio: "inherit",
|
|
12957
13689
|
cwd: repoPath
|
|
12958
13690
|
});
|
|
@@ -12964,7 +13696,7 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
12964
13696
|
fileArgs.push("-f", resolved);
|
|
12965
13697
|
}
|
|
12966
13698
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "port", service, String(internalPort)];
|
|
12967
|
-
const result = (0,
|
|
13699
|
+
const result = (0, import_node_child_process27.spawnSync)("docker", args, {
|
|
12968
13700
|
encoding: "utf-8",
|
|
12969
13701
|
cwd: repoPath
|
|
12970
13702
|
});
|
|
@@ -12978,14 +13710,14 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
12978
13710
|
const port = Number(match[1]);
|
|
12979
13711
|
return Number.isInteger(port) && port > 0 ? port : void 0;
|
|
12980
13712
|
}
|
|
12981
|
-
var
|
|
13713
|
+
var import_node_child_process27, import_node_crypto10, import_node_fs30, import_node_path31, import_yaml7;
|
|
12982
13714
|
var init_docker_run = __esm({
|
|
12983
13715
|
"src/core/docker-run.ts"() {
|
|
12984
13716
|
"use strict";
|
|
12985
|
-
|
|
12986
|
-
|
|
12987
|
-
|
|
12988
|
-
|
|
13717
|
+
import_node_child_process27 = require("child_process");
|
|
13718
|
+
import_node_crypto10 = require("crypto");
|
|
13719
|
+
import_node_fs30 = __toESM(require("fs"));
|
|
13720
|
+
import_node_path31 = __toESM(require("path"));
|
|
12989
13721
|
import_yaml7 = __toESM(require("yaml"));
|
|
12990
13722
|
init_docker_error_guidance();
|
|
12991
13723
|
init_paths();
|
|
@@ -13022,7 +13754,7 @@ function resolveSmCommand(command, defaultEnv, overrideEnv) {
|
|
|
13022
13754
|
}
|
|
13023
13755
|
return command.replace(/\{env\}/g, env);
|
|
13024
13756
|
}
|
|
13025
|
-
function
|
|
13757
|
+
function sleep3(ms) {
|
|
13026
13758
|
return new Promise((resolve) => {
|
|
13027
13759
|
setTimeout(resolve, ms);
|
|
13028
13760
|
});
|
|
@@ -13075,7 +13807,7 @@ function isProcessRunning(pid) {
|
|
|
13075
13807
|
}
|
|
13076
13808
|
}
|
|
13077
13809
|
function readProcessTree(rootPid) {
|
|
13078
|
-
const result = (0,
|
|
13810
|
+
const result = (0, import_node_child_process28.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
|
|
13079
13811
|
if (result.status !== 0) {
|
|
13080
13812
|
return [rootPid];
|
|
13081
13813
|
}
|
|
@@ -13133,7 +13865,7 @@ async function terminateProcessTree(rootPid) {
|
|
|
13133
13865
|
if (!isProcessRunning(rootPid)) {
|
|
13134
13866
|
return;
|
|
13135
13867
|
}
|
|
13136
|
-
await
|
|
13868
|
+
await sleep3(100);
|
|
13137
13869
|
}
|
|
13138
13870
|
if (isProcessRunning(rootPid)) {
|
|
13139
13871
|
killProcessTree(rootPid, "SIGKILL");
|
|
@@ -13186,12 +13918,12 @@ function detectListeningPorts(pids) {
|
|
|
13186
13918
|
if (pids.length === 0) {
|
|
13187
13919
|
return [];
|
|
13188
13920
|
}
|
|
13189
|
-
const result = (0,
|
|
13921
|
+
const result = (0, import_node_child_process28.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
|
|
13190
13922
|
encoding: "utf-8"
|
|
13191
13923
|
});
|
|
13192
13924
|
if (result.error && result.error.code === "ENOENT") {
|
|
13193
13925
|
if (process.platform === "linux") {
|
|
13194
|
-
const ssResult = (0,
|
|
13926
|
+
const ssResult = (0, import_node_child_process28.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
|
|
13195
13927
|
if (ssResult.status === 0 && ssResult.stdout) {
|
|
13196
13928
|
return parseSsListeningPorts(ssResult.stdout, new Set(pids));
|
|
13197
13929
|
}
|
|
@@ -13250,7 +13982,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
|
|
|
13250
13982
|
app.hostRun.command,
|
|
13251
13983
|
true
|
|
13252
13984
|
) : app.hostRun.command;
|
|
13253
|
-
const child = (0,
|
|
13985
|
+
const child = (0, import_node_child_process28.spawn)(spawnCommand, {
|
|
13254
13986
|
cwd: commandCwd,
|
|
13255
13987
|
stdio: "inherit",
|
|
13256
13988
|
shell: true,
|
|
@@ -13320,7 +14052,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
|
|
|
13320
14052
|
);
|
|
13321
14053
|
}
|
|
13322
14054
|
}
|
|
13323
|
-
await
|
|
14055
|
+
await sleep3(POLL_INTERVAL_MS2);
|
|
13324
14056
|
}
|
|
13325
14057
|
} catch (error) {
|
|
13326
14058
|
fatalError = toError(error);
|
|
@@ -13618,7 +14350,7 @@ async function execWithAppEnv(options) {
|
|
|
13618
14350
|
options.command[0],
|
|
13619
14351
|
true
|
|
13620
14352
|
);
|
|
13621
|
-
child = (0,
|
|
14353
|
+
child = (0, import_node_child_process28.spawn)(wrapped, {
|
|
13622
14354
|
cwd: deps.repoPath,
|
|
13623
14355
|
stdio: "inherit",
|
|
13624
14356
|
shell: true,
|
|
@@ -13632,7 +14364,7 @@ async function execWithAppEnv(options) {
|
|
|
13632
14364
|
false
|
|
13633
14365
|
);
|
|
13634
14366
|
const [cmd, ...wrappedArgs] = wrapped;
|
|
13635
|
-
child = (0,
|
|
14367
|
+
child = (0, import_node_child_process28.spawn)(cmd, wrappedArgs, {
|
|
13636
14368
|
cwd: deps.repoPath,
|
|
13637
14369
|
stdio: "inherit",
|
|
13638
14370
|
shell: false,
|
|
@@ -13640,7 +14372,7 @@ async function execWithAppEnv(options) {
|
|
|
13640
14372
|
});
|
|
13641
14373
|
}
|
|
13642
14374
|
} else if (options.shell) {
|
|
13643
|
-
child = (0,
|
|
14375
|
+
child = (0, import_node_child_process28.spawn)(options.command[0], {
|
|
13644
14376
|
cwd: deps.repoPath,
|
|
13645
14377
|
stdio: "inherit",
|
|
13646
14378
|
shell: true,
|
|
@@ -13648,7 +14380,7 @@ async function execWithAppEnv(options) {
|
|
|
13648
14380
|
});
|
|
13649
14381
|
} else {
|
|
13650
14382
|
const [command, ...args] = options.command;
|
|
13651
|
-
child = (0,
|
|
14383
|
+
child = (0, import_node_child_process28.spawn)(command, args, {
|
|
13652
14384
|
cwd: deps.repoPath,
|
|
13653
14385
|
stdio: "inherit",
|
|
13654
14386
|
shell: false,
|
|
@@ -13669,11 +14401,11 @@ async function execWithAppEnv(options) {
|
|
|
13669
14401
|
deps.stopDeps();
|
|
13670
14402
|
}
|
|
13671
14403
|
}
|
|
13672
|
-
var
|
|
14404
|
+
var import_node_child_process28, import_node_net, import_node_process, import_promises, POLL_INTERVAL_MS2, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
|
|
13673
14405
|
var init_app_run = __esm({
|
|
13674
14406
|
"src/core/app-run.ts"() {
|
|
13675
14407
|
"use strict";
|
|
13676
|
-
|
|
14408
|
+
import_node_child_process28 = require("child_process");
|
|
13677
14409
|
import_node_net = __toESM(require("net"));
|
|
13678
14410
|
import_node_process = require("process");
|
|
13679
14411
|
import_promises = require("readline/promises");
|
|
@@ -13807,13 +14539,13 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
13807
14539
|
const startedAt = Date.now();
|
|
13808
14540
|
let rootStat;
|
|
13809
14541
|
try {
|
|
13810
|
-
rootStat =
|
|
14542
|
+
rootStat = import_node_fs31.default.lstatSync(worktreePath);
|
|
13811
14543
|
} catch (error) {
|
|
13812
14544
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
13813
14545
|
}
|
|
13814
14546
|
let rootEntries;
|
|
13815
14547
|
try {
|
|
13816
|
-
rootEntries =
|
|
14548
|
+
rootEntries = import_node_fs31.default.readdirSync(worktreePath, { withFileTypes: true });
|
|
13817
14549
|
} catch (error) {
|
|
13818
14550
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
13819
14551
|
}
|
|
@@ -13839,10 +14571,10 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
13839
14571
|
timedOut = true;
|
|
13840
14572
|
break;
|
|
13841
14573
|
}
|
|
13842
|
-
const entryPath =
|
|
14574
|
+
const entryPath = import_node_path32.default.join(dirPath, entry.name);
|
|
13843
14575
|
let entryStat;
|
|
13844
14576
|
try {
|
|
13845
|
-
entryStat =
|
|
14577
|
+
entryStat = import_node_fs31.default.lstatSync(entryPath);
|
|
13846
14578
|
} catch (error) {
|
|
13847
14579
|
if (error?.code === "ENOENT") continue;
|
|
13848
14580
|
unreadableReason = describeIncompleteWalk(error);
|
|
@@ -13852,7 +14584,7 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
13852
14584
|
if (!entryStat.isDirectory()) continue;
|
|
13853
14585
|
let childEntries;
|
|
13854
14586
|
try {
|
|
13855
|
-
childEntries =
|
|
14587
|
+
childEntries = import_node_fs31.default.readdirSync(entryPath, { withFileTypes: true });
|
|
13856
14588
|
} catch (error) {
|
|
13857
14589
|
unreadableReason = describeIncompleteWalk(error);
|
|
13858
14590
|
break;
|
|
@@ -13916,12 +14648,12 @@ function describeError(error, worktreePath) {
|
|
|
13916
14648
|
}
|
|
13917
14649
|
return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
|
|
13918
14650
|
}
|
|
13919
|
-
var
|
|
14651
|
+
var import_node_fs31, import_node_path32, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
|
|
13920
14652
|
var init_workspace_consumption = __esm({
|
|
13921
14653
|
"src/core/workspace-consumption.ts"() {
|
|
13922
14654
|
"use strict";
|
|
13923
|
-
|
|
13924
|
-
|
|
14655
|
+
import_node_fs31 = __toESM(require("fs"));
|
|
14656
|
+
import_node_path32 = __toESM(require("path"));
|
|
13925
14657
|
init_devpod_environment();
|
|
13926
14658
|
DEFAULT_DEADLINE_MS = 1e4;
|
|
13927
14659
|
BLOCK_SIZE_BYTES = 512;
|
|
@@ -14022,7 +14754,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
|
|
|
14022
14754
|
}
|
|
14023
14755
|
function readGitSnapshot(worktree, commandRunner) {
|
|
14024
14756
|
const comparablePath = comparableWorkspacePath(worktree.path);
|
|
14025
|
-
if (worktree.prunable || !
|
|
14757
|
+
if (worktree.prunable || !import_node_fs32.default.existsSync(comparablePath)) {
|
|
14026
14758
|
return { worktree, checkout: "missing", head: null, committerDate: null };
|
|
14027
14759
|
}
|
|
14028
14760
|
const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
|
|
@@ -14547,7 +15279,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
14547
15279
|
containers = measureContainersFn(worktreePaths);
|
|
14548
15280
|
} catch (error) {
|
|
14549
15281
|
const reason = `container measurement failed: ${describeCause(error)}`;
|
|
14550
|
-
containers = new Map(worktreePaths.map((
|
|
15282
|
+
containers = new Map(worktreePaths.map((path32) => [path32, unknownContainers(reason)]));
|
|
14551
15283
|
}
|
|
14552
15284
|
}
|
|
14553
15285
|
const rows = records.map((record) => {
|
|
@@ -14596,12 +15328,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
14596
15328
|
workspaces: rows
|
|
14597
15329
|
};
|
|
14598
15330
|
}
|
|
14599
|
-
var
|
|
15331
|
+
var import_node_child_process29, import_node_fs32, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
|
|
14600
15332
|
var init_workspace_cleanup = __esm({
|
|
14601
15333
|
"src/core/workspace-cleanup.ts"() {
|
|
14602
15334
|
"use strict";
|
|
14603
|
-
|
|
14604
|
-
|
|
15335
|
+
import_node_child_process29 = require("child_process");
|
|
15336
|
+
import_node_fs32 = __toESM(require("fs"));
|
|
14605
15337
|
init_devpod_workspaces();
|
|
14606
15338
|
init_host_routes();
|
|
14607
15339
|
init_repo_config();
|
|
@@ -14618,7 +15350,7 @@ var init_workspace_cleanup = __esm({
|
|
|
14618
15350
|
];
|
|
14619
15351
|
defaultCommandRunner = (command, args, input2) => {
|
|
14620
15352
|
const environment = command === "git" ? READ_ONLY_GIT_ENV2 : { ...process.env, LC_ALL: "C" };
|
|
14621
|
-
const result = (0,
|
|
15353
|
+
const result = (0, import_node_child_process29.spawnSync)(command, args, {
|
|
14622
15354
|
encoding: "utf-8",
|
|
14623
15355
|
env: environment,
|
|
14624
15356
|
...input2 === void 0 ? {} : { input: input2 }
|
|
@@ -14778,7 +15510,7 @@ var init_version = __esm({
|
|
|
14778
15510
|
|
|
14779
15511
|
// src/cli.ts
|
|
14780
15512
|
var import_commander = require("commander");
|
|
14781
|
-
var CLI_VERSION = true ? "0.0.
|
|
15513
|
+
var CLI_VERSION = true ? "0.0.49" : "0.0.0-dev";
|
|
14782
15514
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
14783
15515
|
function withErrorHandling(action2) {
|
|
14784
15516
|
return async (...args) => {
|
|
@@ -14936,6 +15668,16 @@ repoDevcontainerCommand.command("verify").description("Verify devcontainer onboa
|
|
|
14936
15668
|
}
|
|
14937
15669
|
)
|
|
14938
15670
|
);
|
|
15671
|
+
var profileCommand = program.command("profile").description("Resolve profiles from `.devrouter.yml` without starting a runtime");
|
|
15672
|
+
profileCommand.command("resolve").description("Print the exact apps, dependencies, services, and processes for a profile").option("--repo <path>", "Repository path (defaults to current directory)").option(
|
|
15673
|
+
"--profile <name>",
|
|
15674
|
+
"Profile name or comma-separated selection (defaults to configured profile)"
|
|
15675
|
+
).option("--json", "Output stable JSON").action(
|
|
15676
|
+
withErrorHandling(async (options) => {
|
|
15677
|
+
const { runProfileResolveCommand: runProfileResolveCommand2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
15678
|
+
await runProfileResolveCommand2(options);
|
|
15679
|
+
})
|
|
15680
|
+
);
|
|
14939
15681
|
var appCommand = program.command("app").description("Manage app entries and runtime actions from `.devrouter.yml`");
|
|
14940
15682
|
appCommand.command("add").description("Add or update one app definition in `.devrouter.yml`").requiredOption("--name <name>", "App name").option("--kind <kind>", "app or dependency", "app").option("--host <host>", "Hostname ending with .localhost (required for --kind app)").option("--protocol <protocol>", "http or tcp (required for --kind app)").option(
|
|
14941
15683
|
"--runtime <runtime>",
|