@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.
Files changed (53) hide show
  1. package/dist/agents/pi-sdk-tools.js +107 -0
  2. package/dist/agents/pi-sdk.js +20 -2
  3. package/dist/bootstrap/context-collector.js +5 -1
  4. package/dist/cli/ink-app.js +46 -8
  5. package/dist/cli/loading-tui.js +7 -5
  6. package/dist/cli-entry.js +1 -1
  7. package/dist/electron/config.js +9 -14
  8. package/dist/electron/dialogs.js +16 -53
  9. package/dist/electron/first-run.js +24 -65
  10. package/dist/electron/ipc.js +10 -14
  11. package/dist/electron/logger.js +7 -44
  12. package/dist/electron/main.js +42 -45
  13. package/dist/electron/menu.js +13 -18
  14. package/dist/electron/paths.js +12 -54
  15. package/dist/electron/server.js +18 -57
  16. package/dist/electron/tray.js +15 -53
  17. package/dist/electron/window.js +22 -61
  18. package/dist/electron-build/electron/config.js +21 -0
  19. package/dist/electron-build/electron/config.js.map +1 -0
  20. package/dist/electron-build/electron/dialogs.js +108 -0
  21. package/dist/electron-build/electron/dialogs.js.map +1 -0
  22. package/dist/electron-build/electron/first-run.js +170 -0
  23. package/dist/electron-build/electron/first-run.js.map +1 -0
  24. package/dist/electron-build/electron/ipc.js +20 -0
  25. package/dist/electron-build/electron/ipc.js.map +1 -0
  26. package/dist/electron-build/electron/logger.js +114 -0
  27. package/dist/electron-build/electron/logger.js.map +1 -0
  28. package/dist/electron-build/electron/main.js +79 -0
  29. package/dist/electron-build/electron/main.js.map +1 -0
  30. package/dist/electron-build/electron/menu.js +145 -0
  31. package/dist/electron-build/electron/menu.js.map +1 -0
  32. package/dist/electron-build/electron/paths.js +75 -0
  33. package/dist/electron-build/electron/paths.js.map +1 -0
  34. package/dist/electron-build/electron/server.js +119 -0
  35. package/dist/electron-build/electron/server.js.map +1 -0
  36. package/dist/electron-build/electron/tray.js +111 -0
  37. package/dist/electron-build/electron/tray.js.map +1 -0
  38. package/dist/electron-build/electron/window.js +111 -0
  39. package/dist/electron-build/electron/window.js.map +1 -0
  40. package/dist/electron-build/electron-preload.js +32 -0
  41. package/dist/electron-build/electron-preload.js.map +1 -0
  42. package/dist/electron-build/electron.js +8 -0
  43. package/dist/electron-build/electron.js.map +1 -0
  44. package/dist/electron-build/utils/auto-update.js +606 -0
  45. package/dist/electron-build/utils/auto-update.js.map +1 -0
  46. package/dist/electron-preload.js +16 -19
  47. package/dist/electron.js +1 -4
  48. package/dist/index.js +134 -31
  49. package/dist/llm/config-store.js +18 -2
  50. package/dist/llm/pi-ai.js +29 -11
  51. package/dist/security/tool-gate.js +2 -0
  52. package/dist/utils/auto-update.js +19 -51
  53. package/package.json +6 -5
