@devrouter/cli 0.0.42 → 0.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devrouter.js +383 -256
- package/package.json +2 -1
- package/upgrade-prompts/0.0.43.md +38 -0
- package/upgrade-prompts/0.0.44.md +30 -0
package/dist/devrouter.js
CHANGED
|
@@ -51,6 +51,7 @@ function buildDevrouterSection() {
|
|
|
51
51
|
"- Managed selective profile: `devrouter ensure . --profile <name> --json`",
|
|
52
52
|
"- Host/docker runtime app only: `devrouter app run <host-app> --repo . --yes`",
|
|
53
53
|
"- `devrouter ls`",
|
|
54
|
+
"- Managed devcontainer source configs with `postCreateCommand` and a managed post-start adapter must set `waitFor` exactly to `postCreateCommand` or `postStartCommand`; generated managed configs preserve lifecycle fields and change only `runServices`.",
|
|
54
55
|
DEVROUTER_END_SENTINEL
|
|
55
56
|
].join("\n");
|
|
56
57
|
}
|
|
@@ -293,6 +294,10 @@ profiles:
|
|
|
293
294
|
- Inspect managed desired, active, and drift state with \`devrouter status\` and
|
|
294
295
|
\`devrouter doctor\`; values such as credentials and environment contents are
|
|
295
296
|
never written to managed runtime state.
|
|
297
|
+
- A managed adapter paired with \`postCreateCommand\` requires \`waitFor\` exactly
|
|
298
|
+
\`postCreateCommand\` or \`postStartCommand\` before provider mutation. Generated
|
|
299
|
+
selective configuration preserves lifecycle fields and changes only
|
|
300
|
+
\`runServices\`.
|
|
296
301
|
|
|
297
302
|
## Env var injection
|
|
298
303
|
|
|
@@ -1658,12 +1663,12 @@ var init_host_routes = __esm({
|
|
|
1658
1663
|
|
|
1659
1664
|
// src/core/repo-config.ts
|
|
1660
1665
|
function compareSemver(a, b) {
|
|
1661
|
-
const
|
|
1666
|
+
const parse2 = (v) => {
|
|
1662
1667
|
const match = v.trim().match(/^v?(\d+)\.(\d+)\.(\d+)$/);
|
|
1663
1668
|
return match ? { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) } : { major: 0, minor: 0, patch: 0 };
|
|
1664
1669
|
};
|
|
1665
|
-
const left =
|
|
1666
|
-
const right =
|
|
1670
|
+
const left = parse2(a);
|
|
1671
|
+
const right = parse2(b);
|
|
1667
1672
|
if (left.major !== right.major) return left.major - right.major;
|
|
1668
1673
|
if (left.minor !== right.minor) return left.minor - right.minor;
|
|
1669
1674
|
return left.patch - right.patch;
|
|
@@ -2504,7 +2509,7 @@ function loadRepoConfig(repoPath) {
|
|
|
2504
2509
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
2505
2510
|
const requiredVersion = config.devrouter?.version;
|
|
2506
2511
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
2507
|
-
const cliVersion = true ? "0.0.
|
|
2512
|
+
const cliVersion = true ? "0.0.44" : "0.0.0-dev";
|
|
2508
2513
|
if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
|
|
2509
2514
|
hasWarnedVersionMismatch = true;
|
|
2510
2515
|
process.stderr.write(
|
|
@@ -3035,6 +3040,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
3035
3040
|
`- 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.`,
|
|
3036
3041
|
"- 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.",
|
|
3037
3042
|
"- 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. `workspace up` creates linked worktrees; destructive worktree removal and GC remain ledger-scoped.",
|
|
3043
|
+
"- A managed adapter plus `postCreateCommand` requires `waitFor` exactly `postCreateCommand` or `postStartCommand`; managed selective config preserves lifecycle fields and changes only `runServices`.",
|
|
3038
3044
|
"- 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.",
|
|
3039
3045
|
"- 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.",
|
|
3040
3046
|
"- `devrouter workspace cleanup --repo . --inactive-for 30d --check-merged --json` is report-only and never mutates the workspace runtime, routes, ownership, Git, Docker, applications, worktrees, or branches. It reports ownership, workspace runtime registration, runtime state (`running|stopped|busy|not-found|absent|unknown`), checkout, advisory activity, route, and integration evidence. 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. Runtime `lastUsed` remains advisory.",
|
|
@@ -4005,12 +4011,64 @@ var init_docker = __esm({
|
|
|
4005
4011
|
}
|
|
4006
4012
|
});
|
|
4007
4013
|
|
|
4014
|
+
// src/core/devcontainer-config.ts
|
|
4015
|
+
function isObject(value) {
|
|
4016
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4017
|
+
}
|
|
4018
|
+
function parseDevcontainerConfig(contents, sourcePath) {
|
|
4019
|
+
const errors = [];
|
|
4020
|
+
const parsed = (0, import_jsonc_parser.parse)(contents, errors, {
|
|
4021
|
+
allowTrailingComma: true,
|
|
4022
|
+
disallowComments: false
|
|
4023
|
+
});
|
|
4024
|
+
if (errors.length > 0) {
|
|
4025
|
+
const first = errors[0];
|
|
4026
|
+
throw new Error(
|
|
4027
|
+
`Could not parse Dev Container source config '${sourcePath}' as JSONC at offset ${first.offset}.`
|
|
4028
|
+
);
|
|
4029
|
+
}
|
|
4030
|
+
if (!isObject(parsed)) {
|
|
4031
|
+
throw new Error(`Dev Container source config '${sourcePath}' must contain an object.`);
|
|
4032
|
+
}
|
|
4033
|
+
return parsed;
|
|
4034
|
+
}
|
|
4035
|
+
function readDevcontainerConfig(repoPath) {
|
|
4036
|
+
const sourcePath = import_node_path8.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
4037
|
+
if (!import_node_fs9.default.existsSync(sourcePath) || !import_node_fs9.default.lstatSync(sourcePath).isFile()) {
|
|
4038
|
+
throw new Error(`Managed Dev Container source config does not exist: ${sourcePath}`);
|
|
4039
|
+
}
|
|
4040
|
+
const sourceContents = import_node_fs9.default.readFileSync(sourcePath, "utf-8");
|
|
4041
|
+
return {
|
|
4042
|
+
sourcePath,
|
|
4043
|
+
sourceContents,
|
|
4044
|
+
source: parseDevcontainerConfig(sourceContents, sourcePath)
|
|
4045
|
+
};
|
|
4046
|
+
}
|
|
4047
|
+
function assertManagedDevcontainerLifecycle(repoPath) {
|
|
4048
|
+
const { sourcePath, source } = readDevcontainerConfig(repoPath);
|
|
4049
|
+
if (source.postCreateCommand === void 0) return;
|
|
4050
|
+
if (source.waitFor === "postCreateCommand" || source.waitFor === "postStartCommand") return;
|
|
4051
|
+
const actual = source.waitFor === void 0 ? "is missing" : typeof source.waitFor === "string" ? "is an unsupported string" : "is not a string";
|
|
4052
|
+
throw new Error(
|
|
4053
|
+
`Managed Dev Container source '${sourcePath}' defines postCreateCommand, but waitFor ${actual}. Set waitFor to 'postCreateCommand' or 'postStartCommand' before retrying devrouter ensure.`
|
|
4054
|
+
);
|
|
4055
|
+
}
|
|
4056
|
+
var import_node_fs9, import_node_path8, import_jsonc_parser;
|
|
4057
|
+
var init_devcontainer_config = __esm({
|
|
4058
|
+
"src/core/devcontainer-config.ts"() {
|
|
4059
|
+
"use strict";
|
|
4060
|
+
import_node_fs9 = __toESM(require("fs"));
|
|
4061
|
+
import_node_path8 = __toESM(require("path"));
|
|
4062
|
+
import_jsonc_parser = require("jsonc-parser");
|
|
4063
|
+
}
|
|
4064
|
+
});
|
|
4065
|
+
|
|
4008
4066
|
// src/core/managed-post-start.ts
|
|
4009
4067
|
function renderDeliveryScript(targetPath) {
|
|
4010
4068
|
if (!/^\/tmp\/devrouter\/bin\/[a-zA-Z0-9._-]+$/.test(targetPath)) {
|
|
4011
4069
|
throw new Error(`Unsafe managed runtime delivery path: ${targetPath}`);
|
|
4012
4070
|
}
|
|
4013
|
-
const temporaryPrefix =
|
|
4071
|
+
const temporaryPrefix = import_node_path9.default.posix.basename(targetPath);
|
|
4014
4072
|
return `set -eu
|
|
4015
4073
|
umask 077
|
|
4016
4074
|
runtime_root=/tmp/devrouter
|
|
@@ -4045,8 +4103,8 @@ function deliverRuntimeFile(containerId, targetPath, contents, label) {
|
|
|
4045
4103
|
}
|
|
4046
4104
|
}
|
|
4047
4105
|
function readRegularFileBytes(filePath) {
|
|
4048
|
-
if (!
|
|
4049
|
-
return
|
|
4106
|
+
if (!import_node_fs10.default.existsSync(filePath) || !import_node_fs10.default.lstatSync(filePath).isFile()) return void 0;
|
|
4107
|
+
return import_node_fs10.default.readFileSync(filePath);
|
|
4050
4108
|
}
|
|
4051
4109
|
function readRegularFile(filePath) {
|
|
4052
4110
|
return readRegularFileBytes(filePath)?.toString("utf-8");
|
|
@@ -4056,21 +4114,21 @@ function adapterFingerprint(adapter) {
|
|
|
4056
4114
|
}
|
|
4057
4115
|
function resolveProcessHelperPath() {
|
|
4058
4116
|
const candidates = [
|
|
4059
|
-
|
|
4060
|
-
|
|
4117
|
+
import_node_path9.default.resolve(__dirname, "..", "bin", "devrouter-process"),
|
|
4118
|
+
import_node_path9.default.resolve(__dirname, "..", "..", "bin", "devrouter-process")
|
|
4061
4119
|
];
|
|
4062
|
-
const helperPath = candidates.find((candidate) =>
|
|
4120
|
+
const helperPath = candidates.find((candidate) => import_node_fs10.default.existsSync(candidate));
|
|
4063
4121
|
if (!helperPath) {
|
|
4064
4122
|
throw new Error("Could not locate the packaged devrouter-process helper.");
|
|
4065
4123
|
}
|
|
4066
4124
|
return helperPath;
|
|
4067
4125
|
}
|
|
4068
4126
|
function resolveManagedPostStartPlan(repoPath) {
|
|
4069
|
-
const adapterPath =
|
|
4127
|
+
const adapterPath = import_node_path9.default.join(repoPath, MANAGED_ADAPTER_PATH);
|
|
4070
4128
|
const adapterBytes = readRegularFileBytes(adapterPath);
|
|
4071
4129
|
const adapterText = adapterBytes?.toString("utf-8");
|
|
4072
|
-
const dockerfilePath =
|
|
4073
|
-
const devcontainerPath =
|
|
4130
|
+
const dockerfilePath = import_node_path9.default.join(repoPath, ".devcontainer", "Dockerfile");
|
|
4131
|
+
const devcontainerPath = import_node_path9.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
4074
4132
|
const dockerfile = readRegularFile(dockerfilePath) ?? "";
|
|
4075
4133
|
const devcontainer = readRegularFile(devcontainerPath) ?? "";
|
|
4076
4134
|
const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
|
|
@@ -4093,6 +4151,7 @@ function resolveManagedPostStartPlan(repoPath) {
|
|
|
4093
4151
|
}
|
|
4094
4152
|
return { kind: "unmanaged" };
|
|
4095
4153
|
}
|
|
4154
|
+
assertManagedDevcontainerLifecycle(repoPath);
|
|
4096
4155
|
if (adapter.includes("DEVROUTER_PROCESS_HELPER")) {
|
|
4097
4156
|
return {
|
|
4098
4157
|
kind: "runtime",
|
|
@@ -4110,7 +4169,7 @@ function resolveManagedPostStartPlan(repoPath) {
|
|
|
4110
4169
|
}
|
|
4111
4170
|
function runManagedPostStart(options) {
|
|
4112
4171
|
if (options.plan.kind !== "runtime") return;
|
|
4113
|
-
const helper =
|
|
4172
|
+
const helper = import_node_fs10.default.readFileSync(resolveProcessHelperPath());
|
|
4114
4173
|
deliverRuntimeFile(
|
|
4115
4174
|
options.container.id,
|
|
4116
4175
|
RUNTIME_HELPER_PATH,
|
|
@@ -4198,14 +4257,15 @@ function runManagedProcessAction(options) {
|
|
|
4198
4257
|
}
|
|
4199
4258
|
return "drifted";
|
|
4200
4259
|
}
|
|
4201
|
-
var import_node_child_process5, import_node_crypto3,
|
|
4260
|
+
var import_node_child_process5, import_node_crypto3, import_node_fs10, import_node_path9, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH, ADAPTER_WRAPPER;
|
|
4202
4261
|
var init_managed_post_start = __esm({
|
|
4203
4262
|
"src/core/managed-post-start.ts"() {
|
|
4204
4263
|
"use strict";
|
|
4205
4264
|
import_node_child_process5 = require("child_process");
|
|
4206
4265
|
import_node_crypto3 = require("crypto");
|
|
4207
|
-
|
|
4208
|
-
|
|
4266
|
+
import_node_fs10 = __toESM(require("fs"));
|
|
4267
|
+
import_node_path9 = __toESM(require("path"));
|
|
4268
|
+
init_devcontainer_config();
|
|
4209
4269
|
MANAGED_MARKER = "devrouter:managed devcontainer";
|
|
4210
4270
|
MANAGED_ADAPTER_PATH = ".devcontainer/post-start.sh";
|
|
4211
4271
|
RUNTIME_HELPER_PATH = "/tmp/devrouter/bin/devrouter-process";
|
|
@@ -4271,7 +4331,7 @@ function isLoopbackHost(host) {
|
|
|
4271
4331
|
return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
|
|
4272
4332
|
}
|
|
4273
4333
|
function inspectCompose(composeFile, _workspace) {
|
|
4274
|
-
if (!
|
|
4334
|
+
if (!import_node_fs11.default.existsSync(composeFile)) {
|
|
4275
4335
|
return {
|
|
4276
4336
|
aliases: [],
|
|
4277
4337
|
publishedPorts: [],
|
|
@@ -4280,7 +4340,7 @@ function inspectCompose(composeFile, _workspace) {
|
|
|
4280
4340
|
};
|
|
4281
4341
|
}
|
|
4282
4342
|
try {
|
|
4283
|
-
const raw =
|
|
4343
|
+
const raw = import_node_fs11.default.readFileSync(composeFile, "utf-8");
|
|
4284
4344
|
const parsed = import_yaml3.default.parse(raw);
|
|
4285
4345
|
const root = asRecord(parsed);
|
|
4286
4346
|
const services = asRecord(root?.services);
|
|
@@ -4337,11 +4397,11 @@ function routedProxyApps(config) {
|
|
|
4337
4397
|
);
|
|
4338
4398
|
}
|
|
4339
4399
|
function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
4340
|
-
const devcontainerDir =
|
|
4341
|
-
if (!
|
|
4400
|
+
const devcontainerDir = import_node_path10.default.join(repoPath, ".devcontainer");
|
|
4401
|
+
if (!import_node_fs11.default.existsSync(devcontainerDir)) {
|
|
4342
4402
|
return [];
|
|
4343
4403
|
}
|
|
4344
|
-
const compose = inspectCompose(
|
|
4404
|
+
const compose = inspectCompose(import_node_path10.default.join(devcontainerDir, "docker-compose.yml"), workspace);
|
|
4345
4405
|
const checks = [];
|
|
4346
4406
|
if (compose.parseError) {
|
|
4347
4407
|
checks.push({
|
|
@@ -4368,9 +4428,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
4368
4428
|
details: compose.publishedPorts.length > 0 ? compose.publishedPorts.join(", ") : void 0,
|
|
4369
4429
|
suggestion: compose.parseError || compose.publishedPorts.length > 0 ? "Remove published ports and route services through devnet aliases with devrouter proxy apps." : void 0
|
|
4370
4430
|
});
|
|
4371
|
-
const dockerfilePath =
|
|
4372
|
-
const dockerfileExists =
|
|
4373
|
-
const dockerfile = dockerfileExists ?
|
|
4431
|
+
const dockerfilePath = import_node_path10.default.join(devcontainerDir, "Dockerfile");
|
|
4432
|
+
const dockerfileExists = import_node_fs11.default.existsSync(dockerfilePath);
|
|
4433
|
+
const dockerfile = dockerfileExists ? import_node_fs11.default.readFileSync(dockerfilePath, "utf-8") : "";
|
|
4374
4434
|
const devrouterArtifacts = ["@devrouter/cli", "devrouter-process"].filter(
|
|
4375
4435
|
(artifact) => dockerfile.includes(artifact)
|
|
4376
4436
|
);
|
|
@@ -4425,12 +4485,12 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
4425
4485
|
});
|
|
4426
4486
|
return checks;
|
|
4427
4487
|
}
|
|
4428
|
-
var
|
|
4488
|
+
var import_node_fs11, import_node_path10, import_yaml3;
|
|
4429
4489
|
var init_devcontainer_diagnostics = __esm({
|
|
4430
4490
|
"src/core/devcontainer-diagnostics.ts"() {
|
|
4431
4491
|
"use strict";
|
|
4432
|
-
|
|
4433
|
-
|
|
4492
|
+
import_node_fs11 = __toESM(require("fs"));
|
|
4493
|
+
import_node_path10 = __toESM(require("path"));
|
|
4434
4494
|
import_yaml3 = __toESM(require("yaml"));
|
|
4435
4495
|
init_managed_post_start();
|
|
4436
4496
|
}
|
|
@@ -4438,18 +4498,18 @@ var init_devcontainer_diagnostics = __esm({
|
|
|
4438
4498
|
|
|
4439
4499
|
// src/core/paths.ts
|
|
4440
4500
|
function assertPathWithinRepo(filePath, repoRoot, label) {
|
|
4441
|
-
const resolvedRoot =
|
|
4442
|
-
const resolved =
|
|
4443
|
-
if (resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot +
|
|
4501
|
+
const resolvedRoot = import_node_path11.default.resolve(repoRoot);
|
|
4502
|
+
const resolved = import_node_path11.default.resolve(repoRoot, filePath);
|
|
4503
|
+
if (resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot + import_node_path11.default.sep)) {
|
|
4444
4504
|
throw new Error(`${label} path '${filePath}' escapes the repository root.`);
|
|
4445
4505
|
}
|
|
4446
4506
|
return resolved;
|
|
4447
4507
|
}
|
|
4448
|
-
var
|
|
4508
|
+
var import_node_path11;
|
|
4449
4509
|
var init_paths = __esm({
|
|
4450
4510
|
"src/core/paths.ts"() {
|
|
4451
4511
|
"use strict";
|
|
4452
|
-
|
|
4512
|
+
import_node_path11 = __toESM(require("path"));
|
|
4453
4513
|
}
|
|
4454
4514
|
});
|
|
4455
4515
|
|
|
@@ -4534,11 +4594,11 @@ function reconcileRouteRunConflict(repoPath, app) {
|
|
|
4534
4594
|
}
|
|
4535
4595
|
}
|
|
4536
4596
|
}
|
|
4537
|
-
var
|
|
4597
|
+
var import_node_fs12;
|
|
4538
4598
|
var init_route_state = __esm({
|
|
4539
4599
|
"src/core/route-state.ts"() {
|
|
4540
4600
|
"use strict";
|
|
4541
|
-
|
|
4601
|
+
import_node_fs12 = __toESM(require("fs"));
|
|
4542
4602
|
init_host_routes();
|
|
4543
4603
|
init_workspace();
|
|
4544
4604
|
}
|
|
@@ -4707,7 +4767,7 @@ function stateKey(repoPath, workspace) {
|
|
|
4707
4767
|
return (0, import_node_crypto4.createHash)("sha256").update(`${repoPath}\0${workspace ?? ""}`, "utf-8").digest("hex");
|
|
4708
4768
|
}
|
|
4709
4769
|
function managedRuntimeStatePath(repoPath, workspace) {
|
|
4710
|
-
return
|
|
4770
|
+
return import_node_path12.default.join(DEVROUTER_HOME, "managed-runtime", `${stateKey(repoPath, workspace)}.json`);
|
|
4711
4771
|
}
|
|
4712
4772
|
function isStringArray(value) {
|
|
4713
4773
|
return Array.isArray(value) && value.every((item) => typeof item === "string") && new Set(value).size === value.length;
|
|
@@ -4770,10 +4830,10 @@ function validateState(value, repoPath, workspace) {
|
|
|
4770
4830
|
}
|
|
4771
4831
|
function readManagedRuntimeState(repoPath, workspace) {
|
|
4772
4832
|
const statePath = managedRuntimeStatePath(repoPath, workspace);
|
|
4773
|
-
if (!
|
|
4833
|
+
if (!import_node_fs13.default.existsSync(statePath)) return void 0;
|
|
4774
4834
|
let parsed;
|
|
4775
4835
|
try {
|
|
4776
|
-
parsed = JSON.parse(
|
|
4836
|
+
parsed = JSON.parse(import_node_fs13.default.readFileSync(statePath, "utf-8"));
|
|
4777
4837
|
} catch (error) {
|
|
4778
4838
|
throw new Error(`Could not parse managed runtime state: ${String(error)}`);
|
|
4779
4839
|
}
|
|
@@ -4795,20 +4855,20 @@ function markManagedRuntimeDegraded(state, transitionPhase) {
|
|
|
4795
4855
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4796
4856
|
});
|
|
4797
4857
|
}
|
|
4798
|
-
var import_node_crypto4,
|
|
4858
|
+
var import_node_crypto4, import_node_fs13, import_node_path12;
|
|
4799
4859
|
var init_managed_runtime_state = __esm({
|
|
4800
4860
|
"src/core/managed-runtime-state.ts"() {
|
|
4801
4861
|
"use strict";
|
|
4802
4862
|
import_node_crypto4 = require("crypto");
|
|
4803
|
-
|
|
4804
|
-
|
|
4863
|
+
import_node_fs13 = __toESM(require("fs"));
|
|
4864
|
+
import_node_path12 = __toESM(require("path"));
|
|
4805
4865
|
init_atomic_file();
|
|
4806
4866
|
init_router();
|
|
4807
4867
|
}
|
|
4808
4868
|
});
|
|
4809
4869
|
|
|
4810
4870
|
// src/core/devcontainer-profile.ts
|
|
4811
|
-
function
|
|
4871
|
+
function isObject2(value) {
|
|
4812
4872
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4813
4873
|
}
|
|
4814
4874
|
function nonEmptyString(value, label) {
|
|
@@ -4852,14 +4912,14 @@ function resolveComposeFiles(source, repoPath, linked) {
|
|
|
4852
4912
|
"managedRuntime requires .devcontainer/devcontainer.json to define dockerComposeFile."
|
|
4853
4913
|
);
|
|
4854
4914
|
}
|
|
4855
|
-
const directory =
|
|
4915
|
+
const directory = import_node_path13.default.join(repoPath, ".devcontainer");
|
|
4856
4916
|
const files = references.map((reference) => {
|
|
4857
4917
|
const resolved2 = assertPathWithinRepo(
|
|
4858
4918
|
resolveComposeReference(reference, linked),
|
|
4859
4919
|
directory,
|
|
4860
4920
|
"dockerComposeFile"
|
|
4861
4921
|
);
|
|
4862
|
-
if (!
|
|
4922
|
+
if (!import_node_fs14.default.existsSync(resolved2) || !import_node_fs14.default.lstatSync(resolved2).isFile()) {
|
|
4863
4923
|
throw new Error(`Managed Dev Container compose file does not exist: ${resolved2}`);
|
|
4864
4924
|
}
|
|
4865
4925
|
return resolved2;
|
|
@@ -4867,13 +4927,13 @@ function resolveComposeFiles(source, repoPath, linked) {
|
|
|
4867
4927
|
for (const file of files) {
|
|
4868
4928
|
let parsed;
|
|
4869
4929
|
try {
|
|
4870
|
-
parsed = import_yaml4.default.parse(
|
|
4930
|
+
parsed = import_yaml4.default.parse(import_node_fs14.default.readFileSync(file, "utf-8"));
|
|
4871
4931
|
} catch (error) {
|
|
4872
4932
|
throw new Error(
|
|
4873
4933
|
`Could not parse managed Dev Container compose file '${file}': ${String(error)}`
|
|
4874
4934
|
);
|
|
4875
4935
|
}
|
|
4876
|
-
if (!
|
|
4936
|
+
if (!isObject2(parsed) || !isObject2(parsed.services)) {
|
|
4877
4937
|
throw new Error(`Managed Dev Container compose file '${file}' has no services map.`);
|
|
4878
4938
|
}
|
|
4879
4939
|
}
|
|
@@ -4916,12 +4976,12 @@ function assertIgnoredGeneratedPath(repoPath, generatedPath) {
|
|
|
4916
4976
|
}
|
|
4917
4977
|
}
|
|
4918
4978
|
function assertGeneratedPathOwnership(generatedPath) {
|
|
4919
|
-
if (!
|
|
4920
|
-
const stat =
|
|
4979
|
+
if (!import_node_fs14.default.existsSync(generatedPath)) return;
|
|
4980
|
+
const stat = import_node_fs14.default.lstatSync(generatedPath);
|
|
4921
4981
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
4922
4982
|
throw new Error(`Managed Dev Container path '${generatedPath}' is not a regular file.`);
|
|
4923
4983
|
}
|
|
4924
|
-
const firstLine2 =
|
|
4984
|
+
const firstLine2 = import_node_fs14.default.readFileSync(generatedPath, "utf-8").split(/\r?\n/, 1)[0];
|
|
4925
4985
|
if (firstLine2 !== MANAGED_DEVCONTAINER_MARKER) {
|
|
4926
4986
|
throw new Error(
|
|
4927
4987
|
`Managed Dev Container path '${generatedPath}' exists without the devrouter ownership marker.`
|
|
@@ -4945,21 +5005,7 @@ function inspectManagedDevcontainerConfig(options) {
|
|
|
4945
5005
|
if (!managedRuntime) {
|
|
4946
5006
|
throw new Error("Cannot prepare a managed Dev Container without managedRuntime.");
|
|
4947
5007
|
}
|
|
4948
|
-
const sourcePath =
|
|
4949
|
-
if (!import_node_fs13.default.existsSync(sourcePath) || !import_node_fs13.default.lstatSync(sourcePath).isFile()) {
|
|
4950
|
-
throw new Error(`Managed Dev Container source config does not exist: ${sourcePath}`);
|
|
4951
|
-
}
|
|
4952
|
-
const sourceContents = import_node_fs13.default.readFileSync(sourcePath, "utf-8");
|
|
4953
|
-
let parsed;
|
|
4954
|
-
try {
|
|
4955
|
-
parsed = import_yaml4.default.parse(sourceContents);
|
|
4956
|
-
} catch (error) {
|
|
4957
|
-
throw new Error(`Could not parse managed Dev Container source config: ${String(error)}`);
|
|
4958
|
-
}
|
|
4959
|
-
if (!isObject(parsed)) {
|
|
4960
|
-
throw new Error("Managed Dev Container source config must contain an object.");
|
|
4961
|
-
}
|
|
4962
|
-
const source = parsed;
|
|
5008
|
+
const { sourcePath, sourceContents, source } = readDevcontainerConfig(options.repoPath);
|
|
4963
5009
|
const primaryService = nonEmptyString(source.service, "devcontainer.service");
|
|
4964
5010
|
const compose = resolveComposeFiles(source, options.repoPath, options.linked);
|
|
4965
5011
|
const composeServiceSet = new Set(compose.services);
|
|
@@ -5011,7 +5057,7 @@ function inspectManagedDevcontainerConfig(options) {
|
|
|
5011
5057
|
const contents = `${MANAGED_DEVCONTAINER_MARKER}
|
|
5012
5058
|
${JSON.stringify(effective, null, 2)}
|
|
5013
5059
|
`;
|
|
5014
|
-
const generatedPath =
|
|
5060
|
+
const generatedPath = import_node_path13.default.join(options.repoPath, MANAGED_DEVCONTAINER_PATH);
|
|
5015
5061
|
assertIgnoredGeneratedPath(options.repoPath, generatedPath);
|
|
5016
5062
|
assertGeneratedPathOwnership(generatedPath);
|
|
5017
5063
|
return {
|
|
@@ -5038,13 +5084,13 @@ function writeManagedDevcontainerConfig(plan) {
|
|
|
5038
5084
|
function inspectManagedDevcontainerGeneratedConfig(plan) {
|
|
5039
5085
|
let stat;
|
|
5040
5086
|
try {
|
|
5041
|
-
stat =
|
|
5087
|
+
stat = import_node_fs14.default.lstatSync(plan.generatedPath);
|
|
5042
5088
|
} catch (error) {
|
|
5043
5089
|
if (error.code === "ENOENT") return { status: "missing" };
|
|
5044
5090
|
throw error;
|
|
5045
5091
|
}
|
|
5046
5092
|
if (!stat.isFile() || stat.isSymbolicLink()) return { status: "foreign" };
|
|
5047
|
-
const contents =
|
|
5093
|
+
const contents = import_node_fs14.default.readFileSync(plan.generatedPath, "utf-8");
|
|
5048
5094
|
if (!contents.startsWith(`${MANAGED_DEVCONTAINER_MARKER}
|
|
5049
5095
|
`)) {
|
|
5050
5096
|
return { status: "foreign" };
|
|
@@ -5056,17 +5102,17 @@ function inspectManagedDevcontainerGeneratedConfig(plan) {
|
|
|
5056
5102
|
};
|
|
5057
5103
|
}
|
|
5058
5104
|
function removeManagedDevcontainerConfig(plan) {
|
|
5059
|
-
if (!
|
|
5060
|
-
const stat =
|
|
5105
|
+
if (!import_node_fs14.default.existsSync(plan.generatedPath)) return;
|
|
5106
|
+
const stat = import_node_fs14.default.lstatSync(plan.generatedPath);
|
|
5061
5107
|
if (!stat.isFile()) {
|
|
5062
5108
|
throw new Error(`Managed Dev Container path '${plan.generatedPath}' is not a regular file.`);
|
|
5063
5109
|
}
|
|
5064
|
-
const contents =
|
|
5110
|
+
const contents = import_node_fs14.default.readFileSync(plan.generatedPath, "utf-8");
|
|
5065
5111
|
if (!contents.startsWith(`${MANAGED_DEVCONTAINER_MARKER}
|
|
5066
5112
|
`)) {
|
|
5067
5113
|
throw new Error(`Managed Dev Container path '${plan.generatedPath}' is not devrouter-owned.`);
|
|
5068
5114
|
}
|
|
5069
|
-
|
|
5115
|
+
import_node_fs14.default.unlinkSync(plan.generatedPath);
|
|
5070
5116
|
}
|
|
5071
5117
|
function assertSafeContainerId(containerId) {
|
|
5072
5118
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(containerId)) {
|
|
@@ -5131,16 +5177,17 @@ function stopExactManagedService(containerId, service) {
|
|
|
5131
5177
|
throw new Error(`Could not stop exact managed service '${service}' (${containerId}).`);
|
|
5132
5178
|
}
|
|
5133
5179
|
}
|
|
5134
|
-
var import_node_child_process6, import_node_crypto5,
|
|
5180
|
+
var import_node_child_process6, import_node_crypto5, import_node_fs14, import_node_path13, import_yaml4, MANAGED_DEVCONTAINER_PATH, MANAGED_DEVCONTAINER_MARKER;
|
|
5135
5181
|
var init_devcontainer_profile = __esm({
|
|
5136
5182
|
"src/core/devcontainer-profile.ts"() {
|
|
5137
5183
|
"use strict";
|
|
5138
5184
|
import_node_child_process6 = require("child_process");
|
|
5139
5185
|
import_node_crypto5 = require("crypto");
|
|
5140
|
-
|
|
5141
|
-
|
|
5186
|
+
import_node_fs14 = __toESM(require("fs"));
|
|
5187
|
+
import_node_path13 = __toESM(require("path"));
|
|
5142
5188
|
import_yaml4 = __toESM(require("yaml"));
|
|
5143
5189
|
init_atomic_file();
|
|
5190
|
+
init_devcontainer_config();
|
|
5144
5191
|
init_paths();
|
|
5145
5192
|
MANAGED_DEVCONTAINER_PATH = ".devcontainer/devcontainer.devrouter.json";
|
|
5146
5193
|
MANAGED_DEVCONTAINER_MARKER = "// devrouter:managed devcontainer profile";
|
|
@@ -5178,7 +5225,7 @@ function inspectWorkspaceContainers(options) {
|
|
|
5178
5225
|
function workspaceAppContainers(containers, repoPath) {
|
|
5179
5226
|
return containers.filter((container) => {
|
|
5180
5227
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
5181
|
-
return Boolean(workingDir && sameWorkspacePath(workingDir,
|
|
5228
|
+
return Boolean(workingDir && sameWorkspacePath(workingDir, import_node_path14.default.join(repoPath, ".devcontainer"))) && container.mounts.some(
|
|
5182
5229
|
(mount) => mount.Type === "bind" && sameWorkspacePath(mount.Source, repoPath)
|
|
5183
5230
|
);
|
|
5184
5231
|
});
|
|
@@ -5186,7 +5233,7 @@ function workspaceAppContainers(containers, repoPath) {
|
|
|
5186
5233
|
function hasExactComposeIdentity(container, options) {
|
|
5187
5234
|
if (options.composeProject !== void 0 && container.labels["com.docker.compose.project"] !== options.composeProject || container.labels["com.docker.compose.service"] !== options.service || !sameWorkspacePath(
|
|
5188
5235
|
container.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
5189
|
-
|
|
5236
|
+
import_node_path14.default.join(options.repoPath, ".devcontainer")
|
|
5190
5237
|
)) {
|
|
5191
5238
|
return false;
|
|
5192
5239
|
}
|
|
@@ -5223,12 +5270,12 @@ function resolveRunningWorkspaceContainer(repoPath) {
|
|
|
5223
5270
|
}
|
|
5224
5271
|
return { id: container.id, workspacePath: repoMount.Destination };
|
|
5225
5272
|
}
|
|
5226
|
-
var import_node_child_process7,
|
|
5273
|
+
var import_node_child_process7, import_node_path14, SAFE_INSPECT_TEMPLATE, SIZE_INSPECT_TEMPLATE;
|
|
5227
5274
|
var init_devpod_environment = __esm({
|
|
5228
5275
|
"src/core/devpod-environment.ts"() {
|
|
5229
5276
|
"use strict";
|
|
5230
5277
|
import_node_child_process7 = require("child_process");
|
|
5231
|
-
|
|
5278
|
+
import_node_path14 = __toESM(require("path"));
|
|
5232
5279
|
init_workspace();
|
|
5233
5280
|
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}}}';
|
|
5234
5281
|
SIZE_INSPECT_TEMPLATE = SAFE_INSPECT_TEMPLATE.replace(
|
|
@@ -5682,7 +5729,7 @@ function toRepoStatus(repoPath) {
|
|
|
5682
5729
|
const resolvedRepoPath = resolveRepoPath(repoPath);
|
|
5683
5730
|
const configPath = getRepoConfigPath(resolvedRepoPath);
|
|
5684
5731
|
const explicitRepo = typeof repoPath === "string" && repoPath.trim().length > 0;
|
|
5685
|
-
const configExists =
|
|
5732
|
+
const configExists = import_node_fs15.default.existsSync(configPath);
|
|
5686
5733
|
if (!explicitRepo && !configExists) {
|
|
5687
5734
|
return void 0;
|
|
5688
5735
|
}
|
|
@@ -5830,11 +5877,11 @@ async function collectRouterStatus(repoPath) {
|
|
|
5830
5877
|
}
|
|
5831
5878
|
};
|
|
5832
5879
|
}
|
|
5833
|
-
var
|
|
5880
|
+
var import_node_fs15;
|
|
5834
5881
|
var init_status = __esm({
|
|
5835
5882
|
"src/core/status.ts"() {
|
|
5836
5883
|
"use strict";
|
|
5837
|
-
|
|
5884
|
+
import_node_fs15 = __toESM(require("fs"));
|
|
5838
5885
|
init_docker();
|
|
5839
5886
|
init_managed_runtime_state();
|
|
5840
5887
|
init_managed_runtime_status();
|
|
@@ -5873,8 +5920,8 @@ function tlsSetupCommand(repoPath) {
|
|
|
5873
5920
|
}
|
|
5874
5921
|
function getMkcertRootCAPath(options = {}) {
|
|
5875
5922
|
ensureMkcert();
|
|
5876
|
-
const rootCAPath =
|
|
5877
|
-
if (!
|
|
5923
|
+
const rootCAPath = import_node_path15.default.join(runOrThrow("mkcert", ["-CAROOT"]), "rootCA.pem");
|
|
5924
|
+
if (!import_node_fs16.default.existsSync(rootCAPath)) {
|
|
5878
5925
|
throw new Error(
|
|
5879
5926
|
`mkcert root CA was not found at '${rootCAPath}'. Run: ${tlsSetupCommand(options.repoPath)}`
|
|
5880
5927
|
);
|
|
@@ -5917,6 +5964,9 @@ function parseCertificateDnsHosts(pem) {
|
|
|
5917
5964
|
function isHostCoveredByCertificateHost(host, certificateHost) {
|
|
5918
5965
|
const normalizedHost = normalizeHost(host);
|
|
5919
5966
|
const normalizedCertificateHost = normalizeHost(certificateHost);
|
|
5967
|
+
if (normalizedHost === normalizedCertificateHost) {
|
|
5968
|
+
return true;
|
|
5969
|
+
}
|
|
5920
5970
|
if (normalizedCertificateHost.startsWith("*.")) {
|
|
5921
5971
|
if (normalizedCertificateHost === "*.localhost") {
|
|
5922
5972
|
return false;
|
|
@@ -5928,7 +5978,7 @@ function isHostCoveredByCertificateHost(host, certificateHost) {
|
|
|
5928
5978
|
const wildcardPart = normalizedHost.slice(0, normalizedHost.length - suffix.length);
|
|
5929
5979
|
return wildcardPart.length > 0 && !wildcardPart.includes(".");
|
|
5930
5980
|
}
|
|
5931
|
-
return
|
|
5981
|
+
return false;
|
|
5932
5982
|
}
|
|
5933
5983
|
function findUncoveredCertificateHosts(requiredHosts, certificateHosts) {
|
|
5934
5984
|
const normalizedRequired = normalizeUniqueHosts(requiredHosts);
|
|
@@ -5939,22 +5989,54 @@ function findUncoveredCertificateHosts(requiredHosts, certificateHosts) {
|
|
|
5939
5989
|
)
|
|
5940
5990
|
);
|
|
5941
5991
|
}
|
|
5942
|
-
function
|
|
5943
|
-
|
|
5944
|
-
|
|
5992
|
+
function compactTLSCertificateHosts(hosts) {
|
|
5993
|
+
const normalizedHosts = normalizeUniqueHosts(hosts);
|
|
5994
|
+
const wildcardHosts = new Set(normalizedHosts.filter((host) => host.startsWith("*.")));
|
|
5995
|
+
const siblingGroups = /* @__PURE__ */ new Map();
|
|
5996
|
+
const compacted = new Set(wildcardHosts);
|
|
5997
|
+
for (const host of normalizedHosts) {
|
|
5998
|
+
if (host.startsWith("*.")) {
|
|
5999
|
+
continue;
|
|
6000
|
+
}
|
|
6001
|
+
const labels = host.split(".");
|
|
6002
|
+
if (labels.length < 3 || labels.at(-1) !== "localhost") {
|
|
6003
|
+
compacted.add(host);
|
|
6004
|
+
continue;
|
|
6005
|
+
}
|
|
6006
|
+
const suffix = labels.slice(1).join(".");
|
|
6007
|
+
const siblings = siblingGroups.get(suffix) ?? [];
|
|
6008
|
+
siblings.push(host);
|
|
6009
|
+
siblingGroups.set(suffix, siblings);
|
|
6010
|
+
}
|
|
6011
|
+
for (const [suffix, siblings] of siblingGroups) {
|
|
6012
|
+
const wildcard = `*.${suffix}`;
|
|
6013
|
+
if (siblings.length > 1 || wildcardHosts.has(wildcard)) {
|
|
6014
|
+
compacted.add(wildcard);
|
|
6015
|
+
continue;
|
|
6016
|
+
}
|
|
6017
|
+
compacted.add(siblings[0]);
|
|
5945
6018
|
}
|
|
5946
|
-
const
|
|
5947
|
-
return
|
|
6019
|
+
const selectedWildcards = Array.from(compacted).filter((host) => host.startsWith("*."));
|
|
6020
|
+
return normalizeUniqueHosts(Array.from(compacted)).filter(
|
|
6021
|
+
(host) => host.startsWith("*.") || !selectedWildcards.some((wildcard) => isHostCoveredByCertificateHost(host, wildcard))
|
|
6022
|
+
);
|
|
5948
6023
|
}
|
|
5949
|
-
function
|
|
5950
|
-
|
|
5951
|
-
return readCurrentCertificateHosts();
|
|
5952
|
-
} catch {
|
|
6024
|
+
function readCurrentCertificateHosts(options = {}) {
|
|
6025
|
+
if (!import_node_fs16.default.existsSync(CERT_FILE)) {
|
|
5953
6026
|
return [];
|
|
5954
6027
|
}
|
|
6028
|
+
try {
|
|
6029
|
+
const pem = import_node_fs16.default.readFileSync(CERT_FILE, "utf-8");
|
|
6030
|
+
return parseCertificateDnsHosts(pem);
|
|
6031
|
+
} catch (error) {
|
|
6032
|
+
if (options.replaceMalformed) {
|
|
6033
|
+
return [];
|
|
6034
|
+
}
|
|
6035
|
+
throw error;
|
|
6036
|
+
}
|
|
5955
6037
|
}
|
|
5956
6038
|
function buildDesiredTLSCertificateHosts(requestedHosts, existingCertificateHosts) {
|
|
5957
|
-
return
|
|
6039
|
+
return compactTLSCertificateHosts([
|
|
5958
6040
|
...DEFAULT_TLS_CERT_HOSTS,
|
|
5959
6041
|
...existingCertificateHosts,
|
|
5960
6042
|
...requestedHosts
|
|
@@ -5962,35 +6044,75 @@ function buildDesiredTLSCertificateHosts(requestedHosts, existingCertificateHost
|
|
|
5962
6044
|
}
|
|
5963
6045
|
function getTLSHostCoverage(hosts) {
|
|
5964
6046
|
const requiredHosts = normalizeUniqueHosts([...DEFAULT_TLS_CERT_HOSTS, ...hosts]);
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
6047
|
+
return withFileLockSync(
|
|
6048
|
+
TLS_CERTIFICATE_LOCK_FILE,
|
|
6049
|
+
{ activity: "TLS certificate inspection", waitMs: TLS_CERTIFICATE_LOCK_WAIT_MS },
|
|
6050
|
+
() => {
|
|
6051
|
+
const certificateHosts = readCurrentCertificateHosts();
|
|
6052
|
+
const uncoveredHosts = findUncoveredCertificateHosts(requiredHosts, certificateHosts);
|
|
6053
|
+
return {
|
|
6054
|
+
requiredHosts,
|
|
6055
|
+
certificateHosts,
|
|
6056
|
+
uncoveredHosts
|
|
6057
|
+
};
|
|
6058
|
+
}
|
|
6059
|
+
);
|
|
5972
6060
|
}
|
|
5973
6061
|
async function applyTLSCertificate(options, installTrust) {
|
|
5974
6062
|
ensureRouterFiles();
|
|
5975
|
-
const
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
6063
|
+
const result = withFileLockSync(
|
|
6064
|
+
TLS_CERTIFICATE_LOCK_FILE,
|
|
6065
|
+
{
|
|
6066
|
+
activity: "TLS certificate refresh",
|
|
6067
|
+
target: options.repoPath,
|
|
6068
|
+
waitMs: TLS_CERTIFICATE_LOCK_WAIT_MS
|
|
6069
|
+
},
|
|
6070
|
+
() => {
|
|
6071
|
+
const alreadyEnabled = isTLSEnabled();
|
|
6072
|
+
if (installTrust) {
|
|
6073
|
+
ensureMkcert();
|
|
6074
|
+
runOrThrow("mkcert", ["-install"]);
|
|
6075
|
+
} else {
|
|
6076
|
+
getMkcertRootCAPath({ repoPath: options.repoPath });
|
|
6077
|
+
}
|
|
6078
|
+
const existingCertificateHosts = readCurrentCertificateHosts({
|
|
6079
|
+
replaceMalformed: installTrust
|
|
6080
|
+
});
|
|
6081
|
+
const desiredHosts = buildDesiredTLSCertificateHosts(
|
|
6082
|
+
options.hosts ?? [],
|
|
6083
|
+
existingCertificateHosts
|
|
6084
|
+
);
|
|
6085
|
+
let certificateHosts = [];
|
|
6086
|
+
let uncoveredHosts = [];
|
|
6087
|
+
for (let attempt = 1; attempt <= TLS_CERTIFICATE_WRITE_ATTEMPTS; attempt += 1) {
|
|
6088
|
+
runOrThrow("mkcert", [
|
|
6089
|
+
"-cert-file",
|
|
6090
|
+
CERT_FILE,
|
|
6091
|
+
"-key-file",
|
|
6092
|
+
CERT_KEY_FILE,
|
|
6093
|
+
...desiredHosts
|
|
6094
|
+
]);
|
|
6095
|
+
certificateHosts = readCurrentCertificateHosts();
|
|
6096
|
+
uncoveredHosts = findUncoveredCertificateHosts(desiredHosts, certificateHosts);
|
|
6097
|
+
if (uncoveredHosts.length === 0) {
|
|
6098
|
+
break;
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
if (uncoveredHosts.length > 0) {
|
|
6102
|
+
throw new Error(
|
|
6103
|
+
`mkcert did not produce certificate coverage for host(s): ${uncoveredHosts.join(", ")} after ${TLS_CERTIFICATE_WRITE_ATTEMPTS} attempts`
|
|
6104
|
+
);
|
|
6105
|
+
}
|
|
6106
|
+
setTLSEnabled(true);
|
|
6107
|
+
refreshHostRoutesDynamicFile();
|
|
6108
|
+
return { alreadyEnabled, hosts: certificateHosts };
|
|
6109
|
+
}
|
|
5979
6110
|
);
|
|
5980
|
-
if (installTrust) {
|
|
5981
|
-
ensureMkcert();
|
|
5982
|
-
runOrThrow("mkcert", ["-install"]);
|
|
5983
|
-
} else {
|
|
5984
|
-
getMkcertRootCAPath({ repoPath: options.repoPath });
|
|
5985
|
-
}
|
|
5986
|
-
runOrThrow("mkcert", ["-cert-file", CERT_FILE, "-key-file", CERT_KEY_FILE, ...desiredHosts]);
|
|
5987
|
-
setTLSEnabled(true);
|
|
5988
|
-
refreshHostRoutesDynamicFile();
|
|
5989
6111
|
const routerContainer = await findContainerByName("devrouter-traefik");
|
|
5990
6112
|
if (routerContainer && await isContainerRunning("devrouter-traefik")) {
|
|
5991
6113
|
startRouterStack();
|
|
5992
6114
|
}
|
|
5993
|
-
return
|
|
6115
|
+
return result;
|
|
5994
6116
|
}
|
|
5995
6117
|
async function installTLS(options = {}) {
|
|
5996
6118
|
return applyTLSCertificate(options, true);
|
|
@@ -6032,18 +6154,22 @@ Run: ${tlsSetupCommand(options.repoPath)}`
|
|
|
6032
6154
|
);
|
|
6033
6155
|
}
|
|
6034
6156
|
}
|
|
6035
|
-
var import_node_child_process8, import_node_crypto6,
|
|
6157
|
+
var import_node_child_process8, import_node_crypto6, import_node_fs16, import_node_path15, DEFAULT_TLS_CERT_HOSTS, TLS_CERTIFICATE_LOCK_FILE, TLS_CERTIFICATE_LOCK_WAIT_MS, TLS_CERTIFICATE_WRITE_ATTEMPTS;
|
|
6036
6158
|
var init_tls = __esm({
|
|
6037
6159
|
"src/core/tls.ts"() {
|
|
6038
6160
|
"use strict";
|
|
6039
6161
|
import_node_child_process8 = require("child_process");
|
|
6040
6162
|
import_node_crypto6 = require("crypto");
|
|
6041
|
-
|
|
6042
|
-
|
|
6163
|
+
import_node_fs16 = __toESM(require("fs"));
|
|
6164
|
+
import_node_path15 = __toESM(require("path"));
|
|
6043
6165
|
init_docker();
|
|
6166
|
+
init_file_lock();
|
|
6044
6167
|
init_host_routes();
|
|
6045
6168
|
init_router();
|
|
6046
6169
|
DEFAULT_TLS_CERT_HOSTS = ["localhost", "*.localhost"];
|
|
6170
|
+
TLS_CERTIFICATE_LOCK_FILE = import_node_path15.default.join(DEVROUTER_HOME, "tls-certificate.lock");
|
|
6171
|
+
TLS_CERTIFICATE_LOCK_WAIT_MS = 6e4;
|
|
6172
|
+
TLS_CERTIFICATE_WRITE_ATTEMPTS = 2;
|
|
6047
6173
|
}
|
|
6048
6174
|
});
|
|
6049
6175
|
|
|
@@ -6220,7 +6346,7 @@ function isRuntimeInstalled(runtime) {
|
|
|
6220
6346
|
function readWorkspaceRuntimeConfig() {
|
|
6221
6347
|
let raw;
|
|
6222
6348
|
try {
|
|
6223
|
-
raw = JSON.parse(
|
|
6349
|
+
raw = JSON.parse(import_node_fs17.default.readFileSync(RUNTIME_CONFIG_FILE, "utf-8"));
|
|
6224
6350
|
} catch {
|
|
6225
6351
|
return {};
|
|
6226
6352
|
}
|
|
@@ -6241,7 +6367,7 @@ function readWorkspaceRuntimeConfig() {
|
|
|
6241
6367
|
function inspectWorkspaceRuntimeConfig() {
|
|
6242
6368
|
let rawText;
|
|
6243
6369
|
try {
|
|
6244
|
-
rawText =
|
|
6370
|
+
rawText = import_node_fs17.default.readFileSync(RUNTIME_CONFIG_FILE, "utf-8");
|
|
6245
6371
|
} catch {
|
|
6246
6372
|
return { exists: false, config: {}, problems: [] };
|
|
6247
6373
|
}
|
|
@@ -6292,7 +6418,7 @@ function writeWorkspaceRuntimeConfig(config) {
|
|
|
6292
6418
|
}
|
|
6293
6419
|
next.devsyInactivityTimeout = timeout;
|
|
6294
6420
|
}
|
|
6295
|
-
|
|
6421
|
+
import_node_fs17.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
6296
6422
|
writeFileAtomically(RUNTIME_CONFIG_FILE, `${JSON.stringify(next, null, 2)}
|
|
6297
6423
|
`);
|
|
6298
6424
|
}
|
|
@@ -6400,20 +6526,20 @@ function resetWorkspaceRuntimeCaches() {
|
|
|
6400
6526
|
cachedRuntime = void 0;
|
|
6401
6527
|
cachedSnapshots = void 0;
|
|
6402
6528
|
}
|
|
6403
|
-
var import_node_child_process11,
|
|
6529
|
+
var import_node_child_process11, import_node_fs17, import_node_path16, SUPPORTED_RUNTIMES2, RUNTIME_CONFIG_FILE, INACTIVITY_TIMEOUT_PATTERN, cachedRuntime, cachedSnapshots, UnsupportedWorkspaceRuntimeError, WorkspaceRuntimeOwnershipError;
|
|
6404
6530
|
var init_workspace_runtime = __esm({
|
|
6405
6531
|
"src/core/workspace-runtime.ts"() {
|
|
6406
6532
|
"use strict";
|
|
6407
6533
|
import_node_child_process11 = require("child_process");
|
|
6408
|
-
|
|
6409
|
-
|
|
6534
|
+
import_node_fs17 = __toESM(require("fs"));
|
|
6535
|
+
import_node_path16 = __toESM(require("path"));
|
|
6410
6536
|
init_atomic_file();
|
|
6411
6537
|
init_devpod_registry();
|
|
6412
6538
|
init_devsy_workspaces();
|
|
6413
6539
|
init_router();
|
|
6414
6540
|
init_workspace();
|
|
6415
6541
|
SUPPORTED_RUNTIMES2 = ["devpod", "devsy"];
|
|
6416
|
-
RUNTIME_CONFIG_FILE =
|
|
6542
|
+
RUNTIME_CONFIG_FILE = import_node_path16.default.join(DEVROUTER_HOME, "workspace-runtime.json");
|
|
6417
6543
|
INACTIVITY_TIMEOUT_PATTERN = /^(?:\d+(?:ms|s|m|h))+$/;
|
|
6418
6544
|
UnsupportedWorkspaceRuntimeError = class extends Error {
|
|
6419
6545
|
};
|
|
@@ -6491,12 +6617,12 @@ function parseMinimumNodeMajor(value) {
|
|
|
6491
6617
|
return Number(match[1]);
|
|
6492
6618
|
}
|
|
6493
6619
|
function readPackageJson(repoPath) {
|
|
6494
|
-
const packagePath =
|
|
6495
|
-
if (!
|
|
6620
|
+
const packagePath = import_node_path17.default.join(repoPath, "package.json");
|
|
6621
|
+
if (!import_node_fs18.default.existsSync(packagePath)) {
|
|
6496
6622
|
return void 0;
|
|
6497
6623
|
}
|
|
6498
6624
|
try {
|
|
6499
|
-
return JSON.parse(
|
|
6625
|
+
return JSON.parse(import_node_fs18.default.readFileSync(packagePath, "utf-8"));
|
|
6500
6626
|
} catch {
|
|
6501
6627
|
return void 0;
|
|
6502
6628
|
}
|
|
@@ -6636,13 +6762,13 @@ function buildGlobalToolChecks(repoPath) {
|
|
|
6636
6762
|
checks.push(nodeToolchainCheck(repoPath));
|
|
6637
6763
|
return checks;
|
|
6638
6764
|
}
|
|
6639
|
-
var import_node_child_process12,
|
|
6765
|
+
var import_node_child_process12, import_node_fs18, import_node_path17;
|
|
6640
6766
|
var init_tool_diagnostics = __esm({
|
|
6641
6767
|
"src/core/tool-diagnostics.ts"() {
|
|
6642
6768
|
"use strict";
|
|
6643
6769
|
import_node_child_process12 = require("child_process");
|
|
6644
|
-
|
|
6645
|
-
|
|
6770
|
+
import_node_fs18 = __toESM(require("fs"));
|
|
6771
|
+
import_node_path17 = __toESM(require("path"));
|
|
6646
6772
|
init_workspace_runtime();
|
|
6647
6773
|
}
|
|
6648
6774
|
});
|
|
@@ -6769,7 +6895,7 @@ function failedStartMayHaveAttached(devsyId, repoPath) {
|
|
|
6769
6895
|
}
|
|
6770
6896
|
}
|
|
6771
6897
|
function withMutationLock(activity, target, operation) {
|
|
6772
|
-
|
|
6898
|
+
import_node_fs19.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
6773
6899
|
return withFileLockSync(
|
|
6774
6900
|
DEVSY_MUTATION_LOCK_FILE,
|
|
6775
6901
|
{ activity, target: `'${target}'`, waitMs: DEVSY_MUTATION_WAIT_MS },
|
|
@@ -6914,17 +7040,17 @@ function startDevsyWorkspace(options) {
|
|
|
6914
7040
|
}
|
|
6915
7041
|
});
|
|
6916
7042
|
}
|
|
6917
|
-
var import_node_child_process14,
|
|
7043
|
+
var import_node_child_process14, import_node_fs19, import_node_path18, DEVSY_MUTATION_LOCK_FILE, DEVSY_MUTATION_WAIT_MS, DevsyStartPostconditionError;
|
|
6918
7044
|
var init_devsy_mutation = __esm({
|
|
6919
7045
|
"src/core/devsy-mutation.ts"() {
|
|
6920
7046
|
"use strict";
|
|
6921
7047
|
import_node_child_process14 = require("child_process");
|
|
6922
|
-
|
|
6923
|
-
|
|
7048
|
+
import_node_fs19 = __toESM(require("fs"));
|
|
7049
|
+
import_node_path18 = __toESM(require("path"));
|
|
6924
7050
|
init_devsy_workspaces();
|
|
6925
7051
|
init_file_lock();
|
|
6926
7052
|
init_router();
|
|
6927
|
-
DEVSY_MUTATION_LOCK_FILE =
|
|
7053
|
+
DEVSY_MUTATION_LOCK_FILE = import_node_path18.default.join(DEVROUTER_HOME, "devsy-mutation.lock");
|
|
6928
7054
|
DEVSY_MUTATION_WAIT_MS = 6e4;
|
|
6929
7055
|
DevsyStartPostconditionError = class extends Error {
|
|
6930
7056
|
};
|
|
@@ -6933,7 +7059,7 @@ var init_devsy_mutation = __esm({
|
|
|
6933
7059
|
|
|
6934
7060
|
// src/core/devpod-mutation.ts
|
|
6935
7061
|
function withMutationLock2(activity, target, operation) {
|
|
6936
|
-
|
|
7062
|
+
import_node_fs20.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
6937
7063
|
return withFileLockSync(
|
|
6938
7064
|
DEVPOD_MUTATION_LOCK_FILE,
|
|
6939
7065
|
{ activity, target: `'${target}'`, waitMs: DEVPOD_MUTATION_WAIT_MS },
|
|
@@ -7101,19 +7227,19 @@ function startDevpodWorkspace(options) {
|
|
|
7101
7227
|
}
|
|
7102
7228
|
});
|
|
7103
7229
|
}
|
|
7104
|
-
var import_node_child_process15,
|
|
7230
|
+
var import_node_child_process15, import_node_fs20, import_node_path19, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS, DevpodStartPostconditionError;
|
|
7105
7231
|
var init_devpod_mutation = __esm({
|
|
7106
7232
|
"src/core/devpod-mutation.ts"() {
|
|
7107
7233
|
"use strict";
|
|
7108
7234
|
import_node_child_process15 = require("child_process");
|
|
7109
|
-
|
|
7110
|
-
|
|
7235
|
+
import_node_fs20 = __toESM(require("fs"));
|
|
7236
|
+
import_node_path19 = __toESM(require("path"));
|
|
7111
7237
|
init_devpod_workspaces();
|
|
7112
7238
|
init_devsy_mutation();
|
|
7113
7239
|
init_file_lock();
|
|
7114
7240
|
init_router();
|
|
7115
7241
|
init_workspace_runtime();
|
|
7116
|
-
DEVPOD_MUTATION_LOCK_FILE =
|
|
7242
|
+
DEVPOD_MUTATION_LOCK_FILE = import_node_path19.default.join(DEVROUTER_HOME, "devpod-mutation.lock");
|
|
7117
7243
|
DEVPOD_MUTATION_WAIT_MS = 6e4;
|
|
7118
7244
|
DevpodStartPostconditionError = class extends Error {
|
|
7119
7245
|
};
|
|
@@ -7135,7 +7261,7 @@ function resolveGitCommonDir(repoPath) {
|
|
|
7135
7261
|
if (result.status !== 0 || !output2) {
|
|
7136
7262
|
throw commandError("Could not resolve the Git common directory", repoPath, result.stderr);
|
|
7137
7263
|
}
|
|
7138
|
-
return comparableWorkspacePath(
|
|
7264
|
+
return comparableWorkspacePath(import_node_path20.default.isAbsolute(output2) ? output2 : import_node_path20.default.resolve(repoPath, output2));
|
|
7139
7265
|
}
|
|
7140
7266
|
function resolveGitTopLevel(repoPath) {
|
|
7141
7267
|
const result = (0, import_node_child_process16.spawnSync)("git", ["-C", repoPath, "rev-parse", "--show-toplevel"], {
|
|
@@ -7186,7 +7312,7 @@ function listGitWorktrees(repoPath) {
|
|
|
7186
7312
|
return worktrees;
|
|
7187
7313
|
}
|
|
7188
7314
|
function ownershipDirectory(repoPath) {
|
|
7189
|
-
return
|
|
7315
|
+
return import_node_path20.default.join(resolveGitCommonDir(repoPath), OWNERSHIP_DIR);
|
|
7190
7316
|
}
|
|
7191
7317
|
function validateWorkspace(value, label) {
|
|
7192
7318
|
if (typeof value !== "string" || wsFromBranch(value) !== value) {
|
|
@@ -7214,7 +7340,7 @@ function validateRecord(value, expectedWorkspace) {
|
|
|
7214
7340
|
`workspace ownership file '${expectedWorkspace}' contains identity '${workspace}'`
|
|
7215
7341
|
);
|
|
7216
7342
|
}
|
|
7217
|
-
if (typeof candidate.worktreePath !== "string" || !
|
|
7343
|
+
if (typeof candidate.worktreePath !== "string" || !import_node_path20.default.isAbsolute(candidate.worktreePath)) {
|
|
7218
7344
|
throw new Error("invalid workspace ownership worktreePath");
|
|
7219
7345
|
}
|
|
7220
7346
|
if (candidate.branch !== null && typeof candidate.branch !== "string") {
|
|
@@ -7234,7 +7360,7 @@ function validateRecord(value, expectedWorkspace) {
|
|
|
7234
7360
|
function readRecordFile(filePath, expectedWorkspace) {
|
|
7235
7361
|
let parsed;
|
|
7236
7362
|
try {
|
|
7237
|
-
parsed = JSON.parse(
|
|
7363
|
+
parsed = JSON.parse(import_node_fs21.default.readFileSync(filePath, "utf-8"));
|
|
7238
7364
|
} catch (error) {
|
|
7239
7365
|
if (error instanceof SyntaxError) {
|
|
7240
7366
|
throw new Error(`invalid workspace ownership JSON at '${filePath}'`);
|
|
@@ -7250,7 +7376,7 @@ function listWorkspaceOwnership(repoPath) {
|
|
|
7250
7376
|
function listWorkspaceOwnershipInDirectory(directory) {
|
|
7251
7377
|
let entries;
|
|
7252
7378
|
try {
|
|
7253
|
-
entries =
|
|
7379
|
+
entries = import_node_fs21.default.readdirSync(directory, { withFileTypes: true });
|
|
7254
7380
|
} catch (error) {
|
|
7255
7381
|
if (error.code === "ENOENT") return [];
|
|
7256
7382
|
throw error;
|
|
@@ -7258,14 +7384,14 @@ function listWorkspaceOwnershipInDirectory(directory) {
|
|
|
7258
7384
|
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).sort((left, right) => left.name.localeCompare(right.name)).map((entry) => {
|
|
7259
7385
|
const workspace = entry.name.slice(0, -".json".length);
|
|
7260
7386
|
validateWorkspace(workspace, "filename");
|
|
7261
|
-
return readRecordFile(
|
|
7387
|
+
return readRecordFile(import_node_path20.default.join(directory, entry.name), workspace);
|
|
7262
7388
|
});
|
|
7263
7389
|
}
|
|
7264
7390
|
function writeWorkspaceOwnershipInDirectory(directory, input2) {
|
|
7265
7391
|
const workspace = validateWorkspace(input2.workspace, "workspace");
|
|
7266
7392
|
const devpodId = validateWorkspace(input2.devpodId, "devpodId");
|
|
7267
7393
|
const worktreePath = comparableWorkspacePath(input2.worktreePath);
|
|
7268
|
-
const filePath =
|
|
7394
|
+
const filePath = import_node_path20.default.join(directory, `${workspace}.json`);
|
|
7269
7395
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7270
7396
|
const records = listWorkspaceOwnershipInDirectory(directory);
|
|
7271
7397
|
const existing = records.find((record2) => record2.workspace === workspace);
|
|
@@ -7301,9 +7427,9 @@ function writeWorkspaceOwnershipInDirectory(directory, input2) {
|
|
|
7301
7427
|
return record;
|
|
7302
7428
|
}
|
|
7303
7429
|
function removeWorkspaceOwnershipInDirectory(directory, workspace) {
|
|
7304
|
-
const filePath =
|
|
7430
|
+
const filePath = import_node_path20.default.join(directory, `${validateWorkspace(workspace, "workspace")}.json`);
|
|
7305
7431
|
try {
|
|
7306
|
-
|
|
7432
|
+
import_node_fs21.default.rmSync(filePath);
|
|
7307
7433
|
return true;
|
|
7308
7434
|
} catch (error) {
|
|
7309
7435
|
if (error.code === "ENOENT") return false;
|
|
@@ -7314,7 +7440,7 @@ function sameOwnershipRecord(left, right) {
|
|
|
7314
7440
|
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;
|
|
7315
7441
|
}
|
|
7316
7442
|
function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
|
|
7317
|
-
const filePath =
|
|
7443
|
+
const filePath = import_node_path20.default.join(
|
|
7318
7444
|
directory,
|
|
7319
7445
|
`${validateWorkspace(expected.workspace, "workspace")}.json`
|
|
7320
7446
|
);
|
|
@@ -7326,14 +7452,14 @@ function removeWorkspaceOwnershipIfMatchesInDirectory(directory, expected) {
|
|
|
7326
7452
|
throw error;
|
|
7327
7453
|
}
|
|
7328
7454
|
if (!sameOwnershipRecord(current, expected)) return "changed";
|
|
7329
|
-
|
|
7455
|
+
import_node_fs21.default.rmSync(filePath);
|
|
7330
7456
|
return "removed";
|
|
7331
7457
|
}
|
|
7332
7458
|
function withWorkspaceOwnershipTransaction(repoPath, operation) {
|
|
7333
7459
|
const directory = ownershipDirectory(repoPath);
|
|
7334
|
-
|
|
7460
|
+
import_node_fs21.default.mkdirSync(directory, { recursive: true });
|
|
7335
7461
|
return withFileLockSync(
|
|
7336
|
-
|
|
7462
|
+
import_node_path20.default.join(directory, ".lock"),
|
|
7337
7463
|
{ activity: "workspace ownership transaction", target: `'${repoPath}'`, waitMs: 5e3 },
|
|
7338
7464
|
() => operation({
|
|
7339
7465
|
list: () => listWorkspaceOwnershipInDirectory(directory),
|
|
@@ -7364,7 +7490,7 @@ function inspectWorkspaceOwnership(record, worktrees, devpods) {
|
|
|
7364
7490
|
if (worktree?.locked) {
|
|
7365
7491
|
return { ownerStatus: "locked", devpodStatus, worktree };
|
|
7366
7492
|
}
|
|
7367
|
-
if ((!worktree || worktree.prunable) &&
|
|
7493
|
+
if ((!worktree || worktree.prunable) && import_node_fs21.default.existsSync(record.worktreePath)) {
|
|
7368
7494
|
return { ownerStatus: "conflict", devpodStatus, worktree };
|
|
7369
7495
|
}
|
|
7370
7496
|
if (!worktree || worktree.prunable) {
|
|
@@ -7388,20 +7514,20 @@ function listMissingWorkspaceOwnership(repoPath) {
|
|
|
7388
7514
|
(record) => inspectWorkspaceOwnership(record, worktrees, void 0).ownerStatus === "missing"
|
|
7389
7515
|
);
|
|
7390
7516
|
}
|
|
7391
|
-
var import_node_child_process16,
|
|
7517
|
+
var import_node_child_process16, import_node_fs21, import_node_path20, READ_ONLY_GIT_ENV, OWNERSHIP_VERSION, OWNERSHIP_DIR;
|
|
7392
7518
|
var init_workspace_ownership = __esm({
|
|
7393
7519
|
"src/core/workspace-ownership.ts"() {
|
|
7394
7520
|
"use strict";
|
|
7395
7521
|
import_node_child_process16 = require("child_process");
|
|
7396
|
-
|
|
7397
|
-
|
|
7522
|
+
import_node_fs21 = __toESM(require("fs"));
|
|
7523
|
+
import_node_path20 = __toESM(require("path"));
|
|
7398
7524
|
init_atomic_file();
|
|
7399
7525
|
init_devpod_workspaces();
|
|
7400
7526
|
init_file_lock();
|
|
7401
7527
|
init_workspace();
|
|
7402
7528
|
READ_ONLY_GIT_ENV = { ...process.env, GIT_OPTIONAL_LOCKS: "0" };
|
|
7403
7529
|
OWNERSHIP_VERSION = 1;
|
|
7404
|
-
OWNERSHIP_DIR =
|
|
7530
|
+
OWNERSHIP_DIR = import_node_path20.default.join("devrouter", "workspaces");
|
|
7405
7531
|
}
|
|
7406
7532
|
});
|
|
7407
7533
|
|
|
@@ -7415,10 +7541,10 @@ function inRepositoryWorkspaceScope(repoPath, worktreePath, livePaths) {
|
|
|
7415
7541
|
if (livePaths.some((candidate) => sameWorkspacePath(candidate, worktreePath))) return true;
|
|
7416
7542
|
const comparableRepo = comparableWorkspacePath(repoPath);
|
|
7417
7543
|
const comparableWorktree = comparableWorkspacePath(worktreePath);
|
|
7418
|
-
const localRoot =
|
|
7544
|
+
const localRoot = import_node_path21.default.join(comparableRepo, "trees") + import_node_path21.default.sep;
|
|
7419
7545
|
if (comparableWorktree.startsWith(localRoot)) return true;
|
|
7420
|
-
const legacyPrefix = `${
|
|
7421
|
-
return
|
|
7546
|
+
const legacyPrefix = `${import_node_path21.default.basename(comparableRepo)}-`;
|
|
7547
|
+
return import_node_path21.default.dirname(comparableWorktree) === import_node_path21.default.dirname(comparableRepo) && import_node_path21.default.basename(comparableWorktree).startsWith(legacyPrefix);
|
|
7422
7548
|
}
|
|
7423
7549
|
function previewActions(devpodStatus, routeCount, includeRecord) {
|
|
7424
7550
|
const actions = [
|
|
@@ -7670,11 +7796,11 @@ function applyWorkspaceGc(plan) {
|
|
|
7670
7796
|
candidates
|
|
7671
7797
|
};
|
|
7672
7798
|
}
|
|
7673
|
-
var
|
|
7799
|
+
var import_node_path21;
|
|
7674
7800
|
var init_workspace_gc = __esm({
|
|
7675
7801
|
"src/core/workspace-gc.ts"() {
|
|
7676
7802
|
"use strict";
|
|
7677
|
-
|
|
7803
|
+
import_node_path21 = __toESM(require("path"));
|
|
7678
7804
|
init_devpod_mutation();
|
|
7679
7805
|
init_devpod_workspaces();
|
|
7680
7806
|
init_host_routes();
|
|
@@ -7744,11 +7870,11 @@ function inspectPostgresCredentials(repoPath, config) {
|
|
|
7744
7870
|
parseErrors.push(`${app.name}: ${composeFile} (${message})`);
|
|
7745
7871
|
continue;
|
|
7746
7872
|
}
|
|
7747
|
-
if (!
|
|
7873
|
+
if (!import_node_fs22.default.existsSync(absolutePath)) {
|
|
7748
7874
|
continue;
|
|
7749
7875
|
}
|
|
7750
7876
|
try {
|
|
7751
|
-
const raw =
|
|
7877
|
+
const raw = import_node_fs22.default.readFileSync(absolutePath, "utf-8");
|
|
7752
7878
|
const parsed = import_yaml5.default.parse(raw);
|
|
7753
7879
|
const root = asRecord2(parsed);
|
|
7754
7880
|
const services = asRecord2(root?.services);
|
|
@@ -8027,7 +8153,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
8027
8153
|
const config = runtimeConfig.config;
|
|
8028
8154
|
loadedConfig = config;
|
|
8029
8155
|
loadedWorkspace = runtimeConfig.workspace;
|
|
8030
|
-
const cliVersion = true ? "0.0.
|
|
8156
|
+
const cliVersion = true ? "0.0.44" : "0.0.0-dev";
|
|
8031
8157
|
const configVersion = config.devrouter?.version;
|
|
8032
8158
|
if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
|
|
8033
8159
|
addCheck(checks, {
|
|
@@ -8057,9 +8183,9 @@ async function buildDoctorReport(options = {}) {
|
|
|
8057
8183
|
(app) => app.docker.composeFiles.map((filePath) => ({
|
|
8058
8184
|
app: app.name,
|
|
8059
8185
|
filePath,
|
|
8060
|
-
absolutePath:
|
|
8186
|
+
absolutePath: import_node_path22.default.resolve(repo.path, filePath)
|
|
8061
8187
|
}))
|
|
8062
|
-
).filter((entry) => !
|
|
8188
|
+
).filter((entry) => !import_node_fs22.default.existsSync(entry.absolutePath));
|
|
8063
8189
|
addCheck(checks, {
|
|
8064
8190
|
id: "repo.compose-files",
|
|
8065
8191
|
level: missingComposeFiles.length === 0 ? "ok" : "error",
|
|
@@ -8102,8 +8228,8 @@ async function buildDoctorReport(options = {}) {
|
|
|
8102
8228
|
const missingHostCwds = config.apps.filter((app) => app.runtime === "host").map((app) => ({
|
|
8103
8229
|
app: app.name,
|
|
8104
8230
|
cwd: app.hostRun.cwd,
|
|
8105
|
-
absolutePath:
|
|
8106
|
-
})).filter((entry) => !
|
|
8231
|
+
absolutePath: import_node_path22.default.resolve(repo.path, app.hostRun.cwd)
|
|
8232
|
+
})).filter((entry) => !import_node_fs22.default.existsSync(entry.absolutePath));
|
|
8107
8233
|
addCheck(checks, {
|
|
8108
8234
|
id: "repo.host-cwd",
|
|
8109
8235
|
level: missingHostCwds.length === 0 ? "ok" : "error",
|
|
@@ -8247,12 +8373,12 @@ async function buildDoctorReport(options = {}) {
|
|
|
8247
8373
|
nextSteps
|
|
8248
8374
|
};
|
|
8249
8375
|
}
|
|
8250
|
-
var
|
|
8376
|
+
var import_node_fs22, import_node_path22, import_yaml5, POSTGRES_DEFAULTS;
|
|
8251
8377
|
var init_doctor = __esm({
|
|
8252
8378
|
"src/core/doctor.ts"() {
|
|
8253
8379
|
"use strict";
|
|
8254
|
-
|
|
8255
|
-
|
|
8380
|
+
import_node_fs22 = __toESM(require("fs"));
|
|
8381
|
+
import_node_path22 = __toESM(require("path"));
|
|
8256
8382
|
import_yaml5 = __toESM(require("yaml"));
|
|
8257
8383
|
init_docker();
|
|
8258
8384
|
init_host_routes();
|
|
@@ -8747,11 +8873,11 @@ var init_route_publication = __esm({
|
|
|
8747
8873
|
// src/core/workspace-ensure.ts
|
|
8748
8874
|
function assertOverlay(container, repoPath) {
|
|
8749
8875
|
const workingDir = container.labels["com.docker.compose.project.working_dir"];
|
|
8750
|
-
if (!workingDir || !sameWorkspacePath(workingDir,
|
|
8876
|
+
if (!workingDir || !sameWorkspacePath(workingDir, import_node_path23.default.join(repoPath, ".devcontainer"))) {
|
|
8751
8877
|
throw new Error(`Container '${container.id}' does not belong to the exact worktree.`);
|
|
8752
8878
|
}
|
|
8753
8879
|
const configFiles = (container.labels["com.docker.compose.project.config_files"] ?? "").split(",").filter(Boolean);
|
|
8754
|
-
const expectedOverlay =
|
|
8880
|
+
const expectedOverlay = import_node_path23.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
8755
8881
|
if (!configFiles.some((configFile) => sameWorkspacePath(configFile, expectedOverlay))) {
|
|
8756
8882
|
throw new Error(`Container '${container.id}' was not started with ${DEVCONTAINER_OVERLAY}.`);
|
|
8757
8883
|
}
|
|
@@ -9136,7 +9262,7 @@ function resolvePrimaryTarget(repoPath) {
|
|
|
9136
9262
|
}
|
|
9137
9263
|
function isPrimaryCheckout(repoPath) {
|
|
9138
9264
|
try {
|
|
9139
|
-
return
|
|
9265
|
+
return import_node_fs23.default.statSync(import_node_path23.default.join(repoPath, ".git")).isDirectory();
|
|
9140
9266
|
} catch {
|
|
9141
9267
|
return false;
|
|
9142
9268
|
}
|
|
@@ -9186,8 +9312,8 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
9186
9312
|
const target = linked ? resolveLinkedTarget(repoPath) : resolvePrimaryTarget(repoPath);
|
|
9187
9313
|
let devpodId = target.devpodId;
|
|
9188
9314
|
if (target.kind === "linked") {
|
|
9189
|
-
const overlayPath =
|
|
9190
|
-
if (!
|
|
9315
|
+
const overlayPath = import_node_path23.default.join(repoPath, ".devcontainer", DEVCONTAINER_OVERLAY);
|
|
9316
|
+
if (!import_node_fs23.default.existsSync(overlayPath)) {
|
|
9191
9317
|
throw new Error(`Missing required DevPod compose overlay: ${overlayPath}`);
|
|
9192
9318
|
}
|
|
9193
9319
|
}
|
|
@@ -9254,7 +9380,7 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
9254
9380
|
}
|
|
9255
9381
|
const apps = proxyAppsFromConfig(runtime.config);
|
|
9256
9382
|
const parsedUpstreams = apps.map((app) => parseUpstream(app.upstream));
|
|
9257
|
-
const aliasPrefix = target.kind === "linked" ? target.workspace : wsFromBranch(runtime.config.project?.name ??
|
|
9383
|
+
const aliasPrefix = target.kind === "linked" ? target.workspace : wsFromBranch(runtime.config.project?.name ?? import_node_path23.default.basename(repoPath)) ?? "app";
|
|
9258
9384
|
for (const [index, app] of apps.entries()) {
|
|
9259
9385
|
if (!parsedUpstreams[index].host.startsWith(`${aliasPrefix}-`)) {
|
|
9260
9386
|
const owner = target.kind === "linked" ? "workspace" : "checkout";
|
|
@@ -9698,13 +9824,13 @@ async function workspaceEnsure(requestedRepoPath, options = {}) {
|
|
|
9698
9824
|
}
|
|
9699
9825
|
});
|
|
9700
9826
|
}
|
|
9701
|
-
var import_node_child_process19,
|
|
9827
|
+
var import_node_child_process19, import_node_fs23, import_node_path23, DEVCONTAINER_OVERLAY, DEFAULT_READINESS_TIMEOUT_MS, POLL_INTERVAL_MS;
|
|
9702
9828
|
var init_workspace_ensure = __esm({
|
|
9703
9829
|
"src/core/workspace-ensure.ts"() {
|
|
9704
9830
|
"use strict";
|
|
9705
9831
|
import_node_child_process19 = require("child_process");
|
|
9706
|
-
|
|
9707
|
-
|
|
9832
|
+
import_node_fs23 = __toESM(require("fs"));
|
|
9833
|
+
import_node_path23 = __toESM(require("path"));
|
|
9708
9834
|
init_devcontainer_profile();
|
|
9709
9835
|
init_devpod_environment();
|
|
9710
9836
|
init_devpod_mutation();
|
|
@@ -9784,7 +9910,7 @@ function warnMissingWorkspaceOwnership(repoPath) {
|
|
|
9784
9910
|
);
|
|
9785
9911
|
}
|
|
9786
9912
|
function defaultWorktreePath(mainRepo, ws) {
|
|
9787
|
-
return
|
|
9913
|
+
return import_node_path24.default.join(mainRepo, "trees", ws);
|
|
9788
9914
|
}
|
|
9789
9915
|
function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
9790
9916
|
const ignored = (0, import_node_child_process20.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
|
|
@@ -9792,12 +9918,12 @@ function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
|
9792
9918
|
});
|
|
9793
9919
|
if (ignored.status !== 0) {
|
|
9794
9920
|
throw new Error(
|
|
9795
|
-
`Default worktree root '${
|
|
9921
|
+
`Default worktree root '${import_node_path24.default.join(mainRepo, "trees")}' is not ignored. Add 'trees/' to '${import_node_path24.default.join(mainRepo, ".gitignore")}' or use --path.`
|
|
9796
9922
|
);
|
|
9797
9923
|
}
|
|
9798
9924
|
}
|
|
9799
9925
|
function legacyDefaultWorktreePath(mainRepo, ws) {
|
|
9800
|
-
return
|
|
9926
|
+
return import_node_path24.default.join(import_node_path24.default.dirname(mainRepo), `${import_node_path24.default.basename(mainRepo)}-${ws}`);
|
|
9801
9927
|
}
|
|
9802
9928
|
function teardownFallbackPath(mainRepo, workspace) {
|
|
9803
9929
|
const candidates = [
|
|
@@ -9909,7 +10035,7 @@ function assertFullDownPreflight(mainRepo, target) {
|
|
|
9909
10035
|
if (sameWorkspacePath(target.worktreePath, mainRepo)) {
|
|
9910
10036
|
throw new Error("Refusing to remove the primary Git checkout.");
|
|
9911
10037
|
}
|
|
9912
|
-
if (!target.worktree || target.worktree.prunable || !
|
|
10038
|
+
if (!target.worktree || target.worktree.prunable || !import_node_fs24.default.existsSync(target.worktreePath)) return;
|
|
9913
10039
|
if (target.worktree.locked) {
|
|
9914
10040
|
throw new Error(
|
|
9915
10041
|
`Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
|
|
@@ -9936,8 +10062,8 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
9936
10062
|
if (!ws) {
|
|
9937
10063
|
throw new Error(`Branch '${branch}' does not yield a valid workspace token.`);
|
|
9938
10064
|
}
|
|
9939
|
-
const worktreePath = opts.path ?
|
|
9940
|
-
if (
|
|
10065
|
+
const worktreePath = opts.path ? import_node_path24.default.resolve(opts.path) : defaultWorktreePath(mainRepo, ws);
|
|
10066
|
+
if (import_node_fs24.default.existsSync(worktreePath)) {
|
|
9941
10067
|
const registered = listGitWorktrees(mainRepo).find(
|
|
9942
10068
|
(worktree) => sameWorkspacePath(worktree.path, worktreePath)
|
|
9943
10069
|
);
|
|
@@ -10062,7 +10188,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
10062
10188
|
opts.quiet
|
|
10063
10189
|
);
|
|
10064
10190
|
if (removeWorktree) {
|
|
10065
|
-
if (resolved.worktree && !resolved.worktree.prunable &&
|
|
10191
|
+
if (resolved.worktree && !resolved.worktree.prunable && import_node_fs24.default.existsSync(resolved.worktreePath)) {
|
|
10066
10192
|
const rm = (0, import_node_child_process20.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
|
|
10067
10193
|
encoding: "utf-8"
|
|
10068
10194
|
});
|
|
@@ -10081,7 +10207,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
10081
10207
|
}
|
|
10082
10208
|
return result;
|
|
10083
10209
|
};
|
|
10084
|
-
return resolved.worktree && !resolved.worktree.prunable &&
|
|
10210
|
+
return resolved.worktree && !resolved.worktree.prunable && import_node_fs24.default.existsSync(resolved.worktreePath) ? withWorkspaceLifecycleLock(resolved.worktreePath, operation) : operation();
|
|
10085
10211
|
}
|
|
10086
10212
|
async function mutateWorkspaceOwnedPath(action2, worktreePath, opts = {}) {
|
|
10087
10213
|
const mainRepo = resolveRepoPath(opts.repoPath);
|
|
@@ -10116,13 +10242,13 @@ async function workspaceStop(target, opts = {}) {
|
|
|
10116
10242
|
async function workspaceDown(target, opts = {}) {
|
|
10117
10243
|
return runWorkspaceLifecycle("down", target, opts);
|
|
10118
10244
|
}
|
|
10119
|
-
var import_node_child_process20,
|
|
10245
|
+
var import_node_child_process20, import_node_fs24, import_node_path24;
|
|
10120
10246
|
var init_workspace_lifecycle = __esm({
|
|
10121
10247
|
"src/core/workspace-lifecycle.ts"() {
|
|
10122
10248
|
"use strict";
|
|
10123
10249
|
import_node_child_process20 = require("child_process");
|
|
10124
|
-
|
|
10125
|
-
|
|
10250
|
+
import_node_fs24 = __toESM(require("fs"));
|
|
10251
|
+
import_node_path24 = __toESM(require("path"));
|
|
10126
10252
|
init_devpod_mutation();
|
|
10127
10253
|
init_devpod_workspaces();
|
|
10128
10254
|
init_repo_config();
|
|
@@ -10690,17 +10816,17 @@ function asRecord3(value) {
|
|
|
10690
10816
|
return value;
|
|
10691
10817
|
}
|
|
10692
10818
|
function readJson(filePath) {
|
|
10693
|
-
if (!
|
|
10819
|
+
if (!import_node_fs25.default.existsSync(filePath)) {
|
|
10694
10820
|
return void 0;
|
|
10695
10821
|
}
|
|
10696
10822
|
try {
|
|
10697
|
-
return JSON.parse(
|
|
10823
|
+
return JSON.parse(import_node_fs25.default.readFileSync(filePath, "utf-8"));
|
|
10698
10824
|
} catch {
|
|
10699
10825
|
return void 0;
|
|
10700
10826
|
}
|
|
10701
10827
|
}
|
|
10702
10828
|
function relative(repoPath, filePath) {
|
|
10703
|
-
return
|
|
10829
|
+
return import_node_path25.default.relative(repoPath, filePath) || ".";
|
|
10704
10830
|
}
|
|
10705
10831
|
function redactEnvAssignments(value) {
|
|
10706
10832
|
return value.replace(
|
|
@@ -10741,7 +10867,7 @@ function inspectPackageManager(repoPath, pkg) {
|
|
|
10741
10867
|
["bun.lock", "bun"]
|
|
10742
10868
|
];
|
|
10743
10869
|
for (const [fileName, name] of lockfiles) {
|
|
10744
|
-
if (
|
|
10870
|
+
if (import_node_fs25.default.existsSync(import_node_path25.default.join(repoPath, fileName))) {
|
|
10745
10871
|
return { name, source: fileName };
|
|
10746
10872
|
}
|
|
10747
10873
|
}
|
|
@@ -10756,9 +10882,9 @@ function inspectNode(repoPath, pkg) {
|
|
|
10756
10882
|
if (typeof engines?.node === "string") {
|
|
10757
10883
|
return { version: engines.node, source: "package.json:engines.node" };
|
|
10758
10884
|
}
|
|
10759
|
-
const nvmrc =
|
|
10760
|
-
if (
|
|
10761
|
-
const version =
|
|
10885
|
+
const nvmrc = import_node_path25.default.join(repoPath, ".nvmrc");
|
|
10886
|
+
if (import_node_fs25.default.existsSync(nvmrc)) {
|
|
10887
|
+
const version = import_node_fs25.default.readFileSync(nvmrc, "utf-8").trim();
|
|
10762
10888
|
return { version, source: ".nvmrc" };
|
|
10763
10889
|
}
|
|
10764
10890
|
return void 0;
|
|
@@ -10819,7 +10945,7 @@ function configuredComposeFiles(repoPath) {
|
|
|
10819
10945
|
const files = config.apps.filter(
|
|
10820
10946
|
(app) => app.runtime === "docker"
|
|
10821
10947
|
).flatMap((app) => app.docker.composeFiles).filter(
|
|
10822
|
-
(fileName) => !
|
|
10948
|
+
(fileName) => !import_node_path25.default.isAbsolute(fileName) && !import_node_path25.default.normalize(fileName).startsWith("..")
|
|
10823
10949
|
);
|
|
10824
10950
|
return Array.from(new Set(files));
|
|
10825
10951
|
} catch {
|
|
@@ -10837,7 +10963,7 @@ function composeFiles(repoPath) {
|
|
|
10837
10963
|
...configuredComposeFiles(repoPath)
|
|
10838
10964
|
];
|
|
10839
10965
|
return Array.from(new Set(candidates)).filter(
|
|
10840
|
-
(fileName) =>
|
|
10966
|
+
(fileName) => import_node_fs25.default.existsSync(import_node_path25.default.join(repoPath, fileName))
|
|
10841
10967
|
);
|
|
10842
10968
|
}
|
|
10843
10969
|
function stringArray2(value) {
|
|
@@ -10875,7 +11001,7 @@ function inspectServices(repoPath) {
|
|
|
10875
11001
|
const services = [];
|
|
10876
11002
|
for (const fileName of composeFiles(repoPath)) {
|
|
10877
11003
|
try {
|
|
10878
|
-
const parsed = import_yaml6.default.parse(
|
|
11004
|
+
const parsed = import_yaml6.default.parse(import_node_fs25.default.readFileSync(import_node_path25.default.join(repoPath, fileName), "utf-8"));
|
|
10879
11005
|
const serviceMap = asRecord3(asRecord3(parsed)?.services);
|
|
10880
11006
|
for (const [name, value] of Object.entries(serviceMap ?? {})) {
|
|
10881
11007
|
const service = asRecord3(value);
|
|
@@ -10910,8 +11036,8 @@ function inspectServices(repoPath) {
|
|
|
10910
11036
|
return services;
|
|
10911
11037
|
}
|
|
10912
11038
|
function inspectEnvFiles(repoPath) {
|
|
10913
|
-
const files =
|
|
10914
|
-
const content =
|
|
11039
|
+
const files = import_node_fs25.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
|
|
11040
|
+
const content = import_node_fs25.default.readFileSync(import_node_path25.default.join(repoPath, fileName), "utf-8");
|
|
10915
11041
|
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();
|
|
10916
11042
|
return { path: fileName, names };
|
|
10917
11043
|
});
|
|
@@ -10925,18 +11051,18 @@ function inspectEnvFiles(repoPath) {
|
|
|
10925
11051
|
};
|
|
10926
11052
|
}
|
|
10927
11053
|
function inspectDevcontainer(repoPath) {
|
|
10928
|
-
const dir =
|
|
10929
|
-
if (!
|
|
11054
|
+
const dir = import_node_path25.default.join(repoPath, ".devcontainer");
|
|
11055
|
+
if (!import_node_fs25.default.existsSync(dir)) {
|
|
10930
11056
|
return { exists: false, files: [] };
|
|
10931
11057
|
}
|
|
10932
11058
|
return {
|
|
10933
11059
|
exists: true,
|
|
10934
|
-
files:
|
|
11060
|
+
files: import_node_fs25.default.readdirSync(dir).filter((fileName) => import_node_fs25.default.statSync(import_node_path25.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
|
|
10935
11061
|
};
|
|
10936
11062
|
}
|
|
10937
11063
|
function inspectDevrouter(repoPath) {
|
|
10938
11064
|
const configPath = getRepoConfigPath(repoPath);
|
|
10939
|
-
if (!
|
|
11065
|
+
if (!import_node_fs25.default.existsSync(configPath)) {
|
|
10940
11066
|
return {
|
|
10941
11067
|
exists: false,
|
|
10942
11068
|
configPath,
|
|
@@ -10980,15 +11106,15 @@ function inspectAgentGuidance(repoPath) {
|
|
|
10980
11106
|
["AGENTS.md", "agents"],
|
|
10981
11107
|
["CLAUDE.md", "claude"]
|
|
10982
11108
|
]) {
|
|
10983
|
-
if (
|
|
11109
|
+
if (import_node_fs25.default.existsSync(import_node_path25.default.join(repoPath, fileName))) {
|
|
10984
11110
|
results.push({ path: fileName, kind });
|
|
10985
11111
|
}
|
|
10986
11112
|
}
|
|
10987
|
-
const skillsDir =
|
|
10988
|
-
if (
|
|
10989
|
-
for (const name of
|
|
10990
|
-
const skillPath =
|
|
10991
|
-
if (
|
|
11113
|
+
const skillsDir = import_node_path25.default.join(repoPath, ".agents", "skills");
|
|
11114
|
+
if (import_node_fs25.default.existsSync(skillsDir)) {
|
|
11115
|
+
for (const name of import_node_fs25.default.readdirSync(skillsDir).sort()) {
|
|
11116
|
+
const skillPath = import_node_path25.default.join(skillsDir, name, "SKILL.md");
|
|
11117
|
+
if (import_node_fs25.default.existsSync(skillPath)) {
|
|
10992
11118
|
results.push({ path: relative(repoPath, skillPath), kind: "skill" });
|
|
10993
11119
|
}
|
|
10994
11120
|
}
|
|
@@ -11031,7 +11157,7 @@ function buildIssues(report) {
|
|
|
11031
11157
|
}
|
|
11032
11158
|
function inspectRepo(options = {}) {
|
|
11033
11159
|
const repoPath = resolveRepoPath(options.repo);
|
|
11034
|
-
const pkg = readJson(
|
|
11160
|
+
const pkg = readJson(import_node_path25.default.join(repoPath, "package.json"));
|
|
11035
11161
|
const scripts = inspectScripts(pkg);
|
|
11036
11162
|
const reportWithoutIssues = {
|
|
11037
11163
|
repoPath,
|
|
@@ -11050,12 +11176,12 @@ function inspectRepo(options = {}) {
|
|
|
11050
11176
|
issues: buildIssues(reportWithoutIssues)
|
|
11051
11177
|
};
|
|
11052
11178
|
}
|
|
11053
|
-
var
|
|
11179
|
+
var import_node_fs25, import_node_path25, import_yaml6;
|
|
11054
11180
|
var init_repo_inspect = __esm({
|
|
11055
11181
|
"src/core/repo-inspect.ts"() {
|
|
11056
11182
|
"use strict";
|
|
11057
|
-
|
|
11058
|
-
|
|
11183
|
+
import_node_fs25 = __toESM(require("fs"));
|
|
11184
|
+
import_node_path25 = __toESM(require("path"));
|
|
11059
11185
|
import_yaml6 = __toESM(require("yaml"));
|
|
11060
11186
|
init_repo_config();
|
|
11061
11187
|
}
|
|
@@ -11160,7 +11286,7 @@ function requiredFileChecks(repoPath) {
|
|
|
11160
11286
|
".devcontainer/docker-compose.yml",
|
|
11161
11287
|
".devrouter.yml"
|
|
11162
11288
|
];
|
|
11163
|
-
const missing = required.filter((fileName) => !
|
|
11289
|
+
const missing = required.filter((fileName) => !import_node_fs26.default.existsSync(import_node_path26.default.join(repoPath, fileName)));
|
|
11164
11290
|
return {
|
|
11165
11291
|
id: "repo.devcontainer.verify-files",
|
|
11166
11292
|
level: missing.length === 0 ? "ok" : "error",
|
|
@@ -11379,12 +11505,12 @@ async function verifyDevcontainer(options = {}) {
|
|
|
11379
11505
|
nextSteps: collectNextSteps3(checks)
|
|
11380
11506
|
};
|
|
11381
11507
|
}
|
|
11382
|
-
var
|
|
11508
|
+
var import_node_fs26, import_node_path26;
|
|
11383
11509
|
var init_devcontainer_verify = __esm({
|
|
11384
11510
|
"src/core/devcontainer-verify.ts"() {
|
|
11385
11511
|
"use strict";
|
|
11386
|
-
|
|
11387
|
-
|
|
11512
|
+
import_node_fs26 = __toESM(require("fs"));
|
|
11513
|
+
import_node_path26 = __toESM(require("path"));
|
|
11388
11514
|
init_capabilities();
|
|
11389
11515
|
init_doctor();
|
|
11390
11516
|
init_host_routes();
|
|
@@ -11530,6 +11656,7 @@ function renderDevcontainerJson(projectName) {
|
|
|
11530
11656
|
"service": "app",
|
|
11531
11657
|
"workspaceFolder": "/workspaces/${projectName}",
|
|
11532
11658
|
"postCreateCommand": "bash .devcontainer/post-create.sh",
|
|
11659
|
+
"waitFor": "postCreateCommand",
|
|
11533
11660
|
"customizations": {
|
|
11534
11661
|
"devrouter": {
|
|
11535
11662
|
"managed": "${MANAGED_MARKER2}"
|
|
@@ -11680,7 +11807,7 @@ function packageManagerIssues(repo) {
|
|
|
11680
11807
|
}
|
|
11681
11808
|
function plannedFiles(repoPath, version) {
|
|
11682
11809
|
const repo = inspectRepo({ repo: repoPath });
|
|
11683
|
-
const projectName = sanitizeProjectName(
|
|
11810
|
+
const projectName = sanitizeProjectName(import_node_path27.default.basename(repo.repoPath));
|
|
11684
11811
|
const nodeMajor = majorVersion(repo.node?.version, "24");
|
|
11685
11812
|
const pnpmVersion = repo.packageManager?.name === "pnpm" && repo.packageManager.version ? repo.packageManager.version : DEFAULT_PNPM_VERSION;
|
|
11686
11813
|
const port = inferPort2(repo);
|
|
@@ -11725,8 +11852,8 @@ function plannedFiles(repoPath, version) {
|
|
|
11725
11852
|
};
|
|
11726
11853
|
}
|
|
11727
11854
|
function classifyFile(repoPath, file) {
|
|
11728
|
-
const absolutePath =
|
|
11729
|
-
if (!
|
|
11855
|
+
const absolutePath = import_node_path27.default.join(repoPath, file.relativePath);
|
|
11856
|
+
if (!import_node_fs27.default.existsSync(absolutePath)) {
|
|
11730
11857
|
return {
|
|
11731
11858
|
path: file.relativePath,
|
|
11732
11859
|
action: "create",
|
|
@@ -11734,7 +11861,7 @@ function classifyFile(repoPath, file) {
|
|
|
11734
11861
|
bytes: Buffer.byteLength(file.content)
|
|
11735
11862
|
};
|
|
11736
11863
|
}
|
|
11737
|
-
const current =
|
|
11864
|
+
const current = import_node_fs27.default.readFileSync(absolutePath, "utf-8");
|
|
11738
11865
|
if (!current.includes(MANAGED_MARKER2)) {
|
|
11739
11866
|
return {
|
|
11740
11867
|
path: file.relativePath,
|
|
@@ -11791,11 +11918,11 @@ function buildPlan(repoPath, dryRun, version) {
|
|
|
11791
11918
|
};
|
|
11792
11919
|
}
|
|
11793
11920
|
function writeFile(repoPath, file) {
|
|
11794
|
-
const absolutePath =
|
|
11795
|
-
|
|
11796
|
-
|
|
11921
|
+
const absolutePath = import_node_path27.default.join(repoPath, file.relativePath);
|
|
11922
|
+
import_node_fs27.default.mkdirSync(import_node_path27.default.dirname(absolutePath), { recursive: true });
|
|
11923
|
+
import_node_fs27.default.writeFileSync(absolutePath, file.content, "utf-8");
|
|
11797
11924
|
if (file.executable) {
|
|
11798
|
-
|
|
11925
|
+
import_node_fs27.default.chmodSync(absolutePath, 493);
|
|
11799
11926
|
}
|
|
11800
11927
|
}
|
|
11801
11928
|
function writeDevcontainer(options = {}) {
|
|
@@ -11833,12 +11960,12 @@ function writeDevcontainer(options = {}) {
|
|
|
11833
11960
|
nextSteps: postWriteNextSteps(repoPath)
|
|
11834
11961
|
};
|
|
11835
11962
|
}
|
|
11836
|
-
var
|
|
11963
|
+
var import_node_fs27, import_node_path27, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
|
|
11837
11964
|
var init_devcontainer_write = __esm({
|
|
11838
11965
|
"src/core/devcontainer-write.ts"() {
|
|
11839
11966
|
"use strict";
|
|
11840
|
-
|
|
11841
|
-
|
|
11967
|
+
import_node_fs27 = __toESM(require("fs"));
|
|
11968
|
+
import_node_path27 = __toESM(require("path"));
|
|
11842
11969
|
init_repo_config();
|
|
11843
11970
|
init_repo_inspect();
|
|
11844
11971
|
MANAGED_MARKER2 = "devrouter:managed devcontainer";
|
|
@@ -12229,7 +12356,7 @@ function sanitizeRouterId(value) {
|
|
|
12229
12356
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
12230
12357
|
}
|
|
12231
12358
|
function repoHash(repoPath) {
|
|
12232
|
-
return (0, import_node_crypto8.createHash)("sha1").update(
|
|
12359
|
+
return (0, import_node_crypto8.createHash)("sha1").update(import_node_path28.default.resolve(repoPath)).digest("hex").slice(0, 12);
|
|
12233
12360
|
}
|
|
12234
12361
|
function asDockerApp(app) {
|
|
12235
12362
|
return app.runtime === "docker";
|
|
@@ -12308,11 +12435,11 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
|
|
|
12308
12435
|
if (dockerApps.length === 0) {
|
|
12309
12436
|
throw new Error("No docker apps selected to prepare compose overlay.");
|
|
12310
12437
|
}
|
|
12311
|
-
const cachePath =
|
|
12312
|
-
|
|
12313
|
-
const overlayPath =
|
|
12438
|
+
const cachePath = import_node_path28.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
|
|
12439
|
+
import_node_fs28.default.mkdirSync(cachePath, { recursive: true });
|
|
12440
|
+
const overlayPath = import_node_path28.default.join(cachePath, "compose.devrouter.yml");
|
|
12314
12441
|
const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
|
|
12315
|
-
|
|
12442
|
+
import_node_fs28.default.writeFileSync(overlayPath, import_yaml7.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
|
|
12316
12443
|
return {
|
|
12317
12444
|
overlayPath,
|
|
12318
12445
|
composeFiles: ensureComposeFiles(dockerApps),
|
|
@@ -12432,14 +12559,14 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
12432
12559
|
const port = Number(match[1]);
|
|
12433
12560
|
return Number.isInteger(port) && port > 0 ? port : void 0;
|
|
12434
12561
|
}
|
|
12435
|
-
var import_node_child_process25, import_node_crypto8,
|
|
12562
|
+
var import_node_child_process25, import_node_crypto8, import_node_fs28, import_node_path28, import_yaml7;
|
|
12436
12563
|
var init_docker_run = __esm({
|
|
12437
12564
|
"src/core/docker-run.ts"() {
|
|
12438
12565
|
"use strict";
|
|
12439
12566
|
import_node_child_process25 = require("child_process");
|
|
12440
12567
|
import_node_crypto8 = require("crypto");
|
|
12441
|
-
|
|
12442
|
-
|
|
12568
|
+
import_node_fs28 = __toESM(require("fs"));
|
|
12569
|
+
import_node_path28 = __toESM(require("path"));
|
|
12443
12570
|
import_yaml7 = __toESM(require("yaml"));
|
|
12444
12571
|
init_docker_error_guidance();
|
|
12445
12572
|
init_paths();
|
|
@@ -13261,13 +13388,13 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
13261
13388
|
const startedAt = Date.now();
|
|
13262
13389
|
let rootStat;
|
|
13263
13390
|
try {
|
|
13264
|
-
rootStat =
|
|
13391
|
+
rootStat = import_node_fs29.default.lstatSync(worktreePath);
|
|
13265
13392
|
} catch (error) {
|
|
13266
13393
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
13267
13394
|
}
|
|
13268
13395
|
let rootEntries;
|
|
13269
13396
|
try {
|
|
13270
|
-
rootEntries =
|
|
13397
|
+
rootEntries = import_node_fs29.default.readdirSync(worktreePath, { withFileTypes: true });
|
|
13271
13398
|
} catch (error) {
|
|
13272
13399
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
13273
13400
|
}
|
|
@@ -13293,10 +13420,10 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
13293
13420
|
timedOut = true;
|
|
13294
13421
|
break;
|
|
13295
13422
|
}
|
|
13296
|
-
const entryPath =
|
|
13423
|
+
const entryPath = import_node_path29.default.join(dirPath, entry.name);
|
|
13297
13424
|
let entryStat;
|
|
13298
13425
|
try {
|
|
13299
|
-
entryStat =
|
|
13426
|
+
entryStat = import_node_fs29.default.lstatSync(entryPath);
|
|
13300
13427
|
} catch (error) {
|
|
13301
13428
|
if (error?.code === "ENOENT") continue;
|
|
13302
13429
|
unreadableReason = describeIncompleteWalk(error);
|
|
@@ -13306,7 +13433,7 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
13306
13433
|
if (!entryStat.isDirectory()) continue;
|
|
13307
13434
|
let childEntries;
|
|
13308
13435
|
try {
|
|
13309
|
-
childEntries =
|
|
13436
|
+
childEntries = import_node_fs29.default.readdirSync(entryPath, { withFileTypes: true });
|
|
13310
13437
|
} catch (error) {
|
|
13311
13438
|
unreadableReason = describeIncompleteWalk(error);
|
|
13312
13439
|
break;
|
|
@@ -13370,12 +13497,12 @@ function describeError(error, worktreePath) {
|
|
|
13370
13497
|
}
|
|
13371
13498
|
return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
|
|
13372
13499
|
}
|
|
13373
|
-
var
|
|
13500
|
+
var import_node_fs29, import_node_path29, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
|
|
13374
13501
|
var init_workspace_consumption = __esm({
|
|
13375
13502
|
"src/core/workspace-consumption.ts"() {
|
|
13376
13503
|
"use strict";
|
|
13377
|
-
|
|
13378
|
-
|
|
13504
|
+
import_node_fs29 = __toESM(require("fs"));
|
|
13505
|
+
import_node_path29 = __toESM(require("path"));
|
|
13379
13506
|
init_devpod_environment();
|
|
13380
13507
|
DEFAULT_DEADLINE_MS = 1e4;
|
|
13381
13508
|
BLOCK_SIZE_BYTES = 512;
|
|
@@ -13476,7 +13603,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
|
|
|
13476
13603
|
}
|
|
13477
13604
|
function readGitSnapshot(worktree, commandRunner) {
|
|
13478
13605
|
const comparablePath = comparableWorkspacePath(worktree.path);
|
|
13479
|
-
if (worktree.prunable || !
|
|
13606
|
+
if (worktree.prunable || !import_node_fs30.default.existsSync(comparablePath)) {
|
|
13480
13607
|
return { worktree, checkout: "missing", head: null, committerDate: null };
|
|
13481
13608
|
}
|
|
13482
13609
|
const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
|
|
@@ -14001,7 +14128,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
14001
14128
|
containers = measureContainersFn(worktreePaths);
|
|
14002
14129
|
} catch (error) {
|
|
14003
14130
|
const reason = `container measurement failed: ${describeCause(error)}`;
|
|
14004
|
-
containers = new Map(worktreePaths.map((
|
|
14131
|
+
containers = new Map(worktreePaths.map((path29) => [path29, unknownContainers(reason)]));
|
|
14005
14132
|
}
|
|
14006
14133
|
}
|
|
14007
14134
|
const rows = records.map((record) => {
|
|
@@ -14050,12 +14177,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
14050
14177
|
workspaces: rows
|
|
14051
14178
|
};
|
|
14052
14179
|
}
|
|
14053
|
-
var import_node_child_process27,
|
|
14180
|
+
var import_node_child_process27, import_node_fs30, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
|
|
14054
14181
|
var init_workspace_cleanup = __esm({
|
|
14055
14182
|
"src/core/workspace-cleanup.ts"() {
|
|
14056
14183
|
"use strict";
|
|
14057
14184
|
import_node_child_process27 = require("child_process");
|
|
14058
|
-
|
|
14185
|
+
import_node_fs30 = __toESM(require("fs"));
|
|
14059
14186
|
init_devpod_workspaces();
|
|
14060
14187
|
init_host_routes();
|
|
14061
14188
|
init_repo_config();
|
|
@@ -14232,7 +14359,7 @@ var init_version = __esm({
|
|
|
14232
14359
|
|
|
14233
14360
|
// src/cli.ts
|
|
14234
14361
|
var import_commander = require("commander");
|
|
14235
|
-
var CLI_VERSION = true ? "0.0.
|
|
14362
|
+
var CLI_VERSION = true ? "0.0.44" : "0.0.0-dev";
|
|
14236
14363
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
14237
14364
|
function withErrorHandling(action2) {
|
|
14238
14365
|
return async (...args) => {
|