@positronic/cli 0.0.50 → 0.0.52

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 (36) hide show
  1. package/dist/src/cli.js +37 -68
  2. package/dist/src/commands/brain.js +14 -23
  3. package/dist/src/commands/server.js +28 -41
  4. package/dist/src/components/brain-rerun.js +1 -1
  5. package/dist/src/components/brain-resolver.js +18 -49
  6. package/dist/src/components/brain-run.js +26 -43
  7. package/dist/src/components/brain-top-table.js +153 -0
  8. package/dist/src/components/brain-top.js +139 -0
  9. package/dist/src/components/project-select.js +32 -52
  10. package/dist/src/components/select-list.js +102 -0
  11. package/dist/src/components/top-navigator.js +259 -0
  12. package/dist/src/components/watch-resolver.js +592 -0
  13. package/dist/src/components/watch.js +114 -112
  14. package/dist/src/hooks/useBrainMachine.js +19 -0
  15. package/dist/types/cli.d.ts.map +1 -1
  16. package/dist/types/commands/brain.d.ts +6 -3
  17. package/dist/types/commands/brain.d.ts.map +1 -1
  18. package/dist/types/commands/server.d.ts.map +1 -1
  19. package/dist/types/components/brain-resolver.d.ts.map +1 -1
  20. package/dist/types/components/brain-run.d.ts.map +1 -1
  21. package/dist/types/components/brain-top-table.d.ts +22 -0
  22. package/dist/types/components/brain-top-table.d.ts.map +1 -0
  23. package/dist/types/components/brain-top.d.ts +6 -0
  24. package/dist/types/components/brain-top.d.ts.map +1 -0
  25. package/dist/types/components/project-select.d.ts.map +1 -1
  26. package/dist/types/components/select-list.d.ts +23 -0
  27. package/dist/types/components/select-list.d.ts.map +1 -0
  28. package/dist/types/components/top-navigator.d.ts +6 -0
  29. package/dist/types/components/top-navigator.d.ts.map +1 -0
  30. package/dist/types/components/watch-resolver.d.ts +15 -0
  31. package/dist/types/components/watch-resolver.d.ts.map +1 -0
  32. package/dist/types/components/watch.d.ts +3 -1
  33. package/dist/types/components/watch.d.ts.map +1 -1
  34. package/dist/types/hooks/useBrainMachine.d.ts +37 -0
  35. package/dist/types/hooks/useBrainMachine.d.ts.map +1 -0
  36. package/package.json +5 -4
