@paradow/ledge 0.33.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/cli/{action-QW2ZQQBN.js → action-K42H5BDA.js} +5 -5
  2. package/dist/cli/{attach-ULTYWCJ7.js → attach-KEE4A5YP.js} +28 -15
  3. package/dist/cli/{bridge-CSLF7EW6.js → bridge-X327AZY4.js} +2 -2
  4. package/dist/cli/{chunk-3DB3VOMM.js → chunk-25RVT5MR.js} +3 -103
  5. package/dist/cli/{chunk-GXL3X7M7.js → chunk-3OJCWJAA.js} +2 -2
  6. package/dist/cli/chunk-4OYVM4MT.js +49 -0
  7. package/dist/cli/{chunk-JEGPTNWV.js → chunk-4TCSADFR.js} +5 -23
  8. package/dist/cli/{chunk-HOOHS3MX.js → chunk-53ISXX2R.js} +1 -1
  9. package/dist/cli/{chunk-6IYPRUQG.js → chunk-54KF5RBY.js} +1 -1
  10. package/dist/cli/{chunk-ASYAWVFW.js → chunk-7NHQGP6J.js} +1 -1
  11. package/dist/cli/{chunk-4HXJQEUN.js → chunk-G7XPEVCG.js} +5 -5
  12. package/dist/cli/{chunk-WXE2NOE5.js → chunk-IWY7DSSO.js} +835 -1033
  13. package/dist/cli/{chunk-CSLVBADC.js → chunk-KPDTYX7F.js} +3 -3
  14. package/dist/cli/chunk-RPH773TY.js +6 -0
  15. package/dist/cli/{chunk-CCSUP5ZI.js → chunk-RX5CJ7YH.js} +3 -5
  16. package/dist/cli/{chunk-NLL2EQJ2.js → chunk-T547FLQN.js} +38 -4
  17. package/dist/cli/{chunk-K7UEKSB2.js → chunk-UJ2WBGLW.js} +2 -2
  18. package/dist/cli/{chunk-XOZD4BP5.js → chunk-ULOMKEB6.js} +4 -4
  19. package/dist/cli/{chunk-CPERVAPQ.js → chunk-V4XKKL6K.js} +13 -40
  20. package/dist/cli/{chunk-PZ3F4QH3.js → daemon-RO43S3S5.js} +4655 -2964
  21. package/dist/cli/{decide-PFHHOCFZ.js → decide-KZFYL3HY.js} +5 -5
  22. package/dist/cli/{handover-EMO6JT7O.js → handover-BWNMI2RA.js} +5 -5
  23. package/dist/cli/{hook-W2BU74H5.js → hook-ZLWXI5EQ.js} +21 -71
  24. package/dist/cli/index.js +18 -25
  25. package/dist/cli/launch-GCX7TXOH.js +28 -0
  26. package/dist/cli/{send-NEI4X33R.js → send-SXZAPVRH.js} +5 -5
  27. package/dist/cli/{setup-K42IVMHS.js → setup-TJ32FMVJ.js} +4 -4
  28. package/dist/cli/{status-U2HGGKHF.js → status-YDVRES4E.js} +3 -3
  29. package/dist/cli/{task-PDDCE23D.js → task-TUSOVJK7.js} +5 -5
  30. package/dist/cli/{tui-UPMRMCPC.js → tui-TCVIRZCQ.js} +1401 -1269
  31. package/dist/cli/{uninstall-4IMMR55T.js → uninstall-QO7YMSLA.js} +4 -4
  32. package/dist/cli/{version-LUMK3Z3T.js → version-BAYSYWFP.js} +1 -1
  33. package/dist/cli/who-6AZYSP7T.js +12 -0
  34. package/dist/ledge-guide.md +26 -49
  35. package/package.json +2 -1
  36. package/dist/cli/chunk-4VNKZOC2.js +0 -160
  37. package/dist/cli/chunk-E3LXONBI.js +0 -6
  38. package/dist/cli/chunk-NWIICISV.js +0 -290
  39. package/dist/cli/daemon-5GT545BB.js +0 -2322
  40. package/dist/cli/launch-LP7YJR5N.js +0 -36
  41. package/dist/cli/team-S3ASP22H.js +0 -205
  42. package/dist/cli/who-NM3GPAPJ.js +0 -12
@@ -1,17 +1,14 @@
1
- import {
2
- attachEnv
3
- } from "./chunk-CPERVAPQ.js";
4
1
  import {
5
2
  imageInfo
6
- } from "./chunk-PZ3F4QH3.js";
3
+ } from "./chunk-4OYVM4MT.js";
4
+ import {
5
+ attachEnv
6
+ } from "./chunk-V4XKKL6K.js";
7
7
  import {
8
8
  MAX_FRAME
9
9
  } from "./chunk-OLLQBK6N.js";
