agent-tempo 1.7.0 → 2.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/CLAUDE.md +10 -13
  2. package/README.md +21 -15
  3. package/dashboard/dist/assets/index-BbR7zIdK.js.map +1 -1
  4. package/dashboard/package.json +1 -1
  5. package/dist/activities/hard-terminate.d.ts +2 -2
  6. package/dist/activities/hard-terminate.js +3 -9
  7. package/dist/activities/maestro.d.ts +8 -13
  8. package/dist/activities/maestro.js +3 -10
  9. package/dist/activities/outbox.js +16 -2
  10. package/dist/activities/resolve.d.ts +18 -7
  11. package/dist/activities/resolve.js +58 -46
  12. package/dist/adapters/base.js +3 -0
  13. package/dist/adapters/claude-code/adapter.js +7 -0
  14. package/dist/cli/command-center-command.js +15 -1
  15. package/dist/cli/commands.d.ts +8 -0
  16. package/dist/cli/commands.js +79 -11
  17. package/dist/cli/daemon-command.js +19 -33
  18. package/dist/cli/help-text.js +9 -7
  19. package/dist/cli/home-command.d.ts +26 -0
  20. package/dist/cli/home-command.js +156 -0
  21. package/dist/cli/removed-verbs.d.ts +0 -4
  22. package/dist/cli/removed-verbs.js +23 -12
  23. package/dist/cli/sa-preflight.d.ts +0 -14
  24. package/dist/cli/sa-preflight.js +1 -17
  25. package/dist/cli/startup.d.ts +1 -10
  26. package/dist/cli/startup.js +0 -40
  27. package/dist/cli.js +34 -80
  28. package/dist/client/core.js +13 -15
  29. package/dist/client/index.d.ts +2 -1
  30. package/dist/client/interface.d.ts +5 -14
  31. package/dist/client/with-spawn.d.ts +9 -4
  32. package/dist/client/with-spawn.js +7 -10
  33. package/dist/config.d.ts +14 -13
  34. package/dist/config.js +14 -0
  35. package/dist/constants.d.ts +50 -0
  36. package/dist/constants.js +60 -1
  37. package/dist/daemon.js +81 -0
  38. package/dist/http/auth.d.ts +3 -21
  39. package/dist/http/auth.js +10 -31
  40. package/dist/http/event-types.d.ts +38 -4
  41. package/dist/http/orphans.d.ts +3 -3
  42. package/dist/http/orphans.js +3 -3
  43. package/dist/http/server.d.ts +0 -5
  44. package/dist/http/server.js +79 -29
  45. package/dist/http/snapshot.js +3 -0
  46. package/dist/http/writes.d.ts +1 -1
  47. package/dist/http/writes.js +25 -2
  48. package/dist/observability/nondeterminism-alarm.d.ts +113 -0
  49. package/dist/observability/nondeterminism-alarm.js +162 -0
  50. package/dist/palette/index.d.ts +6 -7
  51. package/dist/palette/index.js +6 -7
  52. package/dist/pi/mission-control/actions.d.ts +67 -1
  53. package/dist/pi/mission-control/actions.js +61 -0
  54. package/dist/pi/mission-control/extension.d.ts +77 -0
  55. package/dist/pi/mission-control/extension.js +171 -0
  56. package/dist/pi/workflow-client.d.ts +14 -16
  57. package/dist/pi/workflow-client.js +18 -44
  58. package/dist/server-tools.js +30 -29
  59. package/dist/server.js +42 -0
  60. package/dist/tools/action-guard.d.ts +23 -0
  61. package/dist/tools/action-guard.js +33 -0
  62. package/dist/tools/coat-check.d.ts +20 -0
  63. package/dist/tools/coat-check.js +129 -0
  64. package/dist/tools/gate.d.ts +13 -0
  65. package/dist/tools/gate.js +88 -0
  66. package/dist/tools/schedule.d.ts +18 -0
  67. package/dist/tools/schedule.js +93 -1
  68. package/dist/tools/stage.d.ts +17 -0
  69. package/dist/tools/stage.js +85 -1
  70. package/dist/tools/state.d.ts +14 -0
  71. package/dist/tools/state.js +95 -0
  72. package/dist/types.d.ts +54 -7
  73. package/dist/upgrade/boot-guard.d.ts +95 -0
  74. package/dist/upgrade/boot-guard.js +88 -0
  75. package/dist/upgrade/from-upgrade.d.ts +95 -0
  76. package/dist/upgrade/from-upgrade.js +330 -0
  77. package/dist/upgrade/phase-engine.js +4 -1
  78. package/dist/utils/duration.d.ts +3 -6
  79. package/dist/utils/duration.js +3 -6
  80. package/dist/utils/format-hosts.d.ts +1 -1
  81. package/dist/utils/format-hosts.js +0 -2
  82. package/dist/utils/grpc-shutdown-guard.js +0 -4
  83. package/dist/utils/hosts.d.ts +4 -10
  84. package/dist/utils/hosts.js +12 -28
  85. package/dist/utils/orphan-guard.d.ts +37 -0
  86. package/dist/utils/orphan-guard.js +26 -0
  87. package/dist/utils/search-attributes.d.ts +28 -18
  88. package/dist/utils/search-attributes.js +48 -18
  89. package/dist/utils/visibility-deadline.d.ts +16 -0
  90. package/dist/utils/visibility-deadline.js +16 -0
  91. package/dist/workflows/maestro-signals.d.ts +0 -13
  92. package/dist/workflows/maestro-signals.js +6 -14
  93. package/dist/workflows/maestro.js +27 -32
  94. package/dist/workflows/scheduler.js +7 -1
  95. package/dist/workflows/session.js +237 -153
  96. package/dist/workflows/signals.d.ts +9 -1
  97. package/dist/workflows/signals.js +8 -6
  98. package/package.json +1 -6
  99. package/workflow-bundle.js +478 -225
  100. package/dist/cli/legacy-migration.d.ts +0 -35
  101. package/dist/cli/legacy-migration.js +0 -341
  102. package/dist/client/ensure-conductor-spawned.d.ts +0 -35
  103. package/dist/client/ensure-conductor-spawned.js +0 -48
  104. package/dist/tui/App.d.ts +0 -85
  105. package/dist/tui/App.js +0 -1791
  106. package/dist/tui/bootstrap-types.d.ts +0 -46
  107. package/dist/tui/bootstrap-types.js +0 -7
  108. package/dist/tui/commands.d.ts +0 -71
  109. package/dist/tui/commands.js +0 -1375
  110. package/dist/tui/components/ChatView.d.ts +0 -35
  111. package/dist/tui/components/ChatView.js +0 -54
  112. package/dist/tui/components/CommandPalette.d.ts +0 -21
  113. package/dist/tui/components/CommandPalette.js +0 -67
  114. package/dist/tui/components/ConversationStream.d.ts +0 -114
  115. package/dist/tui/components/ConversationStream.js +0 -307
  116. package/dist/tui/components/CreateEnsembleWizard.d.ts +0 -19
  117. package/dist/tui/components/CreateEnsembleWizard.js +0 -223
  118. package/dist/tui/components/DestroyConfirmModal.d.ts +0 -17
  119. package/dist/tui/components/DestroyConfirmModal.js +0 -62
  120. package/dist/tui/components/ErrorView.d.ts +0 -31
  121. package/dist/tui/components/ErrorView.js +0 -129
  122. package/dist/tui/components/HomeView.d.ts +0 -54
  123. package/dist/tui/components/HomeView.js +0 -306
  124. package/dist/tui/components/LoadLineupModal.d.ts +0 -18
  125. package/dist/tui/components/LoadLineupModal.js +0 -79
  126. package/dist/tui/components/MainView.d.ts +0 -21
  127. package/dist/tui/components/MainView.js +0 -107
  128. package/dist/tui/components/NewEnsembleModal.d.ts +0 -9
  129. package/dist/tui/components/NewEnsembleModal.js +0 -73
  130. package/dist/tui/components/Picker.d.ts +0 -23
  131. package/dist/tui/components/Picker.js +0 -70
  132. package/dist/tui/components/PlayerDetailView.d.ts +0 -26
  133. package/dist/tui/components/PlayerDetailView.js +0 -118
  134. package/dist/tui/components/PromptArea.d.ts +0 -50
  135. package/dist/tui/components/PromptArea.js +0 -303
  136. package/dist/tui/components/RecruitWizard.d.ts +0 -17
  137. package/dist/tui/components/RecruitWizard.js +0 -221
  138. package/dist/tui/components/RestoreConfirmModal.d.ts +0 -18
  139. package/dist/tui/components/RestoreConfirmModal.js +0 -71
  140. package/dist/tui/components/ScheduleWizard.d.ts +0 -19
  141. package/dist/tui/components/ScheduleWizard.js +0 -259
  142. package/dist/tui/components/Splash.d.ts +0 -23
  143. package/dist/tui/components/Splash.js +0 -221
  144. package/dist/tui/components/StatusBar.d.ts +0 -48
  145. package/dist/tui/components/StatusBar.js +0 -128
  146. package/dist/tui/components/StatusOverlay.d.ts +0 -15
  147. package/dist/tui/components/StatusOverlay.js +0 -76
  148. package/dist/tui/components/TitleBar.d.ts +0 -10
  149. package/dist/tui/components/TitleBar.js +0 -21
  150. package/dist/tui/index.d.ts +0 -15
  151. package/dist/tui/index.js +0 -162
  152. package/dist/tui/ink-context.d.ts +0 -18
  153. package/dist/tui/ink-context.js +0 -59
  154. package/dist/tui/ink-loader.d.ts +0 -26
  155. package/dist/tui/ink-loader.js +0 -42
  156. package/dist/tui/removed-commands.d.ts +0 -9
  157. package/dist/tui/removed-commands.js +0 -22
  158. package/dist/tui/sse-handler.d.ts +0 -52
  159. package/dist/tui/sse-handler.js +0 -157
  160. package/dist/tui/store.d.ts +0 -598
  161. package/dist/tui/store.js +0 -753
  162. package/dist/tui/utils/format.d.ts +0 -56
  163. package/dist/tui/utils/format.js +0 -155
  164. package/dist/tui/utils/fullscreen.d.ts +0 -23
  165. package/dist/tui/utils/fullscreen.js +0 -71
  166. package/dist/tui/utils/history.d.ts +0 -10
  167. package/dist/tui/utils/history.js +0 -85
  168. package/dist/tui/utils/platform.d.ts +0 -45
  169. package/dist/tui/utils/platform.js +0 -258
  170. package/dist/tui/utils/theme.d.ts +0 -21
  171. package/dist/tui/utils/theme.js +0 -24
