@sealant/mend 0.34.2 → 0.34.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/{chunk-JJ66V76Y.js → chunk-5DORQXQT.js} +106 -7
  2. package/dist/{dashboard-OADX3PHI.js → dashboard-KZ4XGZ6W.js} +32 -13
  3. package/dist/main.js +378 -89
  4. package/man/mend-accounts.1 +1 -1
  5. package/man/mend-adopt.1 +1 -1
  6. package/man/mend-attach.1 +1 -1
  7. package/man/mend-codex.1 +1 -1
  8. package/man/mend-completions.1 +1 -1
  9. package/man/mend-connect.1 +1 -1
  10. package/man/mend-continue.1 +1 -1
  11. package/man/mend-doctor.1 +1 -1
  12. package/man/mend-dotfiles-repo.1 +1 -1
  13. package/man/mend-dotfiles-sync.1 +1 -1
  14. package/man/mend-dotfiles.1 +1 -1
  15. package/man/mend-env-cluster.1 +1 -1
  16. package/man/mend-env-load.1 +1 -1
  17. package/man/mend-env-show.1 +1 -1
  18. package/man/mend-folder-create.1 +1 -1
  19. package/man/mend-folder-list.1 +1 -1
  20. package/man/mend-folder-push.1 +1 -1
  21. package/man/mend-folder-rm.1 +1 -1
  22. package/man/mend-git-author.1 +1 -1
  23. package/man/mend-help.1 +1 -1
  24. package/man/mend-invite.1 +1 -1
  25. package/man/mend-keys-init.1 +1 -1
  26. package/man/mend-keys-mode.1 +1 -1
  27. package/man/mend-keys-share.1 +1 -1
  28. package/man/mend-keys-show.1 +1 -1
  29. package/man/mend-land.1 +1 -1
  30. package/man/mend-login.1 +1 -1
  31. package/man/mend-logout.1 +1 -1
  32. package/man/mend-man.1 +1 -1
  33. package/man/mend-members.1 +1 -1
  34. package/man/mend-operator-exposure.1 +1 -1
  35. package/man/mend-operator-gate.1 +1 -1
  36. package/man/mend-operator-grant-owner.1 +1 -1
  37. package/man/mend-operator-org-create.1 +1 -1
  38. package/man/mend-operator-org-invite-owner.1 +1 -1
  39. package/man/mend-operator-org-list.1 +1 -1
  40. package/man/mend-operator-org-rename.1 +1 -1
  41. package/man/mend-operator-reset-link.1 +1 -1
  42. package/man/mend-pair.1 +1 -1
  43. package/man/mend-projects.1 +1 -1
  44. package/man/mend-pull.1 +1 -1
  45. package/man/mend-refresh.1 +1 -1
  46. package/man/mend-rejoin.1 +1 -1
  47. package/man/mend-resume.1 +1 -1
  48. package/man/mend-run.1 +1 -1
  49. package/man/mend-server-logs.1 +1 -1
  50. package/man/mend-server-restart.1 +1 -1
  51. package/man/mend-server-setup.1 +1 -1
  52. package/man/mend-server-start.1 +1 -1
  53. package/man/mend-server-status.1 +1 -1
  54. package/man/mend-server-stop.1 +1 -1
  55. package/man/mend-server-upgrade.1 +1 -1
  56. package/man/mend-server.1 +1 -1
  57. package/man/mend-service-add.1 +1 -1
  58. package/man/mend-service-connect.1 +1 -1
  59. package/man/mend-service-init.1 +1 -1
  60. package/man/mend-service-list.1 +1 -1
  61. package/man/mend-service-logs.1 +1 -1
  62. package/man/mend-service-restart.1 +1 -1
  63. package/man/mend-service-run.1 +1 -1
  64. package/man/mend-service-stop.1 +1 -1
  65. package/man/mend-session-share.1 +1 -1
  66. package/man/mend-sessions.1 +1 -1
  67. package/man/mend-shell.1 +1 -1
  68. package/man/mend-skills-push.1 +1 -1
  69. package/man/mend-skills.1 +1 -1
  70. package/man/mend-snake.1 +1 -1
  71. package/man/mend-ssh-setup.1 +1 -1
  72. package/man/mend-ssh.1 +1 -1
  73. package/man/mend-stop.1 +1 -1
  74. package/man/mend-ui.1 +1 -1
  75. package/man/mend-uninstall.1 +1 -1
  76. package/man/mend-version.1 +1 -1
  77. package/man/mend-worktrees.1 +1 -1
  78. package/man/mend.1 +1 -1
  79. package/package.json +3 -3
