bus-agent 2.3.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/coco-tool.js ADDED
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CoCo Tool Wrapper — Direct bus calls from any tool/exec context
4
+ *
5
+ * Acts as a lightweight RPC bridge between tools (OpenClaw, CLI, etc.)
6
+ * and the CoCo Agent Bus. No MCP needed — direct file access.
7
+ *
8
+ * Usage:
9
+ * node coco-tool.js <tool> [args...]
10
+ *
11
+ * Examples:
12
+ * node coco-tool.js agent_list '{"online_only":true}'
13
+ * node coco-tool.js agent_get_profile '{"name":"coco"}'
14
+ * node coco-tool.js message_send '{"from":"andul","to":"hermes","message":"Hello!"}'
15
+ * node coco-tool.js message_fetch '{"agent_name":"andul"}'
16
+ * node coco-tool.js agent_search '{"query":"code"}'
17
+ * node coco-tool.js agent_register '{"name":"andul","description":"Andul 🐺","capabilities":["chat","code","browsing"]}'
18
+ * node coco-tool.js coco_health '{}'
19
+ */
20
+ const path = require('path');
21
+ const fs = require('fs');
22
+
23
+ // Load AgentBus
24
+ require('./lib/bus');
25
+ const bus = new (require('./lib/bus').AgentBus)();
26
+ const { HermesBridge } = require('./lib/hermes');
27
+ const bridge = new HermesBridge();
28
+ const { Scheduler } = require('./lib/scheduler');
29
+ const scheduler = new Scheduler(bus);
30
+ const { Orchestrator } = require('./lib/orchestrator');
31
+ const orchestrator = new Orchestrator(bus);
32
+
33
+ const tool = process.argv[2];
34
+ const argsRaw = process.argv[3] || '{}';
35
+ let args = {};
36
+ try { args = JSON.parse(argsRaw); } catch {}
37
+
38
+ async function main() {
39
+ let result;
40
+
41
+ switch (tool) {
42
+ // CoCo Health
43
+ case 'coco_health':
44
+ result = await bridge.healthCheck();
45
+ break;
46
+
47
+ // Registrations
48
+ case 'agent_register': {
49
+ const meta = {
50
+ capabilities: args.capabilities || [],
51
+ model: args.model || null,
52
+ tags: args.tags || [],
53
+ version: args.version || '1.0.0',
54
+ endpoints: args.endpoints || {},
55
+ tools: args.tools || [],
56
+ status: args.status || 'idle',
57
+ };
58
+ result = bus.registerAgent(args.name, args.description || '', meta);
59
+ break;
60
+ }
61
+ case 'agent_update_profile':
62
+ result = bus.updateProfile(args.name, args);
63
+ break;
64
+ case 'agent_get_profile':
65
+ result = { agent: bus.getProfile(args.name) };
66
+ break;
67
+ case 'agent_list':
68
+ result = { agents: bus.listAgents(args) };
69
+ break;
70
+ case 'agent_search':
71
+ result = { query: args.query, agents: bus.listAgents({ search: args.query }).slice(0, args.max_results || 20) };
72
+ break;
73
+ case 'agent_heartbeat':
74
+ result = bus.heartbeat(args.name);
75
+ break;
76
+ case 'agent_set_status':
77
+ result = bus.setStatus(args.name, args.status);
78
+ break;
79
+
80
+ // Messages
81
+ case 'message_send':
82
+ result = bus.sendMessage(args.from, args.to, args.message, args.metadata);
83
+ break;
84
+ case 'message_broadcast':
85
+ result = bus.broadcastMessage(args.from, args.message);
86
+ break;
87
+ case 'message_fetch':
88
+ result = { messages: bus.fetchMessages(args.agent_name, args.limit) };
89
+ break;
90
+ case 'message_delete':
91
+ result = bus.deleteMessages(args.agent_name, args.message_ids);
92
+ break;
93
+
94
+ // Channels
95
+ case 'channel_create':
96
+ result = bus.createChannel(args.channel_id, args.topic, args.created_by);
97
+ break;
98
+ case 'channel_join':
99
+ result = bus.joinChannel(args.channel_id, args.agent_name);
100
+ break;
101
+ case 'channel_leave':
102
+ result = bus.leaveChannel(args.channel_id, args.agent_name);
103
+ break;
104
+ case 'channel_send':
105
+ result = bus.channelSend(args.channel_id, args.from, args.message);
106
+ break;
107
+ case 'channel_history':
108
+ result = { messages: bus.channelHistory(args.channel_id, args.limit) };
109
+ break;
110
+
111
+ // Events
112
+ case 'system_get_events':
113
+ result = { events: bus.getEvents(args) };
114
+ break;
115
+
116
+ // Scheduler
117
+ case 'scheduler_add':
118
+ result = scheduler.addJob(args);
119
+ break;
120
+ case 'scheduler_remove':
121
+ result = scheduler.removeJob(args.job_id);
122
+ break;
123
+ case 'scheduler_list':
124
+ result = { jobs: scheduler.listJobs() };
125
+ break;
126
+
127
+ // Auto-Reply Rules
128
+ case 'auto_reply_add':
129
+ result = orchestrator.addRule(args);
130
+ break;
131
+ case 'auto_reply_remove':
132
+ result = orchestrator.removeRule(args.rule_id);
133
+ break;
134
+ case 'auto_reply_list':
135
+ result = { rules: orchestrator.listRules() };
136
+ break;
137
+
138
+ // Workflows
139
+ case 'workflow_create':
140
+ result = orchestrator.createWorkflow(args);
141
+ break;
142
+ case 'workflow_run':
143
+ result = await orchestrator.runWorkflow(args.workflow_id, args.input, args.requester);
144
+ break;
145
+ case 'workflow_remove':
146
+ result = orchestrator.removeWorkflow(args.workflow_id);
147
+ break;
148
+ case 'workflow_list':
149
+ result = { workflows: orchestrator.listWorkflows() };
150
+ break;
151
+
152
+ default:
153
+ console.error(JSON.stringify({ error: `Unknown tool: ${tool}`, tools: getToolList() }));
154
+ process.exit(1);
155
+ }
156
+
157
+ process.stdout.write(JSON.stringify(result, null, 2));
158
+ }
159
+
160
+ function getToolList() {
161
+ return [
162
+ 'coco_health',
163
+ 'agent_register', 'agent_update_profile', 'agent_get_profile',
164
+ 'agent_list', 'agent_search', 'agent_heartbeat', 'agent_set_status',
165
+ 'message_send', 'message_broadcast', 'message_fetch', 'message_delete',
166
+ 'channel_create', 'channel_join', 'channel_leave', 'channel_send', 'channel_history',
167
+ 'system_get_events',
168
+ 'scheduler_add', 'scheduler_remove', 'scheduler_list',
169
+ 'auto_reply_add', 'auto_reply_remove', 'auto_reply_list',
170
+ 'workflow_create', 'workflow_run', 'workflow_remove', 'workflow_list',
171
+ ];
172
+ }
173
+
174
+ main().catch(err => {
175
+ console.error(JSON.stringify({ error: err.message }));
176
+ process.exit(1);
177
+ });
package/coco.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CoCo — Convenience entry point for CLI agents
4
+ *
5
+ * Usage:
6
+ * node coco.js agents # List agents
7
+ * node coco.js inbox # Check your messages
8
+ * node coco.js send "message" # Send to last reply-to agent
9
+ * node coco.js send <to> msg # Send to specific agent
10
+ * node coco.js watch # Watch for new messages
11
+ */
12
+ const { execSync } = require('child_process');
13
+ const path = require('path');
14
+
15
+ const CLI = path.join(__dirname, 'coco-cli.js');
16
+ const args = process.argv.slice(2).join(' ');
17
+
18
+ try {
19
+ const result = execSync(`node "${CLI}" ${args}`, {
20
+ encoding: 'utf-8',
21
+ stdio: 'inherit',
22
+ env: { ...process.env },
23
+ });
24
+ } catch (e) {
25
+ // errors already shown via stdio
26
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "E:\\_system\\.openclaw\\workspace\\repos\\mcp-coco\\index.js": true
3
+ }
package/doctor.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CoCo Doctor — Diagnostics CLI Wrapper
4
+ *
5
+ * Thin wrapper around lib/doctor.js
6
+ * Usage: node doctor.js [--quick] [--fix] [--report] [--watch]
7
+ */
8
+ const path = require('path');
9
+ const { runDiagnostics, watchDiagnostics } = require('./lib/doctor');
10
+
11
+ const args = process.argv.slice(2);
12
+ const opts = {
13
+ busDir: path.join(__dirname, '.bus'),
14
+ quick: args.includes('--quick'),
15
+ fix: args.includes('--fix'),
16
+ report: args.includes('--report'),
17
+ watch: args.includes('--watch'),
18
+ };
19
+
20
+ if (opts.watch) {
21
+ watchDiagnostics(opts);
22
+ } else {
23
+ runDiagnostics(opts);
24
+ }
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Hermes Auto-Forwarder — automatically forwards DMs to Hermes Agent
4
+ *
5
+ * Watches the bus for messages addressed to "hermes", sends them
6
+ * through Hermes CLI (ask_hermes), and posts the reply back
7
+ * to the sender's inbox.
8
+ *
9
+ * Usage:
10
+ * node hermes-forwarder.js
11
+ *
12
+ * Set up as Scheduled Task for auto-start:
13
+ * See scripts/install.ps1 or register manually:
14
+ * New-ScheduledTaskAction -Execute "node.exe" -Argument "...\hermes-forwarder.js"
15
+ */
16
+ const { spawn } = require('child_process');
17
+ const path = require('path');
18
+ const fs = require('fs');
19
+
20
+ const BUS_DIR = path.join(__dirname, '.bus');
21
+ const MSGS_DIR = path.join(BUS_DIR, 'messages');
22
+ const AGENTS_FILE = path.join(BUS_DIR, 'agents.json');
23
+ const TARGET = 'hermes';
24
+ const POLL_MS = 3000;
25
+ const HERMES_TIMEOUT = 190000;
26
+
27
+ // Track processed message IDs
28
+ const processed = new Set();
29
+
30
+ function ensureDir(dir) {
31
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
32
+ }
33
+
34
+ function register() {
35
+ const agents = fs.existsSync(AGENTS_FILE)
36
+ ? JSON.parse(fs.readFileSync(AGENTS_FILE, 'utf-8'))
37
+ : {};
38
+ agents['hermes-fwd'] = {
39
+ name: 'hermes-fwd',
40
+ description: 'Hermes Auto-Forwarder — relays DMs to Hermes Agent and replies back',
41
+ last_seen: new Date().toISOString(),
42
+ registered_at: agents['hermes-fwd']?.registered_at || new Date().toISOString(),
43
+ };
44
+ fs.writeFileSync(AGENTS_FILE, JSON.stringify(agents, null, 2), 'utf-8');
45
+ }
46
+
47
+ /**
48
+ * Send a prompt to Hermes CLI (async with timeout).
49
+ * Returns the cleaned response string.
50
+ */
51
+ function askHermes(prompt) {
52
+ return new Promise((resolve) => {
53
+ const escapedPrompt = prompt.replace(/"/g, '\\"');
54
+ const child = spawn(
55
+ 'cmd.exe',
56
+ ['/c', `hermes chat -q "${escapedPrompt}" --quiet --source tool 2>&1`],
57
+ { windowsHide: true, shell: true }
58
+ );
59
+
60
+ let output = '';
61
+ child.stdout.on('data', (d) => { output += d.toString(); });
62
+ child.stderr.on('data', (d) => { output += d.toString(); });
63
+
64
+ child.on('close', () => {
65
+ const clean = output.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, '');
66
+ const lines = clean.split('\n').filter((l) => {
67
+ const s = l.trim();
68
+ if (!s) return false;
69
+ if (s.startsWith('Query:') || s.startsWith('Resume') || s.startsWith('session_id:')) return false;
70
+ if (s.includes('Normalized model') || s.includes('Initializing')) return false;
71
+ if (s.includes('opencode') || s.includes('deepseek')) return false;
72
+ if (s.startsWith('⚠')) return false;
73
+ return true;
74
+ });
75
+ resolve(lines.join('\n').trim() || '(empty response)');
76
+ });
77
+
78
+ child.on('error', (e) => resolve(`[Hermes error: ${e.message.substring(0, 60)}]`));
79
+
80
+ // Safety timeout
81
+ setTimeout(() => {
82
+ child.kill();
83
+ resolve('[Hermes timeout]');
84
+ }, HERMES_TIMEOUT);
85
+ });
86
+ }
87
+
88
+ function sendReply(to, originalMsg, replyText) {
89
+ const inboxDir = path.join(MSGS_DIR, to);
90
+ ensureDir(inboxDir);
91
+
92
+ const msg = {
93
+ id: `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
94
+ from: TARGET,
95
+ to,
96
+ message: replyText,
97
+ in_reply_to: originalMsg.id,
98
+ timestamp: new Date().toISOString(),
99
+ };
100
+
101
+ fs.writeFileSync(path.join(inboxDir, `${msg.id}.json`), JSON.stringify(msg, null, 2), 'utf-8');
102
+ console.log(` 📬 Reply sent to "${to}" (id: ${msg.id})`);
103
+ }
104
+
105
+ async function poll() {
106
+ const inboxDir = path.join(MSGS_DIR, TARGET);
107
+ if (!fs.existsSync(MSGS_DIR) || !fs.existsSync(inboxDir)) return;
108
+
109
+ const files = fs.readdirSync(inboxDir).filter((f) => f.endsWith('.json'));
110
+
111
+ for (const f of files) {
112
+ if (processed.has(f)) continue;
113
+ processed.add(f);
114
+
115
+ try {
116
+ const msg = JSON.parse(fs.readFileSync(path.join(inboxDir, f), 'utf-8'));
117
+
118
+ // Skip messages from bots/self
119
+ if (['hermes', 'hermes-fwd', 'coco', 'webhook'].includes(msg.from)) continue;
120
+
121
+ console.log(`\n[${new Date().toISOString()}] 📩 DM from "${msg.from}": ${msg.message.substring(0, 80)}`);
122
+
123
+ console.log(` 🤖 Forwarding to Hermes...`);
124
+ const reply = await askHermes(msg.message);
125
+ console.log(` 💬 Hermes: ${reply.substring(0, 80)}`);
126
+
127
+ sendReply(msg.from, msg, reply);
128
+ } catch (err) {
129
+ console.error(` ❌ Error processing ${f}: ${err.message}`);
130
+ }
131
+ }
132
+ }
133
+
134
+ // ── Main ──
135
+
136
+ console.log(`
137
+ ╔══════════════════════════════════════════╗
138
+ ║ MCP CoCo — Hermes Auto-Forwarder ║
139
+ ║ ║
140
+ ║ Any DM to "hermes" → ║
141
+ ║ ask_hermes(prompt) → reply back ║
142
+ ║ ║
143
+ ║ Polling every ${POLL_MS}ms ║
144
+ ╚══════════════════════════════════════════╝
145
+ `);
146
+
147
+ register();
148
+ ensureDir(path.join(MSGS_DIR, TARGET));
149
+
150
+ // Register as forwarder on the webhook gateway too via agents.json
151
+ poll();
152
+ setInterval(poll, POLL_MS);
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "coco": {
4
+ "command": "node",
5
+ "args": ["C:\\Users\\Administrator\\.openclaw\\workspace\\mcp-coco\\index.js"],
6
+ "env": {}
7
+ }
8
+ }
9
+ }
package/index.js ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Bus Agent v2.3 — Universal Agent Communication Hub
4
+ *
5
+ * Usage:
6
+ * node index.js # stdio MCP server (for mcporter)
7
+ * node index.js --daemon # background daemon + scheduler
8
+ * node index.js --health # one-shot health check
9
+ * node index.js --scheduler # Run scheduler standalone
10
+ */
11
+
12
+ const { runStdio } = require('./lib/mcp');
13
+ const { Daemon } = require('./lib/daemon');
14
+ const { AgentBus } = require('./lib/bus');
15
+ const { Scheduler } = require('./lib/scheduler');
16
+
17
+ async function main() {
18
+ const args = process.argv.slice(2);
19
+
20
+ if (args.includes('--daemon')) {
21
+ const daemon = new Daemon();
22
+ await daemon.start();
23
+ return;
24
+ }
25
+
26
+ if (args.includes('--health')) {
27
+ const daemon = new Daemon();
28
+ const ok = await daemon.healthCheck();
29
+ process.exit(ok ? 0 : 1);
30
+ }
31
+
32
+ if (args.includes('--scheduler')) {
33
+ const bus = new AgentBus();
34
+ const scheduler = new Scheduler(bus);
35
+ scheduler.start();
36
+ console.log('CoCo Scheduler running. Press Ctrl+C to stop.');
37
+ // Keep alive
38
+ process.on('SIGINT', () => { scheduler.stop(); process.exit(0); });
39
+ process.on('SIGTERM', () => { scheduler.stop(); process.exit(0); });
40
+ // Hang around
41
+ setInterval(() => {}, 60000);
42
+ return;
43
+ }
44
+
45
+ // Default: stdio MCP server
46
+ await runStdio();
47
+ }
48
+
49
+ main().catch(err => {
50
+ console.error('FATAL:', err.message);
51
+ process.exit(1);
52
+ });