@f2a/network 0.1.2 → 0.1.3

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 (136) hide show
  1. package/package.json +8 -1
  2. package/.github/workflows/ci.yml +0 -113
  3. package/.github/workflows/publish.yml +0 -60
  4. package/MONOREPO.md +0 -58
  5. package/SKILL.md +0 -137
  6. package/dist/adapters/openclaw.d.ts +0 -103
  7. package/dist/adapters/openclaw.d.ts.map +0 -1
  8. package/dist/adapters/openclaw.js +0 -297
  9. package/dist/adapters/openclaw.js.map +0 -1
  10. package/dist/core/connection-manager.d.ts +0 -80
  11. package/dist/core/connection-manager.d.ts.map +0 -1
  12. package/dist/core/connection-manager.js +0 -235
  13. package/dist/core/connection-manager.js.map +0 -1
  14. package/dist/core/connection-manager.test.d.ts +0 -2
  15. package/dist/core/connection-manager.test.d.ts.map +0 -1
  16. package/dist/core/connection-manager.test.js +0 -52
  17. package/dist/core/connection-manager.test.js.map +0 -1
  18. package/dist/core/identity.d.ts +0 -47
  19. package/dist/core/identity.d.ts.map +0 -1
  20. package/dist/core/identity.js +0 -130
  21. package/dist/core/identity.js.map +0 -1
  22. package/dist/core/identity.test.d.ts +0 -2
  23. package/dist/core/identity.test.d.ts.map +0 -1
  24. package/dist/core/identity.test.js +0 -43
  25. package/dist/core/identity.test.js.map +0 -1
  26. package/dist/core/serverless.d.ts +0 -155
  27. package/dist/core/serverless.d.ts.map +0 -1
  28. package/dist/core/serverless.js +0 -615
  29. package/dist/core/serverless.js.map +0 -1
  30. package/dist/daemon/webhook.test.d.ts +0 -2
  31. package/dist/daemon/webhook.test.d.ts.map +0 -1
  32. package/dist/daemon/webhook.test.js +0 -24
  33. package/dist/daemon/webhook.test.js.map +0 -1
  34. package/dist/protocol/messages.d.ts +0 -739
  35. package/dist/protocol/messages.d.ts.map +0 -1
  36. package/dist/protocol/messages.js +0 -188
  37. package/dist/protocol/messages.js.map +0 -1
  38. package/dist/protocol/messages.test.d.ts +0 -2
  39. package/dist/protocol/messages.test.d.ts.map +0 -1
  40. package/dist/protocol/messages.test.js +0 -55
  41. package/dist/protocol/messages.test.js.map +0 -1
  42. package/docs/F2A-PROTOCOL.md +0 -61
  43. package/docs/MOBILE_BOOTSTRAP_DESIGN.md +0 -126
  44. package/docs/a2a-lessons.md +0 -316
  45. package/docs/middleware-guide.md +0 -448
  46. package/docs/readme-update-checklist.md +0 -90
  47. package/docs/reputation-guide.md +0 -396
  48. package/docs/rfcs/001-reputation-system.md +0 -712
  49. package/docs/security-design.md +0 -247
  50. package/install.sh +0 -231
  51. package/packages/openclaw-adapter/README.md +0 -510
  52. package/packages/openclaw-adapter/openclaw.plugin.json +0 -106
  53. package/packages/openclaw-adapter/package.json +0 -40
  54. package/packages/openclaw-adapter/src/announcement-queue.test.ts +0 -449
  55. package/packages/openclaw-adapter/src/announcement-queue.ts +0 -403
  56. package/packages/openclaw-adapter/src/capability-detector.test.ts +0 -99
  57. package/packages/openclaw-adapter/src/capability-detector.ts +0 -183
  58. package/packages/openclaw-adapter/src/claim-handlers.test.ts +0 -974
  59. package/packages/openclaw-adapter/src/claim-handlers.ts +0 -482
  60. package/packages/openclaw-adapter/src/connector.business.test.ts +0 -583
  61. package/packages/openclaw-adapter/src/connector.ts +0 -795
  62. package/packages/openclaw-adapter/src/index.test.ts +0 -82
  63. package/packages/openclaw-adapter/src/index.ts +0 -18
  64. package/packages/openclaw-adapter/src/integration.e2e.test.ts +0 -829
  65. package/packages/openclaw-adapter/src/logger.ts +0 -51
  66. package/packages/openclaw-adapter/src/network-client.test.ts +0 -266
  67. package/packages/openclaw-adapter/src/network-client.ts +0 -251
  68. package/packages/openclaw-adapter/src/network-recovery.test.ts +0 -465
  69. package/packages/openclaw-adapter/src/node-manager.test.ts +0 -136
  70. package/packages/openclaw-adapter/src/node-manager.ts +0 -429
  71. package/packages/openclaw-adapter/src/plugin.test.ts +0 -439
  72. package/packages/openclaw-adapter/src/plugin.ts +0 -104
  73. package/packages/openclaw-adapter/src/reputation.test.ts +0 -221
  74. package/packages/openclaw-adapter/src/reputation.ts +0 -368
  75. package/packages/openclaw-adapter/src/task-guard.test.ts +0 -502
  76. package/packages/openclaw-adapter/src/task-guard.ts +0 -860
  77. package/packages/openclaw-adapter/src/task-queue.concurrency.test.ts +0 -462
  78. package/packages/openclaw-adapter/src/task-queue.edge-cases.test.ts +0 -284
  79. package/packages/openclaw-adapter/src/task-queue.persistence.test.ts +0 -408
  80. package/packages/openclaw-adapter/src/task-queue.ts +0 -668
  81. package/packages/openclaw-adapter/src/tool-handlers.test.ts +0 -906
  82. package/packages/openclaw-adapter/src/tool-handlers.ts +0 -574
  83. package/packages/openclaw-adapter/src/types.ts +0 -361
  84. package/packages/openclaw-adapter/src/webhook-pusher.test.ts +0 -188
  85. package/packages/openclaw-adapter/src/webhook-pusher.ts +0 -220
  86. package/packages/openclaw-adapter/src/webhook-server.test.ts +0 -580
  87. package/packages/openclaw-adapter/src/webhook-server.ts +0 -202
  88. package/packages/openclaw-adapter/tsconfig.json +0 -20
  89. package/src/cli/commands.test.ts +0 -157
  90. package/src/cli/commands.ts +0 -129
  91. package/src/cli/index.test.ts +0 -77
  92. package/src/cli/index.ts +0 -234
  93. package/src/core/autonomous-economy.test.ts +0 -291
  94. package/src/core/autonomous-economy.ts +0 -428
  95. package/src/core/e2ee-crypto.test.ts +0 -125
  96. package/src/core/e2ee-crypto.ts +0 -246
  97. package/src/core/f2a.test.ts +0 -269
  98. package/src/core/f2a.ts +0 -618
  99. package/src/core/p2p-network.test.ts +0 -199
  100. package/src/core/p2p-network.ts +0 -1432
  101. package/src/core/reputation-security.test.ts +0 -403
  102. package/src/core/reputation-security.ts +0 -562
  103. package/src/core/reputation.test.ts +0 -260
  104. package/src/core/reputation.ts +0 -576
  105. package/src/core/review-committee.test.ts +0 -380
  106. package/src/core/review-committee.ts +0 -401
  107. package/src/core/token-manager.test.ts +0 -133
  108. package/src/core/token-manager.ts +0 -140
  109. package/src/daemon/control-server.test.ts +0 -216
  110. package/src/daemon/control-server.ts +0 -292
  111. package/src/daemon/index.test.ts +0 -85
  112. package/src/daemon/index.ts +0 -89
  113. package/src/daemon/main.ts +0 -44
  114. package/src/daemon/start.ts +0 -29
  115. package/src/daemon/webhook.test.ts +0 -68
  116. package/src/daemon/webhook.ts +0 -105
  117. package/src/index.test.ts +0 -436
  118. package/src/index.ts +0 -72
  119. package/src/types/index.test.ts +0 -87
  120. package/src/types/index.ts +0 -341
  121. package/src/types/result.ts +0 -68
  122. package/src/utils/benchmark.ts +0 -237
  123. package/src/utils/logger.ts +0 -331
  124. package/src/utils/middleware.ts +0 -229
  125. package/src/utils/rate-limiter.ts +0 -207
  126. package/src/utils/signature.ts +0 -136
  127. package/src/utils/validation.ts +0 -186
  128. package/tests/docker/Dockerfile.node +0 -23
  129. package/tests/docker/Dockerfile.runner +0 -18
  130. package/tests/docker/docker-compose.test.yml +0 -73
  131. package/tests/integration/message-passing.test.ts +0 -109
  132. package/tests/integration/multi-node.test.ts +0 -92
  133. package/tests/integration/p2p-connection.test.ts +0 -83
  134. package/tests/integration/test-config.ts +0 -32
  135. package/tsconfig.json +0 -21
  136. package/vitest.config.ts +0 -26
