@getmonoceros/workbench 1.38.18 → 1.38.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/bin.js +83 -13
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,12 +18,12 @@ this up front and print platform-specific guidance.
|
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
20
|
# macOS / Linux
|
|
21
|
-
curl -fsSL https://raw.githubusercontent.com/getmonoceros/workbench/main/install.sh | bash
|
|
21
|
+
curl -fsSL https://raw.githubusercontent.com/getmonoceros/workbench/main/installer/install.sh | bash
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
```powershell
|
|
25
25
|
# Windows (PowerShell)
|
|
26
|
-
|
|
26
|
+
irm https://raw.githubusercontent.com/getmonoceros/workbench/main/installer/install.ps1 | iex
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
The script checks Docker + Node, installs the package globally via
|
package/dist/bin.js
CHANGED
|
@@ -479,6 +479,18 @@ function expandEnvRefs(env) {
|
|
|
479
479
|
}
|
|
480
480
|
return out;
|
|
481
481
|
}
|
|
482
|
+
function mergeEnvLayers(...layers) {
|
|
483
|
+
const out = {};
|
|
484
|
+
for (const layer of layers) {
|
|
485
|
+
for (const [key, value] of Object.entries(layer)) {
|
|
486
|
+
const incomingEmpty = value.trim() === "";
|
|
487
|
+
const haveNonEmpty = out[key] !== void 0 && out[key].trim() !== "";
|
|
488
|
+
if (incomingEmpty && haveNonEmpty) continue;
|
|
489
|
+
out[key] = value;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return out;
|
|
493
|
+
}
|
|
482
494
|
function interpolateServices(services, vars) {
|
|
483
495
|
const missing = [];
|
|
484
496
|
const resolved = services.map((svc) => {
|
|
@@ -1439,10 +1451,10 @@ function resolveRepoTokens(config, catalog, envVars) {
|
|
|
1439
1451
|
async function resolveContainerRepoTokens(name, home, catalog) {
|
|
1440
1452
|
const ymlPath = containerConfigPath(name, home);
|
|
1441
1453
|
const { config } = parseConfig(await fs4.readFile(ymlPath, "utf8"), ymlPath);
|
|
1442
|
-
const envVars =
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1454
|
+
const envVars = mergeEnvLayers(
|
|
1455
|
+
readEnvFile(globalEnvPath(home)),
|
|
1456
|
+
readEnvFile(containerEnvPath(name, home))
|
|
1457
|
+
);
|
|
1446
1458
|
return resolveRepoTokens(config, catalog, envVars);
|
|
1447
1459
|
}
|
|
1448
1460
|
function formatTokenUse(use) {
|
|
@@ -4452,6 +4464,9 @@ function generateAgentsMd(input) {
|
|
|
4452
4464
|
lines.push(sub ? ` ${sub}` : "");
|
|
4453
4465
|
}
|
|
4454
4466
|
}
|
|
4467
|
+
for (const mountLine of formatServiceMounts(svc)) {
|
|
4468
|
+
lines.push(mountLine);
|
|
4469
|
+
}
|
|
4455
4470
|
}
|
|
4456
4471
|
lines.push("");
|
|
4457
4472
|
const connEnv = serviceConnectionEnv(input.services);
|
|
@@ -4630,6 +4645,7 @@ function generateAgentsMd(input) {
|
|
|
4630
4645
|
);
|
|
4631
4646
|
lines.push("");
|
|
4632
4647
|
const examplePort = input.ports.length > 0 ? String(input.ports[0]) : "<port>";
|
|
4648
|
+
const secondPort = input.ports.length > 1 ? String(input.ports[1]) : "<port>";
|
|
4633
4649
|
lines.push("## Running a long-running server");
|
|
4634
4650
|
lines.push("");
|
|
4635
4651
|
lines.push(
|
|
@@ -4664,7 +4680,10 @@ function generateAgentsMd(input) {
|
|
|
4664
4680
|
lines.push("{");
|
|
4665
4681
|
lines.push(' "targets": [');
|
|
4666
4682
|
lines.push(
|
|
4667
|
-
` { "name": "
|
|
4683
|
+
` { "name": "api", "command": "<the API's start command>", "port": ${examplePort}, "default": true },`
|
|
4684
|
+
);
|
|
4685
|
+
lines.push(
|
|
4686
|
+
` { "name": "web", "command": "<the web start command>", "port": ${secondPort}, "default": true }`
|
|
4668
4687
|
);
|
|
4669
4688
|
lines.push(" ]");
|
|
4670
4689
|
lines.push("}");
|
|
@@ -4703,6 +4722,17 @@ function generateAgentsMd(input) {
|
|
|
4703
4722
|
"not started. Pass `--target <name>` to start or stop a single one."
|
|
4704
4723
|
);
|
|
4705
4724
|
lines.push("");
|
|
4725
|
+
lines.push(
|
|
4726
|
+
"When you add a server in a later session, revisit the existing",
|
|
4727
|
+
"`launch.json` instead of assuming its current `default` set is complete.",
|
|
4728
|
+
"If the new server belongs to the app that should come up together (a",
|
|
4729
|
+
"backend the frontend calls, a worker the app relies on), give it",
|
|
4730
|
+
'`"default": true` too and place its entry before whatever depends on it.',
|
|
4731
|
+
"A single pre-existing default entry does not mean later servers should",
|
|
4732
|
+
"stay non-default - most servers that make up the running app belong in",
|
|
4733
|
+
"the default set."
|
|
4734
|
+
);
|
|
4735
|
+
lines.push("");
|
|
4706
4736
|
lines.push(
|
|
4707
4737
|
"The server must listen on `0.0.0.0` (not `127.0.0.1`) on the exposed",
|
|
4708
4738
|
"port, or Traefik cannot reach it. You only have the ports already",
|
|
@@ -4740,6 +4770,42 @@ function formatServiceLine(svc) {
|
|
|
4740
4770
|
}
|
|
4741
4771
|
return `- **${svc.name}** (custom image \`${svc.image}\`) \u2014 reachable at \`${reach}\``;
|
|
4742
4772
|
}
|
|
4773
|
+
function formatServiceMounts(svc) {
|
|
4774
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
4775
|
+
for (const spec of svc.volumes) {
|
|
4776
|
+
const mount = parseBindMount(spec);
|
|
4777
|
+
if (!mount) continue;
|
|
4778
|
+
const project = projectOf(mount.source);
|
|
4779
|
+
if (!project) continue;
|
|
4780
|
+
const readOnly = mount.mode === "ro" ? " (read-only)" : "";
|
|
4781
|
+
const bucket = byProject.get(project) ?? [];
|
|
4782
|
+
bucket.push(` - \`${mount.source}\` \u2192 \`${mount.target}\`${readOnly}`);
|
|
4783
|
+
byProject.set(project, bucket);
|
|
4784
|
+
}
|
|
4785
|
+
if (byProject.size === 0) return [];
|
|
4786
|
+
const out = [" Workspace mounts (edit these on the host, then re-apply):"];
|
|
4787
|
+
for (const [project, entries] of byProject) {
|
|
4788
|
+
out.push(` - ${project}:`);
|
|
4789
|
+
out.push(...entries);
|
|
4790
|
+
}
|
|
4791
|
+
return out;
|
|
4792
|
+
}
|
|
4793
|
+
function parseBindMount(spec) {
|
|
4794
|
+
const parts = spec.split(":");
|
|
4795
|
+
if (parts.length < 2) return null;
|
|
4796
|
+
const source = parts[0];
|
|
4797
|
+
let mode;
|
|
4798
|
+
const last = parts[parts.length - 1];
|
|
4799
|
+
if (parts.length >= 3 && !last.includes("/")) {
|
|
4800
|
+
mode = parts.pop();
|
|
4801
|
+
}
|
|
4802
|
+
return { source, target: parts.slice(1).join(":"), mode };
|
|
4803
|
+
}
|
|
4804
|
+
function projectOf(source) {
|
|
4805
|
+
const segments = source.split("/");
|
|
4806
|
+
if (segments[0] !== "projects" || segments.length < 2) return null;
|
|
4807
|
+
return segments[1] ?? null;
|
|
4808
|
+
}
|
|
4743
4809
|
function agentsMdInputFromCreateOptions(opts, featureDisplayMap2, manifestLoader, hostPort = 80) {
|
|
4744
4810
|
const features = [];
|
|
4745
4811
|
for (const [ref, userOptions] of Object.entries(opts.features ?? {})) {
|
|
@@ -8355,11 +8421,13 @@ ${sectionLine(label)}
|
|
|
8355
8421
|
warnOnDeprecatedFeatureRefs(parsed.config.features, globalConfig, logger);
|
|
8356
8422
|
const envPath = containerEnvPath(opts.name, home);
|
|
8357
8423
|
await ensureEnvGitignored(containerConfigsDir(home));
|
|
8358
|
-
const envVars = expandEnvRefs(
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8424
|
+
const envVars = expandEnvRefs(
|
|
8425
|
+
mergeEnvLayers(
|
|
8426
|
+
readEnvFile(globalEnvPath(home)),
|
|
8427
|
+
readEnvFile(envPath),
|
|
8428
|
+
opts.env ?? {}
|
|
8429
|
+
)
|
|
8430
|
+
);
|
|
8363
8431
|
const catalog = await loadComponentCatalog();
|
|
8364
8432
|
const repoTokens = resolveRepoTokens(parsed.config, catalog, envVars);
|
|
8365
8433
|
const featureTokenPrompt = opts.featureTokenPrompt ?? defaultFeatureTokenPrompt;
|
|
@@ -9070,7 +9138,7 @@ var CLI_VERSION;
|
|
|
9070
9138
|
var init_version = __esm({
|
|
9071
9139
|
"src/version.ts"() {
|
|
9072
9140
|
"use strict";
|
|
9073
|
-
CLI_VERSION = true ? "1.38.
|
|
9141
|
+
CLI_VERSION = true ? "1.38.20" : "dev";
|
|
9074
9142
|
}
|
|
9075
9143
|
});
|
|
9076
9144
|
|
|
@@ -12815,7 +12883,7 @@ async function runShare(opts) {
|
|
|
12815
12883
|
if (addresses.length === 0) addresses.push("<host-ip>");
|
|
12816
12884
|
const caPath = await caTrustDisplayPath(tls.caCertPath, home);
|
|
12817
12885
|
const banner = [
|
|
12818
|
-
`Sharing ${opts.name}/${opts.app} on the local network
|
|
12886
|
+
`Sharing ${opts.name}/${opts.app} on the local network:`
|
|
12819
12887
|
];
|
|
12820
12888
|
for (const t of ported) {
|
|
12821
12889
|
banner.push("", ` ${cyan2(t.name)}`);
|
|
@@ -12826,7 +12894,9 @@ async function runShare(opts) {
|
|
|
12826
12894
|
banner.push(
|
|
12827
12895
|
"",
|
|
12828
12896
|
dim(" Trust the local CA once (first device) for warning-free HTTPS:"),
|
|
12829
|
-
dim(` ${caPath}`)
|
|
12897
|
+
dim(` ${caPath}`),
|
|
12898
|
+
"",
|
|
12899
|
+
"Press Ctrl+C to stop sharing."
|
|
12830
12900
|
);
|
|
12831
12901
|
log.info(banner.join("\n"));
|
|
12832
12902
|
const dockerSpawn = opts.dockerSpawn ?? defaultDockerSpawn;
|