@rind-ai/cli 0.4.1 → 0.6.1
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/bin/rind.js +5 -5
- package/lib/assistant-renderer.js +179 -265
- package/lib/choice-menu-state.js +46 -46
- package/lib/cli-input-actions.js +548 -0
- package/lib/cli-output-controller.js +460 -0
- package/lib/cli-runtime-controller.js +350 -0
- package/lib/cli-state-store.js +32 -0
- package/lib/cli-state.js +41 -0
- package/lib/command-controller.js +159 -126
- package/lib/compact-context-state.js +22 -22
- package/lib/components/assistant-message.js +169 -0
- package/lib/components/composer-area.js +25 -0
- package/lib/components/dynamic-block.js +20 -0
- package/lib/components/monitor-stack.js +35 -0
- package/lib/components/text-block.js +47 -0
- package/lib/components/tool-block.js +122 -0
- package/lib/composer-terminal.js +224 -203
- package/lib/event-controller.js +243 -242
- package/lib/frontend-cli-implementation.js +656 -1111
- package/lib/input-controller.js +75 -94
- package/lib/input-errors.js +3 -3
- package/lib/interrupt-state.js +9 -9
- package/lib/line-editor.js +541 -541
- package/lib/local-slash-commands.js +217 -0
- package/lib/markdown-lines.js +103 -0
- package/lib/model-menu-state.js +50 -50
- package/lib/one-shot-progress.js +145 -0
- package/lib/one-shot.js +228 -0
- package/lib/question-menu-state.js +61 -0
- package/lib/rendering.js +1295 -1037
- package/lib/runtime-client.js +241 -193
- package/lib/runtime-env.js +21 -21
- package/lib/runtime-protocol.js +122 -15
- package/lib/slash-command-mode.js +16 -27
- package/lib/slash-menu-state.js +59 -59
- package/lib/{background-controller.js → task-monitor-controller.js} +411 -289
- package/lib/terminal-key.js +97 -97
- package/lib/text-width.js +335 -151
- package/lib/theme-menu-state.js +31 -0
- package/lib/theme.js +134 -0
- package/lib/tool-display.js +680 -0
- package/lib/tui/component.js +55 -0
- package/lib/tui/cursor.js +29 -0
- package/lib/tui/input-buffer.js +172 -0
- package/lib/tui/tui.js +591 -0
- package/lib/turn-controller.js +68 -78
- package/package.json +28 -28
- package/lib/assistant-stream-buffer.js +0 -25
- package/lib/terminal-ui.js +0 -581
|
@@ -1,307 +1,429 @@
|
|
|
1
|
-
import { backgroundMonitorText } from "./rendering.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { backgroundMonitorText, delegateMonitorText, taskMonitorTabs } from "./rendering.js";
|
|
2
|
+
import { runtimeMethods } from "./runtime-protocol.js";
|
|
3
|
+
|
|
4
|
+
const PAGES = ["background", "delegates"];
|
|
5
|
+
|
|
6
|
+
export function createTaskMonitorController({
|
|
7
|
+
request,
|
|
8
|
+
state,
|
|
9
|
+
redraw = () => {},
|
|
10
|
+
log = () => {},
|
|
11
|
+
terminalUi = false,
|
|
12
|
+
}) {
|
|
13
|
+
const tasks = new Map();
|
|
14
|
+
const pendingCommands = new Map();
|
|
15
|
+
const delegates = new Map();
|
|
16
|
+
let refreshTimer = null;
|
|
17
|
+
let listInFlight = null;
|
|
18
|
+
let monitorTimer = null;
|
|
15
19
|
let monitorPollInFlight = false;
|
|
16
20
|
let monitor = null;
|
|
17
21
|
let monitorInputWasActive = false;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
let generation = 0;
|
|
23
|
+
let pollToken = 0;
|
|
24
|
+
|
|
25
|
+
function refresh() {
|
|
26
|
+
if (!terminalUi || state.runtimeClosing) {
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
}
|
|
29
|
+
if (listInFlight) {
|
|
30
|
+
return listInFlight;
|
|
31
|
+
}
|
|
32
|
+
const requestGeneration = generation;
|
|
33
|
+
const promise = request(runtimeMethods.backgroundList)
|
|
27
34
|
.then((result) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
for (const task of listed) {
|
|
31
|
-
const bgId = String(task?.bg_id || "").trim();
|
|
32
|
-
if (!bgId) {
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
ids.add(bgId);
|
|
36
|
-
tasks.set(bgId, { ...(tasks.get(bgId) || {}), ...task, bg_id: bgId });
|
|
37
|
-
}
|
|
38
|
-
for (const bgId of tasks.keys()) {
|
|
39
|
-
if (!ids.has(bgId) && tasks.get(bgId)?.status === "running") {
|
|
40
|
-
tasks.delete(bgId);
|
|
41
|
-
}
|
|
35
|
+
if (requestGeneration !== generation) {
|
|
36
|
+
return;
|
|
42
37
|
}
|
|
38
|
+
const listed = Array.isArray(result?.tasks) ? result.tasks : [];
|
|
39
|
+
const ids = new Set();
|
|
40
|
+
for (const task of listed) {
|
|
41
|
+
const bgId = String(task?.bg_id || "").trim();
|
|
42
|
+
if (!bgId) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
ids.add(bgId);
|
|
46
|
+
tasks.set(bgId, { ...(tasks.get(bgId) || {}), ...task, bg_id: bgId });
|
|
47
|
+
}
|
|
48
|
+
for (const bgId of tasks.keys()) {
|
|
49
|
+
if (!ids.has(bgId) && tasks.get(bgId)?.status === "running") {
|
|
50
|
+
tasks.delete(bgId);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
43
53
|
updateCount();
|
|
44
54
|
if (monitor) {
|
|
45
55
|
monitor.selectedIndex = clampIndex(monitor.selectedIndex);
|
|
46
|
-
redraw(
|
|
56
|
+
redraw();
|
|
47
57
|
}
|
|
48
58
|
})
|
|
49
59
|
.finally(() => {
|
|
50
|
-
listInFlight
|
|
60
|
+
if (listInFlight === promise) {
|
|
61
|
+
listInFlight = null;
|
|
62
|
+
}
|
|
51
63
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
64
|
+
listInFlight = promise;
|
|
65
|
+
return promise;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function updateCount() {
|
|
69
|
+
const backgroundCount = [...tasks.values()].filter((task) => task.status === "running").length;
|
|
70
|
+
const delegateCount = [...delegates.values()].filter((delegate) => delegate.status === "running").length;
|
|
71
|
+
if (
|
|
72
|
+
Number(state.sessionInfo?.background_count) !== backgroundCount
|
|
73
|
+
|| Number(state.sessionInfo?.delegate_count) !== delegateCount
|
|
74
|
+
) {
|
|
75
|
+
state.sessionInfo = {
|
|
76
|
+
...state.sessionInfo,
|
|
77
|
+
background_count: backgroundCount,
|
|
78
|
+
delegate_count: delegateCount,
|
|
79
|
+
};
|
|
80
|
+
redraw();
|
|
81
|
+
}
|
|
82
|
+
if (backgroundCount > 0) {
|
|
83
|
+
startRefresh();
|
|
84
|
+
} else {
|
|
85
|
+
stopRefresh();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function startRefresh() {
|
|
90
|
+
if (refreshTimer || state.runtimeClosing) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
refreshTimer = setInterval(() => {
|
|
94
|
+
void refresh().catch(() => {});
|
|
95
|
+
}, 1000);
|
|
96
|
+
refreshTimer.unref?.();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function stopRefresh() {
|
|
100
|
+
if (!refreshTimer) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
clearInterval(refreshTimer);
|
|
104
|
+
refreshTimer = null;
|
|
105
|
+
}
|
|
106
|
+
|
|
86
107
|
function clear() {
|
|
108
|
+
generation += 1;
|
|
109
|
+
listInFlight = null;
|
|
110
|
+
pollToken += 1;
|
|
111
|
+
monitorPollInFlight = false;
|
|
87
112
|
tasks.clear();
|
|
88
|
-
pendingCommands.clear();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
113
|
+
pendingCommands.clear();
|
|
114
|
+
delegates.clear();
|
|
115
|
+
stopRefresh();
|
|
116
|
+
updateCount();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function clearDelegates() {
|
|
120
|
+
delegates.clear();
|
|
121
|
+
updateCount();
|
|
122
|
+
if (monitor?.page === "delegates") {
|
|
123
|
+
monitor.selectedIndex = 0;
|
|
124
|
+
redraw();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function recordCommand(event) {
|
|
129
|
+
if (event?.tool_name !== "bash" || !event.tool_call_id) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const args = parseObject(event.args_preview);
|
|
133
|
+
if (args.command) {
|
|
134
|
+
pendingCommands.set(event.tool_call_id, String(args.command));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function recordResult(event) {
|
|
139
|
+
const parsed = parseObject(event?.result);
|
|
140
|
+
const data = parsed.data && typeof parsed.data === "object" ? parsed.data : parsed;
|
|
141
|
+
const bgId = String(data?.bg_id || "").trim();
|
|
142
|
+
if (!bgId) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const previous = tasks.get(bgId) || {};
|
|
146
|
+
const command = pendingCommands.get(event.tool_call_id) || previous.command || "";
|
|
147
|
+
tasks.set(bgId, { ...previous, ...data, bg_id: bgId, command });
|
|
148
|
+
if (event.tool_call_id) {
|
|
149
|
+
pendingCommands.delete(event.tool_call_id);
|
|
150
|
+
}
|
|
151
|
+
updateCount();
|
|
152
|
+
void pollMonitor();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function recordDelegateRequest(event) {
|
|
156
|
+
if (event?.tool_name !== "delegate" || !event.tool_call_id) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const args = parseObject(event.args_preview);
|
|
160
|
+
const agentId = String(args.agent_id || "").trim();
|
|
161
|
+
if (!agentId) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
delegates.set(event.tool_call_id, {
|
|
165
|
+
agent_id: agentId,
|
|
166
|
+
task: String(args.task || "").trim(),
|
|
167
|
+
status: "running",
|
|
168
|
+
summary: "",
|
|
169
|
+
});
|
|
170
|
+
updateCount();
|
|
171
|
+
if (monitor?.page === "delegates") {
|
|
172
|
+
monitor.selectedIndex = clampIndex(monitor.selectedIndex);
|
|
173
|
+
redraw();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function recordDelegateResult(event) {
|
|
178
|
+
if (event?.tool_name !== "delegate" || !event.tool_call_id) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const previous = delegates.get(event.tool_call_id);
|
|
182
|
+
if (!previous) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const parsed = parseObject(event.result);
|
|
186
|
+
const data = parsed.data && typeof parsed.data === "object" ? parsed.data : parsed;
|
|
187
|
+
const status = String(data.status || event.status || "completed").trim();
|
|
188
|
+
const summary = String(data.summary || "").trim();
|
|
189
|
+
delegates.set(event.tool_call_id, { ...previous, status, summary });
|
|
190
|
+
updateCount();
|
|
191
|
+
if (monitor?.page === "delegates") {
|
|
192
|
+
redraw();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function enterMonitor() {
|
|
197
|
+
if (monitor || state.runtimeClosing) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
monitorInputWasActive = state.inputActive;
|
|
201
|
+
monitor = { page: initialPage(), selectedIndex: 0, pageChanged: false };
|
|
202
|
+
state.inputActive = true;
|
|
203
|
+
redraw(true);
|
|
204
|
+
void refresh()
|
|
205
|
+
.then(() => {
|
|
206
|
+
if (!monitor) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (!tasks.size && !delegates.size) {
|
|
210
|
+
exitMonitor();
|
|
211
|
+
log("No background tasks or delegates.");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (!monitor.pageChanged) {
|
|
215
|
+
monitor.page = initialPage();
|
|
216
|
+
}
|
|
217
|
+
monitor.selectedIndex = clampIndex(monitor.selectedIndex);
|
|
218
|
+
startMonitorPolling();
|
|
219
|
+
void pollMonitor();
|
|
220
|
+
redraw();
|
|
221
|
+
})
|
|
222
|
+
.catch((error) => {
|
|
223
|
+
if (!monitor || state.runtimeClosing) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
exitMonitor();
|
|
227
|
+
log(`Task monitor failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function exitMonitor() {
|
|
232
|
+
stopMonitorPolling();
|
|
233
|
+
monitor = null;
|
|
234
|
+
state.inputActive = monitorInputWasActive;
|
|
235
|
+
redraw(true);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function startMonitorPolling() {
|
|
239
|
+
if (monitorTimer) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
monitorTimer = setInterval(() => {
|
|
243
|
+
void pollMonitor();
|
|
244
|
+
}, 500);
|
|
245
|
+
monitorTimer.unref?.();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function stopMonitorPolling() {
|
|
249
|
+
if (!monitorTimer) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
clearInterval(monitorTimer);
|
|
253
|
+
monitorTimer = null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function pollMonitor() {
|
|
257
|
+
if (!monitor || monitor.page !== "background" || monitorPollInFlight || state.runtimeClosing) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const available = pageItems("background");
|
|
261
|
+
if (!available.length) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
monitor.selectedIndex = clampIndex(monitor.selectedIndex);
|
|
265
|
+
const selected = available[monitor.selectedIndex];
|
|
266
|
+
if (!selected?.bg_id) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const requestGeneration = generation;
|
|
270
|
+
const requestToken = ++pollToken;
|
|
190
271
|
monitorPollInFlight = true;
|
|
191
|
-
try {
|
|
192
|
-
const result = await request(
|
|
193
|
-
bg_id: selected.bg_id,
|
|
194
|
-
max_output_chars: 20000,
|
|
195
|
-
});
|
|
196
|
-
if (result?.task && typeof result.task === "object") {
|
|
197
|
-
tasks.set(selected.bg_id, {
|
|
198
|
-
...selected,
|
|
199
|
-
...result.task,
|
|
200
|
-
command: selected.command || "",
|
|
201
|
-
});
|
|
202
|
-
updateCount();
|
|
203
|
-
redraw();
|
|
204
|
-
}
|
|
205
|
-
} catch {
|
|
206
|
-
// Periodic list refresh reconciles expired tasks without interrupting input.
|
|
272
|
+
try {
|
|
273
|
+
const result = await request(runtimeMethods.backgroundOutput, {
|
|
274
|
+
bg_id: selected.bg_id,
|
|
275
|
+
max_output_chars: 20000,
|
|
276
|
+
});
|
|
277
|
+
if (requestGeneration === generation && result?.task && typeof result.task === "object") {
|
|
278
|
+
tasks.set(selected.bg_id, {
|
|
279
|
+
...selected,
|
|
280
|
+
...result.task,
|
|
281
|
+
command: selected.command || "",
|
|
282
|
+
});
|
|
283
|
+
updateCount();
|
|
284
|
+
redraw();
|
|
285
|
+
}
|
|
286
|
+
} catch {
|
|
287
|
+
// Periodic list refresh reconciles expired tasks without interrupting input.
|
|
207
288
|
} finally {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
289
|
+
if (requestToken === pollToken) {
|
|
290
|
+
monitorPollInFlight = false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function moveSelection(delta) {
|
|
296
|
+
if (!monitor) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const count = pageItems(monitor.page).length;
|
|
300
|
+
if (!count) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const current = clampIndex(monitor.selectedIndex);
|
|
304
|
+
monitor.selectedIndex = (current + delta + count) % count;
|
|
305
|
+
void pollMonitor();
|
|
306
|
+
redraw();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function switchPage(delta) {
|
|
310
|
+
if (!monitor) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const current = PAGES.indexOf(monitor.page);
|
|
314
|
+
monitor.page = PAGES[(current + delta + PAGES.length) % PAGES.length];
|
|
315
|
+
monitor.pageChanged = true;
|
|
316
|
+
monitor.selectedIndex = clampIndex(monitor.selectedIndex);
|
|
317
|
+
void pollMonitor();
|
|
318
|
+
redraw();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function handleInput(key) {
|
|
322
|
+
const modified = key.ctrl || key.alt || key.shift;
|
|
323
|
+
if (!modified && key.name === "escape") {
|
|
324
|
+
exitMonitor();
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
if (key.ctrl && key.name === "b") {
|
|
328
|
+
exitMonitor();
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
if (!modified && key.name === "left") {
|
|
332
|
+
switchPage(-1);
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
if (!modified && key.name === "right") {
|
|
336
|
+
switchPage(1);
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
if (modified) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
if (key.name === "up" || key.text === "k") {
|
|
343
|
+
moveSelection(-1);
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
if (key.name === "down" || key.text === "j") {
|
|
347
|
+
moveSelection(1);
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function frame(width) {
|
|
354
|
+
const page = monitor?.page || initialPage();
|
|
355
|
+
const list = pageItems(page);
|
|
356
|
+
const selectedIndex = clampIndex(monitor?.selectedIndex, page);
|
|
357
|
+
const contentWidth = Math.max(20, Number(width) - 4);
|
|
358
|
+
const tabs = taskMonitorTabs(page, tasks.size, delegates.size, contentWidth).split("\n");
|
|
359
|
+
const text = page === "delegates"
|
|
360
|
+
? delegateMonitorText(list, selectedIndex, list[selectedIndex], contentWidth)
|
|
361
|
+
: backgroundMonitorText(list, selectedIndex, list[selectedIndex], contentWidth);
|
|
362
|
+
const lines = [...tabs, ...text.split("\n")];
|
|
363
|
+
const selectedRow = list.length
|
|
364
|
+
? tabs.length + 1 + selectedIndex
|
|
365
|
+
: Math.max(0, lines.length - 1);
|
|
366
|
+
return {
|
|
367
|
+
lines,
|
|
368
|
+
focusRow: Math.min(selectedRow, Math.max(0, lines.length - 1)),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
266
372
|
function stop() {
|
|
373
|
+
generation += 1;
|
|
374
|
+
listInFlight = null;
|
|
375
|
+
pollToken += 1;
|
|
376
|
+
monitorPollInFlight = false;
|
|
267
377
|
stopRefresh();
|
|
268
|
-
stopMonitorPolling();
|
|
269
|
-
monitor = null;
|
|
270
|
-
pendingCommands.clear();
|
|
271
|
-
tasks.clear();
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
}
|
|
378
|
+
stopMonitorPolling();
|
|
379
|
+
monitor = null;
|
|
380
|
+
pendingCommands.clear();
|
|
381
|
+
tasks.clear();
|
|
382
|
+
delegates.clear();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function pageItems(page) {
|
|
386
|
+
return page === "delegates" ? [...delegates.values()] : [...tasks.values()];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function initialPage() {
|
|
390
|
+
return tasks.size ? "background" : "delegates";
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function clampIndex(index, page = monitor?.page || "background") {
|
|
394
|
+
const count = pageItems(page).length;
|
|
395
|
+
if (!count) {
|
|
396
|
+
return 0;
|
|
397
|
+
}
|
|
398
|
+
return Math.min(count - 1, Math.max(0, Number(index) || 0));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
refresh,
|
|
403
|
+
recordCommand,
|
|
404
|
+
recordResult,
|
|
405
|
+
recordDelegateRequest,
|
|
406
|
+
recordDelegateResult,
|
|
407
|
+
clearDelegates,
|
|
408
|
+
enterMonitor,
|
|
409
|
+
exitMonitor,
|
|
410
|
+
handleInput,
|
|
411
|
+
clear,
|
|
412
|
+
stop,
|
|
413
|
+
frame,
|
|
414
|
+
isMonitoring: () => Boolean(monitor),
|
|
415
|
+
moveSelection,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function parseObject(value) {
|
|
420
|
+
if (value && typeof value === "object") {
|
|
421
|
+
return value;
|
|
422
|
+
}
|
|
423
|
+
try {
|
|
424
|
+
const parsed = JSON.parse(String(value || ""));
|
|
425
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
426
|
+
} catch {
|
|
427
|
+
return {};
|
|
428
|
+
}
|
|
429
|
+
}
|