@getmonoceros/workbench 1.18.0 → 1.19.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
@@ -1903,6 +1903,41 @@ function curatedServiceEnvDefaults(name) {
1903
1903
  const def = SERVICE_CATALOG[name];
1904
1904
  return def?.env ? { ...def.env } : {};
1905
1905
  }
1906
+ function serviceConnectionEnv(services) {
1907
+ const env = {};
1908
+ for (const svc of services) {
1909
+ if (!isCuratedService(svc.name)) continue;
1910
+ const host = svc.name;
1911
+ if (svc.name === "postgres") {
1912
+ const user = svc.env.POSTGRES_USER ?? "postgres";
1913
+ const pass = svc.env.POSTGRES_PASSWORD ?? "";
1914
+ const db = svc.env.POSTGRES_DB ?? user;
1915
+ const port = svc.port ?? 5432;
1916
+ env.PGHOST = host;
1917
+ env.PGPORT = String(port);
1918
+ env.PGUSER = user;
1919
+ env.PGPASSWORD = pass;
1920
+ env.PGDATABASE = db;
1921
+ env.DATABASE_URL = `postgresql://${user}:${pass}@${host}:${port}/${db}`;
1922
+ } else if (svc.name === "mysql") {
1923
+ const pass = svc.env.MYSQL_ROOT_PASSWORD ?? "";
1924
+ const db = svc.env.MYSQL_DATABASE ?? "";
1925
+ const port = svc.port ?? 3306;
1926
+ env.MYSQL_HOST = host;
1927
+ env.MYSQL_PORT = String(port);
1928
+ env.MYSQL_USER = "root";
1929
+ env.MYSQL_PASSWORD = pass;
1930
+ env.MYSQL_DATABASE = db;
1931
+ if (env.DATABASE_URL === void 0) {
1932
+ env.DATABASE_URL = `mysql://root:${pass}@${host}:${port}/${db}`;
1933
+ }
1934
+ } else if (svc.name === "redis") {
1935
+ const port = svc.port ?? 6379;
1936
+ env.REDIS_URL = `redis://${host}:${port}`;
1937
+ }
1938
+ }
1939
+ return env;
1940
+ }
1906
1941
  function deriveServiceName(image) {
1907
1942
  const lastSegment = image.split("/").pop() ?? image;
1908
1943
  const noTag = lastSegment.split("@")[0].split(":")[0];
@@ -2438,6 +2473,14 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
2438
2473
  for (const v of ideVolumes) {
2439
2474
  lines.push(` - ${v.volume}:${v.target}`);
2440
2475
  }
2476
+ const wsEnv = serviceConnectionEnv(opts.services);
2477
+ const wsEnvKeys = Object.keys(wsEnv);
2478
+ if (wsEnvKeys.length > 0) {
2479
+ lines.push(" environment:");
2480
+ for (const k of wsEnvKeys) {
2481
+ lines.push(` ${k}: ${composeScalar(wsEnv[k])}`);
2482
+ }
2483
+ }
2441
2484
  for (const svc of opts.services) {
2442
2485
  lines.push(` ${svc.name}:`);
2443
2486
  lines.push(` image: ${svc.image}`);
@@ -3216,8 +3259,8 @@ function removeRepoFromDoc(doc, urlOrPath) {
3216
3259
  if (!isMap2(item)) return false;
3217
3260
  const url = item.get("url");
3218
3261
  if (url === urlOrPath) return true;
3219
- const path23 = item.get("path");
3220
- const effectivePath = typeof path23 === "string" ? path23 : typeof url === "string" ? deriveRepoName(url) : void 0;
3262
+ const path24 = item.get("path");
3263
+ const effectivePath = typeof path24 === "string" ? path24 : typeof url === "string" ? deriveRepoName(url) : void 0;
3221
3264
  return effectivePath === urlOrPath;
3222
3265
  });
3223
3266
  if (idx < 0) return false;
@@ -3329,7 +3372,7 @@ async function runAddRepo(input) {
3329
3372
  "Missing repo URL. Usage: monoceros add-repo <containername> <url>."
3330
3373
  );
3331
3374
  }