@@ -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
- const electron_1 = require("electron");
9
- electron_1.contextBridge.exposeInMainWorld('electronAPI', {
6
+ import { contextBridge, ipcRenderer } from 'electron';
7
+ contextBridge.exposeInMainWorld('electronAPI', {
10
8
  // === 原有 (保留) ===
11
- getVersion: () => electron_1.ipcRenderer.invoke('get-version'),
12
- getUserDataPath: () => electron_1.ipcRenderer.invoke('get-user-data-path'),
13
- openExternal: (url) => electron_1.ipcRenderer.invoke('open-external', 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: () => electron_1.ipcRenderer.invoke('get-data-path'),
13
+ getDataPath: () => ipcRenderer.invoke('get-data-path'),
16
14
  // === 新增: 文件 dialog ===
17
- openFile: (opts) => electron_1.ipcRenderer.invoke('dialog:open-file', opts),
18
- saveFile: (opts) => electron_1.ipcRenderer.invoke('dialog:save-file', opts),
19
- openDirectory: (opts) => electron_1.ipcRenderer.invoke('dialog:open-directory', 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) => electron_1.ipcRenderer.invoke('fs:read-text-file', opts),
22
- writeTextFile: (opts) => electron_1.ipcRenderer.invoke('fs:write-text-file', opts),
23
- pathExists: (opts) => electron_1.ipcRenderer.invoke('fs:path-exists', 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: () => electron_1.ipcRenderer.invoke('first-run:seen'),
26
- markFirstRunSeen: () => electron_1.ipcRenderer.invoke('first-run:mark-seen'),
27
- getDataPathSync: () => electron_1.ipcRenderer.invoke('first-run:data-dir'),
28
- getLogsPathSync: () => electron_1.ipcRenderer.invoke('first-run:logs-dir'),
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
- require("./electron/main");
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
- // eslint-disable-next-line @typescript-eslint/no-var-requires
377
- const cm = require('./bootstrap/context-manager.js').getContextManager();
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
- const foundProvider = providerNames.find(([k]) => process.env[k]);
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
- if (e.type === 'step_start') {
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
- // context-compaction 的估算器 (与 pi-sdk estimateHistoryTokens 同源: 4 字符 ≈ 1 token)
1146
- const history = a.messageHistory ?? [];
1147
- let usedTokens = 0;
1148
- try {
1149
- const { estimateTokens } = require('./context-compaction/index.js');
1150
- usedTokens = estimateTokens(history);
1151
- }
1152
- catch {
1153
- usedTokens = Math.max(0, Math.round(JSON.stringify(history).length / 4));
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
- process.stdout.write = () => true;
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
- publishDID(name, keypair).then(({ cid, ipnsName }) => {
2640
- if (cid)
2641
- agentIdentity.cid = cid;
2642
- if (ipnsName)
2643
- agentIdentity.ipnsName = ipnsName;
2644
- }).catch(() => { });
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
- comm = await bootstrapP2P(verifier);
2662
- const connections = comm.getConnections();
2663
- if (connections.length > 0) {
2664
- agentIdentity.peerId = connections[0].publicKey;
2665
- agentIdentity.p2pChannel = 'bolloon-agent-harness';
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) {
@@ -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
- const CONFIG_PATH = path.join(CONFIG_DIR, 'llm-config.json');
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
- await fs.writeFile(CONFIG_PATH, JSON.stringify(this.config, null, 2));
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 = path.join(process.env.HOME || '/tmp', '.bolloon', 'llm-config.json');
597
- const configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
598
- if (configData.activeProvider && configData.providers[configData.activeProvider]) {
599
- console.log('[PiAIModel] Detected provider from config:', configData.activeProvider);
600
- return configData.activeProvider;
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 = path.join(process.env.HOME || '/tmp', '.bolloon', 'llm-config.json');
660
- const configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
661
- const providerConfig = configData.providers[provider];
662
- if (providerConfig?.apiKey) {
663
- apiKey = providerConfig.apiKey;
664
- console.log('[PiAIModel] Loaded apiKey from config for', provider);
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
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
- if (k2 === undefined) k2 = k;
19
- var desc = Object.getOwnPropertyDescriptor(m, k);
20
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
- desc = { enumerable: true, get: function() { return m[k]; } };
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 = (0, child_process_1.execSync)('npm root -g', { encoding: 'utf-8', timeout: 8000 }).trim();
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 = (0, child_process_1.execSync)('npm prefix -g', { encoding: 'utf-8', timeout: 8000 }).trim();
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 = (0, child_process_1.execSync)('npm outdated --json', {
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
- (0, child_process_1.execSync)(args.join(' '), {
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.36",
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.1",
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": {