@sealant/mend 0.34.5 → 0.35.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.
Files changed (79) hide show
  1. package/dist/{chunk-5DORQXQT.js → chunk-SMH65F6Z.js} +25 -1
  2. package/dist/{dashboard-KZ4XGZ6W.js → dashboard-MGR4YUFD.js} +1 -1
  3. package/dist/main.js +343 -265
  4. package/man/mend-accounts.1 +1 -1
  5. package/man/mend-adopt.1 +1 -1
  6. package/man/mend-attach.1 +1 -1
  7. package/man/mend-codex.1 +1 -1
  8. package/man/mend-completions.1 +1 -1
  9. package/man/mend-connect.1 +8 -4
  10. package/man/mend-continue.1 +1 -1
  11. package/man/mend-doctor.1 +1 -1
  12. package/man/mend-dotfiles-repo.1 +1 -1
  13. package/man/mend-dotfiles-sync.1 +1 -1
  14. package/man/mend-dotfiles.1 +1 -1
  15. package/man/mend-env-cluster.1 +1 -1
  16. package/man/mend-env-load.1 +1 -1
  17. package/man/mend-env-show.1 +1 -1
  18. package/man/mend-folder-create.1 +1 -1
  19. package/man/mend-folder-list.1 +1 -1
  20. package/man/mend-folder-push.1 +1 -1
  21. package/man/mend-folder-rm.1 +1 -1
  22. package/man/mend-git-author.1 +1 -1
  23. package/man/mend-help.1 +1 -1
  24. package/man/mend-invite.1 +1 -1
  25. package/man/mend-keys-init.1 +1 -1
  26. package/man/mend-keys-mode.1 +1 -1
  27. package/man/mend-keys-share.1 +1 -1
  28. package/man/mend-keys-show.1 +1 -1
  29. package/man/mend-land.1 +1 -1
  30. package/man/mend-login.1 +1 -1
  31. package/man/mend-logout.1 +1 -1
  32. package/man/mend-man.1 +1 -1
  33. package/man/mend-members.1 +1 -1
  34. package/man/mend-operator-exposure.1 +1 -1
  35. package/man/mend-operator-gate.1 +1 -1
  36. package/man/mend-operator-grant-owner.1 +1 -1
  37. package/man/mend-operator-org-create.1 +1 -1
  38. package/man/mend-operator-org-invite-owner.1 +1 -1
  39. package/man/mend-operator-org-list.1 +1 -1
  40. package/man/mend-operator-org-rename.1 +1 -1
  41. package/man/mend-operator-reset-link.1 +1 -1
  42. package/man/mend-pair.1 +1 -1
  43. package/man/mend-projects.1 +1 -1
  44. package/man/mend-pull.1 +1 -1
  45. package/man/mend-refresh.1 +1 -1
  46. package/man/mend-rejoin.1 +1 -1
  47. package/man/mend-resume.1 +1 -1
  48. package/man/mend-run.1 +1 -1
  49. package/man/mend-server-logs.1 +1 -1
  50. package/man/mend-server-restart.1 +1 -1
  51. package/man/mend-server-setup.1 +1 -1
  52. package/man/mend-server-start.1 +1 -1
  53. package/man/mend-server-status.1 +1 -1
  54. package/man/mend-server-stop.1 +1 -1
  55. package/man/mend-server-upgrade.1 +1 -1
  56. package/man/mend-server.1 +1 -1
  57. package/man/mend-service-add.1 +1 -1
  58. package/man/mend-service-connect.1 +1 -1
  59. package/man/mend-service-init.1 +1 -1
  60. package/man/mend-service-list.1 +1 -1
  61. package/man/mend-service-logs.1 +1 -1
  62. package/man/mend-service-restart.1 +1 -1
  63. package/man/mend-service-run.1 +1 -1
  64. package/man/mend-service-stop.1 +1 -1
  65. package/man/mend-session-share.1 +1 -1
  66. package/man/mend-sessions.1 +1 -1
  67. package/man/mend-shell.1 +1 -1
  68. package/man/mend-skills-push.1 +1 -1
  69. package/man/mend-skills.1 +1 -1
  70. package/man/mend-snake.1 +1 -1
  71. package/man/mend-ssh-setup.1 +1 -1
  72. package/man/mend-ssh.1 +1 -1
  73. package/man/mend-stop.1 +1 -1
  74. package/man/mend-ui.1 +1 -1
  75. package/man/mend-uninstall.1 +1 -1
  76. package/man/mend-version.1 +1 -1
  77. package/man/mend-worktrees.1 +1 -1
  78. package/man/mend.1 +1 -1
  79. package/package.json +3 -3
package/dist/main.js CHANGED
@@ -34,15 +34,15 @@ import {
34
34
  sessionDisplayName,
35
35
  spoken,
36
36
  trackBracketedPaste
37
- } from "./chunk-5DORQXQT.js";
37
+ } from "./chunk-SMH65F6Z.js";
38
38
 
39
39
  // src/main.ts
40
- import { spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
41
- import * as fs15 from "node:fs";
40
+ import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
41
+ import * as fs16 from "node:fs";
42
42
  import { createRequire } from "node:module";
43
43
  import * as net4 from "node:net";
44
- import * as os10 from "node:os";
45
- import * as path16 from "node:path";
44
+ import * as os11 from "node:os";
45
+ import * as path17 from "node:path";
46
46
 
47
47
  // src/agent-share.ts
48
48
  import * as net from "node:net";
@@ -342,15 +342,83 @@ var readClipboardImage = async (platform = process.platform, env = process.env)
342
342
  return null;
343
343
  };
344
344
 
345
- // src/doctor-bundle-collectors.ts
346
- import * as fs3 from "node:fs";
345
+ // src/codex-grant.ts
346
+ import { spawnSync as spawnSync2 } from "node:child_process";
347
+ import * as fs2 from "node:fs";
347
348
  import * as os2 from "node:os";
348
- import * as path3 from "node:path";
349
+ import * as path2 from "node:path";
350
+ var codexCli = (environment = process.env) => ({
351
+ bin: environment["MEND_CODEX_BIN"] ?? "codex",
352
+ env: environment
353
+ });
354
+ var personalCodexHome = (environment = process.env) => {
355
+ const configured = environment["CODEX_HOME"];
356
+ return configured === void 0 || configured === "" ? path2.join(os2.homedir(), ".codex") : configured;
357
+ };
358
+ var runCodexDeviceLogin = (cli, codexHome) => {
359
+ const result = spawnSync2(cli.bin, ["login", "--device-auth"], {
360
+ stdio: "inherit",
361
+ env: { ...cli.env, CODEX_HOME: codexHome }
362
+ });
363
+ return result.error === void 0 && result.status === 0;
364
+ };
365
+ var refreshTokenOf = (authJson) => {
366
+ try {
367
+ const parsed = JSON.parse(authJson);
368
+ if (typeof parsed !== "object" || parsed === null) return null;
369
+ const tokens = parsed.tokens;
370
+ if (typeof tokens !== "object" || tokens === null) return null;
371
+ const refreshToken = tokens.refresh_token;
372
+ return typeof refreshToken === "string" && refreshToken !== "" ? refreshToken : null;
373
+ } catch {
374
+ return null;
375
+ }
376
+ };
377
+ var sameCodexLogin = (a, b) => {
378
+ const first = refreshTokenOf(a);
379
+ return first !== null && first === refreshTokenOf(b);
380
+ };
381
+ var codexGrant = (input) => {
382
+ const home = fs2.mkdtempSync(path2.join(os2.tmpdir(), "mend-codex-grant-"));
383
+ fs2.chmodSync(home, 448);
384
+ try {
385
+ input.say(" Mend needs its own Codex login; it is sent to your server and not kept here");
386
+ input.say(" your own Codex login stays as it is");
387
+ if (!runCodexDeviceLogin(input.cli, home)) {
388
+ return { kind: "failed", reason: "codex: the login did not complete" };
389
+ }
390
+ const file = path2.join(home, "auth.json");
391
+ if (!fs2.existsSync(file)) {
392
+ return { kind: "failed", reason: `codex: logged in, but no auth.json appeared in ${home}` };
393
+ }
394
+ const secret = fs2.readFileSync(file, "utf8").trim();
395
+ if (refreshTokenOf(secret) === null) {
396
+ return {
397
+ kind: "failed",
398
+ reason: "codex: the login wrote no ChatGPT session (an API key login has nothing to refresh)"
399
+ };
400
+ }
401
+ if (input.personalAuthJson !== null && sameCodexLogin(secret, input.personalAuthJson)) {
402
+ return {
403
+ kind: "failed",
404
+ reason: "codex: that is the same login this machine's Codex holds, so both sides would race on refresh \u2014 connect it deliberately with --use-my-login"
405
+ };
406
+ }
407
+ return { kind: "grant", secret };
408
+ } finally {
409
+ fs2.rmSync(home, { recursive: true, force: true });
410
+ }
411
+ };
412
+
413
+ // src/doctor-bundle-collectors.ts
414
+ import * as fs4 from "node:fs";
415
+ import * as os3 from "node:os";
416
+ import * as path4 from "node:path";
349
417
 
350
418
  // src/server-runtime.ts
351
419
  import { spawn } from "node:child_process";
