@bolloon/bolloon-agent 0.1.38 → 0.1.39

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 (33) hide show
  1. package/dist/agents/constraint-layer.js +1 -2
  2. package/dist/web/client.js +2861 -3746
  3. package/dist/web/components/p2p/index.js +226 -264
  4. package/dist/web/ui/message-renderer.js +323 -434
  5. package/dist/web/ui/step-timeline.js +255 -351
  6. package/package.json +1 -1
  7. package/dist/bollharness-integration/llm/pi-ai.js +0 -397
  8. package/dist/bollharness-integration/pi-ecosystem-colony/index.js +0 -365
  9. package/dist/bollharness-integration/pi-ecosystem-goals/index.js +0 -458
  10. package/dist/bollharness-integration/pi-ecosystem-judgment/decision.js +0 -300
  11. package/dist/bollharness-integration/pi-ecosystem-judgment/distillation.js +0 -291
  12. package/dist/bollharness-integration/pi-ecosystem-judgment/index.js +0 -445
  13. package/dist/bollharness-integration/pi-ecosystem-mcp/index.js +0 -331
  14. package/dist/bollharness-integration/pi-ecosystem-subagents/index.js +0 -303
  15. package/dist/constraints/commands.js +0 -100
  16. package/dist/constraints/permissions.js +0 -37
  17. package/dist/constraints/runtime.js +0 -135
  18. package/dist/constraints/session.js +0 -48
  19. package/dist/constraints/system-init.js +0 -51
  20. package/dist/constraints/tools.js +0 -104
  21. package/dist/llm/minimax-provider.js +0 -46
  22. package/dist/llm/minimax.js +0 -45
  23. package/dist/pi-ecosystem-colony/index.js +0 -365
  24. package/dist/runtime/context/minimax-prompt.js +0 -178
  25. package/dist/runtime/context/sys-prompt.js +0 -1
  26. package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
  27. package/dist/social/ant-colony/PheromoneEngine.js +0 -227
  28. package/dist/social/ant-colony/index.js +0 -6
  29. package/dist/social/ant-colony/types.js +0 -24
  30. package/dist/utils/double.js +0 -6
  31. package/dist/web/components/p2p/P2PModal.js +0 -188
  32. package/dist/web/components/p2p/p2p-modal.js +0 -657
  33. package/dist/web/components/p2p/p2p-tools.js +0 -248
