@getmonoceros/workbench 1.17.0 → 1.18.0

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
@@ -3216,8 +3216,8 @@ function removeRepoFromDoc(doc, urlOrPath) {
3216
3216
  if (!isMap2(item)) return false;
3217
3217
  const url = item.get("url");
3218
3218
  if (url === urlOrPath) return true;
3219
- const path22 = item.get("path");
3220
- const effectivePath = typeof path22 === "string" ? path22 : typeof url === "string" ? deriveRepoName(url) : void 0;
3219
+ const path23 = item.get("path");
3220
+ const effectivePath = typeof path23 === "string" ? path23 : typeof url === "string" ? deriveRepoName(url) : void 0;
3221
3221
  return effectivePath === urlOrPath;
3222
3222
  });
3223
3223
  if (idx < 0) return false;
@@ -3329,7 +3329,7 @@ async function runAddRepo(input) {
3329
3329
  "Missing repo URL. Usage: monoceros add-repo <containername> <url>."
3330
3330
  );
3331
3331
  }
3332
- const path22 = (input.path ?? deriveRepoName(url)).trim();
3332
+ const path23 = (input.path ?? deriveRepoName(url)).trim();
3333
3333
  const hasName = typeof input.gitName === "string" && input.gitName.trim().length > 0;
3334
3334
  const hasEmail = typeof input.gitEmail === "string" && input.gitEmail.trim().length > 0;
