@devrouter/cli 0.0.68 → 0.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devrouter-lifecycle-worker.js +570 -178
- package/dist/devrouter.js +772 -330
- package/package.json +1 -1
- package/upgrade-prompts/0.0.69.md +41 -0
- package/upgrade-prompts/0.0.70.md +23 -0
package/dist/devrouter.js
CHANGED
|
@@ -4133,7 +4133,7 @@ function loadRepoConfig(repoPath, read2 = (file) => import_node_fs7.default.read
|
|
|
4133
4133
|
const config = parseConfig(parsed ?? {}, configPath);
|
|
4134
4134
|
const requiredVersion = config.devrouter?.version;
|
|
4135
4135
|
if (requiredVersion && !hasWarnedVersionMismatch) {
|
|
4136
|
-
const cliVersion = true ? "0.0.
|
|
4136
|
+
const cliVersion = true ? "0.0.70" : "0.0.0-dev";
|
|
4137
4137
|
if (cliVersion !== "0.0.0-dev" && compareSemver(requiredVersion, cliVersion) > 0) {
|
|
4138
4138
|
hasWarnedVersionMismatch = true;
|
|
4139
4139
|
process.stderr.write(
|
|
@@ -5386,7 +5386,7 @@ var init_reliability_contract = __esm({
|
|
|
5386
5386
|
|
|
5387
5387
|
// src/core/reliability-operation-store.ts
|
|
5388
5388
|
function installedCliVersion() {
|
|
5389
|
-
return true ? "0.0.
|
|
5389
|
+
return true ? "0.0.70" : "0.0.0-dev";
|
|
5390
5390
|
}
|
|
5391
5391
|
function compareCliVersions(left, right) {
|
|
5392
5392
|
const parts = (value) => value.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -10047,12 +10047,108 @@ function containerIdentity(container) {
|
|
|
10047
10047
|
mounts: container.mounts.map((mount) => JSON.stringify(mount)).sort()
|
|
10048
10048
|
});
|
|
10049
10049
|
}
|
|
10050
|
+
function stopInitialManagedDevsyWorkspace(repoPath, devsyId) {
|
|
10051
|
+
if (!import_node_fs23.default.existsSync(import_node_path31.default.join(repoPath, ".devrouter.yml"))) return false;
|
|
10052
|
+
const linked = isLinkedWorktree(repoPath);
|
|
10053
|
+
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
10054
|
+
const registration2 = () => {
|
|
10055
|
+
resetWorkspaceRuntimeCaches();
|
|
10056
|
+
if (resolveWorkspaceRuntimeOrDefault(repoPath) !== "devsy")
|
|
10057
|
+
throw new Error("Initial managed stop provider selection changed.");
|
|
10058
|
+
const owner2 = inspectDevsyWorkspaceOwnership(listDevsyWorkspaces(), devsyId, repoPath);
|
|
10059
|
+
if (owner2.status !== "owned")
|
|
10060
|
+
throw new Error("Initial managed stop requires one exact Devsy registration.");
|
|
10061
|
+
if (linked) {
|
|
10062
|
+
const record5 = workspace ? readWorkspaceOwnership(repoPath, workspace) : void 0;
|
|
10063
|
+
if (!record5 || record5.devpodId !== devsyId || !sameWorkspacePath(record5.worktreePath, repoPath) || resolveWorktreeWorkspace(repoPath) !== workspace)
|
|
10064
|
+
throw new Error("Initial managed stop workspace ownership changed.");
|
|
10065
|
+
}
|
|
10066
|
+
return owner2.workspace;
|
|
10067
|
+
};
|
|
10068
|
+
const runtime = loadRuntimeConfig(repoPath, workspace ?? "");
|
|
10069
|
+
if (!runtime.config.managedRuntime) return false;
|
|
10070
|
+
const owner = registration2();
|
|
10071
|
+
if (!owner.context || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(owner.context) || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(devsyId))
|
|
10072
|
+
throw new Error("Initial managed stop requires exact provider context.");
|
|
10073
|
+
const plan = inspectManagedDevcontainerConfig({
|
|
10074
|
+
repoPath,
|
|
10075
|
+
config: runtime.config,
|
|
10076
|
+
profile: runtime.resolvedProfile,
|
|
10077
|
+
linked
|
|
10078
|
+
});
|
|
10079
|
+
const providerRoot = import_node_path31.default.resolve(process.env.DEVSY_HOME || import_node_path31.default.join(import_node_os4.default.homedir(), ".devsy"));
|
|
10080
|
+
const featureDirectory = import_node_path31.default.join(
|
|
10081
|
+
providerRoot,
|
|
10082
|
+
"contexts",
|
|
10083
|
+
owner.context,
|
|
10084
|
+
"workspaces",
|
|
10085
|
+
devsyId,
|
|
10086
|
+
"agent",
|
|
10087
|
+
".docker-compose"
|
|
10088
|
+
);
|
|
10089
|
+
const observe = () => {
|
|
10090
|
+
if (!(0, import_node_util9.isDeepStrictEqual)(registration2(), owner) || readManagedRuntimeState(repoPath, workspace))
|
|
10091
|
+
throw new Error("Initial managed stop ownership or runtime state changed.");
|
|
10092
|
+
const matches = inspectWorkspaceContainers().filter(
|
|
10093
|
+
(c) => sameWorkspacePath(
|
|
10094
|
+
c.labels["com.docker.compose.project.working_dir"] ?? "",
|
|
10095
|
+
plan.composeDirectory
|
|
10096
|
+
)
|
|
10097
|
+
);
|
|
10098
|
+
if (!matches.length) return [];
|
|
10099
|
+
const projects = new Set(matches.map((c) => c.labels["com.docker.compose.project"]));
|
|
10100
|
+
if (projects.size !== 1 || !matches[0]?.labels["com.docker.compose.project"])
|
|
10101
|
+
throw new Error("Initial managed stop requires one exact Compose project.");
|
|
10102
|
+
const population = inspectManagedStopContainers(
|
|
10103
|
+
matches[0].labels["com.docker.compose.project"]
|
|
10104
|
+
);
|
|
10105
|
+
if (!sameSet(
|
|
10106
|
+
matches.map((c) => c.id),
|
|
10107
|
+
population.map((c) => c.id)
|
|
10108
|
+
))
|
|
10109
|
+
throw new Error("Initial managed stop population changed.");
|
|
10110
|
+
proveManagedComposePopulation({
|
|
10111
|
+
plan,
|
|
10112
|
+
repoPath,
|
|
10113
|
+
composeProject: matches[0].labels["com.docker.compose.project"],
|
|
10114
|
+
providerRoot,
|
|
10115
|
+
featureDirectory,
|
|
10116
|
+
containers: population
|
|
10117
|
+
});
|
|
10118
|
+
return population;
|
|
10119
|
+
};
|
|
10120
|
+
const initial = observe();
|
|
10121
|
+
const stable = () => {
|
|
10122
|
+
const current = observe();
|
|
10123
|
+
if (!sameSet(
|
|
10124
|
+
initial.map((c) => c.id),
|
|
10125
|
+
current.map((c) => c.id)
|
|
10126
|
+
) || current.some((c) => {
|
|
10127
|
+
const prior = initial.find((p) => p.id === c.id);
|
|
10128
|
+
return !prior || containerIdentity(prior) !== containerIdentity(c) || !prior.state.Running && c.state.Running;
|
|
10129
|
+
}))
|
|
10130
|
+
throw new Error("Initial managed stop population or identity changed.");
|
|
10131
|
+
return current;
|
|
10132
|
+
};
|
|
10133
|
+
stable();
|
|
10134
|
+
if (!initial.length) return false;
|
|
10135
|
+
for (const container of initial) {
|
|
10136
|
+
const current = stable().find((c) => c.id === container.id);
|
|
10137
|
+
if (current?.state.Running)
|
|
10138
|
+
stopExactManagedService(current.id, current.labels["com.docker.compose.service"] ?? "", {
|
|
10139
|
+
timeoutMs: 3e4
|
|
10140
|
+
});
|
|
10141
|
+
}
|
|
10142
|
+
if (stable().some((c) => c.state.Running))
|
|
10143
|
+
throw new Error("Initial managed stop left a workload running.");
|
|
10144
|
+
return true;
|
|
10145
|
+
}
|
|
10050
10146
|
function stopRetainedManagedDevsyWorkspace(options) {
|
|
10051
10147
|
const { repoPath, devsyId } = options;
|
|
10052
10148
|
const linked = isLinkedWorktree(repoPath);
|
|
10053
10149
|
const workspace = linked ? resolveWorktreeWorkspace(repoPath) : void 0;
|
|
10054
10150
|
const retainedState = readManagedRuntimeState(repoPath, workspace);
|
|
10055
|
-
if (!retainedState) return
|
|
10151
|
+
if (!retainedState) return stopInitialManagedDevsyWorkspace(repoPath, devsyId);
|
|
10056
10152
|
const state = retainedState;
|
|
10057
10153
|
if (state.devpodId !== devsyId || linked && !workspace) {
|
|
10058
10154
|
throw new Error("Managed stop requires the exact retained workspace identity.");
|
|
@@ -10247,10 +10343,11 @@ function stopRetainedManagedDevsyWorkspace(options) {
|
|
|
10247
10343
|
if (providerFailed) throw providerError;
|
|
10248
10344
|
return true;
|
|
10249
10345
|
}
|
|
10250
|
-
var import_node_os4, import_node_path31, import_node_util9;
|
|
10346
|
+
var import_node_fs23, import_node_os4, import_node_path31, import_node_util9;
|
|
10251
10347
|
var init_managed_devsy_stop = __esm({
|
|
10252
10348
|
"src/core/managed-devsy-stop.ts"() {
|
|
10253
10349
|
"use strict";
|
|
10350
|
+
import_node_fs23 = __toESM(require("fs"));
|
|
10254
10351
|
import_node_os4 = __toESM(require("os"));
|
|
10255
10352
|
import_node_path31 = __toESM(require("path"));
|
|
10256
10353
|
import_node_util9 = require("util");
|
|
@@ -10808,7 +10905,7 @@ function hasControlCharacter2(value) {
|
|
|
10808
10905
|
function readPolicyFile(filePath) {
|
|
10809
10906
|
let initialStats;
|
|
10810
10907
|
try {
|
|
10811
|
-
initialStats =
|
|
10908
|
+
initialStats = import_node_fs24.default.lstatSync(filePath);
|
|
10812
10909
|
} catch (error) {
|
|
10813
10910
|
if (isErrno(error, "ENOENT")) return { status: "absent" };
|
|
10814
10911
|
return { status: "invalid", error: "network policy could not be read" };
|
|
@@ -10821,16 +10918,16 @@ function readPolicyFile(filePath) {
|
|
|
10821
10918
|
}
|
|
10822
10919
|
let fileDescriptor;
|
|
10823
10920
|
try {
|
|
10824
|
-
fileDescriptor =
|
|
10921
|
+
fileDescriptor = import_node_fs24.default.openSync(
|
|
10825
10922
|
filePath,
|
|
10826
|
-
|
|
10923
|
+
import_node_fs24.default.constants.O_RDONLY | import_node_fs24.default.constants.O_NONBLOCK | (import_node_fs24.default.constants.O_NOFOLLOW ?? 0)
|
|
10827
10924
|
);
|
|
10828
10925
|
} catch (error) {
|
|
10829
10926
|
if (isErrno(error, "ENOENT")) return { status: "absent" };
|
|
10830
10927
|
return { status: "invalid", error: "network policy could not be read" };
|
|
10831
10928
|
}
|
|
10832
10929
|
try {
|
|
10833
|
-
const openedStats =
|
|
10930
|
+
const openedStats = import_node_fs24.default.fstatSync(fileDescriptor);
|
|
10834
10931
|
if (!openedStats.isFile()) {
|
|
10835
10932
|
return { status: "invalid", error: "network policy must be a regular file" };
|
|
10836
10933
|
}
|
|
@@ -10843,12 +10940,12 @@ function readPolicyFile(filePath) {
|
|
|
10843
10940
|
const buffer = Buffer.alloc(openedStats.size);
|
|
10844
10941
|
let offset = 0;
|
|
10845
10942
|
while (offset < buffer.length) {
|
|
10846
|
-
const bytesRead =
|
|
10943
|
+
const bytesRead = import_node_fs24.default.readSync(fileDescriptor, buffer, offset, buffer.length - offset, null);
|
|
10847
10944
|
if (bytesRead === 0)
|
|
10848
10945
|
return { status: "invalid", error: "network policy changed during read" };
|
|
10849
10946
|
offset += bytesRead;
|
|
10850
10947
|
}
|
|
10851
|
-
const finalStats =
|
|
10948
|
+
const finalStats = import_node_fs24.default.fstatSync(fileDescriptor);
|
|
10852
10949
|
if (!finalStats.isFile() || finalStats.size !== openedStats.size) {
|
|
10853
10950
|
return { status: "invalid", error: "network policy changed during read" };
|
|
10854
10951
|
}
|
|
@@ -10857,7 +10954,7 @@ function readPolicyFile(filePath) {
|
|
|
10857
10954
|
return { status: "invalid", error: "network policy could not be read" };
|
|
10858
10955
|
} finally {
|
|
10859
10956
|
try {
|
|
10860
|
-
|
|
10957
|
+
import_node_fs24.default.closeSync(fileDescriptor);
|
|
10861
10958
|
} catch {
|
|
10862
10959
|
}
|
|
10863
10960
|
}
|
|
@@ -10874,11 +10971,11 @@ function assertDisjoint(cidrs, message) {
|
|
|
10874
10971
|
function isErrno(error, code) {
|
|
10875
10972
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
10876
10973
|
}
|
|
10877
|
-
var
|
|
10974
|
+
var import_node_fs24, import_node_path32, NETWORK_POLICY_VERSION, NETWORK_POLICY_FILE, DEFAULT_NETWORK_POLICY_ENDPOINT_RESERVE, NETWORK_POLICY_PREFIX_LENGTHS, MAX_NETWORK_POLICY_FILE_BYTES, MAX_NETWORK_POLICY_ARRAY_ENTRIES, MAX_NETWORK_POLICY_DAEMON_ID_LENGTH, NetworkPolicyValidationError, NETWORK_POLICY_KEYS, PRIVATE_IPV4_RANGES;
|
|
10878
10975
|
var init_network_policy = __esm({
|
|
10879
10976
|
"src/core/network-policy.ts"() {
|
|
10880
10977
|
"use strict";
|
|
10881
|
-
|
|
10978
|
+
import_node_fs24 = __toESM(require("fs"));
|
|
10882
10979
|
import_node_path32 = __toESM(require("path"));
|
|
10883
10980
|
init_network_capacity();
|
|
10884
10981
|
init_router();
|
|
@@ -10984,27 +11081,27 @@ function assertExactKeys(value, keys2) {
|
|
|
10984
11081
|
function readDocument(store) {
|
|
10985
11082
|
let descriptor;
|
|
10986
11083
|
try {
|
|
10987
|
-
descriptor =
|
|
11084
|
+
descriptor = import_node_fs25.default.openSync(
|
|
10988
11085
|
store.filePath,
|
|
10989
|
-
|
|
11086
|
+
import_node_fs25.default.constants.O_RDONLY | import_node_fs25.default.constants.O_NONBLOCK | (import_node_fs25.default.constants.O_NOFOLLOW ?? 0)
|
|
10990
11087
|
);
|
|
10991
11088
|
} catch (error) {
|
|
10992
11089
|
if (isErrno2(error, "ENOENT")) return void 0;
|
|
10993
11090
|
throw claimError("invalid-state", "network claim state could not be read");
|
|
10994
11091
|
}
|
|
10995
11092
|
try {
|
|
10996
|
-
const stat =
|
|
11093
|
+
const stat = import_node_fs25.default.fstatSync(descriptor);
|
|
10997
11094
|
if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES || (stat.mode & 63) !== 0) {
|
|
10998
11095
|
throw claimError("invalid-state", "network claim state is not a bounded private file");
|
|
10999
11096
|
}
|
|
11000
11097
|
const buffer = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
|
|
11001
11098
|
let count = 0;
|
|
11002
11099
|
while (count < buffer.length) {
|
|
11003
|
-
const bytesRead =
|
|
11100
|
+
const bytesRead = import_node_fs25.default.readSync(descriptor, buffer, count, buffer.length - count, null);
|
|
11004
11101
|
if (bytesRead === 0) break;
|
|
11005
11102
|
count += bytesRead;
|
|
11006
11103
|
}
|
|
11007
|
-
const finalStat =
|
|
11104
|
+
const finalStat = import_node_fs25.default.fstatSync(descriptor);
|
|
11008
11105
|
if (count > NETWORK_CLAIMS_MAX_FILE_BYTES || finalStat.size !== stat.size) {
|
|
11009
11106
|
throw claimError("invalid-state", "network claim state changed during read");
|
|
11010
11107
|
}
|
|
@@ -11016,7 +11113,7 @@ function readDocument(store) {
|
|
|
11016
11113
|
}
|
|
11017
11114
|
return validateDocument(parsed, store.daemonId);
|
|
11018
11115
|
} finally {
|
|
11019
|
-
|
|
11116
|
+
import_node_fs25.default.closeSync(descriptor);
|
|
11020
11117
|
}
|
|
11021
11118
|
}
|
|
11022
11119
|
function validateDocument(value, daemonId) {
|
|
@@ -11120,12 +11217,12 @@ function isErrno2(error, code) {
|
|
|
11120
11217
|
function claimError(code, message) {
|
|
11121
11218
|
return new NetworkClaimError(code, message);
|
|
11122
11219
|
}
|
|
11123
|
-
var import_node_crypto13,
|
|
11220
|
+
var import_node_crypto13, import_node_fs25, import_node_path33, NETWORK_CLAIMS_VERSION, NETWORK_CLAIMS_ROOT, NETWORK_CLAIMS_MAX_RECORDS, NETWORK_CLAIMS_MAX_FILE_BYTES, NetworkClaimError, CLAIM_CORE_KEYS, CLAIM_KEYS, FENCE_KEYS, DOCUMENT_KEYS, MAX_CLAIM_TEXT, MAX_ENDPOINT_TEXT;
|
|
11124
11221
|
var init_network_claims = __esm({
|
|
11125
11222
|
"src/core/network-claims.ts"() {
|
|
11126
11223
|
"use strict";
|
|
11127
11224
|
import_node_crypto13 = require("crypto");
|
|
11128
|
-
|
|
11225
|
+
import_node_fs25 = __toESM(require("fs"));
|
|
11129
11226
|
import_node_path33 = __toESM(require("path"));
|
|
11130
11227
|
init_atomic_file();
|
|
11131
11228
|
init_file_lock();
|
|
@@ -11182,7 +11279,7 @@ function networkOwnerKey(repoPath) {
|
|
|
11182
11279
|
function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
11183
11280
|
let names;
|
|
11184
11281
|
try {
|
|
11185
|
-
names =
|
|
11282
|
+
names = import_node_fs26.default.readdirSync(root);
|
|
11186
11283
|
} catch (error) {
|
|
11187
11284
|
if (error.code === "ENOENT") return void 0;
|
|
11188
11285
|
throw new Error("Network claims cannot be inspected; repair is required.");
|
|
@@ -11192,20 +11289,20 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11192
11289
|
let found;
|
|
11193
11290
|
for (const file of files) {
|
|
11194
11291
|
const full = import_node_path34.default.join(root, file);
|
|
11195
|
-
const stat =
|
|
11292
|
+
const stat = import_node_fs26.default.lstatSync(full);
|
|
11196
11293
|
if (!stat.isFile() || stat.size > NETWORK_CLAIMS_MAX_FILE_BYTES)
|
|
11197
11294
|
throw new Error("Invalid retained network claim file.");
|
|
11198
11295
|
let value;
|
|
11199
|
-
const descriptor =
|
|
11296
|
+
const descriptor = import_node_fs26.default.openSync(
|
|
11200
11297
|
full,
|
|
11201
|
-
|
|
11298
|
+
import_node_fs26.default.constants.O_RDONLY | import_node_fs26.default.constants.O_NONBLOCK | import_node_fs26.default.constants.O_NOFOLLOW
|
|
11202
11299
|
);
|
|
11203
11300
|
try {
|
|
11204
|
-
if (!
|
|
11301
|
+
if (!import_node_fs26.default.fstatSync(descriptor).isFile()) throw new Error();
|
|
11205
11302
|
const bytes2 = Buffer.alloc(NETWORK_CLAIMS_MAX_FILE_BYTES + 1);
|
|
11206
11303
|
let size = 0;
|
|
11207
11304
|
while (size < bytes2.length) {
|
|
11208
|
-
const count =
|
|
11305
|
+
const count = import_node_fs26.default.readSync(descriptor, bytes2, size, bytes2.length - size, null);
|
|
11209
11306
|
if (!count) break;
|
|
11210
11307
|
size += count;
|
|
11211
11308
|
}
|
|
@@ -11215,7 +11312,7 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11215
11312
|
} catch {
|
|
11216
11313
|
throw new Error("Retained network claim could not be read safely.");
|
|
11217
11314
|
} finally {
|
|
11218
|
-
|
|
11315
|
+
import_node_fs26.default.closeSync(descriptor);
|
|
11219
11316
|
}
|
|
11220
11317
|
if (typeof value.daemonId !== "string" || `${(0, import_node_crypto14.createHash)("sha256").update(value.daemonId).digest("hex")}.json` !== file)
|
|
11221
11318
|
throw new Error("Retained network daemon identity is invalid.");
|
|
@@ -11231,12 +11328,12 @@ function findOwnedNetworkClaim(repoPath, root = NETWORK_CLAIMS_ROOT) {
|
|
|
11231
11328
|
}
|
|
11232
11329
|
return found;
|
|
11233
11330
|
}
|
|
11234
|
-
var import_node_crypto14,
|
|
11331
|
+
var import_node_crypto14, import_node_fs26, import_node_path34;
|
|
11235
11332
|
var init_network_claim_lookup = __esm({
|
|
11236
11333
|
"src/core/network-claim-lookup.ts"() {
|
|
11237
11334
|
"use strict";
|
|
11238
11335
|
import_node_crypto14 = require("crypto");
|
|
11239
|
-
|
|
11336
|
+
import_node_fs26 = __toESM(require("fs"));
|
|
11240
11337
|
import_node_path34 = __toESM(require("path"));
|
|
11241
11338
|
init_network_claims();
|
|
11242
11339
|
}
|
|
@@ -11396,7 +11493,7 @@ var init_network_provider_effect = __esm({
|
|
|
11396
11493
|
|
|
11397
11494
|
// src/core/devsy-mutation.ts
|
|
11398
11495
|
function withMutationLock(activity, target, operation) {
|
|
11399
|
-
|
|
11496
|
+
import_node_fs27.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
11400
11497
|
return withFileLockSync(
|
|
11401
11498
|
DEVSY_MUTATION_LOCK_FILE,
|
|
11402
11499
|
{
|
|
@@ -11477,12 +11574,12 @@ function stopOwnedDevsyWorkspace(devsyId, worktreePath) {
|
|
|
11477
11574
|
function deleteOwnedDevsyWorkspace(devsyId, worktreePath) {
|
|
11478
11575
|
return mutateOwnedDevsyWorkspace("delete", devsyId, worktreePath);
|
|
11479
11576
|
}
|
|
11480
|
-
var import_node_child_process16,
|
|
11577
|
+
var import_node_child_process16, import_node_fs27, import_node_path35, DEVSY_MUTATION_LOCK_FILE, DEVSY_MUTATION_WAIT_MS;
|
|
11481
11578
|
var init_devsy_mutation = __esm({
|
|
11482
11579
|
"src/core/devsy-mutation.ts"() {
|
|
11483
11580
|
"use strict";
|
|
11484
11581
|
import_node_child_process16 = require("child_process");
|
|
11485
|
-
|
|
11582
|
+
import_node_fs27 = __toESM(require("fs"));
|
|
11486
11583
|
import_node_path35 = __toESM(require("path"));
|
|
11487
11584
|
init_devsy_agent();
|
|
11488
11585
|
init_devsy_workspaces();
|
|
@@ -12213,7 +12310,7 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
|
|
|
12213
12310
|
if (process.platform === "win32")
|
|
12214
12311
|
throw new Error("Lifecycle workers require POSIX process-group ownership.");
|
|
12215
12312
|
const workerPath = import_node_path36.default.join(__dirname, "devrouter-lifecycle-worker.js");
|
|
12216
|
-
if (!
|
|
12313
|
+
if (!import_node_fs28.default.existsSync(workerPath))
|
|
12217
12314
|
throw new Error("The packaged lifecycle worker is missing; rebuild the CLI.");
|
|
12218
12315
|
const child = (0, import_node_child_process17.fork)(workerPath, [], {
|
|
12219
12316
|
detached: true,
|
|
@@ -12442,13 +12539,13 @@ async function runLifecycleWorker(request2, supervision, beforeAdmission) {
|
|
|
12442
12539
|
function newLifecycleIds() {
|
|
12443
12540
|
return { requestId: (0, import_node_crypto16.randomUUID)(), operationId: (0, import_node_crypto16.randomUUID)(), workerId: (0, import_node_crypto16.randomUUID)() };
|
|
12444
12541
|
}
|
|
12445
|
-
var import_node_child_process17, import_node_crypto16,
|
|
12542
|
+
var import_node_child_process17, import_node_crypto16, import_node_fs28, import_node_path36, OUTPUT_BUFFER_LIMIT, DEFAULT_OUTPUT_PAGE_BYTES, LifecycleOutput, LifecycleWorkerAdmissionBusyError;
|
|
12446
12543
|
var init_reliability_worker = __esm({
|
|
12447
12544
|
"src/core/reliability-worker.ts"() {
|
|
12448
12545
|
"use strict";
|
|
12449
12546
|
import_node_child_process17 = require("child_process");
|
|
12450
12547
|
import_node_crypto16 = require("crypto");
|
|
12451
|
-
|
|
12548
|
+
import_node_fs28 = __toESM(require("fs"));
|
|
12452
12549
|
import_node_path36 = __toESM(require("path"));
|
|
12453
12550
|
init_file_lock();
|
|
12454
12551
|
init_reliability_contract();
|
|
@@ -12676,7 +12773,7 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
|
|
|
12676
12773
|
throw new Error(
|
|
12677
12774
|
"Traefik routes did not reload; shared router restart is disabled for this operation."
|
|
12678
12775
|
);
|
|
12679
|
-
|
|
12776
|
+
import_node_fs29.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
12680
12777
|
return withFileLock(
|
|
12681
12778
|
ROUTER_RELOAD_LOCK_FILE,
|
|
12682
12779
|
{
|
|
@@ -12707,12 +12804,12 @@ async function ensureTraefikRouteExpectation(routes, expectation, options) {
|
|
|
12707
12804
|
async function ensureTraefikRoutesRemoved(routes, options = {}) {
|
|
12708
12805
|
return ensureTraefikRouteExpectation(routes, "removed", options);
|
|
12709
12806
|
}
|
|
12710
|
-
var import_node_child_process18,
|
|
12807
|
+
var import_node_child_process18, import_node_fs29, import_node_path37, ROUTER_API_BASE, ROUTER_RELOAD_LOCK_FILE, ROUTER_API_REQUEST_TIMEOUT_SECONDS, DEFAULT_INITIAL_TIMEOUT_MS, DEFAULT_RECOVERY_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS, ROUTER_API_PAGE_SIZE;
|
|
12711
12808
|
var init_traefik_route_health = __esm({
|
|
12712
12809
|
"src/core/traefik-route-health.ts"() {
|
|
12713
12810
|
"use strict";
|
|
12714
12811
|
import_node_child_process18 = require("child_process");
|
|
12715
|
-
|
|
12812
|
+
import_node_fs29 = __toESM(require("fs"));
|
|
12716
12813
|
import_node_path37 = __toESM(require("path"));
|
|
12717
12814
|
init_file_lock();
|
|
12718
12815
|
init_host_routes();
|
|
@@ -12730,7 +12827,7 @@ var init_traefik_route_health = __esm({
|
|
|
12730
12827
|
|
|
12731
12828
|
// src/core/devpod-mutation.ts
|
|
12732
12829
|
function withMutationLock2(activity, target, operation) {
|
|
12733
|
-
|
|
12830
|
+
import_node_fs30.default.mkdirSync(DEVROUTER_HOME, { recursive: true });
|
|
12734
12831
|
return withFileLockSync(
|
|
12735
12832
|
DEVPOD_MUTATION_LOCK_FILE,
|
|
12736
12833
|
{
|
|
@@ -12820,12 +12917,12 @@ function deleteOwnedDevpodWorkspace(devpodId, worktreePath) {
|
|
|
12820
12917
|
resetWorkspaceRuntimeCaches();
|
|
12821
12918
|
return result;
|
|
12822
12919
|
}
|
|
12823
|
-
var import_node_child_process19,
|
|
12920
|
+
var import_node_child_process19, import_node_fs30, import_node_path38, DEVPOD_MUTATION_LOCK_FILE, DEVPOD_MUTATION_WAIT_MS;
|
|
12824
12921
|
var init_devpod_mutation = __esm({
|
|
12825
12922
|
"src/core/devpod-mutation.ts"() {
|
|
12826
12923
|
"use strict";
|
|
12827
12924
|
import_node_child_process19 = require("child_process");
|
|
12828
|
-
|
|
12925
|
+
import_node_fs30 = __toESM(require("fs"));
|
|
12829
12926
|
import_node_path38 = __toESM(require("path"));
|
|
12830
12927
|
init_devpod_workspaces();
|
|
12831
12928
|
init_devsy_mutation();
|
|
@@ -12840,6 +12937,282 @@ var init_devpod_mutation = __esm({
|
|
|
12840
12937
|
}
|
|
12841
12938
|
});
|
|
12842
12939
|
|
|
12940
|
+
// src/core/host-port-claims.ts
|
|
12941
|
+
function isRecord6(value) {
|
|
12942
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12943
|
+
}
|
|
12944
|
+
function optionalLabel(labels, key) {
|
|
12945
|
+
const value = isRecord6(labels) ? labels[key] : void 0;
|
|
12946
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
12947
|
+
}
|
|
12948
|
+
function normalizeHostIp(value) {
|
|
12949
|
+
if (typeof value !== "string") return null;
|
|
12950
|
+
const trimmed = value.trim();
|
|
12951
|
+
if (trimmed.length === 0 || trimmed === "0.0.0.0" || trimmed === "::") return null;
|
|
12952
|
+
return trimmed;
|
|
12953
|
+
}
|
|
12954
|
+
function parseSinglePortValue(value, service, label2) {
|
|
12955
|
+
if (typeof value === "number") {
|
|
12956
|
+
if (!Number.isInteger(value) || value <= 0 || value > 65535) {
|
|
12957
|
+
throw new Error(`Service '${service}' has an out-of-range ${label2} port.`);
|
|
12958
|
+
}
|
|
12959
|
+
return value;
|
|
12960
|
+
}
|
|
12961
|
+
if (typeof value === "string") {
|
|
12962
|
+
const parsed = Number(value);
|
|
12963
|
+
if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
|
|
12964
|
+
throw new Error(`Service '${service}' has an out-of-range ${label2} port '${value}'.`);
|
|
12965
|
+
}
|
|
12966
|
+
return parsed;
|
|
12967
|
+
}
|
|
12968
|
+
throw new Error(`Service '${service}' has an unsupported ${label2} port value.`);
|
|
12969
|
+
}
|
|
12970
|
+
function parseFixedPublishedPort(value, service) {
|
|
12971
|
+
if (value === void 0 || value === null || value === "" || value === 0 || value === "0") {
|
|
12972
|
+
return null;
|
|
12973
|
+
}
|
|
12974
|
+
if (typeof value === "string" && value.includes("-")) {
|
|
12975
|
+
return null;
|
|
12976
|
+
}
|
|
12977
|
+
return parseSinglePortValue(value, service, "published host");
|
|
12978
|
+
}
|
|
12979
|
+
function parsePublishedRange(value, service) {
|
|
12980
|
+
const match = /^(\d+)-(\d+)$/.exec(value.trim());
|
|
12981
|
+
if (!match) return void 0;
|
|
12982
|
+
const lower = Number(match[1]);
|
|
12983
|
+
const upper = Number(match[2]);
|
|
12984
|
+
if (!Number.isInteger(lower) || !Number.isInteger(upper) || lower <= 0 || upper < lower || upper > 65535) {
|
|
12985
|
+
throw new Error(`Service '${service}' publishes an invalid host port range '${value}'.`);
|
|
12986
|
+
}
|
|
12987
|
+
return [lower, upper];
|
|
12988
|
+
}
|
|
12989
|
+
function resolveFixedPublishedHostPorts(renderedConfig) {
|
|
12990
|
+
const services = isRecord6(renderedConfig) ? renderedConfig.services : void 0;
|
|
12991
|
+
const bindings = [];
|
|
12992
|
+
const pushBinding = (binding) => {
|
|
12993
|
+
bindings.push(binding);
|
|
12994
|
+
if (bindings.length > MAX_DESIRED_HOST_PORTS) {
|
|
12995
|
+
throw new Error(
|
|
12996
|
+
`The managed Compose model declares more than ${MAX_DESIRED_HOST_PORTS} fixed host ports.`
|
|
12997
|
+
);
|
|
12998
|
+
}
|
|
12999
|
+
};
|
|
13000
|
+
for (const [service, serviceValue] of Object.entries(isRecord6(services) ? services : {})) {
|
|
13001
|
+
const serviceRecord = isRecord6(serviceValue) ? serviceValue : {};
|
|
13002
|
+
if (!Array.isArray(serviceRecord.ports)) continue;
|
|
13003
|
+
for (const port of serviceRecord.ports) {
|
|
13004
|
+
if (!isRecord6(port)) {
|
|
13005
|
+
throw new Error(`Service '${service}' declares an unsupported port entry.`);
|
|
13006
|
+
}
|
|
13007
|
+
parseSinglePortValue(port.target, service, "container target");
|
|
13008
|
+
const hostIp = normalizeHostIp(port.host_ip);
|
|
13009
|
+
const protocol = typeof port.protocol === "string" && port.protocol ? port.protocol : "tcp";
|
|
13010
|
+
if (typeof port.published !== "string" || !port.published.includes("-")) {
|
|
13011
|
+
const hostPort = parseFixedPublishedPort(port.published, service);
|
|
13012
|
+
if (hostPort === null) continue;
|
|
13013
|
+
pushBinding({ service, hostIp, hostPort, protocol });
|
|
13014
|
+
continue;
|
|
13015
|
+
}
|
|
13016
|
+
const range = parsePublishedRange(port.published, service);
|
|
13017
|
+
if (!range) {
|
|
13018
|
+
throw new Error(
|
|
13019
|
+
`Service '${service}' publishes an unsupported host port value '${String(port.published)}'.`
|
|
13020
|
+
);
|
|
13021
|
+
}
|
|
13022
|
+
for (let hostPort = range[0]; hostPort <= range[1]; hostPort += 1) {
|
|
13023
|
+
pushBinding({ service, hostIp, hostPort, protocol });
|
|
13024
|
+
}
|
|
13025
|
+
}
|
|
13026
|
+
}
|
|
13027
|
+
return bindings;
|
|
13028
|
+
}
|
|
13029
|
+
function hostPortBindingsConflict(desired, holder) {
|
|
13030
|
+
return desired.hostPort === holder.hostPort && desired.protocol === holder.protocol && // Wildcards collide with everything in their protocol family; specific
|
|
13031
|
+
// addresses only collide with themselves. IPv6 nuance intentionally
|
|
13032
|
+
// over-approximates: a loud refusal beats a missed kernel collision.
|
|
13033
|
+
(desired.hostIp === null || holder.hostIp === null || desired.hostIp === holder.hostIp);
|
|
13034
|
+
}
|
|
13035
|
+
function runDocker(args) {
|
|
13036
|
+
const result = (0, import_node_child_process20.spawnSync)("docker", args, {
|
|
13037
|
+
...networkDockerOptions(),
|
|
13038
|
+
encoding: "utf-8",
|
|
13039
|
+
timeout: DOCKER_TIMEOUT_MS,
|
|
13040
|
+
maxBuffer: DOCKER_MAX_BUFFER
|
|
13041
|
+
});
|
|
13042
|
+
if (result.error || result.status !== 0) {
|
|
13043
|
+
const reason = (result.stderr || result.stdout || result.error?.message || "unknown error").toString().trim();
|
|
13044
|
+
throw new Error(`docker ${String(args[0])} failed: ${reason}`);
|
|
13045
|
+
}
|
|
13046
|
+
if (typeof result.stdout !== "string") {
|
|
13047
|
+
throw new Error(`docker ${String(args[0])} returned invalid output.`);
|
|
13048
|
+
}
|
|
13049
|
+
return result.stdout;
|
|
13050
|
+
}
|
|
13051
|
+
function parseHolderPortMap(value) {
|
|
13052
|
+
const ports = [];
|
|
13053
|
+
for (const [key, entries] of Object.entries(isRecord6(value) ? value : {})) {
|
|
13054
|
+
const separator = key.lastIndexOf("/");
|
|
13055
|
+
if (separator <= 0) continue;
|
|
13056
|
+
const protocol = key.slice(separator + 1);
|
|
13057
|
+
if (!Array.isArray(entries)) continue;
|
|
13058
|
+
for (const entry of entries) {
|
|
13059
|
+
if (!isRecord6(entry) || entry.HostPort === void 0 || entry.HostPort === null) continue;
|
|
13060
|
+
const bound = Number(entry.HostPort);
|
|
13061
|
+
if (!Number.isInteger(bound) || bound <= 0) continue;
|
|
13062
|
+
ports.push({ hostIp: normalizeHostIp(entry.HostIp), hostPort: bound, protocol });
|
|
13063
|
+
}
|
|
13064
|
+
}
|
|
13065
|
+
const unique = /* @__PURE__ */ new Map();
|
|
13066
|
+
for (const port of ports) {
|
|
13067
|
+
unique.set(`${port.hostIp ?? ""}|${port.hostPort}|${port.protocol}`, port);
|
|
13068
|
+
}
|
|
13069
|
+
return [...unique.values()];
|
|
13070
|
+
}
|
|
13071
|
+
function parseHolderSnapshots(stdout) {
|
|
13072
|
+
return stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
13073
|
+
let parsed;
|
|
13074
|
+
try {
|
|
13075
|
+
parsed = JSON.parse(line);
|
|
13076
|
+
} catch {
|
|
13077
|
+
throw new Error("Host-port holder inspection returned malformed JSON.");
|
|
13078
|
+
}
|
|
13079
|
+
if (!isRecord6(parsed) || typeof parsed.id !== "string" || parsed.id.length === 0) {
|
|
13080
|
+
throw new Error("Host-port holder inspection returned an invalid container record.");
|
|
13081
|
+
}
|
|
13082
|
+
const name = typeof parsed.name === "string" ? parsed.name.replace(/^\//, "") : parsed.id;
|
|
13083
|
+
return {
|
|
13084
|
+
id: parsed.id,
|
|
13085
|
+
name,
|
|
13086
|
+
composeProject: optionalLabel(parsed.labels, "com.docker.compose.project"),
|
|
13087
|
+
workingDir: optionalLabel(parsed.labels, "com.docker.compose.project.working_dir"),
|
|
13088
|
+
ports: parseHolderPortMap(parsed.ports)
|
|
13089
|
+
};
|
|
13090
|
+
});
|
|
13091
|
+
}
|
|
13092
|
+
function listHolderSnapshots() {
|
|
13093
|
+
const listed = runDocker(["ps", "--filter", "status=running", "--format", "{{.ID}}"]);
|
|
13094
|
+
const ids2 = listed.split(/\r?\n/).map((id5) => id5.trim()).filter(Boolean);
|
|
13095
|
+
if (ids2.length === 0) return [];
|
|
13096
|
+
if (ids2.length > MAX_RUNNING_HOLDERS) {
|
|
13097
|
+
throw new Error(
|
|
13098
|
+
`Host-port holder inspection found ${ids2.length} running containers, above its bound.`
|
|
13099
|
+
);
|
|
13100
|
+
}
|
|
13101
|
+
const inspected = runDocker(["inspect", "--format", HOST_PORT_INSPECT_TEMPLATE, ...ids2]);
|
|
13102
|
+
return parseHolderSnapshots(inspected);
|
|
13103
|
+
}
|
|
13104
|
+
function renderManagedComposeModel(plan, workspace) {
|
|
13105
|
+
const fileArgs = plan.composeFiles.flatMap((file) => ["-f", file]);
|
|
13106
|
+
const result = (0, import_node_child_process20.spawnSync)(
|
|
13107
|
+
"docker",
|
|
13108
|
+
[
|
|
13109
|
+
"compose",
|
|
13110
|
+
"--profile",
|
|
13111
|
+
"*",
|
|
13112
|
+
"--project-directory",
|
|
13113
|
+
plan.composeDirectory,
|
|
13114
|
+
...fileArgs,
|
|
13115
|
+
"config",
|
|
13116
|
+
"--format",
|
|
13117
|
+
"json"
|
|
13118
|
+
],
|
|
13119
|
+
{
|
|
13120
|
+
cwd: plan.composeDirectory,
|
|
13121
|
+
encoding: "utf-8",
|
|
13122
|
+
env: managedComposeEnvironment(workspace),
|
|
13123
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
13124
|
+
timeout: COMPOSE_RENDER_TIMEOUT_MS,
|
|
13125
|
+
maxBuffer: COMPOSE_RENDER_MAX_BUFFER
|
|
13126
|
+
}
|
|
13127
|
+
);
|
|
13128
|
+
if (result.status !== 0 || result.error || !result.stdout?.trim()) {
|
|
13129
|
+
const reason = (result.stderr || result.error?.message || "unknown error").toString().trim().split(/\r?\n/).slice(-3).join("; ");
|
|
13130
|
+
throw new Error(
|
|
13131
|
+
`Host-port claims could not render the managed Compose model that the start would bind: ${reason}`
|
|
13132
|
+
);
|
|
13133
|
+
}
|
|
13134
|
+
try {
|
|
13135
|
+
return JSON.parse(result.stdout);
|
|
13136
|
+
} catch {
|
|
13137
|
+
throw new Error("Host-port claims received a malformed rendered Compose model.");
|
|
13138
|
+
}
|
|
13139
|
+
}
|
|
13140
|
+
function attributeHolderWorktree(workingDir) {
|
|
13141
|
+
if (import_node_path39.default.basename(workingDir) !== ".devcontainer") return void 0;
|
|
13142
|
+
return comparableWorkspacePath(import_node_path39.default.dirname(workingDir));
|
|
13143
|
+
}
|
|
13144
|
+
function holderAttribution(holder, repoPath) {
|
|
13145
|
+
const attribution = {};
|
|
13146
|
+
if (holder.composeProject) attribution.holderComposeProject = holder.composeProject;
|
|
13147
|
+
const worktree = holder.workingDir ? attributeHolderWorktree(holder.workingDir) : void 0;
|
|
13148
|
+
if (!worktree) return attribution;
|
|
13149
|
+
attribution.holderWorktreePath = worktree;
|
|
13150
|
+
try {
|
|
13151
|
+
const owner = listWorkspaceOwnership(repoPath).find(
|
|
13152
|
+
(record5) => comparableWorkspacePath(record5.worktreePath) === worktree
|
|
13153
|
+
);
|
|
13154
|
+
if (owner) {
|
|
13155
|
+
attribution.holderWorkspace = owner.workspace;
|
|
13156
|
+
if (owner.branch) attribution.holderBranch = owner.branch;
|
|
13157
|
+
}
|
|
13158
|
+
} catch {
|
|
13159
|
+
}
|
|
13160
|
+
return attribution;
|
|
13161
|
+
}
|
|
13162
|
+
function remediationFor(holder, attribution) {
|
|
13163
|
+
if (attribution.holderWorktreePath) {
|
|
13164
|
+
return `Stop the holding workspace with 'devrouter stop ${attribution.holderWorktreePath}', or override the consumer's published host binding through its own configuration, then run ensure again.`;
|
|
13165
|
+
}
|
|
13166
|
+
return `Stop or reconfigure the holding container '${holder.name}' and run ensure again; devrouter never rewrites consumer-declared host bindings.`;
|
|
13167
|
+
}
|
|
13168
|
+
function detectHostPortClaimConflicts(options) {
|
|
13169
|
+
const desired = resolveFixedPublishedHostPorts(
|
|
13170
|
+
renderManagedComposeModel(options.plan, options.workspace)
|
|
13171
|
+
);
|
|
13172
|
+
if (desired.length === 0) return [];
|
|
13173
|
+
const holders = listHolderSnapshots();
|
|
13174
|
+
const devcontainerDir = import_node_path39.default.join(options.repoPath, ".devcontainer");
|
|
13175
|
+
const conflicts = [];
|
|
13176
|
+
for (const binding of desired) {
|
|
13177
|
+
for (const holder of holders) {
|
|
13178
|
+
for (const holderPort of holder.ports) {
|
|
13179
|
+
if (!hostPortBindingsConflict(binding, holderPort)) continue;
|
|
13180
|
+
if (holder.workingDir && sameWorkspacePath(holder.workingDir, devcontainerDir)) continue;
|
|
13181
|
+
const attribution = holderAttribution(holder, options.repoPath);
|
|
13182
|
+
conflicts.push({
|
|
13183
|
+
service: binding.service,
|
|
13184
|
+
hostIp: binding.hostIp,
|
|
13185
|
+
hostPort: binding.hostPort,
|
|
13186
|
+
protocol: binding.protocol,
|
|
13187
|
+
holderContainer: holder.name,
|
|
13188
|
+
...attribution,
|
|
13189
|
+
remediation: remediationFor(holder, attribution)
|
|
13190
|
+
});
|
|
13191
|
+
}
|
|
13192
|
+
}
|
|
13193
|
+
}
|
|
13194
|
+
return conflicts;
|
|
13195
|
+
}
|
|
13196
|
+
var import_node_child_process20, import_node_path39, COMPOSE_RENDER_TIMEOUT_MS, COMPOSE_RENDER_MAX_BUFFER, DOCKER_TIMEOUT_MS, DOCKER_MAX_BUFFER, MAX_DESIRED_HOST_PORTS, MAX_RUNNING_HOLDERS, HOST_PORT_INSPECT_TEMPLATE;
|
|
13197
|
+
var init_host_port_claims = __esm({
|
|
13198
|
+
"src/core/host-port-claims.ts"() {
|
|
13199
|
+
"use strict";
|
|
13200
|
+
import_node_child_process20 = require("child_process");
|
|
13201
|
+
import_node_path39 = __toESM(require("path"));
|
|
13202
|
+
init_devcontainer_profile();
|
|
13203
|
+
init_network_effect_scope();
|
|
13204
|
+
init_workspace();
|
|
13205
|
+
init_workspace_ownership();
|
|
13206
|
+
COMPOSE_RENDER_TIMEOUT_MS = 3e4;
|
|
13207
|
+
COMPOSE_RENDER_MAX_BUFFER = 8 * 1024 * 1024;
|
|
13208
|
+
DOCKER_TIMEOUT_MS = 1e4;
|
|
13209
|
+
DOCKER_MAX_BUFFER = 1024 * 1024;
|
|
13210
|
+
MAX_DESIRED_HOST_PORTS = 1024;
|
|
13211
|
+
MAX_RUNNING_HOLDERS = 512;
|
|
13212
|
+
HOST_PORT_INSPECT_TEMPLATE = '{"id":{{json .Id}},"name":{{json .Name}},"labels":{"com.docker.compose.project":{{json (index .Config.Labels "com.docker.compose.project")}},"com.docker.compose.project.working_dir":{{json (index .Config.Labels "com.docker.compose.project.working_dir")}}},"ports":{{json .NetworkSettings.Ports}}}';
|
|
13213
|
+
}
|
|
13214
|
+
});
|
|
13215
|
+
|
|
12843
13216
|
// src/core/http-route-probe.ts
|
|
12844
13217
|
function probeHttpRoute(host, options = {}) {
|
|
12845
13218
|
const maxTimeSeconds = options.maxTimeSeconds ?? DEFAULT_MAX_TIME_SECONDS;
|
|
@@ -12866,7 +13239,7 @@ function probeHttpRoute(host, options = {}) {
|
|
|
12866
13239
|
args.push("--cacert", CERT_FILE);
|
|
12867
13240
|
}
|
|
12868
13241
|
args.push(url);
|
|
12869
|
-
const result = (0,
|
|
13242
|
+
const result = (0, import_node_child_process21.spawnSync)("curl", args, { encoding: "utf-8" });
|
|
12870
13243
|
const stdout = result.stdout?.trim() ?? "";
|
|
12871
13244
|
const status = /^\d{3}$/.test(stdout) ? Number(stdout) : void 0;
|
|
12872
13245
|
const ok = result.status === 0 && status !== void 0 && status >= 100 && status < 500;
|
|
@@ -12901,7 +13274,7 @@ function probeHttpReadinessSync(host, readiness, maxTimeSeconds, repoPath) {
|
|
|
12901
13274
|
}
|
|
12902
13275
|
let result;
|
|
12903
13276
|
try {
|
|
12904
|
-
result = (0,
|
|
13277
|
+
result = (0, import_node_child_process21.spawnSync)("curl", args, {
|
|
12905
13278
|
encoding: "utf-8",
|
|
12906
13279
|
maxBuffer: CONTRACT_MAX_BUFFER,
|
|
12907
13280
|
timeout: Math.ceil(parentTimeoutMs)
|
|
@@ -12983,11 +13356,11 @@ function transportFailure() {
|
|
|
12983
13356
|
classification: "transport"
|
|
12984
13357
|
};
|
|
12985
13358
|
}
|
|
12986
|
-
var
|
|
13359
|
+
var import_node_child_process21, DEFAULT_MAX_TIME_SECONDS, CONTROLLER_CURL_MAX_TIME_SECONDS, PARENT_TIMEOUT_PADDING_MS, CONTRACT_MAX_BUFFER, MIME_TOKEN_RE2;
|
|
12987
13360
|
var init_http_route_probe = __esm({
|
|
12988
13361
|
"src/core/http-route-probe.ts"() {
|
|
12989
13362
|
"use strict";
|
|
12990
|
-
|
|
13363
|
+
import_node_child_process21 = require("child_process");
|
|
12991
13364
|
init_router();
|
|
12992
13365
|
init_tls();
|
|
12993
13366
|
DEFAULT_MAX_TIME_SECONDS = 5;
|
|
@@ -12999,20 +13372,20 @@ var init_http_route_probe = __esm({
|
|
|
12999
13372
|
});
|
|
13000
13373
|
|
|
13001
13374
|
// src/core/managed-host-preparation.ts
|
|
13002
|
-
var
|
|
13375
|
+
var import_node_child_process22, import_node_fs31;
|
|
13003
13376
|
var init_managed_host_preparation = __esm({
|
|
13004
13377
|
"src/core/managed-host-preparation.ts"() {
|
|
13005
13378
|
"use strict";
|
|
13006
|
-
|
|
13007
|
-
|
|
13379
|
+
import_node_child_process22 = require("child_process");
|
|
13380
|
+
import_node_fs31 = __toESM(require("fs"));
|
|
13008
13381
|
init_repo_config();
|
|
13009
13382
|
}
|
|
13010
13383
|
});
|
|
13011
13384
|
|
|
13012
13385
|
// src/core/managed-post-start.ts
|
|
13013
13386
|
function readRegularFileBytes(filePath) {
|
|
13014
|
-
if (!
|
|
13015
|
-
return
|
|
13387
|
+
if (!import_node_fs32.default.existsSync(filePath) || !import_node_fs32.default.lstatSync(filePath).isFile()) return void 0;
|
|
13388
|
+
return import_node_fs32.default.readFileSync(filePath);
|
|
13016
13389
|
}
|
|
13017
13390
|
function readRegularFile(filePath) {
|
|
13018
13391
|
return readRegularFileBytes(filePath)?.toString("utf-8");
|
|
@@ -13021,11 +13394,11 @@ function adapterFingerprint(adapter) {
|
|
|
13021
13394
|
return (0, import_node_crypto17.createHash)("sha256").update(adapter).digest("hex");
|
|
13022
13395
|
}
|
|
13023
13396
|
function resolveManagedPostStartPlan(repoPath) {
|
|
13024
|
-
const adapterPath =
|
|
13397
|
+
const adapterPath = import_node_path40.default.join(repoPath, MANAGED_ADAPTER_PATH);
|
|
13025
13398
|
const adapterBytes = readRegularFileBytes(adapterPath);
|
|
13026
13399
|
const adapterText = adapterBytes?.toString("utf-8");
|
|
13027
|
-
const dockerfilePath =
|
|
13028
|
-
const devcontainerPath =
|
|
13400
|
+
const dockerfilePath = import_node_path40.default.join(repoPath, ".devcontainer", "Dockerfile");
|
|
13401
|
+
const devcontainerPath = import_node_path40.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
13029
13402
|
const dockerfile = readRegularFile(dockerfilePath) ?? "";
|
|
13030
13403
|
const devcontainer = readRegularFile(devcontainerPath) ?? "";
|
|
13031
13404
|
const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
|
|
@@ -13071,7 +13444,7 @@ function assertSafeProcessName(name) {
|
|
|
13071
13444
|
}
|
|
13072
13445
|
function runManagedProcessAction(options) {
|
|
13073
13446
|
assertSafeProcessName(options.name);
|
|
13074
|
-
const result = (0,
|
|
13447
|
+
const result = (0, import_node_child_process23.spawnSync)(
|
|
13075
13448
|
"docker",
|
|
13076
13449
|
[
|
|
13077
13450
|
"exec",
|
|
@@ -13108,14 +13481,14 @@ function runManagedProcessAction(options) {
|
|
|
13108
13481
|
}
|
|
13109
13482
|
return "drifted";
|
|
13110
13483
|
}
|
|
13111
|
-
var
|
|
13484
|
+
var import_node_child_process23, import_node_crypto17, import_node_fs32, import_node_path40, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH;
|
|
13112
13485
|
var init_managed_post_start = __esm({
|
|
13113
13486
|
"src/core/managed-post-start.ts"() {
|
|
13114
13487
|
"use strict";
|
|
13115
|
-
|
|
13488
|
+
import_node_child_process23 = require("child_process");
|
|
13116
13489
|
import_node_crypto17 = require("crypto");
|
|
13117
|
-
|
|
13118
|
-
|
|
13490
|
+
import_node_fs32 = __toESM(require("fs"));
|
|
13491
|
+
import_node_path40 = __toESM(require("path"));
|
|
13119
13492
|
init_devcontainer_config();
|
|
13120
13493
|
init_network_effect_scope();
|
|
13121
13494
|
MANAGED_MARKER = "devrouter:managed devcontainer";
|
|
@@ -13567,7 +13940,7 @@ function readDocker(endpoint, args, timeoutMs) {
|
|
|
13567
13940
|
const env = { ...process.env };
|
|
13568
13941
|
delete env.DOCKER_CONTEXT;
|
|
13569
13942
|
delete env.DOCKER_HOST;
|
|
13570
|
-
const result = (0,
|
|
13943
|
+
const result = (0, import_node_child_process24.spawnSync)("docker", ["--host", endpoint, ...args], {
|
|
13571
13944
|
env,
|
|
13572
13945
|
encoding: "utf-8",
|
|
13573
13946
|
timeout: timeoutMs,
|
|
@@ -13722,11 +14095,11 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
|
|
|
13722
14095
|
}
|
|
13723
14096
|
return result;
|
|
13724
14097
|
}
|
|
13725
|
-
var
|
|
14098
|
+
var import_node_child_process24, NetworkInventoryError, MAX_IDS, MAX_OUTPUT_BYTES, FULL_ID, NETWORK_TEMPLATE, CONTAINER_TEMPLATE;
|
|
13726
14099
|
var init_network_inventory = __esm({
|
|
13727
14100
|
"src/core/network-inventory.ts"() {
|
|
13728
14101
|
"use strict";
|
|
13729
|
-
|
|
14102
|
+
import_node_child_process24 = require("child_process");
|
|
13730
14103
|
init_devpod_environment();
|
|
13731
14104
|
NetworkInventoryError = class extends Error {
|
|
13732
14105
|
};
|
|
@@ -14054,26 +14427,26 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
|
14054
14427
|
try {
|
|
14055
14428
|
if (!endpoint.startsWith("unix:///")) return false;
|
|
14056
14429
|
const socket = endpoint.slice("unix://".length);
|
|
14057
|
-
const table =
|
|
14430
|
+
const table = import_node_fs33.default.readFileSync(import_node_path41.default.join(procRoot, "net/unix"), "utf8");
|
|
14058
14431
|
if (Buffer.byteLength(table) > 1024 * 1024) return false;
|
|
14059
14432
|
const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
|
|
14060
14433
|
if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
|
|
14061
14434
|
const inode = `socket:[${rows2[0][6]}]`;
|
|
14062
|
-
const ownNamespace =
|
|
14063
|
-
const pids =
|
|
14435
|
+
const ownNamespace = import_node_fs33.default.readlinkSync(import_node_path41.default.join(procRoot, "self/ns/net"));
|
|
14436
|
+
const pids = import_node_fs33.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
|
|
14064
14437
|
if (pids.length > 4096) return false;
|
|
14065
14438
|
for (const pid of pids) {
|
|
14066
14439
|
if (Date.now() > deadline) return false;
|
|
14067
14440
|
try {
|
|
14068
|
-
const root =
|
|
14069
|
-
if (
|
|
14070
|
-
if (
|
|
14071
|
-
const descriptors =
|
|
14441
|
+
const root = import_node_path41.default.join(procRoot, pid);
|
|
14442
|
+
if (import_node_path41.default.basename(import_node_fs33.default.readlinkSync(import_node_path41.default.join(root, "exe"))) !== "dockerd") continue;
|
|
14443
|
+
if (import_node_fs33.default.readlinkSync(import_node_path41.default.join(root, "ns/net")) !== ownNamespace) continue;
|
|
14444
|
+
const descriptors = import_node_fs33.default.readdirSync(import_node_path41.default.join(root, "fd"));
|
|
14072
14445
|
if (descriptors.length > 4096) continue;
|
|
14073
14446
|
if (descriptors.some((fd) => {
|
|
14074
14447
|
if (Date.now() > deadline) return false;
|
|
14075
14448
|
try {
|
|
14076
|
-
return
|
|
14449
|
+
return import_node_fs33.default.readlinkSync(import_node_path41.default.join(root, "fd", fd)) === inode;
|
|
14077
14450
|
} catch {
|
|
14078
14451
|
return false;
|
|
14079
14452
|
}
|
|
@@ -14087,12 +14460,12 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
|
14087
14460
|
}
|
|
14088
14461
|
return false;
|
|
14089
14462
|
}
|
|
14090
|
-
var
|
|
14463
|
+
var import_node_fs33, import_node_path41;
|
|
14091
14464
|
var init_network_local_daemon = __esm({
|
|
14092
14465
|
"src/core/network-local-daemon.ts"() {
|
|
14093
14466
|
"use strict";
|
|
14094
|
-
|
|
14095
|
-
|
|
14467
|
+
import_node_fs33 = __toESM(require("fs"));
|
|
14468
|
+
import_node_path41 = __toESM(require("path"));
|
|
14096
14469
|
}
|
|
14097
14470
|
});
|
|
14098
14471
|
|
|
@@ -14120,7 +14493,7 @@ function parseLinuxNetworkRoutes(output2) {
|
|
|
14120
14493
|
function collectNetworkRoutes(options) {
|
|
14121
14494
|
if ((options.platform ?? process.platform) !== "linux" || !hasLocalDockerNetworkNamespace(options.endpoint))
|
|
14122
14495
|
return { status: "unknown", routes: [] };
|
|
14123
|
-
const result = (0,
|
|
14496
|
+
const result = (0, import_node_child_process25.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
|
|
14124
14497
|
encoding: "utf8",
|
|
14125
14498
|
timeout: 5e3,
|
|
14126
14499
|
maxBuffer: 1024 * 1024
|
|
@@ -14128,11 +14501,11 @@ function collectNetworkRoutes(options) {
|
|
|
14128
14501
|
if (result.error || result.status !== 0) return { status: "unknown", routes: [] };
|
|
14129
14502
|
return parseLinuxNetworkRoutes(result.stdout);
|
|
14130
14503
|
}
|
|
14131
|
-
var
|
|
14504
|
+
var import_node_child_process25, import_node_net3;
|
|
14132
14505
|
var init_network_routes = __esm({
|
|
14133
14506
|
"src/core/network-routes.ts"() {
|
|
14134
14507
|
"use strict";
|
|
14135
|
-
|
|
14508
|
+
import_node_child_process25 = require("child_process");
|
|
14136
14509
|
import_node_net3 = require("net");
|
|
14137
14510
|
init_network_capacity();
|
|
14138
14511
|
init_network_local_daemon();
|
|
@@ -14208,14 +14581,14 @@ var init_network_compose = __esm({
|
|
|
14208
14581
|
});
|
|
14209
14582
|
|
|
14210
14583
|
// src/core/network-compose-files.ts
|
|
14211
|
-
var
|
|
14584
|
+
var import_node_child_process26, import_node_crypto19, import_node_fs34, import_node_path42, import_yaml4, MAX_BYTES3;
|
|
14212
14585
|
var init_network_compose_files = __esm({
|
|
14213
14586
|
"src/core/network-compose-files.ts"() {
|
|
14214
14587
|
"use strict";
|
|
14215
|
-
|
|
14588
|
+
import_node_child_process26 = require("child_process");
|
|
14216
14589
|
import_node_crypto19 = require("crypto");
|
|
14217
|
-
|
|
14218
|
-
|
|
14590
|
+
import_node_fs34 = __toESM(require("fs"));
|
|
14591
|
+
import_node_path42 = __toESM(require("path"));
|
|
14219
14592
|
import_yaml4 = __toESM(require("yaml"));
|
|
14220
14593
|
init_atomic_file();
|
|
14221
14594
|
init_devcontainer_profile();
|
|
@@ -14225,11 +14598,11 @@ var init_network_compose_files = __esm({
|
|
|
14225
14598
|
});
|
|
14226
14599
|
|
|
14227
14600
|
// src/core/network-connected-route.ts
|
|
14228
|
-
var
|
|
14601
|
+
var import_node_child_process27;
|
|
14229
14602
|
var init_network_connected_route = __esm({
|
|
14230
14603
|
"src/core/network-connected-route.ts"() {
|
|
14231
14604
|
"use strict";
|
|
14232
|
-
|
|
14605
|
+
import_node_child_process27 = require("child_process");
|
|
14233
14606
|
init_network_capacity();
|
|
14234
14607
|
}
|
|
14235
14608
|
});
|
|
@@ -14319,19 +14692,20 @@ function resolveLinkedTarget(repoPath) {
|
|
|
14319
14692
|
gitCommonDir: resolveGitCommonDir(repoPath)
|
|
14320
14693
|
};
|
|
14321
14694
|
}
|
|
14322
|
-
var
|
|
14695
|
+
var import_node_child_process28, import_node_fs35, import_node_path43;
|
|
14323
14696
|
var init_workspace_ensure = __esm({
|
|
14324
14697
|
"src/core/workspace-ensure.ts"() {
|
|
14325
14698
|
"use strict";
|
|
14326
|
-
|
|
14327
|
-
|
|
14328
|
-
|
|
14699
|
+
import_node_child_process28 = require("child_process");
|
|
14700
|
+
import_node_fs35 = __toESM(require("fs"));
|
|
14701
|
+
import_node_path43 = __toESM(require("path"));
|
|
14329
14702
|
init_devcontainer_profile();
|
|
14330
14703
|
init_devpod_environment();
|
|
14331
14704
|
init_devpod_mutation();
|
|
14332
14705
|
init_devpod_workspaces();
|
|
14333
14706
|
init_devsy_mutation();
|
|
14334
14707
|
init_file_lock();
|
|
14708
|
+
init_host_port_claims();
|
|
14335
14709
|
init_host_routes();
|
|
14336
14710
|
init_http_route_probe();
|
|
14337
14711
|
init_managed_host_preparation();
|
|
@@ -14460,13 +14834,13 @@ function prepareManagedLifecycleOperation(input2) {
|
|
|
14460
14834
|
};
|
|
14461
14835
|
});
|
|
14462
14836
|
}
|
|
14463
|
-
function assertCurrentCapacityController(expected, directory =
|
|
14837
|
+
function assertCurrentCapacityController(expected, directory = import_node_path44.default.join(DEVROUTER_HOME, "controller")) {
|
|
14464
14838
|
const current = new ControllerStore(directory).read();
|
|
14465
14839
|
if (!expected || current?.store !== expected.store || current.epoch !== expected.epoch)
|
|
14466
14840
|
throw new Error("Capacity controller incarnation changed.");
|
|
14467
14841
|
}
|
|
14468
14842
|
function settlePreparedLifecycleCapacity(input2) {
|
|
14469
|
-
const directory = input2.directory ??
|
|
14843
|
+
const directory = input2.directory ?? import_node_path44.default.join(DEVROUTER_HOME, "controller");
|
|
14470
14844
|
const store = new CapacityStore(directory);
|
|
14471
14845
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
14472
14846
|
const snapshot = store.read();
|
|
@@ -14680,7 +15054,7 @@ async function superviseLifecycle(kind, repoPath, options = {}, command) {
|
|
|
14680
15054
|
removeSignals();
|
|
14681
15055
|
}
|
|
14682
15056
|
}
|
|
14683
|
-
function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory =
|
|
15057
|
+
function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory = import_node_path44.default.join(DEVROUTER_HOME, "controller"), controller, admission) {
|
|
14684
15058
|
if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
|
|
14685
15059
|
const capacity = new CapacityStore(directory);
|
|
14686
15060
|
const snapshot = capacity.read();
|
|
@@ -14780,7 +15154,7 @@ function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs,
|
|
|
14780
15154
|
return decision;
|
|
14781
15155
|
}
|
|
14782
15156
|
function capacityPolicyTargets(repoPath) {
|
|
14783
|
-
const policy = readCapacityPolicy(
|
|
15157
|
+
const policy = readCapacityPolicy(import_node_path44.default.join(DEVROUTER_HOME, "controller"));
|
|
14784
15158
|
return policy?.admissions === "enabled" && policy.enrollments.some((entry) => entry.repoPath === repoPath);
|
|
14785
15159
|
}
|
|
14786
15160
|
function controllerWaitSeconds(deadline) {
|
|
@@ -14789,7 +15163,7 @@ function controllerWaitSeconds(deadline) {
|
|
|
14789
15163
|
async function superviseThroughController(input2) {
|
|
14790
15164
|
if (input2.kind === "exec" && !input2.command)
|
|
14791
15165
|
throw new Error("Exec requires a command for controller admission.");
|
|
14792
|
-
const directory =
|
|
15166
|
+
const directory = import_node_path44.default.join(DEVROUTER_HOME, "controller");
|
|
14793
15167
|
const cancellation = { requested: false };
|
|
14794
15168
|
const removeSignals = installLifecycleWaitSignals(cancellation);
|
|
14795
15169
|
const cancelled = () => {
|
|
@@ -14846,7 +15220,7 @@ async function superviseThroughController(input2) {
|
|
|
14846
15220
|
removeSignals();
|
|
14847
15221
|
}
|
|
14848
15222
|
}
|
|
14849
|
-
function renewLifecycleCapacity(request2, policy, samples, controller, directory =
|
|
15223
|
+
function renewLifecycleCapacity(request2, policy, samples, controller, directory = import_node_path44.default.join(DEVROUTER_HOME, "controller"), nowMs = Date.now()) {
|
|
14850
15224
|
return updateReliabilityOperation(request2.identity, (record5) => {
|
|
14851
15225
|
const binding = record5.capacity;
|
|
14852
15226
|
if (!matchesFence(record5, request2.fence) || !binding || binding.operationId !== request2.operationId || binding.workerId !== request2.workerId || binding.controller?.store !== controller.store || binding.controller?.epoch !== controller.epoch)
|
|
@@ -14924,7 +15298,7 @@ function retireQueuedLifecycle(request2, supersededOnly = false) {
|
|
|
14924
15298
|
return true;
|
|
14925
15299
|
});
|
|
14926
15300
|
}
|
|
14927
|
-
function bindLifecycleCapacity(request2, binding, directory =
|
|
15301
|
+
function bindLifecycleCapacity(request2, binding, directory = import_node_path44.default.join(DEVROUTER_HOME, "controller")) {
|
|
14928
15302
|
if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
|
|
14929
15303
|
updateReliabilityOperation(request2.identity, (record5) => {
|
|
14930
15304
|
if (!matchesFence(record5, request2.fence) || record5.worker || record5.state.operation?.id !== request2.operationId || record5.state.operation.status !== "NOT_STARTED" || record5.state.operation.drained)
|
|
@@ -14939,12 +15313,12 @@ function bindLifecycleCapacity(request2, binding, directory = import_node_path43
|
|
|
14939
15313
|
stepRecord(record5, { ...request2.fence, type: "admission", result: "admitted" });
|
|
14940
15314
|
});
|
|
14941
15315
|
}
|
|
14942
|
-
var import_node_crypto21,
|
|
15316
|
+
var import_node_crypto21, import_node_path44, import_node_util10, activeWorker, lockHeld, BUSY_LIFECYCLE_WAIT_MS, BUSY_LIFECYCLE_POLL_MS, CAPACITY_CLIENT_WAIT_MS, BUSY_LIFECYCLE_PROGRESS_MS;
|
|
14943
15317
|
var init_reliability_lifecycle = __esm({
|
|
14944
15318
|
"src/core/reliability-lifecycle.ts"() {
|
|
14945
15319
|
"use strict";
|
|
14946
15320
|
import_node_crypto21 = require("crypto");
|
|
14947
|
-
|
|
15321
|
+
import_node_path44 = __toESM(require("path"));
|
|
14948
15322
|
import_node_util10 = require("util");
|
|
14949
15323
|
init_capacity_accounting();
|
|
14950
15324
|
init_capacity_policy();
|
|
@@ -15431,7 +15805,7 @@ function createCapacityController(options) {
|
|
|
15431
15805
|
const record5 = readReliabilityOperation(identity2);
|
|
15432
15806
|
if (!record5?.capacity || !record5.preparation && !record5.capacity.execSteady || record5.worker || !record5.state.operation?.drained || !["ensure", "exec"].includes(record5.state.operation.kind))
|
|
15433
15807
|
continue;
|
|
15434
|
-
const bytes2 = readControllerEvidence(
|
|
15808
|
+
const bytes2 = readControllerEvidence(import_node_path45.default.join(enrollment.repoPath, ".devrouter.yml"));
|
|
15435
15809
|
const estimates = loadRepoConfig(enrollment.repoPath, () => bytes2).capacity;
|
|
15436
15810
|
if (!estimates) continue;
|
|
15437
15811
|
settlePreparedLifecycleCapacity({
|
|
@@ -15591,12 +15965,12 @@ function createCapacityController(options) {
|
|
|
15591
15965
|
}
|
|
15592
15966
|
};
|
|
15593
15967
|
}
|
|
15594
|
-
var import_node_crypto22,
|
|
15968
|
+
var import_node_crypto22, import_node_path45, import_node_util12;
|
|
15595
15969
|
var init_capacity_controller = __esm({
|
|
15596
15970
|
"src/core/capacity-controller.ts"() {
|
|
15597
15971
|
"use strict";
|
|
15598
15972
|
import_node_crypto22 = require("crypto");
|
|
15599
|
-
|
|
15973
|
+
import_node_path45 = __toESM(require("path"));
|
|
15600
15974
|
import_node_util12 = require("util");
|
|
15601
15975
|
init_capacity_docker_probe();
|
|
15602
15976
|
init_capacity_enrollment();
|
|
@@ -15937,12 +16311,12 @@ async function containers(project, signal) {
|
|
|
15937
16311
|
}
|
|
15938
16312
|
return values.sort((a, b) => a.id.localeCompare(b.id));
|
|
15939
16313
|
}
|
|
15940
|
-
var import_node_crypto24,
|
|
16314
|
+
var import_node_crypto24, import_node_path46, OBSERVATION_INSPECT_TEMPLATE, collectControllerObservation;
|
|
15941
16315
|
var init_controller_observation = __esm({
|
|
15942
16316
|
"src/core/controller-observation.ts"() {
|
|
15943
16317
|
"use strict";
|
|
15944
16318
|
import_node_crypto24 = require("crypto");
|
|
15945
|
-
|
|
16319
|
+
import_node_path46 = __toESM(require("path"));
|
|
15946
16320
|
init_controller_binding();
|
|
15947
16321
|
init_controller_monitor();
|
|
15948
16322
|
init_controller_probe();
|
|
@@ -15985,14 +16359,14 @@ var init_controller_observation = __esm({
|
|
|
15985
16359
|
)).trim().split("\n");
|
|
15986
16360
|
if (metadata.length !== 2) throw new Error("Observation ownership paths unavailable.");
|
|
15987
16361
|
const [gitDir, commonDir] = metadata;
|
|
15988
|
-
const sourcePath =
|
|
16362
|
+
const sourcePath = import_node_path46.default.join(environment.repoPath, ".devcontainer/devcontainer.json");
|
|
15989
16363
|
const files = [
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
16364
|
+
import_node_path46.default.join(commonDir, "devrouter/workspaces", `${environment.workspace}.json`),
|
|
16365
|
+
import_node_path46.default.join(environment.repoPath, ".devrouter.yml"),
|
|
16366
|
+
import_node_path46.default.join(gitDir, "devrouter-workspace"),
|
|
15993
16367
|
managedRuntimeStatePath(environment.repoPath, environment.workspace),
|
|
15994
16368
|
sourcePath,
|
|
15995
|
-
|
|
16369
|
+
import_node_path46.default.join(environment.repoPath, MANAGED_DEVCONTAINER_PATH)
|
|
15996
16370
|
];
|
|
15997
16371
|
const persisted = captureControllerEvidence(files);
|
|
15998
16372
|
if (controllerBindingFingerprint(persisted.contents[0], persisted.contents[1]) !== environment.fingerprint)
|
|
@@ -16017,7 +16391,7 @@ var init_controller_observation = __esm({
|
|
|
16017
16391
|
if (!journal) throw new Error("Observation journal is unavailable.");
|
|
16018
16392
|
const routes = readRoutes().filter((route) => route.repoPath === environment.repoPath);
|
|
16019
16393
|
const before = await containers(state.composeProject, signal);
|
|
16020
|
-
const composeDirectory =
|
|
16394
|
+
const composeDirectory = import_node_path46.default.join(environment.repoPath, ".devcontainer");
|
|
16021
16395
|
const references = typeof source.dockerComposeFile === "string" ? [source.dockerComposeFile] : source.dockerComposeFile;
|
|
16022
16396
|
if (!Array.isArray(references) || !references.length || references.some((value) => typeof value !== "string"))
|
|
16023
16397
|
throw new Error("Observation Compose source is unavailable.");
|
|
@@ -16034,7 +16408,7 @@ var init_controller_observation = __esm({
|
|
|
16034
16408
|
if (!recorded || container.labels["com.docker.compose.project.working_dir"] !== composeDirectory)
|
|
16035
16409
|
throw new Error("Observation Compose ownership is unavailable.");
|
|
16036
16410
|
const files2 = recorded.split(",").map((file) => file.trim());
|
|
16037
|
-
if (files2.some((file) => !
|
|
16411
|
+
if (files2.some((file) => !import_node_path46.default.isAbsolute(file)) || !hasExactComposeIdentity(container, {
|
|
16038
16412
|
repoPath: environment.repoPath,
|
|
16039
16413
|
service: container.labels["com.docker.compose.service"] ?? "",
|
|
16040
16414
|
composeProject: state.composeProject,
|
|
@@ -16393,26 +16767,26 @@ var init_controller_sessions = __esm({
|
|
|
16393
16767
|
|
|
16394
16768
|
// src/core/controller-server.ts
|
|
16395
16769
|
function privateDirectory(directory) {
|
|
16396
|
-
const absolute2 =
|
|
16397
|
-
let current =
|
|
16398
|
-
for (const part of absolute2.slice(current.length).split(
|
|
16399
|
-
current =
|
|
16770
|
+
const absolute2 = import_node_path47.default.resolve(directory);
|
|
16771
|
+
let current = import_node_path47.default.parse(absolute2).root;
|
|
16772
|
+
for (const part of absolute2.slice(current.length).split(import_node_path47.default.sep)) {
|
|
16773
|
+
current = import_node_path47.default.join(current, part);
|
|
16400
16774
|
try {
|
|
16401
|
-
const stat2 =
|
|
16775
|
+
const stat2 = import_node_fs36.default.lstatSync(current);
|
|
16402
16776
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
16403
16777
|
throw new Error("Unsafe controller directory.");
|
|
16404
16778
|
} catch (error) {
|
|
16405
16779
|
if (error.code !== "ENOENT") throw error;
|
|
16406
|
-
|
|
16780
|
+
import_node_fs36.default.mkdirSync(current, { mode: 448 });
|
|
16407
16781
|
}
|
|
16408
16782
|
}
|
|
16409
|
-
const stat =
|
|
16783
|
+
const stat = import_node_fs36.default.lstatSync(absolute2);
|
|
16410
16784
|
if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
16411
16785
|
throw new Error("Controller directory is not private.");
|
|
16412
16786
|
}
|
|
16413
16787
|
function validateOwnedFile(file, socket) {
|
|
16414
16788
|
try {
|
|
16415
|
-
const stat =
|
|
16789
|
+
const stat = import_node_fs36.default.lstatSync(file);
|
|
16416
16790
|
if (stat.uid !== process.getuid?.() || (socket ? !stat.isSocket() : !stat.isFile()) || (stat.mode & 63) !== 0)
|
|
16417
16791
|
throw new Error("Unsafe controller artifact.");
|
|
16418
16792
|
return true;
|
|
@@ -16424,19 +16798,19 @@ function validateOwnedFile(file, socket) {
|
|
|
16424
16798
|
async function runController(options) {
|
|
16425
16799
|
if (options.operations && options.createOperations)
|
|
16426
16800
|
throw new Error("Controller operations have multiple owners.");
|
|
16427
|
-
const socketPath =
|
|
16801
|
+
const socketPath = import_node_path47.default.join(options.directory, "control.sock");
|
|
16428
16802
|
if (Buffer.byteLength(socketPath) > 103) throw new Error("Controller socket path is too long.");
|
|
16429
16803
|
privateDirectory(options.directory);
|
|
16430
|
-
const lockPath =
|
|
16804
|
+
const lockPath = import_node_path47.default.join(options.directory, "owner.lock");
|
|
16431
16805
|
try {
|
|
16432
|
-
const stat =
|
|
16806
|
+
const stat = import_node_fs36.default.lstatSync(lockPath);
|
|
16433
16807
|
if (!stat.isFile() || stat.uid !== process.getuid?.())
|
|
16434
16808
|
throw new Error("Unsafe controller owner lock.");
|
|
16435
16809
|
} catch (error) {
|
|
16436
16810
|
if (error.code !== "ENOENT") throw error;
|
|
16437
16811
|
}
|
|
16438
16812
|
await withFileLock(lockPath, { activity: "controller ownership", waitMs: 0 }, async () => {
|
|
16439
|
-
if (validateOwnedFile(socketPath, true))
|
|
16813
|
+
if (validateOwnedFile(socketPath, true)) import_node_fs36.default.unlinkSync(socketPath);
|
|
16440
16814
|
const sessions = new ControllerSessions(new ControllerStore(options.directory));
|
|
16441
16815
|
const incarnation = sessions.read();
|
|
16442
16816
|
let startupAvailable = true;
|
|
@@ -16815,7 +17189,7 @@ async function runController(options) {
|
|
|
16815
17189
|
resolve();
|
|
16816
17190
|
});
|
|
16817
17191
|
});
|
|
16818
|
-
|
|
17192
|
+
import_node_fs36.default.chmodSync(socketPath, 384);
|
|
16819
17193
|
await new Promise((resolve) => {
|
|
16820
17194
|
finish = resolve;
|
|
16821
17195
|
options.signal.addEventListener("abort", shutdown, { once: true });
|
|
@@ -16834,13 +17208,13 @@ async function runController(options) {
|
|
|
16834
17208
|
}
|
|
16835
17209
|
});
|
|
16836
17210
|
}
|
|
16837
|
-
var
|
|
17211
|
+
var import_node_fs36, import_node_net4, import_node_path47, FRAME_BYTES;
|
|
16838
17212
|
var init_controller_server = __esm({
|
|
16839
17213
|
"src/core/controller-server.ts"() {
|
|
16840
17214
|
"use strict";
|
|
16841
|
-
|
|
17215
|
+
import_node_fs36 = __toESM(require("fs"));
|
|
16842
17216
|
import_node_net4 = __toESM(require("net"));
|
|
16843
|
-
|
|
17217
|
+
import_node_path47 = __toESM(require("path"));
|
|
16844
17218
|
init_controller_monitor();
|
|
16845
17219
|
init_controller_protocol();
|
|
16846
17220
|
init_controller_sessions();
|
|
@@ -16857,7 +17231,7 @@ __export(controller_exports, {
|
|
|
16857
17231
|
runControllerCommand: () => runControllerCommand
|
|
16858
17232
|
});
|
|
16859
17233
|
async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
16860
|
-
const directory =
|
|
17234
|
+
const directory = import_node_path48.default.join(DEVROUTER_HOME, "controller");
|
|
16861
17235
|
try {
|
|
16862
17236
|
if (method === "run") {
|
|
16863
17237
|
const controller = new AbortController();
|
|
@@ -16892,7 +17266,7 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
|
16892
17266
|
if (fields2.timeout !== void 0) fields2.timeout = Number(fields2.timeout);
|
|
16893
17267
|
await controllerRequest(
|
|
16894
17268
|
directory,
|
|
16895
|
-
{ method, ...fields2, ...repo ? { path:
|
|
17269
|
+
{ method, ...fields2, ...repo ? { path: import_node_path48.default.resolve(repo) } : {} },
|
|
16896
17270
|
(value) => process.stdout.write(`${JSON.stringify(value)}
|
|
16897
17271
|
`)
|
|
16898
17272
|
);
|
|
@@ -16907,11 +17281,11 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
|
16907
17281
|
process.exitCode = 1;
|
|
16908
17282
|
}
|
|
16909
17283
|
}
|
|
16910
|
-
var
|
|
17284
|
+
var import_node_path48;
|
|
16911
17285
|
var init_controller = __esm({
|
|
16912
17286
|
"src/commands/controller.ts"() {
|
|
16913
17287
|
"use strict";
|
|
16914
|
-
|
|
17288
|
+
import_node_path48 = __toESM(require("path"));
|
|
16915
17289
|
init_capacity_collector();
|
|
16916
17290
|
init_capacity_controller();
|
|
16917
17291
|
init_capacity_policy();
|
|
@@ -16962,28 +17336,28 @@ function replaceDevrouterSection(content) {
|
|
|
16962
17336
|
return `${content.slice(0, start)}${buildDevrouterSection()}${content.slice(end)}`;
|
|
16963
17337
|
}
|
|
16964
17338
|
function writeRepoFile(repoPath, relPath, content) {
|
|
16965
|
-
const absolutePath = (0,
|
|
16966
|
-
(0,
|
|
16967
|
-
(0,
|
|
17339
|
+
const absolutePath = (0, import_node_path49.join)(repoPath, relPath);
|
|
17340
|
+
(0, import_node_fs37.mkdirSync)((0, import_node_path49.dirname)(absolutePath), { recursive: true });
|
|
17341
|
+
(0, import_node_fs37.writeFileSync)(absolutePath, content, "utf-8");
|
|
16968
17342
|
return absolutePath;
|
|
16969
17343
|
}
|
|
16970
17344
|
function ensureAgentsMdSection(repoPath) {
|
|
16971
|
-
const filePath = (0,
|
|
16972
|
-
if ((0,
|
|
16973
|
-
const content = (0,
|
|
17345
|
+
const filePath = (0, import_node_path49.join)(repoPath, AGENTS_MD);
|
|
17346
|
+
if ((0, import_node_fs37.existsSync)(filePath)) {
|
|
17347
|
+
const content = (0, import_node_fs37.readFileSync)(filePath, "utf-8");
|
|
16974
17348
|
if (content.includes(DEVROUTER_SENTINEL)) {
|
|
16975
17349
|
const updated = replaceDevrouterSection(content);
|
|
16976
17350
|
if (updated === void 0 || updated === content) return { path: filePath, written: false };
|
|
16977
|
-
(0,
|
|
17351
|
+
(0, import_node_fs37.writeFileSync)(filePath, updated, "utf-8");
|
|
16978
17352
|
return { path: filePath, written: true };
|
|
16979
17353
|
}
|
|
16980
|
-
(0,
|
|
17354
|
+
(0, import_node_fs37.writeFileSync)(filePath, `${content.trimEnd()}
|
|
16981
17355
|
|
|
16982
17356
|
${buildDevrouterSection()}
|
|
16983
17357
|
`, "utf-8");
|
|
16984
17358
|
return { path: filePath, written: true };
|
|
16985
17359
|
}
|
|
16986
|
-
(0,
|
|
17360
|
+
(0, import_node_fs37.writeFileSync)(filePath, `# AGENTS.md
|
|
16987
17361
|
|
|
16988
17362
|
${buildDevrouterSection()}
|
|
16989
17363
|
`, "utf-8");
|
|
@@ -16993,12 +17367,12 @@ function ensureSkillFile(repoPath) {
|
|
|
16993
17367
|
const filePath = writeRepoFile(repoPath, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT);
|
|
16994
17368
|
return { path: filePath, written: true };
|
|
16995
17369
|
}
|
|
16996
|
-
var
|
|
17370
|
+
var import_node_fs37, import_node_path49, DEVROUTER_SENTINEL, DEVROUTER_END_SENTINEL, LEGACY_DEVROUTER_SECTION_END, AGENTS_MD, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT;
|
|
16997
17371
|
var init_agents_md = __esm({
|
|
16998
17372
|
"src/core/agents-md.ts"() {
|
|
16999
17373
|
"use strict";
|
|
17000
|
-
|
|
17001
|
-
|
|
17374
|
+
import_node_fs37 = require("fs");
|
|
17375
|
+
import_node_path49 = require("path");
|
|
17002
17376
|
DEVROUTER_SENTINEL = "<!-- devrouter -->";
|
|
17003
17377
|
DEVROUTER_END_SENTINEL = "<!-- /devrouter -->";
|
|
17004
17378
|
LEGACY_DEVROUTER_SECTION_END = "- `devrouter ls`";
|
|
@@ -17459,6 +17833,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
17459
17833
|
"- if kind=app and runtime=proxy:",
|
|
17460
17834
|
" - HTTP-only optional readiness: { path: /api/health, statuses: [200], contentType: application/json }; statuses default to [200], explicit lists allow unique 2xx/4xx codes, never redirects. Paths stay on the configured host with no query, fragment, percent escapes, backslash or dot segments. Media types match their case-insensitive base type.",
|
|
17461
17835
|
" - Declared application readiness failures return applicationReadiness.status=application-error and a nonzero ensure exit, while retaining tooling and routes for application fixes; do not reset caches or recreate the provider. Absent readiness means legacy route liveness, not full application readiness.",
|
|
17836
|
+
" - A configured FIXED published host binding already held by a running container refuses managed ensure before any provider start (hostPortConflicts in --json, nonzero exit) naming the port, holder container, compose project, and owning workspace when attributable. Ephemeral host::container bindings are exempt. Never rewrite consumer ports to resolve a conflict: stop the holding workspace or use the consumer's own override, then ensure again. Unverifiable evidence also refuses.",
|
|
17462
17837
|
' - upstream: "host:port" (an already-running port, e.g. a devcontainer published on 127.0.0.1:3000, or a container reachable by name on a shared Docker network such as `derivatives-db:5432`)',
|
|
17463
17838
|
` - upstream may use the \`${WORKSPACE_PLACEHOLDER}\` placeholder (e.g. \`${WORKSPACE_PLACEHOLDER}-app:3000\`) to target a per-workspace devcontainer alias; it is substituted with the resolved workspace token at runtime and re-validated. Do NOT put \`${WORKSPACE_PLACEHOLDER}\` in \`host\` (rejected) \u2014 the host is auto-namespaced.`,
|
|
17464
17839
|
" - managed `devrouter ensure` requires every HTTP/TCP proxy upstream to begin with the exact resolved workspace/project alias prefix before it mutates DevPod or routes",
|
|
@@ -17565,6 +17940,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
17565
17940
|
"- Avoid pre-wrapper DB assignments such as `DATABASE_URI=... <wrapper> run -- ...`; wrapper-managed env may override those values.",
|
|
17566
17941
|
"- `devrouter doctor --repo <REPO_PATH>` warns on risky pre-wrapper DB assignments before `run --` for host apps that depend on postgres.",
|
|
17567
17942
|
"- With TLS enabled, `devrouter doctor --repo <REPO_PATH>` also warns on cert SAN mismatches for configured hosts (`repo.tls-host-coverage`).",
|
|
17943
|
+
"- `devrouter doctor --repo <REPO_PATH>` errors on configured fixed host-port bindings already held by running containers (`repo.host-port-claims`) and warns when that comparison is unavailable.",
|
|
17568
17944
|
"- Robust one-shot migrate example (argv-safe, no nested shell quoting): `devrouter app exec <name> --repo <REPO_PATH> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload migrate`.",
|
|
17569
17945
|
"- Robust one-shot seed example: `devrouter app exec <name> --repo <REPO_PATH> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload seed`.",
|
|
17570
17946
|
"- Environment probe/debug example (run before migrations): `devrouter app exec <name> --repo <REPO_PATH> --yes -- printenv DB_URL DB_HOST DB_PORT DB_SHADOW_URL`.",
|
|
@@ -18275,11 +18651,11 @@ function extractCurrentVersionFromRepoConfig(repoPath) {
|
|
|
18275
18651
|
};
|
|
18276
18652
|
}
|
|
18277
18653
|
function readPromptDirectory(promptDirPath) {
|
|
18278
|
-
if (!
|
|
18654
|
+
if (!import_node_fs38.default.existsSync(promptDirPath)) {
|
|
18279
18655
|
throw new Error(`Missing ${UPGRADE_PROMPTS_DIR} directory at ${promptDirPath}.`);
|
|
18280
18656
|
}
|
|
18281
18657
|
const releases = [];
|
|
18282
|
-
const files =
|
|
18658
|
+
const files = import_node_fs38.default.readdirSync(promptDirPath, { withFileTypes: true });
|
|
18283
18659
|
for (const file of files) {
|
|
18284
18660
|
if (!file.isFile() || !file.name.endsWith(".md")) {
|
|
18285
18661
|
continue;
|
|
@@ -18289,8 +18665,8 @@ function readPromptDirectory(promptDirPath) {
|
|
|
18289
18665
|
continue;
|
|
18290
18666
|
}
|
|
18291
18667
|
const version = normalizeVersion(basename);
|
|
18292
|
-
const promptPath =
|
|
18293
|
-
const prompt =
|
|
18668
|
+
const promptPath = import_node_path50.default.join(promptDirPath, file.name);
|
|
18669
|
+
const prompt = import_node_fs38.default.readFileSync(promptPath, "utf-8").trim();
|
|
18294
18670
|
releases.push({ version, prompt, promptPath });
|
|
18295
18671
|
}
|
|
18296
18672
|
const deduped = /* @__PURE__ */ new Map();
|
|
@@ -18311,19 +18687,19 @@ function listAvailableUpgradeTargets(currentVersion, releases) {
|
|
|
18311
18687
|
}
|
|
18312
18688
|
function resolvePromptDirectory(explicitPath) {
|
|
18313
18689
|
if (explicitPath) {
|
|
18314
|
-
return
|
|
18690
|
+
return import_node_path50.default.resolve(explicitPath);
|
|
18315
18691
|
}
|
|
18316
|
-
const entryFile = process.argv[1] ?
|
|
18317
|
-
const entryDir =
|
|
18318
|
-
const resolvedEntryDir =
|
|
18692
|
+
const entryFile = process.argv[1] ? import_node_path50.default.resolve(process.argv[1]) : __filename;
|
|
18693
|
+
const entryDir = import_node_path50.default.dirname(entryFile);
|
|
18694
|
+
const resolvedEntryDir = import_node_fs38.default.existsSync(entryFile) ? import_node_path50.default.dirname(import_node_fs38.default.realpathSync(entryFile)) : entryDir;
|
|
18319
18695
|
const candidates = [
|
|
18320
|
-
|
|
18321
|
-
|
|
18322
|
-
|
|
18323
|
-
|
|
18324
|
-
|
|
18696
|
+
import_node_path50.default.resolve(resolvedEntryDir, "..", UPGRADE_PROMPTS_DIR),
|
|
18697
|
+
import_node_path50.default.resolve(entryDir, "..", UPGRADE_PROMPTS_DIR),
|
|
18698
|
+
import_node_path50.default.resolve(__dirname, "..", UPGRADE_PROMPTS_DIR),
|
|
18699
|
+
import_node_path50.default.resolve(__dirname, "..", "..", UPGRADE_PROMPTS_DIR),
|
|
18700
|
+
import_node_path50.default.resolve(process.cwd(), UPGRADE_PROMPTS_DIR)
|
|
18325
18701
|
];
|
|
18326
|
-
const existing = candidates.find((candidate) =>
|
|
18702
|
+
const existing = candidates.find((candidate) => import_node_fs38.default.existsSync(candidate));
|
|
18327
18703
|
return existing ?? candidates[0];
|
|
18328
18704
|
}
|
|
18329
18705
|
function loadUpgradeCatalog(options = {}) {
|
|
@@ -18339,12 +18715,12 @@ function loadUpgradeCatalog(options = {}) {
|
|
|
18339
18715
|
releases
|
|
18340
18716
|
};
|
|
18341
18717
|
}
|
|
18342
|
-
var
|
|
18718
|
+
var import_node_fs38, import_node_path50, SEMVER_RE, UPGRADE_PROMPTS_DIR;
|
|
18343
18719
|
var init_upgrade = __esm({
|
|
18344
18720
|
"src/core/upgrade.ts"() {
|
|
18345
18721
|
"use strict";
|
|
18346
|
-
|
|
18347
|
-
|
|
18722
|
+
import_node_fs38 = __toESM(require("fs"));
|
|
18723
|
+
import_node_path50 = __toESM(require("path"));
|
|
18348
18724
|
init_repo_config();
|
|
18349
18725
|
SEMVER_RE = /^v?(\d+)\.(\d+)\.(\d+)$/;
|
|
18350
18726
|
UPGRADE_PROMPTS_DIR = "upgrade-prompts";
|
|
@@ -18487,7 +18863,7 @@ function isLoopbackHost(host) {
|
|
|
18487
18863
|
return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
|
|
18488
18864
|
}
|
|
18489
18865
|
function inspectCompose(composeFile, _workspace) {
|
|
18490
|
-
if (!
|
|
18866
|
+
if (!import_node_fs39.default.existsSync(composeFile)) {
|
|
18491
18867
|
return {
|
|
18492
18868
|
aliases: [],
|
|
18493
18869
|
publishedPorts: [],
|
|
@@ -18496,7 +18872,7 @@ function inspectCompose(composeFile, _workspace) {
|
|
|
18496
18872
|
};
|
|
18497
18873
|
}
|
|
18498
18874
|
try {
|
|
18499
|
-
const raw =
|
|
18875
|
+
const raw = import_node_fs39.default.readFileSync(composeFile, "utf-8");
|
|
18500
18876
|
const parsed = import_yaml5.default.parse(raw);
|
|
18501
18877
|
const root = asRecord(parsed);
|
|
18502
18878
|
const services = asRecord(root?.services);
|
|
@@ -18552,12 +18928,51 @@ function routedProxyApps(config) {
|
|
|
18552
18928
|
(app) => app.kind !== "dependency" && app.runtime === "proxy"
|
|
18553
18929
|
);
|
|
18554
18930
|
}
|
|
18931
|
+
function buildHostPortClaimsCheck(repoPath, workspace) {
|
|
18932
|
+
try {
|
|
18933
|
+
const linked = isLinkedWorktree(repoPath);
|
|
18934
|
+
const runtime = loadRuntimeConfig(repoPath);
|
|
18935
|
+
const plan = inspectManagedDevcontainerConfig({
|
|
18936
|
+
repoPath,
|
|
18937
|
+
config: runtime.config,
|
|
18938
|
+
profile: runtime.resolvedProfile,
|
|
18939
|
+
linked
|
|
18940
|
+
});
|
|
18941
|
+
const gitCommonDir = linked ? resolveGitCommonDir(repoPath) : void 0;
|
|
18942
|
+
const workspaceEnv = linked && workspace && gitCommonDir ? { token: workspace, gitCommonDir } : void 0;
|
|
18943
|
+
const conflicts = detectHostPortClaimConflicts({ repoPath, plan, workspace: workspaceEnv });
|
|
18944
|
+
if (conflicts.length === 0) {
|
|
18945
|
+
return {
|
|
18946
|
+
id: "repo.host-port-claims",
|
|
18947
|
+
level: "ok",
|
|
18948
|
+
summary: "No configured fixed host-port binding conflicts with a running container."
|
|
18949
|
+
};
|
|
18950
|
+
}
|
|
18951
|
+
return {
|
|
18952
|
+
id: "repo.host-port-claims",
|
|
18953
|
+
level: "error",
|
|
18954
|
+
summary: `${conflicts.length} configured fixed host-port binding${conflicts.length === 1 ? "" : "s"} conflict${conflicts.length === 1 ? "s" : ""} with running containers.`,
|
|
18955
|
+
details: conflicts.map(
|
|
18956
|
+
(conflict) => `${conflict.service}: ${conflict.hostIp ?? "*"}:${conflict.hostPort}/${conflict.protocol} held by '${conflict.holderContainer}'` + (conflict.holderWorkspace ? ` (workspace '${conflict.holderWorkspace}')` : "")
|
|
18957
|
+
).join("; "),
|
|
18958
|
+
suggestion: "Stop the holding workspace or change the consumer's own published binding, then run ensure again. Devrouter never rewrites consumer-declared host bindings."
|
|
18959
|
+
};
|
|
18960
|
+
} catch (error) {
|
|
18961
|
+
return {
|
|
18962
|
+
id: "repo.host-port-claims",
|
|
18963
|
+
level: "warn",
|
|
18964
|
+
summary: "Could not compare configured fixed host-port bindings against live holders.",
|
|
18965
|
+
details: error instanceof Error ? error.message : String(error),
|
|
18966
|
+
suggestion: "Check Docker availability; a managed ensure refuses on this evidence before starting."
|
|
18967
|
+
};
|
|
18968
|
+
}
|
|
18969
|
+
}
|
|
18555
18970
|
function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
18556
|
-
const devcontainerDir =
|
|
18557
|
-
if (!
|
|
18971
|
+
const devcontainerDir = import_node_path51.default.join(repoPath, ".devcontainer");
|
|
18972
|
+
if (!import_node_fs39.default.existsSync(devcontainerDir)) {
|
|
18558
18973
|
return [];
|
|
18559
18974
|
}
|
|
18560
|
-
const compose = inspectCompose(
|
|
18975
|
+
const compose = inspectCompose(import_node_path51.default.join(devcontainerDir, "docker-compose.yml"), workspace);
|
|
18561
18976
|
const checks = [];
|
|
18562
18977
|
if (compose.parseError) {
|
|
18563
18978
|
checks.push({
|
|
@@ -18584,9 +18999,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
18584
18999
|
details: compose.publishedPorts.length > 0 ? compose.publishedPorts.join(", ") : void 0,
|
|
18585
19000
|
suggestion: compose.parseError || compose.publishedPorts.length > 0 ? "Remove published ports and route services through devnet aliases with devrouter proxy apps." : void 0
|
|
18586
19001
|
});
|
|
18587
|
-
const dockerfilePath =
|
|
18588
|
-
const dockerfileExists =
|
|
18589
|
-
const dockerfile = dockerfileExists ?
|
|
19002
|
+
const dockerfilePath = import_node_path51.default.join(devcontainerDir, "Dockerfile");
|
|
19003
|
+
const dockerfileExists = import_node_fs39.default.existsSync(dockerfilePath);
|
|
19004
|
+
const dockerfile = dockerfileExists ? import_node_fs39.default.readFileSync(dockerfilePath, "utf-8") : "";
|
|
18590
19005
|
const devrouterArtifacts = ["@devrouter/cli", "devrouter-process"].filter(
|
|
18591
19006
|
(artifact) => dockerfile.includes(artifact)
|
|
18592
19007
|
);
|
|
@@ -18623,6 +19038,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
18623
19038
|
});
|
|
18624
19039
|
return checks;
|
|
18625
19040
|
}
|
|
19041
|
+
if (config.managedRuntime) {
|
|
19042
|
+
checks.push(buildHostPortClaimsCheck(repoPath, workspace));
|
|
19043
|
+
}
|
|
18626
19044
|
const aliasSet = new Set(
|
|
18627
19045
|
compose.aliases.flatMap((alias) => expandAliasCandidates(alias, workspace)).map(normalizeWorkspaceToken)
|
|
18628
19046
|
);
|
|
@@ -18641,14 +19059,19 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
18641
19059
|
});
|
|
18642
19060
|
return checks;
|
|
18643
19061
|
}
|
|
18644
|
-
var
|
|
19062
|
+
var import_node_fs39, import_node_path51, import_yaml5;
|
|
18645
19063
|
var init_devcontainer_diagnostics = __esm({
|
|
18646
19064
|
"src/core/devcontainer-diagnostics.ts"() {
|
|
18647
19065
|
"use strict";
|
|
18648
|
-
|
|
18649
|
-
|
|
19066
|
+
import_node_fs39 = __toESM(require("fs"));
|
|
19067
|
+
import_node_path51 = __toESM(require("path"));
|
|
18650
19068
|
import_yaml5 = __toESM(require("yaml"));
|
|
19069
|
+
init_devcontainer_profile();
|
|
19070
|
+
init_host_port_claims();
|
|
18651
19071
|
init_managed_post_start();
|
|
19072
|
+
init_repo_config();
|
|
19073
|
+
init_workspace();
|
|
19074
|
+
init_workspace_ownership();
|
|
18652
19075
|
}
|
|
18653
19076
|
});
|
|
18654
19077
|
|
|
@@ -18736,11 +19159,11 @@ function reconcileRouteRunConflict(repoPath, app) {
|
|
|
18736
19159
|
}
|
|
18737
19160
|
}
|
|
18738
19161
|
}
|
|
18739
|
-
var
|
|
19162
|
+
var import_node_fs40;
|
|
18740
19163
|
var init_route_state = __esm({
|
|
18741
19164
|
"src/core/route-state.ts"() {
|
|
18742
19165
|
"use strict";
|
|
18743
|
-
|
|
19166
|
+
import_node_fs40 = __toESM(require("fs"));
|
|
18744
19167
|
init_host_routes();
|
|
18745
19168
|
init_workspace();
|
|
18746
19169
|
}
|
|
@@ -18915,7 +19338,7 @@ function toRepoStatus(repoPath) {
|
|
|
18915
19338
|
const resolvedRepoPath = resolveRepoPath(repoPath);
|
|
18916
19339
|
const configPath = getRepoConfigPath(resolvedRepoPath);
|
|
18917
19340
|
const explicitRepo = typeof repoPath === "string" && repoPath.trim().length > 0;
|
|
18918
|
-
const configExists =
|
|
19341
|
+
const configExists = import_node_fs41.default.existsSync(configPath);
|
|
18919
19342
|
if (!explicitRepo && !configExists) {
|
|
18920
19343
|
return void 0;
|
|
18921
19344
|
}
|
|
@@ -19063,11 +19486,11 @@ async function collectRouterStatus(repoPath) {
|
|
|
19063
19486
|
}
|
|
19064
19487
|
};
|
|
19065
19488
|
}
|
|
19066
|
-
var
|
|
19489
|
+
var import_node_fs41;
|
|
19067
19490
|
var init_status = __esm({
|
|
19068
19491
|
"src/core/status.ts"() {
|
|
19069
19492
|
"use strict";
|
|
19070
|
-
|
|
19493
|
+
import_node_fs41 = __toESM(require("fs"));
|
|
19071
19494
|
init_docker();
|
|
19072
19495
|
init_managed_runtime_state();
|
|
19073
19496
|
init_managed_runtime_status();
|
|
@@ -19084,7 +19507,7 @@ function outputFromResult(result) {
|
|
|
19084
19507
|
return output2.length > 0 ? output2 : void 0;
|
|
19085
19508
|
}
|
|
19086
19509
|
function runTool(command, args = []) {
|
|
19087
|
-
const result = (0,
|
|
19510
|
+
const result = (0, import_node_child_process29.spawnSync)(command, args, {
|
|
19088
19511
|
encoding: "utf-8"
|
|
19089
19512
|
});
|
|
19090
19513
|
if (result.error) {
|
|
@@ -19141,12 +19564,12 @@ function parseMinimumNodeMajor(value) {
|
|
|
19141
19564
|
return Number(match[1]);
|
|
19142
19565
|
}
|
|
19143
19566
|
function readPackageJson(repoPath) {
|
|
19144
|
-
const packagePath =
|
|
19145
|
-
if (!
|
|
19567
|
+
const packagePath = import_node_path52.default.join(repoPath, "package.json");
|
|
19568
|
+
if (!import_node_fs42.default.existsSync(packagePath)) {
|
|
19146
19569
|
return void 0;
|
|
19147
19570
|
}
|
|
19148
19571
|
try {
|
|
19149
|
-
return JSON.parse(
|
|
19572
|
+
return JSON.parse(import_node_fs42.default.readFileSync(packagePath, "utf-8"));
|
|
19150
19573
|
} catch {
|
|
19151
19574
|
return void 0;
|
|
19152
19575
|
}
|
|
@@ -19330,13 +19753,13 @@ function buildGlobalToolChecksWithoutTelemetry(repoPath) {
|
|
|
19330
19753
|
checks.push(nodeToolchainCheck(repoPath));
|
|
19331
19754
|
return checks;
|
|
19332
19755
|
}
|
|
19333
|
-
var
|
|
19756
|
+
var import_node_child_process29, import_node_fs42, import_node_path52, DEVSY_DISABLE_TELEMETRY;
|
|
19334
19757
|
var init_tool_diagnostics = __esm({
|
|
19335
19758
|
"src/core/tool-diagnostics.ts"() {
|
|
19336
19759
|
"use strict";
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19760
|
+
import_node_child_process29 = require("child_process");
|
|
19761
|
+
import_node_fs42 = __toESM(require("fs"));
|
|
19762
|
+
import_node_path52 = __toESM(require("path"));
|
|
19340
19763
|
init_devsy_agent();
|
|
19341
19764
|
init_workspace_runtime();
|
|
19342
19765
|
DEVSY_DISABLE_TELEMETRY = "DEVSY_DISABLE_TELEMETRY";
|
|
@@ -19353,10 +19776,10 @@ function inRepositoryWorkspaceScope(repoPath, worktreePath, livePaths) {
|
|
|
19353
19776
|
if (livePaths.some((candidate) => sameWorkspacePath(candidate, worktreePath))) return true;
|
|
19354
19777
|
const comparableRepo = comparableWorkspacePath(repoPath);
|
|
19355
19778
|
const comparableWorktree = comparableWorkspacePath(worktreePath);
|
|
19356
|
-
const localRoot =
|
|
19779
|
+
const localRoot = import_node_path53.default.join(comparableRepo, "trees") + import_node_path53.default.sep;
|
|
19357
19780
|
if (comparableWorktree.startsWith(localRoot)) return true;
|
|
19358
|
-
const legacyPrefix = `${
|
|
19359
|
-
return
|
|
19781
|
+
const legacyPrefix = `${import_node_path53.default.basename(comparableRepo)}-`;
|
|
19782
|
+
return import_node_path53.default.dirname(comparableWorktree) === import_node_path53.default.dirname(comparableRepo) && import_node_path53.default.basename(comparableWorktree).startsWith(legacyPrefix);
|
|
19360
19783
|
}
|
|
19361
19784
|
function previewActions(devpodStatus, routeCount, includeRecord) {
|
|
19362
19785
|
const actions = [
|
|
@@ -19608,11 +20031,11 @@ function applyWorkspaceGc(plan) {
|
|
|
19608
20031
|
candidates
|
|
19609
20032
|
};
|
|
19610
20033
|
}
|
|
19611
|
-
var
|
|
20034
|
+
var import_node_path53;
|
|
19612
20035
|
var init_workspace_gc = __esm({
|
|
19613
20036
|
"src/core/workspace-gc.ts"() {
|
|
19614
20037
|
"use strict";
|
|
19615
|
-
|
|
20038
|
+
import_node_path53 = __toESM(require("path"));
|
|
19616
20039
|
init_devpod_mutation();
|
|
19617
20040
|
init_devpod_workspaces();
|
|
19618
20041
|
init_host_routes();
|
|
@@ -19682,11 +20105,11 @@ function inspectPostgresCredentials(repoPath, config) {
|
|
|
19682
20105
|
parseErrors.push(`${app.name}: ${composeFile} (${message})`);
|
|
19683
20106
|
continue;
|
|
19684
20107
|
}
|
|
19685
|
-
if (!
|
|
20108
|
+
if (!import_node_fs43.default.existsSync(absolutePath)) {
|
|
19686
20109
|
continue;
|
|
19687
20110
|
}
|
|
19688
20111
|
try {
|
|
19689
|
-
const raw =
|
|
20112
|
+
const raw = import_node_fs43.default.readFileSync(absolutePath, "utf-8");
|
|
19690
20113
|
const parsed = import_yaml6.default.parse(raw);
|
|
19691
20114
|
const root = asRecord2(parsed);
|
|
19692
20115
|
const services = asRecord2(root?.services);
|
|
@@ -19966,7 +20389,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
19966
20389
|
const config = runtimeConfig.config;
|
|
19967
20390
|
loadedConfig = config;
|
|
19968
20391
|
loadedWorkspace = runtimeConfig.workspace;
|
|
19969
|
-
const cliVersion = true ? "0.0.
|
|
20392
|
+
const cliVersion = true ? "0.0.70" : "0.0.0-dev";
|
|
19970
20393
|
const configVersion = config.devrouter?.version;
|
|
19971
20394
|
if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
|
|
19972
20395
|
addCheck(checks, {
|
|
@@ -19996,9 +20419,9 @@ async function buildDoctorReport(options = {}) {
|
|
|
19996
20419
|
(app) => app.docker.composeFiles.map((filePath) => ({
|
|
19997
20420
|
app: app.name,
|
|
19998
20421
|
filePath,
|
|
19999
|
-
absolutePath:
|
|
20422
|
+
absolutePath: import_node_path54.default.resolve(repo.path, filePath)
|
|
20000
20423
|
}))
|
|
20001
|
-
).filter((entry) => !
|
|
20424
|
+
).filter((entry) => !import_node_fs43.default.existsSync(entry.absolutePath));
|
|
20002
20425
|
addCheck(checks, {
|
|
20003
20426
|
id: "repo.compose-files",
|
|
20004
20427
|
level: missingComposeFiles.length === 0 ? "ok" : "error",
|
|
@@ -20041,8 +20464,8 @@ async function buildDoctorReport(options = {}) {
|
|
|
20041
20464
|
const missingHostCwds = config.apps.filter((app) => app.runtime === "host").map((app) => ({
|
|
20042
20465
|
app: app.name,
|
|
20043
20466
|
cwd: app.hostRun.cwd,
|
|
20044
|
-
absolutePath:
|
|
20045
|
-
})).filter((entry) => !
|
|
20467
|
+
absolutePath: import_node_path54.default.resolve(repo.path, app.hostRun.cwd)
|
|
20468
|
+
})).filter((entry) => !import_node_fs43.default.existsSync(entry.absolutePath));
|
|
20046
20469
|
addCheck(checks, {
|
|
20047
20470
|
id: "repo.host-cwd",
|
|
20048
20471
|
level: missingHostCwds.length === 0 ? "ok" : "error",
|
|
@@ -20186,12 +20609,12 @@ async function buildDoctorReport(options = {}) {
|
|
|
20186
20609
|
nextSteps
|
|
20187
20610
|
};
|
|
20188
20611
|
}
|
|
20189
|
-
var
|
|
20612
|
+
var import_node_fs43, import_node_path54, import_yaml6, POSTGRES_DEFAULTS;
|
|
20190
20613
|
var init_doctor = __esm({
|
|
20191
20614
|
"src/core/doctor.ts"() {
|
|
20192
20615
|
"use strict";
|
|
20193
|
-
|
|
20194
|
-
|
|
20616
|
+
import_node_fs43 = __toESM(require("fs"));
|
|
20617
|
+
import_node_path54 = __toESM(require("path"));
|
|
20195
20618
|
import_yaml6 = __toESM(require("yaml"));
|
|
20196
20619
|
init_docker();
|
|
20197
20620
|
init_host_routes();
|
|
@@ -20507,12 +20930,12 @@ function parseSsPortListeners(stdout, port) {
|
|
|
20507
20930
|
return listeners;
|
|
20508
20931
|
}
|
|
20509
20932
|
function findPortListeners(port) {
|
|
20510
|
-
const result = (0,
|
|
20933
|
+
const result = (0, import_node_child_process30.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
|
|
20511
20934
|
encoding: "utf-8"
|
|
20512
20935
|
});
|
|
20513
20936
|
if (result.error && result.error.code === "ENOENT") {
|
|
20514
20937
|
if (process.platform === "linux") {
|
|
20515
|
-
const ssResult = (0,
|
|
20938
|
+
const ssResult = (0, import_node_child_process30.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
|
|
20516
20939
|
if (ssResult.status === 0 && ssResult.stdout) {
|
|
20517
20940
|
return parseSsPortListeners(ssResult.stdout, port);
|
|
20518
20941
|
}
|
|
@@ -20541,11 +20964,11 @@ function findPortListeners(port) {
|
|
|
20541
20964
|
};
|
|
20542
20965
|
});
|
|
20543
20966
|
}
|
|
20544
|
-
var
|
|
20967
|
+
var import_node_child_process30;
|
|
20545
20968
|
var init_ports = __esm({
|
|
20546
20969
|
"src/util/ports.ts"() {
|
|
20547
20970
|
"use strict";
|
|
20548
|
-
|
|
20971
|
+
import_node_child_process30 = require("child_process");
|
|
20549
20972
|
}
|
|
20550
20973
|
});
|
|
20551
20974
|
|
|
@@ -20632,14 +21055,33 @@ async function runEnsureCommand(options) {
|
|
|
20632
21055
|
profile: options.profile,
|
|
20633
21056
|
...options.repair ? { repair: true } : {}
|
|
20634
21057
|
});
|
|
21058
|
+
const conflicts = result.hostPortConflicts ?? [];
|
|
20635
21059
|
const applicationFailed = result.applicationReadiness?.status === "application-error";
|
|
20636
|
-
if (applicationFailed) process.exitCode = 1;
|
|
21060
|
+
if (applicationFailed || conflicts.length > 0) process.exitCode = 1;
|
|
20637
21061
|
if (options.json) {
|
|
20638
21062
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
20639
21063
|
`);
|
|
20640
21064
|
return;
|
|
20641
21065
|
}
|
|
20642
21066
|
const label2 = result.kind === "primary" ? `Primary checkout [profile: ${result.profile}]` : `Workspace '${result.workspace}' [profile: ${result.profile}]`;
|
|
21067
|
+
if (conflicts.length > 0) {
|
|
21068
|
+
const lines = conflicts.map((conflict) => {
|
|
21069
|
+
const desired = `${conflict.hostIp ?? "*"}:${conflict.hostPort}/${conflict.protocol}`;
|
|
21070
|
+
const holder = [
|
|
21071
|
+
`'${conflict.holderContainer}'`,
|
|
21072
|
+
conflict.holderComposeProject ? `compose project '${conflict.holderComposeProject}'` : void 0,
|
|
21073
|
+
conflict.holderWorkspace ? `workspace '${conflict.holderWorkspace}'` : void 0
|
|
21074
|
+
].filter(Boolean).join(", ");
|
|
21075
|
+
return ` - service '${conflict.service}' needs ${desired}, held by ${holder}
|
|
21076
|
+
${conflict.remediation}`;
|
|
21077
|
+
});
|
|
21078
|
+
process.stdout.write(
|
|
21079
|
+
`${label2} was not started: ${conflicts.length} fixed host-port claim${conflicts.length === 1 ? "" : "s"} conflict${conflicts.length === 1 ? "s" : ""} with running containers.
|
|
21080
|
+
${lines.join("\n")}
|
|
21081
|
+
`
|
|
21082
|
+
);
|
|
21083
|
+
return;
|
|
21084
|
+
}
|
|
20643
21085
|
const routes = result.urls.map((url) => ` ${url}`).join("\n");
|
|
20644
21086
|
process.stdout.write(
|
|
20645
21087
|
`${label2} ${applicationFailed ? "has available infrastructure but failed its application readiness contract" : "is ready"} (${result.devpodId}).
|
|
@@ -20891,7 +21333,7 @@ async function runOpenCommand(name) {
|
|
|
20891
21333
|
);
|
|
20892
21334
|
return;
|
|
20893
21335
|
}
|
|
20894
|
-
const result = (0,
|
|
21336
|
+
const result = (0, import_node_child_process31.spawnSync)("open", [url], { encoding: "utf-8" });
|
|
20895
21337
|
if (result.status !== 0) {
|
|
20896
21338
|
const details = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
20897
21339
|
throw new Error(`Unable to open '${url}': ${details || "unknown error"}`);
|
|
@@ -20899,11 +21341,11 @@ async function runOpenCommand(name) {
|
|
|
20899
21341
|
process.stdout.write(`Opened ${url}
|
|
20900
21342
|
`);
|
|
20901
21343
|
}
|
|
20902
|
-
var
|
|
21344
|
+
var import_node_child_process31;
|
|
20903
21345
|
var init_open = __esm({
|
|
20904
21346
|
"src/commands/open.ts"() {
|
|
20905
21347
|
"use strict";
|
|
20906
|
-
|
|
21348
|
+
import_node_child_process31 = require("child_process");
|
|
20907
21349
|
init_docker();
|
|
20908
21350
|
init_host_routes();
|
|
20909
21351
|
init_repo_config();
|
|
@@ -20926,7 +21368,7 @@ async function runLogsCommand(options) {
|
|
|
20926
21368
|
const args = ["logs", "--tail", tail, ROUTER_CONTAINER_NAME];
|
|
20927
21369
|
if (options.follow) {
|
|
20928
21370
|
args.splice(1, 0, "-f");
|
|
20929
|
-
const child = (0,
|
|
21371
|
+
const child = (0, import_node_child_process32.spawn)("docker", args, { stdio: "inherit" });
|
|
20930
21372
|
const onSignal = () => {
|
|
20931
21373
|
child.kill("SIGTERM");
|
|
20932
21374
|
};
|
|
@@ -20940,17 +21382,17 @@ async function runLogsCommand(options) {
|
|
|
20940
21382
|
});
|
|
20941
21383
|
});
|
|
20942
21384
|
} else {
|
|
20943
|
-
const result = (0,
|
|
21385
|
+
const result = (0, import_node_child_process32.spawnSync)("docker", args, { stdio: "inherit" });
|
|
20944
21386
|
if (result.status !== 0) {
|
|
20945
21387
|
throw new Error("Failed to retrieve router logs.");
|
|
20946
21388
|
}
|
|
20947
21389
|
}
|
|
20948
21390
|
}
|
|
20949
|
-
var
|
|
21391
|
+
var import_node_child_process32;
|
|
20950
21392
|
var init_logs = __esm({
|
|
20951
21393
|
"src/commands/logs.ts"() {
|
|
20952
21394
|
"use strict";
|
|
20953
|
-
|
|
21395
|
+
import_node_child_process32 = require("child_process");
|
|
20954
21396
|
init_docker();
|
|
20955
21397
|
init_router();
|
|
20956
21398
|
}
|
|
@@ -20990,17 +21432,17 @@ function asRecord3(value) {
|
|
|
20990
21432
|
return value;
|
|
20991
21433
|
}
|
|
20992
21434
|
function readJson(filePath) {
|
|
20993
|
-
if (!
|
|
21435
|
+
if (!import_node_fs44.default.existsSync(filePath)) {
|
|
20994
21436
|
return void 0;
|
|
20995
21437
|
}
|
|
20996
21438
|
try {
|
|
20997
|
-
return JSON.parse(
|
|
21439
|
+
return JSON.parse(import_node_fs44.default.readFileSync(filePath, "utf-8"));
|
|
20998
21440
|
} catch {
|
|
20999
21441
|
return void 0;
|
|
21000
21442
|
}
|
|
21001
21443
|
}
|
|
21002
21444
|
function relative(repoPath, filePath) {
|
|
21003
|
-
return
|
|
21445
|
+
return import_node_path55.default.relative(repoPath, filePath) || ".";
|
|
21004
21446
|
}
|
|
21005
21447
|
function redactEnvAssignments(value) {
|
|
21006
21448
|
return value.replace(
|
|
@@ -21041,7 +21483,7 @@ function inspectPackageManager(repoPath, pkg) {
|
|
|
21041
21483
|
["bun.lock", "bun"]
|
|
21042
21484
|
];
|
|
21043
21485
|
for (const [fileName, name] of lockfiles) {
|
|
21044
|
-
if (
|
|
21486
|
+
if (import_node_fs44.default.existsSync(import_node_path55.default.join(repoPath, fileName))) {
|
|
21045
21487
|
return { name, source: fileName };
|
|
21046
21488
|
}
|
|
21047
21489
|
}
|
|
@@ -21056,9 +21498,9 @@ function inspectNode(repoPath, pkg) {
|
|
|
21056
21498
|
if (typeof engines?.node === "string") {
|
|
21057
21499
|
return { version: engines.node, source: "package.json:engines.node" };
|
|
21058
21500
|
}
|
|
21059
|
-
const nvmrc =
|
|
21060
|
-
if (
|
|
21061
|
-
const version =
|
|
21501
|
+
const nvmrc = import_node_path55.default.join(repoPath, ".nvmrc");
|
|
21502
|
+
if (import_node_fs44.default.existsSync(nvmrc)) {
|
|
21503
|
+
const version = import_node_fs44.default.readFileSync(nvmrc, "utf-8").trim();
|
|
21062
21504
|
return { version, source: ".nvmrc" };
|
|
21063
21505
|
}
|
|
21064
21506
|
return void 0;
|
|
@@ -21119,7 +21561,7 @@ function configuredComposeFiles(repoPath) {
|
|
|
21119
21561
|
const files = config.apps.filter(
|
|
21120
21562
|
(app) => app.runtime === "docker"
|
|
21121
21563
|
).flatMap((app) => app.docker.composeFiles).filter(
|
|
21122
|
-
(fileName) => !
|
|
21564
|
+
(fileName) => !import_node_path55.default.isAbsolute(fileName) && !import_node_path55.default.normalize(fileName).startsWith("..")
|
|
21123
21565
|
);
|
|
21124
21566
|
return Array.from(new Set(files));
|
|
21125
21567
|
} catch {
|
|
@@ -21137,7 +21579,7 @@ function composeFiles(repoPath) {
|
|
|
21137
21579
|
...configuredComposeFiles(repoPath)
|
|
21138
21580
|
];
|
|
21139
21581
|
return Array.from(new Set(candidates)).filter(
|
|
21140
|
-
(fileName) =>
|
|
21582
|
+
(fileName) => import_node_fs44.default.existsSync(import_node_path55.default.join(repoPath, fileName))
|
|
21141
21583
|
);
|
|
21142
21584
|
}
|
|
21143
21585
|
function stringArray2(value) {
|
|
@@ -21175,7 +21617,7 @@ function inspectServices(repoPath) {
|
|
|
21175
21617
|
const services = [];
|
|
21176
21618
|
for (const fileName of composeFiles(repoPath)) {
|
|
21177
21619
|
try {
|
|
21178
|
-
const parsed = import_yaml7.default.parse(
|
|
21620
|
+
const parsed = import_yaml7.default.parse(import_node_fs44.default.readFileSync(import_node_path55.default.join(repoPath, fileName), "utf-8"));
|
|
21179
21621
|
const serviceMap = asRecord3(asRecord3(parsed)?.services);
|
|
21180
21622
|
for (const [name, value] of Object.entries(serviceMap ?? {})) {
|
|
21181
21623
|
const service = asRecord3(value);
|
|
@@ -21210,8 +21652,8 @@ function inspectServices(repoPath) {
|
|
|
21210
21652
|
return services;
|
|
21211
21653
|
}
|
|
21212
21654
|
function inspectEnvFiles(repoPath) {
|
|
21213
|
-
const files =
|
|
21214
|
-
const content =
|
|
21655
|
+
const files = import_node_fs44.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
|
|
21656
|
+
const content = import_node_fs44.default.readFileSync(import_node_path55.default.join(repoPath, fileName), "utf-8");
|
|
21215
21657
|
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();
|
|
21216
21658
|
return { path: fileName, names };
|
|
21217
21659
|
});
|
|
@@ -21225,18 +21667,18 @@ function inspectEnvFiles(repoPath) {
|
|
|
21225
21667
|
};
|
|
21226
21668
|
}
|
|
21227
21669
|
function inspectDevcontainer(repoPath) {
|
|
21228
|
-
const dir =
|
|
21229
|
-
if (!
|
|
21670
|
+
const dir = import_node_path55.default.join(repoPath, ".devcontainer");
|
|
21671
|
+
if (!import_node_fs44.default.existsSync(dir)) {
|
|
21230
21672
|
return { exists: false, files: [] };
|
|
21231
21673
|
}
|
|
21232
21674
|
return {
|
|
21233
21675
|
exists: true,
|
|
21234
|
-
files:
|
|
21676
|
+
files: import_node_fs44.default.readdirSync(dir).filter((fileName) => import_node_fs44.default.statSync(import_node_path55.default.join(dir, fileName)).isFile()).sort().map((fileName) => `.devcontainer/${fileName}`)
|
|
21235
21677
|
};
|
|
21236
21678
|
}
|
|
21237
21679
|
function inspectDevrouter(repoPath) {
|
|
21238
21680
|
const configPath = getRepoConfigPath(repoPath);
|
|
21239
|
-
if (!
|
|
21681
|
+
if (!import_node_fs44.default.existsSync(configPath)) {
|
|
21240
21682
|
return {
|
|
21241
21683
|
exists: false,
|
|
21242
21684
|
configPath,
|
|
@@ -21280,15 +21722,15 @@ function inspectAgentGuidance(repoPath) {
|
|
|
21280
21722
|
["AGENTS.md", "agents"],
|
|
21281
21723
|
["CLAUDE.md", "claude"]
|
|
21282
21724
|
]) {
|
|
21283
|
-
if (
|
|
21725
|
+
if (import_node_fs44.default.existsSync(import_node_path55.default.join(repoPath, fileName))) {
|
|
21284
21726
|
results.push({ path: fileName, kind });
|
|
21285
21727
|
}
|
|
21286
21728
|
}
|
|
21287
|
-
const skillsDir =
|
|
21288
|
-
if (
|
|
21289
|
-
for (const name of
|
|
21290
|
-
const skillPath =
|
|
21291
|
-
if (
|
|
21729
|
+
const skillsDir = import_node_path55.default.join(repoPath, ".agents", "skills");
|
|
21730
|
+
if (import_node_fs44.default.existsSync(skillsDir)) {
|
|
21731
|
+
for (const name of import_node_fs44.default.readdirSync(skillsDir).sort()) {
|
|
21732
|
+
const skillPath = import_node_path55.default.join(skillsDir, name, "SKILL.md");
|
|
21733
|
+
if (import_node_fs44.default.existsSync(skillPath)) {
|
|
21292
21734
|
results.push({ path: relative(repoPath, skillPath), kind: "skill" });
|
|
21293
21735
|
}
|
|
21294
21736
|
}
|
|
@@ -21331,7 +21773,7 @@ function buildIssues(report) {
|
|
|
21331
21773
|
}
|
|
21332
21774
|
function inspectRepo(options = {}) {
|
|
21333
21775
|
const repoPath = resolveRepoPath(options.repo);
|
|
21334
|
-
const pkg = readJson(
|
|
21776
|
+
const pkg = readJson(import_node_path55.default.join(repoPath, "package.json"));
|
|
21335
21777
|
const scripts = inspectScripts(pkg);
|
|
21336
21778
|
const reportWithoutIssues = {
|
|
21337
21779
|
repoPath,
|
|
@@ -21350,12 +21792,12 @@ function inspectRepo(options = {}) {
|
|
|
21350
21792
|
issues: buildIssues(reportWithoutIssues)
|
|
21351
21793
|
};
|
|
21352
21794
|
}
|
|
21353
|
-
var
|
|
21795
|
+
var import_node_fs44, import_node_path55, import_yaml7;
|
|
21354
21796
|
var init_repo_inspect = __esm({
|
|
21355
21797
|
"src/core/repo-inspect.ts"() {
|
|
21356
21798
|
"use strict";
|
|
21357
|
-
|
|
21358
|
-
|
|
21799
|
+
import_node_fs44 = __toESM(require("fs"));
|
|
21800
|
+
import_node_path55 = __toESM(require("path"));
|
|
21359
21801
|
import_yaml7 = __toESM(require("yaml"));
|
|
21360
21802
|
init_repo_config();
|
|
21361
21803
|
}
|
|
@@ -21460,7 +21902,7 @@ function requiredFileChecks(repoPath) {
|
|
|
21460
21902
|
".devcontainer/docker-compose.yml",
|
|
21461
21903
|
".devrouter.yml"
|
|
21462
21904
|
];
|
|
21463
|
-
const missing = required.filter((fileName) => !
|
|
21905
|
+
const missing = required.filter((fileName) => !import_node_fs45.default.existsSync(import_node_path56.default.join(repoPath, fileName)));
|
|
21464
21906
|
return {
|
|
21465
21907
|
id: "repo.devcontainer.verify-files",
|
|
21466
21908
|
level: missing.length === 0 ? "ok" : "error",
|
|
@@ -21685,12 +22127,12 @@ async function verifyDevcontainer(options = {}) {
|
|
|
21685
22127
|
nextSteps: collectNextSteps3(checks)
|
|
21686
22128
|
};
|
|
21687
22129
|
}
|
|
21688
|
-
var
|
|
22130
|
+
var import_node_fs45, import_node_path56;
|
|
21689
22131
|
var init_devcontainer_verify = __esm({
|
|
21690
22132
|
"src/core/devcontainer-verify.ts"() {
|
|
21691
22133
|
"use strict";
|
|
21692
|
-
|
|
21693
|
-
|
|
22134
|
+
import_node_fs45 = __toESM(require("fs"));
|
|
22135
|
+
import_node_path56 = __toESM(require("path"));
|
|
21694
22136
|
init_capabilities();
|
|
21695
22137
|
init_doctor();
|
|
21696
22138
|
init_host_routes();
|
|
@@ -21987,7 +22429,7 @@ function packageManagerIssues(repo) {
|
|
|
21987
22429
|
}
|
|
21988
22430
|
function plannedFiles(repoPath, version) {
|
|
21989
22431
|
const repo = inspectRepo({ repo: repoPath });
|
|
21990
|
-
const projectName = sanitizeProjectName(
|
|
22432
|
+
const projectName = sanitizeProjectName(import_node_path57.default.basename(repo.repoPath));
|
|
21991
22433
|
const nodeMajor = majorVersion(repo.node?.version, "24");
|
|
21992
22434
|
const pnpmVersion = repo.packageManager?.name === "pnpm" && repo.packageManager.version ? repo.packageManager.version : DEFAULT_PNPM_VERSION;
|
|
21993
22435
|
const port = inferPort2(repo);
|
|
@@ -22032,8 +22474,8 @@ function plannedFiles(repoPath, version) {
|
|
|
22032
22474
|
};
|
|
22033
22475
|
}
|
|
22034
22476
|
function classifyFile(repoPath, file) {
|
|
22035
|
-
const absolutePath =
|
|
22036
|
-
if (!
|
|
22477
|
+
const absolutePath = import_node_path57.default.join(repoPath, file.relativePath);
|
|
22478
|
+
if (!import_node_fs46.default.existsSync(absolutePath)) {
|
|
22037
22479
|
return {
|
|
22038
22480
|
path: file.relativePath,
|
|
22039
22481
|
action: "create",
|
|
@@ -22041,7 +22483,7 @@ function classifyFile(repoPath, file) {
|
|
|
22041
22483
|
bytes: Buffer.byteLength(file.content)
|
|
22042
22484
|
};
|
|
22043
22485
|
}
|
|
22044
|
-
const current =
|
|
22486
|
+
const current = import_node_fs46.default.readFileSync(absolutePath, "utf-8");
|
|
22045
22487
|
if (!current.includes(MANAGED_MARKER2)) {
|
|
22046
22488
|
return {
|
|
22047
22489
|
path: file.relativePath,
|
|
@@ -22098,11 +22540,11 @@ function buildPlan(repoPath, dryRun, version) {
|
|
|
22098
22540
|
};
|
|
22099
22541
|
}
|
|
22100
22542
|
function writeFile(repoPath, file) {
|
|
22101
|
-
const absolutePath =
|
|
22102
|
-
|
|
22103
|
-
|
|
22543
|
+
const absolutePath = import_node_path57.default.join(repoPath, file.relativePath);
|
|
22544
|
+
import_node_fs46.default.mkdirSync(import_node_path57.default.dirname(absolutePath), { recursive: true });
|
|
22545
|
+
import_node_fs46.default.writeFileSync(absolutePath, file.content, "utf-8");
|
|
22104
22546
|
if (file.executable) {
|
|
22105
|
-
|
|
22547
|
+
import_node_fs46.default.chmodSync(absolutePath, 493);
|
|
22106
22548
|
}
|
|
22107
22549
|
}
|
|
22108
22550
|
function writeDevcontainer(options = {}) {
|
|
@@ -22140,12 +22582,12 @@ function writeDevcontainer(options = {}) {
|
|
|
22140
22582
|
nextSteps: postWriteNextSteps(repoPath)
|
|
22141
22583
|
};
|
|
22142
22584
|
}
|
|
22143
|
-
var
|
|
22585
|
+
var import_node_fs46, import_node_path57, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
|
|
22144
22586
|
var init_devcontainer_write = __esm({
|
|
22145
22587
|
"src/core/devcontainer-write.ts"() {
|
|
22146
22588
|
"use strict";
|
|
22147
|
-
|
|
22148
|
-
|
|
22589
|
+
import_node_fs46 = __toESM(require("fs"));
|
|
22590
|
+
import_node_path57 = __toESM(require("path"));
|
|
22149
22591
|
init_repo_config();
|
|
22150
22592
|
init_repo_inspect();
|
|
22151
22593
|
MANAGED_MARKER2 = "devrouter:managed devcontainer";
|
|
@@ -22258,11 +22700,11 @@ var init_repo_devcontainer = __esm({
|
|
|
22258
22700
|
function fail3(message) {
|
|
22259
22701
|
throw new Error(message);
|
|
22260
22702
|
}
|
|
22261
|
-
function
|
|
22703
|
+
function isRecord7(value) {
|
|
22262
22704
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22263
22705
|
}
|
|
22264
22706
|
function requireRecord(value, label2) {
|
|
22265
|
-
if (!
|
|
22707
|
+
if (!isRecord7(value)) fail3(`${label2} must be a mapping.`);
|
|
22266
22708
|
return value;
|
|
22267
22709
|
}
|
|
22268
22710
|
function requireExactKeys(value, allowed, label2) {
|
|
@@ -22479,13 +22921,13 @@ function buildProfilePlanReport(options) {
|
|
|
22479
22921
|
return { ...report, contractPath, bindings };
|
|
22480
22922
|
}
|
|
22481
22923
|
function loadContract(repoPath, contractPath) {
|
|
22482
|
-
if (
|
|
22924
|
+
if (import_node_path58.default.isAbsolute(contractPath)) {
|
|
22483
22925
|
fail3("profile plan contract path must be relative to the repository.");
|
|
22484
22926
|
}
|
|
22485
22927
|
const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
|
|
22486
22928
|
let metadata;
|
|
22487
22929
|
try {
|
|
22488
|
-
metadata =
|
|
22930
|
+
metadata = import_node_fs47.default.lstatSync(absolutePath);
|
|
22489
22931
|
} catch (error) {
|
|
22490
22932
|
fail3(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
|
|
22491
22933
|
}
|
|
@@ -22495,16 +22937,16 @@ function loadContract(repoPath, contractPath) {
|
|
|
22495
22937
|
if (metadata.size > MAX_CONTRACT_BYTES) {
|
|
22496
22938
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
22497
22939
|
}
|
|
22498
|
-
const realRepo =
|
|
22499
|
-
const realContract =
|
|
22940
|
+
const realRepo = import_node_fs47.default.realpathSync(repoPath);
|
|
22941
|
+
const realContract = import_node_fs47.default.realpathSync(absolutePath);
|
|
22500
22942
|
assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
|
|
22501
|
-
const source =
|
|
22943
|
+
const source = import_node_fs47.default.readFileSync(absolutePath, "utf-8");
|
|
22502
22944
|
if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
|
|
22503
22945
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
22504
22946
|
}
|
|
22505
22947
|
return {
|
|
22506
22948
|
contract: parseProfilePlanContract(source),
|
|
22507
|
-
contractPath:
|
|
22949
|
+
contractPath: import_node_path58.default.relative(repoPath, absolutePath).split(import_node_path58.default.sep).join("/")
|
|
22508
22950
|
};
|
|
22509
22951
|
}
|
|
22510
22952
|
function resolveProfilePlan(options) {
|
|
@@ -22518,12 +22960,12 @@ function resolveProfilePlan(options) {
|
|
|
22518
22960
|
contractPath: loaded.contractPath
|
|
22519
22961
|
});
|
|
22520
22962
|
}
|
|
22521
|
-
var
|
|
22963
|
+
var import_node_fs47, import_node_path58, import_yaml8, CONTRACT_VERSION, MAX_CONTRACT_BYTES, MAX_APP_MAPPINGS, MAX_BINDING_KEYS, MAX_BINDING_VALUES, MAX_LITERAL_LENGTH, BINDING_KEY;
|
|
22522
22964
|
var init_profile_plan = __esm({
|
|
22523
22965
|
"src/core/profile-plan.ts"() {
|
|
22524
22966
|
"use strict";
|
|
22525
|
-
|
|
22526
|
-
|
|
22967
|
+
import_node_fs47 = __toESM(require("fs"));
|
|
22968
|
+
import_node_path58 = __toESM(require("path"));
|
|
22527
22969
|
import_yaml8 = require("yaml");
|
|
22528
22970
|
init_paths();
|
|
22529
22971
|
init_profile_resolution();
|
|
@@ -22555,7 +22997,7 @@ async function runProfileResolveCommand(options) {
|
|
|
22555
22997
|
async function runProfilePlanCommand(options) {
|
|
22556
22998
|
const report = resolveProfilePlan(options);
|
|
22557
22999
|
if (options.output) {
|
|
22558
|
-
writeFileAtomically(
|
|
23000
|
+
writeFileAtomically(import_node_path59.default.resolve(options.output), `${JSON.stringify(report, null, 2)}
|
|
22559
23001
|
`);
|
|
22560
23002
|
}
|
|
22561
23003
|
if (options.json) {
|
|
@@ -22591,14 +23033,14 @@ function printProfilePlanSummary(report, output2) {
|
|
|
22591
23033
|
process.stdout.write(`Binding ${key}: ${renderValues(report.bindings[key] ?? [])}
|
|
22592
23034
|
`);
|
|
22593
23035
|
}
|
|
22594
|
-
if (output2) process.stdout.write(`Output: ${
|
|
23036
|
+
if (output2) process.stdout.write(`Output: ${import_node_path59.default.resolve(output2)}
|
|
22595
23037
|
`);
|
|
22596
23038
|
}
|
|
22597
|
-
var
|
|
23039
|
+
var import_node_path59;
|
|
22598
23040
|
var init_profile = __esm({
|
|
22599
23041
|
"src/commands/profile.ts"() {
|
|
22600
23042
|
"use strict";
|
|
22601
|
-
|
|
23043
|
+
import_node_path59 = __toESM(require("path"));
|
|
22602
23044
|
init_atomic_file();
|
|
22603
23045
|
init_output();
|
|
22604
23046
|
init_profile_plan();
|
|
@@ -22888,7 +23330,7 @@ function sanitizeRouterId(value) {
|
|
|
22888
23330
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
22889
23331
|
}
|
|
22890
23332
|
function repoHash(repoPath) {
|
|
22891
|
-
return (0, import_node_crypto26.createHash)("sha1").update(
|
|
23333
|
+
return (0, import_node_crypto26.createHash)("sha1").update(import_node_path60.default.resolve(repoPath)).digest("hex").slice(0, 12);
|
|
22892
23334
|
}
|
|
22893
23335
|
function asDockerApp(app) {
|
|
22894
23336
|
return app.runtime === "docker";
|
|
@@ -22967,11 +23409,11 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
|
|
|
22967
23409
|
if (dockerApps.length === 0) {
|
|
22968
23410
|
throw new Error("No docker apps selected to prepare compose overlay.");
|
|
22969
23411
|
}
|
|
22970
|
-
const cachePath =
|
|
22971
|
-
|
|
22972
|
-
const overlayPath =
|
|
23412
|
+
const cachePath = import_node_path60.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
|
|
23413
|
+
import_node_fs48.default.mkdirSync(cachePath, { recursive: true });
|
|
23414
|
+
const overlayPath = import_node_path60.default.join(cachePath, "compose.devrouter.yml");
|
|
22973
23415
|
const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
|
|
22974
|
-
|
|
23416
|
+
import_node_fs48.default.writeFileSync(overlayPath, import_yaml9.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
|
|
22975
23417
|
return {
|
|
22976
23418
|
overlayPath,
|
|
22977
23419
|
composeFiles: ensureComposeFiles(dockerApps),
|
|
@@ -22985,7 +23427,7 @@ function runDockerComposeUp(repoPath, composeFiles2, overlayPath, services) {
|
|
|
22985
23427
|
fileArgs.push("-f", resolved);
|
|
22986
23428
|
}
|
|
22987
23429
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "up", "-d", "--wait", ...services];
|
|
22988
|
-
const result = (0,
|
|
23430
|
+
const result = (0, import_node_child_process33.spawnSync)("docker", args, {
|
|
22989
23431
|
encoding: "utf-8",
|
|
22990
23432
|
cwd: repoPath
|
|
22991
23433
|
});
|
|
@@ -23013,7 +23455,7 @@ function queryRunningComposeServices(repoPath, composeFiles2, overlayPath, servi
|
|
|
23013
23455
|
"--services",
|
|
23014
23456
|
...services
|
|
23015
23457
|
];
|
|
23016
|
-
const result = (0,
|
|
23458
|
+
const result = (0, import_node_child_process33.spawnSync)("docker", args, {
|
|
23017
23459
|
encoding: "utf-8",
|
|
23018
23460
|
cwd: repoPath
|
|
23019
23461
|
});
|
|
@@ -23039,7 +23481,7 @@ function runDockerComposeStop(repoPath, composeFiles2, overlayPath, services) {
|
|
|
23039
23481
|
fileArgs.push("-f", resolved);
|
|
23040
23482
|
}
|
|
23041
23483
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "stop", ...services];
|
|
23042
|
-
const result = (0,
|
|
23484
|
+
const result = (0, import_node_child_process33.spawnSync)("docker", args, {
|
|
23043
23485
|
encoding: "utf-8",
|
|
23044
23486
|
cwd: repoPath
|
|
23045
23487
|
});
|
|
@@ -23065,7 +23507,7 @@ function runDockerComposeLogs(repoPath, composeFiles2, overlayPath, services, ta
|
|
|
23065
23507
|
String(tail),
|
|
23066
23508
|
...services
|
|
23067
23509
|
];
|
|
23068
|
-
(0,
|
|
23510
|
+
(0, import_node_child_process33.spawnSync)("docker", args, {
|
|
23069
23511
|
stdio: "inherit",
|
|
23070
23512
|
cwd: repoPath
|
|
23071
23513
|
});
|
|
@@ -23077,7 +23519,7 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
23077
23519
|
fileArgs.push("-f", resolved);
|
|
23078
23520
|
}
|
|
23079
23521
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "port", service, String(internalPort)];
|
|
23080
|
-
const result = (0,
|
|
23522
|
+
const result = (0, import_node_child_process33.spawnSync)("docker", args, {
|
|
23081
23523
|
encoding: "utf-8",
|
|
23082
23524
|
cwd: repoPath
|
|
23083
23525
|
});
|
|
@@ -23091,14 +23533,14 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
23091
23533
|
const port = Number(match[1]);
|
|
23092
23534
|
return Number.isInteger(port) && port > 0 ? port : void 0;
|
|
23093
23535
|
}
|
|
23094
|
-
var
|
|
23536
|
+
var import_node_child_process33, import_node_crypto26, import_node_fs48, import_node_path60, import_yaml9;
|
|
23095
23537
|
var init_docker_run = __esm({
|
|
23096
23538
|
"src/core/docker-run.ts"() {
|
|
23097
23539
|
"use strict";
|
|
23098
|
-
|
|
23540
|
+
import_node_child_process33 = require("child_process");
|
|
23099
23541
|
import_node_crypto26 = require("crypto");
|
|
23100
|
-
|
|
23101
|
-
|
|
23542
|
+
import_node_fs48 = __toESM(require("fs"));
|
|
23543
|
+
import_node_path60 = __toESM(require("path"));
|
|
23102
23544
|
import_yaml9 = __toESM(require("yaml"));
|
|
23103
23545
|
init_docker_error_guidance();
|
|
23104
23546
|
init_paths();
|
|
@@ -23188,7 +23630,7 @@ function isProcessRunning(pid) {
|
|
|
23188
23630
|
}
|
|
23189
23631
|
}
|
|
23190
23632
|
function readProcessTree(rootPid) {
|
|
23191
|
-
const result = (0,
|
|
23633
|
+
const result = (0, import_node_child_process34.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
|
|
23192
23634
|
if (result.status !== 0) {
|
|
23193
23635
|
return [rootPid];
|
|
23194
23636
|
}
|
|
@@ -23299,12 +23741,12 @@ function detectListeningPorts(pids) {
|
|
|
23299
23741
|
if (pids.length === 0) {
|
|
23300
23742
|
return [];
|
|
23301
23743
|
}
|
|
23302
|
-
const result = (0,
|
|
23744
|
+
const result = (0, import_node_child_process34.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
|
|
23303
23745
|
encoding: "utf-8"
|
|
23304
23746
|
});
|
|
23305
23747
|
if (result.error && result.error.code === "ENOENT") {
|
|
23306
23748
|
if (process.platform === "linux") {
|
|
23307
|
-
const ssResult = (0,
|
|
23749
|
+
const ssResult = (0, import_node_child_process34.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
|
|
23308
23750
|
if (ssResult.status === 0 && ssResult.stdout) {
|
|
23309
23751
|
return parseSsListeningPorts(ssResult.stdout, new Set(pids));
|
|
23310
23752
|
}
|
|
@@ -23363,7 +23805,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
|
|
|
23363
23805
|
app.hostRun.command,
|
|
23364
23806
|
true
|
|
23365
23807
|
) : app.hostRun.command;
|
|
23366
|
-
const child = (0,
|
|
23808
|
+
const child = (0, import_node_child_process34.spawn)(spawnCommand, {
|
|
23367
23809
|
cwd: commandCwd,
|
|
23368
23810
|
stdio: "inherit",
|
|
23369
23811
|
shell: true,
|
|
@@ -23731,7 +24173,7 @@ async function execWithAppEnv(options) {
|
|
|
23731
24173
|
options.command[0],
|
|
23732
24174
|
true
|
|
23733
24175
|
);
|
|
23734
|
-
child = (0,
|
|
24176
|
+
child = (0, import_node_child_process34.spawn)(wrapped, {
|
|
23735
24177
|
cwd: deps.repoPath,
|
|
23736
24178
|
stdio: "inherit",
|
|
23737
24179
|
shell: true,
|
|
@@ -23745,7 +24187,7 @@ async function execWithAppEnv(options) {
|
|
|
23745
24187
|
false
|
|
23746
24188
|
);
|
|
23747
24189
|
const [cmd, ...wrappedArgs] = wrapped;
|
|
23748
|
-
child = (0,
|
|
24190
|
+
child = (0, import_node_child_process34.spawn)(cmd, wrappedArgs, {
|
|
23749
24191
|
cwd: deps.repoPath,
|
|
23750
24192
|
stdio: "inherit",
|
|
23751
24193
|
shell: false,
|
|
@@ -23753,7 +24195,7 @@ async function execWithAppEnv(options) {
|
|
|
23753
24195
|
});
|
|
23754
24196
|
}
|
|
23755
24197
|
} else if (options.shell) {
|
|
23756
|
-
child = (0,
|
|
24198
|
+
child = (0, import_node_child_process34.spawn)(options.command[0], {
|
|
23757
24199
|
cwd: deps.repoPath,
|
|
23758
24200
|
stdio: "inherit",
|
|
23759
24201
|
shell: true,
|
|
@@ -23761,7 +24203,7 @@ async function execWithAppEnv(options) {
|
|
|
23761
24203
|
});
|
|
23762
24204
|
} else {
|
|
23763
24205
|
const [command, ...args] = options.command;
|
|
23764
|
-
child = (0,
|
|
24206
|
+
child = (0, import_node_child_process34.spawn)(command, args, {
|
|
23765
24207
|
cwd: deps.repoPath,
|
|
23766
24208
|
stdio: "inherit",
|
|
23767
24209
|
shell: false,
|
|
@@ -23782,11 +24224,11 @@ async function execWithAppEnv(options) {
|
|
|
23782
24224
|
deps.stopDeps();
|
|
23783
24225
|
}
|
|
23784
24226
|
}
|
|
23785
|
-
var
|
|
24227
|
+
var import_node_child_process34, import_node_net5, import_node_process, import_promises, POLL_INTERVAL_MS, DEFAULT_PORT_TIMEOUT_MS, PROCESS_TERMINATION_GRACE_MS;
|
|
23786
24228
|
var init_app_run = __esm({
|
|
23787
24229
|
"src/core/app-run.ts"() {
|
|
23788
24230
|
"use strict";
|
|
23789
|
-
|
|
24231
|
+
import_node_child_process34 = require("child_process");
|
|
23790
24232
|
import_node_net5 = __toESM(require("net"));
|
|
23791
24233
|
import_node_process = require("process");
|
|
23792
24234
|
import_promises = require("readline/promises");
|
|
@@ -23920,13 +24362,13 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
23920
24362
|
const startedAt = Date.now();
|
|
23921
24363
|
let rootStat;
|
|
23922
24364
|
try {
|
|
23923
|
-
rootStat =
|
|
24365
|
+
rootStat = import_node_fs49.default.lstatSync(worktreePath);
|
|
23924
24366
|
} catch (error) {
|
|
23925
24367
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
23926
24368
|
}
|
|
23927
24369
|
let rootEntries;
|
|
23928
24370
|
try {
|
|
23929
|
-
rootEntries =
|
|
24371
|
+
rootEntries = import_node_fs49.default.readdirSync(worktreePath, { withFileTypes: true });
|
|
23930
24372
|
} catch (error) {
|
|
23931
24373
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
23932
24374
|
}
|
|
@@ -23952,10 +24394,10 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
23952
24394
|
timedOut = true;
|
|
23953
24395
|
break;
|
|
23954
24396
|
}
|
|
23955
|
-
const entryPath =
|
|
24397
|
+
const entryPath = import_node_path61.default.join(dirPath, entry.name);
|
|
23956
24398
|
let entryStat;
|
|
23957
24399
|
try {
|
|
23958
|
-
entryStat =
|
|
24400
|
+
entryStat = import_node_fs49.default.lstatSync(entryPath);
|
|
23959
24401
|
} catch (error) {
|
|
23960
24402
|
if (error?.code === "ENOENT") continue;
|
|
23961
24403
|
unreadableReason = describeIncompleteWalk(error);
|
|
@@ -23965,7 +24407,7 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
23965
24407
|
if (!entryStat.isDirectory()) continue;
|
|
23966
24408
|
let childEntries;
|
|
23967
24409
|
try {
|
|
23968
|
-
childEntries =
|
|
24410
|
+
childEntries = import_node_fs49.default.readdirSync(entryPath, { withFileTypes: true });
|
|
23969
24411
|
} catch (error) {
|
|
23970
24412
|
unreadableReason = describeIncompleteWalk(error);
|
|
23971
24413
|
break;
|
|
@@ -24029,12 +24471,12 @@ function describeError(error, worktreePath) {
|
|
|
24029
24471
|
}
|
|
24030
24472
|
return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
|
|
24031
24473
|
}
|
|
24032
|
-
var
|
|
24474
|
+
var import_node_fs49, import_node_path61, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
|
|
24033
24475
|
var init_workspace_consumption = __esm({
|
|
24034
24476
|
"src/core/workspace-consumption.ts"() {
|
|
24035
24477
|
"use strict";
|
|
24036
|
-
|
|
24037
|
-
|
|
24478
|
+
import_node_fs49 = __toESM(require("fs"));
|
|
24479
|
+
import_node_path61 = __toESM(require("path"));
|
|
24038
24480
|
init_devpod_environment();
|
|
24039
24481
|
DEFAULT_DEADLINE_MS = 1e4;
|
|
24040
24482
|
BLOCK_SIZE_BYTES = 512;
|
|
@@ -24042,7 +24484,7 @@ var init_workspace_consumption = __esm({
|
|
|
24042
24484
|
});
|
|
24043
24485
|
|
|
24044
24486
|
// src/core/workspace-cleanup.ts
|
|
24045
|
-
function
|
|
24487
|
+
function isRecord8(value) {
|
|
24046
24488
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
24047
24489
|
}
|
|
24048
24490
|
function isSha(value) {
|
|
@@ -24135,7 +24577,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
|
|
|
24135
24577
|
}
|
|
24136
24578
|
function readGitSnapshot(worktree, commandRunner) {
|
|
24137
24579
|
const comparablePath = comparableWorkspacePath(worktree.path);
|
|
24138
|
-
if (worktree.prunable || !
|
|
24580
|
+
if (worktree.prunable || !import_node_fs50.default.existsSync(comparablePath)) {
|
|
24139
24581
|
return { worktree, checkout: "missing", head: null, committerDate: null };
|
|
24140
24582
|
}
|
|
24141
24583
|
const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
|
|
@@ -24186,14 +24628,14 @@ function parseGitHubChanges(value, project, branch) {
|
|
|
24186
24628
|
if (!Array.isArray(value)) return void 0;
|
|
24187
24629
|
const changes = [];
|
|
24188
24630
|
for (const item of value) {
|
|
24189
|
-
if (!
|
|
24190
|
-
const repository =
|
|
24191
|
-
const headRepository =
|
|
24631
|
+
if (!isRecord8(item)) return void 0;
|
|
24632
|
+
const repository = isRecord8(item.repository) ? item.repository.nameWithOwner : void 0;
|
|
24633
|
+
const headRepository = isRecord8(item.headRepository) ? item.headRepository.nameWithOwner : void 0;
|
|
24192
24634
|
if (repository !== project || headRepository !== project || item.headRefName !== branch || !isSha(item.headRefOid) || typeof item.baseRefName !== "string") {
|
|
24193
24635
|
continue;
|
|
24194
24636
|
}
|
|
24195
24637
|
const merged = item.state === "MERGED" && validTimestamp(item.mergedAt);
|
|
24196
|
-
const mergeCommit =
|
|
24638
|
+
const mergeCommit = isRecord8(item.mergeCommit) && isSha(item.mergeCommit.oid) ? item.mergeCommit.oid : void 0;
|
|
24197
24639
|
const baseSha = isSha(item.baseRefOid) ? item.baseRefOid : void 0;
|
|
24198
24640
|
changes.push({
|
|
24199
24641
|
sourceBranch: branch,
|
|
@@ -24210,14 +24652,14 @@ function parseGitLabChanges(value, _project, branch) {
|
|
|
24210
24652
|
if (!Array.isArray(value)) return void 0;
|
|
24211
24653
|
const changes = [];
|
|
24212
24654
|
for (const item of value) {
|
|
24213
|
-
if (!
|
|
24655
|
+
if (!isRecord8(item)) return void 0;
|
|
24214
24656
|
const sourceProjectId = item.source_project_id;
|
|
24215
24657
|
const targetProjectId = item.target_project_id;
|
|
24216
24658
|
const sameProject = typeof sourceProjectId === "number" && typeof targetProjectId === "number" && sourceProjectId === targetProjectId;
|
|
24217
24659
|
if (!sameProject || item.source_branch !== branch || !isSha(item.sha) || typeof item.target_branch !== "string") {
|
|
24218
24660
|
continue;
|
|
24219
24661
|
}
|
|
24220
|
-
const diffRefs =
|
|
24662
|
+
const diffRefs = isRecord8(item.diff_refs) ? item.diff_refs : void 0;
|
|
24221
24663
|
const mergeCommitSha = isSha(item.merge_commit_sha) ? item.merge_commit_sha : void 0;
|
|
24222
24664
|
const baseSha = diffRefs && isSha(diffRefs.base_sha) ? diffRefs.base_sha : void 0;
|
|
24223
24665
|
changes.push({
|
|
@@ -24660,7 +25102,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
24660
25102
|
containers2 = measureContainersFn(worktreePaths);
|
|
24661
25103
|
} catch (error) {
|
|
24662
25104
|
const reason = `container measurement failed: ${describeCause(error)}`;
|
|
24663
|
-
containers2 = new Map(worktreePaths.map((
|
|
25105
|
+
containers2 = new Map(worktreePaths.map((path62) => [path62, unknownContainers(reason)]));
|
|
24664
25106
|
}
|
|
24665
25107
|
}
|
|
24666
25108
|
const rows2 = records.map((record5) => {
|
|
@@ -24710,12 +25152,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
24710
25152
|
networkCapacity: (dependencies.inspectNetworkCapacity ?? inspectNetworkCapacity)()
|
|
24711
25153
|
};
|
|
24712
25154
|
}
|
|
24713
|
-
var
|
|
25155
|
+
var import_node_child_process35, import_node_fs50, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
|
|
24714
25156
|
var init_workspace_cleanup = __esm({
|
|
24715
25157
|
"src/core/workspace-cleanup.ts"() {
|
|
24716
25158
|
"use strict";
|
|
24717
|
-
|
|
24718
|
-
|
|
25159
|
+
import_node_child_process35 = require("child_process");
|
|
25160
|
+
import_node_fs50 = __toESM(require("fs"));
|
|
24719
25161
|
init_devpod_workspaces();
|
|
24720
25162
|
init_host_routes();
|
|
24721
25163
|
init_network_diagnostics();
|
|
@@ -24733,7 +25175,7 @@ var init_workspace_cleanup = __esm({
|
|
|
24733
25175
|
];
|
|
24734
25176
|
defaultCommandRunner = (command, args, input2) => {
|
|
24735
25177
|
const environment = command === "git" ? READ_ONLY_GIT_ENV2 : { ...process.env, LC_ALL: "C" };
|
|
24736
|
-
const result = (0,
|
|
25178
|
+
const result = (0, import_node_child_process35.spawnSync)(command, args, {
|
|
24737
25179
|
encoding: "utf-8",
|
|
24738
25180
|
env: environment,
|
|
24739
25181
|
...input2 === void 0 ? {} : { input: input2 }
|
|
@@ -24827,20 +25269,20 @@ function warnMissingWorkspaceOwnership(repoPath) {
|
|
|
24827
25269
|
);
|
|
24828
25270
|
}
|
|
24829
25271
|
function defaultWorktreePath(mainRepo, ws) {
|
|
24830
|
-
return
|
|
25272
|
+
return import_node_path62.default.join(mainRepo, "trees", ws);
|
|
24831
25273
|
}
|
|
24832
25274
|
function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
24833
|
-
const ignored = (0,
|
|
25275
|
+
const ignored = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
|
|
24834
25276
|
encoding: "utf-8"
|
|
24835
25277
|
});
|
|
24836
25278
|
if (ignored.status !== 0) {
|
|
24837
25279
|
throw new Error(
|
|
24838
|
-
`Default worktree root '${
|
|
25280
|
+
`Default worktree root '${import_node_path62.default.join(mainRepo, "trees")}' is not ignored. Add 'trees/' to '${import_node_path62.default.join(mainRepo, ".gitignore")}' or use --path.`
|
|
24839
25281
|
);
|
|
24840
25282
|
}
|
|
24841
25283
|
}
|
|
24842
25284
|
function legacyDefaultWorktreePath(mainRepo, ws) {
|
|
24843
|
-
return
|
|
25285
|
+
return import_node_path62.default.join(import_node_path62.default.dirname(mainRepo), `${import_node_path62.default.basename(mainRepo)}-${ws}`);
|
|
24844
25286
|
}
|
|
24845
25287
|
function teardownFallbackPath(mainRepo, workspace) {
|
|
24846
25288
|
const candidates = [
|
|
@@ -24952,13 +25394,13 @@ function assertFullDownPreflight(mainRepo, target) {
|
|
|
24952
25394
|
if (sameWorkspacePath(target.worktreePath, mainRepo)) {
|
|
24953
25395
|
throw new Error("Refusing to remove the primary Git checkout.");
|
|
24954
25396
|
}
|
|
24955
|
-
if (!target.worktree || target.worktree.prunable || !
|
|
25397
|
+
if (!target.worktree || target.worktree.prunable || !import_node_fs51.default.existsSync(target.worktreePath)) return;
|
|
24956
25398
|
if (target.worktree.locked) {
|
|
24957
25399
|
throw new Error(
|
|
24958
25400
|
`Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
|
|
24959
25401
|
);
|
|
24960
25402
|
}
|
|
24961
|
-
const status = (0,
|
|
25403
|
+
const status = (0, import_node_child_process36.spawnSync)(
|
|
24962
25404
|
"git",
|
|
24963
25405
|
["-C", target.worktreePath, "status", "--porcelain", "--untracked-files=normal"],
|
|
24964
25406
|
{ encoding: "utf-8" }
|
|
@@ -24992,9 +25434,9 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
24992
25434
|
(candidate) => defaultWorktreePath(mainRepo, candidate)
|
|
24993
25435
|
);
|
|
24994
25436
|
const generatedPath = candidatePaths.find(
|
|
24995
|
-
(candidate) => !
|
|
25437
|
+
(candidate) => !import_node_fs51.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
|
|
24996
25438
|
);
|
|
24997
|
-
const selectedPath = opts.path ?
|
|
25439
|
+
const selectedPath = opts.path ? import_node_path62.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
|
|
24998
25440
|
if (!selectedPath) {
|
|
24999
25441
|
throw new Error(`Could not allocate a collision-safe worktree path for branch '${branch}'.`);
|
|
25000
25442
|
}
|
|
@@ -25003,7 +25445,7 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
25003
25445
|
`Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
|
|
25004
25446
|
);
|
|
25005
25447
|
}
|
|
25006
|
-
if (
|
|
25448
|
+
if (import_node_fs51.default.existsSync(selectedPath)) {
|
|
25007
25449
|
const registered = worktrees.find(
|
|
25008
25450
|
(worktree) => sameWorkspacePath(worktree.path, selectedPath)
|
|
25009
25451
|
);
|
|
@@ -25022,11 +25464,11 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
25022
25464
|
return selectedPath;
|
|
25023
25465
|
}
|
|
25024
25466
|
if (!opts.path) assertDefaultWorktreeRootIgnored(mainRepo);
|
|
25025
|
-
const add = (0,
|
|
25467
|
+
const add = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
|
|
25026
25468
|
encoding: "utf-8"
|
|
25027
25469
|
});
|
|
25028
25470
|
if (add.status !== 0) {
|
|
25029
|
-
const addNew = (0,
|
|
25471
|
+
const addNew = (0, import_node_child_process36.spawnSync)(
|
|
25030
25472
|
"git",
|
|
25031
25473
|
["-C", mainRepo, "worktree", "add", "-b", branch, selectedPath],
|
|
25032
25474
|
{ encoding: "utf-8" }
|
|
@@ -25160,8 +25602,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25160
25602
|
}
|
|
25161
25603
|
const result = await mutateWorkspaceRuntime("delete", resolved, worktrees, opts.quiet);
|
|
25162
25604
|
if (removeWorktree) {
|
|
25163
|
-
if (resolved.worktree && !resolved.worktree.prunable &&
|
|
25164
|
-
const rm = (0,
|
|
25605
|
+
if (resolved.worktree && !resolved.worktree.prunable && import_node_fs51.default.existsSync(resolved.worktreePath)) {
|
|
25606
|
+
const rm = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
|
|
25165
25607
|
encoding: "utf-8"
|
|
25166
25608
|
});
|
|
25167
25609
|
if (rm.status !== 0) {
|
|
@@ -25179,7 +25621,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25179
25621
|
}
|
|
25180
25622
|
return result;
|
|
25181
25623
|
};
|
|
25182
|
-
return resolved.worktree && !resolved.worktree.prunable &&
|
|
25624
|
+
return resolved.worktree && !resolved.worktree.prunable && import_node_fs51.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
|
|
25183
25625
|
}
|
|
25184
25626
|
async function workspaceStop(target, opts = {}) {
|
|
25185
25627
|
return runWorkspaceLifecycle("stop", target, opts);
|
|
@@ -25187,13 +25629,13 @@ async function workspaceStop(target, opts = {}) {
|
|
|
25187
25629
|
async function workspaceDown(target, opts = {}) {
|
|
25188
25630
|
return runWorkspaceLifecycle("down", target, opts);
|
|
25189
25631
|
}
|
|
25190
|
-
var
|
|
25632
|
+
var import_node_child_process36, import_node_fs51, import_node_path62, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
|
|
25191
25633
|
var init_workspace_lifecycle = __esm({
|
|
25192
25634
|
"src/core/workspace-lifecycle.ts"() {
|
|
25193
25635
|
"use strict";
|
|
25194
|
-
|
|
25195
|
-
|
|
25196
|
-
|
|
25636
|
+
import_node_child_process36 = require("child_process");
|
|
25637
|
+
import_node_fs51 = __toESM(require("fs"));
|
|
25638
|
+
import_node_path62 = __toESM(require("path"));
|
|
25197
25639
|
init_devpod_mutation();
|
|
25198
25640
|
init_devpod_workspaces();
|
|
25199
25641
|
init_devsy_mutation();
|
|
@@ -25377,7 +25819,7 @@ var init_version = __esm({
|
|
|
25377
25819
|
|
|
25378
25820
|
// src/cli.ts
|
|
25379
25821
|
var import_commander = require("commander");
|
|
25380
|
-
var CLI_VERSION = true ? "0.0.
|
|
25822
|
+
var CLI_VERSION = true ? "0.0.70" : "0.0.0-dev";
|
|
25381
25823
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
25382
25824
|
function withErrorHandling(action2) {
|
|
25383
25825
|
return async (...args) => {
|
|
@@ -25747,13 +26189,13 @@ workspaceCommand2.command("down").description("Delete a workspace's runtime and
|
|
|
25747
26189
|
await runWorkspaceDownCommand2(target, options);
|
|
25748
26190
|
})
|
|
25749
26191
|
);
|
|
25750
|
-
workspaceCommand2.command("journal settle").description(
|
|
26192
|
+
workspaceCommand2.command("journal").description("Inspect and recover workspace lifecycle journals").command("settle").description(
|
|
25751
26193
|
"Settle an interrupted lifecycle operation as unobservable so ensure and stop can proceed"
|
|
25752
26194
|
).argument("[path]", "Checkout path (defaults to current directory)").option("--json", "Output JSON").action(
|
|
25753
|
-
withErrorHandling(async (
|
|
26195
|
+
withErrorHandling(async (path62, _options, command) => {
|
|
25754
26196
|
const options = command.opts();
|
|
25755
26197
|
const { runWorkspaceJournalSettleCommand: runWorkspaceJournalSettleCommand2 } = await Promise.resolve().then(() => (init_workspace2(), workspace_exports));
|
|
25756
|
-
await runWorkspaceJournalSettleCommand2({ ...
|
|
26198
|
+
await runWorkspaceJournalSettleCommand2({ ...path62 ? { path: path62 } : {}, ...options });
|
|
25757
26199
|
})
|
|
25758
26200
|
);
|
|
25759
26201
|
function parseVersionRequest(argv) {
|