10
- import "./chunk-NWIICISV.js";
11
- import "./chunk-3DB3VOMM.js";
12
- import "./chunk-4VNKZOC2.js";
13
- import "./chunk-JEGPTNWV.js";
14
- import "./chunk-ASYAWVFW.js";
10
+ import "./chunk-4TCSADFR.js";
11
+ import "./chunk-7NHQGP6J.js";
15
12
  import {
16
13
  ACTION_ERROR_CODES,
17
14
  ATTACHMENTS_MAX,
@@ -22,6 +19,7 @@ import {
22
19
  ChatTurnSchema,
23
20
  ContextPrefsSchema,
24
21
  IslandPrefsSchema,
22
+ KERNEL_RIG,
25
23
  LEDGE_GIVER,
26
24
  MachinesSchema,
27
25
  NOTE_MAX,
@@ -30,7 +28,7 @@ import {
30
28
  PROJECT_COLORS,
31
29
  PROTOCOL_PORT,
32
30
  ProjectsSchema,
33
- ROLE_PROMPT_MAX,
31
+ RIG_POLICIES,
34
32
  backendOf,
35
33
  contextNear,
36
34
  isArchived,
@@ -42,15 +40,18 @@ import {
42
40
  projectOf,
43
41
  proposalRows,
44
42
  readDaemonToken,
43
+ rigCommands,
44
+ seatPlan,
45
+ seatsByRole,
46
+ showRigCommand,
45
47
  taskClosed,
46
48
  transmissions
47
- } from "./chunk-WXE2NOE5.js";
48
- import "./chunk-E3LXONBI.js";
49
- import "./chunk-NLL2EQJ2.js";
50
- import "./chunk-6IYPRUQG.js";
49
+ } from "./chunk-IWY7DSSO.js";
50
+ import "./chunk-T547FLQN.js";
51
+ import "./chunk-54KF5RBY.js";
51
52
  import {
52
53
  ledgeDir
53
- } from "./chunk-HOOHS3MX.js";
54
+ } from "./chunk-53ISXX2R.js";
54
55
  import {
55
56
  __commonJS,
56
57
  __export,
@@ -346,7 +347,7 @@ var daemonStore = {
346
347
  return;
347
348
  }
348
349
  if (m.type === "chat.step" || m.type === "chat.delta" || m.type === "chat.done" || m.type === "chat.error") {
349
- const chat = state.chat.map((t) => t.qid !== m.qid ? t : m.type === "chat.step" ? { ...t, steps: [...t.steps, m.step] } : m.type === "chat.done" ? { ...t, answer: m.text, answeredAt: m.at, ...m.proposal ? { proposal: m.proposal } : {}, ...m.proposals ? { proposals: m.proposals } : {}, ...m.relay ? { relay: m.relay } : {}, ...m.closes ? { closes: m.closes } : {}, ...m.projectProposal ? { projectProposal: m.projectProposal } : {}, ...m.team ? { team: m.team } : {}, ...m.tasks ? { tasks: m.tasks } : {}, ...m.actions ? { actions: m.actions } : {} } : m.type === "chat.error" ? { ...t, error: m.message } : t);
350
+ const chat = state.chat.map((t) => t.qid !== m.qid ? t : m.type === "chat.step" ? { ...t, steps: [...t.steps, m.step] } : m.type === "chat.done" ? { ...t, answer: m.text, answeredAt: m.at, ...m.proposal ? { proposal: m.proposal } : {}, ...m.proposals ? { proposals: m.proposals } : {}, ...m.relay ? { relay: m.relay } : {}, ...m.closes ? { closes: m.closes } : {}, ...m.projectProposal ? { projectProposal: m.projectProposal } : {}, ...m.tasks ? { tasks: m.tasks } : {}, ...m.actions ? { actions: m.actions } : {} } : m.type === "chat.error" ? { ...t, error: m.message } : t);
350
351
  const chatLive = m.type === "chat.delta" ? { qid: m.qid, text: (state.chatLive?.qid === m.qid ? state.chatLive.text : "") + m.text } : m.type === "chat.step" ? state.chatLive : null;
351
352
  state = { ...state, chat, chatLive };
352
353
  emit();
@@ -542,9 +543,6 @@ function useMachines(client) {
542
543
  var nullSnapshot = () => null;
543
544
  var noTasks = [];
544
545
  var emptyTasks = () => noTasks;
545
- function useTeams(client) {
546
- return useSyncExternalStore3(client.fleet?.subscribe ?? noopSubscribe, client.fleet?.teams ?? nullSnapshot, nullSnapshot);
547
- }
548
546
  var defaultNear = () => CONTEXT_NEAR;
549
547
  function useContextNear(client) {
550
548
  return useSyncExternalStore3(client.fleet?.subscribe ?? noopSubscribe, () => client.fleet?.contextNear() ?? CONTEXT_NEAR, defaultNear);
@@ -591,7 +589,14 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
591
589
  const scans = /* @__PURE__ */ new Map();
592
590
  const attachmentAsks = /* @__PURE__ */ new Map();
593
591
  const modelLists = /* @__PURE__ */ new Map();
594
- const roleLists = /* @__PURE__ */ new Map();
592
+ const rigFailure = (kind, requestId, machineId, error) => kind === "act" ? { type: "openrig.done", requestId, machineId, ok: false, commands: [], error } : kind === "specs" ? { type: "openrig.specList", requestId, machineId, specs: [], error } : kind === "launch" ? { type: "openrig.launched", requestId, machineId, ok: false, plan: false, commands: [], error } : kind === "seats" ? { type: "openrig.reseated", requestId, machineId, ok: false, commands: [], added: [], removed: [], error } : { type: "openrig.state", requestId, machineId, error };
593
+ const rigAsks = /* @__PURE__ */ new Map();
594
+ const answerFailedRigs = (machine, error) => {
595
+ for (const [requestId, a] of rigAsks) if (a.machine === machine) {
596
+ rigAsks.delete(requestId);
597
+ emit2(rigFailure(a.kind, requestId, machine, error));
598
+ }
599
+ };
595
600
  const operations = /* @__PURE__ */ new Map();
596
601
  const closeRows = /* @__PURE__ */ new Map();
597
602
  const endOperation = (machine) => {
@@ -625,25 +630,17 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
625
630
  emit2({ type: "models.listing", requestId, tool: l.tool, models: [], error });
626
631
  }
627
632
  };
628
- const answerFailedRoles = (machine, error) => {
629
- for (const [requestId, owner] of roleLists) if (owner === machine) {
630
- roleLists.delete(requestId);
631
- emit2({ type: "roles.listing", requestId, roles: [], templates: [], problems: [], error });
632
- }
633
- };
634
633
  let source2 = "local";
635
634
  let projects = null;
636
635
  let island;
637
636
  let contextNear2;
638
637
  let mobile;
639
638
  let mobileError;
640
- let teams = null;
641
639
  let tasks = /* @__PURE__ */ new Map();
642
640
  let taskList = [];
643
641
  const taskLists = /* @__PURE__ */ new Set();
644
642
  let listSeq = 0;
645
643
  const local = (id) => id === LEDGE_GIVER ? id : qualify("local", id);
646
- const teamOut = (t) => ({ ...t, seats: t.seats.map((s) => ({ ...s, sessionId: s.sessionId === null ? null : qualify("local", s.sessionId) })) });
647
644
  const taskOut = (t) => ({
648
645
  ...t,
649
646
  giver: local(t.giver),
@@ -655,14 +652,15 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
655
652
  taskList = [...tasks.values()].sort((a, b2) => a.at.localeCompare(b2.at));
656
653
  listeners2.forEach((fn) => fn());
657
654
  };
655
+ const knowsTasks = (version) => {
656
+ const [major = 0, minor = 0] = (version ?? "").split(".").map(Number);
657
+ return major > 0 || minor >= 17;
658
+ };
658
659
  const askTasks = (p) => {
659
- const ask2 = (extra) => {
660
- const rid = `fleet-tasks-${++listSeq}`;
661
- taskLists.add(rid);
662
- p.client.send({ type: "task.cmd", rid, by: LEDGE_GIVER, op: "list", all: true, ...extra });
663
- };
664
- ask2({});
665
- for (const t of teams ?? []) if (t.state !== "dissolved") ask2({ team: t.id });
660
+ if (!knowsTasks(p.snapshot?.daemon?.version)) return;
661
+ const rid = `fleet-tasks-${++listSeq}`;
662
+ taskLists.add(rid);
663
+ p.client.send({ type: "task.cmd", rid, by: LEDGE_GIVER, op: "list", all: true });
666
664
  };
667
665
  let routing = null, resetAt = "", lastStatus = "connecting";
668
666
  const emit2 = (m) => messages.forEach((fn) => fn(m));
@@ -715,10 +713,10 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
715
713
  const p = peers.get(c.id);
716
714
  return { ...c, status: p.status, ready: available(p), recents: p.snapshot?.recents ?? [], sessions: stateSessions(p), launcher: p.snapshot?.launcher ?? null, version: p.snapshot?.daemon?.version, daemon: p.snapshot?.daemon, error: p.error };
717
715
  });
718
- const next2 = status();
719
- if (next2 !== lastStatus) {
720
- lastStatus = next2;
721
- statuses.forEach((fn) => fn(next2));
716
+ const next3 = status();
717
+ if (next3 !== lastStatus) {
718
+ lastStatus = next3;
719
+ statuses.forEach((fn) => fn(next3));
722
720
  }
723
721
  listeners2.forEach((fn) => fn());
724
722
  }
@@ -731,10 +729,10 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
731
729
  const primary = peers.get("local")?.snapshot;
732
730
  emit2({ type: "snapshot", sessions: [...peers.values()].filter((p) => available(p) || stale(p)).flatMap((p) => p.snapshot.sessions.map((s) => session(p, s))), recents: available(peers.get(selected)) ? peers.get(selected).snapshot.recents : [], launcher: primary?.launcher, daemon: primary?.daemon });
733
731
  for (const p of peers.values()) if (available(p) || stale(p)) for (const [id, lines] of p.previews) emit2({ type: "session.preview", id: qualify(p.config.id, id), lines });
734
- const next2 = status();
735
- if (next2 !== lastStatus) {
736
- lastStatus = next2;
737
- statuses.forEach((fn) => fn(next2));
732
+ const next3 = status();
733
+ if (next3 !== lastStatus) {
734
+ lastStatus = next3;
735
+ statuses.forEach((fn) => fn(next3));
738
736
  }
739
737
  listeners2.forEach((fn) => fn());
740
738
  }
@@ -765,14 +763,14 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
765
763
  ...raw.actions ? { actions: raw.actions.map((a) => ({ ...a, ...a.session ? { session: qualify(p.config.id, a.session) } : {} })) } : {}
766
764
  };
767
765
  }
768
- function apply(next2) {
766
+ function apply(next3) {
769
767
  configuring = true;
770
- for (const [id, p] of peers) if (!next2.some((c) => !c.disabled && c.id === id && c.url === p.config.url && c.ssh === p.config.ssh)) {
768
+ for (const [id, p] of peers) if (!next3.some((c) => !c.disabled && c.id === id && c.url === p.config.url && c.ssh === p.config.ssh)) {
771
769
  p.client.close();
772
770
  p.off.forEach((off) => off());
773
771
  peers.delete(id);
774
772
  }
775
- configs = next2;
773
+ configs = next3;
776
774
  for (const c of active()) {
777
775
  const p = peers.get(c.id);
778
776
  if (p) p.config = c;
@@ -789,15 +787,15 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
789
787
  const machine = p.config.id;
790
788
  if (m.type === "machines") {
791
789
  if (machine !== "local" || !opts.machinesFromDaemon) return;
792
- let next2;
790
+ let next3;
793
791
  try {
794
- next2 = parseMachines(m.machines);
792
+ next3 = parseMachines(m.machines);
795
793
  } catch {
796
794
  return;
797
795
  }
798
- const mine = next2.length === 1 ? loadMachines(prefix) : [];
796
+ const mine = next3.length === 1 ? loadMachines(prefix) : [];
799
797
  source2 = "daemon";
800
- if (next2.length === 1 && mine.length > 1) {
798
+ if (next3.length === 1 && mine.length > 1) {
801
799
  p.client.send({ type: "machines.set", machines: toWire(mine) });
802
800
  return;
803
801
  }
@@ -805,7 +803,7 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
805
803
  localStorage.removeItem(MACHINES_KEY);
806
804
  } catch {
807
805
  }
808
- apply(next2);
806
+ apply(next3);
809
807
  return;
810
808
  }
811
809
  if (m.type === "projects") {
@@ -826,19 +824,6 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
826
824
  listeners2.forEach((fn) => fn());
827
825
  return;
828
826
  }
829
- if (m.type === "teams" || m.type === "team.updated") {
830
- if (machine !== "local") return;
831
- const known2 = new Set((teams ?? []).map((t) => t.id));
832
- teams = m.type === "teams" ? m.teams.map(teamOut) : [...(teams ?? []).filter((t) => t.id !== m.team.id), teamOut(m.team)].sort((a, b2) => a.createdAt.localeCompare(b2.createdAt));
833
- listeners2.forEach((fn) => fn());
834
- if (m.type === "team.updated" && !known2.has(m.team.id) && m.team.state !== "dissolved") {
835
- const rid = `fleet-tasks-${++listSeq}`;
836
- taskLists.add(rid);
837
- p.client.send({ type: "task.cmd", rid, by: LEDGE_GIVER, op: "list", all: true, team: m.team.id });
838
- }
839
- if (m.type === "teams" && p.snapshot?.daemon?.teams) askTasks(p);
840
- return;
841
- }
842
827
  if (m.type === "task.updated") {
843
828
  if (machine === "local") putTasks([m.task]);
844
829
  return;
@@ -871,6 +856,7 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
871
856
  p.restarting = false;
872
857
  p.previews.clear();
873
858
  publish();
859
+ if (machine === "local") askTasks(p);
874
860
  return;
875
861
  }
876
862
  if (m.type === "session.updated") {
@@ -960,16 +946,17 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
960
946
  }
961
947
  return;
962
948
  }
963
- if (m.type === "models.listing") {
964
- if (modelLists.get(m.requestId)?.machine === machine) {
965
- modelLists.delete(m.requestId);
966
- emit2(m);
949
+ if (m.type === "openrig.state" || m.type === "openrig.done" || m.type === "openrig.specList" || m.type === "openrig.launched" || m.type === "openrig.reseated") {
950
+ if (m.requestId !== void 0) {
951
+ if (rigAsks.get(m.requestId)?.machine !== machine) return;
952
+ rigAsks.delete(m.requestId);
967
953
  }
954
+ emit2({ ...m, machineId: machine });
968
955
  return;
969
956
  }
970
- if (m.type === "roles.listing" && m.requestId !== void 0 && roleLists.has(m.requestId)) {
971
- if (roleLists.get(m.requestId) === machine) {
972
- roleLists.delete(m.requestId);
957
+ if (m.type === "models.listing") {
958
+ if (modelLists.get(m.requestId)?.machine === machine) {
959
+ modelLists.delete(m.requestId);
973
960
  emit2(m);
974
961
  }
975
962
  return;
@@ -1016,15 +1003,15 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
1016
1003
  const qid = globalQid(machine, m.qid), old = turns2.get(qid);
1017
1004
  if (!old) return;
1018
1005
  if (m.type === "chat.done") {
1019
- const raw = { ...old, qid: m.qid, answer: m.text, answeredAt: m.at, proposal: m.proposal, proposals: m.proposals, relay: m.relay, closes: m.closes, projectProposal: m.projectProposal, team: m.team, tasks: m.tasks, actions: m.actions };
1006
+ const raw = { ...old, qid: m.qid, answer: m.text, answeredAt: m.at, proposal: m.proposal, proposals: m.proposals, relay: m.relay, closes: m.closes, projectProposal: m.projectProposal, tasks: m.tasks, actions: m.actions };
1020
1007
  const t = mappedTurn(p, { ...raw, steps: [] });
1021
1008
  turns2.set(qid, { ...t, steps: old.steps });
1022
1009
  history();
1023
1010
  } else if (m.type === "chat.error") fail(qid, m.message);
1024
1011
  else if (m.type === "chat.step") {
1025
- const step2 = { ...m.step, ...m.step.id ? { id: qualify(machine, m.step.id) } : {} };
1026
- turns2.set(qid, { ...old, steps: [...old.steps, step2] });
1027
- emit2({ ...m, qid, step: step2 });
1012
+ const step3 = { ...m.step, ...m.step.id ? { id: qualify(machine, m.step.id) } : {} };
1013
+ turns2.set(qid, { ...old, steps: [...old.steps, step3] });
1014
+ emit2({ ...m, qid, step: step3 });
1028
1015
  } else emit2({ ...m, qid });
1029
1016
  return;
1030
1017
  }
@@ -1054,8 +1041,8 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
1054
1041
  answerFailedModels(machine, "outdated");
1055
1042
  return;
1056
1043
  }
1057
- if (m.request === "roles.list" && [...roleLists.values()].includes(machine)) {
1058
- answerFailedRoles(machine, "outdated");
1044
+ if (m.request?.startsWith("openrig.")) {
1045
+ answerFailedRigs(machine, "outdated");
1059
1046
  return;
1060
1047
  }
1061
1048
  if ((m.request === "folder.list" || m.request === "folder.create") && [...listings.values()].some((l) => l.machine === machine)) {
@@ -1091,7 +1078,7 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
1091
1078
  answerUnfoundListings(c.id, "offline");
1092
1079
  answerFailedScans(c.id, "offline");
1093
1080
  answerFailedModels(c.id, "offline");
1094
- answerFailedRoles(c.id, "offline");
1081
+ answerFailedRigs(c.id, "offline");
1095
1082
  for (const [requestId, owner] of documents) if (owner === c.id) {
1096
1083
  documents.delete(requestId);
1097
1084
  emit2({ type: "document.error", requestId, path: "", code: "read_failed" });
@@ -1266,6 +1253,17 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
1266
1253
  }
1267
1254
  modelLists.set(msg.requestId, { machine, tool: msg.tool });
1268
1255
  }
1256
+ if (msg.type === "openrig.status" || msg.type === "openrig.act" || msg.type === "openrig.specs" || msg.type === "openrig.launch" || msg.type === "openrig.seats") {
1257
+ machine = msg.machineId ?? "local";
1258
+ const { machineId: _m, ...rest } = msg;
1259
+ outgoing = rest;
1260
+ const kind = msg.type === "openrig.status" ? "status" : msg.type === "openrig.act" ? "act" : msg.type === "openrig.specs" ? "specs" : msg.type === "openrig.seats" ? "seats" : "launch";
1261
+ if (!available(peers.get(machine))) {
1262
+ emit2(rigFailure(kind, msg.requestId, machine, "offline"));
1263
+ return;
1264
+ }
1265
+ rigAsks.set(msg.requestId, { machine, kind });
1266
+ }
1269
1267
  if (msg.type === "folder.list") {
1270
1268
  if (msg.machineId) machine = msg.machineId;
1271
1269
  else if (msg.requestId !== void 0) machine = "local";
@@ -1293,30 +1291,10 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
1293
1291
  }
1294
1292
  }
1295
1293
  if (msg.type === "update.daemon") machine = "local";
1296
- if (msg.type === "roles.list" && msg.requestId !== void 0) {
1297
- machine = msg.machineId ?? "local";
1298
- if (!available(peers.get(machine))) {
1299
- emit2({ type: "roles.listing", requestId: msg.requestId, roles: [], templates: [], problems: [], error: "offline" });
1300
- return;
1301
- }
1302
- roleLists.set(msg.requestId, machine);
1303
- }
1304
- if (msg.type === "roles.list" || msg.type === "team.launch") {
1305
- if (!("qid" in msg && msg.qid)) machine = msg.machineId ?? "local";
1306
- const { machineId: _m, ...rest } = outgoing;
1307
- outgoing = rest;
1308
- }
1309
- if (msg.type === "team.join" || msg.type === "team.leave" || msg.type === "team.resume" || msg.type === "team.dissolve" || msg.type === "team.save" || msg.type === "task.cmd" && !msg.qid || msg.type === "action.run") machine = "local";
1310
- if (outgoing.type === "team.join" || outgoing.type === "team.leave") {
1311
- const o = splitId(outgoing.session);
1312
- if (o) {
1313
- machine = o.machine;
1314
- outgoing = { ...outgoing, session: o.id };
1315
- }
1316
- }
1294
+ if (msg.type === "task.cmd" && !msg.qid || msg.type === "action.run") machine = "local";
1317
1295
  if (outgoing.type === "task.cmd") {
1318
1296
  const o = outgoing.to ? splitId(outgoing.to) : null;
1319
- if (o && !outgoing.to.startsWith("@")) outgoing = { ...outgoing, to: o.id };
1297
+ if (o) outgoing = { ...outgoing, to: o.id };
1320
1298
  const b2 = splitId(outgoing.by);
1321
1299
  if (b2 && outgoing.by !== LEDGE_GIVER) outgoing = { ...outgoing, by: b2.id };
1322
1300
  }
@@ -1362,15 +1340,15 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
1362
1340
  publish();
1363
1341
  },
1364
1342
  configure(raw) {
1365
- const next2 = parseMachines(raw);
1343
+ const next3 = parseMachines(raw);
1366
1344
  if (source2 === "daemon") {
1367
1345
  const local2 = peers.get("local").client;
1368
1346
  if (local2.status() !== "open") throw Object.assign(new Error("The local daemon is not connected"), { name: "DaemonOffline" });
1369
- local2.send({ type: "machines.set", machines: toWire(next2) });
1347
+ local2.send({ type: "machines.set", machines: toWire(next3) });
1370
1348
  return;
1371
1349
  }
1372
- localStorage.setItem(MACHINES_KEY, JSON.stringify(next2));
1373
- apply(next2);
1350
+ localStorage.setItem(MACHINES_KEY, JSON.stringify(next3));
1351
+ apply(next3);
1374
1352
  },
1375
1353
  configured: () => configs,
1376
1354
  machinesSource: () => source2,
@@ -1414,16 +1392,16 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
1414
1392
  };
1415
1393
  },
1416
1394
  projects: () => projects,
1417
- setProjects(next2) {
1418
- const valid = ProjectsSchema.parse(next2);
1395
+ setProjects(next3) {
1396
+ const valid = ProjectsSchema.parse(next3);
1419
1397
  const local2 = peers.get("local").client;
1420
1398
  if (local2.status() !== "open") throw Object.assign(new Error("The local daemon is not connected"), { name: "DaemonOffline" });
1421
1399
  if (projects === null) throw Object.assign(new Error("Update the local Ledge daemon: it does not know projects"), { name: "DaemonOutdated" });
1422
1400
  local2.send({ type: "projects.set", projects: valid });
1423
1401
  },
1424
1402
  island: () => island,
1425
- setIsland(next2) {
1426
- const valid = IslandPrefsSchema.parse(next2);
1403
+ setIsland(next3) {
1404
+ const valid = IslandPrefsSchema.parse(next3);
1427
1405
  const local2 = peers.get("local").client;
1428
1406
  if (local2.status() !== "open") throw Object.assign(new Error("The local daemon is not connected"), { name: "DaemonOffline" });
1429
1407
  if (island === void 0) throw Object.assign(new Error("Update the local Ledge daemon: it does not know the island settings"), { name: "DaemonOutdated" });
@@ -1431,7 +1409,6 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
1431
1409
  },
1432
1410
  mobile: () => mobile,
1433
1411
  mobileError: () => mobileError,
1434
- teams: () => teams,
1435
1412
  tasks: () => taskList,
1436
1413
  contextNear: () => contextNear2,
1437
1414
  setContextNear(near) {
@@ -1514,8 +1491,8 @@ function getImage(client, attachment, route = {}, preview = false, timeoutMs = T
1514
1491
  }
1515
1492
 
1516
1493
  // src/tui/App.tsx
1517
- import { useCallback, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef13, useState as useState14 } from "react";
1518
- import { useApp, useInput as useInput7, useStdout, useWindowSize as useWindowSize2 } from "ink";
1494
+ import { useCallback as useCallback2, useEffect as useEffect15, useMemo as useMemo7, useRef as useRef14, useState as useState16 } from "react";
1495
+ import { useApp, useInput as useInput8, useStdout, useWindowSize as useWindowSize2 } from "ink";
1519
1496
 
1520
1497
  // src/tui/actions.ts
1521
1498
  import { useEffect as useEffect3, useMemo, useRef as useRef2 } from "react";
@@ -1876,7 +1853,7 @@ async function openImage(file, env = process.env, spawnImpl = spawn2, platform =
1876
1853
  // src/tui/context.tsx
1877
1854
  import { createContext, useContext, useEffect as useEffect4, useRef as useRef3 } from "react";
1878
1855
  import { useWindowSize } from "ink";
1879
- var SCREENS = ["overview", "session", "ask", "launch", "settings", "teams"];
1856
+ var SCREENS = ["overview", "session", "ask", "launch", "settings", "openrig"];
1880
1857
  var TuiContext = createContext(null);
1881
1858
  var useTui = () => {
1882
1859
  const tui = useContext(TuiContext);
@@ -1923,7 +1900,7 @@ function formatBytes(n, lang) {
1923
1900
  var b = (name) => `**${name}**`;
1924
1901
  var short = (text5) => text5.length <= 100 ? text5 : `${text5.slice(0, 99)}\u2026`;
1925
1902
  var need = (v, keys, say) => keys.every((k) => v[k]) ? say(v) : void 0;
1926
- var about = (action, v) => action?.startsWith("team.") && v.team ? "team" : action?.startsWith("task.") && v.task ? "task" : v.session ? "session" : v.team ? "team" : v.task ? "task" : null;
1903
+ var about = (action, v) => action?.startsWith("task.") && v.task ? "task" : v.session ? "session" : v.task ? "task" : null;
1927
1904
  var fr = {
1928
1905
  line: {
1929
1906
  "session.model": (v) => need(v, ["session"], (w) => `Passer ${b(w.session)} sur ${v.model ? `${v.model}${v.effort ? `, effort ${v.effort}` : ""}` : v.effort ? `son mod\xE8le par d\xE9faut, effort ${v.effort}` : "son mod\xE8le et son effort par d\xE9faut"}`),
@@ -1933,26 +1910,21 @@ var fr = {
1933
1910
  "session.resume": (v) => need(v, ["session"], (w) => `Reprendre ${b(w.session)} sur sa conversation`),
1934
1911
  "session.close": (v) => need(v, ["session"], (w) => `Fermer ${b(w.session)}`),
1935
1912
  "session.decide": (v) => need(v, ["session", "decision", "kind"], (w) => `${w.decision === "deny" ? "Refuser" : "Accepter"} la demande de ${w.kind} de ${b(w.session)}${v.note ? ", avec une note" : ""}`),
1936
- "team.join": (v) => need(v, ["session", "team", "role"], (w) => `Faire entrer ${b(w.session)} dans ${b(w.team)} comme @${w.role}`),
1937
- "team.leave": (v) => need(v, ["session", "team"], (w) => `Retirer ${b(w.session)} de ${b(w.team)}`),
1938
- "team.resume": (v) => need(v, ["team"], (w) => `Reprendre l\u2019\xE9quipe ${b(w.team)}`),
1939
- "team.dissolve": (v) => need(v, ["team"], (w) => `Dissoudre l\u2019\xE9quipe ${b(w.team)}`),
1940
- "team.save": (v) => need(v, ["team", "name"], (w) => v.replaces ? `Remplacer le mod\xE8le \xAB ${w.name} \xBB par ${b(w.team)}` : `Enregistrer ${b(w.team)} comme mod\xE8le \xAB ${w.name} \xBB`),
1941
1913
  "task.cancel": (v) => need(v, ["task", "owner"], (w) => `Annuler la t\xE2che \xAB ${short(w.task)} \xBB de ${b(w.owner)}`),
1942
1914
  "task.pass": (v) => need(v, ["task", "owner", "session"], (w) => `Passer la t\xE2che \xAB ${short(w.task)} \xBB de ${b(w.owner)} \xE0 ${b(w.session)}`),
1943
1915
  "task.answer": (v) => need(v, ["task", "session"], (w) => `R\xE9pondre \xE0 la d\xE9cision \xAB ${short(w.task)} \xBB de ${b(w.session)}`)
1944
1916
  },
1945
1917
  why: (code3, action, v) => {
1946
- const s = v.session ? b(v.session) : void 0, t = v.team ? b(v.team) : void 0, k = v.task ? `\xAB ${short(v.task)} \xBB` : void 0;
1918
+ const s = v.session ? b(v.session) : void 0, k = v.task ? `\xAB ${short(v.task)} \xBB` : void 0;
1947
1919
  const gone = "cette action n\u2019est plus possible.";
1948
1920
  switch (code3) {
1949
1921
  case "unknown": {
1950
1922
  const on = about(action, v);
1951
- return on === "team" ? `Ledge ne trouve plus l\u2019\xE9quipe ${t}.` : on === "task" ? `Ledge ne trouve plus la t\xE2che ${k}.` : on === "session" ? `Ledge ne trouve plus ${s}.` : gone;
1923
+ return on === "task" ? `Ledge ne trouve plus la t\xE2che ${k}.` : on === "session" ? `Ledge ne trouve plus ${s}.` : gone;
1952
1924
  }
1953
1925
  case "ambiguous": {
1954
1926
  const on = about(action, v);
1955
- return on === "team" ? `plusieurs \xE9quipes s\u2019appellent ${t} : redemande \xE0 Ledge en pr\xE9cisant laquelle.` : on === "session" ? `plusieurs sessions s\u2019appellent ${s} : redemande \xE0 Ledge en pr\xE9cisant laquelle.` : gone;
1927
+ return on === "session" ? `plusieurs sessions s\u2019appellent ${s} : redemande \xE0 Ledge en pr\xE9cisant laquelle.` : gone;
1956
1928
  }
1957
1929
  case "read_only":
1958
1930
  return s ? `${s} est en lecture seule : Ledge ne peut pas lui \xE9crire.` : gone;
@@ -1974,18 +1946,6 @@ var fr = {
1974
1946
  return s ? `les demandes de ${s} se r\xE8glent dans sa session.` : gone;
1975
1947
  case "no_note":
1976
1948
  return s ? `${s} ne peut pas recevoir de note avec cette r\xE9ponse.` : gone;
1977
- case "member":
1978
- return s && t ? `${s} est d\xE9j\xE0 dans l\u2019\xE9quipe ${t}.` : gone;
1979
- case "not_member":
1980
- return s && t ? `${s} n\u2019est pas dans l\u2019\xE9quipe ${t}.` : gone;
1981
- case "unknown_role":
1982
- return v.role ? `le r\xF4le @${v.role} n\u2019existe pas pour ce projet.` : gone;
1983
- case "not_resting":
1984
- return t ? `l\u2019\xE9quipe ${t} n\u2019est pas au repos : rien \xE0 reprendre.` : gone;
1985
- case "dissolved":
1986
- return t ? `l\u2019\xE9quipe ${t} a \xE9t\xE9 dissoute entre-temps.` : gone;
1987
- case "no_seat":
1988
- return t ? `l\u2019\xE9quipe ${t} n\u2019a aucun poste : rien \xE0 enregistrer comme mod\xE8le.` : gone;
1989
1949
  case "closed":
1990
1950
  return k ? `la t\xE2che ${k} est d\xE9j\xE0 close.` : gone;
1991
1951
  case "holder":
@@ -2012,26 +1972,21 @@ var en = {
2012
1972
  "session.resume": (v) => need(v, ["session"], (w) => `Resume ${b(w.session)} on its conversation`),
2013
1973
  "session.close": (v) => need(v, ["session"], (w) => `Close ${b(w.session)}`),
2014
1974
  "session.decide": (v) => need(v, ["session", "decision", "kind"], (w) => `${w.decision === "deny" ? "Deny" : "Accept"} ${b(w.session)}\u2019s ${w.kind} request${v.note ? ", with a note" : ""}`),
2015
- "team.join": (v) => need(v, ["session", "team", "role"], (w) => `Add ${b(w.session)} to ${b(w.team)} as @${w.role}`),
2016
- "team.leave": (v) => need(v, ["session", "team"], (w) => `Remove ${b(w.session)} from ${b(w.team)}`),
2017
- "team.resume": (v) => need(v, ["team"], (w) => `Resume the team ${b(w.team)}`),
2018
- "team.dissolve": (v) => need(v, ["team"], (w) => `Dissolve the team ${b(w.team)}`),
2019
- "team.save": (v) => need(v, ["team", "name"], (w) => v.replaces ? `Replace the template \u201C${w.name}\u201D with ${b(w.team)}` : `Save ${b(w.team)} as the template \u201C${w.name}\u201D`),
2020
1975
  "task.cancel": (v) => need(v, ["task", "owner"], (w) => `Cancel ${b(w.owner)}\u2019s task \u201C${short(w.task)}\u201D`),
2021
1976
  "task.pass": (v) => need(v, ["task", "owner", "session"], (w) => `Pass ${b(w.owner)}\u2019s task \u201C${short(w.task)}\u201D to ${b(w.session)}`),
2022
1977
  "task.answer": (v) => need(v, ["task", "session"], (w) => `Answer ${b(w.session)}\u2019s decision \u201C${short(w.task)}\u201D`)
2023
1978
  },
2024
1979
  why: (code3, action, v) => {
2025
- const s = v.session ? b(v.session) : void 0, t = v.team ? b(v.team) : void 0, k = v.task ? `\u201C${short(v.task)}\u201D` : void 0;
1980
+ const s = v.session ? b(v.session) : void 0, k = v.task ? `\u201C${short(v.task)}\u201D` : void 0;
2026
1981
  const gone = "this action is no longer possible.";
2027
1982
  switch (code3) {
2028
1983
  case "unknown": {
2029
1984
  const on = about(action, v);
2030
- return on === "team" ? `Ledge can no longer find the team ${t}.` : on === "task" ? `Ledge can no longer find the task ${k}.` : on === "session" ? `Ledge can no longer find ${s}.` : gone;
1985
+ return on === "task" ? `Ledge can no longer find the task ${k}.` : on === "session" ? `Ledge can no longer find ${s}.` : gone;
2031
1986
  }
2032
1987
  case "ambiguous": {
2033
1988
  const on = about(action, v);
2034
- return on === "team" ? `several teams are called ${t}: ask Ledge again and say which one.` : on === "session" ? `several sessions are called ${s}: ask Ledge again and say which one.` : gone;
1989
+ return on === "session" ? `several sessions are called ${s}: ask Ledge again and say which one.` : gone;
2035
1990
  }
2036
1991
  case "read_only":
2037
1992
  return s ? `${s} is read-only: Ledge cannot write to it.` : gone;
@@ -2053,18 +2008,6 @@ var en = {
2053
2008
  return s ? `${s}\u2019s requests are answered in its session.` : gone;
2054
2009
  case "no_note":
2055
2010
  return s ? `${s} cannot take a note with this answer.` : gone;
2056
- case "member":
2057
- return s && t ? `${s} is already in the team ${t}.` : gone;
2058
- case "not_member":
2059
- return s && t ? `${s} is not in the team ${t}.` : gone;
2060
- case "unknown_role":
2061
- return v.role ? `there is no @${v.role} role for this project.` : gone;
2062
- case "not_resting":
2063
- return t ? `the team ${t} is not resting: nothing to resume.` : gone;
2064
- case "dissolved":
2065
- return t ? `the team ${t} was dissolved in the meantime.` : gone;
2066
- case "no_seat":
2067
- return t ? `the team ${t} has no seat: nothing to save as a template.` : gone;
2068
2011
  case "closed":
2069
2012
  return k ? `the task ${k} is already closed.` : gone;
2070
2013
  case "holder":
@@ -2099,11 +2042,6 @@ var fr2 = {
2099
2042
  "session.restore": "Restaurer",
2100
2043
  "session.resume": "Reprendre",
2101
2044
  "session.decide": "Accepter",
2102
- "team.join": "Ajouter",
2103
- "team.leave": "Retirer",
2104
- "team.resume": "Reprendre",
2105
- "team.dissolve": "Dissoudre",
2106
- "team.save": "Enregistrer",
2107
2045
  "task.cancel": "Abandonner",
2108
2046
  "task.pass": "Passer le relais",
2109
2047
  "task.answer": "R\xE9pondre"
@@ -2166,11 +2104,6 @@ var en2 = {
2166
2104
  "session.restore": "Restore",
2167
2105
  "session.resume": "Resume",
2168
2106
  "session.decide": "Accept",
2169
- "team.join": "Add",
2170
- "team.leave": "Remove",
2171
- "team.resume": "Resume",
2172
- "team.dissolve": "Dissolve",
2173
- "team.save": "Save",
2174
2107
  "task.cancel": "Drop",
2175
2108
  "task.pass": "Hand over",
2176
2109
  "task.answer": "Answer"
@@ -2234,7 +2167,7 @@ var fr3 = {
2234
2167
  h: (n) => `${n} h`,
2235
2168
  d: (n) => `${n} j`
2236
2169
  },
2237
- tabs: { overview: "Vue d\u2019ensemble", session: "Session", ask: "Demander \xE0 Ledge", launch: "Nouvelle session", settings: "R\xE9glages", teams: "\xC9quipes" },
2170
+ tabs: { overview: "Vue d\u2019ensemble", session: "Session", ask: "Demander \xE0 Ledge", launch: "Nouvelle session", settings: "R\xE9glages", openrig: "OpenRig" },
2238
2171
  bar: {
2239
2172
  sessions: (n) => `${n} ${plural(n, "session", "sessions")}`,
2240
2173
  machines: (n) => `${n} ${plural(n, "machine", "machines")}`,
@@ -2276,11 +2209,6 @@ var fr3 = {
2276
2209
  scroll: "d\xE9filer",
2277
2210
  space: "espace",
2278
2211
  redraw: "autre nom",
2279
- save: "enregistrer l\u2019\xE9quipe",
2280
- tasks: "t\xE2ches de l\u2019\xE9quipe",
2281
- join: "rejoindre une \xE9quipe",
2282
- resume: "reprendre l\u2019\xE9quipe",
2283
- teamMode: "session ou \xE9quipe",
2284
2212
  handover: "passer le relais",
2285
2213
  decide: "r\xE9pondre \xE0 la d\xE9cision",
2286
2214
  sendAnswer: "envoyer la r\xE9ponse",
@@ -2308,16 +2236,12 @@ var fr3 = {
2308
2236
  deny: "refuser",
2309
2237
  ignore: "ignorer",
2310
2238
  review: "revoir",
2311
- openSession: (name) => `ouvrir ${name}`,
2312
- rolesTemplates: "r\xF4les / mod\xE8les",
2313
- folder: "dossier",
2314
- launchRole: "lancer avec ce r\xF4le",
2315
- launchTeam: "lancer cette \xE9quipe",
2316
- openFile: "ouvrir le fichier",
2317
- ignored: "ignor\xE9s",
2318
- reread: "relire"
2239
+ openSession: (name) => `ouvrir ${name}`
2319
2240
  },
2320
2241
  overview: {
2242
+ rigsGroup: "RIGS",
2243
+ kernelGroup: "KERNEL",
2244
+ rigSeat: (rig, role) => `rig ${rig} \xB7 ${role}`,
2321
2245
  yours: (n) => `\xC0 toi \xB7 ${n}`,
2322
2246
  projects: "Projets",
2323
2247
  exchanges: "\xC9changes",
@@ -2338,11 +2262,6 @@ var fr3 = {
2338
2262
  deny: "Refuser",
2339
2263
  always: "Toujours",
2340
2264
  denyWithNote: "Refuser en expliquant",
2341
- common: "table commune",
2342
- teamLine: (done, given) => `\xE9quipe \xB7 ${done}/${given}`,
2343
- resting: "au repos \xB7 R pour reprendre",
2344
- tasks: (team) => `T\xE2ches \xB7 ${team}`,
2345
- noTask: "Aucune t\xE2che.",
2346
2265
  ledge: "Ledge",
2347
2266
  near: (name, pct) => `${name} \xB7 ctx ${pct} % \xB7 H rel\xE8ve`,
2348
2267
  reliefFailed: (name, why) => `La rel\xE8ve de ${name} a \xE9chou\xE9 : ${why}`,
@@ -2354,10 +2273,7 @@ var fr3 = {
2354
2273
  decision: (giver, title2) => `${giver} te demande : ${title2}`,
2355
2274
  decisionTitle: (giver, title2) => `R\xE9pondre \xE0 ${giver} \xB7 ${title2}`,
2356
2275
  decisionHint: "Ta r\xE9ponse, telle que la session la lira\u2026",
2357
- confirmAnswer: (giver) => `Envoyer \xE0 ${giver} ? Une r\xE9ponse ne se reprend pas`,
2358
- joinWhich: (name) => `${name} rejoint quelle \xE9quipe ? (\u2191\u2193 \u21B5, esc)`,
2359
- noTeamHere: "Aucune \xE9quipe dans ce projet.",
2360
- notInTeam: "Cette session n\u2019est dans aucune \xE9quipe."
2276
+ confirmAnswer: (giver) => `Envoyer \xE0 ${giver} ? Une r\xE9ponse ne se reprend pas`
2361
2277
  },
2362
2278
  session: {
2363
2279
  you: "Vous",
@@ -2415,19 +2331,8 @@ var fr3 = {
2415
2331
  offline: "Le daemon local est hors ligne : la question attendra.",
2416
2332
  steps: { sessions: "les sessions", pane: (n) => `le terminal de ${n}`, reply: (n) => `la r\xE9ponse de ${n}`, git: (n) => `l\u2019\xE9tat git de ${n}`, repo: (p) => p },
2417
2333
  operator: (s, m) => `Ledge \xB7 Op\xE9rateur \xB7 voit ${s} ${plural(s, "session", "sessions")}, ${m} ${plural(m, "machine", "machines")}`,
2418
- teamCard: (name) => `\xC9quipe \xB7 ${name}`,
2419
2334
  taskCard: "T\xE2ches \xE0 confier",
2420
- launchTeam: "Lancer l\u2019\xE9quipe",
2421
- save: "Enregistrer",
2422
- saved: "enregistr\xE9e",
2423
- seats: "Si\xE8ges",
2424
- table: "Table",
2425
- newTable: (project) => `nouvelle table dans la pi\xE8ce ${project}`,
2426
- autonomy: { ask: "demande", edits: "modifie sans demander", auto: "autonome" },
2427
- autoWarning: (roles) => `${roles} travaillera sans demander.`,
2428
- teamLaunched: (at) => `\u2713 \xC9quipe lanc\xE9e \xE0 ${at}`,
2429
2335
  give: "Confier",
2430
- waitTeam: "active une fois l\u2019\xE9quipe lanc\xE9e",
2431
2336
  given: (owner, id, state2) => `\u2713 Confi\xE9e \xE0 ${owner} \xB7 ${id} \xB7 ${state2}`,
2432
2337
  taskState: { open: "\xE0 prendre", taken: "prise", done: "finie", refused: "refus\xE9e", dropped: "abandonn\xE9e" },
2433
2338
  placeholder: "Une question pour Ledge\u2026",
@@ -2478,66 +2383,7 @@ var fr3 = {
2478
2383
  thenAttach: "puis a pour s\u2019attacher \xE0 son terminal",
2479
2384
  missing: { prompt: "\xC9crivez une instruction.", machine: "Aucune machine en ligne.", repos: "Choisissez un d\xE9p\xF4t ou un dossier." },
2480
2385
  launching: "Lancement\u2026",
2481
- launched: (name) => `\u2713 ${name} est lanc\xE9e`,
2482
- teamTitle: "Nouvelle \xE9quipe",
2483
- mode: "Mode",
2484
- session: "session",
2485
- team: "\xE9quipe",
2486
- template: "Mod\xE8le",
2487
- custom: "si\xE8ges choisis un par un",
2488
- seats: "Si\xE8ges",
2489
- teamName: "Nom",
2490
- noRole: "aucun r\xF4le : \xE9cris ~/.ledge/roles/<id>.md",
2491
- loadingRoles: "lecture des r\xF4les\u2026",
2492
- teamPreview: (team, project) => `L\u2019\xE9quipe ${team} s\u2019installera \xE0 une nouvelle table de la pi\xE8ce ${project}.`,
2493
- teamLaunched: (name) => `\u2713 \xC9quipe ${name} lanc\xE9e`,
2494
- teamFailed: (why) => `\xC9quipe non lanc\xE9e : ${why}`,
2495
- noSeat: "Choisissez au moins un si\xE8ge."
2496
- },
2497
- catalog: {
2498
- roles: "r\xF4les",
2499
- templates: "mod\xE8les",
2500
- homeOnly: "Perso seulement",
2501
- repoRoles: "d\xE9p\xF4t \xB7 .ledge/roles",
2502
- homeRoles: "perso \xB7 ~/.ledge/roles",
2503
- repoTeams: "d\xE9p\xF4t \xB7 .ledge/teams",
2504
- homeTeams: "perso \xB7 ~/.ledge/teams",
2505
- repo: "d\xE9p\xF4t",
2506
- home: "perso",
2507
- tool: "outil",
2508
- model: "mod\xE8le",
2509
- effort: "effort",
2510
- autonomy: "autonomie",
2511
- file: "fichier",
2512
- usedIn: "dans",
2513
- heldBy: "tenu par",
2514
- teams: "\xE9quipes",
2515
- replaces: (path2) => `remplace ${path2}`,
2516
- toolDefault: "d\xE9faut de l\u2019outil",
2517
- noTemplate: "aucun mod\xE8le",
2518
- nobody: "personne",
2519
- noTeam: "aucune",
2520
- active: "active",
2521
- resting: "au repos",
2522
- instruction: (n, max) => `Consigne \xB7 ${n}/${max}`,
2523
- noInstruction: "Pas de consigne.",
2524
- seats: (n) => `${n} ${plural(n, "poste", "postes")}`,
2525
- autonomyShort: { ask: "demande", edits: "\xE9ditions", auto: "auto" },
2526
- ignored: (n) => `\u26A0 ${n} ${plural(n, "fichier ignor\xE9", "fichiers ignor\xE9s")}`,
2527
- see: "voir",
2528
- problems: "Fichiers ignor\xE9s",
2529
- noProblem: "Aucun fichier ignor\xE9.",
2530
- loading: "lecture des r\xF4les\u2026",
2531
- noAnswer: "Le daemon n\u2019a pas r\xE9pondu.",
2532
- offline: (m) => `${m} est hors ligne.`,
2533
- outdated: (m) => `Le daemon de ${m} est trop ancien pour le catalogue.`,
2534
- onMachine: (m) => `sur ${m}`,
2535
- roleChecking: (id) => `@${id} : lecture du r\xF4le dans ce dossier\u2026`,
2536
- roleReadIn: (cwd) => `celui de ${cwd}`,
2537
- roleElsewhere: (id, cwd) => `@${id} n\u2019est lu que dans ${cwd} : lanc\xE9 sans r\xF4le`,
2538
- noRole: "Aucun r\xF4le ici : \xE9cris .ledge/roles/<id>.md",
2539
- noTemplateHere: "Aucun mod\xE8le ici : \xE9cris .ledge/teams/<id>.yaml",
2540
- role: "R\xF4le"
2386
+ launched: (name) => `\u2713 ${name} est lanc\xE9e`
2541
2387
  },
2542
2388
  settings: { title: "R\xE9glages", language: "Langue", theme: "Th\xE8me", themes: { auto: "Auto", dark: "Sombre", light: "Clair" }, themeAuto: (tone2) => `Auto (fond ${tone2 === "light" ? "clair" : "sombre"})`, context: "Contexte max", contextHint: "au-del\xE0 : passer le relais", contextUnknown: "mettez \xE0 jour le daemon", contextSaved: (pct) => `Contexte max : ${pct} %`, saved: "R\xE9glage enregistr\xE9.", saveFailed: (m) => `R\xE9glage appliqu\xE9, mais non enregistr\xE9 : ${m}` },
2543
2389
  help: {
@@ -2548,8 +2394,9 @@ var fr3 = {
2548
2394
  ask: "Demander \xE0 Ledge",
2549
2395
  launch: "Nouvelle session",
2550
2396
  settings: "R\xE9glages",
2397
+ openrig: "OpenRig",
2398
+ rigLaunch: "Lancer un rig",
2551
2399
  screens: "changer d\u2019\xE9cran",
2552
- teams: "\xC9quipes",
2553
2400
  field: "Saisie",
2554
2401
  wordBefore: "effacer le mot avant",
2555
2402
  more: (above, below) => [above ? `\u2191 ${above} plus haut` : "", below ? `\u2193 ${below} plus bas` : ""].filter(Boolean).join(" \xB7 ")
@@ -2595,9 +2442,6 @@ var fr3 = {
2595
2442
  noTerminal: (n) => `Pas de terminal pour ${n}.`,
2596
2443
  attachFailed: (code3) => `tmux s\u2019est arr\xEAt\xE9 avec le code ${code3}.`,
2597
2444
  modelSwitch: "Ce daemon ne change pas le mod\xE8le d\u2019une session en cours.",
2598
- joined: (n, team) => `${n} rejoint ${team}.`,
2599
- resumed: (team) => `Reprise de l\u2019\xE9quipe ${team} demand\xE9e.`,
2600
- saved: (path2) => `\xC9quipe enregistr\xE9e : ${path2}`,
2601
2445
  draftKept: "Brouillon gard\xE9 : r pour reprendre.",
2602
2446
  draftQuit: "Brouillon en cours : ctrl+c encore pour quitter quand m\xEAme (il sera perdu).",
2603
2447
  exited: (cmd, code3) => `${cmd} s\u2019est arr\xEAt\xE9 avec le code ${code3}.`
@@ -2614,10 +2458,154 @@ var fr3 = {
2614
2458
  question: (n, text5) => `${n} a une question : ${text5}`,
2615
2459
  title: (n) => n > 0 ? `ledge \xB7 ${n} t\u2019${plural(n, "attend", "attendent")}` : "ledge"
2616
2460
  },
2461
+ openrig: {
2462
+ operatorKey: "op\xE9rateur",
2463
+ operatorHint: "O : lui parler",
2464
+ operatorNotYet: "Ledge ne suit pas encore la session de l\u2019op\xE9rateur : r\xE9essayez dans quelques secondes.",
2465
+ activity: (a) => ({ running: "au travail", idle: "disponible", unknown: "\u2014" })[a] ?? a,
2466
+ title: "OpenRig",
2467
+ daemon: "Daemon",
2468
+ kernel: "Kernel",
2469
+ rigs: "Rigs",
2470
+ machine: "machine",
2471
+ toggle: "d\xE9marrer/arr\xEAter",
2472
+ restart: "red\xE9marrer",
2473
+ unfold: "d\xE9plier",
2474
+ daemonKey: "daemon",
2475
+ kernelKey: "kernel",
2476
+ daemonState: { running: "actif", stopped: "arr\xEAt\xE9", stale: "arr\xEAt\xE9 sans arr\xEAt propre", unknown: "inconnu" },
2477
+ kernelState: { ready: "pr\xEAt", starting: "d\xE9marrage", stopped: "arr\xEAt\xE9", skipped: "sans kernel", absent: "absent", unknown: "inconnu" },
2478
+ look: { running: "en marche", starting: "d\xE9marrage", attention: "\xE0 surveiller", stopped: "arr\xEAt\xE9" },
2479
+ daemonMeta: (port, pid) => [port ? `127.0.0.1:${port}` : "", pid ? `pid ${pid}` : ""].filter(Boolean).join(" \xB7 "),
2480
+ restoreHint: "\u21BB au d\xE9marrage : rigs relanc\xE9s",
2481
+ kernelHint: "l\u2019op\xE9rateur qui aide \xE0 lancer un rig",
2482
+ seats: (running, total) => `${running}/${total}`,
2483
+ attention: (n) => `${n} ${plural(n, "si\xE8ge demande", "si\xE8ges demandent")} attention`,
2484
+ since: (uptime) => `depuis ${uptime}`,
2485
+ snapshot: (s) => `snapshot ${s}`,
2486
+ work: (n) => n ? `${n} ${plural(n, "t\xE2che", "t\xE2ches")}` : "",
2487
+ stoppedOnPurpose: "arr\xEAt\xE9 volontairement",
2488
+ noRigs: "Aucun rig sur cette machine.",
2489
+ notInstalled: "OpenRig n\u2019est pas install\xE9 sur cette machine (la commande rig est introuvable).",
2490
+ stoppedDaemon: "Le daemon OpenRig est arr\xEAt\xE9 : D le d\xE9marre.",
2491
+ offline: "Machine hors ligne.",
2492
+ outdated: "Le daemon Ledge de cette machine ne conna\xEEt pas OpenRig : mettez-le \xE0 jour.",
2493
+ loading: "Lecture d\u2019OpenRig\u2026",
2494
+ version: (v) => `OpenRig ${v}`,
2495
+ confirmTitle: "Confirmer",
2496
+ confirm: (what) => `${what} ?`,
2497
+ what: {
2498
+ "daemon.stop": "Arr\xEAter le daemon OpenRig",
2499
+ "daemon.restart": "Red\xE9marrer le daemon OpenRig",
2500
+ "kernel.stop": "Arr\xEAter le kernel",
2501
+ "kernel.restart": "Red\xE9marrer le kernel",
2502
+ "rig.stop": (rig) => `Arr\xEAter le rig \xAB ${rig} \xBB`,
2503
+ "rig.restart": (rig) => `Red\xE9marrer le rig \xAB ${rig} \xBB`
2504
+ },
2505
+ busy: (names, n) => n ? `${n} ${plural(n, "agent au travail s\u2019arr\xEAte", "agents au travail s\u2019arr\xEAtent")} : ${names}.` : "Aucun agent au travail.",
2506
+ yes: "confirmer",
2507
+ no: "annuler",
2508
+ running: (cmd) => `en cours : ${cmd}`,
2509
+ done: (cmd) => `fait : ${cmd}`,
2510
+ failed: (why) => `\xE9chec : ${why}`,
2511
+ why: { busy: "une autre action OpenRig est en cours sur cette machine", not_installed: "OpenRig n\u2019est pas install\xE9", offline: "machine hors ligne", outdated: "daemon Ledge trop ancien" }
2512
+ },
2513
+ rigLaunch: {
2514
+ title: "Lancer un rig",
2515
+ launchKey: "lancer un rig",
2516
+ count: "nombre de si\xE8ges",
2517
+ permissions: "4 \xB7 Permissions",
2518
+ policyKey: "permissions",
2519
+ policyOf: { auto: "Auto", standard: "Standard", open: "Ouvert", yolo: "YOLO", locked: "Verrouill\xE9" },
2520
+ specPolicy: (p) => `celle de la spec (${p})`,
2521
+ noSpecPolicy: "aucune",
2522
+ asRig: "comme le rig",
2523
+ policyHint: {
2524
+ none: "sans politique : Claude en acceptEdits, Codex en workspace-write",
2525
+ auto: "Claude d\xE9cide seul (mode auto) ; Codex garde la posture par d\xE9faut",
2526
+ standard: "tout passe sauf PR, publication, fusion, force push et destructif (demande une personne) ; r\xE8gles appliqu\xE9es une fois traduites dans les r\xE9glages du harnais",
2527
+ open: "tout passe sauf le destructif ; r\xE8gles appliqu\xE9es une fois traduites dans les r\xE9glages du harnais",
2528
+ yolo: "aucune demande : Claude --dangerously-skip-permissions, Codex danger-full-access",
2529
+ locked: "seuls la cha\xEEne d\u2019outils et rig up/down passent ; r\xE8gles appliqu\xE9es une fois traduites dans les r\xE9glages du harnais"
2530
+ },
2531
+ nonInterruptive: "accepter les avertissements de premier lancement (--non-interruptive)",
2532
+ spec: "1 \xB7 Spec de d\xE9part",
2533
+ where: "2 \xB7 O\xF9",
2534
+ roles: "3 \xB7 Si\xE8ges par r\xF4le",
2535
+ delegation: "5 \xB7 Qui d\xE9l\xE8gue \xE0 qui",
2536
+ preview: "Aper\xE7u \xB7 rig.yaml",
2537
+ specHint: "Le mod\xE8le de rig : ses pods, ses r\xF4les, les liens entre eux.",
2538
+ source: { user: "mes specs", builtin: "OpenRig" },
2539
+ seatsOf: (n) => `${n} ${plural(n, "si\xE8ge", "si\xE8ges")}`,
2540
+ folder: "Dossier",
2541
+ name: "Nom",
2542
+ folderHint: "un dossier de cette machine, ~ accept\xE9",
2543
+ projectFolder: (p) => `projet ${p}`,
2544
+ model: "mod\xE8le",
2545
+ defaultModel: "par d\xE9faut",
2546
+ modelKey: "mod\xE8le",
2547
+ rolesHint: "Un compteur \xE0 3 donne build, build-2, build-3, identiques au premier.",
2548
+ pairs: "par paires",
2549
+ all: "chaque si\xE8ge \u2192 tous",
2550
+ pairsHint: "build-2 \u2192 review-2",
2551
+ allHint: "chaque build \u2192 tous les review",
2552
+ save: "enregistrer dans mes specs",
2553
+ notScaled: "un si\xE8ge par r\xF4le : rien \xE0 d\xE9l\xE9guer de plus",
2554
+ loading: "Lecture des specs\u2026",
2555
+ noSpecs: "Aucune spec sur cette machine.",
2556
+ daemonStopped: "D\xE9marrez le daemon OpenRig d\u2019abord (D sur l\u2019\xE9cran OpenRig).",
2557
+ noCheck: "p v\xE9rifie le lancement (rig up --plan), sans rien d\xE9marrer.",
2558
+ checking: "V\xE9rification\u2026",
2559
+ launching: "Lancement\u2026",
2560
+ passes: (seats) => `\u2713 le plan passe \xB7 ${seats} ${plural(seats, "si\xE8ge", "si\xE8ges")}`,
2561
+ planStatus: (s) => `plan : ${s}`,
2562
+ warning: (text5, seats) => seats ? `${text5} (${seats})` : text5,
2563
+ launched: (name, n) => `Rig \xAB ${name} \xBB lanc\xE9 \xB7 ${n} ${plural(n, "si\xE8ge", "si\xE8ges")}.`,
2564
+ check: "v\xE9rifier (--plan)",
2565
+ launch: (n) => `lancer ${n} ${plural(n, "si\xE8ge", "si\xE8ges")}`,
2566
+ missing: { spec: "Choisissez une spec.", cwd: "Indiquez le dossier de travail.", name: "Le nom du rig : lettres, chiffres, . _ - (pas de - au d\xE9but).", seats: "Au moins un si\xE8ge." },
2567
+ why: {
2568
+ busy: "Une autre action OpenRig est en cours sur cette machine.",
2569
+ cwd_missing: "Ce dossier n\u2019existe pas sur la machine.",
2570
+ spec_not_found: "Cette spec n\u2019est plus dans la biblioth\xE8que.",
2571
+ daemon_stopped: "D\xE9marrez le daemon OpenRig d\u2019abord (D sur l\u2019\xE9cran OpenRig).",
2572
+ not_installed: "OpenRig n\u2019est pas install\xE9 sur cette machine.",
2573
+ offline: "Machine hors ligne.",
2574
+ outdated: "Le daemon Ledge de cette machine ne sait pas lancer de rig : mettez-le \xE0 jour."
2575
+ }
2576
+ },
2577
+ rigSeats: {
2578
+ title: (rig, machine) => `Si\xE8ges \xB7 ${rig} \xB7 ${machine}`,
2579
+ key: "si\xE8ges",
2580
+ stays: (now2) => `Le rig reste en marche. Aujourd\u2019hui : ${now2}.`,
2581
+ role: "r\xF4le",
2582
+ count: "nombre",
2583
+ delegationKey: "d\xE9l\xE9gation",
2584
+ apply: "appliquer",
2585
+ cancel: "annuler",
2586
+ pairs: "d\xE9l\xE9gation par paires",
2587
+ all: "chaque ajout \u2192 tous",
2588
+ linksPairs: "liens ajout\xE9s par paires",
2589
+ linksAll: "liens ajout\xE9s \xE0 tous",
2590
+ nothing: "Changez un nombre : - +",
2591
+ notRunning: "Seul un rig en marche change de si\xE8ges.",
2592
+ busy: (seat) => `${seat} travaille : son travail en cours s\u2019arr\xEAte.`,
2593
+ applying: "en cours\u2026",
2594
+ done: (added, removed, links) => `+${added} \u2212${removed} si\xE8ges${links !== void 0 ? `, ${links} ${plural(links, "lien", "liens")}` : ""}`,
2595
+ linksUnsupported: "cet OpenRig ne sait pas lier des si\xE8ges ajout\xE9s \xE0 un rig en marche (avant 0.7)",
2596
+ why: {
2597
+ rig_not_running: "Le rig n\u2019est plus en marche.",
2598
+ rig_root_unknown: "Impossible de copier un si\xE8ge : les agents du rig sont introuvables.",
2599
+ busy: "Une autre action OpenRig est en cours sur cette machine.",
2600
+ not_installed: "OpenRig n\u2019est pas install\xE9 sur cette machine.",
2601
+ offline: "Machine hors ligne.",
2602
+ outdated: "Le daemon Ledge de cette machine ne sait pas changer les si\xE8ges d\u2019un rig : mettez-le \xE0 jour."
2603
+ }
2604
+ },
2617
2605
  small: {
2618
2606
  tooSmall: (w, h) => `Fen\xEAtre trop petite : ${w}\xD7${h}`,
2619
2607
  atLeast: (w, h) => `il faut au moins ${w}\xD7${h}`,
2620
- waits: (name, question) => question ? `${name} a une question` : `${name} attend ton accord`,
2608
+ waits: (name, question2) => question2 ? `${name} a une question` : `${name} attend ton accord`,
2621
2609
  kept: "brouillon gard\xE9"
2622
2610
  },
2623
2611
  cli: { notTty: "ledge tui: needs a terminal (stdin and stdout must be a TTY)", movedToTab: "ledge tui s\u2019ouvre dans l\u2019onglet Ledge.", loadingProfile: "Windows Terminal charge le profil Ledge\u2026" }
@@ -2629,7 +2617,7 @@ var en3 = {
2629
2617
  lang: { fr: "Fran\xE7ais", en: "English" },
2630
2618
  state: { approval: "Needs you", question: "A question", starting: "Starting", idle: "Available", working: "Working", done: "Finished", error: "Error", readonly: "Read-only" },
2631
2619
  time: { now: "just now", decimal: ".", s: (n) => `${n}s`, min: (n) => `${n} min`, h: (n) => `${n} h`, d: (n) => `${n} d` },
2632
- tabs: { overview: "Overview", session: "Session", ask: "Ask Ledge", launch: "New session", settings: "Settings", teams: "Teams" },
2620
+ tabs: { overview: "Overview", session: "Session", ask: "Ask Ledge", launch: "New session", settings: "Settings", openrig: "OpenRig" },
2633
2621
  bar: {
2634
2622
  sessions: (n) => `${n} ${plural2(n, "session", "sessions")}`,
2635
2623
  machines: (n) => `${n} ${plural2(n, "machine", "machines")}`,
@@ -2671,11 +2659,6 @@ var en3 = {
2671
2659
  scroll: "scroll",
2672
2660
  space: "space",
2673
2661
  redraw: "another name",
2674
- save: "save the team",
2675
- tasks: "team tasks",
2676
- join: "join a team",
2677
- resume: "resume the team",
2678
- teamMode: "session or team",
2679
2662
  handover: "hand over",
2680
2663
  decide: "answer the decision",
2681
2664
  sendAnswer: "send the answer",
@@ -2703,16 +2686,12 @@ var en3 = {
2703
2686
  deny: "deny",
2704
2687
  ignore: "ignore",
2705
2688
  review: "review",
2706
- openSession: (name) => `open ${name}`,
2707
- rolesTemplates: "roles / templates",
2708
- folder: "folder",
2709
- launchRole: "launch with this role",
2710
- launchTeam: "launch this team",
2711
- openFile: "open the file",
2712
- ignored: "ignored",
2713
- reread: "read again"
2689
+ openSession: (name) => `open ${name}`
2714
2690
  },
2715
2691
  overview: {
2692
+ rigsGroup: "RIGS",
2693
+ kernelGroup: "KERNEL",
2694
+ rigSeat: (rig, role) => `rig ${rig} \xB7 ${role}`,
2716
2695
  yours: (n) => `Yours \xB7 ${n}`,
2717
2696
  projects: "Projects",
2718
2697
  exchanges: "Messages",
@@ -2733,11 +2712,6 @@ var en3 = {
2733
2712
  deny: "Deny",
2734
2713
  always: "Always",
2735
2714
  denyWithNote: "Deny with a note",
2736
- common: "common table",
2737
- teamLine: (done, given) => `team \xB7 ${done}/${given}`,
2738
- resting: "resting \xB7 R to resume",
2739
- tasks: (team) => `Tasks \xB7 ${team}`,
2740
- noTask: "No task.",
2741
2715
  ledge: "Ledge",
2742
2716
  near: (name, pct) => `${name} \xB7 ctx ${pct} % \xB7 H hand over`,
2743
2717
  reliefFailed: (name, why) => `The relief of ${name} failed: ${why}`,
@@ -2749,10 +2723,7 @@ var en3 = {
2749
2723
  decision: (giver, title2) => `${giver} asks you: ${title2}`,
2750
2724
  decisionTitle: (giver, title2) => `Answer ${giver} \xB7 ${title2}`,
2751
2725
  decisionHint: "Your answer, as the session will read it\u2026",
2752
- confirmAnswer: (giver) => `Send to ${giver}? An answer cannot be taken back`,
2753
- joinWhich: (name) => `${name} joins which team? (\u2191\u2193 \u21B5, esc)`,
2754
- noTeamHere: "No team in this project.",
2755
- notInTeam: "This session is in no team."
2726
+ confirmAnswer: (giver) => `Send to ${giver}? An answer cannot be taken back`
2756
2727
  },
2757
2728
  session: {
2758
2729
  you: "You",
@@ -2810,19 +2781,8 @@ var en3 = {
2810
2781
  offline: "The local daemon is offline: the question will wait.",
2811
2782
  steps: { sessions: "the sessions", pane: (n) => `${n}'s terminal`, reply: (n) => `${n}'s reply`, git: (n) => `${n}'s git state`, repo: (p) => p },
2812
2783
  operator: (s, m) => `Ledge \xB7 Operator \xB7 sees ${s} ${plural2(s, "session", "sessions")}, ${m} ${plural2(m, "machine", "machines")}`,
2813
- teamCard: (name) => `Team \xB7 ${name}`,
2814
2784
  taskCard: "Tasks to give",
2815
- launchTeam: "Launch the team",
2816
- save: "Save",
2817
- saved: "saved",
2818
- seats: "Seats",
2819
- table: "Table",
2820
- newTable: (project) => `new table in the ${project} room`,
2821
- autonomy: { ask: "asks", edits: "edits without asking", auto: "autonomous" },
2822
- autoWarning: (roles) => `${roles} will work without asking.`,
2823
- teamLaunched: (at) => `\u2713 Team launched at ${at}`,
2824
2785
  give: "Give",
2825
- waitTeam: "active once the team is launched",
2826
2786
  given: (owner, id, state2) => `\u2713 Given to ${owner} \xB7 ${id} \xB7 ${state2}`,
2827
2787
  taskState: { open: "to take", taken: "taken", done: "done", refused: "refused", dropped: "dropped" },
2828
2788
  placeholder: "A question for Ledge\u2026",
@@ -2878,66 +2838,7 @@ var en3 = {
2878
2838
  thenAttach: "then a to attach to its terminal",
2879
2839
  missing: { prompt: "Write an instruction.", machine: "No machine online.", repos: "Pick a repository or a folder." },
2880
2840
  launching: "Launching\u2026",
2881
- launched: (name) => `\u2713 ${name} is started`,
2882
- teamTitle: "New team",
2883
- mode: "Mode",
2884
- session: "session",
2885
- team: "team",
2886
- template: "Template",
2887
- custom: "seats chosen one by one",
2888
- seats: "Seats",
2889
- teamName: "Name",
2890
- noRole: "no role: write ~/.ledge/roles/<id>.md",
2891
- loadingRoles: "reading the roles\u2026",
2892
- teamPreview: (team, project) => `Team ${team} will sit at a new table in the ${project} room.`,
2893
- teamLaunched: (name) => `\u2713 Team ${name} launched`,
2894
- teamFailed: (why) => `Team not launched: ${why}`,
2895
- noSeat: "Choose at least one seat."
2896
- },
2897
- catalog: {
2898
- roles: "roles",
2899
- templates: "templates",
2900
- homeOnly: "Personal only",
2901
- repoRoles: "repository \xB7 .ledge/roles",
2902
- homeRoles: "personal \xB7 ~/.ledge/roles",
2903
- repoTeams: "repository \xB7 .ledge/teams",
2904
- homeTeams: "personal \xB7 ~/.ledge/teams",
2905
- repo: "repository",
2906
- home: "personal",
2907
- tool: "agent",
2908
- model: "model",
2909
- effort: "effort",
2910
- autonomy: "autonomy",
2911
- file: "file",
2912
- usedIn: "in",
2913
- heldBy: "held by",
2914
- teams: "teams",
2915
- replaces: (path2) => `replaces ${path2}`,
2916
- toolDefault: "the agent\u2019s default",
2917
- noTemplate: "no template",
2918
- nobody: "nobody",
2919
- noTeam: "none",
2920
- active: "active",
2921
- resting: "resting",
2922
- instruction: (n, max) => `Instruction \xB7 ${n}/${max}`,
2923
- noInstruction: "No instruction.",
2924
- seats: (n) => `${n} ${plural2(n, "seat", "seats")}`,
2925
- autonomyShort: { ask: "asks", edits: "edits", auto: "auto" },
2926
- ignored: (n) => `\u26A0 ${n} ${plural2(n, "file", "files")} ignored`,
2927
- see: "see",
2928
- problems: "Ignored files",
2929
- noProblem: "No ignored file.",
2930
- loading: "reading the roles\u2026",
2931
- noAnswer: "The daemon did not answer.",
2932
- offline: (m) => `${m} is offline.`,
2933
- outdated: (m) => `${m}\u2019s daemon is too old for the catalog.`,
2934
- onMachine: (m) => `on ${m}`,
2935
- roleChecking: (id) => `@${id}: reading the role in this folder\u2026`,
2936
- roleReadIn: (cwd) => `the one in ${cwd}`,
2937
- roleElsewhere: (id, cwd) => `@${id} is only read in ${cwd}: launched without a role`,
2938
- noRole: "No role here: write .ledge/roles/<id>.md",
2939
- noTemplateHere: "No template here: write .ledge/teams/<id>.yaml",
2940
- role: "Role"
2841
+ launched: (name) => `\u2713 ${name} is started`
2941
2842
  },
2942
2843
  settings: { title: "Settings", language: "Language", theme: "Theme", themes: { auto: "Auto", dark: "Dark", light: "Light" }, themeAuto: (tone2) => `Auto (${tone2} background)`, context: "Max context", contextHint: "past it: hand over", contextUnknown: "update the daemon", contextSaved: (pct) => `Max context: ${pct} %`, saved: "Setting saved.", saveFailed: (m) => `Setting applied, but not saved: ${m}` },
2943
2844
  help: {
@@ -2948,8 +2849,9 @@ var en3 = {
2948
2849
  ask: "Ask Ledge",
2949
2850
  launch: "New session",
2950
2851
  settings: "Settings",
2852
+ openrig: "OpenRig",
2853
+ rigLaunch: "Launch a rig",
2951
2854
  screens: "switch screen",
2952
- teams: "Teams",
2953
2855
  field: "Typing",
2954
2856
  wordBefore: "delete the word before",
2955
2857
  more: (above, below) => [above ? `\u2191 ${above} above` : "", below ? `\u2193 ${below} below` : ""].filter(Boolean).join(" \xB7 ")
@@ -2988,9 +2890,6 @@ var en3 = {
2988
2890
  noTerminal: (n) => `No terminal for ${n}.`,
2989
2891
  attachFailed: (code3) => `tmux stopped with code ${code3}.`,
2990
2892
  modelSwitch: "This daemon cannot change a running session\u2019s model.",
2991
- joined: (n, team) => `${n} joins ${team}.`,
2992
- resumed: (team) => `Asked to resume team ${team}.`,
2993
- saved: (path2) => `Team saved: ${path2}`,
2994
2893
  draftKept: "Draft kept: r to pick it up.",
2995
2894
  draftQuit: "Draft in progress: ctrl+c again to quit anyway (it will be lost).",
2996
2895
  exited: (cmd, code3) => `${cmd} exited with code ${code3}.`
@@ -3007,10 +2906,154 @@ var en3 = {
3007
2906
  question: (n, text5) => `${n} has a question: ${text5}`,
3008
2907
  title: (n) => n > 0 ? `ledge \xB7 ${n} waiting` : "ledge"
3009
2908
  },
2909
+ openrig: {
2910
+ operatorKey: "operator",
2911
+ operatorHint: "O: talk to it",
2912
+ operatorNotYet: "Ledge does not follow the operator\u2019s session yet: try again in a few seconds.",
2913
+ activity: (a) => ({ running: "working", idle: "available", unknown: "\u2014" })[a] ?? a,
2914
+ title: "OpenRig",
2915
+ daemon: "Daemon",
2916
+ kernel: "Kernel",
2917
+ rigs: "Rigs",
2918
+ machine: "machine",
2919
+ toggle: "start/stop",
2920
+ restart: "restart",
2921
+ unfold: "unfold",
2922
+ daemonKey: "daemon",
2923
+ kernelKey: "kernel",
2924
+ daemonState: { running: "running", stopped: "stopped", stale: "stopped uncleanly", unknown: "unknown" },
2925
+ kernelState: { ready: "ready", starting: "starting", stopped: "stopped", skipped: "no kernel", absent: "absent", unknown: "unknown" },
2926
+ look: { running: "running", starting: "starting", attention: "needs a look", stopped: "stopped" },
2927
+ daemonMeta: (port, pid) => [port ? `127.0.0.1:${port}` : "", pid ? `pid ${pid}` : ""].filter(Boolean).join(" \xB7 "),
2928
+ restoreHint: "\u21BB on start: rigs brought back",
2929
+ kernelHint: "the operator that helps start a rig",
2930
+ seats: (running, total) => `${running}/${total}`,
2931
+ attention: (n) => `${n} ${plural2(n, "seat needs", "seats need")} attention`,
2932
+ since: (uptime) => `up ${uptime}`,
2933
+ snapshot: (s) => `snapshot ${s}`,
2934
+ work: (n) => n ? `${n} ${plural2(n, "task", "tasks")}` : "",
2935
+ stoppedOnPurpose: "stopped on purpose",
2936
+ noRigs: "No rig on this machine.",
2937
+ notInstalled: "OpenRig is not installed on this machine (no rig command).",
2938
+ stoppedDaemon: "The OpenRig daemon is stopped: D starts it.",
2939
+ offline: "Machine offline.",
2940
+ outdated: "This machine\u2019s Ledge daemon does not know OpenRig: update it.",
2941
+ loading: "Reading OpenRig\u2026",
2942
+ version: (v) => `OpenRig ${v}`,
2943
+ confirmTitle: "Confirm",
2944
+ confirm: (what) => `${what}?`,
2945
+ what: {
2946
+ "daemon.stop": "Stop the OpenRig daemon",
2947
+ "daemon.restart": "Restart the OpenRig daemon",
2948
+ "kernel.stop": "Stop the kernel",
2949
+ "kernel.restart": "Restart the kernel",
2950
+ "rig.stop": (rig) => `Stop the rig \u201C${rig}\u201D`,
2951
+ "rig.restart": (rig) => `Restart the rig \u201C${rig}\u201D`
2952
+ },
2953
+ busy: (names, n) => n ? `${n} working ${plural2(n, "agent stops", "agents stop")}: ${names}.` : "No agent at work.",
2954
+ yes: "confirm",
2955
+ no: "cancel",
2956
+ running: (cmd) => `running: ${cmd}`,
2957
+ done: (cmd) => `done: ${cmd}`,
2958
+ failed: (why) => `failed: ${why}`,
2959
+ why: { busy: "another OpenRig action runs on this machine", not_installed: "OpenRig is not installed", offline: "machine offline", outdated: "Ledge daemon too old" }
2960
+ },
2961
+ rigLaunch: {
2962
+ title: "Launch a rig",
2963
+ launchKey: "launch a rig",
2964
+ count: "number of seats",
2965
+ permissions: "4 \xB7 Permissions",
2966
+ policyKey: "permissions",
2967
+ policyOf: { auto: "Auto", standard: "Standard", open: "Open", yolo: "YOLO", locked: "Locked" },
2968
+ specPolicy: (p) => `the spec\u2019s (${p})`,
2969
+ noSpecPolicy: "none",
2970
+ asRig: "as the rig",
2971
+ policyHint: {
2972
+ none: "no policy: Claude in acceptEdits, Codex in workspace-write",
2973
+ auto: "Claude decides on its own (auto mode); Codex keeps the default posture",
2974
+ standard: "everything passes but PRs, publishing, merging, force push and destructive (asks a person); rules applied once translated into the harness settings",
2975
+ open: "everything passes but destructive; rules applied once translated into the harness settings",
2976
+ yolo: "no prompt: Claude --dangerously-skip-permissions, Codex danger-full-access",
2977
+ locked: "only the toolchain and rig up/down pass; rules applied once translated into the harness settings"
2978
+ },
2979
+ nonInterruptive: "accept the first-launch warnings (--non-interruptive)",
2980
+ spec: "1 \xB7 Starting spec",
2981
+ where: "2 \xB7 Where",
2982
+ roles: "3 \xB7 Seats per role",
2983
+ delegation: "5 \xB7 Who delegates to whom",
2984
+ preview: "Preview \xB7 rig.yaml",
2985
+ specHint: "The rig\u2019s model: its pods, its roles, the links between them.",
2986
+ source: { user: "my specs", builtin: "OpenRig" },
2987
+ seatsOf: (n) => `${n} ${plural2(n, "seat", "seats")}`,
2988
+ folder: "Folder",
2989
+ name: "Name",
2990
+ folderHint: "a folder of this machine, ~ accepted",
2991
+ projectFolder: (p) => `project ${p}`,
2992
+ model: "model",
2993
+ defaultModel: "default",
2994
+ modelKey: "model",
2995
+ rolesHint: "A counter at 3 gives build, build-2, build-3, the same as the first.",
2996
+ pairs: "in pairs",
2997
+ all: "each seat \u2192 all",
2998
+ pairsHint: "build-2 \u2192 review-2",
2999
+ allHint: "each build \u2192 every review",
3000
+ save: "save in my specs",
3001
+ notScaled: "one seat per role: nothing more to delegate",
3002
+ loading: "Reading the specs\u2026",
3003
+ noSpecs: "No spec on this machine.",
3004
+ daemonStopped: "Start the OpenRig daemon first (D on the OpenRig screen).",
3005
+ noCheck: "p checks the launch (rig up --plan) without starting anything.",
3006
+ checking: "Checking\u2026",
3007
+ launching: "Launching\u2026",
3008
+ passes: (seats) => `\u2713 the plan passes \xB7 ${seats} ${plural2(seats, "seat", "seats")}`,
3009
+ planStatus: (s) => `plan: ${s}`,
3010
+ warning: (text5, seats) => seats ? `${text5} (${seats})` : text5,
3011
+ launched: (name, n) => `Rig \u201C${name}\u201D launched \xB7 ${n} ${plural2(n, "seat", "seats")}.`,
3012
+ check: "check (--plan)",
3013
+ launch: (n) => `launch ${n} ${plural2(n, "seat", "seats")}`,
3014
+ missing: { spec: "Pick a spec.", cwd: "Give the working folder.", name: "The rig\u2019s name: letters, digits, . _ - (no leading -).", seats: "At least one seat." },
3015
+ why: {
3016
+ busy: "Another OpenRig action runs on this machine.",
3017
+ cwd_missing: "This folder does not exist on the machine.",
3018
+ spec_not_found: "This spec is no longer in the library.",
3019
+ daemon_stopped: "Start the OpenRig daemon first (D on the OpenRig screen).",
3020
+ not_installed: "OpenRig is not installed on this machine.",
3021
+ offline: "Machine offline.",
3022
+ outdated: "This machine\u2019s Ledge daemon cannot launch a rig: update it."
3023
+ }
3024
+ },
3025
+ rigSeats: {
3026
+ title: (rig, machine) => `Seats \xB7 ${rig} \xB7 ${machine}`,
3027
+ key: "seats",
3028
+ stays: (now2) => `The rig keeps running. Now: ${now2}.`,
3029
+ role: "role",
3030
+ count: "number",
3031
+ delegationKey: "delegation",
3032
+ apply: "apply",
3033
+ cancel: "cancel",
3034
+ pairs: "delegation in pairs",
3035
+ all: "each added \u2192 all",
3036
+ linksPairs: "links added in pairs",
3037
+ linksAll: "links added to all",
3038
+ nothing: "Change a number: - +",
3039
+ notRunning: "Only a running rig changes seats.",
3040
+ busy: (seat) => `${seat} is working: its work in progress stops.`,
3041
+ applying: "applying\u2026",
3042
+ done: (added, removed, links) => `+${added} \u2212${removed} seats${links !== void 0 ? `, ${links} ${plural2(links, "link", "links")}` : ""}`,
3043
+ linksUnsupported: "this OpenRig cannot link seats added to a running rig (before 0.7)",
3044
+ why: {
3045
+ rig_not_running: "The rig is no longer running.",
3046
+ rig_root_unknown: "Cannot copy a seat: the rig\u2019s agents cannot be found.",
3047
+ busy: "Another OpenRig action runs on this machine.",
3048
+ not_installed: "OpenRig is not installed on this machine.",
3049
+ offline: "Machine offline.",
3050
+ outdated: "This machine\u2019s Ledge daemon cannot change a rig\u2019s seats: update it."
3051
+ }
3052
+ },
3010
3053
  small: {
3011
3054
  tooSmall: (w, h) => `Window too small: ${w}\xD7${h}`,
3012
3055
  atLeast: (w, h) => `needs at least ${w}\xD7${h}`,
3013
- waits: (name, question) => question ? `${name} has a question` : `${name} waits for your approval`,
3056
+ waits: (name, question2) => question2 ? `${name} has a question` : `${name} waits for your approval`,
3014
3057
  kept: "draft kept"
3015
3058
  },
3016
3059
  cli: { notTty: "ledge tui: needs a terminal (stdin and stdout must be a TTY)", movedToTab: "ledge tui opens in the Ledge tab.", loadingProfile: "Windows Terminal is loading the Ledge profile\u2026" }
@@ -3029,37 +3072,33 @@ var useI18n = () => useContext2(I18nContext);
3029
3072
  // src/tui/model/overview.ts
3030
3073
  var listed = (sessions) => sessions.filter((s) => s.state !== "exited");
3031
3074
  var machineOf = (s) => s.machineId ?? splitId(s.id)?.machine ?? "local";
3032
- function overview(sessions, projects, teams = [], tasks = []) {
3075
+ function overview(sessions, projects) {
3033
3076
  const live = listed(sessions);
3034
3077
  const yours = live.filter(isPending).sort((a, b2) => a.lastActivityAt.localeCompare(b2.lastActivityAt) || a.id.localeCompare(b2.id));
3035
3078
  const nodes = /* @__PURE__ */ new Map();
3036
3079
  const known2 = new Map((projects?.projects ?? []).map((p) => [p.id, p]));
3037
- for (const p of projects?.projects ?? []) nodes.set(`p:${p.id}`, { key: `p:${p.id}`, label: p.name, color: p.color, branches: [], count: 0, teams: [] });
3038
- const rooms = /* @__PURE__ */ new Map();
3039
- for (const team of teams) {
3040
- if (team.state === "dissolved") continue;
3041
- const pid = projects ? projectOf({ machineId: team.machine, cwd: team.cwd }, projects) ?? team.project : team.project;
3042
- const node2 = pid ? nodes.get(`p:${pid}`) : void 0;
3043
- if (!node2) continue;
3044
- const mine = tasks.filter((x) => x.team === team.id && x.giver === LEDGE_GIVER);
3045
- const row2 = { key: `${node2.key}|team:${team.id}`, team, sessions: [], done: mine.filter((x) => x.state === "done").length, given: mine.length };
3046
- node2.teams.push(row2);
3047
- rooms.set(team.id, row2);
3048
- }
3080
+ for (const p of projects?.projects ?? []) nodes.set(`p:${p.id}`, { key: `p:${p.id}`, label: p.name, color: p.color, branches: [], count: 0 });
3081
+ const rigNodes = /* @__PURE__ */ new Map();
3049
3082
  for (const s of live) {
3083
+ if (s.rig) {
3084
+ const kind = s.rig.name === "kernel" ? "kernel" : "rig";
3085
+ const key2 = kind === "kernel" ? "g:kernel" : `g:rig:${s.rig.name}`;
3086
+ let node3 = rigNodes.get(key2);
3087
+ if (!node3) {
3088
+ node3 = { key: key2, label: kind === "kernel" ? "Kernel" : s.rig.name, kind, branches: [{ branch: "", sessions: [] }], count: 0 };
3089
+ rigNodes.set(key2, node3);
3090
+ }
3091
+ node3.branches[0].sessions.push(s);
3092
+ node3.count += 1;
3093
+ continue;
3094
+ }
3050
3095
  const pid = projects ? projectOf({ machineId: machineOf(s), cwd: s.cwd }, projects) : null;
3051
3096
  const key = pid && known2.has(pid) ? `p:${pid}` : `r:${machineOf(s)}:${s.repoPath ?? s.cwd}`;
3052
3097
  let node2 = nodes.get(key);
3053
3098
  if (!node2) {
3054
- node2 = { key, label: s.repo ?? s.project, branches: [], count: 0, teams: [] };
3099
+ node2 = { key, label: s.repo ?? s.project, branches: [], count: 0 };
3055
3100
  nodes.set(key, node2);
3056
3101
  }
3057
- const seat = s.team ? rooms.get(s.team) : void 0;
3058
- if (seat && node2.teams.includes(seat)) {
3059
- seat.sessions.push(s);
3060
- node2.count += 1;
3061
- continue;
3062
- }
3063
3102
  const branch = s.branch ?? "-";
3064
3103
  let row2 = node2.branches.find((b2) => b2.branch === branch);
3065
3104
  if (!row2) {
@@ -3069,8 +3108,9 @@ function overview(sessions, projects, teams = [], tasks = []) {
3069
3108
  row2.sessions.push(s);
3070
3109
  node2.count += 1;
3071
3110
  }
3072
- const tree2 = [...nodes.values()].filter((n) => n.count > 0 || n.teams.length > 0);
3073
- const order2 = tree2.flatMap((n) => [...n.branches.flatMap((b2) => b2.sessions.map((s) => s.id)), ...n.teams.flatMap((x) => x.sessions.map((s) => s.id))]);
3111
+ const groups = [...rigNodes.values()].sort((a, b2) => a.kind === b2.kind ? a.label.localeCompare(b2.label) : a.kind === "kernel" ? 1 : -1);
3112
+ const tree2 = [...[...nodes.values()].filter((n) => n.count > 0), ...groups];
3113
+ const order2 = tree2.flatMap((n) => n.branches.flatMap((b2) => b2.sessions.map((s) => s.id)));
3074
3114
  return { yours, tree: tree2, order: order2, machines: new Set(live.map(machineOf)).size };
3075
3115
  }
3076
3116
  var helperCount = (s) => liveHelpers(s).length;
@@ -3096,13 +3136,13 @@ function exchanges(sessions, max, tasks = [], ledge = "Ledge") {
3096
3136
  const byId = new Map(sessions.map((s) => [s.id, s]));
3097
3137
  const bare = (id) => splitId(id)?.id ?? id;
3098
3138
  const find2 = (id) => byId.get(id) ?? sessions.find((x) => bare(x.id) === bare(id));
3099
- const nameOf = (id, then) => id === LEDGE_GIVER ? ledge : find2(id)?.name ?? then;
3139
+ const nameOf2 = (id, then) => id === LEDGE_GIVER ? ledge : find2(id)?.name ?? then;
3100
3140
  return [...sessions.flatMap((s) => {
3101
3141
  const m = s.lastMessage;
3102
3142
  if (!m) return [];
3103
3143
  const peer = m.peer ? find2(m.peer) : void 0;
3104
3144
  return [{ at: m.at, from: s.name, to: peer?.name ?? m.to, summary: m.summary, kind: "message" }];
3105
- }), ...tasks.flatMap((x) => transmissions(x).map((tr) => ({ at: tr.at, from: nameOf(tr.from, tr.fromName), to: nameOf(tr.to, tr.toName), summary: x.title, kind: "task" })))].sort((a, b2) => b2.at.localeCompare(a.at)).slice(0, max);
3145
+ }), ...tasks.flatMap((x) => transmissions(x).map((tr) => ({ at: tr.at, from: nameOf2(tr.from, tr.fromName), to: nameOf2(tr.to, tr.toName), summary: x.title, kind: "task" })))].sort((a, b2) => b2.at.localeCompare(a.at)).slice(0, max);
3106
3146
  }
3107
3147
  var nearContext = (sessions, near) => sessions.filter((s) => s.state !== "exited" && !s.archivedAt && (s.handover ? s.handover.state === "failed" : contextNear(s.context, near)));
3108
3148
  var closedLedgeTasks = (tasks) => tasks.filter((x) => x.giver === LEDGE_GIVER && taskClosed(x) && !x.seen);
@@ -3274,9 +3314,9 @@ var stateColors = (p) => ({
3274
3314
  var STATE_COLOR = stateColors(C);
3275
3315
  var current = "dark";
3276
3316
  var tone = () => current;
3277
- function setTone(next2) {
3278
- current = next2;
3279
- Object.assign(C, PALETTES[next2]);
3317
+ function setTone(next3) {
3318
+ current = next3;
3319
+ Object.assign(C, PALETTES[next3]);
3280
3320
  Object.assign(STATE_COLOR, stateColors(C));
3281
3321
  }
3282
3322
  var projectColor = (color2) => color2 ? PROJECT_COLORS[color2] : C.dim;
@@ -3534,7 +3574,7 @@ function visitParents(tree2, test, visitor, reverse) {
3534
3574
  check = test;
3535
3575
  }
3536
3576
  const is2 = convert(check);
3537
- const step2 = reverse ? -1 : 1;
3577
+ const step3 = reverse ? -1 : 1;
3538
3578
  factory(tree2, void 0, [])();
3539
3579
  function factory(node2, index2, parents) {
3540
3580
  const value = (
@@ -3571,7 +3611,7 @@ function visitParents(tree2, test, visitor, reverse) {
3571
3611
  node2
3572
3612
  );
3573
3613
  if (nodeAsParent.children && result[0] !== SKIP) {
3574
- offset = (reverse ? nodeAsParent.children.length : -1) + step2;
3614
+ offset = (reverse ? nodeAsParent.children.length : -1) + step3;
3575
3615
  grandparents = parents.concat(nodeAsParent);
3576
3616
  while (offset > -1 && offset < nodeAsParent.children.length) {
3577
3617
  const child = nodeAsParent.children[offset];
@@ -3579,7 +3619,7 @@ function visitParents(tree2, test, visitor, reverse) {
3579
3619
  if (subresult[0] === EXIT) {
3580
3620
  return subresult;
3581
3621
  }
3582
- offset = typeof subresult[1] === "number" ? subresult[1] : offset + step2;
3622
+ offset = typeof subresult[1] === "number" ? subresult[1] : offset + step3;
3583
3623
  }
3584
3624
  }
3585
3625
  }
@@ -8222,9 +8262,9 @@ function tokenizeAutolink(effects, ok3, nok) {
8222
8262
  }
8223
8263
  function emailValue(code3) {
8224
8264
  if ((code3 === 45 || asciiAlphanumeric(code3)) && size++ < 63) {
8225
- const next2 = code3 === 45 ? emailValue : emailLabel;
8265
+ const next3 = code3 === 45 ? emailValue : emailLabel;
8226
8266
  effects.consume(code3);
8227
- return next2;
8267
+ return next3;
8228
8268
  }
8229
8269
  return nok(code3);
8230
8270
  }
@@ -11490,11 +11530,11 @@ function tokenizeTable(effects, ok3, nok) {
11490
11530
  }
11491
11531
  }
11492
11532
  const tail = index2 > -1 ? self.events[index2][1].type : null;
11493
- const next2 = tail === "tableHead" || tail === "tableRow" ? bodyRowStart : headRowBefore;
11494
- if (next2 === bodyRowStart && self.parser.lazy[self.now().line]) {
11533
+ const next3 = tail === "tableHead" || tail === "tableRow" ? bodyRowStart : headRowBefore;
11534
+ if (next3 === bodyRowStart && self.parser.lazy[self.now().line]) {
11495
11535
  return nok(code3);
11496
11536
  }
11497
- return next2(code3);
11537
+ return next3(code3);
11498
11538
  }
11499
11539
  function headRowBefore(code3) {
11500
11540
  effects.enter("tableHead");
@@ -12856,9 +12896,9 @@ function preprocess() {
12856
12896
  break;
12857
12897
  }
12858
12898
  case 9: {
12859
- const next2 = Math.ceil(column / 4) * 4;
12899
+ const next3 = Math.ceil(column / 4) * 4;
12860
12900
  chunks.push(-2);
12861
- while (column++ < next2) {
12901
+ while (column++ < next3) {
12862
12902
  chunks.push(-1);
12863
12903
  }
12864
12904
  break;
@@ -13732,8 +13772,8 @@ function trough() {
13732
13772
  if (typeof callback !== "function") {
13733
13773
  throw new TypeError("Expected function as last argument, not " + callback);
13734
13774
  }
13735
- next2(null, ...values);
13736
- function next2(error, ...output) {
13775
+ next3(null, ...values);
13776
+ function next3(error, ...output) {
13737
13777
  const fn = fns[++middlewareIndex];
13738
13778
  let index2 = -1;
13739
13779
  if (error) {
@@ -13747,7 +13787,7 @@ function trough() {
13747
13787
  }
13748
13788
  values = output;
13749
13789
  if (fn) {
13750
- wrap(fn, next2)(...output);
13790
+ wrap(fn, next3)(...output);
13751
13791
  } else {
13752
13792
  callback(null, ...output);
13753
13793
  }
@@ -15401,6 +15441,11 @@ function fit(s, n) {
15401
15441
  return cps.length > n ? `${cps.slice(0, Math.max(0, n - 1)).join("")}\u2026` : s + " ".repeat(n - cps.length);
15402
15442
  }
15403
15443
 
15444
+ // src/tui/model/rigSeat.ts
15445
+ var rigTag = (s) => s.rig ? `[${s.rig.name}] ` : "";
15446
+ var rigRole = (s) => s.rig ? s.rig.role ?? s.rig.seat : "";
15447
+ var seatSecond = (s, rest) => s.rig ? [rigRole(s), rest].filter(Boolean).join(" \xB7 ") : rest;
15448
+
15404
15449
  // src/tui/ui/now.ts
15405
15450
  import { useSyncExternalStore as useSyncExternalStore4 } from "react";
15406
15451
  var listeners = /* @__PURE__ */ new Set();
@@ -15534,10 +15579,10 @@ function fieldLines(text5, cursor, width, o) {
15534
15579
  const at = cursor !== null && cursor >= r.a && cursor < r.b ? cursor : -1;
15535
15580
  if (at < 0) line.push({ text: text5.slice(r.a, r.b), color: o.color });
15536
15581
  else {
15537
- const next2 = after({ text: text5, cursor: at });
15582
+ const next3 = after({ text: text5, cursor: at });
15538
15583
  if (at > r.a) line.push({ text: text5.slice(r.a, at), color: o.color });
15539
- line.push(inv(text5.slice(at, Math.min(next2, r.b))));
15540
- if (next2 < r.b) line.push({ text: text5.slice(next2, r.b), color: o.color });
15584
+ line.push(inv(text5.slice(at, Math.min(next3, r.b))));
15585
+ if (next3 < r.b) line.push({ text: text5.slice(next3, r.b), color: o.color });
15541
15586
  }
15542
15587
  if (cursor === r.b && r.last) line.push(inv(" "));
15543
15588
  return line.filter((s) => s.text !== "" || s === line[0]);
@@ -15655,11 +15700,11 @@ function Field({ value, onChange, onSubmit, onEscape, onBrowse, history = [], ac
15655
15700
  }
15656
15701
  };
15657
15702
  const show = (b2, i) => {
15658
- const next2 = b2 && i !== void 0 ? { ...b2, i } : b2;
15659
- const text5 = next2 ? next2.items[next2.i] : st.current.f.text;
15660
- st.current = { f: { text: text5, cursor: text5.length }, browse: next2, pasted: 0 };
15703
+ const next3 = b2 && i !== void 0 ? { ...b2, i } : b2;
15704
+ const text5 = next3 ? next3.items[next3.i] : st.current.f.text;
15705
+ st.current = { f: { text: text5, cursor: text5.length }, browse: next3, pasted: 0 };
15661
15706
  render2();
15662
- onBrowse?.(inHistory(next2));
15707
+ onBrowse?.(inHistory(next3));
15663
15708
  };
15664
15709
  useInput((input, key) => {
15665
15710
  const { f: f2, browse: browse2 } = st.current;
@@ -15877,6 +15922,10 @@ function SessionCard({ s, note }) {
15877
15922
  " ",
15878
15923
  t.state[state2]
15879
15924
  ] }),
15925
+ s.rig ? /* @__PURE__ */ jsxs5(Text5, { color: C.blue, children: [
15926
+ " ",
15927
+ t.overview.rigSeat(s.rig.name, s.rig.role ?? s.rig.seat)
15928
+ ] }) : null,
15880
15929
  /* @__PURE__ */ jsxs5(Text5, { color: C.dim, children: [
15881
15930
  " ",
15882
15931
  TOOL_NAME[s.tool],
@@ -15954,6 +16003,7 @@ function Tail({ lines, color: color2, empty: empty3, emptyColor }) {
15954
16003
  // src/tui/screens/Overview.tsx
15955
16004
  import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
15956
16005
  var WIDE = 100;
16006
+ var folderOf = (cwd) => (cwd ?? "").split("/").filter(Boolean).at(-1) ?? "";
15957
16007
  function windowed(rows, selectedKey, height) {
15958
16008
  if (rows.length <= height) return [...rows];
15959
16009
  const at = Math.max(0, rows.findIndex((r) => r.key === selectedKey));
@@ -15966,9 +16016,8 @@ function Overview() {
15966
16016
  const { columns, rows: height } = useScreenSize();
15967
16017
  const wide = columns >= WIDE;
15968
16018
  const left2 = wide ? Math.min(64, Math.max(44, Math.floor(columns * 0.42))) : columns;
15969
- const teams = useTeams(tui.client);
15970
16019
  const tasks = useTasks(tui.client);
15971
- const view = useMemo2(() => overview(d.sessions, projects, teams ?? [], tasks), [d.sessions, projects, teams, tasks]);
16020
+ const view = useMemo2(() => overview(d.sessions, projects), [d.sessions, projects]);
15972
16021
  const sel = selected?.id ?? null;
15973
16022
  const closed = closedLedgeTasks(tasks);
15974
16023
  const decisions = useMemo2(() => openDecisions(tasks), [tasks]);
@@ -15990,11 +16039,6 @@ function Overview() {
15990
16039
  };
15991
16040
  const threshold = useContextNear(tui.client);
15992
16041
  const near = useMemo2(() => nearContext(d.sessions, threshold), [d.sessions, threshold]);
15993
- const [tasksShown, setTasksShown] = useState3(true);
15994
- const [joining, setJoining] = useState3(null);
15995
- const node2 = selected ? view.tree.find((n) => n.branches.some((b2) => b2.sessions.includes(selected)) || n.teams.some((x) => x.sessions.includes(selected))) : void 0;
15996
- const seatTeam = selected?.team ? teams?.find((x) => x.id === selected.team && x.state !== "dissolved") : void 0;
15997
- const choices2 = node2?.teams.map((x) => x.team).filter((x) => x.state !== "dissolved" && x.id !== selected?.team) ?? [];
15998
16042
  useScreenKeys((input, key) => {
15999
16043
  if (deciding) {
16000
16044
  if (!deciding.confirm) {
@@ -16016,50 +16060,6 @@ function Overview() {
16016
16060
  if (decisions[0]) setDeciding({ id: decisions[0].id, draft: "", confirm: false });
16017
16061
  return true;
16018
16062
  }
16019
- if (joining) {
16020
- if (key.escape) {
16021
- setJoining(null);
16022
- return true;
16023
- }
16024
- if (input === "j" || key.downArrow) {
16025
- setJoining({ index: (joining.index + 1) % Math.max(1, choices2.length) });
16026
- return true;
16027
- }
16028
- if (input === "k" || key.upArrow) {
16029
- setJoining({ index: (joining.index + choices2.length - 1) % Math.max(1, choices2.length) });
16030
- return true;
16031
- }
16032
- if (key.return && selected && choices2[joining.index]) {
16033
- const team = choices2[joining.index];
16034
- const role = selected.role ?? team.seats.find((x) => x.sessionId === null)?.role;
16035
- setJoining(null);
16036
- if (!role) {
16037
- tui.toast({ text: t.overview.noTeamHere, tone: "info" });
16038
- return true;
16039
- }
16040
- tui.client.send({ type: "team.join", team: team.id, session: selected.id, role });
16041
- tui.toast({ text: t.toast.joined(selected.name, team.name), tone: "ok" });
16042
- return true;
16043
- }
16044
- return true;
16045
- }
16046
- if (input === "J" && selected) {
16047
- if (choices2.length) setJoining({ index: 0 });
16048
- else tui.toast({ text: t.overview.noTeamHere, tone: "info" });
16049
- return true;
16050
- }
16051
- if (input === "R") {
16052
- const resting = (seatTeam?.state === "resting" ? seatTeam : void 0) ?? node2?.teams.find((x) => x.team.state === "resting")?.team ?? view.tree.flatMap((n) => n.teams).find((x) => x.team.state === "resting")?.team;
16053
- if (resting) {
16054
- tui.client.send({ type: "team.resume", team: resting.id });
16055
- tui.toast({ text: t.toast.resumed(resting.name), tone: "ok" });
16056
- }
16057
- return true;
16058
- }
16059
- if (input === "T") {
16060
- setTasksShown((v) => !v);
16061
- return true;
16062
- }
16063
16063
  if (input === "H") {
16064
16064
  const s = (selected && selected.source === "launcher" && selected.state !== "exited" && selected.tool !== "omp" && (!selected.handover || selected.handover.state === "failed") ? selected : void 0) ?? near.find((x) => x.handover?.state !== "failed") ?? near[0];
16065
16065
  if (!s) return true;
@@ -16087,8 +16087,8 @@ function Overview() {
16087
16087
  return true;
16088
16088
  }
16089
16089
  if (key.tab) {
16090
- const next2 = nextWaiting(view.yours, sel);
16091
- if (next2) select(next2);
16090
+ const next3 = nextWaiting(view.yours, sel);
16091
+ if (next3) select(next3);
16092
16092
  return true;
16093
16093
  }
16094
16094
  if (key.return) {
@@ -16112,22 +16112,25 @@ function Overview() {
16112
16112
  }
16113
16113
  return false;
16114
16114
  });
16115
- const tree2 = view.tree.flatMap((n) => {
16116
- const teamed = n.teams.length > 0;
16117
- const common = n.branches.length > 0;
16115
+ const groupRows = (kind) => view.tree.filter((n) => n.kind === kind).flatMap((n) => [
16116
+ ...kind === "rig" ? [{ kind: "project", key: n.key, label: n.label, color: C.blue, count: n.count, rig: true, folder: folderOf(n.branches[0]?.sessions[0]?.cwd) }] : [],
16117
+ ...n.branches.flatMap((b2) => b2.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: kind === "rig" ? 2 : 0, group: kind })))
16118
+ ]);
16119
+ const rigRows = groupRows("rig"), kernelRows = groupRows("kernel");
16120
+ const tree2 = view.tree.filter((n) => !n.kind).flatMap((n) => {
16118
16121
  return [
16119
16122
  { kind: "project", key: n.key, label: n.label, color: projectColor(n.color), count: n.count },
16120
- ...teamed && common ? [{ kind: "common", key: `${n.key}|common` }] : [],
16121
- ...n.branches.flatMap((b2) => [{ kind: "branch", key: `${n.key}|${b2.branch}`, branch: b2.branch, indent: teamed ? 4 : 2 }, ...b2.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: teamed ? 2 : 0 }))]),
16122
- ...n.teams.flatMap((x) => [{ kind: "team", key: x.key, team: x.team, done: x.done, given: x.given }, ...x.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: 2 }))])
16123
+ ...n.branches.flatMap((b2) => [{ kind: "branch", key: `${n.key}|${b2.branch}`, branch: b2.branch, indent: 2 }, ...b2.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: 0 }))])
16123
16124
  ];
16124
16125
  });
16125
16126
  const yoursCount = view.yours.length + decisions.length + closed.length + near.length;
16126
16127
  const yoursHeight = Math.max(1, Math.min(yoursCount, 6));
16127
- const joinHeight = joining && selected ? Math.max(1, choices2.length) + 2 : 0;
16128
16128
  const answerLines = deciding?.confirm ? wrap2(deciding.draft.trim(), Math.max(10, columns - 4)) : [];
16129
16129
  const decideHeight = !deciding ? 0 : deciding.confirm ? Math.min(answerLines.length, Math.max(1, height - 12)) + 2 : fieldRows(columns, height) + 2;
16130
- const treeHeight = Math.max(3, height - 3 - (yoursHeight + 2) - joinHeight - decideHeight - 3);
16130
+ const allHeight = Math.max(3, height - 3 - (yoursHeight + 2) - decideHeight - 3);
16131
+ const kernelHeight = kernelRows.length ? Math.min(kernelRows.length, 5) : 0;
16132
+ const rigsHeight = rigRows.length ? Math.min(rigRows.length, Math.max(3, Math.floor(allHeight / 3))) : 0;
16133
+ const treeHeight = Math.max(3, allHeight - (rigsHeight ? rigsHeight + 2 : 0) - (kernelHeight ? kernelHeight + 2 : 0));
16131
16134
  const inner = left2 - 4, nameW = 7;
16132
16135
  const stateW = Math.max(...Object.values(t.state).map((x) => x.length)) + 1;
16133
16136
  const topicW = Math.max(6, inner - 6 - nameW - stateW - 3);
@@ -16140,8 +16143,13 @@ function Overview() {
16140
16143
  " "
16141
16144
  ] }),
16142
16145
  /* @__PURE__ */ jsx10(Text7, { color: C.fg, bold: true, children: fit(s.name, nameW) }),
16143
- /* @__PURE__ */ jsx10(Text7, { color: C.dim, children: fit(s.repo ?? s.project, 9) }),
16144
- /* @__PURE__ */ jsx10(Text7, { color: STATE_COLOR[st], children: fit(oneLine(s.pending?.text ?? ""), askW) }),
16146
+ s.rig ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
16147
+ /* @__PURE__ */ jsx10(Text7, { children: " " }),
16148
+ /* @__PURE__ */ jsx10(Text7, { color: C.blue, children: rigTag(s) }),
16149
+ /* @__PURE__ */ jsx10(Text7, { color: C.dim, children: seatSecond(s, "") }),
16150
+ /* @__PURE__ */ jsx10(Text7, { children: " " })
16151
+ ] }) : /* @__PURE__ */ jsx10(Text7, { color: C.dim, children: fit(s.repo ?? s.project, 9) }),
16152
+ /* @__PURE__ */ jsx10(Text7, { color: STATE_COLOR[st], children: fit(oneLine(s.pending?.text ?? ""), s.rig ? Math.max(6, askW + 9 - rigTag(s).length - seatSecond(s, "").length - 4) : askW) }),
16145
16153
  /* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
16146
16154
  " ",
16147
16155
  /* @__PURE__ */ jsx10(Ago, { iso: s.lastActivityAt })
@@ -16168,7 +16176,7 @@ function Overview() {
16168
16176
  s.handover?.state === "failed" ? g.fail : "\u25D4",
16169
16177
  " "
16170
16178
  ] }),
16171
- /* @__PURE__ */ jsx10(Text7, { color: C.fg, children: s.handover?.state === "failed" ? t.overview.reliefFailed(s.name, s.handover.reason ?? "?") : t.overview.near(`${s.name}${s.role ? ` @${s.role}` : ""}`, Math.round((s.context?.used ?? 0) / (s.context?.window ?? 1) * 100)) })
16179
+ /* @__PURE__ */ jsx10(Text7, { color: C.fg, children: s.handover?.state === "failed" ? t.overview.reliefFailed(s.name, s.handover.reason ?? "?") : t.overview.near(s.name, Math.round((s.context?.used ?? 0) / (s.context?.window ?? 1) * 100)) })
16172
16180
  ] }, `near:${s.id}`))] });
16173
16181
  const treeBox = /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.projects, flexGrow: 1, children: tree2.length === 0 ? /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.noSession }) : windowed(tree2, sel, treeHeight).map((r) => r.kind === "project" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
16174
16182
  /* @__PURE__ */ jsx10(Text7, { color: r.color, children: g.project }),
@@ -16176,32 +16184,32 @@ function Overview() {
16176
16184
  /* @__PURE__ */ jsx10(Text7, { color: C.fg, bold: true, children: r.label }),
16177
16185
  " ",
16178
16186
  /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.sessions(r.count) })
16179
- ] }, r.key) : r.kind === "common" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", color: C.faint, children: [
16180
- " ",
16181
- g.common,
16182
- " ",
16183
- t.overview.common
16184
- ] }, r.key) : r.kind === "team" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
16185
- /* @__PURE__ */ jsx10(Text7, { children: " " }),
16186
- /* @__PURE__ */ jsx10(Text7, { color: C.coral, children: g.team }),
16187
- " ",
16188
- /* @__PURE__ */ jsx10(Text7, { color: r.team.state === "resting" ? C.dim : C.fg, bold: true, children: r.team.name }),
16189
- " ",
16190
- /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: r.team.state === "resting" ? t.overview.resting : t.overview.teamLine(r.done, r.given) })
16191
16187
  ] }, r.key) : r.kind === "branch" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", color: C.faint, children: [
16192
16188
  " ".repeat(r.indent),
16193
16189
  g.branch,
16194
16190
  " ",
16195
16191
  r.branch
16196
- ] }, r.key) : /* @__PURE__ */ jsx10(SessionRow, { s: r.s, on: r.s.id === sel, nameW, topicW: Math.max(6, topicW - r.indent - (r.s.role ? r.s.role.length + 2 : 0)), stateW, indent: r.indent }, r.key)) });
16192
+ ] }, r.key) : /* @__PURE__ */ jsx10(SessionRow, { s: r.s, on: r.s.id === sel, nameW, topicW: Math.max(6, topicW - r.indent), stateW, indent: r.indent }, r.key)) });
16193
+ const groupBox = (title2, rows, rowsHeight) => rows.length === 0 ? null : /* @__PURE__ */ jsx10(TitledBox, { title: title2, height: rowsHeight + 2, children: windowed(rows, sel, rowsHeight).map((r) => r.kind === "project" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
16194
+ /* @__PURE__ */ jsx10(Text7, { color: r.color, children: "\u2B21" }),
16195
+ " ",
16196
+ /* @__PURE__ */ jsx10(Text7, { color: C.fg, bold: true, children: r.label }),
16197
+ r.folder ? /* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
16198
+ " ",
16199
+ r.folder
16200
+ ] }) : null,
16201
+ /* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
16202
+ " ",
16203
+ t.overview.sessions(r.count)
16204
+ ] })
16205
+ ] }, r.key) : r.kind === "session" ? /* @__PURE__ */ jsx10(SessionRow, { s: r.s, on: r.s.id === sel, nameW, topicW: Math.max(6, topicW - r.indent), stateW, indent: r.indent, group: r.group }, r.key) : null) });
16206
+ const rigsBox = groupBox(t.overview.rigsGroup, rigRows, rigsHeight);
16207
+ const kernelBox = groupBox(t.overview.kernelGroup, kernelRows, kernelHeight);
16197
16208
  const keys = deciding?.confirm ? [["y", t.keys.sendAnswer], ["n esc", t.keys.edit]] : deciding ? [["\u21B5", t.keys.review], ["alt+\u21B5", t.keys.newLine], ["esc", t.keys.cancel]] : [
16198
16209
  ["j k", t.keys.navigate],
16199
16210
  ["\u21B5", t.keys.open],
16200
16211
  ...view.yours.length ? [["tab", t.keys.next], ["y n", t.keys.answer]] : [],
16201
16212
  ["a", t.keys.attach],
16202
- ...seatTeam ? [["T", t.keys.tasks]] : [],
16203
- ...choices2.length ? [["J", t.keys.join]] : [],
16204
- ...view.tree.some((n) => n.teams.some((x) => x.team.state === "resting")) ? [["R", t.keys.resume]] : [],
16205
16213
  ...decisions.length ? [["D", t.keys.decide]] : [],
16206
16214
  ...near.length ? [["H", t.keys.handover]] : [],
16207
16215
  ["/", t.keys.ask],
@@ -16210,36 +16218,17 @@ function Overview() {
16210
16218
  ["?", t.keys.help],
16211
16219
  ["q", t.keys.quit]
16212
16220
  ];
16213
- const teamTasks = seatTeam ? tasks.filter((x) => x.team === seatTeam.id && (!taskClosed(x) || x.updatedAt.slice(0, 10) === (/* @__PURE__ */ new Date()).toISOString().slice(0, 10))).slice(-4) : [];
16214
16221
  return /* @__PURE__ */ jsxs6(Frame, { crumb: tui.machines.map((m) => `${m.label} ${m.ready ? g.machine : "\u25CB"}`).join(" "), keys, children: [
16215
16222
  /* @__PURE__ */ jsxs6(Box8, { flexGrow: 1, children: [
16216
16223
  /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", width: left2, flexShrink: 0, children: [
16217
16224
  yoursBox,
16218
- joining && selected ? /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.joinWhich(selected.name), color: C.coral, titleColor: C.coral, height: joinHeight, children: choices2.map((x, i) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: i === joining.index ? C.sel : void 0, children: [
16219
- /* @__PURE__ */ jsx10(Text7, { color: C.coral, children: g.team }),
16220
- " ",
16221
- x.name
16222
- ] }, x.id)) }) : null,
16223
- treeBox
16225
+ treeBox,
16226
+ rigsBox,
16227
+ kernelBox
16224
16228
  ] }),
16225
16229
  wide ? /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", flexGrow: 1, children: [
16226
16230
  selected ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
16227
- /* @__PURE__ */ jsx10(TitledBox, { title: `${selected.name} \xB7 ${oneLine(selected.topic ?? selected.title)}`, color: isPending(selected) ? C.red : C.line, titleColor: isPending(selected) ? C.red : C.dim, children: /* @__PURE__ */ jsx10(SessionCard, { s: selected, note: true }) }),
16228
- seatTeam && tasksShown ? /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.tasks(`${g.team} ${seatTeam.name}`), height: Math.max(3, teamTasks.length + 2), children: teamTasks.length === 0 ? /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.noTask }) : teamTasks.map((x) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
16229
- /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: x.id }),
16230
- " ",
16231
- /* @__PURE__ */ jsx10(Text7, { color: C.fg, children: x.title }),
16232
- " ",
16233
- /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
16234
- x.giverName,
16235
- " ",
16236
- g.arrow,
16237
- " ",
16238
- x.ownerName
16239
- ] }),
16240
- " ",
16241
- /* @__PURE__ */ jsx10(Text7, { color: x.state === "done" ? C.green : x.state === "taken" ? C.blue : C.faint, children: t.ask.taskState[x.state] })
16242
- ] }, x.id)) }) : null,
16231
+ /* @__PURE__ */ jsx10(TitledBox, { title: `${selected.name}${selected.rig ? ` \xB7 ${t.overview.rigSeat(selected.rig.name, selected.rig.role ?? selected.rig.seat)}` : ""} \xB7 ${oneLine(selected.topic ?? selected.title)}`, color: isPending(selected) ? C.red : C.line, titleColor: isPending(selected) ? C.red : C.dim, children: /* @__PURE__ */ jsx10(SessionCard, { s: selected, note: true }) }),
16243
16232
  /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.terminal(paneName(selected)), flexGrow: 1, children: /* @__PURE__ */ jsx10(Tail, { lines: d.previews.get(selected.id) ?? [], color: C.dim, empty: t.overview.noPreview, emptyColor: C.faint }) })
