@f2a/network 0.1.2 → 0.2.0

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 (226) hide show
  1. package/README.md +278 -63
  2. package/dist/cli/commands.d.ts.map +1 -1
  3. package/dist/cli/commands.js +29 -2
  4. package/dist/cli/commands.js.map +1 -1
  5. package/dist/cli/config.d.ts +176 -0
  6. package/dist/cli/config.d.ts.map +1 -0
  7. package/dist/cli/config.js +386 -0
  8. package/dist/cli/config.js.map +1 -0
  9. package/dist/cli/daemon.d.ts +54 -0
  10. package/dist/cli/daemon.d.ts.map +1 -0
  11. package/dist/cli/daemon.js +572 -0
  12. package/dist/cli/daemon.js.map +1 -0
  13. package/dist/cli/index.js +90 -16
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/cli/init.d.ts +13 -0
  16. package/dist/cli/init.d.ts.map +1 -0
  17. package/dist/cli/init.js +352 -0
  18. package/dist/cli/init.js.map +1 -0
  19. package/dist/core/e2ee-crypto.d.ts +127 -1
  20. package/dist/core/e2ee-crypto.d.ts.map +1 -1
  21. package/dist/core/e2ee-crypto.js +446 -12
  22. package/dist/core/e2ee-crypto.js.map +1 -1
  23. package/dist/core/f2a.d.ts +2 -1
  24. package/dist/core/f2a.d.ts.map +1 -1
  25. package/dist/core/f2a.js +6 -2
  26. package/dist/core/f2a.js.map +1 -1
  27. package/dist/core/identity/encrypted-key-store.d.ts +19 -0
  28. package/dist/core/identity/encrypted-key-store.d.ts.map +1 -0
  29. package/dist/core/identity/encrypted-key-store.js +72 -0
  30. package/dist/core/identity/encrypted-key-store.js.map +1 -0
  31. package/dist/core/identity/identity-manager.d.ts +133 -0
  32. package/dist/core/identity/identity-manager.d.ts.map +1 -0
  33. package/dist/core/identity/identity-manager.js +454 -0
  34. package/dist/core/identity/identity-manager.js.map +1 -0
  35. package/dist/core/identity/index.d.ts +8 -0
  36. package/dist/core/identity/index.d.ts.map +1 -0
  37. package/dist/core/identity/index.js +7 -0
  38. package/dist/core/identity/index.js.map +1 -0
  39. package/dist/core/identity/types.d.ts +70 -0
  40. package/dist/core/identity/types.d.ts.map +1 -0
  41. package/dist/core/identity/types.js +17 -0
  42. package/dist/core/identity/types.js.map +1 -0
  43. package/dist/core/p2p-network.d.ts +26 -0
  44. package/dist/core/p2p-network.d.ts.map +1 -1
  45. package/dist/core/p2p-network.js +434 -105
  46. package/dist/core/p2p-network.js.map +1 -1
  47. package/dist/core/reputation-security.d.ts +15 -0
  48. package/dist/core/reputation-security.d.ts.map +1 -1
  49. package/dist/core/reputation-security.js +73 -3
  50. package/dist/core/reputation-security.js.map +1 -1
  51. package/dist/core/reputation.d.ts +129 -4
  52. package/dist/core/reputation.d.ts.map +1 -1
  53. package/dist/core/reputation.js +294 -1
  54. package/dist/core/reputation.js.map +1 -1
  55. package/dist/core/review-committee.d.ts +2 -2
  56. package/dist/core/review-committee.d.ts.map +1 -1
  57. package/dist/core/review-committee.js +17 -0
  58. package/dist/core/review-committee.js.map +1 -1
  59. package/dist/daemon/control-server.d.ts.map +1 -1
  60. package/dist/daemon/control-server.js +44 -1
  61. package/dist/daemon/control-server.js.map +1 -1
  62. package/dist/daemon/webhook.d.ts +3 -0
  63. package/dist/daemon/webhook.d.ts.map +1 -1
  64. package/dist/daemon/webhook.js +318 -6
  65. package/dist/daemon/webhook.js.map +1 -1
  66. package/dist/index.d.ts +3 -3
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +7 -3
  69. package/dist/index.js.map +1 -1
  70. package/dist/types/index.d.ts +4 -0
  71. package/dist/types/index.d.ts.map +1 -1
  72. package/dist/types/index.js.map +1 -1
  73. package/dist/types/result.d.ts +1 -1
  74. package/dist/types/result.d.ts.map +1 -1
  75. package/dist/types/result.js.map +1 -1
  76. package/dist/utils/crypto-utils.d.ts +17 -0
  77. package/dist/utils/crypto-utils.d.ts.map +1 -0
  78. package/dist/utils/crypto-utils.js +28 -0
  79. package/dist/utils/crypto-utils.js.map +1 -0
  80. package/dist/utils/logger.d.ts +1 -0
  81. package/dist/utils/logger.d.ts.map +1 -1
  82. package/dist/utils/logger.js +9 -3
  83. package/dist/utils/logger.js.map +1 -1
  84. package/dist/utils/rate-limiter.d.ts.map +1 -1
  85. package/dist/utils/rate-limiter.js +3 -1
  86. package/dist/utils/rate-limiter.js.map +1 -1
  87. package/dist/utils/signature.d.ts +47 -1
  88. package/dist/utils/signature.d.ts.map +1 -1
  89. package/dist/utils/signature.js +166 -11
  90. package/dist/utils/signature.js.map +1 -1
  91. package/package.json +9 -1
  92. package/.github/workflows/ci.yml +0 -113
  93. package/.github/workflows/publish.yml +0 -60
  94. package/MONOREPO.md +0 -58
  95. package/SKILL.md +0 -137
  96. package/dist/adapters/openclaw.d.ts +0 -103
  97. package/dist/adapters/openclaw.d.ts.map +0 -1
  98. package/dist/adapters/openclaw.js +0 -297
  99. package/dist/adapters/openclaw.js.map +0 -1
  100. package/dist/core/connection-manager.d.ts +0 -80
  101. package/dist/core/connection-manager.d.ts.map +0 -1
  102. package/dist/core/connection-manager.js +0 -235
  103. package/dist/core/connection-manager.js.map +0 -1
  104. package/dist/core/connection-manager.test.d.ts +0 -2
  105. package/dist/core/connection-manager.test.d.ts.map +0 -1
  106. package/dist/core/connection-manager.test.js +0 -52
  107. package/dist/core/connection-manager.test.js.map +0 -1
  108. package/dist/core/identity.d.ts +0 -47
  109. package/dist/core/identity.d.ts.map +0 -1
  110. package/dist/core/identity.js +0 -130
  111. package/dist/core/identity.js.map +0 -1
  112. package/dist/core/identity.test.d.ts +0 -2
  113. package/dist/core/identity.test.d.ts.map +0 -1
  114. package/dist/core/identity.test.js +0 -43
  115. package/dist/core/identity.test.js.map +0 -1
  116. package/dist/core/serverless.d.ts +0 -155
  117. package/dist/core/serverless.d.ts.map +0 -1
  118. package/dist/core/serverless.js +0 -615
  119. package/dist/core/serverless.js.map +0 -1
  120. package/dist/daemon/webhook.test.d.ts +0 -2
  121. package/dist/daemon/webhook.test.d.ts.map +0 -1
  122. package/dist/daemon/webhook.test.js +0 -24
  123. package/dist/daemon/webhook.test.js.map +0 -1
  124. package/dist/protocol/messages.d.ts +0 -739
  125. package/dist/protocol/messages.d.ts.map +0 -1
  126. package/dist/protocol/messages.js +0 -188
  127. package/dist/protocol/messages.js.map +0 -1
  128. package/dist/protocol/messages.test.d.ts +0 -2
  129. package/dist/protocol/messages.test.d.ts.map +0 -1
  130. package/dist/protocol/messages.test.js +0 -55
  131. package/dist/protocol/messages.test.js.map +0 -1
  132. package/docs/F2A-PROTOCOL.md +0 -61
  133. package/docs/MOBILE_BOOTSTRAP_DESIGN.md +0 -126
  134. package/docs/a2a-lessons.md +0 -316
  135. package/docs/middleware-guide.md +0 -448
  136. package/docs/readme-update-checklist.md +0 -90
  137. package/docs/reputation-guide.md +0 -396
  138. package/docs/rfcs/001-reputation-system.md +0 -712
  139. package/docs/security-design.md +0 -247
  140. package/install.sh +0 -231
  141. package/packages/openclaw-adapter/README.md +0 -510
  142. package/packages/openclaw-adapter/openclaw.plugin.json +0 -106
  143. package/packages/openclaw-adapter/package.json +0 -40
  144. package/packages/openclaw-adapter/src/announcement-queue.test.ts +0 -449
  145. package/packages/openclaw-adapter/src/announcement-queue.ts +0 -403
  146. package/packages/openclaw-adapter/src/capability-detector.test.ts +0 -99
  147. package/packages/openclaw-adapter/src/capability-detector.ts +0 -183
  148. package/packages/openclaw-adapter/src/claim-handlers.test.ts +0 -974
  149. package/packages/openclaw-adapter/src/claim-handlers.ts +0 -482
  150. package/packages/openclaw-adapter/src/connector.business.test.ts +0 -583
  151. package/packages/openclaw-adapter/src/connector.ts +0 -795
  152. package/packages/openclaw-adapter/src/index.test.ts +0 -82
  153. package/packages/openclaw-adapter/src/index.ts +0 -18
  154. package/packages/openclaw-adapter/src/integration.e2e.test.ts +0 -829
  155. package/packages/openclaw-adapter/src/logger.ts +0 -51
  156. package/packages/openclaw-adapter/src/network-client.test.ts +0 -266
  157. package/packages/openclaw-adapter/src/network-client.ts +0 -251
  158. package/packages/openclaw-adapter/src/network-recovery.test.ts +0 -465
  159. package/packages/openclaw-adapter/src/node-manager.test.ts +0 -136
  160. package/packages/openclaw-adapter/src/node-manager.ts +0 -429
  161. package/packages/openclaw-adapter/src/plugin.test.ts +0 -439
  162. package/packages/openclaw-adapter/src/plugin.ts +0 -104
  163. package/packages/openclaw-adapter/src/reputation.test.ts +0 -221
  164. package/packages/openclaw-adapter/src/reputation.ts +0 -368
  165. package/packages/openclaw-adapter/src/task-guard.test.ts +0 -502
  166. package/packages/openclaw-adapter/src/task-guard.ts +0 -860
  167. package/packages/openclaw-adapter/src/task-queue.concurrency.test.ts +0 -462
  168. package/packages/openclaw-adapter/src/task-queue.edge-cases.test.ts +0 -284
  169. package/packages/openclaw-adapter/src/task-queue.persistence.test.ts +0 -408
  170. package/packages/openclaw-adapter/src/task-queue.ts +0 -668
  171. package/packages/openclaw-adapter/src/tool-handlers.test.ts +0 -906
  172. package/packages/openclaw-adapter/src/tool-handlers.ts +0 -574
  173. package/packages/openclaw-adapter/src/types.ts +0 -361
  174. package/packages/openclaw-adapter/src/webhook-pusher.test.ts +0 -188
  175. package/packages/openclaw-adapter/src/webhook-pusher.ts +0 -220
  176. package/packages/openclaw-adapter/src/webhook-server.test.ts +0 -580
  177. package/packages/openclaw-adapter/src/webhook-server.ts +0 -202
  178. package/packages/openclaw-adapter/tsconfig.json +0 -20
  179. package/src/cli/commands.test.ts +0 -157
  180. package/src/cli/commands.ts +0 -129
  181. package/src/cli/index.test.ts +0 -77
  182. package/src/cli/index.ts +0 -234
  183. package/src/core/autonomous-economy.test.ts +0 -291
  184. package/src/core/autonomous-economy.ts +0 -428
  185. package/src/core/e2ee-crypto.test.ts +0 -125
  186. package/src/core/e2ee-crypto.ts +0 -246
  187. package/src/core/f2a.test.ts +0 -269
  188. package/src/core/f2a.ts +0 -618
  189. package/src/core/p2p-network.test.ts +0 -199
  190. package/src/core/p2p-network.ts +0 -1432
  191. package/src/core/reputation-security.test.ts +0 -403
  192. package/src/core/reputation-security.ts +0 -562
  193. package/src/core/reputation.test.ts +0 -260
  194. package/src/core/reputation.ts +0 -576
  195. package/src/core/review-committee.test.ts +0 -380
  196. package/src/core/review-committee.ts +0 -401
  197. package/src/core/token-manager.test.ts +0 -133
  198. package/src/core/token-manager.ts +0 -140
  199. package/src/daemon/control-server.test.ts +0 -216
  200. package/src/daemon/control-server.ts +0 -292
  201. package/src/daemon/index.test.ts +0 -85
  202. package/src/daemon/index.ts +0 -89
  203. package/src/daemon/main.ts +0 -44
  204. package/src/daemon/start.ts +0 -29
  205. package/src/daemon/webhook.test.ts +0 -68
  206. package/src/daemon/webhook.ts +0 -105
  207. package/src/index.test.ts +0 -436
  208. package/src/index.ts +0 -72
  209. package/src/types/index.test.ts +0 -87
  210. package/src/types/index.ts +0 -341
  211. package/src/types/result.ts +0 -68
  212. package/src/utils/benchmark.ts +0 -237
  213. package/src/utils/logger.ts +0 -331
  214. package/src/utils/middleware.ts +0 -229
  215. package/src/utils/rate-limiter.ts +0 -207
  216. package/src/utils/signature.ts +0 -136
  217. package/src/utils/validation.ts +0 -186
  218. package/tests/docker/Dockerfile.node +0 -23
  219. package/tests/docker/Dockerfile.runner +0 -18
  220. package/tests/docker/docker-compose.test.yml +0 -73
  221. package/tests/integration/message-passing.test.ts +0 -109
  222. package/tests/integration/multi-node.test.ts +0 -92
  223. package/tests/integration/p2p-connection.test.ts +0 -83
  224. package/tests/integration/test-config.ts +0 -32
  225. package/tsconfig.json +0 -21
  226. package/vitest.config.ts +0 -26