3335
3335
  if (hasName !== hasEmail) {
@@ -3366,7 +3366,7 @@ async function runAddRepo(input) {
3366
3366
  const providerToWrite = !canonical && explicitProvider ? explicitProvider : void 0;
3367
3367
  const entry2 = {
3368
3368
  url,
3369
- path: path22,
3369
+ path: path23,
3370
3370
  ...hasName && hasEmail ? {
3371
3371
  gitUser: {
3372
3372
  name: input.gitName.trim(),
@@ -6293,7 +6293,7 @@ var CLI_VERSION;
6293
6293
  var init_version = __esm({
6294
6294
  "src/version.ts"() {
6295
6295
  "use strict";
6296
- CLI_VERSION = true ? "1.17.0" : "dev";
6296
+ CLI_VERSION = true ? "1.18.0" : "dev";
6297
6297
  }
6298
6298
  });
6299
6299
 
@@ -6455,8 +6455,8 @@ var init_completion = __esm({
6455
6455
  name: "completion",
6456
6456
  group: "tooling",
6457
6457
  // Hidden from `monoceros --help`: the install scripts wire up
6458
- // completion automatically; manual setup is documented in
6459
- // docs/commands/completion.md. Still runnable directly.
6458
+ // completion automatically; manual setup is documented at
6459
+ // getmonoceros.build/docs/reference/utilities/completion. Still runnable directly.
6460
6460
  hidden: true,
6461
6461
  description: "Print a shell completion script for bash, zsh or PowerShell to stdout. Pipe the output into a file your shell loads at startup. The install scripts (install.sh / install.ps1) call this automatically."
6462
6462
  },
@@ -6482,6 +6482,43 @@ var init_completion = __esm({
6482
6482
  }
6483
6483
  });
6484
6484
 
6485
+ // src/login/services.ts
6486
+ function featureLeaf(ref) {
6487
+ const noTag = ref.replace(/:[^/:]*$/, "");
6488
+ return noTag.slice(noTag.lastIndexOf("/") + 1);
6489
+ }
6490
+ function loginCapableServices(refs) {
6491
+ const out = [];
6492
+ for (const ref of refs) {
6493
+ const service = LOGIN_SERVICE_BY_LEAF[featureLeaf(ref)];
6494
+ if (service && !out.includes(service)) out.push(service);
6495
+ }
6496
+ return out;
6497
+ }
6498
+ function parseCallbackTarget(authUrl) {
6499
+ try {
6500
+ const u = new URL(authUrl);
6501
+ const redirect = u.searchParams.get("redirect_uri");
6502
+ if (!redirect) return null;
6503
+ const r = new URL(redirect);
6504
+ if (r.hostname !== "localhost" && r.hostname !== "127.0.0.1") return null;
6505
+ const port = Number(r.port);
6506
+ if (!Number.isInteger(port) || port <= 0) return null;
6507
+ return { port, pathname: r.pathname };
6508
+ } catch {
6509
+ return null;
6510
+ }
6511
+ }
6512
+ var LOGIN_SERVICE_BY_LEAF;
6513
+ var init_services = __esm({
6514
+ "src/login/services.ts"() {
6515
+ "use strict";
6516
+ LOGIN_SERVICE_BY_LEAF = {
6517
+ "claude-code": "claude"
6518
+ };
6519
+ }
6520
+ });
6521
+
6485
6522
  // src/completion/resolve.ts
6486
6523
  import { existsSync as existsSync8, promises as fs13 } from "fs";
6487
6524
  import path16 from "path";
@@ -6551,6 +6588,7 @@ function dispatchCommand(spec, argTokens, ctx) {
6551
6588
  if (inPostDash && spec.innerArgs) {
6552
6589
  return resolveValues(spec.innerArgs, ctx, ctx.current);
6553
6590
  }
6591
+ if (inPostDash) return [];
6554
6592
  return resolvePreDash(spec, preDash, ctx);
6555
6593
  }
6556
6594
  async function resolvePreDash(spec, preDash, ctx) {
@@ -6637,6 +6675,20 @@ async function listContainerNames(ctx) {
6637
6675
  const entries = await fs13.readdir(dir);
6638
6676
  return entries.filter((e) => e.endsWith(".yml")).map((e) => e.slice(0, -".yml".length)).sort();
6639
6677
  }
6678
+ async function listLoginServices(ctx) {
6679
+ const after = ctx.prev.slice(2);
6680
+ const name = after.find((t) => !t.startsWith("-"));
6681
+ if (!name) return [];
6682
+ const home = ctx.opts.monocerosHome ?? monocerosHome();
6683
+ const cfgPath = path16.join(home, "container-configs", `${name}.yml`);
6684
+ if (!existsSync8(cfgPath)) return [];
6685
+ try {
6686
+ const { config } = await readConfig(cfgPath);
6687
+ return loginCapableServices(config.features.map((f) => f.ref));
6688
+ } catch {
6689
+ return [];
6690
+ }
6691
+ }
6640
6692
  async function listFeatureComponents() {
6641
6693
  const catalog = await loadComponentCatalog();
6642
6694
  return [...catalog.values()].filter((c) => c.file.category === "feature").map((c) => c.name).sort();
@@ -6704,15 +6756,18 @@ var init_resolve = __esm({
6704
6756
  "src/completion/resolve.ts"() {
6705
6757
  "use strict";
6706
6758
  init_paths();
6759
+ init_io();
6707
6760
  init_components();
6708
6761
  init_manifest();
6709
6762
  init_catalog();
6763
+ init_services();
6710
6764
  init_schema();
6711
6765
  ALL_COMMANDS = [
6712
6766
  "init",
6713
6767
  "list-components",
6714
6768
  "shell",
6715
6769
  "run",
6770
+ "login",
6716
6771
  "logs",
6717
6772
  "start",
6718
6773
  "stop",
@@ -6777,7 +6832,11 @@ var init_resolve = __esm({
6777
6832
  }
6778
6833
  },
6779
6834
  shell: { positionals: [containerName] },
6780
- run: { positionals: [containerName] },
6835
+ run: {
6836
+ positionals: [containerName],
6837
+ flags: { "--in": { type: "value" } }
6838
+ },
6839
+ login: { positionals: [containerName, (ctx) => listLoginServices(ctx)] },
6781
6840
  logs: { positionals: [containerName] },
6782
6841
  start: { positionals: [containerName] },
6783
6842
  stop: { positionals: [containerName] },
@@ -7750,8 +7809,262 @@ var init_list_components = __esm({
7750
7809
  }
7751
7810
  });
7752
7811
 
7753
- // src/commands/logs.ts
7812
+ // src/devcontainer/shell.ts
7813
+ import { existsSync as existsSync10 } from "fs";
7814
+ import path18 from "path";
7815
+ async function runShell(opts) {
7816
+ assertContainerExists(opts.root);
7817
+ const spawnFn = opts.spawn ?? spawnDevcontainer;
7818
+ const upCode = await spawnFn(
7819
+ ["up", "--workspace-folder", opts.root, "--mount-workspace-git-root=false"],
7820
+ opts.root,
7821
+ { quiet: true }
7822
+ );
7823
+ if (upCode !== 0) return upCode;
7824
+ return spawnFn(
7825
+ [
7826
+ "exec",
7827
+ "--workspace-folder",
7828
+ opts.root,
7829
+ "--mount-workspace-git-root=false",
7830
+ "bash"
7831
+ ],
7832
+ opts.root,
7833
+ { interactive: true }
7834
+ );
7835
+ }
7836
+ function assertContainerExists(root) {
7837
+ if (!existsSync10(path18.join(root, ".devcontainer"))) {
7838
+ throw new Error(
7839
+ `No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
7840
+ );
7841
+ }
7842
+ }
7843
+ var init_shell = __esm({
7844
+ "src/devcontainer/shell.ts"() {
7845
+ "use strict";
7846
+ init_cli();
7847
+ }
7848
+ });
7849
+
7850
+ // src/login/index.ts
7851
+ import { spawn as spawn8 } from "child_process";
7852
+ import { existsSync as existsSync11, promises as fsp2, readFileSync as readFileSync6 } from "fs";
7853
+ import http from "http";
7854
+ import path19 from "path";
7855
+ import { consola as consola16 } from "consola";
7856
+ function openInBrowser(url) {
7857
+ const platform = process.platform;
7858
+ const [cmd, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
7859
+ try {
7860
+ const child = spawn8(cmd, args, {
7861
+ stdio: "ignore",
7862
+ detached: true
7863
+ });
7864
+ child.on("error", () => {
7865
+ });
7866
+ child.unref();
7867
+ } catch {
7868
+ }
7869
+ }
7870
+ async function runLogin(opts) {
7871
+ const root = containerDir(opts.name);
7872
+ assertContainerExists(root);
7873
+ const { config } = await readConfig(containerConfigPath(opts.name));
7874
+ const capable = loginCapableServices(config.features.map((f) => f.ref));
7875
+ if (capable.length === 0) {
7876
+ consola16.error(
7877
+ `Container "${opts.name}" has no tool with a Monoceros login. (Supported today: claude.)`
7878
+ );
7879
+ return 1;
7880
+ }
7881
+ if (!opts.feature) {
7882
+ consola16.info(
7883
+ `Login-capable tools in "${opts.name}": ${capable.join(", ")}.`
7884
+ );
7885
+ consola16.info(`Log one in with: monoceros login ${opts.name} <tool>`);
7886
+ return 0;
7887
+ }
7888
+ const service = opts.feature;
7889
+ if (!capable.includes(service)) {
7890
+ consola16.error(
7891
+ `"${service}" is not a login-capable tool in "${opts.name}". Available: ${capable.join(", ")}.`
7892
+ );
7893
+ return 1;
7894
+ }
7895
+ if (service !== "claude") {
7896
+ consola16.error(
7897
+ `Login for "${service}" is not implemented yet (only claude).`
7898
+ );
7899
+ return 1;
7900
+ }
7901
+ return runClaudeLogin(opts.name, root, opts.spawn ?? spawnDevcontainer);
7902
+ }
7903
+ async function runClaudeLogin(name, root, spawnFn) {
7904
+ const credFile = path19.join(root, "home", ".claude", ".credentials.json");
7905
+ if (existsSync11(credFile)) {
7906
+ consola16.success(
7907
+ `Claude is already logged in for "${name}". Re-apply or remove the credential to log in again.`
7908
+ );
7909
+ return 0;
7910
+ }
7911
+ const upCode = await spawnFn(
7912
+ ["up", "--workspace-folder", root, "--mount-workspace-git-root=false"],
7913
+ root,
7914
+ { quiet: true }
7915
+ );
7916
+ if (upCode !== 0) return upCode;
7917
+ const relayDir = path19.join(root, RELAY_DIRNAME);
7918
+ const relayScript = path19.join(relayDir, "xdg-open");
7919
+ const urlFile = path19.join(relayDir, "url");
7920
+ await fsp2.mkdir(relayDir, { recursive: true });
7921
+ await fsp2.rm(urlFile, { force: true });
7922
+ await fsp2.writeFile(
7923
+ relayScript,
7924
+ `#!/bin/sh
7925
+ printf '%s\\n' "$1" > "$(dirname "$0")/url"
7926
+ exit 0
7927
+ `,
7928
+ { mode: 493 }
7929
+ );
7930
+ await fsp2.chmod(relayScript, 493);
7931
+ const servers = [];
7932
+ let handledUrl = false;
7933
+ const onAuthUrl = (authUrl) => {
7934
+ consola16.info("Opening the Claude sign-in page in your browser\u2026");
7935
+ openInBrowser(authUrl);
7936
+ const target = parseCallbackTarget(authUrl);
7937
+ if (!target) {
7938
+ return;
7939
+ }
7940
+ const server = http.createServer((req, res) => {
7941
+ const reqUrl = req.url ?? target.pathname;
7942
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
7943
+ res.end(
7944
+ '<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>'
7945
+ );
7946
+ void spawnFn(
7947
+ [
7948
+ "exec",
7949
+ "--workspace-folder",
7950
+ root,
7951
+ "--mount-workspace-git-root=false",
7952
+ "curl",
7953
+ "-fsS",
7954
+ `http://localhost:${target.port}${reqUrl}`
7955
+ ],
7956
+ root,
7957
+ { quiet: true }
7958
+ );
7959
+ });
7960
+ server.on("error", (err) => {
7961
+ consola16.warn(
7962
+ `Could not start the local callback helper on port ${target.port} (${err.message}). If the browser shows a code, paste it into the terminal instead.`
7963
+ );
7964
+ });
7965
+ server.listen(target.port, "127.0.0.1");
7966
+ servers.push(server);
7967
+ };
7968
+ const poll = setInterval(() => {
7969
+ if (handledUrl || !existsSync11(urlFile)) return;
7970
+ let content = "";
7971
+ try {
7972
+ content = readFileSync6(urlFile, "utf8");
7973
+ } catch {
7974
+ return;
7975
+ }
7976
+ if (!content.trim()) return;
7977
+ handledUrl = true;
7978
+ onAuthUrl(content.trim());
7979
+ }, 250);
7980
+ consola16.info(
7981
+ "Starting Claude login. Pick your account in Claude's menu \u2014 the browser opens for you, no copying."
7982
+ );
7983
+ const child = spawn8(
7984
+ process.execPath,
7985
+ [
7986
+ devcontainerCliPath(),
7987
+ "exec",
7988
+ "--workspace-folder",
7989
+ root,
7990
+ "--mount-workspace-git-root=false",
7991
+ "bash",
7992
+ "-lc",
7993
+ `export PATH="/workspaces/${name}/${RELAY_DIRNAME}:$PATH"; exec claude`
7994
+ ],
7995
+ { cwd: root, stdio: "inherit" }
7996
+ );
7997
+ const code = await new Promise((resolve) => {
7998
+ child.on("error", () => resolve(1));
7999
+ child.on("exit", (c) => resolve(c ?? 0));
8000
+ });
8001
+ clearInterval(poll);
8002
+ for (const s of servers) s.close();
8003
+ await fsp2.rm(relayDir, { recursive: true, force: true });
8004
+ if (existsSync11(credFile)) {
8005
+ consola16.success(
8006
+ `Claude is logged in for "${name}". The credential persists across rebuilds.`
8007
+ );
8008
+ }
8009
+ return code;
8010
+ }
8011
+ var RELAY_DIRNAME;
8012
+ var init_login = __esm({
8013
+ "src/login/index.ts"() {
8014
+ "use strict";
8015
+ init_io();
8016
+ init_paths();
8017
+ init_cli();
8018
+ init_shell();
8019
+ init_services();
8020
+ RELAY_DIRNAME = ".monoceros-login";
8021
+ }
8022
+ });
8023
+
8024
+ // src/commands/login.ts
7754
8025
  import { defineCommand as defineCommand13 } from "citty";
8026
+ import { consola as consola17 } from "consola";
8027
+ var loginCommand;
8028
+ var init_login2 = __esm({
8029
+ "src/commands/login.ts"() {
8030
+ "use strict";
8031
+ init_login();
8032
+ loginCommand = defineCommand13({
8033
+ meta: {
8034
+ name: "login",
8035
+ group: "lifecycle",
8036
+ description: "Log a curated tool in inside the container. Opens the sign-in page in your browser for you \u2014 no copying URLs. Today: Claude."
8037
+ },
8038
+ args: {
8039
+ name: {
8040
+ type: "positional",
8041
+ description: "Container name.",
8042
+ required: true
8043
+ },
8044
+ feature: {
8045
+ type: "positional",
8046
+ description: "Which tool to log in (e.g. `claude`). Optional when the container has only one login-capable tool.",
8047
+ required: false
8048
+ }
8049
+ },
8050
+ async run({ args }) {
8051
+ try {
8052
+ const code = await runLogin({
8053
+ name: args.name,
8054
+ ...args.feature ? { feature: args.feature } : {}
8055
+ });
8056
+ process.exit(code);
8057
+ } catch (err) {
8058
+ consola17.error(err instanceof Error ? err.message : String(err));
8059
+ process.exit(1);
8060
+ }
8061
+ }
8062
+ });
8063
+ }
8064
+ });
8065
+
8066
+ // src/commands/logs.ts
8067
+ import { defineCommand as defineCommand14 } from "citty";
7755
8068
  var logsCommand;
7756
8069
  var init_logs = __esm({
7757
8070
  "src/commands/logs.ts"() {
@@ -7759,7 +8072,7 @@ var init_logs = __esm({
7759
8072
  init_paths();
7760
8073
  init_compose();
7761
8074
  init_dispatch();
7762
- logsCommand = defineCommand13({
8075
+ logsCommand = defineCommand14({
7763
8076
  meta: {
7764
8077
  name: "logs",
7765
8078
  group: "run",
@@ -7796,11 +8109,11 @@ var init_logs = __esm({
7796
8109
  });
7797
8110
 
7798
8111
  // src/commands/port.ts
7799
- import { defineCommand as defineCommand14 } from "citty";
7800
- import { consola as consola16 } from "consola";
8112
+ import { defineCommand as defineCommand15 } from "citty";
8113
+ import { consola as consola18 } from "consola";
7801
8114
  async function runPortListing(opts) {
7802
8115
  const out = opts.out ?? process.stdout;
7803
- const info = opts.info ?? ((m) => consola16.info(m));
8116
+ const info = opts.info ?? ((m) => consola18.info(m));
7804
8117
  const parsed = await readConfig(
7805
8118
  containerConfigPath(opts.name, opts.monocerosHome)
7806
8119
  );
@@ -7858,7 +8171,7 @@ var init_port = __esm({
7858
8171
  init_schema();
7859
8172
  init_dynamic();
7860
8173
  init_format();
7861
- portCommand = defineCommand14({
8174
+ portCommand = defineCommand15({
7862
8175
  meta: {
7863
8176
  name: "port",
7864
8177
  group: "discovery",
@@ -7876,7 +8189,7 @@ var init_port = __esm({
7876
8189
  const code = await runPortListing({ name: args.name });
7877
8190
  process.exit(code);
7878
8191
  } catch (err) {
7879
- consola16.error(err instanceof Error ? err.message : String(err));
8192
+ consola18.error(err instanceof Error ? err.message : String(err));
7880
8193
  process.exit(1);
7881
8194
  }
7882
8195
  }
@@ -7885,15 +8198,15 @@ var init_port = __esm({
7885
8198
  });
7886
8199
 
7887
8200
  // src/commands/remove-apt-packages.ts
7888
- import { defineCommand as defineCommand15 } from "citty";
7889
- import { consola as consola17 } from "consola";
8201
+ import { defineCommand as defineCommand16 } from "citty";
8202
+ import { consola as consola19 } from "consola";
7890
8203
  var removeAptPackagesCommand;
7891
8204
  var init_remove_apt_packages = __esm({
7892
8205
  "src/commands/remove-apt-packages.ts"() {
7893
8206
  "use strict";
7894
8207
  init_inner_args();
7895
8208
  init_modify();
7896
- removeAptPackagesCommand = defineCommand15({
8209
+ removeAptPackagesCommand = defineCommand16({
7897
8210
  meta: {
7898
8211
  name: "remove-apt-packages",
7899
8212
  group: "edit",
@@ -7915,7 +8228,7 @@ var init_remove_apt_packages = __esm({
7915
8228
  async run({ args }) {
7916
8229
  const packages = [...getInnerArgs()];
7917
8230
  if (packages.length === 0) {
7918
- consola17.error(
8231
+ consola19.error(
7919
8232
  "No package names given. Usage: `monoceros remove-apt-packages <containername> [--yes] -- <pkg> [<pkg> \u2026]`."
7920
8233
  );
7921
8234
  process.exit(1);
@@ -7928,7 +8241,7 @@ var init_remove_apt_packages = __esm({
7928
8241
  });
7929
8242
  process.exit(result.status === "aborted" ? 1 : 0);
7930
8243
  } catch (err) {
7931
- consola17.error(err instanceof Error ? err.message : String(err));
8244
+ consola19.error(err instanceof Error ? err.message : String(err));
7932
8245
  process.exit(1);
7933
8246
  }
7934
8247
  }
@@ -7937,14 +8250,14 @@ var init_remove_apt_packages = __esm({
7937
8250
  });
7938
8251
 
7939
8252
  // src/commands/remove-feature.ts
7940
- import { defineCommand as defineCommand16 } from "citty";
7941
- import { consola as consola18 } from "consola";
8253
+ import { defineCommand as defineCommand17 } from "citty";
8254
+ import { consola as consola20 } from "consola";
7942
8255
  var removeFeatureCommand;
7943
8256
  var init_remove_feature = __esm({
7944
8257
  "src/commands/remove-feature.ts"() {
7945
8258
  "use strict";
7946
8259
  init_modify();
7947
- removeFeatureCommand = defineCommand16({
8260
+ removeFeatureCommand = defineCommand17({
7948
8261
  meta: {
7949
8262
  name: "remove-feature",
7950
8263
  group: "edit",
@@ -7977,7 +8290,7 @@ var init_remove_feature = __esm({
7977
8290
  });
7978
8291
  process.exit(result.status === "aborted" ? 1 : 0);
7979
8292
  } catch (err) {
7980
- consola18.error(err instanceof Error ? err.message : String(err));
8293
+ consola20.error(err instanceof Error ? err.message : String(err));
7981
8294
  process.exit(1);
7982
8295
  }
7983
8296
  }
@@ -7986,15 +8299,15 @@ var init_remove_feature = __esm({
7986
8299
  });
7987
8300
 
7988
8301
  // src/remove/index.ts
7989
- import { existsSync as existsSync10, promises as fs15 } from "fs";
7990
- import path18 from "path";
7991
- import { consola as consola19 } from "consola";
8302
+ import { existsSync as existsSync12, promises as fs15 } from "fs";
8303
+ import path20 from "path";
8304
+ import { consola as consola21 } from "consola";
7992
8305
  async function runRemove(opts) {
7993
8306
  const home = opts.monocerosHome ?? monocerosHome();
7994
8307
  const logger = opts.logger ?? {
7995
- info: (msg) => consola19.info(msg),
7996
- success: (msg) => consola19.success(msg),
7997
- warn: (msg) => consola19.warn(msg)
8308
+ info: (msg) => consola21.info(msg),
8309
+ success: (msg) => consola21.success(msg),
8310
+ warn: (msg) => consola21.warn(msg)
7998
8311
  };
7999
8312
  if (!REGEX.solutionName.test(opts.name)) {
8000
8313
  throw new Error(
@@ -8004,9 +8317,9 @@ async function runRemove(opts) {
8004
8317
  const ymlPath = containerConfigPath(opts.name, home);
8005
8318
  const envPath = containerEnvPath(opts.name, home);
8006
8319
  const containerPath = containerDir(opts.name, home);
8007
- const hasYml = existsSync10(ymlPath);
8008
- const hasEnv = existsSync10(envPath);
8009
- const hasContainer = existsSync10(containerPath);
8320
+ const hasYml = existsSync12(ymlPath);
8321
+ const hasEnv = existsSync12(envPath);
8322
+ const hasContainer = existsSync12(containerPath);
8010
8323
  if (!hasYml && !hasContainer) {
8011
8324
  throw new Error(
8012
8325
  `Nothing to remove for '${opts.name}': neither ${ymlPath} nor ${containerPath} exists.`
@@ -8032,16 +8345,16 @@ async function runRemove(opts) {
8032
8345
  let backupPath = null;
8033
8346
  if (!opts.noBackup && (hasYml || hasContainer)) {
8034
8347
  const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8035
- backupPath = path18.join(home, "container-backups", `${opts.name}-${ts}`);
8348
+ backupPath = path20.join(home, "container-backups", `${opts.name}-${ts}`);
8036
8349
  await fs15.mkdir(backupPath, { recursive: true });
8037
8350
  if (hasYml) {
8038
- await fs15.copyFile(ymlPath, path18.join(backupPath, `${opts.name}.yml`));
8351
+ await fs15.copyFile(ymlPath, path20.join(backupPath, `${opts.name}.yml`));
8039
8352
  }
8040
8353
  if (hasEnv) {
8041
- await fs15.copyFile(envPath, path18.join(backupPath, `${opts.name}.env`));
8354
+ await fs15.copyFile(envPath, path20.join(backupPath, `${opts.name}.env`));
8042
8355
  }
8043
8356
  if (hasContainer) {
8044
- await fs15.cp(containerPath, path18.join(backupPath, "container"), {
8357
+ await fs15.cp(containerPath, path20.join(backupPath, "container"), {
8045
8358
  recursive: true
8046
8359
  });
8047
8360
  }
@@ -8130,15 +8443,15 @@ var init_remove = __esm({
8130
8443
  });
8131
8444
 
8132
8445
  // src/commands/remove.ts
8133
- import { defineCommand as defineCommand17 } from "citty";
8134
- import { consola as consola20 } from "consola";
8446
+ import { defineCommand as defineCommand18 } from "citty";
8447
+ import { consola as consola22 } from "consola";
8135
8448
  import { createInterface } from "readline/promises";
8136
8449
  var removeCommand;
8137
8450
  var init_remove2 = __esm({
8138
8451
  "src/commands/remove.ts"() {
8139
8452
  "use strict";
8140
8453
  init_remove();
8141
- removeCommand = defineCommand17({
8454
+ removeCommand = defineCommand18({
8142
8455
  meta: {
8143
8456
  name: "remove",
8144
8457
  group: "lifecycle",
@@ -8175,7 +8488,7 @@ var init_remove2 = __esm({
8175
8488
  const skipPrompt = args.yes === true;
8176
8489
  if (!skipPrompt) {
8177
8490
  const warning = noBackup ? `About to remove '${args.name}' WITHOUT a backup. Docker objects, container-configs entry, and container directory will all be deleted.` : `About to remove '${args.name}'. A backup will be written to container-backups/ first, then docker objects, container-configs entry, and container directory will all be deleted.`;
8178
- consola20.warn(warning);
8491
+ consola22.warn(warning);
8179
8492
  const rl = createInterface({
8180
8493
  input: process.stdin,
8181
8494
  output: process.stdout
@@ -8183,7 +8496,7 @@ var init_remove2 = __esm({
8183
8496
  const answer = await rl.question("Continue? [y/N] ");
8184
8497
  rl.close();
8185
8498
  if (!/^y(es)?$/i.test(answer.trim())) {
8186
- consola20.info("Aborted. Nothing changed.");
8499
+ consola22.info("Aborted. Nothing changed.");
8187
8500
  process.exit(0);
8188
8501
  }
8189
8502
  }
@@ -8192,7 +8505,7 @@ var init_remove2 = __esm({
8192
8505
  ...noBackup ? { noBackup: true } : {}
8193
8506
  });
8194
8507
  } catch (err) {
8195
- consola20.error(err instanceof Error ? err.message : String(err));
8508
+ consola22.error(err instanceof Error ? err.message : String(err));
8196
8509
  process.exit(1);
8197
8510
  }
8198
8511
  }
@@ -8201,17 +8514,17 @@ var init_remove2 = __esm({
8201
8514
  });
8202
8515
 
8203
8516
  // src/restore/index.ts
8204
- import { existsSync as existsSync11, promises as fs16 } from "fs";
8205
- import path19 from "path";
8206
- import { consola as consola21 } from "consola";
8517
+ import { existsSync as existsSync13, promises as fs16 } from "fs";
8518
+ import path21 from "path";
8519
+ import { consola as consola23 } from "consola";
8207
8520
  async function runRestore(opts) {
8208
8521
  const home = opts.monocerosHome ?? monocerosHome();
8209
8522
  const logger = opts.logger ?? {
8210
- info: (msg) => consola21.info(msg),
8211
- success: (msg) => consola21.success(msg)
8523
+ info: (msg) => consola23.info(msg),
8524
+ success: (msg) => consola23.success(msg)
8212
8525
  };
8213
- const backup = path19.resolve(opts.backupPath);
8214
- if (!existsSync11(backup)) {
8526
+ const backup = path21.resolve(opts.backupPath);
8527
+ if (!existsSync13(backup)) {
8215
8528
  throw new Error(`Backup not found: ${backup}.`);
8216
8529
  }
8217
8530
  const stat = await fs16.stat(backup);
@@ -8232,24 +8545,24 @@ async function runRestore(opts) {
8232
8545
  }
8233
8546
  const ymlFile = ymlFiles[0];
8234
8547
  const name = ymlFile.replace(/\.yml$/, "");
8235
- const containerInBackup = path19.join(backup, "container");
8236
- const hasContainer = existsSync11(containerInBackup);
8237
- const envInBackup = path19.join(backup, `${name}.env`);
8238
- const hasEnv = existsSync11(envInBackup);
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);
8239
8552
  const destYml = containerConfigPath(name, home);
8240
8553
  const destContainer = containerDir(name, home);
8241
- if (existsSync11(destYml)) {
8554
+ if (existsSync13(destYml)) {
8242
8555
  throw new Error(
8243
8556
  `Refusing to restore: ${destYml} already exists. Remove the current container first (\`monoceros remove ${name}\`) or rename the existing config.`
8244
8557
  );
8245
8558
  }
8246
- if (hasContainer && existsSync11(destContainer)) {
8559
+ if (hasContainer && existsSync13(destContainer)) {
8247
8560
  throw new Error(
8248
8561
  `Refusing to restore: ${destContainer} already exists. Remove the current container first (\`monoceros remove ${name}\`).`
8249
8562
  );
8250
8563
  }
8251
8564
  await fs16.mkdir(containerConfigsDir(home), { recursive: true });
8252
- await fs16.copyFile(path19.join(backup, ymlFile), destYml);
8565
+ await fs16.copyFile(path21.join(backup, ymlFile), destYml);
8253
8566
  if (hasEnv) {
8254
8567
  await fs16.copyFile(envInBackup, containerEnvPath(name, home));
8255
8568
  }
@@ -8274,14 +8587,14 @@ var init_restore = __esm({
8274
8587
  });
8275
8588
 
8276
8589
  // src/commands/restore.ts
8277
- import { defineCommand as defineCommand18 } from "citty";
8278
- import { consola as consola22 } from "consola";
8590
+ import { defineCommand as defineCommand19 } from "citty";
8591
+ import { consola as consola24 } from "consola";
8279
8592
  var restoreCommand;
8280
8593
  var init_restore2 = __esm({
8281
8594
  "src/commands/restore.ts"() {
8282
8595
  "use strict";
8283
8596
  init_restore();
8284
- restoreCommand = defineCommand18({
8597
+ restoreCommand = defineCommand19({
8285
8598
  meta: {
8286
8599
  name: "restore",
8287
8600
  group: "lifecycle",
@@ -8298,7 +8611,7 @@ var init_restore2 = __esm({
8298
8611
  try {
8299
8612
  await runRestore({ backupPath: args["backup-path"] });
8300
8613
  } catch (err) {
8301
- consola22.error(err instanceof Error ? err.message : String(err));
8614
+ consola24.error(err instanceof Error ? err.message : String(err));
8302
8615
  process.exit(1);
8303
8616
  }
8304
8617
  }
@@ -8307,14 +8620,14 @@ var init_restore2 = __esm({
8307
8620
  });
8308
8621
 
8309
8622
  // src/commands/remove-from-url.ts
8310
- import { defineCommand as defineCommand19 } from "citty";
8311
- import { consola as consola23 } from "consola";
8623
+ import { defineCommand as defineCommand20 } from "citty";
8624
+ import { consola as consola25 } from "consola";
8312
8625
  var removeFromUrlCommand;
8313
8626
  var init_remove_from_url = __esm({
8314
8627
  "src/commands/remove-from-url.ts"() {
8315
8628
  "use strict";
8316
8629
  init_modify();
8317
- removeFromUrlCommand = defineCommand19({
8630
+ removeFromUrlCommand = defineCommand20({
8318
8631
  meta: {
8319
8632
  name: "remove-from-url",
8320
8633
  group: "edit",
@@ -8347,7 +8660,7 @@ var init_remove_from_url = __esm({
8347
8660
  });
8348
8661
  process.exit(result.status === "aborted" ? 1 : 0);
8349
8662
  } catch (err) {
8350
- consola23.error(err instanceof Error ? err.message : String(err));
8663
+ consola25.error(err instanceof Error ? err.message : String(err));
8351
8664
  process.exit(1);
8352
8665
  }
8353
8666
  }
@@ -8356,14 +8669,14 @@ var init_remove_from_url = __esm({
8356
8669
  });
8357
8670
 
8358
8671
  // src/commands/remove-language.ts
8359
- import { defineCommand as defineCommand20 } from "citty";
8360
- import { consola as consola24 } from "consola";
8672
+ import { defineCommand as defineCommand21 } from "citty";
8673
+ import { consola as consola26 } from "consola";
8361
8674
  var removeLanguageCommand;
8362
8675
  var init_remove_language = __esm({
8363
8676
  "src/commands/remove-language.ts"() {
8364
8677
  "use strict";
8365
8678
  init_modify();
8366
- removeLanguageCommand = defineCommand20({
8679
+ removeLanguageCommand = defineCommand21({
8367
8680
  meta: {
8368
8681
  name: "remove-language",
8369
8682
  group: "edit",
@@ -8396,7 +8709,7 @@ var init_remove_language = __esm({
8396
8709
  });
8397
8710
  process.exit(result.status === "aborted" ? 1 : 0);
8398
8711
  } catch (err) {
8399
- consola24.error(err instanceof Error ? err.message : String(err));
8712
+ consola26.error(err instanceof Error ? err.message : String(err));
8400
8713
  process.exit(1);
8401
8714
  }
8402
8715
  }
@@ -8405,8 +8718,8 @@ var init_remove_language = __esm({
8405
8718
  });
8406
8719
 
8407
8720
  // src/commands/remove-port.ts
8408
- import { defineCommand as defineCommand21 } from "citty";
8409
- import { consola as consola25 } from "consola";
8721
+ import { defineCommand as defineCommand22 } from "citty";
8722
+ import { consola as consola27 } from "consola";
8410
8723
  function coerceToken2(raw) {
8411
8724
  const n = Number(raw);
8412
8725
  return Number.isFinite(n) ? n : raw;
@@ -8417,7 +8730,7 @@ var init_remove_port = __esm({
8417
8730
  "use strict";
8418
8731
  init_inner_args();
8419
8732
  init_modify();
8420
- removePortCommand = defineCommand21({
8733
+ removePortCommand = defineCommand22({
8421
8734
  meta: {
8422
8735
  name: "remove-port",
8423
8736
  group: "edit",
@@ -8439,7 +8752,7 @@ var init_remove_port = __esm({
8439
8752
  async run({ args }) {
8440
8753
  const tokens = [...getInnerArgs()];
8441
8754
  if (tokens.length === 0) {
8442
- consola25.error(
8755
+ consola27.error(
8443
8756
  "No ports given. Usage: `monoceros remove-port <containername> [--yes] -- <port> [<port> \u2026]`."
8444
8757
  );
8445
8758
  process.exit(1);
@@ -8452,7 +8765,7 @@ var init_remove_port = __esm({
8452
8765
  });
8453
8766
  process.exit(result.status === "aborted" ? 1 : 0);
8454
8767
  } catch (err) {
8455
- consola25.error(err instanceof Error ? err.message : String(err));
8768
+ consola27.error(err instanceof Error ? err.message : String(err));
8456
8769
  process.exit(1);
8457
8770
  }
8458
8771
  }
@@ -8461,14 +8774,14 @@ var init_remove_port = __esm({
8461
8774
  });
8462
8775
 
8463
8776
  // src/commands/remove-repo.ts
8464
- import { defineCommand as defineCommand22 } from "citty";
8465
- import { consola as consola26 } from "consola";
8777
+ import { defineCommand as defineCommand23 } from "citty";
8778
+ import { consola as consola28 } from "consola";
8466
8779
  var removeRepoCommand;
8467
8780
  var init_remove_repo = __esm({
8468
8781
  "src/commands/remove-repo.ts"() {
8469
8782
  "use strict";
8470
8783
  init_modify();
8471
- removeRepoCommand = defineCommand22({
8784
+ removeRepoCommand = defineCommand23({
8472
8785
  meta: {
8473
8786
  name: "remove-repo",
8474
8787
  group: "edit",
@@ -8501,7 +8814,7 @@ var init_remove_repo = __esm({
8501
8814
  });
8502
8815
  process.exit(result.status === "aborted" ? 1 : 0);
8503
8816
  } catch (err) {
8504
- consola26.error(err instanceof Error ? err.message : String(err));
8817
+ consola28.error(err instanceof Error ? err.message : String(err));
8505
8818
  process.exit(1);
8506
8819
  }
8507
8820
  }
@@ -8510,14 +8823,14 @@ var init_remove_repo = __esm({
8510
8823
  });
8511
8824
 
8512
8825
  // src/commands/remove-service.ts
8513
- import { defineCommand as defineCommand23 } from "citty";
8514
- import { consola as consola27 } from "consola";
8826
+ import { defineCommand as defineCommand24 } from "citty";
8827
+ import { consola as consola29 } from "consola";
8515
8828
  var removeServiceCommand;
8516
8829
  var init_remove_service = __esm({
8517
8830
  "src/commands/remove-service.ts"() {
8518
8831
  "use strict";
8519
8832
  init_modify();
8520
- removeServiceCommand = defineCommand23({
8833
+ removeServiceCommand = defineCommand24({
8521
8834
  meta: {
8522
8835
  name: "remove-service",
8523
8836
  group: "edit",
@@ -8550,7 +8863,7 @@ var init_remove_service = __esm({
8550
8863
  });
8551
8864
  process.exit(result.status === "aborted" ? 1 : 0);
8552
8865
  } catch (err) {
8553
- consola27.error(err instanceof Error ? err.message : String(err));
8866
+ consola29.error(err instanceof Error ? err.message : String(err));
8554
8867
  process.exit(1);
8555
8868
  }
8556
8869
  }
@@ -8558,44 +8871,6 @@ var init_remove_service = __esm({
8558
8871
  }
8559
8872
  });
8560
8873
 
8561
- // src/devcontainer/shell.ts
8562
- import { existsSync as existsSync12 } from "fs";
8563
- import path20 from "path";
8564
- async function runShell(opts) {
8565
- assertContainerExists(opts.root);
8566
- const spawnFn = opts.spawn ?? spawnDevcontainer;
8567
- const upCode = await spawnFn(
8568
- ["up", "--workspace-folder", opts.root, "--mount-workspace-git-root=false"],
8569
- opts.root,
8570
- { quiet: true }
8571
- );
8572
- if (upCode !== 0) return upCode;
8573
- return spawnFn(
8574
- [
8575
- "exec",
8576
- "--workspace-folder",
8577
- opts.root,
8578
- "--mount-workspace-git-root=false",
8579
- "bash"
8580
- ],
8581
- opts.root,
8582
- { interactive: true }
8583
- );
8584
- }
8585
- function assertContainerExists(root) {
8586
- if (!existsSync12(path20.join(root, ".devcontainer"))) {
8587
- throw new Error(
8588
- `No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
8589
- );
8590
- }
8591
- }
8592
- var init_shell = __esm({
8593
- "src/devcontainer/shell.ts"() {
8594
- "use strict";
8595
- init_cli();
8596
- }
8597
- });
8598
-
8599
8874
  // src/devcontainer/run.ts
8600
8875
  async function runInContainer(opts) {
8601
8876
  if (opts.command.length === 0) {
@@ -8640,8 +8915,8 @@ var init_run = __esm({
8640
8915
  });
8641
8916
 
8642
8917
  // src/commands/run.ts
8643
- import { defineCommand as defineCommand24 } from "citty";
8644
- import { consola as consola28 } from "consola";
8918
+ import { defineCommand as defineCommand25 } from "citty";
8919
+ import { consola as consola30 } from "consola";
8645
8920
  var runCommand;
8646
8921
  var init_run2 = __esm({
8647
8922
  "src/commands/run.ts"() {
@@ -8649,7 +8924,7 @@ var init_run2 = __esm({
8649
8924
  init_paths();
8650
8925
  init_run();
8651
8926
  init_inner_args();
8652
- runCommand = defineCommand24({
8927
+ runCommand = defineCommand25({
8653
8928
  meta: {
8654
8929
  name: "run",
8655
8930
  group: "run",
@@ -8669,7 +8944,7 @@ var init_run2 = __esm({
8669
8944
  async run({ args }) {
8670
8945
  const command = [...getInnerArgs()];
8671
8946
  if (command.length === 0) {
8672
- consola28.error(
8947
+ consola30.error(
8673
8948
  "No command provided. Usage: `monoceros run <containername> -- <cmd> [args\u2026]`."
8674
8949
  );
8675
8950
  process.exit(1);
@@ -8682,7 +8957,7 @@ var init_run2 = __esm({
8682
8957
  });
8683
8958
  process.exit(exitCode);
8684
8959
  } catch (err) {
8685
- consola28.error(err instanceof Error ? err.message : String(err));
8960
+ consola30.error(err instanceof Error ? err.message : String(err));
8686
8961
  process.exit(1);
8687
8962
  }
8688
8963
  }
@@ -8691,15 +8966,15 @@ var init_run2 = __esm({
8691
8966
  });
8692
8967
 
8693
8968
  // src/commands/shell.ts
8694
- import { defineCommand as defineCommand25 } from "citty";
8695
- import { consola as consola29 } from "consola";
8969
+ import { defineCommand as defineCommand26 } from "citty";
8970
+ import { consola as consola31 } from "consola";
8696
8971
  var shellCommand;
8697
8972
  var init_shell2 = __esm({
8698
8973
  "src/commands/shell.ts"() {
8699
8974
  "use strict";
8700
8975
  init_paths();
8701
8976
  init_shell();
8702
- shellCommand = defineCommand25({
8977
+ shellCommand = defineCommand26({
8703
8978
  meta: {
8704
8979
  name: "shell",
8705
8980
  group: "run",
@@ -8717,7 +8992,7 @@ var init_shell2 = __esm({
8717
8992
  const exitCode = await runShell({ root: containerDir(args.name) });
8718
8993
  process.exit(exitCode);
8719
8994
  } catch (err) {
8720
- consola29.error(err instanceof Error ? err.message : String(err));
8995
+ consola31.error(err instanceof Error ? err.message : String(err));
8721
8996
  process.exit(1);
8722
8997
  }
8723
8998
  }
@@ -8726,8 +9001,8 @@ var init_shell2 = __esm({
8726
9001
  });
8727
9002
 
8728
9003
  // src/commands/start.ts
8729
- import { defineCommand as defineCommand26 } from "citty";
8730
- import { consola as consola30 } from "consola";
9004
+ import { defineCommand as defineCommand27 } from "citty";
9005
+ import { consola as consola32 } from "consola";
8731
9006
  var startCommand;
8732
9007
  var init_start = __esm({
8733
9008
  "src/commands/start.ts"() {
@@ -8739,7 +9014,7 @@ var init_start = __esm({
8739
9014
  init_proxy();
8740
9015
  init_port_check();
8741
9016
  init_dispatch();
8742
- startCommand = defineCommand26({
9017
+ startCommand = defineCommand27({
8743
9018
  meta: {
8744
9019
  name: "start",
8745
9020
  group: "run",
@@ -8764,7 +9039,7 @@ var init_start = __esm({
8764
9039
  hostPort = proxyHostPort(global);
8765
9040
  }
8766
9041
  } catch (err) {
8767
- consola30.warn(
9042
+ consola32.warn(
8768
9043
  `Could not read container yml ahead of start: ${err instanceof Error ? err.message : String(err)}. Skipping Traefik pre-flight.`
8769
9044
  );
8770
9045
  }
@@ -8780,7 +9055,7 @@ var init_start = __esm({
8780
9055
  });
8781
9056
 
8782
9057
  // src/commands/status.ts
8783
- import { defineCommand as defineCommand27 } from "citty";
9058
+ import { defineCommand as defineCommand28 } from "citty";
8784
9059
  var statusCommand;
8785
9060
  var init_status = __esm({
8786
9061
  "src/commands/status.ts"() {
@@ -8788,7 +9063,7 @@ var init_status = __esm({
8788
9063
  init_paths();
8789
9064
  init_compose();
8790
9065
  init_dispatch();
8791
- statusCommand = defineCommand27({
9066
+ statusCommand = defineCommand28({
8792
9067
  meta: {
8793
9068
  name: "status",
8794
9069
  group: "run",
@@ -8818,8 +9093,8 @@ var init_status = __esm({
8818
9093
  });
8819
9094
 
8820
9095
  // src/commands/stop.ts
8821
- import { defineCommand as defineCommand28 } from "citty";
8822
- import { consola as consola31 } from "consola";
9096
+ import { defineCommand as defineCommand29 } from "citty";
9097
+ import { consola as consola33 } from "consola";
8823
9098
  var stopCommand;
8824
9099
  var init_stop = __esm({
8825
9100
  "src/commands/stop.ts"() {
@@ -8828,7 +9103,7 @@ var init_stop = __esm({
8828
9103
  init_compose();
8829
9104
  init_proxy();
8830
9105
  init_dispatch();
8831
- stopCommand = defineCommand28({
9106
+ stopCommand = defineCommand29({
8832
9107
  meta: {
8833
9108
  name: "stop",
8834
9109
  group: "run",
@@ -8853,10 +9128,10 @@ var init_stop = __esm({
8853
9128
  });
8854
9129
  try {
8855
9130
  await maybeStopProxy({
8856
- logger: { info: (msg) => consola31.info(msg) }
9131
+ logger: { info: (msg) => consola33.info(msg) }
8857
9132
  });
8858
9133
  } catch (err) {
8859
- consola31.warn(
9134
+ consola33.warn(
8860
9135
  `Could not tear down the Traefik proxy: ${err instanceof Error ? err.message : String(err)}. Ignored.`
8861
9136
  );
8862
9137
  }
@@ -8868,11 +9143,11 @@ var init_stop = __esm({
8868
9143
  });
8869
9144
 
8870
9145
  // src/tunnel/resolve.ts
8871
- import { existsSync as existsSync13 } from "fs";
8872
- import path21 from "path";
9146
+ import { existsSync as existsSync14 } from "fs";
9147
+ import path22 from "path";
8873
9148
  async function resolveTunnelTarget(opts) {
8874
9149
  const ymlPath = containerConfigPath(opts.name, opts.monocerosHome);
8875
- if (!existsSync13(ymlPath)) {
9150
+ if (!existsSync14(ymlPath)) {
8876
9151
  throw new Error(
8877
9152
  `No yml profile for '${opts.name}' at ${ymlPath}. Run \`monoceros init ${opts.name}\` first.`
8878
9153
  );
@@ -8880,13 +9155,13 @@ async function resolveTunnelTarget(opts) {
8880
9155
  const parsed = await readConfig(ymlPath);
8881
9156
  const config = parsed.config;
8882
9157
  const containerRoot = containerDir(opts.name, opts.monocerosHome);
8883
- if (!existsSync13(containerRoot)) {
9158
+ if (!existsSync14(containerRoot)) {
8884
9159
  throw new Error(
8885
9160
  `Container '${opts.name}' is not materialised at ${containerRoot}. Run \`monoceros apply ${opts.name}\` first.`
8886
9161
  );
8887
9162
  }
8888
- const composePath = path21.join(containerRoot, ".devcontainer", "compose.yaml");
8889
- const isCompose = existsSync13(composePath);
9163
+ const composePath = path22.join(containerRoot, ".devcontainer", "compose.yaml");
9164
+ const isCompose = existsSync14(composePath);
8890
9165
  const parsedTarget = parseTargetArg(opts.target, config);
8891
9166
  const docker = opts.docker ?? defaultDockerExec;
8892
9167
  if (isCompose) {
@@ -9121,8 +9396,8 @@ var init_port_check2 = __esm({
9121
9396
  });
9122
9397
 
9123
9398
  // src/tunnel/run.ts
9124
- import { spawn as spawn8 } from "child_process";
9125
- import { consola as consola32 } from "consola";
9399
+ import { spawn as spawn9 } from "child_process";
9400
+ import { consola as consola34 } from "consola";
9126
9401
  function signalNumber(signal) {
9127
9402
  switch (signal) {
9128
9403
  case "SIGINT":
@@ -9135,8 +9410,8 @@ function signalNumber(signal) {
9135
9410
  }
9136
9411
  async function runTunnel(opts) {
9137
9412
  const log = opts.logger ?? {
9138
- info: (m) => consola32.info(m),
9139
- warn: (m) => consola32.warn(m)
9413
+ info: (m) => consola34.info(m),
9414
+ warn: (m) => consola34.warn(m)
9140
9415
  };
9141
9416
  const resolve = opts.resolve ?? resolveTunnelTarget;
9142
9417
  const resolveArgs3 = {
@@ -9211,7 +9486,7 @@ var init_run3 = __esm({
9211
9486
  init_port_check2();
9212
9487
  SOCAT_IMAGE = "alpine/socat:1.8.0.3";
9213
9488
  defaultDockerSpawn = (args) => {
9214
- const child = spawn8("docker", args, {
9489
+ const child = spawn9("docker", args, {
9215
9490
  stdio: "inherit"
9216
9491
  });
9217
9492
  const exited = new Promise((resolve, reject) => {
@@ -9242,8 +9517,8 @@ var init_run3 = __esm({
9242
9517
  });
9243
9518
 
9244
9519
  // src/commands/tunnel.ts
9245
- import { defineCommand as defineCommand29 } from "citty";
9246
- import { consola as consola33 } from "consola";
9520
+ import { defineCommand as defineCommand30 } from "citty";
9521
+ import { consola as consola35 } from "consola";
9247
9522
  function parseLocalPort(raw) {
9248
9523
  if (raw === void 0) return void 0;
9249
9524
  const n = Number(raw);
@@ -9259,7 +9534,7 @@ var init_tunnel = __esm({
9259
9534
  "src/commands/tunnel.ts"() {
9260
9535
  "use strict";
9261
9536
  init_run3();
9262
- tunnelCommand = defineCommand29({
9537
+ tunnelCommand = defineCommand30({
9263
9538
  meta: {
9264
9539
  name: "tunnel",
9265
9540
  group: "discovery",
@@ -9296,7 +9571,7 @@ var init_tunnel = __esm({
9296
9571
  });
9297
9572
  process.exit(exitCode);
9298
9573
  } catch (err) {
9299
- consola33.error(err instanceof Error ? err.message : String(err));
9574
+ consola35.error(err instanceof Error ? err.message : String(err));
9300
9575
  process.exit(1);
9301
9576
  }
9302
9577
  }
@@ -9305,8 +9580,8 @@ var init_tunnel = __esm({
9305
9580
  });
9306
9581
 
9307
9582
  // src/upgrade/index.ts
9308
- import { existsSync as existsSync14, promises as fs17 } from "fs";
9309
- import { consola as consola34 } from "consola";
9583
+ import { existsSync as existsSync15, promises as fs17 } from "fs";
9584
+ import { consola as consola36 } from "consola";
9310
9585
  async function fetchRuntimeVersions() {
9311
9586
  const tokenUrl = `https://ghcr.io/token?service=ghcr.io&scope=repository:${RUNTIME_REPO}:pull`;
9312
9587
  const tokenRes = await fetch(tokenUrl);
@@ -9344,7 +9619,7 @@ ${yml}`;
9344
9619
  }
9345
9620
  async function runUpgrade(opts) {
9346
9621
  const home = opts.monocerosHome ?? monocerosHome();
9347
- const logger = opts.logger ?? consola34;
9622
+ const logger = opts.logger ?? consola36;
9348
9623
  const fetchVersions = opts.fetchVersions ?? fetchRuntimeVersions;
9349
9624
  if (opts.list) {
9350
9625
  const versions = await fetchVersions();
@@ -9364,7 +9639,7 @@ async function runUpgrade(opts) {
9364
9639
  );
9365
9640
  }
9366
9641
  const ymlPath = containerConfigPath(opts.name, home);
9367
- if (!existsSync14(ymlPath)) {
9642
+ if (!existsSync15(ymlPath)) {
9368
9643
  throw new Error(
9369
9644
  `No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
9370
9645
  );
@@ -9412,7 +9687,7 @@ var init_upgrade = __esm({
9412
9687
  });
9413
9688
 
9414
9689
  // src/commands/upgrade.ts
9415
- import { defineCommand as defineCommand30 } from "citty";
9690
+ import { defineCommand as defineCommand31 } from "citty";
9416
9691
  var upgradeCommand;
9417
9692
  var init_upgrade2 = __esm({
9418
9693
  "src/commands/upgrade.ts"() {
@@ -9420,7 +9695,7 @@ var init_upgrade2 = __esm({
9420
9695
  init_upgrade();
9421
9696
  init_version();
9422
9697
  init_dispatch();
9423
- upgradeCommand = defineCommand30({
9698
+ upgradeCommand = defineCommand31({
9424
9699
  meta: {
9425
9700
  name: "upgrade",
9426
9701
  group: "lifecycle",
@@ -9462,7 +9737,7 @@ var main_exports = {};
9462
9737
  __export(main_exports, {
9463
9738
  main: () => main
9464
9739
  });
9465
- import { defineCommand as defineCommand31 } from "citty";
9740
+ import { defineCommand as defineCommand32 } from "citty";
9466
9741
  var main;
9467
9742
  var init_main = __esm({
9468
9743
  "src/main.ts"() {
@@ -9479,6 +9754,7 @@ var init_main = __esm({
9479
9754
  init_complete();
9480
9755
  init_init2();
9481
9756
  init_list_components();
9757
+ init_login2();
9482
9758
  init_logs();
9483
9759
  init_port();
9484
9760
  init_remove_apt_packages();
@@ -9498,7 +9774,7 @@ var init_main = __esm({
9498
9774
  init_tunnel();
9499
9775
  init_upgrade2();
9500
9776
  init_version();
9501
- main = defineCommand31({
9777
+ main = defineCommand32({
9502
9778
  meta: {
9503
9779
  name: "monoceros",
9504
9780
  version: CLI_VERSION,
@@ -9509,6 +9785,7 @@ var init_main = __esm({
9509
9785
  "list-components": listComponentsCommand,
9510
9786
  shell: shellCommand,
9511
9787
  run: runCommand,
9788
+ login: loginCommand,
9512
9789
  logs: logsCommand,
9513
9790
  start: startCommand,
9514
9791
  stop: stopCommand,
@@ -9794,25 +10071,25 @@ function detectHelpRequest(argv, main2) {
9794
10071
  const separatorIdx = argv.indexOf("--");
9795
10072
  if (helpIdx === -1) return null;
9796
10073
  if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
9797
- const path22 = [];
10074
+ const path23 = [];
9798
10075
  const tokens = argv.slice(
9799
10076
  0,
9800
10077
  separatorIdx === -1 ? argv.length : separatorIdx
9801
10078
  );
9802
10079
  let cursor = main2;
9803
10080
  const mainName = (main2.meta ?? {}).name ?? "monoceros";
9804
- path22.push(mainName);
10081
+ path23.push(mainName);
9805
10082
  for (const tok of tokens) {
9806
10083
  if (tok.startsWith("-")) continue;
9807
10084
  const subs = cursor.subCommands ?? {};
9808
10085
  if (tok in subs) {
9809
10086
  cursor = subs[tok];
9810
- path22.push(tok);
10087
+ path23.push(tok);
9811
10088
  continue;
9812
10089
  }
9813
10090
  break;
9814
10091
  }
9815
- return { path: path22, cmd: cursor };
10092
+ return { path: path23, cmd: cursor };
9816
10093
  }
9817
10094
  async function maybeRenderHelp(argv, main2) {
9818
10095
  const hit = detectHelpRequest(argv, main2);