@getmonoceros/workbench 1.19.1 → 1.19.2

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
@@ -3259,8 +3259,8 @@ function removeRepoFromDoc(doc, urlOrPath) {
3259
3259
  if (!isMap2(item)) return false;
3260
3260
  const url = item.get("url");
3261
3261
  if (url === urlOrPath) return true;
3262
- const path24 = item.get("path");
3263
- const effectivePath = typeof path24 === "string" ? path24 : typeof url === "string" ? deriveRepoName(url) : void 0;
3262
+ const path25 = item.get("path");
3263
+ const effectivePath = typeof path25 === "string" ? path25 : typeof url === "string" ? deriveRepoName(url) : void 0;
3264
3264
  return effectivePath === urlOrPath;
3265
3265
  });
3266
3266
  if (idx < 0) return false;
@@ -3372,7 +3372,7 @@ async function runAddRepo(input) {
3372
3372
  "Missing repo URL. Usage: monoceros add-repo <containername> <url>."
3373
3373
  );
3374
3374
  }
3375
- const path24 = (input.path ?? deriveRepoName(url)).trim();
3375
+ const path25 = (input.path ?? deriveRepoName(url)).trim();
3376
3376
  const hasName = typeof input.gitName === "string" && input.gitName.trim().length > 0;
3377
3377
  const hasEmail = typeof input.gitEmail === "string" && input.gitEmail.trim().length > 0;
