@sealant/mend 0.32.1 → 0.34.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +74 -13
  2. package/dist/{chunk-J3TR4UAG.js → chunk-JJ66V76Y.js} +424 -92
  3. package/dist/{dashboard-3HNFSQZD.js → dashboard-OADX3PHI.js} +10 -1
  4. package/dist/main.js +611 -243
  5. package/man/mend-accounts.1 +1 -1
  6. package/man/mend-adopt.1 +1 -1
  7. package/man/mend-attach.1 +1 -1
  8. package/man/mend-codex.1 +1 -1
  9. package/man/mend-completions.1 +1 -1
  10. package/man/mend-connect.1 +1 -1
  11. package/man/mend-continue.1 +1 -1
  12. package/man/mend-doctor.1 +2 -2
  13. package/man/mend-dotfiles-repo.1 +1 -1
  14. package/man/mend-dotfiles-sync.1 +1 -1
  15. package/man/mend-dotfiles.1 +1 -1
  16. package/man/mend-env-cluster.1 +1 -1
  17. package/man/mend-env-load.1 +1 -1
  18. package/man/mend-env-show.1 +1 -1
  19. package/man/mend-folder-create.1 +1 -1
  20. package/man/mend-folder-list.1 +1 -1
  21. package/man/mend-folder-push.1 +1 -1
  22. package/man/mend-folder-rm.1 +1 -1
  23. package/man/mend-git-author.1 +1 -1
  24. package/man/mend-help.1 +1 -1
  25. package/man/mend-invite.1 +1 -1
  26. package/man/mend-keys-init.1 +1 -1
  27. package/man/mend-keys-mode.1 +1 -1
  28. package/man/mend-keys-share.1 +1 -1
  29. package/man/mend-keys-show.1 +1 -1
  30. package/man/mend-land.1 +1 -1
  31. package/man/mend-login.1 +1 -1
  32. package/man/mend-logout.1 +1 -1
  33. package/man/mend-man.1 +1 -1
  34. package/man/mend-members.1 +1 -1
  35. package/man/mend-operator-exposure.1 +1 -1
  36. package/man/mend-operator-gate.1 +1 -1
  37. package/man/mend-operator-grant-owner.1 +1 -1
  38. package/man/mend-operator-org-create.1 +1 -1
  39. package/man/mend-operator-org-invite-owner.1 +1 -1
  40. package/man/mend-operator-org-list.1 +1 -1
  41. package/man/mend-operator-org-rename.1 +1 -1
  42. package/man/mend-operator-reset-link.1 +1 -1
  43. package/man/mend-pair.1 +1 -1
  44. package/man/mend-projects.1 +1 -1
  45. package/man/mend-pull.1 +1 -1
  46. package/man/mend-refresh.1 +1 -1
  47. package/man/mend-rejoin.1 +1 -1
  48. package/man/mend-resume.1 +1 -1
  49. package/man/mend-run.1 +1 -1
  50. package/man/mend-server-logs.1 +1 -1
  51. package/man/mend-server-restart.1 +1 -1
  52. package/man/mend-server-setup.1 +1 -1
  53. package/man/mend-server-start.1 +1 -1
  54. package/man/mend-server-status.1 +1 -1
  55. package/man/mend-server-stop.1 +1 -1
  56. package/man/mend-server-upgrade.1 +1 -1
  57. package/man/mend-server.1 +1 -1
  58. package/man/mend-service-add.1 +1 -1
  59. package/man/mend-service-connect.1 +1 -1
  60. package/man/mend-service-init.1 +1 -1
  61. package/man/mend-service-list.1 +1 -1
  62. package/man/mend-service-logs.1 +1 -1
  63. package/man/mend-service-restart.1 +1 -1
  64. package/man/mend-service-run.1 +1 -1
  65. package/man/mend-service-stop.1 +1 -1
  66. package/man/mend-session-share.1 +1 -1
  67. package/man/mend-sessions.1 +1 -1
  68. package/man/mend-shell.1 +1 -1
  69. package/man/mend-skills-push.1 +1 -1
  70. package/man/mend-skills.1 +1 -1
  71. package/man/mend-snake.1 +1 -1
  72. package/man/mend-ssh-setup.1 +1 -1
  73. package/man/mend-ssh.1 +1 -1
  74. package/man/mend-stop.1 +1 -1
  75. package/man/mend-ui.1 +1 -1
  76. package/man/mend-uninstall.1 +1 -1
  77. package/man/mend-version.1 +1 -1
  78. package/man/mend-worktrees.1 +1 -1
  79. package/man/mend.1 +1 -1
  80. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  LIVE_STATUSES,
5
5
  agentIsLive,
6
6
  agentOutcome,
7
+ captureLineOf,
7
8
  claudeGrantFacts,
8
9
  createSseParser,
9
10
  cwdFacts,
@@ -27,15 +28,15 @@ import {
27
28
  servicesHoldOf,
28
29
  sessionDisplayName,
29
30
  trackBracketedPaste
30
- } from "./chunk-J3TR4UAG.js";
31
+ } from "./chunk-JJ66V76Y.js";
31
32
 
32
33
  // src/main.ts
