bus-agent 2.3.5 → 2.3.6
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/.env.bus +1 -1
- package/AGENTS.md +6 -6
- package/README.md +4 -4
- package/SKILL.md +23 -23
- package/backup.js +4 -4
- package/bridge.js +5 -5
- package/bus-aliases.sh +9 -9
- package/bus-cli.js +18 -18
- package/bus-tool.js +7 -7
- package/claude-mcp.json +2 -2
- package/clients/{coco-client.ts → bus-client.ts} +6 -6
- package/clients/{coco_client.py → bus_client.py} +7 -7
- package/cursor-mcp.json +1 -1
- package/doctor.js +1 -1
- package/hermes-forwarder.js +1 -1
- package/hermes.example.json +2 -2
- package/index.js +1 -1
- package/lib/backup.js +8 -8
- package/lib/bus.js +1 -1
- package/lib/daemon.js +7 -7
- package/lib/doctor.js +3 -3
- package/lib/mcp.js +10 -10
- package/lib/memory.js +1 -1
- package/lib/orchestrator.js +1 -1
- package/lib/scheduler.js +2 -2
- package/lib/tunnel.js +11 -11
- package/mcporter.example.json +2 -2
- package/opencode-mcp.json +2 -2
- package/package.json +1 -1
- package/scripts/install.bat +1 -1
- package/scripts/install.ps1 +10 -10
- package/setup.js +15 -15
- package/tunnel.js +2 -2
- package/webhook-gateway.js +2 -2
package/.env.bus
CHANGED
package/AGENTS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# MCP
|
|
1
|
+
# MCP Bus — AGENTS.md
|
|
2
2
|
|
|
3
3
|
## Identity
|
|
4
4
|
|
|
5
|
-
MCP
|
|
5
|
+
MCP Bus is a bridge agent that connects OpenClaw and Hermes Agent. It lives in the workspace at `mcp-coco/`.
|
|
6
6
|
|
|
7
7
|
## Files
|
|
8
8
|
|
|
@@ -21,17 +21,17 @@ MCP CoCo is a bridge agent that connects OpenClaw and Hermes Agent. It lives in
|
|
|
21
21
|
- **`ask_hermes`** — Talk to Hermes. Supports multi-turn via `session_key`.
|
|
22
22
|
- **`hermes_send`** — Send messages through Hermes to chat platforms.
|
|
23
23
|
- **`hermes_channels`** — List available Hermes channels.
|
|
24
|
-
- **`
|
|
24
|
+
- **`bus_health`** — Check bridge + Hermes health.
|
|
25
25
|
|
|
26
26
|
## Operational Notes
|
|
27
27
|
|
|
28
|
-
- Daemon mode writes PID to `.
|
|
28
|
+
- Daemon mode writes PID to `.bus-daemon.pid`
|
|
29
29
|
- Hermes session keys stored in `.sessions/` directory
|
|
30
|
-
- Logs go to `
|
|
30
|
+
- Logs go to `bus-daemon.log` in daemon mode
|
|
31
31
|
- Daemon auto-starts Hermes MCP backend (`hermes mcp serve --accept-hooks`)
|
|
32
32
|
|
|
33
33
|
## Related
|
|
34
34
|
|
|
35
|
-
- [MCP
|
|
35
|
+
- [MCP Bus README](./README.md)
|
|
36
36
|
- [MCP Protocol Spec](https://modelcontextprotocol.io)
|
|
37
37
|
- [Hermes Agent](https://github.com/NousResearch/hermes-agent)
|
package/README.md
CHANGED
|
@@ -88,8 +88,8 @@ Agent memory system with dual-mode search:
|
|
|
88
88
|
|
|
89
89
|
### Client SDKs
|
|
90
90
|
|
|
91
|
-
- **Python** — `clients/
|
|
92
|
-
- **TypeScript** — `clients/
|
|
91
|
+
- **Python** — `clients/bus_client.py` provides `BusClient` with methods for registration, messaging, and channel operations.
|
|
92
|
+
- **TypeScript** — `clients/bus-client.ts` provides the same interface for Node.js environments.
|
|
93
93
|
|
|
94
94
|
### External Bridges
|
|
95
95
|
|
|
@@ -145,7 +145,7 @@ Or from a local clone:
|
|
|
145
145
|
### Environment Variables
|
|
146
146
|
|
|
147
147
|
```bash
|
|
148
|
-
export
|
|
148
|
+
export BUS_AGENT=my-agent # Your agent name on the bus
|
|
149
149
|
export BUS_DIR=/path/to/data # Custom data directory (default: ./.bus/ in CWD)
|
|
150
150
|
```
|
|
151
151
|
|
|
@@ -257,7 +257,7 @@ bus memory clear <agent> [--namespace]
|
|
|
257
257
|
| Auto-Reply | 3 | `add`, `remove`, `list` |
|
|
258
258
|
| Workflows | 4 | `create`, `run`, `remove`, `list` |
|
|
259
259
|
| Memory | 10 | `store`, `search`, `recall`, `list`, `forget`, `stats`, `archive`, `clear`, `configure`, `rebuild` |
|
|
260
|
-
| Utilities | 4 | `
|
|
260
|
+
| Utilities | 4 | `bus_health`, `ask_hermes`, `hermes_send`, `hermes_channels` |
|
|
261
261
|
|
|
262
262
|
All tools are prefixed with their category (e.g., `agent_register`, `memory_search`). Full schemas via MCP `tools/list` or `bus help`.
|
|
263
263
|
|
package/SKILL.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# MCP
|
|
1
|
+
# MCP Bus — Universal Agent Communication Hub
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
MCP
|
|
5
|
+
MCP Bus is an **Agent Communication Hub** — an MCP server that lets **any MCP-compatible agent** talk to any other.
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
OpenClaw (Andul) ──┐
|
|
9
|
-
├──▶ MCP
|
|
9
|
+
├──▶ MCP Bus (Agent Bus) ◀──▶ Claude Desktop
|
|
10
10
|
Hermes Agent ──────┘ Cursor
|
|
11
11
|
│ Any MCP agent
|
|
12
12
|
├─ message_send(to, msg)
|
|
@@ -26,7 +26,7 @@ GitHub: https://github.com/ClewCode/bus-agent
|
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
# Stdio mode (via mcporter)
|
|
29
|
-
mcporter add
|
|
29
|
+
mcporter add bus-agent --stdio "node E:\_system\.openclaw\workspace\repos\mcp-coco\index.js"
|
|
30
30
|
|
|
31
31
|
# Daemon mode
|
|
32
32
|
node E:\_system\.openclaw\workspace\repos\mcp-coco\index.js --daemon
|
|
@@ -44,7 +44,7 @@ node E:\_system\.openclaw\workspace\repos\mcp-coco\index.js --health
|
|
|
44
44
|
| `ask_hermes(prompt, session_key?)` | Talk to Hermes Agent (multi-turn) |
|
|
45
45
|
| `hermes_send(target, message)` | Send via Hermes to chat platform |
|
|
46
46
|
| `hermes_channels(platform?)` | List Hermes channels |
|
|
47
|
-
| `
|
|
47
|
+
| `bus_health()` | Health check (CLI + gateway) |
|
|
48
48
|
|
|
49
49
|
### Agent Bus (new)
|
|
50
50
|
|
|
@@ -94,36 +94,36 @@ bus memory rebuild andul
|
|
|
94
94
|
|
|
95
95
|
```bash
|
|
96
96
|
# Agent A registers
|
|
97
|
-
mcporter call
|
|
97
|
+
mcporter call bus-agent.agent_register name="alice"
|
|
98
98
|
|
|
99
99
|
# Agent A sends to B
|
|
100
|
-
mcporter call
|
|
100
|
+
mcporter call bus-agent.message_send from="alice" to="bob" message="Hello!"
|
|
101
101
|
|
|
102
102
|
# Agent B fetches later
|
|
103
|
-
mcporter call
|
|
103
|
+
mcporter call bus-agent.message_fetch agent_name="bob"
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
### 2. Register → Wait (long-poll, real-time)
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
109
|
# Agent B waits for messages (blocks up to 30s)
|
|
110
|
-
mcporter call
|
|
110
|
+
mcporter call bus-agent.message_wait agent_name="bob" timeout_ms=60000
|
|
111
111
|
|
|
112
112
|
# Agent A sends → B gets the message immediately
|
|
113
|
-
mcporter call
|
|
113
|
+
mcporter call bus-agent.message_send from="alice" to="bob" message="Ping!"
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
### 3. Channel (multi-agent chat)
|
|
117
117
|
|
|
118
118
|
```bash
|
|
119
119
|
# Create
|
|
120
|
-
mcporter call
|
|
120
|
+
mcporter call bus-agent.channel_create channel_id="dev" topic="Dev chat"
|
|
121
121
|
|
|
122
122
|
# Join
|
|
123
|
-
mcporter call
|
|
123
|
+
mcporter call bus-agent.channel_join channel_id="dev" agent_name="alice"
|
|
124
124
|
|
|
125
125
|
# Chat (sent to all members)
|
|
126
|
-
mcporter call
|
|
126
|
+
mcporter call bus-agent.channel_send channel_id="dev" from="alice" message="Hey team!"
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
## Webhook Gateway (port 8080)
|
|
@@ -163,13 +163,13 @@ node hermes-forwarder.js
|
|
|
163
163
|
|
|
164
164
|
## Scheduled Tasks (Windows)
|
|
165
165
|
|
|
166
|
-
All
|
|
166
|
+
All Bus services can run as Windows Scheduled Tasks (auto-start on boot, auto-restart on crash):
|
|
167
167
|
|
|
168
168
|
| Task | Purpose | Port |
|
|
169
169
|
|------|---------|------|
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
170
|
+
| `MCP_Bus_Daemon` | Bus MCP server (agent bus) | stdio |
|
|
171
|
+
| `MCP_Bus_Webhook` | Webhook Gateway | :8080 |
|
|
172
|
+
| `MCP_Bus_HermesFwd` | Hermes Auto-Forwarder | — |
|
|
173
173
|
|
|
174
174
|
Install:
|
|
175
175
|
```powershell
|
|
@@ -182,7 +182,7 @@ CLI agents (OpenCode, Claude Code, shell scripts) can access the message bus **d
|
|
|
182
182
|
|
|
183
183
|
```bash
|
|
184
184
|
# Set your agent name (optional, defaults to $USER)
|
|
185
|
-
export
|
|
185
|
+
export BUS_AGENT=opencode
|
|
186
186
|
|
|
187
187
|
# List agents
|
|
188
188
|
node bus-cli.js agents
|
|
@@ -210,7 +210,7 @@ node bus.js send andul "Hey!"
|
|
|
210
210
|
Set this in your shell profile:
|
|
211
211
|
```bash
|
|
212
212
|
# ~/.bashrc or ~/.zshrc or $PROFILE (PowerShell)
|
|
213
|
-
export
|
|
213
|
+
export BUS_AGENT=opencode
|
|
214
214
|
alias bus='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus.js'
|
|
215
215
|
```
|
|
216
216
|
|
|
@@ -233,12 +233,12 @@ When you run `bus send`, it auto-registers your agent on the bus. Other agents (
|
|
|
233
233
|
|
|
234
234
|
## Integration with Hermes
|
|
235
235
|
|
|
236
|
-
MCP
|
|
236
|
+
MCP Bus auto-registers itself as `bus-agent` (default). Hermes can connect via:
|
|
237
237
|
```bash
|
|
238
238
|
mcporter add hermes --stdio "hermes mcp serve --accept-hooks"
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
Then OpenClaw and Hermes can DM through
|
|
241
|
+
Then OpenClaw and Hermes can DM through Bus message bus — no need for direct subprocess calls.
|
|
242
242
|
|
|
243
243
|
## Troubleshooting
|
|
244
244
|
|
|
@@ -271,7 +271,7 @@ All data is JSON. Back up `.bus/` to preserve conversations.
|
|
|
271
271
|
|
|
272
272
|
### Bus Doctor
|
|
273
273
|
|
|
274
|
-
Diagnostics and health checks for the
|
|
274
|
+
Diagnostics and health checks for the Agent bus:
|
|
275
275
|
|
|
276
276
|
```bash
|
|
277
277
|
node doctor.js [--quick] [--fix] [--report] [--watch]
|
|
@@ -281,7 +281,7 @@ node bus-cli.js doctor [--quick] [--fix]
|
|
|
281
281
|
|
|
282
282
|
Performs 18 checks: bus directory, permissions, agent registry (profiles, stale agents), orphaned agents, message integrity, channels, scheduler validity, auto-reply rules, workflows, disk usage, stale PIDs, event logs. With `--fix`, auto-creates missing directories, removes stale PID files, deletes corrupted messages.
|
|
283
283
|
|
|
284
|
-
###
|
|
284
|
+
### Bus Tunnel
|
|
285
285
|
|
|
286
286
|
Cross-machine bus proxy. Expose your local bus to remote agents:
|
|
287
287
|
|
package/backup.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Bus Backup & Restore — Bus State Management CLI Wrapper
|
|
4
4
|
*
|
|
5
5
|
* Thin wrapper around lib/backup.js
|
|
6
6
|
* Usage: see --help
|
|
@@ -13,7 +13,7 @@ const args = process.argv.slice(2);
|
|
|
13
13
|
const cmd = args[0];
|
|
14
14
|
|
|
15
15
|
function help() {
|
|
16
|
-
console.log(`\n╔═══════════════════════════════════════════════╗\n║
|
|
16
|
+
console.log(`\n╔═══════════════════════════════════════════════╗\n║ Bus — Backup & Restore ║\n╚═══════════════════════════════════════════════╝\n`);
|
|
17
17
|
console.log('Usage:');
|
|
18
18
|
console.log(' node backup.js Create backup (timestamped name)');
|
|
19
19
|
console.log(' node backup.js --out <file> Create backup with custom path');
|
|
@@ -26,8 +26,8 @@ function help() {
|
|
|
26
26
|
console.log(' node backup.js --watch <minutes> Auto-backup every N minutes\n');
|
|
27
27
|
console.log('Examples:');
|
|
28
28
|
console.log(' node backup.js');
|
|
29
|
-
console.log(' node backup.js --restore .backups/
|
|
30
|
-
console.log(' node backup.js --diff .backups/
|
|
29
|
+
console.log(' node backup.js --restore .backups/bus-2026-06-24.coco');
|
|
30
|
+
console.log(' node backup.js --diff .backups/bus-2026-06-24.coco');
|
|
31
31
|
console.log(' node backup.js --cleanup 30');
|
|
32
32
|
console.log(' node backup.js --watch 60\n');
|
|
33
33
|
}
|
package/bridge.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Bus External Bridge — Connect external platforms to the Agent Bus
|
|
4
4
|
*
|
|
5
5
|
* Generic bridge pattern that can be adapted for:
|
|
6
6
|
* - Slack (Web API + Events API)
|
|
@@ -66,7 +66,7 @@ class GenericBridge {
|
|
|
66
66
|
registered_at: agents[this.agentName]?.registered_at || new Date().toISOString(),
|
|
67
67
|
};
|
|
68
68
|
fs.writeFileSync(agentsFile, JSON.stringify(agents, null, 2), 'utf-8');
|
|
69
|
-
console.log(` ✅ Registered as "${this.agentName}" on
|
|
69
|
+
console.log(` ✅ Registered as "${this.agentName}" on Agent Bus`);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// Map external channel → bus channel
|
|
@@ -222,7 +222,7 @@ const CHANNELS_DIR = path.join(BUS_DIR, 'channels');
|
|
|
222
222
|
function help() {
|
|
223
223
|
console.log(`
|
|
224
224
|
╔═══════════════════════════════════════════════╗
|
|
225
|
-
║ MCP
|
|
225
|
+
║ MCP Bus — External Platform Bridge ║
|
|
226
226
|
╚═══════════════════════════════════════════════╝
|
|
227
227
|
|
|
228
228
|
Usage:
|
|
@@ -281,11 +281,11 @@ async function main() {
|
|
|
281
281
|
|
|
282
282
|
console.log(`
|
|
283
283
|
╔══════════════════════════════════════════════╗
|
|
284
|
-
║ MCP
|
|
284
|
+
║ MCP Bus — ${type.padEnd(22)}║
|
|
285
285
|
╚══════════════════════════════════════════════╝
|
|
286
286
|
`);
|
|
287
287
|
|
|
288
|
-
bridge.registerAgent(`${type} bridge — connects ${type} to
|
|
288
|
+
bridge.registerAgent(`${type} bridge — connects ${type} to Agent Bus`);
|
|
289
289
|
startBridgeListener(bridge, port);
|
|
290
290
|
bridge.start();
|
|
291
291
|
|
package/bus-aliases.sh
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
# ──
|
|
3
|
-
alias
|
|
4
|
-
alias
|
|
5
|
-
alias
|
|
6
|
-
alias
|
|
7
|
-
alias
|
|
8
|
-
alias
|
|
9
|
-
alias
|
|
10
|
-
alias
|
|
2
|
+
# ── Bus Agent Aliases ──
|
|
3
|
+
alias bus-agents='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus-cli.js agents'
|
|
4
|
+
alias bus-inbox='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus-cli.js inbox'
|
|
5
|
+
alias bus-send='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus-cli.js send'
|
|
6
|
+
alias bus-whoami='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus-cli.js whoami'
|
|
7
|
+
alias bus-status='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus-cli.js status'
|
|
8
|
+
alias bus-search='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus-cli.js search'
|
|
9
|
+
alias bus-watch='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus-cli.js watch'
|
|
10
|
+
alias bus-channel='node E:\_system\.openclaw\workspace\repos\mcp-coco\bus-cli.js channel'
|
package/bus-cli.js
CHANGED
|
@@ -78,7 +78,7 @@ function main() {
|
|
|
78
78
|
|
|
79
79
|
function cmdWhoami() {
|
|
80
80
|
const agents = loadAgents();
|
|
81
|
-
const name = process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
81
|
+
const name = process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
82
82
|
const seen = agents[name];
|
|
83
83
|
const profile = seen || {};
|
|
84
84
|
console.log(`\n👤 Your Agent Identity:`);
|
|
@@ -147,7 +147,7 @@ function cmdAgents(filterArgs) {
|
|
|
147
147
|
// ── Inbox (enhanced with --from filter) ──
|
|
148
148
|
|
|
149
149
|
function cmdInbox(agentName, extra) {
|
|
150
|
-
const agent = agentName || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
150
|
+
const agent = agentName || process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
151
151
|
const dir = path.join(MSGS_DIR, agent);
|
|
152
152
|
|
|
153
153
|
const opts = { from: null, unread: false };
|
|
@@ -204,7 +204,7 @@ function cmdSend(to, text) {
|
|
|
204
204
|
console.log('Usage: bus send <to> <message>');
|
|
205
205
|
process.exit(1);
|
|
206
206
|
}
|
|
207
|
-
const from = process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
207
|
+
const from = process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
208
208
|
const msg = {
|
|
209
209
|
id: `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
|
|
210
210
|
from,
|
|
@@ -223,7 +223,7 @@ function cmdReply(msgId, text) {
|
|
|
223
223
|
console.log('Usage: bus reply <msg-id> <message>');
|
|
224
224
|
process.exit(1);
|
|
225
225
|
}
|
|
226
|
-
const from = process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
226
|
+
const from = process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
227
227
|
|
|
228
228
|
let original = null;
|
|
229
229
|
if (fs.existsSync(MSGS_DIR)) {
|
|
@@ -266,7 +266,7 @@ function cmdProfile(subArgs) {
|
|
|
266
266
|
return cmdProfileEdit(subArgs.slice(1));
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
const agentName = subArgs[0] || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
269
|
+
const agentName = subArgs[0] || process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
270
270
|
const agents = loadAgents();
|
|
271
271
|
const profile = agents[agentName];
|
|
272
272
|
|
|
@@ -295,7 +295,7 @@ function cmdProfile(subArgs) {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
function cmdProfileEdit(edits) {
|
|
298
|
-
const name = process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
298
|
+
const name = process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
299
299
|
const agents = loadAgents();
|
|
300
300
|
if (!agents[name]) {
|
|
301
301
|
console.log(`❌ Not registered. Register first with: send or whoami`);
|
|
@@ -375,7 +375,7 @@ function cmdStatus() {
|
|
|
375
375
|
const online = Object.entries(agents).filter(([, a]) => new Date(a.last_seen).getTime() > cutoff);
|
|
376
376
|
const offline = Object.entries(agents).filter(([, a]) => new Date(a.last_seen).getTime() <= cutoff);
|
|
377
377
|
|
|
378
|
-
const myName = process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
378
|
+
const myName = process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
379
379
|
const me = agents[myName];
|
|
380
380
|
const myOnline = me && new Date(me.last_seen).getTime() > cutoff;
|
|
381
381
|
|
|
@@ -433,7 +433,7 @@ function cmdSubscribe(channelId) {
|
|
|
433
433
|
const logDir = path.join(CHANNELS_DIR, channelId, 'log');
|
|
434
434
|
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
|
435
435
|
|
|
436
|
-
const myName = process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
436
|
+
const myName = process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
437
437
|
|
|
438
438
|
// Join if not already
|
|
439
439
|
const ch = JSON.parse(fs.readFileSync(chPath, 'utf-8'));
|
|
@@ -574,7 +574,7 @@ function cmdChannel(subArgs) {
|
|
|
574
574
|
const chId = subArgs[1];
|
|
575
575
|
const topic = subArgs.slice(2).join(' ') || '';
|
|
576
576
|
if (!chId) { console.log('Usage: bus channel create <name> [topic]'); return; }
|
|
577
|
-
const ch = { id: chId, topic, created_by: process.env.COCO_AGENT || 'cli', created_at: new Date().toISOString(), members: [] };
|
|
577
|
+
const ch = { id: chId, topic, created_by: process.env.BUS_AGENT || process.env.COCO_AGENT || 'cli', created_at: new Date().toISOString(), members: [] };
|
|
578
578
|
fs.writeFileSync(path.join(CHANNELS_DIR, `${chId}.json`), JSON.stringify(ch, null, 2), 'utf-8');
|
|
579
579
|
console.log(`✅ Channel "#${chId}" created`);
|
|
580
580
|
return;
|
|
@@ -582,7 +582,7 @@ function cmdChannel(subArgs) {
|
|
|
582
582
|
|
|
583
583
|
if (sub === 'join') {
|
|
584
584
|
const chId = subArgs[1];
|
|
585
|
-
const agentName = subArgs[2] || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
585
|
+
const agentName = subArgs[2] || process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
586
586
|
if (!chId) { console.log('Usage: bus channel join <name> [agent]'); return; }
|
|
587
587
|
const chPath = path.join(CHANNELS_DIR, `${chId}.json`);
|
|
588
588
|
if (!fs.existsSync(chPath)) { console.log(`❌ Channel "${chId}" not found`); return; }
|
|
@@ -600,7 +600,7 @@ function cmdChannel(subArgs) {
|
|
|
600
600
|
console.log('Usage: bus channel send <channel> <message>');
|
|
601
601
|
return;
|
|
602
602
|
}
|
|
603
|
-
const from = process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
603
|
+
const from = process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
604
604
|
const chPath = path.join(CHANNELS_DIR, `${channelId}.json`);
|
|
605
605
|
if (!fs.existsSync(chPath)) {
|
|
606
606
|
console.log(`❌ Channel "${channelId}" not found`);
|
|
@@ -655,7 +655,7 @@ function cmdChannel(subArgs) {
|
|
|
655
655
|
// ── Watch ──
|
|
656
656
|
|
|
657
657
|
function cmdWatch(agentName) {
|
|
658
|
-
const agent = agentName || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
658
|
+
const agent = agentName || process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'anonymous';
|
|
659
659
|
const dir = path.join(MSGS_DIR, agent);
|
|
660
660
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
661
661
|
|
|
@@ -708,10 +708,10 @@ function cmdTunnel(args) {
|
|
|
708
708
|
const mode = args[0];
|
|
709
709
|
if (!mode || mode === '--help') {
|
|
710
710
|
console.log('Usage: bus tunnel server|client|sync|ssh [options]');
|
|
711
|
-
console.log('
|
|
712
|
-
console.log('
|
|
713
|
-
console.log('
|
|
714
|
-
console.log('
|
|
711
|
+
console.log(' bus tunnel server --port 9090 --secret <token>');
|
|
712
|
+
console.log(' bus tunnel client --host <ip> --port 9090 --secret <token>');
|
|
713
|
+
console.log(' bus tunnel sync --remote http://<ip>:9090');
|
|
714
|
+
console.log(' bus tunnel ssh --remote user@host');
|
|
715
715
|
return;
|
|
716
716
|
}
|
|
717
717
|
const opts = { busDir: BUS_DIR };
|
|
@@ -910,7 +910,7 @@ Commands:
|
|
|
910
910
|
filters: --online, --status, --capability, --tag, --model, --search
|
|
911
911
|
|
|
912
912
|
whoami Show your agent identity
|
|
913
|
-
inbox [agent] [--from X] Check messages (default: \$
|
|
913
|
+
inbox [agent] [--from X] Check messages (default: \$BUS_AGENT)
|
|
914
914
|
send <to> <message> Send a message to another agent
|
|
915
915
|
reply <msg-id> <message> Reply to an incoming message
|
|
916
916
|
profile [agent] Show agent profile details
|
|
@@ -968,7 +968,7 @@ Commands:
|
|
|
968
968
|
memory rebuild <agent> Rebuild vector embeddings for all memories
|
|
969
969
|
|
|
970
970
|
Environment:
|
|
971
|
-
|
|
971
|
+
BUS_AGENT=opencode Set your agent name for inbox/send
|
|
972
972
|
|
|
973
973
|
Examples:
|
|
974
974
|
bus agents --online --capability code-review
|
package/bus-tool.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Bus Tool Wrapper — Direct bus calls from any tool/exec context
|
|
4
4
|
*
|
|
5
5
|
* Acts as a lightweight RPC bridge between tools (OpenClaw, CLI, etc.)
|
|
6
|
-
* and the
|
|
6
|
+
* and the Agent Bus. No MCP needed — direct file access.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
9
|
* node bus-tool.js <tool> [args...]
|
|
10
10
|
*
|
|
11
11
|
* Examples:
|
|
12
12
|
* node bus-tool.js agent_list '{"online_only":true}'
|
|
13
|
-
* node bus-tool.js agent_get_profile '{"name":"
|
|
13
|
+
* node bus-tool.js agent_get_profile '{"name":"bus-agent"}'
|
|
14
14
|
* node bus-tool.js message_send '{"from":"andul","to":"hermes","message":"Hello!"}'
|
|
15
15
|
* node bus-tool.js message_fetch '{"agent_name":"andul"}'
|
|
16
16
|
* node bus-tool.js agent_search '{"query":"code"}'
|
|
17
17
|
* node bus-tool.js agent_register '{"name":"andul","description":"Andul 🐺","capabilities":["chat","code","browsing"]}'
|
|
18
|
-
* node bus-tool.js
|
|
18
|
+
* node bus-tool.js bus_health '{}'
|
|
19
19
|
*/
|
|
20
20
|
const path = require('path');
|
|
21
21
|
const fs = require('fs');
|
|
@@ -39,8 +39,8 @@ async function main() {
|
|
|
39
39
|
let result;
|
|
40
40
|
|
|
41
41
|
switch (tool) {
|
|
42
|
-
//
|
|
43
|
-
case '
|
|
42
|
+
// Bus Health
|
|
43
|
+
case 'bus_health':
|
|
44
44
|
result = await bridge.healthCheck();
|
|
45
45
|
break;
|
|
46
46
|
|
|
@@ -159,7 +159,7 @@ async function main() {
|
|
|
159
159
|
|
|
160
160
|
function getToolList() {
|
|
161
161
|
return [
|
|
162
|
-
'
|
|
162
|
+
'bus_health',
|
|
163
163
|
'agent_register', 'agent_update_profile', 'agent_get_profile',
|
|
164
164
|
'agent_list', 'agent_search', 'agent_heartbeat', 'agent_set_status',
|
|
165
165
|
'message_send', 'message_broadcast', 'message_fetch', 'message_delete',
|
package/claude-mcp.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Bus Agent Client — TypeScript/JS SDK for Agent Bus
|
|
3
3
|
*
|
|
4
|
-
* Direct file-based access to the
|
|
4
|
+
* Direct file-based access to the Agent Bus. No MCP needed.
|
|
5
5
|
* Works in Node.js. For browser, use the WebSocket/proxy approach.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* import {
|
|
8
|
+
* import { BusClient } from './bus-client.js';
|
|
9
9
|
*
|
|
10
|
-
* const bus = new
|
|
10
|
+
* const bus = new BusClient('/path/to/.bus', 'my-agent');
|
|
11
11
|
* await bus.register({ description: 'My agent' });
|
|
12
12
|
* await bus.send('hermes', 'Hello!');
|
|
13
13
|
* const msgs = await bus.fetchMessages();
|
|
@@ -42,7 +42,7 @@ interface BusMessage {
|
|
|
42
42
|
timestamp: string;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export class
|
|
45
|
+
export class BusClient {
|
|
46
46
|
private busDir: string;
|
|
47
47
|
private msgsDir: string;
|
|
48
48
|
private agentsFile: string;
|
|
@@ -54,7 +54,7 @@ export class CoCoClient {
|
|
|
54
54
|
this.msgsDir = path.join(busDir, 'messages');
|
|
55
55
|
this.agentsFile = path.join(busDir, 'agents.json');
|
|
56
56
|
this.channelsDir = path.join(busDir, 'channels');
|
|
57
|
-
this.agentName = agentName || process.env.COCO_AGENT || process.env.USER || 'ts-agent';
|
|
57
|
+
this.agentName = agentName || process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'ts-agent';
|
|
58
58
|
this._ensureDirs();
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Bus Bus Client — Python SDK for Agent Bus
|
|
3
3
|
|
|
4
|
-
Direct file-based access to the
|
|
4
|
+
Direct file-based access to the Bus Agent Bus. No MCP needed.
|
|
5
5
|
Any agent with filesystem access to the bus directory can use this.
|
|
6
6
|
|
|
7
7
|
Usage:
|
|
8
|
-
from
|
|
8
|
+
from bus_client import BusClient
|
|
9
9
|
|
|
10
|
-
bus =
|
|
10
|
+
bus = BusClient("/path/to/bus-agent/.bus", agent_name="my-agent")
|
|
11
11
|
bus.register(description="My Python agent", capabilities=["data-analysis", "api-calls"])
|
|
12
12
|
bus.send("hermes", "Hello from Python!")
|
|
13
13
|
msgs = bus.fetch_messages()
|
|
@@ -22,14 +22,14 @@ import time
|
|
|
22
22
|
import uuid
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class
|
|
25
|
+
class BusClient:
|
|
26
26
|
def __init__(self, bus_dir, agent_name=None):
|
|
27
27
|
self.bus_dir = bus_dir
|
|
28
28
|
self.msgs_dir = os.path.join(bus_dir, "messages")
|
|
29
29
|
self.agents_file = os.path.join(bus_dir, "agents.json")
|
|
30
30
|
self.channels_dir = os.path.join(bus_dir, "channels")
|
|
31
31
|
self.events_dir = os.path.join(bus_dir, "events")
|
|
32
|
-
self.agent_name = agent_name or os.environ.get("COCO_AGENT") or os.environ.get("USER", "python-agent")
|
|
32
|
+
self.agent_name = agent_name or os.environ.get("BUS_AGENT") or os.environ.get("COCO_AGENT") or os.environ.get("USER", "python-agent")
|
|
33
33
|
self._ensure_dirs()
|
|
34
34
|
|
|
35
35
|
def _ensure_dirs(self):
|
|
@@ -55,7 +55,7 @@ class CoCoClient:
|
|
|
55
55
|
# ── Registration ──
|
|
56
56
|
|
|
57
57
|
def register(self, description="", capabilities=None, model=None, tags=None, version="1.0.0", status="idle"):
|
|
58
|
-
"""Register yourself on the
|
|
58
|
+
"""Register yourself on the Bus Agent Bus."""
|
|
59
59
|
agents = self._load_agents()
|
|
60
60
|
existing = agents.get(self.agent_name, {})
|
|
61
61
|
agents[self.agent_name] = {
|
package/cursor-mcp.json
CHANGED
package/doctor.js
CHANGED
package/hermes-forwarder.js
CHANGED
|
@@ -135,7 +135,7 @@ async function poll() {
|
|
|
135
135
|
|
|
136
136
|
console.log(`
|
|
137
137
|
╔══════════════════════════════════════════╗
|
|
138
|
-
║ MCP
|
|
138
|
+
║ MCP Bus — Hermes Auto-Forwarder ║
|
|
139
139
|
║ ║
|
|
140
140
|
║ Any DM to "hermes" → ║
|
|
141
141
|
║ ask_hermes(prompt) → reply back ║
|
package/hermes.example.json
CHANGED
package/index.js
CHANGED
|
@@ -33,7 +33,7 @@ async function main() {
|
|
|
33
33
|
const bus = new AgentBus();
|
|
34
34
|
const scheduler = new Scheduler(bus);
|
|
35
35
|
scheduler.start();
|
|
36
|
-
console.log('
|
|
36
|
+
console.log('Bus Scheduler running. Press Ctrl+C to stop.');
|
|
37
37
|
// Keep alive
|
|
38
38
|
process.on('SIGINT', () => { scheduler.stop(); process.exit(0); });
|
|
39
39
|
process.on('SIGTERM', () => { scheduler.stop(); process.exit(0); });
|
package/lib/backup.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Bus Backup & Restore — Bus State Management Module
|
|
3
3
|
*
|
|
4
4
|
* Export functions, no process.exit, accepts busDir as parameter.
|
|
5
5
|
* Used by: bus-cli.js, backup.js (thin CLI wrapper)
|
|
@@ -21,9 +21,9 @@ function createBackup(busDir, outFile) {
|
|
|
21
21
|
if (!fs.existsSync(backupDir)) fs.mkdirSync(backupDir, { recursive: true });
|
|
22
22
|
|
|
23
23
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
24
|
-
const output = outFile || path.join(backupDir, `
|
|
24
|
+
const output = outFile || path.join(backupDir, `bus-${timestamp}.coco`);
|
|
25
25
|
|
|
26
|
-
console.log(`\n
|
|
26
|
+
console.log(`\n Bus Backup — Creating backup...`);
|
|
27
27
|
console.log(` Source: ${busDir}`);
|
|
28
28
|
console.log(` Output: ${output}\n`);
|
|
29
29
|
|
|
@@ -44,7 +44,7 @@ function createBackup(busDir, outFile) {
|
|
|
44
44
|
walk(busDir);
|
|
45
45
|
|
|
46
46
|
const metadata = {
|
|
47
|
-
version: '2.1.0', tool: '
|
|
47
|
+
version: '2.1.0', tool: 'bus-backup', created_at: new Date().toISOString(),
|
|
48
48
|
hostname: require('os').hostname(), bus_path: busDir, file_count: entries.length,
|
|
49
49
|
checksum: crypto.createHash('sha256').update(entries.map(e => e.path + e.content).join('')).digest('hex'),
|
|
50
50
|
};
|
|
@@ -65,7 +65,7 @@ function createBackup(busDir, outFile) {
|
|
|
65
65
|
function restoreBackup(file, busDir) {
|
|
66
66
|
if (!fs.existsSync(file)) throw new Error(`Backup file not found: ${file}`);
|
|
67
67
|
|
|
68
|
-
console.log(`\n
|
|
68
|
+
console.log(`\n Bus Restore — Restoring from backup...`);
|
|
69
69
|
console.log(` Source: ${file}`);
|
|
70
70
|
|
|
71
71
|
let archive;
|
|
@@ -113,7 +113,7 @@ function listBackups(busDir) {
|
|
|
113
113
|
const files = fs.readdirSync(backupDir).filter(f => f.endsWith('.coco')).sort().reverse();
|
|
114
114
|
if (files.length === 0) { console.log('No backups found.'); return []; }
|
|
115
115
|
|
|
116
|
-
console.log(`\n
|
|
116
|
+
console.log(`\n Bus Backups (${files.length} total):`);
|
|
117
117
|
console.log(' ──────────────────────────────────────────────');
|
|
118
118
|
|
|
119
119
|
let totalSize = 0;
|
|
@@ -165,7 +165,7 @@ function diffBackup(file, busDir) {
|
|
|
165
165
|
if (!fs.existsSync(file)) throw new Error(`Backup file not found: ${file}`);
|
|
166
166
|
const archive = JSON.parse(zlib.gunzipSync(fs.readFileSync(file)).toString('utf-8'));
|
|
167
167
|
|
|
168
|
-
console.log(`\n
|
|
168
|
+
console.log(`\n Bus Diff — Backup vs Current State`);
|
|
169
169
|
console.log(` Backup: ${archive.metadata.created_at}`);
|
|
170
170
|
console.log(' ──────────────────────────────────────────────');
|
|
171
171
|
|
|
@@ -246,7 +246,7 @@ function autoBackup(busDir) {
|
|
|
246
246
|
|
|
247
247
|
function watchMode(busDir, intervalMinutes = 30) {
|
|
248
248
|
const intervalMs = intervalMinutes * 60 * 1000;
|
|
249
|
-
console.log(`\n
|
|
249
|
+
console.log(`\n Bus Auto-Backup — Every ${intervalMinutes} minutes`);
|
|
250
250
|
console.log(` Bus: ${busDir}`);
|
|
251
251
|
console.log(` Press Ctrl+C to stop\n`);
|
|
252
252
|
autoBackup(busDir);
|
package/lib/bus.js
CHANGED
|
@@ -77,7 +77,7 @@ class AgentBus extends EventEmitter {
|
|
|
77
77
|
});
|
|
78
78
|
// Send welcome DM to the new agent
|
|
79
79
|
this.sendMessage('coco', name,
|
|
80
|
-
`👋 Welcome to
|
|
80
|
+
`👋 Welcome to Agent Bus, **${name}**!\n` +
|
|
81
81
|
`You are now connected to ${Object.keys(this._agents).length} agent(s).\n` +
|
|
82
82
|
`Try: agent_list, whoami, or send a message to another agent.`,
|
|
83
83
|
{ type: 'system_welcome' }
|
package/lib/daemon.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Daemon Manager — run MCP
|
|
2
|
+
* Daemon Manager — run MCP Bus as a background process
|
|
3
3
|
* with healthcheck, PID tracking, and auto-recovery for Hermes backend.
|
|
4
4
|
*/
|
|
5
5
|
const { spawn } = require('child_process');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
|
|
9
|
-
const PID_FILE = path.join(__dirname, '..', '.
|
|
10
|
-
const LOG_FILE = path.join(__dirname, '..', '
|
|
9
|
+
const PID_FILE = path.join(__dirname, '..', '.bus-daemon.pid');
|
|
10
|
+
const LOG_FILE = path.join(__dirname, '..', 'bus-daemon.log');
|
|
11
11
|
const HERMES_MCP_PID = path.join(__dirname, '..', '.hermes-mcp.pid');
|
|
12
12
|
|
|
13
13
|
class Daemon {
|
|
14
14
|
/**
|
|
15
|
-
* Start the MCP
|
|
15
|
+
* Start the MCP Bus daemon in background.
|
|
16
16
|
* Returns immediately; the child process runs independently.
|
|
17
17
|
*/
|
|
18
18
|
async start() {
|
|
@@ -21,7 +21,7 @@ class Daemon {
|
|
|
21
21
|
const existingPid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
|
|
22
22
|
try {
|
|
23
23
|
process.kill(existingPid, 0);
|
|
24
|
-
console.error(`MCP
|
|
24
|
+
console.error(`MCP Bus already running (PID: ${existingPid})`);
|
|
25
25
|
process.exit(0);
|
|
26
26
|
return;
|
|
27
27
|
} catch {
|
|
@@ -34,7 +34,7 @@ class Daemon {
|
|
|
34
34
|
const child = spawn(process.execPath, [path.join(__dirname, '..', 'index.js')], {
|
|
35
35
|
detached: true,
|
|
36
36
|
stdio: ['ignore', fs.openSync(LOG_FILE, 'a'), fs.openSync(LOG_FILE, 'a')],
|
|
37
|
-
env: { ...process.env,
|
|
37
|
+
env: { ...process.env, MCP_BUS_DAEMON: '1' },
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
child.unref();
|
|
@@ -42,7 +42,7 @@ class Daemon {
|
|
|
42
42
|
// Write PID
|
|
43
43
|
fs.writeFileSync(PID_FILE, String(child.pid), 'utf-8');
|
|
44
44
|
|
|
45
|
-
console.log(`MCP
|
|
45
|
+
console.log(`MCP Bus daemon started (PID: ${child.pid})`);
|
|
46
46
|
console.log(`Log: ${LOG_FILE}`);
|
|
47
47
|
|
|
48
48
|
// Also start Hermes MCP backend
|
package/lib/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Bus Doctor — Diagnostics & Health Check Module
|
|
3
3
|
*
|
|
4
4
|
* Export functions, no process.exit, accepts busDir as parameter.
|
|
5
5
|
* Used by: bus-cli.js, doctor.js (thin CLI wrapper)
|
|
@@ -253,7 +253,7 @@ function runDiagnostics(opts = {}) {
|
|
|
253
253
|
|
|
254
254
|
// ── Stale PID Files ──
|
|
255
255
|
check('Stale PID files', () => {
|
|
256
|
-
const pidFiles = ['.
|
|
256
|
+
const pidFiles = ['.bus-daemon.pid', '.hermes-mcp.pid'];
|
|
257
257
|
let found = 0;
|
|
258
258
|
for (const pf of pidFiles) {
|
|
259
259
|
const p = path.join(path.dirname(BUS_DIR), pf);
|
|
@@ -293,7 +293,7 @@ function runDiagnostics(opts = {}) {
|
|
|
293
293
|
|
|
294
294
|
// ── Run ──
|
|
295
295
|
if (!cfg.quick) {
|
|
296
|
-
console.log(`\n╔═══════════════════════════════════════════════╗\n║
|
|
296
|
+
console.log(`\n╔═══════════════════════════════════════════════╗\n║ Bus — Diagnostic Report ║\n╚═══════════════════════════════════════════════╝`);
|
|
297
297
|
}
|
|
298
298
|
for (const test of tests) test.fn();
|
|
299
299
|
const summary = summaryText(passed, failed, warnings, fixes);
|
package/lib/mcp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Protocol Handler — JSON-RPC 2.0 over stdio
|
|
3
3
|
*
|
|
4
|
-
* MCP
|
|
4
|
+
* MCP Bus: Agent Profiles, Discovery, Profiles, Scheduler base
|
|
5
5
|
*/
|
|
6
6
|
const readline = require('readline');
|
|
7
7
|
const path = require('path');
|
|
@@ -16,9 +16,9 @@ class MCPServer {
|
|
|
16
16
|
this.bus = new AgentBus();
|
|
17
17
|
this.agentName = 'coco';
|
|
18
18
|
|
|
19
|
-
// Auto-register
|
|
20
|
-
this.bus.registerAgent('coco', 'MCP
|
|
21
|
-
version: '2.
|
|
19
|
+
// Auto-register Bus on bus with full profile
|
|
20
|
+
this.bus.registerAgent('coco', 'MCP Bus — Universal Agent Communication Hub', {
|
|
21
|
+
version: '2.3.5',
|
|
22
22
|
capabilities: [
|
|
23
23
|
'agent-registry', 'agent-discovery', 'agent-profiles',
|
|
24
24
|
'direct-messaging', 'broadcast', 'channels',
|
|
@@ -65,8 +65,8 @@ class MCPServer {
|
|
|
65
65
|
},
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
|
-
name: '
|
|
69
|
-
description: 'Check MCP
|
|
68
|
+
name: 'bus_health',
|
|
69
|
+
description: 'Check MCP Bus bridge and Hermes Agent health status',
|
|
70
70
|
inputSchema: { type: 'object', properties: {} },
|
|
71
71
|
},
|
|
72
72
|
|
|
@@ -441,7 +441,7 @@ class MCPServer {
|
|
|
441
441
|
// Start scheduler
|
|
442
442
|
this.scheduler.start();
|
|
443
443
|
|
|
444
|
-
// Update
|
|
444
|
+
// Update Bus's tool list in profile
|
|
445
445
|
this.bus.updateProfile('coco', { tools: this.tools.map(t => t.name) });
|
|
446
446
|
}
|
|
447
447
|
|
|
@@ -455,7 +455,7 @@ class MCPServer {
|
|
|
455
455
|
return this._respond(id, {
|
|
456
456
|
protocolVersion: '2024-11-05',
|
|
457
457
|
capabilities: { tools: {} },
|
|
458
|
-
serverInfo: { name: 'mcp-
|
|
458
|
+
serverInfo: { name: 'mcp-bus', version: '2.3.5' },
|
|
459
459
|
});
|
|
460
460
|
|
|
461
461
|
case 'notifications/initialized':
|
|
@@ -493,7 +493,7 @@ class MCPServer {
|
|
|
493
493
|
case 'hermes_channels':
|
|
494
494
|
result = await this.bridge.listChannels(args.platform);
|
|
495
495
|
break;
|
|
496
|
-
case '
|
|
496
|
+
case 'bus_health':
|
|
497
497
|
result = await this.bridge.healthCheck();
|
|
498
498
|
break;
|
|
499
499
|
|
|
@@ -510,7 +510,7 @@ class MCPServer {
|
|
|
510
510
|
metadata: {},
|
|
511
511
|
};
|
|
512
512
|
if (args.description) metadata.description = args.description;
|
|
513
|
-
result = this.bus.registerAgent(args.name, args.description || 'MCP agent on
|
|
513
|
+
result = this.bus.registerAgent(args.name, args.description || 'MCP agent on the bus', metadata);
|
|
514
514
|
break;
|
|
515
515
|
}
|
|
516
516
|
case 'agent_update_profile':
|
package/lib/memory.js
CHANGED
package/lib/orchestrator.js
CHANGED
package/lib/scheduler.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Bus Agent Scheduler — Schedule messages to be sent on the bus at specified times
|
|
3
3
|
*
|
|
4
4
|
* Reads a schedule file (.bus/schedule.json) and fires messages on time.
|
|
5
5
|
* Supports cron-like intervals, one-shot, and recurring scheduled messages.
|
|
@@ -226,7 +226,7 @@ class Scheduler {
|
|
|
226
226
|
id: { type: 'string', description: 'Unique job ID (optional, auto-generated)' },
|
|
227
227
|
cron: { type: 'string', description: 'Cron expression: "minute hour day month weekday" (e.g. "0 9 * * 1-5" = weekdays 9am)' },
|
|
228
228
|
at: { type: 'string', description: 'ISO timestamp for one-shot scheduling (e.g. "2026-06-25T09:00:00+07:00")' },
|
|
229
|
-
from: { type: 'string', description: 'Sender agent name (default:
|
|
229
|
+
from: { type: 'string', description: 'Sender agent name (default: bus-agent)' },
|
|
230
230
|
to: { type: 'string', description: 'Target: agent name, "#channel", or "broadcast" (default: broadcast)' },
|
|
231
231
|
message: { type: 'string', description: 'Message content to send' },
|
|
232
232
|
metadata: { type: 'object', description: 'Optional metadata' },
|
package/lib/tunnel.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Bus Tunnel — Cross-machine Bus Proxy Module
|
|
3
3
|
*
|
|
4
4
|
* Export functions, no process.exit, accepts busDir as parameter.
|
|
5
5
|
* Used by: bus-cli.js, tunnel.js (thin CLI wrapper)
|
|
@@ -21,7 +21,7 @@ function hashSecret(secret) {
|
|
|
21
21
|
|
|
22
22
|
function authenticate(req, secret) {
|
|
23
23
|
if (!secret) return true;
|
|
24
|
-
const provided = req.headers['x-
|
|
24
|
+
const provided = req.headers['x-bus-token'];
|
|
25
25
|
return provided && hashSecret(provided) === hashSecret(secret);
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -47,7 +47,7 @@ function startServer(opts = {}) {
|
|
|
47
47
|
const port = opts.port || 9090;
|
|
48
48
|
const secret = opts.secret || null;
|
|
49
49
|
|
|
50
|
-
console.log(`\n
|
|
50
|
+
console.log(`\n Bus Tunnel — Server (receiving end)`);
|
|
51
51
|
console.log(` Listening on :${port}`);
|
|
52
52
|
console.log(` Auth: ${secret ? 'enabled' : 'disabled (INSECURE)'}`);
|
|
53
53
|
console.log(` Bus: ${busDir}\n`);
|
|
@@ -55,7 +55,7 @@ function startServer(opts = {}) {
|
|
|
55
55
|
const server = http.createServer((req, res) => {
|
|
56
56
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
57
57
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
58
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-
|
|
58
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Bus-Token');
|
|
59
59
|
if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
|
|
60
60
|
if (secret && !authenticate(req, secret)) { res.writeHead(401); res.end(JSON.stringify({ error: 'Unauthorized' })); return; }
|
|
61
61
|
|
|
@@ -64,7 +64,7 @@ function startServer(opts = {}) {
|
|
|
64
64
|
|
|
65
65
|
if (req.method === 'GET' && pathname === '/health') {
|
|
66
66
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
67
|
-
res.end(JSON.stringify({ service: '
|
|
67
|
+
res.end(JSON.stringify({ service: 'bus-tunnel', role: 'server', bus: busDir, agents: Object.keys(loadAgents(busDir)).length, uptime: process.uptime() }));
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -170,9 +170,9 @@ async function startClient(opts = {}) {
|
|
|
170
170
|
const interval = opts.interval || 10000;
|
|
171
171
|
const baseUrl = `http://${host}:${port}`;
|
|
172
172
|
const headers = {};
|
|
173
|
-
if (secret) headers['X-
|
|
173
|
+
if (secret) headers['X-Bus-Token'] = secret;
|
|
174
174
|
|
|
175
|
-
console.log(`\n
|
|
175
|
+
console.log(`\n Bus Tunnel — Client (sending end)`);
|
|
176
176
|
console.log(` Remote: ${baseUrl}`);
|
|
177
177
|
console.log(` Sync: Every ${interval}ms\n`);
|
|
178
178
|
|
|
@@ -240,9 +240,9 @@ async function startSync(opts = {}) {
|
|
|
240
240
|
const secret = opts.secret || null;
|
|
241
241
|
const interval = opts.interval || 10000;
|
|
242
242
|
const headers = {};
|
|
243
|
-
if (secret) headers['X-
|
|
243
|
+
if (secret) headers['X-Bus-Token'] = secret;
|
|
244
244
|
|
|
245
|
-
console.log(`\n
|
|
245
|
+
console.log(`\n Bus Tunnel — Bidirectional Sync`);
|
|
246
246
|
console.log(` Remote: ${remote}`);
|
|
247
247
|
console.log(` Local: ${busDir}`);
|
|
248
248
|
console.log(` Sync: Every ${interval}ms\n`);
|
|
@@ -294,8 +294,8 @@ function printSSHHelp(opts = {}) {
|
|
|
294
294
|
const remote = opts.remote || 'user@remote-host';
|
|
295
295
|
const port = opts.port || 9090;
|
|
296
296
|
|
|
297
|
-
console.log(`\n╔═══════════════════════════════════════════════╗\n║
|
|
298
|
-
console.log(`To expose your local
|
|
297
|
+
console.log(`\n╔═══════════════════════════════════════════════╗\n║ Bus Tunnel — SSH Port Forwarding ║\n╚═══════════════════════════════════════════════╝\n`);
|
|
298
|
+
console.log(`To expose your local bus to a remote machine:\n`);
|
|
299
299
|
console.log(`1. On THIS machine, start the tunnel server:`);
|
|
300
300
|
console.log(` node tunnel.js server --port ${port} --secret ***\n`);
|
|
301
301
|
console.log(`2. On the REMOTE machine, create an SSH reverse tunnel:`);
|
package/mcporter.example.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"servers": {
|
|
3
|
-
"
|
|
3
|
+
"bus-agent": {
|
|
4
4
|
"type": "stdio",
|
|
5
5
|
"command": "node",
|
|
6
|
-
"args": ["C:\\Users\\Administrator\\.openclaw\\workspace\\
|
|
6
|
+
"args": ["C:\\Users\\Administrator\\.openclaw\\workspace\\bus-agent\\index.js"]
|
|
7
7
|
},
|
|
8
8
|
"hermes": {
|
|
9
9
|
"type": "stdio",
|
package/opencode-mcp.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bus-agent",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.6",
|
|
4
4
|
"description": "Universal Agent Communication Hub — Connect any AI agent to any other. MCP bus with messaging, channels, memory, scheduling, workflows, and diagnostics.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/scripts/install.bat
CHANGED
package/scripts/install.ps1
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# MCP
|
|
1
|
+
# MCP Bus — Windows Install Script
|
|
2
2
|
# Run as Administrator to install Scheduled Task + mcporter config
|
|
3
3
|
|
|
4
4
|
$ErrorActionPreference = "Stop"
|
|
5
5
|
$RepoDir = Split-Path -Parent $PSScriptRoot
|
|
6
6
|
|
|
7
7
|
Write-Host "╔══════════════════════════════════════════╗" -ForegroundColor Cyan
|
|
8
|
-
Write-Host "║ MCP
|
|
8
|
+
Write-Host "║ MCP Bus — Windows Install ║" -ForegroundColor Cyan
|
|
9
9
|
Write-Host "╚══════════════════════════════════════════╝" -ForegroundColor Cyan
|
|
10
10
|
Write-Host ""
|
|
11
11
|
|
|
@@ -29,11 +29,11 @@ try {
|
|
|
29
29
|
exit 1
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
# 3. Test MCP
|
|
33
|
-
Write-Host "🧪 Testing MCP
|
|
32
|
+
# 3. Test MCP Bus
|
|
33
|
+
Write-Host "🧪 Testing MCP Bus..." -ForegroundColor Yellow
|
|
34
34
|
try {
|
|
35
35
|
$result = node "$RepoDir\bin\cli.js" --health 2>&1
|
|
36
|
-
Write-Host " ✅ MCP
|
|
36
|
+
Write-Host " ✅ MCP Bus works" -ForegroundColor Green
|
|
37
37
|
} catch {
|
|
38
38
|
Write-Host " ⚠️ First run - testing..." -ForegroundColor Yellow
|
|
39
39
|
}
|
|
@@ -45,7 +45,7 @@ try {
|
|
|
45
45
|
} catch {}
|
|
46
46
|
|
|
47
47
|
# 5. Register Scheduled Task for auto-start
|
|
48
|
-
$taskName = "
|
|
48
|
+
$taskName = "MCP_Bus_Daemon"
|
|
49
49
|
$taskExists = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
|
50
50
|
|
|
51
51
|
Write-Host "⚙️ Scheduled Task: $taskName" -ForegroundColor Yellow
|
|
@@ -70,7 +70,7 @@ try {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
# 6. Start daemon
|
|
73
|
-
Write-Host "🚀 Starting MCP
|
|
73
|
+
Write-Host "🚀 Starting MCP Bus daemon..." -ForegroundColor Yellow
|
|
74
74
|
try {
|
|
75
75
|
Start-ScheduledTask -TaskName $taskName
|
|
76
76
|
Write-Host " ✅ Daemon started via Scheduled Task" -ForegroundColor Green
|
|
@@ -90,11 +90,11 @@ if ($LASTEXITCODE -eq 0) {
|
|
|
90
90
|
|
|
91
91
|
Write-Host ""
|
|
92
92
|
Write-Host "╔══════════════════════════════════════════╗" -ForegroundColor Cyan
|
|
93
|
-
Write-Host "║ MCP
|
|
93
|
+
Write-Host "║ MCP Bus Installation Done ║" -ForegroundColor Cyan
|
|
94
94
|
Write-Host "╚══════════════════════════════════════════╝" -ForegroundColor Cyan
|
|
95
95
|
Write-Host ""
|
|
96
96
|
Write-Host "To use from OpenClaw, add to mcporter config:" -ForegroundColor White
|
|
97
|
-
Write-Host " mcporter add
|
|
97
|
+
Write-Host " mcporter add bus-agent --stdio `"node $RepoDir\index.js`"" -ForegroundColor Gray
|
|
98
98
|
Write-Host ""
|
|
99
99
|
Write-Host "Then call:" -ForegroundColor White
|
|
100
|
-
Write-Host " mcporter call
|
|
100
|
+
Write-Host " mcporter call bus-agent.ask_hermes prompt=`"Hello`"" -ForegroundColor Gray
|
package/setup.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Bus Config Wizard — Interactive setup for connecting agents to the Bus
|
|
4
4
|
*
|
|
5
5
|
* Generates MCP configs, env files, and bus registration for:
|
|
6
6
|
* - OpenCode / Clew Code
|
|
@@ -54,7 +54,7 @@ function generateMCPJson(serverName, command, args) {
|
|
|
54
54
|
|
|
55
55
|
function generateMcpConfigOpenCode() {
|
|
56
56
|
return generateMCPJson(
|
|
57
|
-
'
|
|
57
|
+
'bus-agent',
|
|
58
58
|
'node',
|
|
59
59
|
[path.join(COCO_DIR, 'index.js')]
|
|
60
60
|
);
|
|
@@ -62,7 +62,7 @@ function generateMcpConfigOpenCode() {
|
|
|
62
62
|
|
|
63
63
|
function generateMcpConfigClaudeCode() {
|
|
64
64
|
return generateMCPJson(
|
|
65
|
-
'
|
|
65
|
+
'bus-agent',
|
|
66
66
|
'node',
|
|
67
67
|
[path.join(COCO_DIR, 'index.js')]
|
|
68
68
|
);
|
|
@@ -75,23 +75,23 @@ function generateMcpConfigCursor() {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function generateEnvFile(agentName) {
|
|
78
|
-
return `#
|
|
79
|
-
# Generated by
|
|
78
|
+
return `# Bus Agent Configuration
|
|
79
|
+
# Generated by Bus Config Wizard
|
|
80
80
|
|
|
81
81
|
# Your agent name on the bus
|
|
82
|
-
|
|
82
|
+
BUS_AGENT=${agentName}
|
|
83
83
|
|
|
84
|
-
# Path to the
|
|
84
|
+
# Path to the bus data directory
|
|
85
85
|
COCO_BUS_DIR=${BUS_DIR}
|
|
86
86
|
|
|
87
|
-
#
|
|
87
|
+
# Bus CLI path (for bus-cli.js)
|
|
88
88
|
BUS_CLI=${path.join(COCO_DIR, 'bus-cli.js')}
|
|
89
89
|
`;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function generateShellAliases() {
|
|
93
93
|
return `
|
|
94
|
-
# ──
|
|
94
|
+
# ── Bus Aliases ──
|
|
95
95
|
alias bus-agents='node ${path.join(COCO_DIR, 'bus-cli.js')} agents'
|
|
96
96
|
alias bus-inbox='node ${path.join(COCO_DIR, 'bus-cli.js')} inbox'
|
|
97
97
|
alias bus-send='node ${path.join(COCO_DIR, 'bus-cli.js')} send'
|
|
@@ -110,7 +110,7 @@ alias bus-channel='node ${path.join(COCO_DIR, 'bus-cli.js')} channel'
|
|
|
110
110
|
async function wizard() {
|
|
111
111
|
console.log(`
|
|
112
112
|
╔═══════════════════════════════════════════════╗
|
|
113
|
-
║ MCP
|
|
113
|
+
║ MCP Bus — Config Wizard ║
|
|
114
114
|
║ Connect your agents to the Agent Bus ║
|
|
115
115
|
╚═══════════════════════════════════════════════╝
|
|
116
116
|
`);
|
|
@@ -118,7 +118,7 @@ async function wizard() {
|
|
|
118
118
|
const agentName = await question(`What's your agent name? [${process.env.USER || 'agent'}] `) || process.env.USER || 'agent';
|
|
119
119
|
|
|
120
120
|
console.log(`\n🤖 Agent: ${agentName}`);
|
|
121
|
-
console.log(`📁
|
|
121
|
+
console.log(`📁 Bus: ${COCO_DIR}\n`);
|
|
122
122
|
|
|
123
123
|
console.log('Which clients do you want to configure?');
|
|
124
124
|
console.log(' 1) OpenCode / Clew Code');
|
|
@@ -236,7 +236,7 @@ function registerAgentOnBus(agentName) {
|
|
|
236
236
|
const agents = fs.existsSync(agentsFile) ? JSON.parse(fs.readFileSync(agentsFile, 'utf-8')) : {};
|
|
237
237
|
agents[agentName] = {
|
|
238
238
|
name: agentName,
|
|
239
|
-
description: `Setup via
|
|
239
|
+
description: `Setup via Bus Config Wizard`,
|
|
240
240
|
capabilities: [],
|
|
241
241
|
tags: [],
|
|
242
242
|
status: 'idle',
|
|
@@ -253,7 +253,7 @@ function registerAgentOnBus(agentName) {
|
|
|
253
253
|
// ═══════════════════════════════════════════════════════
|
|
254
254
|
|
|
255
255
|
function quickSetup() {
|
|
256
|
-
const agentName = process.env.COCO_AGENT || process.env.USER || 'agent';
|
|
256
|
+
const agentName = process.env.BUS_AGENT || process.env.COCO_AGENT || process.env.USER || 'agent';
|
|
257
257
|
registerAgentOnBus(agentName);
|
|
258
258
|
|
|
259
259
|
// Generate all configs
|
|
@@ -289,7 +289,7 @@ async function main() {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
if (arg === 'list') {
|
|
292
|
-
console.log(`\n📋
|
|
292
|
+
console.log(`\n📋 Agent Bus — Recommended Configs\n`);
|
|
293
293
|
console.log(` 1. OpenCode / Clew Code:`);
|
|
294
294
|
console.log(` Add to .mcp.json:`);
|
|
295
295
|
console.log(` ${JSON.stringify(generateMcpConfigOpenCode(), null, 4).split('\n').map(l => ' ' + l).join('\n')}\n`);
|
|
@@ -301,7 +301,7 @@ async function main() {
|
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
if (arg === 'opencode') {
|
|
304
|
-
registerAgentOnBus(process.env.COCO_AGENT || 'agent');
|
|
304
|
+
registerAgentOnBus(process.env.BUS_AGENT || process.env.COCO_AGENT || 'agent');
|
|
305
305
|
fs.writeFileSync(path.join(COCO_DIR, 'opencode-mcp.json'), JSON.stringify(generateMcpConfigOpenCode(), null, 2), 'utf-8');
|
|
306
306
|
console.log(`✅ OpenCode MCP config generated: opencode-mcp.json`);
|
|
307
307
|
return;
|
package/tunnel.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Bus Tunnel — Cross-machine Bus Proxy CLI Wrapper
|
|
4
4
|
*
|
|
5
5
|
* Thin wrapper around lib/tunnel.js
|
|
6
6
|
* Usage:
|
|
@@ -31,7 +31,7 @@ function parseArgs() {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function help() {
|
|
34
|
-
console.log(`\n╔═══════════════════════════════════════════════╗\n║
|
|
34
|
+
console.log(`\n╔═══════════════════════════════════════════════╗\n║ Bus — Tunnel ║\n╚═══════════════════════════════════════════════╝\n`);
|
|
35
35
|
console.log('Usage:');
|
|
36
36
|
console.log(' node tunnel.js server [options] Start tunnel server (receiving end)');
|
|
37
37
|
console.log(' node tunnel.js client [options] Start tunnel client (sending end)');
|
package/webhook-gateway.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Bus Webhook Gateway — Receive external webhooks and post to Agent Bus
|
|
4
4
|
*
|
|
5
5
|
* Features:
|
|
6
6
|
* - GitHub push, PR, issues, Actions CI status
|
|
@@ -392,7 +392,7 @@ fs.writeFileSync(agentsFile, JSON.stringify(agents, null, 2), 'utf-8');
|
|
|
392
392
|
server.listen(PORT, () => {
|
|
393
393
|
console.log(`
|
|
394
394
|
╔══════════════════════════════════════════════╗
|
|
395
|
-
║ MCP
|
|
395
|
+
║ MCP Bus — Webhook Gateway v2.1 ║
|
|
396
396
|
║ http://localhost:${PORT} ║
|
|
397
397
|
╚══════════════════════════════════════════════╝
|
|
398
398
|
|