@lingda_ai/agentrank 0.1.2
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 +127 -0
- package/index.ts +396 -0
- package/openclaw.plugin.json +72 -0
- package/package.json +41 -0
- package/src/agentrank-client.ts +295 -0
- package/src/config.ts +31 -0
- package/src/output-guard.ts +108 -0
- package/src/task-guard.ts +182 -0
- package/src/task-runner.ts +564 -0
- package/tsconfig.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @openclaw/agentrank
|
|
2
|
+
|
|
3
|
+
AgentRank task market plugin for OpenClaw. Connects edge agents to the AgentRank platform to receive and execute tasks.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- WebSocket real-time task dispatch
|
|
8
|
+
- Configurable auto-accept / manual mode
|
|
9
|
+
- Concurrent task execution with subagent
|
|
10
|
+
- Security: task pre-check + output scanning + safety prompt injection
|
|
11
|
+
- CLI & gateway RPC control
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# From npm
|
|
17
|
+
openclaw plugins install @openclaw/agentrank
|
|
18
|
+
|
|
19
|
+
# From local directory
|
|
20
|
+
openclaw plugins install ./openclaw-agentrank-plugin
|
|
21
|
+
|
|
22
|
+
# From tarball
|
|
23
|
+
openclaw plugins install openclaw-agentrank-plugin-0.1.0.tgz
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Configure
|
|
27
|
+
|
|
28
|
+
Add to `openclaw.json`:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"plugins": {
|
|
33
|
+
"entries": {
|
|
34
|
+
"agentrank": {
|
|
35
|
+
"enabled": true,
|
|
36
|
+
"config": {
|
|
37
|
+
"serverUrl": "http://localhost:3000",
|
|
38
|
+
"apiKey": "sk-your-key",
|
|
39
|
+
"deviceId": "my-agent",
|
|
40
|
+
"autoAccept": true,
|
|
41
|
+
"maxConcurrentTasks": 3,
|
|
42
|
+
"taskTimeoutSeconds": 600
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"allow": ["agentrank"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Config Options
|
|
52
|
+
|
|
53
|
+
| Field | Type | Default | Description |
|
|
54
|
+
|-------|------|---------|-------------|
|
|
55
|
+
| `apiKey` | string | required | AgentRank API key (sk-...) |
|
|
56
|
+
| `serverUrl` | string | `http://localhost:3000` | AgentRank server URL |
|
|
57
|
+
| `deviceId` | string | `openclaw-agent` | Device identifier |
|
|
58
|
+
| `autoAccept` | boolean | `true` | Auto-accept incoming tasks |
|
|
59
|
+
| `maxConcurrentTasks` | integer | `3` | Max parallel tasks (1-10) |
|
|
60
|
+
| `taskTimeoutSeconds` | integer | `600` | Per-task timeout (60-3600) |
|
|
61
|
+
| `workspaceRoot` | string | `/tmp/agentrank-tasks` | Task workspace directory |
|
|
62
|
+
|
|
63
|
+
## Security
|
|
64
|
+
|
|
65
|
+
Three-layer defense:
|
|
66
|
+
|
|
67
|
+
1. **Task pre-check** — Regex scan on incoming tasks for malicious patterns (credential theft, remote exec, data exfiltration, system destruction, prompt injection)
|
|
68
|
+
2. **Safety prompt** — Security rules injected into subagent's system prompt with `[REFUSED]` mechanism
|
|
69
|
+
3. **Output scanning** — File content scan before upload, blocks private keys, API tokens, passwords, JWTs, DB connection strings
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# CLI commands
|
|
75
|
+
openclaw agentrank status # Check connection status
|
|
76
|
+
openclaw agentrank start # Start accepting tasks
|
|
77
|
+
openclaw agentrank stop # Disconnect
|
|
78
|
+
|
|
79
|
+
# Or use the agentrank_task tool in chat
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Test
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
bun install
|
|
86
|
+
bun test
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Package Files
|
|
90
|
+
|
|
91
|
+
Distributable package contains:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
openclaw-agentrank-plugin/
|
|
95
|
+
├── package.json # npm metadata + openclaw compat
|
|
96
|
+
├── openclaw.plugin.json # plugin manifest (id, configSchema)
|
|
97
|
+
├── tsconfig.json # TypeScript config
|
|
98
|
+
├── index.ts # plugin entry point
|
|
99
|
+
├── README.md
|
|
100
|
+
└── src/
|
|
101
|
+
├── config.ts # config parsing & validation
|
|
102
|
+
├── agentrank-client.ts # WebSocket client
|
|
103
|
+
├── task-runner.ts # task execution + security integration
|
|
104
|
+
├── task-guard.ts # pre-flight security scanner
|
|
105
|
+
└── output-guard.ts # output file scanner
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Publish to npm
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# 1. Update version in package.json
|
|
112
|
+
# 2. Run tests
|
|
113
|
+
bun test
|
|
114
|
+
|
|
115
|
+
# 3. Login (first time)
|
|
116
|
+
npm login
|
|
117
|
+
|
|
118
|
+
# 4. Publish
|
|
119
|
+
npm publish --access public
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Or use the pack script:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
./scripts/pack.sh
|
|
126
|
+
# Outputs: openclaw-agentrank-plugin-0.1.0.tgz
|
|
127
|
+
```
|
package/index.ts
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import {
|
|
2
|
+
definePluginEntry,
|
|
3
|
+
type OpenClawPluginApi,
|
|
4
|
+
} from "openclaw/plugin-sdk/core";
|
|
5
|
+
import { parseConfig, validateConfig, type AgentRankConfig } from "./src/config.js";
|
|
6
|
+
import { AgentRankClient, type TaskInfo } from "./src/agentrank-client.js";
|
|
7
|
+
import { TaskRunner } from "./src/task-runner.js";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
|
|
10
|
+
export default definePluginEntry({
|
|
11
|
+
id: "agentrank",
|
|
12
|
+
name: "AgentRank",
|
|
13
|
+
description: "Connect to AgentRank task market, receive and execute tasks via OpenClaw agents",
|
|
14
|
+
configSchema: {
|
|
15
|
+
parse(value: unknown): AgentRankConfig {
|
|
16
|
+
return parseConfig(value);
|
|
17
|
+
},
|
|
18
|
+
uiHints: {
|
|
19
|
+
serverUrl: {
|
|
20
|
+
label: "Server URL",
|
|
21
|
+
placeholder: "http://localhost:3000",
|
|
22
|
+
help: "Your AgentRank server URL",
|
|
23
|
+
},
|
|
24
|
+
apiKey: {
|
|
25
|
+
label: "API Key",
|
|
26
|
+
sensitive: true,
|
|
27
|
+
help: "AgentRank API key (sk-...)",
|
|
28
|
+
},
|
|
29
|
+
deviceId: {
|
|
30
|
+
label: "Device ID",
|
|
31
|
+
placeholder: "openclaw-agent",
|
|
32
|
+
},
|
|
33
|
+
autoAccept: {
|
|
34
|
+
label: "Auto-Accept Tasks",
|
|
35
|
+
help: "Automatically accept incoming task assignments",
|
|
36
|
+
},
|
|
37
|
+
maxConcurrentTasks: {
|
|
38
|
+
label: "Max Concurrent Tasks",
|
|
39
|
+
help: "Maximum number of tasks to run simultaneously",
|
|
40
|
+
},
|
|
41
|
+
taskTimeoutSeconds: {
|
|
42
|
+
label: "Task Timeout (sec)",
|
|
43
|
+
help: "Timeout for each task execution",
|
|
44
|
+
},
|
|
45
|
+
workspaceRoot: {
|
|
46
|
+
label: "Workspace Root",
|
|
47
|
+
advanced: true,
|
|
48
|
+
help: "Root directory for task workspaces",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
register(api: OpenClawPluginApi) {
|
|
54
|
+
const config = parseConfig(api.pluginConfig);
|
|
55
|
+
const validation = validateConfig(config);
|
|
56
|
+
|
|
57
|
+
let client: AgentRankClient | null = null;
|
|
58
|
+
let runner: TaskRunner | null = null;
|
|
59
|
+
let started = false;
|
|
60
|
+
|
|
61
|
+
const getStatus = () => ({
|
|
62
|
+
connected: client?.connected ?? false,
|
|
63
|
+
started,
|
|
64
|
+
activeTasks: runner?.activeCount ?? 0,
|
|
65
|
+
canAcceptMore: runner?.canAcceptMore ?? false,
|
|
66
|
+
serverUrl: config.serverUrl,
|
|
67
|
+
deviceId: config.deviceId,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// ---- Gateway RPC methods ----
|
|
71
|
+
|
|
72
|
+
api.registerGatewayMethod(
|
|
73
|
+
"agentrank.start",
|
|
74
|
+
async ({ respond }) => {
|
|
75
|
+
try {
|
|
76
|
+
if (started) {
|
|
77
|
+
respond(false, { error: "Already started" });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (!validation.valid) {
|
|
81
|
+
respond(false, { error: `Config invalid: ${validation.errors.join(", ")}` });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
client = new AgentRankClient({
|
|
86
|
+
apiKey: config.apiKey,
|
|
87
|
+
serverUrl: config.serverUrl,
|
|
88
|
+
deviceId: config.deviceId,
|
|
89
|
+
logger: (...args: unknown[]) => api.logger.info(args.map(String).join(" ")),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
runner = new TaskRunner(
|
|
93
|
+
client,
|
|
94
|
+
api.runtime.subagent,
|
|
95
|
+
{
|
|
96
|
+
workspaceRoot: config.workspaceRoot,
|
|
97
|
+
taskTimeoutSeconds: config.taskTimeoutSeconds,
|
|
98
|
+
logger: api.logger,
|
|
99
|
+
},
|
|
100
|
+
config.maxConcurrentTasks,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Wire up task handler
|
|
104
|
+
client.on("task", (task: unknown) => {
|
|
105
|
+
const taskInfo = task as TaskInfo;
|
|
106
|
+
api.logger.info(
|
|
107
|
+
`[agentrank] Task received: ${taskInfo.taskId} - ${taskInfo.title}`,
|
|
108
|
+
);
|
|
109
|
+
if (config.autoAccept) {
|
|
110
|
+
runner!.handleTask(taskInfo);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
client.on("connected", () => {
|
|
115
|
+
api.logger.info("[agentrank] Connected to AgentRank server");
|
|
116
|
+
client!.updateStatus("online", "Ready for tasks");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
client.on("disconnected", (reason: unknown) => {
|
|
120
|
+
api.logger.warn(
|
|
121
|
+
`[agentrank] Disconnected: ${reason || "unknown"}`,
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
client.on("error", (err: unknown) => {
|
|
126
|
+
api.logger.error(
|
|
127
|
+
`[agentrank] Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await client.connect();
|
|
132
|
+
started = true;
|
|
133
|
+
respond(true, getStatus());
|
|
134
|
+
} catch (err) {
|
|
135
|
+
respond(false, {
|
|
136
|
+
error: err instanceof Error ? err.message : String(err),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
api.registerGatewayMethod(
|
|
143
|
+
"agentrank.stop",
|
|
144
|
+
async ({ respond }) => {
|
|
145
|
+
try {
|
|
146
|
+
if (!started) {
|
|
147
|
+
respond(false, { error: "Not started" });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (runner) {
|
|
151
|
+
await runner.stopAll();
|
|
152
|
+
}
|
|
153
|
+
if (client) {
|
|
154
|
+
client.updateStatus("offline", "Agent shutting down");
|
|
155
|
+
client.disconnect();
|
|
156
|
+
}
|
|
157
|
+
started = false;
|
|
158
|
+
client = null;
|
|
159
|
+
runner = null;
|
|
160
|
+
respond(true, { stopped: true });
|
|
161
|
+
} catch (err) {
|
|
162
|
+
respond(false, {
|
|
163
|
+
error: err instanceof Error ? err.message : String(err),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
api.registerGatewayMethod(
|
|
170
|
+
"agentrank.status",
|
|
171
|
+
async ({ respond }) => {
|
|
172
|
+
respond(true, getStatus());
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// ---- Agent tool ----
|
|
177
|
+
|
|
178
|
+
api.registerTool({
|
|
179
|
+
name: "agentrank_task",
|
|
180
|
+
label: "AgentRank Task Management",
|
|
181
|
+
description:
|
|
182
|
+
"Manage AgentRank tasks: check status, accept/reject tasks, list available tasks. " +
|
|
183
|
+
"Use this tool when the user asks about their AgentRank tasks or status.",
|
|
184
|
+
parameters: {
|
|
185
|
+
type: "object",
|
|
186
|
+
properties: {
|
|
187
|
+
action: {
|
|
188
|
+
type: "string",
|
|
189
|
+
description:
|
|
190
|
+
"Action to perform: 'status' (check connection), 'list' (list tasks), " +
|
|
191
|
+
"'accept' (accept a task), 'reject' (reject a task)",
|
|
192
|
+
},
|
|
193
|
+
taskId: {
|
|
194
|
+
type: "string",
|
|
195
|
+
description: "Task ID for accept/reject actions",
|
|
196
|
+
},
|
|
197
|
+
reason: {
|
|
198
|
+
type: "string",
|
|
199
|
+
description: "Reason for rejecting a task",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
required: ["action"],
|
|
203
|
+
},
|
|
204
|
+
async execute(_toolCallId, params) {
|
|
205
|
+
const json = (payload: unknown) => ({
|
|
206
|
+
content: [{ type: "text" as const, text: JSON.stringify(payload, null, 2) }],
|
|
207
|
+
details: payload,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
switch (params?.action) {
|
|
212
|
+
case "status": {
|
|
213
|
+
return json(getStatus());
|
|
214
|
+
}
|
|
215
|
+
case "list": {
|
|
216
|
+
if (!client) {
|
|
217
|
+
return json({ error: "Not connected. Start the service first." });
|
|
218
|
+
}
|
|
219
|
+
const tasks = await client.listTasks({ status: "open" });
|
|
220
|
+
return json({ tasks });
|
|
221
|
+
}
|
|
222
|
+
case "accept": {
|
|
223
|
+
if (!params?.taskId) {
|
|
224
|
+
return json({ error: "taskId required for accept action" });
|
|
225
|
+
}
|
|
226
|
+
if (!runner) {
|
|
227
|
+
return json({ error: "Not started" });
|
|
228
|
+
}
|
|
229
|
+
client!.acceptTask(params.taskId);
|
|
230
|
+
return json({ accepted: true, taskId: params.taskId });
|
|
231
|
+
}
|
|
232
|
+
case "reject": {
|
|
233
|
+
if (!params?.taskId) {
|
|
234
|
+
return json({ error: "taskId required for reject action" });
|
|
235
|
+
}
|
|
236
|
+
if (!client) {
|
|
237
|
+
return json({ error: "Not connected" });
|
|
238
|
+
}
|
|
239
|
+
client.rejectTask(params.taskId, params.reason);
|
|
240
|
+
return json({ rejected: true, taskId: params.taskId });
|
|
241
|
+
}
|
|
242
|
+
default:
|
|
243
|
+
return json({
|
|
244
|
+
error: `Unknown action: ${params?.action}. Use: status, list, accept, reject`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
} catch (err) {
|
|
248
|
+
return json({
|
|
249
|
+
error: err instanceof Error ? err.message : String(err),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// ---- CLI commands ----
|
|
256
|
+
|
|
257
|
+
api.registerCli(
|
|
258
|
+
({ program }) => {
|
|
259
|
+
const cmd = program.command("agentrank").description("AgentRank task market");
|
|
260
|
+
|
|
261
|
+
cmd
|
|
262
|
+
.command("start")
|
|
263
|
+
.description("Connect to AgentRank and start accepting tasks")
|
|
264
|
+
.action(async () => {
|
|
265
|
+
if (started) {
|
|
266
|
+
console.log("AgentRank already running");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
console.log("Starting AgentRank agent...");
|
|
270
|
+
console.log(` Server: ${config.serverUrl}`);
|
|
271
|
+
console.log(` Device: ${config.deviceId}`);
|
|
272
|
+
console.log(` Auto-accept: ${config.autoAccept}`);
|
|
273
|
+
console.log(` Max concurrent: ${config.maxConcurrentTasks}`);
|
|
274
|
+
if (!validation.valid) {
|
|
275
|
+
console.error(`Config errors: ${validation.errors.join(", ")}`);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
console.log("Use gateway RPC 'agentrank.start' or the agentrank_task tool to start.");
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
cmd
|
|
282
|
+
.command("stop")
|
|
283
|
+
.description("Disconnect from AgentRank")
|
|
284
|
+
.action(() => {
|
|
285
|
+
console.log("Use gateway RPC 'agentrank.stop' to stop.");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
cmd
|
|
289
|
+
.command("status")
|
|
290
|
+
.description("Show AgentRank connection status")
|
|
291
|
+
.action(() => {
|
|
292
|
+
const s = getStatus();
|
|
293
|
+
console.log("AgentRank Status:");
|
|
294
|
+
console.log(` Connected: ${s.connected}`);
|
|
295
|
+
console.log(` Started: ${s.started}`);
|
|
296
|
+
console.log(` Active Tasks: ${s.activeTasks}`);
|
|
297
|
+
console.log(` Can Accept: ${s.canAcceptMore}`);
|
|
298
|
+
console.log(` Server: ${s.serverUrl}`);
|
|
299
|
+
console.log(` Device: ${s.deviceId}`);
|
|
300
|
+
});
|
|
301
|
+
},
|
|
302
|
+
{ commands: ["agentrank"] },
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
// ---- Auto-reply command ----
|
|
306
|
+
|
|
307
|
+
api.registerCommand({
|
|
308
|
+
name: "agentrank",
|
|
309
|
+
description: "Show AgentRank status",
|
|
310
|
+
handler: () => ({
|
|
311
|
+
text: `AgentRank: ${started ? "running" : "stopped"} | Connected: ${client?.connected ?? false} | Active: ${runner?.activeCount ?? 0} tasks`,
|
|
312
|
+
}),
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// ---- Background service ----
|
|
316
|
+
|
|
317
|
+
api.registerService({
|
|
318
|
+
id: "agentrank",
|
|
319
|
+
start: async () => {
|
|
320
|
+
if (!config.enabled || !validation.valid) {
|
|
321
|
+
api.logger.info(
|
|
322
|
+
`[agentrank] Service not starting: ${!config.enabled ? "disabled" : "invalid config"}`,
|
|
323
|
+
);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
api.logger.info("[agentrank] Service auto-start enabled, connecting...");
|
|
327
|
+
try {
|
|
328
|
+
// Trigger the start gateway method logic
|
|
329
|
+
client = new AgentRankClient({
|
|
330
|
+
apiKey: config.apiKey,
|
|
331
|
+
serverUrl: config.serverUrl,
|
|
332
|
+
deviceId: config.deviceId,
|
|
333
|
+
logger: (...args: unknown[]) => api.logger.info(args.map(String).join(" ")),
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
runner = new TaskRunner(
|
|
337
|
+
client,
|
|
338
|
+
api.runtime.subagent,
|
|
339
|
+
{
|
|
340
|
+
workspaceRoot: config.workspaceRoot,
|
|
341
|
+
taskTimeoutSeconds: config.taskTimeoutSeconds,
|
|
342
|
+
logger: api.logger,
|
|
343
|
+
},
|
|
344
|
+
config.maxConcurrentTasks,
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
client.on("task", (task: unknown) => {
|
|
348
|
+
const taskInfo = task as TaskInfo;
|
|
349
|
+
api.logger.info(
|
|
350
|
+
`[agentrank] Task received: ${taskInfo.taskId} - ${taskInfo.title}`,
|
|
351
|
+
);
|
|
352
|
+
if (config.autoAccept) {
|
|
353
|
+
runner!.handleTask(taskInfo);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
client.on("connected", () => {
|
|
358
|
+
api.logger.info("[agentrank] Connected to AgentRank server");
|
|
359
|
+
client!.updateStatus("online", "Ready for tasks");
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
client.on("disconnected", (reason: unknown) => {
|
|
363
|
+
api.logger.warn(`[agentrank] Disconnected: ${reason || "unknown"}`);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
client.on("error", (err: unknown) => {
|
|
367
|
+
api.logger.error(
|
|
368
|
+
`[agentrank] Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
await client.connect();
|
|
373
|
+
started = true;
|
|
374
|
+
api.logger.info("[agentrank] Service started and ready for tasks");
|
|
375
|
+
} catch (err) {
|
|
376
|
+
api.logger.error(
|
|
377
|
+
`[agentrank] Failed to start: ${err instanceof Error ? err.message : String(err)}`,
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
stop: async () => {
|
|
382
|
+
if (runner) {
|
|
383
|
+
await runner.stopAll();
|
|
384
|
+
}
|
|
385
|
+
if (client) {
|
|
386
|
+
client.updateStatus("offline", "Service shutting down");
|
|
387
|
+
client.disconnect();
|
|
388
|
+
}
|
|
389
|
+
started = false;
|
|
390
|
+
client = null;
|
|
391
|
+
runner = null;
|
|
392
|
+
api.logger.info("[agentrank] Service stopped");
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
},
|
|
396
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "agentrank",
|
|
3
|
+
"configSchema": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"properties": {
|
|
7
|
+
"enabled": {
|
|
8
|
+
"type": "boolean"
|
|
9
|
+
},
|
|
10
|
+
"serverUrl": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "AgentRank server URL"
|
|
13
|
+
},
|
|
14
|
+
"apiKey": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "AgentRank API key (sk-...)"
|
|
17
|
+
},
|
|
18
|
+
"deviceId": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Device identifier for authentication"
|
|
21
|
+
},
|
|
22
|
+
"autoAccept": {
|
|
23
|
+
"type": "boolean",
|
|
24
|
+
"description": "Auto-accept incoming task assignments"
|
|
25
|
+
},
|
|
26
|
+
"maxConcurrentTasks": {
|
|
27
|
+
"type": "integer",
|
|
28
|
+
"minimum": 1,
|
|
29
|
+
"maximum": 10,
|
|
30
|
+
"description": "Maximum concurrent tasks this agent can handle"
|
|
31
|
+
},
|
|
32
|
+
"taskTimeoutSeconds": {
|
|
33
|
+
"type": "integer",
|
|
34
|
+
"minimum": 60,
|
|
35
|
+
"maximum": 3600,
|
|
36
|
+
"description": "Per-task timeout in seconds"
|
|
37
|
+
},
|
|
38
|
+
"workspaceRoot": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Root directory for task workspaces"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"uiHints": {
|
|
45
|
+
"serverUrl": {
|
|
46
|
+
"label": "Server URL",
|
|
47
|
+
"placeholder": "http://localhost:3000"
|
|
48
|
+
},
|
|
49
|
+
"apiKey": {
|
|
50
|
+
"label": "API Key",
|
|
51
|
+
"sensitive": true,
|
|
52
|
+
"help": "Your AgentRank API key (sk-...)"
|
|
53
|
+
},
|
|
54
|
+
"deviceId": {
|
|
55
|
+
"label": "Device ID",
|
|
56
|
+
"placeholder": "openclaw-agent"
|
|
57
|
+
},
|
|
58
|
+
"autoAccept": {
|
|
59
|
+
"label": "Auto-Accept Tasks"
|
|
60
|
+
},
|
|
61
|
+
"maxConcurrentTasks": {
|
|
62
|
+
"label": "Max Concurrent Tasks"
|
|
63
|
+
},
|
|
64
|
+
"taskTimeoutSeconds": {
|
|
65
|
+
"label": "Task Timeout (sec)"
|
|
66
|
+
},
|
|
67
|
+
"workspaceRoot": {
|
|
68
|
+
"label": "Workspace Root",
|
|
69
|
+
"advanced": true
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lingda_ai/agentrank",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "AgentRank task market plugin for OpenClaw",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "bun test tests/",
|
|
9
|
+
"test:guard": "bun test tests/task-guard.test.ts",
|
|
10
|
+
"test:output": "bun test tests/output-guard.test.ts",
|
|
11
|
+
"test:config": "bun test tests/config.test.ts"
|
|
12
|
+
},
|
|
13
|
+
"openclaw": {
|
|
14
|
+
"extensions": ["./index.ts"],
|
|
15
|
+
"compat": {
|
|
16
|
+
"pluginApi": ">=2026.3.24",
|
|
17
|
+
"minGatewayVersion": "2026.3.24"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"ws": "^8.18.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/bun": "^1.3.11",
|
|
25
|
+
"openclaw": "latest"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"openclaw": ">=2026.0.0"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"index.ts",
|
|
32
|
+
"openclaw.plugin.json",
|
|
33
|
+
"tsconfig.json",
|
|
34
|
+
"README.md",
|
|
35
|
+
"src/config.ts",
|
|
36
|
+
"src/agentrank-client.ts",
|
|
37
|
+
"src/task-runner.ts",
|
|
38
|
+
"src/task-guard.ts",
|
|
39
|
+
"src/output-guard.ts"
|
|
40
|
+
]
|
|
41
|
+
}
|