@getmonoceros/workbench 1.10.1 → 1.11.1

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 CHANGED
@@ -51,23 +51,112 @@ function shellQuote(arg) {
51
51
  return `'${arg.replace(/'/g, "'\\''")}'`;
52
52
  }
53
53
 
54
+ // src/devcontainer/wsl-backend-bootstrap.ts
55
+ import { spawnSync as spawnSync2 } from "child_process";
56
+
57
+ // src/util/format.ts
58
+ var ESC = "\x1B[";
59
+ var ANSI_BOLD = `${ESC}1m`;
60
+ var ANSI_UNDERLINE = `${ESC}4m`;
61
+ var ANSI_CYAN = `${ESC}36m`;
62
+ var ANSI_GREY = `${ESC}90m`;
63
+ var ANSI_RESET = `${ESC}0m`;
64
+ function makeWrap(isTty2) {
65
+ return (s, ...codes) => isTty2 ? codes.join("") + s + ANSI_RESET : s;
66
+ }
67
+ function makePalette(isTty2) {
68
+ const wrap = makeWrap(isTty2);
69
+ return {
70
+ bold: (s) => wrap(s, ANSI_BOLD),
71
+ underline: (s) => wrap(s, ANSI_UNDERLINE),
72
+ cyan: (s) => wrap(s, ANSI_CYAN),
73
+ dim: (s) => wrap(s, ANSI_GREY),
74
+ sectionLine: (label) => wrap(`\u25B8 ${label}`, ANSI_BOLD, ANSI_UNDERLINE)
75
+ };
76
+ }
77
+ function colorsFor(stream) {
78
+ return makePalette(stream.isTTY ?? false);
79
+ }
80
+ var stderrPalette = makePalette(process.stderr.isTTY ?? false);
81
+ var bold = stderrPalette.bold;
82
+ var underline = stderrPalette.underline;
83
+ var cyan = stderrPalette.cyan;
84
+ var dim = stderrPalette.dim;
85
+ var sectionLine = stderrPalette.sectionLine;
86
+
87
+ // src/devcontainer/wsl-backend-bootstrap.ts
88
+ function bootstrapWslBackend(opts = {}) {
89
+ const platform = opts.platform ?? process.platform;
90
+ if (platform !== "win32") return;
91
+ const listDistros = opts.wslDistros ?? defaultWslDistros;
92
+ const raw = listDistros();
93
+ if (raw !== null && hasWsl2Distro(raw)) return;
94
+ const probe = opts.probe ?? defaultProbe2;
95
+ if (probe("docker", ["--version"]) !== 0) return;
96
+ const warn = opts.warn ?? ((m) => process.stderr.write(`${m}
97
+ `));
98
+ warn(formatWslBackendHint());
99
+ }
100
+ function hasWsl2Distro(raw) {
101
+ const text = raw.split(String.fromCharCode(0)).join("");
102
+ for (const line of text.split(/\r?\n/)) {
103
+ const trimmed = line.trim();
104
+ if (!trimmed) continue;
105
+ if (/\bNAME\b/i.test(trimmed) && /\bVERSION\b/i.test(trimmed)) continue;
106
+ const tokens = trimmed.replace(/^\*\s*/, "").split(/\s+/);
107
+ if (tokens[tokens.length - 1] === "2") return true;
108
+ }
109
+ return false;
110
+ }
111
+ function formatWslBackendHint() {
112
+ return [
113
+ `Docker's daemon isn't reachable, and no WSL 2 distro is registered.`,
114
+ `Docker Desktop runs on the WSL 2 backend, so without a distro it`,
115
+ `can't start -- often shown as the misleading "Virtualization support`,
116
+ `not detected" (even with virtualization enabled in BIOS).`,
117
+ ``,
118
+ `Fix it in an elevated PowerShell:`,
119
+ ``,
120
+ cyan(` wsl --set-default-version 2`),
121
+ cyan(` wsl --update`),
122
+ cyan(` wsl --install -d Ubuntu`),
123
+ ``,
124
+ `Then reboot and start Docker Desktop. Full walkthrough:`,
125
+ `docs/install-windows.md in the workbench repo.`
126
+ ].join("\n");
127
+ }
128
+ function defaultProbe2(cmd, args) {
129
+ const result = spawnSync2(cmd, [...args], { stdio: "ignore", timeout: 1e4 });
130
+ return result.status ?? 1;
131
+ }
132
+ function defaultWslDistros() {
133
+ const result = spawnSync2("wsl", ["-l", "-v"], {
134
+ encoding: "utf8",
135
+ stdio: ["ignore", "pipe", "ignore"],
136
+ env: { ...process.env, WSL_UTF8: "1" },
137
+ timeout: 5e3
138
+ });
139
+ if (result.status !== 0 || typeof result.stdout !== "string") return null;
140
+ return result.stdout;
141
+ }
142
+
54
143
  // src/help.ts
