@bolloon/bolloon-agent 0.3.35 → 0.3.36

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.
@@ -2131,7 +2131,8 @@ async function ensureKuboReady() {
2131
2131
  }
2132
2132
  }
2133
2133
  }
2134
- async function kuboApi(pathAndQuery, init, timeoutMs = 30000) {
2134
+ /** Kubo HTTP API helper (POST only, 2026-08-03 实测 Kubo 只接受 POST). 2026-08-06 export 供 CLI /ipfs /ipns 命令用. */
2135
+ export async function kuboApi(pathAndQuery, init, timeoutMs = 30000) {
2135
2136
  const controller = new AbortController();
2136
2137
  const timer = setTimeout(() => controller.abort(), timeoutMs);
2137
2138
  try {
@@ -62,6 +62,21 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
62
62
  const skillCache = useRef(null);
63
63
  const pluginCache = useRef(null);
64
64
  const fileCache = useRef(null);
65
+ // ── 程序化选择器 (2026-08-06: /login /model 等命令触发, 复用 MentionPopup 渲染) ──
66
+ const [picker, setPicker] = useState(null);
67
+ const pickerCb = useRef(null);
68
+ const pickerSelRef = useRef(0);
69
+ // 全局钩子: index.ts 命令打开/关闭选择器
70
+ useEffect(() => {
71
+ globalThis.__inkOpenPicker = (itemsArg, title, onPick) => {
72
+ pickerSelRef.current = 0;
73
+ pickerCb.current = onPick;
74
+ setPicker({ title: title || '选择', items: itemsArg, sel: 0 });
75
+ setInput('');
76
+ };
77
+ globalThis.__inkClosePicker = () => { pickerCb.current = null; setPicker(null); };
78
+ return () => { delete globalThis.__inkOpenPicker; delete globalThis.__inkClosePicker; };
79
+ }, []);
65
80
  // ── 输入历史 (↑/↓ 切换) ───────────────────────────────────────────────────
66
81
  const historyRef = useRef([]);
67
82
  const historyIdxRef = useRef(-1); // -1 = 正在编辑新草稿
@@ -296,6 +311,34 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
296
311
  requestExit();
297
312
  return;
298
313
  }
314
+ // ── 程序化选择器: 全键接管 (↑↓ 选择, Enter 确认, Esc 关闭) ──
315
+ if (picker) {
316
+ const itemsArg = picker.items;
317
+ if (key.upArrow) {
318
+ pickerSelRef.current = Math.max(0, pickerSelRef.current - 1);
319
+ setPicker({ ...picker, sel: pickerSelRef.current });
320
+ return;
321
+ }
322
+ if (key.downArrow) {
323
+ pickerSelRef.current = Math.min(itemsArg.length - 1, pickerSelRef.current + 1);
324
+ setPicker({ ...picker, sel: pickerSelRef.current });
325
+ return;
326
+ }
327
+ if ((key.return || key.tab) && itemsArg.length > 0) {
328
+ const it = itemsArg[Math.min(pickerSelRef.current, itemsArg.length - 1)];
329
+ const cb = pickerCb.current;
330
+ pickerCb.current = null;
331
+ setPicker(null);
332
+ cb?.(it);
333
+ return;
334
+ }
335
+ if (key.escape) {
336
+ pickerCb.current = null;
337
+ setPicker(null);
338
+ return;
339
+ }
340
+ return; // 其余键忽略
341
+ }
299
342
  // ── 弹出窗打开: 全键接管 (TextInput focus=false 不处理) ──
300
343
  if (popupOpen) {
301
344
  if (key.upArrow) {
@@ -456,7 +499,7 @@ const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH
456
499
  }, 600);
457
500
  return () => clearInterval(timer);
458
501
  }, [thinking]);
459
- return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { flexGrow: 1, flexDirection: "column", justifyContent: "flex-start", children: [_jsx(LogoBox, { width: terminalW }), _jsx(Messages, { msgs: msgs }), thinking && (_jsx(Box, { children: _jsxs(Text, { color: "yellow", children: [KAOMOJI[thinkingIdx.current], " \u601D\u8003\u4E2D..."] }) }))] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) }), _jsx(Box, { children: _jsx(Text, { children: status }) }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) }), popupOpen && (tabState || mention) && (_jsx(MentionPopup, { title: popupTitle, items: filtered, sel: safeSel, width: terminalW, loading: loadingFiles })), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "#c4d640", children: "\u276F " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, focus: !popupOpen, placeholder: "\u8F93\u5165\u6D88\u606F... @\u667A\u80FD\u4F53 /\u547D\u4EE4 #\u6587\u4EF6 \u00B7 Esc \u53CC\u51FB\u9000\u51FA \u00B7 /queue \u6392\u961F \u00B7 !\u7EC8\u7AEF\u547D\u4EE4" }, tiKey)] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) })] }));
502
+ return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { flexGrow: 1, flexDirection: "column", justifyContent: "flex-start", children: [_jsx(LogoBox, { width: terminalW }), _jsx(Messages, { msgs: msgs }), thinking && (_jsx(Box, { children: _jsxs(Text, { color: "yellow", children: [KAOMOJI[thinkingIdx.current], " \u601D\u8003\u4E2D..."] }) }))] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) }), _jsx(Box, { children: _jsx(Text, { children: status }) }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) }), popupOpen && (tabState || mention) && (_jsx(MentionPopup, { title: popupTitle, items: filtered, sel: safeSel, width: terminalW, loading: loadingFiles })), picker && (_jsx(MentionPopup, { title: picker.title, items: picker.items, sel: Math.min(picker.sel, picker.items.length - 1), width: terminalW })), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "#c4d640", children: "\u276F " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, focus: !popupOpen && !picker, placeholder: "\u8F93\u5165\u6D88\u606F... @\u667A\u80FD\u4F53 /\u547D\u4EE4 #\u6587\u4EF6 \u00B7 Esc \u53CC\u51FB\u9000\u51FA \u00B7 /queue \u6392\u961F \u00B7 !\u7EC8\u7AEF\u547D\u4EE4" }, tiKey)] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: "#c4d640", children: '─'.repeat(terminalW) }) })] }));
460
503
  };
