@sealant/mend 0.36.0-next.632 → 0.36.0-next.645

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 (91) hide show
  1. package/README.md +4 -1
  2. package/dist/{chunk-OHOMU7JP.js → chunk-ASHAV3UV.js} +334 -3
  3. package/dist/{dashboard-34ZANJ24.js → dashboard-4J2WMT3G.js} +142 -17
  4. package/dist/main.js +646 -461
  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 +3 -1
  9. package/man/mend-completions.1 +1 -1
  10. package/man/mend-connect-pi.1 +1 -1
  11. package/man/mend-connect.1 +3 -1
  12. package/man/mend-continue.1 +1 -1
  13. package/man/mend-doctor.1 +1 -1
  14. package/man/mend-dotfiles-repo.1 +1 -1
  15. package/man/mend-dotfiles-sync.1 +1 -1
  16. package/man/mend-dotfiles.1 +1 -1
  17. package/man/mend-env-cluster.1 +1 -1
  18. package/man/mend-env-load.1 +1 -1
  19. package/man/mend-env-show.1 +1 -1
  20. package/man/mend-folder-create.1 +1 -1
  21. package/man/mend-folder-list.1 +1 -1
  22. package/man/mend-folder-push.1 +1 -1
  23. package/man/mend-folder-rm.1 +1 -1
  24. package/man/mend-git-author.1 +1 -1
  25. package/man/mend-help.1 +1 -1
  26. package/man/mend-invite.1 +1 -1
  27. package/man/mend-keys-init.1 +1 -1
  28. package/man/mend-keys-mode.1 +1 -1
  29. package/man/mend-keys-share.1 +1 -1
  30. package/man/mend-keys-show.1 +1 -1
  31. package/man/mend-land.1 +1 -1
  32. package/man/mend-login.1 +1 -1
  33. package/man/mend-logout.1 +1 -1
  34. package/man/mend-man.1 +1 -1
  35. package/man/mend-members.1 +1 -1
  36. package/man/mend-memory-import.1 +1 -1
  37. package/man/mend-memory-rm.1 +1 -1
  38. package/man/mend-memory-show.1 +1 -1
  39. package/man/mend-memory.1 +1 -1
  40. package/man/mend-models.1 +1 -1
  41. package/man/mend-operator-exposure.1 +1 -1
  42. package/man/mend-operator-gate.1 +1 -1
  43. package/man/mend-operator-grant-owner.1 +1 -1
  44. package/man/mend-operator-org-create.1 +1 -1
  45. package/man/mend-operator-org-invite-owner.1 +1 -1
  46. package/man/mend-operator-org-list.1 +1 -1
  47. package/man/mend-operator-org-rename.1 +1 -1
  48. package/man/mend-operator-reset-link.1 +1 -1
  49. package/man/mend-pair.1 +1 -1
  50. package/man/mend-projects.1 +1 -1
  51. package/man/mend-pull.1 +1 -1
  52. package/man/mend-refresh.1 +1 -1
  53. package/man/mend-rejoin.1 +1 -1
  54. package/man/mend-resume.1 +1 -1
  55. package/man/mend-run.1 +1 -1
  56. package/man/mend-secrets-add.1 +1 -1
  57. package/man/mend-secrets-rm.1 +1 -1
  58. package/man/mend-secrets.1 +1 -1
  59. package/man/mend-server-logs.1 +1 -1
  60. package/man/mend-server-restart.1 +1 -1
  61. package/man/mend-server-setup.1 +1 -1
  62. package/man/mend-server-start.1 +1 -1
  63. package/man/mend-server-status.1 +1 -1
  64. package/man/mend-server-stop.1 +1 -1
  65. package/man/mend-server-upgrade.1 +1 -1
  66. package/man/mend-server.1 +1 -1
  67. package/man/mend-service-add.1 +1 -1
  68. package/man/mend-service-connect.1 +1 -1
  69. package/man/mend-service-init.1 +1 -1
  70. package/man/mend-service-list.1 +1 -1
  71. package/man/mend-service-logs.1 +1 -1
  72. package/man/mend-service-restart.1 +1 -1
  73. package/man/mend-service-run.1 +1 -1
  74. package/man/mend-service-stop.1 +1 -1
  75. package/man/mend-session-share.1 +13 -3
  76. package/man/mend-sessions.1 +3 -1
  77. package/man/mend-shell.1 +1 -1
  78. package/man/mend-skills-push.1 +1 -1
  79. package/man/mend-skills.1 +1 -1
  80. package/man/mend-snake.1 +1 -1
  81. package/man/mend-ssh-setup.1 +1 -1
  82. package/man/mend-ssh.1 +1 -1
  83. package/man/mend-stop.1 +1 -1
  84. package/man/mend-ui.1 +1 -1
  85. package/man/mend-uninstall.1 +1 -1
  86. package/man/mend-version.1 +1 -1
  87. package/man/mend-workspace-replace.1 +24 -0
  88. package/man/mend-worktrees-rm.1 +1 -1
  89. package/man/mend-worktrees.1 +1 -1
  90. package/man/mend.1 +5 -2
  91. package/package.json +2 -2
