@bolloon/bolloon-agent 0.3.5 → 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/cli/loading-tui.js +386 -39
- package/dist/cli-entry.js +5 -10
- package/dist/electron/main.js +16 -0
- package/dist/electron/main.js.map +1 -1
- package/dist/index.js +106 -121
- package/dist/utils/auto-update.js +292 -112
- package/dist/utils/auto-update.js.map +1 -0
- package/dist/web/client.js +27 -13
- package/dist/web/server.js +4 -0
- package/dist/web/ui/message-renderer.js +6 -9
- package/package.json +8 -8
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,8 +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
|
-
import {
|
|
16
|
-
//
|
|
16
|
+
import { printBanner, renderDashboard, renderDialog, renderUserMessage, renderAgentMessage, renderToolCall, flowConnector, termWidth } from './cli/loading-tui.js';
|
|
17
|
+
// 启动自动检查更新:后台、节流、检测到新版本自动安装(可被 --no-update / BOLLOON_SKIP_UPDATE 关闭)
|
|
17
18
|
import { createRequire } from 'module';
|
|
18
19
|
const _require = createRequire(import.meta.url);
|
|
19
20
|
const _BOLLOON_VERSION = (() => {
|
|
@@ -43,14 +44,7 @@ const HIDE_CURSOR = '\x1b[?25l';
|
|
|
43
44
|
const SHOW_CURSOR = '\x1b[?25h';
|
|
44
45
|
const s = {
|
|
45
46
|
banner: () => {
|
|
46
|
-
|
|
47
|
-
const pad = Math.max(0, 39 - 17 - verStr.length);
|
|
48
|
-
const spaces = ' '.repeat(pad);
|
|
49
|
-
console.log(`\n${CYAN}${BOLD}
|
|
50
|
-
╔═══════════════════════════════════════════╗
|
|
51
|
-
║ ${WHITE}🤖 Bolloon ${CYAN}${verStr}${spaces}║
|
|
52
|
-
║ ${WHITE}P2P AI Document Processor${CYAN} ║
|
|
53
|
-
╚═══════════════════════════════════════════╝${RESET}\n`);
|
|
47
|
+
printBanner(_BOLLOON_VERSION);
|
|
54
48
|
},
|
|
55
49
|
step: (num, total, text, status) => {
|
|
56
50
|
const check = status === 'ok' ? `${GREEN}✓` :
|
|
@@ -97,6 +91,7 @@ const s = {
|
|
|
97
91
|
},
|
|
98
92
|
dialog: async (title, promptText) => {
|
|
99
93
|
return new Promise((resolve) => {
|
|
94
|
+
console.log(renderDialog({ title, prompt: promptText }));
|
|
100
95
|
const rl = readline.createInterface({
|
|
101
96
|
input: process.stdin,
|
|
102
97
|
output: process.stdout
|
|
@@ -369,6 +364,30 @@ function startCLI(comm) {
|
|
|
369
364
|
}
|
|
370
365
|
readline.emitKeypressEvents(process.stdin);
|
|
371
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');
|
|
372
391
|
showBottomPrompt();
|
|
373
392
|
const promptTimer = setInterval(() => {
|
|
374
393
|
if (isRunning && promptVisible) {
|
|
@@ -463,16 +482,43 @@ async function processInput(input, comm) {
|
|
|
463
482
|
return;
|
|
464
483
|
}
|
|
465
484
|
try {
|
|
485
|
+
// 已发送消息框
|
|
486
|
+
process.stdout.write(renderUserMessage(trimmed) + '\n');
|
|
466
487
|
const a = await getAgent();
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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');
|
|
476
522
|
}
|
|
477
523
|
catch (e) {
|
|
478
524
|
if (!e.message?.includes('ERR_USE_AFTER_CLOSE') && !e.message?.includes('write after end')) {
|
|
@@ -1809,70 +1855,66 @@ function printHelp() {
|
|
|
1809
1855
|
// ---------------------------------------------------------------------------
|
|
1810
1856
|
// Entry point
|
|
1811
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
|
+
}
|
|
1812
1877
|
async function main() {
|
|
1813
|
-
let loading = null;
|
|
1814
1878
|
try {
|
|
1815
1879
|
const args = parseArgs();
|
|
1816
1880
|
if (args.help) {
|
|
1817
1881
|
printHelp();
|
|
1818
1882
|
process.exit(0);
|
|
1819
1883
|
}
|
|
1820
|
-
//
|
|
1821
|
-
//
|
|
1822
|
-
//
|
|
1823
|
-
//
|
|
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
|
+
}
|
|
1824
1901
|
const mode = args.web ? 'web' : 'cli';
|
|
1825
1902
|
const isNonInteractive = !!(args.tool || args.prompt);
|
|
1826
|
-
const isTuiMode = mode === 'cli' && !isNonInteractive && args.tui;
|
|
1827
1903
|
const originalLog = console.log;
|
|
1828
1904
|
const originalInfo = console.info;
|
|
1829
1905
|
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
1830
1906
|
const isSdkLog = (msg) => {
|
|
1831
1907
|
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(msg);
|
|
1832
1908
|
};
|
|
1833
|
-
// CLI interactive mode: suppress all startup output, show minimal spinner
|
|
1834
1909
|
const isCLIInteractive = mode === 'cli' && !isNonInteractive;
|
|
1835
|
-
|
|
1836
|
-
if (loading) {
|
|
1837
|
-
loading.setSteps([
|
|
1838
|
-
'LLM provider 检测',
|
|
1839
|
-
'DIAP 身份生成',
|
|
1840
|
-
'DID 发布到 IPFS',
|
|
1841
|
-
'P2P 网络启动',
|
|
1842
|
-
'iroh transport 启动',
|
|
1843
|
-
'Bolloon 上下文 bootstrap',
|
|
1844
|
-
'Web 服务启动',
|
|
1845
|
-
]);
|
|
1846
|
-
loading.start('启动中...');
|
|
1910
|
+
if (isCLIInteractive) {
|
|
1847
1911
|
console.log = () => { };
|
|
1848
1912
|
console.info = () => { };
|
|
1849
1913
|
process.stdout.write = () => true;
|
|
1850
1914
|
}
|
|
1851
|
-
else if (isTuiMode) {
|
|
1852
|
-
console.log = (...args) => {
|
|
1853
|
-
const msg = args.join(' ');
|
|
1854
|
-
if (isSdkLog(msg))
|
|
1855
|
-
return;
|
|
1856
|
-
originalLog.apply(console, args);
|
|
1857
|
-
};
|
|
1858
|
-
console.info = (...args) => {
|
|
1859
|
-
const msg = args.join(' ');
|
|
1860
|
-
if (isSdkLog(msg))
|
|
1861
|
-
return;
|
|
1862
|
-
originalInfo.apply(console, args);
|
|
1863
|
-
};
|
|
1864
|
-
process.stdout.write = (chunk, ...args) => {
|
|
1865
|
-
const msg = String(chunk);
|
|
1866
|
-
if (isSdkLog(msg))
|
|
1867
|
-
return true;
|
|
1868
|
-
return originalStdoutWrite(chunk, ...args);
|
|
1869
|
-
};
|
|
1870
|
-
}
|
|
1871
1915
|
if (isNonInteractive) {
|
|
1872
1916
|
console.error = () => { };
|
|
1873
1917
|
}
|
|
1874
|
-
s.banner();
|
|
1875
|
-
s.section('系统初始化');
|
|
1876
1918
|
const hasOpenAI = !!process.env.OPENAI_API_KEY;
|
|
1877
1919
|
// 2026-06-15: 修复 — 之前 anthropic 401 是因为 shell env 残留的旧 ANTHROPIC_API_KEY 抢了 provider 选择
|
|
1878
1920
|
// 用 BOLLOON_LLM_PROVIDER env 显式覆盖, 否则还是按 env hasXxx 顺序自动选
|
|
@@ -1896,32 +1938,21 @@ async function main() {
|
|
|
1896
1938
|
hasGlm ? 'GLM' :
|
|
1897
1939
|
hasQwen ? 'Qwen' : null;
|
|
1898
1940
|
if (llmProvider) {
|
|
1899
|
-
s.step(0, 4, `LLM: ${llmProvider}`, 'ok');
|
|
1900
|
-
loading?.completeStep(0, 'ok', `LLM: ${llmProvider}`);
|
|
1901
1941
|
initMinimax({ provider: llmProvider.toLowerCase() });
|
|
1902
1942
|
}
|
|
1903
1943
|
else {
|
|
1904
|
-
s.step(0, 4, 'LLM: 未配置', 'warn');
|
|
1905
|
-
loading?.completeStep(0, 'warn', 'LLM: 未配置');
|
|
1906
1944
|
if (isNonInteractive) {
|
|
1907
1945
|
s.warn('未设置任何 LLM API Key,功能受限');
|
|
1908
1946
|
}
|
|
1909
1947
|
}
|
|
1910
|
-
loading?.startStep(1, '生成 DIAP 身份...');
|
|
1911
1948
|
const { keypair, did, name } = await bootstrapIdentity();
|
|
1912
1949
|
agentIdentity = { did, name, publicKey: Buffer.from(keypair.publicKey).toString('hex') };
|
|
1913
|
-
loading?.completeStep(1, 'ok', `身份 ${name}`);
|
|
1914
|
-
loading?.startStep(2, '发布 DID 到 IPFS...');
|
|
1915
1950
|
publishDID(name, keypair).then(({ cid, ipnsName }) => {
|
|
1916
1951
|
if (cid)
|
|
1917
1952
|
agentIdentity.cid = cid;
|
|
1918
1953
|
if (ipnsName)
|
|
1919
1954
|
agentIdentity.ipnsName = ipnsName;
|
|
1920
|
-
|
|
1921
|
-
}).catch(() => {
|
|
1922
|
-
loading?.completeStep(2, 'warn', 'DID 本地模式');
|
|
1923
|
-
});
|
|
1924
|
-
loading?.startStep(3, '启动 P2P 网络...');
|
|
1955
|
+
}).catch(() => { });
|
|
1925
1956
|
const verifier = createVerificationManager();
|
|
1926
1957
|
let comm = null;
|
|
1927
1958
|
try {
|
|
@@ -1933,9 +1964,7 @@ async function main() {
|
|
|
1933
1964
|
agentIdentity.peerId = connections[0].publicKey;
|
|
1934
1965
|
agentIdentity.p2pChannel = 'bolloon-agent-harness';
|
|
1935
1966
|
}
|
|
1936
|
-
loading?.completeStep(3, 'ok', 'P2P 已连接');
|
|
1937
1967
|
}).catch(err => {
|
|
1938
|
-
loading?.completeStep(3, 'warn', 'P2P Web 模式启动失败');
|
|
1939
1968
|
s.warn(`P2P Web 模式启动失败: ${err.message}`);
|
|
1940
1969
|
});
|
|
1941
1970
|
}
|
|
@@ -1946,32 +1975,24 @@ async function main() {
|
|
|
1946
1975
|
agentIdentity.peerId = connections[0].publicKey;
|
|
1947
1976
|
agentIdentity.p2pChannel = 'bolloon-agent-harness';
|
|
1948
1977
|
}
|
|
1949
|
-
loading?.completeStep(3, 'ok', 'P2P 已连接');
|
|
1950
1978
|
}
|
|
1951
1979
|
}
|
|
1952
1980
|
catch (err) {
|
|
1953
1981
|
s.warn(`P2P 初始化失败: ${err.message}`);
|
|
1954
1982
|
s.warn('将使用无 P2P 模式运行');
|
|
1955
|
-
loading?.completeStep(3, 'error', 'P2P 初始化失败');
|
|
1956
1983
|
}
|
|
1957
|
-
loading?.startStep(4, '启动 iroh transport...');
|
|
1958
1984
|
await bootstrapIroh(keypair, name);
|
|
1959
|
-
loading?.completeStep(4, 'ok', 'iroh 已就绪');
|
|
1960
1985
|
// Bolloon Bootstrap: 启动扫描 + Context 收集 + 挂定时任务
|
|
1961
1986
|
// 失败静默 (主流程不被阻塞)
|
|
1962
|
-
loading?.startStep(5, '正在 bootstrap bolloon 上下文...');
|
|
1963
1987
|
try {
|
|
1964
1988
|
const { bootstrapBolloon } = await import('./pi-ecosystem-judgment/human-value-pipeline.js');
|
|
1965
1989
|
s.info('正在 bootstrap bolloon 上下文...');
|
|
1966
1990
|
const bs = await bootstrapBolloon({ cwd: process.cwd() });
|
|
1967
1991
|
s.info(`Bootstrap 完成 (${bs.durationMs}ms, ${bs.errors.length} 个非致命错误)`);
|
|
1968
|
-
loading?.completeStep(5, 'ok', `Bootstrap 完成 (${bs.durationMs}ms)`);
|
|
1969
1992
|
}
|
|
1970
1993
|
catch (err) {
|
|
1971
1994
|
s.warn(`Bootstrap 失败 (非致命, 主流程继续): ${err.message}`);
|
|
1972
|
-
loading?.completeStep(5, 'warn', 'Bootstrap 失败 (已跳过)');
|
|
1973
1995
|
}
|
|
1974
|
-
s.divider();
|
|
1975
1996
|
if (mode === 'web') {
|
|
1976
1997
|
const port = parseInt(process.env.PORT || '54188');
|
|
1977
1998
|
// 2026-06-16: BOLLOON_DEV_MODE=1 或 selfImprove=true 启动项 → 开发者模式, 启用自迭代 (健康监控+自改总线)
|
|
@@ -1981,12 +2002,9 @@ async function main() {
|
|
|
1981
2002
|
console.log('[startup] BOLLOON_DEV_MODE=1, 开发者模式: 自迭代已启用');
|
|
1982
2003
|
}
|
|
1983
2004
|
const { createWebServer, openBrowser } = await import('./web/server.js');
|
|
1984
|
-
loading?.startStep(6, `启动 Web 服务端口 ${port}...`);
|
|
1985
|
-
s.info(`启动 Web 服务端口 ${port}...`);
|
|
1986
2005
|
// 2026-06-24: CLI 默认 loopback bind (安全), LAN 访问需 BOLLOON_HOST=0.0.0.0
|
|
1987
2006
|
const bindHost = process.env.BOLLOON_HOST;
|
|
1988
2007
|
const { port: actualPort } = await createWebServer(port, { selfImprove, ...(bindHost ? { host: bindHost } : {}) });
|
|
1989
|
-
loading?.completeStep(6, 'ok', `Web 服务 :${actualPort}`);
|
|
1990
2008
|
const displayHost = bindHost ?? '127.0.0.1';
|
|
1991
2009
|
s.success(`浏览器已打开 → http://${displayHost}:${actualPort}`);
|
|
1992
2010
|
openBrowser(`http://${displayHost}:${actualPort}`);
|
|
@@ -2005,46 +2023,13 @@ async function main() {
|
|
|
2005
2023
|
}
|
|
2006
2024
|
}
|
|
2007
2025
|
else {
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
console.info = originalInfo;
|
|
2012
|
-
process.stdout.write = originalStdoutWrite;
|
|
2013
|
-
loading.stop(true);
|
|
2014
|
-
}
|
|
2015
|
-
else {
|
|
2016
|
-
// For non-loading TUI CLI mode: apply SDK filtering if needed
|
|
2017
|
-
if (isTuiMode) {
|
|
2018
|
-
console.log = (...args) => {
|
|
2019
|
-
const msg = args.join(' ');
|
|
2020
|
-
if (isSdkLog(msg))
|
|
2021
|
-
return;
|
|
2022
|
-
originalLog.apply(console, args);
|
|
2023
|
-
};
|
|
2024
|
-
console.info = (...args) => {
|
|
2025
|
-
const msg = args.join(' ');
|
|
2026
|
-
if (isSdkLog(msg))
|
|
2027
|
-
return;
|
|
2028
|
-
originalInfo.apply(console, args);
|
|
2029
|
-
};
|
|
2030
|
-
process.stdout.write = (chunk, ...args) => {
|
|
2031
|
-
const msg = String(chunk);
|
|
2032
|
-
if (isSdkLog(msg))
|
|
2033
|
-
return true;
|
|
2034
|
-
return originalStdoutWrite(chunk, ...args);
|
|
2035
|
-
};
|
|
2036
|
-
}
|
|
2037
|
-
else {
|
|
2038
|
-
console.log = originalLog;
|
|
2039
|
-
console.info = originalInfo;
|
|
2040
|
-
process.stdout.write = originalStdoutWrite;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2026
|
+
console.log = originalLog;
|
|
2027
|
+
console.info = originalInfo;
|
|
2028
|
+
process.stdout.write = originalStdoutWrite;
|
|
2043
2029
|
startCLI(comm);
|
|
2044
2030
|
}
|
|
2045
2031
|
}
|
|
2046
2032
|
catch (e) {
|
|
2047
|
-
loading?.stop(false);
|
|
2048
2033
|
throw e;
|
|
2049
2034
|
}
|
|
2050
2035
|
}
|