@bolloon/bolloon-agent 0.3.19 → 0.3.21
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/cli/loading-tui.js +2 -2
- package/dist/index.js +36 -116
- package/dist/web/client.js +4833 -4328
- package/dist/web/components/p2p/index.js +234 -276
- package/dist/web/ui/message-renderer.js +396 -535
- package/dist/web/ui/step-timeline.js +272 -371
- package/package.json +1 -1
- package/dist/web/components/p2p/P2PModal.js +0 -188
- package/dist/web/components/p2p/p2p-modal.js +0 -664
- package/dist/web/components/p2p/p2p-tools.js +0 -248
package/dist/cli/loading-tui.js
CHANGED
|
@@ -63,7 +63,7 @@ export const BOLLOON_BANNER = [
|
|
|
63
63
|
`${C_DIM}Bolloon Agent v${BOLLOON_VERSION}${RESET}`,
|
|
64
64
|
].join('\n');
|
|
65
65
|
/** 艺术字全部行 (图标在左, BOLLOON 艺术字在右), 供框内渲染 */
|
|
66
|
-
function brandArtLines() {
|
|
66
|
+
export function brandArtLines() {
|
|
67
67
|
const icon = BOLLOON_ICON.split('\n');
|
|
68
68
|
const banner = BOLLOON_BANNER.split('\n');
|
|
69
69
|
const gap = 2;
|
|
@@ -107,7 +107,7 @@ function stripAnsi(s) {
|
|
|
107
107
|
return s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
108
108
|
}
|
|
109
109
|
/** 显示宽度: CJK / 全角符号算 2, 其余算 1 (ANSI 转义不计入) */
|
|
110
|
-
function dispWidth(s) {
|
|
110
|
+
export function dispWidth(s) {
|
|
111
111
|
const clean = s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
112
112
|
let n = 0;
|
|
113
113
|
for (const ch of clean) {
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { createSubAgentManager } from './agents/subagent-manager.js';
|
|
|
15
15
|
import { getGlobalSharedContext } from './social/global-shared-context.js';
|
|
16
16
|
import { createBollharnessIntegration } from './bollharness-integration/index.js';
|
|
17
17
|
import * as readline from 'readline';
|
|
18
|
-
import { printBanner,
|
|
18
|
+
import { printBanner, renderDialog, renderUserMessage, renderAgentMessage, renderToolCall, flowConnector, termWidth, brandArtLines, boxTop, boxRow, boxBottom, dispWidth } from './cli/loading-tui.js';
|
|
19
19
|
// 启动自动检查更新:后台、节流、检测到新版本自动安装(可被 --no-update / BOLLOON_SKIP_UPDATE 关闭)
|
|
20
20
|
import { createRequire } from 'module';
|
|
21
21
|
const _require = createRequire(import.meta.url);
|
|
@@ -345,16 +345,8 @@ function rpcErr(code, msg) {
|
|
|
345
345
|
}
|
|
346
346
|
// ---------------------------------------------------------------------------
|
|
347
347
|
// CLI with persistent bottom prompt
|
|
348
|
-
//
|
|
349
|
-
const SAVE_CURSOR = '\x1b[s';
|
|
350
|
-
const RESTORE_CURSOR = '\x1b[u';
|
|
351
|
-
const MOVE_TO_BOTTOM = '\x1b[999A';
|
|
352
|
-
const HIDE_CURSOR_SEQ = '\x1b[?25l';
|
|
353
|
-
const SHOW_CURSOR_SEQ = '\x1b[?25h';
|
|
354
|
-
const MOVE_UP_1 = '\x1b[1A';
|
|
348
|
+
// 2026-07-28: 改用 readline.createInterface + replReadline 循环
|
|
355
349
|
let isRunning = false;
|
|
356
|
-
let currentInput = '';
|
|
357
|
-
let promptVisible = false;
|
|
358
350
|
let queueMode = false;
|
|
359
351
|
const pendingQueue = [];
|
|
360
352
|
// 底部状态栏数据
|
|
@@ -379,40 +371,14 @@ function statusBarLine() {
|
|
|
379
371
|
const bar = C_OK + '█'.repeat(filled) + C_DIM + '░'.repeat(barLen - filled) + RESET;
|
|
380
372
|
return `${C_ACCENT}${cliModelName}${RESET}${C_DIM} │${RESET} ${cliAgentName} ${C_DIM}│${RESET} ⏱ ${C_ACCENT}${dur}${RESET} ${C_DIM}│${RESET} ${bar} ${C_DIM}${cliContextPct}%${RESET}`;
|
|
381
373
|
}
|
|
382
|
-
function showBottomPrompt() {
|
|
383
|
-
if (!isRunning)
|
|
384
|
-
return;
|
|
385
|
-
promptVisible = true;
|
|
386
|
-
const tw = process.stdout.columns || 80;
|
|
387
|
-
const sep = C_DIM + '─'.repeat(tw) + RESET;
|
|
388
|
-
const hint = currentInput.length === 0 && !queueMode
|
|
389
|
-
? ` ${C_DIM}!cmd${RESET} ${C_DIM}/queue${RESET} ${C_DIM}/help${RESET}`
|
|
390
|
-
: queueMode ? ` ${C_WARN}[队列${pendingQueue.length}]${RESET}` : '';
|
|
391
|
-
const prefix = queueMode ? `${C_WARN}▸${RESET}` : `${C_ACCENT}❯${RESET}`;
|
|
392
|
-
process.stdout.write(`\n${sep}\n${statusBarLine()}\n${sep}\n${C_DIM}${'─'.repeat(tw)}${RESET}\n${prefix} ${currentInput}${hint}`);
|
|
393
|
-
}
|
|
394
|
-
function clearPromptLine() {
|
|
395
|
-
promptVisible = false;
|
|
396
|
-
}
|
|
397
374
|
function startCLI(comm) {
|
|
398
375
|
isRunning = true;
|
|
399
|
-
currentInput = '';
|
|
400
|
-
promptVisible = false;
|
|
401
|
-
try {
|
|
402
|
-
process.stdin.setRawMode(true);
|
|
403
|
-
}
|
|
404
|
-
catch {
|
|
405
|
-
}
|
|
406
|
-
readline.emitKeypressEvents(process.stdin);
|
|
407
|
-
process.stdout.write(CLEAR_LINE);
|
|
408
|
-
// ── Bolloon Agent 仪表盘 (ASCII 艺术字 + 品牌图标) ──
|
|
409
|
-
printBanner(_BOLLOON_VERSION);
|
|
410
376
|
let peerCount = 0;
|
|
411
377
|
try {
|
|
412
378
|
peerCount = comm.getConnections().length;
|
|
413
379
|
}
|
|
414
380
|
catch { /* */ }
|
|
415
|
-
// 读取 LLM 模型名
|
|
381
|
+
// 读取 LLM 模型名
|
|
416
382
|
const providerNames = [
|
|
417
383
|
['OPENAI_API_KEY', 'OpenAI'],
|
|
418
384
|
['ANTHROPIC_API_KEY', 'Anthropic'],
|
|
@@ -427,82 +393,39 @@ function startCLI(comm) {
|
|
|
427
393
|
cliModelName = foundProvider ? foundProvider[1] : '未配置';
|
|
428
394
|
cliAgentName = agentIdentity?.name || 'bolloon';
|
|
429
395
|
cliStartTime = Date.now();
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
process.stdout.write(
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
396
|
+
process.stdout.write(`${C_DIM}输入 !cmd 执行终端 · /queue 入队 · /help 帮助${RESET}\n\n`);
|
|
397
|
+
// 启动框: logo 左对齐(无状态栏 — 状态栏在输入框上方)
|
|
398
|
+
const art = brandArtLines();
|
|
399
|
+
const maxArt = art.reduce((m, l) => Math.max(m, dispWidth(l)), 0);
|
|
400
|
+
const boxW = Math.min(termWidth() - 2, Math.max(40, maxArt) + 4);
|
|
401
|
+
process.stdout.write(boxTop('Bolloon Agent', boxW) + '\n');
|
|
402
|
+
for (const l of art)
|
|
403
|
+
process.stdout.write(boxRow(l, boxW, 'left') + '\n');
|
|
404
|
+
process.stdout.write(boxBottom(boxW) + '\n\n');
|
|
405
|
+
// readline 输入循环(底部没有独立状态栏)
|
|
406
|
+
replReadline(comm);
|
|
407
|
+
}
|
|
408
|
+
async function replReadline(comm) {
|
|
409
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
410
|
+
const SEP = '\x1b[90m─';
|
|
411
|
+
const RST = '\x1b[0m';
|
|
412
|
+
while (isRunning) {
|
|
413
|
+
const tw = process.stdout.columns || 80;
|
|
414
|
+
const sepLine = SEP.repeat(tw) + RST;
|
|
415
|
+
const prefix = queueMode ? `${C_WARN}▸${RST}` : `${C_ACCENT}❯${RST}`;
|
|
416
|
+
const raw = await new Promise(resolve => rl.question(`\n${sepLine}\n${statusBarLine()}\n${sepLine}\n${prefix} `, resolve));
|
|
417
|
+
const trimmed = raw.trim();
|
|
418
|
+
process.stdout.write(`\n${sepLine}\n\n\n\n\n`);
|
|
419
|
+
if (!trimmed)
|
|
420
|
+
continue;
|
|
451
421
|
if (!isRunning)
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
process.stdin.setRawMode(false);
|
|
460
|
-
}
|
|
461
|
-
catch { }
|
|
462
|
-
process.exit(0);
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
if (key.name === 'return') {
|
|
466
|
-
const trimmed = currentInput.trim();
|
|
467
|
-
currentInput = '';
|
|
468
|
-
clearPromptLine();
|
|
469
|
-
if (trimmed) {
|
|
470
|
-
process.stdout.write('\n');
|
|
471
|
-
processInput(trimmed, comm).then(() => {
|
|
472
|
-
if (isRunning)
|
|
473
|
-
showBottomPrompt();
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
else {
|
|
477
|
-
showBottomPrompt();
|
|
478
|
-
}
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
if (key.name === 'backspace') {
|
|
482
|
-
if (currentInput.length > 0) {
|
|
483
|
-
currentInput = currentInput.slice(0, -1);
|
|
484
|
-
showBottomPrompt();
|
|
485
|
-
}
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
if (key.name === 'escape' || (key.ctrl && key.name === 'u')) {
|
|
489
|
-
currentInput = '';
|
|
490
|
-
showBottomPrompt();
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
if (key.name === 'tab') {
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
if (key.name && key.name.length === 1) {
|
|
497
|
-
currentInput += chunk.toString();
|
|
498
|
-
showBottomPrompt();
|
|
499
|
-
}
|
|
500
|
-
};
|
|
501
|
-
process.stdin.on('data', handleInput);
|
|
502
|
-
process.on('exit', () => {
|
|
503
|
-
isRunning = false;
|
|
504
|
-
process.stdout.write(SHOW_CURSOR_SEQ);
|
|
505
|
-
});
|
|
422
|
+
break;
|
|
423
|
+
await processInput(trimmed, comm);
|
|
424
|
+
}
|
|
425
|
+
rl.close();
|
|
426
|
+
process.stdout.write(`\n${CYAN}👋 再见!${RESET}\n`);
|
|
427
|
+
process.stdin.destroy();
|
|
428
|
+
comm.stop();
|
|
506
429
|
}
|
|
507
430
|
async function processInput(input, comm) {
|
|
508
431
|
const trimmed = input.trim();
|
|
@@ -564,11 +487,8 @@ async function processInput(input, comm) {
|
|
|
564
487
|
return;
|
|
565
488
|
}
|
|
566
489
|
if (trimmed === '退出' || trimmed === 'exit' || trimmed === 'quit') {
|
|
567
|
-
|
|
568
|
-
process.stdout.write(`${CYAN}👋 再见!${RESET}\n`);
|
|
490
|
+
process.stdout.write(`\n${CYAN}👋 再见!${RESET}\n`);
|
|
569
491
|
isRunning = false;
|
|
570
|
-
process.stdin.destroy();
|
|
571
|
-
comm.stop();
|
|
572
492
|
return;
|
|
573
493
|
}
|
|
574
494
|
if (trimmed.toLowerCase() === 'peers') {
|