352
- import * as fs2 from "node:fs";
353
- import * as path2 from "node:path";
420
+ import * as fs3 from "node:fs";
421
+ import * as path3 from "node:path";
354
422
  var serverProcessDeadlines = {
355
423
  ordinary: 3e4,
356
424
  pull: 10 * 6e4,
@@ -396,7 +464,7 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
396
464
  return new Promise((resolve7) => {
397
465
  let fd;
398
466
  try {
399
- if (options.stdoutFile !== void 0) fd = fs2.openSync(options.stdoutFile, "wx", 384);
467
+ if (options.stdoutFile !== void 0) fd = fs3.openSync(options.stdoutFile, "wx", 384);
400
468
  const child = spawn(command, [...args], {
401
469
  env,
402
470
  detached: process.platform !== "win32",
@@ -447,12 +515,12 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
447
515
  child.once("close", (status) => {
448
516
  globalThis.clearTimeout(timer);
449
517
  try {
450
- if (fd !== void 0) fs2.fsyncSync(fd);
518
+ if (fd !== void 0) fs3.fsyncSync(fd);
451
519
  } catch {
452
520
  failure2 ??= "Could not durably write process output.";
453
521
  } finally {
454
522
  try {
455
- if (fd !== void 0) fs2.closeSync(fd);
523
+ if (fd !== void 0) fs3.closeSync(fd);
456
524
  } catch {
457
525
  failure2 ??= "Could not close process output file.";
458
526
  }
@@ -466,7 +534,7 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
466
534
  });
467
535
  } catch (cause) {
468
536
  try {
469
- if (fd !== void 0) fs2.closeSync(fd);
537
+ if (fd !== void 0) fs3.closeSync(fd);
470
538
  } catch {
471
539
  }
472
540
  resolve7({
@@ -487,9 +555,9 @@ var serverComposeArgs = (installation, command) => [
487
555
  "--project-directory",
488
556
  installation.directory,
489
557
  "--env-file",
490
- path2.join(installation.directory, "server.env"),
558
+ path3.join(installation.directory, "server.env"),
491
559
  "-f",
492
- path2.join(installation.directory, "compose.yaml"),
560
+ path3.join(installation.directory, "compose.yaml"),
493
561
  ...command
494
562
  ];
495
563
 
@@ -535,11 +603,11 @@ var mapLimit = async (items, limit, work) => {
535
603
  return results;
536
604
  };
537
605
  var pathOf = (command, env = process.env) => {
538
- for (const directory of (env["PATH"] ?? "").split(path3.delimiter)) {
606
+ for (const directory of (env["PATH"] ?? "").split(path4.delimiter)) {
539
607
  if (directory === "") continue;
540
- const candidate = path3.join(directory, command);
608
+ const candidate = path4.join(directory, command);
541
609
  try {
542
- fs3.accessSync(candidate, fs3.constants.X_OK);
610
+ fs4.accessSync(candidate, fs4.constants.X_OK);
543
611
  return candidate;
544
612
  } catch {
545
613
  }
@@ -557,9 +625,9 @@ var cliCollector = (deps) => ({
557
625
  node: process.versions.node,
558
626
  platform: process.platform,
559
627
  arch: process.arch,
560
- osType: os2.type(),
561
- osRelease: os2.release(),
562
- hostname: os2.hostname(),
628
+ osType: os3.type(),
629
+ osRelease: os3.release(),
630
+ hostname: os3.hostname(),
563
631
  term: deps.env["TERM"] ?? null,
564
632
  shell: deps.env["SHELL"] ?? null,
565
633
  lang: deps.env["LANG"] ?? null,
@@ -1029,8 +1097,8 @@ var bundleCollectors = (deps) => {
1029
1097
  };
1030
1098
 
1031
1099
  // src/doctor-bundle.ts
1032
- import * as fs4 from "node:fs";
1033
- import * as path4 from "node:path";
1100
+ import * as fs5 from "node:fs";
1101
+ import * as path5 from "node:path";
1034
1102
  import { gzipSync } from "node:zlib";
1035
1103
 
1036
1104
  // src/help.ts
@@ -1079,19 +1147,20 @@ var COMMANDS = [
1079
1147
  summary: "send this machine's provider credential to the platform",
1080
1148
  synopsis: ["<claude|codex|github> [--use-my-login] [--from-stdin] [--remove]"],
1081
1149
  description: [
1082
- "Sessions run on the platform, so the platform needs your provider login. This reads the file the provider's own CLI wrote when you logged in (codex) or asks gh for its token (github) and stores it under your own user. Nothing is shared with other users.",
1083
- "Claude is different: Mend gets a login of its own. Claude rotates its refresh token, so two copies of one login fight and the one that refreshes second is signed out, and Mend refreshes on a schedule. So this logs in once against a directory Mend keeps, and your own Claude login stays as it is. Run it again whenever Mend says the grant expired."
1150
+ "Sessions run on the platform, so the platform needs your provider login. It is stored under your own user and used only for your work. Nothing is shared with other users.",
1151
+ "Claude and Codex get a login of Mend's own. Both rotate their refresh token, so two copies of one login fight and the one that refreshes second is signed out, and Mend's server refreshes on a schedule. So this logs in through the provider's own flow and your own login stays as it is: Claude against a directory Mend keeps, Codex with a device code, sent and not kept here. Run it again whenever Mend says a login needs reconnecting. GitHub sends gh's token."
1084
1152
  ],
1085
1153
  options: [
1086
1154
  {
1087
1155
  flag: "--use-my-login",
1088
- text: "claude: send the login this machine already uses; both sides then share one grant"
1156
+ text: "claude, codex: send the login this machine already uses; both sides then share it"
1089
1157
  },
1090
1158
  { flag: "--from-stdin", text: "paste a credential instead of reading the provider's file" },
1091
1159
  { flag: "--remove", text: "disconnect the provider" }
1092
1160
  ],
1093
1161
  examples: [
1094
1162
  { command: "mend connect claude", text: "a Claude login of Mend's own, through the browser" },
1163
+ { command: "mend connect codex", text: "a Codex login of Mend's own, with a device code" },
1095
1164
  { command: "mend connect github --remove", text: "" }
1096
1165
  ],
1097
1166
  see: ["accounts"]
@@ -2470,9 +2539,9 @@ var collectBundle = async (collectors) => {
2470
2539
  };
2471
2540
  var bundleFileName = (now) => `mend-bundle-${now.toISOString().replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-")}.tgz`;
2472
2541
  var writeBundle = (files, outPath, now) => {
2473
- const directory = path4.dirname(outPath);
2474
- fs4.mkdirSync(directory, { recursive: true, mode: 448 });
2475
- const root = path4.basename(outPath).replace(/\.(?:tgz|tar\.gz)$/, "");
2542
+ const directory = path5.dirname(outPath);
2543
+ fs5.mkdirSync(directory, { recursive: true, mode: 448 });
2544
+ const root = path5.basename(outPath).replace(/\.(?:tgz|tar\.gz)$/, "");
2476
2545
  const entries = files.map((file) => ({
2477
2546
  path: file.path,
2478
2547
  content: Buffer.from(file.content, "utf8")
@@ -2481,8 +2550,8 @@ var writeBundle = (files, outPath, now) => {
2481
2550
  entries.map((entry) => ({ path: `${root}/${entry.path}`, content: entry.content })),
2482
2551
  now
2483
2552
  );
2484
- fs4.writeFileSync(outPath, bytes, { mode: 384 });
2485
- fs4.chmodSync(outPath, 384);
2553
+ fs5.writeFileSync(outPath, bytes, { mode: 384 });
2554
+ fs5.chmodSync(outPath, 384);
2486
2555
  return {
2487
2556
  path: outPath,
2488
2557
  bytes: bytes.length,
@@ -2543,7 +2612,7 @@ var doctorBundleCommand = async (args, deps) => {
2543
2612
  return;
2544
2613
  }
2545
2614
  const now = deps.now();
2546
- const outPath = path4.resolve(parsed.args.out ?? path4.join(deps.defaultDir, bundleFileName(now)));
2615
+ const outPath = path5.resolve(parsed.args.out ?? path5.join(deps.defaultDir, bundleFileName(now)));
2547
2616
  deps.warn("collecting \xB7 this can take a minute");
2548
2617
  const files = await collectBundle(deps.collectors(parsed.args.tail));
2549
2618
  const written = writeBundle(files, outPath, now);
@@ -2551,14 +2620,14 @@ var doctorBundleCommand = async (args, deps) => {
2551
2620
  };
2552
2621
 
2553
2622
  // src/doctor.ts
2554
- import * as fs6 from "node:fs";
2555
- import * as path6 from "node:path";
2623
+ import * as fs7 from "node:fs";
2624
+ import * as path7 from "node:path";
2556
2625
 
2557
2626
  // src/docker-shutdown.ts
2558
- import { spawnSync as spawnSync2 } from "node:child_process";
2559
- import * as fs5 from "node:fs";
2560
- import * as os3 from "node:os";
2561
- import * as path5 from "node:path";
2627
+ import { spawnSync as spawnSync3 } from "node:child_process";
2628
+ import * as fs6 from "node:fs";
2629
+ import * as os4 from "node:os";
2630
+ import * as path6 from "node:path";
2562
2631
  var DOCKER_CAPTURE_STOP_GRACE_SECONDS = 3600;
2563
2632
  var DOCKERD_DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 15;
2564
2633
  var flagValue = (argv, name) => {
@@ -2583,12 +2652,12 @@ var daemonKindOf = (info) => {
2583
2652
  var defaultConfigPath = (facts, daemon) => {
2584
2653
  switch (daemon) {
2585
2654
  case "desktop":
2586
- return path5.join(facts.home, ".docker", "daemon.json");
2655
+ return path6.join(facts.home, ".docker", "daemon.json");
2587
2656
  case "orbstack":
2588
- return path5.join(facts.home, ".orbstack", "config", "docker.json");
2657
+ return path6.join(facts.home, ".orbstack", "config", "docker.json");
2589
2658
  case "rootless":
2590
- return path5.join(
2591
- facts.xdgConfigHome ?? path5.join(facts.home, ".config"),
2659
+ return path6.join(
2660
+ facts.xdgConfigHome ?? path6.join(facts.home, ".config"),
2592
2661
  "docker",
2593
2662
  "daemon.json"
2594
2663
  );
@@ -2717,7 +2786,7 @@ var parseDockerInfo = (stdout) => {
2717
2786
  };
2718
2787
  var readHostFile = (file) => {
2719
2788
  try {
2720
- return { kind: "read", text: fs5.readFileSync(file, "utf8") };
2789
+ return { kind: "read", text: fs6.readFileSync(file, "utf8") };
2721
2790
  } catch (cause) {
2722
2791
  return cause instanceof Error && "code" in cause && cause.code === "ENOENT" ? { kind: "absent" } : { kind: "unreadable" };
2723
2792
  }
@@ -2725,21 +2794,21 @@ var readHostFile = (file) => {
2725
2794
  var hostProcView = {
2726
2795
  list: (dir) => {
2727
2796
  try {
2728
- return fs5.readdirSync(dir);
2797
+ return fs6.readdirSync(dir);
2729
2798
  } catch {
2730
2799
  return null;
2731
2800
  }
2732
2801
  },
2733
2802
  read: (file) => {
2734
2803
  try {
2735
- return fs5.readFileSync(file, "utf8");
2804
+ return fs6.readFileSync(file, "utf8");
2736
2805
  } catch {
2737
2806
  return null;
2738
2807
  }
2739
2808
  },
2740
2809
  link: (file) => {
2741
2810
  try {
2742
- return fs5.readlinkSync(file);
2811
+ return fs6.readlinkSync(file);
2743
2812
  } catch {
2744
2813
  return null;
2745
2814
  }
@@ -2779,7 +2848,7 @@ var observeDockerd = (proc, host) => {
2779
2848
  const cmdline = proc.read(`/proc/${entry}/cmdline`);
2780
2849
  if (cmdline === null) continue;
2781
2850
  const argv = cmdline.split("\0").filter((arg) => arg !== "");
2782
- if (path5.basename(argv[0] ?? "") === "dockerd") daemons.push({ pid: entry, argv });
2851
+ if (path6.basename(argv[0] ?? "") === "dockerd") daemons.push({ pid: entry, argv });
2783
2852
  }
2784
2853
  if (daemons.length === 0) return { kind: "none" };
2785
2854
  const socket = unixSocketOf(host);
@@ -2839,7 +2908,7 @@ var observeDockerd = (proc, host) => {
2839
2908
  var hostDockerEndpoint = () => {
2840
2909
  const fromEnv = process.env["DOCKER_HOST"];
2841
2910
  if (fromEnv !== void 0 && fromEnv.trim() !== "") return fromEnv.trim();
2842
- const context = spawnSync2(
2911
+ const context = spawnSync3(
2843
2912
  "docker",
2844
2913
  ["context", "inspect", "--format", "{{.Endpoints.docker.Host}}"],
2845
2914
  {
@@ -2865,11 +2934,11 @@ var hostDockerDaemonFacts = (infoStdout) => ({
2865
2934
  info: infoStdout === null ? null : parseDockerInfo(infoStdout),
2866
2935
  ...dockerdFactsOf(observeDockerd(hostProcView, hostDockerEndpoint())),
2867
2936
  readFile: readHostFile,
2868
- home: os3.homedir(),
2937
+ home: os4.homedir(),
2869
2938
  xdgConfigHome: process.env["XDG_CONFIG_HOME"] ?? null
2870
2939
  });
2871
2940
  var observeHostShutdownTimeout = () => {
2872
- const info = spawnSync2("docker", ["info", "--format", "{{json .}}"], {
2941
+ const info = spawnSync3("docker", ["info", "--format", "{{json .}}"], {
2873
2942
  encoding: "utf8",
2874
2943
  timeout: 3e3,
2875
2944
  stdio: ["ignore", "pipe", "ignore"]
@@ -3028,7 +3097,8 @@ var runChecks = async (config, probes) => {
3028
3097
  } : {
3029
3098
  label: provider,
3030
3099
  state: "todo",
3031
- detail: account.status,
3100
+ // A refused refresh ends the login (docs/adr/0008): say what happened, not the enum.
3101
+ detail: account.status === "invalid" ? "reconnect needed \xB7 the provider refused the login" : account.status,
3032
3102
  fix: `mend connect ${provider}`
3033
3103
  }
3034
3104
  );
@@ -3098,10 +3168,10 @@ var runChecks = async (config, probes) => {
3098
3168
  return checks;
3099
3169
  };
3100
3170
  var onPath = (command) => {
3101
- for (const directory of (process.env["PATH"] ?? "").split(path6.delimiter)) {
3171
+ for (const directory of (process.env["PATH"] ?? "").split(path7.delimiter)) {
3102
3172
  if (directory === "") continue;
3103
3173
  try {
3104
- fs6.accessSync(path6.join(directory, command), fs6.constants.X_OK);
3174
+ fs7.accessSync(path7.join(directory, command), fs7.constants.X_OK);
3105
3175
  return true;
3106
3176
  } catch {
3107
3177
  }
@@ -3126,8 +3196,8 @@ var doctorCommand = async (config, localCredential2, claudeGrant2) => {
3126
3196
  };
3127
3197
 
3128
3198
  // src/dotfiles.ts
3129
- import * as fs7 from "node:fs";
3130
- import * as path7 from "node:path";
3199
+ import * as fs8 from "node:fs";
3200
+ import * as path8 from "node:path";
3131
3201
  var DOTFILE_CANDIDATES = [
3132
3202
  {
3133
3203
  group: "shell",
@@ -3170,7 +3240,7 @@ var DOTFILE_CANDIDATES = [
3170
3240
  var MAX_FILE_BYTES = 1024 * 1024;
3171
3241
  var statFile = (home, relative4) => {
3172
3242
  try {
3173
- const stat = fs7.statSync(path7.join(home, relative4));
3243
+ const stat = fs8.statSync(path8.join(home, relative4));
3174
3244
  return stat.isFile() ? stat : null;
3175
3245
  } catch {
3176
3246
  return null;
@@ -3183,9 +3253,9 @@ var scanDotfileCandidates = (home) => DOTFILE_CANDIDATES.flatMap(
3183
3253
  })
3184
3254
  );
3185
3255
  var homeRelativePath = (home, requested) => {
3186
- const relative4 = path7.relative(home, path7.resolve(home, requested));
3187
- const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative4);
3188
- return outside ? null : relative4.split(path7.sep).join("/");
3256
+ const relative4 = path8.relative(home, path8.resolve(home, requested));
3257
+ const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${path8.sep}`) || path8.isAbsolute(relative4);
3258
+ return outside ? null : relative4.split(path8.sep).join("/");
3189
3259
  };
3190
3260
  var readSyncFiles = (home, paths_) => {
3191
3261
  const files = [];
@@ -3205,7 +3275,7 @@ var readSyncFiles = (home, paths_) => {
3205
3275
  }
3206
3276
  files.push({
3207
3277
  path: relative4,
3208
- contentsBase64: fs7.readFileSync(path7.join(home, relative4)).toString("base64"),
3278
+ contentsBase64: fs8.readFileSync(path8.join(home, relative4)).toString("base64"),
3209
3279
  // eslint-disable-next-line no-bitwise -- permission bits are the point
3210
3280
  mode: (stat.mode & 511).toString(8)
3211
3281
  });
@@ -3304,10 +3374,10 @@ var parseGitAuthorArgs = (args) => {
3304
3374
  var gitAuthorLine = (author) => `${author.name} <${author.email}> \xB7 ${author.source === "setting" ? "your setting" : "your account's name and email"}`;
3305
3375
 
3306
3376
  // src/landing.ts
3307
- import { spawnSync as spawnSync3 } from "node:child_process";
3308
- import * as fs8 from "node:fs";
3309
- import * as os4 from "node:os";
3310
- import * as path8 from "node:path";
3377
+ import { spawnSync as spawnSync4 } from "node:child_process";
3378
+ import * as fs9 from "node:fs";
3379
+ import * as os5 from "node:os";
3380
+ import * as path9 from "node:path";
3311
3381
  var paint = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
3312
3382
  var dim = paint("2");
3313
3383
  var green = paint("32");
@@ -3522,7 +3592,7 @@ var landCommand = async (api2, args) => {
3522
3592
  if (!landingSucceeded(report)) process.exitCode = 1;
3523
3593
  };
3524
3594
  var git = (cwd, args) => {
3525
- const result = spawnSync3("git", args, { cwd, encoding: "utf8" });
3595
+ const result = spawnSync4("git", args, { cwd, encoding: "utf8" });
3526
3596
  return {
3527
3597
  status: result.status ?? 1,
3528
3598
  stdout: result.stdout ?? "",
@@ -3545,9 +3615,9 @@ var gitRemotes = (cwd) => {
3545
3615
  var remoteForOrigin = (remotes, originUrl, cwd) => {
3546
3616
  if (originUrl === null) return null;
3547
3617
  const origin = normalizeRemoteUrl(originUrl);
3548
- const originPath = origin === null ? path8.resolve(originUrl.replace(/^file:\/\//, "")) : null;
3618
+ const originPath = origin === null ? path9.resolve(originUrl.replace(/^file:\/\//, "")) : null;
3549
3619
  return remotes.find(
3550
- (remote) => origin === null ? path8.resolve(cwd, remote.url.replace(/^file:\/\//, "")) === originPath : normalizeRemoteUrl(remote.url) === origin
3620
+ (remote) => origin === null ? path9.resolve(cwd, remote.url.replace(/^file:\/\//, "")) === originPath : normalizeRemoteUrl(remote.url) === origin
3551
3621
  ) ?? null;
3552
3622
  };
3553
3623
  var LOG_LINES = 10;
@@ -3570,10 +3640,10 @@ var fetchBundle = (cwd, bundle) => {
3570
3640
  const before = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
3571
3641
  const previous = before.status === 0 ? before.stdout.trim() : null;
3572
3642
  if (previous !== bundle.tip) {
3573
- const dir = fs8.mkdtempSync(path8.join(os4.tmpdir(), "mend-pull-"));
3643
+ const dir = fs9.mkdtempSync(path9.join(os5.tmpdir(), "mend-pull-"));
3574
3644
  try {
3575
- const file = path8.join(dir, "change.bundle");
3576
- fs8.writeFileSync(file, bundle.bytes, { mode: 384 });
3645
+ const file = path9.join(dir, "change.bundle");
3646
+ fs9.writeFileSync(file, bundle.bytes, { mode: 384 });
3577
3647
  const fetched = git(cwd, [
3578
3648
  "fetch",
3579
3649
  "--no-tags",
@@ -3583,7 +3653,7 @@ var fetchBundle = (cwd, bundle) => {
3583
3653
  ]);
3584
3654
  if (fetched.status !== 0) return refusedWith(gitWords(fetched));
3585
3655
  } finally {
3586
- fs8.rmSync(dir, { recursive: true, force: true });
3656
+ fs9.rmSync(dir, { recursive: true, force: true });
3587
3657
  }
3588
3658
  }
3589
3659
  const after = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]).stdout.trim();
@@ -3774,7 +3844,7 @@ var followStart = async (options) => {
3774
3844
 
3775
3845
  // src/login.ts
3776
3846
  import { spawn as spawn2 } from "node:child_process";
3777
- import * as os5 from "node:os";
3847
+ import * as os6 from "node:os";
3778
3848
 
3779
3849
  // src/pair.ts
3780
3850
  var paint2 = (code) => (text) => process.stdout.isTTY === true ? `\x1B[${code}m${text}\x1B[0m` : text;
@@ -3966,7 +4036,7 @@ var resolveServerUrl = async (args, deps) => {
3966
4036
  };
3967
4037
  var loginCommand = async (args, deps) => {
3968
4038
  const base = await resolveServerUrl(args, deps);
3969
- const started = await post(`${base}/api/cli/auth`, { name: os5.hostname() });
4039
+ const started = await post(`${base}/api/cli/auth`, { name: os6.hostname() });
3970
4040
  if (started.status === 429) {
3971
4041
  return fail3(
3972
4042
  `the server asked for a pause \u2014 try again in ${retryAfterSecondsOf(started.json)}s`
@@ -4017,8 +4087,8 @@ var loginCommand = async (args, deps) => {
4017
4087
  };
4018
4088
 
4019
4089
  // src/organization.ts
4020
- import * as fs9 from "node:fs";
4021
- import * as path9 from "node:path";
4090
+ import * as fs10 from "node:fs";
4091
+ import * as path10 from "node:path";
4022
4092
  var paint4 = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
4023
4093
  var dim4 = paint4("2");
4024
4094
  var green4 = paint4("32");
@@ -4082,7 +4152,7 @@ var scanFolder = (dir) => {
4082
4152
  const files = [];
4083
4153
  const skipped = [];
4084
4154
  const walk = (relative4) => {
4085
- const entries = fs9.readdirSync(path9.join(dir, relative4), { withFileTypes: true }).toSorted((a, b) => a.name.localeCompare(b.name));
4155
+ const entries = fs10.readdirSync(path10.join(dir, relative4), { withFileTypes: true }).toSorted((a, b) => a.name.localeCompare(b.name));
4086
4156
  for (const entry of entries) {
4087
4157
  const child = relative4 === "" ? entry.name : `${relative4}/${entry.name}`;
4088
4158
  if (entry.isSymbolicLink()) {
@@ -4091,7 +4161,7 @@ var scanFolder = (dir) => {
4091
4161
  if (SKIPPED_DIRECTORIES.has(entry.name)) skipped.push({ path: child, reason: "skipped" });
4092
4162
  else walk(child);
4093
4163
  } else if (entry.isFile()) {
4094
- const size = fs9.statSync(path9.join(dir, child)).size;
4164
+ const size = fs10.statSync(path10.join(dir, child)).size;
4095
4165
  if (size > FOLDER_FILE_BYTES) skipped.push({ path: child, reason: "over 1 MiB" });
4096
4166
  else files.push({ path: child, size });
4097
4167
  }
@@ -4144,7 +4214,7 @@ var folderCommand = async (api2, args) => {
4144
4214
  }
4145
4215
  case "push": {
4146
4216
  if (dir === void 0) return fail4("usage: mend folder push <name> <dir> [--replace]");
4147
- if (!fs9.existsSync(dir) || !fs9.statSync(dir).isDirectory()) {
4217
+ if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
4148
4218
  return fail4(`${dir} is not a directory`);
4149
4219
  }
4150
4220
  const folder = await folderNamed(api2, name);
@@ -4159,7 +4229,7 @@ var folderCommand = async (api2, args) => {
4159
4229
  files: batch.map((file) => ({
4160
4230
  path: file.path,
4161
4231
  // Read one batch at a time, so a large tree never sits in memory at once.
4162
- contentsBase64: fs9.readFileSync(path9.join(dir, file.path)).toString("base64")
4232
+ contentsBase64: fs10.readFileSync(path10.join(dir, file.path)).toString("base64")
4163
4233
  })),
4164
4234
  // --replace empties the folder with the first batch; the rest add to it.
4165
4235
  merge: !(replace && index === 0)
@@ -4343,8 +4413,8 @@ var operatorCommand = async (api2, tryApi, baseUrl, args) => {
4343
4413
  import { createHmac, randomBytes } from "node:crypto";
4344
4414
  import { readFile } from "node:fs/promises";
4345
4415
  import * as net2 from "node:net";
4346
- import * as os7 from "node:os";
4347
- import * as path11 from "node:path";
4416
+ import * as os8 from "node:os";
4417
+ import * as path12 from "node:path";
4348
4418
 
4349
4419
  // src/server-docker-volumes.ts
4350
4420
  import { createHash as createHash2 } from "node:crypto";
@@ -4562,9 +4632,9 @@ var verifyServerDockerVolumes = (runtime, input) => ownership(runtime, input, "v
4562
4632
 
4563
4633
  // src/server-store.ts
4564
4634
  import { randomUUID } from "node:crypto";
4565
- import * as fs10 from "node:fs";
4566
- import * as os6 from "node:os";
4567
- import * as path10 from "node:path";
4635
+ import * as fs11 from "node:fs";
4636
+ import * as os7 from "node:os";
4637
+ import * as path11 from "node:path";
4568
4638
  var ServerStoreError = class extends Error {
4569
4639
  _tag = "ServerStoreError";
4570
4640
  };
@@ -4585,28 +4655,28 @@ var attempt = (operation) => {
4585
4655
  var hasCode = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
4586
4656
  var readOptional = (file) => {
4587
4657
  try {
4588
- return fs10.readFileSync(file, "utf8");
4658
+ return fs11.readFileSync(file, "utf8");
4589
4659
  } catch (cause) {
4590
4660
  if (hasCode(cause, "ENOENT")) return null;
4591
4661
  throw cause;
4592
4662
  }
4593
4663
  };
4594
4664
  var syncDirectory = (directory) => {
4595
- const fd = fs10.openSync(directory, "r");
4665
+ const fd = fs11.openSync(directory, "r");
4596
4666
  try {
4597
- fs10.fsyncSync(fd);
4667
+ fs11.fsyncSync(fd);
4598
4668
  } finally {
4599
- fs10.closeSync(fd);
4669
+ fs11.closeSync(fd);
4600
4670
  }
4601
4671
  };
4602
4672
  var writeDurable = (file, content, mode = 384) => {
4603
- const fd = fs10.openSync(file, "wx", mode);
4673
+ const fd = fs11.openSync(file, "wx", mode);
4604
4674
  try {
4605
- fs10.writeFileSync(fd, content, "utf8");
4606
- fs10.fchmodSync(fd, mode);
4607
- fs10.fsyncSync(fd);
4675
+ fs11.writeFileSync(fd, content, "utf8");
4676
+ fs11.fchmodSync(fd, mode);
4677
+ fs11.fsyncSync(fd);
4608
4678
  } finally {
4609
- fs10.closeSync(fd);
4679
+ fs11.closeSync(fd);
4610
4680
  }
4611
4681
  };
4612
4682
  var fileKeys = ["identity", "config", "env", "compose", "postgresInit"];
@@ -4626,11 +4696,11 @@ var parseLockOwner = (raw) => {
4626
4696
  };
4627
4697
  var describeOwner = (lockDir) => {
4628
4698
  try {
4629
- const raw = JSON.parse(fs10.readFileSync(path10.join(lockDir, "owner.json"), "utf8"));
4699
+ const raw = JSON.parse(fs11.readFileSync(path11.join(lockDir, "owner.json"), "utf8"));
4630
4700
  const owner = parseLockOwner(raw);
4631
4701
  if (owner === null) return "owner metadata is invalid";
4632
4702
  const label = `PID ${owner.pid} on ${owner.hostname}`;
4633
- if (owner.hostname !== os6.hostname()) return label;
4703
+ if (owner.hostname !== os7.hostname()) return label;
4634
4704
  try {
4635
4705
  process.kill(owner.pid, 0);
4636
4706
  return `${label} is still live`;
@@ -4646,28 +4716,28 @@ var lockGuidance = (lockDir) => new ServerStoreError(
4646
4716
  );
4647
4717
  var acquireLock = (configDir, create) => {
4648
4718
  if (create) {
4649
- fs10.mkdirSync(configDir, { recursive: true, mode: 448 });
4650
- fs10.chmodSync(configDir, 448);
4651
- } else if (!fs10.existsSync(configDir)) {
4719
+ fs11.mkdirSync(configDir, { recursive: true, mode: 448 });
4720
+ fs11.chmodSync(configDir, 448);
4721
+ } else if (!fs11.existsSync(configDir)) {
4652
4722
  throw new ServerStoreError(
4653
4723
  "No Mend server is configured. Run mend server setup explicitly to install one."
4654
4724
  );
4655
4725
  }
4656
- const lockDir = path10.join(configDir, "server.lock");
4726
+ const lockDir = path11.join(configDir, "server.lock");
4657
4727
  try {
4658
- fs10.mkdirSync(lockDir, { mode: 448 });
4728
+ fs11.mkdirSync(lockDir, { mode: 448 });
4659
4729
  } catch (cause) {
4660
4730
  if (hasCode(cause, "EEXIST")) throw lockGuidance(lockDir);
4661
4731
  throw cause;
4662
4732
  }
4663
- const owned = fs10.statSync(lockDir);
4664
- const ownerFile = path10.join(lockDir, "owner.json");
4665
- const owner = `${JSON.stringify({ pid: process.pid, hostname: os6.hostname(), token: randomUUID() })}
4733
+ const owned = fs11.statSync(lockDir);
4734
+ const ownerFile = path11.join(lockDir, "owner.json");
4735
+ const owner = `${JSON.stringify({ pid: process.pid, hostname: os7.hostname(), token: randomUUID() })}
4666
4736
  `;
4667
4737
  let open = true;
4668
4738
  const assertOwned = () => {
4669
- const current = fs10.statSync(lockDir);
4670
- if (!open || current.dev !== owned.dev || current.ino !== owned.ino || fs10.readFileSync(ownerFile, "utf8") !== owner) {
4739
+ const current = fs11.statSync(lockDir);
4740
+ if (!open || current.dev !== owned.dev || current.ino !== owned.ino || fs11.readFileSync(ownerFile, "utf8") !== owner) {
4671
4741
  throw new ServerStoreError(
4672
4742
  "Server lock ownership was lost. Stop and check for another server command before retrying."
4673
4743
  );
@@ -4683,8 +4753,8 @@ var acquireLock = (configDir, create) => {
4683
4753
  release: () => {
4684
4754
  const result = attempt(() => {
4685
4755
  assertOwned();
4686
- fs10.unlinkSync(ownerFile);
4687
- fs10.rmdirSync(lockDir);
4756
+ fs11.unlinkSync(ownerFile);
4757
+ fs11.rmdirSync(lockDir);
4688
4758
  });
4689
4759
  open = false;
4690
4760
  return result;
@@ -4693,7 +4763,7 @@ var acquireLock = (configDir, create) => {
4693
4763
  };
4694
4764
  var readIdentity = (paths) => {
4695
4765
  const identity = readOptional(paths.identity);
4696
- if (identity === null && (fs10.existsSync(paths.active) || fs10.existsSync(paths.generations) && fs10.readdirSync(paths.generations).length > 0)) {
4766
+ if (identity === null && (fs11.existsSync(paths.active) || fs11.existsSync(paths.generations) && fs11.readdirSync(paths.generations).length > 0)) {
4697
4767
  throw new ServerStoreError(
4698
4768
  "Server identity.env is missing. Restore it from a retained generation or backup; refusing to generate replacement credentials."
4699
4769
  );
@@ -4703,7 +4773,7 @@ var readIdentity = (paths) => {
4703
4773
  var readActive = (paths) => {
4704
4774
  let target;
4705
4775
  try {
4706
- target = fs10.readlinkSync(paths.active);
4776
+ target = fs11.readlinkSync(paths.active);
4707
4777
  } catch (cause) {
4708
4778
  if (hasCode(cause, "ENOENT")) return null;
4709
4779
  throw cause;
@@ -4713,13 +4783,13 @@ var readActive = (paths) => {
4713
4783
  "Server active pointer is invalid. Restore the symlink to a retained generation before retrying."
4714
4784
  );
4715
4785
  }
4716
- const directory = path10.join(paths.configDir, target);
4786
+ const directory = path11.join(paths.configDir, target);
4717
4787
  const files = {
4718
- identity: fs10.readFileSync(path10.join(directory, fileNames.identity), "utf8"),
4719
- config: fs10.readFileSync(path10.join(directory, fileNames.config), "utf8"),
4720
- env: fs10.readFileSync(path10.join(directory, fileNames.env), "utf8"),
4721
- compose: fs10.readFileSync(path10.join(directory, fileNames.compose), "utf8"),
4722
- postgresInit: fs10.readFileSync(path10.join(directory, fileNames.postgresInit), "utf8")
4788
+ identity: fs11.readFileSync(path11.join(directory, fileNames.identity), "utf8"),
4789
+ config: fs11.readFileSync(path11.join(directory, fileNames.config), "utf8"),
4790
+ env: fs11.readFileSync(path11.join(directory, fileNames.env), "utf8"),
4791
+ compose: fs11.readFileSync(path11.join(directory, fileNames.compose), "utf8"),
4792
+ postgresInit: fs11.readFileSync(path11.join(directory, fileNames.postgresInit), "utf8")
4723
4793
  };
4724
4794
  if (readIdentity(paths) !== files.identity) {
4725
4795
  throw new ServerStoreError(
@@ -4729,20 +4799,20 @@ var readActive = (paths) => {
4729
4799
  return { directory, files };
4730
4800
  };
4731
4801
  var publishIdentity = (paths, identity) => {
4732
- const temporary = path10.join(paths.configDir, `.identity-${randomUUID()}`);
4802
+ const temporary = path11.join(paths.configDir, `.identity-${randomUUID()}`);
4733
4803
  writeDurable(temporary, identity);
4734
- fs10.linkSync(temporary, paths.identity);
4804
+ fs11.linkSync(temporary, paths.identity);
4735
4805
  syncDirectory(paths.configDir);
4736
- fs10.unlinkSync(temporary);
4806
+ fs11.unlinkSync(temporary);
4737
4807
  };
4738
4808
  var prepareGeneration = (paths, files) => {
4739
- fs10.mkdirSync(paths.generations, { recursive: true, mode: 448 });
4809
+ fs11.mkdirSync(paths.generations, { recursive: true, mode: 448 });
4740
4810
  const generationName = `gen-${randomUUID()}`;
4741
- const directory = path10.join(paths.generations, generationName);
4742
- fs10.mkdirSync(directory, { mode: 448 });
4811
+ const directory = path11.join(paths.generations, generationName);
4812
+ fs11.mkdirSync(directory, { mode: 448 });
4743
4813
  for (const key of fileKeys) {
4744
4814
  writeDurable(
4745
- path10.join(directory, fileNames[key]),
4815
+ path11.join(directory, fileNames[key]),
4746
4816
  files[key],
4747
4817
  key === "postgresInit" ? 493 : 384
4748
4818
  );
@@ -4753,18 +4823,18 @@ var prepareGeneration = (paths, files) => {
4753
4823
  return { directory, files };
4754
4824
  };
4755
4825
  var activateGeneration = (paths, generation) => {
4756
- const relative4 = path10.relative(paths.configDir, generation.directory);
4757
- if (!/^generations\/gen-[0-9a-f-]{36}$/.test(relative4) || readIdentity(paths) !== fs10.readFileSync(path10.join(generation.directory, "identity.env"), "utf8")) {
4826
+ const relative4 = path11.relative(paths.configDir, generation.directory);
4827
+ if (!/^generations\/gen-[0-9a-f-]{36}$/.test(relative4) || readIdentity(paths) !== fs11.readFileSync(path11.join(generation.directory, "identity.env"), "utf8")) {
4758
4828
  throw new ServerStoreError("Cannot activate a generation outside this installation identity.");
4759
4829
  }
4760
4830
  for (const key of fileKeys) {
4761
- if (fs10.readFileSync(path10.join(generation.directory, fileNames[key]), "utf8") !== generation.files[key]) {
4831
+ if (fs11.readFileSync(path11.join(generation.directory, fileNames[key]), "utf8") !== generation.files[key]) {
4762
4832
  throw new ServerStoreError("Cannot activate an incomplete or changed server generation.");
4763
4833
  }
4764
4834
  }
4765
- const pointer = path10.join(paths.configDir, `.active-${randomUUID()}`);
4766
- fs10.symlinkSync(relative4, pointer);
4767
- fs10.renameSync(pointer, paths.active);
4835
+ const pointer = path11.join(paths.configDir, `.active-${randomUUID()}`);
4836
+ fs11.symlinkSync(relative4, pointer);
4837
+ fs11.renameSync(pointer, paths.active);
4768
4838
  syncDirectory(paths.configDir);
4769
4839
  };
4770
4840
  var prepareFiles = (paths, files) => {
@@ -4773,7 +4843,7 @@ var prepareFiles = (paths, files) => {
4773
4843
  throw new ServerStoreError("Server identity already exists; refusing to replace credentials.");
4774
4844
  }
4775
4845
  const active = readActive(paths);
4776
- if (active !== null && fileKeys.every((key) => files[key] === active.files[key]) && (fs10.statSync(path10.join(active.directory, fileNames.postgresInit)).mode & 4095) === 493)
4846
+ if (active !== null && fileKeys.every((key) => files[key] === active.files[key]) && (fs11.statSync(path11.join(active.directory, fileNames.postgresInit)).mode & 4095) === 493)
4777
4847
  return active;
4778
4848
  if (identity === null) publishIdentity(paths, files.identity);
4779
4849
  return prepareGeneration(paths, files);
@@ -4786,9 +4856,9 @@ var commitGeneration = (paths, files) => {
4786
4856
  var createStore = (configDir, lock) => {
4787
4857
  const paths = {
4788
4858
  configDir,
4789
- identity: path10.join(configDir, "identity.env"),
4790
- generations: path10.join(configDir, "generations"),
4791
- active: path10.join(configDir, "active")
4859
+ identity: path11.join(configDir, "identity.env"),
4860
+ generations: path11.join(configDir, "generations"),
4861
+ active: path11.join(configDir, "active")
4792
4862
  };
4793
4863
  const whileOwned = (operation) => attempt(() => {
4794
4864
  lock.assertOwned();
@@ -4801,13 +4871,13 @@ var createStore = (configDir, lock) => {
4801
4871
  prepare: (files) => whileOwned(() => prepareFiles(paths, files)),
4802
4872
  activate: (generation) => whileOwned(() => activateGeneration(paths, generation)),
4803
4873
  createBackup: (previous, target) => whileOwned(() => {
4804
- const backups = path10.join(configDir, "backups");
4805
- fs10.mkdirSync(backups, { recursive: true, mode: 448 });
4806
- fs10.chmodSync(backups, 448);
4807
- const directory = path10.join(backups, `upgrade-${randomUUID()}`);
4808
- fs10.mkdirSync(directory, { mode: 448 });
4874
+ const backups = path11.join(configDir, "backups");
4875
+ fs11.mkdirSync(backups, { recursive: true, mode: 448 });
4876
+ fs11.chmodSync(backups, 448);
4877
+ const directory = path11.join(backups, `upgrade-${randomUUID()}`);
4878
+ fs11.mkdirSync(directory, { mode: 448 });
4809
4879
  writeDurable(
4810
- path10.join(directory, "recovery.json"),
4880
+ path11.join(directory, "recovery.json"),
4811
4881
  `${JSON.stringify(
4812
4882
  {
4813
4883
  previousGeneration: previous.directory,
@@ -4823,21 +4893,21 @@ var createStore = (configDir, lock) => {
4823
4893
  syncDirectory(directory);
4824
4894
  syncDirectory(backups);
4825
4895
  syncDirectory(configDir);
4826
- const partialFile = path10.join(directory, "database.sql.partial");
4896
+ const partialFile = path11.join(directory, "database.sql.partial");
4827
4897
  return {
4828
4898
  directory,
4829
4899
  partialFile,
4830
4900
  complete: () => whileOwned(() => {
4831
- if (fs10.statSync(partialFile).size === 0)
4901
+ if (fs11.statSync(partialFile).size === 0)
4832
4902
  throw new ServerStoreError("Database backup is empty; refusing target startup.");
4833
- const fd = fs10.openSync(partialFile, "r");
4903
+ const fd = fs11.openSync(partialFile, "r");
4834
4904
  try {
4835
- fs10.fchmodSync(fd, 384);
4836
- fs10.fsyncSync(fd);
4905
+ fs11.fchmodSync(fd, 384);
4906
+ fs11.fsyncSync(fd);
4837
4907
  } finally {
4838
- fs10.closeSync(fd);
4908
+ fs11.closeSync(fd);
4839
4909
  }
4840
- fs10.renameSync(partialFile, path10.join(directory, "database.sql"));
4910
+ fs11.renameSync(partialFile, path11.join(directory, "database.sql"));
4841
4911
  syncDirectory(directory);
4842
4912
  })
4843
4913
  };
@@ -4846,7 +4916,7 @@ var createStore = (configDir, lock) => {
4846
4916
  };
4847
4917
  var refuseFlatLayout = (configDir) => {
4848
4918
  if (["server.json", "server.env", "compose.yaml", "postgres-init.sh"].some(
4849
- (name) => fs10.existsSync(path10.join(configDir, name))
4919
+ (name) => fs11.existsSync(path11.join(configDir, name))
4850
4920
  )) {
4851
4921
  throw new ServerStoreError(
4852
4922
  "Unreleased flat server configuration found. Preserve its credentials and volumes; migrate it to the generation layout before retrying. Setup will not replace this identity."
@@ -4854,7 +4924,7 @@ var refuseFlatLayout = (configDir) => {
4854
4924
  }
4855
4925
  };
4856
4926
  var withServerStore = async (directory, operation, options = {}) => {
4857
- const configDir = path10.resolve(directory);
4927
+ const configDir = path11.resolve(directory);
4858
4928
  let lock;
4859
4929
  let result;
4860
4930
  try {
@@ -4871,9 +4941,9 @@ var withServerStore = async (directory, operation, options = {}) => {
4871
4941
  };
4872
4942
 
4873
4943
  // src/version.ts
4874
- import * as fs11 from "node:fs";
4944
+ import * as fs12 from "node:fs";
4875
4945
  var cliVersion = () => {
4876
- const raw = fs11.readFileSync(new URL("../package.json", import.meta.url), "utf8");
4946
+ const raw = fs12.readFileSync(new URL("../package.json", import.meta.url), "utf8");
4877
4947
  const parsed = JSON.parse(raw);
4878
4948
  if (typeof parsed === "object" && parsed !== null && "version" in parsed) {
4879
4949
  const version = parsed.version;
@@ -5576,7 +5646,7 @@ var resolveServerVersion = async (runtime, options, existing) => {
5576
5646
  return version;
5577
5647
  };
5578
5648
  var parseDockerSocketPath = (socket) => {
5579
- if (!path11.isAbsolute(socket) || /[\r\n$'"#:\\]/.test(socket)) {
5649
+ if (!path12.isAbsolute(socket) || /[\r\n$'"#:\\]/.test(socket)) {
5580
5650
  throw setupError(
5581
5651
  "--docker-socket must be an absolute path without line breaks or Compose interpolation characters."
5582
5652
  );
@@ -5602,8 +5672,8 @@ var resolveAssets = async (runtime, serverVersion, existing, store, options) =>
5602
5672
  if (options.assetsDir !== void 0) {
5603
5673
  try {
5604
5674
  const [compose2, postgresInit2] = await Promise.all([
5605
- readFile(path11.join(options.assetsDir, COMPOSE_ASSET), "utf8"),
5606
- readFile(path11.join(options.assetsDir, POSTGRES_INIT_ASSET), "utf8")
5675
+ readFile(path12.join(options.assetsDir, COMPOSE_ASSET), "utf8"),
5676
+ readFile(path12.join(options.assetsDir, POSTGRES_INIT_ASSET), "utf8")
5607
5677
  ]);
5608
5678
  validateComposeAsset(compose2);
5609
5679
  validatePostgresAsset(postgresInit2);
@@ -6129,10 +6199,10 @@ var manageServer = async (command, args, runtime, store) => {
6129
6199
  };
6130
6200
  var nodeServerRuntime = () => {
6131
6201
  const environment = { ...process.env };
6132
- const home = os7.homedir();
6133
- const configHome = environment["XDG_CONFIG_HOME"] ?? path11.join(home, ".config");
6202
+ const home = os8.homedir();
6203
+ const configHome = environment["XDG_CONFIG_HOME"] ?? path12.join(home, ".config");
6134
6204
  return {
6135
- configDir: path11.join(configHome, "mend"),
6205
+ configDir: path12.join(configHome, "mend"),
6136
6206
  platform: process.platform,
6137
6207
  cliVersion: cliVersion(),
6138
6208
  run: (command, args, options) => runServerProcess(command, args, environment, options),
@@ -6585,8 +6655,8 @@ var createServiceTunnels = (options) => {
6585
6655
  };
6586
6656
 
6587
6657
  // src/skills.ts
6588
- import * as fs12 from "node:fs";
6589
- import * as path12 from "node:path";
6658
+ import * as fs13 from "node:fs";
6659
+ import * as path13 from "node:path";
6590
6660
  var DEFAULT_SKILLS_DIR = ".agents/skills";
6591
6661
  var MAX_SKILL_FILES = 64;
6592
6662
  var MAX_SKILL_FILE_BYTES = 512 * 1024;
@@ -6609,12 +6679,12 @@ var readFrontmatterDescription = (contents) => {
6609
6679
  var walkFiles = (root) => {
6610
6680
  const found = [];
6611
6681
  const visit = (dir) => {
6612
- for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
6682
+ for (const entry of fs13.readdirSync(dir, { withFileTypes: true })) {
6613
6683
  if (IGNORED_DIRS.has(entry.name)) continue;
6614
- const absolute = path12.join(dir, entry.name);
6684
+ const absolute = path13.join(dir, entry.name);
6615
6685
  let stat;
6616
6686
  try {
6617
- stat = fs12.statSync(absolute);
6687
+ stat = fs13.statSync(absolute);
6618
6688
  } catch {
6619
6689
  continue;
6620
6690
  }
@@ -6628,7 +6698,7 @@ var walkFiles = (root) => {
6628
6698
  var scanSkillLibrary = (root) => {
6629
6699
  let entries;
6630
6700
  try {
6631
- entries = fs12.readdirSync(root, { withFileTypes: true });
6701
+ entries = fs13.readdirSync(root, { withFileTypes: true });
6632
6702
  } catch {
6633
6703
  return { error: `${root} is not a readable directory` };
6634
6704
  }
@@ -6636,23 +6706,23 @@ var scanSkillLibrary = (root) => {
6636
6706
  const notes = [];
6637
6707
  for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
6638
6708
  if (entry.name.startsWith(".")) continue;
6639
- const skillDir = path12.join(root, entry.name);
6709
+ const skillDir = path13.join(root, entry.name);
6640
6710
  let stat;
6641
6711
  try {
6642
- stat = fs12.statSync(skillDir);
6712
+ stat = fs13.statSync(skillDir);
6643
6713
  } catch {
6644
6714
  continue;
6645
6715
  }
6646
6716
  if (!stat.isDirectory()) continue;
6647
- if (!fs12.existsSync(path12.join(skillDir, "SKILL.md"))) {
6717
+ if (!fs13.existsSync(path13.join(skillDir, "SKILL.md"))) {
6648
6718
  notes.push({ skill: entry.name, message: "no SKILL.md \u2014 skipped" });
6649
6719
  continue;
6650
6720
  }
6651
6721
  const files = [];
6652
6722
  let bytes = 0;
6653
6723
  for (const absolute of walkFiles(skillDir)) {
6654
- const relative4 = path12.relative(skillDir, absolute).split(path12.sep).join("/");
6655
- const buffer = fs12.readFileSync(absolute);
6724
+ const relative4 = path13.relative(skillDir, absolute).split(path13.sep).join("/");
6725
+ const buffer = fs13.readFileSync(absolute);
6656
6726
  if (buffer.byteLength > MAX_SKILL_FILE_BYTES) {
6657
6727
  notes.push({ skill: entry.name, message: `${relative4} is over 512KB \u2014 skipped` });
6658
6728
  continue;
@@ -6687,16 +6757,16 @@ var scanSkillLibrary = (root) => {
6687
6757
  };
6688
6758
 
6689
6759
  // src/ssh-setup.ts
6690
- import * as os9 from "node:os";
6691
- import * as path14 from "node:path";
6760
+ import * as os10 from "node:os";
6761
+ import * as path15 from "node:path";
6692
6762
 
6693
6763
  // ../../packages/workspace-ssh/src/index.ts
6694
- import { spawnSync as spawnSync4 } from "node:child_process";
6764
+ import { spawnSync as spawnSync5 } from "node:child_process";
6695
6765
  import { createHash as createHash3 } from "node:crypto";
6696
- import * as fs13 from "node:fs";
6766
+ import * as fs14 from "node:fs";
6697
6767
  import { isIP as isIP2 } from "node:net";
6698
- import * as os8 from "node:os";
6699
- import * as path13 from "node:path";
6768
+ import * as os9 from "node:os";
6769
+ import * as path14 from "node:path";
6700
6770
  var LEGACY_BLOCK_BEGIN = "# >>> mend workspace ssh (managed by `mend ssh setup`) >>>";
6701
6771
  var LEGACY_BLOCK_END = "# <<< mend workspace ssh <<<";
6702
6772
  var WorkspaceSshError = class extends Error {
@@ -6783,8 +6853,8 @@ var blockBegin = (alias) => `# >>> mend workspace ssh ${alias} (managed) >>>`;
6783
6853
  var blockEnd = (alias) => `# <<< mend workspace ssh ${alias} <<<`;
6784
6854
  var hasControls = (value) => /[\p{Cc}\p{Zl}\p{Zp}]/u.test(value);
6785
6855
  var parseIdentityPath = (value) => {
6786
- const resolved = path13.resolve(
6787
- value.startsWith("~/") ? path13.join(os8.homedir(), value.slice(2)) : value
6856
+ const resolved = path14.resolve(
6857
+ value.startsWith("~/") ? path14.join(os9.homedir(), value.slice(2)) : value
6788
6858
  );
6789
6859
  if (value === "" || hasControls(value) || hasControls(resolved) || value.includes("${") || resolved.includes("${")) {
6790
6860
  return failure(
@@ -6930,7 +7000,7 @@ var workspaceSshPublicKeyFingerprint = (publicKey) => {
6930
7000
  var publicKeyIdentity = (publicKey) => publicKey.trim().split(/\s+/).slice(0, 2).join(" ");
6931
7001
  var agentKeys = () => {
6932
7002
  if (!process.env["SSH_AUTH_SOCK"]) return [];
6933
- const listed = spawnSync4("ssh-add", ["-L"], {
7003
+ const listed = spawnSync5("ssh-add", ["-L"], {
6934
7004
  encoding: "utf8",
6935
7005
  timeout: 3e3,
6936
7006
  stdio: ["ignore", "pipe", "pipe"],
@@ -6940,7 +7010,7 @@ var agentKeys = () => {
6940
7010
  };
6941
7011
  var readText = (file) => {
6942
7012
  try {
6943
- return success(fs13.readFileSync(file, "utf8"));
7013
+ return success(fs14.readFileSync(file, "utf8"));
6944
7014
  } catch (cause) {
6945
7015
  const error = cause instanceof Error && "code" in cause ? cause.code : void 0;
6946
7016
  return error === "ENOENT" ? success("") : failure(new WorkspaceSshError("key", `Could not read ${file}.`, cause));
@@ -6967,7 +7037,7 @@ var readRequiredKey = (keyPath, source) => {
6967
7037
  const privatePath = publicPath.slice(0, -".pub".length);
6968
7038
  const read = readText(publicPath);
6969
7039
  if (!read.ok) return read;
6970
- const derived = spawnSync4("ssh-keygen", ["-y", "-P", "", "-f", privatePath], {
7040
+ const derived = spawnSync5("ssh-keygen", ["-y", "-P", "", "-f", privatePath], {
6971
7041
  encoding: "utf8",
6972
7042
  timeout: 3e3,
6973
7043
  stdio: ["ignore", "pipe", "pipe"],
@@ -6998,19 +7068,19 @@ var readRequiredKey = (keyPath, source) => {
6998
7068
  var readOptionalKey = (keyPath) => {
6999
7069
  const parsed = parseIdentityPath(keyPath);
7000
7070
  if (!parsed.ok) return parsed;
7001
- if (!fs13.existsSync(parsed.value) && !fs13.existsSync(publicKeyPath(parsed.value)))
7071
+ if (!fs14.existsSync(parsed.value) && !fs14.existsSync(publicKeyPath(parsed.value)))
7002
7072
  return success(null);
7003
7073
  return readRequiredKey(parsed.value, "existing");
7004
7074
  };
7005
7075
  var pinAgentKey = (configHome, publicKey) => {
7006
- const identityFile = path13.resolve(
7076
+ const identityFile = path14.resolve(
7007
7077
  configHome,
7008
7078
  "ssh",
7009
7079
  `agent-${hashIdentity(publicKeyIdentity(publicKey))}.pub`
7010
7080
  );
7011
7081
  try {
7012
- fs13.mkdirSync(path13.dirname(identityFile), { recursive: true, mode: 448 });
7013
- fs13.writeFileSync(identityFile, `${publicKey}
7082
+ fs14.mkdirSync(path14.dirname(identityFile), { recursive: true, mode: 448 });
7083
+ fs14.writeFileSync(identityFile, `${publicKey}
7014
7084
  `, { mode: 384 });
7015
7085
  return success({ publicKey, identityFile, source: "agent" });
7016
7086
  } catch (cause) {
@@ -7021,15 +7091,15 @@ var pinAgentKey = (configHome, publicKey) => {
7021
7091
  };
7022
7092
  var generateDedicatedKey = (privatePath) => {
7023
7093
  try {
7024
- fs13.mkdirSync(path13.dirname(privatePath), { recursive: true, mode: 448 });
7094
+ fs14.mkdirSync(path14.dirname(privatePath), { recursive: true, mode: 448 });
7025
7095
  } catch (cause) {
7026
7096
  return failure(
7027
- new WorkspaceSshError("key", `Could not create ${path13.dirname(privatePath)}.`, cause)
7097
+ new WorkspaceSshError("key", `Could not create ${path14.dirname(privatePath)}.`, cause)
7028
7098
  );
7029
7099
  }
7030
- const generated = spawnSync4(
7100
+ const generated = spawnSync5(
7031
7101
  "ssh-keygen",
7032
- ["-t", "ed25519", "-f", privatePath, "-N", "", "-C", `mend-${os8.hostname()}`],
7102
+ ["-t", "ed25519", "-f", privatePath, "-N", "", "-C", `mend-${os9.hostname()}`],
7033
7103
  { encoding: "utf8", timeout: 3e3, stdio: ["ignore", "pipe", "pipe"] }
7034
7104
  );
7035
7105
  if (generated.status !== 0) {
@@ -7053,7 +7123,7 @@ var generateDedicatedKey = (privatePath) => {
7053
7123
  return fresh;
7054
7124
  };
7055
7125
  var dedicatedKey = (configHome, create) => {
7056
- const privatePath = path13.join(configHome, "ssh", "id_ed25519");
7126
+ const privatePath = path14.join(configHome, "ssh", "id_ed25519");
7057
7127
  const existing = readOptionalKey(privatePath);
7058
7128
  if (existing.ok === false) return failure(existing.error);
7059
7129
  if (existing.value !== null || !create) return existing;
@@ -7101,10 +7171,10 @@ var writeWorkspaceSshConfig = (configFile, target, identityFile) => {
7101
7171
  const reconciled = reconcileWorkspaceSshConfig(before.value, target, identityFile);
7102
7172
  if (reconciled.ok === false) return failure(reconciled.error);
7103
7173
  try {
7104
- fs13.mkdirSync(path13.dirname(configFile), { recursive: true, mode: 448 });
7105
- fs13.chmodSync(path13.dirname(configFile), 448);
7106
- fs13.writeFileSync(configFile, reconciled.value, { mode: 384 });
7107
- fs13.chmodSync(configFile, 384);
7174
+ fs14.mkdirSync(path14.dirname(configFile), { recursive: true, mode: 448 });
7175
+ fs14.chmodSync(path14.dirname(configFile), 448);
7176
+ fs14.writeFileSync(configFile, reconciled.value, { mode: 384 });
7177
+ fs14.chmodSync(configFile, 384);
7108
7178
  return success(void 0);
7109
7179
  } catch (cause) {
7110
7180
  return failure(new WorkspaceSshError("config", `Could not write ${configFile}.`, cause));
@@ -7117,7 +7187,7 @@ var dim5 = ansi("2");
7117
7187
  var green5 = ansi("32");
7118
7188
  var warn = ansi("33");
7119
7189
  var say5 = (line) => console.log(line);
7120
- var sshConfigPath = () => path14.join(os9.homedir(), ".ssh", "config");
7190
+ var sshConfigPath = () => path15.join(os10.homedir(), ".ssh", "config");
7121
7191
  var flagValue2 = (args, flag) => {
7122
7192
  const index = args.indexOf(flag);
7123
7193
  return index === -1 || args[index + 1] === void 0 ? null : String(args[index + 1]);
@@ -7187,7 +7257,7 @@ var setup = async (api2, cliHome, serverUrl, args) => {
7187
7257
  if (picked.value === null) return showFailure("No workspace SSH key is available.");
7188
7258
  const registered = await api2("POST", "/workspace-ssh/keys", {
7189
7259
  publicKey: picked.value.publicKey,
7190
- name: os9.hostname()
7260
+ name: os10.hostname()
7191
7261
  });
7192
7262
  const sourceLabel = {
7193
7263
  explicit: "from --key",
@@ -7229,8 +7299,8 @@ var sshCommand = async (args, api2, cliHome, serverUrl) => {
7229
7299
  };
7230
7300
 
7231
7301
  // src/uninstall.ts
7232
- import * as fs14 from "node:fs";
7233
- import * as path15 from "node:path";
7302
+ import * as fs15 from "node:fs";
7303
+ import * as path16 from "node:path";
7234
7304
  var UNINSTALL_USAGE = "usage: mend uninstall [--all | --server | --home] [--yes]";
7235
7305
  var parseUninstallArgs = (args) => {
7236
7306
  let scope = null;
@@ -7252,14 +7322,14 @@ var parseUninstallArgs = (args) => {
7252
7322
  var SERVER_FILES = ["identity.env", "active", "generations", "backups"];
7253
7323
  var countEntries = (directory) => {
7254
7324
  try {
7255
- return fs14.readdirSync(directory).length;
7325
+ return fs15.readdirSync(directory).length;
7256
7326
  } catch {
7257
7327
  return 0;
7258
7328
  }
7259
7329
  };
7260
7330
  var exists = (file) => {
7261
7331
  try {
7262
- fs14.lstatSync(file);
7332
+ fs15.lstatSync(file);
7263
7333
  return true;
7264
7334
  } catch {
7265
7335
  return false;
@@ -7268,7 +7338,7 @@ var exists = (file) => {
7268
7338
  var managedBlockCount = (sshConfigFile) => {
7269
7339
  let config;
7270
7340
  try {
7271
- config = fs14.readFileSync(sshConfigFile, "utf8");
7341
+ config = fs15.readFileSync(sshConfigFile, "utf8");
7272
7342
  } catch {
7273
7343
  return 0;
7274
7344
  }
@@ -7292,27 +7362,27 @@ var describeUninstall = async (runtime, scope) => {
7292
7362
  if (!read.error.message.startsWith("No Mend server is configured")) throw read.error;
7293
7363
  server = "none";
7294
7364
  } else if (read.value === null) {
7295
- server = exists(path15.join(configDir, "identity.env")) ? {
7365
+ server = exists(path16.join(configDir, "identity.env")) ? {
7296
7366
  version: "(no active generation)",
7297
7367
  appUrl: "",
7298
7368
  dockerContext: "",
7299
- generations: countEntries(path15.join(configDir, "generations")),
7300
- backups: countEntries(path15.join(configDir, "backups"))
7369
+ generations: countEntries(path16.join(configDir, "generations")),
7370
+ backups: countEntries(path16.join(configDir, "backups"))
7301
7371
  } : "none";
7302
7372
  } else {
7303
7373
  server = {
7304
7374
  version: read.value.config.serverVersion,
7305
7375
  appUrl: read.value.config.appUrl,
7306
7376
  dockerContext: read.value.config.dockerContext,
7307
- generations: countEntries(path15.join(configDir, "generations")),
7308
- backups: countEntries(path15.join(configDir, "backups"))
7377
+ generations: countEntries(path16.join(configDir, "generations")),
7378
+ backups: countEntries(path16.join(configDir, "backups"))
7309
7379
  };
7310
7380
  }
7311
7381
  }
7312
7382
  let home = null;
7313
7383
  if (scope !== "server") {
7314
- const cliConfig = path15.join(runtime.cliHome, "cli.json");
7315
- const sshDirectory = path15.join(runtime.cliHome, "ssh");
7384
+ const cliConfig = path16.join(runtime.cliHome, "cli.json");
7385
+ const sshDirectory = path16.join(runtime.cliHome, "ssh");
7316
7386
  home = {
7317
7387
  cliConfig: exists(cliConfig) ? cliConfig : null,
7318
7388
  sshDirectory: exists(sshDirectory) ? sshDirectory : null,
@@ -7444,7 +7514,7 @@ var removeServer = async (runtime, failures, leftovers) => {
7444
7514
  }
7445
7515
  }
7446
7516
  for (const name of SERVER_FILES) {
7447
- fs14.rmSync(path15.join(configDir, name), { recursive: true, force: true });
7517
+ fs15.rmSync(path16.join(configDir, name), { recursive: true, force: true });
7448
7518
  }
7449
7519
  server.writeLine(`removed ${configDir}/{identity.env, active, generations, backups}`);
7450
7520
  },
@@ -7455,20 +7525,20 @@ var removeServer = async (runtime, failures, leftovers) => {
7455
7525
  var removeHome = async (runtime, plan, failures, leftovers) => {
7456
7526
  const { server } = runtime;
7457
7527
  if (plan.cliConfig !== null) {
7458
- fs14.rmSync(plan.cliConfig, { force: true });
7528
+ fs15.rmSync(plan.cliConfig, { force: true });
7459
7529
  server.writeLine(`removed ${plan.cliConfig}`);
7460
7530
  }
7461
7531
  if (plan.sshDirectory !== null) {
7462
- fs14.rmSync(plan.sshDirectory, { recursive: true, force: true });
7532
+ fs15.rmSync(plan.sshDirectory, { recursive: true, force: true });
7463
7533
  server.writeLine(`removed ${plan.sshDirectory}`);
7464
7534
  }
7465
7535
  if (plan.managedSshBlocks > 0) {
7466
7536
  try {
7467
- const before = fs14.readFileSync(runtime.sshConfigFile, "utf8");
7537
+ const before = fs15.readFileSync(runtime.sshConfigFile, "utf8");
7468
7538
  const stripped = stripManagedWorkspaceSshBlocks(before);
7469
7539
  if (!stripped.ok) throw stripped.error;
7470
7540
  if (stripped.value.removed > 0) {
7471
- fs14.writeFileSync(runtime.sshConfigFile, stripped.value.config, { mode: 384 });
7541
+ fs15.writeFileSync(runtime.sshConfigFile, stripped.value.config, { mode: 384 });
7472
7542
  server.writeLine(
7473
7543
  `removed ${plural(stripped.value.removed, "managed block")} from ${runtime.sshConfigFile}`
7474
7544
  );
@@ -7481,12 +7551,12 @@ var removeHome = async (runtime, plan, failures, leftovers) => {
7481
7551
  }
7482
7552
  let remaining = [];
7483
7553
  try {
7484
- remaining = fs14.readdirSync(runtime.cliHome);
7554
+ remaining = fs15.readdirSync(runtime.cliHome);
7485
7555
  } catch {
7486
7556
  return;
7487
7557
  }
7488
7558
  if (remaining.length === 0) {
7489
- fs14.rmdirSync(runtime.cliHome);
7559
+ fs15.rmdirSync(runtime.cliHome);
7490
7560
  server.writeLine(`removed ${runtime.cliHome}`);
7491
7561
  } else {
7492
7562
  leftovers.push(
@@ -7550,19 +7620,19 @@ var upgradeUrl = async (input) => {
7550
7620
  // src/main.ts
7551
7621
  var mendCliHome = () => {
7552
7622
  const xdg = process.env["XDG_CONFIG_HOME"];
7553
- const preferred = path16.join(
7554
- xdg === void 0 || xdg === "" ? path16.join(os10.homedir(), ".config") : xdg,
7623
+ const preferred = path17.join(
7624
+ xdg === void 0 || xdg === "" ? path17.join(os11.homedir(), ".config") : xdg,
7555
7625
  "mend"
7556
7626
  );
7557
- const legacy = path16.join(os10.homedir(), ".mend");
7558
- return !fs15.existsSync(preferred) && fs15.existsSync(legacy) ? legacy : preferred;
7627
+ const legacy = path17.join(os11.homedir(), ".mend");
7628
+ return !fs16.existsSync(preferred) && fs16.existsSync(legacy) ? legacy : preferred;
7559
7629
  };
7560
- var CONFIG_PATH = path16.join(mendCliHome(), "cli.json");
7630
+ var CONFIG_PATH = path17.join(mendCliHome(), "cli.json");
7561
7631
  var loadConfig = () => {
7562
7632
  let fileConfig = {};
7563
- if (fs15.existsSync(CONFIG_PATH)) {
7633
+ if (fs16.existsSync(CONFIG_PATH)) {
7564
7634
  try {
7565
- fileConfig = JSON.parse(fs15.readFileSync(CONFIG_PATH, "utf8"));
7635
+ fileConfig = JSON.parse(fs16.readFileSync(CONFIG_PATH, "utf8"));
7566
7636
  } catch {
7567
7637
  fail6(`could not parse ${CONFIG_PATH}`);
7568
7638
  }
@@ -7814,7 +7884,7 @@ var adopt = async (config, args) => {
7814
7884
  }
7815
7885
  const sourceIssue = repositoryCloneUrlIssue(source);
7816
7886
  if (sourceIssue !== null) return fail6(sourceIssue);
7817
- const projectName = name ?? normalizeProjectName(path16.basename(source, ".git"));
7887
+ const projectName = name ?? normalizeProjectName(path17.basename(source, ".git"));
7818
7888
  const auth = authFlagIndex !== -1 && args[authFlagIndex + 1] !== void 0 ? String(args[authFlagIndex + 1]) : null;
7819
7889
  if (auth !== null && auth !== "ambient" && auth !== "mend-key" && auth !== "bridge") {
7820
7890
  return fail6(`--auth takes "ambient", "mend-key", or "bridge", not "${auth}"`);
@@ -7863,7 +7933,7 @@ var findProject = async (config, explicit, adoptCwd = false) => {
7863
7933
  const sourceIssue = repositoryCloneUrlIssue(facts.originUrl);
7864
7934
  if (sourceIssue !== null) return fail6(sourceIssue);
7865
7935
  const adopted = await api(config, "POST", "/projects", {
7866
- name: normalizeProjectName(path16.basename(facts.repoRoot)),
7936
+ name: normalizeProjectName(path17.basename(facts.repoRoot)),
7867
7937
  source: facts.originUrl
7868
7938
  });
7869
7939
  say6(`${green6("\u2713")} adopted \xB7 ${adopted.name} \xB7 ${dim6(adopted.storePath)}`);
@@ -8824,13 +8894,13 @@ var serviceRestart = async (config, args) => {
8824
8894
  };
8825
8895
  var serviceInit = async (args) => {
8826
8896
  const root = gitTopLevel(process.cwd()) ?? process.cwd();
8827
- const target = path16.join(root, "mend.toml");
8828
- if (fs15.existsSync(target)) {
8897
+ const target = path17.join(root, "mend.toml");
8898
+ if (fs16.existsSync(target)) {
8829
8899
  return fail6(`${target} already exists \u2014 edit it directly (init never merges)`);
8830
8900
  }
8831
- const rootFiles = fs15.readdirSync(root);
8901
+ const rootFiles = fs16.readdirSync(root);
8832
8902
  const proposals = [];
8833
- const readRoot = (name) => fs15.readFileSync(path16.join(root, name), "utf8");
8903
+ const readRoot = (name) => fs16.readFileSync(path17.join(root, name), "utf8");
8834
8904
  if (rootFiles.includes("package.json")) {
8835
8905
  proposals.push(...proposeFromPackageJson(readRoot("package.json"), rootFiles));
8836
8906
  }
@@ -8840,13 +8910,13 @@ var serviceInit = async (args) => {
8840
8910
  );
8841
8911
  for (const glob of globs) {
8842
8912
  const parent = glob.replace(/\/?\*+$/, "");
8843
- const dirs = glob.endsWith("*") ? fs15.existsSync(path16.join(root, parent)) ? fs15.readdirSync(path16.join(root, parent)).map((dir) => path16.join(parent, dir)) : [] : [glob];
8913
+ const dirs = glob.endsWith("*") ? fs16.existsSync(path17.join(root, parent)) ? fs16.readdirSync(path17.join(root, parent)).map((dir) => path17.join(parent, dir)) : [] : [glob];
8844
8914
  for (const dir of dirs) {
8845
- const manifest = path16.join(root, dir, "package.json");
8846
- if (!fs15.existsSync(manifest)) continue;
8915
+ const manifest = path17.join(root, dir, "package.json");
8916
+ if (!fs16.existsSync(manifest)) continue;
8847
8917
  for (const proposal of proposeFromWorkspacePackage(
8848
- path16.basename(dir),
8849
- fs15.readFileSync(manifest, "utf8"),
8918
+ path17.basename(dir),
8919
+ fs16.readFileSync(manifest, "utf8"),
8850
8920
  rootFiles
8851
8921
  )) {
8852
8922
  if (!proposals.some((existing) => existing.name === proposal.name)) {
@@ -8890,7 +8960,7 @@ var serviceInit = async (args) => {
8890
8960
  return;
8891
8961
  }
8892
8962
  }
8893
- fs15.writeFileSync(target, toml);
8963
+ fs16.writeFileSync(target, toml);
8894
8964
  say6(`${green6("\u2713")} wrote ${target} \u2014 commit it, then: mend service run ${proposals[0]?.name}`);
8895
8965
  };
8896
8966
  var tunnelServices = async (config, services, portOverride) => {
@@ -8994,23 +9064,23 @@ var serviceCommand = async (config, args) => {
8994
9064
  }
8995
9065
  };
8996
9066
  var isProvider = (value) => value === "claude" || value === "codex" || value === "github";
8997
- var readIfExists = (file) => fs15.existsSync(file) ? fs15.readFileSync(file, "utf8") : null;
9067
+ var readIfExists = (file) => fs16.existsSync(file) ? fs16.readFileSync(file, "utf8") : null;
8998
9068
  var localCredential = (provider) => {
8999
- const home = os10.homedir();
9069
+ const home = os11.homedir();
9000
9070
  switch (provider) {
9001
9071
  case "codex":
9002
9072
  return readIfExists(
9003
- path16.join(process.env["CODEX_HOME"] ?? path16.join(home, ".codex"), "auth.json")
9073
+ path17.join(process.env["CODEX_HOME"] ?? path17.join(home, ".codex"), "auth.json")
9004
9074
  );
9005
9075
  case "claude":
9006
9076
  return readIfExists(
9007
- path16.join(
9008
- process.env["CLAUDE_CONFIG_DIR"] ?? path16.join(home, ".claude"),
9077
+ path17.join(
9078
+ process.env["CLAUDE_CONFIG_DIR"] ?? path17.join(home, ".claude"),
9009
9079
  ".credentials.json"
9010
9080
  )
9011
9081
  );
9012
9082
  case "github": {
9013
- const result = spawnSync5("gh", ["auth", "token"], { encoding: "utf8" });
9083
+ const result = spawnSync6("gh", ["auth", "token"], { encoding: "utf8" });
9014
9084
  const token = result.status === 0 ? result.stdout.trim() : "";
9015
9085
  return token === "" ? null : token;
9016
9086
  }
@@ -9022,7 +9092,7 @@ var accountLine = (account) => {
9022
9092
  const identity = pick("login") ?? pick("email") ?? pick("accountEmail") ?? pick("accountId");
9023
9093
  const suffix = pick("tokenSuffix");
9024
9094
  const facts = [
9025
- account.status === "active" ? "connected" : account.status,
9095
+ account.status === "active" ? "connected" : account.status === "invalid" ? "reconnect needed \xB7 the provider refused the login" : account.status,
9026
9096
  identity,
9027
9097
  suffix === null ? null : `\u2026${suffix}`,
9028
9098
  `since ${account.connectedAt.slice(0, 10)}`
@@ -9055,7 +9125,7 @@ var claudeGrantSecret = () => {
9055
9125
  return read.kind === "missing" ? null : read.secret;
9056
9126
  };
9057
9127
  var doctorBundle = (config, args) => doctorBundleCommand(args, {
9058
- defaultDir: path16.join(mendCliHome(), "bundles"),
9128
+ defaultDir: path17.join(mendCliHome(), "bundles"),
9059
9129
  now: () => /* @__PURE__ */ new Date(),
9060
9130
  say: say6,
9061
9131
  warn: (line) => process.stderr.write(`${line}
@@ -9119,7 +9189,7 @@ var claudeGrant = async () => {
9119
9189
  return null;
9120
9190
  }
9121
9191
  }
9122
- if (status.configDirectory !== null && path16.resolve(status.configDirectory) !== path16.resolve(dir)) {
9192
+ if (status.configDirectory !== null && path17.resolve(status.configDirectory) !== path17.resolve(dir)) {
9123
9193
  fail6(
9124
9194
  `claude: this Claude read ${status.configDirectory} instead of ${dir}, so a separate grant is not possible \u2014 connect the shared one deliberately with --use-my-login`
9125
9195
  );
@@ -9182,8 +9252,16 @@ var connectCommand = async (config, args) => {
9182
9252
  }
9183
9253
  let secret;
9184
9254
  if (flags.includes("--from-stdin")) {
9185
- secret = fs15.readFileSync(0, "utf8").trim();
9255
+ secret = fs16.readFileSync(0, "utf8").trim();
9186
9256
  if (secret === "") return fail6("nothing on stdin");
9257
+ } else if (provider === "codex" && !flags.includes("--use-my-login")) {
9258
+ const grant = codexGrant({
9259
+ cli: codexCli(),
9260
+ personalAuthJson: readIfExists(path17.join(personalCodexHome(), "auth.json")),
9261
+ say: say6
9262
+ });
9263
+ if (grant.kind === "failed") return fail6(grant.reason);
9264
+ secret = grant.secret;
9187
9265
  } else if (provider === "claude" && !flags.includes("--use-my-login")) {
9188
9266
  secret = await claudeGrant();
9189
9267
  if (secret === null) return;
@@ -9193,10 +9271,10 @@ var connectCommand = async (config, args) => {
9193
9271
  const where = provider === "github" ? "`gh auth login` first, or pipe a token: gh auth token | mend connect github --from-stdin" : provider === "codex" ? "`codex login` first, or: mend connect codex --from-stdin < auth.json" : "`claude auth login` first, or: mend connect claude --from-stdin";
9194
9272
  return fail6(`${provider}: no credential on this machine \u2014 ${where}`);
9195
9273
  }
9196
- if (provider === "claude") {
9274
+ if (provider === "claude" || provider === "codex") {
9197
9275
  say6(
9198
9276
  dim6(
9199
- " --use-my-login: Mend and this machine will share one grant, and whichever refreshes second is signed out"
9277
+ " --use-my-login: Mend and this machine will share one login, and whichever refreshes second is signed out"
9200
9278
  )
9201
9279
  );
9202
9280
  }
@@ -9228,14 +9306,14 @@ var takeFlagValue4 = (args, flag) => {
9228
9306
  return at !== -1 && args[at + 1] !== void 0 ? String(args[at + 1]) : null;
9229
9307
  };
9230
9308
  var saveCliConfig = (next) => {
9231
- fs15.mkdirSync(path16.dirname(CONFIG_PATH), { recursive: true, mode: 448 });
9232
- fs15.writeFileSync(
9309
+ fs16.mkdirSync(path17.dirname(CONFIG_PATH), { recursive: true, mode: 448 });
9310
+ fs16.writeFileSync(
9233
9311
  CONFIG_PATH,
9234
9312
  `${JSON.stringify({ url: next.url, token: next.token, deviceId: next.deviceId }, null, 2)}
9235
9313
  `,
9236
9314
  { mode: 384 }
9237
9315
  );
9238
- fs15.chmodSync(CONFIG_PATH, 384);
9316
+ fs16.chmodSync(CONFIG_PATH, 384);
9239
9317
  };
9240
9318
  var login = async (config, args) => {
9241
9319
  await loginCommand(args, {
@@ -9248,7 +9326,7 @@ var login = async (config, args) => {
9248
9326
  });
9249
9327
  };
9250
9328
  var logout = async (config) => {
9251
- if (!fs15.existsSync(CONFIG_PATH) && config.token === null) {
9329
+ if (!fs16.existsSync(CONFIG_PATH) && config.token === null) {
9252
9330
  say6(dim6("nothing saved \u2014 already signed out"));
9253
9331
  return;
9254
9332
  }
@@ -9301,8 +9379,8 @@ var uninstallCommand = async (config, args) => {
9301
9379
  const runtime = {
9302
9380
  server,
9303
9381
  cliHome: mendCliHome(),
9304
- sshConfigFile: path16.join(os10.homedir(), ".ssh", "config"),
9305
- signedIn: config.token === null || !fs15.existsSync(CONFIG_PATH) ? null : { url: config.url, deviceId: config.deviceId },
9382
+ sshConfigFile: path17.join(os11.homedir(), ".ssh", "config"),
9383
+ signedIn: config.token === null || !fs16.existsSync(CONFIG_PATH) ? null : { url: config.url, deviceId: config.deviceId },
9306
9384
  revokeDevice: async () => {
9307
9385
  if (config.deviceId === null) return "no device id saved";
9308
9386
  try {
@@ -9366,7 +9444,7 @@ var keysShare = async (config) => {
9366
9444
  return fail6("SSH_AUTH_SOCK is not set \u2014 start (or plug in) your ssh-agent first");
9367
9445
  }
9368
9446
  await shareAgent({
9369
- url: () => socketUrl(config, "keys-bridge", { host: os10.hostname() }),
9447
+ url: () => socketUrl(config, "keys-bridge", { host: os11.hostname() }),
9370
9448
  agentSock,
9371
9449
  signal: new AbortController().signal,
9372
9450
  onEvent: (event) => {
@@ -9496,7 +9574,7 @@ ${usageOf("dotfiles repo")}`);
9496
9574
  var dotfilesSync = async (config, args) => {
9497
9575
  const all = args.includes("--all");
9498
9576
  const requestedPaths = args.filter((arg) => !arg.startsWith("--"));
9499
- const home = os10.homedir();
9577
+ const home = os11.homedir();
9500
9578
  if (!all && requestedPaths.length === 0) {
9501
9579
  const found = scanDotfileCandidates(home);
9502
9580
  if (found.length === 0) {
@@ -9523,13 +9601,13 @@ var dotfilesSync = async (config, args) => {
9523
9601
  if ("error" in read) return fail6(read.error);
9524
9602
  const result = await api(config, "POST", "/dotfiles/snapshot", {
9525
9603
  files: read.files,
9526
- source: os10.hostname(),
9604
+ source: os11.hostname(),
9527
9605
  merge: false
9528
9606
  });
9529
9607
  const snapshot = result.snapshot;
9530
9608
  if (snapshot === null) return fail6("the server accepted the sync but reports no snapshot");
9531
9609
  say6(
9532
- `${green6("synced")} ${snapshot.files.length} file${snapshot.files.length === 1 ? "" : "s"} from ${os10.hostname()} \xB7 ${dim6(snapshot.sha.slice(0, 7))} ${dim6("\u2014 applies from the next session launch")}`
9610
+ `${green6("synced")} ${snapshot.files.length} file${snapshot.files.length === 1 ? "" : "s"} from ${os11.hostname()} \xB7 ${dim6(snapshot.sha.slice(0, 7))} ${dim6("\u2014 applies from the next session launch")}`
9533
9611
  );
9534
9612
  };
9535
9613
  var envLoad = async (config, args) => {
@@ -9543,10 +9621,10 @@ var envLoad = async (config, args) => {
9543
9621
  const positional = args.filter(
9544
9622
  (arg, i) => !arg.startsWith("--") && args[i - 1] !== "--project" && !consumed.has(i)
9545
9623
  );
9546
- const file = path16.resolve(positional[0] ?? ".env");
9624
+ const file = path17.resolve(positional[0] ?? ".env");
9547
9625
  let contents;
9548
9626
  try {
9549
- contents = fs15.readFileSync(file, "utf8");
9627
+ contents = fs16.readFileSync(file, "utf8");
9550
9628
  } catch {
9551
9629
  return fail6(`cannot read ${file} \u2014 pass a path: mend env load path/to/.env`);
9552
9630
  }
@@ -9562,10 +9640,10 @@ var envLoad = async (config, args) => {
9562
9640
  { contents, allSecret, secretNames }
9563
9641
  );
9564
9642
  if (report.loaded.length === 0 && report.rejected.length === 0 && report.malformedLines.length === 0) {
9565
- say6(dim6(`${path16.basename(file)} has no variables`));
9643
+ say6(dim6(`${path17.basename(file)} has no variables`));
9566
9644
  return;
9567
9645
  }
9568
- say6(`${green6("\u2713")} loaded ${path16.basename(file)} into ${project2.name}`);
9646
+ say6(`${green6("\u2713")} loaded ${path17.basename(file)} into ${project2.name}`);
9569
9647
  for (const line of formatLoadReport(report, { dim: dim6, warn: amber2 })) say6(line);
9570
9648
  const plaintextUrls = report.loaded.filter(
9571
9649
  (entry) => entry.lane === "configuration" && /_(URL|URI|DSN)$/.test(entry.name)
@@ -9756,7 +9834,7 @@ var skillsList = async (config, args) => {
9756
9834
  };
9757
9835
  var skillsPush = async (config, args) => {
9758
9836
  const dirFlag = takeFlagValue4(args, "--dir");
9759
- const root = dirFlag === null ? path16.join(os10.homedir(), DEFAULT_SKILLS_DIR) : path16.resolve(dirFlag);
9837
+ const root = dirFlag === null ? path17.join(os11.homedir(), DEFAULT_SKILLS_DIR) : path17.resolve(dirFlag);
9760
9838
  const scanned = scanSkillLibrary(root);
9761
9839
  if ("error" in scanned) return fail6(scanned.error);
9762
9840
  for (const note of scanned.notes) say6(dim6(` ${note.skill}: ${note.message}`));
@@ -10431,14 +10509,14 @@ var dashboard = async (config, options = {}) => {
10431
10509
  `the dashboard needs Node >= 26 (node:ffi) \u2014 this is ${process.version}; every other command still works`
10432
10510
  );
10433
10511
  }
10434
- const rerun = spawnSync5(
10512
+ const rerun = spawnSync6(
10435
10513
  process.execPath,
10436
10514
  ["--experimental-ffi", "--disable-warning=ExperimentalWarning", ...process.argv.slice(1)],
10437
10515
  { stdio: "inherit" }
10438
10516
  );
10439
10517
  process.exit(rerun.status ?? 0);
10440
10518
  }
10441
- const { runDashboard } = await import("./dashboard-KZ4XGZ6W.js");
10519
+ const { runDashboard } = await import("./dashboard-MGR4YUFD.js");
10442
10520
  const agentShare = await startShareIfBridge(config);
10443
10521
  const tunnels = options.noTunnel === true || serverIsLocal(config) ? null : createServiceTunnels({
10444
10522
  listServices: () => fetchServices(config),
@@ -10472,7 +10550,7 @@ var startShareIfBridge = async (config) => {
10472
10550
  }
10473
10551
  if (access.mode !== "bridge") return null;
10474
10552
  return startAgentShare({
10475
- url: () => socketUrl(config, "keys-bridge", { host: os10.hostname() }),
10553
+ url: () => socketUrl(config, "keys-bridge", { host: os11.hostname() }),
10476
10554
  agentSock
10477
10555
  });
10478
10556
  };
@@ -10512,10 +10590,10 @@ var manCommand = (words) => {
10512
10590
  }
10513
10591
  const version = cliVersion();
10514
10592
  const page = doc === null ? renderManIndex(version) : renderManPage(doc, version);
10515
- const file = path16.join(os10.tmpdir(), `mend-man-${process.pid}-${manFileName(doc)}`);
10516
- fs15.writeFileSync(file, page);
10517
- const result = spawnSync5("man", ["-l", file], { stdio: "inherit" });
10518
- fs15.rmSync(file, { force: true });
10593
+ const file = path17.join(os11.tmpdir(), `mend-man-${process.pid}-${manFileName(doc)}`);
10594
+ fs16.writeFileSync(file, page);
10595
+ const result = spawnSync6("man", ["-l", file], { stdio: "inherit" });
10596
+ fs16.rmSync(file, { force: true });
10519
10597
  if (result.error !== void 0 || result.status !== 0) {
10520
10598
  say6(doc === null ? renderIndex() : renderCommand(doc));
10521
10599
  }