@getmonoceros/workbench 1.36.3 → 1.36.5
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/bin.js +95 -55
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2132,58 +2132,89 @@ async function preflightHostPort(hostPort, opts = {}) {
|
|
|
2132
2132
|
const probe = opts.portProbe ?? realPortProbe;
|
|
2133
2133
|
const result = await probe(hostPort);
|
|
2134
2134
|
if (result.ok) return;
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
function formatHostPortHeldError(hostPort, code, systemMessage) {
|
|
2140
|
-
const isInUse = code === "EADDRINUSE";
|
|
2141
|
-
const lines = [];
|
|
2142
|
-
if (isInUse) {
|
|
2143
|
-
lines.push(`Host port ${hostPort} is already in use by another process.`);
|
|
2144
|
-
lines.push("");
|
|
2145
|
-
lines.push(`Monoceros needs that port for its Traefik proxy (the thing`);
|
|
2146
|
-
lines.push(`that routes <name>.localhost / <name>-<port>.localhost to`);
|
|
2147
|
-
lines.push(`your dev-container). Two ways out:`);
|
|
2148
|
-
lines.push("");
|
|
2149
|
-
lines.push(" 1) Recommended: free the port.");
|
|
2150
|
-
lines.push(" Identify the process holding it:");
|
|
2151
|
-
lines.push(` sudo lsof -iTCP:${hostPort} -sTCP:LISTEN -n -P`);
|
|
2152
|
-
lines.push(` # or: sudo ss -tlnp | grep ":${hostPort}\\b"`);
|
|
2153
|
-
lines.push(" Then stop or reconfigure that service.");
|
|
2154
|
-
lines.push("");
|
|
2155
|
-
lines.push(" 2) Move Monoceros off port 80. Edit (or create)");
|
|
2156
|
-
lines.push(" ~/.monoceros/monoceros-config.yml and add:");
|
|
2157
|
-
lines.push("");
|
|
2158
|
-
lines.push(" schemaVersion: 1");
|
|
2159
|
-
lines.push(" routing:");
|
|
2160
|
-
lines.push(" hostPort: 8080 # any free port");
|
|
2161
|
-
lines.push("");
|
|
2162
|
-
lines.push(" URLs will become http://<name>.localhost:8080/.");
|
|
2163
|
-
lines.push("");
|
|
2164
|
-
lines.push(`Aborting \u2014 re-run after the conflict is resolved.`);
|
|
2165
|
-
} else {
|
|
2166
|
-
lines.push(`Cannot reach host port ${hostPort}: ${systemMessage}`);
|
|
2167
|
-
lines.push("");
|
|
2168
|
-
lines.push(`This is not the typical "port already in use" case \u2014`);
|
|
2169
|
-
lines.push(`Monoceros's pre-flight uses a TCP-connect probe (not a`);
|
|
2170
|
-
lines.push(`bind), so EACCES / privileged-port errors normally don't`);
|
|
2171
|
-
lines.push(`appear here. Most likely something on your host network`);
|
|
2172
|
-
lines.push(`stack (firewall, network namespace, \u2026) is interfering with`);
|
|
2173
|
-
lines.push(`loopback connects.`);
|
|
2174
|
-
lines.push("");
|
|
2175
|
-
lines.push("Workaround: move Monoceros off this port by setting");
|
|
2176
|
-
lines.push("`routing.hostPort` in ~/.monoceros/monoceros-config.yml.");
|
|
2177
|
-
lines.push("");
|
|
2178
|
-
lines.push(`Aborting \u2014 re-run after the issue is resolved.`);
|
|
2135
|
+
if (result.code !== "EADDRINUSE") {
|
|
2136
|
+
throw new Error(
|
|
2137
|
+
formatHostPortHeldError(hostPort, result.code, result.message)
|
|
2138
|
+
);
|
|
2179
2139
|
}
|
|
2180
|
-
|
|
2140
|
+
const live = await containersPublishing(docker, hostPort);
|
|
2141
|
+
if (live.length > 0) {
|
|
2142
|
+
throw new Error(formatLiveContainerError(hostPort, live));
|
|
2143
|
+
}
|
|
2144
|
+
throw new Error(formatLeftoverHolderError(hostPort));
|
|
2145
|
+
}
|
|
2146
|
+
async function containersPublishing(docker, hostPort) {
|
|
2147
|
+
const ps = await docker([
|
|
2148
|
+
"ps",
|
|
2149
|
+
"--filter",
|
|
2150
|
+
`publish=${hostPort}`,
|
|
2151
|
+
"--format",
|
|
2152
|
+
"{{.Names}} ({{.Image}})"
|
|
2153
|
+
]);
|
|
2154
|
+
if (ps.exitCode !== 0) return [];
|
|
2155
|
+
return ps.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
2156
|
+
}
|
|
2157
|
+
function hostPortFallbackLines(hostPort) {
|
|
2158
|
+
return [
|
|
2159
|
+
dim("Or set a different proxy port in ") + cyan2("~/.monoceros/monoceros-config.yml") + dim(":"),
|
|
2160
|
+
" " + cyan2("schemaVersion: 1"),
|
|
2161
|
+
" " + cyan2("routing:"),
|
|
2162
|
+
" " + cyan2(" hostPort: 8080"),
|
|
2163
|
+
"",
|
|
2164
|
+
dim(
|
|
2165
|
+
`Aborting. Re-run once port ${hostPort} is free or a different port is set.`
|
|
2166
|
+
)
|
|
2167
|
+
];
|
|
2168
|
+
}
|
|
2169
|
+
function formatLiveContainerError(hostPort, containers) {
|
|
2170
|
+
const firstName = containers[0].split(" ")[0];
|
|
2171
|
+
return [
|
|
2172
|
+
`Host port ${hostPort} is in use: it is published by a running container.`,
|
|
2173
|
+
"",
|
|
2174
|
+
dim("Held by:"),
|
|
2175
|
+
...containers.map((c) => " " + cyan2(c)),
|
|
2176
|
+
"",
|
|
2177
|
+
dim("Stop or re-map it, then re-run:"),
|
|
2178
|
+
" " + cyan2(`docker stop ${firstName}`),
|
|
2179
|
+
"",
|
|
2180
|
+
...hostPortFallbackLines(hostPort)
|
|
2181
|
+
].join("\n");
|
|
2182
|
+
}
|
|
2183
|
+
function formatLeftoverHolderError(hostPort) {
|
|
2184
|
+
return [
|
|
2185
|
+
`Host port ${hostPort} is in use, but no running container publishes it.`,
|
|
2186
|
+
"",
|
|
2187
|
+
dim("Most likely a leftover docker-proxy whose container is already gone"),
|
|
2188
|
+
dim("(a native dockerd in WSL strands these on restart), or a holder in"),
|
|
2189
|
+
dim("another Docker engine. Fix it one of two ways, then re-run:"),
|
|
2190
|
+
"",
|
|
2191
|
+
dim("Reap the leftover by restarting the Docker daemon (harmless):"),
|
|
2192
|
+
" " + cyan2("sudo systemctl restart docker"),
|
|
2193
|
+
"",
|
|
2194
|
+
...hostPortFallbackLines(hostPort)
|
|
2195
|
+
].join("\n");
|
|
2196
|
+
}
|
|
2197
|
+
function formatHostPortHeldError(hostPort, _code, systemMessage) {
|
|
2198
|
+
return [
|
|
2199
|
+
`Cannot reach host port ${hostPort}: ${systemMessage}`,
|
|
2200
|
+
"",
|
|
2201
|
+
dim('This is not the typical "port already in use" case. The pre-flight'),
|
|
2202
|
+
dim("uses a TCP-connect probe (not a bind), so EACCES / privileged-port"),
|
|
2203
|
+
dim("errors normally do not appear here. Most likely something on your"),
|
|
2204
|
+
dim("host network stack (firewall, network namespace) is interfering with"),
|
|
2205
|
+
dim("loopback connects."),
|
|
2206
|
+
"",
|
|
2207
|
+
dim("Workaround: move Monoceros off this port by setting ") + cyan2("routing.hostPort") + dim(" in ") + cyan2("~/.monoceros/monoceros-config.yml") + dim("."),
|
|
2208
|
+
"",
|
|
2209
|
+
dim("Aborting. Re-run after the issue is resolved.")
|
|
2210
|
+
].join("\n");
|
|
2181
2211
|
}
|
|
2182
2212
|
var CONNECT_TIMEOUT_MS, realPortProbe;
|
|
2183
2213
|
var init_port_check = __esm({
|
|
2184
2214
|
"src/proxy/port-check.ts"() {
|
|
2185
2215
|
"use strict";
|
|
2186
2216
|
init_proxy();
|
|
2217
|
+
init_format();
|
|
2187
2218
|
CONNECT_TIMEOUT_MS = 750;
|
|
2188
2219
|
realPortProbe = (port) => {
|
|
2189
2220
|
return new Promise((resolve) => {
|
|
@@ -4308,6 +4339,8 @@ var init_transform = __esm({
|
|
|
4308
4339
|
// src/briefing/agents-md.ts
|
|
4309
4340
|
function generateAgentsMd(input) {
|
|
4310
4341
|
const lines = [];
|
|
4342
|
+
const hostPort = input.hostPort ?? 80;
|
|
4343
|
+
const portSuffix = hostPort === 80 ? "" : `:${hostPort}`;
|
|
4311
4344
|
lines.push("# Monoceros Container \u2014 Stack Briefing");
|
|
4312
4345
|
lines.push("");
|
|
4313
4346
|
lines.push(
|
|
@@ -4435,18 +4468,18 @@ function generateAgentsMd(input) {
|
|
|
4435
4468
|
const port = input.ports[i];
|
|
4436
4469
|
if (i === 0) {
|
|
4437
4470
|
lines.push(
|
|
4438
|
-
`- ${port} (default route) \u2192 http://${input.containerName}.localhost`
|
|
4471
|
+
`- ${port} (default route) \u2192 http://${input.containerName}.localhost${portSuffix}`
|
|
4439
4472
|
);
|
|
4440
4473
|
} else {
|
|
4441
4474
|
lines.push(
|
|
4442
|
-
`- ${port} \u2192 http://${input.containerName}-${port}.localhost`
|
|
4475
|
+
`- ${port} \u2192 http://${input.containerName}-${port}.localhost${portSuffix}`
|
|
4443
4476
|
);
|
|
4444
4477
|
}
|
|
4445
4478
|
}
|
|
4446
4479
|
lines.push("");
|
|
4447
4480
|
lines.push(
|
|
4448
4481
|
"To show the user a running app, open it in their host browser with",
|
|
4449
|
-
`\`xdg-open http://${input.containerName}.localhost\` \u2014 Monoceros relays`,
|
|
4482
|
+
`\`xdg-open http://${input.containerName}.localhost${portSuffix}\` \u2014 Monoceros relays`,
|
|
4450
4483
|
"browser-opens from the container to the host machine. Also tell the user",
|
|
4451
4484
|
"the URL, so they can open it themselves if no bridge is active."
|
|
4452
4485
|
);
|
|
@@ -4466,7 +4499,7 @@ function generateAgentsMd(input) {
|
|
|
4466
4499
|
" Angular `--allowed-hosts`, CRA `DANGEROUSLY_DISABLE_HOST_CHECK`;",
|
|
4467
4500
|
"- it does **not pin the HMR/live-reload socket** to a fixed host or port",
|
|
4468
4501
|
" \u2014 let it follow the page URL (e.g. for Vite, do not set",
|
|
4469
|
-
|
|
4502
|
+
` \`server.hmr.clientPort\`), so HMR works on \`<name>.localhost${portSuffix}\` and over`,
|
|
4470
4503
|
" the LAN alike;",
|
|
4471
4504
|
"- the **backend is reached via the dev-server proxy** under a relative",
|
|
4472
4505
|
" path (Vite `server.proxy`, Angular `proxy.conf.json`, CRA",
|
|
@@ -4551,7 +4584,7 @@ function generateAgentsMd(input) {
|
|
|
4551
4584
|
"When you build something that serves on a port (a web app, an API),",
|
|
4552
4585
|
"it must keep running after this session ends. A plain `npm start` (or",
|
|
4553
4586
|
"any foreground start) dies the moment the user exits you or closes the",
|
|
4554
|
-
`terminal, and then \`${input.containerName}.localhost\` returns 502 Bad Gateway.`
|
|
4587
|
+
`terminal, and then \`${input.containerName}.localhost${portSuffix}\` returns 502 Bad Gateway.`
|
|
4555
4588
|
);
|
|
4556
4589
|
lines.push("");
|
|
4557
4590
|
lines.push(
|
|
@@ -4643,7 +4676,7 @@ function formatServiceLine(svc) {
|
|
|
4643
4676
|
}
|
|
4644
4677
|
return `- **${svc.name}** (custom image \`${svc.image}\`) \u2014 reachable at \`${reach}\``;
|
|
4645
4678
|
}
|
|
4646
|
-
function agentsMdInputFromCreateOptions(opts, featureDisplayMap2, manifestLoader) {
|
|
4679
|
+
function agentsMdInputFromCreateOptions(opts, featureDisplayMap2, manifestLoader, hostPort = 80) {
|
|
4647
4680
|
const features = [];
|
|
4648
4681
|
for (const [ref, userOptions] of Object.entries(opts.features ?? {})) {
|
|
4649
4682
|
const manifest = manifestLoader?.(ref);
|
|
@@ -4663,7 +4696,8 @@ function agentsMdInputFromCreateOptions(opts, featureDisplayMap2, manifestLoader
|
|
|
4663
4696
|
services: opts.services,
|
|
4664
4697
|
features,
|
|
4665
4698
|
repos: opts.repos ?? [],
|
|
4666
|
-
ports: opts.ports ?? []
|
|
4699
|
+
ports: opts.ports ?? [],
|
|
4700
|
+
hostPort
|
|
4667
4701
|
};
|
|
4668
4702
|
}
|
|
4669
4703
|
function resolveFeatureLines(ref, userOptions, manifest, featureDisplayMap2) {
|
|
@@ -4929,7 +4963,8 @@ async function writeBriefing(input) {
|
|
|
4929
4963
|
agentsMdInputFromCreateOptions(
|
|
4930
4964
|
input.createOpts,
|
|
4931
4965
|
featureDisplayMap(input.components),
|
|
4932
|
-
manifestLoader
|
|
4966
|
+
manifestLoader,
|
|
4967
|
+
input.hostPort ?? 80
|
|
4933
4968
|
)
|
|
4934
4969
|
);
|
|
4935
4970
|
const claudeBody = generateClaudeMd();
|
|
@@ -8317,7 +8352,12 @@ Fix the value in the env file (or the yml).`
|
|
|
8317
8352
|
}
|
|
8318
8353
|
try {
|
|
8319
8354
|
const components = await loadComponentCatalog();
|
|
8320
|
-
await writeBriefing({
|
|
8355
|
+
await writeBriefing({
|
|
8356
|
+
targetDir,
|
|
8357
|
+
createOpts,
|
|
8358
|
+
components,
|
|
8359
|
+
hostPort: proxyHostPort(globalConfig)
|
|
8360
|
+
});
|
|
8321
8361
|
} catch (err) {
|
|
8322
8362
|
const msg = `briefing files not written: ${err instanceof Error ? err.message : String(err)}`;
|
|
8323
8363
|
(logger.warn ?? logger.info)(msg);
|
|
@@ -8810,7 +8850,7 @@ var CLI_VERSION;
|
|
|
8810
8850
|
var init_version = __esm({
|
|
8811
8851
|
"src/version.ts"() {
|
|
8812
8852
|
"use strict";
|
|
8813
|
-
CLI_VERSION = true ? "1.36.
|
|
8853
|
+
CLI_VERSION = true ? "1.36.5" : "dev";
|
|
8814
8854
|
}
|
|
8815
8855
|
});
|
|
8816
8856
|
|