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/.env.coco +11 -0
- package/AGENTS.md +37 -0
- package/LICENSE +21 -0
- package/README.md +370 -0
- package/SKILL.md +314 -0
- package/backup.js +57 -0
- package/bin/cli.js +41 -0
- package/bridge.js +325 -0
- package/claude-mcp.json +10 -0
- package/clients/coco-client.ts +245 -0
- package/clients/coco_client.py +216 -0
- package/coco-aliases.sh +10 -0
- package/coco-cli.js +1002 -0
- package/coco-tool.js +177 -0
- package/coco.js +26 -0
- package/cursor-mcp.json +3 -0
- package/doctor.js +24 -0
- package/hermes-forwarder.js +152 -0
- package/hermes.example.json +9 -0
- package/index.js +52 -0
- package/lib/backup.js +256 -0
- package/lib/bus.js +516 -0
- package/lib/daemon.js +96 -0
- package/lib/doctor.js +333 -0
- package/lib/hermes.js +162 -0
- package/lib/mcp.js +730 -0
- package/lib/memory.js +667 -0
- package/lib/orchestrator.js +426 -0
- package/lib/scheduler.js +259 -0
- package/lib/tunnel.js +317 -0
- package/mcporter.example.json +14 -0
- package/opencode-mcp.json +10 -0
- package/package.json +76 -0
- package/scripts/install.bat +5 -0
- package/scripts/install.ps1 +100 -0
- package/setup.js +320 -0
- package/tunnel.js +66 -0
- package/webhook-gateway.js +420 -0
package/setup.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CoCo Config Wizard — Interactive setup for connecting agents to CoCo Bus
|
|
4
|
+
*
|
|
5
|
+
* Generates MCP configs, env files, and bus registration for:
|
|
6
|
+
* - OpenCode / Clew Code
|
|
7
|
+
* - Claude Code
|
|
8
|
+
* - Cursor
|
|
9
|
+
* - Any MCP client
|
|
10
|
+
* - CLI agents (via coco-cli.js)
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node setup.js # Interactive wizard
|
|
14
|
+
* node setup.js --quick # Quick setup (defaults)
|
|
15
|
+
* node setup.js opencode # Just OpenCode config
|
|
16
|
+
* node setup.js list # List recommended configs
|
|
17
|
+
*/
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const readline = require('readline');
|
|
21
|
+
|
|
22
|
+
const COCO_DIR = __dirname;
|
|
23
|
+
const BUS_DIR = path.join(COCO_DIR, '.bus');
|
|
24
|
+
const MSGS_DIR = path.join(BUS_DIR, 'messages');
|
|
25
|
+
|
|
26
|
+
function rl() {
|
|
27
|
+
return readline.createInterface({
|
|
28
|
+
input: process.stdin,
|
|
29
|
+
output: process.stdout,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function question(query) {
|
|
34
|
+
return new Promise(resolve => {
|
|
35
|
+
const i = rl();
|
|
36
|
+
i.question(query, answer => { i.close(); resolve(answer); });
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ═══════════════════════════════════════════════════════
|
|
41
|
+
// Config Generators
|
|
42
|
+
// ═══════════════════════════════════════════════════════
|
|
43
|
+
|
|
44
|
+
function generateMCPJson(serverName, command, args) {
|
|
45
|
+
return {
|
|
46
|
+
mcpServers: {
|
|
47
|
+
[serverName]: {
|
|
48
|
+
command,
|
|
49
|
+
args,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function generateMcpConfigOpenCode() {
|
|
56
|
+
return generateMCPJson(
|
|
57
|
+
'coco',
|
|
58
|
+
'node',
|
|
59
|
+
[path.join(COCO_DIR, 'index.js')]
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function generateMcpConfigClaudeCode() {
|
|
64
|
+
return generateMCPJson(
|
|
65
|
+
'coco',
|
|
66
|
+
'node',
|
|
67
|
+
[path.join(COCO_DIR, 'index.js')]
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function generateMcpConfigCursor() {
|
|
72
|
+
return {
|
|
73
|
+
[path.join(COCO_DIR, 'index.js')]: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function generateEnvFile(agentName) {
|
|
78
|
+
return `# CoCo Bus Agent Configuration
|
|
79
|
+
# Generated by CoCo Config Wizard
|
|
80
|
+
|
|
81
|
+
# Your agent name on the bus
|
|
82
|
+
COCO_AGENT=${agentName}
|
|
83
|
+
|
|
84
|
+
# Path to the CoCo bus directory
|
|
85
|
+
COCO_BUS_DIR=${BUS_DIR}
|
|
86
|
+
|
|
87
|
+
# CoCo CLI path (for coco-cli.js)
|
|
88
|
+
COCO_CLI=${path.join(COCO_DIR, 'coco-cli.js')}
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function generateShellAliases() {
|
|
93
|
+
return `
|
|
94
|
+
# ── CoCo Bus Aliases ──
|
|
95
|
+
alias coco-agents='node ${path.join(COCO_DIR, 'coco-cli.js')} agents'
|
|
96
|
+
alias coco-inbox='node ${path.join(COCO_DIR, 'coco-cli.js')} inbox'
|
|
97
|
+
alias coco-send='node ${path.join(COCO_DIR, 'coco-cli.js')} send'
|
|
98
|
+
alias coco-whoami='node ${path.join(COCO_DIR, 'coco-cli.js')} whoami'
|
|
99
|
+
alias coco-status='node ${path.join(COCO_DIR, 'coco-cli.js')} status'
|
|
100
|
+
alias coco-search='node ${path.join(COCO_DIR, 'coco-cli.js')} search'
|
|
101
|
+
alias coco-watch='node ${path.join(COCO_DIR, 'coco-cli.js')} watch'
|
|
102
|
+
alias coco-channel='node ${path.join(COCO_DIR, 'coco-cli.js')} channel'
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ═══════════════════════════════════════════════════════
|
|
107
|
+
// Interactive Wizard
|
|
108
|
+
// ═══════════════════════════════════════════════════════
|
|
109
|
+
|
|
110
|
+
async function wizard() {
|
|
111
|
+
console.log(`
|
|
112
|
+
╔═══════════════════════════════════════════════╗
|
|
113
|
+
║ MCP CoCo — Config Wizard ║
|
|
114
|
+
║ Connect your agents to the Agent Bus ║
|
|
115
|
+
╚═══════════════════════════════════════════════╝
|
|
116
|
+
`);
|
|
117
|
+
|
|
118
|
+
const agentName = await question(`What's your agent name? [${process.env.USER || 'agent'}] `) || process.env.USER || 'agent';
|
|
119
|
+
|
|
120
|
+
console.log(`\n🤖 Agent: ${agentName}`);
|
|
121
|
+
console.log(`📁 CoCo: ${COCO_DIR}\n`);
|
|
122
|
+
|
|
123
|
+
console.log('Which clients do you want to configure?');
|
|
124
|
+
console.log(' 1) OpenCode / Clew Code');
|
|
125
|
+
console.log(' 2) Claude Code');
|
|
126
|
+
console.log(' 3) Cursor');
|
|
127
|
+
console.log(' 4) All of the above');
|
|
128
|
+
console.log(' 5) Just shell aliases + env file');
|
|
129
|
+
console.log(' 6) Exit\n');
|
|
130
|
+
|
|
131
|
+
const choice = await question('Choose [4]: ') || '4';
|
|
132
|
+
console.log();
|
|
133
|
+
|
|
134
|
+
// Always register agent on bus
|
|
135
|
+
registerAgentOnBus(agentName);
|
|
136
|
+
|
|
137
|
+
const generated = [];
|
|
138
|
+
|
|
139
|
+
switch (choice) {
|
|
140
|
+
case '1':
|
|
141
|
+
generated.push(...await setupOpenCode(agentName));
|
|
142
|
+
break;
|
|
143
|
+
case '2':
|
|
144
|
+
generated.push(...await setupClaudeCode(agentName));
|
|
145
|
+
break;
|
|
146
|
+
case '3':
|
|
147
|
+
generated.push(...await setupCursor(agentName));
|
|
148
|
+
break;
|
|
149
|
+
case '4':
|
|
150
|
+
generated.push(...await setupOpenCode(agentName));
|
|
151
|
+
generated.push(...await setupClaudeCode(agentName));
|
|
152
|
+
generated.push(...await setupCursor(agentName));
|
|
153
|
+
break;
|
|
154
|
+
case '5':
|
|
155
|
+
generated.push(...generateEnvAndAliases(agentName));
|
|
156
|
+
break;
|
|
157
|
+
case '6':
|
|
158
|
+
console.log('Bye! 🐺');
|
|
159
|
+
process.exit(0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`
|
|
163
|
+
╔═══════════════════════════════════════════════╗
|
|
164
|
+
║ ✅ Setup Complete! ║
|
|
165
|
+
║ ║
|
|
166
|
+
║ Your agent "${agentName}" is now on the bus ║
|
|
167
|
+
║ Run "node coco-cli.js status" to verify ║
|
|
168
|
+
╚═══════════════════════════════════════════════╝
|
|
169
|
+
`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function setupOpenCode(agentName) {
|
|
173
|
+
const config = generateMcpConfigOpenCode();
|
|
174
|
+
const outPath = path.join(COCO_DIR, 'opencode-mcp.json');
|
|
175
|
+
fs.writeFileSync(outPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
176
|
+
console.log(` ✅ OpenCode MCP config → ${outPath}`);
|
|
177
|
+
|
|
178
|
+
// Try to place in common locations
|
|
179
|
+
const opencodeConfigs = [
|
|
180
|
+
path.join(os.homedir(), '.opencode.jsonc'),
|
|
181
|
+
path.join(os.homedir(), '.config', 'opencode.json'),
|
|
182
|
+
];
|
|
183
|
+
for (const loc of opencodeConfigs) {
|
|
184
|
+
const dir = path.dirname(loc);
|
|
185
|
+
if (fs.existsSync(dir)) {
|
|
186
|
+
console.log(` 🔧 Copy to ${loc} to auto-connect`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const [envFile] = generateEnvAndAliases(agentName);
|
|
191
|
+
return ['opencode-mcp.json', envFile];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function setupClaudeCode(agentName) {
|
|
195
|
+
const config = generateMcpConfigClaudeCode();
|
|
196
|
+
const outPath = path.join(COCO_DIR, 'claude-mcp.json');
|
|
197
|
+
fs.writeFileSync(outPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
198
|
+
console.log(` ✅ Claude Code MCP config → ${outPath}`);
|
|
199
|
+
console.log(` Run: claude mcp add coco "node ${path.join(COCO_DIR, 'index.js')}"`);
|
|
200
|
+
|
|
201
|
+
const [envFile] = generateEnvAndAliases(agentName);
|
|
202
|
+
return ['claude-mcp.json', envFile];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function setupCursor(agentName) {
|
|
206
|
+
const config = generateMcpConfigCursor();
|
|
207
|
+
const outPath = path.join(COCO_DIR, 'cursor-mcp.json');
|
|
208
|
+
fs.writeFileSync(outPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
209
|
+
console.log(` ✅ Cursor MCP config → ${outPath}`);
|
|
210
|
+
console.log(` Copy .cursor/mcp.json to your project`);
|
|
211
|
+
|
|
212
|
+
const [envFile] = generateEnvAndAliases(agentName);
|
|
213
|
+
return ['cursor-mcp.json', envFile];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function generateEnvAndAliases(agentName) {
|
|
217
|
+
const envContent = generateEnvFile(agentName);
|
|
218
|
+
const envPath = path.join(COCO_DIR, '.env.coco');
|
|
219
|
+
fs.writeFileSync(envPath, envContent, 'utf-8');
|
|
220
|
+
console.log(` ✅ Environment file → ${envPath}`);
|
|
221
|
+
|
|
222
|
+
const aliasContent = generateShellAliases();
|
|
223
|
+
const aliasPath = path.join(COCO_DIR, 'coco-aliases.sh');
|
|
224
|
+
fs.writeFileSync(aliasPath, aliasContent, 'utf-8');
|
|
225
|
+
console.log(` ✅ Shell aliases → ${aliasPath}`);
|
|
226
|
+
console.log(` Source with: . ${aliasPath}`);
|
|
227
|
+
|
|
228
|
+
return ['.env.coco', 'coco-aliases.sh'];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function registerAgentOnBus(agentName) {
|
|
232
|
+
const agentsFile = path.join(BUS_DIR, 'agents.json');
|
|
233
|
+
if (!fs.existsSync(BUS_DIR)) fs.mkdirSync(BUS_DIR, { recursive: true });
|
|
234
|
+
if (!fs.existsSync(MSGS_DIR)) fs.mkdirSync(MSGS_DIR, { recursive: true });
|
|
235
|
+
|
|
236
|
+
const agents = fs.existsSync(agentsFile) ? JSON.parse(fs.readFileSync(agentsFile, 'utf-8')) : {};
|
|
237
|
+
agents[agentName] = {
|
|
238
|
+
name: agentName,
|
|
239
|
+
description: `Setup via CoCo Config Wizard`,
|
|
240
|
+
capabilities: [],
|
|
241
|
+
tags: [],
|
|
242
|
+
status: 'idle',
|
|
243
|
+
version: '1.0.0',
|
|
244
|
+
last_seen: new Date().toISOString(),
|
|
245
|
+
registered_at: agents[agentName]?.registered_at || new Date().toISOString(),
|
|
246
|
+
};
|
|
247
|
+
fs.writeFileSync(agentsFile, JSON.stringify(agents, null, 2), 'utf-8');
|
|
248
|
+
console.log(` ✅ Registered "${agentName}" on the bus`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ═══════════════════════════════════════════════════════
|
|
252
|
+
// Quick Setup
|
|
253
|
+
// ═══════════════════════════════════════════════════════
|
|
254
|
+
|
|
255
|
+
function quickSetup() {
|
|
256
|
+
const agentName = process.env.COCO_AGENT || process.env.USER || 'agent';
|
|
257
|
+
registerAgentOnBus(agentName);
|
|
258
|
+
|
|
259
|
+
// Generate all configs
|
|
260
|
+
fs.writeFileSync(path.join(COCO_DIR, 'opencode-mcp.json'), JSON.stringify(generateMcpConfigOpenCode(), null, 2), 'utf-8');
|
|
261
|
+
fs.writeFileSync(path.join(COCO_DIR, 'claude-mcp.json'), JSON.stringify(generateMcpConfigClaudeCode(), null, 2), 'utf-8');
|
|
262
|
+
fs.writeFileSync(path.join(COCO_DIR, 'cursor-mcp.json'), JSON.stringify(generateMcpConfigCursor(), null, 2), 'utf-8');
|
|
263
|
+
fs.writeFileSync(path.join(COCO_DIR, '.env.coco'), generateEnvFile(agentName), 'utf-8');
|
|
264
|
+
fs.writeFileSync(path.join(COCO_DIR, 'coco-aliases.sh'), generateShellAliases(), 'utf-8');
|
|
265
|
+
|
|
266
|
+
console.log(`
|
|
267
|
+
╔═══════════════════════════════════════════════╗
|
|
268
|
+
║ ✅ Quick Setup Complete ║
|
|
269
|
+
║ Agent: ${agentName.padEnd(34)}║
|
|
270
|
+
║ ║
|
|
271
|
+
║ Generated: opencode-mcp.json ║
|
|
272
|
+
║ claude-mcp.json ║
|
|
273
|
+
║ cursor-mcp.json ║
|
|
274
|
+
║ .env.coco, coco-aliases.sh ║
|
|
275
|
+
╚═══════════════════════════════════════════════╝
|
|
276
|
+
`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ═══════════════════════════════════════════════════════
|
|
280
|
+
// CLI
|
|
281
|
+
// ═══════════════════════════════════════════════════════
|
|
282
|
+
|
|
283
|
+
async function main() {
|
|
284
|
+
const arg = process.argv[2];
|
|
285
|
+
|
|
286
|
+
if (arg === '--quick' || arg === '-q') {
|
|
287
|
+
quickSetup();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (arg === 'list') {
|
|
292
|
+
console.log(`\n📋 CoCo Bus — Recommended Configs\n`);
|
|
293
|
+
console.log(` 1. OpenCode / Clew Code:`);
|
|
294
|
+
console.log(` Add to .mcp.json:`);
|
|
295
|
+
console.log(` ${JSON.stringify(generateMcpConfigOpenCode(), null, 4).split('\n').map(l => ' ' + l).join('\n')}\n`);
|
|
296
|
+
console.log(` 2. Claude Code:`);
|
|
297
|
+
console.log(` claude mcp add coco "node ${path.join(COCO_DIR, 'index.js')}"\n`);
|
|
298
|
+
console.log(` 3. Cursor:`);
|
|
299
|
+
console.log(` Add to .cursor/mcp.json`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (arg === 'opencode') {
|
|
304
|
+
registerAgentOnBus(process.env.COCO_AGENT || 'agent');
|
|
305
|
+
fs.writeFileSync(path.join(COCO_DIR, 'opencode-mcp.json'), JSON.stringify(generateMcpConfigOpenCode(), null, 2), 'utf-8');
|
|
306
|
+
console.log(`✅ OpenCode MCP config generated: opencode-mcp.json`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Interactive
|
|
311
|
+
await wizard();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Need os for homedir
|
|
315
|
+
const os = require('os');
|
|
316
|
+
|
|
317
|
+
main().catch(err => {
|
|
318
|
+
console.error('Error:', err.message);
|
|
319
|
+
process.exit(1);
|
|
320
|
+
});
|
package/tunnel.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CoCo Tunnel — Cross-machine Bus Proxy CLI Wrapper
|
|
4
|
+
*
|
|
5
|
+
* Thin wrapper around lib/tunnel.js
|
|
6
|
+
* Usage:
|
|
7
|
+
* node tunnel.js server --port 9090 --secret mykey
|
|
8
|
+
* node tunnel.js client --host ... --port ... --secret ...
|
|
9
|
+
* node tunnel.js sync --remote http://... --interval 5000
|
|
10
|
+
* node tunnel.js ssh --remote user@host
|
|
11
|
+
* node tunnel.js --help
|
|
12
|
+
*/
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { startServer, startClient, startSync, printSSHHelp } = require('./lib/tunnel');
|
|
15
|
+
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const mode = args[0];
|
|
18
|
+
|
|
19
|
+
function parseArgs() {
|
|
20
|
+
const opts = { busDir: path.join(__dirname, '.bus') };
|
|
21
|
+
for (let i = 1; i < args.length; i++) {
|
|
22
|
+
switch (args[i]) {
|
|
23
|
+
case '--port': opts.port = parseInt(args[++i], 10); break;
|
|
24
|
+
case '--secret': opts.secret = args[++i]; break;
|
|
25
|
+
case '--host': opts.host = args[++i]; break;
|
|
26
|
+
case '--remote': opts.remote = args[++i]; break;
|
|
27
|
+
case '--interval': opts.interval = parseInt(args[++i], 10); break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return opts;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function help() {
|
|
34
|
+
console.log(`\n╔═══════════════════════════════════════════════╗\n║ CoCo — Tunnel ║\n╚═══════════════════════════════════════════════╝\n`);
|
|
35
|
+
console.log('Usage:');
|
|
36
|
+
console.log(' node tunnel.js server [options] Start tunnel server (receiving end)');
|
|
37
|
+
console.log(' node tunnel.js client [options] Start tunnel client (sending end)');
|
|
38
|
+
console.log(' node tunnel.js sync [options] Bidirectional sync');
|
|
39
|
+
console.log(' node tunnel.js ssh [options] SSH tunnel instructions\n');
|
|
40
|
+
console.log('Options:');
|
|
41
|
+
console.log(' --port <number> Port (default: 9090)');
|
|
42
|
+
console.log(' --host <hostname> Remote host (default: localhost)');
|
|
43
|
+
console.log(' --secret <key> Auth token');
|
|
44
|
+
console.log(' --remote <url> Remote tunnel URL (for sync/client)');
|
|
45
|
+
console.log(' --interval <ms> Sync interval (default: 10000)');
|
|
46
|
+
console.log(' --help This help\n');
|
|
47
|
+
console.log('Examples:');
|
|
48
|
+
console.log(' node tunnel.js server --port 9090 --secret ***');
|
|
49
|
+
console.log(' node tunnel.js client --host 192.168.1.100 --port 9090 --secret ***');
|
|
50
|
+
console.log(' node tunnel.js sync --remote http://192.168.1.100:9090');
|
|
51
|
+
console.log(' node tunnel.js ssh --remote user@server.example.com\n');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function main() {
|
|
55
|
+
if (!mode || mode === '--help') { help(); return; }
|
|
56
|
+
const opts = parseArgs();
|
|
57
|
+
switch (mode) {
|
|
58
|
+
case 'server': startServer(opts); break;
|
|
59
|
+
case 'client': await startClient(opts); break;
|
|
60
|
+
case 'sync': await startSync(opts); break;
|
|
61
|
+
case 'ssh': printSSHHelp(opts); break;
|
|
62
|
+
default: console.error(`Unknown mode: ${mode}`); help(); process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
main().catch(err => { console.error('Error:', err.message); process.exit(1); });
|