@bolloon/bolloon-agent 0.3.6 → 0.3.7

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/index.js CHANGED
@@ -5,6 +5,7 @@ import * as ed25519 from '@noble/ed25519';
5
5
  import { sha512 } from '@noble/hashes/sha2.js';
6
6
  import * as fs from 'fs/promises';
7
7
  import * as path from 'path';
8
+ import { spawn } from 'child_process';
8
9
  import { documentReader } from './documents/reader.js';
9
10
  import { initMinimax } from './constraints/index.js';
10
11
  import { createAgentSession } from './agents/pi-sdk.js';
@@ -12,7 +13,8 @@ import { createSubAgentManager } from './agents/subagent-manager.js';
12
13
  import { getGlobalSharedContext } from './social/global-shared-context.js';
13
14
  import { createBollharnessIntegration } from './bollharness-integration/index.js';
14
15
  import * as readline from 'readline';
15
- // 启动时自动检查更新已禁用 (改用 --update-check / --update-now 显式触发)
16
+ import { printBanner, renderDashboard, renderDialog, renderUserMessage, renderAgentMessage, renderToolCall, flowConnector, termWidth } from './cli/loading-tui.js';
17
+ // 启动自动检查更新:后台、节流、检测到新版本自动安装(可被 --no-update / BOLLOON_SKIP_UPDATE 关闭)
16
18
  import { createRequire } from 'module';
17
19
  const _require = createRequire(import.meta.url);