@@ -4,6 +4,26 @@ var __export = (target, all3) => {
4
4
  __defProp(target, name, { get: all3[name], enumerable: true });
5
5
  };
6
6
 
7
+ // ../../packages/domain/src/workbench/harness-launch.ts
8
+ var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
9
+ var PERMISSION_MODES = ["bypass", "ask"];
10
+
11
+ // ../../packages/domain/src/workbench/agent-start.ts
12
+ var agentStartingLine = (label, facts, elapsedMs) => `${label} is starting${facts.freshMachine ? " on the new machine" : ""} \xB7 ${Math.max(0, Math.round(elapsedMs / 1e3))}s`;
13
+ var MAX_TRUSTED_ELAPSED_MS = 60 * 60 * 1e3;
14
+ var timeOf = (value3) => value3 === void 0 ? Number.NaN : typeof value3 === "string" ? Date.parse(value3) : value3.getTime();
15
+ var agentStartingFacts = (currentAgent, processes, now2) => {
16
+ if (currentAgent === null || currentAgent === void 0) {
17
+ return { startedAt: null, freshMachine: false };
18
+ }
19
+ const created = timeOf(currentAgent.createdAt);
20
+ const trusted = Number.isFinite(created) && created <= now2 && now2 - created <= MAX_TRUSTED_ELAPSED_MS;
21
+ const freshMachine = processes !== void 0 && currentAgent.sealantWorkspaceId !== void 0 && Number.isFinite(created) && !processes.some(
22
+ (process2) => process2.id !== currentAgent.id && process2.sealantWorkspaceId === currentAgent.sealantWorkspaceId && timeOf(process2.createdAt) < created
23
+ );
24
+ return { startedAt: trusted ? created : null, freshMachine };
25
+ };
26
+
7
27
  // ../../node_modules/.pnpm/effect@4.0.0-beta.93/node_modules/effect/dist/Pipeable.js
