@positronic/cli 0.0.55 → 0.0.57
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.
- package/dist/src/cli.js +142 -2
- package/dist/src/commands/auth.js +98 -0
- package/dist/src/commands/brain.js +3 -2
- package/dist/src/commands/helpers.js +48 -10
- package/dist/src/commands/project-config-manager.js +119 -0
- package/dist/src/commands/users.js +91 -0
- package/dist/src/components/agent-chat-view.js +125 -0
- package/dist/src/components/auth-list.js +56 -0
- package/dist/src/components/auth-login.js +209 -0
- package/dist/src/components/auth-logout.js +75 -0
- package/dist/src/components/auth-status.js +88 -0
- package/dist/src/components/brain-run.js +287 -254
- package/dist/src/components/brain-top-table.js +4 -0
- package/dist/src/components/event-detail.js +364 -0
- package/dist/src/components/events-view.js +379 -0
- package/dist/src/components/state-view.js +52 -0
- package/dist/src/components/top-navigator.js +80 -6
- package/dist/src/components/types.js +1 -0
- package/dist/src/components/users-create.js +293 -0
- package/dist/src/components/users-delete.js +294 -0
- package/dist/src/components/users-keys-add.js +156 -0
- package/dist/src/components/users-keys-list.js +119 -0
- package/dist/src/components/users-keys-remove.js +299 -0
- package/dist/src/components/users-list.js +109 -0
- package/dist/src/components/watch-keyboard.js +136 -0
- package/dist/src/components/watch-machine.js +573 -0
- package/dist/src/components/watch-resolver.js +3 -2
- package/dist/src/components/watch.js +390 -36
- package/dist/src/hooks/useApi.js +80 -42
- package/dist/src/lib/request-signer.js +208 -0
- package/dist/src/lib/ssh-key-utils.js +212 -0
- package/dist/src/utils/agent-utils.js +107 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/commands/auth.d.ts +36 -0
- package/dist/types/commands/auth.d.ts.map +1 -0
- package/dist/types/commands/brain.d.ts +2 -1
- package/dist/types/commands/brain.d.ts.map +1 -1
- package/dist/types/commands/helpers.d.ts.map +1 -1
- package/dist/types/commands/project-config-manager.d.ts +43 -0
- package/dist/types/commands/project-config-manager.d.ts.map +1 -1
- package/dist/types/commands/users.d.ts +33 -0
- package/dist/types/commands/users.d.ts.map +1 -0
- package/dist/types/components/agent-chat-view.d.ts +12 -0
- package/dist/types/components/agent-chat-view.d.ts.map +1 -0
- package/dist/types/components/auth-list.d.ts +7 -0
- package/dist/types/components/auth-list.d.ts.map +1 -0
- package/dist/types/components/auth-login.d.ts +9 -0
- package/dist/types/components/auth-login.d.ts.map +1 -0
- package/dist/types/components/auth-logout.d.ts +8 -0
- package/dist/types/components/auth-logout.d.ts.map +1 -0
- package/dist/types/components/auth-status.d.ts +7 -0
- package/dist/types/components/auth-status.d.ts.map +1 -0
- package/dist/types/components/brain-run.d.ts +11 -1
- package/dist/types/components/brain-run.d.ts.map +1 -1
- package/dist/types/components/brain-top-table.d.ts.map +1 -1
- package/dist/types/components/event-detail.d.ts +10 -0
- package/dist/types/components/event-detail.d.ts.map +1 -0
- package/dist/types/components/events-view.d.ts +13 -0
- package/dist/types/components/events-view.d.ts.map +1 -0
- package/dist/types/components/state-view.d.ts +13 -0
- package/dist/types/components/state-view.d.ts.map +1 -0
- package/dist/types/components/top-navigator.d.ts.map +1 -1
- package/dist/types/components/types.d.ts +11 -0
- package/dist/types/components/types.d.ts.map +1 -0
- package/dist/types/components/users-create.d.ts +6 -0
- package/dist/types/components/users-create.d.ts.map +1 -0
- package/dist/types/components/users-delete.d.ts +7 -0
- package/dist/types/components/users-delete.d.ts.map +1 -0
- package/dist/types/components/users-keys-add.d.ts +8 -0
- package/dist/types/components/users-keys-add.d.ts.map +1 -0
- package/dist/types/components/users-keys-list.d.ts +6 -0
- package/dist/types/components/users-keys-list.d.ts.map +1 -0
- package/dist/types/components/users-keys-remove.d.ts +8 -0
- package/dist/types/components/users-keys-remove.d.ts.map +1 -0
- package/dist/types/components/users-list.d.ts +2 -0
- package/dist/types/components/users-list.d.ts.map +1 -0
- package/dist/types/components/watch-keyboard.d.ts +56 -0
- package/dist/types/components/watch-keyboard.d.ts.map +1 -0
- package/dist/types/components/watch-machine.d.ts +171 -0
- package/dist/types/components/watch-machine.d.ts.map +1 -0
- package/dist/types/components/watch-resolver.d.ts +2 -1
- package/dist/types/components/watch-resolver.d.ts.map +1 -1
- package/dist/types/components/watch.d.ts +2 -1
- package/dist/types/components/watch.d.ts.map +1 -1
- package/dist/types/hooks/useApi.d.ts.map +1 -1
- package/dist/types/hooks/useBrainMachine.d.ts +9 -3
- package/dist/types/hooks/useBrainMachine.d.ts.map +1 -1
- package/dist/types/lib/request-signer.d.ts +51 -0
- package/dist/types/lib/request-signer.d.ts.map +1 -0
- package/dist/types/lib/ssh-key-utils.d.ts +45 -0
- package/dist/types/lib/ssh-key-utils.d.ts.map +1 -0
- package/dist/types/utils/agent-utils.d.ts +20 -0
- package/dist/types/utils/agent-utils.d.ts.map +1 -0
- package/package.json +7 -4
|
@@ -0,0 +1,379 @@
|
|
|
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 } from 'react';
|
|
48
|
+
import { Text, Box, useStdout, useInput } from 'ink';
|
|
49
|
+
import { BRAIN_EVENTS } from '@positronic/core';
|
|
50
|
+
import { EventDetail } from './event-detail.js';
|
|
51
|
+
// Format relative timestamp
|
|
52
|
+
function formatTimestamp(timestamp) {
|
|
53
|
+
var now = Date.now();
|
|
54
|
+
var diff = now - timestamp.getTime();
|
|
55
|
+
var seconds = Math.floor(diff / 1000);
|
|
56
|
+
if (seconds < 60) {
|
|
57
|
+
return "".concat(seconds, "s ago");
|
|
58
|
+
}
|
|
59
|
+
var minutes = Math.floor(seconds / 60);
|
|
60
|
+
if (minutes < 60) {
|
|
61
|
+
var secs = seconds % 60;
|
|
62
|
+
return secs > 0 ? "".concat(minutes, "m ").concat(secs, "s ago") : "".concat(minutes, "m ago");
|
|
63
|
+
}
|
|
64
|
+
var hours = Math.floor(minutes / 60);
|
|
65
|
+
var mins = minutes % 60;
|
|
66
|
+
return mins > 0 ? "".concat(hours, "h ").concat(mins, "m ago") : "".concat(hours, "h ago");
|
|
67
|
+
}
|
|
68
|
+
// Truncate text to a max length
|
|
69
|
+
function truncate(text, maxLength) {
|
|
70
|
+
if (text.length <= maxLength) return text;
|
|
71
|
+
return text.slice(0, maxLength - 3) + '...';
|
|
72
|
+
}
|
|
73
|
+
// Format event for display
|
|
74
|
+
function formatEvent(event) {
|
|
75
|
+
switch(event.type){
|
|
76
|
+
case BRAIN_EVENTS.START:
|
|
77
|
+
return {
|
|
78
|
+
symbol: '[>]',
|
|
79
|
+
text: 'Brain started: "'.concat(event.brainTitle, '"'),
|
|
80
|
+
color: 'yellow'
|
|
81
|
+
};
|
|
82
|
+
case BRAIN_EVENTS.COMPLETE:
|
|
83
|
+
return {
|
|
84
|
+
symbol: '[ok]',
|
|
85
|
+
text: 'Brain completed: "'.concat(event.brainTitle, '"'),
|
|
86
|
+
color: 'green'
|
|
87
|
+
};
|
|
88
|
+
case BRAIN_EVENTS.ERROR:
|
|
89
|
+
return {
|
|
90
|
+
symbol: '[!!]',
|
|
91
|
+
text: "Error: ".concat(event.error.message),
|
|
92
|
+
color: 'red'
|
|
93
|
+
};
|
|
94
|
+
case BRAIN_EVENTS.CANCELLED:
|
|
95
|
+
return {
|
|
96
|
+
symbol: '[x]',
|
|
97
|
+
text: 'Brain cancelled: "'.concat(event.brainTitle, '"'),
|
|
98
|
+
color: 'red'
|
|
99
|
+
};
|
|
100
|
+
case BRAIN_EVENTS.STEP_START:
|
|
101
|
+
return {
|
|
102
|
+
symbol: '[.]',
|
|
103
|
+
text: 'Step started: "'.concat(event.stepTitle, '"'),
|
|
104
|
+
color: 'yellow'
|
|
105
|
+
};
|
|
106
|
+
case BRAIN_EVENTS.STEP_COMPLETE:
|
|
107
|
+
return {
|
|
108
|
+
symbol: '[+]',
|
|
109
|
+
text: 'Step completed: "'.concat(event.stepTitle, '"'),
|
|
110
|
+
color: 'green'
|
|
111
|
+
};
|
|
112
|
+
case BRAIN_EVENTS.STEP_RETRY:
|
|
113
|
+
return {
|
|
114
|
+
symbol: '[?]',
|
|
115
|
+
text: 'Step retry: "'.concat(event.stepTitle, '" (attempt ').concat(event.attempt, ")"),
|
|
116
|
+
color: 'yellow'
|
|
117
|
+
};
|
|
118
|
+
case BRAIN_EVENTS.STEP_STATUS:
|
|
119
|
+
return {
|
|
120
|
+
symbol: '[-]',
|
|
121
|
+
text: "Step status update (".concat(event.steps.length, " steps)"),
|
|
122
|
+
color: 'gray'
|
|
123
|
+
};
|
|
124
|
+
case BRAIN_EVENTS.WEBHOOK:
|
|
125
|
+
return {
|
|
126
|
+
symbol: '[~]',
|
|
127
|
+
text: "Waiting for webhook",
|
|
128
|
+
color: 'cyan'
|
|
129
|
+
};
|
|
130
|
+
case BRAIN_EVENTS.WEBHOOK_RESPONSE:
|
|
131
|
+
return {
|
|
132
|
+
symbol: '[<]',
|
|
133
|
+
text: "Webhook response received",
|
|
134
|
+
color: 'cyan'
|
|
135
|
+
};
|
|
136
|
+
case BRAIN_EVENTS.AGENT_START:
|
|
137
|
+
return {
|
|
138
|
+
symbol: '[A]',
|
|
139
|
+
text: 'Agent started: "'.concat(event.stepTitle, '"'),
|
|
140
|
+
color: 'yellow'
|
|
141
|
+
};
|
|
142
|
+
case BRAIN_EVENTS.AGENT_ITERATION:
|
|
143
|
+
return {
|
|
144
|
+
symbol: '[#]',
|
|
145
|
+
text: "Agent iteration ".concat(event.iteration),
|
|
146
|
+
color: 'gray',
|
|
147
|
+
tokens: event.tokensThisIteration
|
|
148
|
+
};
|
|
149
|
+
case BRAIN_EVENTS.AGENT_TOOL_CALL:
|
|
150
|
+
return {
|
|
151
|
+
symbol: '[T]',
|
|
152
|
+
text: "Tool call: ".concat(event.toolName),
|
|
153
|
+
color: 'white'
|
|
154
|
+
};
|
|
155
|
+
case BRAIN_EVENTS.AGENT_TOOL_RESULT:
|
|
156
|
+
return {
|
|
157
|
+
symbol: '[R]',
|
|
158
|
+
text: "Tool result: ".concat(event.toolName),
|
|
159
|
+
color: 'white'
|
|
160
|
+
};
|
|
161
|
+
case BRAIN_EVENTS.AGENT_ASSISTANT_MESSAGE:
|
|
162
|
+
return {
|
|
163
|
+
symbol: '[M]',
|
|
164
|
+
text: "Assistant: ".concat(truncate(event.content, 50)),
|
|
165
|
+
color: 'white'
|
|
166
|
+
};
|
|
167
|
+
case BRAIN_EVENTS.AGENT_COMPLETE:
|
|
168
|
+
return {
|
|
169
|
+
symbol: '[A]',
|
|
170
|
+
text: 'Agent completed: "'.concat(event.terminalToolName, '" (').concat(event.totalIterations, " iter)"),
|
|
171
|
+
color: 'green',
|
|
172
|
+
tokens: event.totalTokens
|
|
173
|
+
};
|
|
174
|
+
case BRAIN_EVENTS.AGENT_TOKEN_LIMIT:
|
|
175
|
+
return {
|
|
176
|
+
symbol: '[!]',
|
|
177
|
+
text: "Token limit reached: ".concat(event.totalTokens, "/").concat(event.maxTokens),
|
|
178
|
+
color: 'red',
|
|
179
|
+
tokens: event.totalTokens
|
|
180
|
+
};
|
|
181
|
+
case BRAIN_EVENTS.AGENT_ITERATION_LIMIT:
|
|
182
|
+
return {
|
|
183
|
+
symbol: '[!]',
|
|
184
|
+
text: "Iteration limit reached: ".concat(event.iteration, "/").concat(event.maxIterations),
|
|
185
|
+
color: 'red',
|
|
186
|
+
tokens: event.totalTokens
|
|
187
|
+
};
|
|
188
|
+
case BRAIN_EVENTS.AGENT_WEBHOOK:
|
|
189
|
+
return {
|
|
190
|
+
symbol: '[W]',
|
|
191
|
+
text: "Agent webhook: ".concat(event.toolName),
|
|
192
|
+
color: 'cyan'
|
|
193
|
+
};
|
|
194
|
+
case BRAIN_EVENTS.AGENT_RAW_RESPONSE_MESSAGE:
|
|
195
|
+
return {
|
|
196
|
+
symbol: '[~]',
|
|
197
|
+
text: "Agent response (iteration ".concat(event.iteration, ")"),
|
|
198
|
+
color: 'gray'
|
|
199
|
+
};
|
|
200
|
+
case BRAIN_EVENTS.PAUSED:
|
|
201
|
+
return {
|
|
202
|
+
symbol: '[||]',
|
|
203
|
+
text: 'Brain paused: "'.concat(event.brainTitle, '"'),
|
|
204
|
+
color: 'cyan'
|
|
205
|
+
};
|
|
206
|
+
case BRAIN_EVENTS.RESUMED:
|
|
207
|
+
return {
|
|
208
|
+
symbol: '[>]',
|
|
209
|
+
text: 'Brain resumed: "'.concat(event.brainTitle, '"'),
|
|
210
|
+
color: 'green'
|
|
211
|
+
};
|
|
212
|
+
default:
|
|
213
|
+
return {
|
|
214
|
+
symbol: '[?]',
|
|
215
|
+
text: "Unknown event: ".concat(event.type),
|
|
216
|
+
color: 'gray'
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Calculate visible window with selection support
|
|
221
|
+
function calculateVisibleWindow(eventsLength, selectedIndex, maxVisible) {
|
|
222
|
+
if (selectedIndex === null) {
|
|
223
|
+
// Auto-scroll: show most recent
|
|
224
|
+
return {
|
|
225
|
+
start: Math.max(0, eventsLength - maxVisible),
|
|
226
|
+
end: eventsLength
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// Keep selection centered when possible
|
|
230
|
+
var half = Math.floor(maxVisible / 2);
|
|
231
|
+
var start = Math.max(0, selectedIndex - half);
|
|
232
|
+
var end = Math.min(eventsLength, start + maxVisible);
|
|
233
|
+
// Adjust if we hit the end
|
|
234
|
+
if (end === eventsLength) {
|
|
235
|
+
start = Math.max(0, end - maxVisible);
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
start: start,
|
|
239
|
+
end: end
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
var EventLine = function(param) {
|
|
243
|
+
var stored = param.stored, isSelected = param.isSelected;
|
|
244
|
+
var _formatEvent = formatEvent(stored.event), symbol = _formatEvent.symbol, text = _formatEvent.text, color = _formatEvent.color, tokens = _formatEvent.tokens;
|
|
245
|
+
var timestamp = formatTimestamp(stored.timestamp);
|
|
246
|
+
return /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, {
|
|
247
|
+
color: isSelected ? 'cyan' : undefined
|
|
248
|
+
}, isSelected ? '› ' : ' '), /*#__PURE__*/ React.createElement(Text, {
|
|
249
|
+
dimColor: true
|
|
250
|
+
}, timestamp.padEnd(12), " "), /*#__PURE__*/ React.createElement(Text, {
|
|
251
|
+
color: isSelected ? 'cyan' : color
|
|
252
|
+
}, symbol, " "), /*#__PURE__*/ React.createElement(Text, {
|
|
253
|
+
color: isSelected ? 'cyan' : undefined
|
|
254
|
+
}, text), tokens !== undefined && /*#__PURE__*/ React.createElement(Text, {
|
|
255
|
+
dimColor: true
|
|
256
|
+
}, " (", tokens.toLocaleString(), " tokens)"));
|
|
257
|
+
};
|
|
258
|
+
export var EventsView = function(param) {
|
|
259
|
+
var events = param.events, _param_totalTokens = param.totalTokens, totalTokens = _param_totalTokens === void 0 ? 0 : _param_totalTokens, _param_isActive = param.isActive, isActive = _param_isActive === void 0 ? true : _param_isActive, onModeChange = param.onModeChange, onViewState = param.onViewState, controlledSelectedIndex = param.selectedIndex, onSelectedIndexChange = param.onSelectedIndexChange;
|
|
260
|
+
var stdout = useStdout().stdout;
|
|
261
|
+
var terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
|
|
262
|
+
// Reserve lines for header, footer, margins, token total
|
|
263
|
+
var maxVisible = Math.max(5, terminalHeight - 8);
|
|
264
|
+
var _useState = _sliced_to_array(useState('list'), 2), mode = _useState[0], setMode = _useState[1];
|
|
265
|
+
var _useState1 = _sliced_to_array(useState(null), 2), internalSelectedIndex = _useState1[0], setInternalSelectedIndex = _useState1[1];
|
|
266
|
+
var _useState2 = _sliced_to_array(useState(0), 2), scrollOffset = _useState2[0], setScrollOffset = _useState2[1];
|
|
267
|
+
// Use controlled value if provided, otherwise use internal state
|
|
268
|
+
var isControlled = controlledSelectedIndex !== undefined;
|
|
269
|
+
var selectedIndex = isControlled ? controlledSelectedIndex : internalSelectedIndex;
|
|
270
|
+
var setSelectedIndex = function(index) {
|
|
271
|
+
if (isControlled) {
|
|
272
|
+
onSelectedIndexChange === null || onSelectedIndexChange === void 0 ? void 0 : onSelectedIndexChange(index);
|
|
273
|
+
} else {
|
|
274
|
+
setInternalSelectedIndex(index);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
// Notify parent of mode changes
|
|
278
|
+
useEffect(function() {
|
|
279
|
+
if (onModeChange) {
|
|
280
|
+
if (mode === 'detail') {
|
|
281
|
+
onModeChange('detail');
|
|
282
|
+
} else if (selectedIndex !== null) {
|
|
283
|
+
onModeChange('navigating');
|
|
284
|
+
} else {
|
|
285
|
+
onModeChange('auto');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}, [
|
|
289
|
+
mode,
|
|
290
|
+
selectedIndex,
|
|
291
|
+
onModeChange
|
|
292
|
+
]);
|
|
293
|
+
// Keep selection valid when events change
|
|
294
|
+
useEffect(function() {
|
|
295
|
+
if (selectedIndex !== null && events.length > 0) {
|
|
296
|
+
if (selectedIndex >= events.length) {
|
|
297
|
+
setSelectedIndex(events.length - 1);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}, [
|
|
301
|
+
events.length,
|
|
302
|
+
selectedIndex
|
|
303
|
+
]);
|
|
304
|
+
// Keyboard handling
|
|
305
|
+
useInput(function(input, key) {
|
|
306
|
+
if (!isActive) return;
|
|
307
|
+
if (mode === 'list') {
|
|
308
|
+
if (key.upArrow || input === 'k') {
|
|
309
|
+
if (selectedIndex === null) {
|
|
310
|
+
// First navigation: start from last event
|
|
311
|
+
if (events.length > 0) {
|
|
312
|
+
setSelectedIndex(events.length - 1);
|
|
313
|
+
}
|
|
314
|
+
} else if (selectedIndex > 0) {
|
|
315
|
+
setSelectedIndex(selectedIndex - 1);
|
|
316
|
+
}
|
|
317
|
+
} else if (key.downArrow || input === 'j') {
|
|
318
|
+
if (selectedIndex === null) {
|
|
319
|
+
if (events.length > 0) {
|
|
320
|
+
setSelectedIndex(events.length - 1);
|
|
321
|
+
}
|
|
322
|
+
} else if (selectedIndex < events.length - 1) {
|
|
323
|
+
setSelectedIndex(selectedIndex + 1);
|
|
324
|
+
}
|
|
325
|
+
} else if (key.return && selectedIndex !== null && events.length > 0) {
|
|
326
|
+
setMode('detail');
|
|
327
|
+
setScrollOffset(0);
|
|
328
|
+
} else if (input === 's' && selectedIndex !== null && onViewState) {
|
|
329
|
+
// View state at selected event
|
|
330
|
+
onViewState(selectedIndex);
|
|
331
|
+
} else if (key.escape && selectedIndex !== null) {
|
|
332
|
+
// Return to auto-scroll mode
|
|
333
|
+
setSelectedIndex(null);
|
|
334
|
+
}
|
|
335
|
+
} else if (mode === 'detail') {
|
|
336
|
+
// Escape or 'b' to go back to list
|
|
337
|
+
if (key.escape || input === 'b') {
|
|
338
|
+
setMode('list');
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}, {
|
|
342
|
+
isActive: isActive
|
|
343
|
+
});
|
|
344
|
+
// Detail view
|
|
345
|
+
if (mode === 'detail' && selectedIndex !== null && events[selectedIndex]) {
|
|
346
|
+
return /*#__PURE__*/ React.createElement(EventDetail, {
|
|
347
|
+
stored: events[selectedIndex],
|
|
348
|
+
scrollOffset: scrollOffset,
|
|
349
|
+
onScrollChange: setScrollOffset,
|
|
350
|
+
isActive: isActive
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
// List view
|
|
354
|
+
var _calculateVisibleWindow = calculateVisibleWindow(events.length, selectedIndex, maxVisible), start = _calculateVisibleWindow.start, end = _calculateVisibleWindow.end;
|
|
355
|
+
var visibleEvents = events.slice(start, end);
|
|
356
|
+
return /*#__PURE__*/ React.createElement(Box, {
|
|
357
|
+
flexDirection: "column"
|
|
358
|
+
}, /*#__PURE__*/ React.createElement(Box, {
|
|
359
|
+
marginBottom: 1
|
|
360
|
+
}, /*#__PURE__*/ React.createElement(Text, {
|
|
361
|
+
bold: true
|
|
362
|
+
}, "Events (", events.length, " total)"), selectedIndex !== null && /*#__PURE__*/ React.createElement(Text, {
|
|
363
|
+
dimColor: true
|
|
364
|
+
}, " • Selected: ", selectedIndex + 1)), visibleEvents.length === 0 ? /*#__PURE__*/ React.createElement(Text, {
|
|
365
|
+
dimColor: true
|
|
366
|
+
}, "Waiting for events...") : visibleEvents.map(function(stored, index) {
|
|
367
|
+
return /*#__PURE__*/ React.createElement(EventLine, {
|
|
368
|
+
key: start + index,
|
|
369
|
+
stored: stored,
|
|
370
|
+
isSelected: selectedIndex === start + index
|
|
371
|
+
});
|
|
372
|
+
}), totalTokens > 0 && /*#__PURE__*/ React.createElement(Box, {
|
|
373
|
+
marginTop: 1
|
|
374
|
+
}, /*#__PURE__*/ React.createElement(Text, {
|
|
375
|
+
dimColor: true
|
|
376
|
+
}, "Total tokens: "), /*#__PURE__*/ React.createElement(Text, {
|
|
377
|
+
bold: true
|
|
378
|
+
}, totalTokens.toLocaleString())));
|
|
379
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text, Box, useStdout, useInput } from 'ink';
|
|
3
|
+
export var StateView = function(param) {
|
|
4
|
+
var state = param.state, title = param.title, scrollOffset = param.scrollOffset, onScrollChange = param.onScrollChange, _param_isActive = param.isActive, isActive = _param_isActive === void 0 ? true : _param_isActive;
|
|
5
|
+
var stdout = useStdout().stdout;
|
|
6
|
+
var terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
|
|
7
|
+
// Reserve lines for header, footer, margins
|
|
8
|
+
var maxLines = Math.max(5, terminalHeight - 6);
|
|
9
|
+
var content = JSON.stringify(state, null, 2);
|
|
10
|
+
var lines = content.split('\n');
|
|
11
|
+
var totalLines = lines.length;
|
|
12
|
+
var maxScroll = Math.max(0, totalLines - maxLines);
|
|
13
|
+
// Page size keeps 2 lines of context
|
|
14
|
+
var pageSize = Math.max(1, maxLines - 2);
|
|
15
|
+
// Handle scrolling
|
|
16
|
+
useInput(function(input, key) {
|
|
17
|
+
if (!isActive) return;
|
|
18
|
+
if (key.upArrow || input === 'k') {
|
|
19
|
+
onScrollChange(Math.max(0, scrollOffset - 1));
|
|
20
|
+
} else if (key.downArrow || input === 'j') {
|
|
21
|
+
onScrollChange(Math.min(maxScroll, scrollOffset + 1));
|
|
22
|
+
} else if (input === ' ' && !key.shift) {
|
|
23
|
+
// Space = page down
|
|
24
|
+
onScrollChange(Math.min(maxScroll, scrollOffset + pageSize));
|
|
25
|
+
} else if (input === ' ' && key.shift) {
|
|
26
|
+
// Shift+Space = page up
|
|
27
|
+
onScrollChange(Math.max(0, scrollOffset - pageSize));
|
|
28
|
+
}
|
|
29
|
+
}, {
|
|
30
|
+
isActive: isActive
|
|
31
|
+
});
|
|
32
|
+
var visibleLines = lines.slice(scrollOffset, scrollOffset + maxLines);
|
|
33
|
+
return /*#__PURE__*/ React.createElement(Box, {
|
|
34
|
+
flexDirection: "column"
|
|
35
|
+
}, /*#__PURE__*/ React.createElement(Box, {
|
|
36
|
+
marginBottom: 1
|
|
37
|
+
}, /*#__PURE__*/ React.createElement(Text, {
|
|
38
|
+
bold: true,
|
|
39
|
+
color: "cyan"
|
|
40
|
+
}, title)), /*#__PURE__*/ React.createElement(Box, {
|
|
41
|
+
flexDirection: "column",
|
|
42
|
+
marginLeft: 2
|
|
43
|
+
}, visibleLines.map(function(line, i) {
|
|
44
|
+
return /*#__PURE__*/ React.createElement(Text, {
|
|
45
|
+
key: scrollOffset + i
|
|
46
|
+
}, line);
|
|
47
|
+
})), totalLines > maxLines && /*#__PURE__*/ React.createElement(Box, {
|
|
48
|
+
marginTop: 1
|
|
49
|
+
}, /*#__PURE__*/ React.createElement(Text, {
|
|
50
|
+
dimColor: true
|
|
51
|
+
}, "Lines ", scrollOffset + 1, "-", Math.min(scrollOffset + maxLines, totalLines), " of ", totalLines)));
|
|
52
|
+
};
|
|
@@ -47,7 +47,8 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
47
47
|
import React, { useState, useEffect, useRef } from 'react';
|
|
48
48
|
import { Text, Box, useStdout, useInput, useApp } from 'ink';
|
|
49
49
|
import { EventSource } from 'eventsource';
|
|
50
|
-
import { getApiBaseUrl, isApiLocalDevMode } from '../commands/helpers.js';
|
|
50
|
+
import { getApiBaseUrl, isApiLocalDevMode, apiClient } from '../commands/helpers.js';
|
|
51
|
+
import { STATUS } from '@positronic/core';
|
|
51
52
|
import { useApiDelete } from '../hooks/useApi.js';
|
|
52
53
|
import { ErrorComponent } from './error.js';
|
|
53
54
|
import { BrainTopTable } from './brain-top-table.js';
|
|
@@ -70,6 +71,11 @@ export var TopNavigator = function(param) {
|
|
|
70
71
|
var _useState8 = _sliced_to_array(useState(false), 2), isKilling = _useState8[0], setIsKilling = _useState8[1];
|
|
71
72
|
var _useState9 = _sliced_to_array(useState(null), 2), killMessage = _useState9[0], setKillMessage = _useState9[1];
|
|
72
73
|
var _useApiDelete = useApiDelete('brain'), killBrain = _useApiDelete.execute, killError = _useApiDelete.error;
|
|
74
|
+
// Pause/resume state (for list mode)
|
|
75
|
+
var _useState10 = _sliced_to_array(useState(false), 2), isPausing = _useState10[0], setIsPausing = _useState10[1];
|
|
76
|
+
var _useState11 = _sliced_to_array(useState(null), 2), pauseMessage = _useState11[0], setPauseMessage = _useState11[1];
|
|
77
|
+
var _useState12 = _sliced_to_array(useState(false), 2), isResuming = _useState12[0], setIsResuming = _useState12[1];
|
|
78
|
+
var _useState13 = _sliced_to_array(useState(null), 2), resumeMessage = _useState13[0], setResumeMessage = _useState13[1];
|
|
73
79
|
var eventSourceRef = useRef(null);
|
|
74
80
|
var hasReceivedDataRef = useRef(false);
|
|
75
81
|
// Filter brains client-side
|
|
@@ -192,12 +198,63 @@ export var TopNavigator = function(param) {
|
|
|
192
198
|
}
|
|
193
199
|
} else if (input === 'x' && filteredBrains.length > 0 && !isKilling) {
|
|
194
200
|
setConfirmingKill(true);
|
|
201
|
+
} else if (input === 'p' && filteredBrains.length > 0 && !isPausing) {
|
|
202
|
+
var brain2 = filteredBrains[selectedIndex];
|
|
203
|
+
if (brain2 && brain2.status === STATUS.RUNNING) {
|
|
204
|
+
setIsPausing(true);
|
|
205
|
+
apiClient.fetch("/brains/runs/".concat(brain2.brainRunId, "/signals"), {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers: {
|
|
208
|
+
'Content-Type': 'application/json'
|
|
209
|
+
},
|
|
210
|
+
body: JSON.stringify({
|
|
211
|
+
type: 'PAUSE'
|
|
212
|
+
})
|
|
213
|
+
}).then(function(res) {
|
|
214
|
+
if (res.status === 202) {
|
|
215
|
+
setPauseMessage("Paused: ".concat(brain2.brainTitle));
|
|
216
|
+
setTimeout(function() {
|
|
217
|
+
return setPauseMessage(null);
|
|
218
|
+
}, 2000);
|
|
219
|
+
}
|
|
220
|
+
}).catch(function() {
|
|
221
|
+
// Silently ignore - user can retry
|
|
222
|
+
}).finally(function() {
|
|
223
|
+
return setIsPausing(false);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
} else if (input === 'r' && filteredBrains.length > 0 && !isResuming) {
|
|
227
|
+
var brain3 = filteredBrains[selectedIndex];
|
|
228
|
+
if (brain3 && brain3.status === STATUS.PAUSED) {
|
|
229
|
+
setIsResuming(true);
|
|
230
|
+
apiClient.fetch("/brains/runs/".concat(brain3.brainRunId, "/signals"), {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers: {
|
|
233
|
+
'Content-Type': 'application/json'
|
|
234
|
+
},
|
|
235
|
+
body: JSON.stringify({
|
|
236
|
+
type: 'RESUME'
|
|
237
|
+
})
|
|
238
|
+
}).then(function(res) {
|
|
239
|
+
if (res.status === 202) {
|
|
240
|
+
setResumeMessage("Resumed: ".concat(brain3.brainTitle));
|
|
241
|
+
setTimeout(function() {
|
|
242
|
+
return setResumeMessage(null);
|
|
243
|
+
}, 2000);
|
|
244
|
+
}
|
|
245
|
+
}).catch(function() {
|
|
246
|
+
// Silently ignore - user can retry
|
|
247
|
+
}).finally(function() {
|
|
248
|
+
return setIsResuming(false);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
195
251
|
} else if (input === 'q' || key.escape) {
|
|
196
252
|
exit();
|
|
197
253
|
}
|
|
198
254
|
} else if (mode === 'detail') {
|
|
199
|
-
// Detail mode navigation -
|
|
200
|
-
|
|
255
|
+
// Detail mode navigation - only escape goes back to list
|
|
256
|
+
// 'b' is reserved for Watch internal navigation (back from agent-chat, state view, etc.)
|
|
257
|
+
if (key.escape) {
|
|
201
258
|
setSelectedRunId(null);
|
|
202
259
|
setMode('list');
|
|
203
260
|
}
|
|
@@ -227,7 +284,7 @@ export var TopNavigator = function(param) {
|
|
|
227
284
|
return /*#__PURE__*/ React.createElement(Watch, {
|
|
228
285
|
runId: selectedRunId,
|
|
229
286
|
manageScreenBuffer: false,
|
|
230
|
-
footer: "
|
|
287
|
+
footer: "s state | e events | a agents | x kill | esc list"
|
|
231
288
|
});
|
|
232
289
|
}
|
|
233
290
|
// List mode - show connecting state
|
|
@@ -235,10 +292,19 @@ export var TopNavigator = function(param) {
|
|
|
235
292
|
return /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, null, "Connecting to watch service..."));
|
|
236
293
|
}
|
|
237
294
|
// Build footer based on state
|
|
238
|
-
var listFooter
|
|
295
|
+
var listFooter;
|
|
239
296
|
if (confirmingKill) {
|
|
240
297
|
var brain = filteredBrains[selectedIndex];
|
|
241
298
|
listFooter = 'Kill "'.concat(brain === null || brain === void 0 ? void 0 : brain.brainTitle, '"? (y/n)');
|
|
299
|
+
} else {
|
|
300
|
+
var selectedBrain = filteredBrains[selectedIndex];
|
|
301
|
+
var pauseResumeAction = '';
|
|
302
|
+
if ((selectedBrain === null || selectedBrain === void 0 ? void 0 : selectedBrain.status) === STATUS.RUNNING) {
|
|
303
|
+
pauseResumeAction = 'p pause • ';
|
|
304
|
+
} else if ((selectedBrain === null || selectedBrain === void 0 ? void 0 : selectedBrain.status) === STATUS.PAUSED) {
|
|
305
|
+
pauseResumeAction = 'r resume • ';
|
|
306
|
+
}
|
|
307
|
+
listFooter = "j/k or ↑/↓ select • Enter watch • ".concat(pauseResumeAction, "x kill • esc quit");
|
|
242
308
|
}
|
|
243
309
|
// List mode - show table
|
|
244
310
|
return /*#__PURE__*/ React.createElement(Box, {
|
|
@@ -253,7 +319,15 @@ export var TopNavigator = function(param) {
|
|
|
253
319
|
color: "yellow"
|
|
254
320
|
}, "Killing brain...")), killMessage && /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, {
|
|
255
321
|
color: "green"
|
|
256
|
-
}, killMessage)),
|
|
322
|
+
}, killMessage)), isPausing && /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, {
|
|
323
|
+
color: "yellow"
|
|
324
|
+
}, "Pausing brain...")), pauseMessage && /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, {
|
|
325
|
+
color: "cyan"
|
|
326
|
+
}, pauseMessage)), isResuming && /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, {
|
|
327
|
+
color: "yellow"
|
|
328
|
+
}, "Resuming brain...")), resumeMessage && /*#__PURE__*/ React.createElement(Box, null, /*#__PURE__*/ React.createElement(Text, {
|
|
329
|
+
color: "green"
|
|
330
|
+
}, resumeMessage)), killError && /*#__PURE__*/ React.createElement(ErrorComponent, {
|
|
257
331
|
error: killError
|
|
258
332
|
}));
|
|
259
333
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|