@@ -0,0 +1,259 @@
1
+ function _array_like_to_array(arr, len) {
2
+ if (len == null || len > arr.length) len = arr.length;
3
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
+ return arr2;
5
+ }
6
+ function _array_with_holes(arr) {
7
+ if (Array.isArray(arr)) return arr;
8
+ }
9
+ function _iterable_to_array_limit(arr, i) {
10
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
11
+ if (_i == null) return;
12
+ var _arr = [];
13
+ var _n = true;
14
+ var _d = false;
15
+ var _s, _e;
16
+ try {
17
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
18
+ _arr.push(_s.value);
19
+ if (i && _arr.length === i) break;
20
+ }
21
+ } catch (err) {
22
+ _d = true;
23
+ _e = err;
24
+ } finally{
25
+ try {
26
+ if (!_n && _i["return"] != null) _i["return"]();
27
+ } finally{
28
+ if (_d) throw _e;
29
+ }
30
+ }
31
+ return _arr;
32
+ }
33
+ function _non_iterable_rest() {
34
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
35
+ }
36
+ function _sliced_to_array(arr, i) {
37
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
38
+ }
39
+ function _unsupported_iterable_to_array(o, minLen) {
40
+ if (!o) return;
41
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
42
+ var n = Object.prototype.toString.call(o).slice(8, -1);
43
+ if (n === "Object" && o.constructor) n = o.constructor.name;
44
+ if (n === "Map" || n === "Set") return Array.from(n);
45
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
46
+ }
47
+ import React, { useState, useEffect, useRef } from 'react';
48
+ import { Text, Box, useStdout, useInput, useApp } from 'ink';
49
+ import { EventSource } from 'eventsource';
50
+ import { getApiBaseUrl, isApiLocalDevMode } from '../commands/helpers.js';
51
+ import { useApiDelete } from '../hooks/useApi.js';
52
+ import { ErrorComponent } from './error.js';
53
+ import { BrainTopTable } from './brain-top-table.js';
54
+ import { Watch } from './watch.js';
55
+ export var TopNavigator = function(param) {
56
+ var brainFilter = param.brainFilter;
57
+ var write = useStdout().write;
58
+ var exit = useApp().exit;
59
+ // Navigation state
60
+ var _useState = _sliced_to_array(useState('list'), 2), mode = _useState[0], setMode = _useState[1];
61
+ var _useState1 = _sliced_to_array(useState(0), 2), selectedIndex = _useState1[0], setSelectedIndex = _useState1[1];
62
+ var _useState2 = _sliced_to_array(useState(null), 2), selectedRunId = _useState2[0], setSelectedRunId = _useState2[1];
63
+ // Data state
64
+ var _useState3 = _sliced_to_array(useState([]), 2), runningBrains = _useState3[0], setRunningBrains = _useState3[1];
65
+ var _useState4 = _sliced_to_array(useState(null), 2), error = _useState4[0], setError = _useState4[1];
66
+ var _useState5 = _sliced_to_array(useState(false), 2), isConnected = _useState5[0], setIsConnected = _useState5[1];
67
+ var _useState6 = _sliced_to_array(useState(0), 2), setTick = _useState6[1];
68
+ // Kill state (for list mode)
69
+ var _useState7 = _sliced_to_array(useState(false), 2), confirmingKill = _useState7[0], setConfirmingKill = _useState7[1];
70
+ var _useState8 = _sliced_to_array(useState(false), 2), isKilling = _useState8[0], setIsKilling = _useState8[1];
71
+ var _useState9 = _sliced_to_array(useState(null), 2), killMessage = _useState9[0], setKillMessage = _useState9[1];
72
+ var _useApiDelete = useApiDelete('brain'), killBrain = _useApiDelete.execute, killError = _useApiDelete.error;
73
+ var eventSourceRef = useRef(null);
74
+ var hasReceivedDataRef = useRef(false);
75
+ // Filter brains client-side
76
+ var filteredBrains = brainFilter ? runningBrains.filter(function(b) {
77
+ return b.brainTitle.toLowerCase().includes(brainFilter.toLowerCase());
78
+ }) : runningBrains;
79
+ // Enter alternate screen buffer on mount, exit on unmount
80
+ // Skip in test environment to avoid interfering with test output capture
81
+ useEffect(function() {
82
+ if (process.env.NODE_ENV === 'test') {
83
+ return;
84
+ }
85
+ // Enter alternate screen buffer and clear
86
+ write('\x1B[?1049h\x1B[2J\x1B[H');
87
+ return function() {
88
+ // Exit alternate screen buffer
89
+ write('\x1B[?1049l');
90
+ };
91
+ }, [
92
+ write
93
+ ]);
94
+ // Update tick every second to refresh duration display (in list mode)
95
+ useEffect(function() {
96
+ if (mode !== 'list') return;
97
+ var interval = setInterval(function() {
98
+ setTick(function(t) {
99
+ return t + 1;
100
+ });
101
+ }, 1000);
102
+ return function() {
103
+ return clearInterval(interval);
104
+ };
105
+ }, [
106
+ mode
107
+ ]);
108
+ // Connect to EventSource for live updates (only in list mode)
109
+ useEffect(function() {
110
+ if (mode !== 'list') {
111
+ // Close existing connection when leaving list mode
112
+ if (eventSourceRef.current) {
113
+ eventSourceRef.current.close();
114
+ eventSourceRef.current = null;
115
+ }
116
+ return;
117
+ }
118
+ var baseUrl = getApiBaseUrl();
119
+ var url = "".concat(baseUrl, "/brains/watch");
120
+ var es = new EventSource(url);
121
+ eventSourceRef.current = es;
122
+ setIsConnected(false);
123
+ setError(null);
124
+ hasReceivedDataRef.current = false;
125
+ es.onopen = function() {
126
+ setIsConnected(true);
127
+ setError(null);
128
+ };
129
+ es.onmessage = function(event) {
130
+ try {
131
+ var data = JSON.parse(event.data);
132
+ setRunningBrains(data.runningBrains || []);
133
+ hasReceivedDataRef.current = true;
134
+ } catch (e) {
135
+ setError(new Error("Error parsing event data: ".concat(e.message)));
136
+ }
137
+ };
138
+ es.onerror = function() {
139
+ // Only show error if we haven't received any data yet
140
+ if (!hasReceivedDataRef.current) {
141
+ var errorMessage = isApiLocalDevMode() ? 'Error connecting to the local development server. Is it running? Start it with "positronic server" or "px s".' : 'Connection failed. Please check your network connection and verify the project URL is correct.';
142
+ setError(new Error(errorMessage));
143
+ setIsConnected(false);
144
+ }
145
+ es.close();
146
+ };
147
+ return function() {
148
+ es.close();
149
+ };
150
+ }, [
151
+ mode
152
+ ]);
153
+ // Keyboard handling
154
+ useInput(function(input, key) {
155
+ if (mode === 'list') {
156
+ // Handle kill confirmation
157
+ if (confirmingKill) {
158
+ if (input === 'y') {
159
+ var brain = filteredBrains[selectedIndex];
160
+ if (brain) {
161
+ setConfirmingKill(false);
162
+ setIsKilling(true);
163
+ killBrain("/brains/runs/".concat(brain.brainRunId)).then(function() {
164
+ setKillMessage("Killed: ".concat(brain.brainTitle));
165
+ setTimeout(function() {
166
+ return setKillMessage(null);
167
+ }, 2000);
168
+ }).finally(function() {
169
+ setIsKilling(false);
170
+ });
171
+ }
172
+ } else if (input === 'n' || key.escape) {
173
+ setConfirmingKill(false);
174
+ }
175
+ return;
176
+ }
177
+ // List mode navigation (arrows and vim j/k)
178
+ if ((key.upArrow || input === 'k') && filteredBrains.length > 0) {
179
+ setSelectedIndex(function(prev) {
180
+ return (prev - 1 + filteredBrains.length) % filteredBrains.length;
181
+ });
182
+ } else if ((key.downArrow || input === 'j') && filteredBrains.length > 0) {
183
+ setSelectedIndex(function(prev) {
184
+ return (prev + 1) % filteredBrains.length;
185
+ });
186
+ } else if (key.return && filteredBrains.length > 0) {
187
+ // Drill into watch view
188
+ var brain1 = filteredBrains[selectedIndex];
189
+ if (brain1) {
190
+ setSelectedRunId(brain1.brainRunId);
191
+ setMode('detail');
192
+ }
193
+ } else if (input === 'x' && filteredBrains.length > 0 && !isKilling) {
194
+ setConfirmingKill(true);
195
+ } else if (input === 'q' || key.escape) {
196
+ exit();
197
+ }
198
+ } else if (mode === 'detail') {
199
+ // Detail mode navigation - b or escape goes back to list
200
+ if (input === 'b' || key.escape) {
201
+ setSelectedRunId(null);
202
+ setMode('list');
203
+ }
204
+ }
205
+ });
206
+ // Adjust selectedIndex if brains list shrinks
207
+ useEffect(function() {
208
+ if (filteredBrains.length > 0 && selectedIndex >= filteredBrains.length) {
209
+ setSelectedIndex(filteredBrains.length - 1);
210
+ }
211
+ }, [
212
+ filteredBrains.length,
213
+ selectedIndex
214
+ ]);
215
+ // Error state
216
+ if (error) {
217
+ return /*#__PURE__*/ React.createElement(ErrorComponent, {
218
+ error: {
219
+ title: 'Connection Error',
220
+ message: error.message,
221
+ details: error.stack
222
+ }
223
+ });
224
+ }
225
+ // Detail mode - show Watch component
226
+ if (mode === 'detail' && selectedRunId) {
227
+ return /*#__PURE__*/ React.createElement(Watch, {
228
+ runId: selectedRunId,
229
+ manageScreenBuffer: false,
230
+ footer: "b back • x kill"
231
+ });
232
+ }
233
+ // List mode - show connecting state
234
+ if (!isConnected) {
235
+ return /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, null, "Connecting to watch service..."));
236
+ }
237
+ // Build footer based on state
238
+ var listFooter = 'j/k or ↑/↓ select • Enter watch • x kill • esc quit';
239
+ if (confirmingKill) {
240
+ var brain = filteredBrains[selectedIndex];
241
+ listFooter = 'Kill "'.concat(brain === null || brain === void 0 ? void 0 : brain.brainTitle, '"? (y/n)');
242
+ }
243
+ // List mode - show table
244
+ return /*#__PURE__*/ React.createElement(Box, {
245
+ flexDirection: "column"
246
+ }, /*#__PURE__*/ React.createElement(BrainTopTable, {
247
+ runningBrains: runningBrains,
248
+ selectedIndex: selectedIndex,
249
+ interactive: true,
250
+ brainFilter: brainFilter,
251
+ footer: listFooter
252
+ }), isKilling && /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, {
253
+ color: "yellow"
254
+ }, "Killing brain...")), killMessage && /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, {
255
+ color: "green"
256
+ }, killMessage)), killError && /*#__PURE__*/ React.createElement(ErrorComponent, {
257
+ error: killError
258
+ }));
259
+ };