33
- import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
34
- import * as fs14 from "node:fs";
34
+ import { spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
35
+ import * as fs15 from "node:fs";
35
36
  import { createRequire } from "node:module";
36
37
  import * as net4 from "node:net";
37
- import * as os9 from "node:os";
38
- import * as path15 from "node:path";
38
+ import * as os10 from "node:os";
39
+ import * as path16 from "node:path";
39
40
 
40
41
  // src/agent-share.ts
41
42
  import * as net from "node:net";
@@ -1139,7 +1140,7 @@ var COMMANDS = [
1139
1140
  summary: "check this machine's setup, or bundle it for a bug report",
1140
1141
  synopsis: ["", "--bundle [--out <path>] [--tail <n>]"],
1141
1142
  description: [
1142
- "Read-only. One line per fact: the server, the sign-in, the platform connection, the provider accounts, the git key. Each unfinished line ends with the command that fixes it.",
1143
+ "Read-only. One line per fact: the server, the sign-in, the platform connection, the provider accounts, the git key, and where docker is on PATH its daemon's shutdown-timeout against the capture grace. Each unfinished line ends with the command that fixes it.",
1143
1144
  "--bundle collects what a maintainer asks for one output at a time into one tar.gz: this CLI and its environment, the doctor lines, the server's health, the local server's configuration (its compose file and the names of its .env keys, never their values), docker version, info, contexts, the Mend and workspace containers with their inspect facts, the local server's container logs, the running workspace containers' logs, every session with its processes, exit codes and argv, the recorded terminal output of each, the connected accounts, and the versions and paths of claude, codex, gh, git and docker on this machine.",
1144
1145
  "Each part is collected on its own: one that fails leaves a <name>.error.txt in the bundle instead of stopping it. One redactor runs over every file before it is written: bearer and header values, Slack, OpenAI, GitHub and AWS token shapes, JWTs, passwords in URLs, and any password, secret or token value. The archive is written with mode 0600. It still contains logs and configuration; read it before you share it."
1145
1146
  ],
@@ -2505,8 +2506,333 @@ var doctorBundleCommand = async (args, deps) => {
2505
2506
  };
2506
2507
 
2507
2508
  // src/doctor.ts
2509
+ import * as fs6 from "node:fs";
2510
+ import * as path6 from "node:path";
2511
+
2512
+ // src/docker-shutdown.ts
2513
+ import { spawnSync as spawnSync2 } from "node:child_process";
2508
2514
  import * as fs5 from "node:fs";
2515
+ import * as os3 from "node:os";
2509
2516
  import * as path5 from "node:path";
2517
+ var DOCKER_CAPTURE_STOP_GRACE_SECONDS = 3600;
2518
+ var DOCKERD_DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 15;
2519
+ var flagValue = (argv, name) => {
2520
+ for (let index = 0; index < argv.length; index += 1) {
2521
+ const arg = argv[index] ?? "";
2522
+ if (arg.startsWith(`${name}=`)) return arg.slice(name.length + 1);
2523
+ if (arg === name) return argv[index + 1] ?? "";
2524
+ }
2525
+ return null;
2526
+ };
2527
+ var seconds = (value) => {
2528
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0) return value;
2529
+ if (typeof value === "string" && /^\d+$/.test(value.trim())) return Number(value.trim());
2530
+ return null;
2531
+ };
2532
+ var daemonKindOf = (info) => {
2533
+ if (/docker desktop/i.test(info.operatingSystem)) return "desktop";
2534
+ if (/orbstack/i.test(info.operatingSystem)) return "orbstack";
2535
+ if (info.securityOptions.some((option) => option.includes("name=rootless"))) return "rootless";
2536
+ return "dockerd";
2537
+ };
2538
+ var defaultConfigPath = (facts, daemon) => {
2539
+ switch (daemon) {
2540
+ case "desktop":
2541
+ return path5.join(facts.home, ".docker", "daemon.json");
2542
+ case "orbstack":
2543
+ return path5.join(facts.home, ".orbstack", "config", "docker.json");
2544
+ case "rootless":
2545
+ return path5.join(
2546
+ facts.xdgConfigHome ?? path5.join(facts.home, ".config"),
2547
+ "docker",
2548
+ "daemon.json"
2549
+ );
2550
+ case "dockerd":
2551
+ return "/etc/docker/daemon.json";
2552
+ }
2553
+ };
2554
+ var readShutdownTimeout = (facts) => {
2555
+ if (facts.info === null) return { kind: "unknown", why: "docker info did not answer" };
2556
+ const daemon = daemonKindOf(facts.info);
2557
+ const managed = daemon === "desktop" || daemon === "orbstack";
2558
+ const argv = managed ? null : facts.dockerdArgv;
2559
+ const unresolved = facts.dockerdUnresolved ?? null;
2560
+ if (argv === null && !managed && unresolved !== null) return { kind: "unknown", why: unresolved };
2561
+ const configPath = (argv === null ? null : flagValue(argv, "--config-file")) ?? defaultConfigPath(facts, daemon);
2562
+ if (argv !== null) {
2563
+ const flag = flagValue(argv, "--shutdown-timeout");
2564
+ if (flag !== null) {
2565
+ const value = seconds(flag);
2566
+ return value === null ? { kind: "unknown", why: `dockerd --shutdown-timeout ${flag} unreadable` } : {
2567
+ kind: "observed",
2568
+ seconds: value,
2569
+ source: "dockerd --shutdown-timeout",
2570
+ daemon,
2571
+ configPath,
2572
+ fromFlag: true
2573
+ };
2574
+ }
2575
+ }
2576
+ const file = facts.readFile(configPath);
2577
+ if (file.kind === "unreadable") return { kind: "unknown", why: `${configPath} unreadable` };
2578
+ if (file.kind === "read") {
2579
+ let parsed;
2580
+ try {
2581
+ parsed = JSON.parse(file.text);
2582
+ } catch {
2583
+ return { kind: "unknown", why: `${configPath} is not JSON` };
2584
+ }
2585
+ const configured = typeof parsed === "object" && parsed !== null && "shutdown-timeout" in parsed ? parsed["shutdown-timeout"] : void 0;
2586
+ if (configured !== void 0) {
2587
+ const value = seconds(configured);
2588
+ return value === null ? { kind: "unknown", why: `shutdown-timeout in ${configPath} unreadable` } : {
2589
+ kind: "observed",
2590
+ seconds: value,
2591
+ source: configPath,
2592
+ daemon,
2593
+ configPath,
2594
+ fromFlag: false
2595
+ };
2596
+ }
2597
+ }
2598
+ if (argv === null && !managed) {
2599
+ return {
2600
+ kind: "unknown",
2601
+ why: `no dockerd process observed here \xB7 not set in ${configPath}`
2602
+ };
2603
+ }
2604
+ return {
2605
+ kind: "observed",
2606
+ seconds: DOCKERD_DEFAULT_SHUTDOWN_TIMEOUT_SECONDS,
2607
+ source: `dockerd default \xB7 not set in ${configPath}`,
2608
+ daemon,
2609
+ configPath,
2610
+ fromFlag: false
2611
+ };
2612
+ };
2613
+ var restartWords = (daemon) => {
2614
+ switch (daemon) {
2615
+ case "desktop":
2616
+ return "restart Docker Desktop";
2617
+ case "orbstack":
2618
+ return "restart OrbStack";
2619
+ case "rootless":
2620
+ return "systemctl --user restart docker";
2621
+ case "dockerd":
2622
+ return "restart dockerd";
2623
+ }
2624
+ };
2625
+ var raiseShutdownTimeoutWords = (reading, grace) => reading.fromFlag ? `raise dockerd --shutdown-timeout to ${grace}, then ${restartWords(reading.daemon)}` : `set "shutdown-timeout": ${grace} in ${reading.configPath}, then ${restartWords(reading.daemon)}`;
2626
+ var dockerShutdownCheck = (reading, grace = DOCKER_CAPTURE_STOP_GRACE_SECONDS) => {
2627
+ if (reading.kind === "unknown") {
2628
+ return {
2629
+ label: "docker",
2630
+ state: "todo",
2631
+ detail: `shutdown-timeout not observed \xB7 ${reading.why}`,
2632
+ fix: null
2633
+ };
2634
+ }
2635
+ if (reading.seconds >= grace) {
2636
+ return {
2637
+ label: "docker",
2638
+ state: "ok",
2639
+ detail: `shutdown-timeout ${reading.seconds} s \xB7 ${reading.source} \xB7 covers the ${grace} s capture grace`,
2640
+ fix: null
2641
+ };
2642
+ }
2643
+ return {
2644
+ label: "docker",
2645
+ state: "todo",
2646
+ detail: `shutdown-timeout ${reading.seconds} s \xB7 ${reading.source} \xB7 below the ${grace} s capture grace`,
2647
+ fix: raiseShutdownTimeoutWords(reading, grace)
2648
+ };
2649
+ };
2650
+ var dockerShutdownSetupLine = (reading, grace = DOCKER_CAPTURE_STOP_GRACE_SECONDS) => {
2651
+ if (reading.kind === "unknown") {
2652
+ return `Docker shutdown-timeout not observed (${reading.why}); a daemon shutdown below ${grace} s kills capture workspaces before they save. See docs/SELF-HOSTING.md.`;
2653
+ }
2654
+ if (reading.seconds >= grace) return null;
2655
+ return `Docker shutdown-timeout is ${reading.seconds} s (${reading.source}), below the ${grace} s capture grace: a host restart or daemon stop kills capture workspaces after ${reading.seconds} s, before they save. To raise it: ${raiseShutdownTimeoutWords(reading, grace)}.`;
2656
+ };
2657
+ var parseDockerInfo = (stdout) => {
2658
+ let parsed;
2659
+ try {
2660
+ parsed = JSON.parse(stdout);
2661
+ } catch {
2662
+ return null;
2663
+ }
2664
+ if (typeof parsed !== "object" || parsed === null || !("OperatingSystem" in parsed)) return null;
2665
+ const operatingSystem = parsed.OperatingSystem;
2666
+ if (typeof operatingSystem !== "string") return null;
2667
+ const options = "SecurityOptions" in parsed ? parsed.SecurityOptions : [];
2668
+ return {
2669
+ operatingSystem,
2670
+ securityOptions: Array.isArray(options) ? options.filter((option) => typeof option === "string") : []
2671
+ };
2672
+ };
2673
+ var readHostFile = (file) => {
2674
+ try {
2675
+ return { kind: "read", text: fs5.readFileSync(file, "utf8") };
2676
+ } catch (cause) {
2677
+ return cause instanceof Error && "code" in cause && cause.code === "ENOENT" ? { kind: "absent" } : { kind: "unreadable" };
2678
+ }
2679
+ };
2680
+ var hostProcView = {
2681
+ list: (dir) => {
2682
+ try {
2683
+ return fs5.readdirSync(dir);
2684
+ } catch {
2685
+ return null;
2686
+ }
2687
+ },
2688
+ read: (file) => {
2689
+ try {
2690
+ return fs5.readFileSync(file, "utf8");
2691
+ } catch {
2692
+ return null;
2693
+ }
2694
+ },
2695
+ link: (file) => {
2696
+ try {
2697
+ return fs5.readlinkSync(file);
2698
+ } catch {
2699
+ return null;
2700
+ }
2701
+ }
2702
+ };
2703
+ var DOCKER_DEFAULT_HOST = "unix:///var/run/docker.sock";
2704
+ var socketPath = (file) => file.replace(/^\/var\/run\//, "/run/");
2705
+ var unixSocketOf = (host) => host.startsWith("unix://") && host.length > "unix://".length ? socketPath(host.slice("unix://".length)) : null;
2706
+ var hostFlags = (argv) => {
2707
+ const hosts = [];
2708
+ for (let index = 1; index < argv.length; index += 1) {
2709
+ const arg = argv[index] ?? "";
2710
+ for (const name of ["-H", "--host"]) {
2711
+ if (arg === name) hosts.push(argv[index + 1] ?? "");
2712
+ else if (arg.startsWith(`${name}=`)) hosts.push(arg.slice(name.length + 1));
2713
+ }
2714
+ }
2715
+ return hosts;
2716
+ };
2717
+ var listeningInodes = (table, socket) => {
2718
+ const inodes = /* @__PURE__ */ new Set();
2719
+ for (const line of table.split("\n").slice(1)) {
2720
+ const fields = line.trim().split(/\s+/);
2721
+ const flags = fields[3];
2722
+ const inode = fields[6];
2723
+ const bound = fields.slice(7).join(" ");
2724
+ if (flags === void 0 || inode === void 0 || bound === "") continue;
2725
+ if ((Number.parseInt(flags, 16) & 65536) === 0) continue;
2726
+ if (socketPath(bound) === socket) inodes.add(inode);
2727
+ }
2728
+ return inodes;
2729
+ };
2730
+ var observeDockerd = (proc, host) => {
2731
+ const daemons = [];
2732
+ for (const entry of proc.list("/proc") ?? []) {
2733
+ if (!/^\d+$/.test(entry)) continue;
2734
+ const cmdline = proc.read(`/proc/${entry}/cmdline`);
2735
+ if (cmdline === null) continue;
2736
+ const argv = cmdline.split("\0").filter((arg) => arg !== "");
2737
+ if (path5.basename(argv[0] ?? "") === "dockerd") daemons.push({ pid: entry, argv });
2738
+ }
2739
+ if (daemons.length === 0) return { kind: "none" };
2740
+ const socket = unixSocketOf(host);
2741
+ if (socket === null) {
2742
+ return {
2743
+ kind: "unresolved",
2744
+ why: `docker talks to ${host} \xB7 not a socket on this machine`
2745
+ };
2746
+ }
2747
+ const selfTable = proc.read("/proc/self/net/unix");
2748
+ const seenHere = selfTable === null ? /* @__PURE__ */ new Set() : listeningInodes(selfTable, socket);
2749
+ const candidates = [];
2750
+ for (const daemon of daemons) {
2751
+ const hosts = hostFlags(daemon.argv);
2752
+ const named = hosts.some((value) => unixSocketOf(value) === socket);
2753
+ const activated = hosts.some((value) => value.startsWith("fd://"));
2754
+ if (hosts.length > 0 && !named && !activated) continue;
2755
+ const table = proc.read(`/proc/${daemon.pid}/net/unix`);
2756
+ const listening = table === null ? null : listeningInodes(table, socket);
2757
+ if (listening !== null) {
2758
+ if (listening.size === 0) continue;
2759
+ if (seenHere.size > 0 && ![...listening].some((inode) => seenHere.has(inode))) continue;
2760
+ }
2761
+ const fds = proc.list(`/proc/${daemon.pid}/fd`);
2762
+ const held = fds === null || listening === null ? null : fds.some((fd) => {
2763
+ const target = proc.link(`/proc/${daemon.pid}/fd/${fd}`);
2764
+ const inode = target?.match(/^socket:\[(\d+)\]$/)?.[1];
2765
+ return inode !== void 0 && listening.has(inode);
2766
+ });
2767
+ if (held === false) continue;
2768
+ const nsPids = proc.read(`/proc/${daemon.pid}/status`)?.split("\n").find((line) => line.startsWith("NSpid:"))?.slice("NSpid:".length).trim().split(/\s+/);
2769
+ candidates.push({
2770
+ ...daemon,
2771
+ owns: held === true,
2772
+ named,
2773
+ nested: nsPids !== void 0 && nsPids.length > 1
2774
+ });
2775
+ }
2776
+ const narrowed = [
2777
+ (candidate) => candidate.owns,
2778
+ (candidate) => candidate.named,
2779
+ (candidate) => !candidate.nested
2780
+ ].reduce((left, prefer) => {
2781
+ const preferred = left.filter(prefer);
2782
+ return preferred.length > 0 ? preferred : left;
2783
+ }, candidates);
2784
+ const [only, ...others] = narrowed;
2785
+ if (only !== void 0 && others.length === 0) {
2786
+ return { kind: "observed", pid: only.pid, argv: only.argv };
2787
+ }
2788
+ const seen = `${daemons.length} dockerd process${daemons.length === 1 ? "" : "es"}`;
2789
+ return {
2790
+ kind: "unresolved",
2791
+ why: only === void 0 ? `${seen} \xB7 none observed serving ${socket}` : `${seen} \xB7 could not tell which serves ${socket}`
2792
+ };
2793
+ };
2794
+ var hostDockerEndpoint = () => {
2795
+ const fromEnv = process.env["DOCKER_HOST"];
2796
+ if (fromEnv !== void 0 && fromEnv.trim() !== "") return fromEnv.trim();
2797
+ const context = spawnSync2(
2798
+ "docker",
2799
+ ["context", "inspect", "--format", "{{.Endpoints.docker.Host}}"],
2800
+ {
2801
+ encoding: "utf8",
2802
+ timeout: 3e3,
2803
+ stdio: ["ignore", "pipe", "ignore"]
2804
+ }
2805
+ );
2806
+ const endpoint = context.status === 0 ? context.stdout.trim() : "";
2807
+ return endpoint === "" ? DOCKER_DEFAULT_HOST : endpoint;
2808
+ };
2809
+ var dockerdFactsOf = (observation) => {
2810
+ switch (observation.kind) {
2811
+ case "observed":
2812
+ return { dockerdArgv: observation.argv, dockerdUnresolved: null };
2813
+ case "none":
2814
+ return { dockerdArgv: null, dockerdUnresolved: null };
2815
+ case "unresolved":
2816
+ return { dockerdArgv: null, dockerdUnresolved: observation.why };
2817
+ }
2818
+ };
2819
+ var hostDockerDaemonFacts = (infoStdout) => ({
2820
+ info: infoStdout === null ? null : parseDockerInfo(infoStdout),
2821
+ ...dockerdFactsOf(observeDockerd(hostProcView, hostDockerEndpoint())),
2822
+ readFile: readHostFile,
2823
+ home: os3.homedir(),
2824
+ xdgConfigHome: process.env["XDG_CONFIG_HOME"] ?? null
2825
+ });
2826
+ var observeHostShutdownTimeout = () => {
2827
+ const info = spawnSync2("docker", ["info", "--format", "{{json .}}"], {
2828
+ encoding: "utf8",
2829
+ timeout: 3e3,
2830
+ stdio: ["ignore", "pipe", "ignore"]
2831
+ });
2832
+ return readShutdownTimeout(hostDockerDaemonFacts(info.status === 0 ? info.stdout : null));
2833
+ };
2834
+
2835
+ // src/doctor.ts
2510
2836
  var TIMEOUT_MS = 3e3;
2511
2837
  var MARKS = { ok: "\u2713", todo: "\u25CB", failed: "\u2717" };
2512
2838
  var LABEL_WIDTH = 11;
@@ -2721,13 +3047,16 @@ var runChecks = async (config, probes) => {
2721
3047
  const machine = signedIn ? await getJson(config, "/machine") : null;
2722
3048
  const exposure = machine === null || machine.value === null ? void 0 : machine.value.exposure;
2723
3049
  checks.push(exposure === void 0 ? notChecked("exposure") : exposureCheck(exposure));
3050
+ if (probes.dockerShutdown !== void 0 && probes.onPath("docker")) {
3051
+ checks.push(dockerShutdownCheck(probes.dockerShutdown()));
3052
+ }
2724
3053
  return checks;
2725
3054
  };
2726
3055
  var onPath = (command) => {
2727
- for (const directory of (process.env["PATH"] ?? "").split(path5.delimiter)) {
3056
+ for (const directory of (process.env["PATH"] ?? "").split(path6.delimiter)) {
2728
3057
  if (directory === "") continue;
2729
3058
  try {
2730
- fs5.accessSync(path5.join(directory, command), fs5.constants.X_OK);
3059
+ fs6.accessSync(path6.join(directory, command), fs6.constants.X_OK);
2731
3060
  return true;
2732
3061
  } catch {
2733
3062
  }
@@ -2740,15 +3069,20 @@ var paintMark = (state, mark) => {
2740
3069
  return `\x1B[${code}m${mark}\x1B[0m`;
2741
3070
  };
2742
3071
  var doctorCommand = async (config, localCredential2, claudeGrant2) => {
2743
- const checks = await runChecks(config, { localCredential: localCredential2, claudeGrant: claudeGrant2, onPath });
3072
+ const checks = await runChecks(config, {
3073
+ localCredential: localCredential2,
3074
+ claudeGrant: claudeGrant2,
3075
+ onPath,
3076
+ dockerShutdown: observeHostShutdownTimeout
3077
+ });
2744
3078
  for (const check of checks) process.stdout.write(`${formatCheck(check, paintMark)}
2745
3079
  `);
2746
3080
  if (checks.some((check) => check.state === "failed")) process.exitCode = 1;
2747
3081
  };
2748
3082
 
2749
3083
  // src/dotfiles.ts
2750
- import * as fs6 from "node:fs";
2751
- import * as path6 from "node:path";
3084
+ import * as fs7 from "node:fs";
3085
+ import * as path7 from "node:path";
2752
3086
  var DOTFILE_CANDIDATES = [
2753
3087
  {
2754
3088
  group: "shell",
@@ -2791,7 +3125,7 @@ var DOTFILE_CANDIDATES = [
2791
3125
  var MAX_FILE_BYTES = 1024 * 1024;
2792
3126
  var statFile = (home, relative4) => {
2793
3127
  try {
2794
- const stat = fs6.statSync(path6.join(home, relative4));
3128
+ const stat = fs7.statSync(path7.join(home, relative4));
2795
3129
  return stat.isFile() ? stat : null;
2796
3130
  } catch {
2797
3131
  return null;
@@ -2804,9 +3138,9 @@ var scanDotfileCandidates = (home) => DOTFILE_CANDIDATES.flatMap(
2804
3138
  })
2805
3139
  );
2806
3140
  var homeRelativePath = (home, requested) => {
2807
- const relative4 = path6.relative(home, path6.resolve(home, requested));
2808
- const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${path6.sep}`) || path6.isAbsolute(relative4);
2809
- return outside ? null : relative4.split(path6.sep).join("/");
3141
+ const relative4 = path7.relative(home, path7.resolve(home, requested));
3142
+ const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative4);
3143
+ return outside ? null : relative4.split(path7.sep).join("/");
2810
3144
  };
2811
3145
  var readSyncFiles = (home, paths_) => {
2812
3146
  const files = [];
@@ -2826,7 +3160,7 @@ var readSyncFiles = (home, paths_) => {
2826
3160
  }
2827
3161
  files.push({
2828
3162
  path: relative4,
2829
- contentsBase64: fs6.readFileSync(path6.join(home, relative4)).toString("base64"),
3163
+ contentsBase64: fs7.readFileSync(path7.join(home, relative4)).toString("base64"),
2830
3164
  // eslint-disable-next-line no-bitwise -- permission bits are the point
2831
3165
  mode: (stat.mode & 511).toString(8)
2832
3166
  });
@@ -2925,10 +3259,10 @@ var parseGitAuthorArgs = (args) => {
2925
3259
  var gitAuthorLine = (author) => `${author.name} <${author.email}> \xB7 ${author.source === "setting" ? "your setting" : "your account's name and email"}`;
2926
3260
 
2927
3261
  // src/landing.ts
2928
- import { spawnSync as spawnSync2 } from "node:child_process";
2929
- import * as fs7 from "node:fs";
2930
- import * as os3 from "node:os";
2931
- import * as path7 from "node:path";
3262
+ import { spawnSync as spawnSync3 } from "node:child_process";
3263
+ import * as fs8 from "node:fs";
3264
+ import * as os4 from "node:os";
3265
+ import * as path8 from "node:path";
2932
3266
  var paint = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
2933
3267
  var dim = paint("2");
2934
3268
  var green = paint("32");
@@ -3143,7 +3477,7 @@ var landCommand = async (api2, args) => {
3143
3477
  if (!landingSucceeded(report)) process.exitCode = 1;
3144
3478
  };
3145
3479
  var git = (cwd, args) => {
3146
- const result = spawnSync2("git", args, { cwd, encoding: "utf8" });
3480
+ const result = spawnSync3("git", args, { cwd, encoding: "utf8" });
3147
3481
  return {
3148
3482
  status: result.status ?? 1,
3149
3483
  stdout: result.stdout ?? "",
@@ -3166,9 +3500,9 @@ var gitRemotes = (cwd) => {
3166
3500
  var remoteForOrigin = (remotes, originUrl, cwd) => {
3167
3501
  if (originUrl === null) return null;
3168
3502
  const origin = normalizeRemoteUrl(originUrl);
3169
- const originPath = origin === null ? path7.resolve(originUrl.replace(/^file:\/\//, "")) : null;
3503
+ const originPath = origin === null ? path8.resolve(originUrl.replace(/^file:\/\//, "")) : null;
3170
3504
  return remotes.find(
3171
- (remote) => origin === null ? path7.resolve(cwd, remote.url.replace(/^file:\/\//, "")) === originPath : normalizeRemoteUrl(remote.url) === origin
3505
+ (remote) => origin === null ? path8.resolve(cwd, remote.url.replace(/^file:\/\//, "")) === originPath : normalizeRemoteUrl(remote.url) === origin
3172
3506
  ) ?? null;
3173
3507
  };
3174
3508
  var LOG_LINES = 10;
@@ -3191,10 +3525,10 @@ var fetchBundle = (cwd, bundle) => {
3191
3525
  const before = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
3192
3526
  const previous = before.status === 0 ? before.stdout.trim() : null;
3193
3527
  if (previous !== bundle.tip) {
3194
- const dir = fs7.mkdtempSync(path7.join(os3.tmpdir(), "mend-pull-"));
3528
+ const dir = fs8.mkdtempSync(path8.join(os4.tmpdir(), "mend-pull-"));
3195
3529
  try {
3196
- const file = path7.join(dir, "change.bundle");
3197
- fs7.writeFileSync(file, bundle.bytes, { mode: 384 });
3530
+ const file = path8.join(dir, "change.bundle");
3531
+ fs8.writeFileSync(file, bundle.bytes, { mode: 384 });
3198
3532
  const fetched = git(cwd, [
3199
3533
  "fetch",
3200
3534
  "--no-tags",
@@ -3204,7 +3538,7 @@ var fetchBundle = (cwd, bundle) => {
3204
3538
  ]);
3205
3539
  if (fetched.status !== 0) return refusedWith(gitWords(fetched));
3206
3540
  } finally {
3207
- fs7.rmSync(dir, { recursive: true, force: true });
3541
+ fs8.rmSync(dir, { recursive: true, force: true });
3208
3542
  }
3209
3543
  }
3210
3544
  const after = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]).stdout.trim();
@@ -3323,7 +3657,7 @@ var pullCommand = async (api2, download2, args, cwd = process.cwd()) => {
3323
3657
 
3324
3658
  // src/login.ts
3325
3659
  import { spawn as spawn2 } from "node:child_process";
3326
- import * as os4 from "node:os";
3660
+ import * as os5 from "node:os";
3327
3661
 
3328
3662
  // src/pair.ts
3329
3663
  var paint2 = (code) => (text) => process.stdout.isTTY === true ? `\x1B[${code}m${text}\x1B[0m` : text;
@@ -3492,8 +3826,8 @@ var post = async (url, body) => {
3492
3826
  };
3493
3827
  var retryAfterSecondsOf = (json2) => {
3494
3828
  if (typeof json2 === "object" && json2 !== null && "retryAfterSeconds" in json2) {
3495
- const seconds = Number(json2.retryAfterSeconds);
3496
- if (Number.isFinite(seconds) && seconds > 0) return seconds;
3829
+ const seconds2 = Number(json2.retryAfterSeconds);
3830
+ if (Number.isFinite(seconds2) && seconds2 > 0) return seconds2;
3497
3831
  }
3498
3832
  return 5;
3499
3833
  };
@@ -3515,7 +3849,7 @@ var resolveServerUrl = async (args, deps) => {
3515
3849
  };
3516
3850
  var loginCommand = async (args, deps) => {
3517
3851
  const base = await resolveServerUrl(args, deps);
3518
- const started = await post(`${base}/api/cli/auth`, { name: os4.hostname() });
3852
+ const started = await post(`${base}/api/cli/auth`, { name: os5.hostname() });
3519
3853
  if (started.status === 429) {
3520
3854
  return fail3(
3521
3855
  `the server asked for a pause \u2014 try again in ${retryAfterSecondsOf(started.json)}s`
@@ -3566,8 +3900,8 @@ var loginCommand = async (args, deps) => {
3566
3900
  };
3567
3901
 
3568
3902
  // src/organization.ts
3569
- import * as fs8 from "node:fs";
3570
- import * as path8 from "node:path";
3903
+ import * as fs9 from "node:fs";
3904
+ import * as path9 from "node:path";
3571
3905
  var paint4 = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
3572
3906
  var dim4 = paint4("2");
3573
3907
  var green4 = paint4("32");
@@ -3631,7 +3965,7 @@ var scanFolder = (dir) => {
3631
3965
  const files = [];
3632
3966
  const skipped = [];
3633
3967
  const walk = (relative4) => {
3634
- const entries = fs8.readdirSync(path8.join(dir, relative4), { withFileTypes: true }).toSorted((a, b) => a.name.localeCompare(b.name));
3968
+ const entries = fs9.readdirSync(path9.join(dir, relative4), { withFileTypes: true }).toSorted((a, b) => a.name.localeCompare(b.name));
3635
3969
  for (const entry of entries) {
3636
3970
  const child = relative4 === "" ? entry.name : `${relative4}/${entry.name}`;
3637
3971
  if (entry.isSymbolicLink()) {
@@ -3640,7 +3974,7 @@ var scanFolder = (dir) => {
3640
3974
  if (SKIPPED_DIRECTORIES.has(entry.name)) skipped.push({ path: child, reason: "skipped" });
3641
3975
  else walk(child);
3642
3976
  } else if (entry.isFile()) {
3643
- const size = fs8.statSync(path8.join(dir, child)).size;
3977
+ const size = fs9.statSync(path9.join(dir, child)).size;
3644
3978
  if (size > FOLDER_FILE_BYTES) skipped.push({ path: child, reason: "over 1 MiB" });
3645
3979
  else files.push({ path: child, size });
3646
3980
  }
@@ -3693,7 +4027,7 @@ var folderCommand = async (api2, args) => {
3693
4027
  }
3694
4028
  case "push": {
3695
4029
  if (dir === void 0) return fail4("usage: mend folder push <name> <dir> [--replace]");
3696
- if (!fs8.existsSync(dir) || !fs8.statSync(dir).isDirectory()) {
4030
+ if (!fs9.existsSync(dir) || !fs9.statSync(dir).isDirectory()) {
3697
4031
  return fail4(`${dir} is not a directory`);
3698
4032
  }
3699
4033
  const folder = await folderNamed(api2, name);
@@ -3708,7 +4042,7 @@ var folderCommand = async (api2, args) => {
3708
4042
  files: batch.map((file) => ({
3709
4043
  path: file.path,
3710
4044
  // Read one batch at a time, so a large tree never sits in memory at once.
3711
- contentsBase64: fs8.readFileSync(path8.join(dir, file.path)).toString("base64")
4045
+ contentsBase64: fs9.readFileSync(path9.join(dir, file.path)).toString("base64")
3712
4046
  })),
3713
4047
  // --replace empties the folder with the first batch; the rest add to it.
3714
4048
  merge: !(replace && index === 0)
@@ -3892,8 +4226,8 @@ var operatorCommand = async (api2, tryApi, baseUrl, args) => {
3892
4226
  import { createHmac, randomBytes } from "node:crypto";
3893
4227
  import { readFile } from "node:fs/promises";
3894
4228
  import * as net2 from "node:net";
3895
- import * as os6 from "node:os";
3896
- import * as path10 from "node:path";
4229
+ import * as os7 from "node:os";
4230
+ import * as path11 from "node:path";
3897
4231
 
3898
4232
  // src/server-docker-volumes.ts
3899
4233
  import { createHash as createHash2 } from "node:crypto";
@@ -4111,9 +4445,9 @@ var verifyServerDockerVolumes = (runtime, input) => ownership(runtime, input, "v
4111
4445
 
4112
4446
  // src/server-store.ts
4113
4447
  import { randomUUID } from "node:crypto";
4114
- import * as fs9 from "node:fs";
4115
- import * as os5 from "node:os";
4116
- import * as path9 from "node:path";
4448
+ import * as fs10 from "node:fs";
4449
+ import * as os6 from "node:os";
4450
+ import * as path10 from "node:path";
4117
4451
  var ServerStoreError = class extends Error {
4118
4452
  _tag = "ServerStoreError";
4119
4453
  };
@@ -4134,28 +4468,28 @@ var attempt = (operation) => {
4134
4468
  var hasCode = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
4135
4469
  var readOptional = (file) => {
4136
4470
  try {
4137
- return fs9.readFileSync(file, "utf8");
4471
+ return fs10.readFileSync(file, "utf8");
4138
4472
  } catch (cause) {
4139
4473
  if (hasCode(cause, "ENOENT")) return null;
4140
4474
  throw cause;
4141
4475
  }
4142
4476
  };
4143
4477
  var syncDirectory = (directory) => {
4144
- const fd = fs9.openSync(directory, "r");
4478
+ const fd = fs10.openSync(directory, "r");
4145
4479
  try {
4146
- fs9.fsyncSync(fd);
4480
+ fs10.fsyncSync(fd);
4147
4481
  } finally {
4148
- fs9.closeSync(fd);
4482
+ fs10.closeSync(fd);
4149
4483
  }
4150
4484
  };
4151
4485
  var writeDurable = (file, content, mode = 384) => {
4152
- const fd = fs9.openSync(file, "wx", mode);
4486
+ const fd = fs10.openSync(file, "wx", mode);
4153
4487
  try {
4154
- fs9.writeFileSync(fd, content, "utf8");
4155
- fs9.fchmodSync(fd, mode);
4156
- fs9.fsyncSync(fd);
4488
+ fs10.writeFileSync(fd, content, "utf8");
4489
+ fs10.fchmodSync(fd, mode);
4490
+ fs10.fsyncSync(fd);
4157
4491
  } finally {
4158
- fs9.closeSync(fd);
4492
+ fs10.closeSync(fd);
4159
4493
  }
4160
4494
  };
4161
4495
  var fileKeys = ["identity", "config", "env", "compose", "postgresInit"];
@@ -4175,11 +4509,11 @@ var parseLockOwner = (raw) => {
4175
4509
  };
4176
4510
  var describeOwner = (lockDir) => {
4177
4511
  try {
4178
- const raw = JSON.parse(fs9.readFileSync(path9.join(lockDir, "owner.json"), "utf8"));
4512
+ const raw = JSON.parse(fs10.readFileSync(path10.join(lockDir, "owner.json"), "utf8"));
4179
4513
  const owner = parseLockOwner(raw);
4180
4514
  if (owner === null) return "owner metadata is invalid";
4181
4515
  const label = `PID ${owner.pid} on ${owner.hostname}`;
4182
- if (owner.hostname !== os5.hostname()) return label;
4516
+ if (owner.hostname !== os6.hostname()) return label;
4183
4517
  try {
4184
4518
  process.kill(owner.pid, 0);
4185
4519
  return `${label} is still live`;
@@ -4195,28 +4529,28 @@ var lockGuidance = (lockDir) => new ServerStoreError(
4195
4529
  );
4196
4530
  var acquireLock = (configDir, create) => {
4197
4531
  if (create) {
4198
- fs9.mkdirSync(configDir, { recursive: true, mode: 448 });
4199
- fs9.chmodSync(configDir, 448);
4200
- } else if (!fs9.existsSync(configDir)) {
4532
+ fs10.mkdirSync(configDir, { recursive: true, mode: 448 });
4533
+ fs10.chmodSync(configDir, 448);
4534
+ } else if (!fs10.existsSync(configDir)) {
4201
4535
  throw new ServerStoreError(
4202
4536
  "No Mend server is configured. Run mend server setup explicitly to install one."
4203
4537
  );
4204
4538
  }
4205
- const lockDir = path9.join(configDir, "server.lock");
4539
+ const lockDir = path10.join(configDir, "server.lock");
4206
4540
  try {
4207
- fs9.mkdirSync(lockDir, { mode: 448 });
4541
+ fs10.mkdirSync(lockDir, { mode: 448 });
4208
4542
  } catch (cause) {
4209
4543
  if (hasCode(cause, "EEXIST")) throw lockGuidance(lockDir);
4210
4544
  throw cause;
4211
4545
  }
4212
- const owned = fs9.statSync(lockDir);
4213
- const ownerFile = path9.join(lockDir, "owner.json");
4214
- const owner = `${JSON.stringify({ pid: process.pid, hostname: os5.hostname(), token: randomUUID() })}
4546
+ const owned = fs10.statSync(lockDir);
4547
+ const ownerFile = path10.join(lockDir, "owner.json");
4548
+ const owner = `${JSON.stringify({ pid: process.pid, hostname: os6.hostname(), token: randomUUID() })}
4215
4549
  `;
4216
4550
  let open = true;
4217
4551
  const assertOwned = () => {
4218
- const current = fs9.statSync(lockDir);
4219
- if (!open || current.dev !== owned.dev || current.ino !== owned.ino || fs9.readFileSync(ownerFile, "utf8") !== owner) {
4552
+ const current = fs10.statSync(lockDir);
4553
+ if (!open || current.dev !== owned.dev || current.ino !== owned.ino || fs10.readFileSync(ownerFile, "utf8") !== owner) {
4220
4554
  throw new ServerStoreError(
4221
4555
  "Server lock ownership was lost. Stop and check for another server command before retrying."
4222
4556
  );
@@ -4232,8 +4566,8 @@ var acquireLock = (configDir, create) => {
4232
4566
  release: () => {
4233
4567
  const result = attempt(() => {
4234
4568
  assertOwned();
4235
- fs9.unlinkSync(ownerFile);
4236
- fs9.rmdirSync(lockDir);
4569
+ fs10.unlinkSync(ownerFile);
4570
+ fs10.rmdirSync(lockDir);
4237
4571
  });
4238
4572
  open = false;
4239
4573
  return result;
@@ -4242,7 +4576,7 @@ var acquireLock = (configDir, create) => {
4242
4576
  };
4243
4577
  var readIdentity = (paths) => {
4244
4578
  const identity = readOptional(paths.identity);
4245
- if (identity === null && (fs9.existsSync(paths.active) || fs9.existsSync(paths.generations) && fs9.readdirSync(paths.generations).length > 0)) {
4579
+ if (identity === null && (fs10.existsSync(paths.active) || fs10.existsSync(paths.generations) && fs10.readdirSync(paths.generations).length > 0)) {
4246
4580
  throw new ServerStoreError(
4247
4581
  "Server identity.env is missing. Restore it from a retained generation or backup; refusing to generate replacement credentials."
4248
4582
  );
@@ -4252,7 +4586,7 @@ var readIdentity = (paths) => {
4252
4586
  var readActive = (paths) => {
4253
4587
  let target;
4254
4588
  try {
4255
- target = fs9.readlinkSync(paths.active);
4589
+ target = fs10.readlinkSync(paths.active);
4256
4590
  } catch (cause) {
4257
4591
  if (hasCode(cause, "ENOENT")) return null;
4258
4592
  throw cause;
@@ -4262,13 +4596,13 @@ var readActive = (paths) => {
4262
4596
  "Server active pointer is invalid. Restore the symlink to a retained generation before retrying."
4263
4597
  );
4264
4598
  }
4265
- const directory = path9.join(paths.configDir, target);
4599
+ const directory = path10.join(paths.configDir, target);
4266
4600
  const files = {
4267
- identity: fs9.readFileSync(path9.join(directory, fileNames.identity), "utf8"),
4268
- config: fs9.readFileSync(path9.join(directory, fileNames.config), "utf8"),
4269
- env: fs9.readFileSync(path9.join(directory, fileNames.env), "utf8"),
4270
- compose: fs9.readFileSync(path9.join(directory, fileNames.compose), "utf8"),
4271
- postgresInit: fs9.readFileSync(path9.join(directory, fileNames.postgresInit), "utf8")
4601
+ identity: fs10.readFileSync(path10.join(directory, fileNames.identity), "utf8"),
4602
+ config: fs10.readFileSync(path10.join(directory, fileNames.config), "utf8"),
4603
+ env: fs10.readFileSync(path10.join(directory, fileNames.env), "utf8"),
4604
+ compose: fs10.readFileSync(path10.join(directory, fileNames.compose), "utf8"),
4605
+ postgresInit: fs10.readFileSync(path10.join(directory, fileNames.postgresInit), "utf8")
4272
4606
  };
4273
4607
  if (readIdentity(paths) !== files.identity) {
4274
4608
  throw new ServerStoreError(
@@ -4278,20 +4612,20 @@ var readActive = (paths) => {
4278
4612
  return { directory, files };
4279
4613
  };
4280
4614
  var publishIdentity = (paths, identity) => {
4281
- const temporary = path9.join(paths.configDir, `.identity-${randomUUID()}`);
4615
+ const temporary = path10.join(paths.configDir, `.identity-${randomUUID()}`);
4282
4616
  writeDurable(temporary, identity);
4283
- fs9.linkSync(temporary, paths.identity);
4617
+ fs10.linkSync(temporary, paths.identity);
4284
4618
  syncDirectory(paths.configDir);
4285
- fs9.unlinkSync(temporary);
4619
+ fs10.unlinkSync(temporary);
4286
4620
  };
4287
4621
  var prepareGeneration = (paths, files) => {
4288
- fs9.mkdirSync(paths.generations, { recursive: true, mode: 448 });
4622
+ fs10.mkdirSync(paths.generations, { recursive: true, mode: 448 });
4289
4623
  const generationName = `gen-${randomUUID()}`;
4290
- const directory = path9.join(paths.generations, generationName);
4291
- fs9.mkdirSync(directory, { mode: 448 });
4624
+ const directory = path10.join(paths.generations, generationName);
4625
+ fs10.mkdirSync(directory, { mode: 448 });
4292
4626
  for (const key of fileKeys) {
4293
4627
  writeDurable(
4294
- path9.join(directory, fileNames[key]),
4628
+ path10.join(directory, fileNames[key]),
4295
4629
  files[key],
4296
4630
  key === "postgresInit" ? 493 : 384
4297
4631
  );
@@ -4302,18 +4636,18 @@ var prepareGeneration = (paths, files) => {
4302
4636
  return { directory, files };
4303
4637
  };
4304
4638
  var activateGeneration = (paths, generation) => {
4305
- const relative4 = path9.relative(paths.configDir, generation.directory);
4306
- if (!/^generations\/gen-[0-9a-f-]{36}$/.test(relative4) || readIdentity(paths) !== fs9.readFileSync(path9.join(generation.directory, "identity.env"), "utf8")) {
4639
+ const relative4 = path10.relative(paths.configDir, generation.directory);
4640
+ if (!/^generations\/gen-[0-9a-f-]{36}$/.test(relative4) || readIdentity(paths) !== fs10.readFileSync(path10.join(generation.directory, "identity.env"), "utf8")) {
4307
4641
  throw new ServerStoreError("Cannot activate a generation outside this installation identity.");
4308
4642
  }
4309
4643
  for (const key of fileKeys) {
4310
- if (fs9.readFileSync(path9.join(generation.directory, fileNames[key]), "utf8") !== generation.files[key]) {
4644
+ if (fs10.readFileSync(path10.join(generation.directory, fileNames[key]), "utf8") !== generation.files[key]) {
4311
4645
  throw new ServerStoreError("Cannot activate an incomplete or changed server generation.");
4312
4646
  }
4313
4647
  }
4314
- const pointer = path9.join(paths.configDir, `.active-${randomUUID()}`);
4315
- fs9.symlinkSync(relative4, pointer);
4316
- fs9.renameSync(pointer, paths.active);
4648
+ const pointer = path10.join(paths.configDir, `.active-${randomUUID()}`);
4649
+ fs10.symlinkSync(relative4, pointer);
4650
+ fs10.renameSync(pointer, paths.active);
4317
4651
  syncDirectory(paths.configDir);
4318
4652
  };
4319
4653
  var prepareFiles = (paths, files) => {
@@ -4322,7 +4656,7 @@ var prepareFiles = (paths, files) => {
4322
4656
  throw new ServerStoreError("Server identity already exists; refusing to replace credentials.");
4323
4657
  }
4324
4658
  const active = readActive(paths);
4325
- if (active !== null && fileKeys.every((key) => files[key] === active.files[key]) && (fs9.statSync(path9.join(active.directory, fileNames.postgresInit)).mode & 4095) === 493)
4659
+ if (active !== null && fileKeys.every((key) => files[key] === active.files[key]) && (fs10.statSync(path10.join(active.directory, fileNames.postgresInit)).mode & 4095) === 493)
4326
4660
  return active;
4327
4661
  if (identity === null) publishIdentity(paths, files.identity);
4328
4662
  return prepareGeneration(paths, files);
@@ -4335,9 +4669,9 @@ var commitGeneration = (paths, files) => {
4335
4669
  var createStore = (configDir, lock) => {
4336
4670
  const paths = {
4337
4671
  configDir,
4338
- identity: path9.join(configDir, "identity.env"),
4339
- generations: path9.join(configDir, "generations"),
4340
- active: path9.join(configDir, "active")
4672
+ identity: path10.join(configDir, "identity.env"),
4673
+ generations: path10.join(configDir, "generations"),
4674
+ active: path10.join(configDir, "active")
4341
4675
  };
4342
4676
  const whileOwned = (operation) => attempt(() => {
4343
4677
  lock.assertOwned();
@@ -4350,13 +4684,13 @@ var createStore = (configDir, lock) => {
4350
4684
  prepare: (files) => whileOwned(() => prepareFiles(paths, files)),
4351
4685
  activate: (generation) => whileOwned(() => activateGeneration(paths, generation)),
4352
4686
  createBackup: (previous, target) => whileOwned(() => {
4353
- const backups = path9.join(configDir, "backups");
4354
- fs9.mkdirSync(backups, { recursive: true, mode: 448 });
4355
- fs9.chmodSync(backups, 448);
4356
- const directory = path9.join(backups, `upgrade-${randomUUID()}`);
4357
- fs9.mkdirSync(directory, { mode: 448 });
4687
+ const backups = path10.join(configDir, "backups");
4688
+ fs10.mkdirSync(backups, { recursive: true, mode: 448 });
4689
+ fs10.chmodSync(backups, 448);
4690
+ const directory = path10.join(backups, `upgrade-${randomUUID()}`);
4691
+ fs10.mkdirSync(directory, { mode: 448 });
4358
4692
  writeDurable(
4359
- path9.join(directory, "recovery.json"),
4693
+ path10.join(directory, "recovery.json"),
4360
4694
  `${JSON.stringify(
4361
4695
  {
4362
4696
  previousGeneration: previous.directory,
@@ -4372,21 +4706,21 @@ var createStore = (configDir, lock) => {
4372
4706
  syncDirectory(directory);
4373
4707
  syncDirectory(backups);
4374
4708
  syncDirectory(configDir);
4375
- const partialFile = path9.join(directory, "database.sql.partial");
4709
+ const partialFile = path10.join(directory, "database.sql.partial");
4376
4710
  return {
4377
4711
  directory,
4378
4712
  partialFile,
4379
4713
  complete: () => whileOwned(() => {
4380
- if (fs9.statSync(partialFile).size === 0)
4714
+ if (fs10.statSync(partialFile).size === 0)
4381
4715
  throw new ServerStoreError("Database backup is empty; refusing target startup.");
4382
- const fd = fs9.openSync(partialFile, "r");
4716
+ const fd = fs10.openSync(partialFile, "r");
4383
4717
  try {
4384
- fs9.fchmodSync(fd, 384);
4385
- fs9.fsyncSync(fd);
4718
+ fs10.fchmodSync(fd, 384);
4719
+ fs10.fsyncSync(fd);
4386
4720
  } finally {
4387
- fs9.closeSync(fd);
4721
+ fs10.closeSync(fd);
4388
4722
  }
4389
- fs9.renameSync(partialFile, path9.join(directory, "database.sql"));
4723
+ fs10.renameSync(partialFile, path10.join(directory, "database.sql"));
4390
4724
  syncDirectory(directory);
4391
4725
  })
4392
4726
  };
@@ -4395,7 +4729,7 @@ var createStore = (configDir, lock) => {
4395
4729
  };
4396
4730
  var refuseFlatLayout = (configDir) => {
4397
4731
  if (["server.json", "server.env", "compose.yaml", "postgres-init.sh"].some(
4398
- (name) => fs9.existsSync(path9.join(configDir, name))
4732
+ (name) => fs10.existsSync(path10.join(configDir, name))
4399
4733
  )) {
4400
4734
  throw new ServerStoreError(
4401
4735
  "Unreleased flat server configuration found. Preserve its credentials and volumes; migrate it to the generation layout before retrying. Setup will not replace this identity."
@@ -4403,7 +4737,7 @@ var refuseFlatLayout = (configDir) => {
4403
4737
  }
4404
4738
  };
4405
4739
  var withServerStore = async (directory, operation, options = {}) => {
4406
- const configDir = path9.resolve(directory);
4740
+ const configDir = path10.resolve(directory);
4407
4741
  let lock;
4408
4742
  let result;
4409
4743
  try {
@@ -4420,9 +4754,9 @@ var withServerStore = async (directory, operation, options = {}) => {
4420
4754
  };
4421
4755
 
4422
4756
  // src/version.ts
4423
- import * as fs10 from "node:fs";
4757
+ import * as fs11 from "node:fs";
4424
4758
  var cliVersion = () => {
4425
- const raw = fs10.readFileSync(new URL("../package.json", import.meta.url), "utf8");
4759
+ const raw = fs11.readFileSync(new URL("../package.json", import.meta.url), "utf8");
4426
4760
  const parsed = JSON.parse(raw);
4427
4761
  if (typeof parsed === "object" && parsed !== null && "version" in parsed) {
4428
4762
  const version = parsed.version;
@@ -4554,21 +4888,21 @@ var parseSetupOptions = (args) => {
4554
4888
  previous.push(value);
4555
4889
  values.set(flag, previous);
4556
4890
  }
4557
- const flagValue2 = (flag) => values.get(flag)?.[0];
4558
- const version = flagValue2("--version");
4559
- const appPort = flagValue2("--port");
4560
- const sshPort = flagValue2("--ssh-port");
4891
+ const flagValue3 = (flag) => values.get(flag)?.[0];
4892
+ const version = flagValue3("--version");
4893
+ const appPort = flagValue3("--port");
4894
+ const sshPort = flagValue3("--ssh-port");
4561
4895
  const origins = values.get("--origin");
4562
4896
  return {
4563
- context: flagValue2("--context"),
4897
+ context: flagValue3("--context"),
4564
4898
  version: version === void 0 ? void 0 : parseVersion(version),
4565
- bind: flagValue2("--bind"),
4566
- url: flagValue2("--url"),
4899
+ bind: flagValue3("--bind"),
4900
+ url: flagValue3("--url"),
4567
4901
  origins: origins === void 0 ? void 0 : origins,
4568
4902
  appPort: appPort === void 0 ? void 0 : parsePort(appPort, "--port"),
4569
4903
  sshPort: sshPort === void 0 ? void 0 : parsePort(sshPort, "--ssh-port"),
4570
- dockerSocket: flagValue2("--docker-socket"),
4571
- assetsDir: flagValue2("--assets-dir"),
4904
+ dockerSocket: flagValue3("--docker-socket"),
4905
+ assetsDir: flagValue3("--assets-dir"),
4572
4906
  offline: values.has("--offline")
4573
4907
  };
4574
4908
  };
@@ -5125,7 +5459,7 @@ var resolveServerVersion = async (runtime, options, existing) => {
5125
5459
  return version;
5126
5460
  };
5127
5461
  var parseDockerSocketPath = (socket) => {
5128
- if (!path10.isAbsolute(socket) || /[\r\n$'"#:\\]/.test(socket)) {
5462
+ if (!path11.isAbsolute(socket) || /[\r\n$'"#:\\]/.test(socket)) {
5129
5463
  throw setupError(
5130
5464
  "--docker-socket must be an absolute path without line breaks or Compose interpolation characters."
5131
5465
  );
@@ -5151,8 +5485,8 @@ var resolveAssets = async (runtime, serverVersion, existing, store, options) =>
5151
5485
  if (options.assetsDir !== void 0) {
5152
5486
  try {
5153
5487
  const [compose2, postgresInit2] = await Promise.all([
5154
- readFile(path10.join(options.assetsDir, COMPOSE_ASSET), "utf8"),
5155
- readFile(path10.join(options.assetsDir, POSTGRES_INIT_ASSET), "utf8")
5488
+ readFile(path11.join(options.assetsDir, COMPOSE_ASSET), "utf8"),
5489
+ readFile(path11.join(options.assetsDir, POSTGRES_INIT_ASSET), "utf8")
5156
5490
  ]);
5157
5491
  validateComposeAsset(compose2);
5158
5492
  validatePostgresAsset(postgresInit2);
@@ -5344,6 +5678,19 @@ var setupServer = async (args, runtime, store) => {
5344
5678
  };
5345
5679
  const assets = await resolveAssets(runtime, serverVersion, existing, store, options);
5346
5680
  const bucket = composeBucket(assets.compose);
5681
+ if (bucket !== void 0 && runtime.dockerDaemonFacts !== void 0) {
5682
+ const info = await runtime.run("docker", [
5683
+ "--context",
5684
+ selectedContext.name,
5685
+ "info",
5686
+ "--format",
5687
+ "{{json .}}"
5688
+ ]);
5689
+ const line = dockerShutdownSetupLine(
5690
+ readShutdownTimeout(runtime.dockerDaemonFacts(info.status === 0 ? info.stdout : null))
5691
+ );
5692
+ if (line !== null) runtime.writeLine(line);
5693
+ }
5347
5694
  const config = {
5348
5695
  ...configWithoutBucket,
5349
5696
  ...bucket === void 0 ? {} : { bucket }
@@ -5665,10 +6012,10 @@ var manageServer = async (command, args, runtime, store) => {
5665
6012
  };
5666
6013
  var nodeServerRuntime = () => {
5667
6014
  const environment = { ...process.env };
5668
- const home = os6.homedir();
5669
- const configHome = environment["XDG_CONFIG_HOME"] ?? path10.join(home, ".config");
6015
+ const home = os7.homedir();
6016
+ const configHome = environment["XDG_CONFIG_HOME"] ?? path11.join(home, ".config");
5670
6017
  return {
5671
- configDir: path10.join(configHome, "mend"),
6018
+ configDir: path11.join(configHome, "mend"),
5672
6019
  platform: process.platform,
5673
6020
  cliVersion: cliVersion(),
5674
6021
  run: (command, args, options) => runServerProcess(command, args, environment, options),
@@ -5687,7 +6034,8 @@ var nodeServerRuntime = () => {
5687
6034
  randomBytes,
5688
6035
  sleep: (milliseconds) => new Promise((resolve7) => setTimeout(resolve7, milliseconds)),
5689
6036
  writeLine: (line) => process.stdout.write(`${line}
5690
- `)
6037
+ `),
6038
+ dockerDaemonFacts: hostDockerDaemonFacts
5691
6039
  };
5692
6040
  };
5693
6041
  var serverCommand = async (args, runtime = nodeServerRuntime()) => {
@@ -6120,8 +6468,8 @@ var createServiceTunnels = (options) => {
6120
6468
  };
6121
6469
 
6122
6470
  // src/skills.ts
6123
- import * as fs11 from "node:fs";
6124
- import * as path11 from "node:path";
6471
+ import * as fs12 from "node:fs";
6472
+ import * as path12 from "node:path";
6125
6473
  var DEFAULT_SKILLS_DIR = ".agents/skills";
6126
6474
  var MAX_SKILL_FILES = 64;
6127
6475
  var MAX_SKILL_FILE_BYTES = 512 * 1024;
@@ -6144,12 +6492,12 @@ var readFrontmatterDescription = (contents) => {
6144
6492
  var walkFiles = (root) => {
6145
6493
  const found = [];
6146
6494
  const visit = (dir) => {
6147
- for (const entry of fs11.readdirSync(dir, { withFileTypes: true })) {
6495
+ for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
6148
6496
  if (IGNORED_DIRS.has(entry.name)) continue;
6149
- const absolute = path11.join(dir, entry.name);
6497
+ const absolute = path12.join(dir, entry.name);
6150
6498
  let stat;
6151
6499
  try {
6152
- stat = fs11.statSync(absolute);
6500
+ stat = fs12.statSync(absolute);
6153
6501
  } catch {
6154
6502
  continue;
6155
6503
  }
@@ -6163,7 +6511,7 @@ var walkFiles = (root) => {
6163
6511
  var scanSkillLibrary = (root) => {
6164
6512
  let entries;
6165
6513
  try {
6166
- entries = fs11.readdirSync(root, { withFileTypes: true });
6514
+ entries = fs12.readdirSync(root, { withFileTypes: true });
6167
6515
  } catch {
6168
6516
  return { error: `${root} is not a readable directory` };
6169
6517
  }
@@ -6171,23 +6519,23 @@ var scanSkillLibrary = (root) => {
6171
6519
  const notes = [];
6172
6520
  for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
6173
6521
  if (entry.name.startsWith(".")) continue;
6174
- const skillDir = path11.join(root, entry.name);
6522
+ const skillDir = path12.join(root, entry.name);
6175
6523
  let stat;
6176
6524
  try {
6177
- stat = fs11.statSync(skillDir);
6525
+ stat = fs12.statSync(skillDir);
6178
6526
  } catch {
6179
6527
  continue;
6180
6528
  }
6181
6529
  if (!stat.isDirectory()) continue;
6182
- if (!fs11.existsSync(path11.join(skillDir, "SKILL.md"))) {
6530
+ if (!fs12.existsSync(path12.join(skillDir, "SKILL.md"))) {
6183
6531
  notes.push({ skill: entry.name, message: "no SKILL.md \u2014 skipped" });
6184
6532
  continue;
6185
6533
  }
6186
6534
  const files = [];
6187
6535
  let bytes = 0;
6188
6536
  for (const absolute of walkFiles(skillDir)) {
6189
- const relative4 = path11.relative(skillDir, absolute).split(path11.sep).join("/");
6190
- const buffer = fs11.readFileSync(absolute);
6537
+ const relative4 = path12.relative(skillDir, absolute).split(path12.sep).join("/");
6538
+ const buffer = fs12.readFileSync(absolute);
6191
6539
  if (buffer.byteLength > MAX_SKILL_FILE_BYTES) {
6192
6540
  notes.push({ skill: entry.name, message: `${relative4} is over 512KB \u2014 skipped` });
6193
6541
  continue;
@@ -6222,16 +6570,16 @@ var scanSkillLibrary = (root) => {
6222
6570
  };
6223
6571
 
6224
6572
  // src/ssh-setup.ts
6225
- import * as os8 from "node:os";
6226
- import * as path13 from "node:path";
6573
+ import * as os9 from "node:os";
6574
+ import * as path14 from "node:path";
6227
6575
 
6228
6576
  // ../../packages/workspace-ssh/src/index.ts
6229
- import { spawnSync as spawnSync3 } from "node:child_process";
6577
+ import { spawnSync as spawnSync4 } from "node:child_process";
6230
6578
  import { createHash as createHash3 } from "node:crypto";
6231
- import * as fs12 from "node:fs";
6579
+ import * as fs13 from "node:fs";
6232
6580
  import { isIP as isIP2 } from "node:net";
6233
- import * as os7 from "node:os";
6234
- import * as path12 from "node:path";
6581
+ import * as os8 from "node:os";
6582
+ import * as path13 from "node:path";
6235
6583
  var LEGACY_BLOCK_BEGIN = "# >>> mend workspace ssh (managed by `mend ssh setup`) >>>";
6236
6584
  var LEGACY_BLOCK_END = "# <<< mend workspace ssh <<<";
6237
6585
  var WorkspaceSshError = class extends Error {
@@ -6318,8 +6666,8 @@ var blockBegin = (alias) => `# >>> mend workspace ssh ${alias} (managed) >>>`;
6318
6666
  var blockEnd = (alias) => `# <<< mend workspace ssh ${alias} <<<`;
6319
6667
  var hasControls = (value) => /[\p{Cc}\p{Zl}\p{Zp}]/u.test(value);
6320
6668
  var parseIdentityPath = (value) => {
6321
- const resolved = path12.resolve(
6322
- value.startsWith("~/") ? path12.join(os7.homedir(), value.slice(2)) : value
6669
+ const resolved = path13.resolve(
6670
+ value.startsWith("~/") ? path13.join(os8.homedir(), value.slice(2)) : value
6323
6671
  );
6324
6672
  if (value === "" || hasControls(value) || hasControls(resolved) || value.includes("${") || resolved.includes("${")) {
6325
6673
  return failure(
@@ -6465,7 +6813,7 @@ var workspaceSshPublicKeyFingerprint = (publicKey) => {
6465
6813
  var publicKeyIdentity = (publicKey) => publicKey.trim().split(/\s+/).slice(0, 2).join(" ");
6466
6814
  var agentKeys = () => {
6467
6815
  if (!process.env["SSH_AUTH_SOCK"]) return [];
6468
- const listed = spawnSync3("ssh-add", ["-L"], {
6816
+ const listed = spawnSync4("ssh-add", ["-L"], {
6469
6817
  encoding: "utf8",
6470
6818
  timeout: 3e3,
6471
6819
  stdio: ["ignore", "pipe", "pipe"],
@@ -6475,7 +6823,7 @@ var agentKeys = () => {
6475
6823
  };
6476
6824
  var readText = (file) => {
6477
6825
  try {
6478
- return success(fs12.readFileSync(file, "utf8"));
6826
+ return success(fs13.readFileSync(file, "utf8"));
6479
6827
  } catch (cause) {
6480
6828
  const error = cause instanceof Error && "code" in cause ? cause.code : void 0;
6481
6829
  return error === "ENOENT" ? success("") : failure(new WorkspaceSshError("key", `Could not read ${file}.`, cause));
@@ -6502,7 +6850,7 @@ var readRequiredKey = (keyPath, source) => {
6502
6850
  const privatePath = publicPath.slice(0, -".pub".length);
6503
6851
  const read = readText(publicPath);
6504
6852
  if (!read.ok) return read;
6505
- const derived = spawnSync3("ssh-keygen", ["-y", "-P", "", "-f", privatePath], {
6853
+ const derived = spawnSync4("ssh-keygen", ["-y", "-P", "", "-f", privatePath], {
6506
6854
  encoding: "utf8",
6507
6855
  timeout: 3e3,
6508
6856
  stdio: ["ignore", "pipe", "pipe"],
@@ -6533,19 +6881,19 @@ var readRequiredKey = (keyPath, source) => {
6533
6881
  var readOptionalKey = (keyPath) => {
6534
6882
  const parsed = parseIdentityPath(keyPath);
6535
6883
  if (!parsed.ok) return parsed;
6536
- if (!fs12.existsSync(parsed.value) && !fs12.existsSync(publicKeyPath(parsed.value)))
6884
+ if (!fs13.existsSync(parsed.value) && !fs13.existsSync(publicKeyPath(parsed.value)))
6537
6885
  return success(null);
6538
6886
  return readRequiredKey(parsed.value, "existing");
6539
6887
  };
6540
6888
  var pinAgentKey = (configHome, publicKey) => {
6541
- const identityFile = path12.resolve(
6889
+ const identityFile = path13.resolve(
6542
6890
  configHome,
6543
6891
  "ssh",
6544
6892
  `agent-${hashIdentity(publicKeyIdentity(publicKey))}.pub`
6545
6893
  );
6546
6894
  try {
6547
- fs12.mkdirSync(path12.dirname(identityFile), { recursive: true, mode: 448 });
6548
- fs12.writeFileSync(identityFile, `${publicKey}
6895
+ fs13.mkdirSync(path13.dirname(identityFile), { recursive: true, mode: 448 });
6896
+ fs13.writeFileSync(identityFile, `${publicKey}
6549
6897
  `, { mode: 384 });
6550
6898
  return success({ publicKey, identityFile, source: "agent" });
6551
6899
  } catch (cause) {
@@ -6556,15 +6904,15 @@ var pinAgentKey = (configHome, publicKey) => {
6556
6904
  };
6557
6905
  var generateDedicatedKey = (privatePath) => {
6558
6906
  try {
6559
- fs12.mkdirSync(path12.dirname(privatePath), { recursive: true, mode: 448 });
6907
+ fs13.mkdirSync(path13.dirname(privatePath), { recursive: true, mode: 448 });
6560
6908
  } catch (cause) {
6561
6909
  return failure(
6562
- new WorkspaceSshError("key", `Could not create ${path12.dirname(privatePath)}.`, cause)
6910
+ new WorkspaceSshError("key", `Could not create ${path13.dirname(privatePath)}.`, cause)
6563
6911
  );
6564
6912
  }
6565
- const generated = spawnSync3(
6913
+ const generated = spawnSync4(
6566
6914
  "ssh-keygen",
6567
- ["-t", "ed25519", "-f", privatePath, "-N", "", "-C", `mend-${os7.hostname()}`],
6915
+ ["-t", "ed25519", "-f", privatePath, "-N", "", "-C", `mend-${os8.hostname()}`],
6568
6916
  { encoding: "utf8", timeout: 3e3, stdio: ["ignore", "pipe", "pipe"] }
6569
6917
  );
6570
6918
  if (generated.status !== 0) {
@@ -6588,7 +6936,7 @@ var generateDedicatedKey = (privatePath) => {
6588
6936
  return fresh;
6589
6937
  };
6590
6938
  var dedicatedKey = (configHome, create) => {
6591
- const privatePath = path12.join(configHome, "ssh", "id_ed25519");
6939
+ const privatePath = path13.join(configHome, "ssh", "id_ed25519");
6592
6940
  const existing = readOptionalKey(privatePath);
6593
6941
  if (existing.ok === false) return failure(existing.error);
6594
6942
  if (existing.value !== null || !create) return existing;
@@ -6636,10 +6984,10 @@ var writeWorkspaceSshConfig = (configFile, target, identityFile) => {
6636
6984
  const reconciled = reconcileWorkspaceSshConfig(before.value, target, identityFile);
6637
6985
  if (reconciled.ok === false) return failure(reconciled.error);
6638
6986
  try {
6639
- fs12.mkdirSync(path12.dirname(configFile), { recursive: true, mode: 448 });
6640
- fs12.chmodSync(path12.dirname(configFile), 448);
6641
- fs12.writeFileSync(configFile, reconciled.value, { mode: 384 });
6642
- fs12.chmodSync(configFile, 384);
6987
+ fs13.mkdirSync(path13.dirname(configFile), { recursive: true, mode: 448 });
6988
+ fs13.chmodSync(path13.dirname(configFile), 448);
6989
+ fs13.writeFileSync(configFile, reconciled.value, { mode: 384 });
6990
+ fs13.chmodSync(configFile, 384);
6643
6991
  return success(void 0);
6644
6992
  } catch (cause) {
6645
6993
  return failure(new WorkspaceSshError("config", `Could not write ${configFile}.`, cause));
@@ -6652,8 +7000,8 @@ var dim5 = ansi("2");
6652
7000
  var green5 = ansi("32");
6653
7001
  var warn = ansi("33");
6654
7002
  var say5 = (line) => console.log(line);
6655
- var sshConfigPath = () => path13.join(os8.homedir(), ".ssh", "config");
6656
- var flagValue = (args, flag) => {
7003
+ var sshConfigPath = () => path14.join(os9.homedir(), ".ssh", "config");
7004
+ var flagValue2 = (args, flag) => {
6657
7005
  const index = args.indexOf(flag);
6658
7006
  return index === -1 || args[index + 1] === void 0 ? null : String(args[index + 1]);
6659
7007
  };
@@ -6670,7 +7018,7 @@ var showStatus = async (api2, cliHome, serverUrl, args) => {
6670
7018
  const parsedTarget = parseWorkspaceSshTarget({
6671
7019
  serverUrl,
6672
7020
  publishedPort: view.gateway.port,
6673
- hostnameOverride: flagValue(args, "--host")
7021
+ hostnameOverride: flagValue2(args, "--host")
6674
7022
  });
6675
7023
  if (parsedTarget.ok === false) return showFailure(parsedTarget.error.message);
6676
7024
  const config = readWorkspaceSshConfig(sshConfigPath());
@@ -6707,14 +7055,14 @@ var setup = async (api2, cliHome, serverUrl, args) => {
6707
7055
  const parsedTarget = parseWorkspaceSshTarget({
6708
7056
  serverUrl,
6709
7057
  publishedPort: view.gateway.port,
6710
- hostnameOverride: flagValue(args, "--host")
7058
+ hostnameOverride: flagValue2(args, "--host")
6711
7059
  });
6712
7060
  if (parsedTarget.ok === false) return showFailure(parsedTarget.error.message);
6713
7061
  const config = readWorkspaceSshConfig(sshConfigPath());
6714
7062
  if (config.ok === false) return showFailure(config.error.message);
6715
7063
  const picked = pickWorkspaceSshKey({
6716
7064
  configHome: cliHome,
6717
- explicitKeyPath: flagValue(args, "--key"),
7065
+ explicitKeyPath: flagValue2(args, "--key"),
6718
7066
  configuredIdentityFile: configuredWorkspaceSshIdentityFile(config.value, parsedTarget.value),
6719
7067
  create: true
6720
7068
  });
@@ -6722,7 +7070,7 @@ var setup = async (api2, cliHome, serverUrl, args) => {
6722
7070
  if (picked.value === null) return showFailure("No workspace SSH key is available.");
6723
7071
  const registered = await api2("POST", "/workspace-ssh/keys", {
6724
7072
  publicKey: picked.value.publicKey,
6725
- name: os8.hostname()
7073
+ name: os9.hostname()
6726
7074
  });
6727
7075
  const sourceLabel = {
6728
7076
  explicit: "from --key",
@@ -6764,8 +7112,8 @@ var sshCommand = async (args, api2, cliHome, serverUrl) => {
6764
7112
  };
6765
7113
 
6766
7114
  // src/uninstall.ts
6767
- import * as fs13 from "node:fs";
6768
- import * as path14 from "node:path";
7115
+ import * as fs14 from "node:fs";
7116
+ import * as path15 from "node:path";
6769
7117
  var UNINSTALL_USAGE = "usage: mend uninstall [--all | --server | --home] [--yes]";
6770
7118
  var parseUninstallArgs = (args) => {
6771
7119
  let scope = null;
@@ -6787,14 +7135,14 @@ var parseUninstallArgs = (args) => {
6787
7135
  var SERVER_FILES = ["identity.env", "active", "generations", "backups"];
6788
7136
  var countEntries = (directory) => {
6789
7137
  try {
6790
- return fs13.readdirSync(directory).length;
7138
+ return fs14.readdirSync(directory).length;
6791
7139
  } catch {
6792
7140
  return 0;
6793
7141
  }
6794
7142
  };
6795
7143
  var exists = (file) => {
6796
7144
  try {
6797
- fs13.lstatSync(file);
7145
+ fs14.lstatSync(file);
6798
7146
  return true;
6799
7147
  } catch {
6800
7148
  return false;
@@ -6803,7 +7151,7 @@ var exists = (file) => {
6803
7151
  var managedBlockCount = (sshConfigFile) => {
6804
7152
  let config;
6805
7153
  try {
6806
- config = fs13.readFileSync(sshConfigFile, "utf8");
7154
+ config = fs14.readFileSync(sshConfigFile, "utf8");
6807
7155
  } catch {
6808
7156
  return 0;
6809
7157
  }
@@ -6827,27 +7175,27 @@ var describeUninstall = async (runtime, scope) => {
6827
7175
  if (!read.error.message.startsWith("No Mend server is configured")) throw read.error;
6828
7176
  server = "none";
6829
7177
  } else if (read.value === null) {
6830
- server = exists(path14.join(configDir, "identity.env")) ? {
7178
+ server = exists(path15.join(configDir, "identity.env")) ? {
6831
7179
  version: "(no active generation)",
6832
7180
  appUrl: "",
6833
7181
  dockerContext: "",
6834
- generations: countEntries(path14.join(configDir, "generations")),
6835
- backups: countEntries(path14.join(configDir, "backups"))
7182
+ generations: countEntries(path15.join(configDir, "generations")),
7183
+ backups: countEntries(path15.join(configDir, "backups"))
6836
7184
  } : "none";
6837
7185
  } else {
6838
7186
  server = {
6839
7187
  version: read.value.config.serverVersion,
6840
7188
  appUrl: read.value.config.appUrl,
6841
7189
  dockerContext: read.value.config.dockerContext,
6842
- generations: countEntries(path14.join(configDir, "generations")),
6843
- backups: countEntries(path14.join(configDir, "backups"))
7190
+ generations: countEntries(path15.join(configDir, "generations")),
7191
+ backups: countEntries(path15.join(configDir, "backups"))
6844
7192
  };
6845
7193
  }
6846
7194
  }
6847
7195
  let home = null;
6848
7196
  if (scope !== "server") {
6849
- const cliConfig = path14.join(runtime.cliHome, "cli.json");
6850
- const sshDirectory = path14.join(runtime.cliHome, "ssh");
7197
+ const cliConfig = path15.join(runtime.cliHome, "cli.json");
7198
+ const sshDirectory = path15.join(runtime.cliHome, "ssh");
6851
7199
  home = {
6852
7200
  cliConfig: exists(cliConfig) ? cliConfig : null,
6853
7201
  sshDirectory: exists(sshDirectory) ? sshDirectory : null,
@@ -6979,7 +7327,7 @@ var removeServer = async (runtime, failures, leftovers) => {
6979
7327
  }
6980
7328
  }
6981
7329
  for (const name of SERVER_FILES) {
6982
- fs13.rmSync(path14.join(configDir, name), { recursive: true, force: true });
7330
+ fs14.rmSync(path15.join(configDir, name), { recursive: true, force: true });
6983
7331
  }
6984
7332
  server.writeLine(`removed ${configDir}/{identity.env, active, generations, backups}`);
6985
7333
  },
@@ -6990,20 +7338,20 @@ var removeServer = async (runtime, failures, leftovers) => {
6990
7338
  var removeHome = async (runtime, plan, failures, leftovers) => {
6991
7339
  const { server } = runtime;
6992
7340
  if (plan.cliConfig !== null) {
6993
- fs13.rmSync(plan.cliConfig, { force: true });
7341
+ fs14.rmSync(plan.cliConfig, { force: true });
6994
7342
  server.writeLine(`removed ${plan.cliConfig}`);
6995
7343
  }
6996
7344
  if (plan.sshDirectory !== null) {
6997
- fs13.rmSync(plan.sshDirectory, { recursive: true, force: true });
7345
+ fs14.rmSync(plan.sshDirectory, { recursive: true, force: true });
6998
7346
  server.writeLine(`removed ${plan.sshDirectory}`);
6999
7347
  }
7000
7348
  if (plan.managedSshBlocks > 0) {
7001
7349
  try {
7002
- const before = fs13.readFileSync(runtime.sshConfigFile, "utf8");
7350
+ const before = fs14.readFileSync(runtime.sshConfigFile, "utf8");
7003
7351
  const stripped = stripManagedWorkspaceSshBlocks(before);
7004
7352
  if (!stripped.ok) throw stripped.error;
7005
7353
  if (stripped.value.removed > 0) {
7006
- fs13.writeFileSync(runtime.sshConfigFile, stripped.value.config, { mode: 384 });
7354
+ fs14.writeFileSync(runtime.sshConfigFile, stripped.value.config, { mode: 384 });
7007
7355
  server.writeLine(
7008
7356
  `removed ${plural(stripped.value.removed, "managed block")} from ${runtime.sshConfigFile}`
7009
7357
  );
@@ -7016,12 +7364,12 @@ var removeHome = async (runtime, plan, failures, leftovers) => {
7016
7364
  }
7017
7365
  let remaining = [];
7018
7366
  try {
7019
- remaining = fs13.readdirSync(runtime.cliHome);
7367
+ remaining = fs14.readdirSync(runtime.cliHome);
7020
7368
  } catch {
7021
7369
  return;
7022
7370
  }
7023
7371
  if (remaining.length === 0) {
7024
- fs13.rmdirSync(runtime.cliHome);
7372
+ fs14.rmdirSync(runtime.cliHome);
7025
7373
  server.writeLine(`removed ${runtime.cliHome}`);
7026
7374
  } else {
7027
7375
  leftovers.push(
@@ -7085,19 +7433,19 @@ var upgradeUrl = async (input) => {
7085
7433
  // src/main.ts
7086
7434
  var mendCliHome = () => {
7087
7435
  const xdg = process.env["XDG_CONFIG_HOME"];
7088
- const preferred = path15.join(
7089
- xdg === void 0 || xdg === "" ? path15.join(os9.homedir(), ".config") : xdg,
7436
+ const preferred = path16.join(
7437
+ xdg === void 0 || xdg === "" ? path16.join(os10.homedir(), ".config") : xdg,
7090
7438
  "mend"
7091
7439
  );
7092
- const legacy = path15.join(os9.homedir(), ".mend");
7093
- return !fs14.existsSync(preferred) && fs14.existsSync(legacy) ? legacy : preferred;
7440
+ const legacy = path16.join(os10.homedir(), ".mend");
7441
+ return !fs15.existsSync(preferred) && fs15.existsSync(legacy) ? legacy : preferred;
7094
7442
  };
7095
- var CONFIG_PATH = path15.join(mendCliHome(), "cli.json");
7443
+ var CONFIG_PATH = path16.join(mendCliHome(), "cli.json");
7096
7444
  var loadConfig = () => {
7097
7445
  let fileConfig = {};
7098
- if (fs14.existsSync(CONFIG_PATH)) {
7446
+ if (fs15.existsSync(CONFIG_PATH)) {
7099
7447
  try {
7100
- fileConfig = JSON.parse(fs14.readFileSync(CONFIG_PATH, "utf8"));
7448
+ fileConfig = JSON.parse(fs15.readFileSync(CONFIG_PATH, "utf8"));
7101
7449
  } catch {
7102
7450
  fail6(`could not parse ${CONFIG_PATH}`);
7103
7451
  }
@@ -7258,8 +7606,8 @@ var withSpinner = async (label, work) => {
7258
7606
  const started = Date.now();
7259
7607
  let frame = 0;
7260
7608
  const timer = setInterval(() => {
7261
- const seconds = Math.round((Date.now() - started) / 1e3);
7262
- process.stdout.write(`\r ${frames[frame % frames.length]} ${label} ${dim6(`${seconds}s`)} `);
7609
+ const seconds2 = Math.round((Date.now() - started) / 1e3);
7610
+ process.stdout.write(`\r ${frames[frame % frames.length]} ${label} ${dim6(`${seconds2}s`)} `);
7263
7611
  frame += 1;
7264
7612
  }, 120);
7265
7613
  try {
@@ -7288,7 +7636,7 @@ var adopt = async (config, args) => {
7288
7636
  }
7289
7637
  const sourceIssue = repositoryCloneUrlIssue(source);
7290
7638
  if (sourceIssue !== null) return fail6(sourceIssue);
7291
- const projectName = name ?? normalizeProjectName(path15.basename(source, ".git"));
7639
+ const projectName = name ?? normalizeProjectName(path16.basename(source, ".git"));
7292
7640
  const auth = authFlagIndex !== -1 && args[authFlagIndex + 1] !== void 0 ? String(args[authFlagIndex + 1]) : null;
7293
7641
  if (auth !== null && auth !== "ambient" && auth !== "mend-key" && auth !== "bridge") {
7294
7642
  return fail6(`--auth takes "ambient", "mend-key", or "bridge", not "${auth}"`);
@@ -7337,7 +7685,7 @@ var findProject = async (config, explicit, adoptCwd = false) => {
7337
7685
  const sourceIssue = repositoryCloneUrlIssue(facts.originUrl);
7338
7686
  if (sourceIssue !== null) return fail6(sourceIssue);
7339
7687
  const adopted = await api(config, "POST", "/projects", {
7340
- name: normalizeProjectName(path15.basename(facts.repoRoot)),
7688
+ name: normalizeProjectName(path16.basename(facts.repoRoot)),
7341
7689
  source: facts.originUrl
7342
7690
  });
7343
7691
  say6(`${green6("\u2713")} adopted \xB7 ${adopted.name} \xB7 ${dim6(adopted.storePath)}`);
@@ -7347,14 +7695,19 @@ var resolvedBackgroundSessions = async (config, project2) => {
7347
7695
  if (project2.backgroundSessions === "on") return true;
7348
7696
  if (project2.backgroundSessions === "off") return false;
7349
7697
  try {
7350
- const settings = await request(
7351
- config,
7352
- "GET",
7353
- "/settings"
7354
- );
7355
- return settings.backgroundSessions !== false;
7698
+ const organization = await request(config, "GET", "/organization/settings");
7699
+ return organization.effective?.backgroundSessions !== false;
7356
7700
  } catch {
7357
- return true;
7701
+ try {
7702
+ const settings = await request(
7703
+ config,
7704
+ "GET",
7705
+ "/settings"
7706
+ );
7707
+ return settings.backgroundSessions !== false;
7708
+ } catch {
7709
+ return true;
7710
+ }
7358
7711
  }
7359
7712
  };
7360
7713
  var askWorktreeName = async () => {
@@ -7800,6 +8153,10 @@ var stopSessions = async (config, targets, summarise) => {
7800
8153
  if (hold !== null) {
7801
8154
  say6(`${amber2(" " + hold)} \xB7 mend stop --services ${session.id.slice(0, 8)}`);
7802
8155
  }
8156
+ const capture = after === null ? null : captureLineOf(after.session);
8157
+ if (capture !== null) {
8158
+ say6(`${amber2(" " + capture)} \xB7 the workspace stops once nothing is pending`);
8159
+ }
7803
8160
  say6(`${cobalt2(" review")} \xB7 ${config.url}/sessions/${session.id}`);
7804
8161
  }
7805
8162
  if (summarise) {
@@ -8187,13 +8544,13 @@ var serviceRestart = async (config, args) => {
8187
8544
  };
8188
8545
  var serviceInit = async (args) => {
8189
8546
  const root = gitTopLevel(process.cwd()) ?? process.cwd();
8190
- const target = path15.join(root, "mend.toml");
8191
- if (fs14.existsSync(target)) {
8547
+ const target = path16.join(root, "mend.toml");
8548
+ if (fs15.existsSync(target)) {
8192
8549
  return fail6(`${target} already exists \u2014 edit it directly (init never merges)`);
8193
8550
  }
8194
- const rootFiles = fs14.readdirSync(root);
8551
+ const rootFiles = fs15.readdirSync(root);
8195
8552
  const proposals = [];
8196
- const readRoot = (name) => fs14.readFileSync(path15.join(root, name), "utf8");
8553
+ const readRoot = (name) => fs15.readFileSync(path16.join(root, name), "utf8");
8197
8554
  if (rootFiles.includes("package.json")) {
8198
8555
  proposals.push(...proposeFromPackageJson(readRoot("package.json"), rootFiles));
8199
8556
  }
@@ -8203,13 +8560,13 @@ var serviceInit = async (args) => {
8203
8560
  );
8204
8561
  for (const glob of globs) {
8205
8562
  const parent = glob.replace(/\/?\*+$/, "");
8206
- const dirs = glob.endsWith("*") ? fs14.existsSync(path15.join(root, parent)) ? fs14.readdirSync(path15.join(root, parent)).map((dir) => path15.join(parent, dir)) : [] : [glob];
8563
+ const dirs = glob.endsWith("*") ? fs15.existsSync(path16.join(root, parent)) ? fs15.readdirSync(path16.join(root, parent)).map((dir) => path16.join(parent, dir)) : [] : [glob];
8207
8564
  for (const dir of dirs) {
8208
- const manifest = path15.join(root, dir, "package.json");
8209
- if (!fs14.existsSync(manifest)) continue;
8565
+ const manifest = path16.join(root, dir, "package.json");
8566
+ if (!fs15.existsSync(manifest)) continue;
8210
8567
  for (const proposal of proposeFromWorkspacePackage(
8211
- path15.basename(dir),
8212
- fs14.readFileSync(manifest, "utf8"),
8568
+ path16.basename(dir),
8569
+ fs15.readFileSync(manifest, "utf8"),
8213
8570
  rootFiles
8214
8571
  )) {
8215
8572
  if (!proposals.some((existing) => existing.name === proposal.name)) {
@@ -8253,7 +8610,7 @@ var serviceInit = async (args) => {
8253
8610
  return;
8254
8611
  }
8255
8612
  }
8256
- fs14.writeFileSync(target, toml);
8613
+ fs15.writeFileSync(target, toml);
8257
8614
  say6(`${green6("\u2713")} wrote ${target} \u2014 commit it, then: mend service run ${proposals[0]?.name}`);
8258
8615
  };
8259
8616
  var tunnelServices = async (config, services, portOverride) => {
@@ -8357,23 +8714,23 @@ var serviceCommand = async (config, args) => {
8357
8714
  }
8358
8715
  };
8359
8716
  var isProvider = (value) => value === "claude" || value === "codex" || value === "github";
8360
- var readIfExists = (file) => fs14.existsSync(file) ? fs14.readFileSync(file, "utf8") : null;
8717
+ var readIfExists = (file) => fs15.existsSync(file) ? fs15.readFileSync(file, "utf8") : null;
8361
8718
  var localCredential = (provider) => {
8362
- const home = os9.homedir();
8719
+ const home = os10.homedir();
8363
8720
  switch (provider) {
8364
8721
  case "codex":
8365
8722
  return readIfExists(
8366
- path15.join(process.env["CODEX_HOME"] ?? path15.join(home, ".codex"), "auth.json")
8723
+ path16.join(process.env["CODEX_HOME"] ?? path16.join(home, ".codex"), "auth.json")
8367
8724
  );
8368
8725
  case "claude":
8369
8726
  return readIfExists(
8370
- path15.join(
8371
- process.env["CLAUDE_CONFIG_DIR"] ?? path15.join(home, ".claude"),
8727
+ path16.join(
8728
+ process.env["CLAUDE_CONFIG_DIR"] ?? path16.join(home, ".claude"),
8372
8729
  ".credentials.json"
8373
8730
  )
8374
8731
  );
8375
8732
  case "github": {
8376
- const result = spawnSync4("gh", ["auth", "token"], { encoding: "utf8" });
8733
+ const result = spawnSync5("gh", ["auth", "token"], { encoding: "utf8" });
8377
8734
  const token = result.status === 0 ? result.stdout.trim() : "";
8378
8735
  return token === "" ? null : token;
8379
8736
  }
@@ -8418,7 +8775,7 @@ var claudeGrantSecret = () => {
8418
8775
  return read.kind === "missing" ? null : read.secret;
8419
8776
  };
8420
8777
  var doctorBundle = (config, args) => doctorBundleCommand(args, {
8421
- defaultDir: path15.join(mendCliHome(), "bundles"),
8778
+ defaultDir: path16.join(mendCliHome(), "bundles"),
8422
8779
  now: () => /* @__PURE__ */ new Date(),
8423
8780
  say: say6,
8424
8781
  warn: (line) => process.stderr.write(`${line}
@@ -8482,7 +8839,7 @@ var claudeGrant = async () => {
8482
8839
  return null;
8483
8840
  }
8484
8841
  }
8485
- if (status.configDirectory !== null && path15.resolve(status.configDirectory) !== path15.resolve(dir)) {
8842
+ if (status.configDirectory !== null && path16.resolve(status.configDirectory) !== path16.resolve(dir)) {
8486
8843
  fail6(
8487
8844
  `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`
8488
8845
  );
@@ -8545,7 +8902,7 @@ var connectCommand = async (config, args) => {
8545
8902
  }
8546
8903
  let secret;
8547
8904
  if (flags.includes("--from-stdin")) {
8548
- secret = fs14.readFileSync(0, "utf8").trim();
8905
+ secret = fs15.readFileSync(0, "utf8").trim();
8549
8906
  if (secret === "") return fail6("nothing on stdin");
8550
8907
  } else if (provider === "claude" && !flags.includes("--use-my-login")) {
8551
8908
  secret = await claudeGrant();
@@ -8591,14 +8948,14 @@ var takeFlagValue4 = (args, flag) => {
8591
8948
  return at !== -1 && args[at + 1] !== void 0 ? String(args[at + 1]) : null;
8592
8949
  };
8593
8950
  var saveCliConfig = (next) => {
8594
- fs14.mkdirSync(path15.dirname(CONFIG_PATH), { recursive: true, mode: 448 });
8595
- fs14.writeFileSync(
8951
+ fs15.mkdirSync(path16.dirname(CONFIG_PATH), { recursive: true, mode: 448 });
8952
+ fs15.writeFileSync(
8596
8953
  CONFIG_PATH,
8597
8954
  `${JSON.stringify({ url: next.url, token: next.token, deviceId: next.deviceId }, null, 2)}
8598
8955
  `,
8599
8956
  { mode: 384 }
8600
8957
  );
8601
- fs14.chmodSync(CONFIG_PATH, 384);
8958
+ fs15.chmodSync(CONFIG_PATH, 384);
8602
8959
  };
8603
8960
  var login = async (config, args) => {
8604
8961
  await loginCommand(args, {
@@ -8611,7 +8968,7 @@ var login = async (config, args) => {
8611
8968
  });
8612
8969
  };
8613
8970
  var logout = async (config) => {
8614
- if (!fs14.existsSync(CONFIG_PATH) && config.token === null) {
8971
+ if (!fs15.existsSync(CONFIG_PATH) && config.token === null) {
8615
8972
  say6(dim6("nothing saved \u2014 already signed out"));
8616
8973
  return;
8617
8974
  }
@@ -8664,8 +9021,8 @@ var uninstallCommand = async (config, args) => {
8664
9021
  const runtime = {
8665
9022
  server,
8666
9023
  cliHome: mendCliHome(),
8667
- sshConfigFile: path15.join(os9.homedir(), ".ssh", "config"),
8668
- signedIn: config.token === null || !fs14.existsSync(CONFIG_PATH) ? null : { url: config.url, deviceId: config.deviceId },
9024
+ sshConfigFile: path16.join(os10.homedir(), ".ssh", "config"),
9025
+ signedIn: config.token === null || !fs15.existsSync(CONFIG_PATH) ? null : { url: config.url, deviceId: config.deviceId },
8669
9026
  revokeDevice: async () => {
8670
9027
  if (config.deviceId === null) return "no device id saved";
8671
9028
  try {
@@ -8729,7 +9086,7 @@ var keysShare = async (config) => {
8729
9086
  return fail6("SSH_AUTH_SOCK is not set \u2014 start (or plug in) your ssh-agent first");
8730
9087
  }
8731
9088
  await shareAgent({
8732
- url: () => socketUrl(config, "keys-bridge", { host: os9.hostname() }),
9089
+ url: () => socketUrl(config, "keys-bridge", { host: os10.hostname() }),
8733
9090
  agentSock,
8734
9091
  signal: new AbortController().signal,
8735
9092
  onEvent: (event) => {
@@ -8859,7 +9216,7 @@ ${usageOf("dotfiles repo")}`);
8859
9216
  var dotfilesSync = async (config, args) => {
8860
9217
  const all = args.includes("--all");
8861
9218
  const requestedPaths = args.filter((arg) => !arg.startsWith("--"));
8862
- const home = os9.homedir();
9219
+ const home = os10.homedir();
8863
9220
  if (!all && requestedPaths.length === 0) {
8864
9221
  const found = scanDotfileCandidates(home);
8865
9222
  if (found.length === 0) {
@@ -8886,13 +9243,13 @@ var dotfilesSync = async (config, args) => {
8886
9243
  if ("error" in read) return fail6(read.error);
8887
9244
  const result = await api(config, "POST", "/dotfiles/snapshot", {
8888
9245
  files: read.files,
8889
- source: os9.hostname(),
9246
+ source: os10.hostname(),
8890
9247
  merge: false
8891
9248
  });
8892
9249
  const snapshot = result.snapshot;
8893
9250
  if (snapshot === null) return fail6("the server accepted the sync but reports no snapshot");
8894
9251
  say6(
8895
- `${green6("synced")} ${snapshot.files.length} file${snapshot.files.length === 1 ? "" : "s"} from ${os9.hostname()} \xB7 ${dim6(snapshot.sha.slice(0, 7))} ${dim6("\u2014 applies from the next session launch")}`
9252
+ `${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")}`
8896
9253
  );
8897
9254
  };
8898
9255
  var envLoad = async (config, args) => {
@@ -8906,10 +9263,10 @@ var envLoad = async (config, args) => {
8906
9263
  const positional = args.filter(
8907
9264
  (arg, i) => !arg.startsWith("--") && args[i - 1] !== "--project" && !consumed.has(i)
8908
9265
  );
8909
- const file = path15.resolve(positional[0] ?? ".env");
9266
+ const file = path16.resolve(positional[0] ?? ".env");
8910
9267
  let contents;
8911
9268
  try {
8912
- contents = fs14.readFileSync(file, "utf8");
9269
+ contents = fs15.readFileSync(file, "utf8");
8913
9270
  } catch {
8914
9271
  return fail6(`cannot read ${file} \u2014 pass a path: mend env load path/to/.env`);
8915
9272
  }
@@ -8925,10 +9282,10 @@ var envLoad = async (config, args) => {
8925
9282
  { contents, allSecret, secretNames }
8926
9283
  );
8927
9284
  if (report.loaded.length === 0 && report.rejected.length === 0 && report.malformedLines.length === 0) {
8928
- say6(dim6(`${path15.basename(file)} has no variables`));
9285
+ say6(dim6(`${path16.basename(file)} has no variables`));
8929
9286
  return;
8930
9287
  }
8931
- say6(`${green6("\u2713")} loaded ${path15.basename(file)} into ${project2.name}`);
9288
+ say6(`${green6("\u2713")} loaded ${path16.basename(file)} into ${project2.name}`);
8932
9289
  for (const line of formatLoadReport(report, { dim: dim6, warn: amber2 })) say6(line);
8933
9290
  const plaintextUrls = report.loaded.filter(
8934
9291
  (entry) => entry.lane === "configuration" && /_(URL|URI|DSN)$/.test(entry.name)
@@ -9119,7 +9476,7 @@ var skillsList = async (config, args) => {
9119
9476
  };
9120
9477
  var skillsPush = async (config, args) => {
9121
9478
  const dirFlag = takeFlagValue4(args, "--dir");
9122
- const root = dirFlag === null ? path15.join(os9.homedir(), DEFAULT_SKILLS_DIR) : path15.resolve(dirFlag);
9479
+ const root = dirFlag === null ? path16.join(os10.homedir(), DEFAULT_SKILLS_DIR) : path16.resolve(dirFlag);
9123
9480
  const scanned = scanSkillLibrary(root);
9124
9481
  if ("error" in scanned) return fail6(scanned.error);
9125
9482
  for (const note of scanned.notes) say6(dim6(` ${note.skill}: ${note.message}`));
@@ -9544,7 +9901,10 @@ var refreshCommand = async (config, args) => {
9544
9901
  }
9545
9902
  if (branches.length > 12) say6(dim6(` \u2026 ${branches.length - 12} more`));
9546
9903
  };
9547
- var rowHold = (row) => row.annotation === void 0 ? null : servicesHoldOf(row.session, row.annotation.currentAgent, row.annotation.liveServices ?? 0);
9904
+ var rowHold = (row) => (
9905
+ // A workspace a stop is still saving (docs/adr/0002) reads what is left, first.
9906
+ captureLineOf(row.session) ?? (row.annotation === void 0 ? null : servicesHoldOf(row.session, row.annotation.currentAgent, row.annotation.liveServices ?? 0))
9907
+ );
9548
9908
  var printSessionRow = (row) => {
9549
9909
  const { session, annotation } = row;
9550
9910
  const live = ACTIVE_STATUSES.has(session.status);
@@ -9642,6 +10002,14 @@ var sessionsCommand = async (config, args) => {
9642
10002
  openComments: annotation.openComments,
9643
10003
  totalComments: annotation.totalComments,
9644
10004
  pendingFollowUp: annotation.pendingFollowUp
10005
+ },
10006
+ capture: session.capturePending === void 0 ? null : {
10007
+ pending: session.capturePending ?? null,
10008
+ pendingBytes: session.capturePendingBytes ?? null,
10009
+ refused: session.captureRefused ?? null,
10010
+ drain: session.captureDrain ?? null,
10011
+ notSaved: (session.captureNotSavedAt ?? null) !== null,
10012
+ line: captureLineOf(session)
9645
10013
  }
9646
10014
  }))
9647
10015
  };
@@ -9774,14 +10142,14 @@ var dashboard = async (config, options = {}) => {
9774
10142
  `the dashboard needs Node >= 26 (node:ffi) \u2014 this is ${process.version}; every other command still works`
9775
10143
  );
9776
10144
  }
9777
- const rerun = spawnSync4(
10145
+ const rerun = spawnSync5(
9778
10146
  process.execPath,
9779
10147
  ["--experimental-ffi", "--disable-warning=ExperimentalWarning", ...process.argv.slice(1)],
9780
10148
  { stdio: "inherit" }
9781
10149
  );
9782
10150
  process.exit(rerun.status ?? 0);
9783
10151
  }
9784
- const { runDashboard } = await import("./dashboard-3HNFSQZD.js");
10152
+ const { runDashboard } = await import("./dashboard-OADX3PHI.js");
9785
10153
  const agentShare = await startShareIfBridge(config);
9786
10154
  const tunnels = options.noTunnel === true || serverIsLocal(config) ? null : createServiceTunnels({
9787
10155
  listServices: () => fetchServices(config),
@@ -9815,7 +10183,7 @@ var startShareIfBridge = async (config) => {
9815
10183
  }
9816
10184
  if (access.mode !== "bridge") return null;
9817
10185
  return startAgentShare({
9818
- url: () => socketUrl(config, "keys-bridge", { host: os9.hostname() }),
10186
+ url: () => socketUrl(config, "keys-bridge", { host: os10.hostname() }),
9819
10187
  agentSock
9820
10188
  });
9821
10189
  };
@@ -9855,10 +10223,10 @@ var manCommand = (words) => {
9855
10223
  }
9856
10224
  const version = cliVersion();
9857
10225
  const page = doc === null ? renderManIndex(version) : renderManPage(doc, version);
9858
- const file = path15.join(os9.tmpdir(), `mend-man-${process.pid}-${manFileName(doc)}`);
9859
- fs14.writeFileSync(file, page);
9860
- const result = spawnSync4("man", ["-l", file], { stdio: "inherit" });
9861
- fs14.rmSync(file, { force: true });
10226
+ const file = path16.join(os10.tmpdir(), `mend-man-${process.pid}-${manFileName(doc)}`);
10227
+ fs15.writeFileSync(file, page);
10228
+ const result = spawnSync5("man", ["-l", file], { stdio: "inherit" });
10229
+ fs15.rmSync(file, { force: true });
9862
10230
  if (result.error !== void 0 || result.status !== 0) {
9863
10231
  say6(doc === null ? renderIndex() : renderCommand(doc));
9864
10232
  }