16244
16233
  ] }) : /* @__PURE__ */ jsx10(TitledBox, { title: t.tabs.session, flexGrow: 1, children: /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.session.noSession }) }),
16245
16234
  /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.exchanges, height: 5, children: (() => {
@@ -16279,7 +16268,7 @@ function Overview() {
16279
16268
  ) : null
16280
16269
  ] });
16281
16270
  }
16282
- function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0 }) {
16271
+ function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0, group }) {
16283
16272
  const { t, g } = useTui();
16284
16273
  const st = botState(s);
16285
16274
  const n = helperCount(s);
@@ -16291,14 +16280,19 @@ function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0 }) {
16291
16280
  " "
16292
16281
  ] }),
16293
16282
  /* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.dim : C.fg, bold: true, children: fit(s.name, nameW) }),
16294
- s.role ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
16295
- "@",
16296
- s.role,
16297
- " "
16298
- ] }) : null,
16299
- /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
16300
- fit(oneLine(s.topic ?? s.title), topicW),
16283
+ group ? /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
16284
+ " ",
16285
+ fit(group === "kernel" ? `${(s.rig?.role ?? "").replace(/\.(agent|lead|worker)$/, "").padEnd(11)}${s.tool}` : s.rig?.role ?? "", topicW - 1),
16301
16286
  " "
16287
+ ] }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
16288
+ s.rig ? /* @__PURE__ */ jsxs6(Text7, { color: C.blue, children: [
16289
+ " ",
16290
+ rigTag(s)
16291
+ ] }) : null,
16292
+ /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
16293
+ fit(seatSecond(s, oneLine(s.topic ?? s.title)), s.rig ? Math.max(0, topicW - rigTag(s).length - 1) : topicW),
16294
+ " "
16295
+ ] })
16302
16296
  ] }),