461
504
  export { InkApp };
462
505
  // ─── 启动 ────────────────────────────────────────────────────────────────────
@@ -21,6 +21,29 @@ const CLI_COMMANDS = [
21
21
  { kind: 'command', label: 'peers', hint: '查看 P2P 节点', insert: 'peers' },
22
22
  { kind: 'command', label: 'iroh', hint: '查看 iroh 状态', insert: 'iroh' },
23
23
  { kind: 'command', label: 'add_friend', hint: '添加好友 <64位hex公钥>', insert: 'add_friend' },
24
+ // 2026-08-06: 系统命令组
25
+ { kind: 'command', label: 'model', hint: '模型供应商选择器 (↑↓ 选择)', insert: 'model' },
26
+ { kind: 'command', label: 'login', hint: '登录模型供应商 (选择器)', insert: 'login' },
27
+ { kind: 'command', label: 'logout', hint: '查看当前供应商', insert: 'logout' },
28
+ { kind: 'command', label: 'now', hint: '当前状态总览', insert: 'now' },
29
+ { kind: 'command', label: 'session', hint: '当前会话信息', insert: 'session' },
30
+ { kind: 'command', label: 'loop', hint: '循环状态 (消息/token)', insert: 'loop' },
31
+ { kind: 'command', label: 'memory', hint: '记忆摘要', insert: 'memory' },
32
+ { kind: 'command', label: 'resume', hint: '恢复上下文', insert: 'resume' },
33
+ { kind: 'command', label: 'goal', hint: '进行中目标', insert: 'goal' },
34
+ { kind: 'command', label: 'tools', hint: '可用工具列表', insert: 'tools' },
35
+ { kind: 'command', label: 'skill', hint: '技能候选', insert: 'skill' },
36
+ { kind: 'command', label: 'mcp', hint: 'MCP 服务器列表', insert: 'mcp' },
37
+ { kind: 'command', label: 'agent', hint: '当前智能体', insert: 'agent' },
38
+ { kind: 'command', label: 'did', hint: 'DID 身份', insert: 'did' },
39
+ { kind: 'command', label: 'ipfs', hint: 'Kubo 状态', insert: 'ipfs' },
40
+ { kind: 'command', label: 'ipns', hint: 'IPNS keys', insert: 'ipns' },
41
+ { kind: 'command', label: 'wallet', hint: '钱包状态', insert: 'wallet' },
42
+ { kind: 'command', label: 'email', hint: '邮件配置', insert: 'email' },
43
+ { kind: 'command', label: 'judgement', hint: '判断力列表', insert: 'judgement' },
44
+ { kind: 'command', label: 'insight', hint: '洞察 (08-Insights)', insert: 'insight' },
45
+ { kind: 'command', label: 'wiki', hint: 'wiki 状态', insert: 'wiki' },
46
+ { kind: 'command', label: 'dream', hint: '随机灵感', insert: 'dream' },
24
47
  ];
25
48
  /** Web 端斜杠命令 (server /message 路由 → LLM 工具) */
