@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 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
+ }