16303
16297
  /* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: fit(s.offline ? t.overview.offline : t.state[st], stateW).trimEnd() }),
16304
16298
  n > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
@@ -16329,9 +16323,9 @@ function useChips(put) {
16329
16323
  const { t, toast } = useTui();
16330
16324
  const [items, setItems] = useState4([]);
16331
16325
  const held = useRef8([]);
16332
- const commit = (next2) => {
16333
- held.current = next2;
16334
- setItems(next2);
16326
+ const commit = (next3) => {
16327
+ held.current = next3;
16328
+ setItems(next3);
16335
16329
  };
16336
16330
  const patch = (key, over) => commit(held.current.map((c) => c.key === key ? { ...c, ...over } : c));
16337
16331
  const reasonOf = (e) => {
@@ -16374,10 +16368,10 @@ function useChips(put) {
16374
16368
  }
16375
16369
  };
16376
16370
  }
16377
- function ChipsLine({ chips }) {
16371
+ function ChipsLine({ chips: chips2 }) {
16378
16372
  const { t } = useTui();
16379
- if (chips.length === 0) return null;
16380
- return /* @__PURE__ */ jsx11(Text8, { wrap: "truncate", children: chips.map((c, i) => /* @__PURE__ */ jsxs7(Text8, { children: [
16373
+ if (chips2.length === 0) return null;
16374
+ return /* @__PURE__ */ jsx11(Text8, { wrap: "truncate", children: chips2.map((c, i) => /* @__PURE__ */ jsxs7(Text8, { children: [
16381
16375
  i ? " " : "",
16382
16376
  /* @__PURE__ */ jsxs7(Text8, { color: c.state === "refused" ? C.red : C.dim, children: [
16383
16377
  "\u25A3 ",
@@ -16396,14 +16390,14 @@ function ChipsLine({ chips }) {
16396
16390
  ] }, c.key)) });
16397
16391
  }
16398
16392
  var EXT = { "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif", "image/webp": "webp" };
16399
- function usePaste2(chips) {
16393
+ function usePaste2(chips2) {
16400
16394
  const { readImage, toast, t } = useTui();
16401
16395
  const reading = useRef8(false);
16402
16396
  return () => {
16403
16397
  if (reading.current) return;
16404
16398
  reading.current = true;
16405
16399
  void readImage().then((got) => {
16406
- if (got) chips.add(got);
16400
+ if (got) chips2.add(got);
16407
16401
  else toast({ text: t.images.none, tone: "info" });
16408
16402
  }, () => toast({ text: t.images.none, tone: "info" })).finally(() => {
16409
16403
  reading.current = false;
@@ -16558,30 +16552,30 @@ function SessionView({ s }) {
16558
16552
  setAnchor({ key, offset: top - lineKeys.indexOf(key) });
16559
16553
  };
16560
16554
  const images = useMemo3(() => (items ? threadImages(items, unfolded) : []).map((ref) => ({ ref, route: { id: s.id } })), [items, unfolded, s.id]);
16561
- const chips = useChips((image2) => putImage(client, image2, { id: s.id }));
16562
- const paste = usePaste2(chips);
16555
+ const chips2 = useChips((image2) => putImage(client, image2, { id: s.id }));
16556
+ const paste = usePaste2(chips2);
16563
16557
  const openImage2 = useOpenImage();
16564
16558
  const machine = splitId(s.id)?.machine ?? "local";
16565
16559
  const canSwitch = machines.find((m) => m.id === machine)?.daemon?.modelSwitch === true;
16566
16560
  const send = (text5, { fromHistory }) => {
16567
- const withImages = mode === "reply" && !fromHistory && chips.items.length > 0;
16561
+ const withImages = mode === "reply" && !fromHistory && chips2.items.length > 0;
16568
16562
  if (!text5.trim() && !withImages) {
16569
16563
  setMode("read");
16570
16564
  return true;
16571
16565
  }
16572
- if (withImages && !chips.ready) return false;
16566
+ if (withImages && !chips2.ready) return false;
16573
16567
  if (withImages && p?.kind === "question") {
16574
16568
  toast({ text: t.images.dialog, tone: "info" });
16575
16569
  return false;
16576
16570
  }
16577
- const taken = withImages ? chips.clear() : [];
16571
+ const taken = withImages ? chips2.clear() : [];
16578
16572
  const back2 = (x) => {
16579
16573
  if (!kept.drafts.get(s.id)) setDraft(x);
16580
- if (taken.length) chips.putBack(taken);
16574
+ if (taken.length) chips2.putBack(taken);
16581
16575
  };
16582
16576
  const how = taken.length ? { attachments: taken.flatMap((c) => c.id ? [c.id] : []) } : void 0;
16583
16577
  if (!(mode === "note" ? actions.denyWithNote(s, text5.trim(), back2) : actions.reply(s, text5, back2, how))) {
16584
- if (taken.length) chips.putBack(taken);
16578
+ if (taken.length) chips2.putBack(taken);
16585
16579
  return false;
16586
16580
  }
16587
16581
  if (text5.trim()) kept.sent.set(s.id, [...kept.sent.get(s.id) ?? [], text5]);
@@ -16596,7 +16590,7 @@ function SessionView({ s }) {
16596
16590
  };
16597
16591
  useInput3((input, key) => {
16598
16592
  if (key.meta && input === "v") paste();
16599
- else if ((key.backspace || key.delete) && draft === "") chips.removeLast();
16593
+ else if ((key.backspace || key.delete) && draft === "") chips2.removeLast();
16600
16594
  }, { isActive: mode === "reply" });
16601
16595
  useScreenKeys((input, key) => {
16602
16596
  if (mode === "model") return true;
@@ -16682,7 +16676,7 @@ function SessionView({ s }) {
16682
16676
  if (input === "y" || input === "n" || input === "A" || /^[1-9]$/.test(input)) return actions.key(s, input);
16683
16677
  return false;
16684
16678
  });
16685
- const title2 = `${s.name} \xB7 ${oneLine(s.topic ?? s.title)} \xB7 ${TOOL_NAME[s.tool]} \xB7 ${modelLabel(s, t.session.defaultModel)}`;
16679
+ const title2 = `${s.name}${s.rig ? ` \xB7 ${t.overview.rigSeat(s.rig.name, s.rig.role ?? s.rig.seat)}` : ""} \xB7 ${oneLine(s.topic ?? s.title)} \xB7 ${TOOL_NAME[s.tool]} \xB7 ${modelLabel(s, t.session.defaultModel)}`;
16686
16680
  const showsThread = mode !== "model" && !terminal && !!items && lines.length > 0;
16687
16681
  const above = lines.length - view - up;
16688
16682
  const foot = up > 0 ? t.session.scrolled(above, up) : lines.length > view ? t.session.above(lines.length - view) : void 0;
@@ -16690,7 +16684,7 @@ function SessionView({ s }) {
16690
16684
  const editing = mode === "reply" || mode === "note";
16691
16685
  const body = rows - 2 - (lastToast ? 1 : 0);
16692
16686
  const inner = Math.max(20, columns - 4);
16693
- const below = (editing ? Math.min(fieldRows(columns, rows), layout(draft, Math.max(10, columns - 4) - 2).length) + 2 : 3) + (chips.items.length ? 1 : 0);
16687
+ const below = (editing ? Math.min(fieldRows(columns, rows), layout(draft, Math.max(10, columns - 4) - 2).length) + 2 : 3) + (chips2.items.length ? 1 : 0);
16694
16688
  let request2 = p ? ask(s, p, t, inner, 4, !editing) : null;
16695
16689
  const over = request2 ? askRows(request2, inner) + below - body : 0;
16696
16690
  if (p && request2 && over > 0) request2 = ask(s, p, t, inner, Math.max(1, 4 - over), !editing);
@@ -16712,7 +16706,7 @@ function SessionView({ s }) {
16712
16706
  }
16713
16707
  ) : terminal ? /* @__PURE__ */ jsx13(Tail, { lines: d.previews.get(s.id) ?? [], color: C.dim, empty: t.overview.noPreview, emptyColor: C.faint }) : !items ? /* @__PURE__ */ jsx13(Text10, { color: C.faint, children: t.session.loading }) : lines.length === 0 ? /* @__PURE__ */ jsx13(Text10, { color: C.faint, children: t.session.empty }) : /* @__PURE__ */ jsx13(Lines, { lines, up, onHeight: setView }) }) : null,
16714
16708
  p && request2 ? /* @__PURE__ */ jsx13(PendingBox, { s, a: request2 }) : null,
16715
- /* @__PURE__ */ jsx13(ChipsLine, { chips: chips.items }),
16709
+ /* @__PURE__ */ jsx13(ChipsLine, { chips: chips2.items }),
16716
16710
  mode === "reply" || mode === "note" ? /* @__PURE__ */ jsx13(
16717
16711
  Field,
16718
16712
  {
@@ -16782,7 +16776,7 @@ function actionRowOf(a) {
16782
16776
  if (a.vars !== void 0) row2.vars = a.vars;
16783
16777
  if (a.detail !== void 0 && a.action === "session.decide" && a.vars?.kind) row2.request = { kind: a.vars.kind, text: a.detail };
16784
16778
  else if (a.detail !== void 0) row2.detail = a.detail;
16785
- const target = a.vars?.session ?? a.vars?.owner ?? a.vars?.team;
16779
+ const target = a.vars?.session ?? a.vars?.owner;
16786
16780
  if (target !== void 0) row2.target = target;
16787
16781
  if (a.vars?.decision === "accept" || a.vars?.decision === "deny") row2.decision = a.vars.decision;
16788
16782
  if (a.action === "session.decide" && row2.decision !== "deny") row2.deny = { summary: a.summary };
@@ -16875,7 +16869,6 @@ function chatView(turns2, live, o) {
16875
16869
  out.push([{ text: "" }], ...markdownLines(text5, { width: width - 2, g, color: C.fg }).map((l) => [pad(2), ...l]));
16876
16870
  if (streaming !== void 0) out.push([pad(2), { text: g.caret, color: C.coral }]);
16877
16871
  } else out.push([{ text: ` ${t.ask.reading}`, color: C.faint }]);
16878
- if (turn.team?.teamId && turn.team.launchedAt) out.push([{ text: ` ${g.team} ${turn.team.name} \xB7 ${t.ask.teamLaunched(clock(turn.team.launchedAt))}`, color: C.green }]);
16879
16872
  for (const c of turn.tasks ?? []) if (c.taskId) out.push([{ text: ` ${c.title} \xB7 ${t.ask.given(c.ownerName ?? c.toName ?? c.to, c.taskId, t.ask.taskState[c.state ?? "open"])}`, color: C.green }]);
16880
16873
  out.push(...actionTrace(turn, t, g, width));
16881
16874
  for (const l of out) {
@@ -16887,7 +16880,7 @@ function chatView(turns2, live, o) {
16887
16880
  }
16888
16881
  function actionTrace(turn, t, g, width) {
16889
16882
  const a = t.ask.action, c = a.copy;
16890
- const cards = !!(turn.actions || turn.proposal || turn.relay || turn.closes || turn.team || turn.tasks || turn.projectProposal);
16883
+ const cards = !!(turn.actions || turn.proposal || turn.relay || turn.closes || turn.tasks || turn.projectProposal);
16891
16884
  if (turn.ignoredAt) {
16892
16885
  const shown = turn.actions ? cardPlan(turn.actions).shown : [];
16893
16886
  const sent = { done: shown.filter((s) => s === "done").length, denied: shown.filter((s) => s === "denied").length, failed: shown.filter((s) => s === "failed").length };
@@ -16916,7 +16909,6 @@ function openCards(turns2) {
16916
16909
  for (const turn of [...turns2].reverse()) {
16917
16910
  if (!turn.answeredAt || turn.ignoredAt) continue;
16918
16911
  if (turn.actions && !cardClosed(turn.actions)) out.push({ kind: "action", qid: turn.qid });
16919
- if (turn.team && !turn.team.teamId) out.push({ kind: "team", qid: turn.qid });
16920
16912
  const tasks = (turn.tasks ?? []).map((c, i) => c.taskId ? -1 : i).filter((i) => i >= 0);
16921
16913
  if (tasks.length) out.push({ kind: "task", qid: turn.qid, rows: tasks });
16922
16914
  const launch2 = proposalRows(turn).map((p, i) => p.launchedId ? -1 : i).filter((i) => i >= 0);
@@ -17111,8 +17103,8 @@ function Ask() {
17111
17103
  const [focus, setFocus] = useState9(-1);
17112
17104
  const [anchor, setAnchor] = useState9(null);
17113
17105
  const [view, setView] = useState9(10);
17114
- const chips = useChips(null);
17115
- const paste = usePaste2(chips);
17106
+ const chips2 = useChips(null);
17107
+ const paste = usePaste2(chips2);
17116
17108
  const openImage2 = useOpenImage();
17117
17109
  const [busy, setBusy] = useState9(null);
17118
17110
  const [rowOf2, setRowOf] = useState9({});
@@ -17126,7 +17118,7 @@ function Ask() {
17126
17118
  if (!s || !turn) return;
17127
17119
  if (turn.error) {
17128
17120
  sentAsk.current = null;
17129
- chips.putBack(s.taken);
17121
+ chips2.putBack(s.taken);
17130
17122
  setDraft(kept.drafts.get("ask") || s.text);
17131
17123
  } else if (turn.answeredAt) sentAsk.current = null;
17132
17124
  }, [d.chat]);
@@ -17135,7 +17127,7 @@ function Ask() {
17135
17127
  lines: Math.floor((height - 6) / Math.max(1, cards.length)),
17136
17128
  height: (text5, indent2) => text5.split("\n").reduce((n, l) => n + Math.max(1, wrapSegs([{ text: l, color: C.fg }], Math.max(10, columns - 8 - indent2)).length), 0)
17137
17129
  };
17138
- const planOf = (turn) => cardPlan(turn.actions ?? [], room);
17130
+ const planOf2 = (turn) => cardPlan(turn.actions ?? [], room);
17139
17131
  useEffect10(() => {
17140
17132
  if (focus >= cards.length) setFocus(cards.length ? cards.length - 1 : -1);
17141
17133
  }, [cards.length, focus]);
@@ -17152,7 +17144,7 @@ function Ask() {
17152
17144
  }, [confirming, cards]);
17153
17145
  useEffect10(() => {
17154
17146
  const turn = confirming && d.chat.find((x) => x.qid === confirming.qid);
17155
- if (turn && !confirming.rows.every((i) => appliable(planOf(turn).shown[i]))) setConfirming(null);
17147
+ if (turn && !confirming.rows.every((i) => appliable(planOf2(turn).shown[i]))) setConfirming(null);
17156
17148
  });
17157
17149
  const ignored = [...d.chat].filter((x) => x.ignoredAt).sort((a, b2) => b2.ignoredAt.localeCompare(a.ignoredAt))[0];
17158
17150
  useEffect10(() => {
@@ -17174,7 +17166,7 @@ function Ask() {
17174
17166
  const local = machines.find((m) => m.id === "local");
17175
17167
  const asking = d.chat.find((x) => !x.answeredAt && !x.error);
17176
17168
  const ask2 = (text5, { fromHistory }) => {
17177
- const images = fromHistory ? [] : chips.uploads();
17169
+ const images = fromHistory ? [] : chips2.uploads();
17178
17170
  if (!text5.trim() && images.length === 0) return true;
17179
17171
  if (!local?.ready) {
17180
17172
  toast({ text: t.ask.offline, tone: "info" });
@@ -17185,7 +17177,7 @@ function Ask() {
17185
17177
  daemonStore.beginTurn({ qid, ...asked2, steps: [] });
17186
17178
  client.send({ type: "chat.ask", qid, text: asked2.question, ...images.length ? { images } : {} });
17187
17179
  kept.asked.push(asked2);
17188
- if (images.length) sentAsk.current = { qid, taken: chips.clear(), text: asked2.question };
17180
+ if (images.length) sentAsk.current = { qid, taken: chips2.clear(), text: asked2.question };
17189
17181
  if (!fromHistory) setDraft("");
17190
17182
  return true;
17191
17183
  };
@@ -17194,21 +17186,13 @@ function Ask() {
17194
17186
  const turn = d.chat.find((x) => x.qid === card2.qid);
17195
17187
  if (!turn) return;
17196
17188
  if (card2.kind === "task") {
17197
- const row2 = card2.rows.find((r) => {
17198
- const c = turn.tasks[r];
17199
- return !c.to.startsWith("@") || c.team || turn.team?.teamId;
17200
- });
17201
- if (row2 === void 0) {
17202
- toast({ text: t.ask.waitTeam, tone: "info" });
17203
- return;
17204
- }
17189
+ const row2 = card2.rows[0];
17205
17190
  setBusy({ qid: card2.qid, turn, errorSeq: d.errors.at(-1)?.seq ?? 0, at: Date.now() });
17206
17191
  client.send({ type: "task.cmd", rid: randomUUID2(), by: LEDGE_GIVER, op: "give", qid: card2.qid, row: row2 });
17207
17192
  return;
17208
17193
  }
17209
17194
  setBusy({ qid: card2.qid, turn, errorSeq: d.errors.at(-1)?.seq ?? 0, at: Date.now() });
17210
- if (card2.kind === "team") client.send({ type: "team.launch", rid: randomUUID2(), qid: card2.qid });
17211
- else if (card2.kind === "launch") {
17195
+ if (card2.kind === "launch") {
17212
17196
  const first = proposalRows(turn)[card2.rows[0]];
17213
17197
  client.send({ type: "session.launch", tool: first.tool, cwd: first.cwd, ...first.prompt ? { prompt: first.prompt } : {}, qid: card2.qid, rows: card2.rows });
17214
17198
  } else if (card2.kind === "relay") client.send({ type: turn.relay?.status === "queued" ? "session.relay.cancel" : "session.relay", qid: card2.qid });
@@ -17223,7 +17207,7 @@ function Ask() {
17223
17207
  const actionKey = (qid, input, key) => {
17224
17208
  const turn = d.chat.find((x) => x.qid === qid);
17225
17209
  if (!turn?.actions) return false;
17226
- const p = planOf(turn);
17210
+ const p = planOf2(turn);
17227
17211
  const sel = selectedRow(p, rowOf2[qid]);
17228
17212
  const ask3 = (rows) => {
17229
17213
  if (rows.some((i) => p.rows[i].risk === "destructive")) setConfirming({ qid, rows });
@@ -17315,7 +17299,7 @@ function Ask() {
17315
17299
  return;
17316
17300
  }
17317
17301
  if ((key.backspace || key.delete) && l.focus === -1 && l.draft === "") {
17318
- chips.removeLast();
17302
+ chips2.removeLast();
17319
17303
  return;
17320
17304
  }
17321
17305
  if (key.meta && input === "o" && l.turnImages.length) {
@@ -17375,11 +17359,6 @@ function Ask() {
17375
17359
  act(cards[focus]);
17376
17360
  return true;
17377
17361
  }
17378
- if (input === "s" && cards[focus].kind === "team") {
17379
- const team = d.chat.find((x) => x.qid === cards[focus].qid)?.team;
17380
- if (team && !team.template && !team.savedPath) client.send({ type: "team.save", rid: randomUUID2(), qid: cards[focus].qid, name: team.name, seats: team.seats.map((x) => ({ role: x.role, tool: x.tool, ...x.model ? { model: x.model } : {}, ...x.effort ? { effort: x.effort } : {}, ...x.name ? { name: x.name } : {} })) });
17381
- return true;
17382
- }
17383
17362
  if (key.escape) {
17384
17363
  setFocus(-1);
17385
17364
  return true;
@@ -17393,23 +17372,23 @@ function Ask() {
17393
17372
  const turn = d.chat.find((x) => x.qid === qid);
17394
17373
  if (!turn?.actions) return [];
17395
17374
  if (confirming?.qid === qid) return [["y", t.keys.apply], ["esc", t.keys.cancel]];
17396
- const p = planOf(turn);
17375
+ const p = planOf2(turn);
17397
17376
  const sel = selectedRow(p, rowOf2[qid]);
17398
17377
  const r = p.rows[sel], s = p.shown[sel], many = p.rows.length > 1;
17399
- const next2 = ["tab", focus + 1 < cards.length ? t.keys.nextCard : t.keys.conversation];
17400
- if (s === "cut") return [["n", t.keys.deny], ["o", t.keys.openSession(r.target ?? "")], ["esc", t.keys.ignore], next2, ["ctrl+n", t.keys.newChat]];
17401
- if (!p.open.length) return [next2, ["ctrl+n", t.keys.newChat], ["esc", t.keys.back]];
17378
+ const next3 = ["tab", focus + 1 < cards.length ? t.keys.nextCard : t.keys.conversation];
17379
+ if (s === "cut") return [["n", t.keys.deny], ["o", t.keys.openSession(r.target ?? "")], ["esc", t.keys.ignore], next3, ["ctrl+n", t.keys.newChat]];
17380
+ if (!p.open.length) return [next3, ["ctrl+n", t.keys.newChat], ["esc", t.keys.back]];
17402
17381
  return [
17403
17382
  ["\u21B5", many ? t.keys.applyRow : t.keys.apply],
17404
17383
  ...many ? [["a", t.keys.applyAll], ["\u2191 \u2193", t.keys.line]] : [],
17405
17384
  ...deniable(r, s) ? [["n", t.keys.deny]] : [],
17406
17385
  ["esc", t.keys.ignore],
17407
- next2,
17386
+ next3,
17408
17387
  ["ctrl+n", t.keys.newChat]
17409
17388
  ];
17410
17389
  };
17411
17390
  const history = askHistory(d.chat, kept.asked);
17412
- const keys = confirm ? [["n esc", t.keys.keep], ["y", t.keys.wipe]] : focus === -1 && browsing ? [["\u21B5", t.keys.resend], ["\u2191 \u2193", t.keys.history], ["esc", t.keys.backToDraft]] : focus === -1 ? [["\u21B5", t.keys.sendOnly], ...review$, ["alt+\u21B5", t.keys.newLine], ["esc", t.keys.back], ["PgUp PgDn", t.keys.scroll], ["\u2191 \u2193", t.keys.history], ["tab", cards.length ? t.keys.nextCard : t.keys.conversation], ["alt+v", t.keys.image], ...turnImages.length ? [["alt+o", t.keys.openImage]] : [], ["ctrl+n", t.keys.newChat], ...stop] : focus === CONVO ? [["j k", t.keys.line], ["PgUp PgDn", t.keys.page], ["G", t.keys.end], ...stop, ["tab", t.keys.question], ["esc", t.keys.question]] : card?.kind === "action" ? [...actionKeys(card.qid), ...stop, ...review$] : [["\u21B5", t.keys.send], ["tab", focus + 1 < cards.length ? t.keys.nextCard : t.keys.conversation], ...card?.kind === "team" ? [["s", t.keys.save]] : [], ["ctrl+n", t.keys.newChat], ...stop, ["esc", t.keys.back]];
17391
+ const keys = confirm ? [["n esc", t.keys.keep], ["y", t.keys.wipe]] : focus === -1 && browsing ? [["\u21B5", t.keys.resend], ["\u2191 \u2193", t.keys.history], ["esc", t.keys.backToDraft]] : focus === -1 ? [["\u21B5", t.keys.sendOnly], ...review$, ["alt+\u21B5", t.keys.newLine], ["esc", t.keys.back], ["PgUp PgDn", t.keys.scroll], ["\u2191 \u2193", t.keys.history], ["tab", cards.length ? t.keys.nextCard : t.keys.conversation], ["alt+v", t.keys.image], ...turnImages.length ? [["alt+o", t.keys.openImage]] : [], ["ctrl+n", t.keys.newChat], ...stop] : focus === CONVO ? [["j k", t.keys.line], ["PgUp PgDn", t.keys.page], ["G", t.keys.end], ...stop, ["tab", t.keys.question], ["esc", t.keys.question]] : card?.kind === "action" ? [...actionKeys(card.qid), ...stop, ...review$] : [["\u21B5", t.keys.send], ["tab", focus + 1 < cards.length ? t.keys.nextCard : t.keys.conversation], ["ctrl+n", t.keys.newChat], ...stop, ["esc", t.keys.back]];
17413
17392
  const foot = up > 0 ? asking && live?.qid === asking.qid ? t.ask.answering : t.session.scrolled(lines.length - view - up, up) : lines.length > view ? focus === CONVO ? t.session.above(lines.length - view) : t.ask.above(lines.length - view) : void 0;
17414
17393
  const reading = focus === CONVO;
17415
17394
  return /* @__PURE__ */ jsxs11(Frame, { crumb: `${t.tabs.ask}`, keys, kept: draft !== "", children: [
@@ -17428,7 +17407,7 @@ function Ask() {
17428
17407
  cards.map((c, i) => {
17429
17408
  const turn = d.chat.find((x) => x.qid === c.qid);
17430
17409
  if (c.kind !== "action") return /* @__PURE__ */ jsx15(CardBox, { card: c, turn, focused: i === focus, busy: busy?.qid === c.qid }, `${c.kind}:${c.qid}`);
17431
- const p = planOf(turn);
17410
+ const p = planOf2(turn);
17432
17411
  const sel = selectedRow(p, rowOf2[c.qid]);
17433
17412
  return /* @__PURE__ */ jsx15(ActionBox, { turn, plan: p, focused: i === focus, busy: busy?.qid === c.qid, sel, confirming: confirming?.qid === c.qid ? confirming.rows : null }, `action:${c.qid}`);
17434
17413
  }),
@@ -17442,7 +17421,7 @@ function Ask() {
17442
17421
  /* @__PURE__ */ jsx15(Button, { label: t.ask.wipe, k: "y" })
17443
17422
  ] })
17444
17423
  ] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
17445
- /* @__PURE__ */ jsx15(ChipsLine, { chips: chips.items }),
17424
+ /* @__PURE__ */ jsx15(ChipsLine, { chips: chips2.items }),
17446
17425
  /* @__PURE__ */ jsx15(
17447
17426
  Field,
17448
17427
  {
@@ -17465,46 +17444,8 @@ function Ask() {
17465
17444
  function CardBox({ card, turn, focused, busy }) {
17466
17445
  const { t, g } = useTui();
17467
17446
  const color2 = focused ? C.coral : C.line;
17468
- if (card.kind === "team") {
17469
- const team = turn.team;
17470
- const auto = team.seats.filter((x) => x.autonomy === "auto").map((x) => `@${x.role}`);
17471
- return /* @__PURE__ */ jsxs11(TitledBox, { title: t.ask.teamCard(team.name), flexShrink: 0, color: color2, titleColor: C.coral, children: [
17472
- /* @__PURE__ */ jsxs11(Text12, { wrap: "truncate", children: [
17473
- /* @__PURE__ */ jsx15(Text12, { color: C.dim, children: t.ask.folder.padEnd(13) }),
17474
- /* @__PURE__ */ jsx15(Text12, { color: C.faint, children: team.cwd })
17475
- ] }),
17476
- /* @__PURE__ */ jsxs11(Text12, { wrap: "truncate", children: [
17477
- /* @__PURE__ */ jsx15(Text12, { color: C.dim, children: t.ask.table.padEnd(13) }),
17478
- g.team,
17479
- " ",
17480
- t.ask.newTable(team.projectName ?? team.project)
17481
- ] }),
17482
- team.seats.map((x) => /* @__PURE__ */ jsxs11(Text12, { wrap: "truncate", children: [
17483
- /* @__PURE__ */ jsx15(Text12, { color: C.dim, children: "".padEnd(13) }),
17484
- /* @__PURE__ */ jsxs11(Text12, { color: C.violet, children: [
17485
- "@",
17486
- x.role
17487
- ] }),
17488
- " ",
17489
- x.label,
17490
- " ",
17491
- /* @__PURE__ */ jsx15(Text12, { color: C.faint, children: [TOOL_NAME[x.tool], x.model, x.effort].filter(Boolean).join(" \xB7 ") }),
17492
- " ",
17493
- /* @__PURE__ */ jsx15(Text12, { color: x.autonomy === "auto" ? C.yellow : C.faint, children: t.ask.autonomy[x.autonomy ?? "ask"] })
17494
- ] }, x.role)),
17495
- auto.length ? /* @__PURE__ */ jsx15(Text12, { color: C.yellow, wrap: "truncate", children: t.ask.autoWarning(auto.join(", ")) }) : null,
17496
- team.error ? /* @__PURE__ */ jsx15(Text12, { color: C.red, wrap: "truncate", children: team.error }) : null,
17497
- team.saveError && !team.savedPath ? /* @__PURE__ */ jsx15(Text12, { color: C.red, wrap: "truncate", children: team.saveError }) : null,
17498
- /* @__PURE__ */ jsxs11(Box12, { columnGap: 2, children: [
17499
- /* @__PURE__ */ jsx15(Button, { label: busy ? `${g.live} ${t.ask.launchTeam}` : t.ask.launchTeam, k: "\u21B5", primary: focused }),
17500
- !team.template ? team.savedPath ? /* @__PURE__ */ jsx15(Text12, { color: C.faint, children: t.ask.saved }) : /* @__PURE__ */ jsx15(Button, { label: t.ask.save, k: "s" }) : null,
17501
- /* @__PURE__ */ jsx15(Text12, { color: C.faint, children: t.ask.nothingWithoutYou })
17502
- ] })
17503
- ] });
17504
- }
17505
17447
  if (card.kind === "task") {
17506
17448
  const tasks = turn.tasks;
17507
- const waits = !!turn.team && !turn.team.teamId;
17508
17449
  return /* @__PURE__ */ jsxs11(TitledBox, { title: t.ask.taskCard, flexShrink: 0, color: color2, titleColor: C.violet, children: [
17509
17450
  card.rows.map((r) => {
17510
17451
  const c = tasks[r];
@@ -17518,10 +17459,7 @@ function CardBox({ card, turn, focused, busy }) {
17518
17459
  ] }) : null
17519
17460
  ] }, r);
17520
17461
  }),
17521
- /* @__PURE__ */ jsxs11(Box12, { columnGap: 2, children: [
17522
- /* @__PURE__ */ jsx15(Button, { label: busy ? `${g.live} ${t.ask.give}` : t.ask.give, k: "\u21B5", primary: focused && !waits }),
17523
- waits ? /* @__PURE__ */ jsx15(Text12, { color: C.faint, children: t.ask.waitTeam }) : null
17524
- ] })
17462
+ /* @__PURE__ */ jsx15(Box12, { columnGap: 2, children: /* @__PURE__ */ jsx15(Button, { label: busy ? `${g.live} ${t.ask.give}` : t.ask.give, k: "\u21B5", primary: focused }) })
17525
17463
  ] });
17526
17464
  }
17527
17465
  if (card.kind === "launch") {
@@ -17581,7 +17519,7 @@ function CardBox({ card, turn, focused, busy }) {
17581
17519
  }
17582
17520
 
17583
17521
  // src/tui/screens/Launch.tsx
17584
- import { useEffect as useEffect11, useMemo as useMemo5, useRef as useRef11, useState as useState10 } from "react";
17522
+ import { useEffect as useEffect11, useMemo as useMemo5, useState as useState10 } from "react";
17585
17523
  import { Box as Box13, Text as Text14, useInput as useInput6 } from "ink";
17586
17524
 
17587
17525
  // ../fleet/src/launch.ts
@@ -17626,8 +17564,8 @@ function missingOf(f, machines) {
17626
17564
  return null;
17627
17565
  }
17628
17566
  function previewOf(f, sessions, projectIdOf) {
17629
- const machineOf3 = (s) => splitId(s.id)?.machine ?? s.machineId ?? "local";
17630
- const taken = sessions.filter((s) => s.state !== "exited" && machineOf3(s) === f.machine).map((s) => s.name);
17567
+ const machineOf2 = (s) => splitId(s.id)?.machine ?? s.machineId ?? "local";
17568
+ const taken = sessions.filter((s) => s.state !== "exited" && machineOf2(s) === f.machine).map((s) => s.name);
17631
17569
  const seated = f.project === null ? 0 : sessions.filter((s) => projectIdOf(s) === f.project).length;
17632
17570
  return { name: pickName(taken), seat: seated + 1 };
17633
17571
  }
@@ -17648,9 +17586,6 @@ function launchMessage(f, name) {
17648
17586
  };
17649
17587
  }
17650
17588
 
17651
- // src/tui/screens/Launch.tsx
17652
- import { randomUUID as randomUUID3 } from "crypto";
17653
-
17654
17589
  // src/tui/ui/TextInput.tsx
17655
17590
  import { Text as Text13, useInput as useInput5 } from "ink";
17656
17591
  import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
@@ -17709,25 +17644,9 @@ function ProjectChoice({ project, on }) {
17709
17644
  ] });
17710
17645
  }
17711
17646
  function Launch() {
17712
- const { catalog } = useTui();
17713
- const [prefill] = useState10(() => {
17714
- const p = catalog.current.launch;
17715
- catalog.current.launch = void 0;
17716
- return p;
17717
- });
17718
- const [team, setTeam] = useState10(prefill?.template !== void 0);
17719
- return team ? /* @__PURE__ */ jsx17(TeamLaunch, { onSession: () => setTeam(false), prefill: prefill?.template !== void 0 ? prefill : void 0 }) : /* @__PURE__ */ jsx17(SessionLaunch, { onTeam: () => setTeam(true), prefill: prefill?.template === void 0 ? prefill : void 0 });
17720
- }
17721
- function SessionLaunch({ onTeam, prefill }) {
17722
17647
  const { client, d, machines, projects, t, g, toast, go, select, small } = useTui();
17723
17648
  const list3 = projects?.projects ?? [];
17724
- const rolePick = (r, tool) => r && (r.tool === void 0 || r.tool === tool) ? { model: r.model ?? null, effort: r.effort ?? null } : NO_PICK;
17725
- const [form, setForm] = useState10(() => {
17726
- const tool = prefill?.role?.tool ?? "claude";
17727
- const base = withPick(launchDefaults(list3, machines, prefill?.project ? { project: prefill.project } : {}, tool), rolePick(prefill?.role, tool));
17728
- const f = prefill ? withMachine(base, list3, prefill.machine) : base;
17729
- return prefill?.cwd ? { ...f, repos: [prefill.cwd] } : f;
17730
- });
17649
+ const [form, setForm] = useState10(() => launchDefaults(list3, machines, {}, "claude"));
17731
17650
  const [field, setField] = useState10("prompt");
17732
17651
  const [folder, setFolder] = useState10("");
17733
17652
  const [repoAt, setRepoAt] = useState10(0);
@@ -17751,33 +17670,6 @@ function SessionLaunch({ onTeam, prefill }) {
17751
17670
  return own4.length ? own4 : (machine?.recents ?? []).slice(0, 6).map((r) => r.cwd);
17752
17671
  }, [project, form.machine, machine?.recents]);
17753
17672
  const withFolder = folder.trim() ? { ...form, repos: MULTI_REPO_TOOLS.has(form.tool) ? [folder.trim(), ...form.repos.filter((r) => r !== folder.trim())] : [folder.trim()] } : form;
17754
- const target = withFolder.repos[0] ?? null;
17755
- const sameMachine = !!prefill?.role && withFolder.machine === prefill.machine;
17756
- const readHere = sameMachine && target !== null && target === prefill.cwd;
17757
- const asked2 = sameMachine && !readHere && target !== null ? `${withFolder.machine}\0${target}` : null;
17758
- const [seen, setSeen] = useState10(null);
17759
- useEffect11(() => {
17760
- if (!asked2) return;
17761
- const [machineId, cwd] = asked2.split("\0");
17762
- const requestId = `tui-role-${randomUUID3()}`;
17763
- const off = client.onMessage((m) => {
17764
- if (m.type !== "roles.listing" || m.requestId !== requestId) return;
17765
- off();
17766
- const found = m.error ? null : m.roles.find((r) => r.id === prefill.role.id) ?? null;
17767
- setSeen({ key: asked2, role: found });
17768
- const was = prefill.role;
17769
- if (found && (found.tool !== was.tool || found.model !== was.model || found.effort !== was.effort))
17770
- setForm((f) => found.tool && found.tool !== f.tool ? withTool(f, found.tool, rolePick(found, found.tool)) : withPick(f, rolePick(found, f.tool)));
17771
- });
17772
- const timer2 = setTimeout(() => client.send({ type: "roles.list", cwd, requestId, ...machineId !== "local" ? { machineId } : {} }), 250);
17773
- return () => {
17774
- clearTimeout(timer2);
17775
- off();
17776
- };
17777
- }, [asked2]);
17778
- const checking = asked2 !== null && seen?.key !== asked2;
17779
- const role = readHere ? prefill.role : asked2 !== null && seen?.key === asked2 ? seen.role ?? void 0 : void 0;
17780
- const other = !!role && role.path !== prefill?.role?.path;
17781
17673
  const projectIdOf = (s) => projects ? projectOf(s, projects) : null;
17782
17674
  const drawn = useMemo5(() => previewOf(withFolder, d.sessions, projectIdOf), [form.machine, form.project, d.sessions.length]);
17783
17675
  const [name, setName] = useState10(null);
@@ -17803,17 +17695,13 @@ function SessionLaunch({ onTeam, prefill }) {
17803
17695
  }, [launching, d.lastLaunched, d.errors]);
17804
17696
  const launch2 = () => {
17805
17697
  if (launching) return;
17806
- if (checking && !missing) {
17807
- toast({ text: t.catalog.roleChecking(prefill.role.id), tone: "info" });
17808
- return;
17809
- }
17810
17698
  if (missing) {
17811
17699
  toast({ text: t.launch.missing[missing], tone: "info" });
17812
17700
  setField(missing === "prompt" ? "prompt" : missing === "machine" ? "machine" : "repos");
17813
17701
  return;
17814
17702
  }
17815
17703
  setLaunching({ seq: d.lastLaunched?.seq ?? 0, errorSeq: d.errors.at(-1)?.seq ?? 0, at: Date.now(), name: shownName });
17816
- client.send({ ...launchMessage(withFolder, shownName), ...role ? { role: role.id } : {} });
17704
+ client.send(launchMessage(withFolder, shownName));
17817
17705
  };
17818
17706
  useInput6((input, key) => {
17819
17707
  if (small && !key.escape) return;
@@ -17825,10 +17713,6 @@ function SessionLaunch({ onTeam, prefill }) {
17825
17713
  launch2();
17826
17714
  return;
17827
17715
  }
17828
- if (key.ctrl && input === "t") {
17829
- onTeam();
17830
- return;
17831
- }
17832
17716
  if (key.ctrl && input === "r") {
17833
17717
  setName(pickName([...d.sessions.map((s) => s.name), shownName]));
17834
17718
  return;
@@ -17854,16 +17738,16 @@ function SessionLaunch({ onTeam, prefill }) {
17854
17738
  }
17855
17739
  if (!by) return;
17856
17740
  if (field === "tool") setForm((f) => {
17857
- const next2 = cycle(tools, f.tool, by);
17858
- return withTool(f, next2, rolePick(role, next2));
17741
+ const next3 = cycle(tools, f.tool, by);
17742
+ return withTool(f, next3);
17859
17743
  });
17860
17744
  else if (field === "model") setForm((f) => withPick(f, { model: cycle(modelIds, f.model, by), effort: null }));
17861
17745
  else if (field === "effort") setForm((f) => withPick(f, { model: f.model, effort: cycle(efforts, f.effort, by) }));
17862
17746
  else if (field === "project") {
17863
17747
  const ids = [...list3.map((p) => p.id), null];
17864
17748
  setForm((f) => {
17865
- const next2 = cycle(ids, f.project, by);
17866
- return next2 === null ? { ...f, project: null, repos: [] } : withProject(f, list3, next2);
17749
+ const next3 = cycle(ids, f.project, by);
17750
+ return next3 === null ? { ...f, project: null, repos: [] } : withProject(f, list3, next3);
17867
17751
  });
17868
17752
  setRepoAt(0);
17869
17753
  } else if (field === "machine") {
@@ -17881,43 +17765,8 @@ function SessionLaunch({ onTeam, prefill }) {
17881
17765
  x === cur ? /* @__PURE__ */ jsx17(Text14, { color: C.fg, bold: true, children: `${g.prev} ${label(x)} ${g.next}` }) : /* @__PURE__ */ jsx17(Text14, { color: C.dim, children: label(x) }),
17882
17766
  " "
17883
17767
  ] }, i)) });
17884
- return /* @__PURE__ */ jsxs13(Frame, { crumb: t.launch.title, keys: [["tab", t.keys.nextField], ["\u2190 \u2192", t.keys.choose], [t.keys.space, t.keys.tick], ["ctrl+s", t.keys.start], ["ctrl+t", t.keys.teamMode], ["ctrl+r", t.keys.redraw], ["esc", t.keys.cancel]], children: [
17768
+ return /* @__PURE__ */ jsxs13(Frame, { crumb: t.launch.title, keys: [["tab", t.keys.nextField], ["\u2190 \u2192", t.keys.choose], [t.keys.space, t.keys.tick], ["ctrl+s", t.keys.start], ["ctrl+r", t.keys.redraw], ["esc", t.keys.cancel]], children: [
17885
17769
  /* @__PURE__ */ jsxs13(TitledBox, { title: t.launch.title, flexGrow: 1, children: [
17886
- /* @__PURE__ */ jsxs13(Box13, { children: [
17887
- /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: " " }),
17888
- /* @__PURE__ */ jsx17(Box13, { width: 14, flexShrink: 0, children: /* @__PURE__ */ jsx17(Text14, { color: C.dim, children: t.launch.mode }) }),
17889
- /* @__PURE__ */ jsxs13(Text14, { children: [
17890
- /* @__PURE__ */ jsxs13(Text14, { color: C.coral, children: [
17891
- g.radioOn,
17892
- " ",
17893
- t.launch.session
17894
- ] }),
17895
- " ",
17896
- /* @__PURE__ */ jsxs13(Text14, { color: C.dim, children: [
17897
- g.radioOff,
17898
- " ",
17899
- t.launch.team
17900
- ] }),
17901
- " ",
17902
- /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: "ctrl+t" })
17903
- ] })
17904
- ] }),
17905
- prefill?.role ? /* @__PURE__ */ jsxs13(Box13, { children: [
17906
- /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: " " }),
17907
- /* @__PURE__ */ jsx17(Box13, { width: 14, flexShrink: 0, children: /* @__PURE__ */ jsx17(Text14, { color: C.dim, children: t.catalog.role }) }),
17908
- role ? /* @__PURE__ */ jsxs13(Text14, { wrap: "truncate", children: [
17909
- /* @__PURE__ */ jsxs13(Text14, { color: C.violet, children: [
17910
- "@",
17911
- role.id
17912
- ] }),
17913
- " ",
17914
- /* @__PURE__ */ jsx17(Text14, { color: C.fg, children: role.label }),
17915
- other ? /* @__PURE__ */ jsxs13(Text14, { color: C.faint, children: [
17916
- " \xB7 ",
17917
- t.catalog.roleReadIn(target)
17918
- ] }) : null
17919
- ] }) : /* @__PURE__ */ jsx17(Text14, { wrap: "truncate", color: C.faint, children: checking ? t.catalog.roleChecking(prefill.role.id) : t.catalog.roleElsewhere(prefill.role.id, prefill.cwd ?? "~/.ledge") })
17920
- ] }) : null,
17921
17770
  row2("prompt", t.launch.instruction, /* @__PURE__ */ jsx17(TextInput, { value: form.prompt, onChange: (v) => setForm((f) => ({ ...f, prompt: v })), active: field === "prompt" })),
17922
17771
  /* @__PURE__ */ jsx17(Text14, { children: " " }),
17923
17772
  row2("tool", t.launch.agent, /* @__PURE__ */ jsx17(Text14, { wrap: "truncate", children: tools.map((x) => /* @__PURE__ */ jsxs13(Text14, { color: x === form.tool ? C.coral : C.dim, children: [
@@ -17971,216 +17820,6 @@ function SessionLaunch({ onTeam, prefill }) {
17971
17820
  ] })
17972
17821
  ] });
17973
17822
  }
17974
- var TEAM_FIELDS = ["project", "folder", "template", "seats", "name"];
17975
- var autonomyColor = (a) => ({ ask: C.faint, edits: C.dim, auto: C.yellow })[a];
17976
- function TeamLaunch({ onSession, prefill }) {
17977
- const { client, machines, projects, t, g, toast, go, small } = useTui();
17978
- const list3 = projects?.projects ?? [];
17979
- const [projectId, setProjectId] = useState10(prefill ? prefill.project : list3[0]?.id ?? null);
17980
- const project = list3.find((p) => p.id === projectId);
17981
- const folders = useMemo5(() => {
17982
- const own4 = (project?.repos ?? []).filter((r) => r.machine === "local").map((r) => r.path);
17983
- const base = own4.length ? own4 : (machines.find((m) => m.id === "local")?.recents ?? []).slice(0, 6).map((r) => r.cwd);
17984
- return prefill?.cwd && projectId === prefill.project && !base.includes(prefill.cwd) ? [prefill.cwd, ...base] : base;
17985
- }, [project, machines]);
17986
- const [folderAt, setFolderAt] = useState10(() => Math.max(0, prefill?.cwd ? folders.indexOf(prefill.cwd) : 0));
17987
- const wanted = useRef11(prefill?.template);
17988
- const cwd = folders[folderAt] ?? folders[0] ?? null;
17989
- const [field, setField] = useState10("template");
17990
- const [defs, setDefs] = useState10(null);
17991
- const [template, setTemplate] = useState10(null);
17992
- const [seats, setSeats] = useState10([]);
17993
- const [seatAt, setSeatAt] = useState10(0);
17994
- const [name, setName] = useState10("");
17995
- const [launching, setLaunching] = useState10(false);
17996
- useEditing(true);
17997
- useEffect11(() => {
17998
- if (!cwd) {
17999
- setDefs({ roles: [], templates: [] });
18000
- return;
18001
- }
18002
- setDefs(null);
18003
- const requestId = `tui-roles-${randomUUID3()}`;
18004
- const off = client.onMessage((m) => {
18005
- if (m.type !== "roles.listing" || m.requestId !== requestId) return;
18006
- off();
18007
- setDefs({ roles: m.roles, templates: m.templates });
18008
- setSeats(m.roles.map((role) => ({ role, tool: role.tool ?? "claude", on: false })));
18009
- const first = m.templates.find((x) => x.id === wanted.current) ?? m.templates[0];
18010
- wanted.current = void 0;
18011
- setTemplate(first?.id ?? null);
18012
- setName((n) => n || first?.name || project?.name || "");
18013
- });
18014
- client.send({ type: "roles.list", cwd, requestId });
18015
- return off;
18016
- }, [cwd]);
18017
- const tpl = defs?.templates.find((x) => x.id === template);
18018
- const chosen = tpl ? tpl.seats.map((x) => {
18019
- const role = defs.roles.find((r) => r.id === x.role);
18020
- return { id: x.role, tool: x.tool ?? role?.tool ?? "claude", autonomy: role?.autonomy ?? "ask" };
18021
- }) : seats.filter((x) => x.on).map((x) => ({ id: x.role.id, tool: x.tool, autonomy: x.role.autonomy ?? "ask" }));
18022
- const auto = chosen.filter((x) => x.autonomy === "auto").map((x) => `@${x.id}`);
18023
- const teamName = name.trim() || tpl?.name || project?.name || "team";
18024
- const launch2 = () => {
18025
- if (launching || !cwd) return;
18026
- if (!chosen.length) {
18027
- toast({ text: t.launch.noSeat, tone: "info" });
18028
- return;
18029
- }
18030
- const rid = randomUUID3();
18031
- setLaunching(true);
18032
- const off = client.onMessage((m) => {
18033
- if (m.type !== "team.launched" && m.type !== "team.failed" || m.rid !== rid) return;
18034
- off();
18035
- setLaunching(false);
18036
- if (m.type === "team.launched") {
18037
- toast({ text: t.launch.teamLaunched(m.team.name), tone: "ok" });
18038
- go("overview");
18039
- } else toast({ text: t.launch.teamFailed(m.reason), tone: "error" });
18040
- });
18041
- client.send({ type: "team.launch", rid, cwd, name: teamName, ...tpl ? { template: tpl.id } : { seats: chosen.map((x) => ({ role: x.id, tool: x.tool })) } });
18042
- };
18043
- const tools = ["claude", "codex", "cursor", "omp"];
18044
- const templates = [...(defs?.templates ?? []).map((x) => x.id), null];
18045
- useInput6((input, key) => {
18046
- if (small && !key.escape) return;
18047
- if (key.escape) {
18048
- go("overview");
18049
- return;
18050
- }
18051
- if (key.ctrl && input === "s") {
18052
- launch2();
18053
- return;
18054
- }
18055
- if (key.ctrl && input === "t") {
18056
- onSession();
18057
- return;
18058
- }
18059
- if (key.tab || key.downArrow || key.return) {
18060
- setField((f) => cycle(TEAM_FIELDS, f, key.shift ? -1 : 1));
18061
- return;
18062
- }
18063
- if (key.upArrow) {
18064
- setField((f) => cycle(TEAM_FIELDS, f, -1));
18065
- return;
18066
- }
18067
- if (field === "name") return;
18068
- const by = key.rightArrow || input === "l" ? 1 : key.leftArrow || input === "h" ? -1 : 0;
18069
- if (field === "project" && by) {
18070
- const ids = list3.map((p) => p.id);
18071
- if (ids.length) {
18072
- setProjectId((cur) => cycle(ids, cur ?? ids[0], by));
18073
- setFolderAt(0);
18074
- }
18075
- } else if (field === "folder" && by && folders.length) setFolderAt((i) => (i + by + folders.length) % folders.length);
18076
- else if (field === "template" && by) {
18077
- const next2 = cycle(templates, template, by);
18078
- setTemplate(next2);
18079
- const x = defs?.templates.find((y) => y.id === next2);
18080
- if (x) setName(x.name);
18081
- } else if (field === "seats" && !tpl) {
18082
- if (input === "j") setSeatAt((i) => seats.length ? (i + 1) % seats.length : 0);
18083
- else if (input === "k") setSeatAt((i) => seats.length ? (i + seats.length - 1) % seats.length : 0);
18084
- else if (input === " ") setSeats((xs) => xs.map((x, i) => i === seatAt ? { ...x, on: !x.on } : x));
18085
- else if (by) setSeats((xs) => xs.map((x, i) => i === seatAt ? { ...x, tool: cycle(tools, x.tool, by) } : x));
18086
- }
18087
- });
18088
- const row2 = (f, label, value) => /* @__PURE__ */ jsxs13(Box13, { backgroundColor: field === f ? C.sel : void 0, children: [
18089
- /* @__PURE__ */ jsx17(Text14, { color: field === f ? C.coral : C.faint, children: field === f ? "\u258C" : " " }),
18090
- /* @__PURE__ */ jsx17(Box13, { width: 14, flexShrink: 0, children: /* @__PURE__ */ jsx17(Text14, { color: C.dim, children: label }) }),
18091
- /* @__PURE__ */ jsx17(Box13, { flexGrow: 1, overflow: "hidden", flexDirection: "column", children: value })
18092
- ] }, f);
18093
- return /* @__PURE__ */ jsxs13(Frame, { crumb: t.launch.teamTitle, keys: [["tab", t.keys.nextField], ["\u2190 \u2192", t.keys.choose], [t.keys.space, t.keys.tick], ["ctrl+s", t.keys.start], ["ctrl+t", t.keys.teamMode], ["esc", t.keys.cancel]], children: [
18094
- /* @__PURE__ */ jsxs13(TitledBox, { title: t.launch.teamTitle, flexGrow: 1, children: [
18095
- /* @__PURE__ */ jsxs13(Box13, { children: [
18096
- /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: " " }),
18097
- /* @__PURE__ */ jsx17(Box13, { width: 14, flexShrink: 0, children: /* @__PURE__ */ jsx17(Text14, { color: C.dim, children: t.launch.mode }) }),
18098
- /* @__PURE__ */ jsxs13(Text14, { children: [
18099
- /* @__PURE__ */ jsxs13(Text14, { color: C.dim, children: [
18100
- g.radioOff,
18101
- " ",
18102
- t.launch.session
18103
- ] }),
18104
- " ",
18105
- /* @__PURE__ */ jsxs13(Text14, { color: C.coral, children: [
18106
- g.radioOn,
18107
- " ",
18108
- t.launch.team
18109
- ] }),
18110
- " ",
18111
- /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: "ctrl+t" })
18112
- ] })
18113
- ] }),
18114
- row2("project", t.launch.project, /* @__PURE__ */ jsx17(Text14, { wrap: "truncate", children: list3.length === 0 ? /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: t.launch.none }) : list3.map((p) => /* @__PURE__ */ jsx17(ProjectChoice, { project: p, on: p.id === projectId }, p.id)) })),
18115
- row2("folder", t.launch.folder, /* @__PURE__ */ jsx17(Text14, { wrap: "truncate", children: cwd ? /* @__PURE__ */ jsxs13(Fragment5, { children: [
18116
- /* @__PURE__ */ jsx17(Text14, { color: C.fg, children: cwd }),
18117
- folders.length > 1 ? /* @__PURE__ */ jsxs13(Text14, { color: C.faint, children: [
18118
- " (",
18119
- folderAt + 1,
18120
- "/",
18121
- folders.length,
18122
- ")"
18123
- ] }) : null
18124
- ] }) : /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: t.launch.none }) })),
18125
- row2("template", t.launch.template, defs === null ? /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: t.launch.loadingRoles }) : /* @__PURE__ */ jsx17(Text14, { wrap: "truncate", children: templates.map((x) => /* @__PURE__ */ jsxs13(Text14, { color: x === template ? C.fg : C.dim, bold: x === template, children: [
18126
- x === template ? `${g.prev} ${x ?? t.launch.custom} ${g.next}` : x ?? t.launch.custom,
18127
- " "
18128
- ] }, x ?? "-")) })),
18129
- row2("seats", t.launch.seats, tpl ? /* @__PURE__ */ jsx17(Text14, { wrap: "truncate", children: chosen.map((x) => /* @__PURE__ */ jsxs13(Text14, { children: [
18130
- /* @__PURE__ */ jsxs13(Text14, { color: C.violet, children: [
18131
- "@",
18132
- x.id
18133
- ] }),
18134
- " ",
18135
- /* @__PURE__ */ jsx17(Text14, { color: C.dim, children: TOOL_NAME[x.tool] }),
18136
- " "
18137
- ] }, x.id)) }) : seats.length === 0 ? /* @__PURE__ */ jsx17(Text14, { color: C.faint, children: t.launch.noRole }) : /* @__PURE__ */ jsx17(Fragment5, { children: seats.map((x, i) => /* @__PURE__ */ jsxs13(Text14, { wrap: "truncate", backgroundColor: field === "seats" && i === seatAt ? C.line : void 0, children: [
18138
- /* @__PURE__ */ jsx17(Text14, { color: x.on ? C.green : C.faint, children: x.on ? g.boxOn : g.boxOff }),
18139
- " ",
18140
- /* @__PURE__ */ jsxs13(Text14, { color: C.violet, children: [
18141
- "@",
18142
- x.role.id
18143
- ] }),
18144
- " ",
18145
- x.role.label,
18146
- " ",
18147
- /* @__PURE__ */ jsxs13(Text14, { color: C.dim, children: [
18148
- g.prev,
18149
- " ",
18150
- TOOL_NAME[x.tool],
18151
- " ",
18152
- g.next
18153
- ] }),
18154
- " ",
18155
- /* @__PURE__ */ jsx17(Text14, { color: autonomyColor(x.role.autonomy ?? "ask"), children: t.ask.autonomy[x.role.autonomy ?? "ask"] })
18156
- ] }, x.role.id)) })),
18157
- row2("name", t.launch.teamName, /* @__PURE__ */ jsx17(TextInput, { value: name, onChange: setName, active: field === "name", placeholder: teamName }))
18158
- ] }),
18159
- /* @__PURE__ */ jsxs13(TitledBox, { title: t.launch.preview, color: C.coral, titleColor: C.coral, children: [
18160
- /* @__PURE__ */ jsxs13(Text14, { wrap: "truncate", children: [
18161
- /* @__PURE__ */ jsx17(Text14, { color: C.coral, children: g.team }),
18162
- " ",
18163
- /* @__PURE__ */ jsx17(Text14, { color: C.fg, bold: true, children: t.launch.teamPreview(teamName, project?.name ?? cwd ?? "-") })
18164
- ] }),
18165
- /* @__PURE__ */ jsx17(Text14, { wrap: "truncate", children: chosen.map((x) => /* @__PURE__ */ jsxs13(Text14, { children: [
18166
- /* @__PURE__ */ jsxs13(Text14, { color: C.violet, children: [
18167
- "@",
18168
- x.id
18169
- ] }),
18170
- " ",
18171
- /* @__PURE__ */ jsx17(Text14, { color: C.dim, children: TOOL_NAME[x.tool] }),
18172
- " ",
18173
- /* @__PURE__ */ jsx17(Text14, { color: autonomyColor(x.autonomy), children: t.ask.autonomy[x.autonomy] }),
18174
- " "
18175
- ] }, x.id)) }),
18176
- auto.length ? /* @__PURE__ */ jsx17(Text14, { color: C.yellow, wrap: "truncate", children: t.ask.autoWarning(auto.join(", ")) }) : null,
18177
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, columnGap: 2, children: [
18178
- /* @__PURE__ */ jsx17(Button, { label: launching ? t.launch.launching : t.ask.launchTeam, k: "ctrl+s", primary: true }),
18179
- /* @__PURE__ */ jsx17(Button, { label: t.launch.cancel, k: "esc" })
18180
- ] })
18181
- ] })
18182
- ] });
18183
- }
18184
17823
 
18185
17824
  // src/tui/screens/Settings.tsx
18186
17825
  import { useState as useState11 } from "react";
@@ -18286,7 +17925,8 @@ function Help() {
18286
17925
  [t.help.field, [["\u21B5", k.sendOnly], ["alt+\u21B5 ctrl+j", k.newLine], ["\u2191 \u2193", k.lineHistory], ["ctrl \u2190 \u2192", k.word], ["ctrl+w", t.help.wordBefore], ["ctrl+u", k.clear], ["esc", k.keepAndLeave]]],
18287
17926
  [t.help.launch, [["tab \u2191 \u2193", k.nextField], ["\u2190 \u2192", k.choose], [t.keys.space, k.tick], ["ctrl+r", t.keys.redraw], ["ctrl+s", k.start], ["esc", k.cancel]]],
18288
17927
  [t.help.settings, [["\u2191 \u2193", k.navigate], ["\u2190 \u2192", k.change]]],
18289
- [t.help.teams, [["j k", k.navigate], ["\u2190 \u2192", k.rolesTemplates], ["c", k.folder], ["\u21B5", k.launchRole], ["o", k.openFile], ["!", k.ignored]]]
17928
+ [t.help.rigLaunch, [["tab \u2191 \u2193", k.nextField], ["\u2190 \u2192", k.choose], ["- +", t.rigLaunch.count], ["m", t.rigLaunch.modelKey], ["o", t.rigLaunch.policyKey], [t.keys.space, k.tick], ["p ctrl+p", t.rigLaunch.check], ["ctrl+s", k.start], ["esc", k.cancel]]],
17929
+ [t.help.openrig, [["tab", t.openrig.machine], ["j k \u2191 \u2193", k.navigate], ["\u21B5", t.openrig.unfold], ["s", t.openrig.toggle], ["R", t.openrig.restart], ["\xB1 =", t.rigSeats.key], ["+", t.rigLaunch.launchKey], ["D", `${t.openrig.daemonKey} \xB7 ${t.openrig.toggle}`], ["K", `${t.openrig.kernelKey} \xB7 ${t.openrig.toggle}`], ["y n", `${t.openrig.yes} \xB7 ${t.openrig.no}`]]]
18290
17930
  ];
18291
17931
  const lines = helpLines(groups, Math.max(20, columns - 4));
18292
17932
  const [view, setView] = useState12(10);
@@ -18317,419 +17957,917 @@ function Help() {
18317
17957
  return /* @__PURE__ */ jsx19(Frame, { crumb: t.help.title, keys: [["esc", k.back], ...maxTop ? [["j k PgUp PgDn", k.scroll]] : []], children: /* @__PURE__ */ jsx19(TitledBox, { title: t.help.title, flexGrow: 1, foot: foot || void 0, children: /* @__PURE__ */ jsx19(Lines, { lines, up: Math.max(0, below), onHeight: setView }) }) });
18318
17958
  }
18319
17959
 
18320
- // src/tui/screens/Teams.tsx
18321
- import { useEffect as useEffect12, useMemo as useMemo7, useState as useState13 } from "react";
17960
+ // src/tui/screens/OpenRig.tsx
17961
+ import { useEffect as useEffect13, useRef as useRef12, useState as useState14 } from "react";
18322
17962
  import { Box as Box16, Text as Text17 } from "ink";
18323
- import { randomUUID as randomUUID4 } from "crypto";
18324
17963
 
18325
- // src/tui/model/catalog.ts
18326
- var machineOf2 = (s) => s.machineId ?? "local";
18327
- function projectOfFolder(projects, cwd, machine = "local") {
18328
- if (!cwd) return null;
18329
- const hit = (projects?.projects ?? []).find((p) => p.repos.some((r) => r.machine === machine && (cwd === r.path || cwd.startsWith(`${r.path}/`))));
18330
- return hit?.id ?? null;
18331
- }
18332
- function catalogScopes(o) {
18333
- const list3 = o.projects?.projects ?? [];
18334
- const selected = o.selected ? { machine: machineOf2(o.selected), cwd: o.selected.repoPath ?? o.selected.cwd } : void 0;
18335
- const memo2 = o.memo.folder ? { machine: o.memo.machine ?? "local", cwd: o.memo.folder } : void 0;
18336
- const firsts = list3.map((p) => {
18337
- const r = p.repos.find((x) => x.machine === "local") ?? p.repos[0];
18338
- return r && { machine: r.machine, cwd: r.path };
17964
+ // ../fleet/src/openrig.ts
17965
+ import { useCallback, useEffect as useEffect12, useRef as useRef11, useState as useState13 } from "react";
17966
+ var OPENRIG_POLL_MS = 5e3;
17967
+ var next2 = 0;
17968
+ function useOpenRig(client, machine, pollMs = OPENRIG_POLL_MS) {
17969
+ const [view, setView] = useState13({ machine, state: null });
17970
+ const [pending, setPending] = useState13(null);
17971
+ const [last, setLast] = useState13(null);
17972
+ const waiting = useRef11(/* @__PURE__ */ new Map());
17973
+ const asked2 = useRef11(null);
17974
+ const ask2 = useCallback(() => {
17975
+ if (machine === null) return;
17976
+ const requestId = `openrig-${++next2}`;
17977
+ asked2.current = requestId;
17978
+ client.send({ type: "openrig.status", requestId, machineId: machine });
17979
+ }, [client, machine]);
17980
+ useEffect12(() => {
17981
+ setView({ machine, state: null });
17982
+ setPending(null);
17983
+ setLast(null);
17984
+ if (machine === null) return;
17985
+ const off = client.onMessage((m) => {
17986
+ if (m.type === "openrig.state") {
17987
+ if ((m.machineId ?? "local") !== machine) return;
17988
+ if (m.requestId !== void 0 && m.requestId !== asked2.current) return;
17989
+ setView((v) => ({ machine, state: m.state ?? (m.error ? null : v.state), ...m.error ? { error: m.error } : {} }));
17990
+ } else if (m.type === "openrig.done") {
17991
+ const resolve = waiting.current.get(m.requestId);
17992
+ if (!resolve) return;
17993
+ waiting.current.delete(m.requestId);
17994
+ resolve(m);
17995
+ }
17996
+ });
17997
+ ask2();
17998
+ const timer2 = setInterval(ask2, pollMs);
17999
+ return () => {
18000
+ off();
18001
+ clearInterval(timer2);
18002
+ for (const [, r] of waiting.current) r({ type: "openrig.done", requestId: "", ok: false, commands: [], error: "cancelled" });
18003
+ waiting.current.clear();
18004
+ };
18005
+ }, [client, machine, ask2, pollMs]);
18006
+ const act = useCallback((action) => {
18007
+ if (machine === null) return Promise.resolve({ type: "openrig.done", requestId: "", ok: false, commands: [], error: "offline" });
18008
+ const requestId = `openrig-act-${++next2}`;
18009
+ setPending(action);
18010
+ setLast(null);
18011
+ return new Promise((resolve) => {
18012
+ waiting.current.set(requestId, (d) => {
18013
+ setPending((p) => p === action ? null : p);
18014
+ setLast(d);
18015
+ resolve(d);
18016
+ });
18017
+ client.send({ type: "openrig.act", requestId, action, machineId: machine });
18018
+ });
18019
+ }, [client, machine]);
18020
+ const shown = view.machine === machine ? view : { state: null };
18021
+ return { state: shown.state, ..."error" in shown && shown.error ? { error: shown.error } : {}, pending, last, act, refresh: ask2 };
18022
+ }
18023
+ function useRigSpecs(client, machine) {
18024
+ const [answer, setAnswer] = useState13(null);
18025
+ const [round, setRound] = useState13(0);
18026
+ useEffect12(() => {
18027
+ if (machine === null) return;
18028
+ const requestId = `openrig-specs-${++next2}`;
18029
+ setAnswer((a) => a?.machine === machine ? a : null);
18030
+ const off = client.onMessage((m) => {
18031
+ if (m.type !== "openrig.specList" || m.requestId !== requestId) return;
18032
+ setAnswer({ machine, specs: m.specs, ...m.error ? { error: m.error } : {} });
18033
+ });
18034
+ client.send({ type: "openrig.specs", requestId, machineId: machine });
18035
+ return off;
18036
+ }, [client, machine, round]);
18037
+ const reload = useCallback(() => setRound((r) => r + 1), []);
18038
+ if (machine === null) return { status: "idle", specs: [], reload };
18039
+ if (!answer || answer.machine !== machine) return { status: "loading", specs: [], reload };
18040
+ return { status: "ready", specs: answer.specs, ...answer.error ? { error: answer.error } : {}, reload };
18041
+ }
18042
+ function launchRig(client, machine, launch2) {
18043
+ const requestId = `openrig-launch-${++next2}`;
18044
+ return new Promise((resolve) => {
18045
+ const off = client.onMessage((m) => {
18046
+ if (m.type !== "openrig.launched" || m.requestId !== requestId) return;
18047
+ off();
18048
+ resolve(m);
18049
+ });
18050
+ client.send({ type: "openrig.launch", requestId, launch: launch2, machineId: machine });
18339
18051
  });
18340
- const places = [selected, memo2, ...firsts, ...o.recents.slice(0, 6)].filter((x) => !!x?.cwd);
18341
- const seen = /* @__PURE__ */ new Set();
18342
- const label = (x) => {
18343
- const p = list3.find((y) => y.id === projectOfFolder(o.projects, x.cwd, x.machine));
18344
- return p ? `${p.name} ${x.cwd}` : x.cwd;
18345
- };
18052
+ }
18053
+ function reseatRig(client, machine, rig, seats, delegation) {
18054
+ const requestId = `openrig-seats-${++next2}`;
18055
+ return new Promise((resolve) => {
18056
+ const off = client.onMessage((m) => {
18057
+ if (m.type !== "openrig.reseated" || m.requestId !== requestId) return;
18058
+ off();
18059
+ resolve(m);
18060
+ });
18061
+ client.send({ type: "openrig.seats", requestId, machineId: machine, rig, seats, ...delegation ? { delegation } : {} });
18062
+ });
18063
+ }
18064
+
18065
+ // src/tui/model/openrig.ts
18066
+ var listedRigs = (s) => (s?.rigs ?? []).filter((r) => r.name !== KERNEL_RIG);
18067
+ var daemonUp = (s) => !!s?.installed && s.daemon.state === "running";
18068
+ var rigRunning = (r) => r.status === "running" || r.running > 0;
18069
+ function rigLook(r) {
18070
+ if (r.attention > 0) return "attention";
18071
+ if (r.status === "running") return "running";
18072
+ return r.running > 0 ? "starting" : "stopped";
18073
+ }
18074
+ var seatBusy = (s) => s.activity === "running" || s.work > 0;
18075
+ var rigToggle = (r) => rigRunning(r) ? { kind: "rig.stop", rig: r.name, snapshot: true } : { kind: "rig.start", rig: r.name };
18076
+ var daemonToggle = (s) => s.daemon.state === "running" ? { kind: "daemon.stop" } : { kind: "daemon.start", restore: "last" };
18077
+ var kernelToggle = (s) => s.kernel.state === "ready" || s.kernel.state === "starting" ? { kind: "kernel.stop" } : { kind: "kernel.start" };
18078
+ var asksFirst = (a) => a.kind.endsWith(".stop") || a.kind.endsWith(".restart");
18079
+ function busySeats(s, a) {
18080
+ const of = (r) => (r?.nodes ?? []).filter(seatBusy);
18081
+ if (a.kind === "rig.stop" || a.kind === "rig.restart") return of(s.rigs.find((r) => r.name === a.rig)).map((n) => n.logicalId);
18082
+ if (a.kind === "kernel.stop" || a.kind === "kernel.restart") return of(s.rigs.find((r) => r.name === KERNEL_RIG)).map((n) => n.logicalId);
18083
+ if (a.kind === "daemon.stop" || a.kind === "daemon.restart") return s.rigs.flatMap((r) => of(r).map((n) => `${r.name} \xB7 ${n.logicalId}`));
18084
+ return [];
18085
+ }
18086
+ var commandsOf = (a) => rigCommands(a).map(showRigCommand);
18087
+ var confirmOf = (s, a) => ({ action: a, busy: busySeats(s, a), commands: commandsOf(a) });
18088
+ var step2 = (at, by, n) => n ? (at + by + n) % n : 0;
18089
+ function keepRig(rigs, at, id) {
18090
+ const found = id ? rigs.findIndex((r) => r.id === id) : -1;
18091
+ return found >= 0 ? found : Math.max(0, Math.min(at, rigs.length - 1));
18092
+ }
18093
+
18094
+ // src/tui/model/rigSeats.ts
18095
+ var canReseat = (r) => !!r && rigRunning(r) && r.nodes.length > 0;
18096
+ function seatRows(r) {
18097
+ const byId = new Map(r.nodes.map((n) => [n.logicalId, n]));
18098
+ return [...seatsByRole(r.nodes)].map(([role, seats]) => ({ role, seats, first: byId.get(seats[0]) }));
18099
+ }
18100
+ var countsOf = (rows) => Object.fromEntries(rows.map((r) => [r.role, r.seats.length]));
18101
+ var withCount = (counts, role, by) => ({ ...counts, [role]: Math.max(0, Math.min(9, (counts[role] ?? 0) + by)) });
18102
+ var changedCounts = (rows, counts) => Object.fromEntries(rows.filter((r) => (counts[r.role] ?? r.seats.length) !== r.seats.length).map((r) => [r.role, counts[r.role]]));
18103
+ var planOf = (r, rows, counts) => seatPlan(r.nodes, changedCounts(rows, counts));
18104
+ function chips(row2, plan) {
18105
+ const removed = new Set(plan.remove);
18346
18106
  return [
18347
- ...places.filter((x) => {
18348
- const k = `${x.machine}\0${x.cwd}`;
18349
- if (seen.has(k)) return false;
18350
- seen.add(k);
18351
- return true;
18352
- }).map((x) => ({ cwd: x.cwd, machine: x.machine, label: label(x) })),
18353
- { cwd: null, machine: "local", label: o.homeOnly }
18107
+ ...row2.seats.map((id) => ({ id, kind: removed.has(id) ? "removed" : "kept" })),
18108
+ ...plan.add.filter((a) => a.role === row2.role).map((a) => ({ id: a.id, kind: "added" }))
18354
18109
  ];
18355
18110
  }
18356
- function initialScope(scopes, selected, memo2) {
18357
- const own4 = selected ? scopes.find((x) => x.machine === machineOf2(selected) && x.cwd === (selected.repoPath ?? selected.cwd)) : void 0;
18358
- const last = memo2.folder !== void 0 ? scopes.find((x) => x.cwd === memo2.folder && (memo2.folder === null || x.machine === (memo2.machine ?? "local"))) : void 0;
18359
- return own4 ?? last ?? scopes[0];
18360
- }
18361
- var bySource = (list3) => ({ repo: list3.filter((x) => x.source === "repo"), home: list3.filter((x) => x.source === "home") });
18362
- var usedIn = (id, templates) => templates.filter((x) => x.seats.some((s) => s.role === id));
18363
- function heldBy(id, sessions, teams, project, projectIdOf) {
18364
- return sessions.filter((s) => s.role === id && s.state !== "exited" && (project === null || projectIdOf(s) === project)).map((s) => ({ name: s.name, team: teams.find((x) => x.id === s.team && x.state !== "dissolved")?.name }));
18111
+ var busyRemovals = (r, plan) => plan.remove.filter((id) => {
18112
+ const n = r.nodes.find((x) => x.logicalId === id);
18113
+ return !!n && seatBusy(n);
18114
+ });
18115
+ function previewCommands(rig, plan, links) {
18116
+ if (!plan.add.length && !plan.remove.length) return [];
18117
+ return [
18118
+ ...plan.add.map((a) => `rig add ${rig} ${a.pod} ${a.id}`),
18119
+ ...plan.remove.map((id) => `rig remove ${rig} ${id}`),
18120
+ ...plan.add.length ? [`rig edge add \u2026 (${links})`] : [],
18121
+ `rig snapshot ${rig}`
18122
+ ];
18365
18123
  }
18366
- var stepIndex = (length, i, by) => length ? ((i + by) % length + length) % length : 0;
18367
18124
 
18368
- // src/tui/ui/Instruction.tsx
18369
- import { useMemo as useMemo6, useRef as useRef12 } from "react";
18370
- import { Box as Box15, Text as Text16, useBoxMetrics as useBoxMetrics4 } from "ink";
18371
- import { jsx as jsx20 } from "react/jsx-runtime";
18372
- function Instruction({ text: text5, empty: empty3 }) {
18373
- const { g, tone: tone2 } = useTui();
18374
- const ref = useRef12(null);
18375
- const { width, height } = useBoxMetrics4(ref);
18376
- const lines = useMemo6(() => width > 0 ? markdownLines(text5, { width, g, color: C.fg }) : [], [text5, width, g, tone2]);
18377
- return /* @__PURE__ */ jsx20(Box15, { ref, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !text5.trim() ? /* @__PURE__ */ jsx20(Text16, { color: C.faint, children: empty3 }) : lines.slice(0, Math.max(1, height)).map((l, i) => /* @__PURE__ */ jsx20(Box15, { flexShrink: 0, children: /* @__PURE__ */ jsx20(LineText, { line: l }) }, i)) });
18125
+ // src/tui/ui/RigSeats.tsx
18126
+ import { Box as Box15, Text as Text16 } from "ink";
18127
+ import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
18128
+ function RigSeats({ rig, machine, rows, counts, at, delegation, plan, applying }) {
18129
+ const { t, g } = useTui();
18130
+ const s = t.rigSeats;
18131
+ const now2 = rows.map((r) => `${r.seats.length} ${r.role}`).join(", ");
18132
+ const commands = previewCommands(rig.name, plan, delegation === "pairs" ? s.linksPairs : s.linksAll);
18133
+ const busy = busyRemovals(rig, plan);
18134
+ return /* @__PURE__ */ jsxs15(TitledBox, { title: s.title(rig.name, machine), color: C.coral, titleColor: C.coral, titleBold: true, flexGrow: 1, children: [
18135
+ /* @__PURE__ */ jsx20(Text16, { wrap: "truncate", color: C.dim, children: s.stays(now2) }),
18136
+ /* @__PURE__ */ jsx20(Text16, { children: " " }),
18137
+ rows.map((r, i) => {
18138
+ const on = i === at, n = counts[r.role] ?? r.seats.length;
18139
+ return /* @__PURE__ */ jsxs15(Box15, { flexShrink: 0, backgroundColor: on ? C.sel : void 0, children: [
18140
+ /* @__PURE__ */ jsx20(Text16, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
18141
+ /* @__PURE__ */ jsx20(Box15, { width: 16, flexShrink: 0, children: /* @__PURE__ */ jsx20(Text16, { wrap: "truncate", color: C.fg, bold: true, children: r.role }) }),
18142
+ /* @__PURE__ */ jsx20(Box15, { width: 26, flexShrink: 0, children: /* @__PURE__ */ jsx20(Text16, { wrap: "truncate", color: C.dim, children: [r.first.runtime, r.first.model].filter(Boolean).join(" \xB7 ") }) }),
18143
+ /* @__PURE__ */ jsx20(Box15, { width: 8, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text16, { children: [
18144
+ /* @__PURE__ */ jsx20(Text16, { color: C.dim, children: "\u2212 " }),
18145
+ /* @__PURE__ */ jsx20(Text16, { color: n === r.seats.length ? C.fg : C.green, bold: true, children: n }),
18146
+ /* @__PURE__ */ jsx20(Text16, { color: C.dim, children: " +" })
18147
+ ] }) }),
18148
+ /* @__PURE__ */ jsx20(Box15, { flexGrow: 1, overflow: "hidden", children: /* @__PURE__ */ jsx20(Text16, { wrap: "truncate", children: chips(r, plan).map((c) => /* @__PURE__ */ jsxs15(Text16, { color: c.kind === "added" ? C.green : c.kind === "removed" ? C.red : C.dim, strikethrough: c.kind === "removed", children: [
18149
+ c.kind === "added" ? `+${c.id}` : c.id,
18150
+ " "
18151
+ ] }, c.id)) }) })
18152
+ ] }, r.role);
18153
+ }),
18154
+ /* @__PURE__ */ jsx20(Text16, { children: " " }),
18155
+ plan.add.length ? /* @__PURE__ */ jsxs15(Text16, { wrap: "truncate", children: [
18156
+ delegation === "pairs" ? /* @__PURE__ */ jsx20(Text16, { color: C.fg, bold: true, children: `${g.prev} ${s.pairs} ${g.next}` }) : /* @__PURE__ */ jsx20(Text16, { color: C.dim, children: s.pairs }),
18157
+ " ",
18158
+ delegation === "all" ? /* @__PURE__ */ jsx20(Text16, { color: C.fg, bold: true, children: `${g.prev} ${s.all} ${g.next}` }) : /* @__PURE__ */ jsx20(Text16, { color: C.dim, children: s.all })
18159
+ ] }) : null,
18160
+ busy.map((id) => /* @__PURE__ */ jsx20(Text16, { wrap: "truncate", color: C.red, children: s.busy(id) }, id)),
18161
+ commands.length ? /* @__PURE__ */ jsx20(Box15, { marginTop: 1, flexDirection: "column", children: commands.map((c) => /* @__PURE__ */ jsx20(Text16, { wrap: "truncate", color: C.faint, children: c }, c)) }) : /* @__PURE__ */ jsx20(Text16, { color: C.faint, children: s.nothing }),
18162
+ applying ? /* @__PURE__ */ jsx20(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text16, { color: C.blue, children: s.applying }) }) : null
18163
+ ] });
18378
18164
  }
18379
18165
 
18380
- // src/tui/screens/Teams.tsx
18381
- import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
18382
- var autonomyColor2 = (a) => a === "auto" ? C.yellow : a === "edits" ? C.blue : C.faint;
18383
- var ROLES_MS = 4e3;
18384
- function Teams() {
18166
+ // src/tui/screens/OpenRig.tsx
18167
+ import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
18168
+ var LOOK = {
18169
+ running: { glyph: "\u25CF", color: () => C.green },
18170
+ starting: { glyph: "\u25CC", color: () => C.blue },
18171
+ attention: { glyph: "\u25B2", color: () => C.yellow },
18172
+ stopped: { glyph: "\u25CB", color: () => C.faint }
18173
+ };
18174
+ function question(t, a) {
18175
+ const w = t.openrig.what;
18176
+ if (a.kind === "rig.stop") return t.openrig.confirm(w["rig.stop"](a.rig));
18177
+ if (a.kind === "rig.restart") return t.openrig.confirm(w["rig.restart"](a.rig));
18178
+ if (a.kind === "daemon.stop" || a.kind === "daemon.restart" || a.kind === "kernel.stop" || a.kind === "kernel.restart") return t.openrig.confirm(w[a.kind]);
18179
+ return "";
18180
+ }
18181
+ function OpenRigScreen() {
18385
18182
  const tui = useTui();
18386
- const { client, d, projects, machines, t, g, selected, catalog, go } = tui;
18387
- const { columns, rows: height } = useScreenSize();
18388
- const wide = columns >= WIDE;
18389
- const left2 = wide ? Math.min(64, Math.max(44, Math.floor(columns * 0.42))) : columns;
18390
- const teams = useTeams(client) ?? [];
18391
- const recents = machines.flatMap((m) => m.recents.map((r) => ({ machine: m.id, cwd: r.cwd })));
18392
- const [memo2] = useState13(() => ({ folder: catalog.current.folder, machine: catalog.current.machine }));
18393
- const scopes = catalogScopes({ selected, memo: memo2, projects, recents, homeOnly: t.catalog.homeOnly });
18394
- const [scope, setScope] = useState13(() => initialScope(scopes, selected, catalog.current));
18395
- const [defs, setDefs] = useState13(null);
18396
- const [view, setView] = useState13("roles");
18397
- const [at, setAt] = useState13({ roles: null, templates: null });
18398
- const [problems, setProblems] = useState13(false);
18399
- const [silent, setSilent] = useState13(false);
18400
- const [reads, setReads] = useState13(0);
18401
- useEffect12(() => {
18402
- setDefs(null);
18403
- setSilent(false);
18404
- const requestId = `tui-catalog-${randomUUID4()}`;
18405
- const off = client.onMessage((m) => {
18406
- if (m.type !== "roles.listing" || m.requestId !== requestId) return;
18407
- off();
18408
- clearTimeout(timer2);
18409
- setDefs({ roles: m.roles, templates: m.templates, problems: m.problems, ...m.error ? { error: m.error } : {} });
18183
+ const { t, machines } = tui;
18184
+ const o = t.openrig;
18185
+ const [machineId, setMachineId] = useState14(tui.kept.rigMachine ?? null);
18186
+ const machine = machines.find((m) => m.id === machineId) ?? machines[0] ?? null;
18187
+ tui.kept.rigMachine = machine?.id ?? null;
18188
+ const rig = useOpenRig(tui.client, machine?.id ?? null);
18189
+ const state2 = rig.state;
18190
+ const rigs = listedRigs(state2);
18191
+ const [at, setAt] = useState14(0);
18192
+ const selectedId = useRef12(null);
18193
+ const [open, setOpen] = useState14(/* @__PURE__ */ new Set());
18194
+ const [confirm, setConfirm] = useState14(null);
18195
+ const [seats, setSeats] = useState14(null);
18196
+ const ids = rigs.map((r) => r.id).join(",");
18197
+ useEffect13(() => {
18198
+ setAt((a) => keepRig(rigs, a, selectedId.current));
18199
+ }, [ids]);
18200
+ const current2 = rigs[Math.min(at, rigs.length - 1)];
18201
+ selectedId.current = current2?.id ?? null;
18202
+ const run2 = (a) => {
18203
+ void rig.act(a).then((d) => {
18204
+ if (d.error === "cancelled") return;
18205
+ tui.toast(d.ok ? { text: o.done(d.commands.join(" && ")), tone: "ok" } : { text: o.failed(o.why[d.error ?? ""] ?? d.error ?? ""), tone: "error" });
18410
18206
  });
18411
- const timer2 = setTimeout(() => {
18412
- off();
18413
- setSilent(true);
18414
- }, ROLES_MS);
18415
- client.send({ type: "roles.list", requestId, ...scope.cwd ? { cwd: scope.cwd } : {}, ...scope.machine !== "local" ? { machineId: scope.machine } : {} });
18416
- return () => {
18417
- off();
18418
- clearTimeout(timer2);
18419
- };
18420
- }, [scope.cwd, scope.machine, reads]);
18421
- const roles = bySource(defs?.roles ?? []), templates = bySource(defs?.templates ?? []);
18422
- const order2 = useMemo7(
18423
- () => view === "roles" ? [...roles.repo.map((role2) => ({ kind: "role", key: role2.id, role: role2 })), ...roles.home.map((role2) => ({ kind: "role", key: role2.id, role: role2 }))] : [...templates.repo.map((template2) => ({ kind: "template", key: template2.id, template: template2 })), ...templates.home.map((template2) => ({ kind: "template", key: template2.id, template: template2 }))],
18424
- [defs, view]
18425
- );
18426
- const current2 = order2.find((r) => r.key === at[view]) ?? order2[0];
18427
- const role = current2?.kind === "role" ? current2.role : void 0;
18428
- const template = current2?.kind === "template" ? current2.template : void 0;
18429
- const project = projectOfFolder(projects, scope.cwd, scope.machine);
18430
- const remote = scope.machine !== "local";
18431
- const machine = machines.find((m) => m.id === scope.machine);
18432
- const machineLabel2 = machine?.label ?? scope.machine;
18433
- const reread = silent || defs?.error === "offline";
18434
- const launchable = !!current2 && !(remote && template);
18207
+ };
18208
+ const ask2 = (a) => {
18209
+ if (!state2) return;
18210
+ if (asksFirst(a)) setConfirm(confirmOf(state2, a));
18211
+ else run2(a);
18212
+ };
18213
+ const seatRig = seats ? rigs.find((r) => r.name === seats.rig) : void 0;
18214
+ const rows = seatRig ? seatRows(seatRig) : [];
18215
+ useEffect13(() => {
18216
+ if (seats && !seats.applying && !canReseat(seatRig)) setSeats(null);
18217
+ }, [seats, seatRig]);
18218
+ const apply = () => {
18219
+ if (!seats || !seatRig || !machine || seats.applying) return;
18220
+ const changed = changedCounts(rows, seats.counts);
18221
+ if (!Object.keys(changed).length) return;
18222
+ const adds = planOf(seatRig, rows, seats.counts).add.length > 0;
18223
+ setSeats((x) => x ? { ...x, applying: true } : x);
18224
+ void reseatRig(tui.client, machine.id, seatRig.name, changed, adds ? seats.delegation : void 0).then((r) => {
18225
+ if (!r.ok) {
18226
+ setSeats((x) => x ? { ...x, applying: false } : x);
18227
+ tui.toast({ text: t.rigSeats.why[r.error ?? ""] ?? r.error ?? "", tone: "error" });
18228
+ return;
18229
+ }
18230
+ setSeats(null);
18231
+ rig.refresh();
18232
+ const done = t.rigSeats.done(r.added.length, r.removed.length, r.links);
18233
+ tui.toast({ text: r.linksUnsupported ? `${done} \xB7 ${t.rigSeats.linksUnsupported}` : done, tone: r.linksUnsupported ? "info" : "ok" });
18234
+ });
18235
+ };
18435
18236
  useScreenKeys((input, key) => {
18436
- if (problems && (key.escape || input === "!")) {
18437
- setProblems(false);
18237
+ if (seats) {
18238
+ if (seats.applying) return true;
18239
+ if (key.escape) {
18240
+ setSeats(null);
18241
+ return true;
18242
+ }
18243
+ if (key.return) {
18244
+ apply();
18245
+ return true;
18246
+ }
18247
+ if (key.upArrow || input === "k") {
18248
+ setSeats((x) => x ? { ...x, at: step2(x.at, -1, rows.length) } : x);
18249
+ return true;
18250
+ }
18251
+ if (key.downArrow || input === "j") {
18252
+ setSeats((x) => x ? { ...x, at: step2(x.at, 1, rows.length) } : x);
18253
+ return true;
18254
+ }
18255
+ const role = rows[seats.at]?.role;
18256
+ if (role && (input === "+" || input === "=")) {
18257
+ setSeats((x) => x ? { ...x, counts: withCount(x.counts, role, 1) } : x);
18258
+ return true;
18259
+ }
18260
+ if (role && input === "-") {
18261
+ setSeats((x) => x ? { ...x, counts: withCount(x.counts, role, -1) } : x);
18262
+ return true;
18263
+ }
18264
+ if (key.leftArrow || key.rightArrow) {
18265
+ setSeats((x) => x ? { ...x, delegation: x.delegation === "pairs" ? "all" : "pairs" } : x);
18266
+ return true;
18267
+ }
18438
18268
  return true;
18439
18269
  }
18440
- if (input === "!") {
18441
- if (defs?.problems.length) setProblems(true);
18270
+ if (confirm) {
18271
+ if (input === "y") {
18272
+ run2(confirm.action);
18273
+ setConfirm(null);
18274
+ return true;
18275
+ }
18276
+ if (input === "n" || key.escape) {
18277
+ setConfirm(null);
18278
+ return true;
18279
+ }
18442
18280
  return true;
18443
18281
  }
18444
- if (input === "r" && reread) {
18445
- setReads((n) => n + 1);
18282
+ if (key.tab) {
18283
+ const i = Math.max(0, machines.findIndex((m) => m.id === machine?.id));
18284
+ setMachineId(machines[step2(i, key.shift ? -1 : 1, machines.length)]?.id ?? null);
18285
+ setOpen(/* @__PURE__ */ new Set());
18446
18286
  return true;
18447
18287
  }
18448
- if (key.leftArrow || key.rightArrow || input === "h" || input === "l") {
18449
- setView((v) => v === "roles" ? "templates" : "roles");
18450
- setProblems(false);
18288
+ if (input === "+") {
18289
+ if (machine) tui.go("rigLaunch");
18451
18290
  return true;
18452
18291
  }
18453
- if (input === "j" || key.downArrow || input === "k" || key.upArrow) {
18454
- const i = Math.max(0, order2.findIndex((r) => r.key === current2?.key));
18455
- const next2 = order2[stepIndex(order2.length, i, input === "j" || key.downArrow ? 1 : -1)];
18456
- if (next2) setAt((x) => ({ ...x, [view]: next2.key }));
18292
+ if (key.upArrow || input === "k") {
18293
+ setAt((i) => step2(i, -1, rigs.length));
18457
18294
  return true;
18458
18295
  }
18459
- if (input === "c") {
18460
- const next2 = scopes[stepIndex(scopes.length, scopes.findIndex((x) => x.cwd === scope.cwd && x.machine === scope.machine), 1)];
18461
- catalog.current.folder = next2.cwd;
18462
- catalog.current.machine = next2.machine;
18463
- setScope(next2);
18464
- setProblems(false);
18296
+ if (key.downArrow || input === "j") {
18297
+ setAt((i) => step2(i, 1, rigs.length));
18465
18298
  return true;
18466
18299
  }
18467
- if (key.return && launchable) {
18468
- catalog.current.launch = { cwd: scope.cwd, machine: scope.machine, project, ...role ? { role } : {}, ...template ? { template: template.id } : {} };
18469
- go("launch");
18300
+ if (key.return) {
18301
+ if (current2) setOpen((s) => {
18302
+ const n = new Set(s);
18303
+ if (n.has(current2.id)) n.delete(current2.id);
18304
+ else n.add(current2.id);
18305
+ return n;
18306
+ });
18307
+ return true;
18308
+ }
18309
+ const busy = rig.pending !== null;
18310
+ if (input === "D") {
18311
+ if (state2?.installed && !busy) ask2(daemonToggle(state2));
18312
+ return true;
18313
+ }
18314
+ if (input === "K") {
18315
+ if (daemonUp(state2) && !busy) ask2(kernelToggle(state2));
18316
+ return true;
18317
+ }
18318
+ if (input === "O") {
18319
+ const op = operatorSession(state2, tui.d.sessions, machine?.id ?? "local");
18320
+ if (op) {
18321
+ tui.select(op.id);
18322
+ tui.go("session");
18323
+ } else if (daemonUp(state2)) tui.toast({ text: o.operatorNotYet, tone: "info" });
18324
+ return true;
18325
+ }
18326
+ if (input === "s") {
18327
+ if (daemonUp(state2) && current2 && !busy) ask2(rigToggle(current2));
18328
+ return true;
18329
+ }
18330
+ if (input === "R") {
18331
+ if (daemonUp(state2) && current2 && !busy) ask2({ kind: "rig.restart", rig: current2.name });
18470
18332
  return true;
18471
18333
  }
18472
- if (input === "o" && current2 && !remote) {
18473
- const editor = (tui.env.VISUAL || tui.env.EDITOR || "vi").split(/\s+/).filter(Boolean);
18474
- tui.runInTerminal([...editor, role?.path ?? template.path]);
18334
+ if (input === "\xB1" || input === "=") {
18335
+ if (!busy && canReseat(current2)) setSeats({ rig: current2.name, counts: countsOf(seatRows(current2)), at: 0, delegation: "pairs", applying: false });
18336
+ else if (current2) tui.toast({ text: t.rigSeats.notRunning, tone: "info" });
18475
18337
  return true;
18476
18338
  }
18477
18339
  return false;
18478
18340
  });
18479
- const head = (key, text5) => ({ kind: "head", key, text: text5 });
18480
- const list3 = view === "roles" ? [
18481
- ...roles.repo.length ? [head("h:repo", t.catalog.repoRoles), ...order2.filter((r) => r.kind === "role" && r.role.source === "repo")] : [],
18482
- ...roles.home.length ? [head("h:home", t.catalog.homeRoles), ...order2.filter((r) => r.kind === "role" && r.role.source === "home")] : []
18483
- ] : [
18484
- ...templates.repo.length ? [head("h:repo", t.catalog.repoTeams), ...order2.filter((r) => r.kind === "template" && r.template.source === "repo")] : [],
18485
- ...templates.home.length ? [head("h:home", t.catalog.homeTeams), ...order2.filter((r) => r.kind === "template" && r.template.source === "home")] : []
18486
- ];
18487
- const issues = defs?.problems ?? [];
18488
- const listHeight = Math.max(3, height - 3 - 3 - 1 - (issues.length ? 2 : 0));
18489
- const inner = left2 - 4;
18490
- const roleById = (id) => defs?.roles.find((r) => r.id === id);
18491
- const autonomy = (r) => r?.autonomy ?? "ask";
18492
- const tabs = /* @__PURE__ */ jsx21(Text17, { wrap: "truncate", children: ["roles", "templates"].map((v, i) => /* @__PURE__ */ jsxs15(Text17, { children: [
18493
- i ? /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: " \xB7 " }) : null,
18494
- /* @__PURE__ */ jsx21(Text17, { color: v === view ? C.coral : C.dim, bold: v === view, children: v === view ? t.catalog[v].toUpperCase() : t.catalog[v] })
18495
- ] }, v)) });
18496
- const listBox = /* @__PURE__ */ jsxs15(TitledBox, { title: scope.label, mark: machines.length > 1 ? { text: `\xB7 ${g.machine} ${machineLabel2}`, color: machine?.ready ? C.green : C.faint } : void 0, flexGrow: 1, children: [
18497
- tabs,
18498
- defs === null ? /* @__PURE__ */ jsx21(Text17, { color: silent ? C.yellow : C.faint, children: silent ? t.catalog.noAnswer : t.catalog.loading }) : defs.error ? /* @__PURE__ */ jsx21(Text17, { color: C.yellow, children: defs.error === "outdated" ? t.catalog.outdated(machineLabel2) : t.catalog.offline(machineLabel2) }) : list3.length === 0 ? /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: view === "roles" ? t.catalog.noRole : t.catalog.noTemplateHere }) : windowed(list3, current2?.key ?? null, listHeight).map((r) => {
18499
- if (r.kind === "head") return /* @__PURE__ */ jsxs15(Text17, { wrap: "truncate", color: C.faint, children: [
18500
- " ",
18501
- r.text
18502
- ] }, r.key);
18503
- const on = r.key === current2?.key;
18504
- return /* @__PURE__ */ jsxs15(Text17, { wrap: "truncate", backgroundColor: on ? C.sel : void 0, children: [
18505
- /* @__PURE__ */ jsx21(Text17, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
18506
- r.kind === "role" ? /* @__PURE__ */ jsxs15(Fragment6, { children: [
18507
- /* @__PURE__ */ jsxs15(Text17, { color: C.violet, children: [
18508
- "@",
18509
- fit(r.role.id, 8)
18341
+ const keys = seats ? [["\u2191 \u2193", t.rigSeats.role], ["- +", t.rigSeats.count], ["\u2190 \u2192", t.rigSeats.delegationKey], ["\u21B5", t.rigSeats.apply], ["esc", t.rigSeats.cancel]] : confirm ? [["y", o.yes], ["n", o.no]] : [["tab", o.machine], ["j k", t.keys.navigate], ["\u21B5", o.unfold], ["s", o.toggle], ["R", o.restart], ["\xB1 =", t.rigSeats.key], ["+", t.rigLaunch.launchKey], ["D", o.daemonKey], ["K", o.kernelKey], ["O", o.operatorKey], ["esc", t.keys.back]];
18342
+ const crumb = [state2?.version ? o.version(state2.version) : o.title, machine?.label].filter(Boolean).join(" \xB7 ");
18343
+ return /* @__PURE__ */ jsxs16(Frame, { crumb, crumbShort: o.title, keys, children: [
18344
+ !machine ? null : rig.error ? /* @__PURE__ */ jsx21(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx21(Text17, { color: C.yellow, children: rig.error === "offline" ? o.offline : o.outdated }) }) : !state2 ? /* @__PURE__ */ jsx21(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: o.loading }) }) : !state2.installed ? /* @__PURE__ */ jsx21(TitledBox, { title: o.title, mark: { text: machine.label, color: C.dim }, flexGrow: 1, children: /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: o.notInstalled }) }) : seats && seatRig ? /* @__PURE__ */ jsx21(RigSeats, { rig: seatRig, machine: machine.label, rows, counts: seats.counts, at: seats.at, delegation: seats.delegation, plan: planOf(seatRig, rows, seats.counts), applying: seats.applying }) : /* @__PURE__ */ jsx21(Body, { state: state2, rigs, at, open, machineLabel: machine.label }),
18345
+ confirm ? /* @__PURE__ */ jsx21(ConfirmBox, { confirm }) : null,
18346
+ rig.pending ? /* @__PURE__ */ jsx21(Box16, { paddingX: 1, height: 1, children: /* @__PURE__ */ jsx21(Text17, { wrap: "truncate", color: C.blue, children: o.running(confirmOf(state2 ?? EMPTY2, rig.pending).commands.join(" && ")) }) }) : null
18347
+ ] });
18348
+ }
18349
+ var EMPTY2 = { installed: true, daemon: { state: "unknown" }, kernel: { state: "unknown" }, rigs: [], at: "" };
18350
+ function Body({ state: state2, rigs, at, open, machineLabel: machineLabel2 }) {
18351
+ const { t } = useTui();
18352
+ const o = t.openrig;
18353
+ const up = daemonUp(state2);
18354
+ const kernelColor = state2.kernel.state === "ready" ? C.green : state2.kernel.state === "starting" ? C.blue : C.faint;
18355
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", flexGrow: 1, children: [
18356
+ /* @__PURE__ */ jsxs16(Box16, { children: [
18357
+ /* @__PURE__ */ jsxs16(TitledBox, { title: `${o.daemon} \xB7 ${machineLabel2}`, flexGrow: 1, width: "50%", children: [
18358
+ /* @__PURE__ */ jsxs16(Text17, { wrap: "truncate", children: [
18359
+ /* @__PURE__ */ jsxs16(Text17, { color: up ? C.green : C.faint, children: [
18360
+ up ? "\u25CF " : "\u25CB ",
18361
+ o.daemonState[state2.daemon.state] ?? state2.daemon.state
18510
18362
  ] }),
18511
- " ",
18512
- /* @__PURE__ */ jsx21(Text17, { color: C.fg, children: fit(r.role.label, 12) }),
18513
- " ",
18514
- /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: fit(r.role.tool ? TOOL_NAME[r.role.tool] : "", 13) }),
18515
- " ",
18516
- /* @__PURE__ */ jsx21(Text17, { color: autonomyColor2(autonomy(r.role)), children: t.catalog.autonomyShort[autonomy(r.role)] })
18517
- ] }) : /* @__PURE__ */ jsxs15(Fragment6, { children: [
18518
- /* @__PURE__ */ jsx21(Text17, { color: C.violet, children: g.team }),
18519
- " ",
18520
- /* @__PURE__ */ jsx21(Text17, { color: C.fg, children: fit(r.template.name, 12) }),
18521
- " ",
18522
- /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: fit(r.template.seats.map((s) => `@${s.role}`).join(" "), Math.max(6, inner - 16)) })
18523
- ] })
18524
- ] }, r.key);
18525
- }),
18526
- issues.length ? /* @__PURE__ */ jsxs15(Fragment6, { children: [
18527
- /* @__PURE__ */ jsx21(Text17, { children: " " }),
18528
- /* @__PURE__ */ jsxs15(Text17, { wrap: "truncate", children: [
18529
- /* @__PURE__ */ jsx21(Text17, { color: C.yellow, children: t.catalog.ignored(issues.length) }),
18530
- " ",
18531
- /* @__PURE__ */ jsxs15(Text17, { color: C.faint, children: [
18532
- "! ",
18533
- t.catalog.see
18534
- ] })
18363
+ /* @__PURE__ */ jsxs16(Text17, { color: C.dim, children: [
18364
+ " ",
18365
+ up ? o.daemonMeta(state2.daemon.port, state2.daemon.pid) : state2.daemon.message ?? ""
18366
+ ] })
18367
+ ] }),
18368
+ /* @__PURE__ */ jsx21(Text17, { wrap: "truncate", color: C.faint, children: up ? o.restoreHint : o.stoppedDaemon })
18369
+ ] }),
18370
+ /* @__PURE__ */ jsxs16(TitledBox, { title: o.kernel, flexGrow: 1, width: "50%", children: [
18371
+ /* @__PURE__ */ jsxs16(Text17, { wrap: "truncate", color: up ? kernelColor : C.faint, children: [
18372
+ state2.kernel.state === "ready" ? "\u25CF " : "\u25CB ",
18373
+ o.kernelState[state2.kernel.state] ?? state2.kernel.state
18374
+ ] }),
18375
+ /* @__PURE__ */ jsx21(Text17, { wrap: "truncate", color: C.faint, children: o.kernelHint }),
18376
+ up && (state2.rigs.find((r) => r.name === "kernel")?.nodes ?? []).map((n) => /* @__PURE__ */ jsxs16(Text17, { wrap: "truncate", children: [
18377
+ /* @__PURE__ */ jsx21(Text17, { color: n.sessionStatus !== "running" ? C.faint : n.activity === "running" ? C.blue : C.green, children: "\u25C6 " }),
18378
+ /* @__PURE__ */ jsx21(Text17, { color: C.fg, children: n.logicalId.padEnd(16) }),
18379
+ /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: n.runtime.padEnd(13) }),
18380
+ /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: n.sessionStatus === "running" ? o.activity(n.activity) : n.sessionStatus }),
18381
+ n.logicalId === "operator.agent" && /* @__PURE__ */ jsxs16(Text17, { color: C.faint, children: [
18382
+ " ",
18383
+ o.operatorHint
18384
+ ] })
18385
+ ] }, n.session))
18535
18386
  ] })
18536
- ] }) : null
18387
+ ] }),
18388
+ /* @__PURE__ */ jsx21(TitledBox, { title: `${o.rigs} \xB7 ${rigs.length}`, flexGrow: 1, children: !up ? /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: o.stoppedDaemon }) : !rigs.length ? /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: o.noRigs }) : rigs.map((r, i) => /* @__PURE__ */ jsx21(RigRow, { rig: r, selected: i === at, open: open.has(r.id) }, r.id)) })
18537
18389
  ] });
18538
- const field = (label, value) => /* @__PURE__ */ jsxs15(Text17, { wrap: "truncate", children: [
18539
- /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: fit(label, 11) }),
18540
- value
18390
+ }
18391
+ function RigRow({ rig, selected, open }) {
18392
+ const { t } = useTui();
18393
+ const o = t.openrig;
18394
+ const look = rigLook(rig), l = LOOK[look];
18395
+ const onPurpose = look === "stopped" && rig.nodes.length > 0 && rig.nodes.every((n) => n.stopped);
18396
+ const note = rig.attention > 0 ? { text: o.attention(rig.attention), color: C.yellow } : onPurpose ? { text: o.stoppedOnPurpose, color: C.dim } : null;
18397
+ const when = [rig.uptime ? o.since(rig.uptime) : "", rig.snapshot ? o.snapshot(rig.snapshot) : ""].filter(Boolean).join(" \xB7 ");
18398
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
18399
+ /* @__PURE__ */ jsx21(Box16, { backgroundColor: selected ? C.sel : void 0, children: /* @__PURE__ */ jsxs16(Text17, { wrap: "truncate", children: [
18400
+ /* @__PURE__ */ jsx21(Text17, { color: selected ? C.coral : C.faint, children: selected ? "\u258C" : " " }),
18401
+ /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: open ? "\u25BE " : "\u25B8 " }),
18402
+ /* @__PURE__ */ jsx21(Text17, { color: C.fg, bold: true, children: rig.name.padEnd(16) }),
18403
+ /* @__PURE__ */ jsx21(Text17, { color: l.color(), children: ` ${l.glyph} ${o.look[look]}`.padEnd(18) }),
18404
+ /* @__PURE__ */ jsx21(Text17, { color: C.fg, children: o.seats(rig.running, rig.seats).padEnd(7) }),
18405
+ note ? /* @__PURE__ */ jsx21(Text17, { color: note.color, children: note.text.padEnd(34) }) : /* @__PURE__ */ jsx21(Text17, { children: "".padEnd(34) }),
18406
+ /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: when })
18407
+ ] }) }),
18408
+ open ? rig.nodes.map((n) => /* @__PURE__ */ jsxs16(Text17, { wrap: "truncate", children: [
18409
+ /* @__PURE__ */ jsx21(Text17, { color: seatBusy(n) ? C.blue : C.faint, children: " \u25C6 " }),
18410
+ /* @__PURE__ */ jsx21(Text17, { color: C.fg, children: n.logicalId.padEnd(18) }),
18411
+ /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: [n.runtime, n.model].filter(Boolean).join(" \xB7 ").padEnd(26) }),
18412
+ /* @__PURE__ */ jsx21(Text17, { color: n.activity === "running" ? C.blue : C.dim, children: o.activity(n.activity).padEnd(12) }),
18413
+ /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: o.work(n.work) })
18414
+ ] }, n.logicalId)) : null
18541
18415
  ] });
18542
- const detail = problems ? /* @__PURE__ */ jsx21(TitledBox, { title: t.catalog.problems, color: C.yellow, titleColor: C.yellow, flexGrow: 1, children: issues.length === 0 ? /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: t.catalog.noProblem }) : issues.map((p) => /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
18543
- /* @__PURE__ */ jsx21(Text17, { wrap: "truncate", color: C.fg, children: p.path }),
18544
- /* @__PURE__ */ jsx21(Text17, { wrap: "wrap", color: C.dim, children: p.reason })
18545
- ] }, p.path)) }) : role ? /* @__PURE__ */ jsxs15(Fragment6, { children: [
18546
- /* @__PURE__ */ jsxs15(TitledBox, { title: `@${role.id} \xB7 ${role.label} \xB7 ${role.source === "repo" ? t.catalog.repo : t.catalog.home}`, color: C.coral, titleColor: C.coral, children: [
18547
- field(t.catalog.tool, /* @__PURE__ */ jsx21(Text17, { color: C.fg, children: role.tool ? TOOL_NAME[role.tool] : t.catalog.toolDefault })),
18548
- field(t.catalog.model, /* @__PURE__ */ jsxs15(Text17, { children: [
18549
- /* @__PURE__ */ jsx21(Text17, { color: role.model ? C.fg : C.faint, children: role.model ?? t.catalog.toolDefault }),
18550
- " ",
18551
- /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: t.catalog.effort }),
18552
- " ",
18553
- /* @__PURE__ */ jsx21(Text17, { color: C.fg, children: role.effort ?? "\u2014" })
18554
- ] })),
18555
- field(t.catalog.autonomy, /* @__PURE__ */ jsx21(Text17, { color: autonomyColor2(autonomy(role)), children: t.ask.autonomy[autonomy(role)] })),
18556
- field(t.catalog.file, /* @__PURE__ */ jsxs15(Text17, { color: C.dim, children: [
18557
- remote ? `${t.catalog.onMachine(machineLabel2)} ` : "",
18558
- role.path
18559
- ] })),
18560
- role.shadows ? field("", /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: t.catalog.replaces(role.shadows) })) : null,
18561
- field(t.catalog.usedIn, (() => {
18562
- const xs = usedIn(role.id, defs?.templates ?? []);
18563
- return xs.length ? /* @__PURE__ */ jsx21(Text17, { children: xs.map((x) => /* @__PURE__ */ jsxs15(Text17, { color: C.violet, children: [
18564
- g.team,
18416
+ }
18417
+ function ConfirmBox({ confirm }) {
18418
+ const { t } = useTui();
18419
+ const o = t.openrig;
18420
+ return /* @__PURE__ */ jsxs16(TitledBox, { title: o.confirmTitle, color: C.coral, titleColor: C.coral, titleBold: true, double: true, children: [
18421
+ /* @__PURE__ */ jsx21(Text17, { wrap: "truncate", color: C.fg, bold: true, children: question(t, confirm.action) }),
18422
+ /* @__PURE__ */ jsx21(Text17, { wrap: "wrap", color: confirm.busy.length ? C.yellow : C.dim, children: o.busy(confirm.busy.join(", "), confirm.busy.length) }),
18423
+ confirm.commands.map((c) => /* @__PURE__ */ jsx21(Text17, { wrap: "truncate", color: C.faint, children: c }, c)),
18424
+ /* @__PURE__ */ jsxs16(Text17, { children: [
18425
+ /* @__PURE__ */ jsx21(Text17, { color: C.fg, bold: true, children: " y " }),
18426
+ /* @__PURE__ */ jsxs16(Text17, { color: C.dim, children: [
18427
+ o.yes,
18428
+ " "
18429
+ ] }),
18430
+ /* @__PURE__ */ jsx21(Text17, { color: C.fg, bold: true, children: " n " }),
18431
+ /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: o.no })
18432
+ ] })
18433
+ ] });
18434
+ }
18435
+ function operatorSession(state2, sessions, machine) {
18436
+ const seat = state2?.rigs.find((r) => r.name === "kernel")?.nodes.find((n) => n.logicalId === "operator.agent")?.session;
18437
+ return seat ? sessions.find((x) => x.rig?.seat === seat && x.state !== "exited" && (x.machineId ?? "local") === machine) : void 0;
18438
+ }
18439
+
18440
+ // src/tui/screens/RigLaunch.tsx
18441
+ import { useEffect as useEffect14, useMemo as useMemo6, useRef as useRef13, useState as useState15 } from "react";
18442
+ import { Box as Box17, Text as Text18, useInput as useInput7 } from "ink";
18443
+
18444
+ // src/tui/model/rigLaunch.ts
18445
+ var RUNTIMES = ["claude-code", "codex"];
18446
+ var orderSpecs = (specs) => [...specs].sort((a, b2) => a.source === b2.source ? a.name.localeCompare(b2.name) : a.source === "user" ? -1 : b2.source === "user" ? 1 : a.source.localeCompare(b2.source));
18447
+ var rolesOf = (spec) => (spec?.pods ?? []).flatMap((p) => p.members.map((m) => ({ role: `${p.id}.${m.id}`, pod: p.id, member: m.id, agent: m.agent, runtime: m.runtime, ...m.model ? { model: m.model } : {}, ...m.policy ? { policy: m.policy } : {} })));
18448
+ var emptyForm = (spec, cwd = "") => ({ spec, cwd, name: null, seats: {}, runtimes: {}, models: {}, delegation: "pairs", save: false, policy: null, policies: {}, nonInterruptive: false });
18449
+ var withSpec = (f, spec) => ({ ...f, spec, seats: {}, runtimes: {}, models: {}, policies: {} });
18450
+ function withPolicy(f, by) {
18451
+ const all2 = [null, ...RIG_POLICIES];
18452
+ return { ...f, policy: all2[(all2.indexOf(f.policy) + by + all2.length) % all2.length] };
18453
+ }
18454
+ function withRolePolicy(f, role) {
18455
+ const all2 = [void 0, ...RIG_POLICIES];
18456
+ const next3 = all2[(all2.indexOf(f.policies[role]) + 1) % all2.length];
18457
+ const { [role]: _gone, ...rest } = f.policies;
18458
+ return { ...f, policies: next3 ? { ...rest, [role]: next3 } : rest };
18459
+ }
18460
+ var yoloChosen = (f, roles) => f.policy === "yolo" || roles.some((r) => f.policies[r.role] === "yolo");
18461
+ var seatsOf = (f, role) => f.seats[role] ?? 1;
18462
+ var withSeats = (f, role, by) => ({ ...f, seats: { ...f.seats, [role]: Math.max(0, Math.min(9, seatsOf(f, role) + by)) } });
18463
+ var runtimeOf = (f, r) => f.runtimes[r.role] ?? r.runtime;
18464
+ function withRuntime(f, r, by) {
18465
+ const cur = runtimeOf(f, r), i = RUNTIMES.indexOf(cur);
18466
+ const next3 = RUNTIMES[((i < 0 ? by > 0 ? -1 : 0 : i) + by + RUNTIMES.length) % RUNTIMES.length];
18467
+ const { [r.role]: _gone, ...rest } = f.runtimes;
18468
+ return { ...f, runtimes: next3 === r.runtime ? rest : { ...rest, [r.role]: next3 } };
18469
+ }
18470
+ var modelOf = (f, r) => f.models[r.role] ?? r.model;
18471
+ function withModel(f, r, model) {
18472
+ const { [r.role]: _gone, ...rest } = f.models;
18473
+ const m = model.trim();
18474
+ return { ...f, models: m && m !== r.model ? { ...rest, [r.role]: m } : rest };
18475
+ }
18476
+ var totalSeats = (f, roles) => roles.reduce((n, r) => n + seatsOf(f, r.role), 0);
18477
+ var scaled = (f, roles) => roles.some((r) => seatsOf(f, r.role) > 1);
18478
+ var nameOf = (f) => f.name?.trim() || f.spec || "";
18479
+ function missingOf2(f, roles) {
18480
+ if (!f.spec) return "spec";
18481
+ if (!f.cwd.trim()) return "cwd";
18482
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(nameOf(f))) return "name";
18483
+ if (totalSeats(f, roles) === 0) return "seats";
18484
+ return null;
18485
+ }
18486
+ function launchOf(f, roles, plan) {
18487
+ const seats = Object.fromEntries(roles.filter((r) => seatsOf(f, r.role) !== 1).map((r) => [r.role, seatsOf(f, r.role)]));
18488
+ const runtimes = Object.fromEntries(roles.filter((r) => f.runtimes[r.role]).map((r) => [r.role, f.runtimes[r.role]]));
18489
+ const models = Object.fromEntries(roles.filter((r) => f.models[r.role]).map((r) => [r.role, f.models[r.role]]));
18490
+ const policies = Object.fromEntries(roles.filter((r) => f.policies[r.role]).map((r) => [r.role, f.policies[r.role]]));
18491
+ return {
18492
+ spec: f.spec,
18493
+ cwd: f.cwd.trim(),
18494
+ name: nameOf(f),
18495
+ ...Object.keys(seats).length ? { seats } : {},
18496
+ ...Object.keys(runtimes).length ? { runtimes } : {},
18497
+ ...Object.keys(models).length ? { models } : {},
18498
+ ...f.policy ? { policy: f.policy } : {},
18499
+ ...Object.keys(policies).length ? { policies } : {},
18500
+ ...f.nonInterruptive && yoloChosen(f, roles) ? { nonInterruptive: true } : {},
18501
+ ...scaled(f, roles) ? { delegation: f.delegation } : {},
18502
+ ...f.save ? { save: true } : {},
18503
+ ...plan ? { plan: true } : {}
18504
+ };
18505
+ }
18506
+ var folderChoices = (projects, machine) => (projects?.projects ?? []).flatMap((p) => p.repos.filter((r) => r.machine === machine).map((r) => ({ project: p.name, path: r.path })));
18507
+ function nextFolder(choices2, cwd, by) {
18508
+ if (!choices2.length) return cwd;
18509
+ const i = choices2.findIndex((c) => c.path === cwd);
18510
+ return choices2[i < 0 ? by > 0 ? 0 : choices2.length - 1 : (i + by + choices2.length) % choices2.length].path;
18511
+ }
18512
+ function warningLines(warnings) {
18513
+ const groups = /* @__PURE__ */ new Map();
18514
+ for (const w of warnings) {
18515
+ const m = /^([A-Za-z0-9_-]+\.[A-Za-z0-9_-]+):\s*(.+)$/.exec(w.trim());
18516
+ const key = m ? m[2] : w.trim(), who = m ? [m[1]] : [];
18517
+ groups.set(key, [...groups.get(key) ?? [], ...who]);
18518
+ }
18519
+ return [...groups].map(([text5, seats]) => ({ text: text5, seats }));
18520
+ }
18521
+ var fieldsOf = (roles, yolo = false) => ["spec", "cwd", "name", ...roles.map((_, i) => `role:${i}`), "policy", ...yolo ? ["nonInterruptive"] : [], "delegation", "save"];
18522
+ var roleAt = (field) => field.startsWith("role:") ? Number(field.slice(5)) : null;
18523
+ function moveField(fields, cur, by) {
18524
+ const i = fields.indexOf(cur);
18525
+ return fields[i < 0 ? 0 : (i + by + fields.length) % fields.length];
18526
+ }
18527
+ var isText = (field) => field === "cwd" || field === "name";
18528
+
18529
+ // src/tui/screens/RigLaunch.tsx
18530
+ import { Fragment as Fragment6, jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
18531
+ var SPEC_ROWS = 7;
18532
+ var specLine = (s) => {
18533
+ const members = s.pods.flatMap((p) => p.members.map((m) => m.id));
18534
+ return `${members.slice(0, 4).join(" + ")}${members.length > 4 ? "\u2026" : ""} \xB7 ${members.length}`;
18535
+ };
18536
+ function RigLaunchScreen() {
18537
+ const tui = useTui();
18538
+ const { t, g, machines, projects, client, toast, go, small } = tui;
18539
+ const l = t.rigLaunch;
18540
+ const machine = machines.find((m) => m.id === tui.kept.rigMachine) ?? machines[0] ?? null;
18541
+ const machineId = machine?.id ?? null;
18542
+ const specsView = useRigSpecs(client, machineId);
18543
+ const specs = useMemo6(() => orderSpecs(specsView.specs), [specsView.specs]);
18544
+ const folders = useMemo6(() => folderChoices(projects, machineId), [projects, machineId]);
18545
+ const [form, setFormState] = useState15(() => emptyForm(null, folders[0]?.path ?? ""));
18546
+ const [field, setField] = useState15("spec");
18547
+ const [modelAt, setModelAt] = useState15(null);
18548
+ const [modelDraft, setModelDraft] = useState15("");
18549
+ const [busy, setBusy] = useState15(null);
18550
+ const [result, setResult] = useState15(null);
18551
+ const alive = useRef13(true);
18552
+ useEffect14(() => () => {
18553
+ alive.current = false;
18554
+ }, []);
18555
+ useEditing(true);
18556
+ const spec = specs.find((s) => s.name === form.spec);
18557
+ const roles = rolesOf(spec);
18558
+ const yolo = yoloChosen(form, roles);
18559
+ const fields = fieldsOf(roles, yolo);
18560
+ useEffect14(() => {
18561
+ if (!form.spec && specs[0]) setFormState((f) => withSpec(f, specs[0].name));
18562
+ }, [specs]);
18563
+ useEffect14(() => {
18564
+ if (!form.cwd && folders[0]) setFormState((f) => ({ ...f, cwd: folders[0].path }));
18565
+ }, [folders]);
18566
+ const setForm = (fn) => {
18567
+ setFormState(fn);
18568
+ setResult(null);
18569
+ };
18570
+ const submit = (plan) => {
18571
+ if (busy || !machineId) return;
18572
+ const missing = missingOf2(form, roles);
18573
+ if (missing) {
18574
+ toast({ text: l.missing[missing], tone: "info" });
18575
+ return;
18576
+ }
18577
+ setBusy(plan ? "check" : "launch");
18578
+ setResult(null);
18579
+ const launch2 = launchOf(form, roles, plan);
18580
+ void launchRig(client, machineId, launch2).then((r) => {
18581
+ if (!alive.current) return;
18582
+ setBusy(null);
18583
+ setResult(r);
18584
+ if (!r.ok) {
18585
+ toast({ text: l.why[r.error ?? ""] ?? r.error ?? "", tone: "error" });
18586
+ return;
18587
+ }
18588
+ if (!plan) {
18589
+ toast({ text: l.launched(launch2.name, r.seats ?? totalSeats(form, roles)), tone: "ok" });
18590
+ go("openrig");
18591
+ }
18592
+ });
18593
+ };
18594
+ const role = roleAt(field) !== null ? roles[roleAt(field)] : void 0;
18595
+ useInput7((input, key) => {
18596
+ if (small && !key.escape) return;
18597
+ if (busy === "launch") return;
18598
+ if (modelAt !== null) {
18599
+ if (key.tab) {
18600
+ const r = roles[modelAt];
18601
+ if (r) setForm((f) => withModel(f, r, modelDraft));
18602
+ setModelAt(null);
18603
+ }
18604
+ return;
18605
+ }
18606
+ if (key.escape) {
18607
+ go("openrig");
18608
+ return;
18609
+ }
18610
+ if (key.ctrl && input === "s") {
18611
+ submit(false);
18612
+ return;
18613
+ }
18614
+ if (key.ctrl && input === "p") {
18615
+ submit(true);
18616
+ return;
18617
+ }
18618
+ if (key.tab || key.downArrow) {
18619
+ setField((f) => moveField(fields, f, key.shift ? -1 : 1));
18620
+ return;
18621
+ }
18622
+ if (key.upArrow) {
18623
+ setField((f) => moveField(fields, f, -1));
18624
+ return;
18625
+ }
18626
+ const by = key.rightArrow ? 1 : key.leftArrow ? -1 : 0;
18627
+ if (field === "cwd" && by) {
18628
+ setForm((f) => ({ ...f, cwd: nextFolder(folders, f.cwd, by) }));
18629
+ return;
18630
+ }
18631
+ if (isText(field)) return;
18632
+ if (input === "p") {
18633
+ submit(true);
18634
+ return;
18635
+ }
18636
+ if (field === "spec" && by && specs.length) {
18637
+ const i = Math.max(0, specs.findIndex((s) => s.name === form.spec));
18638
+ setForm((f) => withSpec(f, specs[(i + by + specs.length) % specs.length].name));
18639
+ return;
18640
+ }
18641
+ if (role) {
18642
+ if (by) setForm((f) => withRuntime(f, role, by));
18643
+ else if (input === "+" || input === "=") setForm((f) => withSeats(f, role.role, 1));
18644
+ else if (input === "-") setForm((f) => withSeats(f, role.role, -1));
18645
+ else if (input === "m") {
18646
+ setModelAt(roleAt(field));
18647
+ setModelDraft(modelOf(form, role) ?? "");
18648
+ } else if (input === "o") setForm((f) => withRolePolicy(f, role.role));
18649
+ return;
18650
+ }
18651
+ if (field === "policy" && by) {
18652
+ setForm((f) => withPolicy(f, by));
18653
+ return;
18654
+ }
18655
+ if (field === "nonInterruptive" && (by || input === " " || key.return)) {
18656
+ setForm((f) => ({ ...f, nonInterruptive: !f.nonInterruptive }));
18657
+ return;
18658
+ }
18659
+ if (field === "delegation" && (by || input === " ")) {
18660
+ setForm((f) => ({ ...f, delegation: f.delegation === "pairs" ? "all" : "pairs" }));
18661
+ return;
18662
+ }
18663
+ if (field === "save" && (by || input === " " || key.return)) {
18664
+ setForm((f) => ({ ...f, save: !f.save }));
18665
+ return;
18666
+ }
18667
+ });
18668
+ const row2 = (f, label, value, width = 9) => /* @__PURE__ */ jsxs17(Box17, { flexShrink: 0, backgroundColor: field === f ? C.sel : void 0, children: [
18669
+ /* @__PURE__ */ jsx22(Text18, { color: field === f ? C.coral : C.faint, children: field === f ? "\u258C" : " " }),
18670
+ /* @__PURE__ */ jsx22(Box17, { width, flexShrink: 0, children: /* @__PURE__ */ jsx22(Text18, { color: C.dim, children: label }) }),
18671
+ /* @__PURE__ */ jsx22(Box17, { flexGrow: 1, overflow: "hidden", children: value })
18672
+ ] }, f);
18673
+ const chosen = (on, text5) => on ? /* @__PURE__ */ jsx22(Text18, { color: C.fg, bold: true, children: `${g.prev} ${text5} ${g.next}` }) : /* @__PURE__ */ jsx22(Text18, { color: C.dim, children: text5 });
18674
+ const at = Math.max(0, specs.findIndex((s) => s.name === form.spec));
18675
+ const from = Math.max(0, Math.min(at - Math.floor(SPEC_ROWS / 2), specs.length - SPEC_ROWS));
18676
+ const folderOf2 = folders.find((f) => f.path === form.cwd);
18677
+ const seats = totalSeats(form, roles);
18678
+ const stopped = specsView.error === "daemon_stopped";
18679
+ const keys = modelAt !== null ? [["\u21B5", t.keys.apply], ["esc", t.keys.cancel]] : [["tab \u2191 \u2193", t.keys.nextField], ["\u2190 \u2192", t.keys.choose], ["- +", l.count], ["m", l.modelKey], ["o", l.policyKey], ["p", l.check], ["ctrl+s", l.launch(seats)], ["esc", t.keys.cancel]];
18680
+ return /* @__PURE__ */ jsx22(Frame, { crumb: `${l.title} \xB7 ${machine?.label ?? "-"}`, crumbShort: l.title, keys, children: /* @__PURE__ */ jsxs17(Box17, { flexGrow: 1, children: [
18681
+ /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", width: "55%", flexShrink: 0, children: [
18682
+ /* @__PURE__ */ jsx22(TitledBox, { title: l.spec, color: field === "spec" ? C.coral : C.line, titleColor: field === "spec" ? C.coral : C.dim, flexShrink: 0, height: (specs.length ? Math.min(SPEC_ROWS, specs.length) : 2) + 2, children: specsView.status === "loading" ? /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: l.loading }) : stopped ? /* @__PURE__ */ jsx22(Text18, { color: C.yellow, children: l.daemonStopped }) : specsView.error ? /* @__PURE__ */ jsx22(Text18, { color: C.yellow, children: l.why[specsView.error] ?? specsView.error }) : !specs.length ? /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: l.noSpecs }) : specs.slice(from, from + SPEC_ROWS).map((s) => {
18683
+ const on = s.name === form.spec;
18684
+ return /* @__PURE__ */ jsx22(Box17, { flexShrink: 0, backgroundColor: on && field === "spec" ? C.sel : void 0, children: /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
18685
+ /* @__PURE__ */ jsxs17(Text18, { color: on ? C.coral : C.dim, children: [
18686
+ on ? g.radioOn : g.radioOff,
18687
+ " "
18688
+ ] }),
18689
+ /* @__PURE__ */ jsx22(Text18, { color: on ? C.fg : C.dim, bold: on, children: s.name.padEnd(16) }),
18690
+ /* @__PURE__ */ jsx22(Text18, { color: C.dim, children: specLine(s).padEnd(24) }),
18691
+ /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: l.source[s.source] ?? s.source })
18692
+ ] }) }, `${s.source}:${s.name}`);
18693
+ }) }),
18694
+ /* @__PURE__ */ jsxs17(TitledBox, { title: l.where, flexShrink: 0, height: folderOf2 ? 5 : 4, children: [
18695
+ row2("cwd", l.folder, /* @__PURE__ */ jsx22(Box17, { children: /* @__PURE__ */ jsx22(TextInput, { value: form.cwd, onChange: (v) => setForm((f) => ({ ...f, cwd: v })), active: field === "cwd" && modelAt === null, placeholder: l.folderHint }) })),
18696
+ folderOf2 ? /* @__PURE__ */ jsx22(Box17, { flexShrink: 0, children: /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", color: C.faint, children: [
18697
+ " ",
18698
+ g.prev,
18565
18699
  " ",
18566
- x.name,
18567
- " "
18568
- ] }, x.id)) }) : /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: t.catalog.noTemplate });
18569
- })()),
18570
- field(t.catalog.heldBy, (() => {
18571
- const xs = heldBy(role.id, d.sessions, teams, project, (s) => projects ? projectOf(s, projects) : null);
18572
- return xs.length ? /* @__PURE__ */ jsx21(Text17, { children: xs.map((x) => /* @__PURE__ */ jsxs15(Text17, { children: [
18573
- /* @__PURE__ */ jsx21(Text17, { color: C.fg, bold: true, children: x.name }),
18574
- x.team ? /* @__PURE__ */ jsxs15(Text17, { color: C.dim, children: [
18700
+ l.projectFolder(folderOf2.project),
18701
+ " ",
18702
+ g.next
18703
+ ] }) }) : null,
18704
+ row2("name", l.name, /* @__PURE__ */ jsx22(Box17, { children: /* @__PURE__ */ jsx22(TextInput, { value: form.name ?? "", onChange: (v) => setForm((f) => ({ ...f, name: v })), active: field === "name" && modelAt === null, placeholder: form.spec ?? "" }) }))
18705
+ ] }),
18706
+ /* @__PURE__ */ jsxs17(TitledBox, { title: l.roles, flexGrow: 1, flexShrink: 1, children: [
18707
+ roles.length ? /* @__PURE__ */ jsx22(Box17, { flexShrink: 0, children: /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", color: C.faint, children: l.rolesHint }) }) : null,
18708
+ roles.map((r, i) => {
18709
+ const f = `role:${i}`, on = field === f, n = seatsOf(form, r.role);
18710
+ const runtime = runtimeOf(form, r), model = modelOf(form, r);
18711
+ return /* @__PURE__ */ jsxs17(Box17, { flexShrink: 0, backgroundColor: on ? C.sel : void 0, children: [
18712
+ /* @__PURE__ */ jsx22(Text18, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
18713
+ /* @__PURE__ */ jsx22(Box17, { width: 14, flexShrink: 0, children: /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", color: C.fg, bold: true, children: r.role }) }),
18714
+ /* @__PURE__ */ jsx22(Box17, { width: 16, flexShrink: 0, children: /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", children: chosen(on, runtime) }) }),
18715
+ /* @__PURE__ */ jsx22(Box17, { width: 7, flexShrink: 0, children: /* @__PURE__ */ jsxs17(Text18, { children: [
18716
+ /* @__PURE__ */ jsx22(Text18, { color: C.dim, children: "\u2212 " }),
18717
+ /* @__PURE__ */ jsx22(Text18, { color: n === 1 ? C.fg : C.green, bold: true, children: n }),
18718
+ /* @__PURE__ */ jsx22(Text18, { color: C.dim, children: " +" })
18719
+ ] }) }),
18720
+ /* @__PURE__ */ jsx22(Box17, { width: 13, flexShrink: 0, children: /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", color: form.policies[r.role] === "yolo" ? C.red : form.policies[r.role] ? C.fg : C.faint, children: form.policies[r.role] ? l.policyOf[form.policies[r.role]] : l.asRig }) }),
18721
+ /* @__PURE__ */ jsx22(Box17, { flexGrow: 1, overflow: "hidden", children: modelAt === i ? /* @__PURE__ */ jsx22(TextInput, { value: modelDraft, onChange: setModelDraft, active: true, onSubmit: (v) => {
18722
+ setForm((x) => withModel(x, r, v));
18723
+ setModelAt(null);
18724
+ }, onEscape: () => setModelAt(null), placeholder: l.defaultModel }) : /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", color: model ? C.fg : C.faint, children: model ?? l.defaultModel }) })
18725
+ ] }, r.role);
18726
+ })
18727
+ ] }),
18728
+ /* @__PURE__ */ jsxs17(TitledBox, { title: l.permissions, flexShrink: 0, children: [
18729
+ row2("policy", "", /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
18730
+ chosen(field === "policy" || form.policy !== null, form.policy === null ? l.specPolicy(spec?.policy ?? l.noSpecPolicy) : l.policyOf[form.policy]),
18731
+ /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: ` ${[null, ...RIG_POLICIES].indexOf(form.policy) + 1}/${RIG_POLICIES.length + 1}` })
18732
+ ] }), 1),
18733
+ /* @__PURE__ */ jsx22(Box17, { flexShrink: 0, paddingLeft: 3, children: /* @__PURE__ */ jsx22(Text18, { wrap: "wrap", color: (form.policy ?? spec?.policy?.replace(/^builtin:/, "")) === "yolo" ? C.red : C.faint, children: l.policyHint[form.policy ?? spec?.policy?.replace(/^builtin:/, "") ?? "none"] ?? l.policyHint.none }) }),
18734
+ yolo ? row2("nonInterruptive", "", /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
18735
+ /* @__PURE__ */ jsx22(Text18, { color: form.nonInterruptive ? C.red : C.faint, children: form.nonInterruptive ? g.boxOn : g.boxOff }),
18736
+ /* @__PURE__ */ jsxs17(Text18, { color: C.dim, children: [
18575
18737
  " ",
18576
- x.team
18577
- ] }) : null,
18578
- " "
18579
- ] }, x.name)) }) : /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: t.catalog.nobody });
18580
- })())
18738
+ l.nonInterruptive
18739
+ ] })
18740
+ ] }), 1) : null
18741
+ ] }),
18742
+ /* @__PURE__ */ jsxs17(TitledBox, { title: l.delegation, flexShrink: 0, height: 4, children: [
18743
+ row2("delegation", "", /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", children: scaled(form, roles) ? /* @__PURE__ */ jsxs17(Fragment6, { children: [
18744
+ chosen(form.delegation === "pairs", l.pairs),
18745
+ /* @__PURE__ */ jsxs17(Text18, { color: C.faint, children: [
18746
+ " ",
18747
+ l.pairsHint,
18748
+ " "
18749
+ ] }),
18750
+ chosen(form.delegation === "all", l.all)
18751
+ ] }) : /* @__PURE__ */ jsx22(Text18, { color: C.faint, children: l.notScaled }) }), 1),
18752
+ row2("save", "", /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
18753
+ /* @__PURE__ */ jsx22(Text18, { color: form.save ? C.green : C.faint, children: form.save ? g.boxOn : g.boxOff }),
18754
+ /* @__PURE__ */ jsxs17(Text18, { color: C.dim, children: [
18755
+ " ",
18756
+ l.save,
18757
+ form.save ? ` : ${nameOf(form)}` : ""
18758
+ ] })
18759
+ ] }), 1)
18760
+ ] })
18581
18761
  ] }),
