@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
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* F2A OpenClaw Connector - Core Types
|
|
3
|
-
*
|
|
4
|
-
* 统一类型定义入口文件
|
|
5
|
-
* - 基础类型定义在此文件
|
|
6
|
-
* - Result 类型从 src/types/result.ts 重新导出(统一错误处理模式)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// ============================================================================
|
|
10
|
-
// 统一 Result 类型(从核心库 re-export)
|
|
11
|
-
// ============================================================================
|
|
12
|
-
|
|
13
|
-
// 重新导出核心 Result 类型,确保整个项目使用统一的错误处理模式
|
|
14
|
-
export type { Result, F2AError, ErrorCode } from '../../../src/types/result.js';
|
|
15
|
-
export { success, failure, failureFromError, createError } from '../../../src/types/result.js';
|
|
16
|
-
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// OpenClaw 配置类型(扩展以支持插件配置访问)
|
|
19
|
-
// ============================================================================
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* OpenClaw 完整配置结构
|
|
23
|
-
*
|
|
24
|
-
* 注意:此接口定义了 OpenClaw 配置的已知结构。
|
|
25
|
-
* 实际配置可能包含更多字段,插件应使用可选链访问。
|
|
26
|
-
*/
|
|
27
|
-
export interface OpenClawConfig extends Record<string, unknown> {
|
|
28
|
-
/** 插件配置容器 */
|
|
29
|
-
plugins?: {
|
|
30
|
-
/** 插件条目映射 */
|
|
31
|
-
entries?: Record<string, { config?: Record<string, unknown> }>;
|
|
32
|
-
};
|
|
33
|
-
/** Agent 配置 */
|
|
34
|
-
agents?: {
|
|
35
|
-
/** 默认 Agent 配置 */
|
|
36
|
-
defaults?: {
|
|
37
|
-
/** 工作空间路径 */
|
|
38
|
-
workspace?: string;
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ============================================================================
|
|
44
|
-
// OpenClaw Plugin SDK Types
|
|
45
|
-
// ============================================================================
|
|
46
|
-
export interface OpenClawPlugin {
|
|
47
|
-
name: string;
|
|
48
|
-
version: string;
|
|
49
|
-
initialize(config: Record<string, unknown>): Promise<void>;
|
|
50
|
-
getTools(): Tool[];
|
|
51
|
-
shutdown?(): Promise<void>;
|
|
52
|
-
onEvent?(event: string, payload: unknown): Promise<void>;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// OpenClaw Plugin API (外部插件可用接口)
|
|
56
|
-
export interface OpenClawPluginApi {
|
|
57
|
-
id: string;
|
|
58
|
-
name: string;
|
|
59
|
-
version?: string;
|
|
60
|
-
description?: string;
|
|
61
|
-
source: string;
|
|
62
|
-
config: OpenClawConfig;
|
|
63
|
-
pluginConfig?: Record<string, unknown>;
|
|
64
|
-
runtime: {
|
|
65
|
-
version: string;
|
|
66
|
-
config: {
|
|
67
|
-
loadConfig: (path?: string) => Promise<Record<string, unknown>>;
|
|
68
|
-
writeConfigFile: (path: string, config: unknown) => Promise<void>;
|
|
69
|
-
};
|
|
70
|
-
system: {
|
|
71
|
-
enqueueSystemEvent: (event: string, payload?: unknown) => void;
|
|
72
|
-
requestHeartbeatNow: () => void;
|
|
73
|
-
runCommandWithTimeout: (command: string, timeoutMs: number) => Promise<{ stdout: string; stderr: string }>;
|
|
74
|
-
};
|
|
75
|
-
media: {
|
|
76
|
-
loadWebMedia: (url: string) => Promise<Buffer>;
|
|
77
|
-
detectMime: (data: Buffer) => string;
|
|
78
|
-
};
|
|
79
|
-
tts: {
|
|
80
|
-
textToSpeechTelephony: (options: { text: string; cfg: unknown }) => Promise<{ audio: Buffer; sampleRate: number }>;
|
|
81
|
-
};
|
|
82
|
-
stt: {
|
|
83
|
-
transcribeAudioFile: (options: { filePath: string; cfg: unknown; mime?: string }) => Promise<{ text?: string }>;
|
|
84
|
-
};
|
|
85
|
-
logging: {
|
|
86
|
-
shouldLogVerbose: () => boolean;
|
|
87
|
-
getChildLogger: (bindings?: Record<string, unknown>) => unknown;
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
logger: {
|
|
91
|
-
debug?: (message: string) => void;
|
|
92
|
-
info: (message: string) => void;
|
|
93
|
-
warn: (message: string) => void;
|
|
94
|
-
error: (message: string) => void;
|
|
95
|
-
};
|
|
96
|
-
registerTool?: (tool: unknown, opts?: { optional?: boolean }) => void;
|
|
97
|
-
registerService?: (service: { id: string; start: () => void | Promise<void>; stop?: () => void | Promise<void> }) => void;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export interface Tool {
|
|
101
|
-
name: string;
|
|
102
|
-
description: string;
|
|
103
|
-
parameters: Record<string, ParameterSchema>;
|
|
104
|
-
handler: (params: any, context: SessionContext) => Promise<ToolResult>;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export interface ParameterSchema {
|
|
108
|
-
type: string;
|
|
109
|
-
description: string;
|
|
110
|
-
required?: boolean;
|
|
111
|
-
enum?: string[];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface SessionContext {
|
|
115
|
-
sessionId: string;
|
|
116
|
-
workspace: string;
|
|
117
|
-
toJSON(): Record<string, unknown>;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export interface ToolResult {
|
|
121
|
-
content: string;
|
|
122
|
-
data?: unknown;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// F2A Network Types
|
|
126
|
-
export interface F2ANodeConfig {
|
|
127
|
-
nodePath: string;
|
|
128
|
-
controlPort: number;
|
|
129
|
-
controlToken: string;
|
|
130
|
-
p2pPort: number;
|
|
131
|
-
enableMDNS: boolean;
|
|
132
|
-
bootstrapPeers: string[];
|
|
133
|
-
/** 请求超时(毫秒),默认 30000 */
|
|
134
|
-
timeoutMs?: number;
|
|
135
|
-
/** 最大重试次数,默认 3 */
|
|
136
|
-
maxRetries?: number;
|
|
137
|
-
/** 重试基础延迟(毫秒),默认 1000 */
|
|
138
|
-
retryDelayMs?: number;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// F2A Plugin Configuration
|
|
142
|
-
export interface F2APluginConfig {
|
|
143
|
-
autoStart?: boolean;
|
|
144
|
-
webhookPort?: number;
|
|
145
|
-
webhookToken?: string;
|
|
146
|
-
agentName?: string;
|
|
147
|
-
capabilities?: string[];
|
|
148
|
-
f2aPath?: string;
|
|
149
|
-
controlPort?: number;
|
|
150
|
-
controlToken?: string;
|
|
151
|
-
p2pPort?: number;
|
|
152
|
-
enableMDNS?: boolean;
|
|
153
|
-
bootstrapPeers?: string[];
|
|
154
|
-
dataDir?: string;
|
|
155
|
-
maxQueuedTasks?: number;
|
|
156
|
-
/** 兜底轮询间隔(毫秒),默认 60 秒 */
|
|
157
|
-
pollInterval?: number;
|
|
158
|
-
/** P1 修复:processing 任务超时时间(毫秒),超过此时间将被重置为 pending,默认 5 分钟 */
|
|
159
|
-
processingTimeoutMs?: number;
|
|
160
|
-
/** Webhook 推送配置 */
|
|
161
|
-
webhookPush?: WebhookPushConfig;
|
|
162
|
-
reputation?: ReputationConfig;
|
|
163
|
-
security?: SecurityConfig;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export interface ReputationConfig {
|
|
167
|
-
enabled: boolean;
|
|
168
|
-
initialScore: number;
|
|
169
|
-
minScoreForService: number;
|
|
170
|
-
decayRate: number;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export interface SecurityConfig {
|
|
174
|
-
requireConfirmation: boolean;
|
|
175
|
-
whitelist: string[];
|
|
176
|
-
blacklist: string[];
|
|
177
|
-
maxTasksPerMinute: number;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export interface AgentInfo {
|
|
181
|
-
peerId: string;
|
|
182
|
-
displayName: string;
|
|
183
|
-
agentType: string;
|
|
184
|
-
version: string;
|
|
185
|
-
capabilities: AgentCapability[];
|
|
186
|
-
multiaddrs: string[];
|
|
187
|
-
lastSeen: number;
|
|
188
|
-
reputation?: number;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export interface AgentCapability {
|
|
192
|
-
name: string;
|
|
193
|
-
description: string;
|
|
194
|
-
tools?: string[];
|
|
195
|
-
parameters?: Record<string, ParameterSchema>;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export interface PeerInfo {
|
|
199
|
-
peerId: string;
|
|
200
|
-
agentInfo?: AgentInfo;
|
|
201
|
-
multiaddrs: string[];
|
|
202
|
-
connected: boolean;
|
|
203
|
-
reputation: number;
|
|
204
|
-
lastSeen: number;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Task Types
|
|
208
|
-
export interface TaskRequest {
|
|
209
|
-
taskId: string;
|
|
210
|
-
taskType: string;
|
|
211
|
-
description: string;
|
|
212
|
-
parameters?: Record<string, unknown>;
|
|
213
|
-
from: string;
|
|
214
|
-
timestamp: number;
|
|
215
|
-
timeout: number;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export interface TaskResponse {
|
|
219
|
-
taskId: string;
|
|
220
|
-
status: 'success' | 'error' | 'rejected' | 'timeout';
|
|
221
|
-
result?: unknown;
|
|
222
|
-
error?: string;
|
|
223
|
-
latency?: number;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export interface DelegateOptions {
|
|
227
|
-
peerId: string;
|
|
228
|
-
taskType: string;
|
|
229
|
-
description: string;
|
|
230
|
-
parameters?: Record<string, unknown>;
|
|
231
|
-
timeout?: number;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Webhook Types
|
|
235
|
-
export interface WebhookEvent {
|
|
236
|
-
type: 'discover' | 'delegate' | 'status' | 'reputation_update';
|
|
237
|
-
payload: unknown;
|
|
238
|
-
timestamp: number;
|
|
239
|
-
signature?: string;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export interface DiscoverWebhookPayload {
|
|
243
|
-
query: {
|
|
244
|
-
capability?: string;
|
|
245
|
-
minReputation?: number;
|
|
246
|
-
};
|
|
247
|
-
requester: string;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
export interface DelegateWebhookPayload extends TaskRequest {
|
|
251
|
-
// TaskRequest 本身已包含所有字段
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Result 类型已从核心库 re-export,见文件顶部
|
|
255
|
-
|
|
256
|
-
// Reputation Types
|
|
257
|
-
export interface ReputationEntry {
|
|
258
|
-
peerId: string;
|
|
259
|
-
score: number;
|
|
260
|
-
successfulTasks: number;
|
|
261
|
-
failedTasks: number;
|
|
262
|
-
totalTasks: number;
|
|
263
|
-
avgResponseTime: number;
|
|
264
|
-
lastInteraction: number;
|
|
265
|
-
history: ReputationEvent[];
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export interface ReputationEvent {
|
|
269
|
-
type: 'task_success' | 'task_failure' | 'task_rejected' | 'timeout' | 'malicious';
|
|
270
|
-
taskId?: string;
|
|
271
|
-
delta: number;
|
|
272
|
-
timestamp: number;
|
|
273
|
-
reason?: string;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Claim Types - 认领模式
|
|
277
|
-
export interface TaskAnnouncement {
|
|
278
|
-
announcementId: string;
|
|
279
|
-
taskType: string;
|
|
280
|
-
description: string;
|
|
281
|
-
requiredCapabilities?: string[];
|
|
282
|
-
estimatedComplexity?: number;
|
|
283
|
-
reward?: number;
|
|
284
|
-
timeout: number;
|
|
285
|
-
from: string;
|
|
286
|
-
timestamp: number;
|
|
287
|
-
status: 'open' | 'claimed' | 'delegated' | 'expired';
|
|
288
|
-
claims?: TaskClaim[];
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export interface TaskClaim {
|
|
292
|
-
claimId: string;
|
|
293
|
-
announcementId: string;
|
|
294
|
-
claimant: string;
|
|
295
|
-
claimantName?: string;
|
|
296
|
-
estimatedTime?: number;
|
|
297
|
-
confidence?: number;
|
|
298
|
-
timestamp: number;
|
|
299
|
-
status: 'pending' | 'accepted' | 'rejected';
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Webhook 推送配置
|
|
304
|
-
*
|
|
305
|
-
* 用于配置 F2A 向 OpenClaw 推送事件通知的参数。
|
|
306
|
-
* 当任务状态变化、收到认领请求等事件发生时,会通过此配置推送通知。
|
|
307
|
-
*
|
|
308
|
-
* @example
|
|
309
|
-
* ```typescript
|
|
310
|
-
* const config: WebhookPushConfig = {
|
|
311
|
-
* url: 'https://openclaw.example.com/webhook/f2a',
|
|
312
|
-
* token: 'secret-token-xxx',
|
|
313
|
-
* timeout: 5000,
|
|
314
|
-
* enabled: true
|
|
315
|
-
* };
|
|
316
|
-
* ```
|
|
317
|
-
*/
|
|
318
|
-
export interface WebhookPushConfig {
|
|
319
|
-
/** OpenClaw webhook URL,用于接收推送通知 */
|
|
320
|
-
url: string;
|
|
321
|
-
/** Webhook 认证 token,用于验证推送请求的合法性 */
|
|
322
|
-
token: string;
|
|
323
|
-
/** 推送超时时间(毫秒),默认 30000ms */
|
|
324
|
-
timeout?: number;
|
|
325
|
-
/** 是否启用 webhook 推送,默认 false */
|
|
326
|
-
enabled?: boolean;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* 认领 Webhook 载荷
|
|
331
|
-
*
|
|
332
|
-
* 当有 Agent 认领你发布的任务广播时,会通过 webhook 推送此载荷。
|
|
333
|
-
* 包含认领者的信息和认领详情,可用于自动或手动审核认领请求。
|
|
334
|
-
*
|
|
335
|
-
* @example
|
|
336
|
-
* ```typescript
|
|
337
|
-
* // Webhook 接收到的认领通知
|
|
338
|
-
* const payload: ClaimWebhookPayload = {
|
|
339
|
-
* announcementId: 'ann-abc123',
|
|
340
|
-
* claimId: 'claim-xyz789',
|
|
341
|
-
* claimant: 'f2a-peer-id-xxx',
|
|
342
|
-
* claimantName: 'MacBook-Pro',
|
|
343
|
-
* estimatedTime: 600000, // 预计 10 分钟完成
|
|
344
|
-
* confidence: 0.85 // 85% 信心完成
|
|
345
|
-
* };
|
|
346
|
-
* ```
|
|
347
|
-
*/
|
|
348
|
-
export interface ClaimWebhookPayload {
|
|
349
|
-
/** 任务广播 ID,对应 TaskAnnouncement.announcementId */
|
|
350
|
-
announcementId: string;
|
|
351
|
-
/** 认领 ID,唯一标识此次认领 */
|
|
352
|
-
claimId: string;
|
|
353
|
-
/** 认领者的 F2A Peer ID */
|
|
354
|
-
claimant: string;
|
|
355
|
-
/** 认领者的显示名称(可选) */
|
|
356
|
-
claimantName?: string;
|
|
357
|
-
/** 预估完成时间(毫秒,可选) */
|
|
358
|
-
estimatedTime?: number;
|
|
359
|
-
/** 完成任务的信心程度(0-1,可选) */
|
|
360
|
-
confidence?: number;
|
|
361
|
-
}
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebhookPusher 边界、竞态和幂等性测试
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
6
|
-
import { WebhookPusher, WebhookPushConfig } from './webhook-pusher.js';
|
|
7
|
-
import type { QueuedTask } from './task-queue.js';
|
|
8
|
-
|
|
9
|
-
// Mock fetch
|
|
10
|
-
const mockFetch = vi.fn();
|
|
11
|
-
global.fetch = mockFetch;
|
|
12
|
-
|
|
13
|
-
describe('WebhookPusher 边界问题', () => {
|
|
14
|
-
let pusher: WebhookPusher;
|
|
15
|
-
const defaultConfig: WebhookPushConfig = {
|
|
16
|
-
url: 'http://localhost:4200',
|
|
17
|
-
token: 'test-token',
|
|
18
|
-
timeout: 1000,
|
|
19
|
-
enabled: true
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
vi.clearAllMocks();
|
|
24
|
-
pusher = new WebhookPusher(defaultConfig);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('推送边界', () => {
|
|
28
|
-
it('应该在 disabled 时不推送', async () => {
|
|
29
|
-
const disabledPusher = new WebhookPusher({
|
|
30
|
-
...defaultConfig,
|
|
31
|
-
enabled: false
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const task: QueuedTask = {
|
|
35
|
-
taskId: 'task-1',
|
|
36
|
-
status: 'pending',
|
|
37
|
-
createdAt: Date.now()
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const result = await disabledPusher.pushTask(task);
|
|
41
|
-
|
|
42
|
-
expect(result.success).toBe(false);
|
|
43
|
-
expect(result.error).toBe('Webhook push disabled');
|
|
44
|
-
expect(mockFetch).not.toHaveBeenCalled();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('应该在 URL 无效时失败', async () => {
|
|
48
|
-
mockFetch.mockRejectedValue(new Error('Invalid URL'));
|
|
49
|
-
|
|
50
|
-
const task: QueuedTask = {
|
|
51
|
-
taskId: 'task-1',
|
|
52
|
-
status: 'pending',
|
|
53
|
-
createdAt: Date.now()
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const result = await pusher.pushTask(task);
|
|
57
|
-
|
|
58
|
-
expect(result.success).toBe(false);
|
|
59
|
-
expect(result.error).toBe('Invalid URL');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('应该在超时时失败', async () => {
|
|
63
|
-
mockFetch.mockImplementation(() =>
|
|
64
|
-
new Promise((_, reject) =>
|
|
65
|
-
setTimeout(() => reject(new Error('Timeout')), 2000)
|
|
66
|
-
)
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const task: QueuedTask = {
|
|
70
|
-
taskId: 'task-1',
|
|
71
|
-
status: 'pending',
|
|
72
|
-
createdAt: Date.now()
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const result = await pusher.pushTask(task);
|
|
76
|
-
|
|
77
|
-
expect(result.success).toBe(false);
|
|
78
|
-
}, 5000);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('冷却期边界', () => {
|
|
82
|
-
it('应该在连续失败 3 次后进入冷却期', async () => {
|
|
83
|
-
mockFetch.mockRejectedValue(new Error('Failed'));
|
|
84
|
-
|
|
85
|
-
const task: QueuedTask = {
|
|
86
|
-
taskId: 'task-1',
|
|
87
|
-
status: 'pending',
|
|
88
|
-
createdAt: Date.now()
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// 连续失败 3 次
|
|
92
|
-
await pusher.pushTask(task);
|
|
93
|
-
await pusher.pushTask(task);
|
|
94
|
-
await pusher.pushTask(task);
|
|
95
|
-
|
|
96
|
-
const status = pusher.getStatus();
|
|
97
|
-
expect(status.consecutiveFailures).toBe(3);
|
|
98
|
-
expect(status.inCooldown).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('应该在冷却期内拒绝推送', async () => {
|
|
102
|
-
mockFetch.mockRejectedValue(new Error('Failed'));
|
|
103
|
-
|
|
104
|
-
const task: QueuedTask = {
|
|
105
|
-
taskId: 'task-1',
|
|
106
|
-
status: 'pending',
|
|
107
|
-
createdAt: Date.now()
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// 触发冷却期
|
|
111
|
-
await pusher.pushTask(task);
|
|
112
|
-
await pusher.pushTask(task);
|
|
113
|
-
await pusher.pushTask(task);
|
|
114
|
-
|
|
115
|
-
// 冷却期内的推送应该失败
|
|
116
|
-
const result = await pusher.pushTask(task);
|
|
117
|
-
expect(result.success).toBe(false);
|
|
118
|
-
// 更新错误消息格式:包含剩余秒数
|
|
119
|
-
expect(result.error).toMatch(/In cooldown \(\d+s remaining\)/);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('应该在冷却期后恢复推送', async () => {
|
|
123
|
-
const quickPusher = new WebhookPusher({
|
|
124
|
-
...defaultConfig
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// 模拟快速冷却期(通过修改内部状态)
|
|
128
|
-
mockFetch.mockRejectedValue(new Error('Failed'));
|
|
129
|
-
|
|
130
|
-
const task: QueuedTask = {
|
|
131
|
-
taskId: 'task-1',
|
|
132
|
-
status: 'pending',
|
|
133
|
-
createdAt: Date.now()
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// 触发冷却期
|
|
137
|
-
await quickPusher.pushTask(task);
|
|
138
|
-
await quickPusher.pushTask(task);
|
|
139
|
-
await quickPusher.pushTask(task);
|
|
140
|
-
|
|
141
|
-
// 手动重置失败计数和时间,模拟冷却期结束
|
|
142
|
-
(quickPusher as any).consecutiveFailures = 0;
|
|
143
|
-
(quickPusher as any).lastFailureTime = 0;
|
|
144
|
-
|
|
145
|
-
// 恢复成功
|
|
146
|
-
mockFetch.mockResolvedValue({ ok: true, status: 200 });
|
|
147
|
-
const result = await quickPusher.pushTask(task);
|
|
148
|
-
|
|
149
|
-
expect(result.success).toBe(true);
|
|
150
|
-
}, 3000);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe('幂等性', () => {
|
|
154
|
-
it('应该支持同一任务多次推送', async () => {
|
|
155
|
-
mockFetch.mockResolvedValue({ ok: true, status: 200 });
|
|
156
|
-
|
|
157
|
-
const task: QueuedTask = {
|
|
158
|
-
taskId: 'task-1',
|
|
159
|
-
status: 'pending',
|
|
160
|
-
createdAt: Date.now()
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const result1 = await pusher.pushTask(task);
|
|
164
|
-
const result2 = await pusher.pushTask(task);
|
|
165
|
-
|
|
166
|
-
expect(result1.success).toBe(true);
|
|
167
|
-
expect(result2.success).toBe(true);
|
|
168
|
-
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe('状态管理', () => {
|
|
173
|
-
it('应该正确返回状态', () => {
|
|
174
|
-
const status = pusher.getStatus();
|
|
175
|
-
|
|
176
|
-
expect(status.enabled).toBe(true);
|
|
177
|
-
expect(status.consecutiveFailures).toBe(0);
|
|
178
|
-
expect(status.inCooldown).toBe(false);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('应该支持动态更新配置', () => {
|
|
182
|
-
pusher.updateConfig({ enabled: false });
|
|
183
|
-
|
|
184
|
-
const status = pusher.getStatus();
|
|
185
|
-
expect(status.enabled).toBe(false);
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
});
|