55
- var ANSI_BOLD = "\x1B[1m";
56
- var ANSI_UNDERLINE = "\x1B[4m";
57
- var ANSI_CYAN = "\x1B[36m";
58
- var ANSI_GREY = "\x1B[90m";
59
- var ANSI_RESET = "\x1B[0m";
144
+ var ANSI_BOLD2 = "\x1B[1m";
145
+ var ANSI_UNDERLINE2 = "\x1B[4m";
146
+ var ANSI_CYAN2 = "\x1B[36m";
147
+ var ANSI_GREY2 = "\x1B[90m";
148
+ var ANSI_RESET2 = "\x1B[0m";
60
149
  function isTty() {
61
150
  return process.stdout.isTTY ?? false;
62
151
  }
63
152
  function color(text, ...codes) {
64
153
  if (!isTty()) return text;
65
- return codes.join("") + text + ANSI_RESET;
154
+ return codes.join("") + text + ANSI_RESET2;
66
155
  }
67
- var bold = (s) => color(s, ANSI_BOLD);
68
- var underline = (s) => color(s, ANSI_UNDERLINE);
69
- var cyan = (s) => color(s, ANSI_CYAN);
70
- var grey = (s) => color(s, ANSI_GREY);
156
+ var bold2 = (s) => color(s, ANSI_BOLD2);
157
+ var underline2 = (s) => color(s, ANSI_UNDERLINE2);
158
+ var cyan2 = (s) => color(s, ANSI_CYAN2);
159
+ var grey = (s) => color(s, ANSI_GREY2);
71
160
  var GROUPS = [
72
161
  { key: "lifecycle", label: "Container lifecycle" },
73
162
  { key: "run", label: "Run + inspect" },
@@ -160,7 +249,7 @@ function collectSubCommands(cmd) {
160
249
  function renderCommandsBlock(entries) {
161
250
  if (entries.length === 0) return [];
162
251
  const lines = [];
163
- lines.push(underline(bold("COMMANDS")));
252
+ lines.push(underline2(bold2("COMMANDS")));
164
253
  const byGroup = /* @__PURE__ */ new Map();
165
254
  for (const entry2 of entries) {
166
255
  const arr = byGroup.get(entry2.group) ?? [];
@@ -170,10 +259,10 @@ function renderCommandsBlock(entries) {
170
259
  const renderSection = (label, items) => {
171
260
  if (items.length === 0) return;
172
261
  lines.push("");
173
- lines.push(underline(grey(label)));
262
+ lines.push(underline2(grey(label)));
174
263
  lines.push("");
175
264
  const rows = items.map((e) => [
176
- cyan(e.name),
265
+ cyan2(e.name),
177
266
  e.description
178
267
  ]);
179
268
  lines.push(alignTable(rows, ""));
@@ -210,27 +299,27 @@ function renderUsageBlock(cmd, commandPath) {
210
299
  lines.push(grey(wrapText(header, terminalWidth(), "")));
211
300
  lines.push("");
212
301
  lines.push(
213
- `${underline(bold("USAGE"))} ${cyan([fullName, ...usageTokens].join(" "))}`
302
+ `${underline2(bold2("USAGE"))} ${cyan2([fullName, ...usageTokens].join(" "))}`
214
303
  );
215
304
  lines.push("");
216
305
  if (positionals.length > 0) {
217
- lines.push(underline(bold("ARGUMENTS")));
306
+ lines.push(underline2(bold2("ARGUMENTS")));
218
307
  lines.push("");
219
308
  const rows = positionals.map((p) => {
220
309
  const isRequired = p.required !== false && p.default === void 0;
221
- return [cyan(p.name.toUpperCase()), renderArgDescription(p, isRequired)];
310
+ return [cyan2(p.name.toUpperCase()), renderArgDescription(p, isRequired)];
222
311
  });
223
312
  lines.push(alignTable(rows, " "));
224
313
  lines.push("");
225
314
  }
226
315
  if (flags.length > 0) {
227
- lines.push(underline(bold("OPTIONS")));
316
+ lines.push(underline2(bold2("OPTIONS")));
228
317
  lines.push("");
229
318
  const rows = flags.map((f) => {
230
319
  const isRequired = f.required === true && f.default === void 0;
231
320
  const aliases = (Array.isArray(f.alias) ? f.alias : f.alias ? [f.alias] : []).map((a) => `-${a}`);
232
321
  const label = [...aliases, `--${f.name}`].join(", ") + renderValueHint(f);
233
- return [cyan(label), renderArgDescription(f, isRequired)];
322
+ return [cyan2(label), renderArgDescription(f, isRequired)];
234
323
  });
235
324
  lines.push(alignTable(rows, " "));
236
325
  lines.push("");
@@ -240,7 +329,7 @@ function renderUsageBlock(cmd, commandPath) {
240
329
  lines.push(line);
241
330
  }
242
331
  lines.push(
243
- `Use ${cyan(`${fullName} <command> --help`)} for more information about a command.`
332
+ `Use ${cyan2(`${fullName} <command> --help`)} for more information about a command.`
244
333
  );
245
334
  lines.push("");
246
335
  }
@@ -573,38 +662,6 @@ function prettyPath(p) {
573
662
  import { spawn } from "child_process";
574
663
  import { promises as fs2 } from "fs";
575
664
  import path2 from "path";
576
-
577
- // src/util/format.ts
578
- var ESC = "\x1B[";
579
- var ANSI_BOLD2 = `${ESC}1m`;
580
- var ANSI_UNDERLINE2 = `${ESC}4m`;
581
- var ANSI_CYAN2 = `${ESC}36m`;
582
- var ANSI_GREY2 = `${ESC}90m`;
583
- var ANSI_RESET2 = `${ESC}0m`;
584
- function makeWrap(isTty2) {
585
- return (s, ...codes) => isTty2 ? codes.join("") + s + ANSI_RESET2 : s;
586
- }
587
- function makePalette(isTty2) {
588
- const wrap = makeWrap(isTty2);
589
- return {
590
- bold: (s) => wrap(s, ANSI_BOLD2),
591
- underline: (s) => wrap(s, ANSI_UNDERLINE2),
592
- cyan: (s) => wrap(s, ANSI_CYAN2),
593
- dim: (s) => wrap(s, ANSI_GREY2),
594
- sectionLine: (label) => wrap(`\u25B8 ${label}`, ANSI_BOLD2, ANSI_UNDERLINE2)
595
- };
596
- }
597
- function colorsFor(stream) {
598
- return makePalette(stream.isTTY ?? false);
599
- }
600
- var stderrPalette = makePalette(process.stderr.isTTY ?? false);
601
- var bold2 = stderrPalette.bold;
602
- var underline2 = stderrPalette.underline;
603
- var cyan2 = stderrPalette.cyan;
604
- var dim = stderrPalette.dim;
605
- var sectionLine = stderrPalette.sectionLine;
606
-
607
- // src/devcontainer/credentials.ts
608
665
  var realGitCredentialFill = (input) => {
609
666
  return new Promise((resolve, reject) => {
610
667
  const child = spawn("git", ["credential", "fill"], {
@@ -649,11 +706,11 @@ function uniqueHttpsHosts(repos) {
649
706
  function installCommandForOS(opts) {
650
707
  switch (process.platform) {
651
708
  case "darwin":
652
- return cyan2(opts.brew);
709
+ return cyan(opts.brew);
653
710
  case "win32":
654
- return cyan2(opts.winget);
711
+ return cyan(opts.winget);
655
712
  default:
656
- if (opts.linuxBrew) return cyan2(opts.linuxBrew);
713
+ if (opts.linuxBrew) return cyan(opts.linuxBrew);
657
714
  return `See ${opts.linuxDocsUrl} for package instructions.`;
658
715
  }
659
716
  }
@@ -674,8 +731,8 @@ function providerSetupHint(host, provider) {
674
731
  install,
675
732
  "",
676
733
  "Then run once:",
677
- cyan2(`gh auth login${hostArg}`),
678
- cyan2(`gh auth setup-git${hostArg}`),
734
+ cyan(`gh auth login${hostArg}`),
735
+ cyan(`gh auth setup-git${hostArg}`),
679
736
  "",
680
737
  "`gh auth login` walks through OAuth in your browser.",
681
738
  "`gh auth setup-git` wires gh into git as a credential helper."
@@ -698,7 +755,7 @@ function providerSetupHint(host, provider) {
698
755
  install,
699
756
  "",
700
757
  "Then run once:",
701
- cyan2(`glab auth login${hostArg}`),
758
+ cyan(`glab auth login${hostArg}`),
702
759
  "",
703
760
  "Choose `HTTPS` when asked for git-protocol, then accept",
704
761
  '"Authenticate Git with your GitLab credentials" \u2014 glab',
@@ -717,7 +774,7 @@ function providerSetupHint(host, provider) {
717
774
  "https://id.atlassian.com/manage-profile/security/api-tokens",
718
775
  "",
719
776
  "Then store it via your OS credential helper:",
720
- cyan2(
777
+ cyan(
721
778
  `git credential approve <<< $'protocol=https\\nhost=${host}\\nusername=<your-atlassian-email>\\npassword=<token>\\n'`
722
779
  )
723
780
  ].join("\n")
@@ -733,7 +790,7 @@ function providerSetupHint(host, provider) {
733
790
  "at least repo-read + repo-write scopes for the repos you need.",
734
791
  "",
735
792
  "Then store it via your OS credential helper:",
736
- cyan2(
793
+ cyan(
737
794
  `git credential approve <<< $'protocol=https\\nhost=${host}\\nusername=<your-bitbucket-username>\\npassword=<token>\\n'`
738
795
  )
739
796
  ].join("\n")
@@ -751,7 +808,7 @@ function providerSetupHint(host, provider) {
751
808
  "need push from the container).",
752
809
  "",
753
810
  "Then store it via your OS credential helper:",
754
- cyan2(
811
+ cyan(
755
812
  `git credential approve <<< $'protocol=https\\nhost=${host}\\nusername=<your-gitea-username>\\npassword=<token>\\n'`
756
813
  )
757
814
  ].join("\n")
@@ -853,7 +910,7 @@ function formatMissingCredentialsError(missing) {
853
910
  "",
854
911
  hint.body,
855
912
  "",
856
- `Then re-run ${cyan2("monoceros apply")}.`
913
+ `Then re-run ${cyan("monoceros apply")}.`
857
914
  ].join("\n");
858
915
  }
859
916
  const lines = [
@@ -867,7 +924,7 @@ function formatMissingCredentialsError(missing) {
867
924
  lines.push(hint.body);
868
925
  lines.push("");
869
926
  }
870
- lines.push(`Then re-run ${cyan2("monoceros apply")}.`);
927
+ lines.push(`Then re-run ${cyan("monoceros apply")}.`);
871
928
  return lines.join("\n");
872
929
  }
873
930
  function formatUnknownProviderError(hosts) {
@@ -879,11 +936,11 @@ function formatUnknownProviderError(hosts) {
879
936
  "For any other host (self-hosted GitLab, Gitea, Bitbucket Server, \u2026)",
880
937
  "declare the provider explicitly in the yml. Edit the repo entry:",
881
938
  "",
882
- cyan2(" repos:"),
883
- cyan2(` - url: https://${sorted[0]}/\u2026`),
884
- cyan2(" provider: gitlab # or: github, bitbucket, gitea"),
939
+ cyan(" repos:"),
940
+ cyan(` - url: https://${sorted[0]}/\u2026`),
941
+ cyan(" provider: gitlab # or: github, bitbucket, gitea"),
885
942
  "",
886
- `Or re-add with ${cyan2("monoceros add-repo <name> <url> --provider=<github|gitlab|bitbucket|gitea>")}.`
943
+ `Or re-add with ${cyan("monoceros add-repo <name> <url> --provider=<github|gitlab|bitbucket|gitea>")}.`
887
944
  ];
888
945
  return lines.join("\n");
889
946
  }
@@ -3787,6 +3844,46 @@ import { spawn as spawn4 } from "child_process";
3787
3844
  import { readFileSync as readFileSync3 } from "fs";
3788
3845
  import { createRequire } from "module";
3789
3846
  import path10 from "path";
3847
+
3848
+ // src/devcontainer/runtime-pull-hint.ts
3849
+ import { Transform as Transform2 } from "stream";
3850
+ var RUNTIME_PULL_MARKER = "No manifest found for ghcr.io/getmonoceros/monoceros-runtime";
3851
+ var RUNTIME_PULL_HINT = 'Downloading the Monoceros runtime image now -- expected on first apply, takes ~1-2 min (Docker pulls the multi-arch base with no progress output). The "No manifest found" line above is harmless. Please wait...';
3852
+ function createRuntimePullHintStream(state) {
3853
+ let buffer = "";
3854
+ const appendHintIfMarker = (block) => {
3855
+ if (state.hinted || !block.includes(RUNTIME_PULL_MARKER)) return block;
3856
+ state.hinted = true;
3857
+ return `${block}${dim(`(i) ${RUNTIME_PULL_HINT}`)}
3858
+ `;
3859
+ };
3860
+ return new Transform2({
3861
+ decodeStrings: true,
3862
+ transform(chunk, _enc, cb) {
3863
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
3864
+ buffer += text;
3865
+ const lastNewline = buffer.lastIndexOf("\n");
3866
+ if (lastNewline === -1) {
3867
+ cb(null);
3868
+ return;
3869
+ }
3870
+ const flushable = buffer.slice(0, lastNewline + 1);
3871
+ buffer = buffer.slice(lastNewline + 1);
3872
+ cb(null, appendHintIfMarker(flushable));
3873
+ },
3874
+ flush(cb) {
3875
+ if (buffer.length === 0) {
3876
+ cb(null);
3877
+ return;
3878
+ }
3879
+ const tail = buffer;
3880
+ buffer = "";
3881
+ cb(null, appendHintIfMarker(tail));
3882
+ }
3883
+ });
3884
+ }
3885
+
3886
+ // src/devcontainer/cli.ts
3790
3887
  var require_ = createRequire(import.meta.url);
3791
3888
  var cachedBinaryPath = null;
3792
3889
  function devcontainerCliPath() {
@@ -3836,8 +3933,9 @@ var spawnDevcontainer = (args, cwd, options = {}) => {
3836
3933
  });
3837
3934
  return;
3838
3935
  }
3839
- child.stdout?.pipe(createSecretMaskStream()).pipe(process.stdout);
3840
- child.stderr?.pipe(createSecretMaskStream()).pipe(process.stderr);
3936
+ const pullHint = { hinted: false };
3937
+ child.stdout?.pipe(createSecretMaskStream()).pipe(createRuntimePullHintStream(pullHint)).pipe(process.stdout);
3938
+ child.stderr?.pipe(createSecretMaskStream()).pipe(createRuntimePullHintStream(pullHint)).pipe(process.stderr);
3841
3939
  child.on("error", reject);
3842
3940
  child.on("exit", (code) => resolve(code ?? 0));
3843
3941
  });
@@ -4068,7 +4166,7 @@ function formatUnreachableReposError(failures) {
4068
4166
  }
4069
4167
  lines.push("");
4070
4168
  }
4071
- lines.push(`Then re-run ${cyan2("monoceros apply")}.`);
4169
+ lines.push(`Then re-run ${cyan("monoceros apply")}.`);
4072
4170
  return lines.join("\n");
4073
4171
  }
4074
4172
  function headerForKind(kind) {
@@ -4151,14 +4249,14 @@ function formatRootlessNotSupportedError() {
4151
4249
  ``,
4152
4250
  `To fix, switch back to standard rootful Docker:`,
4153
4251
  ``,
4154
- cyan2(
4252
+ cyan(
4155
4253
  ` systemctl --user stop docker.service docker.socket 2>/dev/null || true`
4156
4254
  ),
4157
- cyan2(` dockerd-rootless-setuptool.sh uninstall`),
4158
- cyan2(` rootlesskit rm -rf ~/.local/share/docker`),
4159
- cyan2(` unset DOCKER_HOST DOCKER_CONTEXT`),
4160
- cyan2(` sudo systemctl enable --now docker`),
4161
- cyan2(` sudo usermod -aG docker $USER`),
4255
+ cyan(` dockerd-rootless-setuptool.sh uninstall`),
4256
+ cyan(` rootlesskit rm -rf ~/.local/share/docker`),
4257
+ cyan(` unset DOCKER_HOST DOCKER_CONTEXT`),
4258
+ cyan(` sudo systemctl enable --now docker`),
4259
+ cyan(` sudo usermod -aG docker $USER`),
4162
4260
  ``,
4163
4261
  `If you added DOCKER_HOST or DOCKER_CONTEXT to ~/.bashrc /`,
4164
4262
  `~/.profile (the rootless setup may have suggested it), remove`,
@@ -4463,7 +4561,7 @@ ${sectionLine(label)}
4463
4561
  section("Container");
4464
4562
  const featureRefs = parsed.config.features.map((f) => f.ref);
4465
4563
  if (featureRefs.length > 0) {
4466
- logger.info(`Features: ${featureRefs.map((r) => cyan2(r)).join(", ")}`);
4564
+ logger.info(`Features: ${featureRefs.map((r) => cyan(r)).join(", ")}`);
4467
4565
  }
4468
4566
  logger.info(
4469
4567
  dim(
@@ -4502,7 +4600,7 @@ ${sectionLine(label)}
4502
4600
  });
4503
4601
  if (exitCode === 0) {
4504
4602
  section("Next steps");
4505
- logger.info(` ${cyan2(`monoceros shell ${opts.name}`)}`);
4603
+ logger.info(` ${cyan(`monoceros shell ${opts.name}`)}`);
4506
4604
  }
4507
4605
  return { targetDir, configPath: ymlPath, containerExitCode: exitCode };
4508
4606
  }
@@ -4600,7 +4698,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
4600
4698
  }
4601
4699
 
4602
4700
  // src/version.ts
4603
- var CLI_VERSION = true ? "1.10.1" : "dev";
4701
+ var CLI_VERSION = true ? "1.11.1" : "dev";
4604
4702
 
4605
4703
  // src/commands/_dispatch.ts
4606
4704
  import { consola as consola12 } from "consola";
@@ -7196,6 +7294,7 @@ var main = defineCommand30({
7196
7294
 
7197
7295
  // src/bin.ts
7198
7296
  bootstrapDockerGroup();
7297
+ bootstrapWslBackend();
7199
7298
  consumeInnerArgsFromProcessArgv();
7200
7299
  async function entry() {
7201
7300
  if (await maybeRenderHelp(process.argv.slice(2), main)) {