18582
- /* @__PURE__ */ jsx21(TitledBox, { title: t.catalog.instruction(role.prompt.length, ROLE_PROMPT_MAX), flexGrow: 1, children: /* @__PURE__ */ jsx21(Instruction, { text: role.prompt, empty: t.catalog.noInstruction }) })
18583
- ] }) : template ? /* @__PURE__ */ jsxs15(TitledBox, { title: `${g.team} ${template.name} \xB7 ${t.catalog.seats(template.seats.length)} \xB7 ${template.source === "repo" ? t.catalog.repo : t.catalog.home}`, color: C.coral, titleColor: C.coral, flexGrow: 1, children: [
18584
- template.seats.map((s) => {
18585
- const r = roleById(s.role);
18586
- const tool = s.tool ?? r?.tool;
18587
- return /* @__PURE__ */ jsxs15(Text17, { wrap: "truncate", children: [
18588
- /* @__PURE__ */ jsxs15(Text17, { color: C.violet, children: [
18589
- "@",
18590
- fit(s.role, 8)
18591
- ] }),
18592
- " ",
18593
- /* @__PURE__ */ jsx21(Text17, { color: C.fg, children: fit(r?.label ?? "?", 12) }),
18594
- " ",
18595
- /* @__PURE__ */ jsx21(Text17, { color: s.tool ? C.blue : C.dim, children: tool ? TOOL_NAME[tool] : t.catalog.toolDefault }),
18596
- s.model ? /* @__PURE__ */ jsxs15(Text17, { color: C.blue, children: [
18597
- " \xB7 ",
18598
- s.model
18599
- ] }) : r?.model ? /* @__PURE__ */ jsxs15(Text17, { color: C.dim, children: [
18600
- " \xB7 ",
18601
- r.model
18602
- ] }) : null,
18603
- " ",
18604
- /* @__PURE__ */ jsx21(Text17, { color: autonomyColor2(autonomy(r)), children: t.catalog.autonomyShort[autonomy(r)] })
18605
- ] }, s.role);
18606
- }),
18607
- /* @__PURE__ */ jsx21(Text17, { children: " " }),
18608
- (() => {
18609
- const auto = template.seats.filter((s) => roleById(s.role)?.autonomy === "auto").map((s) => `@${s.role}`);
18610
- return auto.length ? /* @__PURE__ */ jsxs15(Text17, { color: C.yellow, wrap: "truncate", children: [
18611
- "\u26A0 ",
18612
- t.ask.autoWarning(auto.join(", "))
18613
- ] }) : null;
18614
- })(),
18615
- field(t.catalog.file, /* @__PURE__ */ jsxs15(Text17, { color: C.dim, children: [
18616
- remote ? `${t.catalog.onMachine(machineLabel2)} ` : "",
18617
- template.path
18618
- ] })),
18619
- field(t.catalog.teams, (() => {
18620
- const xs = teams.filter((x) => x.template === template.id && x.state !== "dissolved");
18621
- return xs.length ? /* @__PURE__ */ jsx21(Text17, { children: xs.map((x) => /* @__PURE__ */ jsxs15(Text17, { children: [
18622
- /* @__PURE__ */ jsxs15(Text17, { color: C.violet, children: [
18623
- g.team,
18624
- " ",
18625
- x.name
18626
- ] }),
18627
- " ",
18628
- /* @__PURE__ */ jsx21(Text17, { color: C.dim, children: x.state === "active" ? t.catalog.active : t.catalog.resting }),
18629
- " "
18630
- ] }, x.id)) }) : /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: t.catalog.noTeam });
18631
- })())
18632
- ] }) : /* @__PURE__ */ jsx21(TitledBox, { title: t.tabs.teams, flexGrow: 1, children: /* @__PURE__ */ jsx21(Text17, { color: C.faint, children: defs === null ? silent ? "" : t.catalog.loading : defs.error ? "" : view === "roles" ? t.catalog.noRole : t.catalog.noTemplateHere }) });
18633
- const keys = [
18634
- ["j k", t.keys.navigate],
18635
- ["\u2190 \u2192", t.keys.rolesTemplates],
18636
- ["c", t.keys.folder],
18637
- ...reread ? [["r", t.keys.reread]] : [],
18638
- ...launchable ? [["\u21B5", view === "roles" ? t.keys.launchRole : t.keys.launchTeam]] : [],
18639
- ...current2 && !remote ? [["o", t.keys.openFile]] : [],
18640
- ...issues.length ? [["!", t.keys.ignored]] : [],
18641
- ["esc", t.keys.back]
18642
- ];
18643
- return /* @__PURE__ */ jsx21(Frame, { crumb: t.tabs.teams, keys, children: /* @__PURE__ */ jsxs15(Box16, { flexGrow: 1, flexDirection: wide ? "row" : "column", children: [
18644
- /* @__PURE__ */ jsx21(Box16, { flexDirection: "column", width: wide ? left2 : void 0, flexShrink: 0, flexGrow: wide ? 0 : 1, children: listBox }),
18645
- /* @__PURE__ */ jsx21(Box16, { flexDirection: "column", flexGrow: 1, children: detail })
18762
+ /* @__PURE__ */ jsx22(Preview, { busy, result, seats })
18646
18763
  ] }) });
