@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,829 +0,0 @@
1
- /**
2
- * F2A 端到端集成测试
3
- * 测试完整的业务流程,使用真实的 SQLite 和 HTTP 服务器
4
- */
5
-
6
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
- import { F2AOpenClawAdapter } from './connector.js';
8
- import { TaskQueue } from './task-queue.js';
9
- import { ReputationSystem } from './reputation.js';
10
- import { AnnouncementQueue } from './announcement-queue.js';
11
- import { WebhookServer, WebhookHandler } from './webhook-server.js';
12
- import { TaskGuard } from './task-guard.js';
13
- import { createServer, IncomingMessage, ServerResponse } from 'http';
14
- import { mkdirSync, rmSync, existsSync, writeFileSync } from 'fs';
15
- import { join } from 'path';
16
- import { randomUUID } from 'crypto';
17
-
18
- // 测试配置 - 使用随机后缀避免冲突
19
- let TEST_DATA_DIR = `/tmp/f2a-integration-test-${Date.now()}`;
20
-
21
- // 辅助函数:获取可用端口(确保端口真正可用)
22
- async function getAvailablePort(): Promise<number> {
23
- return new Promise((resolve, reject) => {
24
- const tempServer = createServer();
25
- tempServer.listen(0, () => {
26
- const address = tempServer.address();
27
- if (address && typeof address === 'object') {
28
- const port = address.port;
29
- tempServer.close(() => {
30
- // 等待端口完全释放
31
- setTimeout(() => resolve(port), 50);
32
- });
33
- } else {
34
- reject(new Error('Failed to get port'));
35
- }
36
- });
37
- });
38
- }
39
-
40
- // 辅助函数:等待端口释放
41
- async function waitForPortRelease(port: number, maxWait: number = 1000): Promise<void> {
42
- const startTime = Date.now();
43
- while (Date.now() - startTime < maxWait) {
44
- try {
45
- await new Promise<void>((resolve, reject) => {
46
- const req = require('http').request({
47
- hostname: 'localhost',
48
- port,
49
- path: '/',
50
- method: 'HEAD',
51
- timeout: 100,
52
- }, () => resolve());
53
- req.on('error', () => resolve());
54
- req.end();
55
- });
56
- await new Promise(r => setTimeout(r, 100));
57
- } catch {
58
- break;
59
- }
60
- }
61
- }
62
-
63
- // 辅助函数:发送 HTTP 请求
64
- async function sendRequest(options: {
65
- port: number;
66
- method?: string;
67
- path?: string;
68
- headers?: Record<string, string>;
69
- body?: unknown;
70
- }): Promise<{ status: number; body: unknown }> {
71
- return new Promise((resolve, reject) => {
72
- const req = {
73
- hostname: 'localhost',
74
- port: options.port,
75
- path: options.path || '/webhook',
76
- method: options.method || 'POST',
77
- headers: {
78
- 'Content-Type': 'application/json',
79
- ...options.headers,
80
- },
81
- };
82
-
83
- const httpReq = require('http').request(req, (res: any) => {
84
- let data = '';
85
- res.on('data', (chunk: Buffer) => (data += chunk));
86
- res.on('end', () => {
87
- try {
88
- const body = data ? JSON.parse(data) : {};
89
- resolve({ status: res.statusCode, body });
90
- } catch {
91
- resolve({ status: res.statusCode, body: data });
92
- }
93
- });
94
- });
95
-
96
- httpReq.on('error', reject);
97
- if (options.body) {
98
- httpReq.write(JSON.stringify(options.body));
99
- }
100
- httpReq.end();
101
- });
102
- }
103
-
104
- describe('F2A 端到端集成测试', () => {
105
- beforeEach(() => {
106
- // 每个测试使用独立的目录
107
- TEST_DATA_DIR = `/tmp/f2a-integration-test-${Date.now()}-${Math.random().toString(36).slice(2)}`;
108
- // 创建测试目录
109
- mkdirSync(TEST_DATA_DIR, { recursive: true });
110
- });
111
-
112
- afterEach(async () => {
113
- // 等待端口释放
114
- await new Promise(r => setTimeout(r, 100));
115
- // 清理测试目录
116
- try {
117
- rmSync(TEST_DATA_DIR, { recursive: true, force: true });
118
- } catch {
119
- // 忽略清理错误
120
- }
121
- });
122
-
123
- describe('任务委托完整流程', () => {
124
- it('应该完成从任务入队 → Webhook 推送 → 处理 → 提交结果的完整流程', async () => {
125
- const port = await getAvailablePort();
126
- const taskQueue = new TaskQueue({
127
- maxSize: 100,
128
- persistDir: TEST_DATA_DIR,
129
- persistEnabled: true,
130
- });
131
-
132
- // 记录收到的推送
133
- let receivedTask: any = null;
134
-
135
- // 创建模拟的 webhook 接收服务器
136
- const webhookReceiver = createServer((req: IncomingMessage, res: ServerResponse) => {
137
- let body = '';
138
- req.on('data', (chunk) => (body += chunk));
139
- req.on('end', () => {
140
- try {
141
- receivedTask = JSON.parse(body);
142
- res.writeHead(200, { 'Content-Type': 'application/json' });
143
- res.end(JSON.stringify({ success: true }));
144
- } catch {
145
- res.writeHead(500);
146
- res.end();
147
- }
148
- });
149
- });
150
-
151
- await new Promise<void>((resolve) => webhookReceiver.listen(port + 1, () => resolve()));
152
-
153
- try {
154
- // 等待服务器完全启动
155
- await new Promise(r => setTimeout(r, 50));
156
-
157
- // 1. 任务入队
158
- const task = taskQueue.add({
159
- taskId: 'e2e-task-1',
160
- taskType: 'code-generation',
161
- description: 'Write a hello world function',
162
- from: 'peer-requester',
163
- timestamp: Date.now(),
164
- timeout: 60000,
165
- });
166
-
167
- expect(task.status).toBe('pending');
168
- expect(task.taskId).toBe('e2e-task-1');
169
-
170
- // 2. 模拟 Webhook 推送
171
- const pendingTasks = taskQueue.getWebhookPending();
172
- expect(pendingTasks.length).toBe(1);
173
-
174
- // 模拟推送到 webhook 接收器
175
- const pushResult = await fetch(`http://localhost:${port + 1}/webhook`, {
176
- method: 'POST',
177
- headers: { 'Content-Type': 'application/json' },
178
- body: JSON.stringify(pendingTasks[0]),
179
- });
180
-
181
- expect(pushResult.ok).toBe(true);
182
-
183
- // 等待接收
184
- await new Promise((r) => setTimeout(r, 100));
185
- expect(receivedTask).not.toBeNull();
186
- expect(receivedTask.taskId).toBe('e2e-task-1');
187
-
188
- // 标记为已推送
189
- taskQueue.markWebhookPushed('e2e-task-1');
190
-
191
- // 3. 模拟处理任务
192
- taskQueue.markProcessing('e2e-task-1');
193
- const processingTask = taskQueue.get('e2e-task-1');
194
- expect(processingTask?.status).toBe('processing');
195
-
196
- // 4. 提交结果
197
- taskQueue.complete('e2e-task-1', {
198
- taskId: 'e2e-task-1',
199
- status: 'success',
200
- result: 'function hello() { console.log("Hello, World!"); }',
201
- latency: 1500,
202
- });
203
-
204
- const completedTask = taskQueue.get('e2e-task-1');
205
- expect(completedTask?.status).toBe('completed');
206
- expect(completedTask?.result).toContain('Hello, World!');
207
-
208
- // 验证统计
209
- const stats = taskQueue.getStats();
210
- expect(stats.completed).toBe(1);
211
- expect(stats.pending).toBe(0);
212
- } finally {
213
- taskQueue.close();
214
- await new Promise<void>((resolve) => webhookReceiver.close(() => resolve()));
215
- }
216
- });
217
-
218
- it('应该正确处理任务失败场景', async () => {
219
- const taskQueue = new TaskQueue({
220
- maxSize: 100,
221
- persistDir: TEST_DATA_DIR,
222
- persistEnabled: true,
223
- });
224
-
225
- try {
226
- // 添加任务
227
- taskQueue.add({
228
- taskId: 'failing-task',
229
- taskType: 'test',
230
- description: 'This task will fail',
231
- from: 'peer-1',
232
- timestamp: Date.now(),
233
- timeout: 30000,
234
- });
235
-
236
- // 标记处理中
237
- taskQueue.markProcessing('failing-task');
238
-
239
- // 提交失败结果
240
- taskQueue.complete('failing-task', {
241
- taskId: 'failing-task',
242
- status: 'error',
243
- error: 'Something went wrong',
244
- latency: 500,
245
- });
246
-
247
- const task = taskQueue.get('failing-task');
248
- expect(task?.status).toBe('failed');
249
- expect(task?.error).toBe('Something went wrong');
250
-
251
- const stats = taskQueue.getStats();
252
- expect(stats.failed).toBe(1);
253
- } finally {
254
- taskQueue.close();
255
- }
256
- });
257
- });
258
-
259
- describe('信誉系统端到端', () => {
260
- it('应该在任务成功后更新信誉并影响权限', async () => {
261
- const reputationSystem = new ReputationSystem(
262
- {
263
- enabled: true,
264
- initialScore: 50,
265
- minScoreForService: 20,
266
- decayRate: 0.01,
267
- },
268
- TEST_DATA_DIR
269
- );
270
-
271
- const peerId = 'peer-test-success';
272
-
273
- // 初始信誉检查
274
- let rep = reputationSystem.getReputation(peerId);
275
- expect(rep.score).toBe(50);
276
- expect(reputationSystem.isAllowed(peerId)).toBe(true);
277
-
278
- // 模拟任务成功
279
- reputationSystem.recordSuccess(peerId, 'task-1', 1000);
280
- reputationSystem.recordSuccess(peerId, 'task-2', 800);
281
-
282
- rep = reputationSystem.getReputation(peerId);
283
- expect(rep.score).toBeGreaterThan(50);
284
- expect(rep.successfulTasks).toBe(2);
285
- expect(rep.avgResponseTime).toBeGreaterThan(0);
286
-
287
- // 多次失败降低信誉
288
- for (let i = 0; i < 5; i++) {
289
- reputationSystem.recordFailure(peerId, `fail-task-${i}`, 'Error');
290
- }
291
-
292
- rep = reputationSystem.getReputation(peerId);
293
- // 50 + 10 + 10 - 20*5 = 50 + 20 - 100 = -30 -> 0
294
- expect(rep.score).toBe(0);
295
- expect(reputationSystem.isAllowed(peerId)).toBe(false);
296
-
297
- // 刷新确保持久化
298
- reputationSystem.flush();
299
- });
300
-
301
- it('应该在持久化后恢复信誉数据', async () => {
302
- const peerId = 'peer-persistence-test';
303
-
304
- // 创建并写入数据
305
- {
306
- const reputationSystem = new ReputationSystem(
307
- {
308
- enabled: true,
309
- initialScore: 50,
310
- minScoreForService: 20,
311
- decayRate: 0.01,
312
- },
313
- TEST_DATA_DIR
314
- );
315
-
316
- reputationSystem.recordSuccess(peerId, 'task-1', 500);
317
- reputationSystem.flush();
318
- }
319
-
320
- // 重新加载验证持久化
321
- {
322
- const reputationSystem = new ReputationSystem(
323
- {
324
- enabled: true,
325
- initialScore: 50,
326
- minScoreForService: 20,
327
- decayRate: 0.01,
328
- },
329
- TEST_DATA_DIR
330
- );
331
-
332
- const rep = reputationSystem.getReputation(peerId);
333
- expect(rep.score).toBeGreaterThan(50);
334
- expect(rep.successfulTasks).toBe(1);
335
- }
336
- });
337
- });
338
-
339
- describe('Webhook 推送流程', () => {
340
- it('应该正确处理 Webhook 推送失败后的重试', async () => {
341
- const port = await getAvailablePort();
342
- let requestCount = 0;
343
- let successAfterAttempts = 3;
344
-
345
- const webhookServer = createServer((req: IncomingMessage, res: ServerResponse) => {
346
- requestCount++;
347
- if (requestCount < successAfterAttempts) {
348
- // 前两次返回错误
349
- res.writeHead(503);
350
- res.end(JSON.stringify({ error: 'Service unavailable' }));
351
- } else {
352
- // 第三次成功
353
- res.writeHead(200, { 'Content-Type': 'application/json' });
354
- res.end(JSON.stringify({ success: true }));
355
- }
356
- });
357
-
358
- await new Promise<void>((resolve) => webhookServer.listen(port + 2, () => resolve()));
359
-
360
- try {
361
- const taskQueue = new TaskQueue({
362
- maxSize: 100,
363
- persistDir: TEST_DATA_DIR,
364
- persistEnabled: true,
365
- });
366
-
367
- taskQueue.add({
368
- taskId: 'retry-task',
369
- taskType: 'test',
370
- description: 'Test webhook retry',
371
- from: 'peer-1',
372
- timestamp: Date.now(),
373
- timeout: 30000,
374
- });
375
-
376
- // 模拟重试逻辑
377
- let lastSuccess = false;
378
- for (let attempt = 1; attempt <= 5; attempt++) {
379
- const result = await fetch(`http://localhost:${port + 2}/webhook`, {
380
- method: 'POST',
381
- headers: { 'Content-Type': 'application/json' },
382
- body: JSON.stringify({ taskId: 'retry-task' }),
383
- });
384
-
385
- if (result.ok) {
386
- lastSuccess = true;
387
- break;
388
- }
389
- }
390
-
391
- expect(lastSuccess).toBe(true);
392
- expect(requestCount).toBe(successAfterAttempts);
393
-
394
- taskQueue.close();
395
- } finally {
396
- await new Promise<void>((resolve) => webhookServer.close(() => resolve()));
397
- }
398
- });
399
- });
400
-
401
- describe('崩溃恢复流程', () => {
402
- it('应该在崩溃后恢复未处理的任务', async () => {
403
- // 第一次:创建任务并模拟崩溃(不正常关闭)
404
- {
405
- const taskQueue = new TaskQueue({
406
- maxSize: 100,
407
- persistDir: TEST_DATA_DIR,
408
- persistEnabled: true,
409
- });
410
-
411
- taskQueue.add({
412
- taskId: 'crash-task-1',
413
- taskType: 'important',
414
- description: 'Important task that should survive crash',
415
- from: 'peer-critical',
416
- timestamp: Date.now(),
417
- timeout: 60000,
418
- });
419
-
420
- taskQueue.add({
421
- taskId: 'crash-task-2',
422
- taskType: 'important',
423
- description: 'Another important task',
424
- from: 'peer-critical',
425
- timestamp: Date.now(),
426
- timeout: 60000,
427
- });
428
-
429
- // 标记一个为 processing
430
- taskQueue.markProcessing('crash-task-1');
431
-
432
- // 不调用 close(),模拟崩溃
433
- // 直接清空内存引用
434
- }
435
-
436
- // 第二次:重新打开,验证恢复
437
- {
438
- const taskQueue = new TaskQueue({
439
- maxSize: 100,
440
- persistDir: TEST_DATA_DIR,
441
- persistEnabled: true,
442
- });
443
-
444
- // 等待恢复
445
- await new Promise((r) => setTimeout(r, 100));
446
-
447
- const stats = taskQueue.getStats();
448
- // 两个任务都应该恢复为 pending(processing 会被重置)
449
- expect(stats.pending).toBe(2);
450
- expect(stats.processing).toBe(0);
451
-
452
- // 验证任务内容
453
- const task1 = taskQueue.get('crash-task-1');
454
- expect(task1?.description).toContain('Important task');
455
- expect(task1?.status).toBe('pending'); // processing 被重置为 pending
456
-
457
- const task2 = taskQueue.get('crash-task-2');
458
- expect(task2?.description).toContain('Another important task');
459
-
460
- taskQueue.close();
461
- }
462
- });
463
-
464
- it('应该正确处理数据库损坏场景', async () => {
465
- // 创建一个损坏的数据库文件
466
- const dbPath = join(TEST_DATA_DIR, 'task-queue.db');
467
- writeFileSync(dbPath, 'corrupted data that is not valid sqlite');
468
-
469
- // 应该能够重建数据库
470
- const taskQueue = new TaskQueue({
471
- maxSize: 100,
472
- persistDir: TEST_DATA_DIR,
473
- persistEnabled: true,
474
- });
475
-
476
- // 应该可以正常添加任务
477
- const task = taskQueue.add({
478
- taskId: 'after-corrupt',
479
- taskType: 'test',
480
- description: 'Task after corruption recovery',
481
- from: 'peer-1',
482
- timestamp: Date.now(),
483
- timeout: 30000,
484
- });
485
-
486
- expect(task.taskId).toBe('after-corrupt');
487
-
488
- taskQueue.close();
489
- });
490
- });
491
-
492
- describe('并发场景', () => {
493
- it('应该正确处理多个 Agent 同时委托任务', async () => {
494
- const taskQueue = new TaskQueue({
495
- maxSize: 1000,
496
- persistDir: TEST_DATA_DIR,
497
- persistEnabled: true,
498
- });
499
-
500
- try {
501
- // 模拟 10 个 Agent 同时委托任务
502
- const agentCount = 10;
503
- const tasksPerAgent = 5;
504
-
505
- const addPromises: Promise<void>[] = [];
506
-
507
- for (let agent = 0; agent < agentCount; agent++) {
508
- for (let task = 0; task < tasksPerAgent; task++) {
509
- addPromises.push(
510
- new Promise<void>((resolve) => {
511
- setImmediate(() => {
512
- try {
513
- taskQueue.add({
514
- taskId: `agent-${agent}-task-${task}`,
515
- taskType: 'concurrent-test',
516
- description: `Task from agent ${agent}`,
517
- from: `peer-agent-${agent}`,
518
- timestamp: Date.now(),
519
- timeout: 30000,
520
- });
521
- } catch {
522
- // 忽略错误
523
- }
524
- resolve();
525
- });
526
- })
527
- );
528
- }
529
- }
530
-
531
- await Promise.all(addPromises);
532
-
533
- const stats = taskQueue.getStats();
534
- expect(stats.pending).toBe(agentCount * tasksPerAgent);
535
- expect(stats.total).toBe(agentCount * tasksPerAgent);
536
-
537
- // 验证每个任务都能被正确获取
538
- for (let agent = 0; agent < agentCount; agent++) {
539
- for (let task = 0; task < tasksPerAgent; task++) {
540
- const t = taskQueue.get(`agent-${agent}-task-${task}`);
541
- expect(t).toBeDefined();
542
- expect(t?.from).toBe(`peer-agent-${agent}`);
543
- }
544
- }
545
- } finally {
546
- taskQueue.close();
547
- }
548
- });
549
-
550
- it('应该正确处理同一任务的并发操作', async () => {
551
- const taskQueue = new TaskQueue({
552
- maxSize: 100,
553
- persistDir: TEST_DATA_DIR,
554
- persistEnabled: true,
555
- });
556
-
557
- try {
558
- taskQueue.add({
559
- taskId: 'concurrent-ops-task',
560
- taskType: 'test',
561
- description: 'Task with concurrent operations',
562
- from: 'peer-1',
563
- timestamp: Date.now(),
564
- timeout: 30000,
565
- });
566
-
567
- // 并发执行多种操作
568
- const operations = [
569
- // 尝试标记为 processing
570
- () => taskQueue.markProcessing('concurrent-ops-task'),
571
- // 尝试读取
572
- () => taskQueue.get('concurrent-ops-task'),
573
- // 再次标记(幂等性测试)
574
- () => taskQueue.markProcessing('concurrent-ops-task'),
575
- ];
576
-
577
- const results = await Promise.all(operations.map((op) => Promise.resolve(op())));
578
-
579
- // 任务应该处于一致的状态
580
- const task = taskQueue.get('concurrent-ops-task');
581
- expect(task).toBeDefined();
582
- expect(['pending', 'processing']).toContain(task?.status);
583
- } finally {
584
- taskQueue.close();
585
- }
586
- });
587
- });
588
-
589
- describe('AnnouncementQueue 端到端', () => {
590
- it('应该完成从发布广播 → 认领 → 接受 → 委托的完整流程', async () => {
591
- const announcementQueue = new AnnouncementQueue({
592
- maxSize: 50,
593
- maxAgeMs: 30 * 60 * 1000,
594
- });
595
-
596
- try {
597
- // 1. 发布者创建任务广播
598
- const announcement = announcementQueue.create({
599
- taskType: 'code-review',
600
- description: 'Review pull request #123',
601
- requiredCapabilities: ['typescript', 'code-review'],
602
- estimatedComplexity: 5,
603
- reward: 100,
604
- timeout: 300000,
605
- from: 'peer-publisher',
606
- });
607
-
608
- expect(announcement.status).toBe('open');
609
- expect(announcement.announcementId).toMatch(/^ann-/);
610
-
611
- // 2. 多个 Agent 认领任务
612
- const claim1 = announcementQueue.submitClaim(announcement.announcementId, {
613
- claimant: 'peer-worker-1',
614
- claimantName: 'Worker Alpha',
615
- estimatedTime: 120000,
616
- confidence: 0.85,
617
- });
618
-
619
- const claim2 = announcementQueue.submitClaim(announcement.announcementId, {
620
- claimant: 'peer-worker-2',
621
- claimantName: 'Worker Beta',
622
- estimatedTime: 90000,
623
- confidence: 0.95,
624
- });
625
-
626
- expect(claim1).toBeDefined();
627
- expect(claim2).toBeDefined();
628
- expect(announcement.claims).toHaveLength(2);
629
-
630
- // 3. 发布者查看认领列表
631
- const myAnnouncements = announcementQueue.getMyAnnouncements('peer-publisher');
632
- expect(myAnnouncements).toHaveLength(1);
633
- expect(myAnnouncements[0].claims).toHaveLength(2);
634
-
635
- // 4. 发布者接受一个认领
636
- const acceptedClaim = announcementQueue.acceptClaim(
637
- announcement.announcementId,
638
- claim2!.claimId
639
- );
640
-
641
- expect(acceptedClaim?.status).toBe('accepted');
642
- expect(announcement.status).toBe('claimed');
643
-
644
- // 其他认领应该被拒绝
645
- const rejectedClaim = announcementQueue.get(announcement.announcementId)?.claims?.find(
646
- (c) => c.claimId === claim1?.claimId
647
- );
648
- expect(rejectedClaim?.status).toBe('rejected');
649
-
650
- // 5. 标记为已委托
651
- announcementQueue.markDelegated(announcement.announcementId);
652
- expect(announcement.status).toBe('delegated');
653
-
654
- // 验证统计
655
- const stats = announcementQueue.getStats();
656
- expect(stats.delegated).toBe(1);
657
- } finally {
658
- announcementQueue.clear();
659
- }
660
- });
661
-
662
- it('应该正确处理广播过期场景', async () => {
663
- const fastExpireQueue = new AnnouncementQueue({
664
- maxSize: 50,
665
- maxAgeMs: 100, // 100ms 过期
666
- });
667
-
668
- try {
669
- const announcement = fastExpireQueue.create({
670
- taskType: 'quick-task',
671
- description: 'This will expire soon',
672
- timeout: 100,
673
- from: 'peer-1',
674
- });
675
-
676
- expect(announcement.status).toBe('open');
677
-
678
- // 等待过期
679
- await new Promise((r) => setTimeout(r, 150));
680
-
681
- // 触发清理(通过创建新任务)
682
- fastExpireQueue.create({
683
- taskType: 'new-task',
684
- description: 'New task',
685
- timeout: 1000,
686
- from: 'peer-2',
687
- });
688
-
689
- const stats = fastExpireQueue.getStats();
690
- expect(stats.expired).toBe(1);
691
- } finally {
692
- fastExpireQueue.clear();
693
- }
694
- });
695
- });
696
-
697
- describe('TaskGuard 集成测试', () => {
698
- it('应该在完整流程中正确应用安全规则', async () => {
699
- const guard = new TaskGuard();
700
-
701
- // 正常任务应该通过
702
- const normalTask = {
703
- taskId: 'normal-task',
704
- taskType: 'code-generation',
705
- description: 'Write a function to sort an array',
706
- from: 'peer-1',
707
- timestamp: Date.now(),
708
- timeout: 30000,
709
- };
710
-
711
- let report = guard.check(normalTask);
712
- expect(report.passed).toBe(true);
713
-
714
- // 危险任务应该被阻止
715
- const dangerousTask = {
716
- taskId: 'dangerous-task',
717
- taskType: 'shell',
718
- description: 'rm -rf /',
719
- from: 'peer-2',
720
- timestamp: Date.now(),
721
- timeout: 30000,
722
- };
723
-
724
- report = guard.check(dangerousTask);
725
- expect(report.passed).toBe(false);
726
- expect(report.blocks.some((b) => b.ruleId === 'dangerous-keywords')).toBe(true);
727
-
728
- // 低信誉用户执行敏感操作应该被阻止
729
- const sensitiveTask = {
730
- taskId: 'sensitive-task',
731
- taskType: 'file-operation',
732
- description: 'Delete old logs',
733
- from: 'peer-3',
734
- timestamp: Date.now(),
735
- timeout: 30000,
736
- };
737
-
738
- report = guard.check(sensitiveTask, {
739
- requesterReputation: {
740
- peerId: 'peer-3',
741
- score: 10, // 非常低的信誉
742
- successfulTasks: 1,
743
- failedTasks: 20,
744
- totalTasks: 21,
745
- avgResponseTime: 5000,
746
- lastInteraction: Date.now(),
747
- history: [],
748
- },
749
- });
750
-
751
- // 检查是否因为低信誉而被阻止或警告
752
- // 注意:具体行为取决于 TaskGuard 的配置
753
- expect(report.results.some((r) => r.ruleId === 'reputation')).toBe(true);
754
- });
755
- });
756
-
757
- describe('WebhookServer 真实 HTTP 测试', () => {
758
- it('应该正确处理真实的 HTTP 请求', async () => {
759
- const port = await getAvailablePort();
760
-
761
- const handler: WebhookHandler = {
762
- onDiscover: async (payload) => ({
763
- capabilities: [{ name: 'test-capability', description: 'Test capability', parameters: {} }],
764
- reputation: 80,
765
- }),
766
- onDelegate: async (payload) => ({
767
- accepted: true,
768
- taskId: payload.taskId,
769
- }),
770
- onStatus: async () => ({
771
- status: 'available',
772
- load: 0.5,
773
- }),
774
- };
775
-
776
- const server = new WebhookServer(port, handler);
777
- await server.start();
778
-
779
- try {
780
- // 测试 discover
781
- const discoverRes = await sendRequest({
782
- port,
783
- body: {
784
- type: 'discover',
785
- payload: { query: {}, requester: 'test-peer' },
786
- timestamp: Date.now(),
787
- },
788
- });
789
- expect(discoverRes.status).toBe(200);
790
- expect((discoverRes.body as any).capabilities).toBeDefined();
791
-
792
- // 测试 delegate
793
- const delegateRes = await sendRequest({
794
- port,
795
- body: {
796
- type: 'delegate',
797
- payload: {
798
- taskId: 'http-test-task',
799
- taskType: 'test',
800
- description: 'Test via HTTP',
801
- from: 'test-peer',
802
- timestamp: Date.now(),
803
- timeout: 30000,
804
- },
805
- timestamp: Date.now(),
806
- },
807
- });
808
- expect(delegateRes.status).toBe(200);
809
- expect((delegateRes.body as any).accepted).toBe(true);
810
-
811
- // 测试 status
812
- const statusRes = await sendRequest({
813
- port,
814
- body: {
815
- type: 'status',
816
- payload: {},
817
- timestamp: Date.now(),
818
- },
819
- });
820
- expect(statusRes.status).toBe(200);
821
- expect((statusRes.body as any).status).toBe('available');
822
- } finally {
823
- await server.stop();
824
- // 等待端口释放
825
- await new Promise((r) => setTimeout(r, 50));
826
- }
827
- });
828
- });
829
- });