26
49
  const WEB_COMMANDS = [
package/dist/cli-entry.js CHANGED
@@ -62,17 +62,24 @@ ${BOLD}选项:${RESET}
62
62
  --help, -h 显示帮助信息
63
63
 
64
64
  ${BOLD}命令:${RESET}
65
- bolloon --read <file> 读取文档
66
- bolloon --summarize <file> 总结文档
67
- bolloon --improve <file> <req> 改进文档
68
- bolloon x402 fetch <url> x402 自动支付 HTTP 请求
69
- bolloon x402 balance <address> 查询 x402 钱包余额
65
+ bolloon update [--now] 检查更新 / 立即更新 (bolloon update --now)
66
+ bolloon model [name] [model] 列出 / 切换模型供应商 (如: bolloon model deepseek deepseek-v4-flash)
67
+ bolloon read <file> 读取文档
68
+ bolloon summarize <file> 总结文档
69
+ bolloon improve <file> <req> 改进文档
70
+ bolloon engine list 列出外部编码智能体
71
+ bolloon engine run <prompt> 委派任务给智能体
72
+ bolloon x402 fetch <url> x402 自动支付 HTTP 请求
73
+ bolloon x402 balance <address> 查询 x402 钱包余额
70
74
 
71
75
  ${BOLD}示例:${RESET}
72
76
  bolloon # 启动图形界面
73
77
  bolloon --web # 启动 Web UI
74
- bolloon --read 想法.md # 读取文档
75
78
  bolloon --cli # 命令行模式
79
+ bolloon model # 查看当前模型供应商
80
+ bolloon model minimax # 切换到 MiniMax
81
+ bolloon update # 检查更新
82
+ bolloon update --now # 立即更新
76
83
 
77
84
  ${BOLD}环境变量:${RESET}
78
85
  MINIMAX_API_KEY MiniMax API 密钥
@@ -139,6 +146,16 @@ function parseArgs() {
139
146
  return { mode: 'engine', args: args.slice(1) };
140
147
  case 'x402':
141
148
  return { mode: 'x402', args: args.slice(1) };
149
+ // 2026-08-06: 子命令形式 (去掉 -- 前缀)
150
+ case 'update':
151
+ return { mode: 'update', args: args.slice(1) };
152
+ case 'model':
153
+ return { mode: 'model', args: args.slice(1) };
154
+ case 'read':
155
+ case 'summarize':
156
+ case 'improve':
157
+ // 映射回旧 --flag 格式传给主程序 (保留 index.ts 现有实现)
158
+ return { mode: 'passthrough', args: [`--${mode}`, ...args.slice(1)] };
142
159
  default:
143
160
  // 传递所有参数给主程序
144
161
  return { mode: 'passthrough', args };
@@ -259,6 +276,89 @@ async function handleX402Command(x402Args) {
259
276
  console.error(`未知 x402 子命令: ${sub}`);
260
277
  process.exit(1);
261
278
  }
279
+ /** update 子命令: 检查 / 执行更新 (bolloon update [--now|now] [packages]) */
280
+ async function handleUpdateCommand(updateArgs) {
281
+ const { checkForUpdates, performUpdate } = await import('./utils/auto-update.js');
282
+ // bolloon update --now / bolloon update now [packages] — 立即更新
283
+ if (updateArgs[0] === '--now' || updateArgs[0] === 'now') {
284
+ const packages = updateArgs.slice(1).filter(a => !a.startsWith('-'));
285
+ console.log('🔄 正在检查并更新...');
286
+ const result = await performUpdate(packages.length > 0 ? packages : undefined);
287
+ if (result.success) {
288
+ console.log(`${GREEN}✅ 更新成功${result.updatedPackages ? `: ${result.updatedPackages.join(', ')}` : ''}${RESET}`);
289
+ if (result.updated)
290
+ console.log(`${YELLOW} 请重新启动应用以使用新版本${RESET}`);
291
+ }
292
+ else {
293
+ console.error(`${MAGENTA}❌ 更新失败: ${result.error}${RESET}`);
294
+ process.exit(1);
295
+ }
296
+ return;
297
+ }
298
+ // 默认: 检查更新
299
+ console.log('🔄 正在检查更新...');
300
+ const info = await checkForUpdates();
301
+ if (info && info.outdated) {
302
+ console.log(`${CYAN}📦 发现更新可用:${RESET}\n`);
303
+ console.log(` 当前版本: ${info.version}`);
304
+ console.log(` 最新版本: ${info.latest}\n`);
305
+ console.log(` 待更新包:`);
306
+ for (const p of info.packages)
307
+ console.log(` - ${p.name}: ${p.current} → ${p.latest}`);
308
+ console.log(`\n 运行 ${GREEN}bolloon update --now${RESET} 执行更新`);
309
+ }
310
+ else {
311
+ console.log(`${GREEN}✅ 已是最新版本${RESET} (${info?.version || 'unknown'})`);
312
+ }
313
+ }
314
+ /** model 子命令: 列出 / 切换模型供应商 (bolloon model [name] [model]) */
315
+ async function handleModelCommand(modelArgs) {
316
+ const { llmConfigStore, PROVIDER_INFO } = await import('./llm/config-store.js');
317
+ await llmConfigStore.initialize();
318
+ // 无参: 列出所有供应商 + 当前 active
319
+ if (modelArgs.length === 0) {
320
+ const config = await llmConfigStore.getConfig();
321
+ console.log(`\n${BOLD}模型供应商${RESET} (当前: ${config.activeProvider})\n`);
322
+ console.log('─'.repeat(58));
323
+ for (const [name, p] of Object.entries(config.providers)) {
324
+ const info = PROVIDER_INFO[name] || {};
325
+ const active = name === config.activeProvider ? '●' : '○';
326
+ const keyState = p.apiKey ? '🔑' : p.requiresApiKey ? '⚠ 无 key' : '';
327
+ const model = p.model || (info.models && info.models[0]) || '';
328
+ console.log(` ${active} ${name.padEnd(10)} ${String(info.name || '').padEnd(16)} ${keyState.padEnd(8)} model: ${model}`);
329
+ }
330
+ console.log(`\n${BOLD}用法:${RESET}`);
331
+ console.log(` bolloon model <name> # 切换到该供应商`);
332
+ console.log(` bolloon model <name> <model> # 切换并指定模型`);
333
+ console.log(` 示例: bolloon model deepseek deepseek-v4-flash`);
334
+ return;
335
+ }
336
+ // 切换供应商
337
+ const name = modelArgs[0].toLowerCase();
338
+ const config = await llmConfigStore.getConfig();
339
+ const providers = config.providers;
340
+ if (!providers[name]) {
341
+ console.error(`${MAGENTA}❌ 未知供应商: ${name}${RESET}`);
342
+ console.error(` 可用: ${Object.keys(providers).join(', ')}`);
343
+ process.exit(1);
344
+ }
345
+ const provider = providers[name];
346
+ if (provider.requiresApiKey && !provider.apiKey) {
347
+ console.error(`${MAGENTA}❌ ${name} 需要 API key (当前未配置)${RESET}`);
348
+ console.error(` 配置方式: ① Web UI API 配置页 ② 环境变量 (如 DEEPSEEK_API_KEY)`);
349
+ process.exit(1);
350
+ }
351
+ await llmConfigStore.setActiveProvider(name);
352
+ let modelNote = '';
353
+ if (modelArgs[1]) {
354
+ await llmConfigStore.updateProvider(name, { model: modelArgs[1] });
355
+ modelNote = `, model=${modelArgs[1]}`;
356
+ }
357
+ const info = PROVIDER_INFO[name] || {};
358
+ console.log(`${GREEN}✅ 已切换到 ${name}${RESET} (${info.name || ''})${modelNote}`);
359
+ console.log(` 当前模型: ${modelArgs[1] || provider.model || (info.models && info.models[0]) || '默认'}`);
360
+ console.log(` 配置已持久化: ~/.bolloon/llm-config.json`);
361
+ }
262
362
  /** 引擎子命令: list / run */
263
363
  async function handleEngineCommand(engineArgs) {
264
364
  if (engineArgs.length === 0) {
@@ -462,6 +562,13 @@ async function main() {
462
562
  case 'x402':
463
563
  await handleX402Command(args);
464
564
  break;
565
+ // 2026-08-06: 子命令 (bolloon update / bolloon model)
566
+ case 'update':
567
+ await handleUpdateCommand(args);
568
+ break;
569
+ case 'model':
570
+ await handleModelCommand(args);
571
+ break;
465
572
  case 'passthrough':
466
573
  // 传递所有参数给主程序
467
574
  try {
package/dist/index.js CHANGED
@@ -581,12 +581,438 @@ async function processInput(input, comm) {
581
581
  appendLine(`${C_WARN}[队列 ${pendingQueue.length}]${RESET} 已入队, 执行完当前后自动运行`);
582
582
  return;
583
583
  }
584
+ // ==================== 2026-08-06: 系统命令组 (/model /now /ipfs /memory ...) ====================
585
+ const cmd = trimmed.toLowerCase();
586
+ // /model /login — 模型供应商选择器 (ink 交互渲染, 复用 MentionPopup)
587
+ if (cmd === '/model' || cmd === '/login') {
588
+ try {
589
+ const { llmConfigStore, PROVIDER_INFO } = await import('./llm/config-store.js');
590
+ await llmConfigStore.initialize();
591
+ const config = await llmConfigStore.getConfig();
592
+ const items = Object.entries(config.providers).map(([name, p]) => ({
593
+ kind: 'command',
594
+ label: name,
595
+ hint: `${String(PROVIDER_INFO[name]?.name || '').padEnd(14)} ${p.apiKey ? '🔑' : p.requiresApiKey ? '⚠ 无key' : ''} ${p.model || ''}`,
596
+ insert: name,
597
+ }));
598
+ globalThis.__inkOpenPicker?.(items, '选择模型供应商 (↑↓ 选择 · Enter 确认 · Esc 取消)', async (it) => {
599
+ try {
600
+ await llmConfigStore.setActiveProvider(it.label);
601
+ const active = await llmConfigStore.getActiveProvider();
602
+ appendLine(`${C_OK}✓ 已切换到 ${it.label} (${String(PROVIDER_INFO[it.label]?.name || '')})${RESET}`);
603
+ appendLine(`${C_DIM} 当前模型: ${config.providers[it.label]?.model || '默认'}${RESET}`);
604
+ }
605
+ catch (e) {
606
+ appendLine(`${C_ERROR}✗ 切换失败: ${String(e.message || e).slice(0, 150)}${RESET}`);
607
+ }
608
+ });
609
+ }
610
+ catch (e) {
611
+ appendLine(`${C_ERROR}/model 失败: ${String(e.message || e).slice(0, 150)}${RESET}`);
612
+ }
613
+ return;
614
+ }
615
+ // /logout — 显示当前供应商 (减法: 登出 = 查看当前, 切换走 /model)
616
+ if (cmd === '/logout') {
617
+ try {
618
+ const { llmConfigStore, PROVIDER_INFO } = await import('./llm/config-store.js');
619
+ await llmConfigStore.initialize();
620
+ const active = await llmConfigStore.getActiveProvider();
621
+ const cfg = await llmConfigStore.getActiveProviderConfig();
622
+ appendLine(`${C_DIM}当前供应商:${RESET} ${C_ACCENT}${active}${RESET} (${String(PROVIDER_INFO[active]?.name || '')})`);
623
+ appendLine(`${C_DIM} 模型: ${cfg?.model || '默认'}${RESET}`);
624
+ appendLine(`${C_DIM} 切换: /model 打开选择器${RESET}`);
625
+ }
626
+ catch { /* 静默 */ }
627
+ return;
628
+ }
629
+ // /now — 当前状态总览
630
+ if (cmd === '/now') {
631
+ try {
632
+ const cm = require('./bootstrap/context-manager.js').getContextManager();
633
+ const usage = cm.getUsage();
634
+ appendLine(`${C_ACCENT}● 当前状态${RESET}`);
635
+ appendLine(` ${C_DIM}智能体:${RESET} ${cliAgentName} ${cliActiveChannelId ? `(${C_DIM}ch:${cliActiveChannelId.slice(0, 12)}${RESET})` : ''}`);
636
+ appendLine(` ${C_DIM}运行:${RESET} ${fmtDuration(Date.now() - cliStartTime)}`);
637
+ appendLine(` ${C_DIM}上下文:${RESET} ${(usage.usedTokens / 1000).toFixed(0)}k / ${(usage.maxTokens / 1000).toFixed(0)}k tokens (${Math.round(usage.pct * 100)}%)${usage.stage === 'warning' ? ` ${C_WARN}⚠ 即将压缩${RESET}` : ''}`);
638
+ const a = await getAgent();
639
+ appendLine(` ${C_DIM}消息:${RESET} ${a.messageHistory?.length ?? 0} 条`);
640
+ }
641
+ catch { /* 静默 */ }
642
+ return;
643
+ }
644
+ // /tools — 可用工具列表
645
+ if (cmd === '/tools') {
646
+ try {
647
+ const a = await getAgent();
648
+ const defs = (a.getToolDefinitions?.() ?? []);
649
+ const names = Array.isArray(defs) ? defs.map((d) => d.name || d.function?.name).filter(Boolean) : Object.keys(defs || {});
650
+ appendLine(`${C_ACCENT}可用工具 (${names.length}):${RESET}`);
651
+ for (const n of names.slice(0, 40))
652
+ appendLine(` ${C_DIM}·${RESET} ${n}`);
653
+ if (names.length > 40)
654
+ appendLine(` ${C_DIM}... 共 ${names.length} 个${RESET}`);
655
+ }
656
+ catch { /* 静默 */ }
657
+ return;
658
+ }
659
+ // /session — 当前会话信息
660
+ if (cmd === '/session') {
661
+ try {
662
+ const a = await getAgent();
663
+ const h = a.messageHistory ?? [];
664
+ appendLine(`${C_ACCENT}会话:${RESET}`);
665
+ appendLine(` ${C_DIM}channel:${RESET} ${a.currentChannelId || '—'}`);
666
+ appendLine(` ${C_DIM}agent:${RESET} ${a.currentAgentId || '—'}`);
667
+ appendLine(` ${C_DIM}消息:${RESET} ${h.length} 条 (${h.length > 15 ? `${h.length - 15} 条已压缩` : '窗口内'})`);
668
+ }
669
+ catch { /* 静默 */ }
670
+ return;
671
+ }
672
+ // /memory — 记忆摘要 (memory-compressor 落盘文件)
673
+ if (cmd === '/memory') {
674
+ try {
675
+ const { getMemoryDir } = await import('./bootstrap/memory-compressor.js');
676
+ const { readdir, readFile } = await import('fs/promises');
677
+ const { join } = await import('path');
678
+ const dir = getMemoryDir(cliAgentName === 'bolloon' ? 'agent' : cliAgentName);
679
+ const files = (await readdir(join(dir, 'sessions')).catch(() => [])).filter((f) => f.endsWith('.summary.md'));
680
+ appendLine(`${C_ACCENT}记忆摘要 (${files.length} 个 session):${RESET}`);
681
+ for (const f of files.slice(-5)) {
682
+ try {
683
+ const raw = await readFile(join(dir, 'sessions', f), 'utf-8');
684
+ const tail = raw.trim().split('\n').slice(-6).join(' ').slice(0, 180);
685
+ appendLine(` ${C_DIM}·${RESET} ${f.replace('.summary.md', '').slice(-30)}`);
686
+ appendLine(` ${C_DIM}${tail}${RESET}`);
687
+ }
688
+ catch { /* 跳过 */ }
689
+ }
690
+ }
691
+ catch { /* 静默 */ }
692
+ return;
693
+ }
694
+ // /resume — 恢复: 最近记忆摘要 + 进行中计划
695
+ if (cmd === '/resume' || cmd.startsWith('/resume ')) {
696
+ try {
697
+ const { getMemoryDir, getSessionSummaryPath } = await import('./bootstrap/memory-compressor.js');
698
+ const { readFile, readdir } = await import('fs/promises');
699
+ const { join } = await import('path');
700
+ const dir = getMemoryDir(cliAgentName === 'bolloon' ? 'agent' : cliAgentName);
701
+ const files = (await readdir(join(dir, 'sessions')).catch(() => [])).filter((f) => f.endsWith('.summary.md'));
702
+ appendLine(`${C_ACCENT}↻ 恢复上下文:${RESET}`);
703
+ if (files.length > 0) {
704
+ const f = files[files.length - 1];
705
+ const raw = await readFile(join(dir, 'sessions', f), 'utf-8');
706
+ const block = raw.trim().split('\n').slice(-12).join('\n').slice(-1200);
707
+ appendLine(` ${C_DIM}最近记忆 (${f.slice(0, 24)}...):${RESET}`);
708
+ for (const line of block.split('\n').slice(-8))
709
+ appendLine(` ${C_DIM}${line.slice(0, 100)}${RESET}`);
710
+ }
711
+ else {
712
+ appendLine(` ${C_DIM}暂无记忆摘要${RESET}`);
713
+ }
714
+ const { listActivePlans } = await import('./agents/plan-store.js');
715
+ const plans = await listActivePlans();
716
+ if (plans.length > 0) {
717
+ appendLine(` ${C_DIM}进行中计划 (${plans.length}):${RESET}`);
718
+ for (const p of plans.slice(0, 3))
719
+ appendLine(` ${C_ACCENT}·${RESET} ${p.goal || p.planId} ${C_DIM}${p.status || ''}${RESET}`);
720
+ }
721
+ }
722
+ catch { /* 静默 */ }
723
+ return;
724
+ }
725
+ // /goal — 进行中的目标/计划
726
+ if (cmd === '/goal') {
727
+ try {
728
+ const { listActivePlans, planToContext } = await import('./agents/plan-store.js');
729
+ const plans = await listActivePlans();
730
+ appendLine(`${C_ACCENT}目标 (${plans.length} 个进行中):${RESET}`);
731
+ if (plans.length === 0) {
732
+ appendLine(` ${C_DIM}无进行中计划 — 可用 /plan 创建${RESET}`);
733
+ }
734
+ for (const p of plans.slice(0, 5)) {
735
+ appendLine(` ${C_ACCENT}●${RESET} ${p.goal || p.planId} ${C_DIM}[${p.status || 'active'}]${RESET}`);
736
+ const steps = Array.isArray(p.steps) ? p.steps : [];
737
+ const done = steps.filter((s) => s.done || s.status === 'done').length;
738
+ if (steps.length > 0)
739
+ appendLine(` ${C_DIM}${done}/${steps.length} 步完成${RESET}`);
740
+ }
741
+ }
742
+ catch { /* 静默 */ }
743
+ return;
744
+ }
745
+ // /skill — 技能候选 (skill-writer 落盘)
746
+ if (cmd === '/skill') {
747
+ try {
748
+ const { listSkillCandidates } = await import('./agents/skill-writer.js');
749
+ const cands = await listSkillCandidates();
750
+ appendLine(`${C_ACCENT}技能候选 (${cands.length}):${RESET}`);
751
+ if (cands.length === 0) {
752
+ appendLine(` ${C_DIM}无候选 — 连续成功工具调用 ≥2 自动生成${RESET}`);
753
+ }
754
+ for (const c of cands.slice(0, 8)) {
755
+ appendLine(` ${C_DIM}·${RESET} ${c.name || '?'} ${C_DIM}(${c.source || ''})${RESET}`);
756
+ }
757
+ }
758
+ catch { /* 静默 */ }
759
+ return;
760
+ }
761
+ // /mcp — MCP 插件/工具列表
762
+ if (cmd === '/mcp') {
763
+ try {
764
+ const { readFile } = await import('fs/promises');
765
+ const { join } = await import('path');
766
+ let servers = {};
767
+ try {
768
+ servers = JSON.parse(await readFile(join(process.env.HOME || '/tmp', '.mcp.json'), 'utf-8')).mcpServers || {};
769
+ }
770
+ catch { /* 无 */ }
771
+ appendLine(`${C_ACCENT}MCP 服务器 (${Object.keys(servers).length}):${RESET}`);
772
+ if (Object.keys(servers).length === 0) {
773
+ appendLine(` ${C_DIM}无 (~/.mcp.json 未配置)${RESET}`);
774
+ }
775
+ for (const [name, s] of Object.entries(servers)) {
776
+ const cmdStr = s?.command || '';
777
+ appendLine(` ${C_DIM}·${RESET} ${name} ${C_DIM}(${String(cmdStr).slice(0, 40)})${RESET}`);
778
+ }
779
+ }
780
+ catch { /* 静默 */ }
781
+ return;
782
+ }
783
+ // /agent — 当前智能体身份
784
+ if (cmd === '/agent') {
785
+ try {
786
+ const a = await getAgent();
787
+ appendLine(`${C_ACCENT}智能体:${RESET}`);
788
+ appendLine(` ${C_DIM}名称:${RESET} ${cliAgentName}`);
789
+ appendLine(` ${C_DIM}agentId:${RESET} ${a.currentAgentId || '—'}`);
790
+ appendLine(` ${C_DIM}channel:${RESET} ${cliActiveChannelId || '—'}`);
791
+ }
792
+ catch { /* 静默 */ }
793
+ return;
794
+ }
795
+ // /did — DID 身份
796
+ if (cmd === '/did') {
797
+ try {
798
+ const { loadOrCreateAgentIdentity } = await import('./agents/agent-identity.js');
799
+ const identity = loadOrCreateAgentIdentity(cliAgentName === 'bolloon' ? 'default-agent' : cliAgentName);
800
+ appendLine(`${C_ACCENT}DID 身份:${RESET}`);
801
+ appendLine(` ${C_DIM}did:${RESET} ${identity.did}`);
802
+ appendLine(` ${C_DIM}publicKey:${RESET} ${identity.publicKey?.slice(0, 32) || '—'}...`);
803
+ appendLine(` ${C_DIM}发布:${RESET} 可用 publish_did 工具发布到 IPFS+IPNS`);
804
+ }
805
+ catch (e) {
806
+ appendLine(`${C_ERROR}/did 失败: ${String(e.message || e).slice(0, 120)}${RESET}`);
807
+ }
808
+ return;
809
+ }
810
+ // /ipfs — Kubo 状态
811
+ if (cmd === '/ipfs') {
812
+ try {
813
+ const { kuboApi } = await import('./agents/pi-sdk-tools.js');
814
+ const id = await kuboApi('/api/v0/id');
815
+ const peers = await kuboApi('/api/v0/swarm/peers');
816
+ const pins = await kuboApi('/api/v0/pin/ls?type=recursive');
817
+ appendLine(`${C_ACCENT}IPFS (Kubo):${RESET}`);
818
+ appendLine(` ${C_DIM}节点:${RESET} ${String(id.ID || '').slice(0, 24)}...`);
819
+ appendLine(` ${C_DIM}版本:${RESET} ${id.AgentVersion || ''}`);
820
+ appendLine(` ${C_DIM}peers:${RESET} ${peers?.Peers?.length ?? 0}`);
821
+ appendLine(` ${C_DIM}pins:${RESET} ${pins?.Keys ? Object.keys(pins.Keys).length : 0}`);
822
+ }
823
+ catch (e) {
824
+ appendLine(`${C_ERROR}/ipfs 失败: ${String(e.message || e).slice(0, 120)}${RESET}`);
825
+ }
826
+ return;
827
+ }
828
+ // /ipns — IPNS 状态 (keys + self 解析)
829
+ if (cmd === '/ipns') {
830
+ try {
831
+ const { kuboApi } = await import('./agents/pi-sdk-tools.js');
832
+ const keys = await kuboApi('/api/v0/key/list');
833
+ const keyList = keys?.Keys || [];
834
+ appendLine(`${C_ACCENT}IPNS keys (${keyList.length}):${RESET}`);
835
+ for (const k of keyList.slice(0, 10)) {
836
+ appendLine(` ${C_DIM}·${RESET} ${k.Name} ${C_DIM}${String(k.Id).slice(0, 20)}...${RESET}`);
837
+ }
838
+ try {
839
+ const r = await kuboApi('/api/v0/name/resolve?arg=ui-deploy&recursive=true&nocache=true', undefined, 15000);
840
+ appendLine(` ${C_DIM}ui-deploy →${RESET} ${r.Path || ''}`);
841
+ }
842
+ catch { /* 无 ui-deploy */ }
843
+ }
844
+ catch (e) {
845
+ appendLine(`${C_ERROR}/ipns 失败: ${String(e.message || e).slice(0, 120)}${RESET}`);
846
+ }
847
+ return;
848
+ }
849
+ // /wallet — 钱包状态
850
+ if (cmd === '/wallet') {
851
+ try {
852
+ const { readFile } = await import('fs/promises');
853
+ const { join } = await import('path');
854
+ let wallets = [];
855
+ try {
856
+ wallets = JSON.parse(await readFile(join(process.env.HOME || '/tmp', '.bolloon', 'wallets.json'), 'utf-8'));
857
+ }
858
+ catch { /* 无 */ }
859
+ appendLine(`${C_ACCENT}钱包 (${Array.isArray(wallets) ? wallets.length : 0}):${RESET}`);
860
+ if (!Array.isArray(wallets) || wallets.length === 0) {
861
+ appendLine(` ${C_DIM}无 — 可用 wallet_create 工具创建 EVM 钱包${RESET}`);
862
+ }
863
+ for (const w of (Array.isArray(wallets) ? wallets : []).slice(0, 5)) {
864
+ appendLine(` ${C_DIM}·${RESET} ${w.name || w.address?.slice(0, 12) || '?'} ${C_DIM}${String(w.address || '').slice(0, 16)}...${RESET}`);
865
+ }
866
+ }
867
+ catch { /* 静默 */ }
868
+ return;
869
+ }
870
+ // /email — 邮件配置状态
871
+ if (cmd === '/email') {
872
+ try {
873
+ const { readFile } = await import('fs/promises');
874
+ const { join } = await import('path');
875
+ let cfg = null;
876
+ try {
877
+ cfg = JSON.parse(await readFile(join(process.env.HOME || '/tmp', '.bolloon', 'smtp.json'), 'utf-8'));
878
+ }
879
+ catch { /* 无 */ }
880
+ appendLine(`${C_ACCENT}邮件 (SMTP):${RESET}`);
881
+ if (!cfg) {
882
+ appendLine(` ${C_DIM}未配置 smtp.json — 发件人: 天墟星剑 <2844169590@qq.com>${RESET}`);
883
+ }
884
+ else {
885
+ appendLine(` ${C_DIM}host:${RESET} ${cfg.host || 'smtp.qq.com'}`);
886
+ appendLine(` ${C_DIM}发件人:${RESET} ${cfg.from || cfg.user || '—'}`);
887
+ }
888
+ }
889
+ catch { /* 静默 */ }
890
+ return;
891
+ }
892
+ // /loop — 当前循环状态 (消息数 + token 估算)
893
+ if (cmd === '/loop') {
894
+ try {
895
+ const a = await getAgent();
896
+ const h = a.messageHistory ?? [];
897
+ let tokens = 0;
898
+ try {
899
+ const { estimateTokens } = require('./context-compaction/index.js');
900
+ tokens = estimateTokens(h);
901
+ }
902
+ catch {
903
+ tokens = Math.round(JSON.stringify(h).length / 4);
904
+ }
905
+ appendLine(`${C_ACCENT}Loop 状态:${RESET}`);
906
+ appendLine(` ${C_DIM}消息:${RESET} ${h.length} 条 (窗口 15, ${Math.max(0, h.length - 15)} 条早期压缩)`);
907
+ appendLine(` ${C_DIM}token:${RESET} ${(tokens / 1000).toFixed(1)}k / 1M (${((tokens / 1_000_000) * 100).toFixed(2)}%)`);
908
+ }
909
+ catch { /* 静默 */ }
910
+ return;
911
+ }
912
+ // /judgement — 判断力列表
913
+ if (cmd === '/judgement' || cmd === '/judgments') {
914
+ try {
915
+ const { loadAllJudgments } = await import('./pi-ecosystem-judgment/human-value-store.js');
916
+ const all = await loadAllJudgments().catch(() => []);
917
+ appendLine(`${C_ACCENT}判断力 (${all.length} 条):${RESET}`);
918
+ for (const j of all.slice(0, 8)) {
919
+ appendLine(` ${C_DIM}·${RESET} ${String(j.decision || '').slice(0, 70)}`);
920
+ }
921
+ }
922
+ catch { /* 静默 */ }
923
+ return;
924
+ }
925
+ // /insight — Context OS 08-Insights 资产
926
+ if (cmd === '/insight') {
927
+ try {
928
+ const { readContextAssets, readAssetBody } = await import('./bootstrap/context-os.js');
929
+ const listings = await readContextAssets('08-Insights');
930
+ const files = listings[0]?.files || [];
931
+ appendLine(`${C_ACCENT}洞察 (${files.length} 篇):${RESET}`);
932
+ if (files.length === 0) {
933
+ appendLine(` ${C_DIM}无 — 价值点路由自动沉淀 insight 到 08-Insights${RESET}`);
934
+ }
935
+ for (const f of files.slice(0, 6)) {
936
+ const body = await readAssetBody('08-Insights', f.file).catch(() => null);
937
+ const firstLine = (body?.body || '').split('\n').filter(l => l.trim() && !l.startsWith('---')).slice(0, 2).join(' ').slice(0, 90);
938
+ appendLine(` ${C_ACCENT}·${RESET} ${f.title} ${C_DIM}${firstLine ? '— ' + firstLine : ''}${RESET}`);
939
+ }
940
+ }
941
+ catch { /* 静默 */ }
942
+ return;
943
+ }
944
+ // /wiki — wiki 状态
945
+ if (cmd === '/wiki') {
946
+ try {
947
+ const { readFile } = await import('fs/promises');
948
+ const { join } = await import('path');
949
+ const root = process.cwd();
950
+ const statusPath = join(root, 'docs', 'wiki', 'current-status.md');
951
+ const raw = await readFile(statusPath, 'utf-8');
952
+ const title = raw.match(/^title:\s*(.+)$/m)?.[1] || 'current-status';
953
+ const confirmed = raw.match(/^last_confirmed:\s*(.+)$/m)?.[1] || '?';
954
+ const supported = (raw.match(/\|\|+/g) || []).length;
955
+ appendLine(`${C_ACCENT}Wiki:${RESET} ${title}`);
956
+ appendLine(` ${C_DIM}last_confirmed:${RESET} ${confirmed}`);
957
+ appendLine(` ${C_DIM}已支持条目:${RESET} ${supported}`);
958
+ appendLine(` ${C_DIM}位置:${RESET} docs/wiki/ (wiki-first 范式)`);
959
+ }
960
+ catch { /* 静默 */ }
961
+ return;
962
+ }
963
+ // /dream — 随机灵感 (从 Insights + Knowledge 资产随机取一条)
964
+ if (cmd === '/dream') {
965
+ try {
966
+ const { readContextAssets, readAssetBody } = await import('./bootstrap/context-os.js');
967
+ const layers = ['08-Insights', '07-Knowledge', '12-Analysis'];
968
+ const pool = [];
969
+ for (const layer of layers) {
970
+ const listings = await readContextAssets(layer);
971
+ for (const f of (listings[0]?.files || []).slice(0, 5)) {
972
+ const body = await readAssetBody(layer, f.file).catch(() => null);
973
+ const lines = (body?.body || '').split('\n').filter(l => l.trim() && !l.startsWith('---') && !l.startsWith('#') && !l.startsWith('>') && !l.startsWith('未来'));
974
+ if (lines[0])
975
+ pool.push(lines[0].trim().slice(0, 100));
976
+ }
977
+ }
978
+ if (pool.length === 0) {
979
+ appendLine(`${C_DIM}🌙 梦境空空 — 多对话让记忆沉淀出洞察后, /dream 就有素材了${RESET}`);
980
+ }
981
+ else {
982
+ const pick = pool[Math.floor(Math.random() * pool.length)];
983
+ appendLine(`${C_DIM}🌙 ${pick}${RESET}`);
984
+ }
985
+ }
986
+ catch { /* 静默 */ }
987
+ return;
988
+ }
584
989
  if (trimmed.toLowerCase() === '/help' || trimmed === 'help') {
585
990
  appendLine(`${C_DIM}命令:${RESET}`);
586
991
  appendLine(` ${C_ACCENT}!<cmd>${RESET} 执行终端命令 ${C_DIM}如 !ls -la${RESET}`);
587
992
  appendLine(` ${C_ACCENT}/queue${RESET} 切换队列模式 ${C_DIM}输入排队, 当前结束后自动执行${RESET}`);
588
993
  appendLine(` ${C_ACCENT}/dequeue${RESET} 出队一条`);
589
994
  appendLine(` ${C_ACCENT}/channel [名字|id|序号]${RESET} 切换当前智能体 ${C_DIM}无参列出所有; 支持名字/ID/序号三种解析${RESET}`);
995
+ appendLine(` ${C_ACCENT}/model${RESET} / ${C_ACCENT}/login${RESET} 模型供应商选择器 ${C_DIM}↑↓ 选择 · Enter 确认 · Esc 取消${RESET}`);
996
+ appendLine(` ${C_ACCENT}/logout${RESET} 查看当前供应商`);
997
+ appendLine(` ${C_ACCENT}/now${RESET} 当前状态总览 ${C_DIM}智能体/运行时间/上下文 tokens/消息数${RESET}`);
998
+ appendLine(` ${C_ACCENT}/session${RESET} 当前会话信息 ${C_DIM}channel/agent/消息窗口${RESET}`);
999
+ appendLine(` ${C_ACCENT}/loop${RESET} 循环状态 ${C_DIM}消息数 + token 估算${RESET}`);
1000
+ appendLine(` ${C_ACCENT}/memory${RESET} 记忆摘要 ${C_DIM}memory-compressor 落盘摘要${RESET}`);
1001
+ appendLine(` ${C_ACCENT}/resume${RESET} 恢复上下文 ${C_DIM}最近记忆 + 进行中计划${RESET}`);
1002
+ appendLine(` ${C_ACCENT}/goal${RESET} 进行中目标 ${C_DIM}plan-store active plans${RESET}`);
1003
+ appendLine(` ${C_ACCENT}/tools${RESET} 可用工具列表`);
1004
+ appendLine(` ${C_ACCENT}/skill${RESET} 技能候选 ${C_DIM}skill-writer 沉淀候选${RESET}`);
1005
+ appendLine(` ${C_ACCENT}/mcp${RESET} MCP 服务器列表`);
1006
+ appendLine(` ${C_ACCENT}/agent${RESET} 当前智能体身份`);
1007
+ appendLine(` ${C_ACCENT}/did${RESET} DID 身份`);
1008
+ appendLine(` ${C_ACCENT}/ipfs${RESET} Kubo 状态 ${C_DIM}节点/peers/pins${RESET}`);
1009
+ appendLine(` ${C_ACCENT}/ipns${RESET} IPNS keys + resolve`);
1010
+ appendLine(` ${C_ACCENT}/wallet${RESET} 钱包状态`);
1011
+ appendLine(` ${C_ACCENT}/email${RESET} 邮件配置`);
1012
+ appendLine(` ${C_ACCENT}/judgement${RESET} 判断力列表`);
1013
+ appendLine(` ${C_ACCENT}/insight${RESET} Context OS 洞察 (08-Insights)`);
1014
+ appendLine(` ${C_ACCENT}/wiki${RESET} wiki 状态`);
1015
+ appendLine(` ${C_ACCENT}/dream${RESET} 随机灵感`);
590
1016
  appendLine(` ${C_ACCENT}@名字${RESET} @ 命中智能体 ${C_DIM}弹出窗选择后发送给智能体${RESET}`);
591
1017
  appendLine(` ${C_ACCENT}/名字${RESET} / 命中命令/技能/插件 ${C_DIM}输入 / 自动弹出${RESET}`);
592
1018
  appendLine(` ${C_ACCENT}#路径${RESET} # 命中文件 ${C_DIM}输入 # 自动弹出文件列表${RESET}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolloon/bolloon-agent",
3
- "version": "0.3.35",
3
+ "version": "0.3.36",
4
4
  "type": "module",
5
5
  "description": "P2P AI Document Agent - 全局安装后执行 `bolloon` 启动产品",
6
6
  "main": "dist/cli-entry.js",