18647
18764
  }
18765
+ function Preview({ busy, result, seats }) {
18766
+ const { t } = useTui();
18767
+ const l = t.rigLaunch;
18768
+ const check = result?.check;
18769
+ return /* @__PURE__ */ jsx22(TitledBox, { title: l.preview, flexGrow: 1, color: C.coral, titleColor: C.coral, children: busy ? /* @__PURE__ */ jsx22(Text18, { color: C.blue, children: busy === "check" ? l.checking : l.launching }) : !result ? /* @__PURE__ */ jsx22(Text18, { wrap: "wrap", color: C.faint, children: l.noCheck }) : /* @__PURE__ */ jsxs17(Fragment6, { children: [
18770
+ check ? check.status === "planned" && !check.errors.length ? /* @__PURE__ */ jsx22(Text18, { color: C.green, bold: true, children: l.passes(result.seats ?? seats) }) : /* @__PURE__ */ jsx22(Text18, { color: check.errors.length ? C.red : C.yellow, bold: true, children: l.planStatus(check.status) }) : null,
18771
+ !result.ok ? /* @__PURE__ */ jsx22(Text18, { wrap: "wrap", color: C.red, children: l.why[result.error ?? ""] ?? result.error }) : null,
18772
+ check?.errors.map((e, i) => /* @__PURE__ */ jsx22(Text18, { wrap: "wrap", color: C.red, children: e }, `e${i}`)),
18773
+ warningLines(check?.warnings ?? []).map((w, i) => /* @__PURE__ */ jsx22(Text18, { wrap: "wrap", color: C.yellow, children: l.warning(w.text, w.seats.join(", ")) }, `w${i}`)),
18774
+ result.commands.map((c) => /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", color: C.faint, children: c }, c)),
18775
+ result.yaml ? /* @__PURE__ */ jsx22(Box17, { marginTop: 1, flexDirection: "column", children: result.yaml.split("\n").map((line, i) => /* @__PURE__ */ jsx22(YamlLine, { line }, i)) }) : null
18776
+ ] }) });
18777
+ }
18778
+ function YamlLine({ line }) {
18779
+ const m = /^(\s*-?\s*)([A-Za-z0-9_.-]+:)(.*)$/.exec(line);
18780
+ if (line.trim().startsWith("#")) return /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", color: C.faint, children: line || " " });
18781
+ if (!m) return /* @__PURE__ */ jsx22(Text18, { wrap: "truncate", color: C.dim, children: line || " " });
18782
+ return /* @__PURE__ */ jsxs17(Text18, { wrap: "truncate", children: [
18783
+ /* @__PURE__ */ jsx22(Text18, { color: C.dim, children: m[1] }),
18784
+ /* @__PURE__ */ jsx22(Text18, { color: C.blue, children: m[2] }),
18785
+ /* @__PURE__ */ jsx22(Text18, { color: C.green, children: m[3] })
18786
+ ] });
18787
+ }
18648
18788
 
18649
18789
  // src/tui/App.tsx
18650
- import { jsx as jsx22 } from "react/jsx-runtime";
18790
+ import { jsx as jsx23 } from "react/jsx-runtime";
18651
18791
  var TOAST_MS = 5e3;
18652
18792
  var QUIT_MS = 2e3;
18653
18793
  function App(props) {
18654
- const [lang, setLangState] = useState14(props.lang);
18655
- const [theme, setThemeState] = useState14(props.theme ?? "auto");
18794
+ const [lang, setLangState] = useState16(props.lang);
18795
+ const [theme, setThemeState] = useState16(props.theme ?? "auto");
18656
18796
  const tone2 = toneFor(theme, props.background ?? tone());
18657
18797
  if (tone2 !== tone()) setTone(tone2);
18658
- return /* @__PURE__ */ jsx22(I18nProvider, { lang, children: /* @__PURE__ */ jsx22(Shell, { ...props, lang, setLang: setLangState, theme, setThemeState, tone: tone2 }) });
18798
+ return /* @__PURE__ */ jsx23(I18nProvider, { lang, children: /* @__PURE__ */ jsx23(Shell, { ...props, lang, setLang: setLangState, theme, setThemeState, tone: tone2 }) });
18659
18799
  }
18660
18800
  function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeState, tone: tone2, background, saveTheme, notify, glyphs, env, run: run2, readImage, openFile }) {
18661
18801
  const { exit: exit3, suspendTerminal } = useApp();
18662
18802
  const { stdout } = useStdout();
18663
- const write = useCallback((seq) => {
18803
+ const write = useCallback2((seq) => {
18664
18804
  if (seq) stdout.write(seq);
18665
18805
  }, [stdout]);
18666
18806
  const d = useDaemon();
18667
18807
  const machines = useMachines(client);
18668
18808
  const projects = useProjects(client);
18669
- const teams = useTeams(client);
18670
18809
  const tasks = useTasks(client);
18671
18810
  const fleet = client.fleet;
18672
18811
  const t = MESSAGES[lang];
18673
- const [screen, setScreen] = useState14("overview");
18674
- const [back2, setBack] = useState14("overview");
18675
- const [selectedId, setSelectedId] = useState14(null);
18676
- const [lastToast, setToast] = useState14(null);
18677
- const [editing, setEditingState] = useState14(false);
18678
- const keys = useRef13(null);
18679
- const kept = useRef13({ drafts: /* @__PURE__ */ new Map(), sent: /* @__PURE__ */ new Map(), asked: [] }).current;
18680
- const armed = useRef13(0);
18681
- const catalog = useRef13({});
18682
- useEffect13(() => {
18812
+ const [screen, setScreen] = useState16("overview");
18813
+ const [back2, setBack] = useState16("overview");
18814
+ const [selectedId, setSelectedId] = useState16(null);
18815
+ const [lastToast, setToast] = useState16(null);
18816
+ const [editing, setEditingState] = useState16(false);
18817
+ const keys = useRef14(null);
18818
+ const kept = useRef14({ drafts: /* @__PURE__ */ new Map(), sent: /* @__PURE__ */ new Map(), asked: [] }).current;
18819
+ const armed = useRef14(0);
18820
+ useEffect15(() => {
18683
18821
  if (!lastToast) return;
18684
18822
  const timer2 = setTimeout(() => setToast(null), lastToast.ms ?? TOAST_MS);
18685
18823
  return () => clearTimeout(timer2);
18686
18824
  }, [lastToast]);
18687
- const toast = useCallback((x) => setToast(x), []);
18688
- const setEditing = useCallback((on) => setEditingState(on), []);
18689
- const view = useMemo8(() => overview(d.sessions, projects, teams ?? [], tasks), [d.sessions, projects, teams, tasks]);
18690
- const order2 = useRef13([]);
18691
- useEffect13(() => {
18825
+ const toast = useCallback2((x) => setToast(x), []);
18826
+ const setEditing = useCallback2((on) => setEditingState(on), []);
18827
+ const view = useMemo7(() => overview(d.sessions, projects), [d.sessions, projects]);
18828
+ const order2 = useRef14([]);
18829
+ useEffect15(() => {
18692
18830
  setSelectedId((cur) => cur === null && view.yours[0] ? view.yours[0].id : keepSelection(view.order, cur, order2.current));
18693
18831
  order2.current = view.order;
18694
18832
  }, [view]);
18695
18833
  const selected = (selectedId ? d.byId.get(selectedId) : void 0) ?? null;
18696
- const seen = useRef13(null);
18697
- const titled = useRef13("");
18698
- useEffect13(() => {
18834
+ const seen = useRef14(null);
18835
+ const titled = useRef14("");
18836
+ useEffect15(() => {
18699
18837
  if (seen.current) for (const text5 of asked(seen.current, d.sessions, t.notify)) write(notification(text5, notify, env));
18700
18838
  seen.current = d.byId;
18701
- const next2 = title(t.notify.title(listed(d.sessions).filter(isPending).length + openDecisions(tasks).length));
18702
- if (next2 !== titled.current) {
18703
- titled.current = next2;
18704
- write(next2);
18839
+ const next3 = title(t.notify.title(listed(d.sessions).filter(isPending).length + openDecisions(tasks).length));
18840
+ if (next3 !== titled.current) {
18841
+ titled.current = next3;
18842
+ write(next3);
18705
18843
  }
18706
18844
  }, [d.byId, tasks]);
18707
18845
  const actions = useActions({ client, sessions: d.sessions, errors: d.errors, t, toast });
18708
- const go = useCallback((next2) => {
18846
+ const go = useCallback2((next3) => {
18709
18847
  setScreen((cur) => {
18710
- if (next2 === "help" || next2 === "settings") setBack(cur === "help" || cur === "settings" ? "overview" : cur);
18711
- return next2;
18848
+ if (next3 === "help" || next3 === "settings") setBack(cur === "help" || cur === "settings" ? "overview" : cur);
18849
+ return next3;
18712
18850
  });
18713
18851
  }, []);
18714
- const setLang = useCallback((next2) => {
18715
- setLangState(next2);
18852
+ const setLang = useCallback2((next3) => {
18853
+ setLangState(next3);
18716
18854
  try {
18717
- saveLang(next2);
18718
- toast({ text: MESSAGES[next2].settings.saved, tone: "ok" });
18855
+ saveLang(next3);
18856
+ toast({ text: MESSAGES[next3].settings.saved, tone: "ok" });
18719
18857
  } catch (e) {
18720
- toast({ text: MESSAGES[next2].settings.saveFailed(e.message), tone: "error" });
18858
+ toast({ text: MESSAGES[next3].settings.saveFailed(e.message), tone: "error" });
18721
18859
  }
18722
18860
  }, [saveLang, setLangState, toast]);
18723
- const setTheme = useCallback((next2) => {
18724
- setThemeState(next2);
18861
+ const setTheme = useCallback2((next3) => {
18862
+ setThemeState(next3);
18725
18863
  try {
18726
- saveTheme?.(next2);
18864
+ saveTheme?.(next3);
18727
18865
  toast({ text: t.settings.saved, tone: "ok" });
18728
18866
  } catch (e) {
18729
18867
  toast({ text: t.settings.saveFailed(e.message), tone: "error" });
18730
18868
  }
18731
18869
  }, [saveTheme, setThemeState, toast, t]);
18732
- const attach = useCallback((s) => {
18870
+ const attach = useCallback2((s) => {
18733
18871
  const cmd = attachCommand(fleet, s);
18734
18872
  if ("why" in cmd) {
18735
18873
  toast({ text: cmd.why === "offline" ? t.toast.offline(s.name) : cmd.why === "omp" ? t.toast.noTerminalOmp(s.name) : t.toast.noTerminal(s.name), tone: "info" });
@@ -18741,7 +18879,7 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
18741
18879
  if (code3 !== 0) toast({ text: t.toast.attachFailed(code3), tone: "error" });
18742
18880
  });
18743
18881
  }, [fleet, suspendTerminal, run2, t, toast, write]);
18744
- const runInTerminal = useCallback((argv) => {
18882
+ const runInTerminal = useCallback2((argv) => {
18745
18883
  void suspendTerminal(async () => {
18746
18884
  const code3 = await (run2 ?? runAttached)(argv);
18747
18885
  if (code3 !== 0) toast({ text: t.toast.exited(argv[0], code3), tone: "error" });
@@ -18776,13 +18914,12 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
18776
18914
  attach,
18777
18915
  runInTerminal,
18778
18916
  env,
18779
- catalog,
18780
18917
  keys,
18781
18918
  kept,
18782
18919
  readImage: readImage ?? (() => readClipboardImage(env)),
18783
18920
  openFile: openFile ?? ((file) => openImage(file, env))
18784
18921
  };
18785
- useInput7((input, key) => {
18922
+ useInput8((input, key) => {
18786
18923
  if (key.ctrl && input === "c") {
18787
18924
  if (keys.current?.(input, key)) return;
18788
18925
  const drafts = [...kept.drafts].some(([id, x]) => x.trim() && (id === "ask" || d.byId.has(id)));
@@ -18829,8 +18966,8 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
18829
18966
  return;
18830
18967
  }
18831
18968
  });
18832
- const Body = { overview: Overview, session: SessionScreen, ask: Ask, launch: Launch, settings: Settings, teams: Teams, help: Help }[screen];
18833
- return /* @__PURE__ */ jsx22(TuiContext.Provider, { value: tui, children: /* @__PURE__ */ jsx22(Body, {}) });
18969
+ const Body2 = { overview: Overview, session: SessionScreen, ask: Ask, launch: Launch, settings: Settings, openrig: OpenRigScreen, rigLaunch: RigLaunchScreen, help: Help }[screen];
18970
+ return /* @__PURE__ */ jsx23(TuiContext.Provider, { value: tui, children: /* @__PURE__ */ jsx23(Body2, {}) });
18834
18971
  }
18835
18972
 
18836
18973
  // src/tui/fleet.ts
@@ -18885,9 +19022,6 @@ var UNICODE = {
18885
19022
  next: "\u25B8",
18886
19023
  ellipsis: "\u2026",
18887
19024
  bar: "\u2502",
18888
- /** A team's table and the common table (roles spec §3.9). */
18889
- team: "\u25A3",
18890
- common: "\u25AD",
18891
19025
  /** Markdown: a list's marks, a quote's bar, a rule, a code block's corners, a link's arrow, the end of a streamed answer. */
18892
19026
  bullet: "\u2022",
18893
19027
  subBullet: "\u25E6",
@@ -18905,7 +19039,7 @@ var UNICODE = {
18905
19039
  stale: "!",
18906
19040
  /** Links are clickable (OSC 8); without, their address follows them. */
18907
19041
  links: true,
18908
- /** A task handed between seats in « Échanges » (#92). */
19042
+ /** A task handed between sessions in « Échanges » (#92). */
18909
19043
  task: "\u25A4"
18910
19044
  };
18911
19045
  var ASCII = {
@@ -18939,8 +19073,6 @@ var ASCII = {
18939
19073
  next: ">",
18940
19074
  ellipsis: "...",
18941
19075
  bar: "|",
18942
- team: "[T]",
18943
- common: "[ ]",
18944
19076
  bullet: "-",
18945
19077
  subBullet: "o",
18946
19078
  quote: "|",
@@ -19138,7 +19270,7 @@ async function backgroundAtStart(theme, env, io) {
19138
19270
  }
19139
19271
 
19140
19272
  // src/tui/index.tsx
19141
- import { jsx as jsx23 } from "react/jsx-runtime";
19273
+ import { jsx as jsx24 } from "react/jsx-runtime";
19142
19274
  var TUI_USAGE = "ledge tui [--notify bell|osc9|off] [--here]";
19143
19275
  function tuiArgs(args) {
19144
19276
  let notify = "bell";
@@ -19177,15 +19309,15 @@ async function runTui(args) {
19177
19309
  const detach = attachClient(client);
19178
19310
  client.connect();
19179
19311
  const app = render(
19180
- /* @__PURE__ */ jsx23(
19312
+ /* @__PURE__ */ jsx24(
19181
19313
  App,
19182
19314
  {
19183
19315
  client,
19184
19316
  lang,
19185
- saveLang: (next2) => saveSettings({ ...loadSettings(), lang: next2 }),
19317
+ saveLang: (next3) => saveSettings({ ...loadSettings(), lang: next3 }),
19186
19318
  theme,
19187
19319
  background,
19188
- saveTheme: (next2) => saveSettings({ ...loadSettings(), theme: next2 }),
19320
+ saveTheme: (next3) => saveSettings({ ...loadSettings(), theme: next3 }),
19189
19321
  notify: parsed.notify,
19190
19322
  glyphs: glyphsFor(process.env),
19191
19323
  env: process.env