@bolloon/bolloon-agent 0.2.8 → 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 -157
  223. package/scripts/build-cli.js +216 -0
  224. package/scripts/build-web.ts +125 -0
  225. package/scripts/postinstall.js +153 -0
  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,664 +0,0 @@
1
- /**
2
- * P2P Modal Web Component
3
- */
4
- import { p2pManager } from './p2p-manager.js';
5
- import { safeName as safeAnyName } from '../../util/safe-name.js';
6
- export class P2PModal extends HTMLElement {
7
- shadow = this.attachShadow({ mode: 'open' });
8
- activeTab = 'identity';
9
- initialized = false;
10
- connectedCallback() {
11
- this.render();
12
- this.attachEvents();
13
- }
14
- show() {
15
- this.classList.add('show');
16
- if (!this.initialized) {
17
- this.initP2P();
18
- this.initialized = true;
19
- }
20
- }
21
- hide() {
22
- this.classList.remove('show');
23
- }
24
- async initP2P() {
25
- const statusIndicator = this.shadow.getElementById('p2p-status-indicator');
26
- const statusText = this.shadow.getElementById('p2p-status-text');
27
- statusIndicator?.classList.add('connecting');
28
- if (statusText)
29
- statusText.textContent = '初始化中...';
30
- try {
31
- const identity = await p2pManager.init();
32
- this.updateIdentityDisplay(identity);
33
- this.updateStatus('online', '已连接');
34
- statusIndicator?.classList.add('online');
35
- }
36
- catch (e) {
37
- this.updateStatus('error', '初始化失败');
38
- }
39
- }
40
- updateStatus(status, text) {
41
- const statusIndicator = this.shadow.getElementById('p2p-status-indicator');
42
- const statusText = this.shadow.getElementById('p2p-status-text');
43
- if (statusIndicator) {
44
- statusIndicator.className = 'status-indicator ' + status;
45
- }
46
- if (statusText)
47
- statusText.textContent = text;
48
- }
49
- updateIdentityDisplay(identity) {
50
- const didEl = this.shadow.getElementById('p2p-did');
51
- const cidEl = this.shadow.getElementById('p2p-cid');
52
- const nodeIdEl = this.shadow.getElementById('p2p-node-id');
53
- const shareLinkEl = this.shadow.getElementById('p2p-invite-link');
54
- const sharePanel = this.shadow.getElementById('p2p-share-panel');
55
- if (didEl && identity.did)
56
- didEl.textContent = identity.did;
57
- if (cidEl && identity.cid)
58
- cidEl.textContent = identity.cid;
59
- if (nodeIdEl && identity.irohNodeId)
60
- nodeIdEl.textContent = identity.irohNodeId;
61
- if (shareLinkEl && identity.did && identity.cid) {
62
- shareLinkEl.value = `bolloon://connect?did=${encodeURIComponent(identity.did)}&cid=${encodeURIComponent(identity.cid)}`;
63
- }
64
- if (sharePanel)
65
- sharePanel.style.display = 'block';
66
- }
67
- switchTab(tab) {
68
- this.activeTab = tab;
69
- this.render();
70
- this.attachEvents();
71
- this.loadActiveTab();
72
- }
73
- async loadActiveTab() {
74
- switch (this.activeTab) {
75
- case 'history':
76
- await this.loadHistory();
77
- break;
78
- case 'messages':
79
- await this.loadMessages();
80
- break;
81
- case 'connect':
82
- await this.loadPeers();
83
- break;
84
- }
85
- }
86
- async loadHistory() {
87
- const historyList = this.shadow.getElementById('p2p-history-list');
88
- if (!historyList)
89
- return;
90
- try {
91
- const history = await p2pManager.getHistory();
92
- this.renderHistory(history, historyList);
93
- }
94
- catch (e) {
95
- console.error('[P2P Modal] 加载历史失败:', e);
96
- }
97
- }
98
- renderHistory(history, container) {
99
- if (history.length === 0) {
100
- container.innerHTML = '<div class="empty-hint">暂无连接历史</div>';
101
- return;
102
- }
103
- const fragment = document.createDocumentFragment();
104
- history.forEach(item => {
105
- const div = document.createElement('div');
106
- div.className = `history-item ${item.isPinned ? 'pinned' : ''}`;
107
- div.innerHTML = `
108
- <div class="history-item-icon">💬</div>
109
- <div class="history-item-info">
110
- <div class="history-item-name">
111
- ${this.escapeHtml(this.safeName(item.name, 'Unknown'))}
112
- ${item.isPinned ? '<span class="pin-icon">📌</span>' : ''}
113
- </div>
114
- <div class="history-item-meta">
115
- <span>上次: ${new Date(item.lastConnectedAt).toLocaleString()}</span>
116
- <span>消息: ${item.totalMessages || 0}</span>
117
- </div>
118
- </div>
119
- <div class="history-item-actions">
120
- <button class="btn-sm btn-secondary" data-action="connect" data-cid="${this.escapeHtml(item.cid)}">连接</button>
121
- <button class="btn-sm btn-secondary" data-action="pin" data-id="${item.id}" data-pinned="${!item.isPinned}">
122
- ${item.isPinned ? '取消置顶' : '置顶'}
123
- </button>
124
- <button class="btn-sm btn-secondary" data-action="delete" data-id="${item.id}">删除</button>
125
- </div>
126
- `;
127
- fragment.appendChild(div);
128
- });
129
- container.innerHTML = '';
130
- container.appendChild(fragment);
131
- this.attachHistoryEvents(container);
132
- }
133
- attachHistoryEvents(container) {
134
- container.querySelectorAll('[data-action]').forEach(btn => {
135
- btn.addEventListener('click', async (e) => {
136
- const el = e.currentTarget;
137
- const action = el.dataset.action;
138
- if (action === 'connect') {
139
- const cid = el.dataset.cid;
140
- if (cid) {
141
- const connectInput = this.shadow.getElementById('p2p-connect-input');
142
- if (connectInput)
143
- connectInput.value = cid;
144
- this.switchTab('connect');
145
- }
146
- }
147
- else if (action === 'pin') {
148
- const id = el.dataset.id;
149
- const pinned = el.dataset.pinned === 'true';
150
- if (id)
151
- await p2pManager.updateHistory(id, { isPinned: pinned });
152
- await this.loadHistory();
153
- }
154
- else if (action === 'delete') {
155
- const id = el.dataset.id;
156
- if (id)
157
- await p2pManager.deleteHistory(id);
158
- await this.loadHistory();
159
- }
160
- });
161
- });
162
- }
163
- async loadMessages() {
164
- const messagesList = this.shadow.getElementById('p2p-messages-list');
165
- const unreadBadge = this.shadow.getElementById('p2p-unread-badge');
166
- if (!messagesList)
167
- return;
168
- try {
169
- const messages = await p2pManager.getMessages();
170
- this.renderMessages(messages, messagesList);
171
- const unread = p2pManager.getUnreadCount();
172
- if (unreadBadge) {
173
- unreadBadge.textContent = String(unread);
174
- unreadBadge.style.display = unread > 0 ? 'inline-flex' : 'none';
175
- }
176
- }
177
- catch (e) {
178
- console.error('[P2P Modal] 加载消息失败:', e);
179
- }
180
- }
181
- renderMessages(messages, container) {
182
- if (messages.length === 0) {
183
- container.innerHTML = '<div class="empty-hint">暂无消息</div>';
184
- return;
185
- }
186
- const fragment = document.createDocumentFragment();
187
- messages.slice(-20).forEach(msg => {
188
- const div = document.createElement('div');
189
- div.className = `message-item ${!msg.isRead ? 'unread' : ''}`;
190
- div.innerHTML = `
191
- <div class="message-header">
192
- <span class="message-sender">${this.escapeHtml(msg.fromName || msg.fromDid)}</span>
193
- <span class="message-time">${new Date(msg.timestamp).toLocaleString()}</span>
194
- </div>
195
- <div class="message-content">${this.escapeHtml(msg.content.substring(0, 200))}</div>
196
- `;
197
- fragment.appendChild(div);
198
- });
199
- container.innerHTML = '';
200
- container.appendChild(fragment);
201
- }
202
- async loadPeers() {
203
- const peersList = this.shadow.getElementById('p2p-peers-list');
204
- const peerCount = this.shadow.getElementById('p2p-peer-count');
205
- if (!peersList)
206
- return;
207
- const peers = p2pManager.getConnectedPeers();
208
- if (peerCount)
209
- peerCount.textContent = String(peers.length);
210
- if (peers.length === 0) {
211
- peersList.innerHTML = '<div class="empty-hint">暂无连接</div>';
212
- return;
213
- }
214
- const fragment = document.createDocumentFragment();
215
- peers.forEach(peer => {
216
- const div = document.createElement('div');
217
- div.className = 'peer-item';
218
- div.innerHTML = `
219
- <div class="peer-status"><span class="dot online"></span></div>
220
- <div class="peer-info">
221
- <div class="peer-name">${this.escapeHtml(this.safeName(peer.info?.name, 'Unknown'))}</div>
222
- <div class="peer-meta">${(peer.nodeId || '').substring(0, 16)}...</div>
223
- </div>
224
- `;
225
- fragment.appendChild(div);
226
- });
227
- peersList.innerHTML = '';
228
- peersList.appendChild(fragment);
229
- }
230
- async handleConnect() {
231
- const input = this.shadow.getElementById('p2p-connect-input');
232
- const progressDiv = this.shadow.getElementById('p2p-connect-progress');
233
- const progressFill = this.shadow.getElementById('p2p-progress-fill');
234
- const progressText = this.shadow.getElementById('p2p-progress-text');
235
- const resultDiv = this.shadow.getElementById('p2p-connect-result');
236
- if (!input?.value.trim())
237
- return;
238
- if (progressDiv)
239
- progressDiv.style.display = 'block';
240
- const updateProgress = (progress) => {
241
- if (progressFill)
242
- progressFill.style.width = `${progress.percent}%`;
243
- if (progressText)
244
- progressText.textContent = progress.message;
245
- };
246
- try {
247
- const result = await p2pManager.connect(input.value, updateProgress);
248
- if (result.success) {
249
- this.showResult(resultDiv, 'success', `已连接到 ${result.name || '节点'}`);
250
- input.value = '';
251
- await this.loadHistory();
252
- await this.loadPeers();
253
- }
254
- else {
255
- this.showResult(resultDiv, 'error', result.error || '连接失败');
256
- }
257
- }
258
- catch (e) {
259
- this.showResult(resultDiv, 'error', e.message);
260
- }
261
- finally {
262
- setTimeout(() => {
263
- if (progressDiv)
264
- progressDiv.style.display = 'none';
265
- }, 2000);
266
- }
267
- }
268
- showResult(el, type, text) {
269
- if (!el)
270
- return;
271
- el.className = `connect-result ${type} show`;
272
- el.textContent = text;
273
- }
274
- escapeHtml(text) {
275
- const div = document.createElement('div');
276
- div.textContent = text;
277
- return div.innerHTML;
278
- }
279
- // 2026-07-06: 通用 name 兜底 — 防止 name=undefined/null/'undefined' 字串
280
- // 时 UI 渲染字面量 "undefined".
281
- safeName(input, fallback) {
282
- // 2026-07-06: 委托共享 safe-name.ts (有单测覆盖); 保持方法签名兼容旧调用.
283
- return safeAnyName(input, fallback || 'Unknown');
284
- }
285
- showToast(message) {
286
- const toast = document.createElement('div');
287
- toast.className = 'toast';
288
- toast.textContent = message;
289
- document.body.appendChild(toast);
290
- setTimeout(() => toast.remove(), 2000);
291
- }
292
- render() {
293
- this.shadow.innerHTML = `
294
- <style>
295
- :host {
296
- display: none;
297
- position: fixed;
298
- inset: 0;
299
- background: rgba(0, 0, 0, 0.7);
300
- z-index: 1000;
301
- align-items: center;
302
- justify-content: center;
303
- }
304
- :host(.show) {
305
- display: flex;
306
- }
307
- .modal {
308
- width: 90%;
309
- max-width: 700px;
310
- max-height: 90vh;
311
- background: var(--bg-secondary, #1e1e2e);
312
- border: 1px solid var(--border, #3a3a4a);
313
- border-radius: 12px;
314
- padding: 24px;
315
- overflow-y: auto;
316
- }
317
- .modal-header {
318
- display: flex;
319
- justify-content: space-between;
320
- align-items: center;
321
- margin-bottom: 20px;
322
- }
323
- .modal-header h2 {
324
- margin: 0;
325
- font-size: 18px;
326
- color: var(--text-primary, #e0e0e0);
327
- }
328
- .modal-close {
329
- background: none;
330
- border: none;
331
- font-size: 24px;
332
- cursor: pointer;
333
- color: var(--text-secondary, #888);
334
- }
335
- .tabs {
336
- display: flex;
337
- gap: 4px;
338
- margin-bottom: 20px;
339
- border-bottom: 1px solid var(--border, #3a3a4a);
340
- padding-bottom: 8px;
341
- }
342
- .tab {
343
- padding: 8px 16px;
344
- background: transparent;
345
- border: none;
346
- border-radius: 6px 6px 0 0;
347
- color: var(--text-secondary, #888);
348
- font-size: 14px;
349
- cursor: pointer;
350
- }
351
- .tab:hover { color: var(--text-primary, #e0e0e0); }
352
- .tab.active {
353
- color: var(--accent, #7c3aed);
354
- background: var(--bg-hover, #2a2a3a);
355
- }
356
- .tab-content { display: none; }
357
- .tab-content.active { display: block; }
358
- .identity-card {
359
- background: var(--bg-hover, #2a2a3a);
360
- border: 1px solid var(--border, #3a3a4a);
361
- border-radius: 8px;
362
- padding: 20px;
363
- margin-bottom: 16px;
364
- }
365
- .status-row {
366
- display: flex;
367
- align-items: center;
368
- gap: 8px;
369
- margin-bottom: 16px;
370
- }
371
- .status-indicator {
372
- width: 10px;
373
- height: 10px;
374
- border-radius: 50%;
375
- background: var(--text-muted, #666);
376
- }
377
- .status-indicator.online { background: var(--success, #22c55e); }
378
- .status-indicator.connecting { background: var(--warning, #eab308); animation: pulse 1s infinite; }
379
- .status-indicator.error { background: var(--error, #ef4444); }
380
- @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
381
- .info-row {
382
- display: flex;
383
- align-items: center;
384
- gap: 8px;
385
- padding: 8px 0;
386
- border-bottom: 1px solid var(--border, #3a3a4a);
387
- }
388
- .info-row:last-child { border-bottom: none; }
389
- .info-label { min-width: 80px; color: var(--text-secondary, #888); font-size: 13px; }
390
- .info-value { flex: 1; font-size: 13px; word-break: break-all; font-family: monospace; }
391
- .copy-btn {
392
- padding: 4px 8px;
393
- background: var(--bg-primary, #252536);
394
- border: 1px solid var(--border, #3a3a4a);
395
- border-radius: 4px;
396
- cursor: pointer;
397
- font-size: 12px;
398
- }
399
- .copy-btn:hover { background: var(--accent, #7c3aed); color: white; }
400
- .btn-primary {
401
- padding: 12px 24px;
402
- background: var(--accent, #7c3aed);
403
- color: white;
404
- border: none;
405
- border-radius: 8px;
406
- cursor: pointer;
407
- font-size: 14px;
408
- width: 100%;
409
- }
410
- .btn-primary:hover { opacity: 0.9; }
411
- .share-panel {
412
- background: var(--bg-hover, #2a2a3a);
413
- border: 1px solid var(--border, #3a3a4a);
414
- border-radius: 8px;
415
- padding: 16px;
416
- margin-bottom: 16px;
417
- }
418
- .share-panel h4 { margin: 0 0 12px 0; color: var(--text-primary, #e0e0e0); }
419
- .share-actions { display: flex; gap: 8px; flex-wrap: wrap; }
420
- .btn-secondary {
421
- padding: 8px 16px;
422
- background: var(--bg-primary, #252536);
423
- border: 1px solid var(--border, #3a3a4a);
424
- border-radius: 6px;
425
- cursor: pointer;
426
- font-size: 13px;
427
- }
428
- .btn-secondary:hover { border-color: var(--accent, #7c3aed); }
429
- .btn-sm { padding: 6px 12px; font-size: 12px; }
430
- .connect-form { display: flex; gap: 8px; margin-bottom: 16px; }
431
- .connect-form input {
432
- flex: 1;
433
- padding: 12px 16px;
434
- background: var(--bg-hover, #2a2a3a);
435
- border: 1px solid var(--border, #3a3a4a);
436
- border-radius: 8px;
437
- color: var(--text-primary, #e0e0e0);
438
- font-size: 14px;
439
- }
440
- .progress {
441
- display: none;
442
- margin-bottom: 16px;
443
- padding: 16px;
444
- background: var(--bg-hover, #2a2a3a);
445
- border-radius: 8px;
446
- }
447
- .progress.show { display: block; }
448
- .progress-bar {
449
- height: 6px;
450
- background: var(--bg-primary, #252536);
451
- border-radius: 3px;
452
- overflow: hidden;
453
- margin-bottom: 8px;
454
- }
455
- .progress-fill {
456
- height: 100%;
457
- background: var(--accent, #7c3aed);
458
- border-radius: 3px;
459
- transition: width 0.3s;
460
- }
461
- .progress-text { color: var(--text-secondary, #888); font-size: 13px; }
462
- .connect-result {
463
- display: none;
464
- padding: 12px;
465
- border-radius: 8px;
466
- font-size: 14px;
467
- margin-bottom: 16px;
468
- }
469
- .connect-result.show { display: block; }
470
- .connect-result.success { background: rgba(34, 197, 94, 0.1); border: 1px solid var(--success, #22c55e); color: var(--success, #22c55e); }
471
- .connect-result.error { background: rgba(239, 68, 68, 0.1); border: 1px solid var(--error, #ef4444); color: var(--error, #ef4444); }
472
- .empty-hint { color: var(--text-muted, #666); font-size: 13px; padding: 32px; text-align: center; }
473
- .history-item, .peer-item {
474
- display: flex;
475
- align-items: center;
476
- gap: 12px;
477
- padding: 12px;
478
- background: var(--bg-hover, #2a2a3a);
479
- border: 1px solid var(--border, #3a3a4a);
480
- border-radius: 8px;
481
- margin-bottom: 8px;
482
- }
483
- .history-item.pinned { border-left: 3px solid var(--accent, #7c3aed); }
484
- .history-item-icon { font-size: 20px; }
485
- .history-item-info { flex: 1; }
486
- .history-item-name { font-weight: 500; display: flex; align-items: center; gap: 6px; }
487
- .history-item-meta { font-size: 12px; color: var(--text-secondary, #888); display: flex; gap: 12px; margin-top: 4px; }
488
- .history-item-actions { display: flex; gap: 8px; }
489
- .peer-status { display: flex; align-items: center; }
490
- .peer-status .dot { width: 8px; height: 8px; border-radius: 50%; }
491
- .peer-status .dot.online { background: var(--success, #22c55e); }
492
- .peer-info { flex: 1; }
493
- .peer-name { font-weight: 500; }
494
- .peer-meta { font-size: 12px; color: var(--text-secondary, #888); }
495
- .message-item {
496
- padding: 12px;
497
- background: var(--bg-hover, #2a2a3a);
498
- border: 1px solid var(--border, #3a3a4a);
499
- border-radius: 8px;
500
- margin-bottom: 8px;
501
- }
502
- .message-item.unread { border-left: 3px solid var(--accent, #7c3aed); }
503
- .message-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
504
- .message-sender { font-weight: 500; }
505
- .message-time { font-size: 12px; color: var(--text-secondary, #888); }
506
- .message-content { font-size: 14px; line-height: 1.5; }
507
- .peers-section {
508
- margin-top: 24px;
509
- padding-top: 16px;
510
- border-top: 1px solid var(--border, #3a3a4a);
511
- }
512
- .peers-section h4 { margin-bottom: 12px; color: var(--text-secondary, #888); font-size: 13px; }
513
- .toolbar { margin-bottom: 12px; display: flex; justify-content: flex-end; }
514
- </style>
515
-
516
- <div class="modal">
517
- <div class="modal-header">
518
- <h2>P2P 网络</h2>
519
- <button class="modal-close" id="modal-close">&times;</button>
520
- </div>
521
-
522
- <div class="tabs">
523
- <button class="tab ${this.activeTab === 'identity' ? 'active' : ''}" data-tab="identity">我的身份</button>
524
- <button class="tab ${this.activeTab === 'connect' ? 'active' : ''}" data-tab="connect">连接</button>
525
- <button class="tab ${this.activeTab === 'history' ? 'active' : ''}" data-tab="history">历史记录</button>
526
- <button class="tab ${this.activeTab === 'messages' ? 'active' : ''}" data-tab="messages">
527
- 消息 <span id="p2p-unread-badge" class="unread-badge" style="display:none">0</span>
528
- </button>
529
- </div>
530
-
531
- <!-- 我的身份 -->
532
- <div class="tab-content ${this.activeTab === 'identity' ? 'active' : ''}" id="tab-identity">
533
- <div class="identity-card">
534
- <div class="status-row">
535
- <span class="status-indicator" id="p2p-status-indicator"></span>
536
- <span id="p2p-status-text">未初始化</span>
537
- </div>
538
- <div class="info-row">
539
- <span class="info-label">DID:</span>
540
- <code class="info-value" id="p2p-did">-</code>
541
- <button class="copy-btn" data-copy="p2p-did">📋</button>
542
- </div>
543
- <div class="info-row">
544
- <span class="info-label">CID:</span>
545
- <code class="info-value" id="p2p-cid">-</code>
546
- <button class="copy-btn" data-copy="p2p-cid">📋</button>
547
- </div>
548
- <div class="info-row">
549
- <span class="info-label">Node ID:</span>
550
- <code class="info-value" id="p2p-node-id">-</code>
551
- <button class="copy-btn" data-copy="p2p-node-id">📋</button>
552
- </div>
553
- </div>
554
-
555
- <button class="btn-primary" id="p2p-init-btn">初始化 P2P</button>
556
-
557
- <div class="share-panel" id="p2p-share-panel" style="display:none">
558
- <h4>分享给好友</h4>
559
- <div class="share-actions">
560
- <button class="btn-secondary" id="p2p-copy-link">📋 复制链接</button>
561
- <button class="btn-secondary" id="p2p-export-file">📁 导出文件</button>
562
- </div>
563
- <div class="info-row" style="margin-top: 12px; display:none" id="p2p-share-link-row">
564
- <input type="text" id="p2p-invite-link" readonly style="flex:1; padding:8px; background:var(--bg-primary); border:1px solid var(--border); border-radius:6px); color:var(--text-primary); font-size:12px; font-family:monospace;">
565
- </div>
566
- </div>
567
- </div>
568
-
569
- <!-- 连接 -->
570
- <div class="tab-content ${this.activeTab === 'connect' ? 'active' : ''}" id="tab-connect">
571
- <div class="connect-form">
572
- <input type="text" id="p2p-connect-input" placeholder="粘贴 CID 或链接...">
573
- <button class="btn-secondary" id="p2p-connect-btn">连接 ▶</button>
574
- </div>
575
- <div class="progress" id="p2p-connect-progress">
576
- <div class="progress-bar"><div class="progress-fill" id="p2p-progress-fill"></div></div>
577
- <span class="progress-text" id="p2p-progress-text">验证输入格式...</span>
578
- </div>
579
- <div class="connect-result" id="p2p-connect-result"></div>
580
- </div>
581
-
582
- <!-- 历史记录 -->
583
- <div class="tab-content ${this.activeTab === 'history' ? 'active' : ''}" id="tab-history">
584
- <div class="toolbar">
585
- <button class="btn-secondary btn-sm" id="p2p-history-refresh">🔄 刷新</button>
586
- </div>
587
- <div id="p2p-history-list"></div>
588
- </div>
589
-
590
- <!-- 消息 -->
591
- <div class="tab-content ${this.activeTab === 'messages' ? 'active' : ''}" id="tab-messages">
592
- <div class="toolbar">
593
- <button class="btn-secondary btn-sm" id="p2p-mark-all-read">全部已读</button>
594
- </div>
595
- <div id="p2p-messages-list"></div>
596
- </div>
597
-
598
- <!-- 已连接节点 -->
599
- <div class="peers-section">
600
- <h4>已连接节点 (<span id="p2p-peer-count">0</span>)</h4>
601
- <div id="p2p-peers-list"></div>
602
- </div>
603
- </div>
604
- `;
605
- }
606
- attachEvents() {
607
- // 关闭按钮
608
- this.shadow.getElementById('modal-close')?.addEventListener('click', () => this.hide());
609
- // 点击外部关闭
610
- this.addEventListener('click', (e) => {
611
- if (e.target === this)
612
- this.hide();
613
- });
614
- // 初始化按钮
615
- this.shadow.getElementById('p2p-init-btn')?.addEventListener('click', () => this.initP2P());
616
- // 复制按钮
617
- this.shadow.querySelectorAll('[data-copy]').forEach(btn => {
618
- btn.addEventListener('click', async (e) => {
619
- const el = e.currentTarget;
620
- const targetId = el.dataset.copy;
621
- const targetEl = this.shadow.getElementById(targetId);
622
- if (targetEl?.textContent && targetEl.textContent !== '-') {
623
- await navigator.clipboard.writeText(targetEl.textContent);
624
- this.showToast('已复制');
625
- }
626
- });
627
- });
628
- // 复制链接
629
- this.shadow.getElementById('p2p-copy-link')?.addEventListener('click', async () => {
630
- const shareLink = this.shadow.getElementById('p2p-invite-link');
631
- if (shareLink?.value) {
632
- await navigator.clipboard.writeText(shareLink.value);
633
- this.showToast('链接已复制');
634
- }
635
- });
636
- // 导出文件
637
- this.shadow.getElementById('p2p-export-file')?.addEventListener('click', () => {
638
- p2pManager.identity.exportIdentityFile();
639
- });
640
- // 标签页切换
641
- this.shadow.querySelectorAll('.tab').forEach(btn => {
642
- btn.addEventListener('click', () => {
643
- const tab = btn.dataset.tab;
644
- if (tab)
645
- this.switchTab(tab);
646
- });
647
- });
648
- // 连接按钮
649
- this.shadow.getElementById('p2p-connect-btn')?.addEventListener('click', () => this.handleConnect());
650
- // 回车连接
651
- this.shadow.getElementById('p2p-connect-input')?.addEventListener('keypress', (e) => {
652
- if (e.key === 'Enter')
653
- this.handleConnect();
654
- });
655
- // 刷新历史
656
- this.shadow.getElementById('p2p-history-refresh')?.addEventListener('click', () => this.loadHistory());
657
- // 全部已读
658
- this.shadow.getElementById('p2p-mark-all-read')?.addEventListener('click', async () => {
659
- await p2pManager.messages.markAllRead();
660
- this.loadMessages();
661
- });
662
- }
663
- }
664
- customElements.define('p2p-modal', P2PModal);