@@ -8,6 +8,7 @@ import {
8
8
  createSseParser,
9
9
  cwdFacts,
10
10
  eventFamilies,
11
+ hasPersonFacts,
11
12
  isPendingId,
12
13
  launchPhaseOf,
13
14
  matchProjectByCwd,
@@ -16,8 +17,13 @@ import {
16
17
  pendingId,
17
18
  repositoryCloneUrlIssue,
18
19
  servicesHoldLine,
19
- withoutLaunchPhase
20
- } from "./chunk-OHOMU7JP.js";
20
+ sessionWorkspaceLines,
21
+ withoutLaunchPhase,
22
+ workspaceLineList,
23
+ workspaceReadsOf,
24
+ worktreeJoinLine,
25
+ wrapWords
26
+ } from "./chunk-ASHAV3UV.js";
21
27
 
22
28
  // src/dashboard.tsx
23
29
  import { CliRenderEvents, createCliRenderer } from "@opentui/core";
@@ -271,6 +277,10 @@ var startingWordsOf = (session) => {
271
277
  return phase.words.endsWith("is saving") ? "waiting for the previous save" : "waiting for the previous session";
272
278
  case "booting":
273
279
  return "booting";
280
+ case "queued":
281
+ return "queued";
282
+ case "building":
283
+ return "building the image";
274
284
  case "preparing":
275
285
  return "preparing the workspace";
276
286
  }
@@ -283,6 +293,10 @@ var startingExplanationOf = (session) => {
283
293
  return phase.words.endsWith("is saving") ? "the previous session in this worktree is still saving \xB7 this one starts from its save" : phase.words;
284
294
  case "booting":
285
295
  return "the workspace is booting \xB7 the agent starts when it is up";
296
+ case "queued":
297
+ return "the launch waits for a worker \xB7 it starts when one takes it";
298
+ case "building":
299
+ return `${phase.words} \xB7 the agent starts once it is built and booted`;
286
300
  case "preparing":
287
301
  return "the workspace has no runtime yet \xB7 after an update, building its image takes about 8 minutes";
288
302
  }
@@ -533,6 +547,51 @@ var fitHints = (hints, width) => {
533
547
  return kept.length === 0 ? "\u2026" : `${kept.join(" \xB7 ")} \u2026`;
534
548
  };
535
549
  var clampIndex = (length, index) => length === 0 ? 0 : Math.max(0, Math.min(length - 1, index));
