@sealant/mend 0.26.0 → 0.27.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 (60) hide show
  1. package/README.md +5 -5
  2. package/dist/{chunk-4MX7ZC34.js → chunk-TBMOGB3T.js} +9 -1
  3. package/dist/{dashboard-JIWYW3EM.js → dashboard-5CBGTPSJ.js} +1 -1
  4. package/dist/main.js +178 -53
  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 +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-help.1 +1 -1
  19. package/man/mend-keys-init.1 +1 -1
  20. package/man/mend-keys-mode.1 +1 -1
  21. package/man/mend-keys-share.1 +1 -1
  22. package/man/mend-keys-show.1 +1 -1
  23. package/man/mend-login.1 +1 -1
  24. package/man/mend-logout.1 +1 -1
  25. package/man/mend-man.1 +1 -1
  26. package/man/mend-pair.1 +1 -1
  27. package/man/mend-projects.1 +1 -1
  28. package/man/mend-refresh.1 +1 -1
  29. package/man/mend-rejoin.1 +1 -1
  30. package/man/mend-resume.1 +1 -1
  31. package/man/mend-run.1 +1 -1
  32. package/man/mend-server-logs.1 +1 -1
  33. package/man/mend-server-restart.1 +2 -2
  34. package/man/mend-server-setup.1 +1 -1
  35. package/man/mend-server-start.1 +1 -1
  36. package/man/mend-server-status.1 +1 -1
  37. package/man/mend-server-stop.1 +2 -2
  38. package/man/mend-server-upgrade.1 +1 -1
  39. package/man/mend-server.1 +1 -1
  40. package/man/mend-service-add.1 +1 -1
  41. package/man/mend-service-connect.1 +1 -1
  42. package/man/mend-service-init.1 +1 -1
  43. package/man/mend-service-list.1 +1 -1
  44. package/man/mend-service-logs.1 +1 -1
  45. package/man/mend-service-restart.1 +1 -1
  46. package/man/mend-service-run.1 +1 -1
  47. package/man/mend-service-stop.1 +1 -1
  48. package/man/mend-sessions.1 +1 -1
  49. package/man/mend-shell.1 +1 -1
  50. package/man/mend-skills-push.1 +1 -1
  51. package/man/mend-skills.1 +1 -1
  52. package/man/mend-ssh-setup.1 +1 -1
  53. package/man/mend-ssh.1 +1 -1
  54. package/man/mend-stop.1 +1 -1
  55. package/man/mend-ui.1 +1 -1
  56. package/man/mend-uninstall.1 +1 -1
  57. package/man/mend-version.1 +1 -1
  58. package/man/mend-worktrees.1 +1 -1
  59. package/man/mend.1 +2 -2
  60. package/package.json +3 -3
package/README.md CHANGED
@@ -47,11 +47,11 @@ mend server upgrade --version 0.24.0 --assets-dir ./release-0.24.0 --offline
47
47
 
48
48
  Each asset directory must contain `compose.v2.yaml` and `postgres-init.sh`. Mend copies them into an
49
49
  immutable private generation; the source directory is not needed afterward. Preload official
50
- `postgres:17-alpine` and canonical `ghcr.io/sealant-sh/mend:VERSION`. Image labels and health must
51
- report that exact version. `--offline` forbids GitHub requests and release-image pulls.
52
- Start/restart always reuse local release images and retained assets. The bundle publishes only the
53
- web port (`--port`, default `3105`) and the SSH gateway port (`--ssh-port`, default `2222`); the two
54
- must differ.
50
+ `postgres:17-alpine`, `dxflrs/garage:v2.4.1` and canonical `ghcr.io/sealant-sh/mend:VERSION`. Image
51
+ labels and health must report that exact version. `--offline` forbids GitHub requests and
52
+ release-image pulls. Start/restart always reuse local release images and retained assets. The bundle
53
+ publishes only the web port (`--port`, default `3105`) and the SSH gateway port (`--ssh-port`,
54
+ default `2222`); the two must differ.
55
55
 
56
56
  Stop/restart/upgrade interrupt connections. Workspace containers and data remain, but active work
57
57
  may lose connectivity and need reconnection. No volumes are deleted. Status/logs never start an
@@ -26640,6 +26640,14 @@ var Project = class extends Schema_exports.Class("Project")({
26640
26640
  * effectively instant. Explicit resource intent: N ready containers per project.
26641
26641
  */
26642
26642
  hotSessions: Schema_exports.Number,
26643
+ /**
26644
+ * The command that builds this project's dependency tree, run by Mend in a workspace whose
26645
+ * tree does not match the executor's platform and by the install job that feeds the shared
26646
+ * cache (ADR-0002 decisions 2/9). Null = detected from the base tree's lockfile at launch.
26647
+ */
26648
+ installCommand: Schema_exports.NullOr(Schema_exports.String).pipe(
26649
+ Schema_exports.withDecodingDefaultKey(Effect_exports.succeed(null))
26650
+ ),
26643
26651
  createdAt: Timestamp,
26644
26652
  updatedAt: Timestamp
26645
26653
  }) {
@@ -27180,7 +27188,7 @@ var ProjectClusterBindingsSnapshot = class extends Schema_exports.Class(
27180
27188
  }) {
27181
27189
  };
27182
27190
 
27183
- // ../../node_modules/.pnpm/@sealant+api-contracts@0.28.0_effect@4.0.0-beta.93/node_modules/@sealant/api-contracts/dist/workspace-environment.js
27191
+ // ../../node_modules/.pnpm/@sealant+api-contracts@0.31.0_effect@4.0.0-beta.93/node_modules/@sealant/api-contracts/dist/workspace-environment.js
27184
27192
  var RESERVED_NAMES = new Map([
27185
27193
  // Identity/process roots the daemon and shells own.
27186
27194
  ...[
@@ -8,7 +8,7 @@ import {
8
8
  normalizeProjectName,
9
9
  pendingId,
10
10
  repositoryCloneUrlIssue
11
- } from "./chunk-4MX7ZC34.js";
11
+ } from "./chunk-TBMOGB3T.js";
12
12
 
13
13
  // src/dashboard.tsx
14
14
  import { CliRenderEvents, createCliRenderer } from "@opentui/core";
package/dist/main.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  repositoryCloneUrlIssue,
18
18
  sessionDisplayName,
19
19
  trackBracketedPaste
20
- } from "./chunk-4MX7ZC34.js";
20
+ } from "./chunk-TBMOGB3T.js";
21
21
 
