@bolloon/bolloon-agent 0.2.9 → 0.2.10

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 (268) hide show
  1. package/README.md +467 -467
  2. package/bin/bolloon-cli.cjs +183 -183
  3. package/bin/bolloon.cjs +0 -0
  4. package/bin/bolloon.js +157 -0
  5. package/dist/agents/agent-manifest-protocol.js +0 -52
  6. package/dist/agents/constraint-layer.js +19 -19
  7. package/dist/agents/pi-sdk.js +287 -1213
  8. package/dist/agents/shell-guard.js +2 -2
  9. package/dist/agents/workflow-pivot-loop.js +2 -81
  10. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  11. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  12. package/dist/bollharness-integration/context-router.js +292 -292
  13. package/dist/bollharness-integration/gate-state-machine.js +13 -26
  14. package/dist/bollharness-integration/integration.js +0 -71
  15. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  16. package/dist/bollharness-integration/skill-adapter.js +127 -52
  17. package/dist/bootstrap/context-collector.js +3 -6
  18. package/dist/bootstrap/lifecycle-hooks.js +1 -15
  19. package/dist/cli-entry.js +35 -39
  20. package/dist/constraint-runtime/src/_archive_helper.js +9 -0
  21. package/dist/constraint-runtime/src/agent/coordinator.js +48 -0
  22. package/dist/constraint-runtime/src/agent/index.js +1 -0
  23. package/dist/constraint-runtime/src/assistant/index.js +12 -0
  24. package/dist/constraint-runtime/src/bootstrap/index.js +12 -0
  25. package/dist/constraint-runtime/src/bootstrap_graph.js +13 -0
  26. package/dist/constraint-runtime/src/bridge/index.js +12 -0
  27. package/dist/constraint-runtime/src/buddy/index.js +12 -0
  28. package/dist/constraint-runtime/src/cli/index.js +12 -0
  29. package/dist/constraint-runtime/src/command_graph.js +10 -0
  30. package/dist/constraint-runtime/src/commands.js +60 -0
  31. package/dist/constraint-runtime/src/components/index.js +12 -0
  32. package/dist/constraint-runtime/src/constants/index.js +12 -0
  33. package/dist/constraint-runtime/src/constraint/budget.js +22 -0
  34. package/dist/constraint-runtime/src/constraint/index.js +2 -0
  35. package/dist/constraint-runtime/src/constraint/permission.js +20 -0
  36. package/dist/constraint-runtime/src/context.js +30 -0
  37. package/dist/constraint-runtime/src/coordinator/index.js +12 -0
  38. package/dist/constraint-runtime/src/cost_hook.js +4 -0
  39. package/dist/constraint-runtime/src/cost_tracker.js +8 -0
  40. package/dist/constraint-runtime/src/deferred_init.js +10 -0
  41. package/dist/constraint-runtime/src/direct_modes.js +6 -0
  42. package/dist/constraint-runtime/src/dynamic-tool-loader.js +85 -0
  43. package/dist/constraint-runtime/src/entrypoints/index.js +12 -0
  44. package/dist/constraint-runtime/src/execution_registry.js +44 -0
  45. package/dist/constraint-runtime/src/history.js +9 -0
  46. package/dist/constraint-runtime/src/hooks/index.js +12 -0
  47. package/dist/constraint-runtime/src/index.js +26 -0
  48. package/dist/constraint-runtime/src/ink.js +4 -0
  49. package/dist/constraint-runtime/src/keybindings/index.js +12 -0
  50. package/dist/constraint-runtime/src/memdir/index.js +12 -0
  51. package/dist/constraint-runtime/src/migrations/index.js +12 -0
  52. package/dist/constraint-runtime/src/models.js +1 -0
  53. package/dist/constraint-runtime/src/moreright/index.js +12 -0
  54. package/dist/constraint-runtime/src/native_ts/index.js +12 -0
  55. package/dist/constraint-runtime/src/output_styles/index.js +12 -0
  56. package/dist/constraint-runtime/src/parity_audit.js +12 -0
  57. package/dist/constraint-runtime/src/plugins/index.js +12 -0
  58. package/dist/constraint-runtime/src/port_manifest.js +11 -0
  59. package/dist/constraint-runtime/src/prefetch.js +9 -0
  60. package/dist/constraint-runtime/src/query.js +1 -0
  61. package/dist/constraint-runtime/src/remote/index.js +12 -0
  62. package/dist/constraint-runtime/src/remote_runtime.js +9 -0
  63. package/dist/constraint-runtime/src/runtime/index.js +1 -0
  64. package/dist/constraint-runtime/src/runtime/session.js +35 -0
  65. package/dist/constraint-runtime/src/schemas/index.js +12 -0
  66. package/dist/constraint-runtime/src/screens/index.js +12 -0
  67. package/dist/constraint-runtime/src/server/index.js +12 -0
  68. package/dist/constraint-runtime/src/services/index.js +12 -0
  69. package/dist/constraint-runtime/src/session_store.js +22 -0
  70. package/dist/constraint-runtime/src/setup.js +30 -0
  71. package/dist/constraint-runtime/src/skills/index.js +1 -0
  72. package/dist/constraint-runtime/src/skills/skill-registry.js +28 -0
  73. package/dist/constraint-runtime/src/state/index.js +12 -0
  74. package/dist/constraint-runtime/src/system_init.js +20 -0
  75. package/dist/constraint-runtime/src/thinking/engine.js +42 -0
  76. package/dist/constraint-runtime/src/thinking/index.js +1 -0
  77. package/dist/constraint-runtime/src/tool_pool.js +8 -0
  78. package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +7 -0
  79. package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +7 -0
  80. package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +7 -0
  81. package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +6 -0
  82. package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +8 -0
  83. package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +6 -0
  84. package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +6 -0
  85. package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +6 -0
  86. package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +6 -0
  87. package/dist/constraint-runtime/src/tools.js +80 -0
  88. package/dist/constraint-runtime/src/transcript.js +19 -0
  89. package/dist/constraint-runtime/src/types/index.js +12 -0
  90. package/dist/constraint-runtime/src/upstream_proxy/index.js +12 -0
  91. package/dist/constraint-runtime/src/utils/index.js +12 -0
  92. package/dist/constraint-runtime/src/vim/index.js +12 -0
  93. package/dist/constraint-runtime/src/voice/index.js +12 -0
  94. package/dist/constraint-runtime/tests/agent.test.js +16 -0
  95. package/dist/constraint-runtime/tests/constraint.test.js +41 -0
  96. package/dist/constraint-runtime/tests/skill.test.js +19 -0
  97. package/dist/constraint-runtime/tests/thinking.test.js +22 -0
  98. package/dist/context-compaction/auto-compact.js +7 -7
  99. package/dist/electron-preload.js +1 -18
  100. package/dist/electron-preload.js.map +1 -1
  101. package/dist/electron.js +168 -3
  102. package/dist/electron.js.map +1 -1
  103. package/dist/index.js +116 -136
  104. package/dist/llm/config-store.js +8 -24
  105. package/dist/llm/llm-judgment-client.js +102 -102
  106. package/dist/llm/pi-ai.js +85 -126
  107. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  108. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  109. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  110. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  111. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  112. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  113. package/dist/llm/system-prompt/layers/core/identity.md +37 -37
  114. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  115. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  116. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  117. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  118. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  119. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  120. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  121. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  122. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  123. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  124. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  125. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  126. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  127. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  128. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  129. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  130. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  131. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  132. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  133. package/dist/llm/tool-manifest/bash.js +3 -3
  134. package/dist/llm/tool-manifest/create_file.js +4 -4
  135. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  136. package/dist/llm/tool-manifest/image_search.js +2 -2
  137. package/dist/llm/tool-manifest/mcp.js +2 -2
  138. package/dist/llm/tool-manifest/message_compose.js +3 -3
  139. package/dist/llm/tool-manifest/places.js +2 -2
  140. package/dist/llm/tool-manifest/present_files.js +1 -1
  141. package/dist/llm/tool-manifest/recipe.js +2 -2
  142. package/dist/llm/tool-manifest/str_replace.js +5 -5
  143. package/dist/llm/tool-manifest/view.js +3 -3
  144. package/dist/llm/tool-manifest/weather.js +4 -4
  145. package/dist/llm/tool-manifest/web.js +4 -4
  146. package/dist/pi-ecosystem-colony/index.js +365 -0
  147. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  148. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  149. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  150. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  151. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  152. package/dist/security/context-router-tool.js +15 -15
  153. package/dist/security/tool-gate.js +0 -11
  154. package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
  155. package/dist/social/ant-colony/PheromoneEngine.js +227 -0
  156. package/dist/social/ant-colony/index.js +6 -0
  157. package/dist/social/ant-colony/types.js +24 -0
  158. package/dist/test/ai-judgment-test.js +80 -0
  159. package/dist/test/bollharness-integration.test.js +318 -0
  160. package/dist/test/channel-agent-multi-dialogue.js +205 -0
  161. package/dist/test/channel-heartbeat-agent-test.js +201 -0
  162. package/dist/test/constraint-layer.test.js +164 -0
  163. package/dist/test/diap-identity-test.js +172 -0
  164. package/dist/test/diap-quick-test.js +62 -0
  165. package/dist/test/global-shared-context.test.js +315 -0
  166. package/dist/test/harness-judgment-injection.test.js +246 -0
  167. package/dist/test/harness-workflow-integrator-test.js +228 -0
  168. package/dist/test/human-value-store.test.js +243 -0
  169. package/dist/test/hybrid-integration-test.js +118 -0
  170. package/dist/test/hybrid-messenger-verify.js +55 -0
  171. package/dist/test/iroh-bistream-debug.js +38 -0
  172. package/dist/test/iroh-communication.test.js +66 -0
  173. package/dist/test/iroh-debug-test.js +57 -0
  174. package/dist/test/iroh-diap-test.js +71 -0
  175. package/dist/test/iroh-direct-connect.js +55 -0
  176. package/dist/test/iroh-e2e-fixed.js +89 -0
  177. package/dist/test/iroh-e2e-same-process.js +63 -0
  178. package/dist/test/iroh-e2e.js +66 -0
  179. package/dist/test/iroh-final-e2e.js +72 -0
  180. package/dist/test/iroh-relay-test.js +37 -0
  181. package/dist/test/iroh-simple-test.js +41 -0
  182. package/dist/test/iroh-transport-verify.js +54 -0
  183. package/dist/test/iroh-transport.test.js +37 -0
  184. package/dist/test/iroh-two-nodes.js +70 -0
  185. package/dist/test/iroh-verify.js +44 -0
  186. package/dist/test/judgment-decision.test.js +219 -0
  187. package/dist/test/llm-judgment-integration.test.js +220 -0
  188. package/dist/test/p2p-agent-complex-dialogue.js +385 -0
  189. package/dist/test/p2p-agent-dialogue.js +341 -0
  190. package/dist/test/p2p-agent-full-bidirectional.js +510 -0
  191. package/dist/test/p2p-agent-harness-flow.js +437 -0
  192. package/dist/test/p2p-agent-harness-single.js +143 -0
  193. package/dist/test/p2p-ai-dialogue-test.js +318 -0
  194. package/dist/test/p2p-cid-connect-test.js +195 -0
  195. package/dist/test/p2p-connect-receiver.js +69 -0
  196. package/dist/test/p2p-doc-transfer.js +110 -0
  197. package/dist/test/p2p-identity-page-test.js +77 -0
  198. package/dist/test/p2p-iroh-test.js +171 -0
  199. package/dist/test/p2p-minimal-test.js +241 -0
  200. package/dist/test/p2p-node-1.js +148 -0
  201. package/dist/test/p2p-node-2.js +148 -0
  202. package/dist/test/p2p-server.js +281 -0
  203. package/dist/test/p2p-two-nodes-test.js +438 -0
  204. package/dist/test/pi-sdk.test.js +44 -0
  205. package/dist/test/set-persona.js +40 -0
  206. package/dist/test/simple.test.js +9 -0
  207. package/dist/test/storage-integration.test.js +150 -0
  208. package/dist/test/subagent-manager.test.js +276 -0
  209. package/dist/test/test-gate-flow.test.js +81 -0
  210. package/dist/test/workflow-engine.test.js +87 -0
  211. package/dist/test/workflow-pivot-loop.test.js +246 -0
  212. package/dist/web/api-config.html +520 -520
  213. package/dist/web/client.js +2917 -3791
  214. package/dist/web/components/p2p/index.js +234 -276
  215. package/dist/web/components/wallet-viem.mjs +118 -118
  216. package/dist/web/index.html +374 -373
  217. package/dist/web/manifest.json +20 -20
  218. package/dist/web/server.js +23 -775
  219. package/dist/web/style.css +4737 -4737
  220. package/dist/web/ui/message-renderer.js +373 -451
  221. package/dist/web/ui/step-timeline.js +255 -351
  222. package/package.json +160 -160
  223. package/scripts/build-cli.js +215 -215
  224. package/scripts/build-web.ts +125 -125
  225. package/scripts/postinstall.js +152 -152
  226. package/bin/bolloon-daemon.sh +0 -207
  227. package/bin/ipfs +0 -0
  228. package/dist/agents/chat-segmenter.js +0 -298
  229. package/dist/agents/judgment-protocol.js +0 -479
  230. package/dist/agents/parse-tool-call.js +0 -304
  231. package/dist/agents/peer-manifest-loader.js +0 -210
  232. package/dist/agents/react-loop.js +0 -120
  233. package/dist/agents/session-store.js +0 -171
  234. package/dist/agents/tool-registry.js +0 -136
  235. package/dist/bootstrap/chat-archiver.js +0 -276
  236. package/dist/bootstrap/memory-compressor.js +0 -170
  237. package/dist/bootstrap/persona-loader.js +0 -94
  238. package/dist/electron/config.js +0 -16
  239. package/dist/electron/config.js.map +0 -1
  240. package/dist/electron/dialogs.js +0 -71
  241. package/dist/electron/dialogs.js.map +0 -1
  242. package/dist/electron/first-run.js +0 -129
  243. package/dist/electron/first-run.js.map +0 -1
  244. package/dist/electron/ipc.js +0 -16
  245. package/dist/electron/ipc.js.map +0 -1
  246. package/dist/electron/logger.js +0 -77
  247. package/dist/electron/logger.js.map +0 -1
  248. package/dist/electron/main.js +0 -60
  249. package/dist/electron/main.js.map +0 -1
  250. package/dist/electron/menu.js +0 -140
  251. package/dist/electron/menu.js.map +0 -1
  252. package/dist/electron/paths.js +0 -33
  253. package/dist/electron/paths.js.map +0 -1
  254. package/dist/electron/server.js +0 -74
  255. package/dist/electron/server.js.map +0 -1
  256. package/dist/electron/tray.js +0 -73
  257. package/dist/electron/tray.js.map +0 -1
  258. package/dist/electron/window.js +0 -69
  259. package/dist/electron/window.js.map +0 -1
  260. package/dist/network/local-inbox-bus.js +0 -73
  261. package/dist/network/p2p-outbox.js +0 -161
  262. package/dist/network/peer-fs.js +0 -420
  263. package/dist/network/peer-resource-bridge.js +0 -216
  264. package/dist/web/components/p2p/P2PModal.js +0 -188
  265. package/dist/web/components/p2p/p2p-modal.js +0 -664
  266. package/dist/web/components/p2p/p2p-tools.js +0 -248
  267. package/dist/web/input-validator.js +0 -103
  268. package/dist/web/util/safe-name.js +0 -32
