@agentlinkdev/agentlink 0.1.0
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/LICENSE +21 -0
- package/README.md +73 -0
- package/bin/cli.js +382 -0
- package/openclaw.plugin.json +83 -0
- package/package.json +62 -0
- package/src/channel.ts +91 -0
- package/src/contacts.ts +69 -0
- package/src/index.ts +269 -0
- package/src/invite.ts +99 -0
- package/src/jobs.ts +156 -0
- package/src/mqtt-client.ts +142 -0
- package/src/mqtt-service.ts +157 -0
- package/src/routing.ts +53 -0
- package/src/state.ts +169 -0
- package/src/tools.ts +352 -0
- package/src/types.ts +258 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AgentLink
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# AgentLink
|
|
2
|
+
|
|
3
|
+
Agent-to-agent coordination for [OpenClaw](https://openclaw.dev). Your agent talks to other people's agents to get things done.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @agentlinkdev/agentlink setup
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
That's it. One command installs the plugin, configures OpenClaw, and generates your agent identity.
|
|
12
|
+
|
|
13
|
+
### Join a group
|
|
14
|
+
|
|
15
|
+
Got an invite code? Include it in setup:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @agentlinkdev/agentlink setup --join AB3X7K
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then restart the gateway:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
openclaw gateway
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Uninstall
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx @agentlinkdev/agentlink uninstall
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Your identity and group data are preserved in `~/.agentlink/`. To fully wipe: `rm -rf ~/.agentlink`.
|
|
34
|
+
|
|
35
|
+
## How it works
|
|
36
|
+
|
|
37
|
+
AgentLink is an OpenClaw plugin that connects agents via MQTT. When you start a coordination, your agent:
|
|
38
|
+
|
|
39
|
+
1. Creates a group with a goal and done condition
|
|
40
|
+
2. Invites other agents (by contact name or invite code)
|
|
41
|
+
3. Coordinates via hub-and-spoke messaging — your agent drives, others respond
|
|
42
|
+
4. Submits jobs to agents based on their capabilities
|
|
43
|
+
5. Declares completion when the goal is met
|
|
44
|
+
|
|
45
|
+
All coordination happens in the background. You state the goal. Agents handle the rest.
|
|
46
|
+
|
|
47
|
+
## Agent tools
|
|
48
|
+
|
|
49
|
+
| Tool | Description |
|
|
50
|
+
|------|-------------|
|
|
51
|
+
| `agentlink_coordinate` | Start a new coordination with a goal and participants |
|
|
52
|
+
| `agentlink_invite_agent` | Invite an agent to join a group |
|
|
53
|
+
| `agentlink_join_group` | Join a group via invite code |
|
|
54
|
+
| `agentlink_submit_job` | Send a job to an agent by capability |
|
|
55
|
+
| `agentlink_complete` | Declare a coordination complete |
|
|
56
|
+
|
|
57
|
+
## CLI commands
|
|
58
|
+
|
|
59
|
+
After installation, these are available via `openclaw agentlink`:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
openclaw agentlink status # connection status, active groups
|
|
63
|
+
openclaw agentlink contacts # list known contacts
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
|
|
68
|
+
- [OpenClaw](https://openclaw.dev) installed with CLI on PATH
|
|
69
|
+
- Node.js >= 18
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { execSync, spawn } = require("child_process");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
const readline = require("readline");
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Constants
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
const OC_CONFIG_PATH = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
15
|
+
const AGENTLINK_DIR = path.join(os.homedir(), ".agentlink");
|
|
16
|
+
const STATE_FILE = path.join(AGENTLINK_DIR, "state.json");
|
|
17
|
+
const PLUGIN_NAME = "agentlink";
|
|
18
|
+
const NPM_PACKAGE = "@agentlinkdev/agentlink";
|
|
19
|
+
const AGENT_ID_RE = /^[a-z0-9][a-z0-9\-]{2,39}$/;
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Helpers
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
function info(msg) {
|
|
26
|
+
console.log(` ${msg}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function success(msg) {
|
|
30
|
+
console.log(` [ok] ${msg}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function warn(msg) {
|
|
34
|
+
console.log(` [warn] ${msg}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function fatal(msg) {
|
|
38
|
+
console.error(` [error] ${msg}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readOcConfig() {
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(fs.readFileSync(OC_CONFIG_PATH, "utf-8"));
|
|
45
|
+
} catch {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getNestedValue(obj, keyPath) {
|
|
51
|
+
const parts = keyPath.split(".");
|
|
52
|
+
let cur = obj;
|
|
53
|
+
for (const p of parts) {
|
|
54
|
+
if (cur == null || typeof cur !== "object") return undefined;
|
|
55
|
+
cur = cur[p];
|
|
56
|
+
}
|
|
57
|
+
return cur;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isPluginInstalled() {
|
|
61
|
+
const cfg = readOcConfig();
|
|
62
|
+
const installs = getNestedValue(cfg, "plugins.installs");
|
|
63
|
+
if (installs && typeof installs === "object") {
|
|
64
|
+
return PLUGIN_NAME in installs;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function verifyOpenclaw() {
|
|
70
|
+
try {
|
|
71
|
+
execSync("openclaw --version", { stdio: "pipe" });
|
|
72
|
+
} catch {
|
|
73
|
+
fatal(
|
|
74
|
+
"openclaw CLI not found on PATH.\n" +
|
|
75
|
+
" Install it first: https://docs.openclaw.dev/getting-started",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function ocConfigSet(key, value) {
|
|
81
|
+
const jsonValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
82
|
+
execSync(`openclaw config set ${key} '${jsonValue}'`, { stdio: "pipe" });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function mergeIntoArray(keyPath, entry) {
|
|
86
|
+
const cfg = readOcConfig();
|
|
87
|
+
const current = getNestedValue(cfg, keyPath);
|
|
88
|
+
const arr = Array.isArray(current) ? current : [];
|
|
89
|
+
if (arr.includes(entry)) {
|
|
90
|
+
info(`${keyPath} already contains "${entry}"`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const merged = [...arr, entry];
|
|
94
|
+
ocConfigSet(keyPath, merged);
|
|
95
|
+
|
|
96
|
+
// Verify
|
|
97
|
+
const after = readOcConfig();
|
|
98
|
+
const result = getNestedValue(after, keyPath);
|
|
99
|
+
if (!Array.isArray(result) || !result.includes(entry)) {
|
|
100
|
+
fatal(`Failed to set ${keyPath} — verification failed.`);
|
|
101
|
+
}
|
|
102
|
+
success(`${keyPath} updated`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function removeFromArray(keyPath, entry) {
|
|
106
|
+
const cfg = readOcConfig();
|
|
107
|
+
const current = getNestedValue(cfg, keyPath);
|
|
108
|
+
if (!Array.isArray(current) || !current.includes(entry)) {
|
|
109
|
+
info(`${keyPath} does not contain "${entry}" — skipping`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const filtered = current.filter((x) => x !== entry);
|
|
113
|
+
ocConfigSet(keyPath, filtered);
|
|
114
|
+
|
|
115
|
+
// Verify
|
|
116
|
+
const after = readOcConfig();
|
|
117
|
+
const result = getNestedValue(after, keyPath);
|
|
118
|
+
if (Array.isArray(result) && result.includes(entry)) {
|
|
119
|
+
fatal(`Failed to remove "${entry}" from ${keyPath} — verification failed.`);
|
|
120
|
+
}
|
|
121
|
+
success(`Removed "${entry}" from ${keyPath}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function readState() {
|
|
125
|
+
try {
|
|
126
|
+
return JSON.parse(fs.readFileSync(STATE_FILE, "utf-8"));
|
|
127
|
+
} catch {
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function writeState(data) {
|
|
133
|
+
fs.mkdirSync(AGENTLINK_DIR, { recursive: true });
|
|
134
|
+
fs.writeFileSync(STATE_FILE, JSON.stringify(data, null, 2));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function generateAgentId() {
|
|
138
|
+
const hostname = os.hostname().toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
139
|
+
const ts = Date.now().toString(36);
|
|
140
|
+
let id = `agent-${hostname}-${ts}`;
|
|
141
|
+
// Truncate to 40 chars max
|
|
142
|
+
if (id.length > 40) {
|
|
143
|
+
id = id.substring(0, 40);
|
|
144
|
+
}
|
|
145
|
+
// Ensure trailing char is not a dash
|
|
146
|
+
id = id.replace(/-+$/, "");
|
|
147
|
+
// Validate
|
|
148
|
+
if (!AGENT_ID_RE.test(id)) {
|
|
149
|
+
// Fallback: use just agent-timestamp
|
|
150
|
+
id = `agent-${ts}`;
|
|
151
|
+
}
|
|
152
|
+
return id;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function promptYesNo(question) {
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
if (!process.stdin.isTTY) {
|
|
158
|
+
resolve(false);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const rl = readline.createInterface({
|
|
162
|
+
input: process.stdin,
|
|
163
|
+
output: process.stdout,
|
|
164
|
+
});
|
|
165
|
+
rl.question(` ${question} `, (answer) => {
|
|
166
|
+
rl.close();
|
|
167
|
+
resolve(answer.trim().toLowerCase().startsWith("y"));
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Setup command
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
async function cmdSetup(args) {
|
|
177
|
+
const joinIdx = args.indexOf("--join");
|
|
178
|
+
const joinCode = joinIdx !== -1 ? args[joinIdx + 1] : null;
|
|
179
|
+
const localIdx = args.indexOf("--local");
|
|
180
|
+
const localPath = localIdx !== -1 ? args[localIdx + 1] : null;
|
|
181
|
+
|
|
182
|
+
if (joinIdx !== -1 && !joinCode) {
|
|
183
|
+
fatal("--join requires a CODE argument");
|
|
184
|
+
}
|
|
185
|
+
if (localIdx !== -1 && !localPath) {
|
|
186
|
+
fatal("--local requires a PATH argument");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log("\n AgentLink Setup\n");
|
|
190
|
+
|
|
191
|
+
// 1. Verify openclaw on PATH
|
|
192
|
+
verifyOpenclaw();
|
|
193
|
+
success("openclaw CLI found");
|
|
194
|
+
|
|
195
|
+
// 2. Check if already installed
|
|
196
|
+
if (isPluginInstalled()) {
|
|
197
|
+
if (!joinCode) {
|
|
198
|
+
const state = readState();
|
|
199
|
+
console.log("\n AgentLink is already installed.");
|
|
200
|
+
if (state.agent_id) {
|
|
201
|
+
info(`Agent ID: ${state.agent_id}`);
|
|
202
|
+
}
|
|
203
|
+
info("Run 'agentlink uninstall' to remove.");
|
|
204
|
+
console.log("");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
info("Plugin already installed — processing --join only");
|
|
208
|
+
} else {
|
|
209
|
+
// 3. Install plugin
|
|
210
|
+
const source = localPath || NPM_PACKAGE;
|
|
211
|
+
info(`Installing plugin from ${source}...`);
|
|
212
|
+
try {
|
|
213
|
+
execSync(`openclaw plugins install ${source}`, { stdio: "inherit" });
|
|
214
|
+
} catch {
|
|
215
|
+
fatal("Plugin installation failed.");
|
|
216
|
+
}
|
|
217
|
+
success("Plugin installed");
|
|
218
|
+
|
|
219
|
+
// 4. Set plugins.allow
|
|
220
|
+
mergeIntoArray("plugins.allow", PLUGIN_NAME);
|
|
221
|
+
|
|
222
|
+
// 5. Set tools.alsoAllow
|
|
223
|
+
const cfg = readOcConfig();
|
|
224
|
+
const toolsAllow = getNestedValue(cfg, "tools.allow");
|
|
225
|
+
if (Array.isArray(toolsAllow) && toolsAllow.length > 0) {
|
|
226
|
+
warn(
|
|
227
|
+
"tools.allow is already set — skipping tools.alsoAllow to avoid conflict.\n" +
|
|
228
|
+
" Add agentlink tools manually to tools.allow if needed.",
|
|
229
|
+
);
|
|
230
|
+
} else {
|
|
231
|
+
mergeIntoArray("tools.alsoAllow", PLUGIN_NAME);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// 6. Generate persistent agent ID
|
|
236
|
+
const state = readState();
|
|
237
|
+
if (!state.agent_id) {
|
|
238
|
+
state.agent_id = generateAgentId();
|
|
239
|
+
writeState(state);
|
|
240
|
+
success(`Agent ID generated: ${state.agent_id}`);
|
|
241
|
+
} else {
|
|
242
|
+
info(`Agent ID: ${state.agent_id} (existing)`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 7. Handle --join
|
|
246
|
+
if (joinCode) {
|
|
247
|
+
if (!state.pending_joins) {
|
|
248
|
+
state.pending_joins = [];
|
|
249
|
+
}
|
|
250
|
+
if (!state.pending_joins.includes(joinCode)) {
|
|
251
|
+
state.pending_joins.push(joinCode);
|
|
252
|
+
writeState(state);
|
|
253
|
+
success(`Queued join code: ${joinCode}`);
|
|
254
|
+
} else {
|
|
255
|
+
info(`Join code ${joinCode} already queued`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 8. Print summary
|
|
260
|
+
console.log("\n --- Setup Summary ---");
|
|
261
|
+
info(`Plugin: installed`);
|
|
262
|
+
info(`Agent ID: ${state.agent_id}`);
|
|
263
|
+
if (joinCode) {
|
|
264
|
+
info(`Join: ${joinCode} (will process on gateway start)`);
|
|
265
|
+
}
|
|
266
|
+
info(`Data dir: ${AGENTLINK_DIR}`);
|
|
267
|
+
console.log("");
|
|
268
|
+
|
|
269
|
+
// 9. Prompt to restart gateway
|
|
270
|
+
if (!process.stdin.isTTY) {
|
|
271
|
+
info("Restart the OpenClaw gateway to activate AgentLink:");
|
|
272
|
+
info(" openclaw gateway stop && openclaw gateway");
|
|
273
|
+
console.log("");
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const restart = await promptYesNo("Restart gateway now? (y/n)");
|
|
278
|
+
if (restart) {
|
|
279
|
+
info("Stopping gateway...");
|
|
280
|
+
try {
|
|
281
|
+
execSync("openclaw gateway stop", { stdio: "pipe" });
|
|
282
|
+
} catch {
|
|
283
|
+
// gateway may not be running — that's fine
|
|
284
|
+
}
|
|
285
|
+
info("Starting gateway...");
|
|
286
|
+
const child = spawn("openclaw", ["gateway"], {
|
|
287
|
+
detached: true,
|
|
288
|
+
stdio: "ignore",
|
|
289
|
+
});
|
|
290
|
+
child.unref();
|
|
291
|
+
success("Gateway restarted in background");
|
|
292
|
+
} else {
|
|
293
|
+
info("Restart manually: openclaw gateway stop && openclaw gateway");
|
|
294
|
+
}
|
|
295
|
+
console.log("");
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
// Uninstall command
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
|
|
302
|
+
async function cmdUninstall() {
|
|
303
|
+
console.log("\n AgentLink Uninstall\n");
|
|
304
|
+
|
|
305
|
+
// 1. Verify openclaw on PATH
|
|
306
|
+
verifyOpenclaw();
|
|
307
|
+
|
|
308
|
+
// 2. Check if installed
|
|
309
|
+
if (!isPluginInstalled()) {
|
|
310
|
+
info("AgentLink is not installed. Nothing to do.");
|
|
311
|
+
console.log("");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// 3. Remove from tools.alsoAllow
|
|
316
|
+
removeFromArray("tools.alsoAllow", PLUGIN_NAME);
|
|
317
|
+
|
|
318
|
+
// 4. Remove from plugins.allow
|
|
319
|
+
removeFromArray("plugins.allow", PLUGIN_NAME);
|
|
320
|
+
|
|
321
|
+
// 5. Uninstall plugin
|
|
322
|
+
info("Uninstalling plugin...");
|
|
323
|
+
try {
|
|
324
|
+
execSync(`openclaw plugins uninstall ${PLUGIN_NAME}`, { stdio: "inherit" });
|
|
325
|
+
} catch {
|
|
326
|
+
warn("Plugin uninstall command failed (may already be removed).");
|
|
327
|
+
}
|
|
328
|
+
success("Plugin uninstalled");
|
|
329
|
+
|
|
330
|
+
// 6. Print preservation notice
|
|
331
|
+
console.log("");
|
|
332
|
+
info(`Uninstalled. Identity preserved in ${AGENTLINK_DIR}`);
|
|
333
|
+
info(`To wipe completely: rm -rf ${AGENTLINK_DIR}`);
|
|
334
|
+
console.log("");
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// Usage
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
|
|
341
|
+
function printUsage() {
|
|
342
|
+
console.log(`
|
|
343
|
+
Usage: agentlink <command> [options]
|
|
344
|
+
|
|
345
|
+
Commands:
|
|
346
|
+
setup Install and configure AgentLink for OpenClaw
|
|
347
|
+
uninstall Remove AgentLink configuration (preserves identity)
|
|
348
|
+
|
|
349
|
+
Setup options:
|
|
350
|
+
--join CODE Join a coordination group after setup
|
|
351
|
+
--local PATH Install from a local path instead of npm
|
|
352
|
+
`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
356
|
+
// Main
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
|
|
359
|
+
async function main() {
|
|
360
|
+
const args = process.argv.slice(2);
|
|
361
|
+
const command = args[0];
|
|
362
|
+
|
|
363
|
+
switch (command) {
|
|
364
|
+
case "setup":
|
|
365
|
+
await cmdSetup(args.slice(1));
|
|
366
|
+
break;
|
|
367
|
+
case "uninstall":
|
|
368
|
+
await cmdUninstall();
|
|
369
|
+
break;
|
|
370
|
+
default:
|
|
371
|
+
printUsage();
|
|
372
|
+
if (command && command !== "--help" && command !== "-h") {
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
main().catch((err) => {
|
|
380
|
+
console.error(" [error]", err.message || err);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "agentlink",
|
|
3
|
+
"name": "AgentLink",
|
|
4
|
+
"description": "Agent-to-agent coordination via MQTT. Your agent talks to other people's agents.",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"configSchema": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": [],
|
|
10
|
+
"properties": {
|
|
11
|
+
"brokerUrl": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "MQTT broker URL (mqtts:// for TLS, mqtt:// for local dev)",
|
|
14
|
+
"default": "mqtt://broker.emqx.io:1883"
|
|
15
|
+
},
|
|
16
|
+
"brokerUsername": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "MQTT broker username"
|
|
19
|
+
},
|
|
20
|
+
"brokerPassword": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "MQTT broker password"
|
|
23
|
+
},
|
|
24
|
+
"agent": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["id"],
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"id": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Unique agent identifier (e.g. rupul-macbook)",
|
|
32
|
+
"pattern": "^[a-z0-9][a-z0-9\\-]{2,39}$"
|
|
33
|
+
},
|
|
34
|
+
"description": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Human-readable agent description"
|
|
37
|
+
},
|
|
38
|
+
"capabilities": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"maxItems": 5,
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"required": ["name", "tool"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"name": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Capability name (exact match for routing)"
|
|
48
|
+
},
|
|
49
|
+
"tool": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "OpenClaw tool ID this capability maps to"
|
|
52
|
+
},
|
|
53
|
+
"description": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "What this capability does"
|
|
56
|
+
},
|
|
57
|
+
"input_hint": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "What input the capability expects"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"output_mode": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"enum": ["user", "debug"],
|
|
69
|
+
"default": "user",
|
|
70
|
+
"description": "user = outcomes only, debug = coordination steps visible"
|
|
71
|
+
},
|
|
72
|
+
"job_timeout_ms": {
|
|
73
|
+
"type": "number",
|
|
74
|
+
"default": 60000,
|
|
75
|
+
"description": "Job timeout in milliseconds"
|
|
76
|
+
},
|
|
77
|
+
"data_dir": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"description": "Override data directory (default: ~/.agentlink)"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentlinkdev/agentlink",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent-to-agent coordination for OpenClaw. Your agent talks to other people's agents.",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentlink": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"openclaw.plugin.json",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"openclaw": {
|
|
17
|
+
"extensions": [
|
|
18
|
+
"./src/index.ts"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"lint": "eslint src/",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"prepublishOnly": "npm test"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"openclaw",
|
|
30
|
+
"agent",
|
|
31
|
+
"coordination",
|
|
32
|
+
"mqtt",
|
|
33
|
+
"multi-agent",
|
|
34
|
+
"a2a",
|
|
35
|
+
"plugin"
|
|
36
|
+
],
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/agentlink-dev/openclaw.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/agentlink-dev/openclaw/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/agentlink-dev/openclaw#readme",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@sinclair/typebox": "^0.34.0",
|
|
51
|
+
"mqtt": "^5.10.0",
|
|
52
|
+
"uuid": "^11.0.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"vitest": "^3.0.0",
|
|
56
|
+
"typescript": "^5.7.0",
|
|
57
|
+
"@types/uuid": "^10.0.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"openclaw": "*"
|
|
61
|
+
}
|
|
62
|
+
}
|