3378
3378
  if (hasName !== hasEmail) {
@@ -3409,7 +3409,7 @@ async function runAddRepo(input) {
3409
3409
  const providerToWrite = !canonical && explicitProvider ? explicitProvider : void 0;
3410
3410
  const entry2 = {
3411
3411
  url,
3412
- path: path24,
3412
+ path: path25,
3413
3413
  ...hasName && hasEmail ? {
3414
3414
  gitUser: {
3415
3415
  name: input.gitName.trim(),
@@ -6407,7 +6407,7 @@ var CLI_VERSION;
6407
6407
  var init_version = __esm({
6408
6408
  "src/version.ts"() {
6409
6409
  "use strict";
6410
- CLI_VERSION = true ? "1.19.1" : "dev";
6410
+ CLI_VERSION = true ? "1.19.2" : "dev";
6411
6411
  }
6412
6412
  });
6413
6413
 
@@ -9004,7 +9004,118 @@ var init_remove_service = __esm({
9004
9004
  }
9005
9005
  });
9006
9006
 
9007
+ // src/devcontainer/browser-bridge.ts
9008
+ import { spawn as spawn10 } from "child_process";
9009
+ import { existsSync as existsSync15, promises as fsp3, readFileSync as readFileSync7 } from "fs";
9010
+ import http2 from "http";
9011
+ import path23 from "path";
9012
+ function openInBrowser2(url) {
9013
+ const platform = process.platform;
9014
+ const [cmd, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
9015
+ try {
9016
+ const child = spawn10(cmd, args, {
9017
+ stdio: "ignore",
9018
+ detached: true
9019
+ });
9020
+ child.on("error", () => {
9021
+ });
9022
+ child.unref();
9023
+ } catch {
9024
+ }
9025
+ }
9026
+ async function startBrowserBridge(opts) {
9027
+ const relayDir = path23.join(opts.root, RELAY_DIRNAME2);
9028
+ const relayScript = path23.join(relayDir, "xdg-open");
9029
+ const urlFile = path23.join(relayDir, "url");
9030
+ await fsp3.mkdir(relayDir, { recursive: true });
9031
+ await fsp3.rm(urlFile, { force: true });
9032
+ await fsp3.writeFile(
9033
+ relayScript,
9034
+ `#!/bin/sh
9035
+ printf '%s\\n' "$1" > "$(dirname "$0")/url"
9036
+ exit 0
9037
+ `,
9038
+ { mode: 493 }
9039
+ );
9040
+ await fsp3.chmod(relayScript, 493);
9041
+ const servers = [];
9042
+ let handled = false;
9043
+ const onUrl = (url) => {
9044
+ openInBrowser2(url);
9045
+ const target = parseCallbackTarget(url);
9046
+ if (!target) return;
9047
+ const server = http2.createServer((req, res) => {
9048
+ const reqUrl = req.url ?? target.pathname;
9049
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
9050
+ res.end(
9051
+ '<html><body style="font-family:sans-serif;padding:3rem">You are signed in. You can close this tab and return to the terminal.</body></html>'
9052
+ );
9053
+ void opts.spawn(
9054
+ [
9055
+ "exec",
9056
+ "--workspace-folder",
9057
+ opts.root,
9058
+ "--mount-workspace-git-root=false",
9059
+ "curl",
9060
+ "-fsS",
9061
+ `http://localhost:${target.port}${reqUrl}`
9062
+ ],
9063
+ opts.root,
9064
+ { quiet: true }
9065
+ );
9066
+ });
9067
+ server.on("error", () => {
9068
+ });
9069
+ server.listen(target.port, "127.0.0.1");
9070
+ servers.push(server);
9071
+ };
9072
+ const poll = setInterval(() => {
9073
+ if (handled || !existsSync15(urlFile)) return;
9074
+ let content = "";
9075
+ try {
9076
+ content = readFileSync7(urlFile, "utf8");
9077
+ } catch {
9078
+ return;
9079
+ }
9080
+ if (!content.trim()) return;
9081
+ handled = true;
9082
+ onUrl(content.trim());
9083
+ }, 250);
9084
+ return {
9085
+ relayDirInContainer: `/workspaces/${opts.name}/${RELAY_DIRNAME2}`,
9086
+ async dispose() {
9087
+ clearInterval(poll);
9088
+ for (const s of servers) s.close();
9089
+ await fsp3.rm(relayDir, { recursive: true, force: true });
9090
+ }
9091
+ };
9092
+ }
9093
+ var RELAY_DIRNAME2;
9094
+ var init_browser_bridge = __esm({
9095
+ "src/devcontainer/browser-bridge.ts"() {
9096
+ "use strict";
9097
+ init_services();
9098
+ RELAY_DIRNAME2 = ".monoceros-bridge";
9099
+ }
9100
+ });
9101
+
9007
9102
  // src/devcontainer/run.ts
9103
+ function wrapExec(command, opts) {
9104
+ const leading = [];
9105
+ const stmts = [];
9106
+ if (opts.pathPrepend) {
9107
+ leading.push(opts.pathPrepend);
9108
+ stmts.push(`export PATH="$${leading.length}:$PATH"`);
9109
+ }
9110
+ if (opts.cwd) {
9111
+ leading.push(opts.cwd);
9112
+ stmts.push(`cd -- "$${leading.length}"`);
9113
+ }
9114
+ if (leading.length === 0) return command;
9115
+ const shift = leading.length === 1 ? "shift" : `shift ${leading.length}`;
9116
+ const script = `${stmts.join(" && ")} && ${shift} && exec "$@"`;
9117
+ return ["bash", "-lc", script, "bash", ...leading, ...command];
9118
+ }
9008
9119
  async function runInContainer(opts) {
9009
9120
  if (opts.command.length === 0) {
9010
9121
  throw new Error(
@@ -9019,29 +9130,35 @@ async function runInContainer(opts) {
9019
9130
  { quiet: true }
9020
9131
  );
9021
9132
  if (upCode !== 0) return upCode;
9022
- const innerExec = opts.cwd ? [
9023
- "bash",
9024
- "-lc",
9025
- 'cd -- "$1" && shift && exec "$@"',
9026
- "bash",
9027
- opts.cwd,
9028
- ...opts.command
9029
- ] : opts.command;
9030
- return spawnFn(
9031
- [
9032
- "exec",
9033
- "--workspace-folder",
9133
+ const bridge = opts.name && process.stdout.isTTY ? await startBrowserBridge({
9134
+ name: opts.name,
9135
+ root: opts.root,
9136
+ spawn: spawnFn
9137
+ }) : null;
9138
+ try {
9139
+ const innerExec = wrapExec(opts.command, {
9140
+ ...bridge ? { pathPrepend: bridge.relayDirInContainer } : {},
9141
+ ...opts.cwd ? { cwd: opts.cwd } : {}
9142
+ });
9143
+ return await spawnFn(
9144
+ [
9145
+ "exec",
9146
+ "--workspace-folder",
9147
+ opts.root,
9148
+ "--mount-workspace-git-root=false",
9149
+ ...innerExec
9150
+ ],
9034
9151
  opts.root,
9035
- "--mount-workspace-git-root=false",
9036
- ...innerExec
9037
- ],
9038
- opts.root,
9039
- { interactive: true }
9040
- );
9152
+ { interactive: true }
9153
+ );
9154
+ } finally {
9155
+ if (bridge) await bridge.dispose();
9156
+ }
9041
9157
  }
9042
9158
  var init_run = __esm({
9043
9159
  "src/devcontainer/run.ts"() {
9044
9160
  "use strict";
9161
+ init_browser_bridge();
9045
9162
  init_cli();
9046
9163
  init_shell();
9047
9164
  }
@@ -9085,6 +9202,7 @@ var init_run2 = __esm({
9085
9202
  try {
9086
9203
  const exitCode = await runInContainer({
9087
9204
  root: containerDir(args.name),
9205
+ name: args.name,
9088
9206
  command,
9089
9207
  ...args.in ? { cwd: args.in } : {}
9090
9208
  });
@@ -9276,11 +9394,11 @@ var init_stop = __esm({
9276
9394
  });
9277
9395
 
9278
9396
  // src/tunnel/resolve.ts
9279
- import { existsSync as existsSync15 } from "fs";
9280
- import path23 from "path";
9397
+ import { existsSync as existsSync16 } from "fs";
9398
+ import path24 from "path";
9281
9399
  async function resolveTunnelTarget(opts) {
9282
9400
  const ymlPath = containerConfigPath(opts.name, opts.monocerosHome);
9283
- if (!existsSync15(ymlPath)) {
9401
+ if (!existsSync16(ymlPath)) {
9284
9402
  throw new Error(
9285
9403
  `No yml profile for '${opts.name}' at ${ymlPath}. Run \`monoceros init ${opts.name}\` first.`
9286
9404
  );
@@ -9288,13 +9406,13 @@ async function resolveTunnelTarget(opts) {
9288
9406
  const parsed = await readConfig(ymlPath);
9289
9407
  const config = parsed.config;
9290
9408
  const containerRoot = containerDir(opts.name, opts.monocerosHome);
9291
- if (!existsSync15(containerRoot)) {
9409
+ if (!existsSync16(containerRoot)) {
9292
9410
  throw new Error(
9293
9411
  `Container '${opts.name}' is not materialised at ${containerRoot}. Run \`monoceros apply ${opts.name}\` first.`
9294
9412
  );
9295
9413
  }
9296
- const composePath = path23.join(containerRoot, ".devcontainer", "compose.yaml");
9297
- const isCompose = existsSync15(composePath);
9414
+ const composePath = path24.join(containerRoot, ".devcontainer", "compose.yaml");
9415
+ const isCompose = existsSync16(composePath);
9298
9416
  const parsedTarget = parseTargetArg(opts.target, config);
9299
9417
  const docker = opts.docker ?? defaultDockerExec;
9300
9418
  if (isCompose) {
@@ -9529,7 +9647,7 @@ var init_port_check2 = __esm({
9529
9647
  });
9530
9648
 
9531
9649
  // src/tunnel/run.ts
9532
- import { spawn as spawn10 } from "child_process";
9650
+ import { spawn as spawn11 } from "child_process";
9533
9651
  import { consola as consola34 } from "consola";
9534
9652
  function signalNumber(signal) {
9535
9653
  switch (signal) {
@@ -9619,7 +9737,7 @@ var init_run3 = __esm({
9619
9737
  init_port_check2();
9620
9738
  SOCAT_IMAGE = "alpine/socat:1.8.0.3";
9621
9739
  defaultDockerSpawn = (args) => {
9622
- const child = spawn10("docker", args, {
9740
+ const child = spawn11("docker", args, {
9623
9741
  stdio: "inherit"
9624
9742
  });
9625
9743
  const exited = new Promise((resolve, reject) => {
@@ -9713,7 +9831,7 @@ var init_tunnel = __esm({
9713
9831
  });
9714
9832
 
9715
9833
  // src/upgrade/index.ts
9716
- import { existsSync as existsSync16, promises as fs17 } from "fs";
9834
+ import { existsSync as existsSync17, promises as fs17 } from "fs";
9717
9835
  import { consola as consola36 } from "consola";
9718
9836
  async function fetchRuntimeVersions() {
9719
9837
  const tokenUrl = `https://ghcr.io/token?service=ghcr.io&scope=repository:${RUNTIME_REPO}:pull`;
@@ -9772,7 +9890,7 @@ async function runUpgrade(opts) {
9772
9890
  );
9773
9891
  }
9774
9892
  const ymlPath = containerConfigPath(opts.name, home);
9775
- if (!existsSync16(ymlPath)) {
9893
+ if (!existsSync17(ymlPath)) {
9776
9894
  throw new Error(
9777
9895
  `No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
9778
9896
  );
@@ -10204,25 +10322,25 @@ function detectHelpRequest(argv, main2) {
10204
10322
  const separatorIdx = argv.indexOf("--");
10205
10323
  if (helpIdx === -1) return null;
10206
10324
  if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
10207
- const path24 = [];
10325
+ const path25 = [];
10208
10326
  const tokens = argv.slice(
10209
10327
  0,
10210
10328
  separatorIdx === -1 ? argv.length : separatorIdx
10211
10329
  );
10212
10330
  let cursor = main2;
10213
10331
  const mainName = (main2.meta ?? {}).name ?? "monoceros";
10214
- path24.push(mainName);
10332
+ path25.push(mainName);
10215
10333
  for (const tok of tokens) {
10216
10334
  if (tok.startsWith("-")) continue;
10217
10335
  const subs = cursor.subCommands ?? {};
10218
10336
  if (tok in subs) {
10219
10337
  cursor = subs[tok];
10220
- path24.push(tok);
10338
+ path25.push(tok);
10221
10339
  continue;
10222
10340
  }
10223
10341
  break;
10224
10342
  }
10225
- return { path: path24, cmd: cursor };
10343
+ return { path: path25, cmd: cursor };
10226
10344
  }
10227
10345
  async function maybeRenderHelp(argv, main2) {
10228
10346
  const hit = detectHelpRequest(argv, main2);