8
28
  var pipeArguments = (self, args2) => {
9
29
  switch (args2.length) {
@@ -27325,7 +27345,7 @@ var leftWords = (facts) => {
27325
27345
  return null;
27326
27346
  };
27327
27347
  var utcTime = (at) => `${at.toISOString().slice(11, 19)} UTC`;
27328
- var timeOf = (at) => {
27348
+ var timeOf2 = (at) => {
27329
27349
  if (at === null || at === void 0) return null;
27330
27350
  const date2 = typeof at === "string" ? new Date(at) : at;
27331
27351
  return Number.isNaN(date2.getTime()) ? null : date2;
@@ -27357,7 +27377,7 @@ var captureStatusLine = (facts) => {
27357
27377
  ].join(" \xB7 ");
27358
27378
  }
27359
27379
  if (overdue !== null) return overdue;
27360
- const failingSince = timeOf(facts.captureFailingSince);
27380
+ const failingSince = timeOf2(facts.captureFailingSince);
27361
27381
  if (failingSince !== null) {
27362
27382
  const error = facts.captureFailingError ?? null;
27363
27383
  return [
@@ -27366,7 +27386,7 @@ var captureStatusLine = (facts) => {
27366
27386
  ].join(" \xB7 ");
27367
27387
  }
27368
27388
  if ((facts.captureRefused ?? 0) > 0) return `not saved \xB7 ${facts.captureRefused} refused`;
27369
- const discardedAt = timeOf(facts.captureDiscardedAt);
27389
+ const discardedAt = timeOf2(facts.captureDiscardedAt);
27370
27390
  if (discardedAt !== null) {
27371
27391
  const by = facts.captureDiscardedBy ?? null;
27372
27392
  return `unsaved work discarded${by === null || by === "" ? "" : ` by ${by}`} at ${utcTime(discardedAt)}`;
@@ -27560,10 +27580,6 @@ var FollowUp = class extends Schema_exports.Class("FollowUp")({
27560
27580
  }) {
27561
27581
  };
27562
27582
 
27563
- // ../../packages/domain/src/workbench/harness-launch.ts
27564
- var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
27565
- var PERMISSION_MODES = ["bypass", "ask"];
27566
-
27567
27583
  // ../../packages/domain/src/workbench/mount.ts
27568
27584
  var ProjectMount = class extends Schema_exports.Class("ProjectMount")({
27569
27585
  id: ProjectMountId,
@@ -28125,6 +28141,15 @@ var SessionProcess = class extends Schema_exports.Class("SessionProcess")({
28125
28141
  /** Services: the host port Mend binds on its private interfaces. */
28126
28142
  hostPort: Schema_exports.NullOr(Schema_exports.Int),
28127
28143
  createdAt: Timestamp,
28144
+ /**
28145
+ * When the process's record first carried output: the first screen an agent drew. Null while
28146
+ * it has drawn nothing, for a process with no run of ours, and on rows from before it was
28147
+ * observed. Older servers omit it.
28148
+ */
28149
+ firstOutputAt: Schema_exports.NullOr(Timestamp).pipe(
28150
+ Schema_exports.withDecodingDefaultKey(Effect_exports.succeed(null)),
28151
+ Schema_exports.withConstructorDefault(Effect_exports.succeed(null))
28152
+ ),
28128
28153
  exitedAt: Schema_exports.NullOr(Timestamp),
28129
28154
  updatedAt: Timestamp
28130
28155
  }) {
@@ -28799,6 +28824,73 @@ var trackBracketedPaste = (output, current) => {
28799
28824
  };
28800
28825
  var pasteBytes = (text, bracketed) => Buffer.from(bracketed ? `\x1B[200~${text}\x1B[201~` : text, "utf8");
28801
28826
 
28827
+ // src/server-request.ts
28828
+ var MendRequestError = class extends Error {
28829
+ kind;
28830
+ status;
28831
+ constructor(kind, message, status = null) {
28832
+ super(message);
28833
+ this.name = "MendRequestError";
28834
+ this.kind = kind;
28835
+ this.status = status;
28836
+ }
28837
+ };
28838
+ var TIMEOUT_CODES = /* @__PURE__ */ new Set([
28839
+ "UND_ERR_HEADERS_TIMEOUT",
28840
+ "UND_ERR_BODY_TIMEOUT"
28841
+ ]);
28842
+ var DROPPED_CODES = /* @__PURE__ */ new Set([
28843
+ "UND_ERR_SOCKET",
28844
+ "UND_ERR_CLOSED",
28845
+ "ECONNRESET",
28846
+ "EPIPE"
28847
+ ]);
28848
+ var codeOf = (value3) => {
28849
+ if (typeof value3 !== "object" || value3 === null || !("code" in value3)) return null;
28850
+ return typeof value3.code === "string" ? value3.code : null;
28851
+ };
28852
+ var nameOf = (value3) => {
28853
+ if (typeof value3 !== "object" || value3 === null || !("name" in value3)) return null;
28854
+ return typeof value3.name === "string" ? value3.name : null;
28855
+ };
28856
+ var noAnswerOf = (error) => {
28857
+ const name = nameOf(error);
28858
+ if (name === "TimeoutError" || name === "AbortError") return "timeout";
28859
+ const cause = typeof error === "object" && error !== null && "cause" in error ? error.cause : null;
28860
+ const code = codeOf(cause) ?? codeOf(error);
28861
+ if (code !== null && TIMEOUT_CODES.has(code)) return "timeout";
28862
+ if (code !== null && DROPPED_CODES.has(code)) return "dropped";
28863
+ return "unreachable";
28864
+ };
28865
+ var spoken = (ms) => {
28866
+ const seconds2 = Math.round(ms / 1e3);
28867
+ if (seconds2 < 90) return `${seconds2}s`;
28868
+ return `${Math.round(seconds2 / 60)} min`;
28869
+ };
28870
+ var noAnswerMessage = (kind, serverUrl, call, waitedMs) => {
28871
+ if (kind === "unreachable")
28872
+ return `cannot reach the Mend server at ${serverUrl} \u2014 is it running?`;
28873
+ if (kind === "timeout") {
28874
+ return `${call} has had no answer from ${serverUrl} after ${spoken(waitedMs)} \u2014 the server accepted it and may still be working on it`;
28875
+ }
28876
+ return `the connection to ${serverUrl} closed before ${call} answered \u2014 the server may still be working on it`;
28877
+ };
28878
+ var noAnswerError = (error, serverUrl, call, waitedMs) => {
28879
+ const kind = noAnswerOf(error);
28880
+ return new MendRequestError(kind, noAnswerMessage(kind, serverUrl, call, waitedMs));
28881
+ };
28882
+ var GATEWAY_STATUSES = /* @__PURE__ */ new Set([
28883
+ 502,
28884
+ 503,
28885
+ 504,
28886
+ 520,
28887
+ 521,
28888
+ 522,
28889
+ 523,
28890
+ 524
28891
+ ]);
28892
+ var mayStillBeWorking = (error) => error instanceof MendRequestError && (error.kind === "timeout" || error.kind === "dropped" || error.kind === "http" && error.status !== null && GATEWAY_STATUSES.has(error.status));
28893
+
28802
28894
  // src/workbench-events.ts
28803
28895
  var createSseParser = () => {
28804
28896
  let buffer = "";
@@ -28848,6 +28940,8 @@ var eventFamilies = (payload) => {
28848
28940
  };
28849
28941
 
28850
28942
  export {
28943
+ agentStartingLine,
28944
+ agentStartingFacts,
28851
28945
  repositoryCloneUrlIssue,
28852
28946
  RepositoryCloneUrl,
28853
28947
  landingFactsFromWire,
@@ -28879,6 +28973,11 @@ export {
28879
28973
  isPasteChunk,
28880
28974
  trackBracketedPaste,
28881
28975
  pasteBytes,
28976
+ MendRequestError,
28977
+ spoken,
28978
+ noAnswerError,
28979
+ GATEWAY_STATUSES,
28980
+ mayStillBeWorking,
28882
28981
  createSseParser,
28883
28982
  eventFamilies
28884
28983
  };
@@ -10,11 +10,12 @@ import {
10
10
  eventFamilies,
11
11
  isPendingId,
12
12
  matchProjectByCwd,
13
+ mayStillBeWorking,
13
14
  normalizeProjectName,
14
15
  pendingId,
15
16
  repositoryCloneUrlIssue,
16
17
  servicesHoldLine
17
- } from "./chunk-JJ66V76Y.js";
18
+ } from "./chunk-5DORQXQT.js";
18
19
 
19
20
  // src/dashboard.tsx
20
21
  import { CliRenderEvents, createCliRenderer } from "@opentui/core";
@@ -2412,6 +2413,15 @@ var StatusLine = ({
2412
2413
  return /* @__PURE__ */ jsx3("text", { height: 1, fg: INK_2, bg: "transparent", children: text });
2413
2414
  };
2414
2415
  var errorText = (error) => error instanceof Error ? error.message : String(error);
2416
+ var startedOrStarting = async (start, starting) => {
2417
+ try {
2418
+ return await start;
2419
+ } catch (error) {
2420
+ if (mayStillBeWorking(error)) return { ...starting, status: "starting" };
2421
+ throw error;
2422
+ }
2423
+ };
2424
+ var startedLine = (session) => session.status === "starting" ? `still starting \xB7 ${sessionDisplayName(session)} \u2014 a attaches once the row reads running` : `started \xB7 ${sessionDisplayName(session)} \u2014 a attaches`;
2415
2425
  var keepSpanVisible = (scroll, top, height, viewRows) => {
2416
2426
  if (scroll === null || viewRows <= 0) return;
2417
2427
  const viewH = Math.max(1, viewRows);
@@ -2597,7 +2607,7 @@ var App = ({ ctx, onQuit }) => {
2597
2607
  `).join("");
2598
2608
  process.stdout.write(
2599
2609
  `
2600
- attached \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
2610
+ attaching \xB7 ${session.harness} \xB7 ${short} \xB7 detach: Ctrl+]
2601
2611
  ${tunneled}
2602
2612
  `
2603
2613
  );
@@ -2641,7 +2651,7 @@ ${tunneled}
2641
2651
  lockRef.current = false;
2642
2652
  }
2643
2653
  say(
2644
- outcome === "unavailable" ? "attach unavailable \u2014 could not connect" : outcome === "detached" || outcome === "interrupted" ? `detached \u2014 ${short} keeps running` : outcome === "dropped" ? `disconnected \xB7 ${short} \u2014 refreshing session status` : `terminal ended \xB7 ${short} \u2014 refreshing session status`
2654
+ outcome === "unavailable" ? "attach unavailable \u2014 could not connect" : outcome === "no-answer" ? `no answer \u2014 the server did not open ${short}'s terminal; it keeps running \xB7 a tries again` : outcome === "detached" || outcome === "interrupted" ? `detached \u2014 ${short} keeps running` : outcome === "dropped" ? `disconnected \xB7 ${short} \u2014 refreshing session status` : `terminal ended \xB7 ${short} \u2014 refreshing session status`
2645
2655
  );
2646
2656
  refetch();
2647
2657
  };
@@ -2663,8 +2673,10 @@ ${tunneled}
2663
2673
  name: vars.name,
2664
2674
  base: vars.base
2665
2675
  });
2666
- await ctx.api("POST", `/sessions/${session.id}/launch`, { argv });
2667
- return session;
2676
+ return startedOrStarting(
2677
+ ctx.api("POST", `/sessions/${session.id}/launch`, { argv }),
2678
+ session
2679
+ );
2668
2680
  },
2669
2681
  onMutate: async (vars) => {
2670
2682
  await queryClient.cancelQueries({ queryKey: WORKBENCH_KEY });
@@ -2700,7 +2712,7 @@ ${tunneled}
2700
2712
  );
2701
2713
  setSessionKey((current) => current === vars.pendingKey ? session.id : current);
2702
2714
  setBusy(null);
2703
- say(`started \xB7 ${sessionDisplayName(session)} \u2014 a attaches`);
2715
+ say(startedLine(session));
2704
2716
  },
2705
2717
  onSettled: (_data, _error, vars) => {
2706
2718
  gate.release(vars.gateKey);
@@ -2708,9 +2720,12 @@ ${tunneled}
2708
2720
  }
2709
2721
  });
2710
2722
  const resumeMutation = useMutation2({
2711
- mutationFn: (vars) => ctx.api("POST", `/sessions/${vars.session.id}/resume`, {
2712
- harness: vars.harness
2713
- }),
2723
+ mutationFn: (vars) => startedOrStarting(
2724
+ ctx.api("POST", `/sessions/${vars.session.id}/resume`, {
2725
+ harness: vars.harness
2726
+ }),
2727
+ vars.session
2728
+ ),
2714
2729
  onMutate: async (vars) => {
2715
2730
  await queryClient.cancelQueries({ queryKey: WORKBENCH_KEY });
2716
2731
  patchWorkbench(
@@ -2735,7 +2750,9 @@ ${tunneled}
2735
2750
  (current) => replaceSession(current, vars.projectId, vars.session.id, resumed)
2736
2751
  );
2737
2752
  setBusy(null);
2738
- say(`resumed \xB7 ${sessionDisplayName(resumed)} \u2014 a attaches`);
2753
+ say(
2754
+ resumed.status === "starting" ? `still resuming \xB7 ${sessionDisplayName(resumed)} \u2014 a attaches once the row reads running` : `resumed \xB7 ${sessionDisplayName(resumed)} \u2014 a attaches`
2755
+ );
2739
2756
  },
2740
2757
  onSettled: (_data, _error, vars) => {
2741
2758
  gate.release(vars.gateKey);
@@ -2846,8 +2863,10 @@ ${tunneled}
2846
2863
  harness: vars.harness,
2847
2864
  label: null
2848
2865
  });
2849
- await ctx.api("POST", `/sessions/${session.id}/launch`, { argv });
2850
- return session;
2866
+ return startedOrStarting(
2867
+ ctx.api("POST", `/sessions/${session.id}/launch`, { argv }),
2868
+ session
2869
+ );
2851
2870
  },
2852
2871
  onMutate: async (vars) => {
2853
2872
  await queryClient.cancelQueries({ queryKey: WORKBENCH_KEY });
@@ -2880,7 +2899,7 @@ ${tunneled}
2880
2899
  );
2881
2900
  setSessionKey((current) => current === vars.pendingKey ? session.id : current);
2882
2901
  setBusy(null);
2883
- say(`started \xB7 ${sessionDisplayName(session)} \u2014 a attaches`);
2902
+ say(startedLine(session));
2884
2903
  },
2885
2904
  onSettled: (_data, _error, vars) => {
2886
2905
  gate.release(vars.gateKey);