@bolloon/bolloon-agent 0.3.36 → 0.3.38
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/agents/pi-sdk-tools.js +107 -0
- package/dist/agents/pi-sdk.js +20 -2
- package/dist/bootstrap/context-collector.js +5 -1
- package/dist/cli/ink-app.js +46 -8
- package/dist/cli/loading-tui.js +7 -5
- package/dist/cli-entry.js +1 -1
- package/dist/electron/config.js +9 -14
- package/dist/electron/dialogs.js +16 -53
- package/dist/electron/first-run.js +24 -65
- package/dist/electron/ipc.js +10 -14
- package/dist/electron/logger.js +7 -44
- package/dist/electron/main.js +42 -45
- package/dist/electron/menu.js +13 -18
- package/dist/electron/paths.js +12 -54
- package/dist/electron/server.js +18 -57
- package/dist/electron/tray.js +15 -53
- package/dist/electron/window.js +22 -61
- package/dist/electron-build/electron/config.js +21 -0
- package/dist/electron-build/electron/config.js.map +1 -0
- package/dist/electron-build/electron/dialogs.js +108 -0
- package/dist/electron-build/electron/dialogs.js.map +1 -0
- package/dist/electron-build/electron/first-run.js +170 -0
- package/dist/electron-build/electron/first-run.js.map +1 -0
- package/dist/electron-build/electron/ipc.js +20 -0
- package/dist/electron-build/electron/ipc.js.map +1 -0
- package/dist/electron-build/electron/logger.js +114 -0
- package/dist/electron-build/electron/logger.js.map +1 -0
- package/dist/electron-build/electron/main.js +79 -0
- package/dist/electron-build/electron/main.js.map +1 -0
- package/dist/electron-build/electron/menu.js +145 -0
- package/dist/electron-build/electron/menu.js.map +1 -0
- package/dist/electron-build/electron/paths.js +75 -0
- package/dist/electron-build/electron/paths.js.map +1 -0
- package/dist/electron-build/electron/server.js +119 -0
- package/dist/electron-build/electron/server.js.map +1 -0
- package/dist/electron-build/electron/tray.js +111 -0
- package/dist/electron-build/electron/tray.js.map +1 -0
- package/dist/electron-build/electron/window.js +111 -0
- package/dist/electron-build/electron/window.js.map +1 -0
- package/dist/electron-build/electron-preload.js +32 -0
- package/dist/electron-build/electron-preload.js.map +1 -0
- package/dist/electron-build/electron.js +8 -0
- package/dist/electron-build/electron.js.map +1 -0
- package/dist/electron-build/utils/auto-update.js +606 -0
- package/dist/electron-build/utils/auto-update.js.map +1 -0
- package/dist/electron-preload.js +16 -19
- package/dist/electron.js +1 -4
- package/dist/index.js +134 -31
- package/dist/llm/config-store.js +18 -2
- package/dist/llm/pi-ai.js +29 -11
- package/dist/security/tool-gate.js +2 -0
- package/dist/utils/auto-update.js +19 -51
- package/package.json +6 -5
package/dist/electron-preload.js
CHANGED
|
@@ -1,32 +1,29 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
1
|
/**
|
|
4
2
|
* Electron Preload 脚本
|
|
5
3
|
* 在渲染进程和主进程之间建立安全的通信桥梁
|
|
6
4
|
* contextIsolation: true, nodeIntegration: false — 只能通过这里暴露的 API 触达主进程
|
|
7
5
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import { contextBridge, ipcRenderer } from 'electron';
|
|
7
|
+
contextBridge.exposeInMainWorld('electronAPI', {
|
|
10
8
|
// === 原有 (保留) ===
|
|
11
|
-
getVersion: () =>
|
|
12
|
-
getUserDataPath: () =>
|
|
13
|
-
openExternal: (url) =>
|
|
9
|
+
getVersion: () => ipcRenderer.invoke('get-version'),
|
|
10
|
+
getUserDataPath: () => ipcRenderer.invoke('get-user-data-path'),
|
|
11
|
+
openExternal: (url) => ipcRenderer.invoke('open-external', url),
|
|
14
12
|
// === 新增: 数据目录 ===
|
|
15
|
-
getDataPath: () =>
|
|
13
|
+
getDataPath: () => ipcRenderer.invoke('get-data-path'),
|
|
16
14
|
// === 新增: 文件 dialog ===
|
|
17
|
-
openFile: (opts) =>
|
|
18
|
-
saveFile: (opts) =>
|
|
19
|
-
openDirectory: (opts) =>
|
|
15
|
+
openFile: (opts) => ipcRenderer.invoke('dialog:open-file', opts),
|
|
16
|
+
saveFile: (opts) => ipcRenderer.invoke('dialog:save-file', opts),
|
|
17
|
+
openDirectory: (opts) => ipcRenderer.invoke('dialog:open-directory', opts),
|
|
20
18
|
// === 新增: 文件系统 (限大小, 主进程守卫) ===
|
|
21
|
-
readTextFile: (opts) =>
|
|
22
|
-
writeTextFile: (opts) =>
|
|
23
|
-
pathExists: (opts) =>
|
|
19
|
+
readTextFile: (opts) => ipcRenderer.invoke('fs:read-text-file', opts),
|
|
20
|
+
writeTextFile: (opts) => ipcRenderer.invoke('fs:write-text-file', opts),
|
|
21
|
+
pathExists: (opts) => ipcRenderer.invoke('fs:path-exists', opts),
|
|
24
22
|
// === 新增: 首启引导 ===
|
|
25
|
-
getFirstRunSeen: () =>
|
|
26
|
-
markFirstRunSeen: () =>
|
|
27
|
-
getDataPathSync: () =>
|
|
28
|
-
getLogsPathSync: () =>
|
|
23
|
+
getFirstRunSeen: () => ipcRenderer.invoke('first-run:seen'),
|
|
24
|
+
markFirstRunSeen: () => ipcRenderer.invoke('first-run:mark-seen'),
|
|
25
|
+
getDataPathSync: () => ipcRenderer.invoke('first-run:data-dir'),
|
|
26
|
+
getLogsPathSync: () => ipcRenderer.invoke('first-run:logs-dir'),
|
|
29
27
|
// === 元数据 ===
|
|
30
28
|
platform: process.platform,
|
|
31
29
|
});
|
|
32
|
-
//# sourceMappingURL=electron-preload.js.map
|
package/dist/electron.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
1
|
/**
|
|
4
2
|
* Electron 入口 shim — 真正逻辑在 src/electron/main.ts
|
|
5
3
|
* (保留 src/electron.ts 平铺入口, 不动 package.json 的 dist/electron.js 解析)
|
|
6
4
|
*/
|
|
7
|
-
|
|
8
|
-
//# sourceMappingURL=electron.js.map
|
|
5
|
+
import './electron/main';
|
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, renderDialog, renderUserMessage, renderAgentMessage, renderToolCallListItem, termWidth } from './cli/loading-tui.js';
|
|
18
|
+
import { printBanner, renderDialog, renderUserMessage, renderAgentMessage, renderMessageBox, renderToolCallListItem, termWidth } from './cli/loading-tui.js';
|
|
19
19
|
import { startInk, stopInk, inkAppendLine as appendLine, inkSetStatus, inkSetThinking } from './cli/ink-app.js';
|
|
20
20
|
// 启动自动检查更新:后台、节流、检测到新版本自动安装(可被 --no-update / BOLLOON_SKIP_UPDATE 关闭)
|
|
21
21
|
import { createRequire } from 'module';
|
|
@@ -230,6 +230,13 @@ async function bootstrapP2P(verifier) {
|
|
|
230
230
|
// ---------------------------------------------------------------------------
|
|
231
231
|
// iroh/Hybrid P2P 初始化
|
|
232
232
|
// ---------------------------------------------------------------------------
|
|
233
|
+
/** 启动路径网络操作超时门: 超时 reject (由调用方 catch 降级) — 防止弱网下 CLI 卡死在启动 (2026-08-07) */
|
|
234
|
+
function withTimeout(p, ms, label) {
|
|
235
|
+
return Promise.race([
|
|
236
|
+
p,
|
|
237
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} 超时 (${ms}ms)`)), ms)),
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
233
240
|
async function bootstrapIroh(keypair, name) {
|
|
234
241
|
s.step(4, 5, '启动 iroh P2P', 'loading');
|
|
235
242
|
try {
|
|
@@ -371,10 +378,14 @@ function fmtDuration(ms) {
|
|
|
371
378
|
return `${h}h ${m % 60}m`;
|
|
372
379
|
}
|
|
373
380
|
/** 2026-08-06: 从 ContextManager 读上下文用量 (CLI 状态栏数据源, 失败退化 0/1M) */
|
|
381
|
+
// 2026-08-07: 不能用 require 加载 ESM 模块 (ERR_REQUIRE_ESM) → startCLI 里 await import 一次缓存引用,
|
|
382
|
+
// 同步函数 getCliCtxUsage/getStatus 复用 — 之前裸 require 与 _require 都抛错被 catch → 状态栏恒 0/1M
|
|
383
|
+
let _ctxManagerRef = null;
|
|
374
384
|
function getCliCtxUsage() {
|
|
375
385
|
try {
|
|
376
|
-
|
|
377
|
-
|
|
386
|
+
const cm = _ctxManagerRef?.getContextManager();
|
|
387
|
+
if (!cm)
|
|
388
|
+
return { pct: 0, usedTokens: 0, maxTokens: 1_000_000, stage: 'normal' };
|
|
378
389
|
const u = cm.getUsage();
|
|
379
390
|
return {
|
|
380
391
|
// 保留浮点 (0-100), 由 buildContextBar 格式化 — round 会让 <0.5% 全变 0, 状态栏像死代码
|
|
@@ -438,7 +449,8 @@ async function startCLI(comm) {
|
|
|
438
449
|
peerCount = comm.getConnections().length;
|
|
439
450
|
}
|
|
440
451
|
catch { /* */ }
|
|
441
|
-
// 读取 LLM 模型名
|
|
452
|
+
// 读取 LLM 模型名 — 优先 bolloon-config.json (activeProvider), 再退 env (2026-08-07:
|
|
453
|
+
// 之前只读 env → 用户配了配置文件但状态栏显示"未配置")
|
|
442
454
|
const providerNames = [
|
|
443
455
|
['OPENAI_API_KEY', 'OpenAI'],
|
|
444
456
|
['ANTHROPIC_API_KEY', 'Anthropic'],
|
|
@@ -449,8 +461,23 @@ async function startCLI(comm) {
|
|
|
449
461
|
['XAI_API_KEY', 'xAI'],
|
|
450
462
|
['TOGETHER_API_KEY', 'Together'],
|
|
451
463
|
];
|
|
452
|
-
|
|
464
|
+
let foundProvider = providerNames.find(([k]) => process.env[k]);
|
|
453
465
|
cliModelName = foundProvider ? foundProvider[1] : '未配置';
|
|
466
|
+
// bolloon-config.json activeProvider 优先 (用户真实配置来源)
|
|
467
|
+
try {
|
|
468
|
+
const { llmConfigStore } = await import('./llm/config-store.js');
|
|
469
|
+
await llmConfigStore.initialize();
|
|
470
|
+
const active = await llmConfigStore.getActiveProvider();
|
|
471
|
+
if (active) {
|
|
472
|
+
const label = String(active).trim();
|
|
473
|
+
cliModelName = label || cliModelName;
|
|
474
|
+
const cfg = await llmConfigStore.getActiveProviderConfig().catch(() => null);
|
|
475
|
+
const model = cfg?.model;
|
|
476
|
+
if (model)
|
|
477
|
+
cliModelName = `${label} · ${model}`;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
catch { /* config-store 失败静默, 用 env 结果 */ }
|
|
454
481
|
cliAgentName = agentIdentity?.name || 'bolloon';
|
|
455
482
|
cliStartTime = Date.now();
|
|
456
483
|
// 恢复上次 active channel (session 恢复: CLI 与 Web 共用 active-channel.json)
|
|
@@ -469,6 +496,11 @@ async function startCLI(comm) {
|
|
|
469
496
|
}
|
|
470
497
|
// 进入 Ink TUI 输入循环
|
|
471
498
|
// 2026-08-06: 初始状态栏也带上下文显示 (0/1M │ [░░░░░░░░░░] 0%)
|
|
499
|
+
// 2026-08-07: 缓存 ContextManager 引用 (require 加载 ESM 抛 ERR_REQUIRE_ESM, 状态栏恒 0 根因)
|
|
500
|
+
try {
|
|
501
|
+
_ctxManagerRef = await import('./bootstrap/context-manager.js');
|
|
502
|
+
}
|
|
503
|
+
catch { /* 降级: getCliCtxUsage 返回 0/1M */ }
|
|
472
504
|
const initialStatus = `${C_ACCENT}${cliModelName}${RESET}${C_DIM} │${RESET} ${cliAgentName} ${C_DIM}│${RESET} ⏱ 0s${C_DIM} │${RESET} ${buildContextBar(getCliCtxUsage())}`;
|
|
473
505
|
startInk((text) => { processInput(text, comm); }, initialStatus, getStatus);
|
|
474
506
|
// Wait on a promise that resolves on Ctrl+C / 双击 Esc
|
|
@@ -1095,7 +1127,38 @@ async function processInput(input, comm) {
|
|
|
1095
1127
|
// 工具调用显示由 tui-shell 的 onStream handler 处理
|
|
1096
1128
|
const response = await a.prompt(trimmed, {
|
|
1097
1129
|
onStream: (e) => {
|
|
1098
|
-
|
|
1130
|
+
// 2026-08-07: 中间思考/状态显示 — 之前只显示工具步骤, LLM 的思考过程
|
|
1131
|
+
// (thinking / status / phase / Reflection) 全被丢弃 → 用户只能看到输入和最终输出
|
|
1132
|
+
// 用户偏好 (2026-08-07): 思考过程 = 圆角框渲染 (和回复同路径 renderMessageBox),
|
|
1133
|
+
// 颜文字动画 (inkSetThinking) 只表示"正在运行", 不承载思考内容
|
|
1134
|
+
if (e.type === 'thinking' && e.content) {
|
|
1135
|
+
// thinking 事件只有 "🤔 开始思考..." 占位 → 不 appendLine, 运行过程由动画表示;
|
|
1136
|
+
// 真正思考内容在 status 的 Reflection/💡 事件 → 下方框渲染
|
|
1137
|
+
}
|
|
1138
|
+
else if (e.phase && !e.type) {
|
|
1139
|
+
const ph = String(e.phase);
|
|
1140
|
+
const detail = e.detail ? ` (${String(e.detail).slice(0, 60)})` : '';
|
|
1141
|
+
const phLabel = {
|
|
1142
|
+
intent_classified: '意图识别',
|
|
1143
|
+
tool_selected: '工具选择',
|
|
1144
|
+
reflection: '反思',
|
|
1145
|
+
planning: '规划',
|
|
1146
|
+
};
|
|
1147
|
+
appendLine(`${C_WARN}◈ ${phLabel[ph] || ph}${detail}${RESET}`);
|
|
1148
|
+
}
|
|
1149
|
+
else if (e.type === 'status' && e.content) {
|
|
1150
|
+
const content = String(e.content);
|
|
1151
|
+
// Reflection / 反思 / 💡 → 圆角思考框 (和回复一样走 renderMessageBox, 白字+亮边框)
|
|
1152
|
+
if (content.includes('Reflection') || content.includes('反思') || content.includes('💡')) {
|
|
1153
|
+
const body = content.replace(/^💡\s*/, '').slice(0, 1500);
|
|
1154
|
+
if (body.trim())
|
|
1155
|
+
appendLine(renderMessageBox({ title: '💡 反思', body, color: C_WARN }));
|
|
1156
|
+
}
|
|
1157
|
+
else if (!content.includes('🔄 循环') && !content.includes('📋 参数')) {
|
|
1158
|
+
appendLine(`${C_DIM}${content}${RESET}`);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
else if (e.type === 'step_start') {
|
|
1099
1162
|
tuiToolCounter++;
|
|
1100
1163
|
tuiToolCalls.push({ tool: e.tool || '?', args: e.args, _t: Date.now() });
|
|
1101
1164
|
}
|
|
@@ -1139,21 +1202,26 @@ async function processInput(input, comm) {
|
|
|
1139
1202
|
}
|
|
1140
1203
|
// 更新状态栏: 上下文进度 (2026-08-06: 每轮按当前 messageHistory 重算并写回 ContextManager,
|
|
1141
1204
|
// 保证状态栏按需更新 — 不依赖 pi-sdk loop 内部上报, 1s 定时器读到的一定是最新值)
|
|
1205
|
+
// 2026-08-07 修复: pi-sdk loop 每轮已用 estimateHistoryTokens() 上报 ContextManager (pi-sdk.ts:1223),
|
|
1206
|
+
// 这里直接读现值 — 之前用 (a as any).messageHistory 重算, 私有字段拿不到恒为 [] → 0,
|
|
1207
|
+
// 还把 pi-sdk 上报的真实值覆盖成 0 (状态栏永远 0.00% 的根因)
|
|
1142
1208
|
try {
|
|
1143
1209
|
const { getContextManager } = await import('./bootstrap/context-manager.js');
|
|
1144
1210
|
const cm = getContextManager();
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1211
|
+
let usage = cm.getUsage();
|
|
1212
|
+
if (!usage || usage.usedTokens <= 0) {
|
|
1213
|
+
// fallback: 非交互/异常路径 pi-sdk 未上报时兜底估算 (4 字符 ≈ 1 token)
|
|
1214
|
+
const hist = a?.agent?.messageHistory ?? a?.piAgent?.messageHistory ?? [];
|
|
1215
|
+
let t = 0;
|
|
1216
|
+
try {
|
|
1217
|
+
const { estimateTokens } = _require('./context-compaction/index.js');
|
|
1218
|
+
t = estimateTokens(hist);
|
|
1219
|
+
}
|
|
1220
|
+
catch {
|
|
1221
|
+
t = Math.max(0, Math.round(JSON.stringify(hist).length / 4));
|
|
1222
|
+
}
|
|
1223
|
+
usage = cm.updateUsage(Math.max(t, usage?.usedTokens ?? 0));
|
|
1154
1224
|
}
|
|
1155
|
-
// 写回数据源 — 状态栏/Web/任何订阅方都拿到新鲜值
|
|
1156
|
-
const usage = cm.updateUsage(usedTokens);
|
|
1157
1225
|
const usageView = {
|
|
1158
1226
|
// 保留浮点 (0-100), buildContextBar 内部格式化
|
|
1159
1227
|
pct: Math.min(100, (usage.usedTokens / Math.max(1, usage.maxTokens)) * 100),
|
|
@@ -2599,7 +2667,16 @@ async function main() {
|
|
|
2599
2667
|
if (isCLIInteractive) {
|
|
2600
2668
|
console.log = () => { };
|
|
2601
2669
|
console.info = () => { };
|
|
2602
|
-
|
|
2670
|
+
// 2026-08-07: 只吞 SDK 结构化日志 (2026-...T 前缀直接写 stdout 的), 其余 (Ink ANSI 渲染) 走原始 write
|
|
2671
|
+
// 不能整体 no-op — Ink 渲染依赖 write callback, 且 pty/管道缓冲满时 no-op 会掩盖真实写状态
|
|
2672
|
+
process.stdout.write = ((chunk, ...rest) => {
|
|
2673
|
+
const s = String(chunk);
|
|
2674
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(s))
|
|
2675
|
+
return true; // SDK 结构化日志 → 吞
|
|
2676
|
+
return originalStdoutWrite(chunk, ...rest);
|
|
2677
|
+
});
|
|
2678
|
+
// 2026-08-07: 交互模式静音 auto-update 后台通知 (stderr), 避免 "🔍 检查更新" 污染 TUI
|
|
2679
|
+
void import('./utils/auto-update.js').then(({ setNotifyQuiet }) => setNotifyQuiet(true)).catch(() => { });
|
|
2603
2680
|
}
|
|
2604
2681
|
if (isNonInteractive) {
|
|
2605
2682
|
console.error = () => { };
|
|
@@ -2636,12 +2713,30 @@ async function main() {
|
|
|
2636
2713
|
}
|
|
2637
2714
|
const { keypair, did, name } = await bootstrapIdentity();
|
|
2638
2715
|
agentIdentity = { did, name, publicKey: Buffer.from(keypair.publicKey).toString('hex') };
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2716
|
+
// 2026-08-07: CLI 模式后台自动安装/启动本地 Kubo (web 模式 server.ts 已有, CLI 缺 → IPNS 发布后无法解析的根因)
|
|
2717
|
+
// fire-and-forget, 不阻塞启动; Kubo 就绪后才发布 DID (避免 registerAgent 在 Kubo 未启动时 30s 超时)
|
|
2718
|
+
// 装好后 /ipfs /ipns /ipfs_add /ipns_publish 工具可用
|
|
2719
|
+
void (async () => {
|
|
2720
|
+
let kuboReady = false;
|
|
2721
|
+
try {
|
|
2722
|
+
const sdk = await import('@diap/sdk');
|
|
2723
|
+
const checkKuboSetup = sdk.checkKuboSetup;
|
|
2724
|
+
if (typeof checkKuboSetup === 'function') {
|
|
2725
|
+
const setup = await checkKuboSetup(true, true);
|
|
2726
|
+
kuboReady = !!(setup?.ready && setup?.daemonRunning);
|
|
2727
|
+
s.info(kuboReady ? 'IPFS 本地 Kubo 就绪 → IPNS 发布/解析可用' : 'Kubo 不可用, IPFS 降级本地模式');
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
catch (e) {
|
|
2731
|
+
s.warn(`Kubo 自动安装失败 (非致命): ${String(e?.message || e).slice(0, 120)}`);
|
|
2732
|
+
}
|
|
2733
|
+
publishDID(name, keypair).then(({ cid, ipnsName }) => {
|
|
2734
|
+
if (cid)
|
|
2735
|
+
agentIdentity.cid = cid;
|
|
2736
|
+
if (ipnsName)
|
|
2737
|
+
agentIdentity.ipnsName = ipnsName;
|
|
2738
|
+
}).catch(() => { });
|
|
2739
|
+
})();
|
|
2645
2740
|
const verifier = createVerificationManager();
|
|
2646
2741
|
let comm = null;
|
|
2647
2742
|
try {
|
|
@@ -2658,11 +2753,18 @@ async function main() {
|
|
|
2658
2753
|
});
|
|
2659
2754
|
}
|
|
2660
2755
|
else {
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2756
|
+
// 2026-08-07: 弱网下 hyperswarm DHT start/joinTopic 可能无限挂起 → 20s 超时门, 超时降级无 P2P 模式
|
|
2757
|
+
comm = await withTimeout(bootstrapP2P(verifier), 20_000, 'P2P 网络初始化')
|
|
2758
|
+
.catch((err) => {
|
|
2759
|
+
s.warn(`P2P 初始化超时/失败, 降级无 P2P 模式: ${err.message}`);
|
|
2760
|
+
return null;
|
|
2761
|
+
});
|
|
2762
|
+
if (comm) {
|
|
2763
|
+
const connections = comm.getConnections();
|
|
2764
|
+
if (connections.length > 0) {
|
|
2765
|
+
agentIdentity.peerId = connections[0].publicKey;
|
|
2766
|
+
agentIdentity.p2pChannel = 'bolloon-agent-harness';
|
|
2767
|
+
}
|
|
2666
2768
|
}
|
|
2667
2769
|
}
|
|
2668
2770
|
}
|
|
@@ -2670,13 +2772,14 @@ async function main() {
|
|
|
2670
2772
|
s.warn(`P2P 初始化失败: ${err.message}`);
|
|
2671
2773
|
s.warn('将使用无 P2P 模式运行');
|
|
2672
2774
|
}
|
|
2673
|
-
await bootstrapIroh(keypair, name)
|
|
2775
|
+
await withTimeout(bootstrapIroh(keypair, name), 15_000, 'iroh P2P 初始化')
|
|
2776
|
+
.catch((err) => s.warn(`iroh 初始化超时, 继续使用 Hyperswarm P2P: ${err.message}`));
|
|
2674
2777
|
// Bolloon Bootstrap: 启动扫描 + Context 收集 + 挂定时任务
|
|
2675
2778
|
// 失败静默 (主流程不被阻塞)
|
|
2676
2779
|
try {
|
|
2677
2780
|
const { bootstrapBolloon } = await import('./pi-ecosystem-judgment/human-value-pipeline.js');
|
|
2678
2781
|
s.info('正在 bootstrap bolloon 上下文...');
|
|
2679
|
-
const bs = await bootstrapBolloon({ cwd: process.cwd() });
|
|
2782
|
+
const bs = await withTimeout(bootstrapBolloon({ cwd: process.cwd() }), 20_000, 'Bolloon 上下文扫描');
|
|
2680
2783
|
s.info(`Bootstrap 完成 (${bs.durationMs}ms, ${bs.errors.length} 个非致命错误)`);
|
|
2681
2784
|
}
|
|
2682
2785
|
catch (err) {
|
package/dist/llm/config-store.js
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
import * as fs from 'fs/promises';
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
const CONFIG_DIR = path.join(process.env.HOME || '/tmp', '.bolloon');
|
|
8
|
-
|
|
8
|
+
// 2026-08-07: llm-config.json → bolloon-config.json (统一配置文件名, Bolloon 自己有写权限)
|
|
9
|
+
// initialize() 会做一次迁移: 旧文件存在且新文件不存在 → 复制旧内容, 之后统一读写新文件
|
|
10
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'bolloon-config.json');
|
|
11
|
+
const LEGACY_CONFIG_PATH = path.join(CONFIG_DIR, 'llm-config.json');
|
|
9
12
|
export const DEFAULT_PROVIDER_CONFIGS = {
|
|
10
13
|
openai: {
|
|
11
14
|
enabled: false,
|
|
@@ -245,6 +248,18 @@ class LLMConfigStore {
|
|
|
245
248
|
return;
|
|
246
249
|
try {
|
|
247
250
|
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
251
|
+
// 2026-08-07: 迁移 — 旧 llm-config.json 存在且新 bolloon-config.json 不存在时复制旧内容
|
|
252
|
+
try {
|
|
253
|
+
await fs.access(CONFIG_PATH);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
try {
|
|
257
|
+
const legacy = await fs.readFile(LEGACY_CONFIG_PATH, 'utf-8');
|
|
258
|
+
await fs.writeFile(CONFIG_PATH, legacy, { mode: 0o600 });
|
|
259
|
+
console.log('[config-store] 已迁移 llm-config.json → bolloon-config.json');
|
|
260
|
+
}
|
|
261
|
+
catch { /* 旧文件也不存在 → 走默认配置 */ }
|
|
262
|
+
}
|
|
248
263
|
const data = await fs.readFile(CONFIG_PATH, 'utf-8');
|
|
249
264
|
const loadedConfig = JSON.parse(data);
|
|
250
265
|
// 确保加载的配置包含所有默认供应商,缺失的用默认值补充
|
|
@@ -271,7 +286,8 @@ class LLMConfigStore {
|
|
|
271
286
|
if (!this.config)
|
|
272
287
|
return;
|
|
273
288
|
this.config.updatedAt = new Date().toISOString();
|
|
274
|
-
|
|
289
|
+
// 2026-08-07: mode 0o600 — 仅当前用户可读写 (含 API key 的敏感配置); Bolloon 自身进程可写
|
|
290
|
+
await fs.writeFile(CONFIG_PATH, JSON.stringify(this.config, null, 2), { mode: 0o600 });
|
|
275
291
|
}
|
|
276
292
|
async getConfig() {
|
|
277
293
|
await this.initialize();
|
package/dist/llm/pi-ai.js
CHANGED
|
@@ -590,14 +590,30 @@ ${prompt}`;
|
|
|
590
590
|
}
|
|
591
591
|
}
|
|
592
592
|
let modelInstance = null;
|
|
593
|
+
/** 2026-08-07: bolloon-config.json 优先, 旧 llm-config.json 兜底 (迁移期兼容) */
|
|
594
|
+
function resolveConfigPath() {
|
|
595
|
+
const home = process.env.HOME || '/tmp';
|
|
596
|
+
const base = path.join(home, '.bolloon');
|
|
597
|
+
for (const name of ['bolloon-config.json', 'llm-config.json']) {
|
|
598
|
+
const p = path.join(base, name);
|
|
599
|
+
try {
|
|
600
|
+
if (fs.existsSync(p))
|
|
601
|
+
return p;
|
|
602
|
+
}
|
|
603
|
+
catch { /* continue */ }
|
|
604
|
+
}
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
593
607
|
function detectProvider() {
|
|
594
608
|
// 首先检查配置文件(优先级最高)
|
|
595
609
|
try {
|
|
596
|
-
const configPath =
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
610
|
+
const configPath = resolveConfigPath();
|
|
611
|
+
if (configPath) {
|
|
612
|
+
const configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
613
|
+
if (configData.activeProvider && configData.providers[configData.activeProvider]) {
|
|
614
|
+
console.log('[PiAIModel] Detected provider from config:', configData.activeProvider);
|
|
615
|
+
return configData.activeProvider;
|
|
616
|
+
}
|
|
601
617
|
}
|
|
602
618
|
}
|
|
603
619
|
catch { }
|
|
@@ -656,12 +672,14 @@ export function initPiAI(config = {}) {
|
|
|
656
672
|
let apiKey = config.apiKey;
|
|
657
673
|
if (!apiKey) {
|
|
658
674
|
try {
|
|
659
|
-
const configPath =
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
675
|
+
const configPath = resolveConfigPath();
|
|
676
|
+
if (configPath) {
|
|
677
|
+
const configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
678
|
+
const providerConfig = configData.providers[provider];
|
|
679
|
+
if (providerConfig?.apiKey) {
|
|
680
|
+
apiKey = providerConfig.apiKey;
|
|
681
|
+
console.log('[PiAIModel] Loaded apiKey from config for', provider);
|
|
682
|
+
}
|
|
665
683
|
}
|
|
666
684
|
}
|
|
667
685
|
catch (e) {
|
|
@@ -64,6 +64,8 @@ const TOOL_WHITELIST = new Set([
|
|
|
64
64
|
'cid_save', 'cid_load', 'cid_update', 'cid_version', 'cid_list', 'cid_share',
|
|
65
65
|
'context_save_snapshot', 'context_restore',
|
|
66
66
|
'ui_save_component', 'ui_load_component',
|
|
67
|
+
// 2026-08-07: Bolloon 自身配置读写 (bolloon-config.json, agent 有修改自身配置权限)
|
|
68
|
+
'bolloon_config_get', 'bolloon_config_set',
|
|
67
69
|
]);
|
|
68
70
|
export const gateWhitelist = { gate: 'whitelist', allowed: true };
|
|
69
71
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* npm 自动更新检查器
|
|
4
3
|
*
|
|
@@ -14,48 +13,11 @@
|
|
|
14
13
|
* 开发态下若 cwd 的 package.json 就是本包则回退到 cwd,不受任意工作目录影响。
|
|
15
14
|
* - 检查频率受节流缓存约束(默认 24h 一次),不会每次启动都打 npm。
|
|
16
15
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
Object.defineProperty(o, k2, desc);
|
|
24
|
-
}) : (function(o, m, k, k2) {
|
|
25
|
-
if (k2 === undefined) k2 = k;
|
|
26
|
-
o[k2] = m[k];
|
|
27
|
-
}));
|
|
28
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
-
}) : function(o, v) {
|
|
31
|
-
o["default"] = v;
|
|
32
|
-
});
|
|
33
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
-
var ownKeys = function(o) {
|
|
35
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
-
var ar = [];
|
|
37
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
-
return ar;
|
|
39
|
-
};
|
|
40
|
-
return ownKeys(o);
|
|
41
|
-
};
|
|
42
|
-
return function (mod) {
|
|
43
|
-
if (mod && mod.__esModule) return mod;
|
|
44
|
-
var result = {};
|
|
45
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
-
__setModuleDefault(result, mod);
|
|
47
|
-
return result;
|
|
48
|
-
};
|
|
49
|
-
})();
|
|
50
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
-
exports.checkAndUpdate = checkAndUpdate;
|
|
52
|
-
exports.checkForUpdates = checkForUpdates;
|
|
53
|
-
exports.performUpdate = performUpdate;
|
|
54
|
-
const child_process_1 = require("child_process");
|
|
55
|
-
const fs = __importStar(require("fs"));
|
|
56
|
-
const path = __importStar(require("path"));
|
|
57
|
-
const https = __importStar(require("https"));
|
|
58
|
-
const http = __importStar(require("http"));
|
|
16
|
+
import { execSync } from 'child_process';
|
|
17
|
+
import * as fs from 'fs';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import * as https from 'https';
|
|
20
|
+
import * as http from 'http';
|
|
59
21
|
// ANSI 颜色
|
|
60
22
|
const RESET = '\x1b[0m';
|
|
61
23
|
const BOLD = '\x1b[1m';
|
|
@@ -66,10 +28,17 @@ const MAGENTA = '\x1b[35m';
|
|
|
66
28
|
const PKG_NAME = '@bolloon/bolloon-agent';
|
|
67
29
|
const BOLLOON_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '/tmp', '.bolloon');
|
|
68
30
|
const UPDATE_CACHE = path.join(BOLLOON_DIR, '.update-check.json');
|
|
31
|
+
/** 2026-08-07: CLI 交互模式下静音自动更新通知 (避免后台检查污染 TUI 屏幕) */
|
|
32
|
+
let notifyQuiet = false;
|
|
33
|
+
export function setNotifyQuiet(v) {
|
|
34
|
+
notifyQuiet = v;
|
|
35
|
+
}
|
|
69
36
|
/**
|
|
70
37
|
* 通知用户。使用 stderr,避免在交互式 CLI 模式下 stdout 被吞掉。
|
|
71
38
|
*/
|
|
72
39
|
function notify(msg, color = RESET) {
|
|
40
|
+
if (notifyQuiet)
|
|
41
|
+
return;
|
|
73
42
|
process.stderr.write(`${color}${msg}${RESET}`);
|
|
74
43
|
}
|
|
75
44
|
function httpGet(url) {
|
|
@@ -113,7 +82,7 @@ function getGlobalBolloonDir() {
|
|
|
113
82
|
const candidates = [];
|
|
114
83
|
// 1. npm root -g(最可靠)
|
|
115
84
|
try {
|
|
116
|
-
const npmRoot =
|
|
85
|
+
const npmRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 8000 }).trim();
|
|
117
86
|
if (npmRoot)
|
|
118
87
|
candidates.push(path.join(npmRoot, '@bolloon', 'bolloon-agent'));
|
|
119
88
|
}
|
|
@@ -122,7 +91,7 @@ function getGlobalBolloonDir() {
|
|
|
122
91
|
}
|
|
123
92
|
// 2. npm prefix -g
|
|
124
93
|
try {
|
|
125
|
-
const npmPrefix =
|
|
94
|
+
const npmPrefix = execSync('npm prefix -g', { encoding: 'utf-8', timeout: 8000 }).trim();
|
|
126
95
|
if (npmPrefix)
|
|
127
96
|
candidates.push(path.join(npmPrefix, 'lib', 'node_modules', '@bolloon', 'bolloon-agent'));
|
|
128
97
|
}
|
|
@@ -280,7 +249,7 @@ async function checkBolloonUpdates() {
|
|
|
280
249
|
*/
|
|
281
250
|
function checkNpmOutdated() {
|
|
282
251
|
try {
|
|
283
|
-
const output =
|
|
252
|
+
const output = execSync('npm outdated --json', {
|
|
284
253
|
encoding: 'utf-8',
|
|
285
254
|
timeout: 30000,
|
|
286
255
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -344,7 +313,7 @@ async function updatePackagesWithVersion(packagesWithVersion) {
|
|
|
344
313
|
const args = ['npm', 'install', '-g', ...packagesWithVersion];
|
|
345
314
|
notify(`\n${CYAN}📦 正在更新包...${RESET}\n`, RESET);
|
|
346
315
|
try {
|
|
347
|
-
|
|
316
|
+
execSync(args.join(' '), {
|
|
348
317
|
encoding: 'utf-8',
|
|
349
318
|
timeout: 300000,
|
|
350
319
|
stdio: 'inherit',
|
|
@@ -462,7 +431,7 @@ function resolveAutoUpdatePolicy() {
|
|
|
462
431
|
* 例如 Electron 用 app.relaunch(),Node 用 detached 重新 spawn)。
|
|
463
432
|
* 若未提供或 autoRestart=false,则仅提示用户手动重启。
|
|
464
433
|
*/
|
|
465
|
-
async function checkAndUpdate(opts = {}) {
|
|
434
|
+
export async function checkAndUpdate(opts = {}) {
|
|
466
435
|
const policy = resolveAutoUpdatePolicy();
|
|
467
436
|
if (policy.blocked) {
|
|
468
437
|
return { hasUpdate: false, info: null, updated: false, message: '跳过更新检查(已显式禁用)' };
|
|
@@ -566,13 +535,13 @@ async function checkAndUpdate(opts = {}) {
|
|
|
566
535
|
/**
|
|
567
536
|
* 仅检查更新,不自动安装
|
|
568
537
|
*/
|
|
569
|
-
async function checkForUpdates() {
|
|
538
|
+
export async function checkForUpdates() {
|
|
570
539
|
return await checkBolloonUpdates();
|
|
571
540
|
}
|
|
572
541
|
/**
|
|
573
542
|
* 手动触发更新
|
|
574
543
|
*/
|
|
575
|
-
async function performUpdate(packages) {
|
|
544
|
+
export async function performUpdate(packages) {
|
|
576
545
|
return await updatePackages(packages);
|
|
577
546
|
}
|
|
578
547
|
// CLI 入口
|
|
@@ -595,4 +564,3 @@ if (process.argv[1]?.includes('auto-update')) {
|
|
|
595
564
|
}
|
|
596
565
|
})();
|
|
597
566
|
}
|
|
598
|
-
//# sourceMappingURL=auto-update.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bolloon/bolloon-agent",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.38",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "P2P AI Document Agent - 全局安装后执行 `bolloon` 启动产品",
|
|
6
6
|
"main": "dist/cli-entry.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dev": "tsx -r dotenv/config src/index.ts",
|
|
36
36
|
"dev:web": "npm run build:web && tsx -r dotenv/config src/index.ts --web",
|
|
37
37
|
"dev:electron": "npm run build:electron && npm run electron:start",
|
|
38
|
-
"electron:start": "NODE_ENV=development electron dist/electron.js",
|
|
38
|
+
"electron:start": "NODE_ENV=development electron dist/electron-build/electron.js",
|
|
39
39
|
"electron:dev": "concurrently \"npm run dev:web\" \"npm run electron:start\"",
|
|
40
40
|
"electron:build": "npm run build:electron && electron-builder",
|
|
41
41
|
"electron:pack": "electron-builder --dir",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@capacitor/ios": "^8.4.2",
|
|
60
60
|
"@chainsafe/libp2p-noise": "^17.0.0",
|
|
61
61
|
"@chainsafe/libp2p-yamux": "^8.0.1",
|
|
62
|
-
"@diap/sdk": "^0.2.
|
|
62
|
+
"@diap/sdk": "^0.2.2",
|
|
63
63
|
"@libp2p/autonat": "^3.0.25",
|
|
64
64
|
"@libp2p/circuit-relay-v2": "^4.2.9",
|
|
65
65
|
"@libp2p/dcutr": "^3.0.20",
|
|
@@ -133,7 +133,8 @@
|
|
|
133
133
|
"files": [
|
|
134
134
|
"dist/**/*",
|
|
135
135
|
"bin/**/*",
|
|
136
|
-
"package.json"
|
|
136
|
+
"package.json",
|
|
137
|
+
"dist/electron-build/**/*"
|
|
137
138
|
],
|
|
138
139
|
"npmRebuild": false,
|
|
139
140
|
"buildDependenciesFromSource": false,
|
|
@@ -147,7 +148,7 @@
|
|
|
147
148
|
}
|
|
148
149
|
],
|
|
149
150
|
"extraMetadata": {
|
|
150
|
-
"main": "dist/electron.js",
|
|
151
|
+
"main": "dist/electron-build/electron.js",
|
|
151
152
|
"type": "commonjs"
|
|
152
153
|
},
|
|
153
154
|
"win": {
|