@@ -1,906 +0,0 @@
1
- /**
2
- * ToolHandlers 单元测试
3
- * 测试工具处理器的所有方法
4
- */
5
-
6
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
- import { ToolHandlers, type ToolHandlerParams } from './tool-handlers.js';
8
- import type { F2AOpenClawAdapter } from './connector.js';
9
- import type { SessionContext, AgentInfo, TaskResponse } from './types.js';
10
- import type { QueuedTask } from './task-queue.js';
11
-
12
- // 创建 mock 依赖
13
- const createMockNetworkClient = () => ({
14
- discoverAgents: vi.fn(),
15
- delegateTask: vi.fn(),
16
- getConnectedPeers: vi.fn(),
17
- sendTaskResponse: vi.fn()
18
- });
19
-
20
- const createMockReputationSystem = () => ({
21
- getReputation: vi.fn(),
22
- isAllowed: vi.fn(),
23
- recordSuccess: vi.fn(),
24
- recordFailure: vi.fn(),
25
- getAllReputations: vi.fn()
26
- });
27
-
28
- const createMockTaskQueue = () => ({
29
- getStats: vi.fn(),
30
- getPending: vi.fn(),
31
- getAll: vi.fn(),
32
- get: vi.fn(),
33
- complete: vi.fn(),
34
- markProcessing: vi.fn()
35
- });
36
-
37
- const createMockNodeManager = () => ({
38
- getStatus: vi.fn()
39
- });
40
-
41
- const createMockAdapter = () => ({
42
- networkClient: createMockNetworkClient(),
43
- reputationSystem: createMockReputationSystem(),
44
- taskQueue: createMockTaskQueue(),
45
- nodeManager: createMockNodeManager(),
46
- config: {
47
- security: {
48
- requireConfirmation: false,
49
- whitelist: [] as string[],
50
- blacklist: [] as string[],
51
- maxTasksPerMinute: 10
52
- }
53
- }
54
- });
55
-
56
- // 创建 mock SessionContext
57
- const createMockSessionContext = (): SessionContext => ({
58
- sessionId: 'test-session-123',
59
- workspace: '/tmp/test-workspace',
60
- toJSON: vi.fn(() => ({ sessionId: 'test-session-123', workspace: '/tmp/test-workspace' }))
61
- });
62
-
63
- // 创建测试用的 AgentInfo
64
- const createMockAgent = (overrides: Partial<AgentInfo> = {}): AgentInfo => ({
65
- peerId: 'peer-test-12345678901234567890',
66
- displayName: 'Test Agent',
67
- capabilities: [{ name: 'code-generation', description: 'Code generation capability' }],
68
- ...overrides
69
- });
70
-
71
- // 创建测试用的 QueuedTask
72
- const createMockTask = (overrides: Partial<QueuedTask> = {}): QueuedTask => ({
73
- taskId: 'task-test-123',
74
- taskType: 'openclaw-task',
75
- description: 'Test task description',
76
- from: 'peer-sender-123',
77
- parameters: {},
78
- status: 'pending',
79
- createdAt: Date.now(),
80
- timeout: 60000,
81
- ...overrides
82
- });
83
-
84
- describe('ToolHandlers', () => {
85
- let toolHandlers: ToolHandlers;
86
- let mockAdapter: any;
87
- let mockContext: SessionContext;
88
-
89
- beforeEach(() => {
90
- vi.clearAllMocks();
91
- mockAdapter = createMockAdapter();
92
- mockContext = createMockSessionContext();
93
- toolHandlers = new ToolHandlers(mockAdapter as F2AOpenClawAdapter);
94
- });
95
-
96
- afterEach(() => {
97
- vi.restoreAllMocks();
98
- });
99
-
100
- describe('handleDiscover', () => {
101
- it('应该成功发现 Agents 并返回格式化内容', async () => {
102
- const agents = [
103
- createMockAgent({ displayName: 'Agent A', peerId: 'peer-a-12345678901234567890' }),
104
- createMockAgent({ displayName: 'Agent B', peerId: 'peer-b-12345678901234567890' })
105
- ];
106
-
107
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
108
- success: true,
109
- data: agents
110
- });
111
-
112
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 80 });
113
-
114
- const result = await toolHandlers.handleDiscover({}, mockContext);
115
-
116
- expect(result.content).toContain('发现 2 个 Agents');
117
- expect(result.data).toEqual({ agents, count: 2 });
118
- });
119
-
120
- it('应该按能力过滤 Agents', async () => {
121
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
122
- success: true,
123
- data: [createMockAgent()]
124
- });
125
-
126
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 80 });
127
-
128
- await toolHandlers.handleDiscover({ capability: 'code-generation' }, mockContext);
129
-
130
- expect(mockAdapter.networkClient.discoverAgents).toHaveBeenCalledWith('code-generation');
131
- });
132
-
133
- it('应该按最低信誉过滤 Agents', async () => {
134
- const agents = [
135
- createMockAgent({ peerId: 'peer-high', displayName: 'High Rep Agent' }),
136
- createMockAgent({ peerId: 'peer-low', displayName: 'Low Rep Agent' })
137
- ];
138
-
139
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
140
- success: true,
141
- data: agents
142
- });
143
-
144
- // getReputation 会被调用多次:过滤时调用,显示时也调用
145
- // peer-high: 过滤时返回 90,显示时返回 90
146
- // peer-low: 过滤时返回 30(被过滤掉,不会显示)
147
- mockAdapter.reputationSystem.getReputation.mockImplementation((peerId: string) => {
148
- if (peerId === 'peer-high') {
149
- return { score: 90 };
150
- }
151
- return { score: 30 };
152
- });
153
-
154
- const result = await toolHandlers.handleDiscover({ min_reputation: 50 }, mockContext);
155
-
156
- expect(result.data?.count).toBe(1);
157
- expect(result.content).toContain('High Rep Agent');
158
- expect(result.content).not.toContain('Low Rep Agent');
159
- });
160
-
161
- it('应该处理发现失败的情况', async () => {
162
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
163
- success: false,
164
- error: 'Network error'
165
- });
166
-
167
- const result = await toolHandlers.handleDiscover({}, mockContext);
168
-
169
- expect(result.content).toBe('发现失败: Network error');
170
- });
171
-
172
- it('应该处理未发现 Agents 的情况', async () => {
173
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
174
- success: true,
175
- data: []
176
- });
177
-
178
- const result = await toolHandlers.handleDiscover({}, mockContext);
179
-
180
- expect(result.content).toBe('🔍 未发现符合条件的 Agents');
181
- });
182
-
183
- it('应该处理 null data 的情况', async () => {
184
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
185
- success: true,
186
- data: null
187
- });
188
-
189
- const result = await toolHandlers.handleDiscover({}, mockContext);
190
-
191
- expect(result.content).toBe('🔍 未发现符合条件的 Agents');
192
- });
193
- });
194
-
195
- describe('handleDelegate', () => {
196
- it('应该成功委托任务给 Agent', async () => {
197
- const agent = createMockAgent();
198
-
199
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
200
- success: true,
201
- data: [agent]
202
- });
203
-
204
- mockAdapter.reputationSystem.isAllowed.mockReturnValue(true);
205
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 80 });
206
-
207
- mockAdapter.networkClient.delegateTask.mockResolvedValue({
208
- success: true,
209
- data: { result: 'Task completed' }
210
- });
211
-
212
- const result = await toolHandlers.handleDelegate({
213
- agent: 'Test Agent',
214
- task: 'Write some code'
215
- }, mockContext);
216
-
217
- expect(result.content).toContain('Test Agent 已完成任务');
218
- expect(result.data).toEqual({ result: 'Task completed' });
219
- });
220
-
221
- it('应该处理找不到 Agent 的情况', async () => {
222
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
223
- success: true,
224
- data: []
225
- });
226
-
227
- const result = await toolHandlers.handleDelegate({
228
- agent: 'Non-existent Agent',
229
- task: 'Write code'
230
- }, mockContext);
231
-
232
- expect(result.content).toBe('❌ 找不到 Agent: Non-existent Agent');
233
- });
234
-
235
- it('应该处理信誉过低的情况', async () => {
236
- const agent = createMockAgent();
237
-
238
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
239
- success: true,
240
- data: [agent]
241
- });
242
-
243
- mockAdapter.reputationSystem.isAllowed.mockReturnValue(false);
244
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 20 });
245
-
246
- const result = await toolHandlers.handleDelegate({
247
- agent: 'Test Agent',
248
- task: 'Write code'
249
- }, mockContext);
250
-
251
- expect(result.content).toContain('信誉过低');
252
- });
253
-
254
- it('应该处理委托失败的情况', async () => {
255
- const agent = createMockAgent();
256
-
257
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
258
- success: true,
259
- data: [agent]
260
- });
261
-
262
- mockAdapter.reputationSystem.isAllowed.mockReturnValue(true);
263
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 80 });
264
-
265
- mockAdapter.networkClient.delegateTask.mockResolvedValue({
266
- success: false,
267
- error: 'Connection timeout'
268
- });
269
-
270
- const result = await toolHandlers.handleDelegate({
271
- agent: 'Test Agent',
272
- task: 'Write code'
273
- }, mockContext);
274
-
275
- expect(result.content).toBe('❌ 委托失败: Connection timeout');
276
- expect(mockAdapter.reputationSystem.recordFailure).toHaveBeenCalled();
277
- });
278
-
279
- it('应该支持 #索引 格式引用 Agent', async () => {
280
- const agents = [
281
- createMockAgent({ displayName: 'First Agent' }),
282
- createMockAgent({ displayName: 'Second Agent' })
283
- ];
284
-
285
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
286
- success: true,
287
- data: agents
288
- });
289
-
290
- mockAdapter.reputationSystem.isAllowed.mockReturnValue(true);
291
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 80 });
292
-
293
- mockAdapter.networkClient.delegateTask.mockResolvedValue({
294
- success: true,
295
- data: {}
296
- });
297
-
298
- await toolHandlers.handleDelegate({
299
- agent: '#1',
300
- task: 'Test task'
301
- }, mockContext);
302
-
303
- expect(mockAdapter.networkClient.delegateTask).toHaveBeenCalledWith(
304
- expect.objectContaining({ peerId: agents[0].peerId })
305
- );
306
- });
307
-
308
- it('应该支持 peerId 精确匹配', async () => {
309
- const agent = createMockAgent({ peerId: 'exact-peer-id-123' });
310
-
311
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
312
- success: true,
313
- data: [agent]
314
- });
315
-
316
- mockAdapter.reputationSystem.isAllowed.mockReturnValue(true);
317
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 80 });
318
-
319
- mockAdapter.networkClient.delegateTask.mockResolvedValue({
320
- success: true,
321
- data: {}
322
- });
323
-
324
- await toolHandlers.handleDelegate({
325
- agent: 'exact-peer-id-123',
326
- task: 'Test task'
327
- }, mockContext);
328
-
329
- expect(mockAdapter.networkClient.delegateTask).toHaveBeenCalledWith(
330
- expect.objectContaining({ peerId: 'exact-peer-id-123' })
331
- );
332
- });
333
-
334
- it('应该支持模糊匹配 Agent 名称', async () => {
335
- const agent = createMockAgent({ displayName: 'Code Helper Bot' });
336
-
337
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
338
- success: true,
339
- data: [agent]
340
- });
341
-
342
- mockAdapter.reputationSystem.isAllowed.mockReturnValue(true);
343
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 80 });
344
-
345
- mockAdapter.networkClient.delegateTask.mockResolvedValue({
346
- success: true,
347
- data: {}
348
- });
349
-
350
- await toolHandlers.handleDelegate({
351
- agent: 'helper',
352
- task: 'Test task'
353
- }, mockContext);
354
-
355
- expect(mockAdapter.networkClient.delegateTask).toHaveBeenCalled();
356
- });
357
-
358
- it('应该传递自定义超时时间', async () => {
359
- const agent = createMockAgent();
360
-
361
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
362
- success: true,
363
- data: [agent]
364
- });
365
-
366
- mockAdapter.reputationSystem.isAllowed.mockReturnValue(true);
367
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 80 });
368
-
369
- mockAdapter.networkClient.delegateTask.mockResolvedValue({
370
- success: true,
371
- data: {}
372
- });
373
-
374
- await toolHandlers.handleDelegate({
375
- agent: 'Test Agent',
376
- task: 'Test task',
377
- timeout: 120000
378
- }, mockContext);
379
-
380
- expect(mockAdapter.networkClient.delegateTask).toHaveBeenCalledWith(
381
- expect.objectContaining({ timeout: 120000 })
382
- );
383
- });
384
- });
385
-
386
- describe('handleBroadcast', () => {
387
- it('应该成功广播任务给所有具备能力的 Agents', async () => {
388
- const agents = [
389
- createMockAgent({ displayName: 'Agent A' }),
390
- createMockAgent({ displayName: 'Agent B' })
391
- ];
392
-
393
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
394
- success: true,
395
- data: agents
396
- });
397
-
398
- mockAdapter.networkClient.delegateTask
399
- .mockResolvedValueOnce({ success: true, data: { result: 'A done' } })
400
- .mockResolvedValueOnce({ success: true, data: { result: 'B done' } });
401
-
402
- const result = await toolHandlers.handleBroadcast({
403
- capability: 'code-generation',
404
- task: 'Write code'
405
- }, mockContext);
406
-
407
- expect(result.content).toContain('2/2 个成功响应');
408
- expect(mockAdapter.networkClient.delegateTask).toHaveBeenCalledTimes(2);
409
- });
410
-
411
- it('应该处理没有具备该能力的 Agents', async () => {
412
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
413
- success: true,
414
- data: []
415
- });
416
-
417
- const result = await toolHandlers.handleBroadcast({
418
- capability: 'non-existent-capability',
419
- task: 'Write code'
420
- }, mockContext);
421
-
422
- expect(result.content).toContain('未发现具备 "non-existent-capability" 能力的 Agents');
423
- });
424
-
425
- it('应该处理 discoverAgents 失败的情况', async () => {
426
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
427
- success: false,
428
- error: 'Discovery failed'
429
- });
430
-
431
- const result = await toolHandlers.handleBroadcast({
432
- capability: 'code-generation',
433
- task: 'Write code'
434
- }, mockContext);
435
-
436
- expect(result.content).toContain('未发现具备 "code-generation" 能力的 Agents');
437
- });
438
-
439
- it('应该处理部分成功的情况', async () => {
440
- const agents = [
441
- createMockAgent({ displayName: 'Agent A' }),
442
- createMockAgent({ displayName: 'Agent B' })
443
- ];
444
-
445
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
446
- success: true,
447
- data: agents
448
- });
449
-
450
- mockAdapter.networkClient.delegateTask
451
- .mockResolvedValueOnce({ success: true, data: {} })
452
- .mockResolvedValueOnce({ success: false, error: 'Failed' });
453
-
454
- const result = await toolHandlers.handleBroadcast({
455
- capability: 'code-generation',
456
- task: 'Write code'
457
- }, mockContext);
458
-
459
- expect(result.content).toContain('1/2 个成功响应');
460
- });
461
-
462
- it('应该处理 min_responses 要求未满足的情况', async () => {
463
- const agents = [
464
- createMockAgent({ displayName: 'Agent A' }),
465
- createMockAgent({ displayName: 'Agent B' })
466
- ];
467
-
468
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
469
- success: true,
470
- data: agents
471
- });
472
-
473
- mockAdapter.networkClient.delegateTask
474
- .mockResolvedValueOnce({ success: false, error: 'Failed' })
475
- .mockResolvedValueOnce({ success: false, error: 'Failed' });
476
-
477
- const result = await toolHandlers.handleBroadcast({
478
- capability: 'code-generation',
479
- task: 'Write code',
480
- min_responses: 2
481
- }, mockContext);
482
-
483
- expect(result.content).toContain('仅 0 个成功响应(需要 2)');
484
- });
485
-
486
- it('应该处理委托异常的情况', async () => {
487
- const agents = [createMockAgent()];
488
-
489
- mockAdapter.networkClient.discoverAgents.mockResolvedValue({
490
- success: true,
491
- data: agents
492
- });
493
-
494
- mockAdapter.networkClient.delegateTask.mockRejectedValue(new Error('Network error'));
495
-
496
- const result = await toolHandlers.handleBroadcast({
497
- capability: 'code-generation',
498
- task: 'Write code'
499
- }, mockContext);
500
-
501
- expect(result.content).toContain('仅 0 个成功响应');
502
- });
503
- });
504
-
505
- describe('handleStatus', () => {
506
- it('应该返回完整的网络状态', async () => {
507
- mockAdapter.nodeManager.getStatus.mockResolvedValue({
508
- success: true,
509
- data: {
510
- running: true,
511
- peerId: 'local-peer-12345678901234567890',
512
- uptime: 3600
513
- }
514
- });
515
-
516
- mockAdapter.networkClient.getConnectedPeers.mockResolvedValue({
517
- success: true,
518
- data: [
519
- { peerId: 'peer-1', agentInfo: { displayName: 'Remote Agent' } }
520
- ]
521
- });
522
-
523
- mockAdapter.taskQueue.getStats.mockReturnValue({
524
- pending: 5,
525
- processing: 2,
526
- completed: 10,
527
- failed: 1,
528
- total: 18
529
- });
530
-
531
- mockAdapter.reputationSystem.getReputation.mockReturnValue({ score: 75 });
532
-
533
- const result = await toolHandlers.handleStatus({}, mockContext);
534
-
535
- expect(result.content).toContain('运行中');
536
- expect(result.content).toContain('60 分钟');
537
- expect(result.content).toContain('5 待处理');
538
- expect(result.data?.peers).toHaveLength(1);
539
- });
540
-
541
- it('应该处理获取状态失败的情况', async () => {
542
- mockAdapter.nodeManager.getStatus.mockResolvedValue({
543
- success: false,
544
- error: 'Node not running'
545
- });
546
-
547
- const result = await toolHandlers.handleStatus({}, mockContext);
548
-
549
- expect(result.content).toBe('❌ 获取状态失败: Node not running');
550
- });
551
-
552
- it('应该处理 getConnectedPeers 失败的情况', async () => {
553
- mockAdapter.nodeManager.getStatus.mockResolvedValue({
554
- success: true,
555
- data: { running: true, peerId: 'local-peer', uptime: 100 }
556
- });
557
-
558
- mockAdapter.networkClient.getConnectedPeers.mockResolvedValue({
559
- success: false,
560
- error: 'No peers'
561
- });
562
-
563
- mockAdapter.taskQueue.getStats.mockReturnValue({
564
- pending: 0,
565
- processing: 0,
566
- completed: 0,
567
- failed: 0,
568
- total: 0
569
- });
570
-
571
- const result = await toolHandlers.handleStatus({}, mockContext);
572
-
573
- expect(result.content).toContain('已连接 Peers: 0');
574
- });
575
-
576
- it('应该处理节点未运行的情况', async () => {
577
- mockAdapter.nodeManager.getStatus.mockResolvedValue({
578
- success: true,
579
- data: { running: false, peerId: null, uptime: 0 }
580
- });
581
-
582
- mockAdapter.networkClient.getConnectedPeers.mockResolvedValue({
583
- success: true,
584
- data: []
585
- });
586
-
587
- mockAdapter.taskQueue.getStats.mockReturnValue({
588
- pending: 0,
589
- processing: 0,
590
- completed: 0,
591
- failed: 0,
592
- total: 0
593
- });
594
-
595
- const result = await toolHandlers.handleStatus({}, mockContext);
596
-
597
- expect(result.content).toContain('已停止');
598
- });
599
- });
600
-
601
- describe('handleReputation', () => {
602
- describe('action: list', () => {
603
- it('应该返回所有信誉记录', async () => {
604
- mockAdapter.reputationSystem.getAllReputations.mockReturnValue([
605
- { peerId: 'peer-1-12345678901234567890', score: 80, successfulTasks: 10, failedTasks: 2 },
606
- { peerId: 'peer-2-12345678901234567890', score: 60, successfulTasks: 5, failedTasks: 3 }
607
- ]);
608
-
609
- const result = await toolHandlers.handleReputation({ action: 'list' }, mockContext);
610
-
611
- expect(result.content).toContain('信誉记录 (2 条)');
612
- expect(result.content).toContain('80');
613
- expect(result.content).toContain('60');
614
- });
615
-
616
- it('应该处理空信誉记录', async () => {
617
- mockAdapter.reputationSystem.getAllReputations.mockReturnValue([]);
618
-
619
- const result = await toolHandlers.handleReputation({ action: 'list' }, mockContext);
620
-
621
- expect(result.content).toContain('信誉记录 (0 条)');
622
- });
623
- });
624
-
625
- describe('action: view', () => {
626
- it('应该返回指定 Peer 的信誉详情', async () => {
627
- mockAdapter.reputationSystem.getReputation.mockReturnValue({
628
- score: 85,
629
- successfulTasks: 20,
630
- failedTasks: 3,
631
- avgResponseTime: 150,
632
- lastInteraction: Date.now()
633
- });
634
-
635
- const result = await toolHandlers.handleReputation({
636
- action: 'view',
637
- peer_id: 'peer-target-12345678901234567890'
638
- }, mockContext);
639
-
640
- expect(result.content).toContain('信誉分: 85');
641
- expect(result.content).toContain('成功任务: 20');
642
- expect(result.content).toContain('失败任务: 3');
643
- });
644
-
645
- it('应该要求提供 peer_id', async () => {
646
- const result = await toolHandlers.handleReputation({ action: 'view' }, mockContext);
647
-
648
- expect(result.content).toBe('❌ view/block/unblock 操作需要提供 peer_id 参数');
649
- });
650
- });
651
-
652
- describe('action: block', () => {
653
- it('应该成功屏蔽 Peer', async () => {
654
- const result = await toolHandlers.handleReputation({
655
- action: 'block',
656
- peer_id: 'peer-to-block-12345678901234567890'
657
- }, mockContext);
658
-
659
- expect(result.content).toContain('已屏蔽');
660
- expect(mockAdapter.config.security.blacklist).toContain('peer-to-block-12345678901234567890');
661
- });
662
-
663
- it('应该要求提供 peer_id', async () => {
664
- const result = await toolHandlers.handleReputation({ action: 'block' }, mockContext);
665
-
666
- expect(result.content).toBe('❌ view/block/unblock 操作需要提供 peer_id 参数');
667
- });
668
-
669
- it('应该在没有 security 配置时创建默认配置', async () => {
670
- mockAdapter.config.security = undefined;
671
-
672
- const result = await toolHandlers.handleReputation({
673
- action: 'block',
674
- peer_id: 'peer-to-block'
675
- }, mockContext);
676
-
677
- expect(mockAdapter.config.security).toBeDefined();
678
- expect(mockAdapter.config.security.blacklist).toContain('peer-to-block');
679
- });
680
- });
681
-
682
- describe('action: unblock', () => {
683
- it('应该成功解除屏蔽', async () => {
684
- mockAdapter.config.security.blacklist = ['peer-to-unblock', 'other-peer'];
685
-
686
- const result = await toolHandlers.handleReputation({
687
- action: 'unblock',
688
- peer_id: 'peer-to-unblock'
689
- }, mockContext);
690
-
691
- expect(result.content).toContain('已解除屏蔽');
692
- expect(mockAdapter.config.security.blacklist).not.toContain('peer-to-unblock');
693
- expect(mockAdapter.config.security.blacklist).toContain('other-peer');
694
- });
695
-
696
- it('应该要求提供 peer_id', async () => {
697
- const result = await toolHandlers.handleReputation({ action: 'unblock' }, mockContext);
698
-
699
- expect(result.content).toBe('❌ view/block/unblock 操作需要提供 peer_id 参数');
700
- });
701
- });
702
-
703
- describe('未知操作', () => {
704
- it('应该返回错误信息', async () => {
705
- const result = await toolHandlers.handleReputation({
706
- action: 'unknown-action'
707
- }, mockContext);
708
-
709
- expect(result.content).toBe('❌ action 参数必须是 list, view, block 或 unblock');
710
- });
711
- });
712
- });
713
-
714
- describe('handlePollTasks', () => {
715
- it('应该返回待处理任务并标记为 processing', async () => {
716
- const tasks = [
717
- createMockTask({ taskId: 'task-1', status: 'pending' }),
718
- createMockTask({ taskId: 'task-2', status: 'pending' })
719
- ];
720
-
721
- mockAdapter.taskQueue.getPending.mockReturnValue(tasks);
722
-
723
- const result = await toolHandlers.handlePollTasks({}, mockContext);
724
-
725
- expect(result.content).toContain('任务列表 (2 个)');
726
- expect(result.data?.count).toBe(2);
727
- expect(mockAdapter.taskQueue.markProcessing).toHaveBeenCalledTimes(2);
728
- });
729
-
730
- it('应该按状态过滤任务', async () => {
731
- const tasks = [
732
- createMockTask({ taskId: 'task-1', status: 'completed' })
733
- ];
734
-
735
- mockAdapter.taskQueue.getAll.mockReturnValue(tasks);
736
-
737
- const result = await toolHandlers.handlePollTasks({ status: 'completed' }, mockContext);
738
-
739
- expect(result.content).toContain('任务列表 (1 个)');
740
- expect(mockAdapter.taskQueue.getAll).toHaveBeenCalled();
741
- expect(mockAdapter.taskQueue.markProcessing).not.toHaveBeenCalled();
742
- });
743
-
744
- it('应该限制返回的任务数量', async () => {
745
- const tasks = Array.from({ length: 20 }, (_, i) =>
746
- createMockTask({ taskId: `task-${i}`, status: 'pending' })
747
- );
748
-
749
- mockAdapter.taskQueue.getPending.mockReturnValue(tasks.slice(0, 5));
750
-
751
- const result = await toolHandlers.handlePollTasks({ limit: 5 }, mockContext);
752
-
753
- expect(mockAdapter.taskQueue.getPending).toHaveBeenCalledWith(5);
754
- });
755
-
756
- it('应该处理没有任务的情况', async () => {
757
- mockAdapter.taskQueue.getPending.mockReturnValue([]);
758
-
759
- const result = await toolHandlers.handlePollTasks({}, mockContext);
760
-
761
- expect(result.content).toBe('📭 没有符合条件的任务');
762
- });
763
-
764
- it('应该正确显示不同状态的任务图标', async () => {
765
- const tasks = [
766
- createMockTask({ taskId: 'task-pending', status: 'pending' }),
767
- createMockTask({ taskId: 'task-processing', status: 'processing' }),
768
- createMockTask({ taskId: 'task-completed', status: 'completed' }),
769
- createMockTask({ taskId: 'task-failed', status: 'failed' })
770
- ];
771
-
772
- mockAdapter.taskQueue.getAll.mockReturnValue(tasks);
773
-
774
- const result = await toolHandlers.handlePollTasks({ status: 'pending' }, mockContext);
775
-
776
- expect(result.content).toContain('⏳');
777
- });
778
- });
779
-
780
- describe('handleSubmitResult', () => {
781
- it('应该成功提交任务结果', async () => {
782
- const task = createMockTask();
783
-
784
- mockAdapter.taskQueue.get.mockReturnValue(task);
785
- mockAdapter.networkClient.sendTaskResponse.mockResolvedValue({ success: true });
786
- mockAdapter.reputationSystem.recordSuccess.mockReturnValue(undefined);
787
-
788
- const result = await toolHandlers.handleSubmitResult({
789
- task_id: 'task-test-123',
790
- result: 'Task completed successfully',
791
- status: 'success'
792
- }, mockContext);
793
-
794
- expect(result.content).toContain('任务结果已提交并发送给原节点');
795
- expect(result.data?.sent).toBe(true);
796
- expect(mockAdapter.taskQueue.complete).toHaveBeenCalled();
797
- expect(mockAdapter.reputationSystem.recordSuccess).toHaveBeenCalled();
798
- });
799
-
800
- it('应该处理任务不存在的情况', async () => {
801
- mockAdapter.taskQueue.get.mockReturnValue(null);
802
-
803
- const result = await toolHandlers.handleSubmitResult({
804
- task_id: 'non-existent-task',
805
- result: 'Result',
806
- status: 'success'
807
- }, mockContext);
808
-
809
- expect(result.content).toContain('找不到任务');
810
- });
811
-
812
- it('应该处理发送响应失败的情况', async () => {
813
- const task = createMockTask();
814
-
815
- mockAdapter.taskQueue.get.mockReturnValue(task);
816
- mockAdapter.networkClient.sendTaskResponse.mockResolvedValue({
817
- success: false,
818
- error: 'Connection lost'
819
- });
820
-
821
- const result = await toolHandlers.handleSubmitResult({
822
- task_id: 'task-test-123',
823
- result: 'Result',
824
- status: 'success'
825
- }, mockContext);
826
-
827
- expect(result.content).toContain('结果已记录,但发送给原节点失败');
828
- expect(result.data?.sent).toBe(false);
829
- });
830
-
831
- it('应该正确处理错误状态的结果', async () => {
832
- const task = createMockTask();
833
-
834
- mockAdapter.taskQueue.get.mockReturnValue(task);
835
- mockAdapter.networkClient.sendTaskResponse.mockResolvedValue({ success: true });
836
- mockAdapter.reputationSystem.recordFailure.mockReturnValue(undefined);
837
-
838
- const result = await toolHandlers.handleSubmitResult({
839
- task_id: 'task-test-123',
840
- result: 'Task failed with error',
841
- status: 'error'
842
- }, mockContext);
843
-
844
- expect(result.content).toContain('任务结果已提交');
845
- expect(mockAdapter.reputationSystem.recordFailure).toHaveBeenCalled();
846
- });
847
-
848
- it('应该计算正确的响应延迟', async () => {
849
- const createdAt = Date.now() - 5000; // 5 秒前创建
850
- const task = createMockTask({ createdAt });
851
-
852
- mockAdapter.taskQueue.get.mockReturnValue(task);
853
- mockAdapter.networkClient.sendTaskResponse.mockResolvedValue({ success: true });
854
-
855
- const result = await toolHandlers.handleSubmitResult({
856
- task_id: 'task-test-123',
857
- result: 'Done',
858
- status: 'success'
859
- }, mockContext);
860
-
861
- expect(result.data?.latency).toBeGreaterThanOrEqual(5000);
862
- });
863
- });
864
-
865
- describe('handleTaskStats', () => {
866
- it('应该返回任务队列统计信息', async () => {
867
- mockAdapter.taskQueue.getStats.mockReturnValue({
868
- pending: 10,
869
- processing: 3,
870
- completed: 50,
871
- failed: 2,
872
- total: 65
873
- });
874
-
875
- const result = await toolHandlers.handleTaskStats({}, mockContext);
876
-
877
- expect(result.content).toContain('待处理: 10');
878
- expect(result.content).toContain('处理中: 3');
879
- expect(result.content).toContain('已完成: 50');
880
- expect(result.content).toContain('失败: 2');
881
- expect(result.content).toContain('总计: 65');
882
- expect(result.data).toEqual({
883
- pending: 10,
884
- processing: 3,
885
- completed: 50,
886
- failed: 2,
887
- total: 65
888
- });
889
- });
890
-
891
- it('应该处理空队列的情况', async () => {
892
- mockAdapter.taskQueue.getStats.mockReturnValue({
893
- pending: 0,
894
- processing: 0,
895
- completed: 0,
896
- failed: 0,
897
- total: 0
898
- });
899
-
900
- const result = await toolHandlers.handleTaskStats({}, mockContext);
901
-
902
- expect(result.content).toContain('待处理: 0');
903
- expect(result.content).toContain('总计: 0');
904
- });
905
- });
906
- });