@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/f2a.ts
DELETED
|
@@ -1,618 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* F2A 主类 - P2P 版本
|
|
3
|
-
* 整合 P2P 网络、能力发现与任务委托
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { EventEmitter } from 'eventemitter3';
|
|
7
|
-
import { P2PNetwork } from './p2p-network.js';
|
|
8
|
-
import { Logger } from '../utils/logger.js';
|
|
9
|
-
import { Middleware } from '../utils/middleware.js';
|
|
10
|
-
import { validateAgentCapability, validateTaskDelegateOptions } from '../utils/validation.js';
|
|
11
|
-
import {
|
|
12
|
-
F2AOptions,
|
|
13
|
-
F2AEvents,
|
|
14
|
-
AgentInfo,
|
|
15
|
-
AgentCapability,
|
|
16
|
-
Result,
|
|
17
|
-
TaskDelegateOptions,
|
|
18
|
-
TaskDelegateResult,
|
|
19
|
-
TaskRequestEvent,
|
|
20
|
-
TaskResponseEvent,
|
|
21
|
-
PeerDiscoveredEvent,
|
|
22
|
-
PeerConnectedEvent,
|
|
23
|
-
PeerDisconnectedEvent,
|
|
24
|
-
RegisteredCapability,
|
|
25
|
-
NetworkStartedEvent,
|
|
26
|
-
success,
|
|
27
|
-
failureFromError
|
|
28
|
-
} from '../types/index.js';
|
|
29
|
-
|
|
30
|
-
// 版本号
|
|
31
|
-
const F2A_VERSION = '1.0.0';
|
|
32
|
-
const PROTOCOL_VERSION = 'f2a/1.0';
|
|
33
|
-
|
|
34
|
-
export interface F2AInstance {
|
|
35
|
-
readonly peerId: string;
|
|
36
|
-
/** 获取 Agent 信息(延迟获取,确保 peerId 在 start() 后才有效) */
|
|
37
|
-
readonly agentInfo: AgentInfo;
|
|
38
|
-
start(): Promise<Result<void>>;
|
|
39
|
-
stop(): Promise<void>;
|
|
40
|
-
|
|
41
|
-
// 能力管理
|
|
42
|
-
registerCapability(capability: AgentCapability, handler: (params: Record<string, unknown>) => Promise<unknown>): void;
|
|
43
|
-
getCapabilities(): AgentCapability[];
|
|
44
|
-
|
|
45
|
-
// 发现
|
|
46
|
-
discoverAgents(capability?: string): Promise<AgentInfo[]>;
|
|
47
|
-
getConnectedPeers(): AgentInfo[];
|
|
48
|
-
getAllPeers(): AgentInfo[];
|
|
49
|
-
|
|
50
|
-
// 任务委托
|
|
51
|
-
delegateTask(options: TaskDelegateOptions): Promise<Result<TaskDelegateResult>>;
|
|
52
|
-
|
|
53
|
-
// 直接通信
|
|
54
|
-
sendTaskTo(peerId: string, taskType: string, description: string, parameters?: Record<string, unknown>): Promise<Result<unknown>>;
|
|
55
|
-
|
|
56
|
-
// 中间件
|
|
57
|
-
useMiddleware(middleware: Middleware): void;
|
|
58
|
-
removeMiddleware(name: string): boolean;
|
|
59
|
-
listMiddlewares(): string[];
|
|
60
|
-
|
|
61
|
-
// DHT
|
|
62
|
-
findPeerViaDHT(peerId: string): Promise<Result<string[]>>;
|
|
63
|
-
getDHTPeerCount(): number;
|
|
64
|
-
isDHTEnabled(): boolean;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export class F2A extends EventEmitter<F2AEvents> implements F2AInstance {
|
|
68
|
-
private _agentInfo: AgentInfo;
|
|
69
|
-
private p2pNetwork: P2PNetwork;
|
|
70
|
-
private options: Required<F2AOptions>;
|
|
71
|
-
private running: boolean = false;
|
|
72
|
-
private registeredCapabilities: Map<string, RegisteredCapability> = new Map();
|
|
73
|
-
private logger: Logger;
|
|
74
|
-
|
|
75
|
-
private constructor(
|
|
76
|
-
agentInfo: AgentInfo,
|
|
77
|
-
p2pNetwork: P2PNetwork,
|
|
78
|
-
options: Required<F2AOptions>
|
|
79
|
-
) {
|
|
80
|
-
super();
|
|
81
|
-
this._agentInfo = agentInfo;
|
|
82
|
-
this.p2pNetwork = p2pNetwork;
|
|
83
|
-
this.options = options;
|
|
84
|
-
this.logger = new Logger({ level: options.logLevel, component: 'F2A' });
|
|
85
|
-
|
|
86
|
-
this.bindEvents();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* 获取 Agent 信息
|
|
91
|
-
* 使用 getter 延迟获取 peerId,避免在 start() 前读到空值
|
|
92
|
-
*/
|
|
93
|
-
get agentInfo(): AgentInfo {
|
|
94
|
-
// 返回一个代理对象,确保 peerId 始终从 p2pNetwork 获取最新值
|
|
95
|
-
return {
|
|
96
|
-
...this._agentInfo,
|
|
97
|
-
peerId: this.running ? this._agentInfo.peerId : ''
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* 工厂方法:创建 F2A 实例
|
|
103
|
-
*/
|
|
104
|
-
static async create(options: F2AOptions = {}): Promise<F2A> {
|
|
105
|
-
// 默认配置
|
|
106
|
-
const mergedOptions: Required<F2AOptions> = {
|
|
107
|
-
displayName: options.displayName || 'F2A Agent',
|
|
108
|
-
agentType: options.agentType || 'openclaw',
|
|
109
|
-
network: {
|
|
110
|
-
listenPort: 0,
|
|
111
|
-
enableMDNS: true,
|
|
112
|
-
enableDHT: false,
|
|
113
|
-
...options.network
|
|
114
|
-
},
|
|
115
|
-
security: {
|
|
116
|
-
level: 'medium',
|
|
117
|
-
requireConfirmation: true,
|
|
118
|
-
verifySignatures: true,
|
|
119
|
-
...options.security
|
|
120
|
-
},
|
|
121
|
-
logLevel: options.logLevel || 'INFO',
|
|
122
|
-
dataDir: options.dataDir || './f2a-data'
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// 创建 AgentInfo
|
|
126
|
-
const agentInfo: AgentInfo = {
|
|
127
|
-
peerId: '', // 启动后由 P2P 网络填充
|
|
128
|
-
displayName: mergedOptions.displayName,
|
|
129
|
-
agentType: mergedOptions.agentType as AgentInfo['agentType'],
|
|
130
|
-
version: F2A_VERSION,
|
|
131
|
-
capabilities: [],
|
|
132
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
133
|
-
lastSeen: Date.now(),
|
|
134
|
-
multiaddrs: []
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// 创建 P2P 网络
|
|
138
|
-
const p2pNetwork = new P2PNetwork(agentInfo, mergedOptions.network);
|
|
139
|
-
|
|
140
|
-
// 创建实例
|
|
141
|
-
const f2a = new F2A(agentInfo, p2pNetwork, mergedOptions);
|
|
142
|
-
|
|
143
|
-
return f2a;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* 启动 F2A
|
|
148
|
-
*/
|
|
149
|
-
async start(): Promise<Result<void>> {
|
|
150
|
-
if (this.running) {
|
|
151
|
-
return failureFromError('NETWORK_ALREADY_RUNNING', 'F2A already running');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
this.logger.info('Starting agent', { displayName: this.agentInfo.displayName });
|
|
155
|
-
|
|
156
|
-
// 启动 P2P 网络
|
|
157
|
-
const result = await this.p2pNetwork.start();
|
|
158
|
-
if (!result.success) {
|
|
159
|
-
return result;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// 更新 agentInfo
|
|
163
|
-
this._agentInfo.peerId = result.data.peerId;
|
|
164
|
-
this._agentInfo.multiaddrs = result.data.addresses;
|
|
165
|
-
|
|
166
|
-
this.running = true;
|
|
167
|
-
|
|
168
|
-
this.emit('network:started', {
|
|
169
|
-
peerId: result.data.peerId,
|
|
170
|
-
listenAddresses: result.data.addresses
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
this.logger.info('Started', { peerId: result.data.peerId.slice(0, 16) });
|
|
174
|
-
|
|
175
|
-
return { success: true, data: undefined };
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* 停止 F2A
|
|
180
|
-
*/
|
|
181
|
-
async stop(): Promise<void> {
|
|
182
|
-
if (!this.running) return;
|
|
183
|
-
|
|
184
|
-
this.logger.info('Stopping');
|
|
185
|
-
|
|
186
|
-
await this.p2pNetwork.stop();
|
|
187
|
-
|
|
188
|
-
this.running = false;
|
|
189
|
-
this.emit('network:stopped');
|
|
190
|
-
|
|
191
|
-
this.logger.info('Stopped');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* 注册能力
|
|
196
|
-
*/
|
|
197
|
-
registerCapability(
|
|
198
|
-
capability: AgentCapability,
|
|
199
|
-
handler: (params: Record<string, unknown>) => Promise<unknown>
|
|
200
|
-
): void {
|
|
201
|
-
// 验证能力定义
|
|
202
|
-
const validation = validateAgentCapability(capability);
|
|
203
|
-
if (!validation.success) {
|
|
204
|
-
this.logger.error('Invalid capability definition', {
|
|
205
|
-
errors: validation.error.errors
|
|
206
|
-
});
|
|
207
|
-
throw new Error(`Invalid capability: ${validation.error.errors.map(e => e.message).join(', ')}`);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
this.registeredCapabilities.set(capability.name, {
|
|
211
|
-
...capability,
|
|
212
|
-
handler
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// 更新 agentInfo
|
|
216
|
-
this.updateAgentCapabilities();
|
|
217
|
-
|
|
218
|
-
this.logger.info('Registered capability', { name: capability.name });
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* 获取已注册的能力
|
|
223
|
-
*/
|
|
224
|
-
getCapabilities(): AgentCapability[] {
|
|
225
|
-
return Array.from(this.registeredCapabilities.values()).map(c => ({
|
|
226
|
-
name: c.name,
|
|
227
|
-
description: c.description,
|
|
228
|
-
tools: c.tools,
|
|
229
|
-
parameters: c.parameters
|
|
230
|
-
}));
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* 发现网络中的 Agents
|
|
235
|
-
*/
|
|
236
|
-
async discoverAgents(capability?: string): Promise<AgentInfo[]> {
|
|
237
|
-
return this.p2pNetwork.discoverAgents(capability);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* 获取已连接的 Peers
|
|
242
|
-
*/
|
|
243
|
-
getConnectedPeers(): AgentInfo[] {
|
|
244
|
-
return this.p2pNetwork.getConnectedPeers()
|
|
245
|
-
.filter(p => p.agentInfo)
|
|
246
|
-
.map(p => p.agentInfo!);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* 获取所有已知的 Peers(包括已断开但已发现的)
|
|
251
|
-
*/
|
|
252
|
-
getAllPeers(): AgentInfo[] {
|
|
253
|
-
// 返回所有已知节点,包括还没有交换 agentInfo 的
|
|
254
|
-
// 如果 agentInfo 不存在,创建一个基本的 AgentInfo
|
|
255
|
-
return this.p2pNetwork.getAllPeers()
|
|
256
|
-
.map(p => {
|
|
257
|
-
if (p.agentInfo) {
|
|
258
|
-
return p.agentInfo;
|
|
259
|
-
}
|
|
260
|
-
// 创建基本的 AgentInfo
|
|
261
|
-
return {
|
|
262
|
-
peerId: p.peerId,
|
|
263
|
-
capabilities: [],
|
|
264
|
-
multiaddrs: p.multiaddrs.map(m => m.toString()),
|
|
265
|
-
lastSeen: p.lastSeen,
|
|
266
|
-
agentType: 'custom' as const,
|
|
267
|
-
version: '0.0.0',
|
|
268
|
-
protocolVersion: '1.0.0'
|
|
269
|
-
};
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* 委托任务给网络
|
|
275
|
-
*/
|
|
276
|
-
async delegateTask(options: TaskDelegateOptions): Promise<Result<TaskDelegateResult>> {
|
|
277
|
-
// 验证任务委托选项
|
|
278
|
-
const validation = validateTaskDelegateOptions(options);
|
|
279
|
-
if (!validation.success) {
|
|
280
|
-
this.logger.error('Invalid task delegate options', {
|
|
281
|
-
errors: validation.error.errors
|
|
282
|
-
});
|
|
283
|
-
return failureFromError(
|
|
284
|
-
'INVALID_OPTIONS',
|
|
285
|
-
`Invalid options: ${validation.error.errors.map(e => e.message).join(', ')}`
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
290
|
-
|
|
291
|
-
this.logger.info('Delegating task', {
|
|
292
|
-
taskId,
|
|
293
|
-
capability: options.capability,
|
|
294
|
-
description: options.description.slice(0, 50)
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
// 可配置的重试选项
|
|
298
|
-
const retryOptions = {
|
|
299
|
-
maxRetries: options.retryOptions?.maxRetries ?? 3,
|
|
300
|
-
retryDelayMs: options.retryOptions?.retryDelayMs ?? 1000,
|
|
301
|
-
discoverTimeoutMs: options.retryOptions?.discoverTimeoutMs ?? 5000
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
// 1. 发现有能力执行任务的 Agents(带重试)
|
|
305
|
-
let agents: AgentInfo[] = [];
|
|
306
|
-
let lastError: string | undefined;
|
|
307
|
-
|
|
308
|
-
for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {
|
|
309
|
-
agents = await this.discoverAgents(options.capability);
|
|
310
|
-
|
|
311
|
-
if (agents.length > 0) {
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (attempt < retryOptions.maxRetries) {
|
|
316
|
-
this.logger.warn(`No agents found, retrying (${attempt + 1}/${retryOptions.maxRetries})`, {
|
|
317
|
-
capability: options.capability
|
|
318
|
-
});
|
|
319
|
-
await new Promise(resolve => setTimeout(resolve, retryOptions.retryDelayMs));
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (agents.length === 0) {
|
|
324
|
-
this.logger.warn('No agents found with capability after retries', {
|
|
325
|
-
capability: options.capability,
|
|
326
|
-
retries: retryOptions.maxRetries
|
|
327
|
-
});
|
|
328
|
-
return failureFromError(
|
|
329
|
-
'CAPABILITY_NOT_SUPPORTED',
|
|
330
|
-
`No agent found with capability: ${options.capability} (after ${retryOptions.maxRetries} retries)`
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
this.logger.info('Found agents with capability', {
|
|
335
|
-
count: agents.length,
|
|
336
|
-
capability: options.capability
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// 2. 发送任务请求
|
|
340
|
-
const timeout = options.timeout || 30000;
|
|
341
|
-
const results: TaskDelegateResult['results'] = [];
|
|
342
|
-
|
|
343
|
-
if (options.parallel) {
|
|
344
|
-
// 并行发送给多个 Agents
|
|
345
|
-
const minResponses = options.minResponses || 1;
|
|
346
|
-
|
|
347
|
-
const promises = agents.map(async (agent) => {
|
|
348
|
-
const startTime = Date.now();
|
|
349
|
-
const result = await this.p2pNetwork.sendTaskRequest(
|
|
350
|
-
agent.peerId,
|
|
351
|
-
options.capability,
|
|
352
|
-
options.description,
|
|
353
|
-
options.parameters,
|
|
354
|
-
timeout
|
|
355
|
-
);
|
|
356
|
-
const latency = Date.now() - startTime;
|
|
357
|
-
|
|
358
|
-
return {
|
|
359
|
-
peerId: agent.peerId,
|
|
360
|
-
status: result.success ? 'success' as const : 'error' as const,
|
|
361
|
-
result: result.success ? result.data : undefined,
|
|
362
|
-
error: result.success ? undefined : (result.error?.message || String(result.error)),
|
|
363
|
-
latency
|
|
364
|
-
};
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
// 等待至少 minResponses 个响应
|
|
368
|
-
const settled = await Promise.allSettled(promises);
|
|
369
|
-
|
|
370
|
-
for (const outcome of settled) {
|
|
371
|
-
if (outcome.status === 'fulfilled') {
|
|
372
|
-
results.push(outcome.value);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// 检查是否达到最小响应数
|
|
377
|
-
const successCount = results.filter(r => r.status === 'success').length;
|
|
378
|
-
if (successCount < minResponses) {
|
|
379
|
-
return failureFromError(
|
|
380
|
-
'TASK_FAILED',
|
|
381
|
-
`Only ${successCount} successful responses, required ${minResponses}`
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
} else {
|
|
385
|
-
// 串行发送,取第一个成功的
|
|
386
|
-
for (const agent of agents) {
|
|
387
|
-
const startTime = Date.now();
|
|
388
|
-
const result = await this.p2pNetwork.sendTaskRequest(
|
|
389
|
-
agent.peerId,
|
|
390
|
-
options.capability,
|
|
391
|
-
options.description,
|
|
392
|
-
options.parameters,
|
|
393
|
-
timeout
|
|
394
|
-
);
|
|
395
|
-
const latency = Date.now() - startTime;
|
|
396
|
-
|
|
397
|
-
results.push({
|
|
398
|
-
peerId: agent.peerId,
|
|
399
|
-
status: result.success ? 'success' : 'error',
|
|
400
|
-
result: result.success ? result.data : undefined,
|
|
401
|
-
error: result.success ? undefined : (result.error?.message || String(result.error)),
|
|
402
|
-
latency
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
if (result.success) {
|
|
406
|
-
break; // 第一个成功就停止
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// 检查是否有成功结果
|
|
411
|
-
if (!results.some(r => r.status === 'success')) {
|
|
412
|
-
return failureFromError('TASK_FAILED', 'All agents failed to execute the task');
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return success({ taskId, results });
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* 直接发送任务给特定 Peer
|
|
421
|
-
*/
|
|
422
|
-
async sendTaskTo(
|
|
423
|
-
peerId: string,
|
|
424
|
-
taskType: string,
|
|
425
|
-
description: string,
|
|
426
|
-
parameters?: Record<string, unknown>
|
|
427
|
-
): Promise<Result<unknown>> {
|
|
428
|
-
return this.p2pNetwork.sendTaskRequest(
|
|
429
|
-
peerId,
|
|
430
|
-
taskType,
|
|
431
|
-
description,
|
|
432
|
-
parameters,
|
|
433
|
-
30000
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* 注册中间件
|
|
439
|
-
*/
|
|
440
|
-
useMiddleware(middleware: Middleware): void {
|
|
441
|
-
this.p2pNetwork.useMiddleware(middleware);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* 移除中间件
|
|
446
|
-
*/
|
|
447
|
-
removeMiddleware(name: string): boolean {
|
|
448
|
-
return this.p2pNetwork.removeMiddleware(name);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* 获取已注册中间件列表
|
|
453
|
-
*/
|
|
454
|
-
listMiddlewares(): string[] {
|
|
455
|
-
return this.p2pNetwork.listMiddlewares();
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* 通过 DHT 查找节点
|
|
460
|
-
*/
|
|
461
|
-
async findPeerViaDHT(peerId: string): Promise<Result<string[]>> {
|
|
462
|
-
return this.p2pNetwork.findPeerViaDHT(peerId);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* 获取 DHT 路由表大小
|
|
467
|
-
*/
|
|
468
|
-
getDHTPeerCount(): number {
|
|
469
|
-
return this.p2pNetwork.getDHTPeerCount();
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* 检查 DHT 是否启用
|
|
474
|
-
*/
|
|
475
|
-
isDHTEnabled(): boolean {
|
|
476
|
-
return this.p2pNetwork.isDHTEnabled();
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* 获取 PeerID
|
|
481
|
-
*/
|
|
482
|
-
get peerId(): string {
|
|
483
|
-
return this.agentInfo.peerId;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* 绑定内部事件
|
|
488
|
-
*/
|
|
489
|
-
private bindEvents(): void {
|
|
490
|
-
// 转发 P2P 网络事件
|
|
491
|
-
this.p2pNetwork.on('peer:discovered', (event) => {
|
|
492
|
-
this.emit('peer:discovered', event);
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
this.p2pNetwork.on('peer:connected', (event) => {
|
|
496
|
-
this.emit('peer:connected', event);
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
this.p2pNetwork.on('peer:disconnected', (event) => {
|
|
500
|
-
this.emit('peer:disconnected', event);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
// 处理收到的任务请求
|
|
504
|
-
this.p2pNetwork.on('message:received', async (message, peerId) => {
|
|
505
|
-
if (message.type === 'TASK_REQUEST') {
|
|
506
|
-
await this.handleTaskRequest(message.payload as TaskRequestEvent, peerId);
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
this.p2pNetwork.on('error', (error) => {
|
|
511
|
-
this.emit('error', error);
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* 处理收到的任务请求
|
|
517
|
-
*/
|
|
518
|
-
private async handleTaskRequest(
|
|
519
|
-
request: TaskRequestEvent,
|
|
520
|
-
fromPeerId: string
|
|
521
|
-
): Promise<void> {
|
|
522
|
-
this.logger.info('Received task request', {
|
|
523
|
-
fromPeerId: fromPeerId.slice(0, 16),
|
|
524
|
-
taskType: request.taskType,
|
|
525
|
-
taskId: request.taskId
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
// 查找对应的能力处理器
|
|
529
|
-
const capability = this.registeredCapabilities.get(request.taskType);
|
|
530
|
-
|
|
531
|
-
if (!capability) {
|
|
532
|
-
this.logger.warn('Capability not supported, rejecting task', {
|
|
533
|
-
taskType: request.taskType,
|
|
534
|
-
fromPeerId: fromPeerId.slice(0, 16)
|
|
535
|
-
});
|
|
536
|
-
// 拒绝任务
|
|
537
|
-
await this.p2pNetwork.sendTaskResponse(
|
|
538
|
-
fromPeerId,
|
|
539
|
-
request.taskId,
|
|
540
|
-
'rejected',
|
|
541
|
-
undefined,
|
|
542
|
-
`Capability not supported: ${request.taskType}`
|
|
543
|
-
);
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// 触发事件,让上层(OpenClaw)可以拦截或监控
|
|
548
|
-
this.emit('task:request', request);
|
|
549
|
-
|
|
550
|
-
// 如果有注册 handler,自动执行任务并发送响应
|
|
551
|
-
if (capability.handler) {
|
|
552
|
-
try {
|
|
553
|
-
const result = await capability.handler(request.parameters || {});
|
|
554
|
-
await this.p2pNetwork.sendTaskResponse(
|
|
555
|
-
fromPeerId,
|
|
556
|
-
request.taskId,
|
|
557
|
-
'success',
|
|
558
|
-
result
|
|
559
|
-
);
|
|
560
|
-
this.logger.info('Task executed successfully', {
|
|
561
|
-
taskId: request.taskId,
|
|
562
|
-
fromPeerId: fromPeerId.slice(0, 16)
|
|
563
|
-
});
|
|
564
|
-
} catch (error) {
|
|
565
|
-
this.logger.error('Task execution failed', {
|
|
566
|
-
taskId: request.taskId,
|
|
567
|
-
error: error instanceof Error ? error.message : String(error)
|
|
568
|
-
});
|
|
569
|
-
await this.p2pNetwork.sendTaskResponse(
|
|
570
|
-
fromPeerId,
|
|
571
|
-
request.taskId,
|
|
572
|
-
'error',
|
|
573
|
-
undefined,
|
|
574
|
-
error instanceof Error ? error.message : String(error)
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
// 如果没有 handler,依赖上层通过 respondToTask 手动响应
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* 发送任务响应(供 OpenClaw 调用)
|
|
583
|
-
*/
|
|
584
|
-
async respondToTask(
|
|
585
|
-
peerId: string,
|
|
586
|
-
taskId: string,
|
|
587
|
-
status: 'success' | 'error' | 'rejected',
|
|
588
|
-
result?: unknown,
|
|
589
|
-
error?: string
|
|
590
|
-
): Promise<Result<void>> {
|
|
591
|
-
const responseResult = await this.p2pNetwork.sendTaskResponse(
|
|
592
|
-
peerId,
|
|
593
|
-
taskId,
|
|
594
|
-
status,
|
|
595
|
-
result,
|
|
596
|
-
error
|
|
597
|
-
);
|
|
598
|
-
|
|
599
|
-
if (responseResult.success) {
|
|
600
|
-
this.emit('task:response', {
|
|
601
|
-
taskId,
|
|
602
|
-
from: this.peerId,
|
|
603
|
-
status,
|
|
604
|
-
result,
|
|
605
|
-
error
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
return responseResult;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* 更新 AgentInfo 中的能力列表
|
|
614
|
-
*/
|
|
615
|
-
private updateAgentCapabilities(): void {
|
|
616
|
-
this._agentInfo.capabilities = this.getCapabilities();
|
|
617
|
-
}
|
|
618
|
-
}
|