@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.
- package/package.json +8 -1
- package/.github/workflows/ci.yml +0 -113
- package/.github/workflows/publish.yml +0 -60
- package/MONOREPO.md +0 -58
- package/SKILL.md +0 -137
- package/dist/adapters/openclaw.d.ts +0 -103
- package/dist/adapters/openclaw.d.ts.map +0 -1
- package/dist/adapters/openclaw.js +0 -297
- package/dist/adapters/openclaw.js.map +0 -1
- package/dist/core/connection-manager.d.ts +0 -80
- package/dist/core/connection-manager.d.ts.map +0 -1
- package/dist/core/connection-manager.js +0 -235
- package/dist/core/connection-manager.js.map +0 -1
- package/dist/core/connection-manager.test.d.ts +0 -2
- package/dist/core/connection-manager.test.d.ts.map +0 -1
- package/dist/core/connection-manager.test.js +0 -52
- package/dist/core/connection-manager.test.js.map +0 -1
- package/dist/core/identity.d.ts +0 -47
- package/dist/core/identity.d.ts.map +0 -1
- package/dist/core/identity.js +0 -130
- package/dist/core/identity.js.map +0 -1
- package/dist/core/identity.test.d.ts +0 -2
- package/dist/core/identity.test.d.ts.map +0 -1
- package/dist/core/identity.test.js +0 -43
- package/dist/core/identity.test.js.map +0 -1
- package/dist/core/serverless.d.ts +0 -155
- package/dist/core/serverless.d.ts.map +0 -1
- package/dist/core/serverless.js +0 -615
- package/dist/core/serverless.js.map +0 -1
- package/dist/daemon/webhook.test.d.ts +0 -2
- package/dist/daemon/webhook.test.d.ts.map +0 -1
- package/dist/daemon/webhook.test.js +0 -24
- package/dist/daemon/webhook.test.js.map +0 -1
- package/dist/protocol/messages.d.ts +0 -739
- package/dist/protocol/messages.d.ts.map +0 -1
- package/dist/protocol/messages.js +0 -188
- package/dist/protocol/messages.js.map +0 -1
- package/dist/protocol/messages.test.d.ts +0 -2
- package/dist/protocol/messages.test.d.ts.map +0 -1
- package/dist/protocol/messages.test.js +0 -55
- package/dist/protocol/messages.test.js.map +0 -1
- package/docs/F2A-PROTOCOL.md +0 -61
- package/docs/MOBILE_BOOTSTRAP_DESIGN.md +0 -126
- package/docs/a2a-lessons.md +0 -316
- package/docs/middleware-guide.md +0 -448
- package/docs/readme-update-checklist.md +0 -90
- package/docs/reputation-guide.md +0 -396
- package/docs/rfcs/001-reputation-system.md +0 -712
- package/docs/security-design.md +0 -247
- package/install.sh +0 -231
- package/packages/openclaw-adapter/README.md +0 -510
- package/packages/openclaw-adapter/openclaw.plugin.json +0 -106
- package/packages/openclaw-adapter/package.json +0 -40
- package/packages/openclaw-adapter/src/announcement-queue.test.ts +0 -449
- package/packages/openclaw-adapter/src/announcement-queue.ts +0 -403
- package/packages/openclaw-adapter/src/capability-detector.test.ts +0 -99
- package/packages/openclaw-adapter/src/capability-detector.ts +0 -183
- package/packages/openclaw-adapter/src/claim-handlers.test.ts +0 -974
- package/packages/openclaw-adapter/src/claim-handlers.ts +0 -482
- package/packages/openclaw-adapter/src/connector.business.test.ts +0 -583
- package/packages/openclaw-adapter/src/connector.ts +0 -795
- package/packages/openclaw-adapter/src/index.test.ts +0 -82
- package/packages/openclaw-adapter/src/index.ts +0 -18
- package/packages/openclaw-adapter/src/integration.e2e.test.ts +0 -829
- package/packages/openclaw-adapter/src/logger.ts +0 -51
- package/packages/openclaw-adapter/src/network-client.test.ts +0 -266
- package/packages/openclaw-adapter/src/network-client.ts +0 -251
- package/packages/openclaw-adapter/src/network-recovery.test.ts +0 -465
- package/packages/openclaw-adapter/src/node-manager.test.ts +0 -136
- package/packages/openclaw-adapter/src/node-manager.ts +0 -429
- package/packages/openclaw-adapter/src/plugin.test.ts +0 -439
- package/packages/openclaw-adapter/src/plugin.ts +0 -104
- package/packages/openclaw-adapter/src/reputation.test.ts +0 -221
- package/packages/openclaw-adapter/src/reputation.ts +0 -368
- package/packages/openclaw-adapter/src/task-guard.test.ts +0 -502
- package/packages/openclaw-adapter/src/task-guard.ts +0 -860
- package/packages/openclaw-adapter/src/task-queue.concurrency.test.ts +0 -462
- package/packages/openclaw-adapter/src/task-queue.edge-cases.test.ts +0 -284
- package/packages/openclaw-adapter/src/task-queue.persistence.test.ts +0 -408
- package/packages/openclaw-adapter/src/task-queue.ts +0 -668
- package/packages/openclaw-adapter/src/tool-handlers.test.ts +0 -906
- package/packages/openclaw-adapter/src/tool-handlers.ts +0 -574
- package/packages/openclaw-adapter/src/types.ts +0 -361
- package/packages/openclaw-adapter/src/webhook-pusher.test.ts +0 -188
- package/packages/openclaw-adapter/src/webhook-pusher.ts +0 -220
- package/packages/openclaw-adapter/src/webhook-server.test.ts +0 -580
- package/packages/openclaw-adapter/src/webhook-server.ts +0 -202
- package/packages/openclaw-adapter/tsconfig.json +0 -20
- package/src/cli/commands.test.ts +0 -157
- package/src/cli/commands.ts +0 -129
- package/src/cli/index.test.ts +0 -77
- package/src/cli/index.ts +0 -234
- package/src/core/autonomous-economy.test.ts +0 -291
- package/src/core/autonomous-economy.ts +0 -428
- package/src/core/e2ee-crypto.test.ts +0 -125
- package/src/core/e2ee-crypto.ts +0 -246
- package/src/core/f2a.test.ts +0 -269
- package/src/core/f2a.ts +0 -618
- package/src/core/p2p-network.test.ts +0 -199
- package/src/core/p2p-network.ts +0 -1432
- package/src/core/reputation-security.test.ts +0 -403
- package/src/core/reputation-security.ts +0 -562
- package/src/core/reputation.test.ts +0 -260
- package/src/core/reputation.ts +0 -576
- package/src/core/review-committee.test.ts +0 -380
- package/src/core/review-committee.ts +0 -401
- package/src/core/token-manager.test.ts +0 -133
- package/src/core/token-manager.ts +0 -140
- package/src/daemon/control-server.test.ts +0 -216
- package/src/daemon/control-server.ts +0 -292
- package/src/daemon/index.test.ts +0 -85
- package/src/daemon/index.ts +0 -89
- package/src/daemon/main.ts +0 -44
- package/src/daemon/start.ts +0 -29
- package/src/daemon/webhook.test.ts +0 -68
- package/src/daemon/webhook.ts +0 -105
- package/src/index.test.ts +0 -436
- package/src/index.ts +0 -72
- package/src/types/index.test.ts +0 -87
- package/src/types/index.ts +0 -341
- package/src/types/result.ts +0 -68
- package/src/utils/benchmark.ts +0 -237
- package/src/utils/logger.ts +0 -331
- package/src/utils/middleware.ts +0 -229
- package/src/utils/rate-limiter.ts +0 -207
- package/src/utils/signature.ts +0 -136
- package/src/utils/validation.ts +0 -186
- package/tests/docker/Dockerfile.node +0 -23
- package/tests/docker/Dockerfile.runner +0 -18
- package/tests/docker/docker-compose.test.yml +0 -73
- package/tests/integration/message-passing.test.ts +0 -109
- package/tests/integration/multi-node.test.ts +0 -92
- package/tests/integration/p2p-connection.test.ts +0 -83
- package/tests/integration/test-config.ts +0 -32
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -26
package/src/core/e2ee-crypto.ts
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 端到端加密模块
|
|
3
|
-
* 使用 X25519 + AES-256-GCM 实现 Agent 间加密通信
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { x25519 } from '@noble/curves/ed25519.js';
|
|
7
|
-
import { randomBytes, createCipheriv, createDecipheriv, createHash, hkdfSync } from 'crypto';
|
|
8
|
-
import { Logger } from '../utils/logger.js';
|
|
9
|
-
|
|
10
|
-
// AES-256-GCM 参数
|
|
11
|
-
const AES_KEY_SIZE = 32; // 256 bits
|
|
12
|
-
const AES_IV_SIZE = 16; // 128 bits
|
|
13
|
-
const AES_TAG_SIZE = 16; // 128 bits
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 加密密钥对
|
|
17
|
-
*/
|
|
18
|
-
export interface EncryptionKeyPair {
|
|
19
|
-
publicKey: Uint8Array;
|
|
20
|
-
privateKey: Uint8Array;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 加密后的消息
|
|
25
|
-
*/
|
|
26
|
-
export interface EncryptedMessage {
|
|
27
|
-
/** 发送方公钥 (用于接收方识别身份) */
|
|
28
|
-
senderPublicKey: string;
|
|
29
|
-
/** nonce/IV */
|
|
30
|
-
iv: string;
|
|
31
|
-
/** 认证标签 */
|
|
32
|
-
authTag: string;
|
|
33
|
-
/** 加密后的密文 */
|
|
34
|
-
ciphertext: string;
|
|
35
|
-
/** 可选的附加认证数据 */
|
|
36
|
-
aad?: string;
|
|
37
|
-
/** 密钥派生使用的随机盐值(每次加密随机生成) */
|
|
38
|
-
salt: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 密钥管理器
|
|
43
|
-
*/
|
|
44
|
-
export class E2EECrypto {
|
|
45
|
-
private keyPair: EncryptionKeyPair | null = null;
|
|
46
|
-
private peerPublicKeys: Map<string, Uint8Array> = new Map();
|
|
47
|
-
private sharedSecrets: Map<string, Uint8Array> = new Map();
|
|
48
|
-
private logger: Logger;
|
|
49
|
-
|
|
50
|
-
constructor() {
|
|
51
|
-
this.logger = new Logger({ component: 'E2EE' });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 初始化密钥对
|
|
56
|
-
*/
|
|
57
|
-
async initialize(): Promise<void> {
|
|
58
|
-
// 生成 X25519 密钥对用于加密
|
|
59
|
-
const privateKey = x25519.utils.randomSecretKey();
|
|
60
|
-
const publicKey = x25519.getPublicKey(privateKey);
|
|
61
|
-
|
|
62
|
-
this.keyPair = { publicKey, privateKey };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 从已有密钥初始化
|
|
67
|
-
*/
|
|
68
|
-
initializeWithKeyPair(privateKey: Uint8Array, publicKey: Uint8Array): void {
|
|
69
|
-
this.keyPair = { privateKey, publicKey };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 获取公钥 (用于广播)
|
|
74
|
-
*/
|
|
75
|
-
getPublicKey(): string | null {
|
|
76
|
-
if (!this.keyPair) return null;
|
|
77
|
-
return Buffer.from(this.keyPair.publicKey).toString('base64');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* 注册对等方的公钥
|
|
82
|
-
*/
|
|
83
|
-
registerPeerPublicKey(peerId: string, publicKeyBase64: string): void {
|
|
84
|
-
try {
|
|
85
|
-
const publicKey = Buffer.from(publicKeyBase64, 'base64');
|
|
86
|
-
this.peerPublicKeys.set(peerId, publicKey);
|
|
87
|
-
|
|
88
|
-
// 预计算共享密钥
|
|
89
|
-
if (this.keyPair) {
|
|
90
|
-
const sharedSecret = x25519.getSharedSecret(this.keyPair.privateKey, publicKey);
|
|
91
|
-
this.sharedSecrets.set(peerId, sharedSecret);
|
|
92
|
-
}
|
|
93
|
-
} catch (error) {
|
|
94
|
-
this.logger.error('Failed to register public key', { peerId, error });
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* 检查是否可以对等方加密通信
|
|
100
|
-
*/
|
|
101
|
-
canEncryptTo(peerId: string): boolean {
|
|
102
|
-
return this.sharedSecrets.has(peerId);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* 加密消息
|
|
107
|
-
*/
|
|
108
|
-
encrypt(peerId: string, plaintext: string, aad?: string): EncryptedMessage | null {
|
|
109
|
-
if (!this.keyPair) {
|
|
110
|
-
this.logger.error('Not initialized');
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const sharedSecret = this.sharedSecrets.get(peerId);
|
|
115
|
-
if (!sharedSecret) {
|
|
116
|
-
this.logger.error('No shared secret for peer', { peerId });
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
// 生成随机盐值(每次加密使用不同的盐值,提高安全性)
|
|
122
|
-
const salt = randomBytes(16);
|
|
123
|
-
|
|
124
|
-
// 从共享密钥派生 AES 密钥
|
|
125
|
-
const aesKey = this.deriveAESKey(sharedSecret, salt);
|
|
126
|
-
|
|
127
|
-
// 生成随机 IV
|
|
128
|
-
const iv = randomBytes(AES_IV_SIZE);
|
|
129
|
-
|
|
130
|
-
// 创建加密器
|
|
131
|
-
const cipher = createCipheriv('aes-256-gcm', aesKey, iv);
|
|
132
|
-
|
|
133
|
-
// 添加 AAD (如果有)
|
|
134
|
-
if (aad) {
|
|
135
|
-
cipher.setAAD(Buffer.from(aad, 'utf-8'));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 加密
|
|
139
|
-
let ciphertext = cipher.update(plaintext, 'utf-8', 'base64');
|
|
140
|
-
ciphertext += cipher.final('base64');
|
|
141
|
-
|
|
142
|
-
// 获取认证标签
|
|
143
|
-
const authTag = cipher.getAuthTag();
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
senderPublicKey: this.getPublicKey()!,
|
|
147
|
-
iv: iv.toString('base64'),
|
|
148
|
-
authTag: authTag.toString('base64'),
|
|
149
|
-
ciphertext,
|
|
150
|
-
aad,
|
|
151
|
-
salt: salt.toString('base64')
|
|
152
|
-
};
|
|
153
|
-
} catch (error) {
|
|
154
|
-
this.logger.error('Encryption failed', { error });
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* 解密消息
|
|
161
|
-
*/
|
|
162
|
-
decrypt(encrypted: EncryptedMessage): string | null {
|
|
163
|
-
if (!this.keyPair) {
|
|
164
|
-
this.logger.error('Not initialized');
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
// 使用发送方公钥计算共享密钥
|
|
170
|
-
const senderPublicKey = Buffer.from(encrypted.senderPublicKey, 'base64');
|
|
171
|
-
const sharedSecret = x25519.getSharedSecret(this.keyPair.privateKey, senderPublicKey);
|
|
172
|
-
|
|
173
|
-
// 使用消息中的盐值派生 AES 密钥(与加密方使用相同的盐值)
|
|
174
|
-
const salt = encrypted.salt ? Buffer.from(encrypted.salt, 'base64') : Buffer.from('F2A-E2EE-SALT-2024', 'utf-8');
|
|
175
|
-
const aesKey = this.deriveAESKey(sharedSecret, salt);
|
|
176
|
-
|
|
177
|
-
// 解码参数
|
|
178
|
-
const iv = Buffer.from(encrypted.iv, 'base64');
|
|
179
|
-
const authTag = Buffer.from(encrypted.authTag, 'base64');
|
|
180
|
-
|
|
181
|
-
// 创建解密器
|
|
182
|
-
const decipher = createDecipheriv('aes-256-gcm', aesKey, iv);
|
|
183
|
-
decipher.setAuthTag(authTag);
|
|
184
|
-
|
|
185
|
-
// 添加 AAD (如果有)
|
|
186
|
-
if (encrypted.aad) {
|
|
187
|
-
decipher.setAAD(Buffer.from(encrypted.aad, 'utf-8'));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// 解密
|
|
191
|
-
let plaintext = decipher.update(encrypted.ciphertext, 'base64', 'utf-8');
|
|
192
|
-
plaintext += decipher.final('utf-8');
|
|
193
|
-
|
|
194
|
-
return plaintext;
|
|
195
|
-
} catch (error) {
|
|
196
|
-
this.logger.error('Decryption failed', { error });
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* 从共享密钥派生 AES 密钥
|
|
203
|
-
* 使用 HKDF (HMAC-based Key Derivation Function) 进行安全密钥派生
|
|
204
|
-
* @param sharedSecret 共享密钥
|
|
205
|
-
* @param salt 随机盐值(每次加密使用不同的盐值,提高安全性)
|
|
206
|
-
*/
|
|
207
|
-
private deriveAESKey(sharedSecret: Uint8Array, salt: Buffer): Buffer {
|
|
208
|
-
// HKDF 参数
|
|
209
|
-
const info = Buffer.from('AES-256-GCM-KEY', 'utf-8'); // 密钥用途标识
|
|
210
|
-
|
|
211
|
-
// 使用 HKDF-SHA256 进行密钥派生
|
|
212
|
-
// hkdfSync(digest, ikm, salt, info, keylen)
|
|
213
|
-
const derivedKey = hkdfSync('sha256', sharedSecret, salt, info, AES_KEY_SIZE);
|
|
214
|
-
|
|
215
|
-
return Buffer.from(derivedKey);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* 获取已注册的对等方公钥
|
|
220
|
-
*/
|
|
221
|
-
getPeerPublicKey(peerId: string): string | null {
|
|
222
|
-
const publicKey = this.peerPublicKeys.get(peerId);
|
|
223
|
-
return publicKey ? Buffer.from(publicKey).toString('base64') : null;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* 序列化密钥对用于存储
|
|
228
|
-
*/
|
|
229
|
-
exportKeyPair(): { publicKey: string; privateKey: string } | null {
|
|
230
|
-
if (!this.keyPair) return null;
|
|
231
|
-
return {
|
|
232
|
-
publicKey: Buffer.from(this.keyPair.publicKey).toString('base64'),
|
|
233
|
-
privateKey: Buffer.from(this.keyPair.privateKey).toString('base64')
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* 获取已注册的对等方数量
|
|
239
|
-
*/
|
|
240
|
-
getRegisteredPeerCount(): number {
|
|
241
|
-
return this.peerPublicKeys.size;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// 单例导出
|
|
246
|
-
export const defaultE2EECrypto = new E2EECrypto();
|
package/src/core/f2a.test.ts
DELETED
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { F2A } from './f2a.js';
|
|
3
|
-
import { AgentCapability, TaskDelegateOptions } from '../types/index.js';
|
|
4
|
-
|
|
5
|
-
// Mock P2PNetwork
|
|
6
|
-
vi.mock('./p2p-network', () => ({
|
|
7
|
-
P2PNetwork: vi.fn().mockImplementation(() => ({
|
|
8
|
-
start: vi.fn().mockResolvedValue({
|
|
9
|
-
success: true,
|
|
10
|
-
data: { peerId: 'test-peer-id', addresses: ['/ip4/127.0.0.1/tcp/9000'] }
|
|
11
|
-
}),
|
|
12
|
-
stop: vi.fn(),
|
|
13
|
-
discoverAgents: vi.fn().mockResolvedValue([]),
|
|
14
|
-
getConnectedPeers: vi.fn().mockReturnValue([]),
|
|
15
|
-
sendTaskRequest: vi.fn(),
|
|
16
|
-
sendTaskResponse: vi.fn().mockResolvedValue({ success: true }),
|
|
17
|
-
useMiddleware: vi.fn(),
|
|
18
|
-
removeMiddleware: vi.fn().mockReturnValue(true),
|
|
19
|
-
listMiddlewares: vi.fn().mockReturnValue(['test-middleware']),
|
|
20
|
-
findPeerViaDHT: vi.fn().mockResolvedValue({ success: false, error: { code: 'DHT_NOT_AVAILABLE', message: 'DHT not enabled' } }),
|
|
21
|
-
getDHTPeerCount: vi.fn().mockReturnValue(0),
|
|
22
|
-
isDHTEnabled: vi.fn().mockReturnValue(false),
|
|
23
|
-
on: vi.fn(),
|
|
24
|
-
getPeerId: vi.fn().mockReturnValue('test-peer-id')
|
|
25
|
-
}))
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
describe('F2A', () => {
|
|
29
|
-
let f2a: F2A;
|
|
30
|
-
|
|
31
|
-
beforeEach(async () => {
|
|
32
|
-
f2a = await F2A.create({
|
|
33
|
-
displayName: 'Test Agent',
|
|
34
|
-
agentType: 'openclaw',
|
|
35
|
-
network: {
|
|
36
|
-
listenPort: 0,
|
|
37
|
-
enableMDNS: false
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
afterEach(async () => {
|
|
43
|
-
await f2a.stop();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('create', () => {
|
|
47
|
-
it('should create F2A instance with default options', async () => {
|
|
48
|
-
const instance = await F2A.create();
|
|
49
|
-
expect(instance).toBeDefined();
|
|
50
|
-
expect(instance.agentInfo.agentType).toBe('openclaw');
|
|
51
|
-
await instance.stop();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should create F2A instance with custom options', async () => {
|
|
55
|
-
const instance = await F2A.create({
|
|
56
|
-
displayName: 'Custom Agent',
|
|
57
|
-
agentType: 'custom'
|
|
58
|
-
});
|
|
59
|
-
expect(instance.agentInfo.displayName).toBe('Custom Agent');
|
|
60
|
-
expect(instance.agentInfo.agentType).toBe('custom');
|
|
61
|
-
await instance.stop();
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
describe('start/stop', () => {
|
|
66
|
-
it('should start successfully', async () => {
|
|
67
|
-
const result = await f2a.start();
|
|
68
|
-
expect(result.success).toBe(true);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should not start twice', async () => {
|
|
72
|
-
await f2a.start();
|
|
73
|
-
const result = await f2a.start();
|
|
74
|
-
expect(result.success).toBe(false);
|
|
75
|
-
expect(result.error?.message || result.error).toContain('already running');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should handle stop before start', async () => {
|
|
79
|
-
const newF2a = await F2A.create();
|
|
80
|
-
await newF2a.stop(); // Should not throw
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should handle multiple stop calls', async () => {
|
|
84
|
-
await f2a.start();
|
|
85
|
-
await f2a.stop();
|
|
86
|
-
await f2a.stop(); // Should not throw
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('capability management', () => {
|
|
91
|
-
it('should register capability', () => {
|
|
92
|
-
const capability: AgentCapability = {
|
|
93
|
-
name: 'test-capability',
|
|
94
|
-
description: 'Test capability',
|
|
95
|
-
tools: ['tool1', 'tool2']
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
f2a.registerCapability(capability, async () => 'result');
|
|
99
|
-
|
|
100
|
-
const capabilities = f2a.getCapabilities();
|
|
101
|
-
expect(capabilities).toHaveLength(1);
|
|
102
|
-
expect(capabilities[0].name).toBe('test-capability');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should register multiple capabilities', () => {
|
|
106
|
-
f2a.registerCapability(
|
|
107
|
-
{ name: 'cap1', description: 'Cap 1', tools: [] },
|
|
108
|
-
async () => 'result1'
|
|
109
|
-
);
|
|
110
|
-
f2a.registerCapability(
|
|
111
|
-
{ name: 'cap2', description: 'Cap 2', tools: [] },
|
|
112
|
-
async () => 'result2'
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
const capabilities = f2a.getCapabilities();
|
|
116
|
-
expect(capabilities).toHaveLength(2);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should update capability when registering same name', () => {
|
|
120
|
-
f2a.registerCapability(
|
|
121
|
-
{ name: 'same-cap', description: 'Original', tools: [] },
|
|
122
|
-
async () => 'original'
|
|
123
|
-
);
|
|
124
|
-
f2a.registerCapability(
|
|
125
|
-
{ name: 'same-cap', description: 'Updated', tools: ['new-tool'] },
|
|
126
|
-
async () => 'updated'
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const capabilities = f2a.getCapabilities();
|
|
130
|
-
expect(capabilities).toHaveLength(1);
|
|
131
|
-
expect(capabilities[0].description).toBe('Updated');
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('events', () => {
|
|
136
|
-
it('should emit network:started event', async () => {
|
|
137
|
-
const eventPromise = new Promise((resolve) => {
|
|
138
|
-
f2a.on('network:started', (event) => {
|
|
139
|
-
resolve(event);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
await f2a.start();
|
|
144
|
-
const event = await eventPromise;
|
|
145
|
-
expect(event).toBeDefined();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('should emit network:stopped event', async () => {
|
|
149
|
-
await f2a.start();
|
|
150
|
-
|
|
151
|
-
const eventPromise = new Promise((resolve) => {
|
|
152
|
-
f2a.on('network:stopped', () => {
|
|
153
|
-
resolve(true);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
await f2a.stop();
|
|
158
|
-
await eventPromise;
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
describe('peer management', () => {
|
|
163
|
-
it('should return empty peers when not started', () => {
|
|
164
|
-
const peers = f2a.getConnectedPeers();
|
|
165
|
-
expect(peers).toEqual([]);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe('task delegation', () => {
|
|
170
|
-
it('should fail delegation when no agents found', async () => {
|
|
171
|
-
const options: TaskDelegateOptions = {
|
|
172
|
-
capability: 'non-existent-capability',
|
|
173
|
-
description: 'Test task'
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const result = await f2a.delegateTask(options);
|
|
177
|
-
expect(result.success).toBe(false);
|
|
178
|
-
expect(result.error?.message || result.error).toContain('No agent found');
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should delegate task with parallel option', async () => {
|
|
182
|
-
const options: TaskDelegateOptions = {
|
|
183
|
-
capability: 'test-cap',
|
|
184
|
-
description: 'Test task',
|
|
185
|
-
parallel: true,
|
|
186
|
-
minResponses: 1
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const result = await f2a.delegateTask(options);
|
|
190
|
-
expect(result.success).toBe(false); // No agents found
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe('sendTaskTo', () => {
|
|
195
|
-
it('should send task to specific peer', async () => {
|
|
196
|
-
await f2a.start();
|
|
197
|
-
// Mock returns undefined, just verify it doesn't throw
|
|
198
|
-
await f2a.sendTaskTo(
|
|
199
|
-
'peer-id',
|
|
200
|
-
'task-type',
|
|
201
|
-
'description',
|
|
202
|
-
{ param: 'value' }
|
|
203
|
-
);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe('respondToTask', () => {
|
|
208
|
-
it('should respond to task successfully', async () => {
|
|
209
|
-
await f2a.start();
|
|
210
|
-
const result = await f2a.respondToTask(
|
|
211
|
-
'peer-id',
|
|
212
|
-
'task-id',
|
|
213
|
-
'success',
|
|
214
|
-
{ data: 'result' }
|
|
215
|
-
);
|
|
216
|
-
expect(result.success).toBe(true);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it('should respond to task with error', async () => {
|
|
220
|
-
await f2a.start();
|
|
221
|
-
const result = await f2a.respondToTask(
|
|
222
|
-
'peer-id',
|
|
223
|
-
'task-id',
|
|
224
|
-
'error',
|
|
225
|
-
undefined,
|
|
226
|
-
'Error message'
|
|
227
|
-
);
|
|
228
|
-
expect(result.success).toBe(true);
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
describe('middleware and DHT facade', () => {
|
|
233
|
-
it('should proxy middleware methods to p2p network', () => {
|
|
234
|
-
const middleware = {
|
|
235
|
-
name: 'test-middleware',
|
|
236
|
-
priority: 10,
|
|
237
|
-
process: vi.fn().mockResolvedValue({ action: 'continue' })
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
f2a.useMiddleware(middleware as any);
|
|
241
|
-
expect(f2a.listMiddlewares()).toEqual(['test-middleware']);
|
|
242
|
-
expect(f2a.removeMiddleware('test-middleware')).toBe(true);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it('should proxy dht methods to p2p network', async () => {
|
|
246
|
-
const lookup = await f2a.findPeerViaDHT('peer-id');
|
|
247
|
-
expect(lookup.success).toBe(false);
|
|
248
|
-
expect(lookup.error?.code).toBe('DHT_NOT_AVAILABLE');
|
|
249
|
-
expect(f2a.getDHTPeerCount()).toBe(0);
|
|
250
|
-
expect(f2a.isDHTEnabled()).toBe(false);
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
describe('error handling', () => {
|
|
255
|
-
it('should emit error event', async () => {
|
|
256
|
-
const errorPromise = new Promise<Error>((resolve) => {
|
|
257
|
-
f2a.on('error', (error) => {
|
|
258
|
-
resolve(error);
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// Emit error through f2a directly
|
|
263
|
-
(f2a as any).emit('error', new Error('Test error'));
|
|
264
|
-
|
|
265
|
-
const error = await errorPromise;
|
|
266
|
-
expect(error.message).toBe('Test error');
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
});
|