@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/cli/index.ts
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* F2A CLI 入口 - P2P 版本
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { request, RequestOptions } from 'http';
|
|
7
|
-
import { existsSync, readFileSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
import { homedir } from 'os';
|
|
10
|
-
|
|
11
|
-
const CONTROL_PORT = parseInt(process.env.F2A_CONTROL_PORT || '9001');
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* 获取控制 Token
|
|
15
|
-
* 优先从环境变量读取,其次从默认文件位置读取
|
|
16
|
-
* @returns 控制 Token,如果未找到返回空字符串
|
|
17
|
-
*/
|
|
18
|
-
function getControlToken(): string {
|
|
19
|
-
// 1. 优先使用环境变量
|
|
20
|
-
const envToken = process.env.F2A_CONTROL_TOKEN;
|
|
21
|
-
if (envToken) {
|
|
22
|
-
return envToken;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// 2. 从默认文件位置读取
|
|
26
|
-
const tokenPath = join(homedir(), '.f2a', 'control-token');
|
|
27
|
-
if (existsSync(tokenPath)) {
|
|
28
|
-
const fileToken = readFileSync(tokenPath, 'utf-8').trim();
|
|
29
|
-
if (fileToken) {
|
|
30
|
-
return fileToken;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// 3. 如果都没有,返回空字符串(会导致认证失败)
|
|
35
|
-
console.warn('⚠️ Warning: F2A_CONTROL_TOKEN not set and no token file found.');
|
|
36
|
-
console.warn(' Token file location:', tokenPath);
|
|
37
|
-
console.warn(' Please start the F2A daemon first, or set F2A_CONTROL_TOKEN.');
|
|
38
|
-
return '';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const CONTROL_TOKEN = getControlToken();
|
|
42
|
-
|
|
43
|
-
interface Args {
|
|
44
|
-
command: string;
|
|
45
|
-
idOrIndex?: string | number;
|
|
46
|
-
capability?: string;
|
|
47
|
-
reason?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 解析命令行参数
|
|
52
|
-
* @returns 解析后的参数对象
|
|
53
|
-
*/
|
|
54
|
-
function parseArgs(): Args {
|
|
55
|
-
const args = process.argv.slice(2);
|
|
56
|
-
|
|
57
|
-
if (args.length === 0) {
|
|
58
|
-
return { command: 'help' };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const command = args[0];
|
|
62
|
-
|
|
63
|
-
// 解析 ID 或序号
|
|
64
|
-
let idOrIndex: string | number | undefined;
|
|
65
|
-
if (args[1]) {
|
|
66
|
-
idOrIndex = /^\d+$/.test(args[1]) ? parseInt(args[1]) : args[1];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 解析能力过滤
|
|
70
|
-
let capability: string | undefined;
|
|
71
|
-
const capIndex = args.indexOf('--capability');
|
|
72
|
-
if (capIndex !== -1 && args[capIndex + 1]) {
|
|
73
|
-
capability = args[capIndex + 1];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// 解析原因
|
|
77
|
-
let reason: string | undefined;
|
|
78
|
-
const reasonIndex = args.indexOf('--reason');
|
|
79
|
-
if (reasonIndex !== -1 && args[reasonIndex + 1]) {
|
|
80
|
-
reason = args[reasonIndex + 1];
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return { command, idOrIndex, capability, reason };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 显示帮助信息
|
|
88
|
-
* @returns void
|
|
89
|
-
*/
|
|
90
|
-
function showHelp(): void {
|
|
91
|
-
console.log(`
|
|
92
|
-
F2A CLI - Friend-to-Agent P2P Networking
|
|
93
|
-
|
|
94
|
-
Usage: f2a [command] [options]
|
|
95
|
-
|
|
96
|
-
Commands:
|
|
97
|
-
status 查看节点状态
|
|
98
|
-
peers 查看已连接的 Peers
|
|
99
|
-
discover [options] 发现网络中的 Agents
|
|
100
|
-
pending 查看待确认连接
|
|
101
|
-
confirm [id|index] 确认连接请求
|
|
102
|
-
reject [id|index] 拒绝连接请求
|
|
103
|
-
help 显示帮助
|
|
104
|
-
|
|
105
|
-
Options:
|
|
106
|
-
-c, --capability 按能力过滤 (discover 命令)
|
|
107
|
-
--reason [text] 拒绝原因 (reject 命令)
|
|
108
|
-
|
|
109
|
-
Environment Variables:
|
|
110
|
-
F2A_CONTROL_PORT 控制服务器端口 (默认: 9001)
|
|
111
|
-
F2A_CONTROL_TOKEN 控制服务器认证 Token
|
|
112
|
-
(如果不设置,会读取 ~/.f2a/control-token)
|
|
113
|
-
|
|
114
|
-
Examples:
|
|
115
|
-
f2a status
|
|
116
|
-
f2a peers
|
|
117
|
-
f2a discover
|
|
118
|
-
f2a discover --capability code-generation
|
|
119
|
-
f2a pending
|
|
120
|
-
f2a confirm 1
|
|
121
|
-
f2a reject 2 --reason "unknown"
|
|
122
|
-
`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* 发送控制命令到 F2A Daemon
|
|
127
|
-
* @param action - 命令动作
|
|
128
|
-
* @param params - 命令参数(可选)
|
|
129
|
-
* @returns Promise,命令执行完成后 resolve
|
|
130
|
-
* @throws 当网络请求失败时 reject
|
|
131
|
-
*/
|
|
132
|
-
async function sendCommand(action: string, params?: Record<string, unknown>): Promise<void> {
|
|
133
|
-
return new Promise((resolve, reject) => {
|
|
134
|
-
const payload = JSON.stringify({ action, ...params });
|
|
135
|
-
|
|
136
|
-
const options: RequestOptions = {
|
|
137
|
-
hostname: '127.0.0.1',
|
|
138
|
-
port: CONTROL_PORT,
|
|
139
|
-
path: '/control',
|
|
140
|
-
method: 'POST',
|
|
141
|
-
headers: {
|
|
142
|
-
'Content-Type': 'application/json',
|
|
143
|
-
'Content-Length': Buffer.byteLength(payload),
|
|
144
|
-
'X-F2A-Token': CONTROL_TOKEN
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const req = request(options, (res) => {
|
|
149
|
-
let data = '';
|
|
150
|
-
res.on('data', chunk => data += chunk);
|
|
151
|
-
res.on('end', () => {
|
|
152
|
-
try {
|
|
153
|
-
const response = JSON.parse(data);
|
|
154
|
-
if (response.success) {
|
|
155
|
-
console.log(JSON.stringify(response, null, 2));
|
|
156
|
-
} else {
|
|
157
|
-
if (res.statusCode === 401) {
|
|
158
|
-
console.error('❌ Authentication failed. Please check your F2A_CONTROL_TOKEN.');
|
|
159
|
-
} else {
|
|
160
|
-
console.error('Error:', response.error);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
resolve();
|
|
164
|
-
} catch {
|
|
165
|
-
console.log(data);
|
|
166
|
-
resolve();
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
req.on('error', (err) => {
|
|
172
|
-
console.error('Failed to connect to F2A daemon:', err.message);
|
|
173
|
-
console.log('Make sure the daemon is running (f2a daemon start)');
|
|
174
|
-
reject(err);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
req.write(payload);
|
|
178
|
-
req.end();
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* 主函数 - CLI 入口
|
|
184
|
-
* @returns Promise,程序退出时 resolve
|
|
185
|
-
* @throws 当命令执行失败时 reject
|
|
186
|
-
*/
|
|
187
|
-
async function main(): Promise<void> {
|
|
188
|
-
const args = parseArgs();
|
|
189
|
-
|
|
190
|
-
switch (args.command) {
|
|
191
|
-
case 'status':
|
|
192
|
-
await sendCommand('status');
|
|
193
|
-
break;
|
|
194
|
-
|
|
195
|
-
case 'peers':
|
|
196
|
-
await sendCommand('peers');
|
|
197
|
-
break;
|
|
198
|
-
|
|
199
|
-
case 'discover':
|
|
200
|
-
await sendCommand('discover', { capability: args.capability });
|
|
201
|
-
break;
|
|
202
|
-
|
|
203
|
-
case 'pending':
|
|
204
|
-
await sendCommand('pending');
|
|
205
|
-
break;
|
|
206
|
-
|
|
207
|
-
case 'confirm':
|
|
208
|
-
if (args.idOrIndex === undefined) {
|
|
209
|
-
console.error('[F2A] 错误: 需要指定 ID 或序号');
|
|
210
|
-
console.error('用法: f2a confirm [id|index]');
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
|
-
await sendCommand('confirm', { id: args.idOrIndex });
|
|
214
|
-
break;
|
|
215
|
-
|
|
216
|
-
case 'reject':
|
|
217
|
-
if (args.idOrIndex === undefined) {
|
|
218
|
-
console.error('[F2A] 错误: 需要指定 ID 或序号');
|
|
219
|
-
console.error('用法: f2a reject [id|index]');
|
|
220
|
-
process.exit(1);
|
|
221
|
-
}
|
|
222
|
-
await sendCommand('reject', { id: args.idOrIndex, reason: args.reason });
|
|
223
|
-
break;
|
|
224
|
-
|
|
225
|
-
case 'help':
|
|
226
|
-
default:
|
|
227
|
-
showHelp();
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
main().catch(err => {
|
|
232
|
-
console.error('[F2A] 错误:', err);
|
|
233
|
-
process.exit(1);
|
|
234
|
-
});
|
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 自治经济系统测试
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
6
|
-
import { AutonomousEconomy, TaskRequest, TaskExecutionResult } from './autonomous-economy.js';
|
|
7
|
-
import { ReputationManager } from './reputation.js';
|
|
8
|
-
import { ReviewCommittee } from './review-committee.js';
|
|
9
|
-
|
|
10
|
-
describe('AutonomousEconomy', () => {
|
|
11
|
-
let economy: AutonomousEconomy;
|
|
12
|
-
let reputationManager: ReputationManager;
|
|
13
|
-
let reviewCommittee: ReviewCommittee;
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
reputationManager = new ReputationManager();
|
|
17
|
-
reviewCommittee = new ReviewCommittee(reputationManager);
|
|
18
|
-
economy = new AutonomousEconomy(reputationManager, reviewCommittee);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe('任务提交', () => {
|
|
22
|
-
it('should submit task with sufficient reputation', () => {
|
|
23
|
-
const task: TaskRequest = {
|
|
24
|
-
taskId: 'task-1',
|
|
25
|
-
requesterId: 'requester-1',
|
|
26
|
-
capability: 'test',
|
|
27
|
-
description: 'Test task',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const result = economy.submitTask(task);
|
|
31
|
-
expect(result.success).toBe(true);
|
|
32
|
-
expect(result.cost).toBeDefined();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should deduct reputation on task submission', () => {
|
|
36
|
-
const before = reputationManager.getReputation('requester-1').score;
|
|
37
|
-
|
|
38
|
-
const task: TaskRequest = {
|
|
39
|
-
taskId: 'task-2',
|
|
40
|
-
requesterId: 'requester-1',
|
|
41
|
-
capability: 'test',
|
|
42
|
-
description: 'Test task',
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
economy.submitTask(task);
|
|
46
|
-
|
|
47
|
-
const after = reputationManager.getReputation('requester-1').score;
|
|
48
|
-
expect(after).toBeLessThan(before);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should apply discount for high reputation user', () => {
|
|
52
|
-
// 提升信誉到 core
|
|
53
|
-
for (let i = 0; i < 10; i++) {
|
|
54
|
-
reputationManager.recordSuccess('high-rep', `task-${i}`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const task: TaskRequest = {
|
|
58
|
-
taskId: 'task-3',
|
|
59
|
-
requesterId: 'high-rep',
|
|
60
|
-
capability: 'test',
|
|
61
|
-
description: 'Test task',
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const result = economy.submitTask(task);
|
|
65
|
-
expect(result.success).toBe(true);
|
|
66
|
-
expect(result.cost!.discount).toBeLessThan(1.0);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('任务完成与结算', () => {
|
|
71
|
-
it('should reward executor on success', () => {
|
|
72
|
-
const task: TaskRequest = {
|
|
73
|
-
taskId: 'task-4',
|
|
74
|
-
requesterId: 'requester-1',
|
|
75
|
-
capability: 'test',
|
|
76
|
-
description: 'Test task',
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
economy.submitTask(task);
|
|
80
|
-
|
|
81
|
-
const before = reputationManager.getReputation('executor-1').score;
|
|
82
|
-
|
|
83
|
-
const result: TaskExecutionResult = {
|
|
84
|
-
taskId: 'task-4',
|
|
85
|
-
executorId: 'executor-1',
|
|
86
|
-
status: 'success',
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
economy.completeTask(result);
|
|
90
|
-
|
|
91
|
-
const after = reputationManager.getReputation('executor-1').score;
|
|
92
|
-
expect(after).toBeGreaterThan(before);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should penalize executor on failure', () => {
|
|
96
|
-
const task: TaskRequest = {
|
|
97
|
-
taskId: 'task-5',
|
|
98
|
-
requesterId: 'requester-1',
|
|
99
|
-
capability: 'test',
|
|
100
|
-
description: 'Test task',
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
economy.submitTask(task);
|
|
104
|
-
|
|
105
|
-
const before = reputationManager.getReputation('executor-1').score;
|
|
106
|
-
|
|
107
|
-
const result: TaskExecutionResult = {
|
|
108
|
-
taskId: 'task-5',
|
|
109
|
-
executorId: 'executor-1',
|
|
110
|
-
status: 'failure',
|
|
111
|
-
error: 'Something went wrong',
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
economy.completeTask(result);
|
|
115
|
-
|
|
116
|
-
const after = reputationManager.getReputation('executor-1').score;
|
|
117
|
-
expect(after).toBeLessThan(before);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should penalize executor on timeout', () => {
|
|
121
|
-
const task: TaskRequest = {
|
|
122
|
-
taskId: 'task-6',
|
|
123
|
-
requesterId: 'requester-1',
|
|
124
|
-
capability: 'test',
|
|
125
|
-
description: 'Test task',
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
economy.submitTask(task);
|
|
129
|
-
|
|
130
|
-
const before = reputationManager.getReputation('executor-1').score;
|
|
131
|
-
|
|
132
|
-
const result: TaskExecutionResult = {
|
|
133
|
-
taskId: 'task-6',
|
|
134
|
-
executorId: 'executor-1',
|
|
135
|
-
status: 'timeout',
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
economy.completeTask(result);
|
|
139
|
-
|
|
140
|
-
const after = reputationManager.getReputation('executor-1').score;
|
|
141
|
-
expect(after).toBeLessThan(before);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe('优先级队列', () => {
|
|
146
|
-
it('should return highest priority task first', () => {
|
|
147
|
-
// 创建高信誉用户
|
|
148
|
-
for (let i = 0; i < 10; i++) {
|
|
149
|
-
reputationManager.recordSuccess('high-priority', `task-${i}`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const lowTask: TaskRequest = {
|
|
153
|
-
taskId: 'low-task',
|
|
154
|
-
requesterId: 'requester-1',
|
|
155
|
-
capability: 'test',
|
|
156
|
-
description: 'Low priority task',
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const highTask: TaskRequest = {
|
|
160
|
-
taskId: 'high-task',
|
|
161
|
-
requesterId: 'high-priority',
|
|
162
|
-
capability: 'test',
|
|
163
|
-
description: 'High priority task',
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
economy.submitTask(lowTask);
|
|
167
|
-
economy.submitTask(highTask);
|
|
168
|
-
|
|
169
|
-
const next = economy.getNextTask();
|
|
170
|
-
expect(next).not.toBeNull();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('should maintain queue order', () => {
|
|
174
|
-
for (let i = 0; i < 5; i++) {
|
|
175
|
-
const task: TaskRequest = {
|
|
176
|
-
taskId: `task-${i}`,
|
|
177
|
-
requesterId: 'requester-1',
|
|
178
|
-
capability: 'test',
|
|
179
|
-
description: `Task ${i}`,
|
|
180
|
-
};
|
|
181
|
-
economy.submitTask(task);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
expect(economy.getQueueLength()).toBe(5);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
describe('任务取消', () => {
|
|
189
|
-
it('should cancel task and refund partial cost', () => {
|
|
190
|
-
const task: TaskRequest = {
|
|
191
|
-
taskId: 'task-cancel',
|
|
192
|
-
requesterId: 'requester-1',
|
|
193
|
-
capability: 'test',
|
|
194
|
-
description: 'Task to cancel',
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
economy.submitTask(task);
|
|
198
|
-
const before = reputationManager.getReputation('requester-1').score;
|
|
199
|
-
|
|
200
|
-
const result = economy.cancelTask('task-cancel');
|
|
201
|
-
expect(result).toBe(true);
|
|
202
|
-
|
|
203
|
-
const after = reputationManager.getReputation('requester-1').score;
|
|
204
|
-
expect(after).toBeGreaterThan(before);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('should return false for non-existent task', () => {
|
|
208
|
-
const result = economy.cancelTask('non-existent');
|
|
209
|
-
expect(result).toBe(false);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
describe('经济统计', () => {
|
|
214
|
-
it('should return economy stats', () => {
|
|
215
|
-
const task: TaskRequest = {
|
|
216
|
-
taskId: 'task-stats',
|
|
217
|
-
requesterId: 'requester-1',
|
|
218
|
-
capability: 'test',
|
|
219
|
-
description: 'Test task',
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
economy.submitTask(task);
|
|
223
|
-
|
|
224
|
-
const stats = economy.getEconomyStats();
|
|
225
|
-
expect(stats.pendingTasks).toBe(1);
|
|
226
|
-
expect(stats.queueLength).toBe(1);
|
|
227
|
-
expect(stats.totalCostDeducted).toBeGreaterThan(0);
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
describe('过期任务清理', () => {
|
|
232
|
-
it('应该清理过期的待处理任务', async () => {
|
|
233
|
-
const task: TaskRequest = {
|
|
234
|
-
taskId: 'task-expired',
|
|
235
|
-
requesterId: 'requester-1',
|
|
236
|
-
capability: 'test',
|
|
237
|
-
description: 'Expired task',
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
economy.submitTask(task);
|
|
241
|
-
expect(economy.getQueueLength()).toBe(1);
|
|
242
|
-
|
|
243
|
-
// 等待足够长的时间确保任务过期
|
|
244
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
245
|
-
const expired = economy.cleanupExpiredTasks(50); // 50ms
|
|
246
|
-
|
|
247
|
-
expect(expired).toContain('task-expired');
|
|
248
|
-
expect(economy.getQueueLength()).toBe(0);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('不应该清理未过期的任务', async () => {
|
|
252
|
-
const task: TaskRequest = {
|
|
253
|
-
taskId: 'task-fresh',
|
|
254
|
-
requesterId: 'requester-1',
|
|
255
|
-
capability: 'test',
|
|
256
|
-
description: 'Fresh task',
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
economy.submitTask(task);
|
|
260
|
-
|
|
261
|
-
// 使用很长的过期时间(24小时)
|
|
262
|
-
const expired = economy.cleanupExpiredTasks(24 * 60 * 60 * 1000);
|
|
263
|
-
|
|
264
|
-
expect(expired.length).toBe(0);
|
|
265
|
-
expect(economy.getQueueLength()).toBe(1);
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
describe('getPendingTask', () => {
|
|
270
|
-
it('应该返回指定任务的信息', () => {
|
|
271
|
-
const task: TaskRequest = {
|
|
272
|
-
taskId: 'task-pending',
|
|
273
|
-
requesterId: 'requester-1',
|
|
274
|
-
capability: 'test',
|
|
275
|
-
description: 'Pending task',
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
economy.submitTask(task);
|
|
279
|
-
const pending = economy.getPendingTask('task-pending');
|
|
280
|
-
|
|
281
|
-
expect(pending).not.toBeNull();
|
|
282
|
-
expect(pending!.task.taskId).toBe('task-pending');
|
|
283
|
-
expect(pending!.task.requesterId).toBe('requester-1');
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
it('应该返回 null 对于不存在的任务', () => {
|
|
287
|
-
const pending = economy.getPendingTask('non-existent');
|
|
288
|
-
expect(pending).toBeNull();
|
|
289
|
-
});
|
|
290
|
-
});
|
|
291
|
-
});
|