18
20
  const _BOLLOON_VERSION = (() => {
@@ -42,14 +44,7 @@ const HIDE_CURSOR = '\x1b[?25l';
42
44
  const SHOW_CURSOR = '\x1b[?25h';
43
45
  const s = {
44
46
  banner: () => {
45
- const verStr = `v${_BOLLOON_VERSION}`;
46
- const pad = Math.max(0, 39 - 17 - verStr.length);
47
- const spaces = ' '.repeat(pad);
48
- console.log(`\n${CYAN}${BOLD}
49
- ╔═══════════════════════════════════════════╗
50
- ║ ${WHITE}🤖 Bolloon ${CYAN}${verStr}${spaces}║
51
- ║ ${WHITE}P2P AI Document Processor${CYAN} ║
52
- ╚═══════════════════════════════════════════╝${RESET}\n`);
47
+ printBanner(_BOLLOON_VERSION);
53
48
  },
54
49
  step: (num, total, text, status) => {
55
50
  const check = status === 'ok' ? `${GREEN}✓` :
@@ -96,6 +91,7 @@ const s = {
96
91
  },
97
92
  dialog: async (title, promptText) => {
98
93
  return new Promise((resolve) => {
94
+ console.log(renderDialog({ title, prompt: promptText }));
99
95
  const rl = readline.createInterface({
100
96
  input: process.stdin,
101
97
  output: process.stdout
@@ -368,6 +364,30 @@ function startCLI(comm) {
368
364
  }
369
365
  readline.emitKeypressEvents(process.stdin);
370
366
  process.stdout.write(CLEAR_LINE);
367
+ // ── Bolloon Agent 仪表盘 (ASCII 艺术字 + 品牌图标) ──
368
+ printBanner(_BOLLOON_VERSION);
369
+ let peerCount = 0;
370
+ try {
371
+ peerCount = comm.getConnections().length;
372
+ }
373
+ catch { /* */ }
374
+ const llmName = process.env.MINIMAX_API_KEY ? 'MiniMax'
375
+ : process.env.OPENAI_API_KEY ? 'OpenAI'
376
+ : process.env.ANTHROPIC_API_KEY ? 'Anthropic'
377
+ : process.env.DEEPSEEK_API_KEY ? 'DeepSeek'
378
+ : '未配置';
379
+ process.stdout.write(renderDashboard({
380
+ title: '系统状态',
381
+ rows: [
382
+ { label: 'LLM Provider', status: llmName === '未配置' ? 'warn' : 'ok', detail: llmName },
383
+ { label: 'P2P 节点', status: peerCount > 0 ? 'ok' : 'warn', detail: `${peerCount} 个` },
384
+ { label: '输入方式', status: 'info', detail: '底部对话框' },
385
+ ],
386
+ }) + '\n');
387
+ process.stdout.write(renderDialog({
388
+ title: 'Bolloon Agent',
389
+ prompt: '输入消息开始对话 · 输入 help 查看命令',
390
+ }) + '\n');
371
391
  showBottomPrompt();
372
392
  const promptTimer = setInterval(() => {
373
393
  if (isRunning && promptVisible) {
@@ -462,16 +482,43 @@ async function processInput(input, comm) {
462
482
  return;
463
483
  }
464
484
  try {
485
+ // 已发送消息框
486
+ process.stdout.write(renderUserMessage(trimmed) + '\n');
465
487
  const a = await getAgent();
466
- const thinking = setInterval(() => {
467
- if (!isRunning)
468
- return;
469
- process.stdout.write('\r \x1b[33m⟳\x1b[0m 思考中... \x1b[?25l');
470
- }, 80);
471
- const response = await a.prompt(trimmed);
472
- clearInterval(thinking);
473
- process.stdout.write('\r' + ' '.repeat(30) + '\r');
474
- process.stdout.write(`${response}\n`);
488
+ const boxW = Math.min(termWidth() - 2, 76);
489
+ const pending = [];
490
+ let firstEvent = true;
491
+ const clearThinking = () => {
492
+ if (firstEvent) {
493
+ process.stdout.write('\r' + ' '.repeat(30) + '\r');
494
+ firstEvent = false;
495
+ }
496
+ };
497
+ const onStream = (e) => {
498
+ if (e.type === 'step_start') {
499
+ pending.push({ tool: e.tool, args: e.args, t0: Date.now() });
500
+ }
501
+ else if (e.type === 'step_done' || e.type === 'step_error') {
502
+ const p = pending.shift();
503
+ clearThinking();
504
+ // 连接线只在第 2 个及之后的工具框前出现
505
+ if (!firstEvent)
506
+ process.stdout.write(flowConnector(boxW) + '\n');
507
+ process.stdout.write(renderToolCall({
508
+ tool: e.tool ?? p?.tool ?? '?',
509
+ args: p?.args,
510
+ status: e.type === 'step_done' ? 'ok' : 'error',
511
+ output: e.output,
512
+ error: e.error,
513
+ durationMs: p ? Date.now() - p.t0 : undefined,
514
+ width: boxW,
515
+ }) + '\n');
516
+ }
517
+ };
518
+ const response = await a.prompt(trimmed, { onStream });
519
+ clearThinking();
520
+ // 智能体回复框 (圆角)
521
+ process.stdout.write(renderAgentMessage(response) + '\n');
475
522
  }
476
523
  catch (e) {
477
524
  if (!e.message?.includes('ERR_USE_AFTER_CLOSE') && !e.message?.includes('write after end')) {
@@ -1808,6 +1855,25 @@ function printHelp() {
1808
1855
  // ---------------------------------------------------------------------------
1809
1856
  // Entry point
1810
1857
  // ---------------------------------------------------------------------------
1858
+ /**
1859
+ * 以相同参数重新启动当前 Node 进程(用于更新后自动应用新版本)。
1860
+ * 先 detached 拉起新进程,再退出旧进程。
1861
+ */
1862
+ function restartCurrentProcess() {
1863
+ try {
1864
+ const entry = process.argv[1];
1865
+ const child = spawn(process.execPath, [entry, ...process.argv.slice(2)], {
1866
+ stdio: 'inherit',
1867
+ detached: true,
1868
+ env: { ...process.env },
1869
+ });
1870
+ child.unref();
1871
+ }
1872
+ catch {
1873
+ // 拉起失败则退回手动重启
1874
+ }
1875
+ process.exit(0);
1876
+ }
1811
1877
  async function main() {
1812
1878
  try {
1813
1879
  const args = parseArgs();
@@ -1815,10 +1881,23 @@ async function main() {
1815
1881
  printHelp();
1816
1882
  process.exit(0);
1817
1883
  }
1818
- // 自动更新已禁用: 启动时不再自动检查/安装更新.
1819
- // 想手动检查: bolloon --update-check
1820
- // 想手动更新: bolloon --update-now [package]
1821
- // 想完全屏蔽 (CI / sandbox): BOLLOON_SKIP_UPDATE=true
1884
+ // 启动自动更新检查(后台执行,不阻塞主流程)。
1885
+ // 检测到新版本会自动安装;安装成功后自动重启以应用新版本。
1886
+ // 可用 --no-update / BOLLOON_SKIP_UPDATE 关闭,
1887
+ // 或用 config.json 的 autoUpdate:false / autoRestart:false / BOLLOON_AUTO_UPDATE=1 控制。
1888
+ // 手动检查: bolloon --update-check
1889
+ // 手动更新: bolloon --update-now [package]
1890
+ if (!args.updateCheck && !args.updateNow) {
1891
+ void (async () => {
1892
+ try {
1893
+ const { checkAndUpdate } = await import('./utils/auto-update.js');
1894
+ await checkAndUpdate({ onUpdated: restartCurrentProcess });
1895
+ }
1896
+ catch {
1897
+ // 自动更新失败不影响主程序启动
1898
+ }
1899
+ })();
1900
+ }
1822
1901
  const mode = args.web ? 'web' : 'cli';
1823
1902
  const isNonInteractive = !!(args.tool || args.prompt);
1824
1903
  const originalLog = console.log;