@devrouter/cli 0.0.68 → 0.0.69
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 +566 -178
- package/dist/devrouter.js +768 -330
- package/package.json +1 -1
- package/upgrade-prompts/0.0.69.md +41 -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.69" : "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.69" : "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,278 @@ 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
|
+
return ports;
|
|
13066
|
+
}
|
|
13067
|
+
function parseHolderSnapshots(stdout) {
|
|
13068
|
+
return stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
13069
|
+
let parsed;
|
|
13070
|
+
try {
|
|
13071
|
+
parsed = JSON.parse(line);
|
|
13072
|
+
} catch {
|
|
13073
|
+
throw new Error("Host-port holder inspection returned malformed JSON.");
|
|
13074
|
+
}
|
|
13075
|
+
if (!isRecord6(parsed) || typeof parsed.id !== "string" || parsed.id.length === 0) {
|
|
13076
|
+
throw new Error("Host-port holder inspection returned an invalid container record.");
|
|
13077
|
+
}
|
|
13078
|
+
const name = typeof parsed.name === "string" ? parsed.name.replace(/^\//, "") : parsed.id;
|
|
13079
|
+
return {
|
|
13080
|
+
id: parsed.id,
|
|
13081
|
+
name,
|
|
13082
|
+
composeProject: optionalLabel(parsed.labels, "com.docker.compose.project"),
|
|
13083
|
+
workingDir: optionalLabel(parsed.labels, "com.docker.compose.project.working_dir"),
|
|
13084
|
+
ports: parseHolderPortMap(parsed.ports)
|
|
13085
|
+
};
|
|
13086
|
+
});
|
|
13087
|
+
}
|
|
13088
|
+
function listHolderSnapshots() {
|
|
13089
|
+
const listed = runDocker(["ps", "--filter", "status=running", "--format", "{{.ID}}"]);
|
|
13090
|
+
const ids2 = listed.split(/\r?\n/).map((id5) => id5.trim()).filter(Boolean);
|
|
13091
|
+
if (ids2.length === 0) return [];
|
|
13092
|
+
if (ids2.length > MAX_RUNNING_HOLDERS) {
|
|
13093
|
+
throw new Error(
|
|
13094
|
+
`Host-port holder inspection found ${ids2.length} running containers, above its bound.`
|
|
13095
|
+
);
|
|
13096
|
+
}
|
|
13097
|
+
const inspected = runDocker(["inspect", "--format", HOST_PORT_INSPECT_TEMPLATE, ...ids2]);
|
|
13098
|
+
return parseHolderSnapshots(inspected);
|
|
13099
|
+
}
|
|
13100
|
+
function renderManagedComposeModel(plan, workspace) {
|
|
13101
|
+
const fileArgs = plan.composeFiles.flatMap((file) => ["-f", file]);
|
|
13102
|
+
const result = (0, import_node_child_process20.spawnSync)(
|
|
13103
|
+
"docker",
|
|
13104
|
+
[
|
|
13105
|
+
"compose",
|
|
13106
|
+
"--profile",
|
|
13107
|
+
"*",
|
|
13108
|
+
"--project-directory",
|
|
13109
|
+
plan.composeDirectory,
|
|
13110
|
+
...fileArgs,
|
|
13111
|
+
"config",
|
|
13112
|
+
"--format",
|
|
13113
|
+
"json"
|
|
13114
|
+
],
|
|
13115
|
+
{
|
|
13116
|
+
cwd: plan.composeDirectory,
|
|
13117
|
+
encoding: "utf-8",
|
|
13118
|
+
env: managedComposeEnvironment(workspace),
|
|
13119
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
13120
|
+
timeout: COMPOSE_RENDER_TIMEOUT_MS,
|
|
13121
|
+
maxBuffer: COMPOSE_RENDER_MAX_BUFFER
|
|
13122
|
+
}
|
|
13123
|
+
);
|
|
13124
|
+
if (result.status !== 0 || result.error || !result.stdout?.trim()) {
|
|
13125
|
+
const reason = (result.stderr || result.error?.message || "unknown error").toString().trim().split(/\r?\n/).slice(-3).join("; ");
|
|
13126
|
+
throw new Error(
|
|
13127
|
+
`Host-port claims could not render the managed Compose model that the start would bind: ${reason}`
|
|
13128
|
+
);
|
|
13129
|
+
}
|
|
13130
|
+
try {
|
|
13131
|
+
return JSON.parse(result.stdout);
|
|
13132
|
+
} catch {
|
|
13133
|
+
throw new Error("Host-port claims received a malformed rendered Compose model.");
|
|
13134
|
+
}
|
|
13135
|
+
}
|
|
13136
|
+
function attributeHolderWorktree(workingDir) {
|
|
13137
|
+
if (import_node_path39.default.basename(workingDir) !== ".devcontainer") return void 0;
|
|
13138
|
+
return comparableWorkspacePath(import_node_path39.default.dirname(workingDir));
|
|
13139
|
+
}
|
|
13140
|
+
function holderAttribution(holder, repoPath) {
|
|
13141
|
+
const attribution = {};
|
|
13142
|
+
if (holder.composeProject) attribution.holderComposeProject = holder.composeProject;
|
|
13143
|
+
const worktree = holder.workingDir ? attributeHolderWorktree(holder.workingDir) : void 0;
|
|
13144
|
+
if (!worktree) return attribution;
|
|
13145
|
+
attribution.holderWorktreePath = worktree;
|
|
13146
|
+
try {
|
|
13147
|
+
const owner = listWorkspaceOwnership(repoPath).find(
|
|
13148
|
+
(record5) => comparableWorkspacePath(record5.worktreePath) === worktree
|
|
13149
|
+
);
|
|
13150
|
+
if (owner) {
|
|
13151
|
+
attribution.holderWorkspace = owner.workspace;
|
|
13152
|
+
if (owner.branch) attribution.holderBranch = owner.branch;
|
|
13153
|
+
}
|
|
13154
|
+
} catch {
|
|
13155
|
+
}
|
|
13156
|
+
return attribution;
|
|
13157
|
+
}
|
|
13158
|
+
function remediationFor(holder, attribution) {
|
|
13159
|
+
if (attribution.holderWorktreePath) {
|
|
13160
|
+
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.`;
|
|
13161
|
+
}
|
|
13162
|
+
return `Stop or reconfigure the holding container '${holder.name}' and run ensure again; devrouter never rewrites consumer-declared host bindings.`;
|
|
13163
|
+
}
|
|
13164
|
+
function detectHostPortClaimConflicts(options) {
|
|
13165
|
+
const desired = resolveFixedPublishedHostPorts(
|
|
13166
|
+
renderManagedComposeModel(options.plan, options.workspace)
|
|
13167
|
+
);
|
|
13168
|
+
if (desired.length === 0) return [];
|
|
13169
|
+
const holders = listHolderSnapshots();
|
|
13170
|
+
const devcontainerDir = import_node_path39.default.join(options.repoPath, ".devcontainer");
|
|
13171
|
+
const conflicts = [];
|
|
13172
|
+
for (const binding of desired) {
|
|
13173
|
+
for (const holder of holders) {
|
|
13174
|
+
for (const holderPort of holder.ports) {
|
|
13175
|
+
if (!hostPortBindingsConflict(binding, holderPort)) continue;
|
|
13176
|
+
if (holder.workingDir && sameWorkspacePath(holder.workingDir, devcontainerDir)) continue;
|
|
13177
|
+
const attribution = holderAttribution(holder, options.repoPath);
|
|
13178
|
+
conflicts.push({
|
|
13179
|
+
service: binding.service,
|
|
13180
|
+
hostIp: binding.hostIp,
|
|
13181
|
+
hostPort: binding.hostPort,
|
|
13182
|
+
protocol: binding.protocol,
|
|
13183
|
+
holderContainer: holder.name,
|
|
13184
|
+
...attribution,
|
|
13185
|
+
remediation: remediationFor(holder, attribution)
|
|
13186
|
+
});
|
|
13187
|
+
}
|
|
13188
|
+
}
|
|
13189
|
+
}
|
|
13190
|
+
return conflicts;
|
|
13191
|
+
}
|
|
13192
|
+
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;
|
|
13193
|
+
var init_host_port_claims = __esm({
|
|
13194
|
+
"src/core/host-port-claims.ts"() {
|
|
13195
|
+
"use strict";
|
|
13196
|
+
import_node_child_process20 = require("child_process");
|
|
13197
|
+
import_node_path39 = __toESM(require("path"));
|
|
13198
|
+
init_devcontainer_profile();
|
|
13199
|
+
init_network_effect_scope();
|
|
13200
|
+
init_workspace();
|
|
13201
|
+
init_workspace_ownership();
|
|
13202
|
+
COMPOSE_RENDER_TIMEOUT_MS = 3e4;
|
|
13203
|
+
COMPOSE_RENDER_MAX_BUFFER = 8 * 1024 * 1024;
|
|
13204
|
+
DOCKER_TIMEOUT_MS = 1e4;
|
|
13205
|
+
DOCKER_MAX_BUFFER = 1024 * 1024;
|
|
13206
|
+
MAX_DESIRED_HOST_PORTS = 1024;
|
|
13207
|
+
MAX_RUNNING_HOLDERS = 512;
|
|
13208
|
+
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}}}';
|
|
13209
|
+
}
|
|
13210
|
+
});
|
|
13211
|
+
|
|
12843
13212
|
// src/core/http-route-probe.ts
|
|
12844
13213
|
function probeHttpRoute(host, options = {}) {
|
|
12845
13214
|
const maxTimeSeconds = options.maxTimeSeconds ?? DEFAULT_MAX_TIME_SECONDS;
|
|
@@ -12866,7 +13235,7 @@ function probeHttpRoute(host, options = {}) {
|
|
|
12866
13235
|
args.push("--cacert", CERT_FILE);
|
|
12867
13236
|
}
|
|
12868
13237
|
args.push(url);
|
|
12869
|
-
const result = (0,
|
|
13238
|
+
const result = (0, import_node_child_process21.spawnSync)("curl", args, { encoding: "utf-8" });
|
|
12870
13239
|
const stdout = result.stdout?.trim() ?? "";
|
|
12871
13240
|
const status = /^\d{3}$/.test(stdout) ? Number(stdout) : void 0;
|
|
12872
13241
|
const ok = result.status === 0 && status !== void 0 && status >= 100 && status < 500;
|
|
@@ -12901,7 +13270,7 @@ function probeHttpReadinessSync(host, readiness, maxTimeSeconds, repoPath) {
|
|
|
12901
13270
|
}
|
|
12902
13271
|
let result;
|
|
12903
13272
|
try {
|
|
12904
|
-
result = (0,
|
|
13273
|
+
result = (0, import_node_child_process21.spawnSync)("curl", args, {
|
|
12905
13274
|
encoding: "utf-8",
|
|
12906
13275
|
maxBuffer: CONTRACT_MAX_BUFFER,
|
|
12907
13276
|
timeout: Math.ceil(parentTimeoutMs)
|
|
@@ -12983,11 +13352,11 @@ function transportFailure() {
|
|
|
12983
13352
|
classification: "transport"
|
|
12984
13353
|
};
|
|
12985
13354
|
}
|
|
12986
|
-
var
|
|
13355
|
+
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
13356
|
var init_http_route_probe = __esm({
|
|
12988
13357
|
"src/core/http-route-probe.ts"() {
|
|
12989
13358
|
"use strict";
|
|
12990
|
-
|
|
13359
|
+
import_node_child_process21 = require("child_process");
|
|
12991
13360
|
init_router();
|
|
12992
13361
|
init_tls();
|
|
12993
13362
|
DEFAULT_MAX_TIME_SECONDS = 5;
|
|
@@ -12999,20 +13368,20 @@ var init_http_route_probe = __esm({
|
|
|
12999
13368
|
});
|
|
13000
13369
|
|
|
13001
13370
|
// src/core/managed-host-preparation.ts
|
|
13002
|
-
var
|
|
13371
|
+
var import_node_child_process22, import_node_fs31;
|
|
13003
13372
|
var init_managed_host_preparation = __esm({
|
|
13004
13373
|
"src/core/managed-host-preparation.ts"() {
|
|
13005
13374
|
"use strict";
|
|
13006
|
-
|
|
13007
|
-
|
|
13375
|
+
import_node_child_process22 = require("child_process");
|
|
13376
|
+
import_node_fs31 = __toESM(require("fs"));
|
|
13008
13377
|
init_repo_config();
|
|
13009
13378
|
}
|
|
13010
13379
|
});
|
|
13011
13380
|
|
|
13012
13381
|
// src/core/managed-post-start.ts
|
|
13013
13382
|
function readRegularFileBytes(filePath) {
|
|
13014
|
-
if (!
|
|
13015
|
-
return
|
|
13383
|
+
if (!import_node_fs32.default.existsSync(filePath) || !import_node_fs32.default.lstatSync(filePath).isFile()) return void 0;
|
|
13384
|
+
return import_node_fs32.default.readFileSync(filePath);
|
|
13016
13385
|
}
|
|
13017
13386
|
function readRegularFile(filePath) {
|
|
13018
13387
|
return readRegularFileBytes(filePath)?.toString("utf-8");
|
|
@@ -13021,11 +13390,11 @@ function adapterFingerprint(adapter) {
|
|
|
13021
13390
|
return (0, import_node_crypto17.createHash)("sha256").update(adapter).digest("hex");
|
|
13022
13391
|
}
|
|
13023
13392
|
function resolveManagedPostStartPlan(repoPath) {
|
|
13024
|
-
const adapterPath =
|
|
13393
|
+
const adapterPath = import_node_path40.default.join(repoPath, MANAGED_ADAPTER_PATH);
|
|
13025
13394
|
const adapterBytes = readRegularFileBytes(adapterPath);
|
|
13026
13395
|
const adapterText = adapterBytes?.toString("utf-8");
|
|
13027
|
-
const dockerfilePath =
|
|
13028
|
-
const devcontainerPath =
|
|
13396
|
+
const dockerfilePath = import_node_path40.default.join(repoPath, ".devcontainer", "Dockerfile");
|
|
13397
|
+
const devcontainerPath = import_node_path40.default.join(repoPath, ".devcontainer", "devcontainer.json");
|
|
13029
13398
|
const dockerfile = readRegularFile(dockerfilePath) ?? "";
|
|
13030
13399
|
const devcontainer = readRegularFile(devcontainerPath) ?? "";
|
|
13031
13400
|
const devrouterPattern = [adapterText, dockerfile, devcontainer].filter((value) => value !== void 0).some(
|
|
@@ -13071,7 +13440,7 @@ function assertSafeProcessName(name) {
|
|
|
13071
13440
|
}
|
|
13072
13441
|
function runManagedProcessAction(options) {
|
|
13073
13442
|
assertSafeProcessName(options.name);
|
|
13074
|
-
const result = (0,
|
|
13443
|
+
const result = (0, import_node_child_process23.spawnSync)(
|
|
13075
13444
|
"docker",
|
|
13076
13445
|
[
|
|
13077
13446
|
"exec",
|
|
@@ -13108,14 +13477,14 @@ function runManagedProcessAction(options) {
|
|
|
13108
13477
|
}
|
|
13109
13478
|
return "drifted";
|
|
13110
13479
|
}
|
|
13111
|
-
var
|
|
13480
|
+
var import_node_child_process23, import_node_crypto17, import_node_fs32, import_node_path40, MANAGED_MARKER, MANAGED_ADAPTER_PATH, RUNTIME_HELPER_PATH;
|
|
13112
13481
|
var init_managed_post_start = __esm({
|
|
13113
13482
|
"src/core/managed-post-start.ts"() {
|
|
13114
13483
|
"use strict";
|
|
13115
|
-
|
|
13484
|
+
import_node_child_process23 = require("child_process");
|
|
13116
13485
|
import_node_crypto17 = require("crypto");
|
|
13117
|
-
|
|
13118
|
-
|
|
13486
|
+
import_node_fs32 = __toESM(require("fs"));
|
|
13487
|
+
import_node_path40 = __toESM(require("path"));
|
|
13119
13488
|
init_devcontainer_config();
|
|
13120
13489
|
init_network_effect_scope();
|
|
13121
13490
|
MANAGED_MARKER = "devrouter:managed devcontainer";
|
|
@@ -13567,7 +13936,7 @@ function readDocker(endpoint, args, timeoutMs) {
|
|
|
13567
13936
|
const env = { ...process.env };
|
|
13568
13937
|
delete env.DOCKER_CONTEXT;
|
|
13569
13938
|
delete env.DOCKER_HOST;
|
|
13570
|
-
const result = (0,
|
|
13939
|
+
const result = (0, import_node_child_process24.spawnSync)("docker", ["--host", endpoint, ...args], {
|
|
13571
13940
|
env,
|
|
13572
13941
|
encoding: "utf-8",
|
|
13573
13942
|
timeout: timeoutMs,
|
|
@@ -13722,11 +14091,11 @@ function collectDockerNetworkInventory(options = {}, dependencies = {}) {
|
|
|
13722
14091
|
}
|
|
13723
14092
|
return result;
|
|
13724
14093
|
}
|
|
13725
|
-
var
|
|
14094
|
+
var import_node_child_process24, NetworkInventoryError, MAX_IDS, MAX_OUTPUT_BYTES, FULL_ID, NETWORK_TEMPLATE, CONTAINER_TEMPLATE;
|
|
13726
14095
|
var init_network_inventory = __esm({
|
|
13727
14096
|
"src/core/network-inventory.ts"() {
|
|
13728
14097
|
"use strict";
|
|
13729
|
-
|
|
14098
|
+
import_node_child_process24 = require("child_process");
|
|
13730
14099
|
init_devpod_environment();
|
|
13731
14100
|
NetworkInventoryError = class extends Error {
|
|
13732
14101
|
};
|
|
@@ -14054,26 +14423,26 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
|
14054
14423
|
try {
|
|
14055
14424
|
if (!endpoint.startsWith("unix:///")) return false;
|
|
14056
14425
|
const socket = endpoint.slice("unix://".length);
|
|
14057
|
-
const table =
|
|
14426
|
+
const table = import_node_fs33.default.readFileSync(import_node_path41.default.join(procRoot, "net/unix"), "utf8");
|
|
14058
14427
|
if (Buffer.byteLength(table) > 1024 * 1024) return false;
|
|
14059
14428
|
const rows2 = table.split("\n").map((line) => line.trim().split(/\s+/)).filter((row) => row[7] === socket && row[3] === "00010000");
|
|
14060
14429
|
if (rows2.length !== 1 || !/^\d+$/.test(rows2[0][6])) return false;
|
|
14061
14430
|
const inode = `socket:[${rows2[0][6]}]`;
|
|
14062
|
-
const ownNamespace =
|
|
14063
|
-
const pids =
|
|
14431
|
+
const ownNamespace = import_node_fs33.default.readlinkSync(import_node_path41.default.join(procRoot, "self/ns/net"));
|
|
14432
|
+
const pids = import_node_fs33.default.readdirSync(procRoot).filter((name) => /^\d+$/.test(name));
|
|
14064
14433
|
if (pids.length > 4096) return false;
|
|
14065
14434
|
for (const pid of pids) {
|
|
14066
14435
|
if (Date.now() > deadline) return false;
|
|
14067
14436
|
try {
|
|
14068
|
-
const root =
|
|
14069
|
-
if (
|
|
14070
|
-
if (
|
|
14071
|
-
const descriptors =
|
|
14437
|
+
const root = import_node_path41.default.join(procRoot, pid);
|
|
14438
|
+
if (import_node_path41.default.basename(import_node_fs33.default.readlinkSync(import_node_path41.default.join(root, "exe"))) !== "dockerd") continue;
|
|
14439
|
+
if (import_node_fs33.default.readlinkSync(import_node_path41.default.join(root, "ns/net")) !== ownNamespace) continue;
|
|
14440
|
+
const descriptors = import_node_fs33.default.readdirSync(import_node_path41.default.join(root, "fd"));
|
|
14072
14441
|
if (descriptors.length > 4096) continue;
|
|
14073
14442
|
if (descriptors.some((fd) => {
|
|
14074
14443
|
if (Date.now() > deadline) return false;
|
|
14075
14444
|
try {
|
|
14076
|
-
return
|
|
14445
|
+
return import_node_fs33.default.readlinkSync(import_node_path41.default.join(root, "fd", fd)) === inode;
|
|
14077
14446
|
} catch {
|
|
14078
14447
|
return false;
|
|
14079
14448
|
}
|
|
@@ -14087,12 +14456,12 @@ function hasLocalDockerNetworkNamespace(endpoint, procRoot = "/proc") {
|
|
|
14087
14456
|
}
|
|
14088
14457
|
return false;
|
|
14089
14458
|
}
|
|
14090
|
-
var
|
|
14459
|
+
var import_node_fs33, import_node_path41;
|
|
14091
14460
|
var init_network_local_daemon = __esm({
|
|
14092
14461
|
"src/core/network-local-daemon.ts"() {
|
|
14093
14462
|
"use strict";
|
|
14094
|
-
|
|
14095
|
-
|
|
14463
|
+
import_node_fs33 = __toESM(require("fs"));
|
|
14464
|
+
import_node_path41 = __toESM(require("path"));
|
|
14096
14465
|
}
|
|
14097
14466
|
});
|
|
14098
14467
|
|
|
@@ -14120,7 +14489,7 @@ function parseLinuxNetworkRoutes(output2) {
|
|
|
14120
14489
|
function collectNetworkRoutes(options) {
|
|
14121
14490
|
if ((options.platform ?? process.platform) !== "linux" || !hasLocalDockerNetworkNamespace(options.endpoint))
|
|
14122
14491
|
return { status: "unknown", routes: [] };
|
|
14123
|
-
const result = (0,
|
|
14492
|
+
const result = (0, import_node_child_process25.spawnSync)("ip", ["-j", "-4", "route", "show", "table", "all"], {
|
|
14124
14493
|
encoding: "utf8",
|
|
14125
14494
|
timeout: 5e3,
|
|
14126
14495
|
maxBuffer: 1024 * 1024
|
|
@@ -14128,11 +14497,11 @@ function collectNetworkRoutes(options) {
|
|
|
14128
14497
|
if (result.error || result.status !== 0) return { status: "unknown", routes: [] };
|
|
14129
14498
|
return parseLinuxNetworkRoutes(result.stdout);
|
|
14130
14499
|
}
|
|
14131
|
-
var
|
|
14500
|
+
var import_node_child_process25, import_node_net3;
|
|
14132
14501
|
var init_network_routes = __esm({
|
|
14133
14502
|
"src/core/network-routes.ts"() {
|
|
14134
14503
|
"use strict";
|
|
14135
|
-
|
|
14504
|
+
import_node_child_process25 = require("child_process");
|
|
14136
14505
|
import_node_net3 = require("net");
|
|
14137
14506
|
init_network_capacity();
|
|
14138
14507
|
init_network_local_daemon();
|
|
@@ -14208,14 +14577,14 @@ var init_network_compose = __esm({
|
|
|
14208
14577
|
});
|
|
14209
14578
|
|
|
14210
14579
|
// src/core/network-compose-files.ts
|
|
14211
|
-
var
|
|
14580
|
+
var import_node_child_process26, import_node_crypto19, import_node_fs34, import_node_path42, import_yaml4, MAX_BYTES3;
|
|
14212
14581
|
var init_network_compose_files = __esm({
|
|
14213
14582
|
"src/core/network-compose-files.ts"() {
|
|
14214
14583
|
"use strict";
|
|
14215
|
-
|
|
14584
|
+
import_node_child_process26 = require("child_process");
|
|
14216
14585
|
import_node_crypto19 = require("crypto");
|
|
14217
|
-
|
|
14218
|
-
|
|
14586
|
+
import_node_fs34 = __toESM(require("fs"));
|
|
14587
|
+
import_node_path42 = __toESM(require("path"));
|
|
14219
14588
|
import_yaml4 = __toESM(require("yaml"));
|
|
14220
14589
|
init_atomic_file();
|
|
14221
14590
|
init_devcontainer_profile();
|
|
@@ -14225,11 +14594,11 @@ var init_network_compose_files = __esm({
|
|
|
14225
14594
|
});
|
|
14226
14595
|
|
|
14227
14596
|
// src/core/network-connected-route.ts
|
|
14228
|
-
var
|
|
14597
|
+
var import_node_child_process27;
|
|
14229
14598
|
var init_network_connected_route = __esm({
|
|
14230
14599
|
"src/core/network-connected-route.ts"() {
|
|
14231
14600
|
"use strict";
|
|
14232
|
-
|
|
14601
|
+
import_node_child_process27 = require("child_process");
|
|
14233
14602
|
init_network_capacity();
|
|
14234
14603
|
}
|
|
14235
14604
|
});
|
|
@@ -14319,19 +14688,20 @@ function resolveLinkedTarget(repoPath) {
|
|
|
14319
14688
|
gitCommonDir: resolveGitCommonDir(repoPath)
|
|
14320
14689
|
};
|
|
14321
14690
|
}
|
|
14322
|
-
var
|
|
14691
|
+
var import_node_child_process28, import_node_fs35, import_node_path43;
|
|
14323
14692
|
var init_workspace_ensure = __esm({
|
|
14324
14693
|
"src/core/workspace-ensure.ts"() {
|
|
14325
14694
|
"use strict";
|
|
14326
|
-
|
|
14327
|
-
|
|
14328
|
-
|
|
14695
|
+
import_node_child_process28 = require("child_process");
|
|
14696
|
+
import_node_fs35 = __toESM(require("fs"));
|
|
14697
|
+
import_node_path43 = __toESM(require("path"));
|
|
14329
14698
|
init_devcontainer_profile();
|
|
14330
14699
|
init_devpod_environment();
|
|
14331
14700
|
init_devpod_mutation();
|
|
14332
14701
|
init_devpod_workspaces();
|
|
14333
14702
|
init_devsy_mutation();
|
|
14334
14703
|
init_file_lock();
|
|
14704
|
+
init_host_port_claims();
|
|
14335
14705
|
init_host_routes();
|
|
14336
14706
|
init_http_route_probe();
|
|
14337
14707
|
init_managed_host_preparation();
|
|
@@ -14460,13 +14830,13 @@ function prepareManagedLifecycleOperation(input2) {
|
|
|
14460
14830
|
};
|
|
14461
14831
|
});
|
|
14462
14832
|
}
|
|
14463
|
-
function assertCurrentCapacityController(expected, directory =
|
|
14833
|
+
function assertCurrentCapacityController(expected, directory = import_node_path44.default.join(DEVROUTER_HOME, "controller")) {
|
|
14464
14834
|
const current = new ControllerStore(directory).read();
|
|
14465
14835
|
if (!expected || current?.store !== expected.store || current.epoch !== expected.epoch)
|
|
14466
14836
|
throw new Error("Capacity controller incarnation changed.");
|
|
14467
14837
|
}
|
|
14468
14838
|
function settlePreparedLifecycleCapacity(input2) {
|
|
14469
|
-
const directory = input2.directory ??
|
|
14839
|
+
const directory = input2.directory ?? import_node_path44.default.join(DEVROUTER_HOME, "controller");
|
|
14470
14840
|
const store = new CapacityStore(directory);
|
|
14471
14841
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
14472
14842
|
const snapshot = store.read();
|
|
@@ -14680,7 +15050,7 @@ async function superviseLifecycle(kind, repoPath, options = {}, command) {
|
|
|
14680
15050
|
removeSignals();
|
|
14681
15051
|
}
|
|
14682
15052
|
}
|
|
14683
|
-
function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory =
|
|
15053
|
+
function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs, maxSampleAgeMs, directory = import_node_path44.default.join(DEVROUTER_HOME, "controller"), controller, admission) {
|
|
14684
15054
|
if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
|
|
14685
15055
|
const capacity = new CapacityStore(directory);
|
|
14686
15056
|
const snapshot = capacity.read();
|
|
@@ -14780,7 +15150,7 @@ function admitLifecycleCapacity(request2, reservation, budgets, samples, nowMs,
|
|
|
14780
15150
|
return decision;
|
|
14781
15151
|
}
|
|
14782
15152
|
function capacityPolicyTargets(repoPath) {
|
|
14783
|
-
const policy = readCapacityPolicy(
|
|
15153
|
+
const policy = readCapacityPolicy(import_node_path44.default.join(DEVROUTER_HOME, "controller"));
|
|
14784
15154
|
return policy?.admissions === "enabled" && policy.enrollments.some((entry) => entry.repoPath === repoPath);
|
|
14785
15155
|
}
|
|
14786
15156
|
function controllerWaitSeconds(deadline) {
|
|
@@ -14789,7 +15159,7 @@ function controllerWaitSeconds(deadline) {
|
|
|
14789
15159
|
async function superviseThroughController(input2) {
|
|
14790
15160
|
if (input2.kind === "exec" && !input2.command)
|
|
14791
15161
|
throw new Error("Exec requires a command for controller admission.");
|
|
14792
|
-
const directory =
|
|
15162
|
+
const directory = import_node_path44.default.join(DEVROUTER_HOME, "controller");
|
|
14793
15163
|
const cancellation = { requested: false };
|
|
14794
15164
|
const removeSignals = installLifecycleWaitSignals(cancellation);
|
|
14795
15165
|
const cancelled = () => {
|
|
@@ -14846,7 +15216,7 @@ async function superviseThroughController(input2) {
|
|
|
14846
15216
|
removeSignals();
|
|
14847
15217
|
}
|
|
14848
15218
|
}
|
|
14849
|
-
function renewLifecycleCapacity(request2, policy, samples, controller, directory =
|
|
15219
|
+
function renewLifecycleCapacity(request2, policy, samples, controller, directory = import_node_path44.default.join(DEVROUTER_HOME, "controller"), nowMs = Date.now()) {
|
|
14850
15220
|
return updateReliabilityOperation(request2.identity, (record5) => {
|
|
14851
15221
|
const binding = record5.capacity;
|
|
14852
15222
|
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 +15294,7 @@ function retireQueuedLifecycle(request2, supersededOnly = false) {
|
|
|
14924
15294
|
return true;
|
|
14925
15295
|
});
|
|
14926
15296
|
}
|
|
14927
|
-
function bindLifecycleCapacity(request2, binding, directory =
|
|
15297
|
+
function bindLifecycleCapacity(request2, binding, directory = import_node_path44.default.join(DEVROUTER_HOME, "controller")) {
|
|
14928
15298
|
if (request2.kind === "stop") throw new Error("Stop never requires capacity admission.");
|
|
14929
15299
|
updateReliabilityOperation(request2.identity, (record5) => {
|
|
14930
15300
|
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 +15309,12 @@ function bindLifecycleCapacity(request2, binding, directory = import_node_path43
|
|
|
14939
15309
|
stepRecord(record5, { ...request2.fence, type: "admission", result: "admitted" });
|
|
14940
15310
|
});
|
|
14941
15311
|
}
|
|
14942
|
-
var import_node_crypto21,
|
|
15312
|
+
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
15313
|
var init_reliability_lifecycle = __esm({
|
|
14944
15314
|
"src/core/reliability-lifecycle.ts"() {
|
|
14945
15315
|
"use strict";
|
|
14946
15316
|
import_node_crypto21 = require("crypto");
|
|
14947
|
-
|
|
15317
|
+
import_node_path44 = __toESM(require("path"));
|
|
14948
15318
|
import_node_util10 = require("util");
|
|
14949
15319
|
init_capacity_accounting();
|
|
14950
15320
|
init_capacity_policy();
|
|
@@ -15431,7 +15801,7 @@ function createCapacityController(options) {
|
|
|
15431
15801
|
const record5 = readReliabilityOperation(identity2);
|
|
15432
15802
|
if (!record5?.capacity || !record5.preparation && !record5.capacity.execSteady || record5.worker || !record5.state.operation?.drained || !["ensure", "exec"].includes(record5.state.operation.kind))
|
|
15433
15803
|
continue;
|
|
15434
|
-
const bytes2 = readControllerEvidence(
|
|
15804
|
+
const bytes2 = readControllerEvidence(import_node_path45.default.join(enrollment.repoPath, ".devrouter.yml"));
|
|
15435
15805
|
const estimates = loadRepoConfig(enrollment.repoPath, () => bytes2).capacity;
|
|
15436
15806
|
if (!estimates) continue;
|
|
15437
15807
|
settlePreparedLifecycleCapacity({
|
|
@@ -15591,12 +15961,12 @@ function createCapacityController(options) {
|
|
|
15591
15961
|
}
|
|
15592
15962
|
};
|
|
15593
15963
|
}
|
|
15594
|
-
var import_node_crypto22,
|
|
15964
|
+
var import_node_crypto22, import_node_path45, import_node_util12;
|
|
15595
15965
|
var init_capacity_controller = __esm({
|
|
15596
15966
|
"src/core/capacity-controller.ts"() {
|
|
15597
15967
|
"use strict";
|
|
15598
15968
|
import_node_crypto22 = require("crypto");
|
|
15599
|
-
|
|
15969
|
+
import_node_path45 = __toESM(require("path"));
|
|
15600
15970
|
import_node_util12 = require("util");
|
|
15601
15971
|
init_capacity_docker_probe();
|
|
15602
15972
|
init_capacity_enrollment();
|
|
@@ -15937,12 +16307,12 @@ async function containers(project, signal) {
|
|
|
15937
16307
|
}
|
|
15938
16308
|
return values.sort((a, b) => a.id.localeCompare(b.id));
|
|
15939
16309
|
}
|
|
15940
|
-
var import_node_crypto24,
|
|
16310
|
+
var import_node_crypto24, import_node_path46, OBSERVATION_INSPECT_TEMPLATE, collectControllerObservation;
|
|
15941
16311
|
var init_controller_observation = __esm({
|
|
15942
16312
|
"src/core/controller-observation.ts"() {
|
|
15943
16313
|
"use strict";
|
|
15944
16314
|
import_node_crypto24 = require("crypto");
|
|
15945
|
-
|
|
16315
|
+
import_node_path46 = __toESM(require("path"));
|
|
15946
16316
|
init_controller_binding();
|
|
15947
16317
|
init_controller_monitor();
|
|
15948
16318
|
init_controller_probe();
|
|
@@ -15985,14 +16355,14 @@ var init_controller_observation = __esm({
|
|
|
15985
16355
|
)).trim().split("\n");
|
|
15986
16356
|
if (metadata.length !== 2) throw new Error("Observation ownership paths unavailable.");
|
|
15987
16357
|
const [gitDir, commonDir] = metadata;
|
|
15988
|
-
const sourcePath =
|
|
16358
|
+
const sourcePath = import_node_path46.default.join(environment.repoPath, ".devcontainer/devcontainer.json");
|
|
15989
16359
|
const files = [
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
16360
|
+
import_node_path46.default.join(commonDir, "devrouter/workspaces", `${environment.workspace}.json`),
|
|
16361
|
+
import_node_path46.default.join(environment.repoPath, ".devrouter.yml"),
|
|
16362
|
+
import_node_path46.default.join(gitDir, "devrouter-workspace"),
|
|
15993
16363
|
managedRuntimeStatePath(environment.repoPath, environment.workspace),
|
|
15994
16364
|
sourcePath,
|
|
15995
|
-
|
|
16365
|
+
import_node_path46.default.join(environment.repoPath, MANAGED_DEVCONTAINER_PATH)
|
|
15996
16366
|
];
|
|
15997
16367
|
const persisted = captureControllerEvidence(files);
|
|
15998
16368
|
if (controllerBindingFingerprint(persisted.contents[0], persisted.contents[1]) !== environment.fingerprint)
|
|
@@ -16017,7 +16387,7 @@ var init_controller_observation = __esm({
|
|
|
16017
16387
|
if (!journal) throw new Error("Observation journal is unavailable.");
|
|
16018
16388
|
const routes = readRoutes().filter((route) => route.repoPath === environment.repoPath);
|
|
16019
16389
|
const before = await containers(state.composeProject, signal);
|
|
16020
|
-
const composeDirectory =
|
|
16390
|
+
const composeDirectory = import_node_path46.default.join(environment.repoPath, ".devcontainer");
|
|
16021
16391
|
const references = typeof source.dockerComposeFile === "string" ? [source.dockerComposeFile] : source.dockerComposeFile;
|
|
16022
16392
|
if (!Array.isArray(references) || !references.length || references.some((value) => typeof value !== "string"))
|
|
16023
16393
|
throw new Error("Observation Compose source is unavailable.");
|
|
@@ -16034,7 +16404,7 @@ var init_controller_observation = __esm({
|
|
|
16034
16404
|
if (!recorded || container.labels["com.docker.compose.project.working_dir"] !== composeDirectory)
|
|
16035
16405
|
throw new Error("Observation Compose ownership is unavailable.");
|
|
16036
16406
|
const files2 = recorded.split(",").map((file) => file.trim());
|
|
16037
|
-
if (files2.some((file) => !
|
|
16407
|
+
if (files2.some((file) => !import_node_path46.default.isAbsolute(file)) || !hasExactComposeIdentity(container, {
|
|
16038
16408
|
repoPath: environment.repoPath,
|
|
16039
16409
|
service: container.labels["com.docker.compose.service"] ?? "",
|
|
16040
16410
|
composeProject: state.composeProject,
|
|
@@ -16393,26 +16763,26 @@ var init_controller_sessions = __esm({
|
|
|
16393
16763
|
|
|
16394
16764
|
// src/core/controller-server.ts
|
|
16395
16765
|
function privateDirectory(directory) {
|
|
16396
|
-
const absolute2 =
|
|
16397
|
-
let current =
|
|
16398
|
-
for (const part of absolute2.slice(current.length).split(
|
|
16399
|
-
current =
|
|
16766
|
+
const absolute2 = import_node_path47.default.resolve(directory);
|
|
16767
|
+
let current = import_node_path47.default.parse(absolute2).root;
|
|
16768
|
+
for (const part of absolute2.slice(current.length).split(import_node_path47.default.sep)) {
|
|
16769
|
+
current = import_node_path47.default.join(current, part);
|
|
16400
16770
|
try {
|
|
16401
|
-
const stat2 =
|
|
16771
|
+
const stat2 = import_node_fs36.default.lstatSync(current);
|
|
16402
16772
|
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
16403
16773
|
throw new Error("Unsafe controller directory.");
|
|
16404
16774
|
} catch (error) {
|
|
16405
16775
|
if (error.code !== "ENOENT") throw error;
|
|
16406
|
-
|
|
16776
|
+
import_node_fs36.default.mkdirSync(current, { mode: 448 });
|
|
16407
16777
|
}
|
|
16408
16778
|
}
|
|
16409
|
-
const stat =
|
|
16779
|
+
const stat = import_node_fs36.default.lstatSync(absolute2);
|
|
16410
16780
|
if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
16411
16781
|
throw new Error("Controller directory is not private.");
|
|
16412
16782
|
}
|
|
16413
16783
|
function validateOwnedFile(file, socket) {
|
|
16414
16784
|
try {
|
|
16415
|
-
const stat =
|
|
16785
|
+
const stat = import_node_fs36.default.lstatSync(file);
|
|
16416
16786
|
if (stat.uid !== process.getuid?.() || (socket ? !stat.isSocket() : !stat.isFile()) || (stat.mode & 63) !== 0)
|
|
16417
16787
|
throw new Error("Unsafe controller artifact.");
|
|
16418
16788
|
return true;
|
|
@@ -16424,19 +16794,19 @@ function validateOwnedFile(file, socket) {
|
|
|
16424
16794
|
async function runController(options) {
|
|
16425
16795
|
if (options.operations && options.createOperations)
|
|
16426
16796
|
throw new Error("Controller operations have multiple owners.");
|
|
16427
|
-
const socketPath =
|
|
16797
|
+
const socketPath = import_node_path47.default.join(options.directory, "control.sock");
|
|
16428
16798
|
if (Buffer.byteLength(socketPath) > 103) throw new Error("Controller socket path is too long.");
|
|
16429
16799
|
privateDirectory(options.directory);
|
|
16430
|
-
const lockPath =
|
|
16800
|
+
const lockPath = import_node_path47.default.join(options.directory, "owner.lock");
|
|
16431
16801
|
try {
|
|
16432
|
-
const stat =
|
|
16802
|
+
const stat = import_node_fs36.default.lstatSync(lockPath);
|
|
16433
16803
|
if (!stat.isFile() || stat.uid !== process.getuid?.())
|
|
16434
16804
|
throw new Error("Unsafe controller owner lock.");
|
|
16435
16805
|
} catch (error) {
|
|
16436
16806
|
if (error.code !== "ENOENT") throw error;
|
|
16437
16807
|
}
|
|
16438
16808
|
await withFileLock(lockPath, { activity: "controller ownership", waitMs: 0 }, async () => {
|
|
16439
|
-
if (validateOwnedFile(socketPath, true))
|
|
16809
|
+
if (validateOwnedFile(socketPath, true)) import_node_fs36.default.unlinkSync(socketPath);
|
|
16440
16810
|
const sessions = new ControllerSessions(new ControllerStore(options.directory));
|
|
16441
16811
|
const incarnation = sessions.read();
|
|
16442
16812
|
let startupAvailable = true;
|
|
@@ -16815,7 +17185,7 @@ async function runController(options) {
|
|
|
16815
17185
|
resolve();
|
|
16816
17186
|
});
|
|
16817
17187
|
});
|
|
16818
|
-
|
|
17188
|
+
import_node_fs36.default.chmodSync(socketPath, 384);
|
|
16819
17189
|
await new Promise((resolve) => {
|
|
16820
17190
|
finish = resolve;
|
|
16821
17191
|
options.signal.addEventListener("abort", shutdown, { once: true });
|
|
@@ -16834,13 +17204,13 @@ async function runController(options) {
|
|
|
16834
17204
|
}
|
|
16835
17205
|
});
|
|
16836
17206
|
}
|
|
16837
|
-
var
|
|
17207
|
+
var import_node_fs36, import_node_net4, import_node_path47, FRAME_BYTES;
|
|
16838
17208
|
var init_controller_server = __esm({
|
|
16839
17209
|
"src/core/controller-server.ts"() {
|
|
16840
17210
|
"use strict";
|
|
16841
|
-
|
|
17211
|
+
import_node_fs36 = __toESM(require("fs"));
|
|
16842
17212
|
import_node_net4 = __toESM(require("net"));
|
|
16843
|
-
|
|
17213
|
+
import_node_path47 = __toESM(require("path"));
|
|
16844
17214
|
init_controller_monitor();
|
|
16845
17215
|
init_controller_protocol();
|
|
16846
17216
|
init_controller_sessions();
|
|
@@ -16857,7 +17227,7 @@ __export(controller_exports, {
|
|
|
16857
17227
|
runControllerCommand: () => runControllerCommand
|
|
16858
17228
|
});
|
|
16859
17229
|
async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
16860
|
-
const directory =
|
|
17230
|
+
const directory = import_node_path48.default.join(DEVROUTER_HOME, "controller");
|
|
16861
17231
|
try {
|
|
16862
17232
|
if (method === "run") {
|
|
16863
17233
|
const controller = new AbortController();
|
|
@@ -16892,7 +17262,7 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
|
16892
17262
|
if (fields2.timeout !== void 0) fields2.timeout = Number(fields2.timeout);
|
|
16893
17263
|
await controllerRequest(
|
|
16894
17264
|
directory,
|
|
16895
|
-
{ method, ...fields2, ...repo ? { path:
|
|
17265
|
+
{ method, ...fields2, ...repo ? { path: import_node_path48.default.resolve(repo) } : {} },
|
|
16896
17266
|
(value) => process.stdout.write(`${JSON.stringify(value)}
|
|
16897
17267
|
`)
|
|
16898
17268
|
);
|
|
@@ -16907,11 +17277,11 @@ async function runControllerCommand(method, options, repo, dependencies = {}) {
|
|
|
16907
17277
|
process.exitCode = 1;
|
|
16908
17278
|
}
|
|
16909
17279
|
}
|
|
16910
|
-
var
|
|
17280
|
+
var import_node_path48;
|
|
16911
17281
|
var init_controller = __esm({
|
|
16912
17282
|
"src/commands/controller.ts"() {
|
|
16913
17283
|
"use strict";
|
|
16914
|
-
|
|
17284
|
+
import_node_path48 = __toESM(require("path"));
|
|
16915
17285
|
init_capacity_collector();
|
|
16916
17286
|
init_capacity_controller();
|
|
16917
17287
|
init_capacity_policy();
|
|
@@ -16962,28 +17332,28 @@ function replaceDevrouterSection(content) {
|
|
|
16962
17332
|
return `${content.slice(0, start)}${buildDevrouterSection()}${content.slice(end)}`;
|
|
16963
17333
|
}
|
|
16964
17334
|
function writeRepoFile(repoPath, relPath, content) {
|
|
16965
|
-
const absolutePath = (0,
|
|
16966
|
-
(0,
|
|
16967
|
-
(0,
|
|
17335
|
+
const absolutePath = (0, import_node_path49.join)(repoPath, relPath);
|
|
17336
|
+
(0, import_node_fs37.mkdirSync)((0, import_node_path49.dirname)(absolutePath), { recursive: true });
|
|
17337
|
+
(0, import_node_fs37.writeFileSync)(absolutePath, content, "utf-8");
|
|
16968
17338
|
return absolutePath;
|
|
16969
17339
|
}
|
|
16970
17340
|
function ensureAgentsMdSection(repoPath) {
|
|
16971
|
-
const filePath = (0,
|
|
16972
|
-
if ((0,
|
|
16973
|
-
const content = (0,
|
|
17341
|
+
const filePath = (0, import_node_path49.join)(repoPath, AGENTS_MD);
|
|
17342
|
+
if ((0, import_node_fs37.existsSync)(filePath)) {
|
|
17343
|
+
const content = (0, import_node_fs37.readFileSync)(filePath, "utf-8");
|
|
16974
17344
|
if (content.includes(DEVROUTER_SENTINEL)) {
|
|
16975
17345
|
const updated = replaceDevrouterSection(content);
|
|
16976
17346
|
if (updated === void 0 || updated === content) return { path: filePath, written: false };
|
|
16977
|
-
(0,
|
|
17347
|
+
(0, import_node_fs37.writeFileSync)(filePath, updated, "utf-8");
|
|
16978
17348
|
return { path: filePath, written: true };
|
|
16979
17349
|
}
|
|
16980
|
-
(0,
|
|
17350
|
+
(0, import_node_fs37.writeFileSync)(filePath, `${content.trimEnd()}
|
|
16981
17351
|
|
|
16982
17352
|
${buildDevrouterSection()}
|
|
16983
17353
|
`, "utf-8");
|
|
16984
17354
|
return { path: filePath, written: true };
|
|
16985
17355
|
}
|
|
16986
|
-
(0,
|
|
17356
|
+
(0, import_node_fs37.writeFileSync)(filePath, `# AGENTS.md
|
|
16987
17357
|
|
|
16988
17358
|
${buildDevrouterSection()}
|
|
16989
17359
|
`, "utf-8");
|
|
@@ -16993,12 +17363,12 @@ function ensureSkillFile(repoPath) {
|
|
|
16993
17363
|
const filePath = writeRepoFile(repoPath, DEVROUTER_SKILL_REL_PATH, DEVROUTER_SKILL_CONTENT);
|
|
16994
17364
|
return { path: filePath, written: true };
|
|
16995
17365
|
}
|
|
16996
|
-
var
|
|
17366
|
+
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
17367
|
var init_agents_md = __esm({
|
|
16998
17368
|
"src/core/agents-md.ts"() {
|
|
16999
17369
|
"use strict";
|
|
17000
|
-
|
|
17001
|
-
|
|
17370
|
+
import_node_fs37 = require("fs");
|
|
17371
|
+
import_node_path49 = require("path");
|
|
17002
17372
|
DEVROUTER_SENTINEL = "<!-- devrouter -->";
|
|
17003
17373
|
DEVROUTER_END_SENTINEL = "<!-- /devrouter -->";
|
|
17004
17374
|
LEGACY_DEVROUTER_SECTION_END = "- `devrouter ls`";
|
|
@@ -17459,6 +17829,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
17459
17829
|
"- if kind=app and runtime=proxy:",
|
|
17460
17830
|
" - 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
17831
|
" - 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.",
|
|
17832
|
+
" - 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
17833
|
' - 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
17834
|
` - 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
17835
|
" - 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 +17936,7 @@ function buildOnboardingPrompt(options = {}) {
|
|
|
17565
17936
|
"- Avoid pre-wrapper DB assignments such as `DATABASE_URI=... <wrapper> run -- ...`; wrapper-managed env may override those values.",
|
|
17566
17937
|
"- `devrouter doctor --repo <REPO_PATH>` warns on risky pre-wrapper DB assignments before `run --` for host apps that depend on postgres.",
|
|
17567
17938
|
"- With TLS enabled, `devrouter doctor --repo <REPO_PATH>` also warns on cert SAN mismatches for configured hosts (`repo.tls-host-coverage`).",
|
|
17939
|
+
"- `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
17940
|
"- 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
17941
|
"- Robust one-shot seed example: `devrouter app exec <name> --repo <REPO_PATH> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload seed`.",
|
|
17570
17942
|
"- 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 +18647,11 @@ function extractCurrentVersionFromRepoConfig(repoPath) {
|
|
|
18275
18647
|
};
|
|
18276
18648
|
}
|
|
18277
18649
|
function readPromptDirectory(promptDirPath) {
|
|
18278
|
-
if (!
|
|
18650
|
+
if (!import_node_fs38.default.existsSync(promptDirPath)) {
|
|
18279
18651
|
throw new Error(`Missing ${UPGRADE_PROMPTS_DIR} directory at ${promptDirPath}.`);
|
|
18280
18652
|
}
|
|
18281
18653
|
const releases = [];
|
|
18282
|
-
const files =
|
|
18654
|
+
const files = import_node_fs38.default.readdirSync(promptDirPath, { withFileTypes: true });
|
|
18283
18655
|
for (const file of files) {
|
|
18284
18656
|
if (!file.isFile() || !file.name.endsWith(".md")) {
|
|
18285
18657
|
continue;
|
|
@@ -18289,8 +18661,8 @@ function readPromptDirectory(promptDirPath) {
|
|
|
18289
18661
|
continue;
|
|
18290
18662
|
}
|
|
18291
18663
|
const version = normalizeVersion(basename);
|
|
18292
|
-
const promptPath =
|
|
18293
|
-
const prompt =
|
|
18664
|
+
const promptPath = import_node_path50.default.join(promptDirPath, file.name);
|
|
18665
|
+
const prompt = import_node_fs38.default.readFileSync(promptPath, "utf-8").trim();
|
|
18294
18666
|
releases.push({ version, prompt, promptPath });
|
|
18295
18667
|
}
|
|
18296
18668
|
const deduped = /* @__PURE__ */ new Map();
|
|
@@ -18311,19 +18683,19 @@ function listAvailableUpgradeTargets(currentVersion, releases) {
|
|
|
18311
18683
|
}
|
|
18312
18684
|
function resolvePromptDirectory(explicitPath) {
|
|
18313
18685
|
if (explicitPath) {
|
|
18314
|
-
return
|
|
18686
|
+
return import_node_path50.default.resolve(explicitPath);
|
|
18315
18687
|
}
|
|
18316
|
-
const entryFile = process.argv[1] ?
|
|
18317
|
-
const entryDir =
|
|
18318
|
-
const resolvedEntryDir =
|
|
18688
|
+
const entryFile = process.argv[1] ? import_node_path50.default.resolve(process.argv[1]) : __filename;
|
|
18689
|
+
const entryDir = import_node_path50.default.dirname(entryFile);
|
|
18690
|
+
const resolvedEntryDir = import_node_fs38.default.existsSync(entryFile) ? import_node_path50.default.dirname(import_node_fs38.default.realpathSync(entryFile)) : entryDir;
|
|
18319
18691
|
const candidates = [
|
|
18320
|
-
|
|
18321
|
-
|
|
18322
|
-
|
|
18323
|
-
|
|
18324
|
-
|
|
18692
|
+
import_node_path50.default.resolve(resolvedEntryDir, "..", UPGRADE_PROMPTS_DIR),
|
|
18693
|
+
import_node_path50.default.resolve(entryDir, "..", UPGRADE_PROMPTS_DIR),
|
|
18694
|
+
import_node_path50.default.resolve(__dirname, "..", UPGRADE_PROMPTS_DIR),
|
|
18695
|
+
import_node_path50.default.resolve(__dirname, "..", "..", UPGRADE_PROMPTS_DIR),
|
|
18696
|
+
import_node_path50.default.resolve(process.cwd(), UPGRADE_PROMPTS_DIR)
|
|
18325
18697
|
];
|
|
18326
|
-
const existing = candidates.find((candidate) =>
|
|
18698
|
+
const existing = candidates.find((candidate) => import_node_fs38.default.existsSync(candidate));
|
|
18327
18699
|
return existing ?? candidates[0];
|
|
18328
18700
|
}
|
|
18329
18701
|
function loadUpgradeCatalog(options = {}) {
|
|
@@ -18339,12 +18711,12 @@ function loadUpgradeCatalog(options = {}) {
|
|
|
18339
18711
|
releases
|
|
18340
18712
|
};
|
|
18341
18713
|
}
|
|
18342
|
-
var
|
|
18714
|
+
var import_node_fs38, import_node_path50, SEMVER_RE, UPGRADE_PROMPTS_DIR;
|
|
18343
18715
|
var init_upgrade = __esm({
|
|
18344
18716
|
"src/core/upgrade.ts"() {
|
|
18345
18717
|
"use strict";
|
|
18346
|
-
|
|
18347
|
-
|
|
18718
|
+
import_node_fs38 = __toESM(require("fs"));
|
|
18719
|
+
import_node_path50 = __toESM(require("path"));
|
|
18348
18720
|
init_repo_config();
|
|
18349
18721
|
SEMVER_RE = /^v?(\d+)\.(\d+)\.(\d+)$/;
|
|
18350
18722
|
UPGRADE_PROMPTS_DIR = "upgrade-prompts";
|
|
@@ -18487,7 +18859,7 @@ function isLoopbackHost(host) {
|
|
|
18487
18859
|
return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
|
|
18488
18860
|
}
|
|
18489
18861
|
function inspectCompose(composeFile, _workspace) {
|
|
18490
|
-
if (!
|
|
18862
|
+
if (!import_node_fs39.default.existsSync(composeFile)) {
|
|
18491
18863
|
return {
|
|
18492
18864
|
aliases: [],
|
|
18493
18865
|
publishedPorts: [],
|
|
@@ -18496,7 +18868,7 @@ function inspectCompose(composeFile, _workspace) {
|
|
|
18496
18868
|
};
|
|
18497
18869
|
}
|
|
18498
18870
|
try {
|
|
18499
|
-
const raw =
|
|
18871
|
+
const raw = import_node_fs39.default.readFileSync(composeFile, "utf-8");
|
|
18500
18872
|
const parsed = import_yaml5.default.parse(raw);
|
|
18501
18873
|
const root = asRecord(parsed);
|
|
18502
18874
|
const services = asRecord(root?.services);
|
|
@@ -18552,12 +18924,51 @@ function routedProxyApps(config) {
|
|
|
18552
18924
|
(app) => app.kind !== "dependency" && app.runtime === "proxy"
|
|
18553
18925
|
);
|
|
18554
18926
|
}
|
|
18927
|
+
function buildHostPortClaimsCheck(repoPath, workspace) {
|
|
18928
|
+
try {
|
|
18929
|
+
const linked = isLinkedWorktree(repoPath);
|
|
18930
|
+
const runtime = loadRuntimeConfig(repoPath);
|
|
18931
|
+
const plan = inspectManagedDevcontainerConfig({
|
|
18932
|
+
repoPath,
|
|
18933
|
+
config: runtime.config,
|
|
18934
|
+
profile: runtime.resolvedProfile,
|
|
18935
|
+
linked
|
|
18936
|
+
});
|
|
18937
|
+
const gitCommonDir = linked ? resolveGitCommonDir(repoPath) : void 0;
|
|
18938
|
+
const workspaceEnv = linked && workspace && gitCommonDir ? { token: workspace, gitCommonDir } : void 0;
|
|
18939
|
+
const conflicts = detectHostPortClaimConflicts({ repoPath, plan, workspace: workspaceEnv });
|
|
18940
|
+
if (conflicts.length === 0) {
|
|
18941
|
+
return {
|
|
18942
|
+
id: "repo.host-port-claims",
|
|
18943
|
+
level: "ok",
|
|
18944
|
+
summary: "No configured fixed host-port binding conflicts with a running container."
|
|
18945
|
+
};
|
|
18946
|
+
}
|
|
18947
|
+
return {
|
|
18948
|
+
id: "repo.host-port-claims",
|
|
18949
|
+
level: "error",
|
|
18950
|
+
summary: `${conflicts.length} configured fixed host-port binding${conflicts.length === 1 ? "" : "s"} conflict${conflicts.length === 1 ? "s" : ""} with running containers.`,
|
|
18951
|
+
details: conflicts.map(
|
|
18952
|
+
(conflict) => `${conflict.service}: ${conflict.hostIp ?? "*"}:${conflict.hostPort}/${conflict.protocol} held by '${conflict.holderContainer}'` + (conflict.holderWorkspace ? ` (workspace '${conflict.holderWorkspace}')` : "")
|
|
18953
|
+
).join("; "),
|
|
18954
|
+
suggestion: "Stop the holding workspace or change the consumer's own published binding, then run ensure again. Devrouter never rewrites consumer-declared host bindings."
|
|
18955
|
+
};
|
|
18956
|
+
} catch (error) {
|
|
18957
|
+
return {
|
|
18958
|
+
id: "repo.host-port-claims",
|
|
18959
|
+
level: "warn",
|
|
18960
|
+
summary: "Could not compare configured fixed host-port bindings against live holders.",
|
|
18961
|
+
details: error instanceof Error ? error.message : String(error),
|
|
18962
|
+
suggestion: "Check Docker availability; a managed ensure refuses on this evidence before starting."
|
|
18963
|
+
};
|
|
18964
|
+
}
|
|
18965
|
+
}
|
|
18555
18966
|
function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
18556
|
-
const devcontainerDir =
|
|
18557
|
-
if (!
|
|
18967
|
+
const devcontainerDir = import_node_path51.default.join(repoPath, ".devcontainer");
|
|
18968
|
+
if (!import_node_fs39.default.existsSync(devcontainerDir)) {
|
|
18558
18969
|
return [];
|
|
18559
18970
|
}
|
|
18560
|
-
const compose = inspectCompose(
|
|
18971
|
+
const compose = inspectCompose(import_node_path51.default.join(devcontainerDir, "docker-compose.yml"), workspace);
|
|
18561
18972
|
const checks = [];
|
|
18562
18973
|
if (compose.parseError) {
|
|
18563
18974
|
checks.push({
|
|
@@ -18584,9 +18995,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
18584
18995
|
details: compose.publishedPorts.length > 0 ? compose.publishedPorts.join(", ") : void 0,
|
|
18585
18996
|
suggestion: compose.parseError || compose.publishedPorts.length > 0 ? "Remove published ports and route services through devnet aliases with devrouter proxy apps." : void 0
|
|
18586
18997
|
});
|
|
18587
|
-
const dockerfilePath =
|
|
18588
|
-
const dockerfileExists =
|
|
18589
|
-
const dockerfile = dockerfileExists ?
|
|
18998
|
+
const dockerfilePath = import_node_path51.default.join(devcontainerDir, "Dockerfile");
|
|
18999
|
+
const dockerfileExists = import_node_fs39.default.existsSync(dockerfilePath);
|
|
19000
|
+
const dockerfile = dockerfileExists ? import_node_fs39.default.readFileSync(dockerfilePath, "utf-8") : "";
|
|
18590
19001
|
const devrouterArtifacts = ["@devrouter/cli", "devrouter-process"].filter(
|
|
18591
19002
|
(artifact) => dockerfile.includes(artifact)
|
|
18592
19003
|
);
|
|
@@ -18623,6 +19034,9 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
18623
19034
|
});
|
|
18624
19035
|
return checks;
|
|
18625
19036
|
}
|
|
19037
|
+
if (config.managedRuntime) {
|
|
19038
|
+
checks.push(buildHostPortClaimsCheck(repoPath, workspace));
|
|
19039
|
+
}
|
|
18626
19040
|
const aliasSet = new Set(
|
|
18627
19041
|
compose.aliases.flatMap((alias) => expandAliasCandidates(alias, workspace)).map(normalizeWorkspaceToken)
|
|
18628
19042
|
);
|
|
@@ -18641,14 +19055,19 @@ function buildDevcontainerChecks(repoPath, config, workspace) {
|
|
|
18641
19055
|
});
|
|
18642
19056
|
return checks;
|
|
18643
19057
|
}
|
|
18644
|
-
var
|
|
19058
|
+
var import_node_fs39, import_node_path51, import_yaml5;
|
|
18645
19059
|
var init_devcontainer_diagnostics = __esm({
|
|
18646
19060
|
"src/core/devcontainer-diagnostics.ts"() {
|
|
18647
19061
|
"use strict";
|
|
18648
|
-
|
|
18649
|
-
|
|
19062
|
+
import_node_fs39 = __toESM(require("fs"));
|
|
19063
|
+
import_node_path51 = __toESM(require("path"));
|
|
18650
19064
|
import_yaml5 = __toESM(require("yaml"));
|
|
19065
|
+
init_devcontainer_profile();
|
|
19066
|
+
init_host_port_claims();
|
|
18651
19067
|
init_managed_post_start();
|
|
19068
|
+
init_repo_config();
|
|
19069
|
+
init_workspace();
|
|
19070
|
+
init_workspace_ownership();
|
|
18652
19071
|
}
|
|
18653
19072
|
});
|
|
18654
19073
|
|
|
@@ -18736,11 +19155,11 @@ function reconcileRouteRunConflict(repoPath, app) {
|
|
|
18736
19155
|
}
|
|
18737
19156
|
}
|
|
18738
19157
|
}
|
|
18739
|
-
var
|
|
19158
|
+
var import_node_fs40;
|
|
18740
19159
|
var init_route_state = __esm({
|
|
18741
19160
|
"src/core/route-state.ts"() {
|
|
18742
19161
|
"use strict";
|
|
18743
|
-
|
|
19162
|
+
import_node_fs40 = __toESM(require("fs"));
|
|
18744
19163
|
init_host_routes();
|
|
18745
19164
|
init_workspace();
|
|
18746
19165
|
}
|
|
@@ -18915,7 +19334,7 @@ function toRepoStatus(repoPath) {
|
|
|
18915
19334
|
const resolvedRepoPath = resolveRepoPath(repoPath);
|
|
18916
19335
|
const configPath = getRepoConfigPath(resolvedRepoPath);
|
|
18917
19336
|
const explicitRepo = typeof repoPath === "string" && repoPath.trim().length > 0;
|
|
18918
|
-
const configExists =
|
|
19337
|
+
const configExists = import_node_fs41.default.existsSync(configPath);
|
|
18919
19338
|
if (!explicitRepo && !configExists) {
|
|
18920
19339
|
return void 0;
|
|
18921
19340
|
}
|
|
@@ -19063,11 +19482,11 @@ async function collectRouterStatus(repoPath) {
|
|
|
19063
19482
|
}
|
|
19064
19483
|
};
|
|
19065
19484
|
}
|
|
19066
|
-
var
|
|
19485
|
+
var import_node_fs41;
|
|
19067
19486
|
var init_status = __esm({
|
|
19068
19487
|
"src/core/status.ts"() {
|
|
19069
19488
|
"use strict";
|
|
19070
|
-
|
|
19489
|
+
import_node_fs41 = __toESM(require("fs"));
|
|
19071
19490
|
init_docker();
|
|
19072
19491
|
init_managed_runtime_state();
|
|
19073
19492
|
init_managed_runtime_status();
|
|
@@ -19084,7 +19503,7 @@ function outputFromResult(result) {
|
|
|
19084
19503
|
return output2.length > 0 ? output2 : void 0;
|
|
19085
19504
|
}
|
|
19086
19505
|
function runTool(command, args = []) {
|
|
19087
|
-
const result = (0,
|
|
19506
|
+
const result = (0, import_node_child_process29.spawnSync)(command, args, {
|
|
19088
19507
|
encoding: "utf-8"
|
|
19089
19508
|
});
|
|
19090
19509
|
if (result.error) {
|
|
@@ -19141,12 +19560,12 @@ function parseMinimumNodeMajor(value) {
|
|
|
19141
19560
|
return Number(match[1]);
|
|
19142
19561
|
}
|
|
19143
19562
|
function readPackageJson(repoPath) {
|
|
19144
|
-
const packagePath =
|
|
19145
|
-
if (!
|
|
19563
|
+
const packagePath = import_node_path52.default.join(repoPath, "package.json");
|
|
19564
|
+
if (!import_node_fs42.default.existsSync(packagePath)) {
|
|
19146
19565
|
return void 0;
|
|
19147
19566
|
}
|
|
19148
19567
|
try {
|
|
19149
|
-
return JSON.parse(
|
|
19568
|
+
return JSON.parse(import_node_fs42.default.readFileSync(packagePath, "utf-8"));
|
|
19150
19569
|
} catch {
|
|
19151
19570
|
return void 0;
|
|
19152
19571
|
}
|
|
@@ -19330,13 +19749,13 @@ function buildGlobalToolChecksWithoutTelemetry(repoPath) {
|
|
|
19330
19749
|
checks.push(nodeToolchainCheck(repoPath));
|
|
19331
19750
|
return checks;
|
|
19332
19751
|
}
|
|
19333
|
-
var
|
|
19752
|
+
var import_node_child_process29, import_node_fs42, import_node_path52, DEVSY_DISABLE_TELEMETRY;
|
|
19334
19753
|
var init_tool_diagnostics = __esm({
|
|
19335
19754
|
"src/core/tool-diagnostics.ts"() {
|
|
19336
19755
|
"use strict";
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19756
|
+
import_node_child_process29 = require("child_process");
|
|
19757
|
+
import_node_fs42 = __toESM(require("fs"));
|
|
19758
|
+
import_node_path52 = __toESM(require("path"));
|
|
19340
19759
|
init_devsy_agent();
|
|
19341
19760
|
init_workspace_runtime();
|
|
19342
19761
|
DEVSY_DISABLE_TELEMETRY = "DEVSY_DISABLE_TELEMETRY";
|
|
@@ -19353,10 +19772,10 @@ function inRepositoryWorkspaceScope(repoPath, worktreePath, livePaths) {
|
|
|
19353
19772
|
if (livePaths.some((candidate) => sameWorkspacePath(candidate, worktreePath))) return true;
|
|
19354
19773
|
const comparableRepo = comparableWorkspacePath(repoPath);
|
|
19355
19774
|
const comparableWorktree = comparableWorkspacePath(worktreePath);
|
|
19356
|
-
const localRoot =
|
|
19775
|
+
const localRoot = import_node_path53.default.join(comparableRepo, "trees") + import_node_path53.default.sep;
|
|
19357
19776
|
if (comparableWorktree.startsWith(localRoot)) return true;
|
|
19358
|
-
const legacyPrefix = `${
|
|
19359
|
-
return
|
|
19777
|
+
const legacyPrefix = `${import_node_path53.default.basename(comparableRepo)}-`;
|
|
19778
|
+
return import_node_path53.default.dirname(comparableWorktree) === import_node_path53.default.dirname(comparableRepo) && import_node_path53.default.basename(comparableWorktree).startsWith(legacyPrefix);
|
|
19360
19779
|
}
|
|
19361
19780
|
function previewActions(devpodStatus, routeCount, includeRecord) {
|
|
19362
19781
|
const actions = [
|
|
@@ -19608,11 +20027,11 @@ function applyWorkspaceGc(plan) {
|
|
|
19608
20027
|
candidates
|
|
19609
20028
|
};
|
|
19610
20029
|
}
|
|
19611
|
-
var
|
|
20030
|
+
var import_node_path53;
|
|
19612
20031
|
var init_workspace_gc = __esm({
|
|
19613
20032
|
"src/core/workspace-gc.ts"() {
|
|
19614
20033
|
"use strict";
|
|
19615
|
-
|
|
20034
|
+
import_node_path53 = __toESM(require("path"));
|
|
19616
20035
|
init_devpod_mutation();
|
|
19617
20036
|
init_devpod_workspaces();
|
|
19618
20037
|
init_host_routes();
|
|
@@ -19682,11 +20101,11 @@ function inspectPostgresCredentials(repoPath, config) {
|
|
|
19682
20101
|
parseErrors.push(`${app.name}: ${composeFile} (${message})`);
|
|
19683
20102
|
continue;
|
|
19684
20103
|
}
|
|
19685
|
-
if (!
|
|
20104
|
+
if (!import_node_fs43.default.existsSync(absolutePath)) {
|
|
19686
20105
|
continue;
|
|
19687
20106
|
}
|
|
19688
20107
|
try {
|
|
19689
|
-
const raw =
|
|
20108
|
+
const raw = import_node_fs43.default.readFileSync(absolutePath, "utf-8");
|
|
19690
20109
|
const parsed = import_yaml6.default.parse(raw);
|
|
19691
20110
|
const root = asRecord2(parsed);
|
|
19692
20111
|
const services = asRecord2(root?.services);
|
|
@@ -19966,7 +20385,7 @@ async function buildDoctorReport(options = {}) {
|
|
|
19966
20385
|
const config = runtimeConfig.config;
|
|
19967
20386
|
loadedConfig = config;
|
|
19968
20387
|
loadedWorkspace = runtimeConfig.workspace;
|
|
19969
|
-
const cliVersion = true ? "0.0.
|
|
20388
|
+
const cliVersion = true ? "0.0.69" : "0.0.0-dev";
|
|
19970
20389
|
const configVersion = config.devrouter?.version;
|
|
19971
20390
|
if (configVersion && cliVersion !== "0.0.0-dev" && compareSemver(configVersion, cliVersion) > 0) {
|
|
19972
20391
|
addCheck(checks, {
|
|
@@ -19996,9 +20415,9 @@ async function buildDoctorReport(options = {}) {
|
|
|
19996
20415
|
(app) => app.docker.composeFiles.map((filePath) => ({
|
|
19997
20416
|
app: app.name,
|
|
19998
20417
|
filePath,
|
|
19999
|
-
absolutePath:
|
|
20418
|
+
absolutePath: import_node_path54.default.resolve(repo.path, filePath)
|
|
20000
20419
|
}))
|
|
20001
|
-
).filter((entry) => !
|
|
20420
|
+
).filter((entry) => !import_node_fs43.default.existsSync(entry.absolutePath));
|
|
20002
20421
|
addCheck(checks, {
|
|
20003
20422
|
id: "repo.compose-files",
|
|
20004
20423
|
level: missingComposeFiles.length === 0 ? "ok" : "error",
|
|
@@ -20041,8 +20460,8 @@ async function buildDoctorReport(options = {}) {
|
|
|
20041
20460
|
const missingHostCwds = config.apps.filter((app) => app.runtime === "host").map((app) => ({
|
|
20042
20461
|
app: app.name,
|
|
20043
20462
|
cwd: app.hostRun.cwd,
|
|
20044
|
-
absolutePath:
|
|
20045
|
-
})).filter((entry) => !
|
|
20463
|
+
absolutePath: import_node_path54.default.resolve(repo.path, app.hostRun.cwd)
|
|
20464
|
+
})).filter((entry) => !import_node_fs43.default.existsSync(entry.absolutePath));
|
|
20046
20465
|
addCheck(checks, {
|
|
20047
20466
|
id: "repo.host-cwd",
|
|
20048
20467
|
level: missingHostCwds.length === 0 ? "ok" : "error",
|
|
@@ -20186,12 +20605,12 @@ async function buildDoctorReport(options = {}) {
|
|
|
20186
20605
|
nextSteps
|
|
20187
20606
|
};
|
|
20188
20607
|
}
|
|
20189
|
-
var
|
|
20608
|
+
var import_node_fs43, import_node_path54, import_yaml6, POSTGRES_DEFAULTS;
|
|
20190
20609
|
var init_doctor = __esm({
|
|
20191
20610
|
"src/core/doctor.ts"() {
|
|
20192
20611
|
"use strict";
|
|
20193
|
-
|
|
20194
|
-
|
|
20612
|
+
import_node_fs43 = __toESM(require("fs"));
|
|
20613
|
+
import_node_path54 = __toESM(require("path"));
|
|
20195
20614
|
import_yaml6 = __toESM(require("yaml"));
|
|
20196
20615
|
init_docker();
|
|
20197
20616
|
init_host_routes();
|
|
@@ -20507,12 +20926,12 @@ function parseSsPortListeners(stdout, port) {
|
|
|
20507
20926
|
return listeners;
|
|
20508
20927
|
}
|
|
20509
20928
|
function findPortListeners(port) {
|
|
20510
|
-
const result = (0,
|
|
20929
|
+
const result = (0, import_node_child_process30.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
|
|
20511
20930
|
encoding: "utf-8"
|
|
20512
20931
|
});
|
|
20513
20932
|
if (result.error && result.error.code === "ENOENT") {
|
|
20514
20933
|
if (process.platform === "linux") {
|
|
20515
|
-
const ssResult = (0,
|
|
20934
|
+
const ssResult = (0, import_node_child_process30.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
|
|
20516
20935
|
if (ssResult.status === 0 && ssResult.stdout) {
|
|
20517
20936
|
return parseSsPortListeners(ssResult.stdout, port);
|
|
20518
20937
|
}
|
|
@@ -20541,11 +20960,11 @@ function findPortListeners(port) {
|
|
|
20541
20960
|
};
|
|
20542
20961
|
});
|
|
20543
20962
|
}
|
|
20544
|
-
var
|
|
20963
|
+
var import_node_child_process30;
|
|
20545
20964
|
var init_ports = __esm({
|
|
20546
20965
|
"src/util/ports.ts"() {
|
|
20547
20966
|
"use strict";
|
|
20548
|
-
|
|
20967
|
+
import_node_child_process30 = require("child_process");
|
|
20549
20968
|
}
|
|
20550
20969
|
});
|
|
20551
20970
|
|
|
@@ -20632,14 +21051,33 @@ async function runEnsureCommand(options) {
|
|
|
20632
21051
|
profile: options.profile,
|
|
20633
21052
|
...options.repair ? { repair: true } : {}
|
|
20634
21053
|
});
|
|
21054
|
+
const conflicts = result.hostPortConflicts ?? [];
|
|
20635
21055
|
const applicationFailed = result.applicationReadiness?.status === "application-error";
|
|
20636
|
-
if (applicationFailed) process.exitCode = 1;
|
|
21056
|
+
if (applicationFailed || conflicts.length > 0) process.exitCode = 1;
|
|
20637
21057
|
if (options.json) {
|
|
20638
21058
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
20639
21059
|
`);
|
|
20640
21060
|
return;
|
|
20641
21061
|
}
|
|
20642
21062
|
const label2 = result.kind === "primary" ? `Primary checkout [profile: ${result.profile}]` : `Workspace '${result.workspace}' [profile: ${result.profile}]`;
|
|
21063
|
+
if (conflicts.length > 0) {
|
|
21064
|
+
const lines = conflicts.map((conflict) => {
|
|
21065
|
+
const desired = `${conflict.hostIp ?? "*"}:${conflict.hostPort}/${conflict.protocol}`;
|
|
21066
|
+
const holder = [
|
|
21067
|
+
`'${conflict.holderContainer}'`,
|
|
21068
|
+
conflict.holderComposeProject ? `compose project '${conflict.holderComposeProject}'` : void 0,
|
|
21069
|
+
conflict.holderWorkspace ? `workspace '${conflict.holderWorkspace}'` : void 0
|
|
21070
|
+
].filter(Boolean).join(", ");
|
|
21071
|
+
return ` - service '${conflict.service}' needs ${desired}, held by ${holder}
|
|
21072
|
+
${conflict.remediation}`;
|
|
21073
|
+
});
|
|
21074
|
+
process.stdout.write(
|
|
21075
|
+
`${label2} was not started: ${conflicts.length} fixed host-port claim${conflicts.length === 1 ? "" : "s"} conflict${conflicts.length === 1 ? "s" : ""} with running containers.
|
|
21076
|
+
${lines.join("\n")}
|
|
21077
|
+
`
|
|
21078
|
+
);
|
|
21079
|
+
return;
|
|
21080
|
+
}
|
|
20643
21081
|
const routes = result.urls.map((url) => ` ${url}`).join("\n");
|
|
20644
21082
|
process.stdout.write(
|
|
20645
21083
|
`${label2} ${applicationFailed ? "has available infrastructure but failed its application readiness contract" : "is ready"} (${result.devpodId}).
|
|
@@ -20891,7 +21329,7 @@ async function runOpenCommand(name) {
|
|
|
20891
21329
|
);
|
|
20892
21330
|
return;
|
|
20893
21331
|
}
|
|
20894
|
-
const result = (0,
|
|
21332
|
+
const result = (0, import_node_child_process31.spawnSync)("open", [url], { encoding: "utf-8" });
|
|
20895
21333
|
if (result.status !== 0) {
|
|
20896
21334
|
const details = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
20897
21335
|
throw new Error(`Unable to open '${url}': ${details || "unknown error"}`);
|
|
@@ -20899,11 +21337,11 @@ async function runOpenCommand(name) {
|
|
|
20899
21337
|
process.stdout.write(`Opened ${url}
|
|
20900
21338
|
`);
|
|
20901
21339
|
}
|
|
20902
|
-
var
|
|
21340
|
+
var import_node_child_process31;
|
|
20903
21341
|
var init_open = __esm({
|
|
20904
21342
|
"src/commands/open.ts"() {
|
|
20905
21343
|
"use strict";
|
|
20906
|
-
|
|
21344
|
+
import_node_child_process31 = require("child_process");
|
|
20907
21345
|
init_docker();
|
|
20908
21346
|
init_host_routes();
|
|
20909
21347
|
init_repo_config();
|
|
@@ -20926,7 +21364,7 @@ async function runLogsCommand(options) {
|
|
|
20926
21364
|
const args = ["logs", "--tail", tail, ROUTER_CONTAINER_NAME];
|
|
20927
21365
|
if (options.follow) {
|
|
20928
21366
|
args.splice(1, 0, "-f");
|
|
20929
|
-
const child = (0,
|
|
21367
|
+
const child = (0, import_node_child_process32.spawn)("docker", args, { stdio: "inherit" });
|
|
20930
21368
|
const onSignal = () => {
|
|
20931
21369
|
child.kill("SIGTERM");
|
|
20932
21370
|
};
|
|
@@ -20940,17 +21378,17 @@ async function runLogsCommand(options) {
|
|
|
20940
21378
|
});
|
|
20941
21379
|
});
|
|
20942
21380
|
} else {
|
|
20943
|
-
const result = (0,
|
|
21381
|
+
const result = (0, import_node_child_process32.spawnSync)("docker", args, { stdio: "inherit" });
|
|
20944
21382
|
if (result.status !== 0) {
|
|
20945
21383
|
throw new Error("Failed to retrieve router logs.");
|
|
20946
21384
|
}
|
|
20947
21385
|
}
|
|
20948
21386
|
}
|
|
20949
|
-
var
|
|
21387
|
+
var import_node_child_process32;
|
|
20950
21388
|
var init_logs = __esm({
|
|
20951
21389
|
"src/commands/logs.ts"() {
|
|
20952
21390
|
"use strict";
|
|
20953
|
-
|
|
21391
|
+
import_node_child_process32 = require("child_process");
|
|
20954
21392
|
init_docker();
|
|
20955
21393
|
init_router();
|
|
20956
21394
|
}
|
|
@@ -20990,17 +21428,17 @@ function asRecord3(value) {
|
|
|
20990
21428
|
return value;
|
|
20991
21429
|
}
|
|
20992
21430
|
function readJson(filePath) {
|
|
20993
|
-
if (!
|
|
21431
|
+
if (!import_node_fs44.default.existsSync(filePath)) {
|
|
20994
21432
|
return void 0;
|
|
20995
21433
|
}
|
|
20996
21434
|
try {
|
|
20997
|
-
return JSON.parse(
|
|
21435
|
+
return JSON.parse(import_node_fs44.default.readFileSync(filePath, "utf-8"));
|
|
20998
21436
|
} catch {
|
|
20999
21437
|
return void 0;
|
|
21000
21438
|
}
|
|
21001
21439
|
}
|
|
21002
21440
|
function relative(repoPath, filePath) {
|
|
21003
|
-
return
|
|
21441
|
+
return import_node_path55.default.relative(repoPath, filePath) || ".";
|
|
21004
21442
|
}
|
|
21005
21443
|
function redactEnvAssignments(value) {
|
|
21006
21444
|
return value.replace(
|
|
@@ -21041,7 +21479,7 @@ function inspectPackageManager(repoPath, pkg) {
|
|
|
21041
21479
|
["bun.lock", "bun"]
|
|
21042
21480
|
];
|
|
21043
21481
|
for (const [fileName, name] of lockfiles) {
|
|
21044
|
-
if (
|
|
21482
|
+
if (import_node_fs44.default.existsSync(import_node_path55.default.join(repoPath, fileName))) {
|
|
21045
21483
|
return { name, source: fileName };
|
|
21046
21484
|
}
|
|
21047
21485
|
}
|
|
@@ -21056,9 +21494,9 @@ function inspectNode(repoPath, pkg) {
|
|
|
21056
21494
|
if (typeof engines?.node === "string") {
|
|
21057
21495
|
return { version: engines.node, source: "package.json:engines.node" };
|
|
21058
21496
|
}
|
|
21059
|
-
const nvmrc =
|
|
21060
|
-
if (
|
|
21061
|
-
const version =
|
|
21497
|
+
const nvmrc = import_node_path55.default.join(repoPath, ".nvmrc");
|
|
21498
|
+
if (import_node_fs44.default.existsSync(nvmrc)) {
|
|
21499
|
+
const version = import_node_fs44.default.readFileSync(nvmrc, "utf-8").trim();
|
|
21062
21500
|
return { version, source: ".nvmrc" };
|
|
21063
21501
|
}
|
|
21064
21502
|
return void 0;
|
|
@@ -21119,7 +21557,7 @@ function configuredComposeFiles(repoPath) {
|
|
|
21119
21557
|
const files = config.apps.filter(
|
|
21120
21558
|
(app) => app.runtime === "docker"
|
|
21121
21559
|
).flatMap((app) => app.docker.composeFiles).filter(
|
|
21122
|
-
(fileName) => !
|
|
21560
|
+
(fileName) => !import_node_path55.default.isAbsolute(fileName) && !import_node_path55.default.normalize(fileName).startsWith("..")
|
|
21123
21561
|
);
|
|
21124
21562
|
return Array.from(new Set(files));
|
|
21125
21563
|
} catch {
|
|
@@ -21137,7 +21575,7 @@ function composeFiles(repoPath) {
|
|
|
21137
21575
|
...configuredComposeFiles(repoPath)
|
|
21138
21576
|
];
|
|
21139
21577
|
return Array.from(new Set(candidates)).filter(
|
|
21140
|
-
(fileName) =>
|
|
21578
|
+
(fileName) => import_node_fs44.default.existsSync(import_node_path55.default.join(repoPath, fileName))
|
|
21141
21579
|
);
|
|
21142
21580
|
}
|
|
21143
21581
|
function stringArray2(value) {
|
|
@@ -21175,7 +21613,7 @@ function inspectServices(repoPath) {
|
|
|
21175
21613
|
const services = [];
|
|
21176
21614
|
for (const fileName of composeFiles(repoPath)) {
|
|
21177
21615
|
try {
|
|
21178
|
-
const parsed = import_yaml7.default.parse(
|
|
21616
|
+
const parsed = import_yaml7.default.parse(import_node_fs44.default.readFileSync(import_node_path55.default.join(repoPath, fileName), "utf-8"));
|
|
21179
21617
|
const serviceMap = asRecord3(asRecord3(parsed)?.services);
|
|
21180
21618
|
for (const [name, value] of Object.entries(serviceMap ?? {})) {
|
|
21181
21619
|
const service = asRecord3(value);
|
|
@@ -21210,8 +21648,8 @@ function inspectServices(repoPath) {
|
|
|
21210
21648
|
return services;
|
|
21211
21649
|
}
|
|
21212
21650
|
function inspectEnvFiles(repoPath) {
|
|
21213
|
-
const files =
|
|
21214
|
-
const content =
|
|
21651
|
+
const files = import_node_fs44.default.readdirSync(repoPath).filter((fileName) => /^\.env(\.|$)/.test(fileName)).sort().map((fileName) => {
|
|
21652
|
+
const content = import_node_fs44.default.readFileSync(import_node_path55.default.join(repoPath, fileName), "utf-8");
|
|
21215
21653
|
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
21654
|
return { path: fileName, names };
|
|
21217
21655
|
});
|
|
@@ -21225,18 +21663,18 @@ function inspectEnvFiles(repoPath) {
|
|
|
21225
21663
|
};
|
|
21226
21664
|
}
|
|
21227
21665
|
function inspectDevcontainer(repoPath) {
|
|
21228
|
-
const dir =
|
|
21229
|
-
if (!
|
|
21666
|
+
const dir = import_node_path55.default.join(repoPath, ".devcontainer");
|
|
21667
|
+
if (!import_node_fs44.default.existsSync(dir)) {
|
|
21230
21668
|
return { exists: false, files: [] };
|
|
21231
21669
|
}
|
|
21232
21670
|
return {
|
|
21233
21671
|
exists: true,
|
|
21234
|
-
files:
|
|
21672
|
+
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
21673
|
};
|
|
21236
21674
|
}
|
|
21237
21675
|
function inspectDevrouter(repoPath) {
|
|
21238
21676
|
const configPath = getRepoConfigPath(repoPath);
|
|
21239
|
-
if (!
|
|
21677
|
+
if (!import_node_fs44.default.existsSync(configPath)) {
|
|
21240
21678
|
return {
|
|
21241
21679
|
exists: false,
|
|
21242
21680
|
configPath,
|
|
@@ -21280,15 +21718,15 @@ function inspectAgentGuidance(repoPath) {
|
|
|
21280
21718
|
["AGENTS.md", "agents"],
|
|
21281
21719
|
["CLAUDE.md", "claude"]
|
|
21282
21720
|
]) {
|
|
21283
|
-
if (
|
|
21721
|
+
if (import_node_fs44.default.existsSync(import_node_path55.default.join(repoPath, fileName))) {
|
|
21284
21722
|
results.push({ path: fileName, kind });
|
|
21285
21723
|
}
|
|
21286
21724
|
}
|
|
21287
|
-
const skillsDir =
|
|
21288
|
-
if (
|
|
21289
|
-
for (const name of
|
|
21290
|
-
const skillPath =
|
|
21291
|
-
if (
|
|
21725
|
+
const skillsDir = import_node_path55.default.join(repoPath, ".agents", "skills");
|
|
21726
|
+
if (import_node_fs44.default.existsSync(skillsDir)) {
|
|
21727
|
+
for (const name of import_node_fs44.default.readdirSync(skillsDir).sort()) {
|
|
21728
|
+
const skillPath = import_node_path55.default.join(skillsDir, name, "SKILL.md");
|
|
21729
|
+
if (import_node_fs44.default.existsSync(skillPath)) {
|
|
21292
21730
|
results.push({ path: relative(repoPath, skillPath), kind: "skill" });
|
|
21293
21731
|
}
|
|
21294
21732
|
}
|
|
@@ -21331,7 +21769,7 @@ function buildIssues(report) {
|
|
|
21331
21769
|
}
|
|
21332
21770
|
function inspectRepo(options = {}) {
|
|
21333
21771
|
const repoPath = resolveRepoPath(options.repo);
|
|
21334
|
-
const pkg = readJson(
|
|
21772
|
+
const pkg = readJson(import_node_path55.default.join(repoPath, "package.json"));
|
|
21335
21773
|
const scripts = inspectScripts(pkg);
|
|
21336
21774
|
const reportWithoutIssues = {
|
|
21337
21775
|
repoPath,
|
|
@@ -21350,12 +21788,12 @@ function inspectRepo(options = {}) {
|
|
|
21350
21788
|
issues: buildIssues(reportWithoutIssues)
|
|
21351
21789
|
};
|
|
21352
21790
|
}
|
|
21353
|
-
var
|
|
21791
|
+
var import_node_fs44, import_node_path55, import_yaml7;
|
|
21354
21792
|
var init_repo_inspect = __esm({
|
|
21355
21793
|
"src/core/repo-inspect.ts"() {
|
|
21356
21794
|
"use strict";
|
|
21357
|
-
|
|
21358
|
-
|
|
21795
|
+
import_node_fs44 = __toESM(require("fs"));
|
|
21796
|
+
import_node_path55 = __toESM(require("path"));
|
|
21359
21797
|
import_yaml7 = __toESM(require("yaml"));
|
|
21360
21798
|
init_repo_config();
|
|
21361
21799
|
}
|
|
@@ -21460,7 +21898,7 @@ function requiredFileChecks(repoPath) {
|
|
|
21460
21898
|
".devcontainer/docker-compose.yml",
|
|
21461
21899
|
".devrouter.yml"
|
|
21462
21900
|
];
|
|
21463
|
-
const missing = required.filter((fileName) => !
|
|
21901
|
+
const missing = required.filter((fileName) => !import_node_fs45.default.existsSync(import_node_path56.default.join(repoPath, fileName)));
|
|
21464
21902
|
return {
|
|
21465
21903
|
id: "repo.devcontainer.verify-files",
|
|
21466
21904
|
level: missing.length === 0 ? "ok" : "error",
|
|
@@ -21685,12 +22123,12 @@ async function verifyDevcontainer(options = {}) {
|
|
|
21685
22123
|
nextSteps: collectNextSteps3(checks)
|
|
21686
22124
|
};
|
|
21687
22125
|
}
|
|
21688
|
-
var
|
|
22126
|
+
var import_node_fs45, import_node_path56;
|
|
21689
22127
|
var init_devcontainer_verify = __esm({
|
|
21690
22128
|
"src/core/devcontainer-verify.ts"() {
|
|
21691
22129
|
"use strict";
|
|
21692
|
-
|
|
21693
|
-
|
|
22130
|
+
import_node_fs45 = __toESM(require("fs"));
|
|
22131
|
+
import_node_path56 = __toESM(require("path"));
|
|
21694
22132
|
init_capabilities();
|
|
21695
22133
|
init_doctor();
|
|
21696
22134
|
init_host_routes();
|
|
@@ -21987,7 +22425,7 @@ function packageManagerIssues(repo) {
|
|
|
21987
22425
|
}
|
|
21988
22426
|
function plannedFiles(repoPath, version) {
|
|
21989
22427
|
const repo = inspectRepo({ repo: repoPath });
|
|
21990
|
-
const projectName = sanitizeProjectName(
|
|
22428
|
+
const projectName = sanitizeProjectName(import_node_path57.default.basename(repo.repoPath));
|
|
21991
22429
|
const nodeMajor = majorVersion(repo.node?.version, "24");
|
|
21992
22430
|
const pnpmVersion = repo.packageManager?.name === "pnpm" && repo.packageManager.version ? repo.packageManager.version : DEFAULT_PNPM_VERSION;
|
|
21993
22431
|
const port = inferPort2(repo);
|
|
@@ -22032,8 +22470,8 @@ function plannedFiles(repoPath, version) {
|
|
|
22032
22470
|
};
|
|
22033
22471
|
}
|
|
22034
22472
|
function classifyFile(repoPath, file) {
|
|
22035
|
-
const absolutePath =
|
|
22036
|
-
if (!
|
|
22473
|
+
const absolutePath = import_node_path57.default.join(repoPath, file.relativePath);
|
|
22474
|
+
if (!import_node_fs46.default.existsSync(absolutePath)) {
|
|
22037
22475
|
return {
|
|
22038
22476
|
path: file.relativePath,
|
|
22039
22477
|
action: "create",
|
|
@@ -22041,7 +22479,7 @@ function classifyFile(repoPath, file) {
|
|
|
22041
22479
|
bytes: Buffer.byteLength(file.content)
|
|
22042
22480
|
};
|
|
22043
22481
|
}
|
|
22044
|
-
const current =
|
|
22482
|
+
const current = import_node_fs46.default.readFileSync(absolutePath, "utf-8");
|
|
22045
22483
|
if (!current.includes(MANAGED_MARKER2)) {
|
|
22046
22484
|
return {
|
|
22047
22485
|
path: file.relativePath,
|
|
@@ -22098,11 +22536,11 @@ function buildPlan(repoPath, dryRun, version) {
|
|
|
22098
22536
|
};
|
|
22099
22537
|
}
|
|
22100
22538
|
function writeFile(repoPath, file) {
|
|
22101
|
-
const absolutePath =
|
|
22102
|
-
|
|
22103
|
-
|
|
22539
|
+
const absolutePath = import_node_path57.default.join(repoPath, file.relativePath);
|
|
22540
|
+
import_node_fs46.default.mkdirSync(import_node_path57.default.dirname(absolutePath), { recursive: true });
|
|
22541
|
+
import_node_fs46.default.writeFileSync(absolutePath, file.content, "utf-8");
|
|
22104
22542
|
if (file.executable) {
|
|
22105
|
-
|
|
22543
|
+
import_node_fs46.default.chmodSync(absolutePath, 493);
|
|
22106
22544
|
}
|
|
22107
22545
|
}
|
|
22108
22546
|
function writeDevcontainer(options = {}) {
|
|
@@ -22140,12 +22578,12 @@ function writeDevcontainer(options = {}) {
|
|
|
22140
22578
|
nextSteps: postWriteNextSteps(repoPath)
|
|
22141
22579
|
};
|
|
22142
22580
|
}
|
|
22143
|
-
var
|
|
22581
|
+
var import_node_fs46, import_node_path57, MANAGED_MARKER2, DEFAULT_DEVROUTER_VERSION, DEFAULT_PNPM_VERSION, VALID_PACKAGE_VERSION_RE;
|
|
22144
22582
|
var init_devcontainer_write = __esm({
|
|
22145
22583
|
"src/core/devcontainer-write.ts"() {
|
|
22146
22584
|
"use strict";
|
|
22147
|
-
|
|
22148
|
-
|
|
22585
|
+
import_node_fs46 = __toESM(require("fs"));
|
|
22586
|
+
import_node_path57 = __toESM(require("path"));
|
|
22149
22587
|
init_repo_config();
|
|
22150
22588
|
init_repo_inspect();
|
|
22151
22589
|
MANAGED_MARKER2 = "devrouter:managed devcontainer";
|
|
@@ -22258,11 +22696,11 @@ var init_repo_devcontainer = __esm({
|
|
|
22258
22696
|
function fail3(message) {
|
|
22259
22697
|
throw new Error(message);
|
|
22260
22698
|
}
|
|
22261
|
-
function
|
|
22699
|
+
function isRecord7(value) {
|
|
22262
22700
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22263
22701
|
}
|
|
22264
22702
|
function requireRecord(value, label2) {
|
|
22265
|
-
if (!
|
|
22703
|
+
if (!isRecord7(value)) fail3(`${label2} must be a mapping.`);
|
|
22266
22704
|
return value;
|
|
22267
22705
|
}
|
|
22268
22706
|
function requireExactKeys(value, allowed, label2) {
|
|
@@ -22479,13 +22917,13 @@ function buildProfilePlanReport(options) {
|
|
|
22479
22917
|
return { ...report, contractPath, bindings };
|
|
22480
22918
|
}
|
|
22481
22919
|
function loadContract(repoPath, contractPath) {
|
|
22482
|
-
if (
|
|
22920
|
+
if (import_node_path58.default.isAbsolute(contractPath)) {
|
|
22483
22921
|
fail3("profile plan contract path must be relative to the repository.");
|
|
22484
22922
|
}
|
|
22485
22923
|
const absolutePath = assertPathWithinRepo(contractPath, repoPath, "Profile plan contract");
|
|
22486
22924
|
let metadata;
|
|
22487
22925
|
try {
|
|
22488
|
-
metadata =
|
|
22926
|
+
metadata = import_node_fs47.default.lstatSync(absolutePath);
|
|
22489
22927
|
} catch (error) {
|
|
22490
22928
|
fail3(`could not inspect profile plan contract '${contractPath}': ${error.message}`);
|
|
22491
22929
|
}
|
|
@@ -22495,16 +22933,16 @@ function loadContract(repoPath, contractPath) {
|
|
|
22495
22933
|
if (metadata.size > MAX_CONTRACT_BYTES) {
|
|
22496
22934
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
22497
22935
|
}
|
|
22498
|
-
const realRepo =
|
|
22499
|
-
const realContract =
|
|
22936
|
+
const realRepo = import_node_fs47.default.realpathSync(repoPath);
|
|
22937
|
+
const realContract = import_node_fs47.default.realpathSync(absolutePath);
|
|
22500
22938
|
assertPathWithinRepo(realContract, realRepo, "Profile plan contract");
|
|
22501
|
-
const source =
|
|
22939
|
+
const source = import_node_fs47.default.readFileSync(absolutePath, "utf-8");
|
|
22502
22940
|
if (Buffer.byteLength(source, "utf-8") > MAX_CONTRACT_BYTES) {
|
|
22503
22941
|
fail3(`profile plan contract exceeds the ${MAX_CONTRACT_BYTES}-byte limit.`);
|
|
22504
22942
|
}
|
|
22505
22943
|
return {
|
|
22506
22944
|
contract: parseProfilePlanContract(source),
|
|
22507
|
-
contractPath:
|
|
22945
|
+
contractPath: import_node_path58.default.relative(repoPath, absolutePath).split(import_node_path58.default.sep).join("/")
|
|
22508
22946
|
};
|
|
22509
22947
|
}
|
|
22510
22948
|
function resolveProfilePlan(options) {
|
|
@@ -22518,12 +22956,12 @@ function resolveProfilePlan(options) {
|
|
|
22518
22956
|
contractPath: loaded.contractPath
|
|
22519
22957
|
});
|
|
22520
22958
|
}
|
|
22521
|
-
var
|
|
22959
|
+
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
22960
|
var init_profile_plan = __esm({
|
|
22523
22961
|
"src/core/profile-plan.ts"() {
|
|
22524
22962
|
"use strict";
|
|
22525
|
-
|
|
22526
|
-
|
|
22963
|
+
import_node_fs47 = __toESM(require("fs"));
|
|
22964
|
+
import_node_path58 = __toESM(require("path"));
|
|
22527
22965
|
import_yaml8 = require("yaml");
|
|
22528
22966
|
init_paths();
|
|
22529
22967
|
init_profile_resolution();
|
|
@@ -22555,7 +22993,7 @@ async function runProfileResolveCommand(options) {
|
|
|
22555
22993
|
async function runProfilePlanCommand(options) {
|
|
22556
22994
|
const report = resolveProfilePlan(options);
|
|
22557
22995
|
if (options.output) {
|
|
22558
|
-
writeFileAtomically(
|
|
22996
|
+
writeFileAtomically(import_node_path59.default.resolve(options.output), `${JSON.stringify(report, null, 2)}
|
|
22559
22997
|
`);
|
|
22560
22998
|
}
|
|
22561
22999
|
if (options.json) {
|
|
@@ -22591,14 +23029,14 @@ function printProfilePlanSummary(report, output2) {
|
|
|
22591
23029
|
process.stdout.write(`Binding ${key}: ${renderValues(report.bindings[key] ?? [])}
|
|
22592
23030
|
`);
|
|
22593
23031
|
}
|
|
22594
|
-
if (output2) process.stdout.write(`Output: ${
|
|
23032
|
+
if (output2) process.stdout.write(`Output: ${import_node_path59.default.resolve(output2)}
|
|
22595
23033
|
`);
|
|
22596
23034
|
}
|
|
22597
|
-
var
|
|
23035
|
+
var import_node_path59;
|
|
22598
23036
|
var init_profile = __esm({
|
|
22599
23037
|
"src/commands/profile.ts"() {
|
|
22600
23038
|
"use strict";
|
|
22601
|
-
|
|
23039
|
+
import_node_path59 = __toESM(require("path"));
|
|
22602
23040
|
init_atomic_file();
|
|
22603
23041
|
init_output();
|
|
22604
23042
|
init_profile_plan();
|
|
@@ -22888,7 +23326,7 @@ function sanitizeRouterId(value) {
|
|
|
22888
23326
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
22889
23327
|
}
|
|
22890
23328
|
function repoHash(repoPath) {
|
|
22891
|
-
return (0, import_node_crypto26.createHash)("sha1").update(
|
|
23329
|
+
return (0, import_node_crypto26.createHash)("sha1").update(import_node_path60.default.resolve(repoPath)).digest("hex").slice(0, 12);
|
|
22892
23330
|
}
|
|
22893
23331
|
function asDockerApp(app) {
|
|
22894
23332
|
return app.runtime === "docker";
|
|
@@ -22967,11 +23405,11 @@ function prepareDockerOverlay(repoPath, appName, apps, publishTcpPorts = false)
|
|
|
22967
23405
|
if (dockerApps.length === 0) {
|
|
22968
23406
|
throw new Error("No docker apps selected to prepare compose overlay.");
|
|
22969
23407
|
}
|
|
22970
|
-
const cachePath =
|
|
22971
|
-
|
|
22972
|
-
const overlayPath =
|
|
23408
|
+
const cachePath = import_node_path60.default.join(CACHE_DIR, repoHash(repoPath), sanitizeRouterId(appName));
|
|
23409
|
+
import_node_fs48.default.mkdirSync(cachePath, { recursive: true });
|
|
23410
|
+
const overlayPath = import_node_path60.default.join(cachePath, "compose.devrouter.yml");
|
|
22973
23411
|
const overlayDocument = buildOverlayDocument(dockerApps, publishTcpPorts);
|
|
22974
|
-
|
|
23412
|
+
import_node_fs48.default.writeFileSync(overlayPath, import_yaml9.default.stringify(overlayDocument, { lineWidth: 0 }), "utf-8");
|
|
22975
23413
|
return {
|
|
22976
23414
|
overlayPath,
|
|
22977
23415
|
composeFiles: ensureComposeFiles(dockerApps),
|
|
@@ -22985,7 +23423,7 @@ function runDockerComposeUp(repoPath, composeFiles2, overlayPath, services) {
|
|
|
22985
23423
|
fileArgs.push("-f", resolved);
|
|
22986
23424
|
}
|
|
22987
23425
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "up", "-d", "--wait", ...services];
|
|
22988
|
-
const result = (0,
|
|
23426
|
+
const result = (0, import_node_child_process33.spawnSync)("docker", args, {
|
|
22989
23427
|
encoding: "utf-8",
|
|
22990
23428
|
cwd: repoPath
|
|
22991
23429
|
});
|
|
@@ -23013,7 +23451,7 @@ function queryRunningComposeServices(repoPath, composeFiles2, overlayPath, servi
|
|
|
23013
23451
|
"--services",
|
|
23014
23452
|
...services
|
|
23015
23453
|
];
|
|
23016
|
-
const result = (0,
|
|
23454
|
+
const result = (0, import_node_child_process33.spawnSync)("docker", args, {
|
|
23017
23455
|
encoding: "utf-8",
|
|
23018
23456
|
cwd: repoPath
|
|
23019
23457
|
});
|
|
@@ -23039,7 +23477,7 @@ function runDockerComposeStop(repoPath, composeFiles2, overlayPath, services) {
|
|
|
23039
23477
|
fileArgs.push("-f", resolved);
|
|
23040
23478
|
}
|
|
23041
23479
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "stop", ...services];
|
|
23042
|
-
const result = (0,
|
|
23480
|
+
const result = (0, import_node_child_process33.spawnSync)("docker", args, {
|
|
23043
23481
|
encoding: "utf-8",
|
|
23044
23482
|
cwd: repoPath
|
|
23045
23483
|
});
|
|
@@ -23065,7 +23503,7 @@ function runDockerComposeLogs(repoPath, composeFiles2, overlayPath, services, ta
|
|
|
23065
23503
|
String(tail),
|
|
23066
23504
|
...services
|
|
23067
23505
|
];
|
|
23068
|
-
(0,
|
|
23506
|
+
(0, import_node_child_process33.spawnSync)("docker", args, {
|
|
23069
23507
|
stdio: "inherit",
|
|
23070
23508
|
cwd: repoPath
|
|
23071
23509
|
});
|
|
@@ -23077,7 +23515,7 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
23077
23515
|
fileArgs.push("-f", resolved);
|
|
23078
23516
|
}
|
|
23079
23517
|
const args = ["compose", ...fileArgs, "-f", overlayPath, "port", service, String(internalPort)];
|
|
23080
|
-
const result = (0,
|
|
23518
|
+
const result = (0, import_node_child_process33.spawnSync)("docker", args, {
|
|
23081
23519
|
encoding: "utf-8",
|
|
23082
23520
|
cwd: repoPath
|
|
23083
23521
|
});
|
|
@@ -23091,14 +23529,14 @@ function queryMappedPort(repoPath, composeFiles2, overlayPath, service, internal
|
|
|
23091
23529
|
const port = Number(match[1]);
|
|
23092
23530
|
return Number.isInteger(port) && port > 0 ? port : void 0;
|
|
23093
23531
|
}
|
|
23094
|
-
var
|
|
23532
|
+
var import_node_child_process33, import_node_crypto26, import_node_fs48, import_node_path60, import_yaml9;
|
|
23095
23533
|
var init_docker_run = __esm({
|
|
23096
23534
|
"src/core/docker-run.ts"() {
|
|
23097
23535
|
"use strict";
|
|
23098
|
-
|
|
23536
|
+
import_node_child_process33 = require("child_process");
|
|
23099
23537
|
import_node_crypto26 = require("crypto");
|
|
23100
|
-
|
|
23101
|
-
|
|
23538
|
+
import_node_fs48 = __toESM(require("fs"));
|
|
23539
|
+
import_node_path60 = __toESM(require("path"));
|
|
23102
23540
|
import_yaml9 = __toESM(require("yaml"));
|
|
23103
23541
|
init_docker_error_guidance();
|
|
23104
23542
|
init_paths();
|
|
@@ -23188,7 +23626,7 @@ function isProcessRunning(pid) {
|
|
|
23188
23626
|
}
|
|
23189
23627
|
}
|
|
23190
23628
|
function readProcessTree(rootPid) {
|
|
23191
|
-
const result = (0,
|
|
23629
|
+
const result = (0, import_node_child_process34.spawnSync)("ps", ["-ax", "-o", "pid=,ppid="], { encoding: "utf-8" });
|
|
23192
23630
|
if (result.status !== 0) {
|
|
23193
23631
|
return [rootPid];
|
|
23194
23632
|
}
|
|
@@ -23299,12 +23737,12 @@ function detectListeningPorts(pids) {
|
|
|
23299
23737
|
if (pids.length === 0) {
|
|
23300
23738
|
return [];
|
|
23301
23739
|
}
|
|
23302
|
-
const result = (0,
|
|
23740
|
+
const result = (0, import_node_child_process34.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")], {
|
|
23303
23741
|
encoding: "utf-8"
|
|
23304
23742
|
});
|
|
23305
23743
|
if (result.error && result.error.code === "ENOENT") {
|
|
23306
23744
|
if (process.platform === "linux") {
|
|
23307
|
-
const ssResult = (0,
|
|
23745
|
+
const ssResult = (0, import_node_child_process34.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
|
|
23308
23746
|
if (ssResult.status === 0 && ssResult.stdout) {
|
|
23309
23747
|
return parseSsListeningPorts(ssResult.stdout, new Set(pids));
|
|
23310
23748
|
}
|
|
@@ -23363,7 +23801,7 @@ async function runHostApp(repoPath, app, extraEnv = {}, secretManager, env, work
|
|
|
23363
23801
|
app.hostRun.command,
|
|
23364
23802
|
true
|
|
23365
23803
|
) : app.hostRun.command;
|
|
23366
|
-
const child = (0,
|
|
23804
|
+
const child = (0, import_node_child_process34.spawn)(spawnCommand, {
|
|
23367
23805
|
cwd: commandCwd,
|
|
23368
23806
|
stdio: "inherit",
|
|
23369
23807
|
shell: true,
|
|
@@ -23731,7 +24169,7 @@ async function execWithAppEnv(options) {
|
|
|
23731
24169
|
options.command[0],
|
|
23732
24170
|
true
|
|
23733
24171
|
);
|
|
23734
|
-
child = (0,
|
|
24172
|
+
child = (0, import_node_child_process34.spawn)(wrapped, {
|
|
23735
24173
|
cwd: deps.repoPath,
|
|
23736
24174
|
stdio: "inherit",
|
|
23737
24175
|
shell: true,
|
|
@@ -23745,7 +24183,7 @@ async function execWithAppEnv(options) {
|
|
|
23745
24183
|
false
|
|
23746
24184
|
);
|
|
23747
24185
|
const [cmd, ...wrappedArgs] = wrapped;
|
|
23748
|
-
child = (0,
|
|
24186
|
+
child = (0, import_node_child_process34.spawn)(cmd, wrappedArgs, {
|
|
23749
24187
|
cwd: deps.repoPath,
|
|
23750
24188
|
stdio: "inherit",
|
|
23751
24189
|
shell: false,
|
|
@@ -23753,7 +24191,7 @@ async function execWithAppEnv(options) {
|
|
|
23753
24191
|
});
|
|
23754
24192
|
}
|
|
23755
24193
|
} else if (options.shell) {
|
|
23756
|
-
child = (0,
|
|
24194
|
+
child = (0, import_node_child_process34.spawn)(options.command[0], {
|
|
23757
24195
|
cwd: deps.repoPath,
|
|
23758
24196
|
stdio: "inherit",
|
|
23759
24197
|
shell: true,
|
|
@@ -23761,7 +24199,7 @@ async function execWithAppEnv(options) {
|
|
|
23761
24199
|
});
|
|
23762
24200
|
} else {
|
|
23763
24201
|
const [command, ...args] = options.command;
|
|
23764
|
-
child = (0,
|
|
24202
|
+
child = (0, import_node_child_process34.spawn)(command, args, {
|
|
23765
24203
|
cwd: deps.repoPath,
|
|
23766
24204
|
stdio: "inherit",
|
|
23767
24205
|
shell: false,
|
|
@@ -23782,11 +24220,11 @@ async function execWithAppEnv(options) {
|
|
|
23782
24220
|
deps.stopDeps();
|
|
23783
24221
|
}
|
|
23784
24222
|
}
|
|
23785
|
-
var
|
|
24223
|
+
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
24224
|
var init_app_run = __esm({
|
|
23787
24225
|
"src/core/app-run.ts"() {
|
|
23788
24226
|
"use strict";
|
|
23789
|
-
|
|
24227
|
+
import_node_child_process34 = require("child_process");
|
|
23790
24228
|
import_node_net5 = __toESM(require("net"));
|
|
23791
24229
|
import_node_process = require("process");
|
|
23792
24230
|
import_promises = require("readline/promises");
|
|
@@ -23920,13 +24358,13 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
23920
24358
|
const startedAt = Date.now();
|
|
23921
24359
|
let rootStat;
|
|
23922
24360
|
try {
|
|
23923
|
-
rootStat =
|
|
24361
|
+
rootStat = import_node_fs49.default.lstatSync(worktreePath);
|
|
23924
24362
|
} catch (error) {
|
|
23925
24363
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
23926
24364
|
}
|
|
23927
24365
|
let rootEntries;
|
|
23928
24366
|
try {
|
|
23929
|
-
rootEntries =
|
|
24367
|
+
rootEntries = import_node_fs49.default.readdirSync(worktreePath, { withFileTypes: true });
|
|
23930
24368
|
} catch (error) {
|
|
23931
24369
|
return { status: "unknown", reason: describeError(error, worktreePath) };
|
|
23932
24370
|
}
|
|
@@ -23952,10 +24390,10 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
23952
24390
|
timedOut = true;
|
|
23953
24391
|
break;
|
|
23954
24392
|
}
|
|
23955
|
-
const entryPath =
|
|
24393
|
+
const entryPath = import_node_path61.default.join(dirPath, entry.name);
|
|
23956
24394
|
let entryStat;
|
|
23957
24395
|
try {
|
|
23958
|
-
entryStat =
|
|
24396
|
+
entryStat = import_node_fs49.default.lstatSync(entryPath);
|
|
23959
24397
|
} catch (error) {
|
|
23960
24398
|
if (error?.code === "ENOENT") continue;
|
|
23961
24399
|
unreadableReason = describeIncompleteWalk(error);
|
|
@@ -23965,7 +24403,7 @@ function measureWorktreeConsumption(worktreePath, options) {
|
|
|
23965
24403
|
if (!entryStat.isDirectory()) continue;
|
|
23966
24404
|
let childEntries;
|
|
23967
24405
|
try {
|
|
23968
|
-
childEntries =
|
|
24406
|
+
childEntries = import_node_fs49.default.readdirSync(entryPath, { withFileTypes: true });
|
|
23969
24407
|
} catch (error) {
|
|
23970
24408
|
unreadableReason = describeIncompleteWalk(error);
|
|
23971
24409
|
break;
|
|
@@ -24029,12 +24467,12 @@ function describeError(error, worktreePath) {
|
|
|
24029
24467
|
}
|
|
24030
24468
|
return `could not stat worktree path '${worktreePath}': ${error?.message ?? String(error)}`;
|
|
24031
24469
|
}
|
|
24032
|
-
var
|
|
24470
|
+
var import_node_fs49, import_node_path61, DEFAULT_DEADLINE_MS, BLOCK_SIZE_BYTES;
|
|
24033
24471
|
var init_workspace_consumption = __esm({
|
|
24034
24472
|
"src/core/workspace-consumption.ts"() {
|
|
24035
24473
|
"use strict";
|
|
24036
|
-
|
|
24037
|
-
|
|
24474
|
+
import_node_fs49 = __toESM(require("fs"));
|
|
24475
|
+
import_node_path61 = __toESM(require("path"));
|
|
24038
24476
|
init_devpod_environment();
|
|
24039
24477
|
DEFAULT_DEADLINE_MS = 1e4;
|
|
24040
24478
|
BLOCK_SIZE_BYTES = 512;
|
|
@@ -24042,7 +24480,7 @@ var init_workspace_consumption = __esm({
|
|
|
24042
24480
|
});
|
|
24043
24481
|
|
|
24044
24482
|
// src/core/workspace-cleanup.ts
|
|
24045
|
-
function
|
|
24483
|
+
function isRecord8(value) {
|
|
24046
24484
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
24047
24485
|
}
|
|
24048
24486
|
function isSha(value) {
|
|
@@ -24135,7 +24573,7 @@ function evaluateWorkspaceActivity(evidence, cutoff) {
|
|
|
24135
24573
|
}
|
|
24136
24574
|
function readGitSnapshot(worktree, commandRunner) {
|
|
24137
24575
|
const comparablePath = comparableWorkspacePath(worktree.path);
|
|
24138
|
-
if (worktree.prunable || !
|
|
24576
|
+
if (worktree.prunable || !import_node_fs50.default.existsSync(comparablePath)) {
|
|
24139
24577
|
return { worktree, checkout: "missing", head: null, committerDate: null };
|
|
24140
24578
|
}
|
|
24141
24579
|
const head = gitOutput(comparablePath, ["rev-parse", "--verify", "HEAD"], commandRunner);
|
|
@@ -24186,14 +24624,14 @@ function parseGitHubChanges(value, project, branch) {
|
|
|
24186
24624
|
if (!Array.isArray(value)) return void 0;
|
|
24187
24625
|
const changes = [];
|
|
24188
24626
|
for (const item of value) {
|
|
24189
|
-
if (!
|
|
24190
|
-
const repository =
|
|
24191
|
-
const headRepository =
|
|
24627
|
+
if (!isRecord8(item)) return void 0;
|
|
24628
|
+
const repository = isRecord8(item.repository) ? item.repository.nameWithOwner : void 0;
|
|
24629
|
+
const headRepository = isRecord8(item.headRepository) ? item.headRepository.nameWithOwner : void 0;
|
|
24192
24630
|
if (repository !== project || headRepository !== project || item.headRefName !== branch || !isSha(item.headRefOid) || typeof item.baseRefName !== "string") {
|
|
24193
24631
|
continue;
|
|
24194
24632
|
}
|
|
24195
24633
|
const merged = item.state === "MERGED" && validTimestamp(item.mergedAt);
|
|
24196
|
-
const mergeCommit =
|
|
24634
|
+
const mergeCommit = isRecord8(item.mergeCommit) && isSha(item.mergeCommit.oid) ? item.mergeCommit.oid : void 0;
|
|
24197
24635
|
const baseSha = isSha(item.baseRefOid) ? item.baseRefOid : void 0;
|
|
24198
24636
|
changes.push({
|
|
24199
24637
|
sourceBranch: branch,
|
|
@@ -24210,14 +24648,14 @@ function parseGitLabChanges(value, _project, branch) {
|
|
|
24210
24648
|
if (!Array.isArray(value)) return void 0;
|
|
24211
24649
|
const changes = [];
|
|
24212
24650
|
for (const item of value) {
|
|
24213
|
-
if (!
|
|
24651
|
+
if (!isRecord8(item)) return void 0;
|
|
24214
24652
|
const sourceProjectId = item.source_project_id;
|
|
24215
24653
|
const targetProjectId = item.target_project_id;
|
|
24216
24654
|
const sameProject = typeof sourceProjectId === "number" && typeof targetProjectId === "number" && sourceProjectId === targetProjectId;
|
|
24217
24655
|
if (!sameProject || item.source_branch !== branch || !isSha(item.sha) || typeof item.target_branch !== "string") {
|
|
24218
24656
|
continue;
|
|
24219
24657
|
}
|
|
24220
|
-
const diffRefs =
|
|
24658
|
+
const diffRefs = isRecord8(item.diff_refs) ? item.diff_refs : void 0;
|
|
24221
24659
|
const mergeCommitSha = isSha(item.merge_commit_sha) ? item.merge_commit_sha : void 0;
|
|
24222
24660
|
const baseSha = diffRefs && isSha(diffRefs.base_sha) ? diffRefs.base_sha : void 0;
|
|
24223
24661
|
changes.push({
|
|
@@ -24660,7 +25098,7 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
24660
25098
|
containers2 = measureContainersFn(worktreePaths);
|
|
24661
25099
|
} catch (error) {
|
|
24662
25100
|
const reason = `container measurement failed: ${describeCause(error)}`;
|
|
24663
|
-
containers2 = new Map(worktreePaths.map((
|
|
25101
|
+
containers2 = new Map(worktreePaths.map((path62) => [path62, unknownContainers(reason)]));
|
|
24664
25102
|
}
|
|
24665
25103
|
}
|
|
24666
25104
|
const rows2 = records.map((record5) => {
|
|
@@ -24710,12 +25148,12 @@ function buildWorkspaceCleanupReport(options = {}, dependencies = {}) {
|
|
|
24710
25148
|
networkCapacity: (dependencies.inspectNetworkCapacity ?? inspectNetworkCapacity)()
|
|
24711
25149
|
};
|
|
24712
25150
|
}
|
|
24713
|
-
var
|
|
25151
|
+
var import_node_child_process35, import_node_fs50, DEFAULT_INACTIVE_FOR, READ_ONLY_GIT_ENV2, ACTIVITY_SOURCES, defaultCommandRunner;
|
|
24714
25152
|
var init_workspace_cleanup = __esm({
|
|
24715
25153
|
"src/core/workspace-cleanup.ts"() {
|
|
24716
25154
|
"use strict";
|
|
24717
|
-
|
|
24718
|
-
|
|
25155
|
+
import_node_child_process35 = require("child_process");
|
|
25156
|
+
import_node_fs50 = __toESM(require("fs"));
|
|
24719
25157
|
init_devpod_workspaces();
|
|
24720
25158
|
init_host_routes();
|
|
24721
25159
|
init_network_diagnostics();
|
|
@@ -24733,7 +25171,7 @@ var init_workspace_cleanup = __esm({
|
|
|
24733
25171
|
];
|
|
24734
25172
|
defaultCommandRunner = (command, args, input2) => {
|
|
24735
25173
|
const environment = command === "git" ? READ_ONLY_GIT_ENV2 : { ...process.env, LC_ALL: "C" };
|
|
24736
|
-
const result = (0,
|
|
25174
|
+
const result = (0, import_node_child_process35.spawnSync)(command, args, {
|
|
24737
25175
|
encoding: "utf-8",
|
|
24738
25176
|
env: environment,
|
|
24739
25177
|
...input2 === void 0 ? {} : { input: input2 }
|
|
@@ -24827,20 +25265,20 @@ function warnMissingWorkspaceOwnership(repoPath) {
|
|
|
24827
25265
|
);
|
|
24828
25266
|
}
|
|
24829
25267
|
function defaultWorktreePath(mainRepo, ws) {
|
|
24830
|
-
return
|
|
25268
|
+
return import_node_path62.default.join(mainRepo, "trees", ws);
|
|
24831
25269
|
}
|
|
24832
25270
|
function assertDefaultWorktreeRootIgnored(mainRepo) {
|
|
24833
|
-
const ignored = (0,
|
|
25271
|
+
const ignored = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "check-ignore", "-q", "--no-index", "trees/"], {
|
|
24834
25272
|
encoding: "utf-8"
|
|
24835
25273
|
});
|
|
24836
25274
|
if (ignored.status !== 0) {
|
|
24837
25275
|
throw new Error(
|
|
24838
|
-
`Default worktree root '${
|
|
25276
|
+
`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
25277
|
);
|
|
24840
25278
|
}
|
|
24841
25279
|
}
|
|
24842
25280
|
function legacyDefaultWorktreePath(mainRepo, ws) {
|
|
24843
|
-
return
|
|
25281
|
+
return import_node_path62.default.join(import_node_path62.default.dirname(mainRepo), `${import_node_path62.default.basename(mainRepo)}-${ws}`);
|
|
24844
25282
|
}
|
|
24845
25283
|
function teardownFallbackPath(mainRepo, workspace) {
|
|
24846
25284
|
const candidates = [
|
|
@@ -24952,13 +25390,13 @@ function assertFullDownPreflight(mainRepo, target) {
|
|
|
24952
25390
|
if (sameWorkspacePath(target.worktreePath, mainRepo)) {
|
|
24953
25391
|
throw new Error("Refusing to remove the primary Git checkout.");
|
|
24954
25392
|
}
|
|
24955
|
-
if (!target.worktree || target.worktree.prunable || !
|
|
25393
|
+
if (!target.worktree || target.worktree.prunable || !import_node_fs51.default.existsSync(target.worktreePath)) return;
|
|
24956
25394
|
if (target.worktree.locked) {
|
|
24957
25395
|
throw new Error(
|
|
24958
25396
|
`Worktree '${target.worktreePath}' is locked; unlock it before workspace down.`
|
|
24959
25397
|
);
|
|
24960
25398
|
}
|
|
24961
|
-
const status = (0,
|
|
25399
|
+
const status = (0, import_node_child_process36.spawnSync)(
|
|
24962
25400
|
"git",
|
|
24963
25401
|
["-C", target.worktreePath, "status", "--porcelain", "--untracked-files=normal"],
|
|
24964
25402
|
{ encoding: "utf-8" }
|
|
@@ -24992,9 +25430,9 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
24992
25430
|
(candidate) => defaultWorktreePath(mainRepo, candidate)
|
|
24993
25431
|
);
|
|
24994
25432
|
const generatedPath = candidatePaths.find(
|
|
24995
|
-
(candidate) => !
|
|
25433
|
+
(candidate) => !import_node_fs51.default.existsSync(candidate) && !worktrees.some((worktree) => sameWorkspacePath(worktree.path, candidate))
|
|
24996
25434
|
);
|
|
24997
|
-
const selectedPath = opts.path ?
|
|
25435
|
+
const selectedPath = opts.path ? import_node_path62.default.resolve(opts.path) : existingBranch?.path ?? generatedPath;
|
|
24998
25436
|
if (!selectedPath) {
|
|
24999
25437
|
throw new Error(`Could not allocate a collision-safe worktree path for branch '${branch}'.`);
|
|
25000
25438
|
}
|
|
@@ -25003,7 +25441,7 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
25003
25441
|
`Branch '${branch}' already uses worktree '${existingBranch.path}', not '${selectedPath}'.`
|
|
25004
25442
|
);
|
|
25005
25443
|
}
|
|
25006
|
-
if (
|
|
25444
|
+
if (import_node_fs51.default.existsSync(selectedPath)) {
|
|
25007
25445
|
const registered = worktrees.find(
|
|
25008
25446
|
(worktree) => sameWorkspacePath(worktree.path, selectedPath)
|
|
25009
25447
|
);
|
|
@@ -25022,11 +25460,11 @@ async function workspaceUp(branch, opts = {}) {
|
|
|
25022
25460
|
return selectedPath;
|
|
25023
25461
|
}
|
|
25024
25462
|
if (!opts.path) assertDefaultWorktreeRootIgnored(mainRepo);
|
|
25025
|
-
const add = (0,
|
|
25463
|
+
const add = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "worktree", "add", selectedPath, branch], {
|
|
25026
25464
|
encoding: "utf-8"
|
|
25027
25465
|
});
|
|
25028
25466
|
if (add.status !== 0) {
|
|
25029
|
-
const addNew = (0,
|
|
25467
|
+
const addNew = (0, import_node_child_process36.spawnSync)(
|
|
25030
25468
|
"git",
|
|
25031
25469
|
["-C", mainRepo, "worktree", "add", "-b", branch, selectedPath],
|
|
25032
25470
|
{ encoding: "utf-8" }
|
|
@@ -25160,8 +25598,8 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25160
25598
|
}
|
|
25161
25599
|
const result = await mutateWorkspaceRuntime("delete", resolved, worktrees, opts.quiet);
|
|
25162
25600
|
if (removeWorktree) {
|
|
25163
|
-
if (resolved.worktree && !resolved.worktree.prunable &&
|
|
25164
|
-
const rm = (0,
|
|
25601
|
+
if (resolved.worktree && !resolved.worktree.prunable && import_node_fs51.default.existsSync(resolved.worktreePath)) {
|
|
25602
|
+
const rm = (0, import_node_child_process36.spawnSync)("git", ["-C", mainRepo, "worktree", "remove", resolved.worktreePath], {
|
|
25165
25603
|
encoding: "utf-8"
|
|
25166
25604
|
});
|
|
25167
25605
|
if (rm.status !== 0) {
|
|
@@ -25179,7 +25617,7 @@ async function runWorkspaceLifecycle(action2, target, opts = {}) {
|
|
|
25179
25617
|
}
|
|
25180
25618
|
return result;
|
|
25181
25619
|
};
|
|
25182
|
-
return resolved.worktree && !resolved.worktree.prunable &&
|
|
25620
|
+
return resolved.worktree && !resolved.worktree.prunable && import_node_fs51.default.existsSync(resolved.worktreePath) ? withLifecycleOperationLock(resolved.worktreePath, operation) : operation();
|
|
25183
25621
|
}
|
|
25184
25622
|
async function workspaceStop(target, opts = {}) {
|
|
25185
25623
|
return runWorkspaceLifecycle("stop", target, opts);
|
|
@@ -25187,13 +25625,13 @@ async function workspaceStop(target, opts = {}) {
|
|
|
25187
25625
|
async function workspaceDown(target, opts = {}) {
|
|
25188
25626
|
return runWorkspaceLifecycle("down", target, opts);
|
|
25189
25627
|
}
|
|
25190
|
-
var
|
|
25628
|
+
var import_node_child_process36, import_node_fs51, import_node_path62, WORKSPACE_ALLOCATION_LOCK_WAIT_MS;
|
|
25191
25629
|
var init_workspace_lifecycle = __esm({
|
|
25192
25630
|
"src/core/workspace-lifecycle.ts"() {
|
|
25193
25631
|
"use strict";
|
|
25194
|
-
|
|
25195
|
-
|
|
25196
|
-
|
|
25632
|
+
import_node_child_process36 = require("child_process");
|
|
25633
|
+
import_node_fs51 = __toESM(require("fs"));
|
|
25634
|
+
import_node_path62 = __toESM(require("path"));
|
|
25197
25635
|
init_devpod_mutation();
|
|
25198
25636
|
init_devpod_workspaces();
|
|
25199
25637
|
init_devsy_mutation();
|
|
@@ -25377,7 +25815,7 @@ var init_version = __esm({
|
|
|
25377
25815
|
|
|
25378
25816
|
// src/cli.ts
|
|
25379
25817
|
var import_commander = require("commander");
|
|
25380
|
-
var CLI_VERSION = true ? "0.0.
|
|
25818
|
+
var CLI_VERSION = true ? "0.0.69" : "0.0.0-dev";
|
|
25381
25819
|
var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
|
|
25382
25820
|
function withErrorHandling(action2) {
|
|
25383
25821
|
return async (...args) => {
|
|
@@ -25747,13 +26185,13 @@ workspaceCommand2.command("down").description("Delete a workspace's runtime and
|
|
|
25747
26185
|
await runWorkspaceDownCommand2(target, options);
|
|
25748
26186
|
})
|
|
25749
26187
|
);
|
|
25750
|
-
workspaceCommand2.command("journal settle").description(
|
|
26188
|
+
workspaceCommand2.command("journal").description("Inspect and recover workspace lifecycle journals").command("settle").description(
|
|
25751
26189
|
"Settle an interrupted lifecycle operation as unobservable so ensure and stop can proceed"
|
|
25752
26190
|
).argument("[path]", "Checkout path (defaults to current directory)").option("--json", "Output JSON").action(
|
|
25753
|
-
withErrorHandling(async (
|
|
26191
|
+
withErrorHandling(async (path62, _options, command) => {
|
|
25754
26192
|
const options = command.opts();
|
|
25755
26193
|
const { runWorkspaceJournalSettleCommand: runWorkspaceJournalSettleCommand2 } = await Promise.resolve().then(() => (init_workspace2(), workspace_exports));
|
|
25756
|
-
await runWorkspaceJournalSettleCommand2({ ...
|
|
26194
|
+
await runWorkspaceJournalSettleCommand2({ ...path62 ? { path: path62 } : {}, ...options });
|
|
25757
26195
|
})
|
|
25758
26196
|
);
|
|
25759
26197
|
function parseVersionRequest(argv) {
|