u-foo 2.3.14 → 2.3.16
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/package.json +1 -1
- package/src/chat/agentViewController.js +253 -3
- package/src/chat/index.js +13 -0
package/package.json
CHANGED
|
@@ -32,6 +32,7 @@ function createAgentViewController(options = {}) {
|
|
|
32
32
|
connectAgentInput = () => {},
|
|
33
33
|
disconnectAgentInput = () => {},
|
|
34
34
|
sendRaw = () => {},
|
|
35
|
+
sendBusMessage = () => {},
|
|
35
36
|
sendResize = () => {},
|
|
36
37
|
requestScreenSnapshot = () => {},
|
|
37
38
|
} = options;
|
|
@@ -47,6 +48,9 @@ function createAgentViewController(options = {}) {
|
|
|
47
48
|
let agentBarVisible = false;
|
|
48
49
|
let detachedChildren = null;
|
|
49
50
|
let agentInputSuppressUntil = 0;
|
|
51
|
+
let busInputValue = "";
|
|
52
|
+
let busInputCursor = 0;
|
|
53
|
+
let busLogLines = [];
|
|
50
54
|
const originalRender = screen.render.bind(screen);
|
|
51
55
|
let renderFrozen = false;
|
|
52
56
|
|
|
@@ -63,6 +67,125 @@ function createAgentViewController(options = {}) {
|
|
|
63
67
|
return processStdout.columns || 80;
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
function stripAnsi(text = "") {
|
|
71
|
+
return String(text || "").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
|
|
72
|
+
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function clamp(value, min, max) {
|
|
76
|
+
const normalized = Number.isFinite(value) ? Math.floor(value) : min;
|
|
77
|
+
return Math.max(min, Math.min(max, normalized));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function fitText(text = "", width = 1) {
|
|
81
|
+
const normalizedWidth = Math.max(1, width);
|
|
82
|
+
const clean = stripAnsi(String(text || "")).replace(/\r/g, "");
|
|
83
|
+
if (clean.length <= normalizedWidth) {
|
|
84
|
+
return clean + " ".repeat(normalizedWidth - clean.length);
|
|
85
|
+
}
|
|
86
|
+
if (normalizedWidth <= 1) return clean.slice(0, normalizedWidth);
|
|
87
|
+
return clean.slice(0, normalizedWidth - 1) + "…";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function horizontalLine(width = 80) {
|
|
91
|
+
return "─".repeat(Math.max(1, width));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function plainLine(text = "", width = 80) {
|
|
95
|
+
return fitText(text, Math.max(1, width));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function wrapTextLine(text = "", width = 80) {
|
|
99
|
+
const inner = Math.max(1, width);
|
|
100
|
+
const clean = stripAnsi(String(text || ""));
|
|
101
|
+
if (!clean) return [""];
|
|
102
|
+
const lines = [];
|
|
103
|
+
let rest = clean;
|
|
104
|
+
while (rest.length > inner) {
|
|
105
|
+
lines.push(rest.slice(0, inner));
|
|
106
|
+
rest = rest.slice(inner);
|
|
107
|
+
}
|
|
108
|
+
lines.push(rest);
|
|
109
|
+
return lines;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getWrappedBusLogLines(width = 80) {
|
|
113
|
+
const inner = Math.max(1, width);
|
|
114
|
+
const wrapped = [];
|
|
115
|
+
for (const line of busLogLines) {
|
|
116
|
+
wrapped.push(...wrapTextLine(line, inner));
|
|
117
|
+
}
|
|
118
|
+
return wrapped;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function writeAt(row, content = "") {
|
|
122
|
+
processStdout.write(`\x1b[${row};1H\x1b[2K${content}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function resetBusView(agentId) {
|
|
126
|
+
busInputValue = "";
|
|
127
|
+
busInputCursor = 0;
|
|
128
|
+
const label = getAgentLabel(agentId);
|
|
129
|
+
busLogLines = [
|
|
130
|
+
`ufoo internal · ${label}`,
|
|
131
|
+
"",
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function appendBusLog(text = "") {
|
|
136
|
+
const clean = stripAnsi(String(text || "")).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
137
|
+
if (busLogLines.length === 0) busLogLines.push("");
|
|
138
|
+
for (const char of clean) {
|
|
139
|
+
if (char === "\n") {
|
|
140
|
+
busLogLines.push("");
|
|
141
|
+
} else {
|
|
142
|
+
busLogLines[busLogLines.length - 1] += char;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (busLogLines.length > 1000) {
|
|
146
|
+
busLogLines = busLogLines.slice(-1000);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getBusInputViewport(width) {
|
|
151
|
+
const inner = Math.max(1, width - 4);
|
|
152
|
+
const value = String(busInputValue || "").replace(/\n/g, "⏎");
|
|
153
|
+
let start = 0;
|
|
154
|
+
if (busInputCursor >= inner) {
|
|
155
|
+
start = busInputCursor - inner + 1;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
text: value.slice(start, start + inner),
|
|
159
|
+
cursorCol: Math.max(0, busInputCursor - start),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function renderBusView() {
|
|
164
|
+
if (currentView !== "agent" || !agentViewUsesBus) return;
|
|
165
|
+
const rows = getRows();
|
|
166
|
+
const cols = getCols();
|
|
167
|
+
const width = Math.max(20, cols);
|
|
168
|
+
const inputTop = Math.max(4, rows - 3);
|
|
169
|
+
const logContentTop = 1;
|
|
170
|
+
const logContentBottom = Math.max(logContentTop, inputTop - 1);
|
|
171
|
+
const logContentHeight = Math.max(1, logContentBottom - logContentTop + 1);
|
|
172
|
+
|
|
173
|
+
processStdout.write("\x1b[?25l");
|
|
174
|
+
const visibleLines = getWrappedBusLogLines(width).slice(-logContentHeight);
|
|
175
|
+
for (let i = 0; i < logContentHeight; i += 1) {
|
|
176
|
+
writeAt(logContentTop + i, plainLine(visibleLines[i] || "", width));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
writeAt(inputTop, horizontalLine(width));
|
|
180
|
+
const viewport = getBusInputViewport(width);
|
|
181
|
+
writeAt(inputTop + 1, plainLine(`> ${viewport.text}`, width));
|
|
182
|
+
writeAt(inputTop + 2, horizontalLine(width));
|
|
183
|
+
|
|
184
|
+
renderAgentDashboard();
|
|
185
|
+
const cursorCol = clamp(3 + viewport.cursorCol, 1, width);
|
|
186
|
+
processStdout.write(`\x1b[${inputTop + 1};${cursorCol}H\x1b[?25h`);
|
|
187
|
+
}
|
|
188
|
+
|
|
66
189
|
function renderAgentDashboard() {
|
|
67
190
|
if (!agentBarVisible && getFocusMode() !== "dashboard") return;
|
|
68
191
|
const rows = getRows();
|
|
@@ -127,8 +250,8 @@ function createAgentViewController(options = {}) {
|
|
|
127
250
|
agentInputSuppressUntil = now() + 300;
|
|
128
251
|
agentViewUsesBus = Boolean(options.useBus);
|
|
129
252
|
if (agentViewUsesBus) {
|
|
130
|
-
|
|
131
|
-
|
|
253
|
+
resetBusView(agentId);
|
|
254
|
+
renderBusView();
|
|
132
255
|
} else {
|
|
133
256
|
const sockPath = getInjectSockPath(agentId);
|
|
134
257
|
connectAgentOutput(sockPath);
|
|
@@ -153,6 +276,9 @@ function createAgentViewController(options = {}) {
|
|
|
153
276
|
agentViewUsesBus = false;
|
|
154
277
|
agentOutputSuppressed = false;
|
|
155
278
|
agentBarVisible = false;
|
|
279
|
+
busInputValue = "";
|
|
280
|
+
busInputCursor = 0;
|
|
281
|
+
busLogLines = [];
|
|
156
282
|
|
|
157
283
|
currentView = "main";
|
|
158
284
|
viewingAgent = null;
|
|
@@ -199,6 +325,117 @@ function createAgentViewController(options = {}) {
|
|
|
199
325
|
agentOutputSuppressed = true;
|
|
200
326
|
}
|
|
201
327
|
|
|
328
|
+
function insertBusInput(text = "") {
|
|
329
|
+
const value = String(text || "");
|
|
330
|
+
if (!value) return;
|
|
331
|
+
busInputValue = busInputValue.slice(0, busInputCursor) + value + busInputValue.slice(busInputCursor);
|
|
332
|
+
busInputCursor += value.length;
|
|
333
|
+
renderBusView();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function deleteBusInputBeforeCursor() {
|
|
337
|
+
if (busInputCursor <= 0) return;
|
|
338
|
+
busInputValue = busInputValue.slice(0, busInputCursor - 1) + busInputValue.slice(busInputCursor);
|
|
339
|
+
busInputCursor -= 1;
|
|
340
|
+
renderBusView();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function clearBusInput() {
|
|
344
|
+
busInputValue = "";
|
|
345
|
+
busInputCursor = 0;
|
|
346
|
+
renderBusView();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function submitBusInput() {
|
|
350
|
+
const text = String(busInputValue || "").trim();
|
|
351
|
+
if (!text) {
|
|
352
|
+
renderBusView();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
appendBusLog(`> ${text}\n`);
|
|
356
|
+
busInputValue = "";
|
|
357
|
+
busInputCursor = 0;
|
|
358
|
+
sendBusMessage(viewingAgent, text);
|
|
359
|
+
renderBusView();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function handleBusAgentKey(ch, key = {}) {
|
|
363
|
+
if (currentView !== "agent" || !agentViewUsesBus) return false;
|
|
364
|
+
const keyName = key && key.name;
|
|
365
|
+
|
|
366
|
+
if (keyName === "down") return false;
|
|
367
|
+
|
|
368
|
+
if (keyName === "escape") {
|
|
369
|
+
exitAgentView();
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
if (keyName === "return" || keyName === "enter") {
|
|
373
|
+
if (key && (key.shift || key.meta)) {
|
|
374
|
+
insertBusInput("\n");
|
|
375
|
+
} else {
|
|
376
|
+
submitBusInput();
|
|
377
|
+
}
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
if (key && key.ctrl && keyName === "u") {
|
|
381
|
+
clearBusInput();
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
if (key && key.ctrl && keyName === "a") {
|
|
385
|
+
busInputCursor = 0;
|
|
386
|
+
renderBusView();
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
if (key && key.ctrl && keyName === "e") {
|
|
390
|
+
busInputCursor = busInputValue.length;
|
|
391
|
+
renderBusView();
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
if (keyName === "left") {
|
|
395
|
+
busInputCursor = Math.max(0, busInputCursor - 1);
|
|
396
|
+
renderBusView();
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
if (keyName === "right") {
|
|
400
|
+
busInputCursor = Math.min(busInputValue.length, busInputCursor + 1);
|
|
401
|
+
renderBusView();
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
if (keyName === "home") {
|
|
405
|
+
busInputCursor = 0;
|
|
406
|
+
renderBusView();
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
if (keyName === "end") {
|
|
410
|
+
busInputCursor = busInputValue.length;
|
|
411
|
+
renderBusView();
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
if (keyName === "backspace") {
|
|
415
|
+
deleteBusInputBeforeCursor();
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
if (keyName === "delete") {
|
|
419
|
+
if (busInputCursor < busInputValue.length) {
|
|
420
|
+
busInputValue = busInputValue.slice(0, busInputCursor) + busInputValue.slice(busInputCursor + 1);
|
|
421
|
+
renderBusView();
|
|
422
|
+
}
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
if (ch && ch.length > 1 && (!keyName || keyName.length !== 1)) {
|
|
426
|
+
insertBusInput(ch.replace(/\r\n/g, "\n").replace(/\r/g, "\n"));
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
const insertChar = (ch && ch.length === 1)
|
|
430
|
+
? ch
|
|
431
|
+
: (keyName && keyName.length === 1 ? keyName : "");
|
|
432
|
+
if (insertChar && !/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(insertChar)) {
|
|
433
|
+
insertBusInput(insertChar);
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
|
|
202
439
|
function sendRawToAgent(data) {
|
|
203
440
|
sendRaw(data);
|
|
204
441
|
}
|
|
@@ -219,6 +456,11 @@ function createAgentViewController(options = {}) {
|
|
|
219
456
|
const cleaned = text
|
|
220
457
|
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
|
|
221
458
|
.replace(/\x1b\[(?:[?>=]?[0-9]*c|[?]?6n|5n)/g, "");
|
|
459
|
+
if (agentViewUsesBus) {
|
|
460
|
+
appendBusLog(cleaned);
|
|
461
|
+
renderBusView();
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
222
464
|
if (cleaned) processStdout.write(cleaned);
|
|
223
465
|
if (agentBarVisible) {
|
|
224
466
|
const rows = getRows();
|
|
@@ -244,7 +486,11 @@ function createAgentViewController(options = {}) {
|
|
|
244
486
|
const cols = getCols();
|
|
245
487
|
processStdout.write(`\x1b[1;${rows - 1}r`);
|
|
246
488
|
sendResize(cols, Math.max(1, rows - 1));
|
|
247
|
-
|
|
489
|
+
if (agentViewUsesBus) {
|
|
490
|
+
renderBusView();
|
|
491
|
+
} else {
|
|
492
|
+
renderAgentDashboard();
|
|
493
|
+
}
|
|
248
494
|
return true;
|
|
249
495
|
}
|
|
250
496
|
|
|
@@ -270,6 +516,9 @@ function createAgentViewController(options = {}) {
|
|
|
270
516
|
|
|
271
517
|
function setAgentOutputSuppressed(value) {
|
|
272
518
|
agentOutputSuppressed = Boolean(value);
|
|
519
|
+
if (!agentOutputSuppressed && agentViewUsesBus) {
|
|
520
|
+
renderBusView();
|
|
521
|
+
}
|
|
273
522
|
}
|
|
274
523
|
|
|
275
524
|
function isAgentBarVisible() {
|
|
@@ -295,6 +544,7 @@ function createAgentViewController(options = {}) {
|
|
|
295
544
|
writeToAgentTerm,
|
|
296
545
|
placeAgentCursor,
|
|
297
546
|
handleResizeInAgentView,
|
|
547
|
+
handleBusAgentKey,
|
|
298
548
|
};
|
|
299
549
|
}
|
|
300
550
|
|
package/src/chat/index.js
CHANGED
|
@@ -1565,6 +1565,16 @@ async function runChat(projectRoot, options = {}) {
|
|
|
1565
1565
|
sendRaw: (data) => {
|
|
1566
1566
|
sendRawWithCapabilities(data);
|
|
1567
1567
|
},
|
|
1568
|
+
sendBusMessage: (target, message) => {
|
|
1569
|
+
if (!target || !message) return;
|
|
1570
|
+
send({
|
|
1571
|
+
type: IPC_REQUEST_TYPES.BUS_SEND,
|
|
1572
|
+
target,
|
|
1573
|
+
message,
|
|
1574
|
+
injection_mode: "immediate",
|
|
1575
|
+
source: "chat-internal-agent-view",
|
|
1576
|
+
});
|
|
1577
|
+
},
|
|
1568
1578
|
sendResize: (cols, rows) => {
|
|
1569
1579
|
sendResizeWithCapabilities(cols, rows);
|
|
1570
1580
|
},
|
|
@@ -2026,6 +2036,9 @@ async function runChat(projectRoot, options = {}) {
|
|
|
2026
2036
|
if (key && key.ctrl && key.name === "c") {
|
|
2027
2037
|
return; // handled by screen.key(["C-c"])
|
|
2028
2038
|
}
|
|
2039
|
+
if (agentViewController && agentViewController.handleBusAgentKey(ch, key)) {
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2029
2042
|
// Down arrow: enter agents bar (same pattern as normal chat dashboard)
|
|
2030
2043
|
if (key && key.name === "down") {
|
|
2031
2044
|
enterAgentDashboardMode();
|