@@ -1,306 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.partitionEnsembles = partitionEnsembles;
37
- exports.HomeView = HomeView;
38
- /**
39
- * HomeView — three-list landing surface (Online / Paused / Offline). Polls
40
- * `listEnsembles` every 10s; `r` forces an immediate refresh.
41
- *
42
- * Offline rows whose ensemble name matches the cwd's git-root basename are
43
- * pinned to the top of the offline list with a `⬡` badge — the visual
44
- * affordance for "press Enter to restore the ensemble you created here".
45
- * Online and Paused rows are not cwd-pinned — entering them is a no-op
46
- * navigation, not a restore that needs a per-cwd hint.
47
- */
48
- const react_1 = __importStar(require("react"));
49
- const ink_context_1 = require("../ink-context");
50
- const theme_1 = require("../utils/theme");
51
- const platform_1 = require("../utils/platform");
52
- const REFRESH_INTERVAL_MS = 10_000;
53
- /**
54
- * Split + sort ensembles into Online / Paused / Offline lists with
55
- * cwd-match rows pinned to the top of the Offline list. Pure function for
56
- * testability.
57
- */
58
- function partitionEnsembles(ensembles, cwdGitRoot, cwdMatcher = defaultCwdMatcher) {
59
- const online = [];
60
- const paused = [];
61
- const offlineMatch = [];
62
- const offlineOther = [];
63
- for (const e of ensembles) {
64
- if (e.state === 'online') {
65
- online.push(e);
66
- }
67
- else if (e.state === 'paused') {
68
- paused.push(e);
69
- }
70
- else if (e.state === 'offline') {
71
- if (cwdGitRoot && cwdMatcher(e, cwdGitRoot))
72
- offlineMatch.push(e);
73
- else
74
- offlineOther.push(e);
75
- }
76
- }
77
- // Alphabetical — recency-desc will replace this once `lastActivityAt` is
78
- // threaded through `EnsembleSummary`.
79
- online.sort((a, b) => a.name.localeCompare(b.name));
80
- paused.sort((a, b) => a.name.localeCompare(b.name));
81
- offlineMatch.sort((a, b) => a.name.localeCompare(b.name));
82
- offlineOther.sort((a, b) => a.name.localeCompare(b.name));
83
- const offline = [...offlineMatch, ...offlineOther];
84
- const flat = [
85
- ...online.map((ensemble) => ({ ensemble, isCwdMatch: false })),
86
- ...paused.map((ensemble) => ({ ensemble, isCwdMatch: false })),
87
- ...offlineMatch.map((ensemble) => ({ ensemble, isCwdMatch: true })),
88
- ...offlineOther.map((ensemble) => ({ ensemble, isCwdMatch: false })),
89
- ];
90
- return { online, paused, offline, flat, cwdMatchCount: offlineMatch.length };
91
- }
92
- /**
93
- * Shallow equality over the fields HomeView renders. Keeps the 10s poll
94
- * from thrashing referential identity when the backend snapshot is
95
- * unchanged.
96
- */
97
- function ensemblesEqual(a, b) {
98
- if (a === b)
99
- return true;
100
- if (a.length !== b.length)
101
- return false;
102
- for (let i = 0; i < a.length; i++) {
103
- const x = a[i];
104
- const y = b[i];
105
- if (x.name !== y.name || x.state !== y.state
106
- || x.playerCount !== y.playerCount
107
- || x.hasConductor !== y.hasConductor
108
- || x.conductorStatus !== y.conductorStatus)
109
- return false;
110
- }
111
- return true;
112
- }
113
- /** Matches the `agent-tempo up` default: ensemble named after the repo root. */
114
- function defaultCwdMatcher(ensemble, gitRoot) {
115
- const basename = gitRoot.split(/[\\/]/).filter(Boolean).pop() ?? '';
116
- return basename.length > 0 && ensemble.name === basename;
117
- }
118
- function HomeView(props) {
119
- const { initial, client, onEnterEnsemble, onCreateEnsemble, onLoadLineup, onRestoreEnsemble, onQuit, } = props;
120
- const { Box, Text, useInput } = (0, ink_context_1.useInk)();
121
- const [ensembles, setEnsembles] = (0, react_1.useState)(initial.ensembles);
122
- const [selectedIdx, setSelectedIdx] = (0, react_1.useState)(0);
123
- const [refreshing, setRefreshing] = (0, react_1.useState)(false);
124
- const [error, setError] = (0, react_1.useState)(null);
125
- // #306: Track whether the first refresh has completed so the empty state
126
- // doesn't flash "No ensembles yet" before the discovery query returns.
127
- // Bootstrap may pass a stale or empty `initial.ensembles`; only after the
128
- // mount-time refresh do we trust an empty list as "really empty."
129
- const [firstRefreshDone, setFirstRefreshDone] = (0, react_1.useState)(initial.ensembles.length > 0);
130
- const lists = (0, react_1.useMemo)(() => partitionEnsembles(ensembles, initial.cwdGitRoot), [ensembles, initial.cwdGitRoot]);
131
- // Clamp selection whenever the list shrinks underneath the cursor.
132
- (0, react_1.useEffect)(() => {
133
- if (selectedIdx >= lists.flat.length) {
134
- setSelectedIdx(Math.max(0, lists.flat.length - 1));
135
- }
136
- }, [lists.flat.length, selectedIdx]);
137
- // Debounced refresh: setInterval fires every 10s; `r` key calls refresh()
138
- // immediately. In-flight refreshes short-circuit via `refreshing` flag so
139
- // a slow query doesn't stack behind a fast one.
140
- const refreshingRef = (0, react_1.useRef)(false);
141
- const refresh = (0, react_1.useCallback)(async () => {
142
- if (refreshingRef.current)
143
- return;
144
- refreshingRef.current = true;
145
- setRefreshing(true);
146
- try {
147
- const next = await client.listEnsembles();
148
- // Identity-stable: skip the setState when the 10s poll returns an
149
- // equivalent snapshot (common case on an idle host), so HomeView's
150
- // memoized `partitionEnsembles` result doesn't re-compute.
151
- setEnsembles((prev) => ensemblesEqual(prev, next) ? prev : next);
152
- setError(null);
153
- }
154
- catch (err) {
155
- setError(err instanceof Error ? err.message : String(err));
156
- }
157
- finally {
158
- refreshingRef.current = false;
159
- setRefreshing(false);
160
- setFirstRefreshDone(true);
161
- }
162
- }, [client]);
163
- (0, react_1.useEffect)(() => {
164
- // #306: Kick an immediate refresh on mount so the user doesn't see
165
- // "No ensembles yet" while waiting for the 10s polling timer's first
166
- // tick. The timer continues polling at the regular cadence after.
167
- refresh();
168
- const timer = setInterval(refresh, REFRESH_INTERVAL_MS);
169
- return () => clearInterval(timer);
170
- }, [refresh]);
171
- useInput((0, react_1.useCallback)((input, key) => {
172
- const total = lists.flat.length;
173
- if (key.upArrow && total > 0) {
174
- setSelectedIdx((i) => Math.max(0, i - 1));
175
- return;
176
- }
177
- if (key.downArrow && total > 0) {
178
- setSelectedIdx((i) => Math.min(total - 1, i + 1));
179
- return;
180
- }
181
- if (key.return && total > 0) {
182
- const row = lists.flat[selectedIdx];
183
- if (!row)
184
- return;
185
- // Online + Paused both navigate into the ensemble — paused ensembles
186
- // are signal-paused but their workflows are alive and the user can
187
- // run `/play` from inside. Offline requires the full restore path.
188
- if (row.ensemble.state === 'online' || row.ensemble.state === 'paused') {
189
- onEnterEnsemble(row.ensemble.name);
190
- }
191
- else if (row.ensemble.state === 'offline') {
192
- onRestoreEnsemble(row.ensemble.name);
193
- }
194
- return;
195
- }
196
- if (input === 'n' || input === 'N') {
197
- onCreateEnsemble();
198
- return;
199
- }
200
- if (input === 'l' || input === 'L') {
201
- onLoadLineup();
202
- return;
203
- }
204
- if (input === 'r' || input === 'R') {
205
- void refresh();
206
- return;
207
- }
208
- if (input === 'q' || input === 'Q') {
209
- onQuit();
210
- return;
211
- }
212
- }, [lists.flat, selectedIdx, onEnterEnsemble, onRestoreEnsemble, onCreateEnsemble, onLoadLineup, onQuit, refresh]));
213
- return renderBody({
214
- Box, Text,
215
- lists, selectedIdx,
216
- refreshing, error,
217
- badges: initial.badges,
218
- firstRefreshDone,
219
- });
220
- }
221
- function renderBody(props) {
222
- const { Box, Text, lists, selectedIdx, refreshing, error, badges, firstRefreshDone } = props;
223
- const icons = (0, platform_1.statusIcons)((0, platform_1.supportsUnicode)());
224
- const isEmpty = lists.flat.length === 0;
225
- const header = react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.accent, key: 'home-header' }, ' agent-tempo');
226
- const statusLine = react_1.default.createElement(Text, { key: 'home-status', color: theme_1.THEME.dim }, ` N new · L lineup · ↵ enter/restore · r refresh${refreshing ? ' …' : ''} · Q quit`);
227
- const children = [header, statusLine, ...renderBadges(Text, badges)];
228
- if (error) {
229
- children.push(react_1.default.createElement(Text, { key: 'home-err', color: theme_1.THEME.error }, ` refresh failed: ${error}`));
230
- }
231
- if (isEmpty) {
232
- // #306: Until the first refresh completes, show a loading state instead
233
- // of "No ensembles yet" — bootstrap may pass an empty initial list while
234
- // discovery is in flight, and flashing the "no ensembles" copy is jarring.
235
- children.push(react_1.default.createElement(Box, { key: 'home-empty', marginTop: 1 }, firstRefreshDone
236
- ? react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ' No ensembles yet. Press ', react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.text }, 'N'), ' to create one, or ', react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.text }, 'L'), ' to load a lineup.')
237
- : react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ' Loading ensembles …')));
238
- }
239
- else {
240
- // Three sections in priority order: Online → Paused → Offline.
241
- // Each section's header is skipped when the section is empty so the
242
- // landing page stays compact on a fresh ensemble (no "Paused (none)"
243
- // / "Offline (none)" noise). Cursor offsets must mirror the flat
244
- // sequence built by `partitionEnsembles`.
245
- let cursorOffset = 0;
246
- if (lists.online.length > 0) {
247
- const start = cursorOffset;
248
- children.push(react_1.default.createElement(Box, { key: 'home-online', marginTop: 1, flexDirection: 'column' }, react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.text }, ' Online'), ...lists.online.map((e, i) => renderRow({
249
- Text, key: `online-${e.name}`, icons,
250
- ensemble: e,
251
- selected: (start + i) === selectedIdx,
252
- isCwdMatch: false,
253
- }))));
254
- cursorOffset += lists.online.length;
255
- }
256
- if (lists.paused.length > 0) {
257
- const start = cursorOffset;
258
- children.push(react_1.default.createElement(Box, { key: 'home-paused', marginTop: 1, flexDirection: 'column' }, react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.text }, ' Paused'), ...lists.paused.map((e, i) => renderRow({
259
- Text, key: `paused-${e.name}`, icons,
260
- ensemble: e,
261
- selected: (start + i) === selectedIdx,
262
- isCwdMatch: false,
263
- }))));
264
- cursorOffset += lists.paused.length;
265
- }
266
- if (lists.offline.length > 0) {
267
- const start = cursorOffset;
268
- children.push(react_1.default.createElement(Box, { key: 'home-offline', marginTop: 1, flexDirection: 'column' }, react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.text }, ' Offline'), ...lists.offline.map((e, i) => renderRow({
269
- Text, key: `offline-${e.name}`, icons,
270
- ensemble: e,
271
- selected: (start + i) === selectedIdx,
272
- isCwdMatch: i < lists.cwdMatchCount,
273
- }))));
274
- }
275
- }
276
- return react_1.default.createElement(Box, { flexDirection: 'column' }, ...children);
277
- }
278
- function renderRow({ Text, key, icons, ensemble, selected, isCwdMatch }) {
279
- const cursor = selected ? '\u276F' : ' ';
280
- // Online uses the active glyph; paused + offline both use the stale
281
- // glyph (no live attachment activity). Color disambiguates: warning
282
- // for paused (transient, fast resume via /play) vs dim for offline
283
- // (needs /restore to come back).
284
- const glyph = ensemble.state === 'online' ? icons.active : icons.stale;
285
- const glyphColor = ensemble.state === 'online' ? theme_1.THEME.success
286
- : ensemble.state === 'paused' ? theme_1.THEME.warning
287
- : theme_1.THEME.dim;
288
- const cwdBadge = isCwdMatch ? '\u2B21 ' : '';
289
- const playerSuffix = ` (${ensemble.playerCount} player${ensemble.playerCount === 1 ? '' : 's'})`;
290
- return react_1.default.createElement(Text, { key, color: selected ? theme_1.THEME.text : theme_1.THEME.textMuted }, ` ${cursor} `, react_1.default.createElement(Text, { color: glyphColor }, `${glyph} `), isCwdMatch
291
- ? react_1.default.createElement(Text, { color: theme_1.THEME.accent, bold: true }, cwdBadge)
292
- : null, react_1.default.createElement(Text, { bold: selected, color: selected ? theme_1.THEME.text : theme_1.THEME.textMuted }, ensemble.name), react_1.default.createElement(Text, { color: theme_1.THEME.dim }, playerSuffix));
293
- }
294
- function renderBadges(Text, badges) {
295
- const parts = [];
296
- if (badges.orphanCount > 0) {
297
- parts.push(react_1.default.createElement(Text, { key: 'b-orph', color: theme_1.THEME.warning }, ` ${badges.orphanCount} orphan${badges.orphanCount === 1 ? '' : 's'} on this host`));
298
- }
299
- if (badges.daemonLogErrors && badges.daemonLogErrors.count > 0) {
300
- parts.push(react_1.default.createElement(Text, { key: 'b-log', color: theme_1.THEME.error }, ` daemon log: ${badges.daemonLogErrors.count} recent error${badges.daemonLogErrors.count === 1 ? '' : 's'}`));
301
- }
302
- if (badges.outdatedVersion) {
303
- parts.push(react_1.default.createElement(Text, { key: 'b-ver', color: theme_1.THEME.warning }, ` upgrade available: v${badges.outdatedVersion.latest} (${badges.outdatedVersion.severity})`));
304
- }
305
- return parts;
306
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * Lineup picker — text input for a lineup name or path. Validation runs
3
- * through `resolveLineupPath` → `loadLineup` so schema errors (e.g.
4
- * missing conductor) render inline with the CLI error text.
5
- */
6
- import React from 'react';
7
- export interface LoadLineupModalProps {
8
- /** Called with the inferred ensemble name + resolved lineup path after a successful validate. */
9
- onSubmit: (args: {
10
- ensemble: string;
11
- lineupPath: string;
12
- }) => void;
13
- onCancel: () => void;
14
- submitting?: boolean;
15
- /** Error surfaced by the caller's submit handler (e.g. spawn failure). */
16
- error?: string | null;
17
- }
18
- export declare function LoadLineupModal({ onSubmit, onCancel, submitting, error }: LoadLineupModalProps): React.ReactElement;
@@ -1,79 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.LoadLineupModal = LoadLineupModal;
37
- /**
38
- * Lineup picker — text input for a lineup name or path. Validation runs
39
- * through `resolveLineupPath` → `loadLineup` so schema errors (e.g.
40
- * missing conductor) render inline with the CLI error text.
41
- */
42
- const react_1 = __importStar(require("react"));
43
- const ink_context_1 = require("../ink-context");
44
- const theme_1 = require("../utils/theme");
45
- const loader_1 = require("../../ensemble/loader");
46
- function LoadLineupModal({ onSubmit, onCancel, submitting, error }) {
47
- const { Box, Text, TextInput, useInput } = (0, ink_context_1.useInk)();
48
- const [value, setValue] = (0, react_1.useState)('');
49
- const [validationError, setValidationError] = (0, react_1.useState)(null);
50
- useInput((0, react_1.useCallback)((_input, key) => {
51
- if (key.escape)
52
- onCancel();
53
- }, [onCancel]));
54
- const handleSubmit = (0, react_1.useCallback)((raw) => {
55
- const input = raw.trim();
56
- if (!input)
57
- return;
58
- try {
59
- const resolved = (0, loader_1.resolveLineupPath)(input);
60
- const lineup = (0, loader_1.loadLineup)(resolved.path);
61
- setValidationError(null);
62
- onSubmit({ ensemble: lineup.name, lineupPath: resolved.path });
63
- }
64
- catch (err) {
65
- setValidationError(err instanceof Error ? err.message : String(err));
66
- }
67
- }, [onSubmit]);
68
- const currentError = error ?? validationError;
69
- return react_1.default.createElement(Box, { flexDirection: 'column' }, react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.accent }, ' Load lineup'), react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ' Name (saved or shipped) or path to a .yaml file.'), react_1.default.createElement(Box, { marginTop: 1 }, react_1.default.createElement(Text, { color: theme_1.THEME.accent }, ' ? '), react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.text }, 'Lineup:')), submitting
70
- ? react_1.default.createElement(Box, { marginLeft: 3, marginTop: 1 }, react_1.default.createElement(Text, { color: theme_1.THEME.warning }, `\u2026 Starting ensemble from "${value.trim()}"\u2026`))
71
- : react_1.default.createElement(Box, { marginLeft: 3, marginTop: 1 }, react_1.default.createElement(Text, { color: theme_1.THEME.accent }, '> '), react_1.default.createElement(TextInput, {
72
- value,
73
- onChange: (next) => { setValue(next); if (validationError)
74
- setValidationError(null); },
75
- onSubmit: handleSubmit,
76
- })), currentError
77
- ? react_1.default.createElement(Box, { flexDirection: 'column', marginTop: 1 }, ...currentError.split('\n').map((line, i) => react_1.default.createElement(Text, { key: `err-${i}`, color: theme_1.THEME.error }, ` \u2717 ${line}`)))
78
- : null, react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ' Enter to load \u00B7 Esc to cancel'));
79
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * MainView — single-column ensemble dashboard.
3
- * Renders as a SINGLE Text element (1 Yoga node) with newlines for rows.
4
- * All nested Text = ink-virtual-text (0 Yoga nodes).
5
- */
6
- import React from 'react';
7
- import type { MaestroPlayerInfo, MaestroRelayMessage } from '../../types';
8
- export interface ScheduleInfo {
9
- name: string;
10
- spec: string;
11
- target: string;
12
- }
13
- export interface MainViewProps {
14
- ensemble: string;
15
- players: MaestroPlayerInfo[];
16
- messages: MaestroRelayMessage[];
17
- schedules?: ScheduleInfo[];
18
- /** Total number of static items in scrollback (for indicator). */
19
- staticItemCount?: number;
20
- }
21
- export declare function MainView({ ensemble, players, messages, schedules, staticItemCount }: MainViewProps): React.CElement<{}, React.Component<{}, any, any>>;
@@ -1,107 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.MainView = MainView;
7
- /**
8
- * MainView — single-column ensemble dashboard.
9
- * Renders as a SINGLE Text element (1 Yoga node) with newlines for rows.
10
- * All nested Text = ink-virtual-text (0 Yoga nodes).
11
- */
12
- const react_1 = __importDefault(require("react"));
13
- const ink_context_1 = require("../ink-context");
14
- const platform_1 = require("../utils/platform");
15
- const theme_1 = require("../utils/theme");
16
- const format_1 = require("../utils/format");
17
- /** Pad or truncate with ellipsis to exactly `len` chars. */
18
- function pad(s, len) {
19
- if (s.length > len)
20
- return s.slice(0, len - 1) + '\u2026';
21
- return s + ' '.repeat(len - s.length);
22
- }
23
- /** Format a timestamp to HH:MM. */
24
- function formatTime(ts) {
25
- try {
26
- const d = new Date(ts);
27
- return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
28
- }
29
- catch {
30
- return '??:??';
31
- }
32
- }
33
- /** Truncate text with ellipsis. */
34
- function truncate(text, max) {
35
- if (text.length <= max)
36
- return text;
37
- return text.slice(0, max - 1) + '\u2026';
38
- }
39
- function MainView({ ensemble, players, messages, schedules, staticItemCount }) {
40
- const { Text } = (0, ink_context_1.useInk)();
41
- const unicode = (0, platform_1.supportsUnicode)();
42
- const icons = (0, platform_1.statusIcons)(unicode);
43
- const children = [];
44
- // ── Players section ──
45
- children.push('\n');
46
- children.push(react_1.default.createElement(Text, { key: 'ph', bold: true, color: theme_1.THEME.accent }, ' Players'));
47
- children.push('\n');
48
- if (players.length === 0) {
49
- children.push(react_1.default.createElement(Text, { key: 'np', color: theme_1.THEME.dim }, ' No players'));
50
- }
51
- else {
52
- for (let i = 0; i < players.length; i++) {
53
- const p = players[i];
54
- // Collapse the attachment phase via the Option-B helpers in utils/format.
55
- const icon = p.isConductor ? icons.conductor : icons[(0, format_1.phaseToIconName)(p.phase)];
56
- const color = (0, format_1.phaseToColor)(p.phase);
57
- const typeName = p.playerType || p.agentType || '';
58
- const part = p.part || '';
59
- if (i > 0)
60
- children.push('\n');
61
- children.push(react_1.default.createElement(Text, { key: p.playerId, color }, ` ${icon} ${pad(p.playerId, 20)} ${pad(typeName, 22)} ${truncate(part, 35)}`));
62
- }
63
- }
64
- // ── Separator ──
65
- children.push('\n');
66
- children.push(react_1.default.createElement(Text, { key: 'sep1', color: theme_1.THEME.border }, ' ' + '\u2500'.repeat(50)));
67
- // ── Recent Activity section ──
68
- children.push('\n');
69
- children.push(react_1.default.createElement(Text, { key: 'ah', bold: true, color: theme_1.THEME.accent }, ' Recent Activity'));
70
- children.push('\n');
71
- const recentMessages = messages.slice(-3);
72
- if (recentMessages.length === 0 && !staticItemCount) {
73
- children.push(react_1.default.createElement(Text, { key: 'na', color: theme_1.THEME.dim }, ' No activity yet'));
74
- }
75
- else {
76
- // Scrollback indicator
77
- if (staticItemCount && staticItemCount > 0) {
78
- children.push(react_1.default.createElement(Text, { key: 'scrollback', color: theme_1.THEME.dim }, ` \u2191 ${staticItemCount} messages in scrollback (scroll up in terminal)`));
79
- children.push('\n');
80
- }
81
- // Latest 3 messages as preview
82
- for (let i = 0; i < recentMessages.length; i++) {
83
- const m = recentMessages[i];
84
- const time = formatTime(m.timestamp);
85
- const msgText = truncate(m.text.replace(/\n/g, ' '), 60);
86
- if (i > 0)
87
- children.push('\n');
88
- children.push(react_1.default.createElement(Text, { key: m.id || `msg-${i}`, color: theme_1.THEME.dim }, ` ${time} ${m.from} ${icons.arrow} ${m.to}: ${msgText}`));
89
- }
90
- }
91
- // ── Schedules section (only if there are schedules) ──
92
- if (schedules && schedules.length > 0) {
93
- children.push('\n');
94
- children.push(react_1.default.createElement(Text, { key: 'sep2', color: theme_1.THEME.border }, ' ' + '\u2500'.repeat(50)));
95
- children.push('\n');
96
- children.push(react_1.default.createElement(Text, { key: 'sh', bold: true, color: theme_1.THEME.accent }, ` Schedules (${schedules.length} active)`));
97
- children.push('\n');
98
- for (let i = 0; i < schedules.length; i++) {
99
- const s = schedules[i];
100
- if (i > 0)
101
- children.push('\n');
102
- children.push(react_1.default.createElement(Text, { key: `sched-${i}`, color: theme_1.THEME.textMuted }, ` \u21BB ${pad(s.name, 20)}${pad(s.spec, 18)}${icons.arrow} ${s.target}`));
103
- }
104
- }
105
- // Single Text element — all children are ink-virtual-text (0 Yoga nodes)
106
- return react_1.default.createElement(Text, null, ...children);
107
- }
@@ -1,9 +0,0 @@
1
- /** Single-prompt blank-ensemble creation. Enter submits, Esc cancels. */
2
- import React from 'react';
3
- export interface NewEnsembleModalProps {
4
- onSubmit: (name: string) => void;
5
- onCancel: () => void;
6
- submitting?: boolean;
7
- error?: string | null;
8
- }
9
- export declare function NewEnsembleModal({ onSubmit, onCancel, submitting, error }: NewEnsembleModalProps): React.ReactElement;
@@ -1,73 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.NewEnsembleModal = NewEnsembleModal;
37
- /** Single-prompt blank-ensemble creation. Enter submits, Esc cancels. */
38
- const react_1 = __importStar(require("react"));
39
- const ink_context_1 = require("../ink-context");
40
- const theme_1 = require("../utils/theme");
41
- const validation_1 = require("../../utils/validation");
42
- function NewEnsembleModal({ onSubmit, onCancel, submitting, error }) {
43
- const { Box, Text, TextInput, useInput } = (0, ink_context_1.useInk)();
44
- const [value, setValue] = (0, react_1.useState)('');
45
- const [validationError, setValidationError] = (0, react_1.useState)(null);
46
- useInput((0, react_1.useCallback)((_input, key) => {
47
- if (key.escape)
48
- onCancel();
49
- }, [onCancel]));
50
- const handleSubmit = (0, react_1.useCallback)((raw) => {
51
- const name = raw.trim();
52
- if (!name)
53
- return;
54
- const nameError = (0, validation_1.validateEnsembleName)(name);
55
- if (nameError) {
56
- setValidationError(nameError);
57
- return;
58
- }
59
- setValidationError(null);
60
- onSubmit(name);
61
- }, [onSubmit]);
62
- const currentError = error ?? validationError;
63
- return react_1.default.createElement(Box, { flexDirection: 'column' }, react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.accent }, ' New ensemble'), react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ' Conductor defaults: name "conductor", agent AGENT_TEMPO_DEFAULT_AGENT.'), react_1.default.createElement(Box, { marginTop: 1 }, react_1.default.createElement(Text, { color: theme_1.THEME.accent }, ' ? '), react_1.default.createElement(Text, { bold: true, color: theme_1.THEME.text }, 'Ensemble name:')), submitting
64
- ? react_1.default.createElement(Box, { marginLeft: 3, marginTop: 1 }, react_1.default.createElement(Text, { color: theme_1.THEME.warning }, `\u2026 Creating "${value.trim()}"\u2026`))
65
- : react_1.default.createElement(Box, { marginLeft: 3, marginTop: 1 }, react_1.default.createElement(Text, { color: theme_1.THEME.accent }, '> '), react_1.default.createElement(TextInput, {
66
- value,
67
- onChange: (next) => { setValue(next); if (validationError)
68
- setValidationError(null); },
69
- onSubmit: handleSubmit,
70
- })), currentError
71
- ? react_1.default.createElement(Text, { color: theme_1.THEME.error }, ` \u2717 ${currentError}`)
72
- : null, react_1.default.createElement(Text, { color: theme_1.THEME.dim }, ' Enter to create \u00B7 Esc to cancel'));
73
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * Picker — generic interactive list overlay.
3
- * Renders as a SINGLE Text element (1 Yoga node) with newlines for rows.
4
- */
5
- import React from 'react';
6
- export interface PickerItem {
7
- id: string;
8
- label: string;
9
- detail: string;
10
- meta?: string;
11
- color?: string;
12
- icon?: string;
13
- current?: boolean;
14
- /** Group header — items with the same group are rendered under a shared header. */
15
- group?: string;
16
- }
17
- export interface PickerProps {
18
- title: string;
19
- items: PickerItem[];
20
- selectedIndex: number;
21
- hint?: string;
22
- }
23
- export declare function Picker({ title, items, selectedIndex, hint }: PickerProps): React.DetailedReactHTMLElement<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> | React.CElement<{}, React.Component<{}, any, any>>;