@@ -1,216 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { ControlServer } from './control-server.js';
3
- import { TokenManager } from '../core/token-manager.js';
4
-
5
- // Track mock server instances
6
- let lastMockServer: any = null;
7
-
8
- const TEST_TOKEN = 'test-token-12345';
9
-
10
- vi.mock('http', () => ({
11
- createServer: vi.fn((handler) => {
12
- lastMockServer = {
13
- listen: vi.fn((port, callback) => {
14
- if (callback) callback();
15
- return { port };
16
- }),
17
- close: vi.fn((callback) => {
18
- if (callback) callback();
19
- }),
20
- on: vi.fn(),
21
- _handler: handler
22
- };
23
- return lastMockServer;
24
- })
25
- }));
26
-
27
- // Mock TokenManager
28
- vi.mock('../core/token-manager', () => ({
29
- TokenManager: vi.fn().mockImplementation(() => ({
30
- getToken: vi.fn().mockReturnValue(TEST_TOKEN),
31
- verifyToken: vi.fn((token) => token === TEST_TOKEN),
32
- getTokenPath: vi.fn().mockReturnValue('/mock/path'),
33
- logTokenUsage: vi.fn() // 添加审计日志方法
34
- }))
35
- }));
36
-
37
- describe('ControlServer', () => {
38
- let mockF2A: any;
39
- let server: ControlServer;
40
-
41
- beforeEach(() => {
42
- vi.clearAllMocks();
43
- lastMockServer = null;
44
-
45
- mockF2A = {
46
- peerId: 'test-peer-id',
47
- agentInfo: { displayName: 'Test Agent' },
48
- getConnectedPeers: vi.fn().mockReturnValue([
49
- { peerId: 'peer1', displayName: 'Peer 1' }
50
- ]),
51
- discoverAgents: vi.fn().mockResolvedValue([
52
- { peerId: 'agent1', displayName: 'Agent 1' }
53
- ])
54
- };
55
-
56
- server = new ControlServer(mockF2A, 9001);
57
- });
58
-
59
- afterEach(() => {
60
- server.stop();
61
- });
62
-
63
- describe('start/stop', () => {
64
- it('should start server on specified port', async () => {
65
- await server.start();
66
- expect(lastMockServer).not.toBeNull();
67
- expect(lastMockServer.listen).toHaveBeenCalledWith(9001, expect.any(Function));
68
- });
69
-
70
- it('should stop server gracefully', async () => {
71
- await server.start();
72
- server.stop();
73
- expect(lastMockServer.close).toHaveBeenCalled();
74
- });
75
- });
76
-
77
- describe('request handling', () => {
78
- const createMockReq = (method: string, body?: object, headers?: Record<string, string>) => ({
79
- method,
80
- headers: {
81
- 'x-f2a-token': TEST_TOKEN,
82
- ...headers
83
- },
84
- socket: { remoteAddress: '127.0.0.1' },
85
- on: vi.fn((event, callback) => {
86
- if (event === 'data' && body) {
87
- callback(Buffer.from(JSON.stringify(body)));
88
- }
89
- if (event === 'end') {
90
- callback();
91
- }
92
- })
93
- });
94
-
95
- const createMockRes = () => ({
96
- writeHead: vi.fn(),
97
- end: vi.fn(),
98
- setHeader: vi.fn()
99
- });
100
-
101
- it('should handle OPTIONS request for CORS', async () => {
102
- await server.start();
103
-
104
- const handler = lastMockServer._handler;
105
- const req = createMockReq('OPTIONS');
106
- const res = createMockRes();
107
-
108
- handler(req, res);
109
-
110
- expect(res.writeHead).toHaveBeenCalledWith(200);
111
- expect(res.end).toHaveBeenCalled();
112
- });
113
-
114
- it('should reject non-POST methods', async () => {
115
- await server.start();
116
-
117
- const handler = lastMockServer._handler;
118
- const req = createMockReq('GET');
119
- const res = createMockRes();
120
-
121
- handler(req, res);
122
-
123
- expect(res.writeHead).toHaveBeenCalledWith(405);
124
- });
125
-
126
- it('should handle status command', async () => {
127
- await server.start();
128
-
129
- const handler = lastMockServer._handler;
130
- const req = createMockReq('POST', { action: 'status' });
131
- const res = createMockRes();
132
-
133
- handler(req, res);
134
-
135
- await new Promise(resolve => setTimeout(resolve, 10));
136
-
137
- expect(res.writeHead).toHaveBeenCalledWith(200);
138
- const responseData = JSON.parse(res.end.mock.calls[0][0]);
139
- expect(responseData.success).toBe(true);
140
- expect(responseData.peerId).toBe('test-peer-id');
141
- });
142
-
143
- it('should handle peers command', async () => {
144
- await server.start();
145
-
146
- const handler = lastMockServer._handler;
147
- const req = createMockReq('POST', { action: 'peers' });
148
- const res = createMockRes();
149
-
150
- handler(req, res);
151
-
152
- await new Promise(resolve => setTimeout(resolve, 10));
153
-
154
- expect(res.writeHead).toHaveBeenCalledWith(200);
155
- const responseData = JSON.parse(res.end.mock.calls[0][0]);
156
- expect(responseData.success).toBe(true);
157
- expect(responseData.peers).toHaveLength(1);
158
- });
159
-
160
- it('should handle discover command', async () => {
161
- await server.start();
162
-
163
- const handler = lastMockServer._handler;
164
- const req = createMockReq('POST', { action: 'discover', capability: 'test' });
165
- const res = createMockRes();
166
-
167
- handler(req, res);
168
-
169
- await new Promise(resolve => setTimeout(resolve, 10));
170
-
171
- expect(res.writeHead).toHaveBeenCalledWith(200);
172
- const responseData = JSON.parse(res.end.mock.calls[0][0]);
173
- expect(responseData.success).toBe(true);
174
- });
175
-
176
- it('should handle unknown commands', async () => {
177
- await server.start();
178
-
179
- const handler = lastMockServer._handler;
180
- const req = createMockReq('POST', { action: 'unknown' });
181
- const res = createMockRes();
182
-
183
- handler(req, res);
184
-
185
- await new Promise(resolve => setTimeout(resolve, 10));
186
-
187
- expect(res.writeHead).toHaveBeenCalledWith(400);
188
- });
189
-
190
- it('should handle invalid JSON', async () => {
191
- await server.start();
192
-
193
- const handler = lastMockServer._handler;
194
- const req = {
195
- method: 'POST',
196
- headers: { 'x-f2a-token': TEST_TOKEN },
197
- socket: { remoteAddress: '127.0.0.1' },
198
- on: vi.fn((event, callback) => {
199
- if (event === 'data') {
200
- callback(Buffer.from('invalid json'));
201
- }
202
- if (event === 'end') {
203
- callback();
204
- }
205
- })
206
- };
207
- const res = createMockRes();
208
-
209
- handler(req, res);
210
-
211
- await new Promise(resolve => setTimeout(resolve, 10));
212
-
213
- expect(res.writeHead).toHaveBeenCalledWith(400);
214
- });
215
- });
216
- });
@@ -1,292 +0,0 @@
1
- /**
2
- * HTTP 控制服务器
3
- * 接收 CLI 命令 - P2P 版本
4
- */
5
-
6
- import { createServer, Server, IncomingMessage, ServerResponse } from 'http';
7
- import { F2A } from '../core/f2a.js';
8
- import { TokenManager } from '../core/token-manager.js';
9
- import { Logger } from '../utils/logger.js';
10
- import { RateLimiter } from '../utils/rate-limiter.js';
11
-
12
- export interface ControlServerOptions {
13
- port: number;
14
- token?: string;
15
- /** 允许的 CORS 来源列表,默认为 ['http://localhost'] */
16
- allowedOrigins?: string[];
17
- }
18
-
19
- /** 默认允许的 CORS 来源 */
20
- const DEFAULT_ALLOWED_ORIGINS = ['http://localhost'];
21
-
22
- export class ControlServer {
23
- private server?: Server;
24
- private f2a: F2A;
25
- private port: number;
26
- private tokenManager: TokenManager;
27
- private logger: Logger;
28
- private rateLimiter: RateLimiter;
29
- private allowedOrigins: string[];
30
-
31
- constructor(f2a: F2A, port: number, tokenManager?: TokenManager, options?: ControlServerOptions) {
32
- this.f2a = f2a;
33
- this.port = port;
34
- this.tokenManager = tokenManager || new TokenManager();
35
- this.logger = new Logger({ component: 'ControlServer' });
36
- // 速率限制: 每分钟最多 60 个请求
37
- this.rateLimiter = new RateLimiter({ maxRequests: 60, windowMs: 60000 });
38
- // CORS 配置:优先使用传入的 allowedOrigins,否则使用默认值
39
- this.allowedOrigins = options?.allowedOrigins ?? DEFAULT_ALLOWED_ORIGINS;
40
- }
41
-
42
- /**
43
- * 启动控制服务器
44
- */
45
- start(): Promise<void> {
46
- return new Promise((resolve, reject) => {
47
- this.server = createServer((req, res) => {
48
- this.handleRequest(req, res);
49
- });
50
-
51
- this.server.on('error', reject);
52
-
53
- this.server.listen(this.port, () => {
54
- this.logger.info('Listening', { port: this.port });
55
- resolve();
56
- });
57
- });
58
- }
59
-
60
- /**
61
- * 停止控制服务器
62
- */
63
- stop(): void {
64
- if (this.server) {
65
- this.server.close();
66
- this.server = undefined;
67
- }
68
- // 清理速率限制器资源
69
- this.rateLimiter.stop();
70
- this.logger.info('Stopped');
71
- }
72
-
73
- /**
74
- * 从 Authorization header 提取 Bearer token
75
- */
76
- private extractBearerToken(authHeader: string | undefined): string | undefined {
77
- if (!authHeader) return undefined;
78
- const match = authHeader.match(/^Bearer\s+(.+)$/i);
79
- return match ? match[1] : undefined;
80
- }
81
-
82
- /**
83
- * 处理请求
84
- */
85
- private handleRequest(req: IncomingMessage, res: ServerResponse): void {
86
- // 设置 CORS - 使用配置的允许来源
87
- const origin = req.headers.origin;
88
- const allowOrigin = origin && this.allowedOrigins.includes(origin)
89
- ? origin
90
- : this.allowedOrigins[0];
91
-
92
- res.setHeader('Access-Control-Allow-Origin', allowOrigin);
93
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
94
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-F2A-Token');
95
-
96
- if (req.method === 'OPTIONS') {
97
- res.writeHead(200);
98
- res.end();
99
- return;
100
- }
101
-
102
- // 健康检查端点 (不需要认证)
103
- if (req.method === 'GET' && req.url === '/health') {
104
- res.writeHead(200);
105
- res.end(JSON.stringify({ status: 'ok', peerId: this.f2a.peerId }));
106
- return;
107
- }
108
-
109
- // GET /status - 获取状态 (需要认证)
110
- if (req.method === 'GET' && req.url === '/status') {
111
- const clientIp = req.socket.remoteAddress || 'unknown';
112
- if (!this.rateLimiter.allowRequest(clientIp)) {
113
- res.writeHead(429);
114
- res.end(JSON.stringify({ success: false, error: 'Too many requests' }));
115
- return;
116
- }
117
- // 支持 X-F2A-Token 或 Authorization: Bearer xxx
118
- const token = req.headers['x-f2a-token'] as string | undefined
119
- || this.extractBearerToken(req.headers.authorization);
120
- if (!this.tokenManager.verifyToken(token)) {
121
- res.writeHead(401);
122
- res.end(JSON.stringify({ success: false, error: 'Unauthorized' }));
123
- return;
124
- }
125
- res.writeHead(200);
126
- res.end(JSON.stringify({
127
- success: true,
128
- peerId: this.f2a.peerId,
129
- multiaddrs: this.f2a.agentInfo.multiaddrs || []
130
- }));
131
- return;
132
- }
133
-
134
- // GET /peers - 获取已知的 Peers (需要认证)
135
- if (req.method === 'GET' && req.url === '/peers') {
136
- const clientIp = req.socket.remoteAddress || 'unknown';
137
- if (!this.rateLimiter.allowRequest(clientIp)) {
138
- res.writeHead(429);
139
- res.end(JSON.stringify({ success: false, error: 'Too many requests' }));
140
- return;
141
- }
142
- // 支持 X-F2A-Token 或 Authorization: Bearer xxx
143
- const token = req.headers['x-f2a-token'] as string | undefined
144
- || this.extractBearerToken(req.headers.authorization);
145
- if (!this.tokenManager.verifyToken(token)) {
146
- res.writeHead(401);
147
- res.end(JSON.stringify({ success: false, error: 'Unauthorized' }));
148
- return;
149
- }
150
- // 返回所有已知的节点(包括已断开但已发现的)
151
- const peers = this.f2a.getAllPeers();
152
- res.writeHead(200);
153
- res.end(JSON.stringify(peers));
154
- return;
155
- }
156
-
157
- if (req.method !== 'POST') {
158
- res.writeHead(405);
159
- res.end(JSON.stringify({
160
- success: false,
161
- error: 'Method not allowed',
162
- code: 'METHOD_NOT_ALLOWED'
163
- }));
164
- return;
165
- }
166
- const clientIp = req.socket.remoteAddress || 'unknown';
167
- if (!this.rateLimiter.allowRequest(clientIp)) {
168
- this.logger.warn('Rate limit exceeded', { clientIp });
169
- res.writeHead(429);
170
- res.end(JSON.stringify({
171
- success: false,
172
- error: 'Too many requests',
173
- code: 'RATE_LIMIT_EXCEEDED'
174
- }));
175
- return;
176
- }
177
-
178
- // 验证 Token
179
- const token = req.headers['x-f2a-token'] as string | undefined;
180
-
181
- if (!this.tokenManager.verifyToken(token)) {
182
- // 记录失败的验证尝试
183
- this.tokenManager.logTokenUsage({
184
- ip: clientIp,
185
- action: 'auth',
186
- success: false
187
- });
188
-
189
- this.logger.warn('Unauthorized request', { clientIp });
190
- res.writeHead(401);
191
- res.end(JSON.stringify({
192
- success: false,
193
- error: 'Unauthorized: Invalid or missing token',
194
- code: 'UNAUTHORIZED'
195
- }));
196
- return;
197
- }
198
-
199
- // 记录成功的验证
200
- this.tokenManager.logTokenUsage({
201
- ip: clientIp,
202
- action: 'auth',
203
- success: true
204
- });
205
-
206
- let body = '';
207
- req.on('data', chunk => body += chunk);
208
- req.on('end', () => {
209
- this.processCommand(body, res);
210
- });
211
- }
212
-
213
- /**
214
- * 处理命令
215
- */
216
- private processCommand(body: string, res: ServerResponse): void {
217
- try {
218
- const command = JSON.parse(body);
219
-
220
- switch (command.action) {
221
- case 'status':
222
- this.handleStatus(res);
223
- break;
224
- case 'peers':
225
- this.handlePeers(res);
226
- break;
227
- case 'discover':
228
- this.handleDiscover(command.capability, res);
229
- break;
230
- default:
231
- res.writeHead(400);
232
- res.end(JSON.stringify({
233
- success: false,
234
- error: 'Unknown action',
235
- code: 'UNKNOWN_ACTION'
236
- }));
237
- }
238
- } catch {
239
- res.writeHead(400);
240
- res.end(JSON.stringify({
241
- success: false,
242
- error: 'Invalid JSON',
243
- code: 'INVALID_JSON'
244
- }));
245
- }
246
- }
247
-
248
- /**
249
- * 获取状态
250
- */
251
- private handleStatus(res: ServerResponse): void {
252
- res.writeHead(200);
253
- res.end(JSON.stringify({
254
- success: true,
255
- peerId: this.f2a.peerId,
256
- agentInfo: this.f2a.agentInfo
257
- }));
258
- }
259
-
260
- /**
261
- * 获取已连接的 Peers
262
- */
263
- private handlePeers(res: ServerResponse): void {
264
- const peers = this.f2a.getConnectedPeers();
265
- res.writeHead(200);
266
- res.end(JSON.stringify({
267
- success: true,
268
- peers
269
- }));
270
- }
271
-
272
- /**
273
- * 发现 Agents
274
- */
275
- private async handleDiscover(capability: string | undefined, res: ServerResponse): Promise<void> {
276
- try {
277
- const agents = await this.f2a.discoverAgents(capability);
278
- res.writeHead(200);
279
- res.end(JSON.stringify({
280
- success: true,
281
- agents
282
- }));
283
- } catch (error) {
284
- res.writeHead(500);
285
- res.end(JSON.stringify({
286
- success: false,
287
- error: error instanceof Error ? error.message : String(error),
288
- code: 'DISCOVER_FAILED'
289
- }));
290
- }
291
- }
292
- }
@@ -1,85 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { F2ADaemon } from './index.js';
3
-
4
- // Mock dependencies
5
- vi.mock('../core/f2a', () => ({
6
- F2A: {
7
- create: vi.fn().mockResolvedValue({
8
- start: vi.fn().mockResolvedValue({ success: true }),
9
- stop: vi.fn(),
10
- peerId: 'test-peer-id'
11
- })
12
- }
13
- }));
14
-
15
- vi.mock('./control-server', () => ({
16
- ControlServer: vi.fn().mockImplementation(() => ({
17
- start: vi.fn().mockResolvedValue(undefined),
18
- stop: vi.fn()
19
- }))
20
- }));
21
-
22
- describe('F2ADaemon', () => {
23
- let daemon: F2ADaemon;
24
-
25
- beforeEach(() => {
26
- daemon = new F2ADaemon({
27
- displayName: 'Test Daemon'
28
- });
29
- });
30
-
31
- describe('lifecycle', () => {
32
- it('should create daemon with options', () => {
33
- expect(daemon).toBeDefined();
34
- expect(daemon.isRunning()).toBe(false);
35
- });
36
-
37
- it('should start successfully', async () => {
38
- await daemon.start();
39
- expect(daemon.isRunning()).toBe(true);
40
- });
41
-
42
- it('should not start twice', async () => {
43
- await daemon.start();
44
- expect(daemon.isRunning()).toBe(true);
45
-
46
- await expect(daemon.start()).rejects.toThrow('Daemon already running');
47
- });
48
-
49
- it('should stop gracefully', async () => {
50
- await daemon.start();
51
- await daemon.stop();
52
- expect(daemon.isRunning()).toBe(false);
53
- });
54
-
55
- it('should handle stop before start', async () => {
56
- await daemon.stop(); // Should not throw
57
- expect(daemon.isRunning()).toBe(false);
58
- });
59
- });
60
-
61
- describe('getters', () => {
62
- it('should return F2A instance', async () => {
63
- await daemon.start();
64
- const f2a = daemon.getF2A();
65
- expect(f2a).toBeDefined();
66
- });
67
-
68
- it('should return undefined when not running', () => {
69
- const f2a = daemon.getF2A();
70
- expect(f2a).toBeUndefined();
71
- });
72
- });
73
-
74
- describe('options', () => {
75
- it('should use default control port', () => {
76
- const defaultDaemon = new F2ADaemon();
77
- expect(defaultDaemon).toBeDefined();
78
- });
79
-
80
- it('should accept custom control port', () => {
81
- const customDaemon = new F2ADaemon({ controlPort: 8080 });
82
- expect(customDaemon).toBeDefined();
83
- });
84
- });
85
- });
@@ -1,89 +0,0 @@
1
- /**
2
- * F2A Daemon
3
- * 后台服务主入口 - P2P 版本
4
- */
5
-
6
- import { F2A } from '../core/f2a.js';
7
- import { ControlServer } from './control-server.js';
8
- import { F2AOptions, WebhookConfig } from '../types/index.js';
9
-
10
- export interface DaemonOptions extends F2AOptions {
11
- webhook?: WebhookConfig;
12
- controlPort?: number;
13
- }
14
-
15
- export class F2ADaemon {
16
- private options: DaemonOptions;
17
- private f2a?: F2A;
18
- private controlServer?: ControlServer;
19
- private running: boolean = false;
20
-
21
- constructor(options: DaemonOptions = {}) {
22
- this.options = {
23
- controlPort: 9001,
24
- ...options
25
- };
26
- }
27
-
28
- /**
29
- * 启动 Daemon
30
- */
31
- async start(): Promise<void> {
32
- if (this.running) {
33
- throw new Error('Daemon already running');
34
- }
35
-
36
- console.log('[Daemon] Starting F2A Daemon...');
37
-
38
- // 创建并启动 F2A
39
- this.f2a = await F2A.create(this.options);
40
- const result = await this.f2a.start();
41
-
42
- if (!result.success) {
43
- const errorData = (result as { error: unknown }).error;
44
- const errorMsg = typeof errorData === 'string'
45
- ? errorData
46
- : JSON.stringify(errorData);
47
- throw new Error(`Failed to start F2A: ${errorMsg}`);
48
- }
49
-
50
- // 启动控制服务器
51
- this.controlServer = new ControlServer(this.f2a, this.options.controlPort!);
52
- await this.controlServer.start();
53
-
54
- this.running = true;
55
- console.log(`[Daemon] F2A Daemon started with peerId: ${this.f2a.peerId.slice(0, 16)}...`);
56
- }
57
-
58
- /**
59
- * 停止 Daemon
60
- */
61
- async stop(): Promise<void> {
62
- if (!this.running) return;
63
-
64
- console.log('[Daemon] Stopping F2A Daemon...');
65
-
66
- await this.controlServer?.stop();
67
- await this.f2a?.stop();
68
-
69
- this.running = false;
70
- console.log('[Daemon] F2A Daemon stopped');
71
- }
72
-
73
- /**
74
- * 获取 F2A 实例
75
- */
76
- getF2A(): F2A | undefined {
77
- return this.f2a;
78
- }
79
-
80
- /**
81
- * 是否运行中
82
- */
83
- isRunning(): boolean {
84
- return this.running;
85
- }
86
- }
87
-
88
- // 默认导出
89
- export default F2ADaemon;