omniwire 2.4.0 → 2.5.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/README.md +65 -24
- package/dist/mcp/server.js +152 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/nodes/manager.js +67 -122
- package/dist/nodes/manager.js.map +1 -1
- package/dist/nodes/transfer.d.ts +1 -1
- package/dist/nodes/transfer.js +59 -51
- package/dist/nodes/transfer.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,26 +1,38 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://capsule-render.vercel.app/api?type=
|
|
4
|
-
<source media="(prefers-color-scheme: light)" srcset="https://capsule-render.vercel.app/api?type=
|
|
5
|
-
<img alt="OmniWire" src="https://capsule-render.vercel.app/api?type=
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://capsule-render.vercel.app/api?type=venom&color=0:0A0E14,50:0D1B2A,100:1B2838&height=220§ion=header&text=OmniWire&fontSize=80&fontColor=59C2FF&animation=fadeIn&fontAlignY=32&desc=Multi-Agent%20Mesh%20Control%20%E2%80%94%2053%20MCP%20Tools%20%C2%B7%20A2A%20Protocol%20%C2%B7%20~80ms%20Latency&descSize=16&descColor=8B949E&descAlignY=58" />
|
|
4
|
+
<source media="(prefers-color-scheme: light)" srcset="https://capsule-render.vercel.app/api?type=venom&color=0:E8EAED,50:D4D8DE,100:59C2FF&height=220§ion=header&text=OmniWire&fontSize=80&fontColor=0A0E14&animation=fadeIn&fontAlignY=32&desc=Multi-Agent%20Mesh%20Control%20%E2%80%94%2053%20MCP%20Tools%20%C2%B7%20A2A%20Protocol%20%C2%B7%20~80ms%20Latency&descSize=16&descColor=586069&descAlignY=58" />
|
|
5
|
+
<img alt="OmniWire" src="https://capsule-render.vercel.app/api?type=venom&color=0:0A0E14,50:0D1B2A,100:1B2838&height=220§ion=header&text=OmniWire&fontSize=80&fontColor=59C2FF&animation=fadeIn&fontAlignY=32&desc=Multi-Agent%20Mesh%20Control%20%E2%80%94%2053%20MCP%20Tools%20%C2%B7%20A2A%20Protocol%20%C2%B7%20~80ms%20Latency&descSize=16&descColor=8B949E&descAlignY=58" />
|
|
6
6
|
</picture>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/omniwire"><img src="https://img.shields.io/npm/v/omniwire?style=for-the-badge&logo=npm&color=CB3837&labelColor=0A0E14" alt="npm" /></a>
|
|
11
|
-
<img src="https://img.shields.io/badge/MCP-
|
|
12
|
-
<img src="https://img.shields.io/badge/A2A-
|
|
13
|
-
<img src="https://img.shields.io/badge/
|
|
14
|
-
<img src="https://img.shields.io/badge/
|
|
11
|
+
<img src="https://img.shields.io/badge/MCP-53_tools-59C2FF?style=for-the-badge&labelColor=0A0E14" alt="tools" />
|
|
12
|
+
<img src="https://img.shields.io/badge/A2A-protocol-00C853?style=for-the-badge&labelColor=0A0E14" alt="A2A" />
|
|
13
|
+
<img src="https://img.shields.io/badge/latency-~80ms-FF6D00?style=for-the-badge&labelColor=0A0E14" alt="latency" />
|
|
14
|
+
<img src="https://img.shields.io/badge/lz4-transfer-CC93E6?style=for-the-badge&labelColor=0A0E14" alt="lz4" />
|
|
15
15
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-8B949E?style=for-the-badge&labelColor=0A0E14" alt="license" /></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
|
+
<br/>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<b>The infrastructure layer for AI agent swarms.</b>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
18
24
|
<p align="center">
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
53 MCP tools • Agent-to-Agent messaging • Distributed task queues • Capability routing<br/>
|
|
26
|
+
AES-128-GCM SSH2 • LZ4 transfers • Circuit breakers • Multi-path failover<br/>
|
|
27
|
+
Session chaining • Pipeline DAGs • Blackboard architecture • Event pub/sub
|
|
22
28
|
</p>
|
|
23
29
|
|
|
30
|
+
<br/>
|
|
31
|
+
|
|
32
|
+
> **v2.5** — AES-128-GCM cipher preference, 2s keepalive, LZ4 compression, `shuf` port finder, SFTP-first reads, agent registry, blackboard, task queues, capability routing. See [changelog](#changelog).
|
|
33
|
+
|
|
34
|
+
<br/>
|
|
35
|
+
|
|
24
36
|
---
|
|
25
37
|
|
|
26
38
|
## Quick Start
|
|
@@ -71,7 +83,7 @@ graph TB
|
|
|
71
83
|
direction TB
|
|
72
84
|
MCP["MCP Protocol Layer<br/>stdio | SSE | REST"]
|
|
73
85
|
|
|
74
|
-
subgraph tools["
|
|
86
|
+
subgraph tools["53 Tools"]
|
|
75
87
|
direction LR
|
|
76
88
|
EXEC["Execution<br/>exec run batch<br/>broadcast pipeline"]
|
|
77
89
|
AGENT["Agentic<br/>store watch task<br/>a2a events locks"]
|
|
@@ -207,7 +219,7 @@ Nodes --push--> PostgreSQL (cyberbase)
|
|
|
207
219
|
|
|
208
220
|
---
|
|
209
221
|
|
|
210
|
-
## All
|
|
222
|
+
## All 53 Tools
|
|
211
223
|
|
|
212
224
|
### Execution (5)
|
|
213
225
|
|
|
@@ -219,7 +231,7 @@ Nodes --push--> PostgreSQL (cyberbase)
|
|
|
219
231
|
| `omniwire_broadcast` | Execute on all nodes simultaneously. JSON format support. |
|
|
220
232
|
| `omniwire_pipeline` | Multi-step DAG. `{{prev}}`/`{{stepN}}` interpolation, per-step error handling, cross-node. |
|
|
221
233
|
|
|
222
|
-
### Agentic / A2A (
|
|
234
|
+
### Agentic / A2A (13)
|
|
223
235
|
|
|
224
236
|
| Tool | Description |
|
|
225
237
|
|------|-------------|
|
|
@@ -231,6 +243,10 @@ Nodes --push--> PostgreSQL (cyberbase)
|
|
|
231
243
|
| `omniwire_semaphore` | Distributed locking. Atomic acquire/release to prevent race conditions. |
|
|
232
244
|
| `omniwire_event` | Pub/sub events. Emit/poll timestamped events per topic. ACP/A2A/ACPX compatible. |
|
|
233
245
|
| `omniwire_workflow` | Define and run reusable named workflows (DAGs). Stored on disk, triggered by any agent. |
|
|
246
|
+
| `omniwire_agent_registry` | Register/discover agents by capabilities. Dynamic A2A routing. Heartbeat. |
|
|
247
|
+
| `omniwire_blackboard` | Shared blackboard for agent swarms. Post findings, read, search across topics. |
|
|
248
|
+
| `omniwire_task_queue` | Distributed task queue. Enqueue/dequeue with priorities. Complete/fail reporting. |
|
|
249
|
+
| `omniwire_capability` | Query node capabilities (tools, runtimes, GPU). Intelligent task routing. |
|
|
234
250
|
|
|
235
251
|
### Files & Transfer (6)
|
|
236
252
|
|
|
@@ -297,16 +313,30 @@ Nodes --push--> PostgreSQL (cyberbase)
|
|
|
297
313
|
|
|
298
314
|
## Performance
|
|
299
315
|
|
|
300
|
-
| Operation | Latency |
|
|
301
|
-
|
|
302
|
-
| Command exec |
|
|
303
|
-
| Mesh status
|
|
304
|
-
| File read (<1MB) |
|
|
305
|
-
| Transfer (10MB) |
|
|
306
|
-
|
|
|
307
|
-
|
|
|
308
|
-
|
|
|
309
|
-
|
|
|
316
|
+
| Operation | Latency | v2.5 Optimization |
|
|
317
|
+
|-----------|---------|-------------------|
|
|
318
|
+
| **Command exec** | **~80ms** | AES-128-GCM cipher, persistent SSH2 channel, zero-fork `:` ping |
|
|
319
|
+
| **Mesh status** | **~100ms** | Parallel probes, 5s cache, single `/proc` read (no pipes) |
|
|
320
|
+
| **File read (<1MB)** | **~60ms** | SFTP-first path (skips `cat` shell fork) |
|
|
321
|
+
| **Transfer (10MB)** | **~120ms** | LZ4 compression (10x faster than gzip), 50ms bind delay |
|
|
322
|
+
| **Transfer (1GB)** | **~8s** | aria2c 16-connection parallel, 150ms server startup |
|
|
323
|
+
| **Pipeline (5 steps)** | **~400ms** | `{{prev}}` interpolation, no extra tool calls |
|
|
324
|
+
| **Health check (all)** | **~90ms** | Parallel Promise.allSettled, structured JSON |
|
|
325
|
+
| **A2A message** | **~85ms** | File-append queue, atomic dequeue |
|
|
326
|
+
| **Config push** | **~150ms** | Parallel deploy + Obsidian mirror |
|
|
327
|
+
| **Reconnect** | **~300ms** | 300ms initial delay (was 500ms), 2s keepalive detection |
|
|
328
|
+
|
|
329
|
+
**Optimizations in v2.5:**
|
|
330
|
+
- **Cipher**: AES-128-GCM (AES-NI accelerated) preferred over default negotiation
|
|
331
|
+
- **Key exchange**: curve25519-sha256 preferred (fastest modern KEX)
|
|
332
|
+
- **Keepalive**: 2s interval, 2 retries = 4s dead detection (was 6s)
|
|
333
|
+
- **Port finder**: `shuf` (pure bash) replaces `python3 -c socket` (saves ~30ms per transfer)
|
|
334
|
+
- **Compression**: LZ4-1 for transfers (10x faster than gzip, ~same ratio for mixed data)
|
|
335
|
+
- **Buffer**: Array push + join replaces string concatenation (O(n) vs O(n^2) for large outputs)
|
|
336
|
+
- **Status**: Single `/proc` read replaces multiple piped commands
|
|
337
|
+
- **Health ping**: `:` builtin replaces `true` (no hash lookup, no fork)
|
|
338
|
+
- **Reads**: SFTP subsystem tried first, falls back to `cat` only on failure
|
|
339
|
+
- **Circuit breaker**: 15s recovery (was 20s), 10s reconnect cap (was 15s)
|
|
310
340
|
|
|
311
341
|
---
|
|
312
342
|
|
|
@@ -357,6 +387,17 @@ Create `~/.omniwire/mesh.json`:
|
|
|
357
387
|
|
|
358
388
|
## Changelog
|
|
359
389
|
|
|
390
|
+
<details>
|
|
391
|
+
<summary><b>v2.5.0 -- Performance Overhaul, A2A Protocol Expansion</b></summary>
|
|
392
|
+
|
|
393
|
+
**Performance**: AES-128-GCM cipher, curve25519-sha256 KEX, 2s keepalive, LZ4 transfers (10x faster), `shuf` port finder (-30ms), SFTP-first reads, array buffer concat, `/proc` single-read status, `:` builtin health ping, 300ms reconnect start, 15s circuit breaker.
|
|
394
|
+
|
|
395
|
+
**4 new A2A tools** (49 -> 53): agent_registry (capability discovery), blackboard (swarm collaboration), task_queue (distributed work), capability (node routing).
|
|
396
|
+
|
|
397
|
+
**Connectivity**: Always-on 2s keepalive with 4s dead detection. 5s connect timeout. 10s reconnect cap. 15s circuit recovery.
|
|
398
|
+
|
|
399
|
+
</details>
|
|
400
|
+
|
|
360
401
|
<details>
|
|
361
402
|
<summary><b>v2.4.0 -- Agentic Loop, A2A, Multi-Agent Orchestration</b></summary>
|
|
362
403
|
|
|
@@ -396,7 +437,7 @@ Multi-path SSH (WireGuard/Tailscale/Public), SSH key caching, CyberBase integrat
|
|
|
396
437
|
```
|
|
397
438
|
omniwire/
|
|
398
439
|
src/
|
|
399
|
-
mcp/ MCP server (
|
|
440
|
+
mcp/ MCP server (53 tools, 3 transports)
|
|
400
441
|
nodes/ SSH2 pool, transfer engine, PTY, tunnels
|
|
401
442
|
sync/ CyberSync + CyberBase (PostgreSQL, Obsidian, encryption)
|
|
402
443
|
protocol/ Mesh config, types, path parsing
|
package/dist/mcp/server.js
CHANGED
|
@@ -67,7 +67,7 @@ const resultStore = new Map(); // key -> value store for chaining
|
|
|
67
67
|
export function createOmniWireServer(manager, transfer) {
|
|
68
68
|
const server = new McpServer({
|
|
69
69
|
name: 'omniwire',
|
|
70
|
-
version: '2.
|
|
70
|
+
version: '2.5.0',
|
|
71
71
|
});
|
|
72
72
|
const shells = new ShellManager(manager);
|
|
73
73
|
const realtime = new RealtimeChannel(manager);
|
|
@@ -1156,6 +1156,157 @@ tags: ${meshNode.tags.join(', ')}`;
|
|
|
1156
1156
|
}
|
|
1157
1157
|
return fail('invalid action');
|
|
1158
1158
|
});
|
|
1159
|
+
// --- Tool 42: omniwire_agent_registry ---
|
|
1160
|
+
server.tool('omniwire_agent_registry', 'Register/discover agents on the mesh. Agents announce their capabilities and other agents can discover them. Enables dynamic A2A routing and capability-based task delegation.', {
|
|
1161
|
+
action: z.enum(['register', 'deregister', 'discover', 'list', 'heartbeat']).describe('Action'),
|
|
1162
|
+
node: z.string().optional().describe('Node hosting registry (default: contabo)'),
|
|
1163
|
+
agent_id: z.string().optional().describe('Unique agent ID'),
|
|
1164
|
+
capabilities: z.array(z.string()).optional().describe('Agent capabilities (e.g., ["scan", "exploit", "report"])'),
|
|
1165
|
+
metadata: z.string().optional().describe('JSON metadata about the agent'),
|
|
1166
|
+
capability: z.string().optional().describe('Capability to search for (discover action)'),
|
|
1167
|
+
}, async ({ action, node, agent_id, capabilities, metadata, capability }) => {
|
|
1168
|
+
const nodeId = node ?? 'contabo';
|
|
1169
|
+
const regDir = '/tmp/.omniwire-agents';
|
|
1170
|
+
if (action === 'register') {
|
|
1171
|
+
if (!agent_id)
|
|
1172
|
+
return fail('agent_id required');
|
|
1173
|
+
const entry = JSON.stringify({ id: agent_id, capabilities: capabilities ?? [], metadata: metadata ?? '{}', ts: Date.now(), node: nodeId });
|
|
1174
|
+
const escaped = entry.replace(/'/g, "'\\''");
|
|
1175
|
+
await manager.exec(nodeId, `mkdir -p ${regDir} && echo '${escaped}' > ${regDir}/${agent_id}.json`);
|
|
1176
|
+
return okBrief(`agent ${agent_id} registered (${(capabilities ?? []).join(', ')})`);
|
|
1177
|
+
}
|
|
1178
|
+
if (action === 'deregister') {
|
|
1179
|
+
if (!agent_id)
|
|
1180
|
+
return fail('agent_id required');
|
|
1181
|
+
await manager.exec(nodeId, `rm -f ${regDir}/${agent_id}.json`);
|
|
1182
|
+
return okBrief(`agent ${agent_id} deregistered`);
|
|
1183
|
+
}
|
|
1184
|
+
if (action === 'heartbeat') {
|
|
1185
|
+
if (!agent_id)
|
|
1186
|
+
return fail('agent_id required');
|
|
1187
|
+
await manager.exec(nodeId, `[ -f ${regDir}/${agent_id}.json ] && tmp=$(cat ${regDir}/${agent_id}.json) && echo "$tmp" | sed 's/"ts":[0-9]*/"ts":${Date.now()}/' > ${regDir}/${agent_id}.json`);
|
|
1188
|
+
return okBrief(`agent ${agent_id} heartbeat`);
|
|
1189
|
+
}
|
|
1190
|
+
if (action === 'discover' && capability) {
|
|
1191
|
+
const result = await manager.exec(nodeId, `grep -l '"${capability}"' ${regDir}/*.json 2>/dev/null | xargs -I{} cat {} 2>/dev/null`);
|
|
1192
|
+
return ok(nodeId, result.durationMs, result.stdout || '(no agents with that capability)', `discover ${capability}`);
|
|
1193
|
+
}
|
|
1194
|
+
if (action === 'list') {
|
|
1195
|
+
const result = await manager.exec(nodeId, `cat ${regDir}/*.json 2>/dev/null || echo '(no agents)'`);
|
|
1196
|
+
return ok(nodeId, result.durationMs, result.stdout, 'agent registry');
|
|
1197
|
+
}
|
|
1198
|
+
return fail('invalid action');
|
|
1199
|
+
});
|
|
1200
|
+
// --- Tool 43: omniwire_blackboard ---
|
|
1201
|
+
server.tool('omniwire_blackboard', 'Shared blackboard for multi-agent collaboration. Agents post findings, hypotheses, and decisions to topic-scoped boards. Other agents read and build on them. Classic AI blackboard architecture for agent swarms.', {
|
|
1202
|
+
action: z.enum(['post', 'read', 'topics', 'clear', 'search']).describe('Action'),
|
|
1203
|
+
node: z.string().optional(),
|
|
1204
|
+
topic: z.string().optional().describe('Board topic (e.g., "recon-findings", "vuln-analysis")'),
|
|
1205
|
+
content: z.string().optional().describe('Content to post'),
|
|
1206
|
+
author: z.string().optional().describe('Author agent ID'),
|
|
1207
|
+
query: z.string().optional().describe('Search query (grep pattern) for search action'),
|
|
1208
|
+
limit: z.number().optional().describe('Max entries (default: 20)'),
|
|
1209
|
+
}, async ({ action, node, topic, content, author, query, limit }) => {
|
|
1210
|
+
const nodeId = node ?? 'contabo';
|
|
1211
|
+
const bbDir = '/tmp/.omniwire-blackboard';
|
|
1212
|
+
const n = limit ?? 20;
|
|
1213
|
+
if (action === 'post') {
|
|
1214
|
+
if (!topic || !content)
|
|
1215
|
+
return fail('topic and content required');
|
|
1216
|
+
const entry = JSON.stringify({ ts: Date.now(), author: author ?? 'agent', content });
|
|
1217
|
+
const escaped = entry.replace(/'/g, "'\\''");
|
|
1218
|
+
await manager.exec(nodeId, `mkdir -p ${bbDir} && echo '${escaped}' >> ${bbDir}/${topic}.log`);
|
|
1219
|
+
return okBrief(`posted to ${topic} (${content.length} chars)`);
|
|
1220
|
+
}
|
|
1221
|
+
if (action === 'read') {
|
|
1222
|
+
if (!topic)
|
|
1223
|
+
return fail('topic required');
|
|
1224
|
+
const result = await manager.exec(nodeId, `tail -${n} ${bbDir}/${topic}.log 2>/dev/null || echo '(empty board)'`);
|
|
1225
|
+
return ok(nodeId, result.durationMs, result.stdout, `board:${topic}`);
|
|
1226
|
+
}
|
|
1227
|
+
if (action === 'topics') {
|
|
1228
|
+
const result = await manager.exec(nodeId, `ls -1 ${bbDir}/*.log 2>/dev/null | xargs -I{} sh -c 'echo "$(basename {} .log) $(wc -l < {})"' 2>/dev/null || echo '(no topics)'`);
|
|
1229
|
+
return ok(nodeId, result.durationMs, result.stdout, 'blackboard topics');
|
|
1230
|
+
}
|
|
1231
|
+
if (action === 'search' && query) {
|
|
1232
|
+
const escaped = query.replace(/'/g, "'\\''");
|
|
1233
|
+
const topicFilter = topic ? `${bbDir}/${topic}.log` : `${bbDir}/*.log`;
|
|
1234
|
+
const result = await manager.exec(nodeId, `grep -h '${escaped}' ${topicFilter} 2>/dev/null | tail -${n}`);
|
|
1235
|
+
return ok(nodeId, result.durationMs, result.stdout || '(no matches)', `search:${query}`);
|
|
1236
|
+
}
|
|
1237
|
+
if (action === 'clear') {
|
|
1238
|
+
if (!topic)
|
|
1239
|
+
return fail('topic required');
|
|
1240
|
+
await manager.exec(nodeId, `rm -f ${bbDir}/${topic}.log`);
|
|
1241
|
+
return okBrief(`board ${topic} cleared`);
|
|
1242
|
+
}
|
|
1243
|
+
return fail('invalid action');
|
|
1244
|
+
});
|
|
1245
|
+
// --- Tool 44: omniwire_task_queue ---
|
|
1246
|
+
server.tool('omniwire_task_queue', 'Distributed task queue for agent swarms. Producers enqueue tasks, consumer agents dequeue and process them. Supports priorities, deadlines, and result reporting. Core A2A work distribution primitive.', {
|
|
1247
|
+
action: z.enum(['enqueue', 'dequeue', 'complete', 'fail', 'status', 'pending']).describe('Action'),
|
|
1248
|
+
node: z.string().optional(),
|
|
1249
|
+
queue: z.string().optional().describe('Queue name (default: "default")'),
|
|
1250
|
+
task: z.string().optional().describe('Task payload (JSON) for enqueue'),
|
|
1251
|
+
priority: z.number().optional().describe('Priority 0-9, higher = more urgent (default: 5)'),
|
|
1252
|
+
task_id: z.string().optional().describe('Task ID for complete/fail'),
|
|
1253
|
+
result: z.string().optional().describe('Result data for complete'),
|
|
1254
|
+
error: z.string().optional().describe('Error message for fail'),
|
|
1255
|
+
}, async ({ action, node, queue, task, priority, task_id, result: taskResult, error: taskError }) => {
|
|
1256
|
+
const nodeId = node ?? 'contabo';
|
|
1257
|
+
const qName = queue ?? 'default';
|
|
1258
|
+
const qDir = `/tmp/.omniwire-taskq/${qName}`;
|
|
1259
|
+
if (action === 'enqueue') {
|
|
1260
|
+
if (!task)
|
|
1261
|
+
return fail('task required');
|
|
1262
|
+
const id = `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
1263
|
+
const pri = priority ?? 5;
|
|
1264
|
+
const entry = JSON.stringify({ id, priority: pri, ts: Date.now(), status: 'pending', task: JSON.parse(task) });
|
|
1265
|
+
const escaped = entry.replace(/'/g, "'\\''");
|
|
1266
|
+
await manager.exec(nodeId, `mkdir -p ${qDir} && echo '${escaped}' > ${qDir}/${pri}_${id}.task`);
|
|
1267
|
+
return okBrief(`enqueued ${id} (priority ${pri})`);
|
|
1268
|
+
}
|
|
1269
|
+
if (action === 'dequeue') {
|
|
1270
|
+
// Take highest priority task (9 first, then 8, ...)
|
|
1271
|
+
const result = await manager.exec(nodeId, `f=$(ls -r ${qDir}/*.task 2>/dev/null | head -1); [ -n "$f" ] && cat "$f" && rm -f "$f" || echo '(empty queue)'`);
|
|
1272
|
+
return ok(nodeId, result.durationMs, result.stdout, `dequeue:${qName}`);
|
|
1273
|
+
}
|
|
1274
|
+
if (action === 'complete' && task_id) {
|
|
1275
|
+
const entry = JSON.stringify({ id: task_id, status: 'complete', ts: Date.now(), result: taskResult ?? '' });
|
|
1276
|
+
const escaped = entry.replace(/'/g, "'\\''");
|
|
1277
|
+
await manager.exec(nodeId, `mkdir -p ${qDir}/done && echo '${escaped}' > ${qDir}/done/${task_id}.result`);
|
|
1278
|
+
return okBrief(`task ${task_id} completed`);
|
|
1279
|
+
}
|
|
1280
|
+
if (action === 'fail' && task_id) {
|
|
1281
|
+
const entry = JSON.stringify({ id: task_id, status: 'failed', ts: Date.now(), error: taskError ?? 'unknown' });
|
|
1282
|
+
const escaped = entry.replace(/'/g, "'\\''");
|
|
1283
|
+
await manager.exec(nodeId, `mkdir -p ${qDir}/failed && echo '${escaped}' > ${qDir}/failed/${task_id}.result`);
|
|
1284
|
+
return okBrief(`task ${task_id} failed`);
|
|
1285
|
+
}
|
|
1286
|
+
if (action === 'status') {
|
|
1287
|
+
const result = await manager.exec(nodeId, `echo "pending: $(ls ${qDir}/*.task 2>/dev/null | wc -l)"; echo "done: $(ls ${qDir}/done/*.result 2>/dev/null | wc -l)"; echo "failed: $(ls ${qDir}/failed/*.result 2>/dev/null | wc -l)"`);
|
|
1288
|
+
return ok(nodeId, result.durationMs, result.stdout, `queue:${qName}`);
|
|
1289
|
+
}
|
|
1290
|
+
if (action === 'pending') {
|
|
1291
|
+
const result = await manager.exec(nodeId, `cat ${qDir}/*.task 2>/dev/null | head -20 || echo '(empty)'`);
|
|
1292
|
+
return ok(nodeId, result.durationMs, result.stdout, `pending:${qName}`);
|
|
1293
|
+
}
|
|
1294
|
+
return fail('invalid action');
|
|
1295
|
+
});
|
|
1296
|
+
// --- Tool 45: omniwire_capability ---
|
|
1297
|
+
server.tool('omniwire_capability', 'Query node capabilities for intelligent task routing. Returns what tools, runtimes, and resources each node has. Agents use this to decide WHERE to dispatch tasks.', {
|
|
1298
|
+
node: z.string().optional().describe('Specific node (default: all online)'),
|
|
1299
|
+
check: z.array(z.string()).optional().describe('Specific capabilities to check (e.g., ["docker", "python3", "gpu", "nmap"])'),
|
|
1300
|
+
}, async ({ node, check }) => {
|
|
1301
|
+
const checks = check ?? ['docker', 'python3', 'node', 'go', 'nmap', 'ffuf', 'git', 'psql', 'lz4', 'aria2c', 'gcc'];
|
|
1302
|
+
const cmd = checks.map((c) => `command -v ${c} >/dev/null 2>&1 && echo "${c}:yes" || echo "${c}:no"`).join('; ');
|
|
1303
|
+
if (node) {
|
|
1304
|
+
const result = await manager.exec(node, cmd);
|
|
1305
|
+
return ok(node, result.durationMs, result.stdout, 'capabilities');
|
|
1306
|
+
}
|
|
1307
|
+
const results = await manager.execAll(cmd);
|
|
1308
|
+
return multiResult(results);
|
|
1309
|
+
});
|
|
1159
1310
|
return server;
|
|
1160
1311
|
}
|
|
1161
1312
|
//# sourceMappingURL=server.js.map
|