22
22
  // src/main.ts
23
23
  import { spawn as spawn3, spawnSync as spawnSync2 } from "node:child_process";
@@ -1112,7 +1112,7 @@ var COMMANDS = [
1112
1112
  {
1113
1113
  name: "server stop",
1114
1114
  section: "this machine",
1115
- summary: "stop Mend and Postgres without deleting data",
1115
+ summary: "stop Mend, Postgres and Garage without deleting data",
1116
1116
  synopsis: [""],
1117
1117
  description: [
1118
1118
  "Stops only the installation's Compose services. Connections are interrupted. Workspace containers and volumes remain, but active work may lose connectivity and need reconnection. No volume deletion or Docker prune is performed."
@@ -1125,7 +1125,7 @@ var COMMANDS = [
1125
1125
  summary: "restart Mend using the same generation and pin",
1126
1126
  synopsis: ["[--offline]"],
1127
1127
  description: [
1128
- "Checks preloaded images before stopping Mend, then starts the saved generation and verifies exact-version health. Postgres stays running. Connections are interrupted; workspace containers and data remain, but active work may need reconnection."
1128
+ "Checks preloaded images before stopping Mend, then starts the saved generation and verifies exact-version health. Postgres and Garage stay running. Connections are interrupted; workspace containers and data remain, but active work may need reconnection."
1129
1129
  ],
1130
1130
  options: [{ flag: "--offline", text: "use local release images and assets" }],
1131
1131
  see: ["server status", "server logs"]
@@ -1759,7 +1759,7 @@ var loginCommand = async (args, deps) => {
1759
1759
  };
1760
1760
 
1761
1761
  // src/server-setup.ts
1762
- import { randomBytes } from "node:crypto";
1762
+ import { createHmac, randomBytes } from "node:crypto";
1763
1763
  import { readFile } from "node:fs/promises";
1764
1764
  import * as net2 from "node:net";
1765
1765
  import * as os3 from "node:os";
@@ -1772,6 +1772,11 @@ var MEND_DOCKER_NAMESPACE = {
1772
1772
  store: "mend-store",
1773
1773
  control: "mend-control"
1774
1774
  };
1775
+ var MEND_DOCKER_NAMESPACE_WITH_GARAGE = {
1776
+ ...MEND_DOCKER_NAMESPACE,
1777
+ garage: "mend-garage"
1778
+ };
1779
+ var secondaryVolumesOf = (namespace) => namespace.garage === void 0 ? [namespace.control] : [namespace.control, namespace.garage];
1775
1780
  var SERVER_VOLUME_OWNER_LABEL = "dev.sealant.mend.installation";
1776
1781
  var ServerVolumeOwnershipError = class extends Error {
1777
1782
  /** Stable error discriminator. */
@@ -1863,9 +1868,10 @@ var inProjectNamespace = (name, project2) => ["-", "_"].some(
1863
1868
  (separator) => name.startsWith(`${project2}${separator}`) && name.length > project2.length + 1
1864
1869
  );
1865
1870
  var reservedResourceName = (name, kind, namespace) => {
1866
- if ([namespace.project, namespace.store, namespace.control].includes(name)) return true;
1871
+ if ([namespace.project, namespace.store, namespace.control, namespace.garage ?? ""].includes(name))
1872
+ return true;
1867
1873
  return ["-", "_"].some(
1868
- (separator) => (kind === "container" ? ["mend", "postgres"] : ["default"]).some((service) => {
1874
+ (separator) => (kind === "container" ? ["mend", "postgres", "garage"] : ["default"]).some((service) => {
1869
1875
  const reserved = `${namespace.project}${separator}${service}`;
1870
1876
  return name === reserved || kind === "container" && name.startsWith(reserved) && /^[-_](?:[0-9]+|run[-_][a-f0-9]+)$/.test(name.slice(reserved.length));
1871
1877
  })
@@ -1901,7 +1907,9 @@ var inspectForeignProject = async (runtime, context, kind, name, project2) => {
1901
1907
  }
1902
1908
  };
1903
1909
  var refuseOldData = async (runtime, context, namespace, volumes) => {
1904
- if (volumes.some((name) => name === namespace.control || name.startsWith(`${namespace.project}_`)))
1910
+ if (volumes.some(
1911
+ (name) => secondaryVolumesOf(namespace).includes(name) || name.startsWith(`${namespace.project}_`)
1912
+ ))
1905
1913
  return fail3("unowned-data", "existing volumes without anchor");
1906
1914
  for (const kind of ["volume", "container", "network"]) {
1907
1915
  const labelled = await listNames(runtime, context, kind, namespace.project);
@@ -1930,9 +1938,9 @@ var refuseOldData = async (runtime, context, namespace, volumes) => {
1930
1938
  };
1931
1939
  var parseOwnershipInput = (input) => {
1932
1940
  const namespace = input.namespace ?? MEND_DOCKER_NAMESPACE;
1933
- if (!input.dockerContext.trim() || input.identityBytes.byteLength === 0 || !/^[a-z0-9][a-z0-9_-]*$/.test(namespace.project) || ![namespace.store, namespace.control].every(
1941
+ if (!input.dockerContext.trim() || input.identityBytes.byteLength === 0 || !/^[a-z0-9][a-z0-9_-]*$/.test(namespace.project) || ![namespace.store, ...secondaryVolumesOf(namespace)].every(
1934
1942
  (name) => /^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/.test(name)
1935
- ) || namespace.store === namespace.control)
1943
+ ) || (/* @__PURE__ */ new Set([namespace.store, ...secondaryVolumesOf(namespace)])).size !== 1 + secondaryVolumesOf(namespace).length)
1936
1944
  return fail3("invalid-input", "ownership inputs");
1937
1945
  const owner = createHash("sha256").update(input.identityBytes).digest("hex");
1938
1946
  return { _tag: "ok", value: { owner, namespace } };
@@ -1955,13 +1963,16 @@ var ownership = async (runtime, input, mode) => {
1955
1963
  }
1956
1964
  const current = await listNames(runtime, input.dockerContext, "volume");
1957
1965
  if (current._tag === "error") return current;
1958
- if (current.value.includes(namespace.control)) {
1959
- const control = await inspectOwner(runtime, input.dockerContext, namespace.control, owner);
1960
- if (control._tag === "error") return control;
1961
- } else {
1962
- if (mode === "verify") return fail3("missing", "control");
1963
- const control = await ensureVolume(runtime, input.dockerContext, namespace.control, owner);
1964
- if (control._tag === "error") return control;
1966
+ for (const name of secondaryVolumesOf(namespace)) {
1967
+ const role = name === namespace.control ? "control" : "garage";
1968
+ if (current.value.includes(name)) {
1969
+ const owned = await inspectOwner(runtime, input.dockerContext, name, owner);
1970
+ if (owned._tag === "error") return owned;
1971
+ } else {
1972
+ if (mode === "verify") return fail3("missing", role);
1973
+ const created = await ensureVolume(runtime, input.dockerContext, name, owner);
1974
+ if (created._tag === "error") return created;
1975
+ }
1965
1976
  }
1966
1977
  return { _tag: "ok", value: { owner, namespace } };
1967
1978
  };
@@ -2471,6 +2482,8 @@ var COMPOSE_ASSET = "compose.v2.yaml";
2471
2482
  var POSTGRES_INIT_ASSET = "postgres-init.sh";
2472
2483
  var RELEASE_BASE = "https://github.com/sealant-sh/Mend/releases/download";
2473
2484
  var LATEST_RELEASE_URL = "https://api.github.com/repos/sealant-sh/Mend/releases/latest";
2485
+ var GARAGE_IMAGE = "dxflrs/garage:v2.4.1";
2486
+ var GARAGE_BUCKET = "mend";
2474
2487
  var ServerSetupError = class extends Error {
2475
2488
  _tag = "ServerSetupError";
2476
2489
  };
@@ -2486,6 +2499,12 @@ var requiredString = (fields, key) => {
2486
2499
  }
2487
2500
  return value;
2488
2501
  };
2502
+ var requiredBucket = (fields) => {
2503
+ const value = fields.get("bucket");
2504
+ if (value !== "garage")
2505
+ throw setupError("Server config is corrupt: bucket must be garage when present.");
2506
+ return value;
2507
+ };
2489
2508
  var requiredInteger = (fields, key) => {
2490
2509
  const value = fields.get(key);
2491
2510
  if (typeof value !== "number" || !Number.isInteger(value)) {
@@ -2668,7 +2687,8 @@ var parseServerConfig = (raw) => {
2668
2687
  ),
2669
2688
  appPort: requiredInteger(fields, "appPort"),
2670
2689
  sshPort: requiredInteger(fields, "sshPort"),
2671
- ...fields.has("registryPort") ? { registryPort: requiredInteger(fields, "registryPort") } : {}
2690
+ ...fields.has("registryPort") ? { registryPort: requiredInteger(fields, "registryPort") } : {},
2691
+ ...fields.has("bucket") ? { bucket: requiredBucket(fields) } : {}
2672
2692
  };
2673
2693
  if (config.schemaVersion !== CONFIG_SCHEMA_VERSION || config.assetContract !== ASSET_CONTRACT && !LEGACY_ASSET_CONTRACTS.has(config.assetContract)) {
2674
2694
  throw setupError(
@@ -2893,10 +2913,15 @@ var resolveLatestVersion = async (runtime) => {
2893
2913
  if (version === "latest") throw setupError("GitHub returned an invalid latest release tag.");
2894
2914
  return version;
2895
2915
  };
2896
- var validateComposeAsset = (body) => {
2916
+ var composeServiceNames = (body) => {
2897
2917
  const [, afterServices = ""] = body.split(/^services:\s*$/m);
2898
2918
  const [servicesBlock = ""] = afterServices.split(/^\S/m);
2899
- const serviceNames = [...servicesBlock.matchAll(/^ {2}([0-9A-Za-z_-]+):\s*$/gm)].map((match) => match[1]).filter((name) => name !== void 0).toSorted((left, right) => left.localeCompare(right));
2919
+ return [...servicesBlock.matchAll(/^ {2}([0-9A-Za-z_-]+):\s*$/gm)].map((match) => match[1]).filter((name) => name !== void 0).toSorted((left, right) => left.localeCompare(right));
2920
+ };
2921
+ var composeBucket = (body) => composeServiceNames(body).includes("garage") ? "garage" : void 0;
2922
+ var validateComposeAsset = (body) => {
2923
+ const serviceNames = composeServiceNames(body);
2924
+ const withGarage = serviceNames.includes("garage");
2900
2925
  const volumeSections = body.split(/^volumes:[ \t]*$/m);
2901
2926
  const volumeBlock = (volumeSections[1] ?? "").split(/^\S/m)[0] ?? "";
2902
2927
  const volumeDeclarations = [
@@ -2904,7 +2929,8 @@ var validateComposeAsset = (body) => {
2904
2929
  ];
2905
2930
  for (const [name, variable] of [
2906
2931
  ["mend-store", "MEND_STORE_VOLUME_NAME"],
2907
- ["mend-control", "MEND_CONTROL_VOLUME_NAME"]
2932
+ ["mend-control", "MEND_CONTROL_VOLUME_NAME"],
2933
+ ...withGarage ? [["mend-garage", "MEND_GARAGE_VOLUME_NAME"]] : []
2908
2934
  ]) {
2909
2935
  const declarations = volumeDeclarations.filter((match) => match[1] === name);
2910
2936
  const properties = (declarations[0]?.[2] ?? "").split("\n").map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#"));
@@ -2930,9 +2956,22 @@ var validateComposeAsset = (body) => {
2930
2956
  "mend-ssh:",
2931
2957
  "mend-postgres:",
2932
2958
  "/var/lib/mend/store",
2933
- "/run/sealant/sockets"
2959
+ "/run/sealant/sockets",
2960
+ // The capture store's bucket (a release since the capture store); an older release's asset
2961
+ // has none and is still a valid v2 bundle.
2962
+ ...withGarage ? [
2963
+ `image: ${GARAGE_IMAGE}`,
2964
+ " garage:",
2965
+ "mend-garage:",
2966
+ "MEND_GARAGE_RPC_SECRET",
2967
+ "MEND_GARAGE_ADMIN_TOKEN",
2968
+ "MEND_GARAGE_KEY_ID",
2969
+ "MEND_GARAGE_KEY_SECRET",
2970
+ "MEND_BLOB_STORE",
2971
+ "MEND_SESSION_ENDPOINT_URL"
2972
+ ] : []
2934
2973
  ];
2935
- if (requiredFragments.some((fragment) => !body.includes(fragment)) || serviceNames.join(",") !== "mend,postgres") {
2974
+ if (requiredFragments.some((fragment) => !body.includes(fragment)) || serviceNames.join(",") !== "mend,postgres" && serviceNames.join(",") !== "garage,mend,postgres") {
2936
2975
  throw setupError(`Downloaded ${COMPOSE_ASSET} does not implement ${ASSET_CONTRACT}.`);
2937
2976
  }
2938
2977
  };
@@ -2962,6 +3001,27 @@ var renderIdentity = (secrets) => [
2962
3001
  `WORKSPACE_SSH_GATEWAY_TOKEN=${secrets.workspaceSshGatewayToken}`,
2963
3002
  ""
2964
3003
  ].join("\n");
3004
+ var garageSecrets = (secrets) => {
3005
+ const derive = (label) => createHmac("sha256", Buffer.from(secrets.betterAuthSecret, "hex")).update(label).digest("hex");
3006
+ return {
3007
+ rpcSecret: derive("mend-garage-rpc-secret"),
3008
+ adminToken: derive("mend-garage-admin-token"),
3009
+ keyId: `GK${derive("mend-garage-key-id").slice(0, 24)}`,
3010
+ keySecret: derive("mend-garage-key-secret")
3011
+ };
3012
+ };
3013
+ var renderGarage = (secrets, config) => {
3014
+ if (config.bucket !== "garage") return [];
3015
+ const garage = garageSecrets(secrets);
3016
+ return [
3017
+ `MEND_GARAGE_RPC_SECRET=${garage.rpcSecret}`,
3018
+ `MEND_GARAGE_ADMIN_TOKEN=${garage.adminToken}`,
3019
+ `MEND_GARAGE_KEY_ID=${garage.keyId}`,
3020
+ `MEND_GARAGE_KEY_SECRET=${garage.keySecret}`,
3021
+ "MEND_GARAGE_VOLUME_NAME=mend-garage"
3022
+ ];
3023
+ };
3024
+ var namespaceOf = (config) => config.bucket === "garage" ? MEND_DOCKER_NAMESPACE_WITH_GARAGE : MEND_DOCKER_NAMESPACE;
2965
3025
  var renderSecrets = (secrets, config) => {
2966
3026
  const sshHost = parseUrl(config.appUrl, "Server config appUrl").hostname.replace(/^\[|\]$/g, "");
2967
3027
  const composeBind = net2.isIP(config.bind) === 6 ? `[${config.bind}]` : config.bind;
@@ -2978,6 +3038,7 @@ var renderSecrets = (secrets, config) => {
2978
3038
  `SEALANT_SSH_HOST=${sshHost}`,
2979
3039
  "MEND_STORE_VOLUME_NAME=mend-store",
2980
3040
  "MEND_CONTROL_VOLUME_NAME=mend-control",
3041
+ ...renderGarage(secrets, config),
2981
3042
  `DOCKER_SOCKET_PATH=${config.dockerSocket}`,
2982
3043
  ""
2983
3044
  ].join("\n");
@@ -3147,6 +3208,17 @@ var checkLocalImages = async (runtime, config, policy = "local") => {
3147
3208
  );
3148
3209
  if (postgres.status !== 0)
3149
3210
  throw commandFailure("Preload postgres:17-alpine before continuing", postgres);
3211
+ if (config.bucket === "garage") {
3212
+ const garage = await inspectImage(
3213
+ runtime,
3214
+ config.dockerContext,
3215
+ GARAGE_IMAGE,
3216
+ "{{.Id}}",
3217
+ policy
3218
+ );
3219
+ if (garage.status !== 0)
3220
+ throw commandFailure(`Preload ${GARAGE_IMAGE} before continuing`, garage);
3221
+ }
3150
3222
  };
3151
3223
  var checkComposeImages = async (runtime, installation) => {
3152
3224
  const output = await runtime.run(
@@ -3160,14 +3232,50 @@ var checkComposeImages = async (runtime, installation) => {
3160
3232
  const images = output.stdout.trim().split(/\s+/).toSorted();
3161
3233
  const expected = [
3162
3234
  `ghcr.io/sealant-sh/mend:${installation.config.serverVersion}`,
3163
- "postgres:17-alpine"
3235
+ "postgres:17-alpine",
3236
+ ...installation.config.bucket === "garage" ? [GARAGE_IMAGE] : []
3164
3237
  ].toSorted();
3165
3238
  if (images.join("\n") !== expected.join("\n")) {
3166
3239
  throw setupError(
3167
- "Compose must use only the canonical pinned Mend image and official postgres:17-alpine."
3240
+ `Compose must use only the canonical pinned Mend image, official postgres:17-alpine${installation.config.bucket === "garage" ? ` and ${GARAGE_IMAGE}` : ""}.`
3168
3241
  );
3169
3242
  }
3170
3243
  };
3244
+ var initGarage = async (runtime, installation, secrets) => {
3245
+ if (installation.config.bucket !== "garage") return;
3246
+ const garage = garageSecrets(secrets);
3247
+ const exec = (args) => runtime.run(
3248
+ "docker",
3249
+ serverComposeArgs(
3250
+ { directory: installation.directory, dockerContext: installation.config.dockerContext },
3251
+ ["exec", "-T", "garage", "/garage", "-c", "/etc/garage.toml", ...args]
3252
+ ),
3253
+ { timeoutMs: serverProcessDeadlines.ordinary }
3254
+ );
3255
+ const status = await exec(["status"]);
3256
+ if (status.status !== 0 || status.error !== void 0)
3257
+ throw commandFailure("Garage did not answer its status", status);
3258
+ const node = status.stdout.match(/^([0-9a-f]{16})\s/m)?.[1];
3259
+ if (node === void 0) throw setupError("Garage reported no node in `garage status`.");
3260
+ await exec(["layout", "assign", "-z", "mend", "-c", "1GB", node]);
3261
+ await exec(["layout", "apply", "--version", "1"]);
3262
+ await exec(["bucket", "create", GARAGE_BUCKET]);
3263
+ await exec(["key", "import", "--yes", "-n", GARAGE_BUCKET, garage.keyId, garage.keySecret]);
3264
+ await exec([
3265
+ "bucket",
3266
+ "allow",
3267
+ "--read",
3268
+ "--write",
3269
+ "--owner",
3270
+ GARAGE_BUCKET,
3271
+ "--key",
3272
+ GARAGE_BUCKET
3273
+ ]);
3274
+ const info = await exec(["bucket", "info", GARAGE_BUCKET]);
3275
+ if (info.status !== 0 || info.error !== void 0 || !info.stdout.includes(garage.keyId))
3276
+ throw commandFailure(`Garage bucket ${GARAGE_BUCKET} is not readable by Mend's key`, info);
3277
+ runtime.writeLine(`Capture store bucket ${GARAGE_BUCKET} is laid out in Garage`);
3278
+ };
3171
3279
  var startingNotice = (runtime, version) => runtime.writeLine(
3172
3280
  `Starting Mend ${version} containers; Docker waits up to ${serverProcessDeadlines.composeWaitSeconds}s for them to report healthy`
3173
3281
  );
@@ -3214,7 +3322,7 @@ var setupServer = async (args, runtime, store) => {
3214
3322
  const operatingSystem = await checkDocker(runtime, selectedContext.name);
3215
3323
  runtime.writeLine(`Using Docker context "${selectedContext.name}" (${selectedContext.endpoint})`);
3216
3324
  const serverVersion = await resolveServerVersion(runtime, options, existing?.config ?? null);
3217
- const config = {
3325
+ const configWithoutBucket = {
3218
3326
  schemaVersion: CONFIG_SCHEMA_VERSION,
3219
3327
  assetContract: ASSET_CONTRACT,
3220
3328
  serverVersion,
@@ -3227,17 +3335,24 @@ var setupServer = async (args, runtime, store) => {
3227
3335
  ...validateExposure(existing?.config ?? null, options)
3228
3336
  };
3229
3337
  const assets = await resolveAssets(runtime, serverVersion, existing, store, options);
3338
+ const bucket = composeBucket(assets.compose);
3339
+ const config = {
3340
+ ...configWithoutBucket,
3341
+ ...bucket === void 0 ? {} : { bucket }
3342
+ };
3230
3343
  const secrets = savedSecrets ?? createSecrets(runtime);
3231
3344
  const generation = persistSetup(store, config, secrets, assets);
3232
3345
  await checkComposeImages(runtime, { directory: generation.directory, config });
3233
3346
  const ownership2 = await claimServerDockerVolumes(runtime, {
3234
3347
  dockerContext: config.dockerContext,
3235
- identityBytes: Buffer.from(generation.files.identity)
3348
+ identityBytes: Buffer.from(generation.files.identity),
3349
+ namespace: namespaceOf(config)
3236
3350
  });
3237
3351
  if (ownership2._tag === "error") throw setupError(ownership2.error.message);
3238
3352
  await checkLocalImages(runtime, config, options.offline ? "local" : "pull-missing");
3239
3353
  storeValue(store.activate(generation));
3240
3354
  await startCompose(runtime, config, generation);
3355
+ await initGarage(runtime, { directory: generation.directory, config }, secrets);
3241
3356
  await probeHealth(runtime, config.appUrl, config.serverVersion);
3242
3357
  runtime.writeLine(`Mend ${config.serverVersion} is reachable at ${config.appUrl}`);
3243
3358
  runtime.writeLine(
@@ -3302,7 +3417,7 @@ var composeCommand = async (runtime, installation, args) => {
3302
3417
  var interruptionNotice = (runtime) => runtime.writeLine(
3303
3418
  "Connections will be interrupted. Workspace containers and data are retained, but active work can lose connectivity and may need reconnection. Mend does not stop workspace containers."
3304
3419
  );
3305
- var startInstallation = async (runtime, installation) => {
3420
+ var startInstallation = async (runtime, installation, secrets) => {
3306
3421
  startingNotice(runtime, installation.config.serverVersion);
3307
3422
  await composeCommand(runtime, installation, [
3308
3423
  "up",
@@ -3312,6 +3427,7 @@ var startInstallation = async (runtime, installation) => {
3312
3427
  "never",
3313
3428
  "--no-build"
3314
3429
  ]);
3430
+ await initGarage(runtime, installation, secrets);
3315
3431
  await probeHealth(runtime, installation.config.appUrl, installation.config.serverVersion);
3316
3432
  runtime.writeLine(
3317
3433
  `Mend ${installation.config.serverVersion} is reachable at ${installation.config.appUrl}`
@@ -3349,11 +3465,17 @@ var upgradeServer = async (args, runtime, store, existing) => {
3349
3465
  const previous = storeValue(store.readActive());
3350
3466
  if (previous === null) throw setupError("The active server generation is missing.");
3351
3467
  const assets = await resolveAssets(runtime, version, existing, store, options);
3352
- const { registryPort: _legacyRegistryPort, ...carried } = existing.config;
3468
+ const {
3469
+ registryPort: _legacyRegistryPort,
3470
+ bucket: _previousBucket,
3471
+ ...carried
3472
+ } = existing.config;
3473
+ const bucket = composeBucket(assets.compose);
3353
3474
  const config = {
3354
3475
  ...carried,
3355
3476
  assetContract: ASSET_CONTRACT,
3356
- serverVersion: version
3477
+ serverVersion: version,
3478
+ ...bucket === void 0 ? {} : { bucket }
3357
3479
  };
3358
3480
  const secrets = parseSecrets(previous.files.identity);
3359
3481
  const files = {
@@ -3371,6 +3493,14 @@ var upgradeServer = async (args, runtime, store, existing) => {
3371
3493
  const target = storeValue(store.prepare(files));
3372
3494
  const installation = { directory: target.directory, config };
3373
3495
  await checkComposeImages(runtime, installation);
3496
+ if (config.bucket === "garage" && existing.config.bucket !== "garage") {
3497
+ const ownership2 = await claimServerDockerVolumes(runtime, {
3498
+ dockerContext: config.dockerContext,
3499
+ identityBytes: Buffer.from(previous.files.identity),
3500
+ namespace: namespaceOf(config)
3501
+ });
3502
+ if (ownership2._tag === "error") throw setupError(ownership2.error.message);
3503
+ }
3374
3504
  const running = await composeCommand(runtime, existing, [
3375
3505
  "ps",
3376
3506
  "--status",
@@ -3418,7 +3548,7 @@ var upgradeServer = async (args, runtime, store, existing) => {
3418
3548
  recovery = "Could not reselect the previous generation. Inspect active before retrying any command.";
3419
3549
  else if (appWasRunning) {
3420
3550
  try {
3421
- await startInstallation(runtime, existing);
3551
+ await startInstallation(runtime, existing, secrets);
3422
3552
  recovery = "Previous pin and app recovered.";
3423
3553
  } catch {
3424
3554
  recovery = "Previous pin retained, but the old app could not recover. Run mend server start after fixing Docker.";
@@ -3430,7 +3560,7 @@ var upgradeServer = async (args, runtime, store, existing) => {
3430
3560
  );
3431
3561
  }
3432
3562
  try {
3433
- await startInstallation(runtime, installation);
3563
+ await startInstallation(runtime, installation, secrets);
3434
3564
  } catch {
3435
3565
  throw setupError(
3436
3566
  `Mend ${version} startup, exact-version health or registry verification failed; migrations may have begun. The target pin remains active. Do NOT downgrade or restore the database automatically. Run mend server logs --tail 100 and mend server status; fix the target, then mend server start --offline. Previous generation: ${previous.directory}. Target generation: ${target.directory}. Database backup and recovery record: ${backup.directory}.`
@@ -3480,9 +3610,11 @@ var manageServer = async (command, args, runtime, store) => {
3480
3610
  if (identity === null) throw setupError("The persisted server identity is missing.");
3481
3611
  const ownership2 = await verifyServerDockerVolumes(runtime, {
3482
3612
  dockerContext: installation.config.dockerContext,
3483
- identityBytes: Buffer.from(identity)
3613
+ identityBytes: Buffer.from(identity),
3614
+ namespace: namespaceOf(installation.config)
3484
3615
  });
3485
3616
  if (ownership2._tag === "error") throw setupError(ownership2.error.message);
3617
+ const secrets = parseSecrets(identity);
3486
3618
  if (command === "upgrade") return upgradeServer(args, runtime, store, installation);
3487
3619
  if (command === "logs") {
3488
3620
  let tail = 100;
@@ -3512,7 +3644,7 @@ var manageServer = async (command, args, runtime, store) => {
3512
3644
  interruptionNotice(runtime);
3513
3645
  await composeCommand(runtime, installation, ["stop", "--timeout", "30"]);
3514
3646
  runtime.writeLine(
3515
- "Mend and Postgres stopped. Volumes, configuration and workspace containers are retained."
3647
+ `Mend, Postgres${installation.config.bucket === "garage" ? " and Garage" : ""} stopped. Volumes, configuration and workspace containers are retained.`
3516
3648
  );
3517
3649
  return;
3518
3650
  }
@@ -3521,7 +3653,7 @@ var manageServer = async (command, args, runtime, store) => {
3521
3653
  interruptionNotice(runtime);
3522
3654
  await composeCommand(runtime, installation, ["stop", "--timeout", "30", "mend"]);
3523
3655
  }
3524
- await startInstallation(runtime, installation);
3656
+ await startInstallation(runtime, installation, secrets);
3525
3657
  };
3526
3658
  var nodeServerRuntime = () => {
3527
3659
  const environment = { ...process.env };
@@ -4503,7 +4635,7 @@ var planLines = (plan, configDir) => {
4503
4635
  );
4504
4636
  if (dockerContext !== "") {
4505
4637
  lines.push(
4506
- ` containers mend, postgres \xB7 volumes ${[MEND_DOCKER_NAMESPACE.store, MEND_DOCKER_NAMESPACE.control, "mend-config", "mend-ssh", "mend-postgres"].join(", ")} \xB7 image ghcr.io/sealant-sh/mend:${version}`
4638
+ ` containers mend, postgres, garage \xB7 volumes ${[MEND_DOCKER_NAMESPACE_WITH_GARAGE.store, ...secondaryVolumesOf(MEND_DOCKER_NAMESPACE_WITH_GARAGE), "mend-config", "mend-ssh", "mend-postgres"].join(", ")} \xB7 image ghcr.io/sealant-sh/mend:${version}`
4507
4639
  );
4508
4640
  }
4509
4641
  lines.push(
@@ -4559,40 +4691,33 @@ var removeServer = async (runtime, failures, leftovers) => {
4559
4691
  `docker compose down failed: ${(down.error ?? down.stderr.trim()) || "no output"}. Containers and files are retained; fix Docker and run mend uninstall again.`
4560
4692
  );
4561
4693
  }
4562
- server.writeLine("removed containers mend, postgres and the Compose-owned volumes");
4694
+ server.writeLine("removed containers mend, postgres, garage and the Compose-owned volumes");
4695
+ const namespace = installation.config.bucket === "garage" ? MEND_DOCKER_NAMESPACE_WITH_GARAGE : MEND_DOCKER_NAMESPACE;
4696
+ const dataVolumes = [namespace.store, ...secondaryVolumesOf(namespace)];
4563
4697
  const identity = store.readIdentity();
4564
4698
  if (identity._tag === "error") throw identity.error;
4565
4699
  const owned = identity.value === null ? null : await verifyServerDockerVolumes(server, {
4566
4700
  dockerContext: context,
4567
- identityBytes: Buffer.from(identity.value)
4701
+ identityBytes: Buffer.from(identity.value),
4702
+ namespace
4568
4703
  });
4569
4704
  if (owned !== null && owned._tag === "ok") {
4570
4705
  const removed = await server.run(
4571
4706
  "docker",
4572
- dockerArgs(
4573
- context,
4574
- "volume",
4575
- "rm",
4576
- MEND_DOCKER_NAMESPACE.store,
4577
- MEND_DOCKER_NAMESPACE.control
4578
- )
4707
+ dockerArgs(context, "volume", "rm", ...dataVolumes)
4579
4708
  );
4580
4709
  if (removed.status === 0) {
4581
- server.writeLine(
4582
- `removed volumes ${MEND_DOCKER_NAMESPACE.store}, ${MEND_DOCKER_NAMESPACE.control}`
4583
- );
4710
+ server.writeLine(`removed volumes ${dataVolumes.join(", ")}`);
4584
4711
  } else {
4585
4712
  failures.push(
4586
- `could not remove volumes ${MEND_DOCKER_NAMESPACE.store}, ${MEND_DOCKER_NAMESPACE.control}: ${(removed.error ?? removed.stderr.trim()) || "no output"}`
4713
+ `could not remove volumes ${dataVolumes.join(", ")}: ${(removed.error ?? removed.stderr.trim()) || "no output"}`
4587
4714
  );
4588
4715
  }
4589
4716
  } else if (owned !== null && owned.error.reason === "missing") {
4590
- server.writeLine(
4591
- `volumes ${MEND_DOCKER_NAMESPACE.store}, ${MEND_DOCKER_NAMESPACE.control} were already gone`
4592
- );
4717
+ server.writeLine(`volumes ${dataVolumes.join(", ")} were already gone`);
4593
4718
  } else {
4594
4719
  leftovers.push(
4595
- `volumes ${MEND_DOCKER_NAMESPACE.store}, ${MEND_DOCKER_NAMESPACE.control}: ownership could not be confirmed for this installation, so they stay (docker --context ${context} volume ls)`
4720
+ `volumes ${dataVolumes.join(", ")}: ownership could not be confirmed for this installation, so they stay (docker --context ${context} volume ls)`
4596
4721
  );
4597
4722
  }
4598
4723
  const image = `ghcr.io/sealant-sh/mend:${installation.config.serverVersion}`;
@@ -7062,7 +7187,7 @@ var dashboard = async (config) => {
7062
7187
  );
7063
7188
  process.exit(rerun.status ?? 0);
7064
7189
  }
7065
- const { runDashboard } = await import("./dashboard-JIWYW3EM.js");
7190
+ const { runDashboard } = await import("./dashboard-5CBGTPSJ.js");
7066
7191
  const agentShare = await startShareIfBridge(config);
7067
7192
  try {
7068
7193
  await runDashboard({
@@ -1,4 +1,4 @@
1
- .TH MEND-ACCOUNTS 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-ACCOUNTS 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-accounts \- your connected accounts on the platform
4
4
  .SH SYNOPSIS
package/man/mend-adopt.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-ADOPT 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-ADOPT 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-adopt \- adopt a repository into the store
4
4
  .SH SYNOPSIS
package/man/mend-attach.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-ATTACH 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-ATTACH 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-attach \- reattach this terminal to a running session
4
4
  .SH SYNOPSIS
package/man/mend-codex.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-CODEX 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-CODEX 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-codex \- launch codex, claude, or opencode in a recorded worktree
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-COMPLETIONS 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-COMPLETIONS 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-completions \- print the TAB\-completion hook
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-CONNECT 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-CONNECT 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-connect \- send this machine's provider credential to the platform
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-CONTINUE 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-CONTINUE 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-continue \- resume a session with its pending review follow\-up
4
4
  .SH SYNOPSIS
package/man/mend-doctor.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-DOCTOR 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-DOCTOR 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-doctor \- check this machine's setup
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-DOTFILES-SYNC 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-DOTFILES-SYNC 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-dotfiles\-sync \- capture config files from this machine into your store
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-DOTFILES 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-DOTFILES 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-dotfiles \- your dotfiles on the server: repo and synced files
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-ENV-CLUSTER 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-ENV-CLUSTER 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-env\-cluster \- bind Kubernetes secrets and configmaps to the workspaces
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-ENV-LOAD 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-ENV-LOAD 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-env\-load \- load a .env into the project
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-ENV-SHOW 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-ENV-SHOW 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-env\-show \- what the project store holds: names only, never values
4
4
  .SH SYNOPSIS
package/man/mend-help.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-HELP 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-HELP 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-help \- this index, or one command's page
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-KEYS-INIT 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-KEYS-INIT 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-keys\-init \- create your Mend key (ed25519) on the server
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-KEYS-MODE 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-KEYS-MODE 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-keys\-mode \- how your remotes are reached: mend\-key or bridge
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-KEYS-SHARE 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-KEYS-SHARE 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-keys\-share \- relay this machine's ssh\-agent to the server
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-KEYS-SHOW 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-KEYS-SHOW 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-keys\-show \- print your Mend public key
4
4
  .SH SYNOPSIS
package/man/mend-login.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-LOGIN 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-LOGIN 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-login \- sign this terminal in through the browser
4
4
  .SH SYNOPSIS
package/man/mend-logout.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-LOGOUT 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-LOGOUT 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-logout \- revoke this terminal's device token and forget it
4
4
  .SH SYNOPSIS
package/man/mend-man.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-MAN 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-MAN 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-man \- one command's page, or the whole manual, in man
4
4
  .SH SYNOPSIS
package/man/mend-pair.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-PAIR 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-PAIR 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-pair \- pair a phone or a second machine
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-PROJECTS 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-PROJECTS 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-projects \- adopted projects and their live sessions
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-REFRESH 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-REFRESH 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-refresh \- fetch origin's branches into the store
4
4
  .SH SYNOPSIS
package/man/mend-rejoin.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-REJOIN 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-REJOIN 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-rejoin \- attach if live, otherwise resume
4
4
  .SH SYNOPSIS
package/man/mend-resume.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-RESUME 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-RESUME 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-resume \- rejoin a settled session with its state restored
4
4
  .SH SYNOPSIS
package/man/mend-run.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-RUN 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-RUN 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-run \- run any command as a session
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVER-LOGS 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVER-LOGS 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-server\-logs \- print a bounded tail of the installation's container logs
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVER-RESTART 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVER-RESTART 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-server\-restart \- restart Mend using the same generation and pin
4
4
  .SH SYNOPSIS
@@ -6,7 +6,7 @@ mend\-server\-restart \- restart Mend using the same generation and pin
6
6
  .br
7
7
  .SH DESCRIPTION
8
8
  .PP
9
- Checks preloaded images before stopping Mend, then starts the saved generation and verifies exact\-version health. Postgres stays running. Connections are interrupted; workspace containers and data remain, but active work may need reconnection.
9
+ Checks preloaded images before stopping Mend, then starts the saved generation and verifies exact\-version health. Postgres and Garage stay running. Connections are interrupted; workspace containers and data remain, but active work may need reconnection.
10
10
  .SH OPTIONS
11
11
  .TP
12
12
  .B \-\-offline
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVER-SETUP 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVER-SETUP 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-server\-setup \- install or repair the local Mend server
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVER-START 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVER-START 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-server\-start \- start the selected server generation
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVER-STATUS 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVER-STATUS 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-server\-status \- show the saved pin, generation and container state
4
4
  .SH SYNOPSIS
@@ -1,6 +1,6 @@
1
- .TH MEND-SERVER-STOP 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVER-STOP 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
- mend\-server\-stop \- stop Mend and Postgres without deleting data
3
+ mend\-server\-stop \- stop Mend, Postgres and Garage without deleting data
4
4
  .SH SYNOPSIS
5
5
  .B mend server stop
6
6
  .br
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVER-UPGRADE 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVER-UPGRADE 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-server\-upgrade \- upgrade to an explicit version with a database backup
4
4
  .SH SYNOPSIS
package/man/mend-server.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVER 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVER 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-server \- install and manage this machine's Mend server
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVICE-ADD 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVICE-ADD 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-service\-add \- adopt a port something in the workspace already listens on
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVICE-CONNECT 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVICE-CONNECT 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-service\-connect \- bring live Services to this machine's loopback
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVICE-INIT 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVICE-INIT 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-service\-init \- scaffold mend.toml from package.json and compose ports
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVICE-LIST 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVICE-LIST 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-service\-list \- every live service and its observed state
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVICE-LOGS 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVICE-LOGS 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-service\-logs \- follow a supervised service's output
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVICE-RESTART 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVICE-RESTART 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-service\-restart \- re\-run a service's recorded command, same URL
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVICE-RUN 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVICE-RUN 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-service\-run \- start and supervise a server in the session's workspace
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SERVICE-STOP 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SERVICE-STOP 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-service\-stop \- stop a service and close its host port
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SESSIONS 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SESSIONS 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-sessions \- sessions with their review facts
4
4
  .SH SYNOPSIS
package/man/mend-shell.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-SHELL 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SHELL 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-shell \- open a shell in a live session's workspace
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SKILLS-PUSH 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SKILLS-PUSH 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-skills\-push \- upload ~/.agents/skills into the library
4
4
  .SH SYNOPSIS
package/man/mend-skills.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-SKILLS 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SKILLS 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-skills \- your skill library on the server, or a project's
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-SSH-SETUP 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SSH-SETUP 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-ssh\-setup \- make this machine ready to ssh into workspaces, once
4
4
  .SH SYNOPSIS
package/man/mend-ssh.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-SSH 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-SSH 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-ssh \- workspace SSH status: gateway, registered keys, ssh config
4
4
  .SH SYNOPSIS
package/man/mend-stop.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-STOP 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-STOP 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-stop \- stop the agent; the record and the review remain
4
4
  .SH SYNOPSIS
package/man/mend-ui.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND-UI 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-UI 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-ui \- the dashboard: every project and session, live
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-UNINSTALL 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-UNINSTALL 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-uninstall \- remove the server, this machine's Mend files, or both
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-VERSION 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-VERSION 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-version \- this CLI's version, and the server's when it answers
4
4
  .SH SYNOPSIS
@@ -1,4 +1,4 @@
1
- .TH MEND-WORKTREES 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND-WORKTREES 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend\-worktrees \- every worktree and the sessions inside it
4
4
  .SH SYNOPSIS
package/man/mend.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH MEND 1 "" "mend 0.26.0" "mend manual"
1
+ .TH MEND 1 "" "mend 0.27.2" "mend manual"
2
2
  .SH NAME
3
3
  mend \- the agent workbench
4
4
  .SH SYNOPSIS
@@ -137,7 +137,7 @@ show the saved pin, generation and container state
137
137
  start the selected server generation
138
138
  .TP
139
139
  .B mend server stop
140
- stop Mend and Postgres without deleting data
140
+ stop Mend, Postgres and Garage without deleting data
141
141
  .TP
142
142
  .B mend server restart
143
143
  restart Mend using the same generation and pin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sealant/mend",
3
- "version": "0.26.0",
3
+ "version": "0.27.2",
4
4
  "description": "The mend CLI: adopt a repository into Mend's store, launch your coding agent in a recorded session worktree, detach and reattach from any terminal",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -48,8 +48,8 @@
48
48
  "typescript": "^5.7.2",
49
49
  "vitest": "^3.0.5",
50
50
  "@mend/domain": "0.0.0",
51
- "@mend/typescript": "0.0.0",
52
- "@mend/workspace-ssh": "0.0.0"
51
+ "@mend/workspace-ssh": "0.0.0",
52
+ "@mend/typescript": "0.0.0"
53
53
  },
54
54
  "engines": {
55
55
  "node": ">=22"