@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.
- package/README.md +278 -63
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +29 -2
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/config.d.ts +176 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +386 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/daemon.d.ts +54 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +572 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/cli/index.js +90 -16
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +13 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +352 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/core/e2ee-crypto.d.ts +127 -1
- package/dist/core/e2ee-crypto.d.ts.map +1 -1
- package/dist/core/e2ee-crypto.js +446 -12
- package/dist/core/e2ee-crypto.js.map +1 -1
- package/dist/core/f2a.d.ts +2 -1
- package/dist/core/f2a.d.ts.map +1 -1
- package/dist/core/f2a.js +6 -2
- package/dist/core/f2a.js.map +1 -1
- package/dist/core/identity/encrypted-key-store.d.ts +19 -0
- package/dist/core/identity/encrypted-key-store.d.ts.map +1 -0
- package/dist/core/identity/encrypted-key-store.js +72 -0
- package/dist/core/identity/encrypted-key-store.js.map +1 -0
- package/dist/core/identity/identity-manager.d.ts +133 -0
- package/dist/core/identity/identity-manager.d.ts.map +1 -0
- package/dist/core/identity/identity-manager.js +454 -0
- package/dist/core/identity/identity-manager.js.map +1 -0
- package/dist/core/identity/index.d.ts +8 -0
- package/dist/core/identity/index.d.ts.map +1 -0
- package/dist/core/identity/index.js +7 -0
- package/dist/core/identity/index.js.map +1 -0
- package/dist/core/identity/types.d.ts +70 -0
- package/dist/core/identity/types.d.ts.map +1 -0
- package/dist/core/identity/types.js +17 -0
- package/dist/core/identity/types.js.map +1 -0
- package/dist/core/p2p-network.d.ts +26 -0
- package/dist/core/p2p-network.d.ts.map +1 -1
- package/dist/core/p2p-network.js +434 -105
- package/dist/core/p2p-network.js.map +1 -1
- package/dist/core/reputation-security.d.ts +15 -0
- package/dist/core/reputation-security.d.ts.map +1 -1
- package/dist/core/reputation-security.js +73 -3
- package/dist/core/reputation-security.js.map +1 -1
- package/dist/core/reputation.d.ts +129 -4
- package/dist/core/reputation.d.ts.map +1 -1
- package/dist/core/reputation.js +294 -1
- package/dist/core/reputation.js.map +1 -1
- package/dist/core/review-committee.d.ts +2 -2
- package/dist/core/review-committee.d.ts.map +1 -1
- package/dist/core/review-committee.js +17 -0
- package/dist/core/review-committee.js.map +1 -1
- package/dist/daemon/control-server.d.ts.map +1 -1
- package/dist/daemon/control-server.js +44 -1
- package/dist/daemon/control-server.js.map +1 -1
- package/dist/daemon/webhook.d.ts +3 -0
- package/dist/daemon/webhook.d.ts.map +1 -1
- package/dist/daemon/webhook.js +318 -6
- package/dist/daemon/webhook.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/result.d.ts +1 -1
- package/dist/types/result.d.ts.map +1 -1
- package/dist/types/result.js.map +1 -1
- package/dist/utils/crypto-utils.d.ts +17 -0
- package/dist/utils/crypto-utils.d.ts.map +1 -0
- package/dist/utils/crypto-utils.js +28 -0
- package/dist/utils/crypto-utils.js.map +1 -0
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +9 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/rate-limiter.d.ts.map +1 -1
- package/dist/utils/rate-limiter.js +3 -1
- package/dist/utils/rate-limiter.js.map +1 -1
- package/dist/utils/signature.d.ts +47 -1
- package/dist/utils/signature.d.ts.map +1 -1
- package/dist/utils/signature.js +166 -11
- package/dist/utils/signature.js.map +1 -1
- package/package.json +9 -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/utils/validation.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* F2A 输入验证 Schema
|
|
3
|
-
* 使用 Zod 进行运行时类型验证
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
|
|
8
|
-
// ============================================================================
|
|
9
|
-
// 基础类型 Schema
|
|
10
|
-
// ============================================================================
|
|
11
|
-
|
|
12
|
-
export const LogLevelSchema = z.enum(['DEBUG', 'INFO', 'WARN', 'ERROR']);
|
|
13
|
-
|
|
14
|
-
export const SecurityLevelSchema = z.enum(['low', 'medium', 'high']);
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// Agent 能力 Schema
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
export const ParameterSchemaSchema = z.object({
|
|
21
|
-
type: z.enum(['string', 'number', 'boolean', 'object', 'array']),
|
|
22
|
-
required: z.boolean().optional(),
|
|
23
|
-
default: z.unknown().optional(),
|
|
24
|
-
description: z.string().optional()
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
export const AgentCapabilitySchema = z.object({
|
|
28
|
-
name: z.string().min(1).max(64).regex(/^[a-z0-9-]+$/),
|
|
29
|
-
description: z.string().min(1).max(256),
|
|
30
|
-
tools: z.array(z.string()).max(32),
|
|
31
|
-
parameters: z.record(ParameterSchemaSchema).optional()
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// 网络配置 Schema
|
|
36
|
-
// ============================================================================
|
|
37
|
-
|
|
38
|
-
export const P2PNetworkConfigSchema = z.object({
|
|
39
|
-
listenPort: z.number().int().min(0).max(65535).optional(),
|
|
40
|
-
listenAddresses: z.array(z.string()).optional(),
|
|
41
|
-
bootstrapPeers: z.array(z.string()).optional(),
|
|
42
|
-
enableMDNS: z.boolean().optional(),
|
|
43
|
-
enableDHT: z.boolean().optional(),
|
|
44
|
-
dhtServerMode: z.boolean().optional()
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
export const SecurityConfigSchema = z.object({
|
|
48
|
-
level: SecurityLevelSchema,
|
|
49
|
-
requireConfirmation: z.boolean(),
|
|
50
|
-
verifySignatures: z.boolean(),
|
|
51
|
-
whitelist: z.set(z.string()).optional(),
|
|
52
|
-
blacklist: z.set(z.string()).optional(),
|
|
53
|
-
rateLimit: z.object({
|
|
54
|
-
maxRequests: z.number().int().positive(),
|
|
55
|
-
windowMs: z.number().int().positive()
|
|
56
|
-
}).optional()
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
export const F2AOptionsSchema = z.object({
|
|
60
|
-
displayName: z.string().min(1).max(64).optional(),
|
|
61
|
-
agentType: z.enum(['openclaw', 'claude-code', 'codex', 'custom']).optional(),
|
|
62
|
-
network: P2PNetworkConfigSchema.optional(),
|
|
63
|
-
security: SecurityConfigSchema.optional(),
|
|
64
|
-
logLevel: LogLevelSchema.optional(),
|
|
65
|
-
dataDir: z.string().optional()
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// ============================================================================
|
|
69
|
-
// 任务委托 Schema
|
|
70
|
-
// ============================================================================
|
|
71
|
-
|
|
72
|
-
export const TaskDelegateOptionsSchema = z.object({
|
|
73
|
-
capability: z.string().min(1).max(64),
|
|
74
|
-
description: z.string().min(1).max(1024),
|
|
75
|
-
parameters: z.record(z.unknown()).optional(),
|
|
76
|
-
timeout: z.number().int().min(1000).max(300000).optional(), // 1s - 5min
|
|
77
|
-
parallel: z.boolean().optional(),
|
|
78
|
-
minResponses: z.number().int().min(1).max(10).optional()
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// ============================================================================
|
|
82
|
-
// 消息协议 Schema
|
|
83
|
-
// ============================================================================
|
|
84
|
-
|
|
85
|
-
export const F2AMessageTypeSchema = z.enum([
|
|
86
|
-
'DISCOVER',
|
|
87
|
-
'DISCOVER_RESP',
|
|
88
|
-
'CAPABILITY_QUERY',
|
|
89
|
-
'CAPABILITY_RESPONSE',
|
|
90
|
-
'TASK_REQUEST',
|
|
91
|
-
'TASK_RESPONSE',
|
|
92
|
-
'TASK_DELEGATE',
|
|
93
|
-
'PING',
|
|
94
|
-
'PONG'
|
|
95
|
-
]);
|
|
96
|
-
|
|
97
|
-
export const F2AMessageSchema = z.object({
|
|
98
|
-
id: z.string().uuid(),
|
|
99
|
-
type: F2AMessageTypeSchema,
|
|
100
|
-
from: z.string().min(1),
|
|
101
|
-
to: z.string().optional(),
|
|
102
|
-
timestamp: z.number().int().positive(),
|
|
103
|
-
ttl: z.number().int().positive().optional(),
|
|
104
|
-
payload: z.unknown()
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
export const TaskRequestPayloadSchema = z.object({
|
|
108
|
-
taskId: z.string().uuid(),
|
|
109
|
-
taskType: z.string().min(1).max(64),
|
|
110
|
-
description: z.string().min(1).max(1024),
|
|
111
|
-
parameters: z.record(z.unknown()).optional(),
|
|
112
|
-
timeout: z.number().int().min(1).max(300).optional() // seconds
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
export const TaskResponsePayloadSchema = z.object({
|
|
116
|
-
taskId: z.string().uuid(),
|
|
117
|
-
status: z.enum(['success', 'error', 'rejected', 'delegated']),
|
|
118
|
-
result: z.unknown().optional(),
|
|
119
|
-
error: z.string().max(1024).optional(),
|
|
120
|
-
delegatedTo: z.string().optional()
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// ============================================================================
|
|
124
|
-
// Webhook Schema
|
|
125
|
-
// ============================================================================
|
|
126
|
-
|
|
127
|
-
export const WebhookConfigSchema = z.object({
|
|
128
|
-
url: z.string().url(),
|
|
129
|
-
token: z.string().min(1),
|
|
130
|
-
timeout: z.number().int().positive().optional(),
|
|
131
|
-
retries: z.number().int().min(0).max(10).optional(),
|
|
132
|
-
retryDelay: z.number().int().positive().optional()
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// ============================================================================
|
|
136
|
-
// 验证函数
|
|
137
|
-
// ============================================================================
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* 验证 F2A 配置
|
|
141
|
-
*/
|
|
142
|
-
export function validateF2AOptions(options: unknown) {
|
|
143
|
-
return F2AOptionsSchema.safeParse(options);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* 验证任务委托选项
|
|
148
|
-
*/
|
|
149
|
-
export function validateTaskDelegateOptions(options: unknown) {
|
|
150
|
-
return TaskDelegateOptionsSchema.safeParse(options);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* 验证 Agent 能力
|
|
155
|
-
*/
|
|
156
|
-
export function validateAgentCapability(capability: unknown) {
|
|
157
|
-
return AgentCapabilitySchema.safeParse(capability);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* 验证 F2A 消息
|
|
162
|
-
*/
|
|
163
|
-
export function validateF2AMessage(message: unknown) {
|
|
164
|
-
return F2AMessageSchema.safeParse(message);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* 验证任务请求载荷
|
|
169
|
-
*/
|
|
170
|
-
export function validateTaskRequestPayload(payload: unknown) {
|
|
171
|
-
return TaskRequestPayloadSchema.safeParse(payload);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* 验证任务响应载荷
|
|
176
|
-
*/
|
|
177
|
-
export function validateTaskResponsePayload(payload: unknown) {
|
|
178
|
-
return TaskResponsePayloadSchema.safeParse(payload);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* 验证 Webhook 配置
|
|
183
|
-
*/
|
|
184
|
-
export function validateWebhookConfig(config: unknown) {
|
|
185
|
-
return WebhookConfigSchema.safeParse(config);
|
|
186
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# F2A Node Docker Image
|
|
2
|
-
# 用于在容器中运行 F2A 节点
|
|
3
|
-
|
|
4
|
-
FROM node:22-alpine
|
|
5
|
-
|
|
6
|
-
WORKDIR /app
|
|
7
|
-
|
|
8
|
-
# 复制所有文件(包括 tsconfig.json)
|
|
9
|
-
COPY . .
|
|
10
|
-
|
|
11
|
-
# 安装所有依赖(libp2p 是 ESM 模块,需要完整依赖)
|
|
12
|
-
RUN npm install --ignore-scripts
|
|
13
|
-
|
|
14
|
-
# 环境变量
|
|
15
|
-
ENV NODE_ENV=production
|
|
16
|
-
ENV F2A_CONTROL_PORT=9001
|
|
17
|
-
|
|
18
|
-
# 健康检查
|
|
19
|
-
HEALTHCHECK --interval=5s --timeout=3s --retries=10 \
|
|
20
|
-
CMD wget -q --spider http://localhost:9001/health || exit 1
|
|
21
|
-
|
|
22
|
-
# 启动命令
|
|
23
|
-
CMD ["node", "dist/daemon/main.js"]
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# F2A Test Runner Docker Image
|
|
2
|
-
# 用于运行集成测试
|
|
3
|
-
|
|
4
|
-
FROM node:22-alpine
|
|
5
|
-
|
|
6
|
-
WORKDIR /app
|
|
7
|
-
|
|
8
|
-
# 先复制所有文件(包括 tsconfig.json)
|
|
9
|
-
COPY . .
|
|
10
|
-
|
|
11
|
-
# 安装所有依赖(包括 devDependencies),忽略 prepare 脚本
|
|
12
|
-
RUN npm install --ignore-scripts
|
|
13
|
-
|
|
14
|
-
# 构建项目
|
|
15
|
-
RUN npm run build
|
|
16
|
-
|
|
17
|
-
# 运行集成测试
|
|
18
|
-
CMD ["npm", "run", "test:integration"]
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# F2A Integration Test Environment
|
|
2
|
-
# 使用方式:
|
|
3
|
-
# npm run test:docker # 默认 3 个节点
|
|
4
|
-
# npm run test:docker:10 # 10 个节点
|
|
5
|
-
|
|
6
|
-
services:
|
|
7
|
-
# 引导节点(稳定,其他节点连接它)
|
|
8
|
-
bootstrap:
|
|
9
|
-
build:
|
|
10
|
-
context: ../..
|
|
11
|
-
dockerfile: tests/docker/Dockerfile.node
|
|
12
|
-
networks:
|
|
13
|
-
f2a-test-net:
|
|
14
|
-
aliases:
|
|
15
|
-
- bootstrap.f2a.local
|
|
16
|
-
environment:
|
|
17
|
-
- F2A_CONTROL_PORT=9001
|
|
18
|
-
- F2A_P2P_PORT=9000
|
|
19
|
-
- F2A_CONTROL_TOKEN=${TEST_TOKEN:-test-token-integration}
|
|
20
|
-
- NODE_ENV=production
|
|
21
|
-
healthcheck:
|
|
22
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9001/health"]
|
|
23
|
-
interval: 5s
|
|
24
|
-
timeout: 3s
|
|
25
|
-
retries: 10
|
|
26
|
-
start_period: 10s
|
|
27
|
-
|
|
28
|
-
# 动态生成的节点(使用 --scale 参数调整数量)
|
|
29
|
-
node:
|
|
30
|
-
build:
|
|
31
|
-
context: ../..
|
|
32
|
-
dockerfile: tests/docker/Dockerfile.node
|
|
33
|
-
networks:
|
|
34
|
-
- f2a-test-net
|
|
35
|
-
environment:
|
|
36
|
-
- F2A_CONTROL_PORT=9001
|
|
37
|
-
- F2A_CONTROL_TOKEN=${TEST_TOKEN:-test-token-integration}
|
|
38
|
-
- NODE_ENV=production
|
|
39
|
-
- BOOTSTRAP_PEERS=/dns4/bootstrap.f2a.local/tcp/9000
|
|
40
|
-
depends_on:
|
|
41
|
-
bootstrap:
|
|
42
|
-
condition: service_healthy
|
|
43
|
-
healthcheck:
|
|
44
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9001/health"]
|
|
45
|
-
interval: 5s
|
|
46
|
-
timeout: 3s
|
|
47
|
-
retries: 10
|
|
48
|
-
start_period: 5s
|
|
49
|
-
# 注意:使用 docker compose up --scale node=N 来控制节点数量
|
|
50
|
-
|
|
51
|
-
# 测试运行器
|
|
52
|
-
test-runner:
|
|
53
|
-
build:
|
|
54
|
-
context: ../..
|
|
55
|
-
dockerfile: tests/docker/Dockerfile.runner
|
|
56
|
-
networks:
|
|
57
|
-
- f2a-test-net
|
|
58
|
-
environment:
|
|
59
|
-
- RUN_INTEGRATION_TESTS=true
|
|
60
|
-
- TEST_BOOTSTRAP_HTTP=http://bootstrap:9001
|
|
61
|
-
- TEST_NODE_COUNT=${NODE_COUNT:-3}
|
|
62
|
-
- TEST_TOKEN=${TEST_TOKEN:-test-token-integration}
|
|
63
|
-
depends_on:
|
|
64
|
-
bootstrap:
|
|
65
|
-
condition: service_healthy
|
|
66
|
-
node:
|
|
67
|
-
condition: service_healthy
|
|
68
|
-
volumes:
|
|
69
|
-
- ../../coverage:/app/coverage # 导出覆盖率报告
|
|
70
|
-
|
|
71
|
-
networks:
|
|
72
|
-
f2a-test-net:
|
|
73
|
-
driver: bridge
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 消息传递集成测试
|
|
3
|
-
* 测试节点之间的消息发送和接收
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, it, expect, beforeAll } from 'vitest';
|
|
7
|
-
import { getBootstrapHttp } from './test-config';
|
|
8
|
-
|
|
9
|
-
const shouldRun = process.env.RUN_INTEGRATION_TESTS === 'true';
|
|
10
|
-
|
|
11
|
-
describe.skipIf(!shouldRun)('消息传递集成测试', () => {
|
|
12
|
-
// 使用 HTTP URL 格式,而不是 libp2p 多地址
|
|
13
|
-
const bootstrapAddr = getBootstrapHttp();
|
|
14
|
-
const testToken = process.env.TEST_TOKEN || 'test-token-integration';
|
|
15
|
-
|
|
16
|
-
beforeAll(async () => {
|
|
17
|
-
// 等待所有节点就绪
|
|
18
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe('任务请求和响应', () => {
|
|
22
|
-
it('应该能发送任务请求并收到响应', async () => {
|
|
23
|
-
// 1. 发送任务请求
|
|
24
|
-
const taskPayload = {
|
|
25
|
-
capability: 'echo',
|
|
26
|
-
description: 'Echo test message',
|
|
27
|
-
parameters: {
|
|
28
|
-
message: 'Hello from integration test'
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const sendResponse = await fetch(`${bootstrapAddr}/task`, {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
headers: {
|
|
35
|
-
'Content-Type': 'application/json',
|
|
36
|
-
'Authorization': `Bearer ${testToken}`
|
|
37
|
-
},
|
|
38
|
-
body: JSON.stringify(taskPayload)
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// 如果节点没有注册 echo 能力,测试可能失败
|
|
42
|
-
// 这是预期的,因为这是基础测试
|
|
43
|
-
if (!sendResponse.ok) {
|
|
44
|
-
console.log('Task endpoint not available, skipping');
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const result = await sendResponse.json();
|
|
49
|
-
expect(result.taskId).toBeDefined();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('应该能广播消息到所有节点', async () => {
|
|
53
|
-
const broadcastPayload = {
|
|
54
|
-
type: 'DISCOVER',
|
|
55
|
-
payload: {
|
|
56
|
-
test: true
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const response = await fetch(`${bootstrapAddr}/broadcast`, {
|
|
61
|
-
method: 'POST',
|
|
62
|
-
headers: {
|
|
63
|
-
'Content-Type': 'application/json',
|
|
64
|
-
'Authorization': `Bearer ${testToken}`
|
|
65
|
-
},
|
|
66
|
-
body: JSON.stringify(broadcastPayload)
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// 广播可能不可用,取决于实现
|
|
70
|
-
if (response.ok) {
|
|
71
|
-
const result = await response.json();
|
|
72
|
-
expect(result.delivered).toBeGreaterThanOrEqual(0);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe('能力发现', () => {
|
|
78
|
-
it('应该能发现节点的注册能力', async () => {
|
|
79
|
-
const response = await fetch(`${bootstrapAddr}/capabilities`, {
|
|
80
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
if (response.ok) {
|
|
84
|
-
const capabilities = await response.json();
|
|
85
|
-
expect(Array.isArray(capabilities)).toBe(true);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('错误处理', () => {
|
|
91
|
-
it('应该拒绝无效的认证令牌', async () => {
|
|
92
|
-
const response = await fetch(`${bootstrapAddr}/status`, {
|
|
93
|
-
headers: { 'Authorization': 'Bearer invalid-token' }
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
expect(response.status).toBe(401);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('应该返回正确的错误信息', async () => {
|
|
100
|
-
const response = await fetch(`${bootstrapAddr}/invalid-endpoint`, {
|
|
101
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// 405 Method Not Allowed (GET not supported for unknown paths)
|
|
105
|
-
// or 404 Not Found - both are acceptable error responses
|
|
106
|
-
expect([404, 405]).toContain(response.status);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 多节点压力测试
|
|
3
|
-
* 测试多节点场景下的性能和稳定性
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, it, expect, beforeAll } from 'vitest';
|
|
7
|
-
import { getBootstrapHttp } from './test-config';
|
|
8
|
-
|
|
9
|
-
const shouldRun = process.env.RUN_INTEGRATION_TESTS === 'true';
|
|
10
|
-
|
|
11
|
-
describe.skipIf(!shouldRun)('多节点压力测试', () => {
|
|
12
|
-
// 使用 HTTP URL 格式,而不是 libp2p 多地址
|
|
13
|
-
const bootstrapAddr = getBootstrapHttp();
|
|
14
|
-
const nodeCount = parseInt(process.env.TEST_NODE_COUNT || '3');
|
|
15
|
-
const testToken = process.env.TEST_TOKEN || 'test-token-integration';
|
|
16
|
-
|
|
17
|
-
beforeAll(async () => {
|
|
18
|
-
// 等待所有节点就绪
|
|
19
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('并发请求', () => {
|
|
23
|
-
it('应该能同时处理多个请求', async () => {
|
|
24
|
-
const requestCount = 10;
|
|
25
|
-
const requests = [];
|
|
26
|
-
|
|
27
|
-
for (let i = 0; i < requestCount; i++) {
|
|
28
|
-
requests.push(
|
|
29
|
-
fetch(`${bootstrapAddr}/status`, {
|
|
30
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
31
|
-
}).then(r => ({ ok: r.ok, index: i }))
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const results = await Promise.all(requests);
|
|
36
|
-
const successCount = results.filter(r => r.ok).length;
|
|
37
|
-
|
|
38
|
-
expect(successCount).toBe(requestCount);
|
|
39
|
-
}, 30000); // 30 秒超时
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('节点负载', () => {
|
|
43
|
-
it('节点连接数应该在合理范围内', async () => {
|
|
44
|
-
// 等待节点完成发现
|
|
45
|
-
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
46
|
-
|
|
47
|
-
const response = await fetch(`${bootstrapAddr}/peers`, {
|
|
48
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const peers = await response.json();
|
|
52
|
-
|
|
53
|
-
// 打印诊断信息
|
|
54
|
-
console.log('Node count:', nodeCount, 'Connected peers:', peers.length);
|
|
55
|
-
|
|
56
|
-
// 放宽条件:至少有一个节点连接就算成功
|
|
57
|
-
expect(peers.length).toBeGreaterThanOrEqual(1);
|
|
58
|
-
}, 15000); // 增加测试超时到 15 秒
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('稳定性', () => {
|
|
62
|
-
it('连续请求应该都能成功', async () => {
|
|
63
|
-
const requestCount = 5;
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < requestCount; i++) {
|
|
66
|
-
const response = await fetch(`${bootstrapAddr}/health`, {
|
|
67
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
expect(response.ok).toBe(true);
|
|
71
|
-
|
|
72
|
-
// 短暂延迟
|
|
73
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('节点状态应该一致', async () => {
|
|
78
|
-
// 多次获取状态,验证一致性
|
|
79
|
-
const responses = await Promise.all([
|
|
80
|
-
fetch(`${bootstrapAddr}/status`, {
|
|
81
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
82
|
-
}).then(r => r.json()),
|
|
83
|
-
fetch(`${bootstrapAddr}/status`, {
|
|
84
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
85
|
-
}).then(r => r.json())
|
|
86
|
-
]);
|
|
87
|
-
|
|
88
|
-
// peerId 应该一致
|
|
89
|
-
expect(responses[0].peerId).toBe(responses[1].peerId);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* P2P 连接集成测试
|
|
3
|
-
* 测试节点之间的连接建立和发现
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, it, expect } from 'vitest';
|
|
7
|
-
import { getBootstrapHttp } from './test-config';
|
|
8
|
-
|
|
9
|
-
// 只在集成测试环境运行
|
|
10
|
-
const shouldRun = process.env.RUN_INTEGRATION_TESTS === 'true';
|
|
11
|
-
|
|
12
|
-
describe.skipIf(!shouldRun)('P2P 连接集成测试', () => {
|
|
13
|
-
// 使用 HTTP URL 格式,而不是 libp2p 多地址
|
|
14
|
-
const bootstrapAddr = getBootstrapHttp();
|
|
15
|
-
const nodeCount = parseInt(process.env.TEST_NODE_COUNT || '3');
|
|
16
|
-
const testToken = process.env.TEST_TOKEN || 'test-token-integration';
|
|
17
|
-
|
|
18
|
-
describe('节点启动和健康检查', () => {
|
|
19
|
-
it('引导节点应该健康运行', async () => {
|
|
20
|
-
const response = await fetch(`${bootstrapAddr}/health`, {
|
|
21
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
expect(response.ok).toBe(true);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('引导节点应该有正确的 Peer ID', async () => {
|
|
28
|
-
const response = await fetch(`${bootstrapAddr}/status`, {
|
|
29
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const status = await response.json();
|
|
33
|
-
expect(status.peerId).toBeDefined();
|
|
34
|
-
expect(status.peerId.length).toBeGreaterThan(10);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe('节点发现', () => {
|
|
39
|
-
it('引导节点应该发现所有节点', async () => {
|
|
40
|
-
// 等待节点完成发现
|
|
41
|
-
await new Promise(resolve => setTimeout(resolve, 10000)); // 增加等待时间到 10 秒
|
|
42
|
-
|
|
43
|
-
const response = await fetch(`${bootstrapAddr}/peers`, {
|
|
44
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const peers = await response.json();
|
|
48
|
-
|
|
49
|
-
// 打印诊断信息
|
|
50
|
-
console.log('Expected:', nodeCount, 'Got:', peers.length);
|
|
51
|
-
console.log('Peers:', peers.map((p: any) => p.peerId));
|
|
52
|
-
|
|
53
|
-
expect(peers.length).toBeGreaterThanOrEqual(1); // 至少有一个节点连接
|
|
54
|
-
}, 15000); // 增加测试超时到 15 秒
|
|
55
|
-
|
|
56
|
-
it('节点应该知道引导节点的地址', async () => {
|
|
57
|
-
// 这个测试验证节点是否正确配置了引导节点
|
|
58
|
-
const response = await fetch(`${bootstrapAddr}/status`, {
|
|
59
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const status = await response.json();
|
|
63
|
-
expect(status.multiaddrs).toBeDefined();
|
|
64
|
-
expect(status.multiaddrs.length).toBeGreaterThan(0);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe('网络拓扑', () => {
|
|
69
|
-
it('所有节点应该在同一个网络中', async () => {
|
|
70
|
-
const response = await fetch(`${bootstrapAddr}/peers`, {
|
|
71
|
-
headers: { 'Authorization': `Bearer ${testToken}` }
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const peers = await response.json();
|
|
75
|
-
|
|
76
|
-
// 验证所有节点都有有效的 Peer ID
|
|
77
|
-
for (const peer of peers) {
|
|
78
|
-
expect(peer.peerId).toBeDefined();
|
|
79
|
-
// connected 属性可能不在 AgentInfo 中,跳过检查
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const defaultBootstrapHttp = 'http://bootstrap:9001';
|
|
2
|
-
|
|
3
|
-
function normalizeBootstrapUrl(raw: string): string {
|
|
4
|
-
if (raw.startsWith('http://') || raw.startsWith('https://')) {
|
|
5
|
-
return raw;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
// 兼容 /dns4/bootstrap/tcp/9000 这样的多地址配置
|
|
9
|
-
const match = raw.match(/^\/dns4\/([^/]+)\/tcp\/(\d+)$/);
|
|
10
|
-
if (match) {
|
|
11
|
-
const [, host, port] = match;
|
|
12
|
-
const controlPort = port === '9000' ? '9001' : port;
|
|
13
|
-
return `http://${host}:${controlPort}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return raw;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function getBootstrapHttp(): string {
|
|
20
|
-
const explicitHttp = process.env.TEST_BOOTSTRAP_HTTP;
|
|
21
|
-
if (explicitHttp) {
|
|
22
|
-
return normalizeBootstrapUrl(explicitHttp);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const bootstrapAddr = process.env.TEST_BOOTSTRAP_ADDR;
|
|
26
|
-
if (bootstrapAddr) {
|
|
27
|
-
return normalizeBootstrapUrl(bootstrapAddr);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return defaultBootstrapHttp;
|
|
31
|
-
}
|
|
32
|
-
|
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"lib": ["ES2022"],
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"forceConsistentCasingInFileNames": true,
|
|
12
|
-
"resolveJsonModule": true,
|
|
13
|
-
"declaration": true,
|
|
14
|
-
"declarationMap": true,
|
|
15
|
-
"sourceMap": true,
|
|
16
|
-
"moduleResolution": "NodeNext",
|
|
17
|
-
"allowSyntheticDefaultImports": true
|
|
18
|
-
},
|
|
19
|
-
"include": ["src/**/*.ts"],
|
|
20
|
-
"exclude": ["node_modules", "src/**/*.test.ts", "src/**/*.spec.ts"]
|
|
21
|
-
}
|
package/vitest.config.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
globals: true,
|
|
6
|
-
environment: 'node',
|
|
7
|
-
coverage: {
|
|
8
|
-
reporter: ['text', 'json', 'html'],
|
|
9
|
-
exclude: [
|
|
10
|
-
'node_modules/',
|
|
11
|
-
'dist/',
|
|
12
|
-
'tests/',
|
|
13
|
-
'**/*.d.ts',
|
|
14
|
-
'src/utils/benchmark.ts',
|
|
15
|
-
'src/utils/middleware.ts',
|
|
16
|
-
'src/utils/signature.ts'
|
|
17
|
-
],
|
|
18
|
-
thresholds: {
|
|
19
|
-
statements: 60,
|
|
20
|
-
branches: 75,
|
|
21
|
-
functions: 65,
|
|
22
|
-
lines: 60
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
});
|