3332
- const path23 = (input.path ?? deriveRepoName(url)).trim();
3375
+ const path24 = (input.path ?? deriveRepoName(url)).trim();
3333
3376
  const hasName = typeof input.gitName === "string" && input.gitName.trim().length > 0;
3334
3377
  const hasEmail = typeof input.gitEmail === "string" && input.gitEmail.trim().length > 0;
3335
3378
  if (hasName !== hasEmail) {
@@ -3366,7 +3409,7 @@ async function runAddRepo(input) {
3366
3409
  const providerToWrite = !canonical && explicitProvider ? explicitProvider : void 0;
3367
3410
  const entry2 = {
3368
3411
  url,
3369
- path: path23,
3412
+ path: path24,
3370
3413
  ...hasName && hasEmail ? {
3371
3414
  gitUser: {
3372
3415
  name: input.gitName.trim(),
@@ -4767,22 +4810,42 @@ function generateAgentsMd(input) {
4767
4810
  lines.push(formatServiceLine(svc));
4768
4811
  }
4769
4812
  lines.push("");
4770
- lines.push(
4771
- "Credentials for these services are intentionally not stored in this",
4772
- "file. If you need to connect from code or from the command line, ask",
4773
- "the user in the current chat; they will paste the values directly.",
4774
- "Do not commit credentials provided this way into any file in the",
4775
- "repo \u2014 they belong in the user's `.env` on the host, not in source",
4776
- "control."
4777
- );
4813
+ const connEnv = serviceConnectionEnv(input.services);
4814
+ if (Object.keys(connEnv).length > 0) {
4815
+ lines.push(
4816
+ "Connection details for the curated services above are already set as",
4817
+ "environment variables in this container. Read them from the",
4818
+ "environment \u2014 do not ask the user for credentials, and do not",
4819
+ "hardcode them:"
4820
+ );
4821
+ lines.push("");
4822
+ if (connEnv.DATABASE_URL !== void 0) {
4823
+ lines.push(
4824
+ "- `DATABASE_URL` \u2014 the SQL database. Engine-specific variables are",
4825
+ " set too (`PGHOST`/`PGPORT`/`PGUSER`/`PGPASSWORD`/`PGDATABASE` for",
4826
+ " Postgres, `MYSQL_*` for MySQL)."
4827
+ );
4828
+ }
4829
+ if (connEnv.REDIS_URL !== void 0) {
4830
+ lines.push("- `REDIS_URL` \u2014 Redis.");
4831
+ }
4832
+ lines.push("");
4833
+ lines.push(
4834
+ "These are dev-only defaults for the local container, fine to use",
4835
+ "directly. Prefer reading the variable (e.g. `process.env.DATABASE_URL`)",
4836
+ "over copying its value into code."
4837
+ );
4838
+ }
4778
4839
  const hasCustom = input.services.some((s) => !isCuratedService(s.name));
4779
4840
  if (hasCustom) {
4780
4841
  lines.push("");
4781
4842
  lines.push(
4782
4843
  "For custom-image services, Monoceros does not know the service's",
4783
- "configuration (env vars, ports beyond the primary one, required",
4784
- "volumes). Treat such a service as a black box reachable at the",
4785
- "listed address; if you need more, ask the user."
4844
+ "configuration or credentials (env vars, ports beyond the primary one,",
4845
+ "required volumes). Treat such a service as a black box reachable at",
4846
+ "the listed address; if you need to connect, ask the user in the",
4847
+ "current chat. Do not commit credentials into the repo \u2014 they belong",
4848
+ "in the user's `.env` on the host."
4786
4849
  );
4787
4850
  }
4788
4851
  lines.push("");
@@ -4866,6 +4929,54 @@ function generateAgentsMd(input) {
4866
4929
  " DataGrip) to one of the services."
4867
4930
  );
4868
4931
  lines.push("");
4932
+ if (input.ports.length > 0) {
4933
+ lines.push("## Running a long-running server");
4934
+ lines.push("");
4935
+ lines.push(
4936
+ "When you build something that serves on a port (a web app, an API),",
4937
+ "start it as a **detached** background process so it keeps running after",
4938
+ "this session ends. A plain `npm start` (or any foreground start) dies",
4939
+ "the moment the user exits you or closes the terminal \u2014 and then",
4940
+ `\`${input.containerName}.localhost\` returns 502 Bad Gateway.`
4941
+ );
4942
+ lines.push("");
4943
+ lines.push(
4944
+ "Launch it in a new session with `setsid`, using the project's own",
4945
+ "start command, and record the process-group PID + log under the",
4946
+ "container's logs directory:"
4947
+ );
4948
+ lines.push("");
4949
+ lines.push("```");
4950
+ lines.push(
4951
+ `setsid sh -c 'echo $$ >/workspaces/${input.containerName}/logs/<app>.pid; \\`
4952
+ );
4953
+ lines.push(
4954
+ ` exec <the project's start command> >/workspaces/${input.containerName}/logs/<app>.log 2>&1' </dev/null &`
4955
+ );
4956
+ lines.push("```");
4957
+ lines.push("");
4958
+ lines.push(
4959
+ "Use whatever start command the project actually uses (`npm start`,",
4960
+ "`./mvnw spring-boot:run`, `python app.py`, `go run .`, \u2026) \u2014 do not force",
4961
+ "a language-specific one. `<app>` is a short name you choose."
4962
+ );
4963
+ lines.push("");
4964
+ lines.push("To stop it, kill the whole process group (also stops children");
4965
+ lines.push("like node under npm or java under maven):");
4966
+ lines.push("");
4967
+ lines.push("```");
4968
+ lines.push(
4969
+ `kill -TERM -$(cat /workspaces/${input.containerName}/logs/<app>.pid)`
4970
+ );
4971
+ lines.push("```");
4972
+ lines.push("");
4973
+ lines.push(
4974
+ `The user can follow the output with \`monoceros logs ${input.containerName} <app>\``,
4975
+ "on the host. The server must listen on `0.0.0.0` (not `127.0.0.1`) on",
4976
+ "the exposed port, or Traefik cannot reach it."
4977
+ );
4978
+ lines.push("");
4979
+ }
4869
4980
  lines.push("## Command reference");
4870
4981
  lines.push("");
4871
4982
  lines.push(
@@ -5373,10 +5484,12 @@ var init_cli = __esm({
5373
5484
  cachedBinaryPath = null;
5374
5485
  spawnDevcontainer = (args, cwd, options = {}) => {
5375
5486
  const binPath = devcontainerCliPath();
5487
+ const env = { ...process.env, DOCKER_CLI_HINTS: "false" };
5376
5488
  return new Promise((resolve, reject) => {
5377
5489
  if (options.interactive) {
5378
5490
  const child2 = spawn4(process.execPath, [binPath, ...args], {
5379
5491
  cwd,
5492
+ env,
5380
5493
  stdio: "inherit"
5381
5494
  });
5382
5495
  child2.on("error", reject);
@@ -5385,6 +5498,7 @@ var init_cli = __esm({
5385
5498
  }
5386
5499
  const child = spawn4(process.execPath, [binPath, ...args], {
5387
5500
  cwd,
5501
+ env,
5388
5502
  stdio: ["ignore", "pipe", "pipe"]
5389
5503
  });
5390
5504
  if (options.quiet) {
@@ -6293,7 +6407,7 @@ var CLI_VERSION;
6293
6407
  var init_version = __esm({
6294
6408
  "src/version.ts"() {
6295
6409
  "use strict";
6296
- CLI_VERSION = true ? "1.18.0" : "dev";
6410
+ CLI_VERSION = true ? "1.19.1" : "dev";
6297
6411
  }
6298
6412
  });
6299
6413
 
@@ -7931,7 +8045,6 @@ exit 0
7931
8045
  const servers = [];
7932
8046
  let handledUrl = false;
7933
8047
  const onAuthUrl = (authUrl) => {
7934
- consola16.info("Opening the Claude sign-in page in your browser\u2026");
7935
8048
  openInBrowser(authUrl);
7936
8049
  const target = parseCallbackTarget(authUrl);
7937
8050
  if (!target) {
@@ -7977,9 +8090,7 @@ exit 0
7977
8090
  handledUrl = true;
7978
8091
  onAuthUrl(content.trim());
7979
8092
  }, 250);
7980
- consola16.info(
7981
- "Starting Claude login. Pick your account in Claude's menu \u2014 the browser opens for you, no copying."
7982
- );
8093
+ consola16.info("Logging in to Claude \u2014 a browser window will open for you.");
7983
8094
  const child = spawn8(
7984
8095
  process.execPath,
7985
8096
  [
@@ -7990,9 +8101,13 @@ exit 0
7990
8101
  "--mount-workspace-git-root=false",
7991
8102
  "bash",
7992
8103
  "-lc",
7993
- `export PATH="/workspaces/${name}/${RELAY_DIRNAME}:$PATH"; exec claude`
8104
+ `export PATH="/workspaces/${name}/${RELAY_DIRNAME}:$PATH"; exec claude auth login`
7994
8105
  ],
7995
- { cwd: root, stdio: "inherit" }
8106
+ {
8107
+ cwd: root,
8108
+ env: { ...process.env, DOCKER_CLI_HINTS: "false" },
8109
+ stdio: "inherit"
8110
+ }
7996
8111
  );
7997
8112
  const code = await new Promise((resolve) => {
7998
8113
  child.on("error", () => resolve(1));
@@ -8064,7 +8179,18 @@ var init_login2 = __esm({
8064
8179
  });
8065
8180
 
8066
8181
  // src/commands/logs.ts
8182
+ import { spawn as spawn9 } from "child_process";
8183
+ import { existsSync as existsSync12 } from "fs";
8184
+ import path20 from "path";
8067
8185
  import { defineCommand as defineCommand14 } from "citty";
8186
+ function tailLogFile(file, follow) {
8187
+ const [cmd, args] = follow ? ["tail", ["-F", file]] : ["cat", [file]];
8188
+ return new Promise((resolve, reject) => {
8189
+ const child = spawn9(cmd, args, { stdio: "inherit" });
8190
+ child.on("error", reject);
8191
+ child.on("exit", (code) => resolve(code ?? 0));
8192
+ });
8193
+ }
8068
8194
  var logsCommand;
8069
8195
  var init_logs = __esm({
8070
8196
  "src/commands/logs.ts"() {
@@ -8076,7 +8202,7 @@ var init_logs = __esm({
8076
8202
  meta: {
8077
8203
  name: "logs",
8078
8204
  group: "run",
8079
- description: "Tail logs from the compose services of the named dev-container. Pass --no-follow for a one-shot dump."
8205
+ description: "Tail logs from a compose service of the named dev-container, or from a long-running app started inside it (logs/<app>.log). Pass --no-follow for a one-shot dump."
8080
8206
  },
8081
8207
  args: {
8082
8208
  name: {
@@ -8086,7 +8212,7 @@ var init_logs = __esm({
8086
8212
  },
8087
8213
  service: {
8088
8214
  type: "string",
8089
- description: "Restrict to a single compose service (e.g. postgres). Defaults to all."
8215
+ description: "A compose service (e.g. postgres) or an app whose log is at logs/<service>.log. Defaults to all compose services."
8090
8216
  },
8091
8217
  follow: {
8092
8218
  type: "boolean",
@@ -8096,10 +8222,17 @@ var init_logs = __esm({
8096
8222
  }
8097
8223
  },
8098
8224
  run({ args }) {
8225
+ const service = typeof args.service === "string" ? args.service : void 0;
8226
+ if (service) {
8227
+ const logFile = path20.join(containerLogsDir(args.name), `${service}.log`);
8228
+ if (existsSync12(logFile)) {
8229
+ return dispatch(() => tailLogFile(logFile, args.follow));
8230
+ }
8231
+ }
8099
8232
  return dispatch(
8100
8233
  () => runLogs({
8101
8234
  root: containerDir(args.name),
8102
- ...typeof args.service === "string" ? { service: args.service } : {},
8235
+ ...service ? { service } : {},
8103
8236
  follow: args.follow
8104
8237
  })
8105
8238
  );
@@ -8299,8 +8432,8 @@ var init_remove_feature = __esm({
8299
8432
  });
8300
8433
 
8301
8434
  // src/remove/index.ts
8302
- import { existsSync as existsSync12, promises as fs15 } from "fs";
8303
- import path20 from "path";
8435
+ import { existsSync as existsSync13, promises as fs15 } from "fs";
8436
+ import path21 from "path";
8304
8437
  import { consola as consola21 } from "consola";
8305
8438
  async function runRemove(opts) {
8306
8439
  const home = opts.monocerosHome ?? monocerosHome();
@@ -8317,9 +8450,9 @@ async function runRemove(opts) {
8317
8450
  const ymlPath = containerConfigPath(opts.name, home);
8318
8451
  const envPath = containerEnvPath(opts.name, home);
8319
8452
  const containerPath = containerDir(opts.name, home);
8320
- const hasYml = existsSync12(ymlPath);
8321
- const hasEnv = existsSync12(envPath);
8322
- const hasContainer = existsSync12(containerPath);
8453
+ const hasYml = existsSync13(ymlPath);
8454
+ const hasEnv = existsSync13(envPath);
8455
+ const hasContainer = existsSync13(containerPath);
8323
8456
  if (!hasYml && !hasContainer) {
8324
8457
  throw new Error(
8325
8458
  `Nothing to remove for '${opts.name}': neither ${ymlPath} nor ${containerPath} exists.`
@@ -8345,16 +8478,16 @@ async function runRemove(opts) {
8345
8478
  let backupPath = null;
8346
8479
  if (!opts.noBackup && (hasYml || hasContainer)) {
8347
8480
  const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8348
- backupPath = path20.join(home, "container-backups", `${opts.name}-${ts}`);
8481
+ backupPath = path21.join(home, "container-backups", `${opts.name}-${ts}`);
8349
8482
  await fs15.mkdir(backupPath, { recursive: true });
8350
8483
  if (hasYml) {
8351
- await fs15.copyFile(ymlPath, path20.join(backupPath, `${opts.name}.yml`));
8484
+ await fs15.copyFile(ymlPath, path21.join(backupPath, `${opts.name}.yml`));
8352
8485
  }
8353
8486
  if (hasEnv) {
8354
- await fs15.copyFile(envPath, path20.join(backupPath, `${opts.name}.env`));
8487
+ await fs15.copyFile(envPath, path21.join(backupPath, `${opts.name}.env`));
8355
8488
  }
8356
8489
  if (hasContainer) {
8357
- await fs15.cp(containerPath, path20.join(backupPath, "container"), {
8490
+ await fs15.cp(containerPath, path21.join(backupPath, "container"), {
8358
8491
  recursive: true
8359
8492
  });
8360
8493
  }
@@ -8514,8 +8647,8 @@ var init_remove2 = __esm({
8514
8647
  });
8515
8648
 
8516
8649
  // src/restore/index.ts
8517
- import { existsSync as existsSync13, promises as fs16 } from "fs";
8518
- import path21 from "path";
8650
+ import { existsSync as existsSync14, promises as fs16 } from "fs";
8651
+ import path22 from "path";
8519
8652
  import { consola as consola23 } from "consola";
8520
8653
  async function runRestore(opts) {
8521
8654
  const home = opts.monocerosHome ?? monocerosHome();
@@ -8523,8 +8656,8 @@ async function runRestore(opts) {
8523
8656
  info: (msg) => consola23.info(msg),
8524
8657
  success: (msg) => consola23.success(msg)
8525
8658
  };
8526
- const backup = path21.resolve(opts.backupPath);
8527
- if (!existsSync13(backup)) {
8659
+ const backup = path22.resolve(opts.backupPath);
8660
+ if (!existsSync14(backup)) {
8528
8661
  throw new Error(`Backup not found: ${backup}.`);
8529
8662
  }
8530
8663
  const stat = await fs16.stat(backup);
@@ -8545,24 +8678,24 @@ async function runRestore(opts) {
8545
8678
  }
8546
8679
  const ymlFile = ymlFiles[0];
8547
8680
  const name = ymlFile.replace(/\.yml$/, "");
8548
- const containerInBackup = path21.join(backup, "container");
8549
- const hasContainer = existsSync13(containerInBackup);
8550
- const envInBackup = path21.join(backup, `${name}.env`);
8551
- const hasEnv = existsSync13(envInBackup);
8681
+ const containerInBackup = path22.join(backup, "container");
8682
+ const hasContainer = existsSync14(containerInBackup);
8683
+ const envInBackup = path22.join(backup, `${name}.env`);
8684
+ const hasEnv = existsSync14(envInBackup);
8552
8685
  const destYml = containerConfigPath(name, home);
8553
8686
  const destContainer = containerDir(name, home);
8554
- if (existsSync13(destYml)) {
8687
+ if (existsSync14(destYml)) {
8555
8688
  throw new Error(
8556
8689
  `Refusing to restore: ${destYml} already exists. Remove the current container first (\`monoceros remove ${name}\`) or rename the existing config.`
8557
8690
  );
8558
8691
  }
8559
- if (hasContainer && existsSync13(destContainer)) {
8692
+ if (hasContainer && existsSync14(destContainer)) {
8560
8693
  throw new Error(
8561
8694
  `Refusing to restore: ${destContainer} already exists. Remove the current container first (\`monoceros remove ${name}\`).`
8562
8695
  );
8563
8696
  }
8564
8697
  await fs16.mkdir(containerConfigsDir(home), { recursive: true });
8565
- await fs16.copyFile(path21.join(backup, ymlFile), destYml);
8698
+ await fs16.copyFile(path22.join(backup, ymlFile), destYml);
8566
8699
  if (hasEnv) {
8567
8700
  await fs16.copyFile(envInBackup, containerEnvPath(name, home));
8568
8701
  }
@@ -9143,11 +9276,11 @@ var init_stop = __esm({
9143
9276
  });
9144
9277
 
9145
9278
  // src/tunnel/resolve.ts
9146
- import { existsSync as existsSync14 } from "fs";
9147
- import path22 from "path";
9279
+ import { existsSync as existsSync15 } from "fs";
9280
+ import path23 from "path";
9148
9281
  async function resolveTunnelTarget(opts) {
9149
9282
  const ymlPath = containerConfigPath(opts.name, opts.monocerosHome);
9150
- if (!existsSync14(ymlPath)) {
9283
+ if (!existsSync15(ymlPath)) {
9151
9284
  throw new Error(
9152
9285
  `No yml profile for '${opts.name}' at ${ymlPath}. Run \`monoceros init ${opts.name}\` first.`
9153
9286
  );
@@ -9155,13 +9288,13 @@ async function resolveTunnelTarget(opts) {
9155
9288
  const parsed = await readConfig(ymlPath);
9156
9289
  const config = parsed.config;
9157
9290
  const containerRoot = containerDir(opts.name, opts.monocerosHome);
9158
- if (!existsSync14(containerRoot)) {
9291
+ if (!existsSync15(containerRoot)) {
9159
9292
  throw new Error(
9160
9293
  `Container '${opts.name}' is not materialised at ${containerRoot}. Run \`monoceros apply ${opts.name}\` first.`
9161
9294
  );
9162
9295
  }
9163
- const composePath = path22.join(containerRoot, ".devcontainer", "compose.yaml");
9164
- const isCompose = existsSync14(composePath);
9296
+ const composePath = path23.join(containerRoot, ".devcontainer", "compose.yaml");
9297
+ const isCompose = existsSync15(composePath);
9165
9298
  const parsedTarget = parseTargetArg(opts.target, config);
9166
9299
  const docker = opts.docker ?? defaultDockerExec;
9167
9300
  if (isCompose) {
@@ -9396,7 +9529,7 @@ var init_port_check2 = __esm({
9396
9529
  });
9397
9530
 
9398
9531
  // src/tunnel/run.ts
9399
- import { spawn as spawn9 } from "child_process";
9532
+ import { spawn as spawn10 } from "child_process";
9400
9533
  import { consola as consola34 } from "consola";
9401
9534
  function signalNumber(signal) {
9402
9535
  switch (signal) {
@@ -9486,7 +9619,7 @@ var init_run3 = __esm({
9486
9619
  init_port_check2();
9487
9620
  SOCAT_IMAGE = "alpine/socat:1.8.0.3";
9488
9621
  defaultDockerSpawn = (args) => {
9489
- const child = spawn9("docker", args, {
9622
+ const child = spawn10("docker", args, {
9490
9623
  stdio: "inherit"
9491
9624
  });
9492
9625
  const exited = new Promise((resolve, reject) => {
@@ -9580,7 +9713,7 @@ var init_tunnel = __esm({
9580
9713
  });
9581
9714
 
9582
9715
  // src/upgrade/index.ts
9583
- import { existsSync as existsSync15, promises as fs17 } from "fs";
9716
+ import { existsSync as existsSync16, promises as fs17 } from "fs";
9584
9717
  import { consola as consola36 } from "consola";
9585
9718
  async function fetchRuntimeVersions() {
9586
9719
  const tokenUrl = `https://ghcr.io/token?service=ghcr.io&scope=repository:${RUNTIME_REPO}:pull`;
@@ -9639,7 +9772,7 @@ async function runUpgrade(opts) {
9639
9772
  );
9640
9773
  }
9641
9774
  const ymlPath = containerConfigPath(opts.name, home);
9642
- if (!existsSync15(ymlPath)) {
9775
+ if (!existsSync16(ymlPath)) {
9643
9776
  throw new Error(
9644
9777
  `No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
9645
9778
  );
@@ -10071,25 +10204,25 @@ function detectHelpRequest(argv, main2) {
10071
10204
  const separatorIdx = argv.indexOf("--");
10072
10205
  if (helpIdx === -1) return null;
10073
10206
  if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
10074
- const path23 = [];
10207
+ const path24 = [];
10075
10208
  const tokens = argv.slice(
10076
10209
  0,
10077
10210
  separatorIdx === -1 ? argv.length : separatorIdx
10078
10211
  );
10079
10212
  let cursor = main2;
10080
10213
  const mainName = (main2.meta ?? {}).name ?? "monoceros";
10081
- path23.push(mainName);
10214
+ path24.push(mainName);
10082
10215
  for (const tok of tokens) {
10083
10216
  if (tok.startsWith("-")) continue;
10084
10217
  const subs = cursor.subCommands ?? {};
10085
10218
  if (tok in subs) {
10086
10219
  cursor = subs[tok];
10087
- path23.push(tok);
10220
+ path24.push(tok);
10088
10221
  continue;
10089
10222
  }
10090
10223
  break;
10091
10224
  }
10092
- return { path: path23, cmd: cursor };
10225
+ return { path: path24, cmd: cursor };
10093
10226
  }
10094
10227
  async function maybeRenderHelp(argv, main2) {
10095
10228
  const hit = detectHelpRequest(argv, main2);