@@ -1,19 +1,15 @@
1
- /**
2
- * P2P Modal - 纯 TypeScript 版本
3
- */
4
- import { safeName as safeAnyName } from '../../util/safe-name.js';
1
+ import { safeName as safeAnyName } from "../../util/safe-name.js";
5
2
  class P2PModalUI {
6
- modal = null;
7
- overlay = null;
8
- connectedPeers = new Map();
9
- constructor() {
10
- this.createModal();
11
- }
12
- createModal() {
13
- // 创建遮罩层
14
- this.overlay = document.createElement('div');
15
- this.overlay.className = 'p2p-modal-overlay';
16
- this.overlay.style.cssText = `
3
+ modal = null;
4
+ overlay = null;
5
+ connectedPeers = /* @__PURE__ */ new Map();
6
+ constructor() {
7
+ this.createModal();
8
+ }
9
+ createModal() {
10
+ this.overlay = document.createElement("div");
11
+ this.overlay.className = "p2p-modal-overlay";
12
+ this.overlay.style.cssText = `
17
13
  display: none;
18
14
  position: fixed;
19
15
  inset: 0;
@@ -22,10 +18,9 @@ class P2PModalUI {
22
18
  align-items: center;
23
19
  justify-content: center;
24
20
  `;
25
- // 创建弹窗
26
- this.modal = document.createElement('div');
27
- this.modal.className = 'p2p-modal';
28
- this.modal.style.cssText = `
21
+ this.modal = document.createElement("div");
22
+ this.modal.className = "p2p-modal";
23
+ this.modal.style.cssText = `
29
24
  width: 90%;
30
25
  max-width: 650px;
31
26
  max-height: 90vh;
@@ -36,87 +31,74 @@ class P2PModalUI {
36
31
  display: flex;
37
32
  flex-direction: column;
38
33
  `;
39
- this.overlay.appendChild(this.modal);
40
- document.body.appendChild(this.overlay);
41
- // 点击遮罩关闭
42
- this.overlay.addEventListener('click', (e) => {
43
- if (e.target === this.overlay)
44
- this.hide();
45
- });
46
- this.render();
47
- }
48
- async render() {
49
- if (!this.modal)
50
- return;
51
- // 加载连接历史
52
- await this.loadConnectionHistory();
53
- // 获取身份信息
54
- let identityHtml = '<div class="p2p-loading">加载中...</div>';
55
- let identityData = { name: '未知', did: '未知', cid: '未知' };
56
- try {
57
- // 尝试获取 iroh 身份(包含 CID)
58
- const irohResp = await fetch('/api/iroh/init', { method: 'POST' });
59
- const irohData = await irohResp.json();
60
- if (irohData.cid) {
61
- // 2026-07-06: 防御 irohData.name 'undefined'/'null'/空白 的情况,
62
- // 否则 UI 会渲染字面量 "undefined". (multi-confirmed fix: 避免再现 v0.2.7 的 bug)
63
- const rawName = (typeof irohData.name === 'string' && irohData.name.trim()
64
- && irohData.name.trim() !== 'undefined'
65
- && irohData.name.trim() !== 'null') ? irohData.name.trim() : '';
66
- const tailName = rawName.split('-').slice(-1)[0] || '';
67
- identityData = {
68
- name: rawName || tailName || 'Bolloon',
69
- did: irohData.did || '未知',
70
- cid: irohData.cid || '未知',
71
- nodeId: irohData.irohNodeId ? irohData.irohNodeId.substring(0, 20) + '...' : '未知'
72
- };
73
- }
74
- else {
75
- // 回退到 /api/identity
76
- const resp = await fetch('/api/identity');
77
- identityData = await resp.json();
78
- identityData.cid = identityData.cid || '未初始化';
79
- identityData.nodeId = identityData.nodeId || '未知';
80
- }
81
- identityHtml = `
34
+ this.overlay.appendChild(this.modal);
35
+ document.body.appendChild(this.overlay);
36
+ this.overlay.addEventListener("click", (e) => {
37
+ if (e.target === this.overlay) this.hide();
38
+ });
39
+ this.render();
40
+ }
41
+ async render() {
42
+ if (!this.modal) return;
43
+ await this.loadConnectionHistory();
44
+ let identityHtml = '<div class="p2p-loading">\u52A0\u8F7D\u4E2D...</div>';
45
+ let identityData = { name: "\u672A\u77E5", did: "\u672A\u77E5", cid: "\u672A\u77E5" };
46
+ try {
47
+ const irohResp = await fetch("/api/iroh/init", { method: "POST" });
48
+ const irohData = await irohResp.json();
49
+ if (irohData.cid) {
50
+ const rawName = typeof irohData.name === "string" && irohData.name.trim() && irohData.name.trim() !== "undefined" && irohData.name.trim() !== "null" ? irohData.name.trim() : "";
51
+ const tailName = rawName.split("-").slice(-1)[0] || "";
52
+ identityData = {
53
+ name: rawName || tailName || "Bolloon",
54
+ did: irohData.did || "\u672A\u77E5",
55
+ cid: irohData.cid || "\u672A\u77E5",
56
+ nodeId: irohData.irohNodeId ? irohData.irohNodeId.substring(0, 20) + "..." : "\u672A\u77E5"
57
+ };
58
+ } else {
59
+ const resp = await fetch("/api/identity");
60
+ identityData = await resp.json();
61
+ identityData.cid = identityData.cid || "\u672A\u521D\u59CB\u5316";
62
+ identityData.nodeId = identityData.nodeId || "\u672A\u77E5";
63
+ }
64
+ identityHtml = `
82
65
  <div class="p2p-section">
83
- <h3>身份信息</h3>
66
+ <h3>\u8EAB\u4EFD\u4FE1\u606F</h3>
84
67
  <div class="p2p-info-row">
85
- <span class="label">名称:</span>
86
- <span class="value">${this.escapeHtml(this.safeName(identityData.name, '未知'))}</span>
68
+ <span class="label">\u540D\u79F0:</span>
69
+ <span class="value">${this.escapeHtml(this.safeName(identityData.name, "\u672A\u77E5"))}</span>
87
70
  </div>
88
71
  <div class="p2p-info-row">
89
72
  <span class="label">CID:</span>
90
- <span class="value mono">${this.escapeHtml(identityData.cid || '未知')}</span>
73
+ <span class="value mono">${this.escapeHtml(identityData.cid || "\u672A\u77E5")}</span>
91
74
  </div>
92
75
  <div class="p2p-info-row">
93
76
  <span class="label">DID:</span>
94
- <span class="value mono">${this.escapeHtml(identityData.did || '未知')}</span>
77
+ <span class="value mono">${this.escapeHtml(identityData.did || "\u672A\u77E5")}</span>
95
78
  </div>
96
79
  <div class="p2p-info-row">
97
80
  <span class="label">Node ID:</span>
98
- <span class="value mono">${this.escapeHtml(identityData.nodeId || '未知')}</span>
81
+ <span class="value mono">${this.escapeHtml(identityData.nodeId || "\u672A\u77E5")}</span>
99
82
  </div>
100
83
  <div class="p2p-actions">
101
- <button class="p2p-btn-secondary" id="p2p-copy-cid-btn">复制 CID</button>
102
- <button class="p2p-btn-secondary" id="p2p-copy-did-btn">复制 DID</button>
103
- <button class="p2p-btn-secondary" id="p2p-copy-nodeid-btn">复制 Node ID</button>
84
+ <button class="p2p-btn-secondary" id="p2p-copy-cid-btn">\u590D\u5236 CID</button>
85
+ <button class="p2p-btn-secondary" id="p2p-copy-did-btn">\u590D\u5236 DID</button>
86
+ <button class="p2p-btn-secondary" id="p2p-copy-nodeid-btn">\u590D\u5236 Node ID</button>
104
87
  </div>
105
88
  </div>
106
89
  `;
107
- }
108
- catch {
109
- identityHtml = '<div class="p2p-error">无法加载身份信息</div>';
110
- }
111
- this.modal.innerHTML = `
90
+ } catch {
91
+ identityHtml = '<div class="p2p-error">\u65E0\u6CD5\u52A0\u8F7D\u8EAB\u4EFD\u4FE1\u606F</div>';
92
+ }
93
+ this.modal.innerHTML = `
112
94
  <div class="p2p-header">
113
- <h2>P2P 网络</h2>
114
- <button class="p2p-close" id="p2p-close-btn">×</button>
95
+ <h2>P2P \u7F51\u7EDC</h2>
96
+ <button class="p2p-close" id="p2p-close-btn">\xD7</button>
115
97
  </div>
116
98
  <div class="p2p-tabs">
117
- <button class="p2p-tab active" data-tab="identity">身份</button>
118
- <button class="p2p-tab" data-tab="connect">连接</button>
119
- <button class="p2p-tab" data-tab="messages">消息</button>
99
+ <button class="p2p-tab active" data-tab="identity">\u8EAB\u4EFD</button>
100
+ <button class="p2p-tab" data-tab="connect">\u8FDE\u63A5</button>
101
+ <button class="p2p-tab" data-tab="messages">\u6D88\u606F</button>
120
102
  </div>
121
103
  <div class="p2p-content">
122
104
  <div class="p2p-tab-content active" data-tab="identity">
@@ -124,213 +106,192 @@ class P2PModalUI {
124
106
  </div>
125
107
  <div class="p2p-tab-content" data-tab="connect">
126
108
  <div class="p2p-section">
127
- <h3>连接到节点</h3>
109
+ <h3>\u8FDE\u63A5\u5230\u8282\u70B9</h3>
128
110
  <div class="p2p-connect-form">
129
- <input type="text" id="p2p-connect-input" placeholder="输入节点 ID CID">
130
- <button class="p2p-btn-primary" id="p2p-connect-btn">连接</button>
111
+ <input type="text" id="p2p-connect-input" placeholder="\u8F93\u5165\u8282\u70B9 ID \u6216 CID">
112
+ <button class="p2p-btn-primary" id="p2p-connect-btn">\u8FDE\u63A5</button>
131
113
  </div>
132
114
  <div id="p2p-connect-result" class="p2p-result"></div>
133
115
  </div>
134
116
  <div class="p2p-section">
135
- <h3>已连接的节点</h3>
117
+ <h3>\u5DF2\u8FDE\u63A5\u7684\u8282\u70B9</h3>
136
118
  <div id="p2p-connected-peers" class="p2p-connected-peers">
137
- 暂无连接
119
+ \u6682\u65E0\u8FDE\u63A5
138
120
  </div>
139
121
  </div>
140
122
  </div>
141
123
  <div class="p2p-tab-content" data-tab="messages">
142
124
  <div class="p2p-section">
143
- <h3>收到的消息</h3>
125
+ <h3>\u6536\u5230\u7684\u6D88\u606F</h3>
144
126
  <div id="p2p-messages-list" class="p2p-messages">
145
- 暂无消息
127
+ \u6682\u65E0\u6D88\u606F
146
128
  </div>
147
129
  </div>
148
130
  </div>
149
131
  </div>
150
132
  `;
151
- // 绑定事件
152
- this.bindEvents();
153
- }
154
- bindEvents() {
155
- if (!this.modal)
156
- return;
157
- // 关闭按钮
158
- const closeBtn = this.modal.querySelector('#p2p-close-btn');
159
- closeBtn?.addEventListener('click', () => this.hide());
160
- // 标签切换
161
- this.modal.querySelectorAll('.p2p-tab').forEach(tab => {
162
- tab.addEventListener('click', (e) => {
163
- const target = e.target;
164
- const tabName = target.dataset.tab;
165
- if (tabName)
166
- this.switchTab(tabName);
167
- });
168
- });
169
- // 复制 CID
170
- const copyCidBtn = this.modal.querySelector('#p2p-copy-cid-btn');
171
- copyCidBtn?.addEventListener('click', async () => {
172
- try {
173
- const irohResp = await fetch('/api/iroh/init', { method: 'POST' });
174
- const data = await irohResp.json();
175
- if (data.cid) {
176
- await navigator.clipboard.writeText(data.cid);
177
- this.showToast('CID 已复制到剪贴板');
178
- }
179
- }
180
- catch (e) {
181
- console.error('复制失败:', e);
182
- }
183
- });
184
- // 复制 DID
185
- const copyDidBtn = this.modal.querySelector('#p2p-copy-did-btn');
186
- copyDidBtn?.addEventListener('click', async () => {
187
- try {
188
- const irohResp = await fetch('/api/iroh/init', { method: 'POST' });
189
- const data = await irohResp.json();
190
- if (data.did) {
191
- await navigator.clipboard.writeText(data.did);
192
- this.showToast('DID 已复制到剪贴板');
193
- }
194
- }
195
- catch (e) {
196
- console.error('复制失败:', e);
197
- }
198
- });
199
- // 复制 Node ID
200
- const copyNodeIdBtn = this.modal.querySelector('#p2p-copy-nodeid-btn');
201
- copyNodeIdBtn?.addEventListener('click', async () => {
202
- try {
203
- const irohResp = await fetch('/api/iroh/init', { method: 'POST' });
204
- const data = await irohResp.json();
205
- if (data.irohNodeId) {
206
- await navigator.clipboard.writeText(data.irohNodeId);
207
- this.showToast('Node ID 已复制到剪贴板');
208
- }
209
- }
210
- catch (e) {
211
- console.error('复制失败:', e);
212
- }
213
- });
214
- // 连接按钮
215
- const connectBtn = this.modal.querySelector('#p2p-connect-btn');
216
- connectBtn?.addEventListener('click', () => this.handleConnect());
217
- }
218
- switchTab(tabName) {
219
- if (!this.modal)
220
- return;
221
- this.modal.querySelectorAll('.p2p-tab').forEach(tab => {
222
- tab.classList.toggle('active', tab.getAttribute('data-tab') === tabName);
223
- });
224
- this.modal.querySelectorAll('.p2p-tab-content').forEach(content => {
225
- content.classList.toggle('active', content.getAttribute('data-tab') === tabName);
226
- });
227
- }
228
- async handleConnect() {
229
- const input = document.getElementById('p2p-connect-input');
230
- const result = document.getElementById('p2p-connect-result');
231
- if (!input || !result)
232
- return;
233
- const cid = input.value.trim();
234
- if (!cid)
235
- return;
236
- result.innerHTML = '<div class="p2p-loading">连接中...</div>';
237
- result.className = 'p2p-result';
238
- try {
239
- const resp = await fetch('/api/iroh/connect', {
240
- method: 'POST',
241
- headers: { 'Content-Type': 'application/json' },
242
- body: JSON.stringify({ cid })
243
- });
244
- const data = await resp.json();
245
- if (data.ok) {
246
- result.innerHTML = `<div class="p2p-success">连接成功!节点: ${this.escapeHtml(this.safeName(data.nodeName, cid.substring(0, 16)))}...</div>`;
247
- result.className = 'p2p-result success';
248
- // 清空输入框
249
- input.value = '';
250
- // 保存已连接节点到后端
251
- try {
252
- await fetch('/api/p2p/history', {
253
- method: 'POST',
254
- headers: { 'Content-Type': 'application/json' },
255
- body: JSON.stringify({
256
- cid: cid, // 输入的 CID 或 Node ID
257
- irohNodeId: data.targetNodeId,
258
- did: data.targetNodeId, // 用 Node ID 作为 DID
259
- name: data.nodeName || cid.substring(0, 16)
260
- })
261
- });
262
- }
263
- catch (e) {
264
- console.error('保存连接失败:', e);
265
- }
266
- }
267
- else {
268
- result.innerHTML = `<div class="p2p-error">${this.escapeHtml(data.error || '连接失败')}</div>`;
269
- result.className = 'p2p-result error';
270
- }
133
+ this.bindEvents();
134
+ }
135
+ bindEvents() {
136
+ if (!this.modal) return;
137
+ const closeBtn = this.modal.querySelector("#p2p-close-btn");
138
+ closeBtn?.addEventListener("click", () => this.hide());
139
+ this.modal.querySelectorAll(".p2p-tab").forEach((tab) => {
140
+ tab.addEventListener("click", (e) => {
141
+ const target = e.target;
142
+ const tabName = target.dataset.tab;
143
+ if (tabName) this.switchTab(tabName);
144
+ });
145
+ });
146
+ const copyCidBtn = this.modal.querySelector("#p2p-copy-cid-btn");
147
+ copyCidBtn?.addEventListener("click", async () => {
148
+ try {
149
+ const irohResp = await fetch("/api/iroh/init", { method: "POST" });
150
+ const data = await irohResp.json();
151
+ if (data.cid) {
152
+ await navigator.clipboard.writeText(data.cid);
153
+ this.showToast("CID \u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F");
271
154
  }
272
- catch (e) {
273
- const msg = e instanceof Error ? e.message : '连接失败';
274
- result.innerHTML = `<div class="p2p-error">${this.escapeHtml(msg)}</div>`;
275
- result.className = 'p2p-result error';
155
+ } catch (e) {
156
+ console.error("\u590D\u5236\u5931\u8D25:", e);
157
+ }
158
+ });
159
+ const copyDidBtn = this.modal.querySelector("#p2p-copy-did-btn");
160
+ copyDidBtn?.addEventListener("click", async () => {
161
+ try {
162
+ const irohResp = await fetch("/api/iroh/init", { method: "POST" });
163
+ const data = await irohResp.json();
164
+ if (data.did) {
165
+ await navigator.clipboard.writeText(data.did);
166
+ this.showToast("DID \u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F");
276
167
  }
277
- }
278
- async loadConnectionHistory() {
279
- try {
280
- const resp = await fetch('/api/p2p/history');
281
- const history = await resp.json();
282
- this.connectedPeers.clear();
283
- for (const entry of history) {
284
- this.connectedPeers.set(entry.irohNodeId || entry.did, {
285
- nodeId: entry.irohNodeId || entry.did,
286
- name: entry.name || 'Unknown',
287
- time: entry.lastConnectedAt || Date.now()
288
- });
289
- }
290
- this.updateConnectedPeersDisplay();
168
+ } catch (e) {
169
+ console.error("\u590D\u5236\u5931\u8D25:", e);
170
+ }
171
+ });
172
+ const copyNodeIdBtn = this.modal.querySelector("#p2p-copy-nodeid-btn");
173
+ copyNodeIdBtn?.addEventListener("click", async () => {
174
+ try {
175
+ const irohResp = await fetch("/api/iroh/init", { method: "POST" });
176
+ const data = await irohResp.json();
177
+ if (data.irohNodeId) {
178
+ await navigator.clipboard.writeText(data.irohNodeId);
179
+ this.showToast("Node ID \u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F");
291
180
  }
292
- catch (e) {
293
- console.error('加载连接历史失败:', e);
181
+ } catch (e) {
182
+ console.error("\u590D\u5236\u5931\u8D25:", e);
183
+ }
184
+ });
185
+ const connectBtn = this.modal.querySelector("#p2p-connect-btn");
186
+ connectBtn?.addEventListener("click", () => this.handleConnect());
187
+ }
188
+ switchTab(tabName) {
189
+ if (!this.modal) return;
190
+ this.modal.querySelectorAll(".p2p-tab").forEach((tab) => {
191
+ tab.classList.toggle("active", tab.getAttribute("data-tab") === tabName);
192
+ });
193
+ this.modal.querySelectorAll(".p2p-tab-content").forEach((content) => {
194
+ content.classList.toggle("active", content.getAttribute("data-tab") === tabName);
195
+ });
196
+ }
197
+ async handleConnect() {
198
+ const input = document.getElementById("p2p-connect-input");
199
+ const result = document.getElementById("p2p-connect-result");
200
+ if (!input || !result) return;
201
+ const cid = input.value.trim();
202
+ if (!cid) return;
203
+ result.innerHTML = '<div class="p2p-loading">\u8FDE\u63A5\u4E2D...</div>';
204
+ result.className = "p2p-result";
205
+ try {
206
+ const resp = await fetch("/api/iroh/connect", {
207
+ method: "POST",
208
+ headers: { "Content-Type": "application/json" },
209
+ body: JSON.stringify({ cid })
210
+ });
211
+ const data = await resp.json();
212
+ if (data.ok) {
213
+ result.innerHTML = `<div class="p2p-success">\u8FDE\u63A5\u6210\u529F\uFF01\u8282\u70B9: ${this.escapeHtml(this.safeName(data.nodeName, cid.substring(0, 16)))}...</div>`;
214
+ result.className = "p2p-result success";
215
+ input.value = "";
216
+ try {
217
+ await fetch("/api/p2p/history", {
218
+ method: "POST",
219
+ headers: { "Content-Type": "application/json" },
220
+ body: JSON.stringify({
221
+ cid,
222
+ // 输入的 CID 或 Node ID
223
+ irohNodeId: data.targetNodeId,
224
+ did: data.targetNodeId,
225
+ // 用 Node ID 作为 DID
226
+ name: data.nodeName || cid.substring(0, 16)
227
+ })
228
+ });
229
+ } catch (e) {
230
+ console.error("\u4FDD\u5B58\u8FDE\u63A5\u5931\u8D25:", e);
294
231
  }
232
+ } else {
233
+ result.innerHTML = `<div class="p2p-error">${this.escapeHtml(data.error || "\u8FDE\u63A5\u5931\u8D25")}</div>`;
234
+ result.className = "p2p-result error";
235
+ }
236
+ } catch (e) {
237
+ const msg = e instanceof Error ? e.message : "\u8FDE\u63A5\u5931\u8D25";
238
+ result.innerHTML = `<div class="p2p-error">${this.escapeHtml(msg)}</div>`;
239
+ result.className = "p2p-result error";
295
240
  }
296
- updateConnectedPeersDisplay() {
297
- const container = document.getElementById('p2p-connected-peers');
298
- if (!container)
299
- return;
300
- if (this.connectedPeers.size === 0) {
301
- container.innerHTML = '<div class="p2p-empty">暂无连接</div>';
302
- return;
303
- }
304
- let html = '';
305
- for (const [nodeId, info] of this.connectedPeers) {
306
- const shortId = nodeId.substring(0, 16) + '...';
307
- const time = new Date(info.time).toLocaleTimeString();
308
- html += `
241
+ }
242
+ async loadConnectionHistory() {
243
+ try {
244
+ const resp = await fetch("/api/p2p/history");
245
+ const history = await resp.json();
246
+ this.connectedPeers.clear();
247
+ for (const entry of history) {
248
+ this.connectedPeers.set(entry.irohNodeId || entry.did, {
249
+ nodeId: entry.irohNodeId || entry.did,
250
+ name: entry.name || "Unknown",
251
+ time: entry.lastConnectedAt || Date.now()
252
+ });
253
+ }
254
+ this.updateConnectedPeersDisplay();
255
+ } catch (e) {
256
+ console.error("\u52A0\u8F7D\u8FDE\u63A5\u5386\u53F2\u5931\u8D25:", e);
257
+ }
258
+ }
259
+ updateConnectedPeersDisplay() {
260
+ const container = document.getElementById("p2p-connected-peers");
261
+ if (!container) return;
262
+ if (this.connectedPeers.size === 0) {
263
+ container.innerHTML = '<div class="p2p-empty">\u6682\u65E0\u8FDE\u63A5</div>';
264
+ return;
265
+ }
266
+ let html = "";
267
+ for (const [nodeId, info] of this.connectedPeers) {
268
+ const shortId = nodeId.substring(0, 16) + "...";
269
+ const time = new Date(info.time).toLocaleTimeString();
270
+ html += `
309
271
  <div class="p2p-peer-item">
310
272
  <span class="p2p-peer-name">${this.escapeHtml(this.safeName(info.name, shortId))}</span>
311
273
  <span class="p2p-peer-id">${shortId}</span>
312
274
  <span class="p2p-peer-time">${time}</span>
313
275
  </div>
314
276
  `;
315
- }
316
- container.innerHTML = html;
317
- }
318
- escapeHtml(str) {
319
- const div = document.createElement('div');
320
- div.textContent = str;
321
- return div.innerHTML;
322
277
  }
323
- // 2026-07-06: 通用 name 兜底 — 防止 name=undefined/null/'undefined' 字串
324
- // 时 UI 渲染字面量 "undefined".
325
- safeName(input, fallback) {
326
- // 2026-07-06: 委托共享 safe-name.ts (有单测覆盖); 保持方法签名兼容旧调用.
327
- return safeAnyName(input, fallback || '未知');
328
- }
329
- showToast(message) {
330
- const toast = document.createElement('div');
331
- toast.className = 'p2p-toast';
332
- toast.textContent = message;
333
- toast.style.cssText = `
278
+ container.innerHTML = html;
279
+ }
280
+ escapeHtml(str) {
281
+ const div = document.createElement("div");
282
+ div.textContent = str;
283
+ return div.innerHTML;
284
+ }
285
+ // 2026-07-06: 通用 name 兜底 — 防止 name=undefined/null/'undefined' 字串
286
+ // 时 UI 渲染字面量 "undefined".
287
+ safeName(input, fallback) {
288
+ return safeAnyName(input, fallback || "\u672A\u77E5");
289
+ }
290
+ showToast(message) {
291
+ const toast = document.createElement("div");
292
+ toast.className = "p2p-toast";
293
+ toast.textContent = message;
294
+ toast.style.cssText = `
334
295
  position: fixed;
335
296
  bottom: 24px;
336
297
  left: 50%;
@@ -343,23 +304,22 @@ class P2PModalUI {
343
304
  font-size: 14px;
344
305
  z-index: 2000;
345
306
  `;
346
- document.body.appendChild(toast);
347
- setTimeout(() => toast.remove(), 2000);
348
- }
349
- show() {
350
- if (this.overlay) {
351
- this.overlay.style.display = 'flex';
352
- this.render();
353
- }
307
+ document.body.appendChild(toast);
308
+ setTimeout(() => toast.remove(), 2e3);
309
+ }
310
+ show() {
311
+ if (this.overlay) {
312
+ this.overlay.style.display = "flex";
313
+ this.render();
354
314
  }
355
- hide() {
356
- if (this.overlay) {
357
- this.overlay.style.display = 'none';
358
- }
315
+ }
316
+ hide() {
317
+ if (this.overlay) {
318
+ this.overlay.style.display = "none";
359
319
  }
320
+ }
360
321
  }
361
- // 样式
362
- const style = document.createElement('style');
322
+ const style = document.createElement("style");
363
323
  style.textContent = `
364
324
  .p2p-modal-overlay { display: none; }
365
325
  .p2p-header {
@@ -548,10 +508,8 @@ style.textContent = `
548
508
  }
549
509
  `;
550
510
  document.head.appendChild(style);
551
- // 全局实例
552
511
  const p2pModal = new P2PModalUI();
553
- // 导出到 window
554
512
  window.p2pModal = p2pModal;
555
513
  window.showP2PModal = () => p2pModal.show();
556
514
  window.hideP2PModal = () => p2pModal.hide();
557
- console.log('[P2P Modal] 已初始化');
515
+ console.log("[P2P Modal] \u5DF2\u521D\u59CB\u5316");