550
+ var VIEWER_KEY = ["viewer"];
551
+ var viewerNeeded = (data) => data !== void 0 && [...data.details.values()].some((detail) => hasPersonFacts(detail.sessions));
552
+ var fetchViewer = async (ctx) => {
553
+ const [view, members] = await Promise.all([
554
+ ctx.api("GET", "/organization").catch(() => null),
555
+ ctx.api("GET", "/organization/members").catch(() => [])
556
+ ]);
557
+ return { userId: view?.userId ?? null, members };
558
+ };
559
+ var WORKSPACE_FACTS_KEY = (sessionId, reads) => [
560
+ "workspace-facts",
561
+ sessionId,
562
+ reads.waiting,
563
+ reads.retirement
564
+ ];
565
+ var fetchWorkspaceFacts = async (api, sessionId, reads) => {
566
+ const [wait, retirement] = await Promise.all([
567
+ reads.waiting ? api("GET", `/sessions/${sessionId}/waiting`).catch(() => null) : null,
568
+ reads.retirement ? api(
569
+ "GET",
570
+ `/sessions/${sessionId}/workspace-retirement`
571
+ ).catch(() => null) : null
572
+ ]);
573
+ return { wait, retirement };
574
+ };
575
+ var workspaceFactRows = (session, viewer, facts, width) => workspaceLineList(
576
+ sessionWorkspaceLines({
577
+ sessionId: session.id,
578
+ livePeople: session.livePeople ?? [],
579
+ viewer: viewer?.userId ?? null,
580
+ wait: facts?.wait ?? null,
581
+ retirement: facts?.retirement ?? null,
582
+ members: viewer?.members ?? []
583
+ })
584
+ ).flatMap((line) => {
585
+ const indent = line.startsWith(" ") ? " " : "";
586
+ return wrapWords(line, width - indent.length).map((row) => `${indent}${row}`);
587
+ });
588
+ var worktreeJoinRows = (data, projectId, worktree, viewerId, width) => {
589
+ const detail = data?.details.get(projectId);
590
+ const id = "id" in worktree ? worktree.id : detail?.worktrees?.find((candidate) => candidate.name === worktree.name)?.id;
591
+ if (detail === void 0 || id === void 0) return [];
592
+ const line = worktreeJoinLine(detail.sessions, id, viewerId);
593
+ return line === null ? [] : wrapWords(line, width);
594
+ };
536
595
  var KEY_BINDINGS = [
537
596
  {
538
597
  verb: "moveUp",
@@ -2437,7 +2496,8 @@ var SessionFacts = ({
2437
2496
  group,
2438
2497
  item,
2439
2498
  rows,
2440
- tunnels
2499
+ tunnels,
2500
+ workspaceRows
2441
2501
  }) => {
2442
2502
  const { session, annotation, services } = item;
2443
2503
  const color = STATUS_COLOR[session.status] ?? MUTED;
@@ -2468,6 +2528,18 @@ var SessionFacts = ({
2468
2528
  /* @__PURE__ */ jsx3("span", { fg: MUTED, children: session.branch }),
2469
2529
  /* @__PURE__ */ jsx3("span", { fg: FAINT, children: session.baseSha === "" ? "" : ` vs ${session.baseRef ?? session.baseSha.slice(0, 12)}` })
2470
2530
  ] }, "status"),
2531
+ // Who else is live here, what a turn waits for, a workspace waiting to be replaced: right
2532
+ // under the status, before the facts a short pane may drop.
2533
+ ...workspaceRows.map((row, index) => /* @__PURE__ */ jsx3(
2534
+ "text",
2535
+ {
2536
+ height: 1,
2537
+ bg: "transparent",
2538
+ fg: row.startsWith(" ") ? MUTED : INK_2,
2539
+ children: ` ${row}`
2540
+ },
2541
+ `workspace-${index}`
2542
+ )),
2471
2543
  /* @__PURE__ */ jsx3("text", { height: 1, bg: "transparent", fg: FAINT, children: [
2472
2544
  ...timeAgo(session.createdAt) === "" ? [] : [`created ${timeAgo(session.createdAt)}`],
2473
2545
  isPendingId(session.id) ? "provisioning" : session.id.slice(0, 8),
@@ -2630,6 +2702,28 @@ var App = ({ ctx, onQuit }) => {
2630
2702
  refetchInterval: previewLive ? 8e3 : false,
2631
2703
  retry: 1
2632
2704
  });
2705
+ const viewer = useQuery2({
2706
+ queryKey: VIEWER_KEY,
2707
+ queryFn: () => fetchViewer(ctx),
2708
+ enabled: viewerNeeded(data),
2709
+ staleTime: Number.POSITIVE_INFINITY,
2710
+ retry: false
2711
+ });
2712
+ const workspaceReads = selectedSession === null ? { waiting: false, retirement: false } : workspaceReadsOf(selectedSession);
2713
+ const workspaceFacts = useQuery2({
2714
+ queryKey: WORKSPACE_FACTS_KEY(previewSessionId ?? "none", workspaceReads),
2715
+ queryFn: () => fetchWorkspaceFacts(ctx.api, previewSessionId ?? "", workspaceReads),
2716
+ enabled: previewSessionId !== null && previewLive && detailWidth > 0 && (workspaceReads.waiting || workspaceReads.retirement),
2717
+ staleTime: 3e3,
2718
+ refetchInterval: previewLive ? 8e3 : false,
2719
+ retry: 1
2720
+ });
2721
+ const workspaceRows = selectedSession === null || !previewLive || detailWidth === 0 ? [] : workspaceFactRows(
2722
+ selectedSession,
2723
+ viewer.data,
2724
+ workspaceFacts.data,
2725
+ Math.max(16, detailWidth - 4)
2726
+ );
2633
2727
  const preview = useMemo2(
2634
2728
  () => transcript.data === void 0 ? [] : previewLines(transcript.data, {
2635
2729
  width: Math.max(16, detailWidth - 6),
@@ -2638,7 +2732,10 @@ var App = ({ ctx, onQuit }) => {
2638
2732
  [transcript.data, detailWidth]
2639
2733
  );
2640
2734
  const [previewOffset, setPreviewOffset] = useState3(0);
2641
- const factRows = Math.max(0, Math.min(SESSION_FACT_ROWS, layout.detailRows - 2));
2735
+ const factRows = Math.max(
2736
+ 0,
2737
+ Math.min(SESSION_FACT_ROWS + workspaceRows.length, layout.detailRows - 2)
2738
+ );
2642
2739
  const showFactRule = factRows > 0 && layout.detailRows - factRows > 1;
2643
2740
  const waiting = selectedSession !== null && (selectedSession.status === "starting" || isPendingId(selectedSession.id));
2644
2741
  const snake = useSnake({
@@ -3606,6 +3703,22 @@ ${tunneled}
3606
3703
  return COLUMN_TITLE[column];
3607
3704
  };
3608
3705
  const pickerTitle = picker === null ? "" : picker.worktree === void 0 ? picker.session === null ? "new session \xB7 pick a harness" : `resume ${sessionDisplayName(picker.session)} \xB7 pick a harness` : `new session in ${picker.worktree.name} \xB7 pick a harness`;
3706
+ const modalWidth = Math.min(74, terminalCols - 4);
3707
+ const pickerJoinRows = picker?.worktree?.id == null || selectedProject === null ? [] : worktreeJoinRows(
3708
+ data,
3709
+ selectedProject.project.id,
3710
+ { id: picker.worktree.id },
3711
+ viewer.data?.userId ?? null,
3712
+ modalWidth - 4
3713
+ );
3714
+ const pickerJoinHeight = pickerJoinRows.length === 0 ? 0 : pickerJoinRows.length + 1;
3715
+ const creatingJoinRows = creating === null || !creating.joins ? [] : worktreeJoinRows(
3716
+ data,
3717
+ creating.projectId,
3718
+ { name: creating.name },
3719
+ viewer.data?.userId ?? null,
3720
+ modalWidth - 7
3721
+ );
3609
3722
  const creatingHeight = 2 + 1 + 1 + 6 + 1 + deriveHarnesses(null).length;
3610
3723
  const footerText = adoptOffer === null ? editing === null ? creating === null ? picker === null ? ` ${fitHints(verbHints(focus), Math.max(12, terminalCols - 2))}` : " \u2191\u2193 move \xB7 enter start \xB7 esc cancel" : creating.step === "name" ? " enter continue \xB7 esc cancel" : creating.step === "base" ? " type to filter \xB7 \u2191\u2193 move \xB7 enter choose base \xB7 esc back" : " \u2191\u2193 move \xB7 enter launch \xB7 esc back" : " enter save \xB7 esc cancel" : " enter adopt \xB7 \u2190\u2192 auth mode \xB7 esc not now";
3611
3724
  const loadFailure = data === void 0 && failureReason !== null ? errorText(failureReason) : null;
@@ -3778,7 +3891,8 @@ ${tunneled}
3778
3891
  group: selectedGroup,
3779
3892
  item: selectedItem,
3780
3893
  rows: factRows,
3781
- tunnels: openTunnels
3894
+ tunnels: openTunnels,
3895
+ workspaceRows
3782
3896
  }
3783
3897
  ),
3784
3898
  showFactRule ? /* @__PURE__ */ jsx3("text", { height: 1, bg: "transparent", fg: FAINT, children: ` ${"\u2500".repeat(Math.max(4, detailWidth - 4))}` }) : null,
@@ -3873,15 +3987,18 @@ ${tunneled}
3873
3987
  ]
3874
3988
  }
3875
3989
  ) : null,
3876
- picker === null ? null : /* @__PURE__ */ jsx3(
3990
+ picker === null ? null : /* @__PURE__ */ jsxs3(
3877
3991
  "box",
3878
3992
  {
3879
3993
  position: "absolute",
3880
3994
  zIndex: 12,
3881
3995
  left: Math.max(1, Math.floor((terminalCols - Math.min(74, terminalCols - 4)) / 2)),
3882
- top: Math.max(1, Math.floor((terminalRows - (2 + pickerItems.length)) / 2)),
3996
+ top: Math.max(
3997
+ 1,
3998
+ Math.floor((terminalRows - (2 + pickerItems.length + pickerJoinHeight)) / 2)
3999
+ ),
3883
4000
  width: Math.min(74, terminalCols - 4),
3884
- height: 2 + pickerItems.length,
4001
+ height: 2 + pickerItems.length + pickerJoinHeight,
3885
4002
  border: true,
3886
4003
  borderStyle: "rounded",
3887
4004
  borderColor: ACCENT,
@@ -3889,15 +4006,19 @@ ${tunneled}
3889
4006
  titleAlignment: "left",
3890
4007
  backgroundColor: SURFACE,
3891
4008
  flexDirection: "column",
3892
- children: pickerItems.map((item, index) => /* @__PURE__ */ jsx3(
3893
- HarnessRow,
3894
- {
3895
- item,
3896
- selected: index === pickerIndex,
3897
- background: SURFACE
3898
- },
3899
- String(item.harness)
3900
- ))
4009
+ children: [
4010
+ pickerJoinRows.map((row, index) => /* @__PURE__ */ jsx3("text", { height: 1, bg: SURFACE, fg: INK_2, children: ` ${row}` }, `join-${index}`)),
4011
+ pickerJoinRows.length === 0 ? null : /* @__PURE__ */ jsx3("text", { height: 1, bg: SURFACE, children: " " }),
4012
+ pickerItems.map((item, index) => /* @__PURE__ */ jsx3(
4013
+ HarnessRow,
4014
+ {
4015
+ item,
4016
+ selected: index === pickerIndex,
4017
+ background: SURFACE
4018
+ },
4019
+ String(item.harness)
4020
+ ))
4021
+ ]
3901
4022
  }
3902
4023
  ),
3903
4024
  editing === null ? null : /* @__PURE__ */ jsx3(
@@ -4053,6 +4174,10 @@ ${tunneled}
4053
4174
  const branch = creating.joins || creating.branches === null ? void 0 : filterBranches(creating.branches, creating.query)[index];
4054
4175
  const active = creating.step === "base";
4055
4176
  if (branch === void 0) {
4177
+ const joinRow = creatingJoinRows[index];
4178
+ if (joinRow !== void 0) {
4179
+ return /* @__PURE__ */ jsx3("text", { height: 1, bg: SURFACE, fg: INK_2, children: ` ${joinRow}` }, `slot-${index}`);
4180
+ }
4056
4181
  const notice = creating.joins ? null : baseStepNotice(creating);
4057
4182
  return /* @__PURE__ */ jsx3(
4058
4183
  "text",