@@ -1,227 +0,0 @@
1
- /**
2
- * PheromoneEngine - 信息素管理引擎
3
- *
4
- * 模拟蚂蚁的信息素系统,用于引导智能体发现和路由决策
5
- */
6
- import * as fs from 'fs/promises';
7
- import * as path from 'path';
8
- import { PheromoneType, DEFAULT_PHEROMONE_CONFIG, } from './types.js';
9
- export { PheromoneType };
10
- const PHEROMONE_DB_PATH = path.join(process.env.HOME || '/tmp', '.bolloon', 'ant-colony', 'pheromones.json');
11
- export class PheromoneEngine {
12
- trails = new Map();
13
- fields = new Map();
14
- evaporationTimer = null;
15
- saveTimer = null;
16
- dirty = false;
17
- async initialize() {
18
- await fs.mkdir(path.dirname(PHEROMONE_DB_PATH), { recursive: true });
19
- await this.load();
20
- this.startEvaporation();
21
- this.startAutoSave();
22
- }
23
- startEvaporation() {
24
- this.evaporationTimer = setInterval(() => {
25
- this.evaporate();
26
- }, DEFAULT_PHEROMONE_CONFIG.evaporationInterval);
27
- }
28
- startAutoSave() {
29
- this.saveTimer = setInterval(() => {
30
- if (this.dirty) {
31
- this.save().catch(console.error);
32
- this.dirty = false;
33
- }
34
- }, 30000);
35
- }
36
- /**
37
- * 放置信息素
38
- */
39
- async deposit(type, sourceDid, targetDid, strength = 0.5, options) {
40
- const key = this.getTrailKey(type, sourceDid, targetDid);
41
- const existing = this.trails.get(key);
42
- const newStrength = existing
43
- ? Math.min(1, existing.strength + strength * 0.3)
44
- : Math.min(1, strength);
45
- const trail = {
46
- id: key,
47
- type,
48
- sourceDid,
49
- targetDid,
50
- strength: newStrength,
51
- decayRate: DEFAULT_PHEROMONE_CONFIG.decayRate,
52
- createdAt: existing?.createdAt || Date.now(),
53
- lastUpdate: Date.now(),
54
- capability: options?.capability,
55
- qualityScore: options?.qualityScore,
56
- };
57
- this.trails.set(key, trail);
58
- this.dirty = true;
59
- if (options?.capability) {
60
- this.updateField(type, targetDid, options.capability);
61
- }
62
- }
63
- /**
64
- * 读取某方向的信息素强度
65
- */
66
- getStrength(type, targetDid) {
67
- let totalStrength = 0;
68
- let count = 0;
69
- for (const trail of this.trails.values()) {
70
- if (trail.type === type && trail.targetDid === targetDid) {
71
- totalStrength += trail.strength;
72
- count++;
73
- }
74
- }
75
- return count > 0 ? totalStrength / count : 0;
76
- }
77
- /**
78
- * 查询特定能力的最佳节点
79
- */
80
- queryByCapability(capability, limit = 5) {
81
- const field = this.fields.get(capability);
82
- if (!field)
83
- return [];
84
- const candidates = [];
85
- for (const trail of this.trails.values()) {
86
- if (trail.type === PheromoneType.CAPABILITY &&
87
- trail.capability?.includes(capability)) {
88
- const score = trail.strength * (trail.qualityScore || 0.5);
89
- candidates.push({ did: trail.targetDid, strength: score });
90
- }
91
- }
92
- candidates.sort((a, b) => b.strength - a.strength);
93
- return candidates.slice(0, limit).map((c) => c.did);
94
- }
95
- /**
96
- * 获取到达某节点的最佳下一跳
97
- */
98
- getNextHop(targetDid, excludeDids = []) {
99
- let bestHop = null;
100
- let bestStrength = 0;
101
- for (const trail of this.trails.values()) {
102
- if (trail.targetDid === targetDid && trail.strength > bestStrength) {
103
- if (!excludeDids.includes(trail.sourceDid)) {
104
- bestHop = trail.sourceDid;
105
- bestStrength = trail.strength;
106
- }
107
- }
108
- }
109
- return bestHop;
110
- }
111
- /**
112
- * 获取所有已知节点及其信息素强度
113
- */
114
- getAllNodeStrengths() {
115
- const result = new Map();
116
- for (const trail of this.trails.values()) {
117
- const current = result.get(trail.targetDid) || 0;
118
- result.set(trail.targetDid, Math.max(current, trail.strength));
119
- }
120
- return result;
121
- }
122
- /**
123
- * 获取信息素统计
124
- */
125
- getStats() {
126
- let totalStrength = 0;
127
- for (const trail of this.trails.values()) {
128
- totalStrength += trail.strength;
129
- }
130
- return {
131
- totalTrails: this.trails.size,
132
- avgStrength: this.trails.size > 0 ? totalStrength / this.trails.size : 0,
133
- capabilityCount: this.fields.size,
134
- };
135
- }
136
- getTrailKey(type, source, target) {
137
- return `${type}:${source}:${target}`;
138
- }
139
- updateField(type, targetDid, capability) {
140
- if (!capability)
141
- return;
142
- for (const cap of capability) {
143
- let field = this.fields.get(cap);
144
- if (!field) {
145
- field = {
146
- capability: cap,
147
- avgStrength: 0,
148
- nodeCount: 0,
149
- lastUpdate: Date.now(),
150
- };
151
- this.fields.set(cap, field);
152
- }
153
- const trails = Array.from(this.trails.values()).filter((t) => t.type === type && t.capability?.includes(cap));
154
- field.avgStrength =
155
- trails.reduce((sum, t) => sum + t.strength, 0) / (trails.length || 1);
156
- field.nodeCount = new Set(trails.map((t) => t.targetDid)).size;
157
- field.lastUpdate = Date.now();
158
- }
159
- }
160
- evaporate() {
161
- const now = Date.now();
162
- const toDelete = [];
163
- for (const [key, trail] of this.trails.entries()) {
164
- const ageHours = (now - trail.lastUpdate) / (1000 * 60 * 60);
165
- const decayFactor = Math.pow(1 - trail.decayRate, ageHours);
166
- trail.strength = trail.strength * decayFactor;
167
- if (trail.strength < DEFAULT_PHEROMONE_CONFIG.minStrength ||
168
- now - trail.createdAt > DEFAULT_PHEROMONE_CONFIG.maxTrailAge) {
169
- toDelete.push(key);
170
- }
171
- else {
172
- trail.lastUpdate = now;
173
- }
174
- }
175
- for (const key of toDelete) {
176
- this.trails.delete(key);
177
- }
178
- if (toDelete.length > 0) {
179
- this.dirty = true;
180
- this.save().catch(console.error);
181
- }
182
- }
183
- async save() {
184
- try {
185
- const data = {
186
- trails: Array.from(this.trails.values()),
187
- fields: Array.from(this.fields.entries()),
188
- };
189
- await fs.writeFile(PHEROMONE_DB_PATH, JSON.stringify(data, null, 2));
190
- }
191
- catch (e) {
192
- console.warn('[PheromoneEngine] Save failed:', e);
193
- }
194
- }
195
- async load() {
196
- try {
197
- const data = await fs.readFile(PHEROMONE_DB_PATH, 'utf-8');
198
- const parsed = JSON.parse(data);
199
- this.trails.clear();
200
- for (const t of parsed.trails || []) {
201
- this.trails.set(t.id, t);
202
- }
203
- this.fields.clear();
204
- for (const [cap, field] of parsed.fields || []) {
205
- this.fields.set(cap, field);
206
- }
207
- console.log(`[PheromoneEngine] Loaded ${this.trails.size} trails, ${this.fields.size} fields`);
208
- }
209
- catch {
210
- console.log('[PheromoneEngine] No existing pheromone data, starting fresh');
211
- }
212
- }
213
- shutdown() {
214
- if (this.evaporationTimer) {
215
- clearInterval(this.evaporationTimer);
216
- }
217
- if (this.saveTimer) {
218
- clearInterval(this.saveTimer);
219
- }
220
- if (this.dirty) {
221
- this.save()
222
- .then(() => console.log('[PheromoneEngine] Saved on shutdown'))
223
- .catch(console.error);
224
- }
225
- }
226
- }
227
- export const pheromoneEngine = new PheromoneEngine();
@@ -1,6 +0,0 @@
1
- /**
2
- * Ant Colony Module - 蚁群心跳系统核心模块
3
- */
4
- export { PheromoneType, DEFAULT_PHEROMONE_CONFIG, DEFAULT_ADAPTIVE_CONFIG, } from './types.js';
5
- export { PheromoneEngine, pheromoneEngine } from './PheromoneEngine.js';
6
- export { AdaptiveHeartbeat } from './AdaptiveHeartbeat.js';
@@ -1,24 +0,0 @@
1
- /**
2
- * Ant Colony Types - 信息素系统类型定义
3
- */
4
- export var PheromoneType;
5
- (function (PheromoneType) {
6
- PheromoneType["DISCOVERY"] = "discovery";
7
- PheromoneType["CAPABILITY"] = "capability";
8
- })(PheromoneType || (PheromoneType = {}));
9
- export const DEFAULT_PHEROMONE_CONFIG = {
10
- decayRate: 0.05,
11
- evaporationInterval: 5 * 60 * 1000,
12
- maxTrailAge: 24 * 60 * 60 * 1000,
13
- minStrength: 0.05,
14
- maxStrength: 1.0,
15
- };
16
- export const DEFAULT_ADAPTIVE_CONFIG = {
17
- minInterval: 10 * 1000,
18
- maxInterval: 120 * 1000,
19
- baseInterval: 30 * 1000,
20
- lowActivityThreshold: 0.5,
21
- highActivityThreshold: 5,
22
- pheromoneBoostFactor: 0.3,
23
- discoveryBoostFactor: 0.4,
24
- };
@@ -1,6 +0,0 @@
1
- /**
2
- * double.ts — 阶段 D loop 验证 (故意坏)
3
- */
4
- export function double(n) {
5
- return n * 2; // 修复: 正确实现 double
6
- }
@@ -1,188 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from 'react';
3
- import { p2pManager } from './p2p-manager.js';
4
- export function P2PModal({ visible, onClose }) {
5
- const [activeTab, setActiveTab] = useState('identity');
6
- const [initialized, setInitialized] = useState(false);
7
- const [status, setStatus] = useState('idle');
8
- const [statusText, setStatusText] = useState('未初始化');
9
- const [identity, setIdentity] = useState(null);
10
- const [connectInput, setConnectInput] = useState('');
11
- const [progress, setProgress] = useState(null);
12
- const [progressVisible, setProgressVisible] = useState(false);
13
- const [connectResult, setConnectResult] = useState(null);
14
- const [history, setHistory] = useState([]);
15
- const [messages, setMessages] = useState([]);
16
- const [unreadCount, setUnreadCount] = useState(0);
17
- const [peers, setPeers] = useState([]);
18
- const [persistentConnections, setPersistentConnections] = useState([]);
19
- const [toast, setToast] = useState(null);
20
- useEffect(() => {
21
- if (visible && !initialized) {
22
- console.log('[P2P Modal] visible=true, initialized=false, calling initP2P');
23
- initP2P();
24
- }
25
- else if (visible && initialized && !identity) {
26
- // 已初始化但没有identity,可能是首次未触发
27
- console.log('[P2P Modal] visible=true, initialized=true, but no identity, calling initP2P again');
28
- initP2P();
29
- }
30
- }, [visible, initialized]);
31
- useEffect(() => {
32
- if (visible) {
33
- loadActiveTab();
34
- }
35
- }, [activeTab, visible]);
36
- async function initP2P() {
37
- setStatus('connecting');
38
- setStatusText('初始化中...');
39
- try {
40
- const identity = await p2pManager.init();
41
- setIdentity(identity);
42
- setStatus('online');
43
- setStatusText('已连接');
44
- setInitialized(true);
45
- }
46
- catch (e) {
47
- setStatus('error');
48
- setStatusText('初始化失败');
49
- }
50
- }
51
- async function loadActiveTab() {
52
- switch (activeTab) {
53
- case 'history':
54
- await loadHistory();
55
- break;
56
- case 'messages':
57
- await loadMessages();
58
- break;
59
- case 'connect':
60
- loadPeers();
61
- loadPersistentConnections();
62
- break;
63
- }
64
- }
65
- async function loadHistory() {
66
- try {
67
- const hist = await p2pManager.getHistory();
68
- setHistory(hist);
69
- }
70
- catch (e) {
71
- console.error('[P2P Modal] 加载历史失败:', e);
72
- }
73
- }
74
- async function loadMessages() {
75
- try {
76
- const msgs = await p2pManager.getMessages();
77
- setMessages(msgs);
78
- const unread = p2pManager.getUnreadCount();
79
- setUnreadCount(unread);
80
- }
81
- catch (e) {
82
- console.error('[P2P Modal] 加载消息失败:', e);
83
- }
84
- }
85
- function loadPeers() {
86
- const connectedPeers = p2pManager.getConnectedPeers();
87
- setPeers(connectedPeers);
88
- }
89
- async function loadPersistentConnections() {
90
- try {
91
- const connections = await p2pManager.getPersistentConnections();
92
- setPersistentConnections(connections);
93
- }
94
- catch (e) {
95
- console.error('[P2P Modal] 加载持久连接失败:', e);
96
- }
97
- }
98
- async function handleToggleConnection(connection) {
99
- const newStatus = connection.status === 'connected' ? 'disconnected' : 'connected';
100
- const enable = newStatus === 'connected';
101
- try {
102
- const success = await p2pManager.toggleConnection(connection, enable);
103
- if (success) {
104
- await loadPersistentConnections();
105
- loadPeers();
106
- showToast(enable ? '正在连接...' : '已断开');
107
- }
108
- else {
109
- showToast('操作失败');
110
- }
111
- }
112
- catch (e) {
113
- showToast('操作失败');
114
- }
115
- }
116
- async function handleOpenChannel(channelId) {
117
- showToast(`打开通道: ${channelId}`);
118
- onClose();
119
- }
120
- async function handleConnect() {
121
- if (!connectInput.trim())
122
- return;
123
- setProgressVisible(true);
124
- setProgress({ stage: 'init', percent: 0, message: '验证输入格式...' });
125
- setConnectResult(null);
126
- try {
127
- const result = await p2pManager.connectAndCreateChannel(connectInput, (p) => setProgress(p));
128
- if (result.success) {
129
- setConnectResult({ type: 'success', text: `已连接到 ${result.name || '节点'}` });
130
- setConnectInput('');
131
- await loadHistory();
132
- loadPeers();
133
- loadPersistentConnections();
134
- }
135
- else {
136
- setConnectResult({ type: 'error', text: result.error || '连接失败' });
137
- }
138
- }
139
- catch (e) {
140
- setConnectResult({ type: 'error', text: e.message });
141
- }
142
- finally {
143
- setTimeout(() => {
144
- setProgressVisible(false);
145
- setProgress(null);
146
- }, 2000);
147
- }
148
- }
149
- async function handleHistoryAction(action, item) {
150
- if (action === 'connect') {
151
- setConnectInput(item.cid);
152
- setActiveTab('connect');
153
- }
154
- else if (action === 'pin') {
155
- await p2pManager.updateHistory(item.id, { isPinned: !item.isPinned });
156
- await loadHistory();
157
- }
158
- else if (action === 'delete') {
159
- await p2pManager.deleteHistory(item.id);
160
- await loadHistory();
161
- }
162
- }
163
- async function handleMarkAllRead() {
164
- await p2pManager.messages.markAllRead();
165
- await loadMessages();
166
- }
167
- function copyToClipboard(text) {
168
- navigator.clipboard.writeText(text);
169
- showToast('已复制');
170
- }
171
- function showToast(msg) {
172
- setToast(msg);
173
- setTimeout(() => setToast(null), 2000);
174
- }
175
- function handleCopyLink() {
176
- if (identity) {
177
- const link = `bolloon://connect?did=${encodeURIComponent(identity.did)}&cid=${encodeURIComponent(identity.cid)}`;
178
- navigator.clipboard.writeText(link);
179
- showToast('链接已复制');
180
- }
181
- }
182
- function handleExportFile() {
183
- p2pManager.identity.exportIdentityFile();
184
- }
185
- if (!visible)
186
- return null;
187
- return (_jsxs("div", { className: "p2p-modal-overlay", onClick: (e) => e.target === e.currentTarget && onClose(), children: [_jsxs("div", { className: "p2p-modal", children: [_jsxs("div", { className: "modal-header", children: [_jsx("h2", { children: "P2P \u7F51\u7EDC" }), _jsx("button", { className: "modal-close", onClick: onClose, children: "\u00D7" })] }), _jsxs("div", { className: "tabs", children: [_jsx("button", { className: `tab ${activeTab === 'identity' ? 'active' : ''}`, onClick: () => setActiveTab('identity'), children: "\u6211\u7684\u8EAB\u4EFD" }), _jsx("button", { className: `tab ${activeTab === 'connect' ? 'active' : ''}`, onClick: () => setActiveTab('connect'), children: "\u8FDE\u63A5" }), _jsx("button", { className: `tab ${activeTab === 'history' ? 'active' : ''}`, onClick: () => setActiveTab('history'), children: "\u5386\u53F2\u8BB0\u5F55" }), _jsxs("button", { className: `tab ${activeTab === 'messages' ? 'active' : ''}`, onClick: () => setActiveTab('messages'), children: ["\u6D88\u606F ", unreadCount > 0 && _jsx("span", { className: "unread-badge", children: unreadCount })] })] }), activeTab === 'identity' && (_jsxs("div", { className: "tab-content active", children: [_jsxs("div", { className: "identity-card", children: [_jsxs("div", { className: "status-row", children: [_jsx("span", { className: `status-indicator ${status}` }), _jsx("span", { children: statusText })] }), _jsxs("div", { className: "info-row", children: [_jsx("span", { className: "info-label", children: "DID:" }), _jsx("code", { className: "info-value", children: identity?.did || '-' }), identity?.did && _jsx("button", { className: "copy-btn", onClick: () => copyToClipboard(identity.did), children: "\uD83D\uDCCB" })] }), _jsxs("div", { className: "info-row", children: [_jsx("span", { className: "info-label", children: "CID:" }), _jsx("code", { className: "info-value", children: identity?.cid || '-' }), identity?.cid && _jsx("button", { className: "copy-btn", onClick: () => copyToClipboard(identity.cid), children: "\uD83D\uDCCB" })] }), _jsxs("div", { className: "info-row", children: [_jsx("span", { className: "info-label", children: "Node ID:" }), _jsx("code", { className: "info-value", children: identity?.irohNodeId || '-' }), identity?.irohNodeId && _jsx("button", { className: "copy-btn", onClick: () => copyToClipboard(identity.irohNodeId), children: "\uD83D\uDCCB" })] })] }), !initialized && _jsx("button", { className: "btn-primary", onClick: initP2P, children: "\u521D\u59CB\u5316 P2P" }), identity && (_jsxs("div", { className: "share-panel", children: [_jsx("h4", { children: "\u5206\u4EAB\u7ED9\u597D\u53CB" }), _jsxs("div", { className: "share-actions", children: [_jsx("button", { className: "btn-secondary", onClick: handleCopyLink, children: "\uD83D\uDCCB \u590D\u5236\u94FE\u63A5" }), _jsx("button", { className: "btn-secondary", onClick: handleExportFile, children: "\uD83D\uDCC1 \u5BFC\u51FA\u6587\u4EF6" })] })] }))] })), activeTab === 'connect' && (_jsxs("div", { className: "tab-content active", children: [_jsxs("div", { className: "connect-form", children: [_jsx("input", { type: "text", placeholder: "\u7C98\u8D34 CID \u6216\u94FE\u63A5...", value: connectInput, onChange: (e) => setConnectInput(e.target.value), onKeyPress: (e) => e.key === 'Enter' && handleConnect() }), _jsx("button", { className: "btn-secondary", onClick: handleConnect, children: "\u8FDE\u63A5 \u25B6" })] }), progressVisible && progress && (_jsxs("div", { className: "progress show", children: [_jsx("div", { className: "progress-bar", children: _jsx("div", { className: "progress-fill", style: { width: `${progress.percent}%` } }) }), _jsx("span", { className: "progress-text", children: progress.message })] })), connectResult && (_jsx("div", { className: `connect-result ${connectResult.type} show`, children: connectResult.text })), _jsxs("div", { className: "persistent-peers-section", children: [_jsxs("h4", { children: ["\u6301\u4E45\u8FDE\u63A5 (", persistentConnections.length, ")"] }), persistentConnections.length === 0 ? (_jsx("div", { className: "empty-hint", children: "\u6682\u65E0\u6301\u4E45\u8FDE\u63A5" })) : (persistentConnections.map((conn) => (_jsxs("div", { className: `persistent-peer-item ${conn.status}`, children: [_jsx("div", { className: "peer-status-indicator", children: _jsx("span", { className: `dot ${conn.status === 'connected' ? 'online' : 'offline'}` }) }), _jsxs("div", { className: "peer-info", children: [_jsxs("div", { className: "peer-name", children: [conn.peerName || 'Unknown', conn.isAutoConnect && _jsx("span", { className: "auto-badge", children: "\u81EA\u52A8" })] }), _jsxs("div", { className: "peer-meta", children: [_jsxs("span", { children: ["DID: ", conn.peerDid?.substring(0, 16), "..."] }), _jsxs("span", { children: ["\u72B6\u6001: ", conn.status === 'connected' ? '已连接' : '未连接'] })] })] }), _jsxs("div", { className: "peer-actions", children: [_jsx("button", { className: `btn-sm ${conn.status === 'connected' ? 'btn-danger' : 'btn-primary'}`, onClick: () => handleToggleConnection(conn), children: conn.status === 'connected' ? '断开' : '连接' }), conn.channelId && (_jsx("button", { className: "btn-sm btn-secondary", onClick: () => handleOpenChannel(conn.channelId), children: "\u5BF9\u8BDD" }))] })] }, conn.id))))] }), _jsxs("div", { className: "peers-section", children: [_jsxs("h4", { children: ["\u5F53\u524D\u8FDE\u63A5 (", peers.length, ")"] }), _jsxs("div", { id: "p2p-peers-list", children: [peers.length === 0 && _jsx("div", { className: "empty-hint", children: "\u6682\u65E0\u8FDE\u63A5" }), peers.map((peer, i) => (_jsxs("div", { className: "peer-item", children: [_jsx("div", { className: "peer-status", children: _jsx("span", { className: "dot online" }) }), _jsxs("div", { className: "peer-info", children: [_jsx("div", { className: "peer-name", children: peer.info?.name || 'Unknown' }), _jsxs("div", { className: "peer-meta", children: [(peer.nodeId || '').substring(0, 16), "..."] })] })] }, i)))] })] })] })), activeTab === 'history' && (_jsxs("div", { className: "tab-content active", children: [_jsx("div", { className: "toolbar", children: _jsx("button", { className: "btn-secondary btn-sm", onClick: loadHistory, children: "\uD83D\uDD04 \u5237\u65B0" }) }), _jsxs("div", { id: "p2p-history-list", children: [history.length === 0 && _jsx("div", { className: "empty-hint", children: "\u6682\u65E0\u8FDE\u63A5\u5386\u53F2" }), history.map((item) => (_jsxs("div", { className: `history-item ${item.isPinned ? 'pinned' : ''}`, children: [_jsx("div", { className: "history-item-icon", children: "\uD83D\uDCAC" }), _jsxs("div", { className: "history-item-info", children: [_jsxs("div", { className: "history-item-name", children: [item.name || 'Unknown', item.isPinned && _jsx("span", { className: "pin-icon", children: "\uD83D\uDCCC" })] }), _jsxs("div", { className: "history-item-meta", children: [_jsxs("span", { children: ["\u4E0A\u6B21: ", new Date(item.lastConnectedAt).toLocaleString()] }), _jsxs("span", { children: ["\u6D88\u606F: ", item.totalMessages || 0] })] })] }), _jsxs("div", { className: "history-item-actions", children: [_jsx("button", { className: "btn-sm btn-secondary", onClick: () => handleHistoryAction('connect', item), children: "\u8FDE\u63A5" }), _jsx("button", { className: "btn-sm btn-secondary", onClick: () => handleHistoryAction('pin', item), children: item.isPinned ? '取消置顶' : '置顶' }), _jsx("button", { className: "btn-sm btn-secondary", onClick: () => handleHistoryAction('delete', item), children: "\u5220\u9664" })] })] }, item.id)))] })] })), activeTab === 'messages' && (_jsxs("div", { className: "tab-content active", children: [_jsx("div", { className: "toolbar", children: _jsx("button", { className: "btn-secondary btn-sm", onClick: handleMarkAllRead, children: "\u5168\u90E8\u5DF2\u8BFB" }) }), _jsxs("div", { id: "p2p-messages-list", children: [messages.length === 0 && _jsx("div", { className: "empty-hint", children: "\u6682\u65E0\u6D88\u606F" }), messages.slice(-20).map((msg) => (_jsxs("div", { className: `message-item ${!msg.isRead ? 'unread' : ''}`, children: [_jsxs("div", { className: "message-header", children: [_jsx("span", { className: "message-sender", children: msg.fromName || msg.fromDid }), _jsx("span", { className: "message-time", children: new Date(msg.timestamp).toLocaleString() })] }), _jsx("div", { className: "message-content", children: msg.content.substring(0, 200) })] }, msg.id)))] })] }))] }), toast && _jsx("div", { className: "toast", children: toast })] }));
188
- }